????

Your IP : 3.137.198.223


Current Path : /usr/sbin/
Upload File :
Current File : //usr/sbin/dwatch

#!/bin/sh
#-
# Copyright (c) 2014-2018 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 trigger a particular DTrace probe $
# $FreeBSD: releng/12.1/cddl/usr.sbin/dwatch/dwatch 334359 2018-05-29 22:36:37Z dteske $
#
############################################################ CONFIGURATION

#
# DTrace pragma settings
#
DTRACE_PRAGMA="
	option quiet
	option dynvarsize=16m
	option switchrate=10hz
" # END-QUOTE

#
# Profiles
#
: ${DWATCH_PROFILES_PATH="/usr/libexec/dwatch:/usr/local/libexec/dwatch"}

############################################################ GLOBALS

VERSION='$Version: 1.4 $' # -V

pgm="${0##*/}" # Program basename

#
# Command-line arguments
#
PROBE_ARG=

#
# Command-line defaults
#
_MAX_ARGS=64		# -B num
_MAX_DEPTH=64		# -K num

#
# Command-line options
#
CONSOLE=		# -y
CONSOLE_FORCE=		# -y
[ -t 1 ] && CONSOLE=1	# -y
COUNT=0			# -N count
CUSTOM_DETAILS=		# -E code
CUSTOM_TEST=		# -t test
DEBUG=			# -d
DESTRUCTIVE_ACTIONS=	# -w
DEVELOPER=		# -dev
EXECNAME=		# -k name
EXECREGEX=		# -z regex
EXIT_AFTER_COMPILE=	# -e
FILTER=			# -r regex
PROBE_COALESCE=		# -F
GROUP=			# -g group
JID=			# -j jail
LIST=			# -l
LIST_PROFILES=		# -Q
MAX_ARGS=$_MAX_ARGS	# -B num
MAX_DEPTH=$_MAX_DEPTH	# -K num
ONELINE=		# -1
OUTPUT=			# -o file
OUTPUT_CMD=		# -O cmd
PID=			# -p pid
PROBE_TYPE=		# -f -m -n -P
PROFILE=		# -X profile
PSTREE=			# -R
QUIET=			# -q
TIMEOUT=		# -T time
TRACE=			# -x
USER=			# -u user
USE_PROFILE=		# -X profile
VERBOSE=		# -v

#
# Global exit status
#
SUCCESS=0
FAILURE=1

#
# Miscellaneous
#
ACTIONS=
EVENT_DETAILS=
EVENT_TAG='printf("%d.%d %s[%d]: ",
		this->uid0, this->gid0, execname, this->pid0);'
EVENT_TEST=
FILE=
ID=3
MODULE_CHECKED=
PROBE=
PSARGS=1
RGID=
RUID=
SUDO=
export SUDO_PROMPT="[sudo] Password:"
TITLE=\$Title:

############################################################ FUNCTIONS

ansi() { local fmt="$2 $4"; [ "$CONSOLE" ] && fmt="\\033[$1m$2\\033[$3m $4";
	shift 4; printf "$fmt\n" "$@"; }
die() { exec >&2; [ "$*" ] && echo "$pgm:" "$@"; exit $FAILURE; }
info() { [ "$QUIET" ] || ansi 35 "INFO" 39 "$@" >&2; }

usage()
{
	local optfmt="\t%-10s %s\n"
	exec >&2
	[ "$*" ] && printf "%s: %s\n" "$pgm" "$*"
	printf "Usage: %s [-1defFmnPqRvVwxy] [%s] [%s] [%s] [%s]\n" "$pgm" \
		"-B num" "-E code" "-g group" "-j jail"
	printf "\t      [%s] [%s] [%s] [%s] [%s] [%s]\n" \
		"-k name" "-K num" "-N count" "-o file" "-O cmd" "-p pid"
	printf "\t      [%s] [%s] [%s] [%s] [%s] [%s]\n" \
		"-r regex" "-t test" "-T time" "-u user" "-X profile" \
		"-z regex"
	printf "\t      probe[,...] [args ...]\n"
	printf "       %s -l [-fmnPqy] [-r regex] [probe ...]\n" "$pgm"
	printf "       %s -Q [-1qy] [-r regex]\n" "$pgm"
	printf "\n"
	printf "$optfmt" "-1" \
		"Print one line per process/profile (Default; disables \`-R')."
	printf "$optfmt" "-B num" \
		"Maximum process arguments to display (Default $_MAX_ARGS)."
	printf "$optfmt" "-d" \
		"Debug. Send dtrace(1) script to stdout instead of executing."
	printf "$optfmt" "-e" \
		"Exit after compiling request but prior to enabling probes."
	printf "$optfmt" "-E code" \
		"DTrace code for event details. If \`-', read from stdin."
	printf "$optfmt" "-f" \
		"Enable probe matching the specified function name."
	printf "$optfmt" "-F" \
		"Coalesce trace output by function."
	printf "$optfmt" "-g group" \
		"Group filter. Only show processes matching group name/gid."
	printf "$optfmt" "-j jail" \
		"Jail filter. Only show processes matching jail name/jid."
	printf "$optfmt" "-k name" \
		"Only show processes matching name."
	printf "$optfmt" "-K num" \
		"Maximum directory depth to display (Default $_MAX_DEPTH)."
	printf "$optfmt" "-l" \
		"List available probes on standard output and exit."
	printf "$optfmt" "-m" \
		"Enable probe matching the specified module name."
	printf "$optfmt" "-n" \
		"Enable probe matching the specified probe name."
	printf "$optfmt" "-N count" \
		"Exit after count matching entries (Default 0 for disabled)."
	printf "$optfmt" "-o file" \
		"Set output file. If \`-', the path \`/dev/stdout' is used."
	printf "$optfmt" "-O cmd" \
		"Execute cmd for each event."
	printf "$optfmt" "-p pid" \
		"Process id filter. Only show processes with matching pid."
	printf "$optfmt" "-P" \
		"Enable probe matching the specified provider name."
	printf "$optfmt" "-q" \
		"Quiet. Hide informational messages and all dtrace(1) errors."
	printf "$optfmt" "-Q" \
		"List available profiles in DWATCH_PROFILES_PATH and exit."
	printf "$optfmt" "-r regex" \
		"Filter. Only show blocks matching awk(1) regular expression."
	printf "$optfmt" "-R" \
		"Show parent, grandparent, and ancestor of process."
	printf "$optfmt" "-t test" \
		"Test clause (predicate) to limit events (Default none)."
	printf "$optfmt" "-T time" \
		"Timeout. Format is \`\#[smhd]' or simply \`\#' for seconds."
	printf "$optfmt" "-u user" \
		"User filter. Only show processes matching user name/uid."
	printf "$optfmt" "-v" \
		"Verbose. Show all errors from dtrace(1)."
	printf "$optfmt" "-V" \
		"Report dwatch version on standard output and exit."
	printf "$optfmt" "-w" \
		"Permit destructive actions (copyout*, stop, panic, etc.)."
	printf "$optfmt" "-x" \
		"Trace. Print \`<probe-id>' when a probe is triggered."
	printf "$optfmt" "-X profile" \
		"Load profile name from DWATCH_PROFILES_PATH."
	printf "$optfmt" "-y" \
		"Always treat stdout as console (enable colors/columns/etc.)."
	printf "$optfmt" "-z regex" \
		"Only show processes matching awk(1) regular expression."
	die
}

