1330559Sdteske#!/bin/sh
2330559Sdteske#-
3330559Sdteske# Copyright (c) 2014-2018 Devin Teske
4330559Sdteske# All rights reserved.
5330559Sdteske#
6330559Sdteske# Redistribution and use in source and binary forms, with or without
7330559Sdteske# modification, are permitted provided that the following conditions
8330559Sdteske# are met:
9330559Sdteske# 1. Redistributions of source code must retain the above copyright
10330559Sdteske#    notice, this list of conditions and the following disclaimer.
11330559Sdteske# 2. Redistributions in binary form must reproduce the above copyright
12330559Sdteske#    notice, this list of conditions and the following disclaimer in the
13330559Sdteske#    documentation and/or other materials provided with the distribution.
14330559Sdteske#
15330559Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16330559Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17330559Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18330559Sdteske# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19330559Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20330559Sdteske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21330559Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22330559Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23330559Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24330559Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25330559Sdteske# SUCH DAMAGE.
26330559Sdteske#
27330559Sdteske############################################################ IDENT(1)
28330559Sdteske#
29330559Sdteske# $Title: Watch processes as they trigger a particular DTrace probe $
30330559Sdteske# $FreeBSD: stable/11/cddl/usr.sbin/dwatch/dwatch 334392 2018-05-30 18:27:48Z dteske $
31330559Sdteske#
32330559Sdteske############################################################ CONFIGURATION
33330559Sdteske
34330559Sdteske#
35330559Sdteske# DTrace pragma settings
36330559Sdteske#
37330559SdteskeDTRACE_PRAGMA="
38330559Sdteske	option quiet
39330559Sdteske	option dynvarsize=16m
40330559Sdteske	option switchrate=10hz
41330559Sdteske" # END-QUOTE
42330559Sdteske
43330559Sdteske#
44330559Sdteske# Profiles
45330559Sdteske#
46330559Sdteske: ${DWATCH_PROFILES_PATH="/usr/libexec/dwatch:/usr/local/libexec/dwatch"}
47330559Sdteske
48330559Sdteske############################################################ GLOBALS
49330559Sdteske
50334392SdteskeVERSION='$Version: 1.4 $' # -V
51330559Sdteske
52330559Sdteskepgm="${0##*/}" # Program basename
53330559Sdteske
54330559Sdteske#
55330559Sdteske# Command-line arguments
56330559Sdteske#
57330559SdteskePROBE_ARG=
58330559Sdteske
59330559Sdteske#
60333617Sdteske# Command-line defaults
61333617Sdteske#
62333617Sdteske_MAX_ARGS=64		# -B num
63333617Sdteske_MAX_DEPTH=64		# -K num
64333617Sdteske
65333617Sdteske#
66330559Sdteske# Command-line options
67330559Sdteske#
68330559SdteskeCONSOLE=		# -y
69330559SdteskeCONSOLE_FORCE=		# -y
70330559Sdteske[ -t 1 ] && CONSOLE=1	# -y
71330559SdteskeCOUNT=0			# -N count
72330559SdteskeCUSTOM_DETAILS=		# -E code
73330559SdteskeCUSTOM_TEST=		# -t test
74330559SdteskeDEBUG=			# -d
75330559SdteskeDESTRUCTIVE_ACTIONS=	# -w
76333617SdteskeDEVELOPER=		# -dev
77330559SdteskeEXECNAME=		# -k name
78330559SdteskeEXECREGEX=		# -z regex
79330559SdteskeEXIT_AFTER_COMPILE=	# -e
80330559SdteskeFILTER=			# -r regex
81330559SdteskePROBE_COALESCE=		# -F
82330559SdteskeGROUP=			# -g group
83330559SdteskeJID=			# -j jail
84330559SdteskeLIST=			# -l
85330559SdteskeLIST_PROFILES=		# -Q
86333617SdteskeMAX_ARGS=$_MAX_ARGS	# -B num
87333617SdteskeMAX_DEPTH=$_MAX_DEPTH	# -K num
88330559SdteskeONELINE=		# -1
89330559SdteskeOUTPUT=			# -o file
90330559SdteskeOUTPUT_CMD=		# -O cmd
91330559SdteskePID=			# -p pid
92330559SdteskePROBE_TYPE=		# -f -m -n -P
93330559SdteskePROFILE=		# -X profile
94330559SdteskePSTREE=			# -R
95330559SdteskeQUIET=			# -q
96330559SdteskeTIMEOUT=		# -T time
97330559SdteskeTRACE=			# -x
98330559SdteskeUSER=			# -u user
99330559SdteskeUSE_PROFILE=		# -X profile
100330559SdteskeVERBOSE=		# -v
101330559Sdteske
102330559Sdteske#
103330559Sdteske# Global exit status
104330559Sdteske#
105330559SdteskeSUCCESS=0
106330559SdteskeFAILURE=1
107330559Sdteske
108330559Sdteske#
109330559Sdteske# Miscellaneous
110330559Sdteske#
111330559SdteskeACTIONS=
112330559SdteskeEVENT_DETAILS=
113330559SdteskeEVENT_TAG='printf("%d.%d %s[%d]: ",
114330559Sdteske		this->uid0, this->gid0, execname, this->pid0);'
115330559SdteskeEVENT_TEST=
116330559SdteskeFILE=
117330559SdteskeID=3
118330559SdteskeMODULE_CHECKED=
119330559SdteskePROBE=
120330559SdteskePSARGS=1
121330559SdteskeRGID=
122330559SdteskeRUID=
123330559SdteskeSUDO=
124330559Sdteskeexport SUDO_PROMPT="[sudo] Password:"
125330559SdteskeTITLE=\$Title:
126330559Sdteske
127330559Sdteske############################################################ FUNCTIONS
128330559Sdteske
129330559Sdteskeansi() { local fmt="$2 $4"; [ "$CONSOLE" ] && fmt="\\033[$1m$2\\033[$3m $4";
130330559Sdteske	shift 4; printf "$fmt\n" "$@"; }
131330559Sdteskedie() { exec >&2; [ "$*" ] && echo "$pgm:" "$@"; exit $FAILURE; }
132330559Sdteskeinfo() { [ "$QUIET" ] || ansi 35 "INFO" 39 "$@" >&2; }
133330559Sdteske
134330559Sdteskeusage()
135330559Sdteske{
136330559Sdteske	local optfmt="\t%-10s %s\n"
137330559Sdteske	exec >&2
138330559Sdteske	[ "$*" ] && printf "%s: %s\n" "$pgm" "$*"
139330559Sdteske	printf "Usage: %s [-1defFmnPqRvVwxy] [%s] [%s] [%s] [%s]\n" "$pgm" \
140330559Sdteske		"-B num" "-E code" "-g group" "-j jail"
141330559Sdteske	printf "\t      [%s] [%s] [%s] [%s] [%s] [%s]\n" \
142330559Sdteske		"-k name" "-K num" "-N count" "-o file" "-O cmd" "-p pid"
143330559Sdteske	printf "\t      [%s] [%s] [%s] [%s] [%s] [%s]\n" \
144330559Sdteske		"-r regex" "-t test" "-T time" "-u user" "-X profile" \
145330559Sdteske		"-z regex"
146330559Sdteske	printf "\t      probe[,...] [args ...]\n"
147330559Sdteske	printf "       %s -l [-fmnPqy] [-r regex] [probe ...]\n" "$pgm"
148330559Sdteske	printf "       %s -Q [-1qy] [-r regex]\n" "$pgm"
149330559Sdteske	printf "\n"
150330559Sdteske	printf "$optfmt" "-1" \
151330559Sdteske		"Print one line per process/profile (Default; disables \`-R')."
152330559Sdteske	printf "$optfmt" "-B num" \
153333617Sdteske		"Maximum process arguments to display (Default $_MAX_ARGS)."
154330559Sdteske	printf "$optfmt" "-d" \
155330559Sdteske		"Debug. Send dtrace(1) script to stdout instead of executing."
156330559Sdteske	printf "$optfmt" "-e" \
157330559Sdteske		"Exit after compiling request but prior to enabling probes."
158330559Sdteske	printf "$optfmt" "-E code" \
159330559Sdteske		"DTrace code for event details. If \`-', read from stdin."
160330559Sdteske	printf "$optfmt" "-f" \
161330559Sdteske		"Enable probe matching the specified function name."
162330559Sdteske	printf "$optfmt" "-F" \
163330559Sdteske		"Coalesce trace output by function."
164330559Sdteske	printf "$optfmt" "-g group" \
165330559Sdteske		"Group filter. Only show processes matching group name/gid."
166330559Sdteske	printf "$optfmt" "-j jail" \
167330559Sdteske		"Jail filter. Only show processes matching jail name/jid."
168330559Sdteske	printf "$optfmt" "-k name" \
169330559Sdteske		"Only show processes matching name."
170330559Sdteske	printf "$optfmt" "-K num" \
171333617Sdteske		"Maximum directory depth to display (Default $_MAX_DEPTH)."
172330559Sdteske	printf "$optfmt" "-l" \
173330559Sdteske		"List available probes on standard output and exit."
174330559Sdteske	printf "$optfmt" "-m" \
175330559Sdteske		"Enable probe matching the specified module name."
176330559Sdteske	printf "$optfmt" "-n" \
177330559Sdteske		"Enable probe matching the specified probe name."
178330559Sdteske	printf "$optfmt" "-N count" \
179330559Sdteske		"Exit after count matching entries (Default 0 for disabled)."
180330559Sdteske	printf "$optfmt" "-o file" \
181330559Sdteske		"Set output file. If \`-', the path \`/dev/stdout' is used."
182330559Sdteske	printf "$optfmt" "-O cmd" \
183330559Sdteske		"Execute cmd for each event."
184330559Sdteske	printf "$optfmt" "-p pid" \
185330559Sdteske		"Process id filter. Only show processes with matching pid."
186330559Sdteske	printf "$optfmt" "-P" \
187330559Sdteske		"Enable probe matching the specified provider name."
188330559Sdteske	printf "$optfmt" "-q" \
189330559Sdteske		"Quiet. Hide informational messages and all dtrace(1) errors."
190330559Sdteske	printf "$optfmt" "-Q" \
191330559Sdteske		"List available profiles in DWATCH_PROFILES_PATH and exit."
192330559Sdteske	printf "$optfmt" "-r regex" \
193330559Sdteske		"Filter. Only show blocks matching awk(1) regular expression."
194330559Sdteske	printf "$optfmt" "-R" \
195330559Sdteske		"Show parent, grandparent, and ancestor of process."
196330559Sdteske	printf "$optfmt" "-t test" \
197330559Sdteske		"Test clause (predicate) to limit events (Default none)."
198330559Sdteske	printf "$optfmt" "-T time" \
199330559Sdteske		"Timeout. Format is \`\#[smhd]' or simply \`\#' for seconds."
200330559Sdteske	printf "$optfmt" "-u user" \
201330559Sdteske		"User filter. Only show processes matching user name/uid."
202330559Sdteske	printf "$optfmt" "-v" \
203330559Sdteske		"Verbose. Show all errors from dtrace(1)."
204330559Sdteske	printf "$optfmt" "-V" \
205330559Sdteske		"Report dwatch version on standard output and exit."
206330559Sdteske	printf "$optfmt" "-w" \
207330559Sdteske		"Permit destructive actions (copyout*, stop, panic, etc.)."
208330559Sdteske	printf "$optfmt" "-x" \
209330559Sdteske		"Trace. Print \`<probe-id>' when a probe is triggered."
210330559Sdteske	printf "$optfmt" "-X profile" \
211330559Sdteske		"Load profile name from DWATCH_PROFILES_PATH."
212330559Sdteske	printf "$optfmt" "-y" \
213330559Sdteske		"Always treat stdout as console (enable colors/columns/etc.)."
214330559Sdteske	printf "$optfmt" "-z regex" \
215330559Sdteske		"Only show processes matching awk(1) regular expression."
216330559Sdteske	die
217330559Sdteske}
218330559Sdteske
219330559Sdteskedtrace_cmd()
220330559Sdteske{
221330559Sdteske	local status stdout
222330559Sdteske	local timeout=
223330559Sdteske
224330559Sdteske	if [ "$1" = "-t" ]; then
225330559Sdteske		shift
226330559Sdteske		[ "$TIMEOUT" ] && timeout=1
227330559Sdteske	fi
228330559Sdteske
229330559Sdteske	exec 3>&1
230330559Sdteske	stdout=3
231330559Sdteske
232330559Sdteske	#
233330559Sdteske	# Filter dtrace(1) stderr while preserving exit status
234330559Sdteske	#
235330559Sdteske	status=$(
236330559Sdteske		exec 4>&1
237330559Sdteske		to_status=4
238330559Sdteske		( trap 'echo $? >&$to_status' EXIT
239330559Sdteske			eval $SUDO ${timeout:+timeout \"\$TIMEOUT\"} dtrace \
240330559Sdteske				\"\$@\" 2>&1 ${QUIET:+2> /dev/null} >&$stdout
241330559Sdteske		) | dtrace_stderr_filter >&2
242330559Sdteske	)
243330559Sdteske
244330559Sdteske	return $status
245330559Sdteske}
246330559Sdteske
247330559Sdteskedtrace_stderr_filter()
248330559Sdteske{
249330559Sdteske	if [ "$VERBOSE" ]; then
250330559Sdteske		cat
251330559Sdteske		return
252330559Sdteske		# NOTREACHED
253330559Sdteske	fi
254330559Sdteske
255330559Sdteske	awk ' # Start awk(1) stderr-filter
256330559Sdteske	/[[:digit:]]+ drops? on CPU [[:digit:]]+/ { next }
257330559Sdteske	/failed to write to <stdout>: No such file or directory/ { next }
258330559Sdteske	/failed to write to <stdout>: Broken pipe/ { next }
259330559Sdteske	/processing aborted: Broken pipe/ { next }
260330559Sdteske	/invalid address \(0x[[:xdigit:]]+\) in action #[[:digit:]]+/ { next }
261330559Sdteske	/out of scratch space in action #[[:digit:]]+/ { next }
262330559Sdteske	/^Bus error$/ { next }
263330559Sdteske	{ print; fflush() }
264330559Sdteske	' # END-QUOTE
265330559Sdteske}
266330559Sdteske
267330559Sdteskeexpand_probe()
268330559Sdteske{
269330559Sdteske	local OPTIND=1 OPTARG flag
270330559Sdteske	local type=
271330559Sdteske
272330559Sdteske	while getopts t: flag; do
273330559Sdteske		case "$flag" in
274330559Sdteske		t) type="$OPTARG" ;;
275330559Sdteske		esac
276330559Sdteske	done
277330559Sdteske	shift $(( $OPTIND - 1 ))
278330559Sdteske
279330559Sdteske	local probe="$1"
280330559Sdteske	case "$probe" in
281330559Sdteske	*:*)
282330559Sdteske		echo "$probe"
283330559Sdteske		return $SUCCESS
284330559Sdteske		;;
285330559Sdteske	esac
286330559Sdteske
287330559Sdteske	dtrace_cmd -l | awk -v probe="$probe" -v type="$type" '
288330559Sdteske	# Start awk(1) processor
289330559Sdteske	#################################################### BEGIN
290330559Sdteske	BEGIN { getline dtrace_header }
291330559Sdteske	#################################################### FUNCTIONS
292330559Sdteske	function dump(unused1,unused2) {
293330559Sdteske		if (n) {
294330559Sdteske			if (NcF[n] == 1) f = N2F[n]
295330559Sdteske			if (NcM[n] == 1) m = N2M[n]
296330559Sdteske			if (NcP[n] == 1) p = N2P[n]
297330559Sdteske		} else if (f) {
298330559Sdteske			if (FcM[f] == 1) m = F2M[f]
299330559Sdteske			if (FcP[f] == 1) p = F2P[f]
300330559Sdteske			if (FcN[f] == 0 && found) n = "entry"
301330559Sdteske		} else if (m) {
302330559Sdteske			if (McP[m] == 1) p = M2P[m]
303330559Sdteske		}
304330559Sdteske		printf "%s:%s:%s:%s\n", p, m, f, n
305330559Sdteske		exit !found
306330559Sdteske	}
307330559Sdteske	function inFMP() { return probe in F || probe in M || probe in P }
308330559Sdteske	function inNMP() { return probe in N || probe in M || probe in P }
309330559Sdteske	function inNFP() { return probe in N || probe in F || probe in P }
310330559Sdteske	function inNFM() { return probe in N || probe in F || probe in M }
311330559Sdteske	function diva(value, peerA, peerB, peerC) {
312330559Sdteske		return value >= peerA && value >= peerB && value >= peerC
313330559Sdteske	}
314330559Sdteske	#################################################### MAIN
315330559Sdteske	type == "name" && $NF != probe { next }
316330559Sdteske	type == "function" && NF >=4 && $(NF-1) != probe { next }
317330559Sdteske	type == "module" && NF == 5 && $(NF-2) != probe { next }
318330559Sdteske	type == "provider" && $2 != probe { next }
319330559Sdteske	type || $2 == probe || $3 == probe || $4 == probe || $5 == probe {
320330559Sdteske		P[_p = $2]++
321330559Sdteske		M[_m = (NF >= 5 ? $(NF-2) : "")]++
322330559Sdteske		F[_f = (NF >= 4 ? $(NF-1) : "")]++
323330559Sdteske		N[_n = $NF]++
324330559Sdteske		if (N2F[_n] != _f) NcF[_n]++; N2F[_n] = _f
325330559Sdteske		if (N2M[_n] != _m) NcM[_n]++; N2M[_n] = _m
326330559Sdteske		if (N2P[_n] != _p) NcP[_n]++; N2P[_n] = _p
327330559Sdteske		if (_n !~ /entry|return/) {
328330559Sdteske			if (F2N[_f] != _n) FcN[_f]++
329330559Sdteske			F2N[_f] = _n
330330559Sdteske		}
331330559Sdteske		if (F2M[_f] != _m) FcM[_f]++; F2M[_f] = _m
332330559Sdteske		if (F2P[_f] != _p) FcP[_f]++; F2P[_f] = _p
333330559Sdteske		if (M2P[_m] != _p) McP[_m]++; M2P[_m] = _p
334330559Sdteske	}
335330559Sdteske	#################################################### END
336330559Sdteske	END {
337330559Sdteske		if (type == "name")     dump(n = probe, found = probe in N)
338330559Sdteske		if (type == "function") dump(f = probe, found = probe in F)
339330559Sdteske		if (type == "module")   dump(m = probe, found = probe in M)
340330559Sdteske		if (type == "provider") dump(p = probe, found = probe in P)
341330559Sdteske		if (probe in N) {
342330559Sdteske			found = 1
343330559Sdteske			if (!inFMP()) dump(n = probe)
344330559Sdteske			if (diva(F[probe], N[probe], M[probe], P[probe]))
345330559Sdteske				dump(f = probe)
346330559Sdteske			if (diva(M[probe], N[probe], F[probe], P[probe]))
347330559Sdteske				dump(m = probe)
348330559Sdteske			if (diva(P[probe], N[probe], F[probe], M[probe]))
349330559Sdteske				dump(p = probe)
350330559Sdteske			dump(n = probe) # N is the diva
351330559Sdteske		} else if (probe in F) {
352330559Sdteske			found = 1
353330559Sdteske			if (!inNMP()) dump(f = probe)
354330559Sdteske			if (diva(N[probe], F[probe], M[probe], P[probe]))
355330559Sdteske				dump(n = probe)
356330559Sdteske			if (diva(M[probe], F[probe], N[probe], P[probe]))
357330559Sdteske				dump(m = probe)
358330559Sdteske			if (diva(P[probe], F[probe], N[probe], M[probe]))
359330559Sdteske				dump(p = probe)
360330559Sdteske			dump(f = probe) # F is the diva
361330559Sdteske		} else if (probe in M) {
362330559Sdteske			found = 1
363330559Sdteske			if (!inNFP()) dump(m = probe)
364330559Sdteske			if (diva(N[probe], M[probe], F[probe], P[probe]))
365330559Sdteske				dump(n = probe)
366330559Sdteske			if (diva(F[probe], M[probe], N[probe], P[probe]))
367330559Sdteske				dump(f = probe)
368330559Sdteske			if (diva(P[probe], M[probe], N[probe], F[probe]))
369330559Sdteske				dump(p = probe)
370330559Sdteske			dump(m = probe) # M is the diva
371330559Sdteske		} else if (probe in P) {
372330559Sdteske			found = 1
373330559Sdteske			if (!inNFM()) dump(p = probe)
374330559Sdteske			if (diva(N[probe], P[probe], F[probe], M[probe]))
375330559Sdteske				dump(n = probe)
376330559Sdteske			if (diva(F[probe], P[probe], N[probe], M[probe]))
377330559Sdteske				dump(f = probe)
378330559Sdteske			if (diva(M[probe], P[probe], N[probe], F[probe]))
379330559Sdteske				dump(m = probe)
380330559Sdteske			dump(p = probe) # P is the diva
381330559Sdteske		}
382330559Sdteske		if (!found) print probe
383330559Sdteske		exit !found
384330559Sdteske	}
385330559Sdteske	' # END-QUOTE
386330559Sdteske}
387330559Sdteske
388330559Sdteskelist_probes()
389330559Sdteske{
390330559Sdteske	local OPTIND=1 OPTARG flag
391330559Sdteske	local column=0 header="PROVIDER:MODULE:FUNCTION:NAME"
392330559Sdteske	local filter= quiet= type=
393330559Sdteske
394330559Sdteske	while getopts f:qt: flag; do
395330559Sdteske		case "$flag" in
396330559Sdteske		f) filter="$OPTARG" ;;
397330559Sdteske		q) quiet=1 ;;
398330559Sdteske		t) type="$OPTARG" ;;
399330559Sdteske		esac
400330559Sdteske	done
401330559Sdteske	shift $(( $OPTIND - 1 ))
402330559Sdteske
403330559Sdteske	if [ $# -eq 0 ]; then
404330559Sdteske		case "$type" in
405330559Sdteske		provider) column=1 header="PROVIDER" ;;
406330559Sdteske		module)   column=2 header="MODULE" ;;
407330559Sdteske		function) column=3 header="FUNCTION" ;;
408330559Sdteske		name)     column=4 header="NAME" ;;
409330559Sdteske		esac
410330559Sdteske	fi
411330559Sdteske
412330559Sdteske	[ "$quiet" ] || echo "$header"
413330559Sdteske
414330559Sdteske	local arg probe=
415330559Sdteske	for arg in "$@"; do
416330559Sdteske		arg=$( expand_probe -t "$type" -- "$arg" )
417330559Sdteske		probe="$probe${probe:+, }$arg"
418330559Sdteske	done
419330559Sdteske
420330559Sdteske	dtrace_cmd -l${probe:+n "$probe"} | awk -v pattern="$(
421330559Sdteske		# Prevent backslashes from being lost
422330559Sdteske		echo "$filter" | awk 'gsub(/\\/,"&&")||1'
423330559Sdteske	)" -v want="$column" -v console="$CONSOLE" '
424330559Sdteske		BEGIN { getline dtrace_header }
425330559Sdteske		function ans(seq) { return console ? "\033[" seq "m" : "" }
426330559Sdteske		NF > 3 && $(NF-1) ~ /^#/ { next }
427330559Sdteske		!_[$0 = column[0] = sprintf("%s:%s:%s:%s",
428330559Sdteske			column[1] = $2,
429330559Sdteske			column[2] = (NF >= 5 ? $(NF-2) : ""),
430330559Sdteske			column[3] = (NF >= 4 ? $(NF-1) : ""),
431330559Sdteske			column[4] = $NF)]++ &&
432330559Sdteske			!__[$0 = column[want]]++ &&
433330559Sdteske			gsub(pattern, ans("31;1") "&" ans("39;22")) {
434330559Sdteske				print | "sort"
435330559Sdteske			}
436330559Sdteske		END { close("sort") }
437330559Sdteske	' # END-QUOTE
438330559Sdteske
439330559Sdteske	exit $SUCCESS
440330559Sdteske}
441330559Sdteske
442330559Sdteskelist_profiles()
443330559Sdteske{
444330559Sdteske	local OPTIND=1 OPTARG flag
445330559Sdteske	local filter= oneline= quiet=
446330559Sdteske
447330559Sdteske	while getopts 1f:q flag; do
448330559Sdteske		case "$flag" in
449330559Sdteske		1) oneline=1 ;;
450330559Sdteske		f) filter="$OPTARG" ;;
451330559Sdteske		q) quiet=1 ;;
452330559Sdteske		esac
453330559Sdteske	done
454330559Sdteske	shift $(( $OPTIND - 1 ))
455330559Sdteske
456330559Sdteske	# Prevent backslashes from being lost
457330559Sdteske	filter=$( echo "$filter" | awk 'gsub(/\\/,"&&")||1' )
458330559Sdteske
459330559Sdteske	# Build a list of profiles available
460330559Sdteske	local profiles
461330559Sdteske	profiles=$( { IFS=:
462330559Sdteske		for dir in $DWATCH_PROFILES_PATH; do
463330559Sdteske			[ -d "$dir" ] || continue
464330559Sdteske			for path in $dir/*; do
465330559Sdteske				[ -f "$path" ] || continue
466330559Sdteske				name="${path##*/}"
467330559Sdteske				[ "$name" = "${name%%[!0-9A-Za-z_-]*}" ] ||
468330559Sdteske					continue
469330559Sdteske				echo $name
470330559Sdteske			done
471330559Sdteske		done
472330559Sdteske	} | sort -u )
473330559Sdteske
474330559Sdteske	# Get the longest profile name
475330559Sdteske	local longest_profile_name
476330559Sdteske	longest_profile_name=$( echo "$profiles" |
477330559Sdteske		awk -v N=0 '(L = length($0)) > N { N = L } END { print N }' )
478330559Sdteske
479330559Sdteske	# Get the width of the terminal
480330559Sdteske	local max_size="$( stty size 2> /dev/null )"
481330559Sdteske	: ${max_size:=24 80}
482330559Sdteske	local max_width="${max_size#*[$IFS]}"
483330559Sdteske
484330559Sdteske	# Determine how many columns we can display
485330559Sdteske	local x=$longest_profile_name ncols=1
486330559Sdteske	[ "$QUIET" ] || x=$(( $x + 8 )) # Accommodate leading tab character
487330559Sdteske	x=$(( $x + 3 + $longest_profile_name )) # Preload end of next column
488330559Sdteske	while [ $x -lt $max_width ]; do
489330559Sdteske		ncols=$(( $ncols + 1 ))
490330559Sdteske		x=$(( $x + 3 + $longest_profile_name ))
491330559Sdteske	done
492330559Sdteske
493330559Sdteske	# Output single lines if sent to a pipe
494330559Sdteske	if [ "$oneline" ]; then
495330559Sdteske		echo "$profiles" | awk -v filter="$filter" -v cons="$CONSOLE" '
496330559Sdteske			function ans(s) { return cons ? "\033[" s "m" : "" }
497330559Sdteske			gsub(filter, ans("31;1") "&" ans("39;22"))
498330559Sdteske		' # END-QUOTE
499333617Sdteske		exit $SUCCESS
500330559Sdteske	fi
501330559Sdteske
502330559Sdteske	[ "$quiet" ] || echo PROFILES:
503330559Sdteske	echo "$profiles" | awk \
504330559Sdteske		-v colsize=$longest_profile_name \
505330559Sdteske		-v console="$CONSOLE" \
506330559Sdteske		-v ncols=$ncols \
507330559Sdteske		-v quiet="$quiet" \
508330559Sdteske		-v filter="$filter" \
509330559Sdteske	' # Begin awk(1) processor
510330559Sdteske		function ans(seq) { return console ? "\033[" seq "m" : "" }
511330559Sdteske		BEGIN {
512330559Sdteske			row_item[1] = ""
513330559Sdteske			replace = ans("31;1") "&" ans("39;22")
514330559Sdteske			ansi_offset = length(replace) - 1
515330559Sdteske		}
516330559Sdteske		function print_row()
517330559Sdteske		{
518330559Sdteske			cs = colsize + ansi_offset * \
519330559Sdteske				gsub(filter, replace, row_item[1])
520330559Sdteske			printf "%s%-*s", quiet ? "" : "\t", cs, row_item[1]
521330559Sdteske			for (i = 2; i <= cur_col; i++) {
522330559Sdteske				cs = colsize + ansi_offset * \
523330559Sdteske					gsub(filter, replace, row_item[i])
524330559Sdteske				printf "   %-*s", cs, row_item[i]
525330559Sdteske			}
526330559Sdteske			printf "\n"
527330559Sdteske		}
528330559Sdteske		$0 ~ filter {
529330559Sdteske			n++
530330559Sdteske			cur_col = ((n - 1) % ncols) + 1
531330559Sdteske			row_item[cur_col] = $0
532330559Sdteske			if (cur_col == ncols) print_row()
533330559Sdteske		}
534330559Sdteske		END { if (cur_col < ncols) print_row() }
535330559Sdteske	' # END-QUOTE
536330559Sdteske
537330559Sdteske	exit $SUCCESS
538330559Sdteske}
539330559Sdteske
540333617Sdteskeshell_escape()
541333617Sdteske{
542333617Sdteske	echo "$*" | awk 'gsub(/'\''/, "&\\\\&&")||1'
543333617Sdteske}
544333617Sdteske
545330559Sdteskeload_profile()
546330559Sdteske{
547330559Sdteske	local profile="$1"
548330559Sdteske
549330559Sdteske	[ "$profile" ] ||
550330559Sdteske		die "missing profile argument (\`$pgm -Q' to list profiles)"
551330559Sdteske
552330559Sdteske	local oldIFS="$IFS"
553330559Sdteske	local dir found=
554333617Sdteske	local ARGV=
555330559Sdteske
556333617Sdteske	[ $COUNT -gt 0 ] &&			ARGV="$ARGV -N $COUNT"
557333617Sdteske	[ "$DEBUG" ] &&				ARGV="$ARGV -d"
558333617Sdteske	[ "$DESTRUCTIVE_ACTIONS" ] &&		ARGV="$ARGV -w"
559333617Sdteske	[ "$EXIT_AFTER_COMPILE" ] &&		ARGV="$ARGV -e"
560333617Sdteske	[ "$GROUP" ] &&				ARGV="$ARGV -g $GROUP"
561333617Sdteske	[ "$JID" ] &&				ARGV="$ARGV -j $JID"
562333617Sdteske	[ $MAX_ARGS -ne $_MAX_ARGS ] &&		ARGV="$ARGV -B $MAX_ARGS"
563333617Sdteske	[ $MAX_DEPTH -ne $_MAX_DEPTH ] &&	ARGV="$ARGV -K $MAX_DEPTH"
564333617Sdteske	[ "$ONELINE" ] &&			ARGV="$ARGV -1"
565333617Sdteske	[ "$PID" ] &&				ARGV="$ARGV -p $PID"
566333617Sdteske	[ "$PSTREE" ] &&			ARGV="$ARGV -R"
567333617Sdteske	[ "$QUIET" ] &&				ARGV="$ARGV -q"
568333617Sdteske	[ "$TIMEOUT" ] &&			ARGV="$ARGV -T $TIMEOUT"
569333617Sdteske	[ "$TRACE" ] &&				ARGV="$ARGV -x"
570333617Sdteske	[ "$USER" ] &&				ARGV="$ARGV -u $USER"
571333617Sdteske	[ "$VERBOSE" ] &&			ARGV="$ARGV -v"
572333617Sdteske
573333617Sdteske	[ "$FILTER" ] &&
574333617Sdteske		ARGV="$ARGV -r '$( shell_escape "$FILTER" )'"
575333617Sdteske	[ "$EXECREGEX" ] &&
576333617Sdteske		ARGV="$ARGV -z '$( shell_escape "$EXECREGEX" )'"
577333617Sdteske	[ "$CUSTOM_DETAILS" ] &&
578333617Sdteske		ARGV="$ARGV -E '$( shell_escape "$EVENT_DETAILS" )'"
579334392Sdteske	[ "$CUSTOM_TEST" ] &&
580334392Sdteske		ARGV="$ARGV -t '$( shell_escape "$CUSTOM_TEST" )'"
581333617Sdteske	[ "$OUTPUT" ] &&
582333617Sdteske		ARGV="$ARGV -o '$( shell_escape "$OUTPUT" )'"
583333617Sdteske	[ "$OUTPUT_CMD" ] &&
584333617Sdteske		ARGV="$ARGV -O '$( shell_escape "$OUTPUT_CMD" )'"
585333617Sdteske
586333617Sdteske	case "$PROBE_TYPE" in
587333617Sdteske	provider) ARGV="$ARGV -P" ;;
588333617Sdteske	  module) ARGV="$ARGV -m" ;;
589333617Sdteske	function) ARGV="$ARGV -f" ;;
590333617Sdteske	    name) ARGV="$ARGV -n" ;;
591333617Sdteske	esac
592333617Sdteske
593330559Sdteske	IFS=:
594330559Sdteske	for dir in $DWATCH_PROFILES_PATH; do
595330559Sdteske		[ -d "$dir" ] || continue
596330559Sdteske		[ -f "$dir/$profile" ] || continue
597330559Sdteske		PROFILE="$profile" found=1
598330559Sdteske		info "Sourcing $profile profile [found in %s]" "$dir"
599330559Sdteske		. "$dir/$profile"
600330559Sdteske		break
601330559Sdteske	done
602330559Sdteske	IFS="$oldIFS"
603330559Sdteske
604330559Sdteske	[ "$found" ] ||
605330559Sdteske		die "no module named \`$profile' (\`$pgm -Q' to list profiles)"
606330559Sdteske}
607330559Sdteske
608330559Sdteskepproc()
609330559Sdteske{
610330559Sdteske	local OPTIND=1 OPTARG flag
611330559Sdteske	local P= N=0
612330559Sdteske
613330559Sdteske	while getopts P: flag; do
614330559Sdteske		case "$flag" in
615330559Sdteske		P) P="$OPTARG" ;;
616330559Sdteske		esac
617330559Sdteske	done
618330559Sdteske	shift $(( OPTIND - 1 ))
619330559Sdteske
620330559Sdteske	local proc=$1
621330559Sdteske	if [ ! "$proc" ]; then
622330559Sdteske		if [ "$P" = "0" ]; then
623330559Sdteske			proc="curthread->td_proc"
624330559Sdteske		else
625330559Sdteske			proc="this->proc ? this->proc->p_pptr : NULL"
626330559Sdteske		fi
627330559Sdteske	fi
628330559Sdteske
629330559Sdteske	awk 'NR > 1 && $0 { $0 = "\t" $0 }
630330559Sdteske		gsub(/\\\t/, "\t") || 1
631330559Sdteske	' <<-EOFPREAMBLE
632330559Sdteske	this->proc = $proc;
633330559Sdteske	this->uid$P = this->proc ? this->proc->p_ucred->cr_uid : -1;
634330559Sdteske	this->gid$P = this->proc ? this->proc->p_ucred->cr_rgid : -1;
635330559Sdteske	this->pid$P = this->proc ? this->proc->p_pid : -1;
636330559Sdteske	this->jid$P = this->proc ? this->proc->p_ucred->cr_prison->pr_id : -1;
637330559Sdteske
638330559Sdteske	this->p_args = this->proc ? this->proc->p_args : 0;
639330559Sdteske	this->ar_length = this->p_args ? this->p_args->ar_length : 0;
640330559Sdteske	this->ar_args = (char *)(this->p_args ? this->p_args->ar_args : 0);
641330559Sdteske
642330559Sdteske	this->args$P = this->arg${P}_$N = this->ar_length > 0 ?
643330559Sdteske	\	this->ar_args : stringof(this->proc->p_comm);
644330559Sdteske	this->len = this->ar_length > 0 ? strlen(this->ar_args) + 1 : 0;
645330559Sdteske	this->ar_args += this->len;
646330559Sdteske	this->ar_length -= this->len;
647330559Sdteske
648330559Sdteske	EOFPREAMBLE
649330559Sdteske
650330559Sdteske	awk -v P=$P -v MAX_ARGS=$MAX_ARGS '
651330559Sdteske		$0 { $0 = "\t" $0 }
652330559Sdteske		buf = buf $0 "\n" { }
653330559Sdteske		END {
654330559Sdteske			while (++N <= MAX_ARGS) {
655330559Sdteske				$0 = buf
656330559Sdteske				gsub(/P/, P)
657330559Sdteske				gsub(/N/, N)
658330559Sdteske				gsub(/\\\t/, "\t")
659330559Sdteske				sub(/\n$/, "")
660330559Sdteske				print
661330559Sdteske			}
662330559Sdteske		}
663330559Sdteske	' <<-EOFARGS
664330559Sdteske	this->argP_N = this->ar_length > 0 ? this->ar_args : "";
665330559Sdteske	this->argsP = strjoin(this->argsP,
666330559Sdteske	\	strjoin(this->argP_N != "" ? " " : "", this->argP_N));
667330559Sdteske	this->len = this->ar_length > 0 ? strlen(this->ar_args) + 1 : 0;
668330559Sdteske	this->ar_args += this->len;
669330559Sdteske	this->ar_length -= this->len;
670330559Sdteske
671330559Sdteske	EOFARGS
672330559Sdteske
673330559Sdteske	N=$(( $MAX_ARGS + 1 ))
674330559Sdteske	awk 'sub(/^\\\t/, "\t") || 1, $0 = "\t" $0' <<-EOFPROC
675330559Sdteske	this->arg${P}_$N = this->ar_length > 0 ? "..." : "";
676330559Sdteske	this->args$P = strjoin(this->args$P,
677330559Sdteske	\	strjoin(this->arg${P}_$N != "" ? " " : "", this->arg${P}_$N));
678330559Sdteske	EOFPROC
679330559Sdteske}
680330559Sdteske
681330559Sdteskepproc_dump()
682330559Sdteske{
683330559Sdteske	local OPTIND=1 OPTARG flag
684330559Sdteske	local verbose=
685330559Sdteske
686330559Sdteske	while getopts v flag; do
687330559Sdteske		case "$flag" in
688330559Sdteske		v) verbose=1 ;;
689330559Sdteske		esac
690330559Sdteske	done
691330559Sdteske	shift $(( $OPTIND - 1 ))
692330559Sdteske
693330559Sdteske	local P=$1
694330559Sdteske	if [ "$verbose" ]; then
695330559Sdteske		awk -v P=$P '
696330559Sdteske			BEGIN { printf "\t" }
697330559Sdteske			NR > 1 && $0 { $0 = "\t" $0 }
698330559Sdteske			buf = buf $0 "\n" { }
699330559Sdteske			END {
700330559Sdteske				$0 = buf
701330559Sdteske				if (P < 3) S = sprintf("%" 7-2*(P+1) "s", "")
702330559Sdteske				gsub(/S/, S)
703330559Sdteske				gsub(/B/, P < 3 ? "\\" : "")
704330559Sdteske				gsub(/\\\t/, "\t")
705330559Sdteske				sub(/\n$/, "")
706330559Sdteske				print
707330559Sdteske			}
708330559Sdteske		' <<-EOFPREAMBLE
709330559Sdteske		printf(" SB-+= %05d %d.%d %s\n",
710330559Sdteske		\	this->pid$P, this->uid$P, this->gid$P, this->args$P);
711330559Sdteske		EOFPREAMBLE
712330559Sdteske	else
713330559Sdteske		cat <<-EOFPREAMBLE
714330559Sdteske		printf("%s", this->args$P);
715330559Sdteske		EOFPREAMBLE
716330559Sdteske	fi
717330559Sdteske}
718330559Sdteske
719330559Sdteske############################################################ MAIN
720330559Sdteske
721330559Sdteske# If we're running as root, no need for sudo(8)
722330559Sdteske[ "$( id -u )" != 0 ] && type sudo > /dev/null 2>&1 && SUDO=sudo
723330559Sdteske
724330559Sdteske#
725330559Sdteske# Process command-line options
726330559Sdteske#
727330559Sdteskewhile getopts 1B:deE:fFg:j:k:K:lmnN:o:O:p:PqQr:Rt:T:u:vVwxX:yz: flag; do
728330559Sdteske	case "$flag" in
729330559Sdteske	1) ONELINE=1 PSTREE= ;;
730330559Sdteske	B) MAX_ARGS="$OPTARG" ;;
731330559Sdteske	d) DEBUG=1 ;;
732330559Sdteske	e) EXIT_AFTER_COMPILE=1 ;;
733330559Sdteske	E) CUSTOM_DETAILS=1
734330559Sdteske	   EVENT_DETAILS="${EVENT_DETAILS%;}"
735330559Sdteske	   [ "$EVENT_DETAILS" ] && EVENT_DETAILS="$EVENT_DETAILS;
736330559Sdteske		printf(\" \");
737330559Sdteske		" # END-QUOTE
738330559Sdteske	   # Read event code from stdin if `-' is argument
739330559Sdteske	   [ "$OPTARG" = "-" ] && OPTARG=$( cat )
740330559Sdteske	   EVENT_DETAILS="$EVENT_DETAILS$OPTARG" ;;
741330559Sdteske	f) PROBE_TYPE=function ;;
742330559Sdteske	F) PROBE_COALESCE=1 ;;
743330559Sdteske	g) GROUP="$OPTARG" ;;
744330559Sdteske	j) JID="$OPTARG" ;;
745330559Sdteske	k) EXECNAME="$EXECNAME${EXECNAME:+ }$OPTARG"
746330559Sdteske	   case "$OPTARG" in
747330559Sdteske	   \**\*) name="${OPTARG%\*}"
748330559Sdteske		predicate="strstr(execname, \"${name#\*}\") != NULL" ;;
749330559Sdteske	   \**) name="${OPTARG#\*}"
750330559Sdteske		predicate="strstr(execname, \"$name\") == (execname +"
751330559Sdteske		predicate="$predicate strlen(execname) - ${#name})" ;;
752330559Sdteske	   *\*) predicate="strstr(execname, \"${OPTARG%\*}\") == execname" ;;
753330559Sdteske	   *) predicate="execname == \"$OPTARG\""
754330559Sdteske	   esac
755330559Sdteske	   EVENT_TEST="$predicate${EVENT_TEST:+ ||
756330559Sdteske		($EVENT_TEST)}" ;;
757330559Sdteske	K) MAX_DEPTH="$OPTARG" ;;
758330559Sdteske	l) LIST=1 ;;
759330559Sdteske	m) PROBE_TYPE=module ;;
760330559Sdteske	n) PROBE_TYPE=name ;;
761330559Sdteske	N) COUNT="$OPTARG" ;;
762330559Sdteske	o) OUTPUT="$OPTARG" ;;
763330559Sdteske	O) OUTPUT_CMD="$OPTARG" ;;
764330559Sdteske	p) PID="$OPTARG" ;;
765330559Sdteske	P) PROBE_TYPE=provider ;;
766330559Sdteske	q) QUIET=1 ;;
767330559Sdteske	Q) LIST_PROFILES=1 ;;
768330559Sdteske	r) FILTER="$OPTARG" ;;
769330559Sdteske	R) PSTREE=1 ;;
770330559Sdteske	t) CUSTOM_TEST="${CUSTOM_TEST:+($CUSTOM_TEST) && }$OPTARG" ;;
771330559Sdteske	T) TIMEOUT="$OPTARG" ;;
772330559Sdteske	u) USER="$OPTARG" ;;
773330559Sdteske	v) VERBOSE=1 ;;
774330559Sdteske	V) vers="${VERSION#\$*[:\$]}"
775330559Sdteske	   vers="${vers% \$}"
776330559Sdteske	   printf "%s: %s\n" "$pgm" "${vers# }"
777330559Sdteske	   exit ;;
778330559Sdteske	w) DESTRUCTIVE_ACTIONS=1 ;;
779330559Sdteske	x) TRACE=1 ;;
780330559Sdteske	X) USE_PROFILE=1 PROFILE="$OPTARG" ;;
781330559Sdteske	y) CONSOLE=1 CONSOLE_FORCE=1 ;;
782330559Sdteske	z) EXECREGEX="$OPTARG" ;;
783330559Sdteske	*) usage
784330559Sdteske	   # NOTREACHED
785330559Sdteske	esac
786330559Sdteskedone
787330559Sdteskeshift $(( $OPTIND - 1 ))
788330559Sdteske
789330559Sdteske#
790330559Sdteske# List probes if `-l' was given
791330559Sdteske#
792330559Sdteske[ "$LIST" ] &&
793330559Sdteske	list_probes -f "$FILTER" ${QUIET:+-q} -t "$PROBE_TYPE" -- "$@"
794330559Sdteske	# NOTREACHED
795330559Sdteske
796330559Sdteske#
797330559Sdteske# List profiles if `-Q' was given
798330559Sdteske#
799330559Sdteske[ "$LIST_PROFILES" ] &&
800330559Sdteske	list_profiles ${ONELINE:+-1} -f "$FILTER" ${QUIET:+-q}
801330559Sdteske	# NOTREACHED
802330559Sdteske
803330559Sdteske#
804330559Sdteske# Validate number of arguments
805330559Sdteske#
806330559Sdteskeif [ ! "$PROFILE" ]; then
807330559Sdteske	# If not given `-X profile' then a probe argument is required
808330559Sdteske	[ $# -gt 0 ] || usage # NOTREACHED
809330559Sdteskefi
810330559Sdteske
811330559Sdteske#
812330559Sdteske# Validate `-N count' option argument
813330559Sdteske#
814330559Sdteskecase "$COUNT" in
815330559Sdteske"") usage "-N option requires a number argument" ;; # NOTREACHED
816330559Sdteske*[!0-9]*) usage "-N argument must be a number" ;; # NOTREACHED
817330559Sdteskeesac
818330559Sdteske
819330559Sdteske#
820330559Sdteske# Validate `-B num' option argument
821330559Sdteske#
822330559Sdteskecase "$MAX_ARGS" in
823330559Sdteske"") usage "-B option requires a number argument" ;; # NOTREACHED
824330559Sdteske*[!0-9]*) usage "-B argument must be a number" ;; # NOTREACHED
825330559Sdteskeesac
826330559Sdteske
827330559Sdteske#
828330559Sdteske# Validate `-K num' option argument
829330559Sdteske#
830330559Sdteskecase "$MAX_DEPTH" in
831330559Sdteske"") usage "-K option requires a number argument" ;; # NOTREACHED
832330559Sdteske*[!0-9]*) usage "-K argument must be a number" ;; # NOTREACHED
833330559Sdteskeesac
834330559Sdteske
835330559Sdteske#
836330559Sdteske# Validate `-j jail' option argument
837330559Sdteske#
838330559Sdteskecase "$JID" in
839330559Sdteske"") : fall through ;;
840330559Sdteske*[!0-9]*) JID=$( jls -j "$JID" jid ) || exit ;;
841330559Sdteskeesac
842330559Sdteske
843330559Sdteske#
844330559Sdteske# Validate `-u user' option argument
845330559Sdteske#
846330559Sdteskecase "$USER" in
847330559Sdteske"") : fall through ;;
848330559Sdteske*[![:alnum:]_-]*) RUID="$USER" ;;
849330559Sdteske*[!0-9]*) RUID=$( id -u "$USER" 2> /dev/null ) || die "No such user: $USER" ;;
850330559Sdteske*) RUID=$USER
851330559Sdteskeesac
852330559Sdteske
853330559Sdteske#
854330559Sdteske# Validate `-g group' option argument
855330559Sdteske#
856330559Sdteskecase "$GROUP" in
857330559Sdteske"") : fall-through ;;
858330559Sdteske*[![:alnum:]_-]*) RGID="$GROUP" ;;
859330559Sdteske*[!0-9]*)
860330559Sdteske	RGID=$( getent group | awk -F: -v group="$GROUP" '
861330559Sdteske		$1 == group { print $3; exit found=1 }
862330559Sdteske		END { exit !found }
863330559Sdteske	' ) || die "No such group: $GROUP" ;;
864330559Sdteske*) RGID=$GROUP
865330559Sdteskeesac
866330559Sdteske
867330559Sdteske#
868330559Sdteske# Expand probe argument into probe(s)
869330559Sdteske#
870330559Sdteskecase "$1" in
871330559Sdteske-*) : Assume dtrace options such as "-c cmd" or "-p pid" ;; # No probe(s) given
872330559Sdteske*)
873330559Sdteske	PROBE_ARG="$1"
874330559Sdteske	shift
875330559Sdteskeesac
876330559Sdteskeif [ "$PROBE_ARG" ]; then
877330559Sdteske	oldIFS="$IFS"
878330559Sdteske	IFS="$IFS,"
879330559Sdteske	for arg in $PROBE_ARG; do
880330559Sdteske		arg=$( expand_probe -t "$PROBE_TYPE" -- "$arg" )
881330559Sdteske		PROBE="$PROBE${PROBE:+, }$arg"
882330559Sdteske	done
883330559Sdteske	IFS="$oldIFS"
884330559Sdteskefi
885330559Sdteske
886330559Sdteske#
887333617Sdteske# Developer switch
888333617Sdteske#
889333617Sdteske[ "$DEBUG" -a "$EXIT_AFTER_COMPILE" -a "$VERBOSE" ] && DEVELOPER=1 DEBUG=
890333617Sdteske
891333617Sdteske#
892330559Sdteske# Set default event details if `-E code' was not given
893330559Sdteske#
894330559Sdteske[ "$CUSTOM_DETAILS" ] || EVENT_DETAILS=$( pproc_dump 0 )
895330559Sdteske
896330559Sdteske#
897330559Sdteske# Load profile if given `-X profile'
898330559Sdteske#
899330559Sdteske[ "$USE_PROFILE" ] && load_profile "$PROFILE"
900330559Sdteske[ "$PROBE" ] || die "PROBE not defined by profile and none given as argument"
901330559Sdteske
902330559Sdteske#
903330559Sdteske# Show the user what's being watched
904330559Sdteske#
905333617Sdteske[ "$DEBUG$EXIT_AFTER_COMPILE" ] || info "Watching '$PROBE' ..."
906330559Sdteske
907330559Sdteske#
908330559Sdteske# Header for watched probe entry
909330559Sdteske#
910330559Sdteskecase "$PROBE" in
911330559Sdteske*,*) : fall-through ;;
912330559Sdteske*:execve:entry|execve:entry)
913330559Sdteske	ACTIONS=$( awk 'gsub(/\\\t/, "\t") || 1' <<-EOF
914330559Sdteske		$PROBE /* probe ID $ID */
915330559Sdteske		{${TRACE:+
916330559Sdteske		\	printf("<$ID>");}
917330559Sdteske		\	this->caller_execname = execname;
918330559Sdteske		}
919330559Sdteske		EOF
920330559Sdteske	)
921330559Sdteske	PROBE="${PROBE%entry}return"
922330559Sdteske	ID=$(( $ID + 1 ))
923330559Sdteske	EVENT_TEST="execname != this->caller_execname${EVENT_TEST:+ &&
924330559Sdteske		($EVENT_TEST)}"
925330559Sdteske	EVENT_TAG='printf("%d.%d %s[%d]: ",
926330559Sdteske		this->uid1, this->gid1, this->caller_execname, this->pid1);'
927330559Sdteske	;;
928330559Sdteskeesac
929330559Sdteske
930330559Sdteske#
931330559Sdteske# Jail clause/predicate
932330559Sdteske#
933330559Sdteskeif [ "$JID" ]; then
934330559Sdteske	prison_id="curthread->td_proc->p_ucred->cr_prison->pr_id"
935330559Sdteske	EVENT_TEST="$prison_id == $JID${EVENT_TEST:+ &&
936330559Sdteske		($EVENT_TEST)}"
937330559Sdteskefi
938330559Sdteske
939330559Sdteske#
940330559Sdteske# Custom test clause/predicate
941330559Sdteske#
942330559Sdteskeif [ "$CUSTOM_TEST" ]; then
943330559Sdteske	case "$EVENT_TEST" in
944330559Sdteske	"") EVENT_TEST="$CUSTOM_TEST" ;;
945330559Sdteske	 *) EVENT_TEST="$EVENT_TEST &&
946330559Sdteske		($CUSTOM_TEST)"
947330559Sdteske	esac
948330559Sdteskefi
949330559Sdteske
950330559Sdteske#
951330559Sdteske# Make sure dynamic code has trailing semi-colons if non-NULL
952330559Sdteske#
953330559SdteskeEVENT_TAG="${EVENT_TAG%;}${EVENT_TAG:+;}"
954330559SdteskeEVENT_DETAILS="${EVENT_DETAILS%;}${EVENT_DETAILS:+;}"
955330559Sdteske
956330559Sdteske#
957330559Sdteske# DTrace script
958330559Sdteske#
959330559Sdteske# If `-d' is given, script is sent to stdout for debugging
960330559Sdteske# If `-c count", `-g group', `-r regex', or `-u user' is given, run script with
961330559Sdteske# dtrace and send output to awk(1) post-processor (making sure to preserve the
962330559Sdteske# exit code returned by dtrace invocation). Otherwise, simply run script with
963330559Sdteske# dtrace and then exit.
964330559Sdteske#
965330559Sdteskeexec 9<<EOF
966330559Sdteske$PROBE /* probe ID 2 */
967330559Sdteske{${TRACE:+
968330559Sdteske	printf("<2>");
969330559Sdteske}
970330559Sdteske	/*
971330559Sdteske	 * Examine process, parent process, and grandparent process details
972330559Sdteske	 */
973330559Sdteske
974330559Sdteske	/******************* CURPROC *******************/
975330559Sdteske
976330559Sdteske	$( pproc -P0 )
977330559Sdteske
978330559Sdteske	/******************* PPARENT *******************/
979330559Sdteske
980330559Sdteske	$( if [ "$PSTREE" ]; then pproc -P1; else echo -n \
981330559Sdteske	"this->proc = this->proc ? this->proc->p_pptr : NULL;
982330559Sdteske	this->pid1 = this->proc ? this->proc->p_pid : -1;
983330559Sdteske	this->uid1 = this->proc ? this->proc->p_ucred->cr_uid : -1;
984330559Sdteske	this->gid1 = this->proc ? this->proc->p_ucred->cr_rgid : -1;
985330559Sdteske	this->jid1 = this->proc ? this->proc->p_ucred->cr_prison->pr_id : -1;"
986330559Sdteske	fi )
987330559Sdteske
988330559Sdteske	/******************* GPARENT *******************/
989330559Sdteske
990330559Sdteske	$( [ "$PSTREE" ] && pproc -P2 )
991330559Sdteske
992330559Sdteske	/******************* APARENT *******************/
993330559Sdteske
994330559Sdteske	$( [ "$PSTREE" ] && pproc -P3 )
995330559Sdteske}
996330559SdteskeEOF
997330559SdteskePSARGS_ACTION=$( cat <&9 )
998330559Sdteske[ "$OUTPUT" -a ! "$CONSOLE_FORCE" ] && CONSOLE=
999330559Sdteske{
1000330559Sdteske	if [ "$DEBUG" ]; then
1001330559Sdteske		# Send script to stdout
1002330559Sdteske		cat
1003330559Sdteske		exit
1004330559Sdteske	fi
1005330559Sdteske
1006330559Sdteske	if [ "$CUSTOM_TEST$EXECNAME$JID$OUTPUT$TIMEOUT$TRACE$VERBOSE" -a \
1007330559Sdteske	    ! "$QUIET" ]
1008330559Sdteske	then
1009330559Sdteske		msg=Setting
1010330559Sdteske		[ "$CUSTOM_TEST" ] && msg="$msg test: $CUSTOM_TEST"
1011330559Sdteske		[ "$EXECNAME" ] && msg="$msg execname: $EXECNAME"
1012330559Sdteske		[ "$JID" ] && msg="$msg jid: $JID"
1013330559Sdteske		[ "$OUTPUT" ] && msg="$msg output: $OUTPUT"
1014330559Sdteske		[ "$TIMEOUT" ] && msg="$msg timeout: $TIMEOUT"
1015330559Sdteske		[ "$TRACE" ] && msg="$msg trace: $TRACE"
1016330559Sdteske		[ "$VERBOSE" ] && msg="$msg verbose: $VERBOSE"
1017330559Sdteske		info "$msg"
1018330559Sdteske	fi
1019330559Sdteske
1020330559Sdteske	exec 3>&1
1021330559Sdteske	console_stdout=3
1022330559Sdteske
1023333617Sdteske	#
1024333617Sdteske	# Developer debugging aide
1025333617Sdteske	#
1026333617Sdteske	if [ "$DEVELOPER" ]; then
1027333617Sdteske		#
1028333617Sdteske		# Run, capture the error line, and focus it
1029333617Sdteske		#
1030333617Sdteske		# Example error text to capture line number from:
1031333617Sdteske		# 	dtrace: failed to compile script /dev/stdin: line 669: ...
1032333617Sdteske		#
1033333617Sdteske		errline=
1034333617Sdteske		stdin_buf=$( cat )
1035333617Sdteske		stderr_buf=$( echo "$stdin_buf" |
1036333617Sdteske			dtrace_cmd -t -es /dev/stdin "$@" 2>&1 > /dev/null )
1037333617Sdteske		status=$?
1038333617Sdteske		if [ "$stderr_buf" ]; then
1039333617Sdteske			errline=$( echo "$stderr_buf" | awk '
1040333617Sdteske				BEGIN {
1041333617Sdteske					ti = "\033[31m"
1042333617Sdteske					te = "\033[39m"
1043333617Sdteske				}
1044333617Sdteske				{ line = $0 }
1045333617Sdteske				sub(/.*: line /, "") && sub(/:.*/, "") {
1046333617Sdteske					print # to errline
1047333617Sdteske					sub("line " $0, ti "&" te, line)
1048333617Sdteske				}
1049333617Sdteske				{ print line > "/dev/stderr" }
1050333617Sdteske			' 2>&3 )
1051333617Sdteske		fi
1052333617Sdteske		if  [ "$errline" ]; then
1053333617Sdteske			echo "$stdin_buf" | awk -v line="${errline%%[^0-9]*}" '
1054333617Sdteske				BEGIN {
1055333617Sdteske					start = line < 10 ? 1 : line - 10
1056333617Sdteske					end = line + 10
1057333617Sdteske					slen = length(sprintf("%u", start))
1058333617Sdteske					elen = length(sprintf("%u", end))
1059333617Sdteske					N = elen > slen ? elen : slen
1060333617Sdteske					ti[line] = "\033[31m"
1061333617Sdteske					te[line] = "\033[39m"
1062333617Sdteske					fmt = "%s%*u %s%s\n"
1063333617Sdteske				}
1064333617Sdteske				NR < start { next }
1065333617Sdteske				NR == start, NR == end {
1066333617Sdteske					printf(fmt, ti[NR], N, NR, $0, te[NR])
1067333617Sdteske				}
1068333617Sdteske				NR > end { exit }
1069333617Sdteske			' # END-QUOTE
1070333617Sdteske		fi
1071333617Sdteske		exit $status
1072333617Sdteske	fi
1073333617Sdteske
1074330559Sdteske	if [ $COUNT -eq 0 -a ! "$EXECREGEX$FILTER$GROUP$OUTPUT_CMD$PID$USER" ]
1075330559Sdteske	then
1076330559Sdteske		case "$OUTPUT" in
1077330559Sdteske		-) output_path=/dev/stdout ;;
1078330559Sdteske		*) output_path="$OUTPUT"
1079330559Sdteske		esac
1080330559Sdteske
1081330559Sdteske		# Run script without pipe to awk post-processor
1082330559Sdteske		dtrace_cmd -t \
1083330559Sdteske			${DESTRUCTIVE_ACTIONS:+-w} \
1084330559Sdteske			${EXIT_AFTER_COMPILE:+-e} \
1085330559Sdteske			${OUTPUT:+-o "$output_path"} \
1086330559Sdteske			-s /dev/stdin \
1087330559Sdteske			"$@"
1088330559Sdteske		exit
1089330559Sdteske	fi
1090330559Sdteske
1091330559Sdteske	# Prevent backslashes from being lost
1092330559Sdteske	FILTER=$( echo "$FILTER" | awk 'gsub(/\\/,"&&")||1' )
1093330559Sdteske	EXECREGEX=$( echo "$EXECREGEX" | awk 'gsub(/\\/,"&&")||1' )
1094330559Sdteske
1095330559Sdteske	if [ ! "$QUIET" ]; then
1096330559Sdteske		msg=Filtering
1097330559Sdteske		[ "$EXECREGEX" ] && msg="$msg execregex: $EXECREGEX"
1098330559Sdteske		[ "$FILTER" ] && msg="$msg filter: $FILTER"
1099330559Sdteske		[ "$GROUP" ] && msg="$msg group: $GROUP"
1100330559Sdteske		[ "$OUTPUT_CMD" ] && msg="$msg cmd: $OUTPUT_CMD"
1101330559Sdteske		[ "$PID" ] && msg="$msg pid: $PID"
1102330559Sdteske		[ "$USER" ] && msg="$msg user: $USER"
1103330559Sdteske		[ $COUNT -gt 0 ] && msg="$msg count: $COUNT"
1104330559Sdteske		info "$msg"
1105330559Sdteske	fi
1106330559Sdteske
1107330559Sdteske	#
1108330559Sdteske	# Send script output to post-processor for filtering
1109330559Sdteske	#
1110330559Sdteske	status=$(
1111330559Sdteske		exec 4>&1
1112330559Sdteske		to_status=4
1113330559Sdteske		( exec 5>&1; to_dtrace_stderr_filter=5; (
1114330559Sdteske			trap 'echo $? >&$to_status' EXIT
1115330559Sdteske			eval $SUDO ${TIMEOUT:+timeout \"\$TIMEOUT\"} dtrace \
1116330559Sdteske				${EXIT_AFTER_COMPILE:+-e} \
1117330559Sdteske				${DESTRUCTIVE_ACTIONS:+-w} \
1118330559Sdteske				-s /dev/stdin \
1119330559Sdteske				\"\$@\" \
1120330559Sdteske				2>&$to_dtrace_stderr_filter \
1121330559Sdteske				${QUIET:+2> /dev/null}
1122330559Sdteske		) | $SUDO awk \
1123330559Sdteske			-v cmd="$OUTPUT_CMD" \
1124330559Sdteske			-v console="$CONSOLE" \
1125330559Sdteske			-v count=$COUNT \
1126330559Sdteske			-v execregex="$EXECREGEX" \
1127330559Sdteske			-v filter="$FILTER" \
1128330559Sdteske			-v gid="$RGID" \
1129330559Sdteske			-v output="$OUTPUT" \
1130330559Sdteske			-v pid="$PID" \
1131330559Sdteske			-v pstree=$PSTREE \
1132330559Sdteske			-v quiet=$QUIET \
1133330559Sdteske			-v tty=$( ps -o tty= -p $$ ) \
1134330559Sdteske			-v uid="$RUID" \
1135330559Sdteske		' # Start awk(1) post-processor
1136330559Sdteske		############################################ BEGIN
1137330559Sdteske		BEGIN {
1138330559Sdteske			true = 1
1139330559Sdteske			ansi = "(\\033\\[[[:digit:];]+m)?"
1140330559Sdteske			num = year = day = "[[:digit:]]+"
1141330559Sdteske			month = "[[:alpha:]]+"
1142330559Sdteske			date = year " " month " +" day
1143330559Sdteske			time = "[012][0-9]:[0-5][0-9]:[0-5][0-9]"
1144330559Sdteske			date_time = ansi date " +" time ansi
1145330559Sdteske			name1 = "[^\\[]*"
1146330559Sdteske			name2 = "[^\\n]*"
1147330559Sdteske			if (output == "-")
1148330559Sdteske				output = "/dev/stdout"
1149330559Sdteske
1150330559Sdteske			#
1151330559Sdteske			# Field definitions
1152330559Sdteske			#
1153330559Sdteske			nexecmatches = 2
1154330559Sdteske			execstart[1] = sprintf( \
1155330559Sdteske				"^(%s) (%s)\\.(%s) (%s)\\[(%s)\\]: ",
1156330559Sdteske				date_time, num, num, name1, num)
1157330559Sdteske			execstart[2] = sprintf( \
1158330559Sdteske				"\\n +\\\\?-\\+= (%s) (%s)\\.(%s) ",
1159330559Sdteske				num, num, num)
1160330559Sdteske			npidmatches = 2
1161330559Sdteske			pidstart[1] = sprintf("^(%s) (%s)\\.(%s) (%s)\\[",
1162330559Sdteske				date_time, num, num, name1)
1163330559Sdteske			pidstart[2] = "\\n +\\\\?-\\+= "
1164330559Sdteske			pidpreen[2] = "^0*"
1165330559Sdteske			piddeflt[2] = "0"
1166330559Sdteske			ngidmatches = 2
1167330559Sdteske			gidstart[1] = sprintf("^(%s) (%s)\\.", date_time, num)
1168330559Sdteske			gidstart[2] = sprintf("\\n +\\\\?-\\+= (%s) (%s)\\.",
1169330559Sdteske				ansi num ansi, num)
1170330559Sdteske			nuidmatches = 2
1171330559Sdteske			uidstart[1] = sprintf("^(%s) ", date_time)
1172330559Sdteske			uidstart[2] = sprintf("\\n +\\\\?-\\+= (%s) ",
1173330559Sdteske				ansi num ansi)
1174330559Sdteske		}
1175330559Sdteske		############################################ FUNCTIONS
1176330559Sdteske		function strip(s) { gsub(/\033\[[0-9;]*m/, "", s); return s }
1177330559Sdteske		function esc(str) { gsub(/'\''/, "&\\\\&&", str); return str }
1178330559Sdteske		function arg(str) { return "'\''" esc(str) "'\''" }
1179330559Sdteske		function env(var, str) { return var "=" arg(str) " " }
1180330559Sdteske		function ans(seq) { return console ? "\033[" seq "m" : "" }
1181330559Sdteske		function runcmd() {
1182330559Sdteske			return system(sprintf("%s/bin/sh -c %s",
1183330559Sdteske				env("TAG", strip(tag)) \
1184330559Sdteske					env("DETAILS", strip(details)),
1185330559Sdteske				arg(cmd)))
1186330559Sdteske		}
1187330559Sdteske		function filter_block() {
1188330559Sdteske			if (length(lines) < 1) return 0
1189330559Sdteske			block_match = 0
1190330559Sdteske			newstr = ""
1191330559Sdteske			start = 1
1192330559Sdteske			if (match(lines, "^(" date_time ") ")) {
1193330559Sdteske				newstr = newstr substr(lines, 1,
1194330559Sdteske					RSTART + RLENGTH - 1)
1195330559Sdteske				start = RSTART + RLENGTH
1196330559Sdteske			}
1197330559Sdteske			replace = ans("31;1") "&" ans("39;22")
1198330559Sdteske			workstr = substr(lines, start)
1199330559Sdteske			if (gsub(filter, replace, workstr)) block_match = 1
1200330559Sdteske			lines = newstr workstr
1201330559Sdteske			return block_match
1202330559Sdteske		}
1203330559Sdteske		function filter_field(startre, fieldre, matchre, isword,
1204330559Sdteske			preenre, defaultstr)
1205330559Sdteske		{
1206330559Sdteske			if (length(lines) < 1) return 0
1207330559Sdteske			field_match = 0
1208330559Sdteske			newstr = ""
1209330559Sdteske			start = 1
1210330559Sdteske			while ((workstr = substr(lines, start)) &&
1211330559Sdteske				(workstr ~ (startre fieldre)))
1212330559Sdteske			{
1213330559Sdteske				match(workstr, startre)
1214330559Sdteske				start += end = RSTART + RLENGTH - 1
1215330559Sdteske				newstr = newstr substr(workstr, 1, end)
1216330559Sdteske				workstr = substr(workstr, end + 1)
1217330559Sdteske				match(workstr, fieldre)
1218330559Sdteske				start += end = RSTART + RLENGTH - 1
1219330559Sdteske				field = matchstr = substr(workstr, 1, end)
1220330559Sdteske				sub(preenre, "", matchstr)
1221330559Sdteske				if (!matchstr) matchstr = defaultstr
1222330559Sdteske				if (isword) {
1223330559Sdteske					if (match(matchstr, matchre) &&
1224330559Sdteske						RSTART == 1 &&
1225330559Sdteske						RLENGTH == length(matchstr)) {
1226330559Sdteske						field_match = 1
1227330559Sdteske						field = ans(7) field ans(27)
1228330559Sdteske					}
1229330559Sdteske				} else {
1230330559Sdteske					replace = ans(7) "&" ans(27)
1231330559Sdteske					if (gsub(matchre, replace, matchstr)) {
1232330559Sdteske						field_match = 1
1233330559Sdteske						field = matchstr
1234330559Sdteske					}
1235330559Sdteske				}
1236330559Sdteske				newstr = newstr field
1237330559Sdteske			}
1238330559Sdteske			lines = newstr workstr
1239330559Sdteske			return field_match
1240330559Sdteske		}
1241330559Sdteske		function dump() {
1242330559Sdteske			lines = block
1243330559Sdteske			block = ""
1244330559Sdteske			found = 0
1245330559Sdteske			if (execregex != "") {
1246330559Sdteske				for (n = 1; n <= nexecmatches; n++)
1247330559Sdteske					if (filter_field(execstart[n], name2,
1248330559Sdteske						execregex)) found = 1
1249330559Sdteske				if (!found) return
1250330559Sdteske			}
1251330559Sdteske			if (pid != "") {
1252330559Sdteske				for (n = 1; n <= npidmatches; n++)
1253330559Sdteske					if (filter_field(pidstart[n], num, pid,
1254330559Sdteske						true, pidpreen[n],
1255330559Sdteske						piddeflt[n])) found = 1
1256330559Sdteske				if (!found) return
1257330559Sdteske			}
1258330559Sdteske			if (gid != "") {
1259330559Sdteske				for (n = 1; n <= ngidmatches; n++)
1260330559Sdteske					if (filter_field(gidstart[n], num,
1261330559Sdteske						gid, true)) found = 1
1262330559Sdteske				if (!found) return
1263330559Sdteske			}
1264330559Sdteske			if (uid != "") {
1265330559Sdteske				for (n = 1; n <= nuidmatches; n++)
1266330559Sdteske					if (filter_field(uidstart[n], num,
1267330559Sdteske						uid, true)) found = 1
1268330559Sdteske				if (!found) return
1269330559Sdteske			}
1270330559Sdteske			if (filter != "" && !filter_block()) return
1271330559Sdteske			if (lines) {
1272330559Sdteske				stdout = 1
1273330559Sdteske				if (output) {
1274330559Sdteske					stdout = 0
1275330559Sdteske					if (!console) lines = strip(lines)
1276330559Sdteske					print lines > output
1277330559Sdteske				} else if (cmd) {
1278330559Sdteske					if (!quiet) print lines
1279330559Sdteske					tag = details = lines
1280330559Sdteske					sub(/: .*/, "", tag)
1281330559Sdteske					sub(/.*: /, "", details)
1282330559Sdteske					if (!console) tag = strip(tag)
1283330559Sdteske					runcmd()
1284330559Sdteske				} else print lines
1285330559Sdteske			}
1286330559Sdteske			fflush()
1287330559Sdteske			++matches
1288330559Sdteske		}
1289330559Sdteske		############################################ MAIN
1290330559Sdteske		{ block = (block ? block "\n" : block) $0 }
1291330559Sdteske		!pstree { dump() }
1292330559Sdteske		$0 ~ sprintf("^%6s\\\\-\\+= %s ", "", num) { dump() }
1293330559Sdteske		count && matches >= count { exit }
1294330559Sdteske		############################################ END
1295330559Sdteske		END {
1296330559Sdteske			dump()
1297330559Sdteske			system(sprintf("pkill -t %s dtrace %s", tty,
1298330559Sdteske				quiet ? "2> /dev/null" : ""))
1299330559Sdteske		}
1300330559Sdteske		' >&$console_stdout ) | dtrace_stderr_filter >&2
1301330559Sdteske	) # status
1302330559Sdteske	exit $status
1303330559Sdteske
1304330559Sdteske} <<EOF
1305330559Sdteske#!/usr/sbin/dtrace -s
1306330559Sdteske/* -
1307330559Sdteske * Copyright (c) 2014-2018 Devin Teske <dteske@FreeBSD.org>
1308330559Sdteske * All rights reserved.
1309330559Sdteske * Redistribution and use in source and binary forms, with or without
1310330559Sdteske * modification, are permitted provided that the following conditions
1311330559Sdteske * are met:
1312330559Sdteske * 1. Redistributions of source code must retain the above copyright
1313330559Sdteske *    notice, this list of conditions and the following disclaimer.
1314330559Sdteske * 2. Redistributions in binary form must reproduce the above copyright
1315330559Sdteske *    notice, this list of conditions and the following disclaimer in the
1316330559Sdteske *    documentation and/or other materials provided with the distribution.
1317330559Sdteske *
1318330559Sdteske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \`\`AS IS'' AND
1319330559Sdteske * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1320330559Sdteske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1321330559Sdteske * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1322330559Sdteske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1323330559Sdteske * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1324330559Sdteske * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1325330559Sdteske * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1326330559Sdteske * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1327330559Sdteske * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1328330559Sdteske * SUCH DAMAGE.
1329330559Sdteske *
1330330559Sdteske * $TITLE dtrace(1) script to log process(es) triggering $PROBE $
1331330559Sdteske * \$FreeBSD: stable/11/cddl/usr.sbin/dwatch/dwatch 334392 2018-05-30 18:27:48Z dteske $
1332330559Sdteske */
1333330559Sdteske
1334330559Sdteske$( echo "$DTRACE_PRAGMA" | awk '
1335330559Sdteske	!/^[[:space:]]*(#|$)/, sub(/^[[:space:]]*/, "#pragma D ")||1
1336330559Sdteske' )
1337330559Sdteske
1338330559Sdteskeint console;
1339330559Sdteske
1340330559Sdteskedtrace:::BEGIN { console = ${CONSOLE:-0} } /* probe ID 1 */
1341330559Sdteske
1342330559Sdteske/*********************************************************/
1343330559Sdteske
1344330559Sdteske${PSARGS:+$PSARGS_ACTION}
1345330559Sdteske${ACTIONS:+
1346330559Sdteske/*********************************************************/
1347330559Sdteske
1348330559Sdteske$ACTIONS
1349330559Sdteske}
1350330559Sdteske/*********************************************************/
1351330559Sdteske
1352334392Sdteske$PROBE${EVENT_TEST:+ / $EVENT_TEST /} /* probe ID $ID */
1353330559Sdteske{${TRACE:+
1354330559Sdteske	printf("<$ID>");
1355330559Sdteske}
1356330559Sdteske	/***********************************************/
1357330559Sdteske
1358330559Sdteske	printf("%s%Y%s ",
1359330559Sdteske		console ? "\033[32m" : "",
1360330559Sdteske		walltimestamp,
1361330559Sdteske		console ? "\033[39m" : "");
1362330559Sdteske
1363330559Sdteske	/****************** EVENT_TAG ******************/
1364330559Sdteske
1365330559Sdteske	${EVENT_TAG#[[:space:]]}
1366330559Sdteske${PROBE_COALESCE:+
1367330559Sdteske	/**************** PROBE_COALESCE ***************/
1368330559Sdteske
1369330559Sdteske	printf("%s%s:%s:%s:%s ", probename == "entry" ? "-> " :
1370330559Sdteske			probename == "return" ? "<- " :
1371330559Sdteske			probename == "start" ? "-> " :
1372330559Sdteske			probename == "done" ? "<- " : " | ",
1373330559Sdteske		probeprov, probemod, probefunc, probename);
1374330559Sdteske}
1375330559Sdteske	/**************** EVENT_DETAILS ****************/
1376330559Sdteske
1377330559Sdteske	${EVENT_DETAILS#[[:space:]]}
1378330559Sdteske
1379330559Sdteske	/***********************************************/
1380330559Sdteske
1381330559Sdteske	printf("\\n");
1382330559Sdteske${PSTREE:+
1383330559Sdteske	/*
1384330559Sdteske	 * Print process, parent, grandparent, and ancestor details
1385330559Sdteske	 */
1386330559Sdteske$(	pproc_dump -v 3
1387330559Sdteske	pproc_dump -v 2
1388330559Sdteske	pproc_dump -v 1
1389330559Sdteske	pproc_dump -v 0
1390330559Sdteske)}
1391330559Sdteske}
1392330559SdteskeEOF
1393333617Sdteske# NOTREACHED
1394330559Sdteske
1395330559Sdteske################################################################################
1396330559Sdteske# END
1397330559Sdteske################################################################################
1398