Program Configuration Files in Linux

By: Kerry D. Mathews II July 20th 1997

Have you ever looked at the initialization files from other programs 
and said: "I wish my Linux app could read init files like that?"
Well, hold your breath no longer. 

In this short article, I'll go over some code that will make it possible. 
I've made a single function that will perform reading the configuration file,
but not write. The reason is i felt it was unneccesary overhead to launch
a program just to modify its configuration file. In my approach, I can use
any ordinary text editor (e.g. vim), after all why re-invent the wheel.  

The code, for this function, will essentially search a file for a KEYWORD 
and then grab corresponding VALUE.
Requirements: A function that takes two parameters, a filename, keyword, 
and retrieves a corresponding value. Returning an appropriate execution
status (error value) is also needed.
First ponder the reasons for having to read values from a file. (hmm..) 

Well, it:
  1. Avoids passing parameters on the command line.
  2. It also avoids having to hardcode values in the executable.
  3. And thirdly, it is very ergonomic to the administrator of that program.
For the rest of this article, I'll refer to the parameter file as: 
the Config File. But please remember it fills the role of an init file 
and a runtime parameter file.
Minor requirements are:
Enough requirements.
We are now ready to examine the Config file.
Again, I do things as simple as possible. I only expect to get a string 
value from the Config file. You can take the code and modify it to your 
requirements. A single entry in the Config file cannot span more (or less)
than 1 line. Entries into the Config file should look like 
"{keyword}={value}\n" ... in psuedo code of course.

Valid entries in a Config File should look like:
That looks good. Easy enough for my seven year old daughter to maintain.  :)
The returned value is a NULL terminated string containing all the characters 
after the first "=" and up to, but not including, the '\n'.
From the examples above we can say: 
For keyword: [WATERHOSE] the value is: [199.200.11.31] 
For keyword: [dinner_time] the value is: [11pm GMT]
We can conclude that spaces, tabs, and other characters will be included 
in the value variable.
I am satisfied with the Config File and the keyword/value synergy. 
Let's look at the code next.

The name and prototype of our new found functions is:



int read_config_var( char *values_file, char *keyword , char value[] );
The char *values_file is the full path name of the Config File.  The char *keyword is the keyword.  The char value[] is an array that is filled by read_config_var(). The return values are:
Value could be a char *, but i decided against that. I used an array,
because if its fixed length nature.
The meat of our function is below. You can see that it uses fgets() to fill
the variable str. (oh.. yuck, the length is hardcoded) You can make your own
judgements on that call. Then a string comparison between the variables 
'str' and 'keyword'. If the keyword is found in str, then sprintf() is used
to firmly place that value in the variable 'value'.

        for(;;)         {                 fgets(str, 80, _file);                 if( ferror(_file) || feof(_file) ) return(UGLY);                 len = strlen(str);                 if( strncmp(keyword, str, strlen(keyword)) == 0 )                 {                      if (str[len - 1] == '\n') str[--len] = 0;                     sprintf(value, "%s", &str[strlen(keyword)+1] ); break;                 }         } 
One glaring weak point is that hardcoded fgets parameter. If the value of 
that exceeds that of the dimension of value[], bad things will happen.
If you don't need anything over 80 characters, then your safe. But, if you
do ensure that value[] and str[] and the fgets(%,max_length,%) are all the
same length. A worldly global can be useful in its place, 
like FILENAME_MAX or UCHAR_MAX.

OK. We are nearly done. The last topic is implementation.

Below is an example of how our function can be used.



int main( int argc, char *argv[] )  {  char * v_file = "./Secret_Addresses";  char * keyword = "Kurt Cobain";  char value[80] ;
    switch( read_config_var(v_file, keyword, value) ) 
    {
        case 0: 
            printf("\nValue for %s in %s is %s\n", keyword, v_file, value); 
            break;
        case -1: 
            printf("\nFile Error for [%s] \n", v_file); break;
        case -2: 
            printf("\nBad User Parm for [%s] \n", keyword); break;
        default: 
            printf("\nUnknown Error Occurred \n"); break;
    }
return 0; 
}

A couple of quick notes on the Config File:
  1. There is no restriction on the name or location of the Config File.
  2. The attributes of the Config File, I suggest, should be world readable

  3. and only writable by the program administrator.
  4. For the location of the Config File, I suggest using paths like:

  5. /usr/local/bin/program_name/program_name.cfg
    or
    /etc/conf.program_name

Bonus Notes
In earlier articles, the topic of daemons came up. Well written daemons will
have startup and shutdown functions. The startup functions, should, read
from configuration files and implement those values. A smart approach
to a well written daemon, is to re-read the configuration file when a
certain signal is raised (i.e. SIGUSR1). That way, you proggie need not be
stopped, just because you changed parameters. (nifty?) 

Below is a sample snippet:

struct sigaction SignalAct; 

SignalAct.sa_handler = SigCatch;
sigemptyset( &SignalAct.sa_mask );
SignalAct.sa_flags = 0;
void SigCatch(int sig)
{
char * funct_name = "SigCatch";

   if (sig == SIGUSR1) /* re - initialize daemon */
   {
        shutdown_gracefully();
        sleep(3);
        init_system();
    }

} /* end SigCatch */





There you have a very handy function for program configurability. 
It is so conveinent you may want to include it in your utility library. 
Another thought, you may want to develop a corresponding write function.
Additionally; I claim no ownership, rights, or responsibilities for 
this code.
Final Notes: The source code for this function and examples is in 
values.zip 

Compile by using: cc -o main main.c values.c 
This was written for the 2.7.2.1 gcc compiler on the 2.0.27 Linux OS.