C'è un modo per cambiare le variabili d'ambiente di un altro processo in Unix?


105

Su Unix, c'è un modo in cui un processo può cambiare le variabili d'ambiente di un altro (supponendo che siano tutti eseguiti dallo stesso utente)? Una soluzione generale sarebbe la migliore, ma in caso contrario, che dire del caso specifico in cui uno è figlio dell'altro?

Modifica: che ne dici di tramite gdb?


Questo mi sembra più che brutto. Qual è il vero problema che vuoi risolvere?
Jens

1
Esempio: vorrei definire una variabile di ambiente in modo che ogni nuova app, lanciata dall'interfaccia utente, la ottenga. Non conosco alcun metodo tranne la definizione delle variabili in uno degli script di avvio e RE-LOGIN. Tuttavia, vorrei non eseguire nuovamente il login, ma definire semplicemente le variabili nella sessione corrente in modo che le nuove app lo ottengano, senza disconnettermi dall'interfaccia utente.
AlikElzin-kilaka

Risposte:


142

Tramite gdb:

(gdb) attach process_id

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

(gdb) detach

Questo è un trucco piuttosto brutto e dovrebbe essere fatto solo nel contesto di uno scenario di debug, ovviamente.


8
Quindi questo sembra implicare che puoi effettivamente cambiare l'ambiente di un processo se ti colleghi al processo come fa GDB e poi ti scolleghi. Sembra che sarebbe possibile scrivere un programma che fa solo questo.
lutto

3
"Sembra che sarebbe possibile scrivere un programma che fa solo questo" Infatti .. lo è.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

2
Funziona anche su Windows usando cygwin, per processi che non sono compilati usando cygwin!
Juan Carlos Muñoz

11
Nota che questo funziona solo se il processo non ha memorizzato nella cache il valore in modo permanente dopo un getenv precedente.
Andrew

1
ptrace: Operation not permitted
gerrit

22

Probabilmente puoi farlo tecnicamente (vedi altre risposte), ma potrebbe non aiutarti.

La maggior parte dei programmi si aspetterà che le variabili env non possano essere modificate dall'esterno dopo l'avvio, quindi la maggior parte probabilmente leggerà solo le variabili a cui sono interessati all'avvio e inizializzerà in base a ciò. Quindi cambiarli in seguito non farà alcuna differenza, poiché il programma non li rileggerà mai.

Se hai pubblicato questo come un problema concreto, dovresti probabilmente adottare un approccio diverso. Se fosse solo per curiosità: Bella domanda :-).


1
Il caso d'uso più comune in cui sarebbe utile è fare in modo che i processi figli ereditino le nuove variabili di ambiente, ad esempio, nell'ambiente desktop in cui si desidera che i nuovi terminali utilizzino le nuove variabili.
Hjulle

13

Sostanzialmente no. Se si disponeva di privilegi sufficienti (root, o giù di lì) e si è cercato in / dev / kmem (memoria del kernel) e si sono apportate modifiche all'ambiente del processo e se il processo ha effettivamente fatto nuovamente riferimento alla variabile di ambiente in seguito (ovvero, il processo non aveva già preso una copia dell'env var e non stava usando solo quella copia), quindi forse, se sei stato fortunato e intelligente, e il vento soffiava nella giusta direzione e la fase lunare era corretta, forse, potresti ottenere qualcosa.


2
Non ho avuto la risposta.
AlikElzin-kilaka

@kilaka: La parola chiave è la seconda: no . Il resto della risposta sta dicendo che se si hanno i privilegi di root o si esegue un debugger, allora forse si può fare, ma per tutti gli scopi pratici, la risposta è No .
Jonathan Leffler

Hai uno script di shell in esecuzione; vuoi cambiare l'ambiente nel processo genitore del tuo script di shell ... così lo script di shell si avvia gdbsul processo genitore ed è programmato per fare la modifica, e funziona senza mandare in crash il processo genitore. OK, probabilmente puoi farlo, ma non è qualcosa che farai di routine. Ai fini pratici, quindi, la risposta rimane No . Il resto della risposta copre le alternative teoricamente possibili, in qualche modo impraticabili.
Jonathan Leffler

7

Citando Jerry Peek:

Non puoi insegnare nuovi trucchi a un vecchio cane.

L'unica cosa che puoi fare è cambiare la variabile d'ambiente del processo figlio prima di avviarlo: ottiene la copia dell'ambiente genitore, mi dispiace.

Vedere http://www.unix.com.ua/orelly/unix/upt/ch06_02.htm per i dettagli.

Solo un commento sulla risposta sull'uso di / proc. Sotto linux / proc è supportato ma, non funziona, non puoi cambiare il /proc/${pid}/environfile, anche se sei root: è assolutamente di sola lettura.


Il che lascia ancora la domanda: dove sono effettivamente memorizzati i valori di env var? Viene fatto dal kernel? Oppure la shell memorizza i valori e / proc / <pid> /viron li ottiene da lì?
oliver

