Skip to content
tat edited this page Sep 13, 2012 · 2 revisions

u_env

The basic idea behind the u_env module is to load configuration values (i.e. values that a program needs at run-time) from a shell script which exports some variables in a given namespace. We let the shell act in the background - using just some env(1) and pipe(2) tricks to redirect and capture the variables - which in turn gives us a great deal of power and flexibility in just a couple of C lines.

A Simple Configuration File

Let’s start with a very simple configuration file in which three variables are set and exported to the environment.

export PFX_VAR1="tip"
export PFX_VAR2="tap"
export PFX_VAR3=$((10 * 100 * 1000))

A (Slightly) More Complex Configuration File

Here we have two alternate configurations (i.e. two different set of values for the same three variables in the PFX_ namespace) which can be selected by just setting the top level USE_DEFAULT_CONF variable value.

USE_DEFAULT_CONF="true"

default_conf ()
{
    export PFX_VAR1="tip"
    export PFX_VAR2="tap"
    export PFX_VAR3=$((10 * 100 * 1000))

alternate_conf ()
{
    local X=`jot -r -c 1 a z`
    export PFX_VAR1=$X"ic"
    export PFX_VAR2=$X"ac"
    export PFX_VAR3=$X"oe"
}

if [ $USE_DEFAULT_CONF = "true" ]
then
    default_conf
else
    alternate_conf
fi

Things worth noting are:

  • the conditional behaviour;
  • bash does the math (!) and spawns external utilities (e.g. jot(1));
  • it is possible to do complex substitutions;
  • every information one can get through the shell is available (e.g. cat /proc/something);
  • aggregation of other configuration modules is trivial via the source shell built-in.

Source File

Now let’s take a look at the C code that uses the u_env API. Basically we have two function calls: the first one u_env_init() asks the shell to parse and evaluate the supplied configuration/script file. This function shall be called just once, i.e. when we need to load all the variables in the supplied namespace.

#include <stdlib.h>
#include <u/libu.h>
 
int main (int ac, char *av[])
{
    int i;
    const char *v, *vp[] = { "PFX_VAR1", "PFX_VAR2", "PFX_VAR3" };
 
    con_err_if (ac != 2);
==> con_err_if (u_env_init("PFX_", av[1])); <==

Then for each needed variable we load its value using the u_env_var() function:

    for (i = 0; i < 3; i++)
    {
    ==> if ((v = u_env_var(vp[i])) == NULL) <==
            con("%s not set", vp[i]);
        else
            con("%s set to %s", vp[i], v);
    }
 
    return EXIT_SUCCESS;
err:
    return EXIT_FAILURE;
}

Note that when the USE_DEFAULT_CONF variable is set to true, running the test program on the two different configuration files produces the same output:

PFX_VAR1 set to tip
PFX_VAR2 set to tap
PFX_VAR3 set to 1000000

That's all.

Clone this wiki locally