How to use a static environment

Giuseppe Modugno giuseppe.modugno.loqed@gmail.com
Fri Apr 6 07:25:00 GMT 2018


Il 05/04/2018 18:34, Bob Dunlop ha scritto:
> On Thu, Apr 05 at 05:46, Giuseppe Modugno wrote:
>> Il 05/04/2018 17:32, Bob Dunlop ha scritto:
>>> Hi,
>>>
>>> I don't know the newlib specifics but have played with this in other
>>> library environments before.
>>>
>>> The answer to your first question "Should unsetenv() free?" the
>>> answer is No because unsetenv() doesn't know where the original string
>>> came from.  You could be freeing something that was never malloc'd.
>> setenv() calls malloc to copy and save the new variable/value pair in a
>> single string. So I was thinking the behaviour of unsetenv() should be
>> symmetrical, i.e. unsetenv() should have free. Otherwise:
>>
>>   ? setenv("TZ", "whatever", 1);
>>   ? unsetenv("TZ");
>>
>> and you will have a memory leak... right?
> Yes you have a memory leak.  It's even documented in the Glibc putenv man
> page, you either accept a memory leak or violate SUSv2.
 From the online glibc reference manual, I read:

> The difference to the setenv function is that the *exact string given 
> as the parameter string is put into the environment*. If the user 
> should change the string after the putenv call this will reflect 
> automatically in the environment. This also requires that string not 
> be an automatic variable whose scope is left before the variable is 
> removed from the environment. The same applies of course to 
> dynamically allocated variables which are freed later.
I don't think putenv() implementation of newlib behaves in the same way. 
putenv() calls setenv(), so they are identical.
IMHO it is better the behaviour of glibc putenv() (I don't know if it is 
a standard behaviour), because it would avoid dynamic allocation (that 
must be avoided in some circumstances). Moreover, in my case I could 
safely change the string "TZ=<value>" at a later time, without calling 
again putenv() (and this is explicitly specified in the glibc manual).

> Also as I said before you don't have a clue where the original environment
> value came from.  In my early Unix days exec() set it up on the top of stack
> like it was auto variables in the function that called main().  Think I saw
> a system once that passed it as a special data segment/page.  I've certainly
> seen embedded systems where the initial values were in ROM and the pointer
> array built on the fly, pointing at the ROM until changes were made.
In glibc, if you call only putenv() and unsetenv(), you have full 
control of environment variables without memory leaks:

   char *tz_env = malloc(100);
   strpcy(tz_env, "TZ=<timezone1>");
   putenv(tz_env);
   tzset();
   ...
   strcpy(tz_env, "TZ=<timezone2>2);
   /* No need to call putenv() again, because environment points exactly 
to tz_env */
   tzset();
   ...
   unsetenv("TZ");
   free(tz_env);

In this case you don't have any memory leak.
Even in glibc, you will have a memory leak if you call unsetenv() after 
sentev().

> Problem is to find the lowest common denominator of the different libraries
> and platforms.  How about:
>
>    static char tzbuf[] = "TZ=Value_long_enough_for_any_we_want_to_set";
>    char *tzbuf_value;
>
>    /* Need to clear any previous string as we don't know where it's been.
>     * This is probably a one off memory leak.
>     */
>    unsetenv("TZ");
>
>    /* Set our buffer although library may take a malloc'd copy */
>    setenv( tzbuf );
>    /* Find the value buffer location (either our original or the copy) */
>    tzbuf_value = getenv("TZ");
>
>    strcpy( tzbuf_value, "UTC0" );
>    etc...
It is tricky, but it's a possible solution. Anyway IMHO it's better to 
implement putenv() to use the string passed as argument without 
duplicating it.

Regading putenv(), I read the following code:

int
_DEFUN (_putenv_r, (reent_ptr, str),
     struct _reent *reent_ptr _AND
     char   *str)
{
   register char *p, *equal;
   int rval;

   p = _strdup_r (reent_ptr, str);

   if (!p)
     return 1;

   if (!(equal = strchr (p, '=')))
     {
       (void) _free_r (reent_ptr, p);
       return 1;
     }

   *equal = '\0';
   rval = _setenv_r (reent_ptr, p, equal + 1, 1);
   (void) _free_r (reent_ptr, p);

   return rval;
}

I don't explain why the check for the presence of equal sign is after 
calling strdup (so we need to call free). IMHO it would be better to 
make the check immediately.

> Should work unless you have a system that stores the environment as a single
> buffer "key1\0value1\0key2\0value2\0\0" and shuffles the buffer up and down
> as you expand/contract the individual values.  A 1980's embedded system if
> I recall correctly.



More information about the Newlib mailing list