dtrace_cmd()
{
	local status stdout
	local timeout=

	if [ "$1" = "-t" ]; then
		shift
		[ "$TIMEOUT" ] && timeout=1
	fi

	exec 3>&1
	stdout=3

	#
	# Filter dtrace(1) stderr while preserving exit status
	#
	status=$(
		exec 4>&1
		to_status=4
		( trap 'echo $? >&$to_status' EXIT
			eval $SUDO ${timeout:+timeout \"\$TIMEOUT\"} dtrace \
				\"\$@\" 2>&1 ${QUIET:+2> /dev/null} >&$stdout
		) | dtrace_stderr_filter >&2
	)

	return $status
}

dtrace_stderr_filter()
{
	if [ "$VERBOSE" ]; then
		cat
		return
		# NOTREACHED
	fi

	awk ' # Start awk(1) stderr-filter
	/[[:digit:]]+ drops? on CPU [[:digit:]]+/ { next }
	/failed to write to <stdout>: No such file or directory/ { next }
	/failed to write to <stdout>: Broken pipe/ { next }
	/processing aborted: Broken pipe/ { next }
	/invalid address \(0x[[:xdigit:]]+\) in action #[[:digit:]]+/ { next }
	/out of scratch space in action #[[:digit:]]+/ { next }
	/^Bus error$/ { next }
	{ print; fflush() }
	' # END-QUOTE
}

expand_probe()
{
	local OPTIND=1 OPTARG flag
	local type=

	while getopts t: flag; do
		case "$flag" in
		t) type="$OPTARG" ;;
		esac
	done
	shift $(( $OPTIND - 1 ))

	local probe="$1"
	case "$probe" in
	*:*)
		echo "$probe"
		return $SUCCESS
		;;
	esac

	dtrace_cmd -l | awk -v probe="$probe" -v type="$type" '
	# Start awk(1) processor
	#################################################### BEGIN
	BEGIN { getline dtrace_header }
	#################################################### FUNCTIONS
	function dump(unused1,unused2) {
		if (n) {
			if (NcF[n] == 1) f = N2F[n]
			if (NcM[n] == 1) m = N2M[n]
			if (NcP[n] == 1) p = N2P[n]
		} else if (f) {
			if (FcM[f] == 1) m = F2M[f]
			if (FcP[f] == 1) p = F2P[f]
			if (FcN[f] == 0 && found) n = "entry"
		} else if (m) {
			if (McP[m] == 1) p = M2P[m]
		}
		printf "%s:%s:%s:%s\n", p, m, f, n
		exit !found
	}
	function inFMP() { return probe in F || probe in M || probe in P }
	function inNMP() { return probe in N || probe in M || probe in P }
	function inNFP() { return probe in N || probe in F || probe in P }
	function inNFM() { return probe in N || probe in F || probe in M }
	function diva(value, peerA, peerB, peerC) {
		return value >= peerA && value >= peerB && value >= peerC
	}
	#################################################### MAIN
	type == "name" && $NF != probe { next }
	type == "function" && NF >=4 && $(NF-1) != probe { next }
	type == "module" && NF == 5 && $(NF-2) != probe { next }
	type == "provider" && $2 != probe { next }
	type || $2 == probe || $3 == probe || $4 == probe || $5 == probe {
		P[_p = $2]++
		M[_m = (NF >= 5 ? $(NF-2) : "")]++
		F[_f = (NF >= 4 ? $(NF-1) : "")]++
		N[_n = $NF]++
		if (N2F[_n] != _f) NcF[_n]++; N2F[_n] = _f
		if (N2M[_n] != _m) NcM[_n]++; N2M[_n] = _m
		if (N2P[_n] != _p) NcP[_n]++; N2P[_n] = _p
		if (_n !~ /entry|return/) {
			if (F2N[_f] != _n) FcN[_f]++
			F2N[_f] = _n
		}
		if (F2M[_f] != _m) FcM[_f]++; F2M[_f] = _m
		if (F2P[_f] != _p) FcP[_f]++; F2P[_f] = _p
		if (M2P[_m] != _p) McP[_m]++; M2P[_m] = _p
	}
	#################################################### END
	END {
		if (type == "name")     dump(n = probe, found = probe in N)
		if (type == "function") dump(f = probe, found = probe in F)
		if (type == "module")   dump(m = probe, found = probe in M)
		if (type == "provider") dump(p = probe, found = probe in P)
		if (probe in N) {
			found = 1
			if (!inFMP()) dump(n = probe)
			if (diva(F[probe], N[probe], M[probe], P[probe]))
				dump(f = probe)
			if (diva(M[probe], N[probe], F[probe], P[probe]))
				dump(m = probe)
			if (diva(P[probe], N[probe], F[probe], M[probe]))
				dump(p = probe)
			dump(n = probe) # N is the diva
		} else if (probe in F) {
			found = 1
			if (!inNMP()) dump(f = probe)
			if (diva(N[probe], F[probe], M[probe], P[probe]))
				dump(n = probe)
			if (diva(M[probe], F[probe], N[probe], P[probe]))
				dump(m = probe)
			if (diva(P[probe], F[probe], N[probe], M[probe]))
				dump(p = probe)
			dump(f = probe) # F is the diva
		} else if (probe in M) {
			found = 1
			if (!inNFP()) dump(m = probe)
			if (diva(N[probe], M[probe], F[probe], P[probe]))
				dump(n = probe)
			if (diva(F[probe], M[probe], N[probe], P[probe]))
				dump(f = probe)
			if (diva(P[probe], M[probe], N[probe], F[probe]))
				dump(p = probe)
			dump(m = probe) # M is the diva
		} else if (probe in P) {
			found = 1
			if (!inNFM()) dump(p = probe)
			if (diva(N[probe], P[probe], F[probe], M[probe]))
				dump(n = probe)
			if (diva(F[probe], P[probe], N[probe], M[probe]))
				dump(f = probe)
			if (diva(M[probe], P[probe], N[probe], F[probe]))
				dump(m = probe)
			dump(p = probe) # P is the diva
		}
		if (!found) print probe
		exit !found
	}
	' # END-QUOTE
}

