1#!/bin/sh 2#- 3# Copyright (c) 2014-2018 Devin Teske 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26# 27############################################################ IDENT(1) 28# 29# $Title: Watch processes as they trigger a particular DTrace probe $ 30# 31############################################################ CONFIGURATION 32 33# 34# DTrace pragma settings 35# 36DTRACE_PRAGMA=" 37 option quiet 38 option dynvarsize=16m 39 option switchrate=10hz 40" # END-QUOTE 41 42# 43# Profiles 44# 45: ${DWATCH_PROFILES_PATH="/usr/libexec/dwatch:/usr/local/libexec/dwatch"} 46 47############################################################ GLOBALS 48 49VERSION='$Version: 1.4 $' # -V 50 51pgm="${0##*/}" # Program basename 52 53# 54# Command-line arguments 55# 56PROBE_ARG= 57 58# 59# Command-line defaults 60# 61_MAX_ARGS=64 # -B num 62_MAX_DEPTH=64 # -K num 63 64# 65# Command-line options 66# 67CONSOLE= # -y 68CONSOLE_FORCE= # -y 69[ -t 1 ] && CONSOLE=1 # -y 70COUNT=0 # -N count 71CUSTOM_DETAILS= # -E code 72CUSTOM_TEST= # -t test 73DEBUG= # -d 74DESTRUCTIVE_ACTIONS= # -w 75DEVELOPER= # -dev 76EXECNAME= # -k name 77EXECREGEX= # -z regex 78EXIT_AFTER_COMPILE= # -e 79FILTER= # -r regex 80PROBE_COALESCE= # -F 81GROUP= # -g group 82JID= # -j jail 83LIST= # -l 84LIST_PROFILES= # -Q 85MAX_ARGS=$_MAX_ARGS # -B num 86MAX_DEPTH=$_MAX_DEPTH # -K num 87ONELINE= # -1 88OUTPUT= # -o file 89OUTPUT_CMD= # -O cmd 90PID= # -p pid 91PROBE_TYPE= # -f -m -n -P 92PROFILE= # -X profile 93PSTREE= # -R 94QUIET= # -q 95TIMEOUT= # -T time 96TRACE= # -x 97USER= # -u user 98USE_PROFILE= # -X profile 99VERBOSE= # -v 100 101# 102# Global exit status 103# 104SUCCESS=0 105FAILURE=1 106 107# 108# Miscellaneous 109# 110ACTIONS= 111EVENT_DETAILS= 112EVENT_TAG='printf("%d.%d %s[%d]: ", 113 this->uid0, this->gid0, execname, this->pid0);' 114EVENT_TEST= 115FILE= 116ID=3 117MODULE_CHECKED= 118PROBE= 119PSARGS=1 120RGID= 121RUID= 122SUDO= 123export SUDO_PROMPT="[sudo] Password:" 124TITLE=\$Title: 125 126############################################################ FUNCTIONS 127 128ansi() { local fmt="$2 $4"; [ "$CONSOLE" ] && fmt="\\033[$1m$2\\033[$3m $4"; 129 shift 4; printf "$fmt\n" "$@"; } 130die() { exec >&2; [ "$*" ] && echo "$pgm:" "$@"; exit $FAILURE; } 131info() { [ "$QUIET" ] || ansi 35 "INFO" 39 "$@" >&2; } 132 133usage() 134{ 135 local optfmt="\t%-10s %s\n" 136 exec >&2 137 [ "$*" ] && printf "%s: %s\n" "$pgm" "$*" 138 printf "Usage: %s [-1defFmnPqRvVwxy] [%s] [%s] [%s] [%s]\n" "$pgm" \ 139 "-B num" "-E code" "-g group" "-j jail" 140 printf "\t [%s] [%s] [%s] [%s] [%s] [%s]\n" \ 141 "-k name" "-K num" "-N count" "-o file" "-O cmd" "-p pid" 142 printf "\t [%s] [%s] [%s] [%s] [%s] [%s]\n" \ 143 "-r regex" "-t test" "-T time" "-u user" "-X profile" \ 144 "-z regex" 145 printf "\t probe[,...] [args ...]\n" 146 printf " %s -l [-fmnPqy] [-r regex] [probe ...]\n" "$pgm" 147 printf " %s -Q [-1qy] [-r regex]\n" "$pgm" 148 printf "\n" 149 printf "$optfmt" "-1" \ 150 "Print one line per process/profile (Default; disables \`-R')." 151 printf "$optfmt" "-B num" \ 152 "Maximum process arguments to display (Default $_MAX_ARGS)." 153 printf "$optfmt" "-d" \ 154 "Debug. Send dtrace(1) script to stdout instead of executing." 155 printf "$optfmt" "-e" \ 156 "Exit after compiling request but prior to enabling probes." 157 printf "$optfmt" "-E code" \ 158 "DTrace code for event details. If \`-', read from stdin." 159 printf "$optfmt" "-f" \ 160 "Enable probe matching the specified function name." 161 printf "$optfmt" "-F" \ 162 "Coalesce trace output by function." 163 printf "$optfmt" "-g group" \ 164 "Group filter. Only show processes matching group name/gid." 165 printf "$optfmt" "-j jail" \ 166 "Jail filter. Only show processes matching jail name/jid." 167 printf "$optfmt" "-k name" \ 168 "Only show processes matching name." 169 printf "$optfmt" "-K num" \ 170 "Maximum directory depth to display (Default $_MAX_DEPTH)." 171 printf "$optfmt" "-l" \ 172 "List available probes on standard output and exit." 173 printf "$optfmt" "-m" \ 174 "Enable probe matching the specified module name." 175 printf "$optfmt" "-n" \ 176 "Enable probe matching the specified probe name." 177 printf "$optfmt" "-N count" \ 178 "Exit after count matching entries (Default 0 for disabled)." 179 printf "$optfmt" "-o file" \ 180 "Set output file. If \`-', the path \`/dev/stdout' is used." 181 printf "$optfmt" "-O cmd" \ 182 "Execute cmd for each event." 183 printf "$optfmt" "-p pid" \ 184 "Process id filter. Only show processes with matching pid." 185 printf "$optfmt" "-P" \ 186 "Enable probe matching the specified provider name." 187 printf "$optfmt" "-q" \ 188 "Quiet. Hide informational messages and all dtrace(1) errors." 189 printf "$optfmt" "-Q" \ 190 "List available profiles in DWATCH_PROFILES_PATH and exit." 191 printf "$optfmt" "-r regex" \ 192 "Filter. Only show blocks matching awk(1) regular expression." 193 printf "$optfmt" "-R" \ 194 "Show parent, grandparent, and ancestor of process." 195 printf "$optfmt" "-t test" \ 196 "Test clause (predicate) to limit events (Default none)." 197 printf "$optfmt" "-T time" \ 198 "Timeout. Format is \`\#[smhd]' or simply \`\#' for seconds." 199 printf "$optfmt" "-u user" \ 200 "User filter. Only show processes matching user name/uid." 201 printf "$optfmt" "-v" \ 202 "Verbose. Show all errors from dtrace(1)." 203 printf "$optfmt" "-V" \ 204 "Report dwatch version on standard output and exit." 205 printf "$optfmt" "-w" \ 206 "Permit destructive actions (copyout*, stop, panic, etc.)." 207 printf "$optfmt" "-x" \ 208 "Trace. Print \`<probe-id>' when a probe is triggered." 209 printf "$optfmt" "-X profile" \ 210 "Load profile name from DWATCH_PROFILES_PATH." 211 printf "$optfmt" "-y" \ 212 "Always treat stdout as console (enable colors/columns/etc.)." 213 printf "$optfmt" "-z regex" \ 214 "Only show processes matching awk(1) regular expression." 215 die 216} 217 218dtrace_cmd() 219{ 220 local status stdout 221 local timeout= 222 223 if [ "$1" = "-t" ]; then 224 shift 225 [ "$TIMEOUT" ] && timeout=1 226 fi 227 228 exec 3>&1 229 stdout=3 230 231 # 232 # Filter dtrace(1) stderr while preserving exit status 233 # 234 status=$( 235 exec 4>&1 236 to_status=4 237 ( trap 'echo $? >&$to_status' EXIT 238 eval $SUDO ${timeout:+timeout \"\$TIMEOUT\"} dtrace \ 239 \"\$@\" 2>&1 ${QUIET:+2> /dev/null} >&$stdout 240 ) | dtrace_stderr_filter >&2 241 ) 242 243 return $status 244} 245 246dtrace_stderr_filter() 247{ 248 if [ "$VERBOSE" ]; then 249 cat 250 return 251 # NOTREACHED 252 fi 253 254 awk ' # Start awk(1) stderr-filter 255 /[[:digit:]]+ drops? on CPU [[:digit:]]+/ { next } 256 /failed to write to <stdout>: No such file or directory/ { next } 257 /failed to write to <stdout>: Broken pipe/ { next } 258 /processing aborted: Broken pipe/ { next } 259 /invalid address \(0x[[:xdigit:]]+\) in action #[[:digit:]]+/ { next } 260 /out of scratch space in action #[[:digit:]]+/ { next } 261 /^Bus error$/ { next } 262 { print; fflush() } 263 ' # END-QUOTE 264} 265 266expand_probe() 267{ 268 local OPTIND=1 OPTARG flag 269 local type= 270 271 while getopts t: flag; do 272 case "$flag" in 273 t) type="$OPTARG" ;; 274 esac 275 done 276 shift $(( $OPTIND - 1 )) 277 278 local probe="$1" 279 case "$probe" in 280 *:*) 281 echo "$probe" 282 return $SUCCESS 283 ;; 284 esac 285 286 dtrace_cmd -l | awk -v probe="$probe" -v type="$type" ' 287 # Start awk(1) processor 288 #################################################### BEGIN 289 BEGIN { getline dtrace_header } 290 #################################################### FUNCTIONS 291 function dump(unused1,unused2) { 292 if (n) { 293 if (NcF[n] == 1) f = N2F[n] 294 if (NcM[n] == 1) m = N2M[n] 295 if (NcP[n] == 1) p = N2P[n] 296 } else if (f) { 297 if (FcM[f] == 1) m = F2M[f] 298 if (FcP[f] == 1) p = F2P[f] 299 if (FcN[f] == 0 && found) n = "entry" 300 } else if (m) { 301 if (McP[m] == 1) p = M2P[m] 302 } 303 printf "%s:%s:%s:%s\n", p, m, f, n 304 exit !found 305 } 306 function inFMP() { return probe in F || probe in M || probe in P } 307 function inNMP() { return probe in N || probe in M || probe in P } 308 function inNFP() { return probe in N || probe in F || probe in P } 309 function inNFM() { return probe in N || probe in F || probe in M } 310 function diva(value, peerA, peerB, peerC) { 311 return value >= peerA && value >= peerB && value >= peerC 312 } 313 #################################################### MAIN 314 type == "name" && $NF != probe { next } 315 type == "function" && NF >=4 && $(NF-1) != probe { next } 316 type == "module" && NF == 5 && $(NF-2) != probe { next } 317 type == "provider" && $2 != probe { next } 318 type || $2 == probe || $3 == probe || $4 == probe || $5 == probe { 319 P[_p = $2]++ 320 M[_m = (NF >= 5 ? $(NF-2) : "")]++ 321 F[_f = (NF >= 4 ? $(NF-1) : "")]++ 322 N[_n = $NF]++ 323 if (N2F[_n] != _f) NcF[_n]++; N2F[_n] = _f 324 if (N2M[_n] != _m) NcM[_n]++; N2M[_n] = _m 325 if (N2P[_n] != _p) NcP[_n]++; N2P[_n] = _p 326 if (_n !~ /entry|return/) { 327 if (F2N[_f] != _n) FcN[_f]++ 328 F2N[_f] = _n 329 } 330 if (F2M[_f] != _m) FcM[_f]++; F2M[_f] = _m 331 if (F2P[_f] != _p) FcP[_f]++; F2P[_f] = _p 332 if (M2P[_m] != _p) McP[_m]++; M2P[_m] = _p 333 } 334 #################################################### END 335 END { 336 if (type == "name") dump(n = probe, found = probe in N) 337 if (type == "function") dump(f = probe, found = probe in F) 338 if (type == "module") dump(m = probe, found = probe in M) 339 if (type == "provider") dump(p = probe, found = probe in P) 340 if (probe in N) { 341 found = 1 342 if (!inFMP()) dump(n = probe) 343 if (diva(F[probe], N[probe], M[probe], P[probe])) 344 dump(f = probe) 345 if (diva(M[probe], N[probe], F[probe], P[probe])) 346 dump(m = probe) 347 if (diva(P[probe], N[probe], F[probe], M[probe])) 348 dump(p = probe) 349 dump(n = probe) # N is the diva 350 } else if (probe in F) { 351 found = 1 352 if (!inNMP()) dump(f = probe) 353 if (diva(N[probe], F[probe], M[probe], P[probe])) 354 dump(n = probe) 355 if (diva(M[probe], F[probe], N[probe], P[probe])) 356 dump(m = probe) 357 if (diva(P[probe], F[probe], N[probe], M[probe])) 358 dump(p = probe) 359 dump(f = probe) # F is the diva 360 } else if (probe in M) { 361 found = 1 362 if (!inNFP()) dump(m = probe) 363 if (diva(N[probe], M[probe], F[probe], P[probe])) 364 dump(n = probe) 365 if (diva(F[probe], M[probe], N[probe], P[probe])) 366 dump(f = probe) 367 if (diva(P[probe], M[probe], N[probe], F[probe])) 368 dump(p = probe) 369 dump(m = probe) # M is the diva 370 } else if (probe in P) { 371 found = 1 372 if (!inNFM()) dump(p = probe) 373 if (diva(N[probe], P[probe], F[probe], M[probe])) 374 dump(n = probe) 375 if (diva(F[probe], P[probe], N[probe], M[probe])) 376 dump(f = probe) 377 if (diva(M[probe], P[probe], N[probe], F[probe])) 378 dump(m = probe) 379 dump(p = probe) # P is the diva 380 } 381 if (!found) print probe 382 exit !found 383 } 384 ' # END-QUOTE 385} 386 387list_probes() 388{ 389 local OPTIND=1 OPTARG flag 390 local column=0 header="PROVIDER:MODULE:FUNCTION:NAME" 391 local filter= quiet= type= 392 393 while getopts f:qt: flag; do 394 case "$flag" in 395 f) filter="$OPTARG" ;; 396 q) quiet=1 ;; 397 t) type="$OPTARG" ;; 398 esac 399 done 400 shift $(( $OPTIND - 1 )) 401 402 if [ $# -eq 0 ]; then 403 case "$type" in 404 provider) column=1 header="PROVIDER" ;; 405 module) column=2 header="MODULE" ;; 406 function) column=3 header="FUNCTION" ;; 407 name) column=4 header="NAME" ;; 408 esac 409 fi 410 411 [ "$quiet" ] || echo "$header" 412 413 local arg probe= 414 for arg in "$@"; do 415 arg=$( expand_probe -t "$type" -- "$arg" ) 416 probe="$probe${probe:+, }$arg" 417 done 418 419 dtrace_cmd -l${probe:+n "$probe"} | awk -v pattern="$( 420 # Prevent backslashes from being lost 421 echo "$filter" | awk 'gsub(/\\/,"&&")||1' 422 )" -v want="$column" -v console="$CONSOLE" ' 423 BEGIN { getline dtrace_header } 424 function ans(seq) { return console ? "\033[" seq "m" : "" } 425 NF > 3 && $(NF-1) ~ /^#/ { next } 426 !_[$0 = column[0] = sprintf("%s:%s:%s:%s", 427 column[1] = $2, 428 column[2] = (NF >= 5 ? $(NF-2) : ""), 429 column[3] = (NF >= 4 ? $(NF-1) : ""), 430 column[4] = $NF)]++ && 431 !__[$0 = column[want]]++ && 432 gsub(pattern, ans("31;1") "&" ans("39;22")) { 433 print | "sort" 434 } 435 END { close("sort") } 436 ' # END-QUOTE 437 438 exit $SUCCESS 439} 440 441list_profiles() 442{ 443 local OPTIND=1 OPTARG flag 444 local filter= oneline= quiet= 445 446 while getopts 1f:q flag; do 447 case "$flag" in 448 1) oneline=1 ;; 449 f) filter="$OPTARG" ;; 450 q) quiet=1 ;; 451 esac 452 done 453 shift $(( $OPTIND - 1 )) 454 455 # Prevent backslashes from being lost 456 filter=$( echo "$filter" | awk 'gsub(/\\/,"&&")||1' ) 457 458 # Build a list of profiles available 459 local profiles 460 profiles=$( { IFS=: 461 for dir in $DWATCH_PROFILES_PATH; do 462 [ -d "$dir" ] || continue 463 for path in $dir/*; do 464 [ -f "$path" ] || continue 465 name="${path##*/}" 466 [ "$name" = "${name%%[!0-9A-Za-z_-]*}" ] || 467 continue 468 echo $name 469 done 470 done 471 } | sort -u ) 472 473 # Get the longest profile name 474 local longest_profile_name 475 longest_profile_name=$( echo "$profiles" | 476 awk -v N=0 '(L = length($0)) > N { N = L } END { print N }' ) 477 478 # Get the width of the terminal 479 local max_size="$( stty size 2> /dev/null )" 480 : ${max_size:=24 80} 481 local max_width="${max_size#*[$IFS]}" 482 483 # Determine how many columns we can display 484 local x=$longest_profile_name ncols=1 485 [ "$QUIET" ] || x=$(( $x + 8 )) # Accommodate leading tab character 486 x=$(( $x + 3 + $longest_profile_name )) # Preload end of next column 487 while [ $x -lt $max_width ]; do 488 ncols=$(( $ncols + 1 )) 489 x=$(( $x + 3 + $longest_profile_name )) 490 done 491 492 # Output single lines if sent to a pipe 493 if [ "$oneline" ]; then 494 echo "$profiles" | awk -v filter="$filter" -v cons="$CONSOLE" ' 495 function ans(s) { return cons ? "\033[" s "m" : "" } 496 gsub(filter, ans("31;1") "&" ans("39;22")) 497 ' # END-QUOTE 498 exit $SUCCESS 499 fi 500 501 [ "$quiet" ] || echo PROFILES: 502 echo "$profiles" | awk \ 503 -v colsize=$longest_profile_name \ 504 -v console="$CONSOLE" \ 505 -v ncols=$ncols \ 506 -v quiet="$quiet" \ 507 -v filter="$filter" \ 508 ' # Begin awk(1) processor 509 function ans(seq) { return console ? "\033[" seq "m" : "" } 510 BEGIN { 511 row_item[1] = "" 512 replace = ans("31;1") "&" ans("39;22") 513 ansi_offset = length(replace) - 1 514 } 515 function print_row() 516 { 517 cs = colsize + ansi_offset * \ 518 gsub(filter, replace, row_item[1]) 519 printf "%s%-*s", quiet ? "" : "\t", cs, row_item[1] 520 for (i = 2; i <= cur_col; i++) { 521 cs = colsize + ansi_offset * \ 522 gsub(filter, replace, row_item[i]) 523 printf " %-*s", cs, row_item[i] 524 } 525 printf "\n" 526 } 527 $0 ~ filter { 528 n++ 529 cur_col = ((n - 1) % ncols) + 1 530 row_item[cur_col] = $0 531 if (cur_col == ncols) print_row() 532 } 533 END { if (cur_col < ncols) print_row() } 534 ' # END-QUOTE 535 536 exit $SUCCESS 537} 538 539shell_escape() 540{ 541 echo "$*" | awk 'gsub(/'\''/, "&\\\\&&")||1' 542} 543 544load_profile() 545{ 546 local profile="$1" 547 548 [ "$profile" ] || 549 die "missing profile argument (\`$pgm -Q' to list profiles)" 550 551 local oldIFS="$IFS" 552 local dir found= 553 local ARGV= 554 555 [ $COUNT -gt 0 ] && ARGV="$ARGV -N $COUNT" 556 [ "$DEBUG" ] && ARGV="$ARGV -d" 557 [ "$DESTRUCTIVE_ACTIONS" ] && ARGV="$ARGV -w" 558 [ "$EXIT_AFTER_COMPILE" ] && ARGV="$ARGV -e" 559 [ "$GROUP" ] && ARGV="$ARGV -g $GROUP" 560 [ "$JID" ] && ARGV="$ARGV -j $JID" 561 [ $MAX_ARGS -ne $_MAX_ARGS ] && ARGV="$ARGV -B $MAX_ARGS" 562 [ $MAX_DEPTH -ne $_MAX_DEPTH ] && ARGV="$ARGV -K $MAX_DEPTH" 563 [ "$ONELINE" ] && ARGV="$ARGV -1" 564 [ "$PID" ] && ARGV="$ARGV -p $PID" 565 [ "$PSTREE" ] && ARGV="$ARGV -R" 566 [ "$QUIET" ] && ARGV="$ARGV -q" 567 [ "$TIMEOUT" ] && ARGV="$ARGV -T $TIMEOUT" 568 [ "$TRACE" ] && ARGV="$ARGV -x" 569 [ "$USER" ] && ARGV="$ARGV -u $USER" 570 [ "$VERBOSE" ] && ARGV="$ARGV -v" 571 572 [ "$FILTER" ] && 573 ARGV="$ARGV -r '$( shell_escape "$FILTER" )'" 574 [ "$EXECREGEX" ] && 575 ARGV="$ARGV -z '$( shell_escape "$EXECREGEX" )'" 576 [ "$CUSTOM_DETAILS" ] && 577 ARGV="$ARGV -E '$( shell_escape "$EVENT_DETAILS" )'" 578 [ "$CUSTOM_TEST" ] && 579 ARGV="$ARGV -t '$( shell_escape "$CUSTOM_TEST" )'" 580 [ "$OUTPUT" ] && 581 ARGV="$ARGV -o '$( shell_escape "$OUTPUT" )'" 582 [ "$OUTPUT_CMD" ] && 583 ARGV="$ARGV -O '$( shell_escape "$OUTPUT_CMD" )'" 584 585 case "$PROBE_TYPE" in 586 provider) ARGV="$ARGV -P" ;; 587 module) ARGV="$ARGV -m" ;; 588 function) ARGV="$ARGV -f" ;; 589 name) ARGV="$ARGV -n" ;; 590 esac 591 592 IFS=: 593 for dir in $DWATCH_PROFILES_PATH; do 594 [ -d "$dir" ] || continue 595 [ -f "$dir/$profile" ] || continue 596 PROFILE="$profile" found=1 597 info "Sourcing $profile profile [found in %s]" "$dir" 598 . "$dir/$profile" 599 break 600 done 601 IFS="$oldIFS" 602 603 [ "$found" ] || 604 die "no module named \`$profile' (\`$pgm -Q' to list profiles)" 605} 606 607pproc() 608{ 609 local OPTIND=1 OPTARG flag 610 local P= N=0 611 612 while getopts P: flag; do 613 case "$flag" in 614 P) P="$OPTARG" ;; 615 esac 616 done 617 shift $(( OPTIND - 1 )) 618 619 local proc=$1 620 if [ ! "$proc" ]; then 621 if [ "$P" = "0" ]; then 622 proc="curthread->td_proc" 623 else 624 proc="this->proc ? this->proc->p_pptr : NULL" 625 fi 626 fi 627 628 awk 'NR > 1 && $0 { $0 = "\t" $0 } 629 gsub(/\\\t/, "\t") || 1 630 ' <<-EOFPREAMBLE 631 this->proc = $proc; 632 this->uid$P = this->proc ? this->proc->p_ucred->cr_uid : -1; 633 this->gid$P = this->proc ? this->proc->p_ucred->cr_rgid : -1; 634 this->pid$P = this->proc ? this->proc->p_pid : -1; 635 this->jid$P = this->proc ? this->proc->p_ucred->cr_prison->pr_id : -1; 636 637 this->p_args = this->proc ? this->proc->p_args : 0; 638 this->ar_length = this->p_args ? this->p_args->ar_length : 0; 639 this->ar_args = (char *)(this->p_args ? this->p_args->ar_args : 0); 640 641 this->args$P = this->arg${P}_$N = this->ar_length > 0 ? 642 \ this->ar_args : stringof(this->proc->p_comm); 643 this->len = this->ar_length > 0 ? strlen(this->ar_args) + 1 : 0; 644 this->ar_args += this->len; 645 this->ar_length -= this->len; 646 647 EOFPREAMBLE 648 649 awk -v P=$P -v MAX_ARGS=$MAX_ARGS ' 650 $0 { $0 = "\t" $0 } 651 buf = buf $0 "\n" { } 652 END { 653 while (++N <= MAX_ARGS) { 654 $0 = buf 655 gsub(/P/, P) 656 gsub(/N/, N) 657 gsub(/\\\t/, "\t") 658 sub(/\n$/, "") 659 print 660 } 661 } 662 ' <<-EOFARGS 663 this->argP_N = this->ar_length > 0 ? this->ar_args : ""; 664 this->argsP = strjoin(this->argsP, 665 \ strjoin(this->argP_N != "" ? " " : "", this->argP_N)); 666 this->len = this->ar_length > 0 ? strlen(this->ar_args) + 1 : 0; 667 this->ar_args += this->len; 668 this->ar_length -= this->len; 669 670 EOFARGS 671 672 N=$(( $MAX_ARGS + 1 )) 673 awk 'sub(/^\\\t/, "\t") || 1, $0 = "\t" $0' <<-EOFPROC 674 this->arg${P}_$N = this->ar_length > 0 ? "..." : ""; 675 this->args$P = strjoin(this->args$P, 676 \ strjoin(this->arg${P}_$N != "" ? " " : "", this->arg${P}_$N)); 677 EOFPROC 678} 679 680pproc_dump() 681{ 682 local OPTIND=1 OPTARG flag 683 local verbose= 684 685 while getopts v flag; do 686 case "$flag" in 687 v) verbose=1 ;; 688 esac 689 done 690 shift $(( $OPTIND - 1 )) 691 692 local P=$1 693 if [ "$verbose" ]; then 694 awk -v P=$P ' 695 BEGIN { printf "\t" } 696 NR > 1 && $0 { $0 = "\t" $0 } 697 buf = buf $0 "\n" { } 698 END { 699 $0 = buf 700 if (P < 3) S = sprintf("%" 7-2*(P+1) "s", "") 701 gsub(/S/, S) 702 gsub(/B/, P < 3 ? "\\" : "") 703 gsub(/\\\t/, "\t") 704 sub(/\n$/, "") 705 print 706 } 707 ' <<-EOFPREAMBLE 708 printf(" SB-+= %05d %d.%d %s\n", 709 \ this->pid$P, this->uid$P, this->gid$P, this->args$P); 710 EOFPREAMBLE 711 else 712 cat <<-EOFPREAMBLE 713 printf("%s", this->args$P); 714 EOFPREAMBLE 715 fi 716} 717 718############################################################ MAIN 719 720# If we're running as root, no need for sudo(8) 721[ "$( id -u )" != 0 ] && type sudo > /dev/null 2>&1 && SUDO=sudo 722 723# 724# Process command-line options 725# 726while getopts 1B:deE:fFg:j:k:K:lmnN:o:O:p:PqQr:Rt:T:u:vVwxX:yz: flag; do 727 case "$flag" in 728 1) ONELINE=1 PSTREE= ;; 729 B) MAX_ARGS="$OPTARG" ;; 730 d) DEBUG=1 ;; 731 e) EXIT_AFTER_COMPILE=1 ;; 732 E) CUSTOM_DETAILS=1 733 EVENT_DETAILS="${EVENT_DETAILS%;}" 734 [ "$EVENT_DETAILS" ] && EVENT_DETAILS="$EVENT_DETAILS; 735 printf(\" \"); 736 " # END-QUOTE 737 # Read event code from stdin if `-' is argument 738 [ "$OPTARG" = "-" ] && OPTARG=$( cat ) 739 EVENT_DETAILS="$EVENT_DETAILS$OPTARG" ;; 740 f) PROBE_TYPE=function ;; 741 F) PROBE_COALESCE=1 ;; 742 g) GROUP="$OPTARG" ;; 743 j) JID="$OPTARG" ;; 744 k) EXECNAME="$EXECNAME${EXECNAME:+ }$OPTARG" 745 case "$OPTARG" in 746 \**\*) name="${OPTARG%\*}" 747 predicate="strstr(execname, \"${name#\*}\") != NULL" ;; 748 \**) name="${OPTARG#\*}" 749 predicate="strstr(execname, \"$name\") == (execname +" 750 predicate="$predicate strlen(execname) - ${#name})" ;; 751 *\*) predicate="strstr(execname, \"${OPTARG%\*}\") == execname" ;; 752 *) predicate="execname == \"$OPTARG\"" 753 esac 754 EVENT_TEST="$predicate${EVENT_TEST:+ || 755 ($EVENT_TEST)}" ;; 756 K) MAX_DEPTH="$OPTARG" ;; 757 l) LIST=1 ;; 758 m) PROBE_TYPE=module ;; 759 n) PROBE_TYPE=name ;; 760 N) COUNT="$OPTARG" ;; 761 o) OUTPUT="$OPTARG" ;; 762 O) OUTPUT_CMD="$OPTARG" ;; 763 p) PID="$OPTARG" ;; 764 P) PROBE_TYPE=provider ;; 765 q) QUIET=1 ;; 766 Q) LIST_PROFILES=1 ;; 767 r) FILTER="$OPTARG" ;; 768 R) PSTREE=1 ;; 769 t) CUSTOM_TEST="${CUSTOM_TEST:+($CUSTOM_TEST) && }$OPTARG" ;; 770 T) TIMEOUT="$OPTARG" ;; 771 u) USER="$OPTARG" ;; 772 v) VERBOSE=1 ;; 773 V) vers="${VERSION#\$*[:\$]}" 774 vers="${vers% \$}" 775 printf "%s: %s\n" "$pgm" "${vers# }" 776 exit ;; 777 w) DESTRUCTIVE_ACTIONS=1 ;; 778 x) TRACE=1 ;; 779 X) USE_PROFILE=1 PROFILE="$OPTARG" ;; 780 y) CONSOLE=1 CONSOLE_FORCE=1 ;; 781 z) EXECREGEX="$OPTARG" ;; 782 *) usage 783 # NOTREACHED 784 esac 785done 786shift $(( $OPTIND - 1 )) 787 788# 789# List probes if `-l' was given 790# 791[ "$LIST" ] && 792 list_probes -f "$FILTER" ${QUIET:+-q} -t "$PROBE_TYPE" -- "$@" 793 # NOTREACHED 794 795# 796# List profiles if `-Q' was given 797# 798[ "$LIST_PROFILES" ] && 799 list_profiles ${ONELINE:+-1} -f "$FILTER" ${QUIET:+-q} 800 # NOTREACHED 801 802# 803# Validate number of arguments 804# 805if [ ! "$PROFILE" ]; then 806 # If not given `-X profile' then a probe argument is required 807 [ $# -gt 0 ] || usage # NOTREACHED 808fi 809 810# 811# Validate `-N count' option argument 812# 813case "$COUNT" in 814"") usage "-N option requires a number argument" ;; # NOTREACHED 815*[!0-9]*) usage "-N argument must be a number" ;; # NOTREACHED 816esac 817 818# 819# Validate `-B num' option argument 820# 821case "$MAX_ARGS" in 822"") usage "-B option requires a number argument" ;; # NOTREACHED 823*[!0-9]*) usage "-B argument must be a number" ;; # NOTREACHED 824esac 825 826# 827# Validate `-K num' option argument 828# 829case "$MAX_DEPTH" in 830"") usage "-K option requires a number argument" ;; # NOTREACHED 831*[!0-9]*) usage "-K argument must be a number" ;; # NOTREACHED 832esac 833 834# 835# Validate `-j jail' option argument 836# 837case "$JID" in 838"") : fall through ;; 839*[!0-9]*) JID=$( jls -j "$JID" jid ) || exit ;; 840esac 841 842# 843# Validate `-u user' option argument 844# 845case "$USER" in 846"") : fall through ;; 847*[![:alnum:]_-]*) RUID="$USER" ;; 848*[!0-9]*) RUID=$( id -u "$USER" 2> /dev/null ) || die "No such user: $USER" ;; 849*) RUID=$USER 850esac 851 852# 853# Validate `-g group' option argument 854# 855case "$GROUP" in 856"") : fall-through ;; 857*[![:alnum:]_-]*) RGID="$GROUP" ;; 858*[!0-9]*) 859 RGID=$( getent group | awk -F: -v group="$GROUP" ' 860 $1 == group { print $3; exit found=1 } 861 END { exit !found } 862 ' ) || die "No such group: $GROUP" ;; 863*) RGID=$GROUP 864esac 865 866# 867# Expand probe argument into probe(s) 868# 869case "$1" in 870-*) : Assume dtrace options such as "-c cmd" or "-p pid" ;; # No probe(s) given 871*) 872 PROBE_ARG="$1" 873 shift 874esac 875if [ "$PROBE_ARG" ]; then 876 oldIFS="$IFS" 877 IFS="$IFS," 878 for arg in $PROBE_ARG; do 879 arg=$( expand_probe -t "$PROBE_TYPE" -- "$arg" ) 880 PROBE="$PROBE${PROBE:+, }$arg" 881 done 882 IFS="$oldIFS" 883fi 884 885# 886# Developer switch 887# 888[ "$DEBUG" -a "$EXIT_AFTER_COMPILE" -a "$VERBOSE" ] && DEVELOPER=1 DEBUG= 889 890# 891# Set default event details if `-E code' was not given 892# 893[ "$CUSTOM_DETAILS" ] || EVENT_DETAILS=$( pproc_dump 0 ) 894 895# 896# Load profile if given `-X profile' 897# 898[ "$USE_PROFILE" ] && load_profile "$PROFILE" 899[ "$PROBE" ] || die "PROBE not defined by profile and none given as argument" 900 901# 902# Show the user what's being watched 903# 904[ "$DEBUG$EXIT_AFTER_COMPILE" ] || info "Watching '$PROBE' ..." 905 906# 907# Header for watched probe entry 908# 909case "$PROBE" in 910*,*) : fall-through ;; 911*:execve:entry|execve:entry) 912 ACTIONS=$( awk 'gsub(/\\\t/, "\t") || 1' <<-EOF 913 $PROBE /* probe ID $ID */ 914 {${TRACE:+ 915 \ printf("<$ID>");} 916 \ this->caller_execname = execname; 917 } 918 EOF 919 ) 920 PROBE="${PROBE%entry}return" 921 ID=$(( $ID + 1 )) 922 EVENT_TEST="execname != this->caller_execname${EVENT_TEST:+ && 923 ($EVENT_TEST)}" 924 EVENT_TAG='printf("%d.%d %s[%d]: ", 925 this->uid1, this->gid1, this->caller_execname, this->pid1);' 926 ;; 927esac 928 929# 930# Jail clause/predicate 931# 932if [ "$JID" ]; then 933 prison_id="curthread->td_proc->p_ucred->cr_prison->pr_id" 934 EVENT_TEST="$prison_id == $JID${EVENT_TEST:+ && 935 ($EVENT_TEST)}" 936fi 937 938# 939# Custom test clause/predicate 940# 941if [ "$CUSTOM_TEST" ]; then 942 case "$EVENT_TEST" in 943 "") EVENT_TEST="$CUSTOM_TEST" ;; 944 *) EVENT_TEST="$EVENT_TEST && 945 ($CUSTOM_TEST)" 946 esac 947fi 948 949# 950# Make sure dynamic code has trailing semi-colons if non-NULL 951# 952EVENT_TAG="${EVENT_TAG%;}${EVENT_TAG:+;}" 953EVENT_DETAILS="${EVENT_DETAILS%;}${EVENT_DETAILS:+;}" 954 955# 956# DTrace script 957# 958# If `-d' is given, script is sent to stdout for debugging 959# If `-c count", `-g group', `-r regex', or `-u user' is given, run script with 960# dtrace and send output to awk(1) post-processor (making sure to preserve the 961# exit code returned by dtrace invocation). Otherwise, simply run script with 962# dtrace and then exit. 963# 964exec 9<<EOF 965$PROBE /* probe ID 2 */ 966{${TRACE:+ 967 printf("<2>"); 968} 969 /* 970 * Examine process, parent process, and grandparent process details 971 */ 972 973 /******************* CURPROC *******************/ 974 975 $( pproc -P0 ) 976 977 /******************* PPARENT *******************/ 978 979 $( if [ "$PSTREE" ]; then pproc -P1; else echo -n \ 980 "this->proc = this->proc ? this->proc->p_pptr : NULL; 981 this->pid1 = this->proc ? this->proc->p_pid : -1; 982 this->uid1 = this->proc ? this->proc->p_ucred->cr_uid : -1; 983 this->gid1 = this->proc ? this->proc->p_ucred->cr_rgid : -1; 984 this->jid1 = this->proc ? this->proc->p_ucred->cr_prison->pr_id : -1;" 985 fi ) 986 987 /******************* GPARENT *******************/ 988 989 $( [ "$PSTREE" ] && pproc -P2 ) 990 991 /******************* APARENT *******************/ 992 993 $( [ "$PSTREE" ] && pproc -P3 ) 994} 995EOF 996PSARGS_ACTION=$( cat <&9 ) 997[ "$OUTPUT" -a ! "$CONSOLE_FORCE" ] && CONSOLE= 998{ 999 if [ "$DEBUG" ]; then 1000 # Send script to stdout 1001 cat 1002 exit 1003 fi 1004 1005 if [ "$CUSTOM_TEST$EXECNAME$JID$OUTPUT$TIMEOUT$TRACE$VERBOSE" -a \ 1006 ! "$QUIET" ] 1007 then 1008 msg=Setting 1009 [ "$CUSTOM_TEST" ] && msg="$msg test: $CUSTOM_TEST" 1010 [ "$EXECNAME" ] && msg="$msg execname: $EXECNAME" 1011 [ "$JID" ] && msg="$msg jid: $JID" 1012 [ "$OUTPUT" ] && msg="$msg output: $OUTPUT" 1013 [ "$TIMEOUT" ] && msg="$msg timeout: $TIMEOUT" 1014 [ "$TRACE" ] && msg="$msg trace: $TRACE" 1015 [ "$VERBOSE" ] && msg="$msg verbose: $VERBOSE" 1016 info "$msg" 1017 fi 1018 1019 exec 3>&1 1020 console_stdout=3 1021 1022 # 1023 # Developer debugging aide 1024 # 1025 if [ "$DEVELOPER" ]; then 1026 # 1027 # Run, capture the error line, and focus it 1028 # 1029 # Example error text to capture line number from: 1030 # dtrace: failed to compile script /dev/stdin: line 669: ... 1031 # 1032 errline= 1033 stdin_buf=$( cat ) 1034 stderr_buf=$( echo "$stdin_buf" | 1035 dtrace_cmd -t -es /dev/stdin "$@" 2>&1 > /dev/null ) 1036 status=$? 1037 if [ "$stderr_buf" ]; then 1038 errline=$( echo "$stderr_buf" | awk ' 1039 BEGIN { 1040 ti = "\033[31m" 1041 te = "\033[39m" 1042 } 1043 { line = $0 } 1044 sub(/.*: line /, "") && sub(/:.*/, "") { 1045 print # to errline 1046 sub("line " $0, ti "&" te, line) 1047 } 1048 { print line > "/dev/stderr" } 1049 ' 2>&3 ) 1050 fi 1051 if [ "$errline" ]; then 1052 echo "$stdin_buf" | awk -v line="${errline%%[^0-9]*}" ' 1053 BEGIN { 1054 start = line < 10 ? 1 : line - 10 1055 end = line + 10 1056 slen = length(sprintf("%u", start)) 1057 elen = length(sprintf("%u", end)) 1058 N = elen > slen ? elen : slen 1059 ti[line] = "\033[31m" 1060 te[line] = "\033[39m" 1061 fmt = "%s%*u %s%s\n" 1062 } 1063 NR < start { next } 1064 NR == start, NR == end { 1065 printf(fmt, ti[NR], N, NR, $0, te[NR]) 1066 } 1067 NR > end { exit } 1068 ' # END-QUOTE 1069 fi 1070 exit $status 1071 fi 1072 1073 if [ $COUNT -eq 0 -a ! "$EXECREGEX$FILTER$GROUP$OUTPUT_CMD$PID$USER" ] 1074 then 1075 case "$OUTPUT" in 1076 -) output_path=/dev/stdout ;; 1077 *) output_path="$OUTPUT" 1078 esac 1079 1080 # Run script without pipe to awk post-processor 1081 dtrace_cmd -t \ 1082 ${DESTRUCTIVE_ACTIONS:+-w} \ 1083 ${EXIT_AFTER_COMPILE:+-e} \ 1084 ${OUTPUT:+-o "$output_path"} \ 1085 -s /dev/stdin \ 1086 "$@" 1087 exit 1088 fi 1089 1090 # Prevent backslashes from being lost 1091 FILTER=$( echo "$FILTER" | awk 'gsub(/\\/,"&&")||1' ) 1092 EXECREGEX=$( echo "$EXECREGEX" | awk 'gsub(/\\/,"&&")||1' ) 1093 1094 if [ ! "$QUIET" ]; then 1095 msg=Filtering 1096 [ "$EXECREGEX" ] && msg="$msg execregex: $EXECREGEX" 1097 [ "$FILTER" ] && msg="$msg filter: $FILTER" 1098 [ "$GROUP" ] && msg="$msg group: $GROUP" 1099 [ "$OUTPUT_CMD" ] && msg="$msg cmd: $OUTPUT_CMD" 1100 [ "$PID" ] && msg="$msg pid: $PID" 1101 [ "$USER" ] && msg="$msg user: $USER" 1102 [ $COUNT -gt 0 ] && msg="$msg count: $COUNT" 1103 info "$msg" 1104 fi 1105 1106 # 1107 # Send script output to post-processor for filtering 1108 # 1109 status=$( 1110 exec 4>&1 1111 to_status=4 1112 ( exec 5>&1; to_dtrace_stderr_filter=5; ( 1113 trap 'echo $? >&$to_status' EXIT 1114 eval $SUDO ${TIMEOUT:+timeout \"\$TIMEOUT\"} dtrace \ 1115 ${EXIT_AFTER_COMPILE:+-e} \ 1116 ${DESTRUCTIVE_ACTIONS:+-w} \ 1117 -s /dev/stdin \ 1118 \"\$@\" \ 1119 2>&$to_dtrace_stderr_filter \ 1120 ${QUIET:+2> /dev/null} 1121 ) | $SUDO awk \ 1122 -v cmd="$OUTPUT_CMD" \ 1123 -v console="$CONSOLE" \ 1124 -v count=$COUNT \ 1125 -v execregex="$EXECREGEX" \ 1126 -v filter="$FILTER" \ 1127 -v gid="$RGID" \ 1128 -v output="$OUTPUT" \ 1129 -v pid="$PID" \ 1130 -v pstree=$PSTREE \ 1131 -v quiet=$QUIET \ 1132 -v tty=$( ps -o tty= -p $$ ) \ 1133 -v uid="$RUID" \ 1134 ' # Start awk(1) post-processor 1135 ############################################ BEGIN 1136 BEGIN { 1137 true = 1 1138 ansi = "(\\033\\[[[:digit:];]+m)?" 1139 num = year = day = "[[:digit:]]+" 1140 month = "[[:alpha:]]+" 1141 date = year " " month " +" day 1142 time = "[012][0-9]:[0-5][0-9]:[0-5][0-9]" 1143 date_time = ansi date " +" time ansi 1144 name1 = "[^\\[]*" 1145 name2 = "[^\\n]*" 1146 if (output == "-") 1147 output = "/dev/stdout" 1148 1149 # 1150 # Field definitions 1151 # 1152 nexecmatches = 2 1153 execstart[1] = sprintf( \ 1154 "^(%s) (%s)\\.(%s) (%s)\\[(%s)\\]: ", 1155 date_time, num, num, name1, num) 1156 execstart[2] = sprintf( \ 1157 "\\n +\\\\?-\\+= (%s) (%s)\\.(%s) ", 1158 num, num, num) 1159 npidmatches = 2 1160 pidstart[1] = sprintf("^(%s) (%s)\\.(%s) (%s)\\[", 1161 date_time, num, num, name1) 1162 pidstart[2] = "\\n +\\\\?-\\+= " 1163 pidpreen[2] = "^0*" 1164 piddeflt[2] = "0" 1165 ngidmatches = 2 1166 gidstart[1] = sprintf("^(%s) (%s)\\.", date_time, num) 1167 gidstart[2] = sprintf("\\n +\\\\?-\\+= (%s) (%s)\\.", 1168 ansi num ansi, num) 1169 nuidmatches = 2 1170 uidstart[1] = sprintf("^(%s) ", date_time) 1171 uidstart[2] = sprintf("\\n +\\\\?-\\+= (%s) ", 1172 ansi num ansi) 1173 } 1174 ############################################ FUNCTIONS 1175 function strip(s) { gsub(/\033\[[0-9;]*m/, "", s); return s } 1176 function esc(str) { gsub(/'\''/, "&\\\\&&", str); return str } 1177 function arg(str) { return "'\''" esc(str) "'\''" } 1178 function env(var, str) { return var "=" arg(str) " " } 1179 function ans(seq) { return console ? "\033[" seq "m" : "" } 1180 function runcmd() { 1181 return system(sprintf("%s/bin/sh -c %s", 1182 env("TAG", strip(tag)) \ 1183 env("DETAILS", strip(details)), 1184 arg(cmd))) 1185 } 1186 function filter_block() { 1187 if (length(lines) < 1) return 0 1188 block_match = 0 1189 newstr = "" 1190 start = 1 1191 if (match(lines, "^(" date_time ") ")) { 1192 newstr = newstr substr(lines, 1, 1193 RSTART + RLENGTH - 1) 1194 start = RSTART + RLENGTH 1195 } 1196 replace = ans("31;1") "&" ans("39;22") 1197 workstr = substr(lines, start) 1198 if (gsub(filter, replace, workstr)) block_match = 1 1199 lines = newstr workstr 1200 return block_match 1201 } 1202 function filter_field(startre, fieldre, matchre, isword, 1203 preenre, defaultstr) 1204 { 1205 if (length(lines) < 1) return 0 1206 field_match = 0 1207 newstr = "" 1208 start = 1 1209 while ((workstr = substr(lines, start)) && 1210 (workstr ~ (startre fieldre))) 1211 { 1212 match(workstr, startre) 1213 start += end = RSTART + RLENGTH - 1 1214 newstr = newstr substr(workstr, 1, end) 1215 workstr = substr(workstr, end + 1) 1216 match(workstr, fieldre) 1217 start += end = RSTART + RLENGTH - 1 1218 field = matchstr = substr(workstr, 1, end) 1219 sub(preenre, "", matchstr) 1220 if (!matchstr) matchstr = defaultstr 1221 if (isword) { 1222 if (match(matchstr, matchre) && 1223 RSTART == 1 && 1224 RLENGTH == length(matchstr)) { 1225 field_match = 1 1226 field = ans(7) field ans(27) 1227 } 1228 } else { 1229 replace = ans(7) "&" ans(27) 1230 if (gsub(matchre, replace, matchstr)) { 1231 field_match = 1 1232 field = matchstr 1233 } 1234 } 1235 newstr = newstr field 1236 } 1237 lines = newstr workstr 1238 return field_match 1239 } 1240 function dump() { 1241 lines = block 1242 block = "" 1243 found = 0 1244 if (execregex != "") { 1245 for (n = 1; n <= nexecmatches; n++) 1246 if (filter_field(execstart[n], name2, 1247 execregex)) found = 1 1248 if (!found) return 1249 } 1250 if (pid != "") { 1251 for (n = 1; n <= npidmatches; n++) 1252 if (filter_field(pidstart[n], num, pid, 1253 true, pidpreen[n], 1254 piddeflt[n])) found = 1 1255 if (!found) return 1256 } 1257 if (gid != "") { 1258 for (n = 1; n <= ngidmatches; n++) 1259 if (filter_field(gidstart[n], num, 1260 gid, true)) found = 1 1261 if (!found) return 1262 } 1263 if (uid != "") { 1264 for (n = 1; n <= nuidmatches; n++) 1265 if (filter_field(uidstart[n], num, 1266 uid, true)) found = 1 1267 if (!found) return 1268 } 1269 if (filter != "" && !filter_block()) return 1270 if (lines) { 1271 stdout = 1 1272 if (output) { 1273 stdout = 0 1274 if (!console) lines = strip(lines) 1275 print lines > output 1276 } else if (cmd) { 1277 if (!quiet) print lines 1278 tag = details = lines 1279 sub(/: .*/, "", tag) 1280 sub(/.*: /, "", details) 1281 if (!console) tag = strip(tag) 1282 runcmd() 1283 } else print lines 1284 } 1285 fflush() 1286 ++matches 1287 } 1288 ############################################ MAIN 1289 { block = (block ? block "\n" : block) $0 } 1290 !pstree { dump() } 1291 $0 ~ sprintf("^%6s\\\\-\\+= %s ", "", num) { dump() } 1292 count && matches >= count { exit } 1293 ############################################ END 1294 END { 1295 dump() 1296 system(sprintf("pkill -t %s dtrace %s", tty, 1297 quiet ? "2> /dev/null" : "")) 1298 } 1299 ' >&$console_stdout ) | dtrace_stderr_filter >&2 1300 ) # status 1301 exit $status 1302 1303} <<EOF 1304#!/usr/sbin/dtrace -s 1305/* - 1306 * Copyright (c) 2014-2018 Devin Teske <dteske@FreeBSD.org> 1307 * All rights reserved. 1308 * Redistribution and use in source and binary forms, with or without 1309 * modification, are permitted provided that the following conditions 1310 * are met: 1311 * 1. Redistributions of source code must retain the above copyright 1312 * notice, this list of conditions and the following disclaimer. 1313 * 2. Redistributions in binary form must reproduce the above copyright 1314 * notice, this list of conditions and the following disclaimer in the 1315 * documentation and/or other materials provided with the distribution. 1316 * 1317 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \`\`AS IS'' AND 1318 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1319 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1320 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1321 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1322 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1323 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1324 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1325 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1326 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1327 * SUCH DAMAGE. 1328 * 1329 * $TITLE dtrace(1) script to log process(es) triggering $PROBE $ 1330 */ 1331 1332$( echo "$DTRACE_PRAGMA" | awk ' 1333 !/^[[:space:]]*(#|$)/, sub(/^[[:space:]]*/, "#pragma D ")||1 1334' ) 1335 1336int console; 1337 1338dtrace:::BEGIN { console = ${CONSOLE:-0} } /* probe ID 1 */ 1339 1340/*********************************************************/ 1341 1342${PSARGS:+$PSARGS_ACTION} 1343${ACTIONS:+ 1344/*********************************************************/ 1345 1346$ACTIONS 1347} 1348/*********************************************************/ 1349 1350$PROBE${EVENT_TEST:+ / $EVENT_TEST /} /* probe ID $ID */ 1351{${TRACE:+ 1352 printf("<$ID>"); 1353} 1354 /***********************************************/ 1355 1356 printf("%s%Y%s ", 1357 console ? "\033[32m" : "", 1358 walltimestamp, 1359 console ? "\033[39m" : ""); 1360 1361 /****************** EVENT_TAG ******************/ 1362 1363 ${EVENT_TAG#[[:space:]]} 1364${PROBE_COALESCE:+ 1365 /**************** PROBE_COALESCE ***************/ 1366 1367 printf("%s%s:%s:%s:%s ", probename == "entry" ? "-> " : 1368 probename == "return" ? "<- " : 1369 probename == "start" ? "-> " : 1370 probename == "done" ? "<- " : " | ", 1371 probeprov, probemod, probefunc, probename); 1372} 1373 /**************** EVENT_DETAILS ****************/ 1374 1375 ${EVENT_DETAILS#[[:space:]]} 1376 1377 /***********************************************/ 1378 1379 printf("\\n"); 1380${PSTREE:+ 1381 /* 1382 * Print process, parent, grandparent, and ancestor details 1383 */ 1384$( pproc_dump -v 3 1385 pproc_dump -v 2 1386 pproc_dump -v 1 1387 pproc_dump -v 0 1388)} 1389} 1390EOF 1391# NOTREACHED 1392 1393################################################################################ 1394# END 1395################################################################################ 1396