Como ler variáveis ​​de ambiente de um processo

43

O Linux /proc/<pid>/environnão é atualizado (como eu o entendo, o arquivo contém o ambiente inicial do processo).

Como posso ler o ambiente atual de um processo ?

Jonathan Ben-Avraham
fonte

Respostas:

20

/proc/$pid/environatualiza se o processo alterar seu próprio ambiente. Mas muitos programas não se incomodam em mudar seu próprio ambiente, porque é um pouco inútil: o ambiente de um programa não é visível através dos canais normais, apenas através /proce ps, e nem todas as variantes unix possuem esse tipo de recurso, portanto, os aplicativos não dependem nele.

No que diz respeito ao kernel, o ambiente aparece apenas como o argumento da execvechamada do sistema que inicia o programa. O Linux expõe uma área na memória /proce alguns programas atualizam essa área, enquanto outros não. Em particular, acho que nenhum shell atualiza essa área. Como a área tem um tamanho fixo, seria impossível adicionar novas variáveis ​​ou alterar o tamanho de um valor.

Gilles 'SO- parar de ser mau'
fonte
portanto, efetivamente não há como acessar o processo '* envp (conjunto de indicadores para as configurações do ambiente). @Gilles, você pode mostrar se é possível conectar o depurador e ler o conjunto de ponteiros para as configurações do ambiente.
precisa saber é o seguinte
2
@ Nikhil Claro, é. Mas só porque você escreve PATH=fooem um shell não significa que ele será modificado *envp. Em alguns shells, isso atualizou apenas uma estrutura de dados interna e é o código de execução do programa externo que é atualizado *envp. Olhe assign_in_envem variables.cna fonte bash, por exemplo.
Gilles 'SO- stop be evil'
9
@ Gilles: Esta resposta é, na melhor das hipóteses, enganosa (-1). O ambiente em / proc / $$ / environ é lido da pilha do processo. Veja fs / proc / base.c. Este é o ambiente inicial. Nunca é atualizado e, de fato, não pode ser. O ambiente usado pelo libc setenv é alocado no heap e inicializado com o conteúdo do ambiente na pilha. Se o processo chamar libc, forklibc fará a sys_forkchamada usando o ambiente alocado para heap para o processo filho.
Jonathan Ben-Avraham
7
@ JonathanBen-Avraham Você está certo que o ambiente inicial não é atualizado em nenhum shell. No entanto, essa área não é apenas para leitura no Linux, encontrei programas que a usam para relatar seu status (os relatórios de status argvsão mais comuns, mas ambos existem).
Gilles 'SO- stop be evil'
39

Você pode ler o ambiente inicial de um processo em /proc/<pid>/environ.

Se um processo alterar seu ambiente, para ler o ambiente, você deve ter a tabela de símbolos para o processo e usar a ptracechamada do sistema (por exemplo, usando gdb) para ler o ambiente a partir da char **__environvariável global . Não há outra maneira de obter o valor de qualquer variável de um processo Linux em execução.

Essa é a resposta. Agora, para algumas anotações.

O exposto acima pressupõe que o processo seja compatível com POSIX, o que significa que o processo gerencia seu ambiente usando uma variável global char **__environconforme especificado nas especificações de referência .

