Existe uma maneira de alterar as variáveis ​​de ambiente de outro processo no Unix?

105

No Unix, existe alguma maneira de um processo alterar as variáveis ​​de ambiente de outro (assumindo que todas estejam sendo executadas pelo mesmo usuário)? Uma solução geral seria a melhor, mas se não, o que dizer do caso específico em que um é filho do outro?

Edit: Que tal via gdb?

Raldi
fonte
Isso me parece mais do que feio. Qual é o problema real que você deseja resolver?
Jens
1
Exemplo: eu gostaria de definir uma variável de ambiente para que cada novo aplicativo - iniciado pela IU - pudesse obtê-la. Não conheço nenhum método, exceto definir as variáveis ​​em um dos scripts de inicialização e RE-LOGIN. No entanto, gostaria de não fazer login novamente, mas apenas definir as variáveis ​​na sessão atual para que novos aplicativos possam obtê-la - sem fazer logout da IU.
AlikElzin-kilaka

Respostas:

142

Via gdb:

(gdb) attach process_id

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

(gdb) detach

Este é um hack bastante desagradável e só deve ser feito no contexto de um cenário de depuração, é claro.

An̲̳̳drew
fonte
8
Portanto, isso parece implicar que você pode realmente alterar o ambiente de um processo se anexar ao processo como o GDB faz e, em seguida, desconectar. Parece que seria possível escrever um programa que fizesse apenas isso.
sofrer
3
"Parece que seria possível escrever um programa que fizesse apenas isso" Certamente .. é.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
2
Ele funciona até no Windows usando o cygwin, para processos que não são compilados usando o cygwin!
Juan Carlos Muñoz
11
Observe que isso só funciona se o processo não tiver armazenado em cache permanentemente o valor após um getenv anterior.
Retirou em
1
ptrace: Operation not permitted
gerrit
22

Você provavelmente pode fazer isso tecnicamente (veja outras respostas), mas pode não ajudá-lo.

A maioria dos programas espera que env vars não possam ser alterados externamente após a inicialização, portanto, a maioria provavelmente irá apenas ler os vars em que estão interessados ​​na inicialização e inicializar com base nisso. Portanto, alterá-los posteriormente não fará diferença, já que o programa nunca os relê.

Se você postou isso como um problema concreto, provavelmente deveria adotar uma abordagem diferente. Se fosse apenas por curiosidade: Boa pergunta :-).

Sleske
fonte
1
O caso de uso mais comum em que seria útil é fazer com que os processos filhos herdem as novas variáveis ​​de ambiente, por exemplo, no ambiente de desktop onde você deseja que novos terminais usem as novas variáveis.
Hjulle
13

Substancialmente, não. Se você tinha privilégios suficientes (root ou por aí) e vasculhou / dev / kmem (memória do kernel), e fez alterações no ambiente do processo, e se o processo realmente referenciou novamente a variável de ambiente depois (ou seja, o processo ainda não tinha tirado uma cópia do env var e não estava usando apenas essa cópia), então talvez, se você fosse sortudo e inteligente, e o vento estivesse soprando na direção certa, e a fase da lua estivesse correta, talvez, você pode conseguir algo.

Jonathan Leffler
fonte
2
Não obtive a resposta.
AlikElzin-kilaka
@kilaka: A palavra chave é a segunda - Não . O resto da resposta está dizendo que se você tiver privilégios de root ou estiver executando um depurador, talvez consiga fazer isso, mas para todos os efeitos práticos, a resposta é Não .
Jonathan Leffler
Você tem um script de shell em execução; você deseja alterar o ambiente no processo pai do seu script de shell ... então o script de shell é iniciado gdbno processo pai e é programado para fazer a mudança e funciona sem travar o processo pai. OK - você provavelmente pode fazer isso, mas não é algo que você fará de forma rotineira. Para fins práticos, portanto, a resposta continua sendo não . O resto da resposta cobre as alternativas off-the-wall teoricamente possíveis, um tanto impraticáveis.
Jonathan Leffler
7

Citando Jerry Peek:

Você não pode ensinar novos truques a um cachorro velho.

A única coisa que você pode fazer é alterar a variável de ambiente do processo filho antes de iniciá-lo: ele obtém a cópia do ambiente pai, desculpe.

Consulte http://www.unix.com.ua/orelly/unix/upt/ch06_02.htm para obter detalhes.

Apenas um comentário sobre a resposta sobre o uso de / proc. Em linux / proc é suportado, mas não funciona, você não pode alterar o /proc/${pid}/environarquivo, mesmo se você for root: é absolutamente somente leitura.

Davide
fonte
O que ainda deixa a questão: onde os valores env var realmente são armazenados? Isso é feito pelo kernel? Ou o shell armazena os valores e / proc / <pid> / Environment os obtém de lá?
Oliver,
Este é um detalhe de implementação e pode ser uma boa pergunta (separada). Acho que todo UNIX usa sua própria forma de armazenamento, mas todos compartilham o comportamento descrito acima, que faz parte das especificações.
Davide
7

