/* This is an attempt to support the startup of systemd service inside a vserver Systemd is trying to achieve something with a syntax base solution instead of an API. The old sysv init script could do anything, because they were program. But they rely on no service or API, se were crude. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; static DEBUG_KEY d_buildreq ("buildreq","Logic to find service/target dependancies"); static DEBUG_KEY d_goctl ("goctl","General debug for goctl"); static string p_unit("Unit"); static string p_after("After"); static string p_install("Install"); static string p_wantedby("WantedBy"); static string p_wants("Wants"); static string p_requires("Requires"); static string p_sysv_scripts("sysv-scripts.target"); // Pseudo target for sysv scripts static string p_service("Service"); static string p_environment("Environment"); static string p_environmentfile("EnvironmentFile"); static string p_execreload("ExecReload"); static string p_execstart("ExecStart"); static string p_execstartpre("ExecStartPre"); static string p_execstartpost("ExecStartPost"); static string p_execstop("ExecStop"); static string p_pidfile("PIDFile"); static string p_privatetmp("PrivateTmp"); static string p_type("Type"); static string p_forking("forking"); static string p_notify("notify"); static string p_idle("idle"); static string p_user("User"); static bool verbose = false; // This holds the information for a service or a target // A service file is made of parts (or section) like [UNIT] // Parts contains features identified with a keyw=value ... // A feature may be repeated. The values are appended. // A feature with no value: keyword= // just erase the whole feature enum SERVICE_TYPE{ TYPE_UNKNOWN, TYPE_SERVICE, TYPE_TARGET, TYPE_MOUNT, TYPE_SOCKET, TYPE_AUTOMOUNT, TYPE_TIMER, TYPE_PATH }; enum START_TYPE{ START_SIMPLE,START_FORKING,START_NOTIFY,START_IDLE }; typedef map > MAPVECTOR; typedef map MAPPARTS; class SERVICE{ string name; string filename; SERVICE_TYPE type; bool is_sysv; // This is a system V startup script, not a real systemd service MAPPARTS parts; const vector &get_feature (const string &part, const string &key) const; const string &get_feature_one (const string &part, const string &key) const; int exec (bool simul, int pid, const string &cmd, const vector &environ) const; int exec (bool simul, int pid, const vector &cmd, const vector &environ) const; int daemon_exec (bool simul, const string &cmd, const vector &environ, const string &pidfile) const; string patchcmd(int pid, const string &cmd) const; const string &getpidfile() const; void readenviron (vector &tb) const; START_TYPE getstarttype() const; const string getuser() const; public: SERVICE(const char *name, const char *official_path, const char *local_path, SERVICE_TYPE type); SERVICE(const char *name, SERVICE_TYPE type); SERVICE(const string &name, SERVICE_TYPE type); SERVICE(){} const string &getname() const { return name; } const string getfullname() const { return name + (type == TYPE_SERVICE ? ".service" : ".target"); } void dump() const; int start(bool simul, bool verbose) const; int stop(bool simul, bool verbose) const; int reload(bool simul) const; int status() const; bool is_running() const; void requires(vector &req) const; void wants(vector &req) const; const vector &wanted_by() const; bool is_service() const{ return type == TYPE_SERVICE; } bool is_target() const{ return type == TYPE_TARGET; } // Add a value to a feature in a part void append (const string &part, const string &keyw, const char *val){ vector &curvals = parts[part][keyw]; curvals.push_back(val); } // Erase a feature in a part void reset (const string &part, const string &keyw){ MAPPARTS::iterator it = parts.find(part); if (it != parts.end()){ MAPVECTOR::iterator jit = it->second.find(keyw); if (jit != it->second.end()){ it->second.erase (jit); } } } void set_sysv (){ is_sysv = true; } }; SERVICE::SERVICE(const char *_name, SERVICE_TYPE _type) { is_sysv = false; type = _type; const char *pt = strchr(_name,'.'); if (pt == NULL){ name = _name; }else{ name = string(_name,pt-_name); } } SERVICE::SERVICE(const string &_name, SERVICE_TYPE _type) { is_sysv = false; type = _type; const char *start = _name.c_str(); const char *pt = strchr(start,'.'); if (pt == NULL){ name = _name; }else{ name = _name.substr(0,pt-start); } } SERVICE::SERVICE(const char *_name, const char *official_path, const char *local_path, SERVICE_TYPE _type) { is_sysv = false; type = _type; const char *pt = strchr(_name,'.'); if (pt == NULL){ name = _name; }else{ name = string(_name,pt-_name); } glocal SERVICE *service = this; glocal string part; //printf ("Reading service %s %s\n",official_name,local_name); const char *fname = official_path; if (file_exist(local_path)) fname = local_path; filename = fname; (fname,true); tlmp_error ("Service file %s is empty\n",info.filename); const char *pt = str_skip(line); if (*pt != '\0' && *pt != '#'){ if (*pt == '['){ pt++; const char *start = pt; while (*pt != ']' && *pt != '\0') pt++; glocal.part = string(start,pt-start); //printf ("Reading part :%s:\n",glocal.part.c_str()); }else if (strncmp(pt,".include",8) && isspace(pt[9])){ pt = str_skip(pt+8); include (pt); }else{ const char *pteq = strchr(pt,'='); if (pteq == NULL){ tlmp_error ("Reading service file %s: Invalid line %d %s\n",info.filename,noline,line); }else{ string keyw = string(pt,pteq-pt); //printf ("keyw=%s val=%s\n",keyw.c_str(),pteq+1); pteq = str_skip(pteq+1); if (*pteq == '\0'){ glocal.service->reset (glocal.part,keyw); }else{ glocal.service->append (glocal.part,keyw,pteq); } } } } return 0; const string pidfile = get_feature_one(p_service,p_pidfile); if (pidfile.size() == 0){ char tmp[PATH_MAX]; snprintf (tmp,sizeof(tmp)-1,"/var/run/%s.pid",name.c_str()); append(p_service,p_pidfile,tmp); } const string type = get_feature_one(p_service,p_type); if (type.size()==0){ append (p_service,p_type,"simple"); } } bool SERVICE::is_running() const { return status() > 0; } const vector &SERVICE::get_feature (const string &part, const string &key) const { static vector empty; MAPPARTS::const_iterator it = parts.find(part); if (it != parts.end()){ MAPVECTOR::const_iterator jit = it->second.find(key); if (jit != it->second.end()){ return jit->second; } } return empty; } /* Get a value for a feature which may only occur once */ const string &SERVICE::get_feature_one (const string &part, const string &key) const { static string empty; const vector &tmp = get_feature(part,key); if (tmp.size()>0){ if (tmp.size() > 1){ tlmp_error ("Feature %s in part %s can't be repeated, first value used\n",key.c_str(),part.c_str()); } return tmp[0]; } return empty; } void SERVICE::requires(vector &req) const { req = get_feature (p_unit,p_requires); } void SERVICE::wants(vector &req) const { req = get_feature (p_unit,p_wants); } static void log_printf (const char *ctl, ...) { printf ("\t"); va_list list; va_start (list,ctl); vprintf (ctl,list); va_end (list); } /* Replace $variable with content. MAINPID is supplied. All other variable are expected from /etc/sysconfig/NAME */ string SERVICE::patchcmd(int pid, const string &cmd) const { string ret = cmd; string sysconfig = string("/etc/sysconfig/")+ name; while (1){ const char *start = ret.c_str(); const char *ptdol = strchr(start,'$'); if (ptdol == NULL) break; string var; const char *ptvar = ptdol+1; const char *ptend = ptvar; if (*ptvar == '{'){ ptvar++; const char *pt = ptvar; while (*pt != '\0' && *pt != '}') pt++; var = string(ptvar,pt-ptvar); if (*pt == '}') pt++; ptend = pt; }else{ const char *pt = ptvar; while (isalpha(*pt) || *pt == '_') pt++; var = string(ptvar,pt-ptvar); ptend = pt; } //printf ("var = %s\n",var.c_str()); char buf[1000]; buf[0] = '\0'; if (strcmp(var.c_str(),"MAINPID")==0){ snprintf (buf,sizeof(buf)-1,"%d",pid); }else{ if (!file_exist (sysconfig.c_str())){ tlmp_error ("File %s is missing, can't find proper configuration for service %s\n",sysconfig.c_str(),name.c_str()); ret = ""; break; } char syscmd[1000]; snprintf (syscmd,sizeof(syscmd)-1,"%s= ; . %s; echo $%s",var.c_str(),sysconfig.c_str(),var.c_str()); //printf ("syscmd = %s\n",syscmd); FILE *fin = popen(syscmd,"r"); if (fin == NULL){ tlmp_error ("Evaluating variable %s, Can't execute %s\n",var.c_str(),syscmd); ret = ""; break; }else{ if (fgets(buf,sizeof(buf)-1,fin)!=NULL){ strip_end (buf); } pclose (fin); } } ret = string(start,ptdol-start)+buf+ptend; } return ret; } /* Final step to perform the execve systemcall, common do ::exec and ::daemon_exec */ static void commonexec(const string &cmd, const vector &environ) { vector tb; int n = str_splitline (cmd.c_str(),' ',tb); const char *args[n+1]; for (int i=0; i &environ) const { int ret = -1; if (cmd.size() == 0){ ret = 0; }else{ string tmp = patchcmd(pid,cmd); if (tmp.size() > 0){ log_printf ("Exec %s\n",tmp.c_str()); if(simul){ ret = 0; }else{ pid_t pid = fork(); if (pid == 0){ chdir ("/"); for (unsigned i=0; i &cmds, const vector &environ) const { int ret = 0; for (unsigned i=0; i int SERVICE::daemon_exec( bool simul, const string &cmd, const vector &environ, const string &pidfile) const { int ret = -1; string tmpcmd = patchcmd(-1,cmd); if (tmpcmd.size() > 0){ log_printf ("Exec %s\n",tmpcmd.c_str()); if (simul){ ret = 0; }else{ pid_t pid = fork(); if (pid == 0){ chdir ("/"); if (is_sysv){ for (int i=3; i<1024; i++) close (i); // Close all handles system (tmpcmd.c_str()); }else{ int tbout[2],tberr[2]; if (pipe(tbout)!=-1 && pipe(tberr)!=-1){ pid_t pid2 = fork(); if (pid2 == 0){ int fd = open ("/dev/null",O_RDONLY); if (fd == -1){ tlmp_error ("Can't open /dev/null, can't detach\n"); }else{ dup2 (fd,0); dup2 (tbout[1],1); dup2 (tberr[1],2); } for (int i=3; i<1024; i++) close (i); // Close all handles setsid(); if (pidfile.size() > 0){ (pidfile.c_str(),false); fprintf (fout,"%d\n",getpid()); return 0; } commonexec (tmpcmd,environ); }else if (pid2 == (pid_t)-1){ tlmp_error ("Can't fork (%s)",strerror(errno)); }else{ // We listen for 10 seconds on the stdout and stderr of the service // and quit glocal const char *name = name.c_str(); glocal int fderr = tberr[0]; openlog (glocal.name,0,LOG_DAEMON); (); endserver = true; //printf ("%s-%s: %s\n",glocal.name,no==glocal.fderr ? "err": "out",line); syslog (no==glocal.fderr ? LOG_ERR : LOG_INFO,"%s",line); //printf ("%s IDLE\n",glocal.name); endserver = true; o.inject (tbout[0]); o.inject (tberr[0]); o.loop(); //printf ("goctl %s stop listening\n",glocal.name); } } } _exit (0); }else if (pid == (pid_t)-1){ tlmp_error ("Can't fork (%s)",strerror(errno)); }else{ if (is_sysv){ // We have to wait until the service goes in background int st; while (waitpid(pid,&st,0)!=-1); } ret = 0; } } } return ret; } const string &SERVICE::getpidfile() const { return get_feature_one (p_service,p_pidfile); } /* Return -1 if service is not running Returns 0 if the service is not running, but should be Returns > 0 if the service is running */ int SERVICE::status() const { glocal int ret = -1; const string &pidfile = getpidfile(); if (pidfile.size() > 0){ (pidfile.c_str(),true); if (noline == 0){ glocal.ret = 0; if (verbose) printf ("status pidfile line 0: %s\n",line); int pid = atoi(line); if (pid > 0 && kill(pid,0)!=-1) glocal.ret = pid; } return 0; } return glocal.ret; } // Process the environment value and read the environfile void SERVICE::readenviron (vector &environ) const { environ.push_back("PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"); const string &environment = get_feature_one (p_service,p_environment); str_splitline (environment.c_str(),' ',environ); const string &environfile = get_feature_one (p_service,p_environmentfile); if (environfile.size() > 0){ const char *pt = environfile.c_str(); glocal bool required = true; if (*pt == '-'){ pt++; glocal.required = false; } glocal vector *environ = &environ; glocal const char *ptname = name.c_str(); (pt,true); line = str_skip(line); if (line[0] != '#' && line[0] != '\0'){ (*glocal.environ).push_back(line); } return 0; if (glocal.required){ tlmp_error ("Service %s, environment file %s is missing\n" ,glocal.ptname,fname); } } } START_TYPE SERVICE::getstarttype() const { START_TYPE ret = START_SIMPLE; string type = get_feature_one(p_service,p_type); if (type == p_forking){ ret = START_FORKING; }else if (type == p_notify){ ret = START_NOTIFY; }else if (type == p_idle){ ret = START_IDLE; } return ret; } const string SERVICE::getuser() const { string ret = get_feature_one(p_service,p_user); if (ret.size()==0) ret = name; return ret; } int SERVICE::start(bool simul, bool verbose) const { int ret = -1; if (status()>0){ if (verbose) log_printf ("Service %s is already running\n",name.c_str()); ret = 0; }else{ const string &pidfile = getpidfile(); { // Create the parent directory for the PID file if needed const char *start = pidfile.c_str(); const char *pt = strrchr(start,'/'); if (pt != NULL){ string dir (start,pt-start); const string user = getuser(); if(file_mkdirp(dir.c_str(),user.c_str(),user.c_str(),0755)==-1){ tlmp_error("Service %s: Can't create directory %s for the PID file (%s)\n" ,name.c_str(),dir.c_str(),strerror(errno)); } } // Create a directory in /run string dir = string("/run/") + name; if(file_mkdirp(dir.c_str(),0,0,0755)==-1){ tlmp_error("Service %s: Can't create directory %s (%s)\n" ,name.c_str(),dir.c_str(),strerror(errno)); } } const string &execstart = get_feature_one (p_service,p_execstart); const vector &execstartpre = get_feature (p_service,p_execstartpre); const vector &execstartpost = get_feature (p_service,p_execstartpost); vector environ; readenviron(environ); if (!simul) syslog (LOG_INFO,"Starting service %s",name.c_str()); if (exec(simul,-1,execstartpre,environ)==0 && daemon_exec(simul,execstart,environ,pidfile)==0){ if (execstartpost.size() > 0){ // We have to wait until we know the pid of the daemon int pid = -1; int i; for (i=0; i<10; i++){ pid = status(); if (pid > 0) break; usleep(1000); } if (verbose) log_printf ("Service %s, pid=%d i=%d\n",name.c_str(),pid,i); if (pid > 0){ exec (simul,pid,execstartpost,environ); ret = 0; } }else{ ret = 0; } } } return ret; } int SERVICE::stop(bool simul, bool verbose) const { int ret = -1; int pid = status(); const string &pidfile= getpidfile(); if (pid <= 0){ if (verbose) log_printf ("Service is not running\n"); unlink (pidfile.c_str()); ret = 0; }else{ if (!simul) syslog (LOG_INFO,"Stopping service %s",name.c_str()); const string &execstop = get_feature_one (p_service,p_execstop); vector environ; readenviron(environ); if (execstop.size() > 0){ ret = exec (simul,pid,execstop,environ); }else{ log_printf ("Killing pid %d with signal TERM\n",pid); if (simul){ ret = 0; }else{ ret = kill (-pid,SIGTERM); } } if (ret == 0 && !is_sysv){ unlink (pidfile.c_str()); } } return ret; } int SERVICE::reload(bool simul) const { int ret = -1; const string &execreload = get_feature_one (p_service,p_execreload); if (execreload.size() == 0){ tlmp_error ("Service %s can't reload\n",name.c_str()); }else{ int pid = status(); if (pid <=0 ){ log_printf ("Service %s is not running, can't reload\n",name.c_str()); }else{ if (!simul) syslog (LOG_INFO,"Reloading service %s",name.c_str()); vector environ; readenviron(environ); ret = exec (simul,pid,execreload,environ); } } return ret; } const vector &SERVICE::wanted_by() const { return get_feature (p_install,p_wantedby); } void SERVICE::dump() const { for (MAPPARTS::const_iterator it = parts.begin(); it != parts.end(); it++){ printf ("[%s]\n",it->first.c_str()); for (MAPVECTOR::const_iterator jit = it->second.begin(); jit != it->second.end(); jit++){ for (vector::const_iterator hit = jit->second.begin(); hit != jit->second.end(); hit++){ printf ("\t%s=%s\n",jit->first.c_str(),hit->c_str()); } } } vector environ; readenviron(environ); printf ("[exec ENVIRON]\n"); for (unsigned i=0; i static void goctl_load_dir_services(const char *service_dir, const char *local_dir, map &services) { glocal map *services = &services; glocal const char *local_dir = local_dir; (service_dir); return false; const char *pt = strchr(basename,'.'); if (pt != NULL && file_type(path)==0){ string localfile = string(glocal.local_dir) + "/" + basename; SERVICE_TYPE type = TYPE_UNKNOWN; if(strcmp(pt,".service")==0){ type = TYPE_SERVICE; }else if (strcmp(pt,".target")==0){ type = TYPE_TARGET; }else if (strcmp(pt,".socket")==0){ type = TYPE_SOCKET; }else if (strcmp(pt,".mount")==0){ type = TYPE_MOUNT; }else if (strcmp(pt,".automount")==0){ type = TYPE_AUTOMOUNT; }else if (strcmp(pt,".path")==0){ type = TYPE_PATH; }else if (strcmp(pt,".timer")==0){ type = TYPE_TIMER; } SERVICE serv (basename,path,localfile.c_str(),type); (*glocal.services)[basename] = serv; } } static void goctl_loaddepends(SERVICE &serv, const char *service_dir, const char *local_dir) { glocal SERVICE *service = &serv; static const char *tbsuffix[]={".wants",".requires",NULL}; static const char *tbkeyw[]={"Wants","Requires",NULL}; for (int j=0; tbsuffix[j] != NULL; j++){ glocal string keyw = tbkeyw[j]; // the local_dir has priority string tmp = string(local_dir)+"/"+serv.getfullname()+tbsuffix[j]; if (!file_exist(tmp.c_str())){ tmp = string(service_dir)+"/"+serv.getfullname()+tbsuffix[j]; } debug_printf (d_buildreq,"depend opendir %s\n",tmp.c_str()); (tmp.c_str()); glocal.service->append (p_unit,glocal.keyw,basename); } } static int goctl_loadservices(const char *service_dir, const char *local_dir, map &services) { int ret = -1; goctl_load_dir_services (service_dir, local_dir,services); // It is possible to define services in the local directory directly goctl_load_dir_services (local_dir, local_dir,services); // Load the wants and requires directory content. For each services, one of those // directories exists. It allows one to add wants and requires just // by droping a symlink in these directories, pointing to a service. for (map::iterator it=services.begin(); it != services.end(); it++){ goctl_loaddepends (it->second,service_dir,local_dir); } return ret; } static string goctl_patchname (const char *name) { string ret (name); if (strchr(name,'.')==NULL) ret += ".service"; return ret; } static SERVICE *goctl_findservice(const char *name, map &services) { string service = goctl_patchname (name); SERVICE *ret = NULL; map::iterator it = services.find (service); if (it != services.end()){ ret = &it->second; }else{ tlmp_error ("Unknown service %s\n",service.c_str()); } return ret; } static SERVICE *goctl_findservice(const string &name, map &services) { return goctl_findservice(name.c_str(),services); } #if 0 static void goctl_buildwanted (const map &services, const string &target, vector &subs) { for (map::const_iterator it = services.begin(); it != services.end(); it++){ string wanted = it->second.wanted_by(); if (wanted == target) subs.push_back (it->first); } printf ("Wantedby %s -> ",target.c_str()); for (unsigned i=0; i &t, vector &tbwords) { for (unsigned i=0; i &services, const string &service, vector &reqs) { set done; vector todo; todo.push_back(service); while (todo.size() > 0){ string name = todo[0]; reqs.push_back(name); todo.erase (todo.begin()); debug_printf (d_buildreq,"todo.size %lu inspect %s\n",todo.size(),name.c_str()); const SERVICE *serv = goctl_findservice (name.c_str(),services); if (serv == NULL){ done.insert (name); }else{ vector tb; if (serv->is_service()){ vector tmp; serv->requires(tmp); str_split(tmp,tb); serv->wants(tmp); str_split(tmp,tb); }else{ vector tmp; serv->requires(tmp); str_split(tmp,tb); serv->wants(tmp); str_split(tmp,tb); //goctl_buildwanted(services,name,tb); } for (unsigned i=0; i static void goctl_loadsysv (const char *dir, map &services) { glocal vector sysvs; // Collect the services in glocal map *services = &services; (dir); if (basename[0] == 'S' && isdigit(basename[1]) && isdigit(basename[2])){ glocal.sysvs.push_back (basename); } // The order is important sort (glocal.sysvs.rbegin(),glocal.sysvs.rend()); for (vector::iterator it = glocal.sysvs.begin(); it != glocal.sysvs.end(); it++){ const char *basename = it->c_str(); const char *name = basename+3; string sname = string(name) + ".service"; SERVICE s (sname.c_str(),TYPE_SERVICE); s.set_sysv(); char tmp[100]; snprintf (tmp,sizeof(tmp)-1,"%s/%s start",dir,basename); s.append (p_service,p_execstart,tmp); snprintf (tmp,sizeof(tmp)-1,"%s/%s stop",dir,basename); s.append (p_service,p_execstop,tmp); snprintf (tmp,sizeof(tmp)-1,"/var/run/%s.pid",name); s.append (p_service,p_pidfile,tmp); (*glocal.services)[sname] = s; SERVICE *multi = goctl_findservice (p_sysv_scripts,*glocal.services); if (multi == NULL){ tlmp_error ("Can't find multi-user.target, can't register systemV service %s\n",name); }else{ multi->append(p_unit,p_wants,sname.c_str()); } } } int main (int argc, char *argv[]) { glocal int ret = -1; glocal const char *service_dir = "/lib/systemd/system"; glocal const char *local_dir = "/etc/systemd/system"; glocal bool simul = false; glocal.ret = (argc,argv); setproginfo ("goctl",VERSION,"go service manager daemon\n" "\n" "\tgoctl list-services\n" "\tgoctl list-targets\n" "\n" "\tgoctl dump service/target\n" "\n" "\tgoctl reload service\n" "\tgoctl restart service/target\n" "\tgoctl start service/target\n" "\tgoctl status service\n" "\tgoctl stop service/target\n" ); setarg (' ',"servdir","Directory holding service files and targets",glocal.service_dir,false); setarg (' ',"localdir","Directory holding local modifications",glocal.local_dir,false); setarg (' ',"testmode","Show what will be done, do not do it",glocal.simul,false); setarg ('v',"verbose","Show more information",verbose,false); int ret = -1; string deftarget = "multi-user.target"; const char *command = argv[0]; map services; goctl_loadservices (glocal.service_dir,glocal.local_dir,services); if (services.find(p_sysv_scripts)==services.end()){ // Add the pseudo target for sysv scripts end somewhere SERVICE serv (p_sysv_scripts,TYPE_TARGET); goctl_loaddepends (serv,glocal.service_dir,glocal.local_dir); services[p_sysv_scripts] = serv; } goctl_loadsysv ("/etc/rc.d/rc3.d",services); debug_printf (d_goctl,"End parsing services\n"); { char buf[PATH_MAX]; int n = readlink("/etc/systemd/system/default.target",buf,sizeof(buf)-1); if (n!=-1){ buf[n] = '\0'; const char *pt = strrchr(buf,'/'); if (pt != NULL){ deftarget = (pt+1); } } } if (argc >= 2){ bool restart = strcmp(command,"restart")==0; if (strcmp(command,"start")==0 || restart){ for (int i=1; i reqs; if (goctl_buildreq(services,name,reqs)==-1){ tlmp_error ("Can't solve the start sequence for %s\n",name.c_str()); }else{ for (vector::reverse_iterator it=reqs.rbegin(); it != reqs.rend(); it++){ const SERVICE *s = goctl_findservice (it->c_str(),services); if (s != NULL && s->is_service()){ if (restart){ if (verbose) printf ("restart %s\n",it->c_str()); ret = s->stop(glocal.simul,verbose); if (ret == 0){ ret = s->start(glocal.simul,verbose); } }else{ if (verbose) printf ("start %s\n",it->c_str()); ret = s->start(glocal.simul,verbose); } } } } } } }else if (strcmp(command,"status")==0){ for (int i=1; iis_target()){ vector reqs; if (goctl_buildreq(services,name,reqs)==-1){ tlmp_error ("Can't solve the start sequence for %s\n",name); }else{ for (vector::reverse_iterator it=reqs.rbegin(); it != reqs.rend(); it++){ const SERVICE *s = goctl_findservice (it->c_str(),services); if (s != NULL && s->is_service()){ const char *servname = it->c_str(); if (verbose) printf ("status %s\n",servname); ret = s->status(); if (ret == -1){ printf ("\tService %s is not running\n",servname); }else if (ret == 0){ printf ("\tService %s is not running but should be\n",servname); } } } } }else{ ret = serv->status(); if (ret == -1){ printf ("Service %s is not running\n",name); }else if (ret == 0){ printf ("Service %s is not running but should be\n",name); }else{ printf ("Service %s is running (pid %d)\n",name,ret); } } } } }else if (strcmp(command,"stop")==0){ for (int i=1; iis_service()){ printf ("Stoping service %s:\n",name); ret = serv->stop(glocal.simul,verbose); if (ret == -1) printf ("\tfail\n"); }else if (serv->is_target()){ vector reqs; if (goctl_buildreq(services,name,reqs)==-1){ tlmp_error ("Can't solve the start sequence for %s\n",name); }else{ for (vector::iterator it=reqs.begin(); it != reqs.end(); it++){ const SERVICE *s = goctl_findservice (it->c_str(),services); if (s != NULL && s->is_service()){ if(verbose) printf ("stop service %s\n",it->c_str()); ret = s->stop(glocal.simul,verbose); } } } } } } }else if (strcmp(command,"reload")==0){ for (int i=1; ireload(glocal.simul); printf ("\t%s\n",ret == 0 ? "done" : "fail"); } } }else if (strcmp(command,"dump")==0){ const SERVICE *serv = goctl_findservice (argv[1],services); if (serv != NULL){ serv->dump(); } ret = 0; }else{ tlmp_error ("Invalid command: %s\n",command); usage(); } }else if (argc == 1 && strcmp(command,"list-services")==0){ for (map::iterator it = services.begin(); it != services.end(); it++){ if (it->second.is_service()) printf ("%s\n",it->first.c_str()); } ret = 0; }else if (argc == 1 && strcmp(command,"list-targets")==0){ for (map::iterator it = services.begin(); it != services.end(); it++){ if (it->second.is_target()) printf ("%s\n",it->first.c_str()); } ret = 0; }else{ tlmp_error ("Invalid command: %s\n",command); usage(); } return ret; return glocal.ret; }