/* Marcelo Tosatti : unset LANG and LC_ALL before running chkconfig to avoid parse problems with traslations */ #include #include #include #include "redhat.h" #include #include #include #include "redhat.m" #include "../module_apis/status_api.h" #include "../module_apis/package_api.h" #include "../module_apis/servicectl_apidef.h" #include #include static HELP_FILE help_sysv ("redhat","sysv"); class SERVICE_SYSV_REDHAT: public SERVICE{ public: char levels[7]; SSTRING statusbuf; // Buffer to return a formatted value // in getrunstatus(). /*~PROTOBEG~ SERVICE_SYSV_REDHAT */ public: SERVICE_SYSV_REDHAT (const char *_name, const char *_desc, int _state, bool states[7]); int control (SERVICE_OPER oper); int edit (bool popup); int edit (void); const char *getconfstatus (void); const char *getrunstatus (void); int install_levels (void); private: void readinfo (SSTRINGS&desc, char suggest_levels[7]); public: void showstatus (void); /*~PROTOEND~ SERVICE_SYSV_REDHAT */ }; PUBLIC SERVICE_SYSV_REDHAT::SERVICE_SYSV_REDHAT( const char *_name, const char *_desc, int _state, bool states[7]) : SERVICE (_name,_desc,_state,OWNER_DISTRIB) { for (int i=0; i<7; i++) levels[i] = states[i] ? 1 : 0; } static const char initd[] = "/etc/rc.d/init.d"; static const char redhat_sysv[]="redhat/sysv.c"; PUBLIC int SERVICE_SYSV_REDHAT::control (SERVICE_OPER oper) { int ret = -1; if (perm_rootaccess(MSG_U(P_CTRLSERV,"control service activity"))){ char startcmd[100]; snprintf (startcmd,sizeof(startcmd)-1,"%s/%s start",initd,name.get()); char stopcmd[100]; snprintf (stopcmd,sizeof(stopcmd)-1,"%s/%s stop",initd,name.get()); if (oper == SERVICE_START){ ret = netconf_system (15,startcmd); }else if (oper == SERVICE_STOP){ ret = netconf_system (15,stopcmd); }else if (oper == SERVICE_RESTART){ // We do a stop/start as restart is not always reliable netconf_system (15,stopcmd); ret = netconf_system (15,startcmd); } } return ret; } static char zero[7]; /* Return a string indicating if the service is running */ PUBLIC const char *SERVICE_SYSV_REDHAT::getrunstatus() { char lockfile[PATH_MAX]; sprintf (lockfile,"/var/lock/subsys/%s",name.get()); bool running = file_exist(lockfile); const char *ret = ""; if (running){ statusbuf.setfrom (MSG_U(I_RUNNING,"Running")); if (state == 0){ SSTRINGS desc; char suggest_levels[7]; readinfo (desc,suggest_levels); if (memcmp(suggest_levels,zero,sizeof(zero))!=0 && memcmp(levels,suggest_levels,sizeof(levels))!=0){ statusbuf.appendf (" %s",MSG_U(I_CUSTOMLEVELS,"(modified)")); } } ret = statusbuf.get(); } return ret; } /* Return a string indicating if the service is enabled */ PUBLIC const char *SERVICE_SYSV_REDHAT::getconfstatus() { return state == 0 ? MSG_U(I_SYSVENABLED,"Automatic") : MSG_U(I_SYSVDISABLED,"Manual"); } PUBLIC void SERVICE_SYSV_REDHAT::showstatus() { STATUS_API *api = status_api_init(redhat_sysv); if (api != NULL){ char cmd[100],title[100]; snprintf (cmd,sizeof(cmd)-1,"%s/%s",initd,name.get()); snprintf (title,sizeof(title)-1,MSG_U(T_SERVNAME,"Service %s"),name.get()); api->showcommand (title,cmd,"status"); status_api_end(api); } } /* Put in place a custom set of runlevels */ PUBLIC int SERVICE_SYSV_REDHAT::install_levels() { int ret = 0; // We must call chkconfig once to disable some runlevels // and then another time to enable others for (int l=0; l<2; l++){ char tmp[100]; strcpy (tmp,"--level "); bool one = false; for (int i=0; i<7; i++){ if (levels[i] == l){ sprintf (tmp+strlen(tmp),"%d",i); one = true; } } if (one){ strcat (tmp," "); strcat (tmp,name.get()); strcat (tmp,l ? " on" : " off"); ret |= netconf_system_if ("chkconfig",tmp); } } return ret; } /* Read some information at the start of the sysv script */ PRIVATE void SERVICE_SYSV_REDHAT::readinfo( SSTRINGS &desc, char suggest_levels[7]) { memset (suggest_levels,0,7); desc.remove_all(); char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s/%s",initd,name.get()); FILE *fin = fopen (path,"r"); if (fin != NULL){ char buf[1000]; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ if (buf[0] != '#') break; strip_end (buf); // Check for a continuation character int last = strlen (buf) - 1; bool cont = false; if (buf[last] == '\\'){ buf[last] = '\0'; cont = true; } if (strncmp(buf,"# chkconfig:",12)==0){ const char *lev = str_skip(buf+12); if (*lev == '-'){ suggest_levels[3] = 1; suggest_levels[4] = 1; suggest_levels[5] = 1; }else{ while (isdigit(*lev)) suggest_levels[*lev++-'0'] = 1; } }else if (strncmp(buf,"# description:",14)==0){ desc.add (new SSTRING(str_skip(buf+14))); if (!cont) break; }else if (desc.getnb() > 0){ desc.add (new SSTRING(str_skip(buf+1))); if (!cont) break; } } fclose (fin); } } PUBLIC int SERVICE_SYSV_REDHAT::edit(bool popup) { int ret = 0; int nof = 0; SSTRINGS desc; char suggest_levels[7]; readinfo (desc,suggest_levels); char enabled = state==0; PACKAGE_API *pkg_api = package_api_init ("redhat/sysv.cc"); SSTRING pkg; PACKAGE_VERSION ver; bool pkgfound = false; if (pkg_api != NULL){ // Get the package from the sysv script char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s/%s",initd,name.get()); pkgfound = pkg_api->path2pkg (pkg_api,path,pkg,ver) != -1; } DIALOG dia; if (popup) dia.setcontext (""); dia.newf_title (MSG_U(I_BASE,"Basic info"),1,"",MSG_R(I_BASE)); dia.newf_chk (MSG_U(F_CONFIGURED,"Startup") ,enabled,MSG_R(I_SYSVENABLED)); dia.newline(); SSTRING statusstr(getrunstatus()); int status_field = dia.getnb(); dia.newf_str (MSG_U(F_STATUS,"Status"),statusstr); // dia.set_lastreadonly(); dia.newline(); if (pkgfound){ dia.newf_info (MSG_U(F_PACKAGE,"Package name"),pkg.get()); dia.newline(); dia.newf_info (MSG_U(F_PKGVER,"Package version"),ver.str); dia.newline(); }else if (pkg_api == NULL){ // Tell the user that managerpm is not installed dia.newf_info (MSG_R(F_PACKAGE) ,MSG_U(I_NOMANAGERPM,"(No package manager available)")); dia.newline(); }else{ dia.newf_info (MSG_R(F_PACKAGE),""); dia.newline(); } if (dialog_mode != DIALOG_GUI){ for (int i=0; iget()); } }else{ dia.gui_group (MSG_R(F_DESCRIPTION)); for (int i=0; iget()); dia.newline(); } dia.gui_end (); dia.gui_dispolast (GUI_H_LEFT,2,GUI_V_TOP,1); dia.newline(); } dia.newf_title (MSG_U(I_RUNLEVELS,"Run levels"),1,"",MSG_R(I_RUNLEVELS)); char previous_levels[7]; memcpy (previous_levels,levels,sizeof(previous_levels)); for (int i=0; i<7; i++){ static const char *tblevels[]={ MSG_U(I_LEVEL0,"Halt"), MSG_U(I_LEVEL1,"Single user"), MSG_U(I_LEVEL2,"Multi-user/Text"), MSG_U(I_LEVEL3,"Multi-user/Text/Full network"), MSG_U(I_LEVEL4,"Not used"), MSG_U(I_LEVEL5,"Multi-user/Graphical"), MSG_U(I_LEVEL6,"Reboot") }; char tmp[20]; snprintf (tmp,sizeof(tmp)-1,MSG_U(F_RUNLEVEL,"Level %d"),i); if (suggest_levels[i]){ strcat (tmp," "); strcat (tmp,MSG_U(I_RECOMMENDED,"(default)")); } dia.newf_chk (tmp,levels[i],tblevels[i]); dia.newline(); } 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)); int butopt = MENUBUT_USR1|MENUBUT_USR2|MENUBUT_USR3; if (status_api_available(redhat_sysv)){ dia.setbutinfo (MENU_USR4,MSG_U(B_STATUS,"Status"),MSG_R(B_STATUS)); butopt |= MENUBUT_USR4; } if (pkg_api != NULL && pkgfound){ dia.setbutinfo (MENU_USR5,MSG_U(B_PKGINFO,"Pkg info"),MSG_R(B_PKGINFO)); butopt |= MENUBUT_USR5; } char title[100]; snprintf (title,sizeof(title)-1,MSG_U(T_ONESERVICE,"Service %s") ,name.get()); while (1){ MENU_STATUS code = dia.edit (title ,MSG_U(I_ONESERVICE,"You can enable/disable a service\n" "or you can start and stop it manually") ,help_sysv,nof ,MENUBUT_CANCEL|MENUBUT_ACCEPT|butopt); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_USR1 || code == MENU_USR2 || code == MENU_USR3){ control (code == MENU_USR1 ? SERVICE_START : (code == MENU_USR2 ? SERVICE_STOP : SERVICE_RESTART)); statusstr.setfrom (getrunstatus()); dia.reload (status_field); ret = 1; }else if (code == MENU_USR4){ showstatus(); }else if (code == MENU_USR5){ pkg_api->showinfo (pkg_api,pkg.get()); }else if (code == MENU_ACCEPT){ state = enabled ? 0 : 1; if (state != previous){ // Ok the service is changing state completly // from emabled to disabled or the reverse if (enabled){ if (memcmp(levels,zero,sizeof(levels))!=0){ // Ok, the admin has enabled the service and // selected specific runlevels ret |= install_levels (); }else{ memcpy (levels,suggest_levels,sizeof(levels)); ret |= install_levels(); #if 0 char tmp[100]; sprintf (tmp,"%s reset",name.get()); ret |= netconf_system_if ("chkconfig",tmp); #endif } }else{ char tmp[100]; sprintf (tmp,"--level 0123456 %s off",name.get()); ret |= netconf_system_if ("chkconfig",tmp); } }else if (enabled && memcmp(levels,previous_levels,sizeof(levels))!=0){ // Ok, the service is still enabled, but the runlevels // settings have changed. install_levels (); } break; } } package_api_end (pkg_api); return ret || state != previous; } PUBLIC int SERVICE_SYSV_REDHAT::edit() { return edit (true); } /* Collect all or a single sysv service */ static int sysv_collect (SERVICES &tb, const char *optname) { int ret = -1; bool lc = false, langb = false; char *lc_all = NULL, *lang = NULL; if(getenv("LC_ALL") != NULL) { lc = true; lc_all = strdup(getenv("LC_ALL")); } if(getenv("LANG") != NULL) { langb = true; lang = strdup(getenv("LANG")); } setenv("LC_ALL","",1); setenv("LANG","",1); SSTRING arg; arg.setfromf ("--list %s",optname); POPEN pop ("chkconfig",arg.get()); if(langb == true) { setenv("LANG",lang,1); free(lang); } if(lc == true) { setenv("LC_ALL",lc_all,1); free(lc_all); } if (pop.isok()){ ret = 0; while (pop.wait(10)!=-1){ char buf[1000]; while (pop.readout(buf,sizeof(buf)-1)!=-1){ strip_end (buf); if (buf[0] != '\0'){ ret++; bool states[7]; memset (states,0,sizeof(states)); char name[100]; int state = 2; const char *pt = str_copyword(name,buf,sizeof(name)-1); pt = str_skip(pt); /* #Specification: /sbin/chkconfig / parsing output Linuxconf uses the /sbin/chkconfig utility to get the state of the various system V services. Since rh7, chkconfig also reports the state of the various services configured by xinetd. Those services are already handled by inetdconf, so we discard them. We use a trick to tell the sysv service lines apart from the xinetd service lines. sysv service lines have the following format word N:... where N is a single digit representing the run level. An xinetd service is either on or off, so it lacks this N: */ if (isdigit(*pt) && pt[1] == ':'){ while (1){ char word[100]; pt = str_copyword (word,pt,sizeof(word)-1); if (word[0] == '\0') break; int level = atoi(word); if (strstr(word,":on")!=NULL){ states[level] = true; state = 0; } } tb.add (new SERVICE_SYSV_REDHAT (name,"",state,states)); } } } } } return ret; } /* Collect all sysv service */ static int sysv_collect (SERVICES &tb) { return sysv_collect (tb,""); } static const char initd_dirnew[]="/etc/init.d"; static const char initd_dirold[]="/etc/rc.d/init.d"; static const char *sysv_getinitdir() { const char *ret = initd_dirnew; if(!file_exist(ret)) ret = initd_dirold; return ret; } /* Present the control dialog for a given service. Return -1 if the service does not exist */ static int sysv_control_name (const char *name) { int ret = -1; char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s/%s",sysv_getinitdir(),name); if (file_exist(path)){ ret = 0; SERVICES tb; int n = sysv_collect (tb,name); if (n == 1){ ((SERVICE_SYSV_REDHAT*)tb.getitem(0))->edit(true); } } return ret; } /* Try to present the control dialog of the service associated with the program "path". From the program name we try to match a service or a package. Return -1 if the service can't be identified. */ static int sysv_control (const char *path) { int ret = -1; const char *name = strrchr(path,'/'); if (name != NULL){ name++; // Ok, we look for /etc/rc.d/init.d/name // If this does not work, we will try the package info ret = sysv_control_name (name); } if (ret == -1){ PACKAGE_API *pkg_api = package_api_init ("redhat/sysv.cc"); if (pkg_api != NULL){ PACKAGE_VERSION ver; SSTRING pkg; if (pkg_api->path2pkg (pkg_api,path,pkg,ver) != -1){ // Ok, we have the name of the package ret = sysv_control_name(pkg.get()); if (ret == -1){ // Ok, no service of this name // We search in the package for a file in /etc/rc.d/init.d SSTRINGS lst; int nb = pkg_api->listfiles (pkg_api,pkg.get(),lst); const char *initd_dir = sysv_getinitdir(); int lendir = strlen(initd_dir); for (int i=0; iget(); if (strncmp(s,initd_dir,lendir)==0 && s[lendir] == '/'){ ret = sysv_control_name (s+lendir+1); } } } } package_api_end (pkg_api); } } return ret; } void *sysv_api_get () { SERVICECTL_API *api = new SERVICECTL_API; api->collect = sysv_collect; api->control = sysv_control; return api; } void sysv_api_release (void *obj) { SERVICECTL_API *api = (SERVICECTL_API*)obj; delete api; } class CONFIG_FILE_SYSV: public CONFIG_FILE{ /*~PROTOBEG~ CONFIG_FILE_SYSV */ public: CONFIG_FILE_SYSV (void); int archive (SSTREAM&ss)const; int extract (SSTREAM&ss); /*~PROTOEND~ CONFIG_FILE_SYSV */ }; PUBLIC CONFIG_FILE_SYSV::CONFIG_FILE_SYSV() : CONFIG_FILE ("/etc/sysconfig/sysv.state",help_nil ,CONFIGF_VIRTUAL,"services") { } PUBLIC int CONFIG_FILE_SYSV::archive(SSTREAM &ss) const { SERVICES servs; sysv_collect(servs); configf_sendexist (ss,true); int n = servs.getnb(); for (int i=0; ilevels[j]) sprintf (states+strlen(states),"%d",j); } ss.printf ("%s %s\n",s->name.get(),states); } return 0; } PUBLIC int CONFIG_FILE_SYSV::extract(SSTREAM &ss) { SERVICES servs; sysv_collect (servs); int n = servs.getnb(); char line[100]; while (ss.gets(line,sizeof(line)-1) != NULL){ char name[100],lev[100]; if (sscanf(line,"%s %s",name,lev)==2){ char levels[7]; memset (levels,0,sizeof(levels)); char *pt = lev; while (isdigit(*pt)) levels[*pt++ - '0'] = 1; for (int j=0; jname.cmp(name)==0){ if (memcmp(levels,s->levels,sizeof(levels))!=0){ memcpy (s->levels,levels,sizeof(levels)); s->install_levels(); } break; } } } } return 0; } static CONFIG_FILE_SYSV sysv_state;