Eu poderia pensar em uma maneira bastante artificial de fazer isso, e não funcionará para processos arbitrários.

Suponha que você escreva sua própria biblioteca compartilhada que implementa 'char * getenv'. Em seguida, você configura env 'LD_PRELOAD' ou 'LD_LIBRARY_PATH'. vars para que ambos os processos sejam executados com a biblioteca compartilhada pré-carregada.

Dessa forma, você terá essencialmente um controle sobre o código da função 'getenv'. Então, você poderia fazer todos os tipos de truques desagradáveis. Seu 'getenv' pode consultar o arquivo de configuração externo ou segmento SHM para valores alternativos de env vars. Ou você pode fazer pesquisa / substituição regexp nos valores solicitados. Ou ...

Não consigo pensar em uma maneira fácil de fazer isso para processos arbitrários em execução (mesmo se você for root), exceto reescrever o vinculador dinâmico (ld-linux.so).

ADEpt
fonte
Isso deve ser factível. Você poderia ter um pequeno banco de dados gdbm para os pares var = value. Eu tenho algo semelhante para malloc em stromberg.dnsalias.org/~strombrg/malloc-wrapper
dstromberg
Eu acho que este método requer premeditação embora. Você também deve ter cuidado para não aplicá-lo acidentalmente a muitos processos.
dstromberg
3

Ou faça com que seu processo atualize um arquivo de configuração para o novo processo e:

  • execute um kill -HUP no novo processo para reler o arquivo de configuração atualizado, ou
  • faça com que o processo verifique se há atualizações no arquivo de configuração de vez em quando. Se forem encontradas alterações, releia o arquivo de configuração.
Rob Wells
fonte
2

Não tão longe quanto o que sei. Na verdade, você está tentando se comunicar de um processo para outro, que exige um dos métodos IPC (memória compartilhada, semáforos, soquetes, etc.). Tendo recebido dados por um desses métodos, você pode definir variáveis ​​de ambiente ou realizar outras ações mais diretamente.

Stephen Darlington
fonte
1

Se o seu unix suporta o sistema de arquivos / proc, então é trivial LER o env - você pode ler o ambiente, a linha de comando e muitos outros atributos de qualquer processo que você possui dessa forma. Mudando ... Bem, eu posso pensar em uma maneira, mas é uma ideia RUIM.

O caso mais geral ... Não sei, mas duvido que haja uma resposta portátil.

(Editado: minha resposta original assumiu que o OP queria LER o env, não alterá-lo)

Mike G.
fonte
Opa, editei minha resposta - presumi que ele queria ler o env, não alterá-lo.
Mike G.
1
Não me deixe esperando. Qual é a sua má ideia?
raldi,
No Linux, acredito que você PODE ser capaz de abrir / proc / <pid> / mem leitura-gravação para outro processo seu ... Não tenho certeza, no entanto. Tentar e realmente mexer com o meio ambiente seria DEFINITIVAMENTE uma má ideia. Portanto, não estou sugerindo que você experimente ...
Mike G.
1

O UNIX está cheio de comunicação entre processos. Verifique se sua instância de destino tem algum. O Dbus está se tornando um padrão no IPC "desktop".

Eu mudo as variáveis ​​de ambiente dentro do gerenciador de janelas Awesome usando awesome-client com é um Dbus "remetente" de código lua.

DVD
fonte
1

Não é uma resposta direta, mas ... Outro dia Raymond Chen tinha um raciocínio [baseado no Windows] em torno disso : -

... Embora certamente haja maneiras não suportadas de fazer isso ou maneiras que funcionem com a ajuda de um depurador, não há nada que seja compatível com o acesso programático à linha de comando de outro processo, pelo menos nada fornecido pelo kernel. ...

O fato de não haver é consequência do princípio de não rastrear informações de que você não precisa. O kernel não precisa obter a linha de comando de outro processo. Ele pega a linha de comando passada para a CreateProcessfunção e a copia no espaço de endereço do processo que está sendo iniciado, em um local onde a GetCommandLinefunção pode recuperá-la. Uma vez que o processo pode acessar sua própria linha de comando, as responsabilidades do kernel estão feitas.

Como a linha de comando é copiada para o espaço de endereço do processo, o processo pode até mesmo gravar na memória que contém a linha de comando e modificá-la. Se isso acontecer, a linha de comando original será perdida para sempre; a única cópia conhecida foi substituída.

Em outras palavras, qualquer um desses recursos do kernel seria

  • difícil de implementar
  • potencialmente uma preocupação de segurança

No entanto, a razão mais provável é simplesmente que há casos de uso limitados para tal recurso.

Ruben Bartelink
fonte
1

Parece que putenv não funciona agora, mas setenv sim. Eu estava testando a resposta aceita enquanto tentava definir a variável no shell atual sem sucesso

$] 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 a variante como funciona:

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