?

Log in

No account? Create an account
 
 
12 November 2010 @ 06:55 pm
copyenv: Copy the environmental variables of any running process  
In a previous article I talked about copysshenv, which is meant to extract the environmental variables related to working with an ssh-agent out of a running process. As explained in the article, this is necessary if your system starts ssh-agent when you log in to a desktop, but you later want to access the same running agent when you log in remotely. ssh-agent uses environmental variables to let ssh clients know how to talk to it. The normal way for an ssh client to have access to those variables is the have been a child (or grandchild, or great grandchild, etc) of the ssh-agent process, inheriting the variables that way. If you can't arrange that, you're out of luck, unless you run on Linux and use copysshenv, in which case you can find the ID of a process that has the environment variables and use copysshenv to extract them from the process.

That version of copysshenv has two flaws. First, you have to know the ID number of the process (usually by using the ps command. Second, it only worked for ssh-related environmental variables.

So I wrote copyenv, which you can get here: http://impson.tzo.com/~jdimpson/bin/copyenv. Handling the second flaw is actually trivial, and is achieved by no longer limiting the output to the ssh variables. In fact, I've since re-implemented copysshenv by replacing it with the following one-line script: copyenv | grep SSH.

The old copysshenv required the process ID as the command-line argument, but the new copyenv allows either the process ID or the process name. It works a lot like the Linux version of the killall command. (In fact, copyenv uses the pidof, which is part of the same package as killall and probable shares source code.) As such, it has some of the same drawbacks of pidof and killall, which is the inability to determine which process is desired if there are two or more instances of the same process running. In this case, copyenv will print the environmental variables of all of them, but for each variable printed, it will print the ID of the process it came from.

Here's a truncated example of running copyenv on all the running bash processes. The output is sorted so you can compare the same variable across each running copy of bash.

jdimpson@artoo:~$ copyenv bash | sort
export COLORTERM=gnome-terminal # pid 7669
export COLORTERM=gnome-terminal # pid 7672
export COLORTERM=gnome-terminal # pid 7681
export COLORTERM=gnome-terminal # pid 7689
export COLORTERM=gnome-terminal # pid 9732
export DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-OQ3vPHkSk8,guid=ac373a4c08f01e435d5427a24aa28ce3 # pid 7669
export DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-OQ3vPHkSk8,guid=ac373a4c08f01e435d5427a24aa28ce3 # pid 7672
export DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-OQ3vPHkSk8,guid=ac373a4c08f01e435d5427a24aa28ce3 # pid 7681
export DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-OQ3vPHkSk8,guid=ac373a4c08f01e435d5427a24aa28ce3 # pid 7689
export DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-OQ3vPHkSk8,guid=ac373a4c08f01e435d5427a24aa28ce3 # pid 9732
export DISPLAY=:1.0 # pid 7669
export DISPLAY=:1.0 # pid 7672
export DISPLAY=:1.0 # pid 7681
export DISPLAY=:1.0 # pid 7689
export DISPLAY=:1.0 # pid 9732
export GNOME_DESKTOP_SESSION_ID=Default # pid 7669
export GNOME_DESKTOP_SESSION_ID=Default # pid 7672
export GNOME_DESKTOP_SESSION_ID=Default # pid 7681
export GNOME_DESKTOP_SESSION_ID=Default # pid 7689
export GNOME_DESKTOP_SESSION_ID=Default # pid 9732
export GNOME_KEYRING_SOCKET=/tmp/keyring-sZEbo7/socket # pid 7669
export GNOME_KEYRING_SOCKET=/tmp/keyring-sZEbo7/socket # pid 7672
export GNOME_KEYRING_SOCKET=/tmp/keyring-sZEbo7/socket # pid 7681
export GNOME_KEYRING_SOCKET=/tmp/keyring-sZEbo7/socket # pid 7689
export GNOME_KEYRING_SOCKET=/tmp/keyring-sZEbo7/socket # pid 9732
export GTK_RC_FILES=/etc/gtk/gtkrc:/home/jdimpson/.gtkrc-1.2-gnome2 # pid 7669
export GTK_RC_FILES=/etc/gtk/gtkrc:/home/jdimpson/.gtkrc-1.2-gnome2 # pid 7672
export GTK_RC_FILES=/etc/gtk/gtkrc:/home/jdimpson/.gtkrc-1.2-gnome2 # pid 7681
export GTK_RC_FILES=/etc/gtk/gtkrc:/home/jdimpson/.gtkrc-1.2-gnome2 # pid 7689
export GTK_RC_FILES=/etc/gtk/gtkrc:/home/jdimpson/.gtkrc-1.2-gnome2 # pid 9732
export HISTCONTROL=ignoreboth # pid 9732
export HOME=/home/jdimpson # pid 4580
export HOME=/home/jdimpson # pid 7669
export HOME=/home/jdimpson # pid 7672
export HOME=/home/jdimpson # pid 7681
export HOME=/home/jdimpson # pid 7689
export HOME=/home/jdimpson # pid 9732
export LANG=en_US.UTF-8 # pid 4580
export LANG=en_US.UTF-8 # pid 7669
export LANG=en_US.UTF-8 # pid 7672
export LANG=en_US.UTF-8 # pid 7681
export LANG=en_US.UTF-8 # pid 7689
export LANG=en_US.UTF-8 # pid 9732
export "LESSCLOSE=/usr/bin/lesspipe %s %s" # pid 9732
export "LESSOPEN=| /usr/bin/lesspipe %s" # pid 9732
export LOGNAME=jdimpson # pid 4580
export LOGNAME=jdimpson # pid 7669
export LOGNAME=jdimpson # pid 7672
export LOGNAME=jdimpson # pid 7681
export LOGNAME=jdimpson # pid 7689
export LOGNAME=jdimpson # pid 9732
export LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.svgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36: # pid 9732
export MAIL=/var/mail/jdimpson # pid 4580
export MAIL=/var/mail/jdimpson # pid 7669
export MAIL=/var/mail/jdimpson # pid 7672
export MAIL=/var/mail/jdimpson # pid 7681
export MAIL=/var/mail/jdimpson # pid 7689
export MAIL=/var/mail/jdimpson # pid 9732
export OLDPWD=/ # pid 7669
export OLDPWD=/ # pid 7672
export OLDPWD=/ # pid 7681
export OLDPWD=/ # pid 7689
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:$HOME/bin:/home/jdimpson/bin # pid 9732
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:$HOME/bin # pid 4580
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:$HOME/bin # pid 7669
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:$HOME/bin # pid 7672
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:$HOME/bin # pid 7681
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:$HOME/bin # pid 7689
export previous=N # pid 7669
export previous=N # pid 7672
export previous=N # pid 7681
export previous=N # pid 7689
export previous=N # pid 9732
export PREVLEVEL=N # pid 7669
export PREVLEVEL=N # pid 7672
export PREVLEVEL=N # pid 7681
export PREVLEVEL=N # pid 7689
export PREVLEVEL=N # pid 9732
export PWD=/home/jdimpson # pid 7669
export PWD=/home/jdimpson # pid 7672
export PWD=/home/jdimpson # pid 7681
export PWD=/home/jdimpson # pid 7689
export PWD=/home/jdimpson # pid 9732
export QEMU_ALSA_DAC_BUFFER_SIZE=4096 # pid 4580
export QEMU_ALSA_DAC_BUFFER_SIZE=4096 # pid 7669
export QEMU_ALSA_DAC_BUFFER_SIZE=4096 # pid 7672
export QEMU_ALSA_DAC_BUFFER_SIZE=4096 # pid 7681
export QEMU_ALSA_DAC_BUFFER_SIZE=4096 # pid 7689
export QEMU_ALSA_DAC_BUFFER_SIZE=4096 # pid 9732
export QEMU_AUDIO_ADC_FIXED_FREQ=48000 # pid 4580
export QEMU_AUDIO_ADC_FIXED_FREQ=48000 # pid 7669
export QEMU_AUDIO_ADC_FIXED_FREQ=48000 # pid 7672
export QEMU_AUDIO_ADC_FIXED_FREQ=48000 # pid 7681
export QEMU_AUDIO_ADC_FIXED_FREQ=48000 # pid 7689
export QEMU_AUDIO_ADC_FIXED_FREQ=48000 # pid 9732
export QEMU_AUDIO_DAC_FIXED_FREQ=48000 # pid 4580
export QEMU_AUDIO_DAC_FIXED_FREQ=48000 # pid 7669
export QEMU_AUDIO_DAC_FIXED_FREQ=48000 # pid 7672
export QEMU_AUDIO_DAC_FIXED_FREQ=48000 # pid 7681
export QEMU_AUDIO_DAC_FIXED_FREQ=48000 # pid 7689
export QEMU_AUDIO_DAC_FIXED_FREQ=48000 # pid 9732
export QEMU_AUDIO_DRV=alsa # pid 4580
export QEMU_AUDIO_DRV=alsa # pid 7669
export QEMU_AUDIO_DRV=alsa # pid 7672
export QEMU_AUDIO_DRV=alsa # pid 7681
export QEMU_AUDIO_DRV=alsa # pid 7689
export QEMU_AUDIO_DRV=alsa # pid 9732
export QUIET=no # pid 7669
export QUIET=no # pid 7672
export QUIET=no # pid 7681
export QUIET=no # pid 7689
export QUIET=no # pid 9732
export runlevel=2 # pid 7669
export RUNLEVEL=2 # pid 7669
export runlevel=2 # pid 7672
export RUNLEVEL=2 # pid 7672
export runlevel=2 # pid 7681
export RUNLEVEL=2 # pid 7681
export runlevel=2 # pid 7689
export RUNLEVEL=2 # pid 7689
export runlevel=2 # pid 9732
export RUNLEVEL=2 # pid 9732
export SESSION_MANAGER=local/artoo:/tmp/.ICE-unix/6910 # pid 7669
export SESSION_MANAGER=local/artoo:/tmp/.ICE-unix/6910 # pid 7672
export SESSION_MANAGER=local/artoo:/tmp/.ICE-unix/6910 # pid 7681
export SESSION_MANAGER=local/artoo:/tmp/.ICE-unix/6910 # pid 7689
export SESSION_MANAGER=local/artoo:/tmp/.ICE-unix/6910 # pid 9732
export SHELL=/bin/bash # pid 4580
export SHELL=/bin/bash # pid 7669
export SHELL=/bin/bash # pid 7672
export SHELL=/bin/bash # pid 7681
export SHELL=/bin/bash # pid 7689
export SHELL=/bin/bash # pid 9732
export SHLVL=2 # pid 7669
export SHLVL=2 # pid 7672
export SHLVL=2 # pid 7681
export SHLVL=2 # pid 7689
export SHLVL=3 # pid 9732
export SSH_AGENT_PID=6937 # pid 7669
export SSH_AGENT_PID=6937 # pid 7672
export SSH_AGENT_PID=6937 # pid 7681
export SSH_AGENT_PID=6937 # pid 7689
export SSH_AGENT_PID=6937 # pid 9732
export SSH_AUTH_SOCK=/tmp/keyring-sZEbo7/ssh # pid 7669
export SSH_AUTH_SOCK=/tmp/keyring-sZEbo7/ssh # pid 7672
export SSH_AUTH_SOCK=/tmp/keyring-sZEbo7/ssh # pid 7681
export SSH_AUTH_SOCK=/tmp/keyring-sZEbo7/ssh # pid 7689
export SSH_AUTH_SOCK=/tmp/keyring-sZEbo7/ssh # pid 9732
export SSH_AUTH_SOCK=/tmp/ssh-HgWvto4579/agent.4579 # pid 4580
export "SSH_CLIENT=10.0.0.9 1493 22" # pid 4580
export "SSH_CONNECTION=10.0.0.9 1493 10.0.0.5 22" # pid 4580
export SSH_TTY=/dev/pts/4 # pid 4580
export STY=9731.pine # pid 9732
export "TERMCAP=SC|screen|VT 100/ANSI X3.64 virtual terminal:n :DO=E[%dB:LE=E[%dD:RI=E[%dC:UP=E[%dA:bs:bt=E[Z:n :cd=E[J:ce=E[K:cl=E[HE[J:cm=E[%i%d;%dH:ct=E[3g:n :do=^J:nd=E[C:pt:rc=E8:rs=Ec:sc=E7:st=EH:up=EM:n :le=^H:bl=^G:cr=^M:it#8:ho=E[H:nw=EE:ta=^I:is=E)0:n :li#24:co#80:am:xn:xv:LP:sr=EM:al=E[L:AL=E[%dL:n :cs=E[%i%d;%dr:dl=E[M:DL=E[%dM:dc=E[P:DC=E[%dP:n :im=E[4h:ei=E[4l:mi:IC=E[%d@:ks=E[?1hE=:n :ke=E[?1lE>:vi=E[?25l:ve=E[34hE[?25h:vs=E[34l:n :ti=E[?1049h:te=E[?1049l:us=E[4m:ue=E[24m:so=E[3m:n :se=E[23m:mb=E[5m:md=E[1m:mr=E[7m:me=E[m:ms:n :Co#8:pa#64:AF=E[3%dm:AB=E[4%dm:op=E[39;49m:AX:n :vb=Eg:G0:as=E(0:ae=E(B:n :ac=140140aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~..--++,,hhII00:n :po=E[5i:pf=E[4i:k0=E[10~:k1=EOP:k2=EOQ:k3=EOR:n :k4=EOS:k5=E[15~:k6=E[17~:k7=E[18~:k8=E[19~:n :k9=E[20~:k;=E[21~:F1=E[23~:F2=E[24~:F3=EO2P:n :F4=EO2Q:F5=EO2R:F6=EO2S:F7=E[15;2~:F8=E[17;2~:n :F9=E[18;2~:FA=E[19;2~:kb=:K2=EOE:kB=E[Z:n :*4=E[3;2~:*7=E[1;2F:#2=E[1;2H:#3=E[2;2~:#4=E[1;2D:n :%c=E[6;2~:%e=E[5;2~:%i=E[1;2C:kh=E[1~:@1=E[1~:n :kH=E[4~:@7=E[4~:kN=E[6~:kP=E[5~:kI=E[2~:kD=E[3~:n :ku=EOA:kd=EOB:kr=EOC:kl=EOD:km:" # pid 9732


Like copysshenv, the output from copyenv is suitable to copy to a file that can then be "sourced". A practical application would be to duplicate the DBUS settings from another bash process:


jdimpson@artoo:~$ env | grep DBUS
jdimpson@artoo:~$ ps auxwww | grep bash
jdimpson 520 0.0 0.1 5968 3392 pts/0 Ss Nov09 0:00 bash
jdimpson 528 0.0 0.1 5968 3444 pts/1 Ss Nov09 0:00 bash
jdimpson 533 0.0 0.1 5968 3476 pts/2 Ss Nov09 0:00 bash
jdimpson 540 0.0 0.1 5968 3428 pts/3 Ss Nov09 0:00 bash
jdimpson@artoo:~$ copyenv 533 | grep DBUS > tmp
jdimpson@artoo:~$ cat tmp
export DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-9GBQaur4oj,guid=1ce705587b2d8d0bee00c8094cd9540c
jdimpson@artoo:~$ . tmp
jdimpson@artoo:~$ env | grep DBUS
export DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-9GBQaur4oj,guid=1ce705587b2d8d0bee00c8094cd9540c
jdimpson@artoo:~$



Here's what copyenv looks like:


#!/bin/sh

# print the environmental variables of a process, given the process name or the process ID.

# copyright 2009 Jeremy D. Impson
# note: TERMCAP env var embeds newlines by putting a \, then a real newline.  this script removes the leading \ and newline, replacing it with literal \n. but it does nothing to unescaped newlines. They will break this script, or at least, generate incorrect output.

PROC=$1
if [ -z $PROC ]; then
	PROC=$$;
fi
# if $PROC isn't a process ID number, assume it's the name of the process, 
# and use pgrep or pidof to translate to process ID number.
if [ ! -e "/proc/$PROC/environ" ]; then
	if which pgrep > /dev/null; then
		#PROC=`pgrep -x -u $USER $PROC`; # less elegant but more robust to let the file permissions on the /proc/<pid>/environ file handle access control
		PROC=`pgrep -x $PROC`;
	else if which pidof > /dev/null; then
		PROC=`pidof $PROC`;
	fi;  fi
fi
RET=0;
CNT=0;
# if we had to translate the process name to ID number, then we may have found multiple processes 
# with the same name. count them.
for pid in $PROC; do
	CNT=`expr $CNT + 1`;
done
# *sigh* At this point I should just rewrite in perl.  Too many multiline hacks.
if [ $CNT -gt 1 ]; then
	PRINTPID=yes
fi

# look up the environmental variables for each process, and print them out.
for pid in $PROC; do
	if [ ! -e "/proc/$pid/environ" ]; then
		echo "Can't find process environment for $1 (/proc/$PROC/environ)" >&2 ;
		RET=1;
	else
		if [ ! -r "/proc/$pid/environ" ]; then
			echo "Can't read environment for $pid (/proc/$pid/environ)" >&2 ;
			RET=1;
		else 
			#sed -e ':a' -e '$!N;s/\\\n/\\n/;ta' -e 's/\x00/\n/g' < /proc/$pid/environ | sed -e 's/^/export "/' -e 's/$/"/' -e "s/$/ # pid $pid/";
			sed -e ':a' -e '$!N;s/\\\n/\\n/;ta' -e 's/\x00/\n/g' < /proc/$pid/environ | while read VAR; do
				if echo $VAR | egrep -q '[ 	]'; then
					echo -n export \"$VAR\"  # VAR has spaces, so print quotes
				else
					echo -n export $VAR 
				fi
				if [ -z "$PRINTPID" ]; then
					echo;
				else
					echo " # pid $pid"; # if there are more than one process, print a comment to help keep track
				fi
			done
		fi
	fi
done
exit $RET;


copyenv first tests to see if the user has supplied any argument. If not, copyenv will use it's own process ID number. This probably isn't useful except for demonstration purposes. Then, it checks the user's argument to see if it is avalid process ID number by looking up an entry in the /proc filesystem. If the numer is a valid ID, and the user has permission to look at the process' environment, this check will succeed. If it fails, copyenv assumes the user argument is a process name rather than ID number, and uses either pgrep or pidof to convert the name into one or more ID numbers.

copyenv then looks up the environvmental variables for each ID number in turn, reformatting each into a human- and shell-readable format, and printing the result. If there were more than one ID numbers, copyenv also prints the process ID that each variable value is associated with, in case that is important to you. (Makes it good for later grepping, for example.)

The value of any environmental variable may contain a space, so copyenv detects this and tries to quote the value so that the output is suitable to be sourced by a shell. It tries to detect spaces, and only qoutes them if a space is present. It should just quote all of them, but that would require detecting and escaping any embedded quotes in the variable values. I really should do that, because right now, if a variable value contains both space and embedded quotes, the output of copyenv will be invalid shell syntax.

Some common environmental variables are complex, such as the TERMCAP variable, which contains embedded newlines. copyenv does not handle this correctly. I can't think of an easy way to correct this, and I've never needed to copy the TERMCAP variable, but it would be nice to handle this correctly, for sake of completeness.
 
 
 
(Deleted comment)
peparate on April 9th, 2011 03:36 pm (UTC)
Thanks for posting, I like this blog!

cadaaten on April 14th, 2011 05:09 pm (UTC)
I don’t usually reply to posts but I will in this case.