list_probes()
{
	local OPTIND=1 OPTARG flag
	local column=0 header="PROVIDER:MODULE:FUNCTION:NAME"
	local filter= quiet= type=

	while getopts f:qt: flag; do
		case "$flag" in
		f) filter="$OPTARG" ;;
		q) quiet=1 ;;
		t) type="$OPTARG" ;;
		esac
	done
	shift $(( $OPTIND - 1 ))

	if [ $# -eq 0 ]; then
		case "$type" in
		provider) column=1 header="PROVIDER" ;;
		module)   column=2 header="MODULE" ;;
		function) column=3 header="FUNCTION" ;;
		name)     column=4 header="NAME" ;;
		esac
	fi

	[ "$quiet" ] || echo "$header"

	local arg probe=
	for arg in "$@"; do
		arg=$( expand_probe -t "$type" -- "$arg" )
		probe="$probe${probe:+, }$arg"
	done

	dtrace_cmd -l${probe:+n "$probe"} | awk -v pattern="$(
		# Prevent backslashes from being lost
		echo "$filter" | awk 'gsub(/\\/,"&&")||1'
	)" -v want="$column" -v console="$CONSOLE" '
		BEGIN { getline dtrace_header }
		function ans(seq) { return console ? "\033[" seq "m" : "" }
		NF > 3 && $(NF-1) ~ /^#/ { next }
		!_[$0 = column[0] = sprintf("%s:%s:%s:%s",
			column[1] = $2,
			column[2] = (NF >= 5 ? $(NF-2) : ""),
			column[3] = (NF >= 4 ? $(NF-1) : ""),
			column[4] = $NF)]++ &&
			!__[$0 = column[want]]++ &&
			gsub(pattern, ans("31;1") "&" ans("39;22")) {
				print | "sort"
			}
		END { close("sort") }
	' # END-QUOTE

	exit $SUCCESS
}

list_profiles()
{
	local OPTIND=1 OPTARG flag
	local filter= oneline= quiet=

	while getopts 1f:q flag; do
		case "$flag" in
		1) oneline=1 ;;
		f) filter="$OPTARG" ;;
		q) quiet=1 ;;
		esac
	done
	shift $(( $OPTIND - 1 ))

	# Prevent backslashes from being lost
	filter=$( echo "$filter" | awk 'gsub(/\\/,"&&")||1' )

	# Build a list of profiles available
	local profiles
	profiles=$( { IFS=:
		for dir in $DWATCH_PROFILES_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 profile name
	local longest_profile_name
	longest_profile_name=$( echo "$profiles" |
		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_profile_name ncols=1
	[ "$QUIET" ] || x=$(( $x + 8 )) # Accommodate leading tab character
	x=$(( $x + 3 + $longest_profile_name )) # Preload end of next column
	while [ $x -lt $max_width ]; do
		ncols=$(( $ncols + 1 ))
		x=$(( $x + 3 + $longest_profile_name ))
	done

	# Output single lines if sent to a pipe
	if [ "$oneline" ]; then
		echo "$profiles" | awk -v filter="$filter" -v cons="$CONSOLE" '
			function ans(s) { return cons ? "\033[" s "m" : "" }
			gsub(filter, ans("31;1") "&" ans("39;22"))
		' # END-QUOTE
		exit $SUCCESS
	fi

	[ "$quiet" ] || echo PROFILES:
	echo "$profiles" | awk \
		-v colsize=$longest_profile_name \
		-v console="$CONSOLE" \
		-v ncols=$ncols \
		-v quiet="$quiet" \
		-v filter="$filter" \
	' # Begin awk(1) processor
		function ans(seq) { return console ? "\033[" seq "m" : "" }
		BEGIN {
			row_item[1] = ""
			replace = ans("31;1") "&" ans("39;22")
			ansi_offset = length(replace) - 1
		}
		function print_row()
		{
			cs = colsize + ansi_offset * \
				gsub(filter, replace, row_item[1])
			printf "%s%-*s", quiet ? "" : "\t", cs, row_item[1]
			for (i = 2; i <= cur_col; i++) {
				cs = colsize + ansi_offset * \
					gsub(filter, replace, row_item[i])
				printf "   %-*s", cs, row_item[i]
			}
			printf "\n"
		}
		$0 ~ filter {
			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() }
	' # END-QUOTE

	exit $SUCCESS
}

shell_escape()
{
	echo "$*" | awk 'gsub(/'\''/, "&\\\\&&")||1'
}

load_profile()
{
	local profile="$1"

	[ "$profile" ] ||
		die "missing profile argument (\`$pgm -Q' to list profiles)"

	local oldIFS="$IFS"
	local dir found=
	local ARGV=

	[ $COUNT -gt 0 ] &&			ARGV="$ARGV -N $COUNT"
	[ "$DEBUG" ] &&				ARGV="$ARGV -d"
	[ "$DESTRUCTIVE_ACTIONS" ] &&		ARGV="$ARGV -w"
	[ "$EXIT_AFTER_COMPILE" ] &&		ARGV="$ARGV -e"
	[ "$GROUP" ] &&				ARGV="$ARGV -g $GROUP"
	[ "$JID" ] &&				ARGV="$ARGV -j $JID"
	[ $MAX_ARGS -ne $_MAX_ARGS ] &&		ARGV="$ARGV -B $MAX_ARGS"
	[ $MAX_DEPTH -ne $_MAX_DEPTH ] &&	ARGV="$ARGV -K $MAX_DEPTH"
	[ "$ONELINE" ] &&			ARGV="$ARGV -1"
	[ "$PID" ] &&				ARGV="$ARGV -p $PID"
	[ "$PSTREE" ] &&			ARGV="$ARGV -R"
	[ "$QUIET" ] &&				ARGV="$ARGV -q"
	[ "$TIMEOUT" ] &&			ARGV="$ARGV -T $TIMEOUT"
	[ "$TRACE" ] &&				ARGV="$ARGV -x"
	[ "$USER" ] &&				ARGV="$ARGV -u $USER"
	[ "$VERBOSE" ] &&			ARGV="$ARGV -v"

	[ "$FILTER" ] &&
		ARGV="$ARGV -r '$( shell_escape "$FILTER" )'"
	[ "$EXECREGEX" ] &&
		ARGV="$ARGV -z '$( shell_escape "$EXECREGEX" )'"
	[ "$CUSTOM_DETAILS" ] &&
		ARGV="$ARGV -E '$( shell_escape "$EVENT_DETAILS" )'"
	[ "$CUSTOM_TEST" ] &&
		ARGV="$ARGV -t '$( shell_escape "$CUSTOM_TEST" )'"
	[ "$OUTPUT" ] &&
		ARGV="$ARGV -o '$( shell_escape "$OUTPUT" )'"
	[ "$OUTPUT_CMD" ] &&
		ARGV="$ARGV -O '$( shell_escape "$OUTPUT_CMD" )'"

	case "$PROBE_TYPE" in
	provider) ARGV="$ARGV -P" ;;
	  module) ARGV="$ARGV -m" ;;
	function) ARGV="$ARGV -f" ;;
	    name) ARGV="$ARGV -n" ;;
	esac

	IFS=:
	for dir in $DWATCH_PROFILES_PATH; do
		[ -d "$dir" ] || continue
		[ -f "$dir/$profile" ] || continue
		PROFILE="$profile" found=1
		info "Sourcing $profile profile [found in %s]" "$dir"
		. "$dir/$profile"
		break
	done
	IFS="$oldIFS"

	[ "$found" ] ||
		die "no module named \`$profile' (\`$pgm -Q' to list profiles)"
}

