putenv() and setenv()

How do you modify the environment in UNIX?

The Specifications

There are two functions available to set environment variables: putenv() and setenv(), plus a third function unsetenv() to remove values from the environment.

int putenv(char *string);

putenv takes a string of the form NAME=VALUE. This is reasonably convenient as it gets for adding a fixed value to the environment but less so if either of the name or value aren't fixed. Also it does not copy the string, which has the following important implications:

int setenv(const char *envname,
           const char *envval,
           int overwrite);

setenv takes the name and value separately and allows you to back out of the variable is already set. This is adequately convenient for most possible applications. It creates a new copy, so:

The usage and rationale section states that (1) setenv is preferred and (2) putenv is the only way to avoid leaks. These two statements seem inconsistent, but there you go.

int unsetenv(const char *name);

unsetenv is straightforward enough: it just removes a name from the environment.

Although presumably an implementation is free to magically know that it must free any copies that were made with setenv but not strings passed via putenv there is no guarantee given that it does so, so it must be assumed that memory is leaked by this function. For most applications this isn't a big problem: they modify the environment rarely or never, or do so just before calling execve, making leaks short-lived.

Threads

None of these functions are required to be thread-safe (though some implementations do support such a guarantee). So be extremely cautious using them from multithreaded programs or from libraries. Using them from signal handlers is probably right out.

Implementation Variations

Linux

Some ancient versions of Linux putenv will copy the string. Also some ancient versions declare the argument as const char *; code written assuming this may fail to compile on more recent versions or different platforms.

FreeBSD and Mac OS X

putenv is documented as being equivalent to the obvious setenv call; i.e. it copies the string, in violation of the specification.

Solaris

Solaris has putenv but lacks setenv and unsetenv before 5.10 (AFAICT).

HPUX

(Some versions of?) HPUX putenv declare the argument as const char *; code written assuming this may fail to compile on more recent versions or different platforms.

(Some versions of?) HPUX do not have setenv or unsetenv.

(Please feel free to contribute to this section.)

Conclusions

putenv is very widely available, but it might or might not copy its argument, risking memory leaks.

You can't assume that setenv or unsetenv are universally available, though the situation does appear to be improved on modern platforms and setenv at least should not be hard to implement in terms of putenv.

If you do use setenv or unsetenv then memory leaks are very likely.

There is no completely portable way to remove something from the environment (the getenv() rationale says conforming applications may not modify the environment array directly, though I've not yet found this stated in a normative section.)

Avoid any kind of concurrency beteween these functions.

RJK | Contents