#!/usr/bin/perl # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2006 Sun Microsystems Inc. All rights reserved. # Use is subject to license terms. # # Some concepts in this script have been cribed from the scripts in the # DTracetoolkit by Brendan Gregg: http://users.tpg.com.au/adsln4yb/dtrace.html use Getopt::Std; use Sun::Solaris::Privilege qw(:ALL); &Usage() if $ARGV[0] eq "--help"; getopts('He:fhn:o:p:vz:') || &Usage(); &Usage() if $opt_h; $FILTER = ""; $COMMAND= 0; if ($opt_e) { $COMMAND= 1 if defined $opt_e; $FILTER = "(pid == \$target)"; } else { $FILTER = "(execname == \"$opt_n\")" if defined $opt_n; if ($FILTER) { $FILTER = "$FILTER && (pid == $opt_p)" if defined $opt_p; } else { $FILTER = "(pid == $opt_p)" if defined $opt_p; } if ($FILTER) { $FILTER = "$FILTER && (zonename == \"$opt_z\")" if defined $opt_z; } else { $FILTER = "(zonename == \"$opt_z\")" if defined $opt_z; } &Usage if not $FILTER; } $FOLLOW = 0; $FOLLOW = 1 if defined $opt_f; $HEADER = 1; $HEADER = 0 if defined $opt_H; $VERBOSE = 0; $VERBOSE = 1 if defined $opt_v; $dscript = <child = 0; } syscall:::entry /($FILTER) || self->child/ { self->start = timestamp; } /* Follow children */ syscall::fork*:entry /$FOLLOW && self->start/ { /* track this parent process */ trackedpid[pid] = 1; } syscall::fork*:return /$FOLLOW && trackedpid[ppid]/ { /* set as child */ self->child = 1; } sdt:::priv-ok /($FILTER) || self->child/ { printf("USED:%d:%d:%d:%s:%d:%d\\n", pid, ppid, uid, execname, timestamp, arg0); } sdt:::priv-err /($FILTER) || self->child/ { printf("NEED:%d:%d:%d:%s:%d:%d\\n", pid, ppid, uid, execname, timestamp, arg0); } END $SIG{INT} = \&Cleanup_Signal; # Ctrl-C $SIG{QUIT} = \&Cleanup_Signal; # Ctrl-\ $SIG{TERM} = \&Cleanup_Signal; # TERM $dtrace = "/usr/sbin/dtrace -n '$dscript'"; if ($COMMAND) { $dtrace = $dtrace . " -c \"$opt_e\""; } open(DTRACE, "$dtrace |") || die "failed to start dtrace\n"; if ($opt_o) { open(OUTPUT, ">", "$opt_o") || die "open of $opt_e failed: $!"; } else { open(OUTPUT, ">&1") || die "can't dup stdout"; } if ($HEADER) { if ($VERBOSE) { printf(OUTPUT "%-4s %-18s %-6s %-6s %-6s %-20s %s\n", "STAT", "TIMESTAMP", "PPID", "PID", "UID", "PRIV", "CMD"); } else { printf(OUTPUT "%-4s %s\n", "STAT", "PRIV"); } } while (chomp($line = )) { ($need, $ppid, $pid, $uid, $execname, $time, $privnum) = split(':', $line); if ($need) { if ($VERBOSE) { printf(OUTPUT "%-4s %-18s %-6s %-6s %-6s %-20s %s\n", $need, $time, $pid, $ppid, $uid, priv_getbynum($privnum), $execname); } else { printf(OUTPUT "%-4s %s\n", $need, priv_getbynum($privnum)); } } } close(DTRACE); close(OUTPUT) if defined $opt_o; sub Cleanup_Signal { } sub Usage { printf(STDERR "privdebug [-f] [-v] [-H] [-o out]\n"); printf(STDERR " %-15s\t%s", "-n ", "Debug a specific program name\n"); printf(STDERR " %-15s\t%s", "-p ", "Debug a specific process ID\n"); printf(STDERR " %-15s\t%s", "-z ", "Debug a specific zone name\n"); printf(STDERR "\nprivdebug [-f] [-v] [-H] [-o out]\n"); printf(STDERR " %-15s\t%s", "-e ", "Execute and debug a specific command\n"); printf(STDERR "\nprivdebug --help | -h\n"); exit(1); }