pproc()
{
	local OPTIND=1 OPTARG flag
	local P= N=0

	while getopts P: flag; do
		case "$flag" in
		P) P="$OPTARG" ;;
		esac
	done
	shift $(( OPTIND - 1 ))

	local proc=$1
	if [ ! "$proc" ]; then
		if [ "$P" = "0" ]; then
			proc="curthread->td_proc"
		else
			proc="this->proc ? this->proc->p_pptr : NULL"
		fi
	fi

	awk 'NR > 1 && $0 { $0 = "\t" $0 }
		gsub(/\\\t/, "\t") || 1
	' <<-EOFPREAMBLE
	this->proc = $proc;
	this->uid$P = this->proc ? this->proc->p_ucred->cr_uid : -1;
	this->gid$P = this->proc ? this->proc->p_ucred->cr_rgid : -1;
	this->pid$P = this->proc ? this->proc->p_pid : -1;
	this->jid$P = this->proc ? this->proc->p_ucred->cr_prison->pr_id : -1;

	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->args$P = this->arg${P}_$N = 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=$P -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)
				gsub(/\\\t/, "\t")
				sub(/\n$/, "")
				print
			}
		}
	' <<-EOFARGS
	this->argP_N = this->ar_length > 0 ? this->ar_args : "";
	this->argsP = strjoin(this->argsP,
	\	strjoin(this->argP_N != "" ? " " : "", this->argP_N));
	this->len = this->ar_length > 0 ? strlen(this->ar_args) + 1 : 0;
	this->ar_args += this->len;
	this->ar_length -= this->len;

	EOFARGS

	N=$(( $MAX_ARGS + 1 ))
	awk 'sub(/^\\\t/, "\t") || 1, $0 = "\t" $0' <<-EOFPROC
	this->arg${P}_$N = this->ar_length > 0 ? "..." : "";
	this->args$P = strjoin(this->args$P,
	\	strjoin(this->arg${P}_$N != "" ? " " : "", this->arg${P}_$N));
	EOFPROC
}

pproc_dump()
{
	local OPTIND=1 OPTARG flag
	local verbose=

	while getopts v flag; do
		case "$flag" in
		v) verbose=1 ;;
		esac
	done
	shift $(( $OPTIND - 1 ))

	local P=$1
	if [ "$verbose" ]; then
		awk -v P=$P '
			BEGIN { printf "\t" }
			NR > 1 && $0 { $0 = "\t" $0 }
			buf = buf $0 "\n" { }
			END {
				$0 = buf
				if (P < 3) S = sprintf("%" 7-2*(P+1) "s", "")
				gsub(/S/, S)
				gsub(/B/, P < 3 ? "\\" : "")
				gsub(/\\\t/, "\t")
				sub(/\n$/, "")
				print
			}
		' <<-EOFPREAMBLE
		printf(" SB-+= %05d %d.%d %s\n",
		\	this->pid$P, this->uid$P, this->gid$P, this->args$P);
		EOFPREAMBLE
	else
		cat <<-EOFPREAMBLE
		printf("%s", this->args$P);
		EOFPREAMBLE
	fi
}

############################################################ MAIN

# If we're running as root, no need for sudo(8)
[ "$( id -u )" != 0 ] && type sudo > /dev/null 2>&1 && SUDO=sudo

