Czy istnieje sposób na zmianę zmiennych środowiskowych innego procesu w systemie Unix?

105

Czy w systemie Unix istnieje sposób, w jaki jeden proces może zmienić zmienne środowiskowe innego (zakładając, że wszystkie są uruchamiane przez tego samego użytkownika)? Najlepsze byłoby rozwiązanie ogólne, ale jeśli nie, to co z konkretnym przypadkiem, w którym jedno jest dzieckiem drugiego?

Edycja: A może przez gdb?

raldi
źródło
To wydaje mi się więcej niż brzydkie. Jaki jest rzeczywisty problem, który chcesz rozwiązać?
Jens
1
Przykład: chciałbym zdefiniować zmienną środowiskową, aby każda nowa aplikacja - uruchamiana przez interfejs użytkownika - otrzymywała ją. Nie znam żadnej metody poza definiowaniem zmiennych w jednym ze skryptów startowych i PONOWNIE LOGOWANIE. Chciałbym jednak nie logować się ponownie, ale po prostu zdefiniować zmienne w bieżącej sesji, aby nowe aplikacje je otrzymały - bez wylogowywania się z interfejsu użytkownika.
AlikElzin-kilaka

Odpowiedzi:

143

Przez gdb:

(gdb) attach process_id

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

(gdb) detach

Jest to dość paskudny hack i oczywiście należy go robić tylko w kontekście scenariusza debugowania.

Andrzej
źródło
8
Wydaje się więc, że faktycznie możesz zmienić środowisko procesu, jeśli dołączysz do procesu tak jak robi to GDB, a następnie się odłączysz. Wydaje się, że byłoby możliwe napisanie programu, który robi tylko to.
opłakiwać
3
„Wydaje się, że można byłoby napisać program, który robi tylko to”. Rzeczywiście… jest.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
2
Działa nawet w systemie Windows przy użyciu cygwin, dla procesów, które nie są kompilowane przy użyciu cygwin!
Juan Carlos Muñoz
12
Zauważ, że działa to tylko wtedy, gdy proces nie zapisał na stałe wartości w pamięci podręcznej po wcześniejszym getenv.
An̲̳̳drew
2
W niektórych systemach gdb może dać następujący błąd 'putenv' has unknown return type; cast the call to its declared return type:; w takich przypadkach powinieneś zmienić putenvpołączenie na to:call (int) putenv ("env_var_name=env_var_value")
Emir Uner,
22

Prawdopodobnie możesz to zrobić technicznie (zobacz inne odpowiedzi), ale może ci to nie pomóc.

Większość programów spodziewa się, że zmiennych środowiska nie można zmienić z zewnątrz po uruchomieniu, dlatego większość prawdopodobnie po prostu odczyta zmienne, którymi są zainteresowane, podczas uruchamiania i zainicjuje na tej podstawie. Zatem ich późniejsza zmiana nie będzie miała znaczenia, ponieważ program nigdy ich nie przeczyta ponownie.

Jeśli opublikowałeś to jako konkretny problem, prawdopodobnie powinieneś przyjąć inne podejście. Gdyby to było tylko z ciekawości: fajne pytanie :-).

sleske
źródło
1
Najczęstszym przypadkiem użycia, w którym byłoby to użyteczne, jest sprawienie, aby procesy potomne dziedziczyły nowe zmienne środowiskowe, na przykład w środowisku graficznym, w którym nowe terminale mają używać nowych zmiennych.
Hjulle,
13

Zasadniczo nie. Jeśli miałeś wystarczające uprawnienia (root lub w pobliżu) i grzebałeś w / dev / kmem (pamięć jądra) i wprowadziłeś zmiany w środowisku procesu, i jeśli proces faktycznie ponownie odwoływał się później do zmiennej środowiskowej (to znaczy proces nie zrobiłeś jeszcze kopii env var i nie używałeś tylko tej kopii), to może, jeśli miałeś szczęście i spryt, a wiatr wiał we właściwym kierunku, a faza księżyca była, być może, możesz coś osiągnąć.

