If, in future, you ever want to track environment variable changes like this, (and without running ktrace(1)
which requires you to select some process), you use the script below which uses dtrace(1)
. The script output is not as clean as the kdump(1)
one, but, DTrace lets you scan all processes on the system.
Warning: I don't expect the method I've used--which involves trawling through the kernel's proc
structure--to work on anything other than NetBSD. I have another, generic, version which works on FreeBSD (and should also on other OSes with DTrace), but, as explained in the script, that version doesn't work on PIE/PIC executables on NetBSD.
Enjoy.
#!/bin/sh
#
# env-trace.sh - Show environment variables of (new) programs using dtrace(1).
# Warning: this script is weird: it contains 3 languages: shell, awk & dtrace.
# Modify at your own peril.
set -eu
me=${0##*/}
PAT=""
usage()
{
printf '%s: Show enviroment variables of (new) programs using DTrace.\n' $me
printf 'Usage: %s [-h] -p STR\n' $me
printf '
-h This help message.
-p STR The string to search for in environment. Eg. "PATH="
Use "=" to show all environment vars.
'
}
while getopts hp: opts
do case $opts in
h) usage
exit 0
;;
p) PAT="$OPTARG"
;;
\?) usage 1>&2
exit 1
;;
esac
done
shift $((OPTIND - 1))
if [ -z "$PAT" ]
then printf 1>&2 '%s: ERROR: empty search string\n' $me
printf 1>&2 'Try "-h" for help.\n'
exit 1
fi
# Do NOT remove "exec"--it is required for proper functioning.
#
exec sed -n -e '/^\/\* ___DTRACE_CODE_START___ \*\/$/,/^\/\* ___DTRACE_CODE_END___\*\/$/p' $0 |
sudo dtrace -q -s /dev/stdin |
awk -v PAT=$PAT '
#
# Hex string -> ASCII char
# Adapted from the Gawk Manual at https://gnu.org/software/gawk/
#
function xtochr(str, c, i, k, n, ret)
{
n = length(str)
ret = 0
for (i = 1; i <= n; i++) {
c = substr(str, i, 1)
c = tolower(c)
# index() returns 0 if c not in string,
# includes c == "0"
k = index("123456789abcdef", c)
ret = ret * 16 + k
}
return ret
}
BEGIN {
str = ""
}
{
if ($0 == " 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef") {
next # skip tracemem() header
}
if ($0 !~ /^[[:blank:]]+[[:xdigit:]]+: ([[:xdigit:]]{2} ){1,16}[[:blank:]]+(.){1,16}$/) {
print # pass-through everything else
next
}
# Convert hex dump back into ASCII and search
# for the string we want.
#
for (i = 2; i <= NF; i++) {
if (i == 18) # skip ASCII component entirely
break;
c = xtochr($i)
if (c == 0) { # end-of-string marker
if (index(str, PAT) > 0)
printf("%s\n", str)
str = ""
}
str = str sprintf("%c", c)
}
}
'
/* ___DTRACE_CODE_START___ */
/*
* On NetBSD, the standard "syscall::execve:{entry,return}" probe stuff
* glitches occasionally when copying stuff from userspace for standard
* executables, and fails completely for position-independent executables.
* So, we the "proc" provider which is _highly_ kernel specific.
*/
proc:::create
/pid != $pid/
{
printf("%d %d %s", uid, pid, execname);
/* Copy `struct ps_strings' from userspace. */
pss = (struct ps_strings *)copyin(args[0]->p_psstrp, sizeof (struct ps_strings));
/* Copy env. ptr. array from userspace. */
envc = pss->ps_nenvstr; /* no. of env. strings */
printf(" envc=%d", envc);
envp = (uintptr_t)pss->ps_envstr;
addrs = (uintptr_t *)copyin(envp, envc * sizeof (uintptr_t));
/* Copy env. string array from userspace. */
first = addrs[0]; /* addr. of first env. string */
last = addrs[envc - 1]; /* addr. of last env. string */
s = copyinstr(last); /* copy last env. string from US */
n = strlen(s) + 1; /* must include '\0' at end */
len = (last - first) + n; /* actual size of env. strings */
printf(" len=%d", len);
buf = copyin(first, len);
/*
* stringof(copyin(buf, len)); followed by printf("%S\n");
* prints the whole buffer safely when the buffer is the one
* associated with read(2) and write(2), but, stringof()
* truncates at the first '\0' in our case, so we dump
* the buffer using tracemem() and munge it back into
* ASCII using awk(1).
*/
tracemem(buf, 5000, len);
printf("\n");
}
proc:::exec-success
/pid != $pid/
{
printf("%d %d executed=%s\n\n", uid, pid, execname);
}
proc:::exit
/pid != $pid/
{
}
/* ___DTRACE_CODE_END___ */