121

On Unix, is there any way that one process can change another's environment variables (assuming they're all being run by the same user)? A general solution would be best, but if not, what about the specific case where one is a child of the other?

Edit: How about via gdb?

codeforester
  • 39,467
  • 16
  • 112
  • 140
raldi
  • 21,344
  • 33
  • 76
  • 86
  • This strikes me as more than ugly. What is the actual problem you want to solve? – Jens Jun 02 '12 at 08:30
  • 1
    Example: I'd like to define an environment variable so that every new app - launched by the UI - would get it. I don't know of any method except defining the variables in one of the startup scripts and RE-LOGIN. I would however would like to not re-login, but just define the variables in the current session so that new apps would get it - without logging out of the UI. – AlikElzin-kilaka Jul 22 '12 at 15:18

12 Answers12

159

Via gdb:

(gdb) attach process_id

(gdb) call putenv ("env_var_name=env_var_value")

(gdb) detach

This is quite a nasty hack and should only be done in the context of a debugging scenario, of course.

An̲̳̳drew
  • 13,375
  • 13
  • 47
  • 46
  • 10
    So this seems to imply that you can indeed change the environment of a process if you attach to the process as GDB does, and then detach. It seems it would be possible to write a program that does only this. – grieve Nov 05 '08 at 20:43
  • 3
    "It seems it would be possible to write a program that does only this" Indeed.. it is. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Feb 23 '10 at 23:22
  • 2
    It even works on Windows using cygwin, for processes that are not compiled using cygwin! – Juan Carlos Muñoz Oct 05 '12 at 18:39
  • 13
    Note that this only works if the process has not permanently cached the value after a prior getenv. – An̲̳̳drew Jun 24 '13 at 15:12
  • Amazingly, this actually works. I was able to add an environment variable to a running X/MATE session using this technique on mate-session and mate-panel. Newly launched terminals inherited the variable just like they normally would. – Animism Feb 09 '14 at 05:37
  • 1
    `ptrace: Operation not permitted` – gerrit Mar 22 '14 at 15:30
  • Btw. [this shellscript allows](https://gist.github.com/coderofsalvation/e1376e4d2b29607431df) `sudo export_pid FOO=bar 11234` using the gdb technique. Ps. gdb requires you to run as root. – coderofsalvation Jun 24 '15 at 10:53
  • I applied this answer to change the timezone in a running instance of IRSSI, by calling putenv("TZ=/path/to/timezone"). – Patrick Conheady Sep 23 '16 at 05:25
  • I get this error: `No symbol "setenv" in current context.` – vaibhavatul47 Dec 14 '17 at 05:34
  • 1
    Update: https://www.sourceware.org/gdb/onlinedocs/gdb.html#Environment this helped. – vaibhavatul47 Dec 14 '17 at 06:53
  • 5
    On some systems gdb may give the following error: `'putenv' has unknown return type; cast the call to its declared return type`; in those cases you should change `putenv` call to this: `call (int) putenv ("env_var_name=env_var_value")` – Emir Uner Feb 20 '19 at 09:33
  • 1
    What if you grow the space in the process allocated to env vars? Does this overwrite something else? That is, if VAR=valuex initially, and you use gdbm to setenv/putenv VAR=valuexy, are you overflowing a buffer? – dstromberg Apr 23 '19 at 04:23
  • @dstromberg I'm sure that all putenv implementations allocate fresh memory. That should not be a concern. – An̲̳̳drew May 19 '22 at 14:40
24

You probably can do it technically (see other answers), but it might not help you.

Most programs will expect that env vars cannot be changed from the outside after startup, hence most will probably just read the vars they are interested in at startup and initialize based on that. So changing them afterwards will not make a difference, since the program will never re-read them.

If you posted this as a concrete problem, you should probably take a different approach. If it was just out of curiosity: Nice question :-).

sleske
  • 81,358
  • 34
  • 189
  • 227
  • 1
    The most common use case where it would be useful is to make child processes inherit the new environment variables, for example, in desktop environment where you want new terminals to use the new variables. – Hjulle Dec 10 '18 at 11:40
15

Substantially, no. If you had sufficient privileges (root, or thereabouts) and poked around /dev/kmem (kernel memory), and you made changes to the process's environment, and if the process actually re-referenced the environment variable afterwards (that is, the process had not already taken a copy of the env var and was not using just that copy), then maybe, if you were lucky and clever, and the wind was blowing in the right direction, and the phase of the moon was correct, perhaps, you might achieve something.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • @kilaka: The key word is the second one — **No**. The rest of the answer is saying that if you have root privileges or are running a debugger, then maybe you can do it, but for all practical purposes, the answer is **No**. – Jonathan Leffler Jul 22 '12 at 15:31
  • 1
    You've got a shell script running; you want to change the environment in your shell script's parent process...so the shell script launches `gdb` on the parent process and is scripted into do making the change, and it works without crashing the parent process. OK — you probably can do it, but it isn't something you're going to do on a routine basis. For practical purposes, therefore, the answer remains **No**. The rest of the answer covers the off-the-wall theoretically possible, somewhat impractically doable alternatives. – Jonathan Leffler Jul 25 '12 at 17:21
8

I could think of the rather contrived way to do that, and it will not work for arbitrary processes.

Suppose that you write your own shared library which implements 'char *getenv'. Then, you set up 'LD_PRELOAD' or 'LD_LIBRARY_PATH' env. vars so that both your processes are run with your shared library preloaded.