Questo è un dettaglio di implementazione e potrebbe essere una buona domanda (separata). Penso che ogni UNIX utilizzi il proprio modo per l'archiviazione, ma tutti condividono il comportamento descritto sopra, che fa parte delle specifiche.
Davide

7

Potrei pensare al modo piuttosto artificioso per farlo, e non funzionerà per processi arbitrari.

Supponi di scrivere la tua libreria condivisa che implementi 'char * getenv'. Quindi, imposti "LD_PRELOAD" o "LD_LIBRARY_PATH" env. vars in modo che entrambi i processi vengano eseguiti con la libreria condivisa precaricata.

In questo modo, avrai essenzialmente il controllo sul codice della funzione "getenv". Quindi, potresti fare tutti i tipi di brutti trucchi. Il tuo "getenv" potrebbe consultare il file di configurazione esterno o il segmento SHM per i valori alternativi di env vars. Oppure puoi eseguire la ricerca / sostituzione regexp sui valori richiesti. O ...

Non riesco a pensare a un modo semplice per farlo per processi in esecuzione arbitrari (anche se sei root), a meno di riscrivere il linker dinamico (ld-linux.so).


Questo dovrebbe essere fattibile. Potresti avere un piccolo database gdbm per le coppie var = value. Ho qualcosa di simile per malloc su stromberg.dnsalias.org/~strombrg/malloc-wrapper
dstromberg

Penso che questo metodo richieda una riflessione. Dovresti anche stare attento a non applicarlo accidentalmente a troppi processi.
dstromberg

3

Oppure fai in modo che il tuo processo aggiorni un file di configurazione per il nuovo processo e poi:

  • eseguire un kill -HUP sul nuovo processo per rileggere il file di configurazione aggiornato, o
  • chiedi al processo di controllare il file di configurazione per gli aggiornamenti di tanto in tanto. Se vengono rilevate modifiche, rileggere il file di configurazione.

2

Non per quanto ne so. In realtà stai cercando di comunicare da un processo a un altro che richiede uno dei metodi IPC (memoria condivisa, semafori, socket, ecc.). Dopo aver ricevuto i dati con uno di questi metodi, è possibile impostare le variabili di ambiente o eseguire altre azioni in modo più diretto.


1

Se il tuo unix supporta il filesystem / proc, allora è banale LEGGERE l'env: puoi leggere l'ambiente, la riga di comando e molti altri attributi di qualsiasi processo che possiedi in quel modo. Cambiarlo ... Beh, posso pensare a un modo, ma è un'idea CATTIVA.

Il caso più generale ... non lo so, ma dubito che ci sia una risposta portatile.

(Modificato: la mia risposta originale presupponeva che l'OP volesse LEGGERE l'env, non cambiarlo)


Ops, ho modificato la mia risposta - stavo assumendo che volesse leggere l'env, non cambiarlo.
Mike G.

1
Non lasciarmi in sospeso. Qual è la tua pessima idea?
raldi

Su Linux, credo che POTRESTI essere in grado di aprire / proc / <pid> / mem read-write per altri processi che possiedi ... Non ne sono sicuro, però. Provare, e effettivamente interferire con l'ambiente, sarebbe SICURAMENTE una cattiva idea. Quindi non ti sto suggerendo di provarlo ...
Mike G.

1

UNIX è pieno di comunicazioni tra processi. Controlla se la tua istanza di destinazione ne ha alcuni. Dbus sta diventando uno standard nell'IPC "desktop".

Cambio le variabili d'ambiente all'interno di Awesome window manager usando awesome-client con è un "mittente" Dbus di codice lua.


1

Non una risposta diretta ma ... Raymond Chen aveva una logica [basata su Windows] su questo solo l'altro giorno : -

... Sebbene ci siano certamente modi non supportati per farlo o modi che funzionano con l'assistenza di un debugger, non c'è nulla che sia supportato per l'accesso programmatico alla riga di comando di un altro processo, almeno niente fornito dal kernel. ...

Il fatto che non ci sia è una conseguenza del principio di non tenere traccia delle informazioni di cui non hai bisogno. Il kernel non ha bisogno di ottenere la riga di comando di un altro processo. Prende la riga di comando passata alla CreateProcessfunzione e la copia nello spazio degli indirizzi del processo avviato, in una posizione in cui la GetCommandLinefunzione può recuperarla. Una volta che il processo può accedere alla propria riga di comando, le responsabilità del kernel sono esaurite.

Poiché la riga di comando viene copiata nello spazio degli indirizzi del processo, il processo potrebbe persino scrivere nella memoria che contiene la riga di comando e modificarla. Se ciò accade, la riga di comando originale viene persa per sempre; l'unica copia nota è stata sovrascritta.

In altre parole, qualsiasi funzionalità del kernel di questo tipo sarebbe

  • difficile da implementare
  • potenzialmente un problema di sicurezza

Tuttavia, la ragione più probabile è semplicemente che ci sono casi d'uso limitati per tale struttura.


1

Sembra che putenv non funzioni ora, ma setenv . Stavo testando la risposta accettata mentre cercavo di impostare la variabile nella shell corrente senza successo

$] 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=

e la variante come funziona:

$] 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.