O ambiente inicial de um processo é passado para o processo em um buffer de tamanho fixo na pilha do processo. (O mecanismo usual que faz isso é linux//fs/exec.c:do_execve_common(...).) Como o tamanho do buffer é calculado para não exceder o tamanho necessário para o ambiente inicial, você não pode adicionar novas variáveis ​​sem apagar as variáveis ​​existentes ou esmagar a pilha. Portanto, qualquer esquema razoável para permitir mudanças no ambiente de um processo usaria o heap, onde a memória em tamanhos arbitrários pode ser alocada e liberada, o que é exatamente o que o GNU libc( glibc) faz por você.

Se o processo for utilizado glibc, ele será compatível com POSIX, __environsendo declarado no glibc//posix/environ.cGlibc inicializado __environcom um ponteiro para a memória que é mallocdo heap do processo e copia o ambiente inicial da pilha para essa área de heap. Cada vez que o processo usa a setenvfunção, glibcfaz um reallocpara ajustar o tamanho da área que __environaponta para acomodar o novo valor ou variável. (Você pode baixar o código-fonte da glibc com git clone git://sourceware.org/git/glibc.git glibc). Para realmente entender o mecanismo, você também precisará ler o código Hurd hurd//init/init.c:frob_kernel_process()(git clone git: //git.sv.gnu.org/hurd/hurd.git hurd).

Agora, se o novo processo for forkeditado apenas , sem uma execsubstituição subsequente da pilha, a magia de cópia de argumento e ambiente será concluída linux//kernel/fork.c:do_fork(...), onde as copy_processchamadas de rotina dup_task_structque alocam a pilha do novo processo chamando alloc_thread_info_node, chama setup_thread_stack( linux//include/linux/sched.h) para o novo processo usando alloc_thread_info_node.

Finalmente, a __environconvenção POSIX é uma convenção de espaço do usuário . Não tem conexão com nada no kernel do Linux. Você pode escrever um programa de espaço do usuário sem usar glibce sem o __environglobal e, em seguida, gerenciar as variáveis ​​de ambiente da maneira que desejar. Ninguém o prenderá por fazer isso, mas você terá que escrever suas próprias funções de gerenciamento de ambiente ( setenv/ getenv) e seus próprios wrappers, sys_exece é provável que ninguém seja capaz de adivinhar onde você colocou as alterações em seu ambiente.

Jonathan Ben-Avraham
fonte
Muitos dos arquivos /proc/[pid]/parecem ter uma codificação estranha (alguém pode saber o que e por quê). Para mim, simplesmente cat environimprimiria as variáveis ​​de ambiente em um formato realmente difícil de ler. cat environ | stringsresolveu isso para mim.
retrohacker
@retrohacker Isso fornece uma solução mais robusta: askubuntu.com/questions/978711/…
Frank Kusters
20

Ele é atualizado à medida que o processo adquire / exclui suas variáveis ​​de ambiente. Você tem uma referência informando que o environarquivo não foi atualizado para o processo em seu diretório de processo em / proc filesystem?

xargs --null --max-args=1 echo < /proc/self/environ

ou

xargs --null --max-args=1 echo < /proc/<pid>/environ

ou

ps e -p <pid>

O pstexto acima imprimirá as variáveis ​​de ambiente do processo no formato de saída, o processamento de texto (análise / filtragem) é necessário para ver as variáveis ​​de ambiente como uma lista.

Solaris (não solicitado, mas para referência vou postar aqui):

/usr/ucb/ps -wwwe <pid>

ou

pargs -e <pid> 

EDIT: / proc / pid / environ não é atualizado! Eu estou corrigido. O processo de verificação está abaixo. No entanto, os filhos dos quais o processo é submetido à bifurcação herdam a variável de ambiente do processo e ela é visível em seu respectivo arquivo / proc / self / environment. (Use strings)

Com no shell: aqui xargs é um processo filho e, portanto, herda a variável de ambiente e também reflete em seu /proc/self/environarquivo.

[centos@centos t]$ printenv  | grep MASK
[centos@centos t]$ export MASK=NIKHIL
[centos@centos t]$ printenv  | grep MASK
MASK=NIKHIL
[centos@centos t]$ xargs --null --max-args=1 echo < /proc/self/environ  | grep MASK
MASK=NIKHIL
[centos@centos t]$ unset MASK
[centos@centos t]$ printenv  | grep MASK
[centos@centos t]$ xargs --null --max-args=1 echo < /proc/self/environ  | grep MASK
[centos@centos t]$

Verificando-o de outra sessão, em que o terminal / sessão não é o processo filho do shell em que a variável de ambiente está definida.

Verificando de outro terminal / sessão no mesmo host:

terminal1:: Observe que printenv é fork e é um processo filho do bash e, portanto, lê seu próprio arquivo environment.

[centos@centos t]$ echo $$
2610
[centos@centos t]$ export SPIDEY=NIKHIL
[centos@centos t]$ printenv | grep SPIDEY
SPIDEY=NIKHIL
[centos@centos t]$ 

terminal2: no mesmo host - não o inicie no mesmo shell em que a variável acima foi definida, inicie o terminal separadamente.

[centos@centos ~]$ echo $$
4436
[centos@centos ~]$ xargs --null --max-args=1 echo < /proc/self/environ | grep -i spidey
[centos@centos ~]$ strings -f /proc/2610/environ | grep -i spidey
[centos@centos ~]$ xargs --null --max-args=1 echo < /proc/2610/environ | grep -i spidey
[centos@centos ~]$ 
Nikhil Mulley
fonte
1
Eu faço export foo=barna sessão de uma festa (pid xxxx), depois faço cat /proc/xxxx/environ | tr \\0 \\nna sessão de outra festa e não vejo foo.
Atualizei a resposta acima com um exemplo, verificando o mesmo processo no shell.
Nikhil Mulley
Você está certo. Eu estou corrigido. Obrigado. Agora preciso ler meus manuais para verificar as variáveis ​​de ambiente de um processo diferente no grupo de processos do usuário.
precisa saber é o seguinte
1
Mais uma coisa: tentei verificar o ambiente anexando gdb o pid, mas ainda não há referência lá. O bloco de variáveis ​​de ambiente na memória é realocado sempre que há uma alteração e não está refletindo no arquivo de ambiente de seu próprio processo no sistema de arquivos proc, mas, no entanto, permite ser herdado pelo processo filho. Isso significa que é mais fácil saber detalhes intrínsecos quando o fork ocorre, como o processo filho obtém as variáveis ​​de ambiente copiadas como estão.
precisa saber é o seguinte
Espero @Gilles jogaria um pouco de sua luz da tocha neste .. :-)
Nikhil Mulley
7

Bem, o seguinte não está relacionado às reais intenções do autor, mas se você realmente deseja "LER" /proc/<pid>/environ, pode tentar

strings /proc/<pid>/environ

o que é melhor que catisso.

fibonacci
fonte
1
+1 para strings. Mantenha simples.
Ed Randall
Concordo @EdRandall, isto parece a abordagem mais fácil vs xargs --null.
Per Lundberg
O "arquivo" é nulo, substitua nulos por nova linha e as ferramentas normais funcionam novamente (com as advertências usuais), por exemplo:tr '\0' '\n' < /proc/$$/environ | ...
Thor
Desde que a variável de ambiente em si não contenha novas linhas. Cascas como o BASH às vezes fazem isso para 'funções exportadas'.
anthony