This way, you will essentially have a control over the code of the 'getenv' function. Then, you could do all sorts of nasty tricks. Your 'getenv' could consult external config file or SHM segment for alternate values of env vars. Or you could do regexp search/replace on the requested values. Or ...

I can't think of an easy way to do that for arbitrary running processes (even if you are root), short of rewriting dynamic linker (ld-linux.so).

ADEpt
  • 5,504
  • 1
  • 25
  • 32
  • This should be doable. You could have a little gdbm database for the var=value pairs. I have something similar for malloc at http://stromberg.dnsalias.org/~strombrg/malloc-wrapper/ – dstromberg Apr 23 '19 at 04:18
  • I think this method requires forethought though. You'd also have to be careful not to accidentally apply it to too many processes. – dstromberg Apr 23 '19 at 04:25
7

Quoting Jerry Peek:

You can't teach an old dog new tricks.

The only thing you can do is to change the environment variable of the child process before starting it: it gets the copy of the parent environment, sorry.

See http://www.unix.com.ua/orelly/unix/upt/ch06_02.htm for details.

Just a comment on the answer about using /proc. Under linux /proc is supported but, it does not work, you cannot change the /proc/${pid}/environ file, even if you are root: it is absolutely read-only.

Davide
  • 17,098
  • 11
  • 52
  • 68
  • Which still leaves the question: where are env var values actually stored? Is that done by the kernel? Or does the shell store the values, and /proc//environ gets them from there? – oliver Oct 16 '08 at 16:46
  • This is an implementation detail, and it might be a (separate) good question. I think every UNIX uses its own way for the storage, but all of them share the behavior described above, which is part of the specifications. – Davide Oct 16 '08 at 20:09
4

It seems that putenv doesn't work now, but setenv does. I was testing the accepted answer while trying to set the variable in the current shell with no success

$] sudo gdb -p $$
(gdb) call putenv("TEST=1234")
$1 = 0
(gdb) call (char*) getenv("TEST")
$2 = 0x0
(gdb) detach
(gdb) quit
$] echo "TEST=$TEST"
TEST=

and the variant how it works:

$] sudo gdb -p $$
(gdb) call (int) setenv("TEST", "1234", 1)
$1 = 0
(gdb) call (char*) getenv("TEST")
$2 = 0x55f19ff5edc0 "1234"
(gdb) detach
(gdb) quit
$] echo "TEST=$TEST"
TEST=1234
Kakash1hatake
  • 128
  • 12
3

Or get your process to update a config file for the new process and then either:

  • perform a kill -HUP on the new process to reread the updated config file, or
  • have the process check the config file for updates every now and then. If changes are found, then reread the config file.
phil pirozhkov
  • 4,740
  • 2
  • 33
  • 40
Rob Wells
  • 36,220
  • 13
  • 81
  • 146
2

Not as far as I know. Really you're trying to communicate from one process to another which calls for one of the IPC methods (shared memory, semaphores, sockets, etc.). Having received data by one of these methods you could then set environment variables or perform other actions more directly.

Stephen Darlington
  • 51,577
  • 12
  • 107
  • 152
1

If your unix supports the /proc filesystem, then it's trivial to READ the env - you can read the environment, commandline, and many other attributes of any process you own that way. Changing it... Well, I can think of a way, but it's a BAD idea.

The more general case... I don't know, but I doubt there's a portable answer.

(Edited: my original answer assumed the OP wanted to READ the env, not change it)

Mike G.
  • 1,670
  • 11
  • 17
  • Ooops, edited my answer - I was assuming he wanted to read the env, not change it. – Mike G. Oct 15 '08 at 15:12
  • 1
    Don't leave me hanging. What's your bad idea? – raldi Oct 15 '08 at 15:23
  • On Linux, I believe you MIGHT be able to open /proc//mem read-write for other process you own... I'm not sure, though. Trying, and actually messing with the environment, would DEFINITELY be a bad idea. So I'm not suggesting you try it... – Mike G. Oct 16 '08 at 00:22
1

Not a direct answer but... Raymond Chen had a [Windows-based] rationale around this only the other day :-

... Although there are certainly unsupported ways of doing it or ways that work with the assistance of a debugger, there’s nothing that is supported for programmatic access to another process’s command line, at least nothing provided by the kernel. ...

That there isn’t is a consequence of the principle of not keeping track of information which you don’t need. The kernel has no need to obtain the command line of another process. It takes the command line passed to the CreateProcess function and copies it into the address space of the process being launched, in a location where the GetCommandLine function can retrieve it. Once the process can access its own command line, the kernel’s responsibilities are done.

Since the command line is copied into the process’s address space, the process might even write to the memory that holds the command line and modify it. If that happens, then the original command line is lost forever; the only known copy got overwritten.

In other words, any such kernel facilities would be

  • difficult to implement
  • potentially a security concern

However the most likely reason is simply that there's limited use cases for such a facility.

Community
  • 1
  • 1
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
1

UNIX is full of Inter-process communication. Check if your target instance has some. Dbus is becoming a standard in "desktop" IPC.

I change environment variables inside of Awesome window manager using awesome-client with is a Dbus "sender" of lua code.

dvd
  • 1,750
  • 2
  • 10
  • 9
0

Yes, you can, but it doesn't make sense. To do that you should invade into a running process and change its memory.

Environment variables are the part of the running process, that also means env vars are copied and stored to the memory of a process when it started, and only the process itself owns it.

If you change evn vars when the process is trying to read them it could be corrupted, but you still can change them if you attach a debugger to the process, because it has access to the memory of the attached process.

DenisKolodin
  • 13,501
  • 3
  • 62
  • 65