Re: Is set-environment-variable needed after all? Lassi Kortela 09 Dec 2019 11:00 UTC
> I didn't even know about putenv, not at all a System V fan, but it > appears that's why its in POSIX, see this comment for POSIX setenv: > https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html > >> There was considerable debate as to whether the System V putenv() >> function or the BSD setenv() function should be required as a >> mandatory function. The setenv() function was chosen because it >> permitted the implementation of the unsetenv() function to delete >> environmental variables, without specifying an additional >> interface. The putenv() function is available as part of the XSI >> option. > > It looks very sketchy, see this from POSIX, and there's lots more: > https://pubs.opengroup.org/onlinepubs/9699919799/functions/putenv.html > >> The putenv() function need not be thread-safe. > > It doesn't even have EINVAL defined as a possible error in case you > don't pass it a string including '=' (it takes "name=value"), only > ENOMEM if you run out of memory. Thanks for digging up this information. That sure is a pessimal API! > I just checked glibc, and while the comments express concern about > running out of memory---it was started in 1987, and it's conceivable > someone might want to use it in larger memory embedded environments, > it has a feature to reuse existing strings---it too never frees, and > reallocs as needed. See e.g. this from an unofficial repo: > https://github.com/bminor/glibc/blob/master/stdlib/setenv.c > > Today this memory leaking probably isn't very important, outside of > embedded systems. Agreed. A lot of the complications probably come from the fact that memory was tight in the 1980s, so they couldn't just strdup() all strings at the API boundary. Here's musl libc: <https://github.com/bradfa/musl/tree/master/src/env>. unsetenv() actually calls free() on some strings. >> Not sure how much the memory leak matters. If an implementation wanted >> to go to a ton of effort, it could keep a list of pointers to "putenv() >> strings managed by Scheme" and the delete-environment-variable! could >> check whether it's one of those. > > That's assuming nothing called in the meanwhile made a copy of the > pointer to the putenv'ed string; again from the POSIX entry on putenv: > >> Although the space used by string is no longer used once a new >> string which defines name is passed to putenv(), if any thread in >> the application has used getenv() to retrieve a pointer to this >> variable, it should not be freed by calling free(). If the changed >> environment variable is one known by the system (such as the locale >> environment variables) the application should never free the buffer >> used by earlier calls to putenv() for the same variable. You're right. > glibc locks the array; per this, which setenv refers to, it's required: > https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html > >> Conforming multi-threaded applications shall not use the environ >> variable to access or modify any environment variable while any >> other thread is concurrently modifying any environment variable. A >> call to any function dependent on any environment variable shall be >> considered a use of the environ variable to access that environment >> variable. OK, so it's mandatory. > All that said, except for special cases like development and debugging, > I've never had a need to set environment variables outside of process > edge conditions, which the process SRFI will take care of. If no one > can come up with a library where you really need to set or reset an > environment variable, as John implicitly notes good time libraries > don't depend on TZ, not wrapping both setenv and unsetenv as John > suggested sounds best. We could also write a separate SRFI as a follow-up to SFI 98 with just set-environment-variable! and delete-environment-variable! :) As was done with the timespec SRFI.