Jonathan Leffler
źródło
2
Nie otrzymałem odpowiedzi.
AlikElzin-kilaka
@kilaka: Kluczowym słowem jest drugie - nie . Reszta odpowiedzi mówi, że jeśli masz uprawnienia roota lub używasz debugera, być może możesz to zrobić, ale ze względów praktycznych odpowiedź brzmi: nie .
Jonathan Leffler
Masz uruchomiony skrypt powłoki; chcesz zmienić środowisko w procesie macierzystym twojego skryptu powłoki ... więc skrypt powłoki uruchamia gdbsię w procesie nadrzędnym i jest zaprogramowany w celu wykonania zmiany, i działa bez awarii procesu nadrzędnego. OK - prawdopodobnie możesz to zrobić, ale nie jest to coś, co masz zamiar robić rutynowo. Dlatego ze względów praktycznych odpowiedź brzmi: nie . Reszta odpowiedzi obejmuje teoretycznie możliwe poza ścianą, nieco niewykonalne w praktyce alternatywy.
Jonathan Leffler,
7

Cytując Jerry'ego Peeka:

Nie możesz nauczyć starego psa nowych sztuczek.

Jedyne, co możesz zrobić, to zmienić zmienną środowiskową procesu potomnego przed jego uruchomieniem: przepraszam, pobiera on kopię środowiska nadrzędnego.

Szczegółowe informacje można znaleźć pod adresem http://www.unix.com.ua/orelly/unix/upt/ch06_02.htm .

Tylko komentarz do odpowiedzi na temat używania / proc. Pod linux / proc jest obsługiwany, ale nie działa, nie możesz zmienić /proc/${pid}/environpliku, nawet jeśli jesteś rootem: jest on absolutnie tylko do odczytu.

Davide
źródło
Pozostaje jednak pytanie: gdzie faktycznie przechowywane są wartości env var? Czy jest to robione przez jądro? A może powłoka przechowuje wartości, a / proc / <pid> / environment pobiera je stamtąd?
oliver
To jest szczegół implementacji i może to być (oddzielne) dobre pytanie. Myślę, że każdy UNIX używa własnego sposobu przechowywania, ale wszystkie z nich mają podobne zachowanie opisane powyżej, które jest częścią specyfikacji.
Davide,
7

Mógłbym wymyślić dość wymyślny sposób, aby to zrobić, i nie zadziała w przypadku arbitralnych procesów.

Załóżmy, że piszesz własną bibliotekę współdzieloną, która implementuje 'char * getenv'. Następnie ustaw „LD_PRELOAD” lub „LD_LIBRARY_PATH” env. vars, aby oba procesy działały z wstępnie załadowaną biblioteką współdzieloną.

W ten sposób będziesz mieć kontrolę nad kodem funkcji „getenv”. Następnie można było robić różnego rodzaju paskudne sztuczki. Twój „getenv” może sprawdzić w zewnętrznym pliku konfiguracyjnym lub segmencie SHM alternatywne wartości zmiennych env. Lub możesz przeszukać / zamienić regexp na żądanych wartościach. Albo ...

Nie mogę wymyślić prostego sposobu na zrobienie tego dla dowolnie działających procesów (nawet jeśli jesteś rootem), poza przepisaniem dynamicznego linkera (ld-linux.so).

Adept
źródło
To powinno być wykonalne. Możesz mieć małą bazę danych gdbm dla par zmienna = wartość. Mam coś podobnego do malloc w stromberg.dnsalias.org/~strombrg/malloc-wrapper
dstromberg
Myślę, że ta metoda wymaga jednak przemyślenia. Trzeba też uważać, aby przypadkowo nie zastosować go do zbyt wielu procesów.
dstromberg
3

Lub poproś proces o zaktualizowanie pliku konfiguracyjnego dla nowego procesu, a następnie:

  • wykonaj kill -HUP na nowym procesie, aby ponownie odczytać zaktualizowany plik konfiguracyjny lub
  • niech proces sprawdza plik konfiguracyjny pod kątem aktualizacji od czasu do czasu. Jeśli zostaną znalezione zmiany, ponownie wczytaj plik konfiguracyjny.
