May be I am missing something, but I cannot find setenv
partner to the std.os.getenv
function. How can I set or change value of an env variable?
I can’t find it either.
But if you need to set/override the value of some environment variable that you’re passing to a subprocess you’re executing, you can probably achieve that via std.process.getEnvMap()
+ modify the map + std.os.execvpeZ()
.
I need to modify environment of the currently running process. This is what setenv
and putenv
syscalls do.
Just a note: they are not syscalls. Both these are memory manipulations within process address space, so OS is not needed here. Try ls -lR /usr/share/man/man3 | grep getenv
and ls -lR /usr/share/man/man2 | grep getenv
.
you are right. getenv/setenv
are part of libc
.
I do not know what exactly setenv
is doing, but here is an example (in C), which sets an envvar without using setenv
:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[], char *envp[])
{
char *new_env_var = calloc(1, 128);
sprintf(new_env_var, "OPTION=10");
envp[0] = new_env_var;
char *option = getenv("OPTION");
if (option) {
printf("OPTION = %s\n", option);
}
}
$ ./a.out
OPTION = 10
Maybe, something similar can be done in Zig, not sure about it.
hmm… it is interesting
“Now applications don’t have to use that API. They can use their own way to manage the list of environment variables. Shells for instance, map environment variables to shell variables and are likely not to use putenv/setenv libc functions”
This is a bit strange actually. As @jmc noted, usually we pass environment to a process we are going to exec
. And for this purpose we can store our strings wherever as we want and then pass them to execvpe
. The only thing I do not understand here is where do we get current environment from? From /proc/<PID>/environ
?
setenv()
typically does nothing more than to find the corresponding entry in the environ
array and replace it with a new value. Then, your following getenv()
calls will see the new value there. See: Environment Variables
The question I have is: if you’re not modifying the environment for the purpose of spawning subprocesses who will see it modified, then what good is it to modify it for the currently running process?
Oops, I’ve completely forgotten that
envp
is NULL-terminated, so we can collect all of them using for (ev = envp ; *ev; ev++)
, store them wherever we want (say, in a hash map), do everything we want with them and finally use them when launching a process.
That’s exactly what std.process.getEnvMap
is for. From an earlier reply:
And for cross-platform code, a std.process.EnvMap
is what is expected in std.ChildProcess.run
:
Yes, I’ve already got it.
The question is why @slonik-az wants to modify environment and then use modifications within current process (if I got the intention right). To me it does not make much sense.
One possible use would be to influence a generic function which is already written to look at the environment – you set the new / modified value, and call your function “as usual”.
One more little detail, sorry :-). According to the output of this tiny program
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[], char *envp[])
{
char **ev = envp;
int k = 0;
for (; *ev; ev++, k++) {
printf("envp[%d] -> %p -> %p:\n'%s'\n", k, *ev, ev, *ev);
}
printf("%p\n", &ev);
printf("%p\n", malloc(100));
}
i.e.
...
envp[48] -> 0x7ffebd217f96 -> 0x7ffebd215878:
'DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus'
envp[49] -> 0x7ffebd217fcc -> 0x7ffebd215880:
'OLDPWD=/home/user/'
envp[50] -> 0x7ffebd217fe6 -> 0x7ffebd215888:
'_=./a.out'
0x7ffebd2155e0
0x562433ff66b0
all this stuff (both envp[k]
pointers as well as strings themselves) are just sitting on the stack.
Right, environment-dependent functions are the best I suppose there is some use there for
LC_ALL
and similar variables, though it still saddens me.
I personally do not think that wigwagging ENVVARS within a process is good idea in general.
I am linking to a library that uses environment variables to set formatting parameters for printing in the current process. I do not like this design choice but there is nothing I can do.
BTW, it is strange to provide getenv
but no setenv
. Is it just an oversight or intentinal?
My guess is that it’s an oversight. It’s not fully clear what an implementation would look like, though, since I believe it would need to involve heap allocation that lives to the end of the program.
See musl’s setenv
/putenv
implementations for example.
Note that you can always link libc and call setenv
if you want a quick solution.
This is exactly what setenv
is doing when invoked for first time (if I got it right) -
it duplicates the entire environment on the heap. Initially environment lives below the stack bottom. And the problem is not in life-times. That initial stack frame, containing the environment (along with other stuff like argc
and argv
) also has global life time.
The problem is adding something to that region of memory. Well, we can override pointers (as I did in my examle) or even strings, but we can not add some new variable.
Aha, this is exactly the use case @gonzo wrote about!
And, yes, something definitely wrong with such an approach.