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