#include #include #include #include #include #include #include #include #include "misc.h" #include "sstream.h" #include "confdb.h" #include "subsys.h" #include "../paths.h" #include #include /* #Specification: /etc/conf.linuxconf / permissions For security reasons, /etc/conf.linuxconf is unreadable for all users except root. There is a lot of information in it allowing potential intruders to spot information that they really don't need to know. For exemple, /etc/conf.linuxconf tells which users have which administrative privilege. This is not much for an intruder, but it certainly tells which user passwords have move value than others. */ static HELP_FILE help_linuxconf ("misc","linuxconf"); CONFIG_FILE f_linuxconf (ETC_CONF_LINUXCONF ,help_linuxconf ,CONFIGF_OPTIONAL|CONFIGF_MANAGED|CONFIGF_NOARCH ,"root","root",0600); static CONFIG_FILE f_locale_alias (USR_LOCALE_ALIAS,help_nil,CONFIGF_OPTIONAL); static CONFDB *(*linuxconf_fctnew)(CONFDB*)=NULL; /* Record the hook function so a module may co-manage the /etc/conf.linuxconf file. */ void linuxconf_sethook(CONFDB *(*fct)(CONFDB*)) { linuxconf_fctnew = fct; } /* Change the path of the /etc/conf.linuxconf file This function is used by application written with liblinuxconf wishing to use the linuxconf_xxx functions to save and retrieve their settings. This must be called early enough during application initialisation because the original file may be opened real soon. */ void linuxconf_setdbpath (const char *path) { f_linuxconf.setkey (path); } static void linuxconf_init() { if (ui_context.tb == NULL){ ui_context.linuxconf_date = f_linuxconf.getdate(); ui_context.tb = new CONFDB (f_linuxconf); ui_context.tb->patchsys(); if (linuxconf_fctnew!=NULL){ /* #Specification: /etc/conf.linuxconf / spreaded in distributions The file /etc/conf.linuxconf does contain various stuff which is normally stored in various distribution specific (proprietary) files and format. The function linuxconf_sethook() lets a module insert a filter object in the handling of the /etc/conf.linuxconf files. This filter object may intercept request to this file and dispatch those to the proper cnnfig files. The function pointer provided will be called with a CONFDB object handling the current /etc/conf.linuxconf (there is one conf.linuxconf per administration tree when using the netadm module). It return another CONFDB object which does whatever it wants. This CONFDB object is probably derived from CONFDB and insert all kind of filtering. The function called has the responsability to delete CONFDB object passed as an argument when it is not needed anymore. */ ui_context.tb = linuxconf_fctnew (ui_context.tb); } } } /* Get a pointer to the main linuxconf's configuration database */ CONFDB *linuxconf_getdb() { linuxconf_init(); return ui_context.tb; } /* Locate one configuration parameter. Return NULL if not found. */ const char *linuxconf_getval (const char *prefix, const char *key) { linuxconf_init(); return ui_context.tb->getval (prefix,key); } /* Locate one configuration parameter. Return defval if not found. */ const char *linuxconf_getval ( const char *prefix, const char *key, const char *defval) { linuxconf_init(); return ui_context.tb->getval (prefix,key,defval); } /* Locate one numeric configuration parameter. Return defval if not found. */ int linuxconf_getvalnum (const char *prefix, const char *key, int defval) { linuxconf_init(); return ui_context.tb->getvalnum(prefix,key,defval); } /* Locate all configuration parameter with the same key. Return the number found. */ int linuxconf_getall ( const char *prefix, const char *key, SSTRINGS &lst, bool copy) // Take a copy of the values { linuxconf_init(); return ui_context.tb->getall (prefix,key,lst,copy); } /* Remove all entry with a given key. */ void linuxconf_removeall (const char *prefix, const char *key) { if (ui_context.tb != NULL) ui_context.tb->removeall (prefix,key); } /* Forget the in memory copy of /etc/conf.linuxconf */ void linuxconf_forget() { delete ui_context.tb; ui_context.tb = NULL; } /* Save the configuration parameters Return -1 if any error. */ static int linuxconf_save( PRIVILEGE *priv, bool do_archive) // Trigger the archiving of all modified sections // of conf.linuxconf { int ret = 0; if (ui_context.tb != NULL){ if (do_archive){ // First we must archive the various parts of conf.linuxconf SSTRINGS tb; ui_context.tb->getmodsys(tb); for (int i=0; iget()); } } ret = ui_context.tb->save(priv); if (ret == -1 && errno == EPERM){ // Avoid keeping in memory records potentially entered // by a user who don't know the root password. linuxconf_forget(); }else{ ui_context.linuxconf_date = f_linuxconf.getdate(); } } return ret; } /* Save the configuration parameters Return -1 if any error. */ int linuxconf_save(PRIVILEGE *priv) { return linuxconf_save (priv,true); } /* Save the configuration parameters Return -1 if any error. */ int linuxconf_save() { return linuxconf_save (NULL); } /* Record the name of the subsystem which will own the next entries added */ void linuxconf_setcursys (const char *sys) { linuxconf_init(); ui_context.tb->setcursys (sys); } class POPEN; class CONFIG_FILE_LINUXCONF: public CONFIG_FILE{ const char *subsys; /*~PROTOBEG~ CONFIG_FILE_LINUXCONF */ public: CONFIG_FILE_LINUXCONF (const char *_path, const char *_subsys); int archive (SSTREAM&ss)const; int archive (void)const; int extract (SSTREAM&ss); int extract (void); /*~PROTOEND~ CONFIG_FILE_LINUXCONF */ }; PUBLIC CONFIG_FILE_LINUXCONF::CONFIG_FILE_LINUXCONF( const char *_path, const char *_subsys) : CONFIG_FILE (_path,help_linuxconf,CONFIGF_VIRTUAL,_subsys) { subsys = _subsys; } PUBLIC int CONFIG_FILE_LINUXCONF::archive (SSTREAM &ss) const { linuxconf_init(); return ui_context.tb->archive (ss,subsys); } PUBLIC int CONFIG_FILE_LINUXCONF::archive () const { return CONFIG_FILE::archive(); } PUBLIC int CONFIG_FILE_LINUXCONF::extract (SSTREAM &ss) { linuxconf_init(); return ui_context.tb->extract (ss,subsys); } PUBLIC int CONFIG_FILE_LINUXCONF::extract () { return CONFIG_FILE::extract(); } /* Archive all section of conf.linuxconf corresponding to the subsystems. */ int linuxconf_archive(const char *subsys) { char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s-%s",ETC_CONF_LINUXCONF,subsys); CONFIG_FILE_LINUXCONF conf(path,subsys); return conf.archive(); } /* Compute the md5 checksum of all section of conf.linuxconf corresponding to the subsystems. */ int linuxconf_md5sum(const char *subsys, SSTREAM &ss) { char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s-%s",ETC_CONF_LINUXCONF,subsys); CONFIG_FILE_LINUXCONF conf(path,subsys); char sum[100]; int ret = conf.md5sum(sum); ss.printf ("%s\t%s\n",conf.getpath(),sum); return ret; } /* Extract the section of conf.linuxconf corresponding to a subsystem */ int linuxconf_extract (const char *subsys) { char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s-%s",ETC_CONF_LINUXCONF,subsys); CONFIG_FILE_LINUXCONF conf(path,subsys); return conf.extract(); } /* Add one record to the configuration file */ void linuxconf_add (const char *prefix, const char *key, const char *val) { linuxconf_init(); ui_context.tb->add (prefix,key,val); } /* Add one record in the configuration file */ void linuxconf_add (const char *prefix, const char *key, const SSTRING &val) { linuxconf_init(); ui_context.tb->add (prefix,key,val); } /* Add one record in the configuration file */ void linuxconf_add (const char *prefix, const char *key, int val) { linuxconf_init(); ui_context.tb->add (prefix,key,val); } /* Replace one record in the configuration file */ void linuxconf_replace (const char *prefix, const char *key, const char *val) { linuxconf_init(); ui_context.tb->replace (prefix,key,val); } /* Replace one record in the configuration file Remove the record if the value is empty or NULL. */ void linuxconf_replace_if (const char *prefix, const char *key, const char *val) { linuxconf_init(); ui_context.tb->replace_if (prefix,key,val); } /* Replace one record in the configuration file */ void linuxconf_replace (const char *prefix, const char *key, char val) { linuxconf_init(); ui_context.tb->replace (prefix,key,val); } /* Replace one record in the configuration file */ void linuxconf_replace (const char *prefix, const char *key, int val) { linuxconf_init(); ui_context.tb->replace (prefix,key,val); } /* Replace one record in the configuration file */ void linuxconf_replace (const char *prefix, const char *key, bool val) { linuxconf_init(); ui_context.tb->replace (prefix,key,val); } /* Replace one record in the configuration file */ void linuxconf_replace (const char *prefix, const char *key, long val) { linuxconf_init(); ui_context.tb->replace (prefix,key,val); } /* Replace one record in the configuration file */ void linuxconf_replace (const char *prefix, const char *key, const SSTRING &val) { linuxconf_init(); ui_context.tb->replace (prefix,key,val); } /* Replace one record in the configuration file. If the value is empty, the record is removed from the configuration file. */ void linuxconf_replace_if (const char *prefix, const char *key, const SSTRING &val) { linuxconf_init(); ui_context.tb->replace_if (prefix,key,val); } /* Replace all records to the configuration file for a key */ void linuxconf_replace ( const char *prefix, const char *key, const SSTRINGS &vals) { linuxconf_init(); ui_context.tb->replace (prefix,key,vals); } /* Get the directory name used to differentiate distribution specific stuff in linuxconf */ const char *linuxconf_getdistdir() { static char *dir = NULL; if (dir == NULL){ const char *ret = linuxconf_getval ("LINUXCONF","distribution"); if (ret == NULL){ static bool done = false; if (!done && geteuid()==0){ done = true; fprintf (stderr, "\n" "**** /etc/conf.linuxconf is incomplete.\n" " It must hold a line identifying the linux distribution.\n" " This probably means that Linuxconf was not properly installed\n" " or that somebody cleared /etc/conf.linuxconf.\n" "\n" " Linuxconf will behave badly unless this is fixed.\n" "\n" " A line like this must be written in /etc/conf.linuxconf:\n" "\n" " LINUXCONF.distribution redhat\n" ); sleep (4); } ret = "std"; } dir = strdup(ret); } return dir; } /* Fix a path which contain the /DIST/ subdir with the */ void linuxconf_fixdistdir (const char *templat, char path[PATH_MAX]) { char tmp[PATH_MAX]; strcpy (tmp,templat); char *pt = strstr(tmp,"/DIST/"); if (pt == NULL){ strcpy (path,tmp); }else{ *pt = '\0'; snprintf (path,PATH_MAX-1,"%s/%s/%s",tmp,linuxconf_getdistdir(),pt+6); } } /* Reload /etc/conf.linuxconf if the copy on disk is different from our in memory copy. Return != 0 if the file was reloaded */ int linuxconf_reloadif() { int ret = 0; long date = f_linuxconf.getdate(); if (date != ui_context.linuxconf_date){ linuxconf_forget(); linuxconf_init(); ret = 1; } return ret; } static const char K_LINUXCONF[]="linuxconf"; static const char K_LANG[]="lang"; static const char K_LASTLANG[]="lastlang"; static const char K_LANGMODE[]="langmode"; static const char K_GUIMODE[]="guimode"; static const char K_COLORMODE[]="colormode"; static const char K_PREFIXTRIG[]="prefixtrig"; /* Get the selected language prefix */ const char *linuxconf_getlangmanual() { const char *ret = linuxconf_getval (K_LINUXCONF,K_LANG); if (ret == NULL) ret = "eng"; return ret; } /* Lookup the /usr/share/locale/locale.alias for a given alias. Return the corresponding value, or the alias if not found. The value is placed in buffer. */ static const char *locale_getalias(const char *alias, char buffer[], int bufsiz) { const char *ret = alias; FILE_CFG *fin = f_locale_alias.fopen ("r"); if (fin != NULL){ char buf[1000]; while (fgets_strip(buf,sizeof(buf)-1,fin,NULL)!=NULL){ char word[100]; char *pt = str_copyword (word,buf,sizeof(word)-1); if (pt != NULL && strcmp(word,alias)==0){ pt = str_skip(pt); strcpy_cut (buffer,pt,bufsiz); ret = buffer; break; } } fclose (fin); } return ret; } /* Get the selected language prefix (automatic or manual) */ const char *linuxconf_getlang() { const char *ret = getenv("LINUXCONF_LANG"); if (ret == NULL || strlen(ret) > 5) ret = linuxconf_getlangmanual(); if (linuxconf_getlangmode()){ const char *envlang = getenv("LANG"); const char *lastlang = linuxconf_getval (K_LINUXCONF,K_LASTLANG); if (envlang == NULL){ envlang = lastlang; }else{ if ((lastlang == NULL || strcmp(envlang,lastlang)!=0) && geteuid()==0){ linuxconf_setcursys (subsys_noarch); linuxconf_replace (K_LINUXCONF,K_LASTLANG,envlang); xconf_fopencfg_bypass (true); linuxconf_save(NULL,false); xconf_fopencfg_bypass (false); } } if (envlang != NULL && strlen(envlang)>=2){ char bufali[100]; envlang = locale_getalias (envlang,bufali,sizeof(bufali)-1); static char ret2[3]; ret2[0] = tolower(envlang[0]); ret2[1] = tolower(envlang[1]); ret2[2] = '\0'; ret = ret2; /* We have into envlang the first two letters ie for fr_FR we copy just fr. Perhaps we should do something for variants in the future. For this we should test if the subvariant exist and if not try just the first two letters */ } } if (strcmp(ret,"en")==0) ret = "eng"; return ret; } /* Record the operation language which will be used if -the selection mode is set to manual -or there is no setting in the environnement */ void linuxconf_setlang (const char *lang) { if (lang[0] == '\0'){ linuxconf_removeall (K_LINUXCONF,K_LANG); }else{ linuxconf_replace (K_LINUXCONF,K_LANG,lang); } } /* Get the selection mode for the language (manual = 0, automatic = 1) */ bool linuxconf_getlangmode() { return linuxconf_getvalnum (K_LINUXCONF,K_LANGMODE,1) != 0; } /* Record the selection mode for the language */ void linuxconf_setlangmode (bool mode) { if (mode){ linuxconf_removeall (K_LINUXCONF,K_LANGMODE); }else{ linuxconf_replace (K_LINUXCONF,K_LANGMODE,mode); } } /* Find out if the GUI mode is allowed by the user. It will be turned off on some slow machine. */ bool linuxconf_getguimode() { return linuxconf_getvalnum (K_LINUXCONF,K_GUIMODE,1) != 0; } void linuxconf_setguimode (bool mode) { if (mode){ linuxconf_removeall (K_LINUXCONF,K_GUIMODE); }else{ linuxconf_replace (K_LINUXCONF,K_GUIMODE,mode); } } /* Find out if linuxconf is allowed to use color in text mode */ bool linuxconf_getcolormode() { return linuxconf_getvalnum (K_LINUXCONF,K_COLORMODE,1); } void linuxconf_setcolormode (bool mode) { if (mode){ linuxconf_removeall (K_LINUXCONF,K_COLORMODE); }else{ linuxconf_replace (K_LINUXCONF,K_COLORMODE,mode); } } /* Get the threshold value to enable the filtering for long list. When a record list is longer than this threshold, a popup lets the user enter a search prefix. The default is 60 entries. */ int linuxconf_getprefixtrig() { return linuxconf_getvalnum (K_LINUXCONF,K_PREFIXTRIG,60); } void linuxconf_setprefixtrig (int trig) { if (trig == 60){ linuxconf_removeall (K_LINUXCONF,K_PREFIXTRIG); }else{ linuxconf_replace (K_LINUXCONF,K_PREFIXTRIG,trig); } } /* Load the messages for a given module or linuxconf itself */ void linuxconf_loadmsg (const char *prefix, const char *rev) { char bdict[30]; snprintf (bdict,sizeof(bdict)-1,"%s-msg-%s",prefix,rev); const char *lang = linuxconf_getlang(); translat_load (USR_LIB_LINUXCONF "/help","LINUXCONF_DICT",bdict ,"LINUXCONF_LANG",lang); } /* Load the messages for linuxconf lib The message are looked for in pkgdir first. If missing, they are looked for in /usr/lib/linuxconf-lib/ */ void linuxconf_loadlibmsg (const char *pkgdir) { char bdict[30]; const char *lang = linuxconf_getlang(); snprintf (bdict,sizeof(bdict)-1,"linuxconf-msg-%s",PACKAGE_REV); char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s/help.eng/%s.eng",pkgdir,bdict); const char *path_help; if (file_exist(path)){ snprintf (path,sizeof(path)-1,"%s/help",pkgdir); path_help = path; }else{ path_help = "/usr/lib/linuxconf-lib/help"; } translat_load (path_help,"LINUXCONF_DICT",bdict,"LINUXCONF_LANG",lang); } static const char DISTRIB[]="--distrib--"; /* Obtain distribution dependant information. Normally, this is not stored in conf.linuxconf, but the distribution related module act as a filter and may inject whatever value needed. This trickery is used to provide distribution dependant information. For example, does this distribution supports the command restart for the sysv init scripts. */ int distrib_getvalnum (const char *key, int defval) { return linuxconf_getvalnum (DISTRIB,key,defval); } const char *distrib_getval (const char *key) { return linuxconf_getval (DISTRIB,key); } void distrib_replace (const char *key, const char *val) { linuxconf_replace (DISTRIB,key,val); } /* Tells if this distribution has been enhanced. This means that linuxconf relies 100% on sysv script to initialise anything. This defeats most of its internal rules. */ bool distrib_isenhanced() { return distrib_getvalnum("sysvenh",0) != 0; } /* Obtain the release number of the distribution. Return unknown if it can't be identified. */ const char *distrib_getrelease () { return linuxconf_getval (DISTRIB,"release","unknown"); }