Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why was getenv standardised but not setenv?

From answers and comments on this question, I understand that getenv is defined by the C++ standard, but setenv is not. And indeed, the following program

#include <cstdlib>
#include <iostream>

int main ( int argc, char **argv )
{
    std::cout << std::getenv("PATH") << std::endl;  // no errors

    std::setenv("PATH", "/home/phydeaux/.local/bin:...", true);  // error
}

does not compile for me (clang 3.9).

Why was one of these seemingly complementary functions standardised but not the other?

like image 227
Phydeaux Avatar asked Aug 29 '17 11:08

Phydeaux


2 Answers

The C90 standard includes getenv(); therefore, the C++98 standard did too.

When the C standard was originally created, the precedent for environment setting was putenv(); the setenv() function was not devised until later. The standard committee avoided creating new functions when it could, but also avoided standardizing problematic functions when possible (yes, localeconv() and gets() are counter-examples). The behaviour of putenv() is problematic. You have to pass it memory which is not of automatic duration, but you can't know whether you can ever use it again. It's like a forced memory leak. It was A Good Thing™ that putenv() was not standardized.

The rationale for the C standard explicitly says (§7.20.4.5, p163):

A corresponding putenv function was omitted from the Standard, since its utility outside a multi-process environment is questionable, and since its definition is properly the domain of an operating system standard.

Platform-specific APIs step in and provide the missing functionality in a way suitable to them.


The first editions of the POSIX standard (1988 trial use; 1990) did not include setenv() or putenv(). The X/Open Portability Guide (XPG) Issue 1 did include putenv() based on its appearance in the SVID (System V Interface Definition) — which did not include setenv(). The XPG Issue 6 added setenv() and unsetenv() (see the history sections for the functions at the URLs linked to). Curiously, on a Mac running macOS Sierra 10.12.6, man 3 setenv has a history section that identifies:

The functions setenv() and unsetenv() appeared in Version 7 AT&T UNIX. The putenv() function appeared in 4.3BSD-Reno.

This is unexpected and probably erroneous since the UNIX Programmer's Manual Vol 1 (1979) does not include any of putenv(), setenv() or unsetenv(). The putenv() function was added to the AT&T variants of Unix at some stage in the 80s; it was in the SVID and documented by the time SVR4 was released in 1990 and may have been part of System III. I think they almost have the platforms reversed. 4.3BSD-Reno was released in June 1990, after both the first C and POSIX standards were released.

There was some discussion in comments with Random832 , now removed, mentioning TUHS – The Unix Heritage Society as a source of information about ancient versions of Unix. The chain included my observation: If nothing else, this discussion emphasizes why the standards committees did well to steer clear of 'setting the environment'! It appears that putenv() was not in 7th Edition UNIX, contrary to my memory. I'm fairly sure it was available in a system I used from 1983, which was a lot of 7th Edition with some material from System III, some from PWB. It is a part of SVR4 (I've a manual for that), and was defined in some version of the SVID (probably before SVR4).

The C rationale also mentions concerns about gets() but included it despite those concerns; it was (very sensibly) removed from C11, of course (but POSIX still refers to C99, not C11).

like image 198
Jonathan Leffler Avatar answered Oct 19 '22 20:10

Jonathan Leffler


setenv is not possible in some of the original environments C was defined for.

getenv allows you to see your environment. creating a new process with exec[lv][p][e] allows you to create a child with an inherited or new environment.

However, setenv, would modify the state of the calling process, which wasn't always possible.

I guess it is because it increases the writable interface for the caller, and was not needed originally, and is a security risk these days.

like image 4
mksteve Avatar answered Oct 19 '22 20:10

mksteve