This is the mail archive of the newlib@sourceware.org mailing list for the newlib project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: How to use a static environment


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.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]