#pragma implementation #include #include #include #include "misc.h" #include "confdb.h" #include "sstream.h" static const char K_BASE[]="base"; class CONFDB_SUBSYS: public ARRAY_OBJ{ public: SSTRING sys; bool modified; CONFDB_SUBSYS (const char *s) { modified = false; sys.setfrom (s); } }; typedef ARRAY_OBJS CONFDB_SUBSYSS; struct CONFDB_INTERNAL{ const char *cursys; // NULL or point into tbsys[] CONFDB_SUBSYS *cursyso; // Object corresponding to cursys CONFDB_SUBSYSS tbsys; CONFIG_FILE *fcfg; bool subsys_scope; bool equal_sign; // Keyword are separated from values using // an equal sign char comcar; // Character to indicate start of comment }; PUBLIC CONFOBJ::CONFOBJ ( const char *_sys, const char *_key, const char *_val) { sys = _sys; key.setfrom (_key); val.setfrom (_val); } /* Add a non-parsed line to the database */ PUBLIC void CONFDB::addline (const char *buf) { const char *key = ""; // By default, a line is recorded // as a comment (see savesys()) const char *val = buf; const char *start = str_skip(buf); char keyw[200]; if (start[0] == '\0' || start[0] != internal->comcar){ char *pt; if (internal->equal_sign){ pt = strchr(start,'='); if (pt != NULL){ char *ptkey = keyw; while (start < pt && (size_t)(ptkey-keyw) < sizeof(keyw)-1){ *ptkey++ = *start++; } *ptkey = '\0'; strip_end (keyw); key=keyw; val = str_skip (pt+1); } }else{ char *pt = str_copyword (keyw,buf,sizeof(keyw)-1); key = keyw; val = str_skip(pt); } } addk (key,val); } PRIVATE void CONFDB::init() { internal = new CONFDB_INTERNAL; internal->cursys = NULL; internal->subsys_scope = false; internal->equal_sign = false; internal->comcar = '#'; setcursys(K_BASE); } PRIVATE void CONFDB::resetmodified() { for (int i=0; itbsys.getnb(); i++){ internal->tbsys.getitem(i)->modified = false; } } PUBLIC void CONFDB::initload ( CONFIG_FILE &_fcfg, bool use_equal_sign, // Is the config file of type "key val" or "key=val" const char comcar) { init(); internal->equal_sign = use_equal_sign; internal->comcar = comcar; internal->fcfg = &_fcfg; /* #Specification: misc / CONFDB / intro The CONFDB object was designed to support the /etc/conf.linuxconf file. This save configuration information in an ascii file with a simple one line one record format. The line start with a key and the value(s) (words or whatever) follow up to the end of the line. The key is separated from the value(s) by blank characters. */ FILE_CFG *fin = internal->fcfg->fopen ("r"); if (fin != NULL){ char buf[1000]; int noline = 0; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ /* #Specification: /etc/conf.linuxconf / format /etc/conf.linuxconf is an ascii file. It contain all the information used to configured most services which lack a standard configuration file. Its format is simple # keyword value ... # The file is maintained by linuxconf. No comments or whatever are allowed. This mecanism is handled by the CONFDB object. It is expect to be used for other stuff than /etc/conf.linuxconf. */ /* #Specification: /etc/conf.linuxconf / format / subsystem This file is broken into subsystem. A subsystem is identified by a line like this # [subsys] # Every entries below this line belong to this subsystem until another subsystem definition is reached or the end of the file. The file generally does not start with a subsystem definition. Entry at the beginning of the file belongs to the main subsystem (noname). */ noline++; strip_end (buf); if (buf[0] == '['){ char *pt = buf+1; while (*pt != '\0' && *pt != ']') pt++; *pt = '\0'; setcursys (buf+1); }else if (buf[0] != '\0'){ addline (buf); } } fclose (fin); setcursys(K_BASE); resetmodified(); } } PUBLIC CONFDB::CONFDB(CONFIG_FILE &_fcfg) { initload (_fcfg,false,'#'); } PUBLIC CONFDB::CONFDB( CONFIG_FILE &_fcfg, bool use_equal_sign, const char comcar) { initload (_fcfg,use_equal_sign,comcar); } /* Constructor used when the database is not simply read from a file */ PUBLIC CONFDB::CONFDB() { init(); internal->fcfg = NULL; } PUBLIC CONFDB::~CONFDB() { delete internal; } PUBLIC CONFOBJ *CONFDB::getitem(int no) const { return (CONFOBJ*)ARRAY::getitem(no); } PRIVATE CONFDB_SUBSYS *CONFDB::locatesys( const char *sys) { CONFDB_SUBSYS *ret = NULL; CONFDB_SUBSYSS *tbsys = &internal->tbsys; int nbsys = tbsys->getnb(); for (int i=0; igetitem(i); if (subsys->sys.cmp(sys)==0){ ret = subsys; break; } } return ret; } /* Return the pointer corresponding to the subsystem. Caller are performing direct pointer compare */ PRIVATE const char *CONFDB::locatesyspt( const char *sys) { CONFDB_SUBSYS *subsys = locatesys (sys); return subsys == NULL ? NULL : subsys->sys.get(); } /* Record the new current subsystem used to add entries in the CONFDB This current subsys stay current until it is changed or the file is saved. If the subsystem is unknown, it is added. subsystem are case sensitive. */ PUBLIC VIRTUAL void CONFDB::setcursys ( const char *_subsys, bool _subsys_scope) // Only update record member of the sys-system { internal->subsys_scope = _subsys_scope; internal->cursyso = locatesys (_subsys); if (internal->cursyso == NULL){ CONFDB_SUBSYS *newsys = new CONFDB_SUBSYS (_subsys); internal->cursyso = newsys; internal->tbsys.add (newsys); } internal->cursys = internal->cursyso->sys.get(); } /* Record the new current subsystem used to add entries in the CONFDB This current subsys stay current until it is changed or the file is saved. If the subsystem is unknown, it is added. subsystem are case sensitive. */ PUBLIC VIRTUAL void CONFDB::setcursys (const char *_subsys) { setcursys (_subsys,false); } /* Save all entries owned by a subsystem */ PRIVATE void CONFDB::savesys (const char *sys, SSTREAM &ss) { bool section_done = false; int nb = getnb(); for (int i=0; isys == sys){ if (!section_done){ section_done = true; if (sys != NULL) ss.printf ("[%s]\n",sys); } if (o->key.is_empty()){ // Comments ss.printf ("%s\n",o->val.get()); }else{ ss.printf ("%s%s%s\n" ,o->key.get() ,internal->equal_sign ? "=" : " " ,o->val.get()); } } } } /* Update the configuration file Return -1 if any error. */ PUBLIC VIRTUAL int CONFDB::save(PRIVILEGE *priv) { int ret = -1; if (internal->fcfg != NULL){ FILE_CFG *fout = internal->fcfg->fopen (priv,"w"); if (fout != NULL){ SSTREAM_FILE_CFG ss(fout); savesys (NULL,ss); CONFDB_SUBSYSS *tbsys = &internal->tbsys; int nbsys = tbsys->getnb(); for (int i=0; igetitem(i)->sys.get(),ss); } ret = fclose (fout); if (ret == 0) resetmodified(); } } setcursys (K_BASE); return ret; } PUBLIC int CONFDB::save() { return save(NULL); } /* Build a key */ static const char *confdb_bkey( const char *prefix, const char *key, char buf[PATH_MAX]) { /* #Specification: linuxconf / /etc/conf.linuxconf / keys The keyword of /etc/conf.linuxconf use a special format convention. With a dot notation, the keyword is splitted in two part: The first identify the system and the second represent one parameter of this system. This strategy prevent clashes between two systems. */ unsigned lenkey = strlen(key); if (prefix != NULL){ unsigned lenprefix = strlen(prefix); assert (lenkey+lenprefix < PATH_MAX-2); strcpy (buf,prefix); strcat (buf,"."); strcat (buf,key); }else{ assert (lenkey < PATH_MAX); strcpy (buf,key); } return buf; } /* Find a record. using a single key Return NULL if not found. */ PUBLIC VIRTUAL const char *CONFDB::getvalk( const char *key, const char *defval) { int nb = getnb(); for (int i=0; isubsys_scope && o->sys != internal->cursys) continue; if (o->key.cmp(key)==0){ defval = o->val.get(); break; } } return defval; } /* Find a record. Return NULL if not found. */ PUBLIC VIRTUAL const char *CONFDB::getval( const char *prefix, const char *key, const char *defval) { char bkey[PATH_MAX]; confdb_bkey(prefix,key,bkey); return getvalk (bkey,defval); } /* Find a record. Return NULL if not found. */ PUBLIC const char *CONFDB::getval(const char *prefix, const char *key) { return getval (prefix,key,NULL); } /* Locate one numeric configuration parameter. Return defval if not found. */ PUBLIC int CONFDB::getvalnum ( const char *prefix, const char *key, int defval) { const char *val = getval (prefix,key); if (val != NULL) defval = atoi(val); return defval; } /* Locate one numeric configuration parameter. Return defval if not found. */ PUBLIC double CONFDB::getvalf ( const char *prefix, const char *key, double defval) { const char *val = getval (prefix,key); if (val != NULL) defval = atof(val); return defval; } /* Locate all configuration parameter with the same key. Return the number found. */ PUBLIC VIRTUAL int CONFDB::getall ( const char *prefix, const char *key, SSTRINGS &lst, bool copy) // Take a copy of the values { int ret = 0; if (!copy) lst.neverdelete(); int nb = getnb(); char bkey[PATH_MAX]; confdb_bkey(prefix,key,bkey); for (int i=0; ikey.cmp(bkey)==0){ SSTRING *val = &o->val; if (copy) val = new SSTRING (*val); lst.add (val); ret++; } } return ret; } /* Remove all entry with a given key. */ PRIVATE void CONFDB::removeallk (const char *key) { int nb = getnb(); bool modified = false; for (int i=0; isubsys_scope && o->sys != internal->cursys) continue; if (o->key.cmp(key)==0){ remove_del (o); i--; nb--; modified = true; } } if (modified){ internal->cursyso->modified = true; } } /* Remove all entry with a given key. */ PUBLIC VIRTUAL void CONFDB::removeall (const char *prefix, const char *key) { char bkey[PATH_MAX]; confdb_bkey(prefix,key,bkey); removeallk (bkey); } PRIVATE void CONFDB::addk ( const char *key, const char *val) { ARRAY::add (new CONFOBJ(internal->cursys,key,val)); internal->cursyso->modified = true; } /* Add one record to the configuration file */ PUBLIC VIRTUAL void CONFDB::add ( const char *prefix, const char *key, const char *val) { char bkey[PATH_MAX]; confdb_bkey(prefix,key,bkey); addk (bkey,val); } /* Add one record to the configuration file */ PUBLIC void CONFDB::add ( const char *prefix, const char *key, const SSTRING &val) { add (prefix,key,val.get()); } /* Add one record to the configuration file */ PUBLIC void CONFDB::add ( const char *prefix, const char *key, int val) { char buf[20]; sprintf (buf,"%d",val); add (prefix,key,buf); } /* Add one record to the configuration file */ PUBLIC void CONFDB::add ( const char *prefix, const char *key, bool val) { add (prefix,key,val ? 1 : 0); } /* Replace one record in the configuration file */ PUBLIC void CONFDB::replacek (const char *key, const char *val) { removeallk(key); if (val != NULL) addk (key,val); } /* Replace one record in the configuration file */ PUBLIC void CONFDB::replace (const char *prefix, const char *key, const char *val) { removeall(prefix,key); if (val != NULL) add (prefix,key,val); } /* Replace one record in the configuration file. If the value is empty or NULL, the record is simply removed. */ PUBLIC void CONFDB::replace_if (const char *prefix, const char *key, const char *val) { removeall(prefix,key); if (val != NULL && val[0] != '\0') add (prefix,key,val); } /* Replace one record in the configuration file */ PUBLIC void CONFDB::replace (const char *prefix, const char *key, char val) { char buf[20]; sprintf (buf,"%d",val); replace (prefix,key,buf); } /* Replace one record in the configuration file */ PUBLIC void CONFDB::replace (const char *prefix, const char *key, int val) { char buf[20]; sprintf (buf,"%d",val); replace (prefix,key,buf); } /* Replace one record in the configuration file */ PUBLIC void CONFDB::replace (const char *prefix, const char *key, bool val) { replace (prefix,key,val ? 1 : 0); } /* Replace one record in the configuration file */ PUBLIC void CONFDB::replace (const char *prefix, const char *key, long val) { char buf[20]; sprintf (buf,"%ld",val); replace (prefix,key,buf); } /* Replace one record in the configuration file */ PUBLIC void CONFDB::replace (const char *prefix, const char *key, double val) { char buf[30]; sprintf (buf,"%f",val); replace (prefix,key,buf); } /* Replace one record in the configuration file */ PUBLIC void CONFDB::replace ( const char *prefix, const char *key, const SSTRING &val) { replace (prefix,key,val.get()); } /* Replace one record in the configuration file. The record is removed if the value is empty. */ PUBLIC void CONFDB::replace_if ( const char *prefix, const char *key, const SSTRING &val) { replace_if (prefix,key,val.get()); } /* Replace one record in the configuration file */ PUBLIC void CONFDB::replace ( const char *prefix, const char *key, const SSTRINGS &vals) { removeall(prefix,key); int nb = vals.getnb(); for (int i=0; isys == sys){ remove_del (o); i--; nb--; } } } /* Extract one sub-system of a CONFDB file */ PUBLIC VIRTUAL int CONFDB::extract (SSTREAM &ss, const char *_sys) { delsys (_sys); setcursys (_sys); char line[1000]; while (ss.gets(line,sizeof(line)-1) != NULL){ strip_end (line); if (line[0] != '\0') addline (line); } return save(); } /* Initially, there were no concept of subsystem in CONFDB files. They were introduce to ease archiving and remote administration on a sub-system per sub-system basis. This function try to distribute the various information in conf.linuxconf for those who are using linuxconf prior to 1.9r26.18 So this function is called only from misc/linuxconf.c as an autorepair. Note that sub-system are not important for the proper working of linuxconf. Mostly the ordering of the lines in that file has never been important. The organisation in sub-system is just for archiving. */ PUBLIC void CONFDB::patchsys() { if (internal->tbsys.getnb() == 1){ FILE *fin = fopen ("/usr/lib/linuxconf/lib/conf.linuxconf-patch","r"); if (fin != NULL){ char buf[200]; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ strip_end (buf); if (buf[0] != '\0' && buf[0] != '#'){ char key[100],sys[100]; if (sscanf(buf,"%s %s",key,sys)==2){ int keylen = strlen(key); int n = getnb(); for (int i=0; ikey.ncmp(key,keylen)==0){ setcursys (sys); o->sys = internal->cursys; } } } } } fclose (fin); } } setcursys (K_BASE); } /* Get the list of modified subsystems */ PUBLIC VIRTUAL int CONFDB::getmodsys (SSTRINGS &tb) { int ret = 0; for (int i=0; itbsys.getnb(); i++){ CONFDB_SUBSYS *sys = internal->tbsys.getitem(i); if (sys->modified){ tb.add (new SSTRING(sys->sys.get())); ret++; } } return ret; }