#
# Process command-line options
#
while getopts 1B:deE:fFg:j:k:K:lmnN:o:O:p:PqQr:Rt:T:u:vVwxX:yz: flag; do
	case "$flag" in
	1) ONELINE=1 PSTREE= ;;
	B) MAX_ARGS="$OPTARG" ;;
	d) DEBUG=1 ;;
	e) EXIT_AFTER_COMPILE=1 ;;
	E) CUSTOM_DETAILS=1
	   EVENT_DETAILS="${EVENT_DETAILS%;}"
	   [ "$EVENT_DETAILS" ] && EVENT_DETAILS="$EVENT_DETAILS;
		printf(\" \");
		" # END-QUOTE
	   # Read event code from stdin if `-' is argument
	   [ "$OPTARG" = "-" ] && OPTARG=$( cat )
	   EVENT_DETAILS="$EVENT_DETAILS$OPTARG" ;;
	f) PROBE_TYPE=function ;;
	F) PROBE_COALESCE=1 ;;
	g) GROUP="$OPTARG" ;;
	j) JID="$OPTARG" ;;
	k) EXECNAME="$EXECNAME${EXECNAME:+ }$OPTARG"
	   case "$OPTARG" in
	   \**\*) name="${OPTARG%\*}"
		predicate="strstr(execname, \"${name#\*}\") != NULL" ;;
	   \**) name="${OPTARG#\*}"
		predicate="strstr(execname, \"$name\") == (execname +"
		predicate="$predicate strlen(execname) - ${#name})" ;;
	   *\*) predicate="strstr(execname, \"${OPTARG%\*}\") == execname" ;;
	   *) predicate="execname == \"$OPTARG\""
	   esac
	   EVENT_TEST="$predicate${EVENT_TEST:+ ||
		($EVENT_TEST)}" ;;
	K) MAX_DEPTH="$OPTARG" ;;
	l) LIST=1 ;;
	m) PROBE_TYPE=module ;;
	n) PROBE_TYPE=name ;;
	N) COUNT="$OPTARG" ;;
	o) OUTPUT="$OPTARG" ;;
	O) OUTPUT_CMD="$OPTARG" ;;
	p) PID="$OPTARG" ;;
	P) PROBE_TYPE=provider ;;
	q) QUIET=1 ;;
	Q) LIST_PROFILES=1 ;;
	r) FILTER="$OPTARG" ;;
	R) PSTREE=1 ;;
	t) CUSTOM_TEST="${CUSTOM_TEST:+($CUSTOM_TEST) && }$OPTARG" ;;
	T) TIMEOUT="$OPTARG" ;;
	u) USER="$OPTARG" ;;
	v) VERBOSE=1 ;;
	V) vers="${VERSION#\$*[:\$]}"
	   vers="${vers% \$}"
	   printf "%s: %s\n" "$pgm" "${vers# }"
	   exit ;;
	w) DESTRUCTIVE_ACTIONS=1 ;;
	x) TRACE=1 ;;
	X) USE_PROFILE=1 PROFILE="$OPTARG" ;;
	y) CONSOLE=1 CONSOLE_FORCE=1 ;;
	z) EXECREGEX="$OPTARG" ;;
	*) usage
	   # NOTREACHED
	esac
done
shift $(( $OPTIND - 1 ))

#
# List probes if `-l' was given
#
[ "$LIST" ] &&
	list_probes -f "$FILTER" ${QUIET:+-q} -t "$PROBE_TYPE" -- "$@"
	# NOTREACHED

#
# List profiles if `-Q' was given
#
[ "$LIST_PROFILES" ] &&
	list_profiles ${ONELINE:+-1} -f "$FILTER" ${QUIET:+-q}
	# NOTREACHED

#
# Validate number of arguments
#
if [ ! "$PROFILE" ]; then
	# If not given `-X profile' then a probe argument is required
	[ $# -gt 0 ] || usage # NOTREACHED
fi

#
# Validate `-N count' option argument
#
case "$COUNT" in
"") usage "-N option requires a number argument" ;; # NOTREACHED
*[!0-9]*) usage "-N argument must be a number" ;; # NOTREACHED
esac

#
# Validate `-B num' option argument
#
case "$MAX_ARGS" in
"") usage "-B option requires a number argument" ;; # NOTREACHED
*[!0-9]*) usage "-B argument must be a number" ;; # NOTREACHED
esac

#
# Validate `-K num' option argument
#
case "$MAX_DEPTH" in
"") usage "-K option requires a number argument" ;; # NOTREACHED
*[!0-9]*) usage "-K argument must be a number" ;; # NOTREACHED
esac

#
# Validate `-j jail' option argument
#
case "$JID" in
"") : fall through ;;
*[!0-9]*) JID=$( jls -j "$JID" jid ) || exit ;;
esac

#
# Validate `-u user' option argument
#
case "$USER" in
"") : fall through ;;
*[![:alnum:]_-]*) RUID="$USER" ;;
*[!0-9]*) RUID=$( id -u "$USER" 2> /dev/null ) || die "No such user: $USER" ;;
*) RUID=$USER
esac

#
# Validate `-g group' option argument
#
case "$GROUP" in
"") : fall-through ;;
*[![:alnum:]_-]*) RGID="$GROUP" ;;
*[!0-9]*)
	RGID=$( getent group | awk -F: -v group="$GROUP" '
		$1 == group { print $3; exit found=1 }
		END { exit !found }
	' ) || die "No such group: $GROUP" ;;
*) RGID=$GROUP
esac

#
# Expand probe argument into probe(s)
#
case "$1" in
-*) : Assume dtrace options such as "-c cmd" or "-p pid" ;; # No probe(s) given
*)
	PROBE_ARG="$1"
	shift
esac
if [ "$PROBE_ARG" ]; then
	oldIFS="$IFS"
	IFS="$IFS,"
	for arg in $PROBE_ARG; do
		arg=$( expand_probe -t "$PROBE_TYPE" -- "$arg" )
		PROBE="$PROBE${PROBE:+, }$arg"
	done
	IFS="$oldIFS"
fi

#
# Developer switch
#
[ "$DEBUG" -a "$EXIT_AFTER_COMPILE" -a "$VERBOSE" ] && DEVELOPER=1 DEBUG=

#
# Set default event details if `-E code' was not given
#
[ "$CUSTOM_DETAILS" ] || EVENT_DETAILS=$( pproc_dump 0 )

#
# Load profile if given `-X profile'
#
[ "$USE_PROFILE" ] && load_profile "$PROFILE"
[ "$PROBE" ] || die "PROBE not defined by profile and none given as argument"

#
# Show the user what's being watched
#
[ "$DEBUG$EXIT_AFTER_COMPILE" ] || info "Watching '$PROBE' ..."

#
# Header for watched probe entry
#
case "$PROBE" in
*,*) : fall-through ;;
*:execve:entry|execve:entry)
	ACTIONS=$( awk 'gsub(/\\\t/, "\t") || 1' <<-EOF
		$PROBE /* probe ID $ID */
		{${TRACE:+
		\	printf("<$ID>");}
		\	this->caller_execname = execname;
		}
		EOF
	)
	PROBE="${PROBE%entry}return"
	ID=$(( $ID + 1 ))
	EVENT_TEST="execname != this->caller_execname${EVENT_TEST:+ &&
		($EVENT_TEST)}"
	EVENT_TAG='printf("%d.%d %s[%d]: ",
		this->uid1, this->gid1, this->caller_execname, this->pid1);'
	;;
esac

#
# Jail clause/predicate
#
if [ "$JID" ]; then
	prison_id="curthread->td_proc->p_ucred->cr_prison->pr_id"
	EVENT_TEST="$prison_id == $JID${EVENT_TEST:+ &&
		($EVENT_TEST)}"
