#include #include #include #include #ifndef LINUXCONF_AOUT #include #endif #include #include #include #include #include #include #include "../paths.h" #include "netconf.h" #include "netconf.m" #include "internal.h" #include #include extern NETCONF_HELP_FILE help_control; static NETCONF_HELP_FILE help_dropin ("dropin"); static NETCONF_HELP_FILE help_pidfile ("pidfile"); static NETCONF_HELP_FILE help_actnew ("actnew"); #define DR_NOTCONFIG 100 #define DR_ENABLED 0 #define DR_TMPDIS 1 #define DR_DISABLED 2 PUBLIC RCSYSV::RCSYSV() { nbcfgpid = 0; no_reload = 0; override = false; } PROTECTED void RCSYSV::deletepids() { for (int i=0; i MAXSYSV_PROCESS){ nbproc = MAXSYSV_PROCESS; fprintf (stderr,"Too many processes in sysv init script %s, max. %d\n" ,name.get(),MAXSYSV_PROCESS); } for (int j=0; j MAXSYSV_PROCESS){ nbcfgpid = MAXSYSV_PROCESS; fprintf (stderr,"Too many PID files in sysv init script %s, max. %d\n" ,name.get(),MAXSYSV_PROCESS); } for (int i=0; iget() ,help_pidfile ,CONFIGF_PROBED|CONFIGF_OPTIONAL); tbcfgpid[i] = cfg; cmd.cprocess[i].pidfile = cfg; if (i == 0) cmd.cstart.pidfile = cfg; // This is not correct // Need more thinking } } /* #Specification: dropin / principle / read-write A dropin is a somewhat read-only. When installing a package, you can "drop" a control file in /etc/linuxconf/control and the user won't have to change anything on it. This allow the developper to safely provide newer versions without fearing the the user have modify it. Yet, the user can modify it, at least logically. Linuxconf has two ways to edit a control file. One is to edit it and update the file straight in /etc/linuxconf/control. The other one is to save only the changes in /etc/conf.linuxconf. The resulting merge of the original and the changes in /etc/conf.linuxconf is used to control the machine later. A mecanism will be needed to let the user view some changes in a control file when a new version of the package is installed. A revision number is associated with each control file. This revision number is stored in /etc/conf.linuxconf whenever a change is being done to the control file "logically". By comparing the revision number in /etc/conf.linuxconf and the one in /etc/linuxconf/control/, Linuxconf will know if some action is required when installating a new package. The "logical" editability of a dropin control file is not expect to be used often. This will avoid "bypassing" linuxconf is some exceptionnal case. */ # /* Site modification to a dropin are saved in /etc/conf.linuxconf. A special prefix is built for all line to differentiate the different dropins. */ PRIVATE void DROPIN::makekey1( const char *key1, char buf[100]) { snprintf (buf,100-1,"DROPIN-%s-%s",getname(),key1); } PRIVATE void DROPIN::getval ( CONFDB &conf, const char *key1, const char *key2, SSTRING &v, const char *defval) { const char *val = NULL; if (localconf){ char localkey1[100]; makekey1(key1,localkey1); val = linuxconf_getval(localkey1,key2); } if (val == NULL){ val = conf.getval(key1,key2,defval); } v.setfrom(val); } PRIVATE void DROPIN::getval ( CONFDB &conf, const char *key1, const char *key2, int &v, int defval) { int val = -1; if (localconf){ char localkey1[100]; makekey1(key1,localkey1); val = linuxconf_getvalnum(localkey1,key2,-1); } if (val == -1){ val = conf.getvalnum(key1,key2,defval); } v = val; } PRIVATE void DROPIN::getval ( CONFDB &conf, const char *key1, const char *key2, char &v, int defval) { int intv; getval (conf,key1,key2,intv,defval); v = intv; } PRIVATE void DROPIN::getall ( CONFDB &conf, const char *key1, const char *key2, SSTRINGS &tb) { if (localconf){ char localkey1[100]; makekey1 (key1,localkey1); linuxconf_getall(localkey1,key2,tb,1); } if (tb.getnb()==0){ conf.getall(key1,key2,tb,1); } } PUBLIC DROPIN::DROPIN(const char *_name, bool _localconf) { char _path[PATH_MAX]; snprintf (_path,sizeof(_path)-1,"%s/%s",ETC_LINUXCONF_CONTROL,_name); localconf = _localconf; path.setfrom (_path); CONFIG_FILE cfg (_path,help_dropin,CONFIGF_MANAGED); CONFDB conf(cfg); name.setfrom (conf.getval(K_KEY,K_PROGNAME,"")); revision.setfrom (conf.getval(K_KEY,K_REVISION,"")); staterun = orig_staterun = linuxconf_getvalnum(K_ENABLED ,getname(),DR_NOTCONFIG); getval (conf,K_KEY,K_DESC,desc,""); getall (conf,K_KEY,K_COMMENT,comments); getval (conf,K_KEY,K_STARTAFTER,startafter,""); getval (conf,K_KEY,K_STARTLEVEL,startlevel,0); getval (conf,K_KEY,K_STOPLEVEL,stoplevel,0); getval (conf,K_KEY,K_MODULE,module,""); getval (conf,K_CMD,K_START,cmd.start,""); getval (conf,K_CMD,K_STOP,cmd.stop,""); getval (conf,K_CMD,K_RELOAD,cmd.reload,""); getval (conf,K_CMD,K_BOOT,cmd.boot,""); getval (conf,K_CMD,K_PROBE,cmd.probe,""); SSTRINGS tmp; getall (conf,K_PATH,K_MONITOR,tmp); for (int i=0; iget()); } getall (conf,K_CMD,K_PROCESS,processes); getall (conf,K_PATH,K_PIDFILE,pidfiles); // There was only one PID file per dropin so the field // was always there in the dropin file, but sometime // empty. Now you can have a list, so an empty list // means no pidfile, unlike one empty field. // The same apply to processes. pidfiles.remove_empty(); processes.remove_empty(); getval (conf,K_KEY,K_NORELOAD,no_reload,0); if (no_reload){ for (int j=0; jautoreload = 1; } no_reload = 0; } init_command(); } PUBLIC DROPIN::DROPIN() { localconf = false; startlevel = 0; stoplevel = 0; } PRIVATE void DROPIN::replace ( CONFDB &conf, const char *key1, const char *key2, SSTRING &v, bool &differ) // Will be set to true if at least // one field was saved in /etc/conf.linuxconf { if (!localconf){ conf.replace (key1,key2,v); }else{ char localkey1[100]; makekey1(key1,localkey1); const char *origv = conf.getval(key1,key2); if (origv == NULL) origv = ""; if (v.cmp(origv)!=0){ linuxconf_replace (localkey1,key2,v); differ = true; }else{ linuxconf_removeall (localkey1,key2); } } } PRIVATE void DROPIN::replace ( CONFDB &conf, const char *key1, const char *key2, char v, bool &differ) // Will be set to true if at least // one field was saved in /etc/conf.linuxconf { if (!localconf){ conf.replace (key1,key2,v); }else{ char localkey1[100]; makekey1(key1,localkey1); if (v != conf.getvalnum(key1,key2,0)){ linuxconf_replace (localkey1,key2,v); differ = true; }else{ linuxconf_removeall (localkey1,key2); } } } PRIVATE void DROPIN::replace ( CONFDB &conf, const char *key1, const char *key2, SSTRINGS &tb, bool &differ) // Will be set to true if at least // one field was saved in /etc/conf.linuxconf { if (!localconf){ conf.replace (key1,key2,tb); }else{ SSTRINGS origtb; conf.getall (key1,key2,origtb,0); bool same = false; if (tb.getnb()==origtb.getnb()){ same = true; for (int i=0; icmp(*s2)!=0){ same = false; break; } } } char localkey1[100]; makekey1(key1,localkey1); if (same){ linuxconf_removeall (localkey1,key2); }else{ linuxconf_replace (localkey1,key2,tb); differ = true; } } } PRIVATE void DROPIN::replace ( CONFDB &conf, const char *key1, const char *key2, CONFIG_SYSVS &tb, bool &differ) // Will be set to true if at least // one field was saved in /etc/conf.linuxconf { SSTRINGS tmp; for (int i=0; ipath.get(),s->autoreload ? " autoreload" : ""); tmp.add (new SSTRING (buf)); } replace (conf,key1,key2,tmp,differ); } PUBLIC int DROPIN::write() { if (path.is_empty()){ char buf[PATH_MAX]; snprintf (buf,sizeof(buf)-1,"%s/%s",ETC_LINUXCONF_CONTROL,getname()); path.setfrom (buf); } if (!file_exist(ETC_LINUXCONF_CONTROL)){ // Make sure the directory do exist mkdir (ETC_LINUXCONF,0755); mkdir (ETC_LINUXCONF_CONTROL,0755); } CONFIG_FILE cfg (path.get(),help_dropin ,CONFIGF_MANAGED|CONFIGF_OPTIONAL); CONFDB conf(cfg); if (!localconf){ conf.replace (K_KEY,K_PROGNAME,name); conf.replace (K_KEY,K_REVISION,revision); } bool differ = false; replace (conf,K_KEY,K_DESC,desc,differ); replace (conf,K_KEY,K_COMMENT,comments,differ); replace (conf,K_KEY,K_STARTAFTER,startafter,differ); replace (conf,K_KEY,K_STARTLEVEL,startlevel,differ); replace (conf,K_KEY,K_STOPLEVEL,stoplevel,differ); replace (conf,K_KEY,K_MODULE,module,differ); replace (conf,K_CMD,K_START,cmd.start,differ); replace (conf,K_CMD,K_STOP,cmd.stop,differ); replace (conf,K_CMD,K_RELOAD,cmd.reload,differ); replace (conf,K_CMD,K_BOOT,cmd.boot,differ); replace (conf,K_CMD,K_PROBE,cmd.probe,differ); replace (conf,K_CMD,K_PROCESS,processes,differ); replace (conf,K_PATH,K_MONITOR,monitors,differ); replace (conf,K_PATH,K_PIDFILE,pidfiles,differ); #if 0 // There is now one autoreload flag for each config file // Old dropin are patch are read time replace (conf,K_KEY,K_NORELOAD,no_reload,differ); #else conf.removeall (K_KEY,K_NORELOAD); #endif init_command(); int ret; if (localconf){ char localkey1[100]; makekey1 (K_KEY,localkey1); linuxconf_replace (K_ENABLED,getname(),staterun); if (differ){ linuxconf_replace (localkey1,K_REVISION,revision); }else{ linuxconf_removeall (localkey1,K_REVISION); } ret = linuxconf_save(); }else{ ret = conf.save(); } return ret; } static DROPINS *dropins; /* Load this host version of the dropins. into a local variable to avoid reloading them all the time. */ static void dropin_loadlocal() { if (dropins == NULL) dropins = new DROPINS(true); } /* Free this host version of the dropins. */ static void dropin_freelocal() { delete dropins; dropins = NULL; } PROTECTED VIRTUAL int RCSYSV::getstaterun() { return DR_ENABLED; } PROTECTED int DROPIN::getstaterun() { return staterun; } PROTECTED int RCSYSV::exec (COMMAND &c) { int ret = -1; while (1){ bool config_needed; if (c.checkpath(config_needed)==0){ ret = c.system(); break; }else if (config_needed){ if (perm_checkpass()){ SERVICES tb; if (edit(tb) != 0 || getstaterun() != DR_ENABLED) break; } }else{ break; } } return ret; } PUBLIC int RCSYSV::start() { int ret = 0; if (!cmd.start.is_empty()) ret = exec (cmd.cstart); return ret; } /* Find the oldest process for a package A sysv script may define several processes, but the oldest is used to tell if a restart is needed */ PUBLIC PROC *RCSYSV::findprocess(bool &problem) { problem = false; PROC *ret = NULL; int n = processes.getnb(); if (n == 0){ ret = cmd.cstart.findprocess(); }else{ long low = 0x7fffffff; bool missing = false; for (int i=0; igetstarttime(); if (start < low){ low = start; ret = p; } }else{ missing = true; } } if (ret != NULL && missing){ // If one process is missing, the package is not // in a healty state. Ideally, it should be stop // and restarted. So we signal a problem problem = true; } } return ret; } PUBLIC int RCSYSV::restart() { int ret = -1; if (no_reload){ ret = 0; }else if (cmd.reload.is_empty()){ ret = DAEMON::restart(); }else{ ret = exec (cmd.creload); } return ret; } PUBLIC int RCSYSV::stop() { int ret = -1; if (!cmd.stop.is_empty()){ bool problem; if (findprocess(problem)!=NULL){ ret = exec (cmd.cstop); } }else if (processes.getnb() > 0){ for (int i=0; iget()); } daemon_saverestartdb(); } } PUBLIC int RCSYSV::startif() { int ret = 0; if (cmd.probe.is_empty()){ SSTRINGS tb; tb.neverdelete(); int n=monitors.getnb(); for (int i=0; iautoreload){ tb.add (&s->path); } } ret = DAEMON::startif_file (tb); }else{ /* #Specification: dropins and sysv scripts / probe mode Both dropin and sysv script can supply their own probing method. When supplied, linuxconf will called the method. In case of a sysv script, it call the script with the command "probe". In case of a dropin it simply call the supplied command (probe command) with the probe argument. Both commands return 0 or more lines. Those lines are commands that should be executed to bring the service in sync with its configuration. Normally a probe command will return one of the verb # stop start restart # For sysv script, the script will be called with the appropriate verb. Note that a probe may return many lines and they will be all executed, one after the other. In the case of a dropin, the stop, start and restart verb will trigger the stop,start et restart command of the dropin (if the stop or the restart method command are missing, a default action is computed to achieve that). But a probe function may return other verb (or lines with argument). In the case of a sysv script, the script will be called with those lines. For dropin, the probe command itself will be called with those arguments instead of the probe argument it normally receive. */ SSTRINGS tbtasks; char buf[PATH_MAX+PATH_MAX]; { snprintf (buf,sizeof(buf)-1,"%s probe",cmd.probe.get()); POPEN pop (buf); while (pop.wait(10)>0){ char line[1000]; while (pop.readout(line,sizeof(line)-1) != -1){ strip_end (line); if (line[0] != '\0'){ tbtasks.add (new SSTRING(line)); } } } } if (tbtasks.getnb() > 0){ net_prtlog (NETLOG_WHY,MSG_U(I_PROBECMD ,"The following command told me something had to be done\n")); net_prtlog (NETLOG_WHY,"%s\n",buf); for (int i=0; iget(); if (strcmp(verb,"start")==0){ start(); }else if (strcmp(verb,"stop")==0){ stop(); }else if (strcmp(verb,"restart")==0){ setrestarttime(); restart(); }else{ setrestarttime(); snprintf (buf,sizeof(buf)-1,"%s %s",cmd.probe.get() ,verb); ret = netconf_system (15,buf); } } } } return ret; } /* Check if a given package must be activated */ PUBLIC int DROPIN::startif () { int ret = 0; if (mayrun()){ char buf[200]; snprintf (buf,sizeof(buf)-1,MSG_U(T_STARTSYS,"Starting %s"),getdesc()); net_title (buf); ret = RCSYSV::startif(); }else{ ret = stop(); } return ret; } /* Add a bunch of fields, including some empty one in the dialog */ static void dropin_add_bunch( DIALOG &dia, SSTRINGS &tb, const char *title) { // Always add 3 empty lines for config files int add = 3; dia.newf_title( "",title); int i; for (i=0; iis_empty()) add--; dia.newf_str ("",*s); } for (i=0; ipath.is_empty()) add--; dia.newf_str ("",s->path); dia.newf_chk ("",s->autoreload,MSG_U(F_AUTORELOAD,"Autoreloaded")); } for (i=0; ipath); dia.newf_chk ("",s->autoreload,MSG_R(F_AUTORELOAD)); } } /* A Sysv configuration can't be edited and the path of the start,restart,stop command can't be wrong, so this function is just a stub so the RCSYSV::exec() function may be more general and do its path checking. */ PUBLIC VIRTUAL int RCSYSV::edit(const SERVICES &) { return -1; } PUBLIC int DROPIN::edit(const SERVICES &tb) { /* #Spécification: dropins / configuration / override or redefine There is two way a dropin definition may be edited. One is to edit the control file directly and the other is to store the modification in /etc/conf.linuxconf. The idea is that those file should never be modified by the end user (the admin). This allows reliable replacement during a package update. Further the overrides stored in /etc/modules.conf are partial. If for example, you have changed the way a package is started by changing the start command, only this change will be stored in /etc/conf.linuxconf. If the package is upgrade later, the stop command may have changed and you will inherit the change. For sure, there is a limit to all this, but this is the picture. The other important point is that overwriting a control file with another one won't destroy your own changes. Editing or overriding is done with the same dialog. Linuxconf picks the difference between the original configuration and the new one and store those in /etc/conf.linuxconf. */ int ret = -1; DIALOG dia; if (localconf){ static const char *tbopt[]={ MSG_U(O_ENABLED,"Enabled"), MSG_U(O_TMPDIS,"Temp-disabled"), MSG_U(O_DISABLED,"Disabled"), NULL }; dia.newf_chkm ("",staterun,tbopt); dia.newf_title ("",""); } dia.newf_str (MSG_U(F_PKGNAME,"Package's name"),name); if (localconf) dia.set_lastreadonly(); dia.newf_str (MSG_U(F_REVISION,"Dropin revision"),revision); if (localconf) dia.set_lastreadonly(); dia.newf_str (MSG_U(F_PKGDESC,"Package's description"),desc); dia.newf_str (MSG_U(F_PKGSTART,"Start command"),cmd.start); dia.newf_str (MSG_U(F_PKGSTOP,"Stop command"),cmd.stop); #if 0 dia.newf_chk ("",no_reload,MSG_U(F_NORELOAD ,"No reload/restart needed")); #endif dia.newf_str (MSG_U(F_PKGRELOAD,"Reload command"),cmd.reload); dia.newf_str (MSG_U(F_PKGPROBE,"Probe command"),cmd.probe); dia.newf_str (MSG_U(F_BOOTCMD,"Boot time cleanup"),cmd.boot); dia.newf_str (MSG_U(F_PKGMODULE,"Module name or path"),module); dropin_add_bunch (dia,processes,MSG_U(F_PROCNAME,"Process names")); dropin_add_bunch (dia,pidfiles,MSG_U(F_PIDFILE,"PID files")); dia.newf_title ("",MSG_U(T_ACTIVATION,"Activation control")); FIELD_COMBO *comb = dia.newf_combo (MSG_U(F_STARTAFTER,"Start after package"),startafter); { for (int i=0; iaddopt (s->name.get(),s->desc.get()); } } FIELD_ENUM *fenum = dia.newf_enum (MSG_U(F_STARTLEVEL,"Start at runlevel"),startlevel); fenum->addopt (MSG_U(F_LOCAL,"No networking")); fenum->addopt (MSG_U(F_CLIENT,"Client networking")); fenum->addopt (MSG_U(F_SERVER,"Server networking")); fenum = dia.newf_enum (MSG_U(F_STOPLEVEL,"Stop at runlevel"),stoplevel); fenum->addopt (MSG_U(F_DONTSTOP,"Never stop")); fenum->addopt (MSG_R(F_LOCAL)); fenum->addopt (MSG_R(F_CLIENT)); fenum->addopt (MSG_R(F_SERVER)); dropin_add_bunch(dia,monitors,MSG_U(F_PKGCONFIG,"Config files")); dropin_add_bunch(dia,comments,MSG_U(F_PKGCOMMENTS,"Comments")); int nof = 0; while (1){ int but = MENUBUT_ACCEPT|MENUBUT_CANCEL; if (!localconf) but |= MENUBUT_DEL; MENU_STATUS code = dia.edit_form (MSG_U(T_DROPCONF,"Dropin configuration") ,MSG_U(I_DROPCONF,"You can control how Linuxconf will manage\n" "a given package, when it will start, stop, and reload/restart\n" "and how and why\n") ,help_dropin ,nof ,but); if (code == MENU_CANCEL || code == MENU_ESCAPE){ dia.restore(); break; }else if (code == MENU_DEL){ if (xconf_delok() && perm_rootaccess(MSG_U(P_DELDROP,"to delete a dropin"))){ if (!path.is_empty()){ CONFIG_FILE cfg (path.get(),help_dropin ,CONFIGF_MANAGED|CONFIGF_OPTIONAL); cfg.unlink(); } ret = 1; break; } }else if (code == MENU_ACCEPT){ processes.remove_empty(); pidfiles.remove_empty(); monitors.remove_empty(); comments.remove_empty(); write (); ret = 0; break; } } return ret; } PUBLIC DROPIN *DROPINS::getitem(int no) { return (DROPIN*)ARRAY::getitem(no); } PUBLIC DROPINS::DROPINS(bool _localconf) { localconf = _localconf; SSTRINGS tb; int nb = dir_getlist(ETC_LINUXCONF_CONTROL,tb); for (int i=0; iget(); if (strcmp(name,".")!=0 && strcmp(name,"..")!=0 && strstr(name,".OLD")==NULL){ add (new DROPIN (name,_localconf)); } } } /* Activate all possible packages related to a given run level and which must be start after last_pkg */ PUBLIC int DROPINS::activate( int netlevel, // Which netlevel are we activating const char *last_pkg, // Which package was updated last BOOTRCS &rcs) { int ret = 0; int n = getnb(); for (int i=0; istartlevel == netlevel && d->startafter.cmp(last_pkg)==0 && d->mayrun()){ int lret = d->startif(); ret |= lret; const char *dname = d->getname(); rcs.startsome (dname); if (lret == 0) activate (netlevel,dname,rcs); } } return ret; } /* Deactivate all possible packages related to a given run level and which must be stop after last_pkg */ PUBLIC int DROPINS::deactivate( int netlevel) // Which netlevel is the current target { int ret = 0; int n = getnb(); for (int i=0; istoplevel > netlevel || !d->mayrun()){ char buf[200]; snprintf (buf,sizeof(buf)-1,MSG_U(T_STOPSYS,"Stopping %s") ,d->getdesc()); net_title (buf); ret |= d->stop(); } } return ret; } /* Execute all tasks required at boot time (cleanup) */ PUBLIC int DROPINS::doboot() { int ret = 0; int n = getnb(); for (int i=0; iboot(); } return ret; } int dropin_activate( int netlevel, // Which netlevel are we activating const char *last_pkg, // Which package was updated last BOOTRCS &rcs) // Potentially start some SysV init script { dropin_loadlocal(); return dropins->activate (netlevel,last_pkg,rcs); } int dropin_deactivate( int netlevel) // Which netlevel are we activating { dropin_loadlocal(); return dropins->deactivate (netlevel); } /* Run some specific tasks required by some dropins at boot time */ int dropin_doboot() { net_prtlog (NETLOG_TITLE,MSG_U(T_BOOTDROPIN ,"Dropin's boot time commands\n")); dropin_loadlocal(); return dropins->doboot (); } static int cmp_by_name (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { DROPIN *d1 = (DROPIN*)o1; DROPIN *d2 = (DROPIN*)o2; return strcmp(d1->getname(),d2->getname()); } PUBLIC int DROPINS::edit () { int ret = -1; int nof = 0; while (1){ sort (cmp_by_name); DIALOG dia; int n = getnb(); for (int i=0; igetname(),d->desc.get()); } dia.addwhat (MSG_U(I_ADDDROPIN,"Select [Add] to add a new dropin definition")); MENU_STATUS code = dia.editmenu ( MSG_U(T_DROPINMNG,"Dropin management") ,MSG_U(I_DROPINMNG,"You are allowed to modify, delete and\n" "add new dropins. Dropins provide information\n" "letting information control and manage add-on package") ,help_dropin ,nof,MENUBUT_ADD); SERVICES tbs; start_getservtb(tbs); getservtb (tbs); tbs.sort(); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else if (code == MENU_ADD){ DROPIN *d = new DROPIN; int ok = d->edit (tbs); manage_edit (d,ok); }else if (nof >=0 && nof < getnb()){ DROPIN *d = getitem(nof); int ok = d->edit (tbs); manage_edit (d,ok); } } return ret; } /* Make correction to dropin's definition. */ int dropin_editlocal() { dropin_loadlocal(); int ret = dropins->edit (); dropin_freelocal(); return ret; } /* Edit the origianl definition of the dropins. This will generally be used by package author. */ int dropin_editorig() { DROPINS *dropins = new DROPINS(false); int ret = dropins->edit (); dropin_freelocal(); return ret; } /* Update the "staterun" of any dropin if it has changed */ PUBLIC int DROPINS::savestate() { bool save = false; int n = getnb(); for (int i=0; istaterun != d->orig_staterun){ linuxconf_replace (K_ENABLED,d->getname(),d->staterun); save = true; } } if (save) linuxconf_save(); return save == true; } /* Check if some dropin must be re-activated. */ PUBLIC void DROPINS::activate_new() { int n = getnb(); for (int i=0; iactivate_new(); } savestate(); } /* Check if some new dropins must be activated */ void dropin_activate_new() { dropin_loadlocal(); dropins->activate_new(); } /* This function is called to enable dropins which were temporarily disabled. */ PUBLIC void DROPINS::reenable() { int n = getnb(); for (int i=0; istaterun == DR_TMPDIS){ net_prtlog (NETLOG_VERB ,MSG_U(I_REENABLE,"Re-enabling service %s\n") ,d->getname()); d->staterun = DR_ENABLED; } } savestate(); } /* Check if some dropins must be re-activated */ void dropin_reenable() { dropin_loadlocal(); dropins->reenable(); } class SERVICE_DROPIN: public SERVICE{ /*~PROTOBEG~ SERVICE_DROPIN */ public: SERVICE_DROPIN (const char *_name, const char *_desc, int _state); int control (SERVICE_OPER oper); int edit (void); const char *getconfstatus (void); const char *getrunstatus (void); /*~PROTOEND~ SERVICE_DROPIN */ }; PUBLIC SERVICE_DROPIN::SERVICE_DROPIN( const char *_name, const char *_desc, int _state) : SERVICE (_name,_desc,_state,OWNER_DROPIN) { } /* Start, stop, restart a service */ PUBLIC int SERVICE_DROPIN::control (SERVICE_OPER oper) { DROPIN drop (name.get(),false); int ret = -1; if (oper == SERVICE_STOP){ ret = drop.stop(); }else if (oper == SERVICE_START){ ret = drop.start(); }else if (oper == SERVICE_RESTART){ ret = drop.restart(); } process_flushcache(); return ret; } /* Return a string indicating if the service is running */ PUBLIC const char *SERVICE_DROPIN::getrunstatus() { DROPIN drop (name.get(),false); bool problem; return drop.findprocess(problem) == NULL ? "" : MSG_U(I_RUNNING,"Running"); } /* Return a string indicating if the service is enabled */ PUBLIC const char *SERVICE_DROPIN::getconfstatus() { static const char *tbstate[]={ MSG_R(I_ENABLED), MSG_R(I_TMPDISABLED), MSG_R(I_DISABLED), NULL }; return tbstate[(unsigned)state]; } PUBLIC int SERVICE_DROPIN::edit() { int ret = 0; int nof = 0; DIALOG dia; while (1){ dia.remove_all(); static const char *tbstate[]={ MSG_R(I_ENABLED), MSG_R(I_TMPDISABLED), MSG_R(I_DISABLED), NULL }; dia.newf_chkm (MSG_R(F_STATUS),state,tbstate); dia.setbutinfo (MENU_USR1,MSG_U(B_START,"Start"),MSG_R(B_START)); dia.setbutinfo (MENU_USR2,MSG_U(B_STOP,"Stop"),MSG_R(B_STOP)); dia.setbutinfo (MENU_USR3,MSG_U(B_RESTART,"Restart"),MSG_R(B_RESTART)); MENU_STATUS code = dia.edit_form (MSG_R(T_ONESERVICE) ,MSG_R(I_ONESERVICE) ,help_control,nof ,MENUBUT_CANCEL|MENUBUT_ACCEPT |MENUBUT_USR1|MENUBUT_USR2|MENUBUT_USR3); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_USR1){ control (SERVICE_START); ret = 1; }else if (code == MENU_USR2){ control (SERVICE_STOP); ret = 1; }else if (code == MENU_USR3){ control (SERVICE_RESTART); ret = 1; }else{ ret = 1; break; } } return ret; } /* Get the information to create the service activity dialog */ PUBLIC int DROPINS::getservtb(SERVICES &tb) { int n = getnb(); for (int i=0; igetname(),d->getdesc(),d->staterun)); } return n; } /* Set the state information from the service activity dialog */ PUBLIC int DROPINS::setservtb(SERVICES &tb) { int n = getnb(); int nbs = tb.getnb(); for (int i=0; igetname(); for (int j = 0; jowner == OWNER_DROPIN && s->name.cmp(name)==0){ d->staterun = s->state; break; } } } return n; } /* Get a list of all dropin so we can create the control panel service activity dialog */ int dropin_getservtb (SERVICES &tb) { dropin_loadlocal(); return dropins->getservtb(tb); } /* Set the state of all dropins from the SERVICES table */ int dropin_setservtb (SERVICES &tb) { dropin_loadlocal(); return dropins->setservtb(tb); } /* Return != 0 if the dropin were saved (maybe because it was not needed) */ int dropin_savestate () { assert (dropins != NULL); return dropins->savestate(); } void dropin_addbootequiv(BOOTRCS &rcs) { DROPINS drps (true); int n = drps.getnb(); for (int i=0; igetname(); rcs.add (new BOOTRC(name,name)); } } /* Load all the module from the various dropins */ void dropin_module_load () { #ifndef LINUXCONF_AOUT dropin_loadlocal(); int n = dropins->getnb(); for (int i=0; igetitem(i); const char *modpath = d->module.get(); if (*modpath != '\0' && d->staterun != DR_DISABLED){ char path[PATH_MAX]; if (module_locate(modpath,path)==-1){ xconf_error (MSG_U(E_DRPNOMODPATH ,"Module %s not found for dropin %s") ,modpath,d->getname()); }else{ module_loadcheck (path); } } } #endif } PUBLIC DROPIN_SUBSYSS::DROPIN_SUBSYSS() { tb = NULL; nbtb = 0; } PUBLIC void DROPIN_SUBSYSS::alloc(int n) { for (int t=0; t 0){ const char *subsys = getname(); subs.tb[subs.nbtb++] = new LINUXCONF_SUBSYS (subsys,desc.get()); for (int j=0; j < nbf; j++){ const char *f = monitors.getitem(j)->path.get(); if (configf_locate(f)==NULL){ new CONFIG_FILE (f,help_nil,CONFIGF_OPTIONAL,subsys); } } } } static void dropin_lister_fct() { /* #Specification: dropin / file archiving / principle All config file specified in a dropin participate in the configuration versionning. Quite often, a module (defined by a dropin) define itself few CONFIG_FILE object which are the same one defined in the dropin itself. The module may have a special way to archive these config file. So the file defined in the dropin are defined as CONFIG_FILE only if they are not already by some modules. For each dropin, a subsystem is defined with the name of the dropin. This could be introduced in a dropin definition (the subsystem itself). The idea would be that a several dropins are part of the same sub-systems. We will see if this is need. */ // We recreate (and keep) the LINUXCONF_SUBSYS object each time // we are called. We trash the old one and create new ones. static DROPIN_SUBSYSS subs; dropin_loadlocal(); int n = dropins->getnb(); subs.alloc (n); if (subs.tb != NULL){ for (int i=0; igetitem(i)->list_config(subs); } } } static CONFIG_FILE_LISTER dropin_lister(dropin_lister_fct);