Rob Wells
źródło
2

O ile wiem, nie. Naprawdę próbujesz komunikować się z jednego procesu do drugiego, co wymaga jednej z metod IPC (pamięć współdzielona, ​​semafory, gniazda itp.). Po otrzymaniu danych jedną z tych metod można było ustawić zmienne środowiskowe lub wykonać inne czynności bardziej bezpośrednio.

Stephen Darlington
źródło
1

Jeśli twój unix obsługuje system plików / proc, to po prostu CZYTAĆ env - możesz czytać środowisko, wiersz poleceń i wiele innych atrybutów dowolnego procesu, który posiadasz w ten sposób. Zmieniam to ... Cóż, przychodzi mi do głowy sposób, ale to ZŁY pomysł.

Bardziej ogólny przypadek ... Nie wiem, ale wątpię, czy istnieje przenośna odpowiedź.

(Edytowano: moja pierwotna odpowiedź zakładała, że ​​OP chciał ODCZYTAĆ env, a nie go zmienić)

Mike G.
źródło
Ups, zredagowałem odpowiedź - zakładałem, że chciał przeczytać env, a nie zmienić.
Mike G.
1
Nie zostawiaj mnie wiszącego. Jaki jest twój zły pomysł?
raldi
W Linuksie myślę, że MOŻESZ być w stanie otworzyć / proc / <pid> / mem do odczytu i zapisu dla innego procesu, który posiadasz ... Nie jestem jednak pewien. Próbowanie, a właściwie ingerowanie w środowisko, ZDEFINIOWANO byłoby złym pomysłem. Więc nie sugeruję, żebyś spróbował ...
Mike G.,
1

UNIX jest pełen komunikacji międzyprocesowej. Sprawdź, czy Twoja docelowa instancja ma jakieś pliki. Dbus staje się standardem w "desktopowych" IPC.

Zmieniam zmienne środowiskowe wewnątrz Awesome Window Managera używając awesome-client z "nadawcą" Dbus kodu Lua.

płyta DVD
źródło
1

Nie jest to bezpośrednia odpowiedź, ale ... Raymond Chen miał uzasadnienie [oparte na Windows] dotyczące tego tylko niedawno : -

... Chociaż z pewnością istnieją nieobsługiwane sposoby robienia tego lub sposoby, które działają z pomocą debugera, nie ma nic, co jest obsługiwane w programowym dostępie do wiersza poleceń innego procesu, a przynajmniej nic nie jest dostarczane przez jądro. ...

To, że tak nie jest, jest konsekwencją zasady nie śledzenia informacji, których nie potrzebujesz. Jądro nie musi pobierać wiersza poleceń innego procesu. Pobiera wiersz poleceń przekazany do CreateProcessfunkcji i kopiuje go do przestrzeni adresowej uruchamianego procesu, w lokalizacji, w którejGetCommandLine funkcja może go pobrać. Gdy proces uzyska dostęp do własnej linii poleceń, obowiązki jądra są wykonywane.

Ponieważ wiersz poleceń jest kopiowany do przestrzeni adresowej procesu, proces może nawet zapisywać w pamięci, w której znajduje się wiersz poleceń, i modyfikować go. Jeśli tak się stanie, oryginalna linia poleceń zostanie utracona na zawsze; jedyna znana kopia została nadpisana.

Innymi słowy, takie udogodnienia jądra byłyby

  • trudne do wdrożenia
  • potencjalnie zagrożenie bezpieczeństwa

Jednak najbardziej prawdopodobnym powodem jest po prostu to, że istnieją ograniczone przypadki użycia takiej funkcji.

Ruben Bartelink
źródło
1

Wygląda na to, że putenv teraz nie działa, ale setenv działa. Testowałem zaakceptowaną odpowiedź, próbując bez powodzenia ustawić zmienną w bieżącej powłoce

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

i wariant, jak to działa:

$] 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
źródło