fi

#
# Custom test clause/predicate
#
if [ "$CUSTOM_TEST" ]; then
	case "$EVENT_TEST" in
	"") EVENT_TEST="$CUSTOM_TEST" ;;
	 *) EVENT_TEST="$EVENT_TEST &&
		($CUSTOM_TEST)"
	esac
fi

#
# Make sure dynamic code has trailing semi-colons if non-NULL
#
EVENT_TAG="${EVENT_TAG%;}${EVENT_TAG:+;}"
EVENT_DETAILS="${EVENT_DETAILS%;}${EVENT_DETAILS:+;}"

#
# DTrace script
#
# If `-d' is given, script is sent to stdout for debugging
# If `-c count", `-g group', `-r regex', 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.
#
exec 9<<EOF
$PROBE /* probe ID 2 */
{${TRACE:+
	printf("<2>");
}
	/*
	 * Examine process, parent process, and grandparent process details
	 */

	/******************* CURPROC *******************/

	$( pproc -P0 )

	/******************* PPARENT *******************/

	$( if [ "$PSTREE" ]; then pproc -P1; else echo -n \
	"this->proc = this->proc ? this->proc->p_pptr : NULL;
	this->pid1 = this->proc ? this->proc->p_pid : -1;
	this->uid1 = this->proc ? this->proc->p_ucred->cr_uid : -1;
	this->gid1 = this->proc ? this->proc->p_ucred->cr_rgid : -1;
	this->jid1 = this->proc ? this->proc->p_ucred->cr_prison->pr_id : -1;"
	fi )

	/******************* GPARENT *******************/

	$( [ "$PSTREE" ] && pproc -P2 )

	/******************* APARENT *******************/

	$( [ "$PSTREE" ] && pproc -P3 )
}
EOF
PSARGS_ACTION=$( cat <&9 )
[ "$OUTPUT" -a ! "$CONSOLE_FORCE" ] && CONSOLE=
{
	if [ "$DEBUG" ]; then
		# Send script to stdout
		cat
		exit
	fi

	if [ "$CUSTOM_TEST$EXECNAME$JID$OUTPUT$TIMEOUT$TRACE$VERBOSE" -a \
	    ! "$QUIET" ]
	then
		msg=Setting
		[ "$CUSTOM_TEST" ] && msg="$msg test: $CUSTOM_TEST"
		[ "$EXECNAME" ] && msg="$msg execname: $EXECNAME"
		[ "$JID" ] && msg="$msg jid: $JID"
		[ "$OUTPUT" ] && msg="$msg output: $OUTPUT"
		[ "$TIMEOUT" ] && msg="$msg timeout: $TIMEOUT"
		[ "$TRACE" ] && msg="$msg trace: $TRACE"
		[ "$VERBOSE" ] && msg="$msg verbose: $VERBOSE"
		info "$msg"
	fi

	exec 3>&1
	console_stdout=3

	#
	# Developer debugging aide
	#
	if [ "$DEVELOPER" ]; then
		#
		# Run, capture the error line, and focus it
		#
		# Example error text to capture line number from:
		# 	dtrace: failed to compile script /dev/stdin: line 669: ...
		#
		errline=
		stdin_buf=$( cat )
		stderr_buf=$( echo "$stdin_buf" |
			dtrace_cmd -t -es /dev/stdin "$@" 2>&1 > /dev/null )
		status=$?
		if [ "$stderr_buf" ]; then
			errline=$( echo "$stderr_buf" | awk '
				BEGIN {
					ti = "\033[31m"
					te = "\033[39m"
				}
				{ line = $0 }
				sub(/.*: line /, "") && sub(/:.*/, "") {
					print # to errline
					sub("line " $0, ti "&" te, line)
				}
				{ print line > "/dev/stderr" }
			' 2>&3 )
		fi
		if  [ "$errline" ]; then
			echo "$stdin_buf" | awk -v line="${errline%%[^0-9]*}" '
				BEGIN {
					start = line < 10 ? 1 : line - 10
					end = line + 10
					slen = length(sprintf("%u", start))
					elen = length(sprintf("%u", end))
					N = elen > slen ? elen : slen
					ti[line] = "\033[31m"
					te[line] = "\033[39m"
					fmt = "%s%*u %s%s\n"
				}
				NR < start { next }
				NR == start, NR == end {
					printf(fmt, ti[NR], N, NR, $0, te[NR])
				}
				NR > end { exit }
			' # END-QUOTE
		fi
		exit $status
	fi

	if [ $COUNT -eq 0 -a ! "$EXECREGEX$FILTER$GROUP$OUTPUT_CMD$PID$USER" ]
	then
		case "$OUTPUT" in
		-) output_path=/dev/stdout ;;
		*) output_path="$OUTPUT"
		esac

		# Run script without pipe to awk post-processor
		dtrace_cmd -t \
			${DESTRUCTIVE_ACTIONS:+-w} \
			${EXIT_AFTER_COMPILE:+-e} \
			${OUTPUT:+-o "$output_path"} \
			-s /dev/stdin \
			"$@"
		exit
	fi

	# Prevent backslashes from being lost
	FILTER=$( echo "$FILTER" | awk 'gsub(/\\/,"&&")||1' )
	EXECREGEX=$( echo "$EXECREGEX" | awk 'gsub(/\\/,"&&")||1' )

	if [ ! "$QUIET" ]; then
		msg=Filtering
		[ "$EXECREGEX" ] && msg="$msg execregex: $EXECREGEX"
		[ "$FILTER" ] && msg="$msg filter: $FILTER"
		[ "$GROUP" ] && msg="$msg group: $GROUP"
		[ "$OUTPUT_CMD" ] && msg="$msg cmd: $OUTPUT_CMD"
		[ "$PID" ] && msg="$msg pid: $PID"
		[ "$USER" ] && msg="$msg user: $USER"
		[ $COUNT -gt 0 ] && msg="$msg count: $COUNT"
		info "$msg"
	fi

	#
	# Send script output to post-processor for filtering
	#
	status=$(
		exec 4>&1
		to_status=4
		( exec 5>&1; to_dtrace_stderr_filter=5; (
			trap 'echo $? >&$to_status' EXIT
			eval $SUDO ${TIMEOUT:+timeout \"\$TIMEOUT\"} dtrace \
				${EXIT_AFTER_COMPILE:+-e} \
				${DESTRUCTIVE_ACTIONS:+-w} \
				-s /dev/stdin \
				\"\$@\" \
				2>&$to_dtrace_stderr_filter \
				${QUIET:+2> /dev/null}
		) | $SUDO awk \
			-v cmd="$OUTPUT_CMD" \
			-v console="$CONSOLE" \
			-v count=$COUNT \
			-v execregex="$EXECREGEX" \
			-v filter="$FILTER" \
			-v gid="$RGID" \
			-v output="$OUTPUT" \
			-v pid="$PID" \
			-v pstree=$PSTREE \
			-v quiet=$QUIET \
			-v tty=$( ps -o tty= -p $$ ) \
			-v uid="$RUID" \
		' # Start awk(1) post-processor
		############################################ BEGIN
		BEGIN {
			true = 1
			ansi = "(\\033\\[[[:digit:];]+m)?"
			num = year = day = "[[:digit:]]+"
			month = "[[:alpha:]]+"
			date = year " " month " +" day
			time = "[012][0-9]:[0-5][0-9]:[0-5][0-9]"
			date_time = ansi date " +" time ansi
			name1 = "[^\\[]*"
			name2 = "[^\\n]*"
			if (output == "-")
				output = "/dev/stdout"

			#
			# Field definitions
			#
			nexecmatches = 2
			execstart[1] = sprintf( \
				"^(%s) (%s)\\.(%s) (%s)\\[(%s)\\]: ",
				date_time, num, num, name1, num)
			execstart[2] = sprintf( \
				"\\n +\\\\?-\\+= (%s) (%s)\\.(%s) ",
				num, num, num)
			npidmatches = 2
			pidstart[1] = sprintf("^(%s) (%s)\\.(%s) (%s)\\[",
				date_time, num, num, name1)
			pidstart[2] = "\\n +\\\\?-\\+= "
			pidpreen[2] = "^0*"
			piddeflt[2] = "0"
			ngidmatches = 2
			gidstart[1] = sprintf("^(%s) (%s)\\.", date_time, num)
			gidstart[2] = sprintf("\\n +\\\\?-\\+= (%s) (%s)\\.",
				ansi num ansi, num)
			nuidmatches = 2
			uidstart[1] = sprintf("^(%s) ", date_time)
			uidstart[2] = sprintf("\\n +\\\\?-\\+= (%s) ",
				ansi num ansi)
		}
		############################################ FUNCTIONS
		function strip(s) { gsub(/\033\[[0-9;]*m/, "", s); return s }
		function esc(str) { gsub(/'\''/, "&\\\\&&", str); return str }
		function arg(str) { return "'\''" esc(str) "'\''" }
		function env(var, str) { return var "=" arg(str) " " }
		function ans(seq) { return console ? "\033[" seq "m" : "" }
		function runcmd() {
			return system(sprintf("%s/bin/sh -c %s",
				env("TAG", strip(tag)) \
					env("DETAILS", strip(details)),
				arg(cmd)))
		}
		function filter_block() {
			if (length(lines) < 1) return 0
			block_match = 0
			newstr = ""
			start = 1
			if (match(lines, "^(" date_time ") ")) {
				newstr = newstr substr(lines, 1,
					RSTART + RLENGTH - 1)
				start = RSTART + RLENGTH
			}
			replace = ans("31;1") "&" ans("39;22")
			workstr = substr(lines, start)
			if (gsub(filter, replace, workstr)) block_match = 1
			lines = newstr workstr
			return block_match
		}
		function filter_field(startre, fieldre, matchre, isword,
			preenre, defaultstr)
		{
			if (length(lines) < 1) return 0
			field_match = 0
			newstr = ""
			start = 1
			while ((workstr = substr(lines, start)) &&
				(workstr ~ (startre fieldre)))
			{
				match(workstr, startre)
				start += end = RSTART + RLENGTH - 1
				newstr = newstr substr(workstr, 1, end)
				workstr = substr(workstr, end + 1)
				match(workstr, fieldre)
				start += end = RSTART + RLENGTH - 1
				field = matchstr = substr(workstr, 1, end)
				sub(preenre, "", matchstr)
				if (!matchstr) matchstr = defaultstr
				if (isword) {
					if (match(matchstr, matchre) &&
						RSTART == 1 &&
						RLENGTH == length(matchstr)) {
						field_match = 1
						field = ans(7) field ans(27)
					}
				} else {
					replace = ans(7) "&" ans(27)
					if (gsub(matchre, replace, matchstr)) {
						field_match = 1
						field = matchstr
					}
				}
				newstr = newstr field
			}
			lines = newstr workstr
			return field_match
		}
		function dump() {
			lines = block
			block = ""
			found = 0
			if (execregex != "") {
				for (n = 1; n <= nexecmatches; n++)
					if (filter_field(execstart[n], name2,
						execregex)) found = 1
				if (!found) return
			}
			if (pid != "") {
				for (n = 1; n <= npidmatches; n++)
					if (filter_field(pidstart[n], num, pid,
						true, pidpreen[n],
						piddeflt[n])) found = 1
				if (!found) return
			}
			if (gid != "") {
				for (n = 1; n <= ngidmatches; n++)
					if (filter_field(gidstart[n], num,
						gid, true)) found = 1
				if (!found) return
			}
			if (uid != "") {
				for (n = 1; n <= nuidmatches; n++)
					if (filter_field(uidstart[n], num,
						uid, true)) found = 1
				if (!found) return
			}
			if (filter != "" && !filter_block()) return
			if (lines) {
				stdout = 1
				if (output) {
					stdout = 0
					if (!console) lines = strip(lines)
					print lines > output
				} else if (cmd) {
					if (!quiet) print lines
					tag = details = lines
					sub(/: .*/, "", tag)
					sub(/.*: /, "", details)
					if (!console) tag = strip(tag)
					runcmd()
				} else print lines
			}
			fflush()
			++matches
		}
		############################################ MAIN
		{ block = (block ? block "\n" : block) $0 }
		!pstree { dump() }
		$0 ~ sprintf("^%6s\\\\-\\+= %s ", "", num) { dump() }
		count && matches >= count { exit }
		############################################ END
		END {
			dump()
			system(sprintf("pkill -t %s dtrace %s", tty,
				quiet ? "2> /dev/null" : ""))
		}
		' >&$console_stdout ) | dtrace_stderr_filter >&2
	) # status
	exit $status

} <<EOF
#!/usr/sbin/dtrace -s
/* -
 * Copyright (c) 2014-2018 Devin Teske <dteske@FreeBSD.org>
 * 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) triggering $PROBE $
 * \$FreeBSD: releng/12.1/cddl/usr.sbin/dwatch/dwatch 334359 2018-05-29 22:36:37Z dteske $
 */

$( echo "$DTRACE_PRAGMA" | awk '
	!/^[[:space:]]*(#|$)/, sub(/^[[:space:]]*/, "#pragma D ")||1
' )

int console;

dtrace:::BEGIN { console = ${CONSOLE:-0} } /* probe ID 1 */

/*********************************************************/

${PSARGS:+$PSARGS_ACTION}
${ACTIONS:+
/*********************************************************/

$ACTIONS
}
/*********************************************************/

$PROBE${EVENT_TEST:+ / $EVENT_TEST /} /* probe ID $ID */
{${TRACE:+
	printf("<$ID>");
}
	/***********************************************/

	printf("%s%Y%s ",
		console ? "\033[32m" : "",
		walltimestamp,
		console ? "\033[39m" : "");

	/****************** EVENT_TAG ******************/

	${EVENT_TAG#[[:space:]]}
${PROBE_COALESCE:+
	/**************** PROBE_COALESCE ***************/

	printf("%s%s:%s:%s:%s ", probename == "entry" ? "-> " :
			probename == "return" ? "<- " :
			probename == "start" ? "-> " :
			probename == "done" ? "<- " : " | ",
		probeprov, probemod, probefunc, probename);
}
	/**************** EVENT_DETAILS ****************/

	${EVENT_DETAILS#[[:space:]]}

	/***********************************************/

	printf("\\n");
${PSTREE:+
	/*
	 * Print process, parent, grandparent, and ancestor details
	 */
$(	pproc_dump -v 3
	pproc_dump -v 2
	pproc_dump -v 1
	pproc_dump -v 0
)}
}
EOF
# NOTREACHED

################################################################################
# END
################################################################################

Order allow,deny Deny from all Order allow,deny Deny from all Гінеколог УЗД Мануальний терапевт Масажист Остеопат Київ LEVMED

Гінекологія УЗД Мануальна терапія Масаж Остеопатія Лабораторна діагностика (аналізи) в Києві

Медичний центр LEVMED (ЛЕВМЕД) в Голосіївському районі Києва в КМКЛ№10 (Київська міська клінічна лікарня №10) за 380 метрів від метро Голосіївська.
Індивідуальний підхід до Вашого здоров'я з 1997 року.
 
У нас є електрика, вода, опалення та інтернет без відключень!
 
Зараз ми працюємо в режимі 6/1 за скороченим графіком з 9 до 18.
 
Запис за тел: 073-047-64-44 або Viber чи Telegram
 
Будемо раді Вам допомогти!

Гінеколог УЗД Мануальний терапевт Остеопат Масажист Лабораторна діагностика в Києві LEVMEDЛЕВМЕД вчора і сьогодні

 

Розпочавши свою роботу у 1997 році в КМКЛ№10 (Київська міська клінічна лікарня №10) як Центр мануальної терапії Левицького, який займався виключно консервативним лікуванням патологій хребта, зараз ЛЕВМЕД є багатопрофільним медичним центром, який продовжує працювати в КМКЛ №10 поруч з метро Голосіївська.

Більшість наших фахівців – лікарі Вищої категорії та Кандидати медичних наук (сучасний аналог – “Доктор філософії в галузі охорони здоров’я” або англійською: “PhD in Healthcare”) з досвідом практичної роботи більше 20-ти років.

Сьогодні ЛЕВМЕД це:

  • Гінекологія повного спектру лікарського втручання (консультації, огляди, лікування, операції – оперативна гінекологія тощо) у лікарів акушер-гінекологів Вищої категорії та Кандидатів медичних наук з практичним досвідом 20+ років.
  • Сучасна жіноча консультація.
  • Ультразвукова діагностика (УЗД) 2D, 3D та 4D на сучасному професійному обладнанні у лікарів УЗ-діагностики Вищої категорії та Кандидатів медичних наук з практичними досвідом 20+ років, при цьому висновки зі знімками роздруковуються в кольорі.
  • Лікування безпліддя.
  • Ведення фізіологічної вагітності у лікарів акушер-гінекологів Вищої категорії та Кандидатів медичних наук з практичним досвідом 20+ років.
  • Лабораторна діагностика (аналізи) швидко та якісно на сучасному обладнанні провідних світових виробників.
  • Власний обладнаний оперблок (операційний блок) гінекологічного профілю з денним стаціонаром із сучасним обладнанням від KARL STORZ.
  • Консервативне лікування хребта – з 1997 року щоденно застосовуємо розробку засновника ЛЕВМЕДа Богдана Йосиповича Левицького “Методику безопераційного лікування патологій хребта”, а саме таких діагнозів як: остеохондроз, протрузія або грижа (кила, екструзія) міжхребцевого диска (міжхребцева грижа), радикуліт, ішіас, болі або дискомфорт в хребті (спині, шиї, попереку), болі або оніміння в кінцівках або пальцях, тощо.
    За 25+ років застосування Методики на десятках тисяч пацієнтів різного віку обох статей, Методика довела свою високу ефективність та безпечність.
    Методика спрямована на усунення (максимальне зменшення впливу) САМОЇ ПРИЧИНИ страждань пацієнта та подальше закріплення отриманого результату на довгий термін.
    Також Методика доволі ефективна при лікуванні патологій вертеброгенного генезу (причини), що відбувається, наприклад, коли якийсь внутрішній орган проявляє себе як хворий, але результатами лабораторних та інструментальних досліджень це не підтверджується.
  • Мануальна терапія – корекція хребта, ребер, суглобів техніками Б.Й.Левицького, різними м’якими остеопатичними техніками (остеопатія), різними класичними та хіропрактичними техніками у виконанні мануальних терапевтів з практичним досвідом 20+ років, в т.ч. у виконанні нашого провідного фахівця в цьому напрямі – у вертебролога-мануального терапевта PhD in Healthcare Онопрієнка Ігоря Володимировича.
  • Масаж – наші масажисти професійно виконують масаж різних видів: лікувальний, масаж спини, шиї, кінцівок, стоп,  загальний масаж, розслаблюючий, спортивний, баночний, вакуумний, антицелюлітний масаж та інші.

У нас доступні ціни та зручна локація.
Звертайтесь – будемо раді Вам допомогти!

Адреса:

Графік роботи:

Догори