Index: usr.sbin/dwatch/Makefile =================================================================== --- /dev/null +++ usr.sbin/dwatch/Makefile @@ -0,0 +1,15 @@ +# $FreeBSD$ + +.include + +SUBDIR= libexec + +.if ${MK_EXAMPLES} != "no" +SUBDIR+= examples +.endif + +SCRIPTS= dwatch + +MAN= dwatch.8 + +.include Index: usr.sbin/dwatch/dwatch =================================================================== --- /dev/null +++ usr.sbin/dwatch/dwatch @@ -0,0 +1,617 @@ +#!/bin/sh +#- +# Copyright (c) 2014-2017 Devin Teske +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +############################################################ IDENT(1) +# +# $Title: Watch processes as they enter a particular DTrace probe $ +# $FreeBSD$ +# +############################################################ CONFIGURATION + +# +# DTrace pragma settings +# +DTRACE_PRAGMA=" + option quiet + option dynvarsize=16m + option switchrate=10hz +" # END-QUOTE + +# +# Modules +# +: ${DWATCH_MODULES_PATH="/usr/libexec/dwatch:/usr/local/libexec/dwatch"} + +############################################################ GLOBALS + +pgm="${0##*/}" # Program basename + +# +# Command-line arguments +# +PROBE= +MODULE= + +# +# Command-line options +# +COUNT=0 # -c count +DEBUG= # -d +EVENT_CODE= # -e code +FILTER= # -f regex +GROUP= # -g group +LIST= # -l +MAX_ARGS=64 # -m num +MAX_DEPTH=64 # -n num +QUIET= # -q +USER= # -u user +VERBOSE= # -v + +# +# Global exit status +# +SUCCESS=0 +FAILURE=1 + +# +# Miscellaneous +# +ACTIONS= +DEFAULT_EVENT_TAG='printf("%d.%d %s[%d]: ", + this->uid0, this->gid0, execname, this->pid0);' +DETAILS= +EVENT_TAG= +ID=2 +PRINT=entry +RGID= +RUID= +SUDO= +export SUDO_PROMPT="[sudo] Password:" + +############################################################ FUNCTIONS + +usage() +{ + local optfmt="\t%-9s %s\n" + exec >&2 + [ "$*" ] && printf "%s: %s\n" "$pgm" "$*" + printf "Usage: %s [OPTIONS] probe | module\n" "$pgm" + printf " %s [OPTIONS] -l [pattern]\n" "$pgm" + printf "OPTIONS:\n" + printf "$optfmt" "-c count" \ + "Exit after count matching entries (Default 0 for disabled)." + printf "$optfmt" "-d" \ + "Debug. Send dtrace(1) script to stdout instead of executing." + printf "$optfmt" "-e code" \ + "DTrace code for event details. If \`-', read from stdin." + printf "$optfmt" "-f regex" \ + "Filter. Only show blocks matching awk(1) regular expression." + printf "$optfmt" "-g group" \ + "Group filter. Only show processes matching group name/gid." + printf "$optfmt" "-h" \ + "Help. Display syntax and available modules then exit." + printf "$optfmt" "-l" \ + "List available probe points on standard output and exit." + printf "$optfmt" "-M" \ + "Disable modules. Same as setting DWATCH_MODULES_PATH to NULL." + printf "$optfmt" "-m num" \ + "Maximum number of arguments to display (Default $MAX_ARGS)." + printf "$optfmt" "-n num" \ + "Maximum directory depth to display (Default $MAX_DEPTH)." + printf "$optfmt" "-q" \ + "Quiet. Disable informational messages displayed on stderr." + printf "$optfmt" "-u user" \ + "User filter. Only show processes matching user name/uid." + printf "$optfmt" "-v" \ + "Verbose. Show parent, grandparent, and ancestor of process." + + # Build a list of modules available + local modules + modules=$( { IFS=: + for dir in $DWATCH_MODULES_PATH; do + [ -d "$dir" ] || continue + for path in $dir/*; do + [ -f "$path" ] || continue + name="${path##*/}" + [ "$name" = "${name%%[!0-9A-Za-z_]*}" ] || + continue + echo $name + done + done + } | sort -u ) + + # Get the longest module name + local longest_module_name + longest_module_name=$( echo "$modules" | + awk -v N=0 '(L = length($0)) > N { N = L } END { print N }' ) + + # Get the width of the terminal + local max_size="$( stty size 2> /dev/null )" + : ${max_size:=24 80} + local max_width="${max_size#*[$IFS]}" + + # Determine how many columns we can display + local x=$longest_module_name ncols=1 + x=$(( $x + 8 )) # Accommodate leading tab character + x=$(( $x + 3 + $longest_module_name )) # Preload end of next column + while [ $x -lt $max_width ]; do + ncols=$(( $ncols + 1 )) + x=$(( $x + 3 + $longest_module_name )) + done + + printf "MODULES:\n" + echo "$modules" | + awk -v ncols=$ncols -v colsize=$longest_module_name ' + BEGIN { row_item[1] = "" } + function print_row() + { + printf "\t%-*s", colsize, row_item[1] + for (i = 2; i <= cur_col; i++) + printf " %-*s", colsize, row_item[i] + printf "\n" + } + { + n++ + cur_col = (( n - 1 ) % ncols ) + 1 + row_item[cur_col] = $0 + if ( cur_col == ncols ) print_row() + } + END { if (cur_col < ncols) print_row() }' + + exit $FAILURE +} + +pproc() +{ + local proc="curthread->td_proc" + [ $1 -gt 0 ] && proc="this->proc->p_pptr" + + awk 'NR > 1 && $0 { $0 = "\t" $0 } 1' <<-EOFPREAMBLE + this->proc = $proc; + this->pid$1 = this->proc->p_pid; + this->uid$1 = this->proc->p_ucred->cr_uid; + this->gid$1 = this->proc->p_ucred->cr_rgid; + this->p_args = this->proc ? this->proc->p_args : 0; + this->ar_length = this->p_args ? this->p_args->ar_length : 0; + this->ar_args = (char *)(this->p_args ? this->p_args->ar_args : 0); + + this->arg${1}_0 = this->ar_length > 0 ? + this->ar_args : stringof(this->proc->p_comm); + this->len = this->ar_length > 0 ? strlen(this->ar_args) + 1 : 0; + this->ar_args += this->len; + this->ar_length -= this->len; + + EOFPREAMBLE + + awk -v P=$1 -v MAX_ARGS=$MAX_ARGS ' + $0 { $0 = "\t" $0 } + buf = buf $0 "\n" { } + END { + while (++N <= MAX_ARGS) { + $0 = buf + gsub(/P/, P) + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFARGS + this->argP_N = this->ar_length > 0 ? this->ar_args : ""; + this->len = this->ar_length > 0 ? strlen(this->ar_args) + 1 : 0; + this->ar_args += this->len; + this->ar_length -= this->len; + + EOFARGS + + awk '$0 = "\t" $0' <<-EOFPROC + this->arg${1}_$(( $MAX_ARGS + 1 )) = this->ar_length > 0 ? "..." : ""; + EOFPROC +} + +pproc_dump() +{ + local OPTIND=1 OPTARG flag + local nl=1 verbose= + + while getopts nv flag; do + case "$flag" in + n) nl= ;; + v) verbose=1 ;; + esac + done + shift $(( $OPTIND - 1 )) + + [ "$verbose" ] && awk -v P=$1 ' + $0 { $0 = "\t" $0 } + buf = buf $0 "\n" { } + END { + $0 = buf + gsub(/S/, P < 3 ? sprintf("%" 7-2*(P+1) "s", "") : "") + gsub(/B/, P < 3 ? "\\" : "") + print + } + ' <<-EOFPREAMBLE + printf(" SB-+= %05d %d.%d %s", + this->pid$1, this->uid$1, this->gid$1, this->arg${1}_0); + EOFPREAMBLE + + awk -v P=$1 -v MAX_ARGS=$MAX_ARGS ' + $0 { $0 = "\t" $0 } + buf = buf $0 "\n" { } + END { + while (++N <= MAX_ARGS) { + $0 = buf + if (N == 1) sub(/^\t/, "") + gsub(/P/, P) + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFARGS + printf("%s%s", this->argP_N != "" ? " " : "", this->argP_N); + EOFARGS + + if [ "$nl" ]; then + awk '$0 = "\t" $0' <<-EOFTAIL + printf("%s", this->arg${1}_0 != "" ? "\\n" : ""); + EOFTAIL + fi +} + +############################################################ MAIN + +# +# Process command-line options +# +while getopts c:de:f:g:hlMm:n:qu:v flag; do + case "$flag" in + c) COUNT="$OPTARG" ;; + d) DEBUG=1 ;; + e) EVENT_CODE=1 DETAILS="$OPTARG" ;; + f) FILTER="$OPTARG" ;; + g) GROUP="$OPTARG" ;; + h) usage ;; # NOTREACHED + l) LIST=1 ;; + M) DWATCH_MODULES_PATH= ;; + m) MAX_ARGS="$OPTARG" ;; + n) MAX_DEPTH="$OPTARG" ;; + q) QUIET=1 ;; + u) USER="$OPTARG" ;; + v) VERBOSE=1 ;; + *) usage # NOTREACHED + esac +done +shift $(( $OPTIND - 1 )) + +# If we're running as root, no need for sudo(8) +[ "$( id -u )" != 0 ] && type sudo > /dev/null 2>&1 && SUDO=sudo + +# +# Read event code from stdin if `-' is the argument to `-e code' option +# +[ "$EVENT_CODE" -a "$DETAILS" = "-" ] && DETAILS=$( cat ) + +# +# List probes if `-l' was given +# +if [ "$LIST" ]; then + $SUDO dtrace -l | awk -v pattern="$1" '$NF == "entry" && + !_[$0 = $2 ":" (NF > 4 ? $3 : "") ":" $--NF]++ && + $0 ~ pattern' + exit +fi + +# +# Validate number of arguments +# +[ $# -gt 0 ] || usage "missing probe/module argument" # NOTREACHED + +# +# Validate `-c count' option argument +# +case "$COUNT" in +"") usage "-c option requires a number argument" ;; # NOTREACHED +*[!0-9]*) usage "-c argument must be a number" ;; # NOTREACHED +esac + +# +# Validate `-g group' option argument +# +case "$GROUP" in +"") : fall through ;; +*[!0-9]*) + if ! RGID=$( getent group | + awk -F: -v group="$GROUP" '$1 == group { print $3 }' ) + then + echo "$pgm: No such group: $GROUP" >&2 + exit $FAILURE + fi + ;; +*) RGID=$GROUP +esac + +# +# Validate `-m num' option argument +# +case "$MAX_ARGS" in +"") usage "-m option requires a number argument" ;; # NOTREACHED +*[!0-9]*) usage "-m argument must be a number" ;; # NOTREACHED +esac + +# +# Validate `-n num' option argument +# +case "$MAX_DEPTH" in +"") usage "-n option requires a number argument" ;; # NOTREACHED +*[!0-9]*) usage "-n argument must be a number" ;; # NOTREACHED +esac + +# +# Validate `-u user' option argument +# +case "$USER" in +"") : fall through ;; +*[!0-9]*) + if ! RUID=$( id -u "$USER" ); then + echo "$pgm: No such user: $USER" >&2 + exit $FAILURE + fi + ;; +*) RUID=$USER +esac + +# +# Load kernel module(s) if necessary +# +if type kldstat > /dev/null 2>&1; then + kldstat -qm dtrace || $SUDO kldload dtraceall || exit +fi + +# +# If argument is not fully qualified probe (e.g., one that contains ":"), check +# for a module by that name, otherwise try to expand the probe name. +# +PROBE="$1" +case "$PROBE" in +*[![:alnum:]_]*) : fall through ;; +*) + found=1 + oldIFS="$IFS" IFS=: + for dir in $DWATCH_MODULES_PATH; do + [ -d "$dir" ] || continue + [ -f "$dir/$PROBE" ] || continue + MODULE="$PROBE" found=1 + unset EVENT_TAG + . "$dir/$MODULE" + : ${PROBE:=$MODULE} + : ${ID:=2} + : ${PRINT:=entry} + : ${EVENT_TAG=$DEFAULT_EVENT_TAG} + break + done + IFS="$oldIFS" + + # A loaded module may have left PROBE unqualified + case "$PROBE" in + *[![:alnum:]_]*) : fall through ;; + *) + P=$( $SUDO dtrace -ln "$PROBE:entry" | awk ' + $NF == "entry" { print $2 "::" $(NF-1); exit found++ } + END { exit !found } + ' ) && PROBE="$P" + esac +esac + +# +# Header for watched probe entry +# +[ "$MODULE" ] || case "$PROBE" in +*:execve) + PRINT="return /execname != this->caller_execname/" + EVENT_TAG='printf("%d.%d %s[%d]: ", + this->uid1, this->gid1, this->caller_execname, this->pid1);' + ;; +*) + EVENT_TAG="$DEFAULT_EVENT_TAG" +esac + +# +# Event details +# +[ "$MODULE" -o "$EVENT_CODE" ] || DETAILS=" + printf(\"%s\", this->arg0_0); + $( pproc_dump -n 0 ) +" # END-QUOTE + +# +# DTrace script +# +# If `-d' is given, script is sent to stdout for debugging +# If `-c count", `-f regex', `-g group', or `-u user' is given, run script with +# dtrace and send output to awk(1) post-processor (making sure to preserve the +# exit code returned by dtrace invocation). Otherwise, simply run script with +# dtrace and then exit. +# +{ + if [ "$DEBUG" ]; then + # Send script to stdout + cat + exit + fi + if ! [ "$FILTER" -o "$COUNT" != 0 -o "$USER" -o "$GROUP" ]; then + # Run script without filter + $SUDO dtrace -s /dev/stdin + exit + fi + + if [ ! "$QUIET" ]; then + msg="dtrace:" + [ "$COUNT" ] && msg="$msg count: $COUNT" + [ "$FILTER" ] && msg="$msg filter: $FILTER" + [ "$GROUP" ] && msg="$msg group: $GROUP" + [ "$USER" ] && msg="$msg user: $USER" + echo "$msg" >&2 + fi + + # Send script output to post-processor for filtering + exec 3>&1 + exec 4>&2 + status=$( exec 5>&1; ( + trap 'echo $? >&5' EXIT + ( trap exit INT; $SUDO dtrace -s /dev/stdin ) & + echo $! + ) 2>&4 | awk \ + -v count="$COUNT" \ + -v filter="$FILTER" \ + -v gid=$RGID \ + -v uid=$RUID \ + -v verbose=$VERBOSE \ + ' # Start awk(1) post-processor + ############################################# BEGIN + BEGIN { + getline dtrace + pid = num = "[[:digit:]]+" + fmt = "(\n%6s\\\\-\\+= %s|^[^\n]*) %s\\.%s [^\n]*$" + uidfilter = sprintf(fmt, "", pid, uid, num) + gidfilter = sprintf(fmt, "", pid, num, gid) + } + ############################################# FUNCTIONS + function dump() { + lines = block + block = "" + if (uid && lines !~ uidfilter) return + if (gid && lines !~ gidfilter) return + if (filter && lines !~ filter) return + if (lines) print lines + ++matches + } + ############################################# MAIN + { block = (block ? block "\n" : block) $0 } + !verbose { dump() } + $0 ~ sprintf("^%6s\\\\-\\+= %s ", "", pid) { dump() } + count && matches >= count { exit } + ############################################# END + END { dump(); system(sprintf("kill %s", dtrace)) } + ' >&3 ) + exit $status + +} < + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \`\`AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * \$Title: dtrace(1) script to log process(es) entering $PROBE $ + * \$FreeBSD$ + */ + +$( echo "$DTRACE_PRAGMA" | awk ' + !/^[[:space:]]*(#|$)/, sub(/^[[:space:]]*/, "#pragma D ")||1 +' ) + +/*********************************************************/ + +syscall::execve:entry /* probe ID 1 */ +{ + this->caller_execname = execname; +} +${ACTIONS:+ +/*********************************************************/ + +$ACTIONS} +/*********************************************************/ + +$PROBE:$PRINT /* probe ID $ID */ +{ + /* + * Examine process, parent process, and grandparent process details + */ + + /******************* CURPROC *******************/ + + $( pproc 0 ) + + /******************* PPARENT *******************/ + + $( if [ "$VERBOSE" ]; then pproc 1; else echo " + this->proc = this->proc->p_pptr; + this->pid1 = this->proc->p_pid; + this->uid1 = this->proc->p_ucred->cr_uid; + this->gid1 = this->proc->p_ucred->cr_rgid; + "; fi ) + + /******************* GPARENT *******************/ + + $( [ "$VERBOSE" ] && pproc 2 ) + + /******************* APARENT *******************/ + + $( [ "$VERBOSE" ] && pproc 3 ) + + /***********************************************/ + + /* + * Print process, parent, grandparent, and ancestor details + */ + + printf("%Y ", timestamp + 1468045731600000000); + $EVENT_TAG + $DETAILS + printf("\\n"); + + $( if [ "$VERBOSE" ]; then + pproc_dump -v 3 + pproc_dump -v 2 + pproc_dump -v 1 + pproc_dump -v 0 + fi ) +} +EOF + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/dwatch.8 =================================================================== --- /dev/null +++ usr.sbin/dwatch/dwatch.8 @@ -0,0 +1,203 @@ +.\" Copyright (c) 2014-2017 Devin Teske +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd Mar 12, 2017 +.Dt DWATCH 8 +.Os +.Sh NAME +.Nm dwatch +.Nd Watch processes as they enter a particular DTrace probe +.Sh SYNOPSIS +.Nm +.Op OPTIONS +probe | module +.Nm +.Fl l Op pattern +.Sh DESCRIPTION +The +.Nm +utility uses +.Xr dtrace 1 +to display information when a process enters a given probe. +The following options are available: +.Bl -tag -width "-c count" +.It Fl c Ar count +Exit after +.Ar count +matching entries +.Pq Default 0 for disabled . +.It Fl d +Debug. +Send +.Xr dtrace 1 +script to stdout instead of executing. +.It Fl e Ar code +DTrace code for event details. +If `-', read from stdin. +This allows you to customize what is printed after the date/time and user info. +By default, the name and arguments of the program entering the probe are shown. +.It Fl f Ar regex +Filter. +Only show blocks matching +.Xr awk 1 +regular expression. +.It Fl g Ar group +Group filter. +Only show processes matching +.Ar group +name/gid. +.It Fl h +Help. +Display syntax and available modules then exit. +.It Fl l +List available probe points on standard output and exit. +.It Fl M +Disable modules. +Same as setting DWATCH_MODULES_PATH to NULL. +.It Fl m Ar num +Maximum number of arguments to display +.Pq Default 64 . +.It Fl n Ar num +Maximum directory depth to display +.Pq Default 64 . +.It Fl q +Quiet. +Disable informational messages displayed on stderr. +.It Fl u Ar user +User filter. +Only show processes matching +.Ar user +name/uid. +.It Fl v +Verbose. +Show parent, +grandparent, +and ancestor of process. +.El +.Pp +Either the name of a +.Xr dwatch 8 +module +.Pq available with Ql Nm Fl h +or +.Xr dtrace 1 +probe +.Pq available with Ql Nm Fl l +must be given as the first/only non-option argument. +.Pp +If given an argument consisting of only alpha-numeric/underscore characters, +.Xr dwatch 8 +checks for a module by that name in the colon-separated list of directories in +.Ev DWATCH_MODULES_PATH +.Pq unless given Ql Fl M . +If no module is found, +.Xr dwatch 8 +tries to expand the probe name using the following +.Xr dtrace 1 +command: +.Pp +.Bl -tag -width indent+ +.It +dtrace -ln :entry +.El +.Pp +For example, if given a probe of +.Dq Li foo , +.Xr dwatch 8 +will execute +.Ql Li dtrace -ln foo:entry +and take the first probe available. +.Pp +Each time a process enters the given probe, +the date/time of entry, +name/id of process, +and +.Pq if no module and not given Ql Fl e Ar code +the process name and arguments are printed. +.Pp +If the probe is +.Ql Li execve +the full name and arguments of the forked child process are instead shown. +.Sh MODULES +Modules customize the data printed during events. +Modules are loaded from a colon-separated list of directories in +.Ev DWATCH_MODULES_PATH . +Below is an incomplete list of modules available with a basic description: +.Pp +.Bl -tag -width "vop_readdir" +.It chmod +Print arguments being passed to chmod(2) +.It fchmod +Print arguments being passed to fchmod(2) +.It fchmodat +Print arguments being passed to fchmodat(2) +.It kill +Print arguments being passed to kill(2) +.It lchmod +Print arguments being passed to lchmod(2) +.It nanosleep +Print arguments being passed to lchmod(2) +.It vop_create +Print filesystem paths being created by VOP_CREATE(9) +.It vop_lookup +Print filesystem paths being looked-up by VOP_LOOKUP(9) +.It vop_mkdir +Print directory paths being created by VOP_MKDIR(9) +.It vop_mknod +Print device node paths being created by VOP_MKNOD(9) +.It vop_readdir +Print directory paths being read by VOP_READDIR(9) +.It vop_remove +Print filesystem paths being removed by VOP_REMOVE(9) +.It vop_rename +Print filesystem paths being renamed by VOP_RENAME(9) +.It vop_rmdir +Print directory paths being removed by VOP_RMDIR(9) +.It vop_symlink +Print symlink paths being created by VOP_SYMLINK(9) +.El +.Sh ENVIRONMENT VARIABLES +The following environment variables affect the execution of +.Nm : +.Bl -tag -width "DWATCH_MODULES_PATH" +.It Ev DWATCH_MODULES_PATH +If DWATCH_MODULES_PATH is set, +.Nm +will search for modules in the colon-separated list of directories +instead of the default +.Ql Li /usr/libexec/dwatch:/usr/local/libexec/dwatch . +If set to NULL, no modules will be loaded. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr dtrace 1 +.Sh HISTORY +.Nm +first appeared in +.Fx 12.0-CURRENT . +.Sh AUTHORS +.An Devin Teske Aq Mt dteske@FreeBSD.org Index: usr.sbin/dwatch/examples/Makefile =================================================================== --- /dev/null +++ usr.sbin/dwatch/examples/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +FILESDIR= ${SHAREDIR}/examples/dwatch +FILES= module_template + +.include Index: usr.sbin/dwatch/examples/module_template =================================================================== --- /dev/null +++ usr.sbin/dwatch/examples/module_template @@ -0,0 +1,65 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for XXX entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Put some text here +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +#PROBE= + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +#exec 9< Index: usr.sbin/dwatch/libexec/chmod =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/chmod @@ -0,0 +1,66 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for chmod(2) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print arguments being passed to chmod(2) +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=syscall::chmod + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +#exec 9<path = (string)copyinstr(arg0); + printf("chmod(path \"%s\", mode %04o)", this->path, (mode_t)arg1); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/fchmod =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/fchmod @@ -0,0 +1,65 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for fchmod(2) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print arguments being passed to fchmod(2) +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=syscall::fchmod + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +#exec 9<path = (string)copyinstr(arg1); + printf("fchmodat(fd %i, path \"%s\", mode %04o, flag %i)", + (int)arg0, this->path, (mode_t)arg2, (int)arg3); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/kill =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/kill @@ -0,0 +1,67 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for kill(2) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print arguments being passed to kill(2) +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=syscall::kill + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +#exec 9< 0 ? arg0 : arg0 == -1 ? -1 : arg0 * -1, + (int)arg1); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/lchmod =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/lchmod @@ -0,0 +1,66 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for lchmod(2) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print arguments being passed to lchmod(2) +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=syscall::lchmod + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +#exec 9<path = (string)copyinstr(arg0); + printf("lchmod(path \"%s\", mode %04o)", this->path, (mode_t)arg1); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/nanosleep =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/nanosleep @@ -0,0 +1,69 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for nanosleep(2) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print arguments being passed to nanosleep(2) +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=syscall::nanosleep + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +#exec 9<rqtp = (struct timespec *)copyin(arg0, sizeof(struct timespec)); + this->rmtp = (struct timespec *)copyin(arg1, sizeof(struct timespec)); + printf("nanosleep(rqtp {%d, %d}, rmtp {%d, %d})", + this->rqtp->tv_sec, this->rqtp->tv_nsec, + this->rmtp->tv_sec, this->rmtp->tv_nsec); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/vop_create =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/vop_create @@ -0,0 +1,175 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for VOP_CREATE(9) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print filesystem paths being created by VOP_CREATE(9) +# NB: All paths are shown even if error prevents their creation. +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=vfs:vop:vop_create + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +exec 9<vp = (struct vnode *)arg0; + this->ncp = this->vp != NULL ? + this->vp->v_cache_dst.tqh_first : 0; + this->fi_name = args[1] ? ( + args[1]->a_cnp != NULL ? + stringof(args[1]->a_cnp->cn_nameptr) : "" + ) : ""; + this->mount = this->vp != NULL ? + this->vp->v_mount : NULL; /* ptr to vfs we are in */ + this->fi_fs = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_fstypename) : ""; + this->fi_mount = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_mntonname) : ""; + this->d_name = args[0]->v_cache_dd != NULL ? + stringof(args[0]->v_cache_dd->nc_name) : ""; +} + +$PROBE:entry /this->vp == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->fi_name == ""/ /* probe ID 3 */ +{ + this->ncp = 0; +} + +/*********************************************************/ + +$PROBE:entry /this->ncp/ /* probe ID 4 (depth 1) */ +{ + this->dvp = this->ncp->nc_dvp != NULL ? ( + this->ncp->nc_dvp != NULL ? + this->ncp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->name1 = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->name1 == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->name1 == "/" || this->name1 == ""/ /* probe ID 5 */ +{ + this->dvp = 0; +} + +/*********************************************************/ + +/* + * BEGIN Pathname-depth iterators + */ + +$( awk -v ID=6 -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (DEPTH = 2; DEPTH <= MAX_DEPTH; DEPTH++) { + $0 = buf + gsub(/DEPTH/, DEPTH) + gsub(/IDNUM/, ID++) + sub(/\n$/, "") + print + } + } +' <dvp/ /* probe ID IDNUM (depth DEPTH) */ +{ + this->dvp = this->dvp->nc_dvp != NULL ? ( + this->dvp->nc_dvp != NULL ? + this->dvp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->nameDEPTH = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +EOFDEPTH +) + +/* + * END Pathname-depth iterators + */ +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $MAX_DEPTH + 5 )) + +############################################################ PRINT ACTION + +# The default value is simply `entry'. This is paired with $PROBE (which can be +# auto-probed when unset or NULL) and can be customized to include clauses for +# the final print action which must be true before the print action will run. + +PRINT="entry /this->fi_mount != 0/" + +############################################################ EVENT TAG + +# The EVENT_TAG is run inside the print action after the timestamp has been +# printed. By default, `UID.GID CMD[PID]: ' of the process is printed. + +#exec 9<fi_mount, this->fi_mount != 0 ? ( + this->fi_mount == "/" ? "" : "/" + ) : "/"); + $( awk -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (N = MAX_DEPTH; N > 0; N--) { + $0 = (N < MAX_DEPTH ? "\t" : "") buf + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFDEPTH + printf("%s%s", this->name = this->nameN, this->name != "" ? "/" : ""); + EOFDEPTH + ) + + /* Print the parent directory name */ + this->name = this->d_name != 0 ? this->d_name : ""; + printf("%s%s", this->name, this->name != "" ? "/" : ""); + + /* Print the entry name */ + this->name = this->fi_name != 0 ? this->fi_name : ""; + printf("%s", this->name); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/vop_lookup =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/vop_lookup @@ -0,0 +1,175 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for VOP_LOOKUP(9) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print filesystem paths being looked-up by VOP_LOOKUP(9) +# NB: All paths are shown even if error prevents their lookup. +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=vfs:vop:vop_lookup + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +exec 9<vp = (struct vnode *)arg0; + this->ncp = this->vp != NULL ? + this->vp->v_cache_dst.tqh_first : 0; + this->fi_name = args[1] ? ( + args[1]->a_cnp != NULL ? + stringof(args[1]->a_cnp->cn_nameptr) : "" + ) : ""; + this->mount = this->vp != NULL ? + this->vp->v_mount : NULL; /* ptr to vfs we are in */ + this->fi_fs = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_fstypename) : ""; + this->fi_mount = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_mntonname) : ""; + this->d_name = args[0]->v_cache_dd != NULL ? + stringof(args[0]->v_cache_dd->nc_name) : ""; +} + +$PROBE:entry /this->vp == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->fi_name == ""/ /* probe ID 3 */ +{ + this->ncp = 0; +} + +/*********************************************************/ + +$PROBE:entry /this->ncp/ /* probe ID 4 (depth 1) */ +{ + this->dvp = this->ncp->nc_dvp != NULL ? ( + this->ncp->nc_dvp != NULL ? + this->ncp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->name1 = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->name1 == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->name1 == "/" || this->name1 == ""/ /* probe ID 5 */ +{ + this->dvp = 0; +} + +/*********************************************************/ + +/* + * BEGIN Pathname-depth iterators + */ + +$( awk -v ID=6 -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (DEPTH = 2; DEPTH <= MAX_DEPTH; DEPTH++) { + $0 = buf + gsub(/DEPTH/, DEPTH) + gsub(/IDNUM/, ID++) + sub(/\n$/, "") + print + } + } +' <dvp/ /* probe ID IDNUM (depth DEPTH) */ +{ + this->dvp = this->dvp->nc_dvp != NULL ? ( + this->dvp->nc_dvp != NULL ? + this->dvp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->nameDEPTH = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +EOFDEPTH +) + +/* + * END Pathname-depth iterators + */ +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $MAX_DEPTH + 5 )) + +############################################################ PRINT ACTION + +# The default value is simply `entry'. This is paired with $PROBE (which can be +# auto-probed when unset or NULL) and can be customized to include clauses for +# the final print action which must be true before the print action will run. + +PRINT="entry /this->fi_mount != 0/" + +############################################################ EVENT TAG + +# The EVENT_TAG is run inside the print action after the timestamp has been +# printed. By default, `UID.GID CMD[PID]: ' of the process is printed. + +#exec 9<fi_mount, this->fi_mount != 0 ? ( + this->fi_mount == "/" ? "" : "/" + ) : "/"); + $( awk -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (N = MAX_DEPTH; N > 0; N--) { + $0 = (N < MAX_DEPTH ? "\t" : "") buf + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFDEPTH + printf("%s%s", this->name = this->nameN, this->name != "" ? "/" : ""); + EOFDEPTH + ) + + /* Print the parent directory name */ + this->name = this->d_name != 0 ? this->d_name : ""; + printf("%s%s", this->name, this->name != "" ? "/" : ""); + + /* Print the entry name */ + this->name = this->fi_name != 0 ? this->fi_name : ""; + printf("%s", this->name); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/vop_mkdir =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/vop_mkdir @@ -0,0 +1,175 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for VOP_MKDIR(9) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print directory paths being created by VOP_MKDIR(9) +# NB: All paths are shown even if error prevents their creation. +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=vfs:vop:vop_mkdir + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +exec 9<vp = (struct vnode *)arg0; + this->ncp = this->vp != NULL ? + this->vp->v_cache_dst.tqh_first : 0; + this->fi_name = args[1] ? ( + args[1]->a_cnp != NULL ? + stringof(args[1]->a_cnp->cn_nameptr) : "" + ) : ""; + this->mount = this->vp != NULL ? + this->vp->v_mount : NULL; /* ptr to vfs we are in */ + this->fi_fs = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_fstypename) : ""; + this->fi_mount = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_mntonname) : ""; + this->d_name = args[0]->v_cache_dd != NULL ? + stringof(args[0]->v_cache_dd->nc_name) : ""; +} + +$PROBE:entry /this->vp == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->fi_name == ""/ /* probe ID 3 */ +{ + this->ncp = 0; +} + +/*********************************************************/ + +$PROBE:entry /this->ncp/ /* probe ID 4 (depth 1) */ +{ + this->dvp = this->ncp->nc_dvp != NULL ? ( + this->ncp->nc_dvp != NULL ? + this->ncp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->name1 = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->name1 == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->name1 == "/" || this->name1 == ""/ /* probe ID 5 */ +{ + this->dvp = 0; +} + +/*********************************************************/ + +/* + * BEGIN Pathname-depth iterators + */ + +$( awk -v ID=6 -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (DEPTH = 2; DEPTH <= MAX_DEPTH; DEPTH++) { + $0 = buf + gsub(/DEPTH/, DEPTH) + gsub(/IDNUM/, ID++) + sub(/\n$/, "") + print + } + } +' <dvp/ /* probe ID IDNUM (depth DEPTH) */ +{ + this->dvp = this->dvp->nc_dvp != NULL ? ( + this->dvp->nc_dvp != NULL ? + this->dvp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->nameDEPTH = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +EOFDEPTH +) + +/* + * END Pathname-depth iterators + */ +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $MAX_DEPTH + 5 )) + +############################################################ PRINT ACTION + +# The default value is simply `entry'. This is paired with $PROBE (which can be +# auto-probed when unset or NULL) and can be customized to include clauses for +# the final print action which must be true before the print action will run. + +PRINT="entry /this->fi_mount != 0/" + +############################################################ EVENT TAG + +# The EVENT_TAG is run inside the print action after the timestamp has been +# printed. By default, `UID.GID CMD[PID]: ' of the process is printed. + +#exec 9<fi_mount, this->fi_mount != 0 ? ( + this->fi_mount == "/" ? "" : "/" + ) : "/"); + $( awk -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (N = MAX_DEPTH; N > 0; N--) { + $0 = (N < MAX_DEPTH ? "\t" : "") buf + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFDEPTH + printf("%s%s", this->name = this->nameN, this->name != "" ? "/" : ""); + EOFDEPTH + ) + + /* Print the parent directory name */ + this->name = this->d_name != 0 ? this->d_name : ""; + printf("%s%s", this->name, this->name != "" ? "/" : ""); + + /* Print the entry name */ + this->name = this->fi_name != 0 ? this->fi_name : ""; + printf("%s", this->name); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/vop_mknod =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/vop_mknod @@ -0,0 +1,175 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for VOP_MKNOD(9) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print device node paths being created by VOP_MKNOD(9) +# NB: All paths are shown even if error prevents their creation. +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=vfs:vop:vop_mknod + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +exec 9<vp = (struct vnode *)arg0; + this->ncp = this->vp != NULL ? + this->vp->v_cache_dst.tqh_first : 0; + this->fi_name = args[1] ? ( + args[1]->a_cnp != NULL ? + stringof(args[1]->a_cnp->cn_nameptr) : "" + ) : ""; + this->mount = this->vp != NULL ? + this->vp->v_mount : NULL; /* ptr to vfs we are in */ + this->fi_fs = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_fstypename) : ""; + this->fi_mount = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_mntonname) : ""; + this->d_name = args[0]->v_cache_dd != NULL ? + stringof(args[0]->v_cache_dd->nc_name) : ""; +} + +$PROBE:entry /this->vp == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->fi_name == ""/ /* probe ID 3 */ +{ + this->ncp = 0; +} + +/*********************************************************/ + +$PROBE:entry /this->ncp/ /* probe ID 4 (depth 1) */ +{ + this->dvp = this->ncp->nc_dvp != NULL ? ( + this->ncp->nc_dvp != NULL ? + this->ncp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->name1 = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->name1 == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->name1 == "/" || this->name1 == ""/ /* probe ID 5 */ +{ + this->dvp = 0; +} + +/*********************************************************/ + +/* + * BEGIN Pathname-depth iterators + */ + +$( awk -v ID=6 -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (DEPTH = 2; DEPTH <= MAX_DEPTH; DEPTH++) { + $0 = buf + gsub(/DEPTH/, DEPTH) + gsub(/IDNUM/, ID++) + sub(/\n$/, "") + print + } + } +' <dvp/ /* probe ID IDNUM (depth DEPTH) */ +{ + this->dvp = this->dvp->nc_dvp != NULL ? ( + this->dvp->nc_dvp != NULL ? + this->dvp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->nameDEPTH = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +EOFDEPTH +) + +/* + * END Pathname-depth iterators + */ +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $MAX_DEPTH + 5 )) + +############################################################ PRINT ACTION + +# The default value is simply `entry'. This is paired with $PROBE (which can be +# auto-probed when unset or NULL) and can be customized to include clauses for +# the final print action which must be true before the print action will run. + +PRINT="entry /this->fi_mount != 0/" + +############################################################ EVENT TAG + +# The EVENT_TAG is run inside the print action after the timestamp has been +# printed. By default, `UID.GID CMD[PID]: ' of the process is printed. + +#exec 9<fi_mount, this->fi_mount != 0 ? ( + this->fi_mount == "/" ? "" : "/" + ) : "/"); + $( awk -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (N = MAX_DEPTH; N > 0; N--) { + $0 = (N < MAX_DEPTH ? "\t" : "") buf + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFDEPTH + printf("%s%s", this->name = this->nameN, this->name != "" ? "/" : ""); + EOFDEPTH + ) + + /* Print the parent directory name */ + this->name = this->d_name != 0 ? this->d_name : ""; + printf("%s%s", this->name, this->name != "" ? "/" : ""); + + /* Print the entry name */ + this->name = this->fi_name != 0 ? this->fi_name : ""; + printf("%s", this->name); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/vop_readdir =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/vop_readdir @@ -0,0 +1,166 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for VOP_READDIR(9) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print directory paths being read by VOP_READDIR(9) +# NB: All paths are shown even if error prevents their reading. +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=vfs:vop:vop_readdir + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +exec 9<vp = (struct vnode *)arg0; + this->ncp = this->vp != NULL ? + this->vp->v_cache_dst.tqh_first : 0; + this->mount = this->vp != NULL ? + this->vp->v_mount : NULL; /* ptr to vfs we are in */ + this->fi_fs = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_fstypename) : ""; + this->fi_mount = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_mntonname) : ""; + this->d_name = args[0]->v_cache_dd != NULL ? + stringof(args[0]->v_cache_dd->nc_name) : ""; +} + +$PROBE:entry /this->vp == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == ""/ /* probe ID 3 */ +{ + this->ncp = 0; +} + +/*********************************************************/ + +$PROBE:entry /this->ncp/ /* probe ID 4 (depth 1) */ +{ + this->dvp = this->ncp->nc_dvp != NULL ? ( + this->ncp->nc_dvp != NULL ? + this->ncp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->name1 = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->name1 == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->name1 == "/" || this->name1 == ""/ /* probe ID 5 */ +{ + this->dvp = 0; +} + +/*********************************************************/ + +/* + * BEGIN Pathname-depth iterators + */ + +$( awk -v ID=6 -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (DEPTH = 2; DEPTH <= MAX_DEPTH; DEPTH++) { + $0 = buf + gsub(/DEPTH/, DEPTH) + gsub(/IDNUM/, ID++) + sub(/\n$/, "") + print + } + } +' <dvp/ /* probe ID IDNUM (depth DEPTH) */ +{ + this->dvp = this->dvp->nc_dvp != NULL ? ( + this->dvp->nc_dvp != NULL ? + this->dvp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->nameDEPTH = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +EOFDEPTH +) + +/* + * END Pathname-depth iterators + */ +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $MAX_DEPTH + 5 )) + +############################################################ PRINT ACTION + +# The default value is simply `entry'. This is paired with $PROBE (which can be +# auto-probed when unset or NULL) and can be customized to include clauses for +# the final print action which must be true before the print action will run. + +PRINT="entry /this->fi_mount != 0/" + +############################################################ EVENT TAG + +# The EVENT_TAG is run inside the print action after the timestamp has been +# printed. By default, `UID.GID CMD[PID]: ' of the process is printed. + +#exec 9<fi_mount, this->fi_mount != 0 ? ( + this->fi_mount == "/" ? "" : "/" + ) : "/"); + $( awk -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (N = MAX_DEPTH; N > 0; N--) { + $0 = (N < MAX_DEPTH ? "\t" : "") buf + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFDEPTH + printf("%s%s", this->name = this->nameN, this->name != "" ? "/" : ""); + EOFDEPTH + ) + + /* Print the parent directory name */ + this->name = this->d_name != 0 ? this->d_name : ""; + printf("%s%s", this->name, this->name != "" ? "/" : ""); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/vop_remove =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/vop_remove @@ -0,0 +1,175 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for VOP_REMOVE(9) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print filesystem paths being removed by VOP_REMOVE(9) +# NB: All paths are shown even if error prevents their removal. +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=vfs:vop:vop_remove + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +exec 9<vp = (struct vnode *)arg0; + this->ncp = this->vp != NULL ? + this->vp->v_cache_dst.tqh_first : 0; + this->fi_name = args[1] ? ( + args[1]->a_cnp != NULL ? + stringof(args[1]->a_cnp->cn_nameptr) : "" + ) : ""; + this->mount = this->vp != NULL ? + this->vp->v_mount : NULL; /* ptr to vfs we are in */ + this->fi_fs = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_fstypename) : ""; + this->fi_mount = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_mntonname) : ""; + this->d_name = args[0]->v_cache_dd != NULL ? + stringof(args[0]->v_cache_dd->nc_name) : ""; +} + +$PROBE:entry /this->vp == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->fi_name == ""/ /* probe ID 3 */ +{ + this->ncp = 0; +} + +/*********************************************************/ + +$PROBE:entry /this->ncp/ /* probe ID 4 (depth 1) */ +{ + this->dvp = this->ncp->nc_dvp != NULL ? ( + this->ncp->nc_dvp != NULL ? + this->ncp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->name1 = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->name1 == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->name1 == "/" || this->name1 == ""/ /* probe ID 5 */ +{ + this->dvp = 0; +} + +/*********************************************************/ + +/* + * BEGIN Pathname-depth iterators + */ + +$( awk -v ID=6 -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (DEPTH = 2; DEPTH <= MAX_DEPTH; DEPTH++) { + $0 = buf + gsub(/DEPTH/, DEPTH) + gsub(/IDNUM/, ID++) + sub(/\n$/, "") + print + } + } +' <dvp/ /* probe ID IDNUM (depth DEPTH) */ +{ + this->dvp = this->dvp->nc_dvp != NULL ? ( + this->dvp->nc_dvp != NULL ? + this->dvp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->nameDEPTH = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +EOFDEPTH +) + +/* + * END Pathname-depth iterators + */ +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $MAX_DEPTH + 5 )) + +############################################################ PRINT ACTION + +# The default value is simply `entry'. This is paired with $PROBE (which can be +# auto-probed when unset or NULL) and can be customized to include clauses for +# the final print action which must be true before the print action will run. + +PRINT="entry /this->fi_mount != 0/" + +############################################################ EVENT TAG + +# The EVENT_TAG is run inside the print action after the timestamp has been +# printed. By default, `UID.GID CMD[PID]: ' of the process is printed. + +#exec 9<fi_mount, this->fi_mount != 0 ? ( + this->fi_mount == "/" ? "" : "/" + ) : "/"); + $( awk -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (N = MAX_DEPTH; N > 0; N--) { + $0 = (N < MAX_DEPTH ? "\t" : "") buf + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFDEPTH + printf("%s%s", this->name = this->nameN, this->name != "" ? "/" : ""); + EOFDEPTH + ) + + /* Print the parent directory name */ + this->name = this->d_name != 0 ? this->d_name : ""; + printf("%s%s", this->name, this->name != "" ? "/" : ""); + + /* Print the entry name */ + this->name = this->fi_name != 0 ? this->fi_name : ""; + printf("%s", this->name); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/vop_rename =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/vop_rename @@ -0,0 +1,262 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for VOP_RENAME(9) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print filesystem paths being renamed by VOP_RENAME(9) +# NB: All paths are shown even if error prevents their rename. +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=vfs:vop:vop_rename + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +exec 9<fvp = args[1] ? args[1]->a_fdvp : NULL; + this->fncp = this->fvp != NULL ? + this->fvp->v_cache_dst.tqh_first : 0; + this->ffi_name = args[1] ? ( + args[1]->a_fcnp != NULL ? + stringof(args[1]->a_fcnp->cn_nameptr) : "" + ) : ""; + this->fmount = this->fvp != NULL ? + this->fvp->v_mount : NULL; /* ptr to vfs we are in */ + this->ffi_fs = this->fmount != NULL ? + stringof(this->fmount->mnt_stat.f_fstypename) : ""; + this->ffi_mount = this->fmount != NULL ? + stringof(this->fmount->mnt_stat.f_mntonname) : ""; + this->fd_name = args[0]->v_cache_dd != NULL ? + stringof(args[0]->v_cache_dd->nc_name) : ""; + + this->tvp = args[1] ? args[1]->a_tdvp : NULL; + this->tncp = this->tvp != NULL ? + this->tvp->v_cache_dst.tqh_first : 0; + this->tfi_name = args[1] ? ( + args[1]->a_tcnp != NULL ? + stringof(args[1]->a_tcnp->cn_nameptr) : "" + ) : ""; + this->tmount = this->tvp != NULL ? + this->tvp->v_mount : NULL; /* ptr to vfs we are in */ + this->tfi_fs = this->tmount != NULL ? + stringof(this->tmount->mnt_stat.f_fstypename) : ""; + this->tfi_mount = this->tmount != NULL ? + stringof(this->tmount->mnt_stat.f_mntonname) : ""; + this->td_name = this->tvp != NULL ? ( + this->tvp->v_cache_dd != NULL ? + stringof(this->tvp->v_cache_dd->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->fvp == 0 || this->ffi_fs == 0 || + this->ffi_fs == "devfs" || this->ffi_fs == "" || + this->ffi_name == ""/ /* probe ID 3 */ +{ + this->fncp = 0; +} + +$PROBE:entry /this->tvp == 0 || this->tfi_fs == 0 || + this->tfi_fs == "devfs" || this->tfi_fs == "" || + this->tfi_name == ""/ /* probe ID 4 */ +{ + this->tncp = 0; +} + +/*********************************************************/ + +$PROBE:entry /this->fncp/ /* probe ID 5 (depth 1) */ +{ + this->fdvp = this->fncp->nc_dvp != NULL ? ( + this->fncp->nc_dvp != NULL ? + this->fncp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->fname1 = this->fdvp != 0 ? ( + this->fdvp->nc_name != 0 ? stringof(this->fdvp->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->tncp/ /* probe ID 6 (depth 1) */ +{ + this->tdvp = this->tncp->nc_dvp != NULL ? ( + this->tncp->nc_dvp != NULL ? + this->tncp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->tname1 = this->tdvp != 0 ? ( + this->tdvp->nc_name != 0 ? stringof(this->tdvp->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->fname1 == 0 || this->ffi_fs == 0 || + this->ffi_fs == "devfs" || this->ffi_fs == "" || + this->fname1 == "/" || this->fname1 == ""/ /* probe ID 7 */ +{ + this->fdvp = 0; +} + +$PROBE:entry /this->tname1 == 0 || this->tfi_fs == 0 || + this->tfi_fs == "devfs" || this->tfi_fs == "" || + this->tname1 == "/" || this->tname1 == ""/ /* probe ID 8 */ +{ + this->tdvp = 0; +} + +/*********************************************************/ + +/* + * BEGIN Pathname-depth iterators + */ + +$( awk -v ID=9 -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (DEPTH = 2; DEPTH <= MAX_DEPTH; DEPTH++) { + $0 = buf + gsub(/DEPTH/, DEPTH) + gsub(/IDNUM1/, ID) + gsub(/IDNUM2/, ID + 1) + sub(/\n$/, "") + print + ID = ID + 2 + } + } +' <fdvp/ /* probe ID IDNUM1 (depth DEPTH) */ +{ + this->fdvp = this->fdvp->nc_dvp != NULL ? ( + this->fdvp->nc_dvp != NULL ? + this->fdvp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->fnameDEPTH = this->fdvp != 0 ? ( + this->fdvp->nc_name != 0 ? stringof(this->fdvp->nc_name) : "" + ) : ""; +} +$PROBE:entry /this->tdvp/ /* probe ID IDNUM2 (depth DEPTH) */ +{ + this->tdvp = this->tdvp->nc_dvp != NULL ? ( + this->tdvp->nc_dvp != NULL ? + this->tdvp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->tnameDEPTH = this->tdvp != 0 ? ( + this->tdvp->nc_name != 0 ? stringof(this->tdvp->nc_name) : "" + ) : ""; +} + +EOFDEPTH +) + +/* + * END Pathname-depth iterators + */ +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $MAX_DEPTH + 8 )) + +############################################################ PRINT ACTION + +# The default value is simply `entry'. This is paired with $PROBE (which can be +# auto-probed when unset or NULL) and can be customized to include clauses for +# the final print action which must be true before the print action will run. + +PRINT="entry /this->ffi_mount != 0 && this->tfi_mount != 0/" + +############################################################ EVENT TAG + +# The EVENT_TAG is run inside the print action after the timestamp has been +# printed. By default, `UID.GID CMD[PID]: ' of the process is printed. + +#exec 9<ffi_mount, this->ffi_mount != 0 ? ( + this->ffi_mount == "/" ? "" : "/" + ) : "/"); + $( awk -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (N = MAX_DEPTH; N > 0; N--) { + $0 = (N < MAX_DEPTH ? "\t" : "") buf + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFDEPTH + printf("%s%s", this->fname = this->fnameN, this->fname != "" ? "/" : ""); + EOFDEPTH + ) + + /* Print the 'from' parent directory name */ + this->fname = this->fd_name != 0 ? this->fd_name : ""; + printf("%s%s", this->fname, this->fname != "" ? "/" : ""); + + /* Print the 'from' entry name */ + this->fname = this->ffi_name != 0 ? this->ffi_name : ""; + printf("%s", this->fname); + + printf(" -> "); + + /* + * Print 'to' full path + * NB: Up-to but not including the parent directory (printed below) + */ + printf("%s%s", this->tfi_mount, this->tfi_mount != 0 ? ( + this->tfi_mount == "/" ? "" : "/" + ) : "/"); + $( awk -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (N = MAX_DEPTH; N > 0; N--) { + $0 = (N < MAX_DEPTH ? "\t" : "") buf + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFDEPTH + printf("%s%s", this->tname = this->tnameN, this->tname != "" ? "/" : ""); + EOFDEPTH + ) + + /* Print the 'to' parent directory name */ + this->tname = this->td_name != 0 ? this->td_name : ""; + printf("%s%s", this->tname, this->tname != "" ? "/" : ""); + + /* Print the 'to' entry name */ + this->tname = this->tfi_name != 0 ? this->tfi_name : ""; + printf("%s", this->tname); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/vop_rmdir =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/vop_rmdir @@ -0,0 +1,175 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for VOP_RMDIR(9) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print directory paths being removed by VOP_RMDIR(9) +# NB: All paths are shown even if error prevents their removal. +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=vfs:vop:vop_rmdir + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +exec 9<vp = (struct vnode *)arg0; + this->ncp = this->vp != NULL ? + this->vp->v_cache_dst.tqh_first : 0; + this->fi_name = args[1] ? ( + args[1]->a_cnp != NULL ? + stringof(args[1]->a_cnp->cn_nameptr) : "" + ) : ""; + this->mount = this->vp != NULL ? + this->vp->v_mount : NULL; /* ptr to vfs we are in */ + this->fi_fs = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_fstypename) : ""; + this->fi_mount = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_mntonname) : ""; + this->d_name = args[0]->v_cache_dd != NULL ? + stringof(args[0]->v_cache_dd->nc_name) : ""; +} + +$PROBE:entry /this->vp == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->fi_name == ""/ /* probe ID 3 */ +{ + this->ncp = 0; +} + +/*********************************************************/ + +$PROBE:entry /this->ncp/ /* probe ID 4 (depth 1) */ +{ + this->dvp = this->ncp->nc_dvp != NULL ? ( + this->ncp->nc_dvp != NULL ? + this->ncp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->name1 = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->name1 == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->name1 == "/" || this->name1 == ""/ /* probe ID 5 */ +{ + this->dvp = 0; +} + +/*********************************************************/ + +/* + * BEGIN Pathname-depth iterators + */ + +$( awk -v ID=6 -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (DEPTH = 2; DEPTH <= MAX_DEPTH; DEPTH++) { + $0 = buf + gsub(/DEPTH/, DEPTH) + gsub(/IDNUM/, ID++) + sub(/\n$/, "") + print + } + } +' <dvp/ /* probe ID IDNUM (depth DEPTH) */ +{ + this->dvp = this->dvp->nc_dvp != NULL ? ( + this->dvp->nc_dvp != NULL ? + this->dvp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->nameDEPTH = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +EOFDEPTH +) + +/* + * END Pathname-depth iterators + */ +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $MAX_DEPTH + 5 )) + +############################################################ PRINT ACTION + +# The default value is simply `entry'. This is paired with $PROBE (which can be +# auto-probed when unset or NULL) and can be customized to include clauses for +# the final print action which must be true before the print action will run. + +PRINT="entry /this->fi_mount != 0/" + +############################################################ EVENT TAG + +# The EVENT_TAG is run inside the print action after the timestamp has been +# printed. By default, `UID.GID CMD[PID]: ' of the process is printed. + +#exec 9<fi_mount, this->fi_mount != 0 ? ( + this->fi_mount == "/" ? "" : "/" + ) : "/"); + $( awk -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (N = MAX_DEPTH; N > 0; N--) { + $0 = (N < MAX_DEPTH ? "\t" : "") buf + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFDEPTH + printf("%s%s", this->name = this->nameN, this->name != "" ? "/" : ""); + EOFDEPTH + ) + + /* Print the parent directory name */ + this->name = this->d_name != 0 ? this->d_name : ""; + printf("%s%s", this->name, this->name != "" ? "/" : ""); + + /* Print the entry name */ + this->name = this->fi_name != 0 ? this->fi_name : ""; + printf("%s", this->name); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################ Index: usr.sbin/dwatch/libexec/vop_symlink =================================================================== --- /dev/null +++ usr.sbin/dwatch/libexec/vop_symlink @@ -0,0 +1,179 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for VOP_SYMLINK(9) entry $ +# $Copyright: 2014-2017 Devin Teske. All rights reserved. $ +# $FreeBSD$ +# +############################################################ DESCRIPTION +# +# Print symlink paths being created by VOP_SYMLINK(9) +# NB: All paths are shown even if error prevents their creation. +# +############################################################ PROBE + +# This is optional, if you don't set or it is NULL, dwatch(8) will try to +# dynamically determine the probe via the module's filename. For example, if +# the module file is named `foo' then dwatch(8) will try `dtrace -ln foo:entry' +# to determine an appropriate probe for watching `foo'. + +PROBE=vfs:vop:vop_symlink + +############################################################ ACTIONS + +# Optional actions to be performed before hitting the final print action + +exec 9<vp = (struct vnode *)arg0; + this->ncp = this->vp != NULL ? + this->vp->v_cache_dst.tqh_first : 0; + this->target = args[1] ? args[1]->a_target : ""; + this->fi_name = args[1] ? ( + args[1]->a_cnp != NULL ? + stringof(args[1]->a_cnp->cn_nameptr) : "" + ) : ""; + this->mount = this->vp != NULL ? + this->vp->v_mount : NULL; /* ptr to vfs we are in */ + this->fi_fs = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_fstypename) : ""; + this->fi_mount = this->mount != NULL ? + stringof(this->mount->mnt_stat.f_mntonname) : ""; + this->d_name = args[0]->v_cache_dd != NULL ? + stringof(args[0]->v_cache_dd->nc_name) : ""; +} + +$PROBE:entry /this->vp == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->fi_name == ""/ /* probe ID 3 */ +{ + this->ncp = 0; +} + +/*********************************************************/ + +$PROBE:entry /this->ncp/ /* probe ID 4 (depth 1) */ +{ + this->dvp = this->ncp->nc_dvp != NULL ? ( + this->ncp->nc_dvp != NULL ? + this->ncp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->name1 = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +$PROBE:entry /this->name1 == 0 || this->fi_fs == 0 || + this->fi_fs == "devfs" || this->fi_fs == "" || + this->name1 == "/" || this->name1 == ""/ /* probe ID 5 */ +{ + this->dvp = 0; +} + +/*********************************************************/ + +/* + * BEGIN Pathname-depth iterators + */ + +$( awk -v ID=6 -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (DEPTH = 2; DEPTH <= MAX_DEPTH; DEPTH++) { + $0 = buf + gsub(/DEPTH/, DEPTH) + gsub(/IDNUM/, ID++) + sub(/\n$/, "") + print + } + } +' <dvp/ /* probe ID IDNUM (depth DEPTH) */ +{ + this->dvp = this->dvp->nc_dvp != NULL ? ( + this->dvp->nc_dvp != NULL ? + this->dvp->nc_dvp->v_cache_dst.tqh_first : 0 + ) : 0; + this->nameDEPTH = this->dvp != 0 ? ( + this->dvp->nc_name != 0 ? stringof(this->dvp->nc_name) : "" + ) : ""; +} + +EOFDEPTH +) + +/* + * END Pathname-depth iterators + */ +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $MAX_DEPTH + 5 )) + +############################################################ PRINT ACTION + +# The default value is simply `entry'. This is paired with $PROBE (which can be +# auto-probed when unset or NULL) and can be customized to include clauses for +# the final print action which must be true before the print action will run. + +PRINT="entry /this->fi_mount != 0/" + +############################################################ EVENT TAG + +# The EVENT_TAG is run inside the print action after the timestamp has been +# printed. By default, `UID.GID CMD[PID]: ' of the process is printed. + +#exec 9<fi_mount, this->fi_mount != 0 ? ( + this->fi_mount == "/" ? "" : "/" + ) : "/"); + $( awk -v MAX_DEPTH=$MAX_DEPTH ' + buf = buf $0 "\n" { } + END { + for (N = MAX_DEPTH; N > 0; N--) { + $0 = (N < MAX_DEPTH ? "\t" : "") buf + gsub(/N/, N) + sub(/\n$/, "") + print + } + } + ' <<-EOFDEPTH + printf("%s%s", this->name = this->nameN, this->name != "" ? "/" : ""); + EOFDEPTH + ) + + /* Print the parent directory name */ + this->name = this->d_name != 0 ? this->d_name : ""; + printf("%s%s", this->name, this->name != "" ? "/" : ""); + + /* Print the entry name */ + this->name = this->fi_name != 0 ? this->fi_name : ""; + printf("%s", this->name); + + /* Print the target */ + printf(" -> %s", this->target); +EOF +DETAILS=$( cat <&9 ) + +################################################################################ +# END +################################################################################