]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - cddl/contrib/dtracetoolkit/Apps/shellsnoop
MFC r368207,368607:
[FreeBSD/stable/10.git] / cddl / contrib / dtracetoolkit / Apps / shellsnoop
1 #!/usr/bin/sh
2 #
3 # shellsnoop - A program to print read/write details from shells,
4 #              such as keystrokes and command outputs.
5 #              Written using DTrace (Solaris 10 3/05).
6 #
7 # This program sounds somewhat dangerous (snooping keystrokes), but is
8 # no more so than /usr/bin/truss, and both need root or dtrace privileges to
9 # run. In fact, less dangerous, as we only print visible text (not password
10 # text, for example). Having said that, it goes without saying that this
11 # program shouldn't be used for breeching privacy of other users.
12 #
13 # This was written as a tool to demonstrate the capabilities of DTrace.
14 #
15 # $Id: shellsnoop 19 2007-09-12 07:47:59Z brendan $
16 #
17 # USAGE:        shellsnoop [-hqsv] [-p PID] [-u UID]
18 #
19 #               -q              # quiet, only print data
20 #               -s              # include start time, us
21 #               -v              # include start time, string
22 #               -p PID          # process ID to snoop
23 #               -u UID          # user ID to snoop
24 #  eg,
25 #               shellsnoop              # default output
26 #               shellsnoop -v           # human readable timestamps
27 #               shellsnoop -p 1892      # snoop this PID only
28 #               shellsnoop -qp 1892     # watch this PID data only
29 #       
30 # FIELDS:
31 #               UID             User ID
32 #               PID             process ID
33 #               PPID            parent process ID
34 #               COMM            command name
35 #               DIR             direction (R read, W write)
36 #               TEXT            text contained in the read/write
37 #               TIME            timestamp for the command, us
38 #               STRTIME         timestamp for the command, string
39 #
40 # SEE ALSO: ttywatcher
41 #
42 # COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
43 #
44 # CDDL HEADER START
45 #
46 #  The contents of this file are subject to the terms of the
47 #  Common Development and Distribution License, Version 1.0 only
48 #  (the "License").  You may not use this file except in compliance
49 #  with the License.
50 #
51 #  You can obtain a copy of the license at Docs/cddl1.txt
52 #  or http://www.opensolaris.org/os/licensing.
53 #  See the License for the specific language governing permissions
54 #  and limitations under the License.
55 #
56 # CDDL HEADER END
57 #
58 # Author: Brendan Gregg  [Sydney, Australia]
59 #
60 # 28-Mar-2004   Brendan Gregg   Created this.
61 # 21-Jan-2005      "      "     Wrapped in sh to provide options.
62 # 30-Nov-2005      "      "     Fixed trailing buffer text bug.
63 # 30-Nov-2005      "      "     Fixed sh no keystroke text in quiet bug.
64 # 30-Nov-2005      "      "     Last update.
65
66
67
68 ##############################
69 # --- Process Arguments ---
70 #
71 opt_pid=0; opt_uid=0; opt_time=0; opt_timestr=0; opt_quiet=0; opt_debug=0
72 filter=0; pid=0; uid=0
73
74 while getopts dhp:qsu:v name
75 do
76         case $name in
77         d)      opt_debug=1 ;;
78         p)      opt_pid=1; pid=$OPTARG ;;
79         q)      opt_quiet=1 ;;
80         s)      opt_time=1 ;;
81         u)      opt_uid=1; uid=$OPTARG ;;
82         v)      opt_timestr=1 ;;
83         h|?)    cat <<-END >&2
84                 USAGE: shellsnoop [-hqsv] [-p PID] [-u UID]
85                        shellsnoop               # default output
86                                 -q              # quiet, only print data
87                                 -s              # include start time, us
88                                 -v              # include start time, string
89                                 -p PID          # process ID to snoop
90                                 -u UID          # user ID to snoop
91                 END
92                 exit 1
93         esac
94 done
95
96 if [ $opt_quiet -eq 1 ]; then
97         opt_time=0; opt_timestr=0
98 fi
99 if [ $opt_pid -eq 1 -o $opt_uid -eq 1 ]; then
100         filter=1
101 fi
102
103
104 #################################
105 # --- Main Program, DTrace ---
106 #
107 dtrace -n '
108  /*
109   * Command line arguments
110   */
111  inline int OPT_debug   = '$opt_debug';
112  inline int OPT_quiet   = '$opt_quiet';
113  inline int OPT_pid     = '$opt_pid';
114  inline int OPT_uid     = '$opt_uid';
115  inline int OPT_time    = '$opt_time';
116  inline int OPT_timestr = '$opt_timestr';
117  inline int FILTER      = '$filter';
118  inline int PID         = '$pid';
119  inline int UID         = '$uid';
120  
121  #pragma D option quiet
122  #pragma D option switchrate=20hz
123  
124  /*
125   * Print header
126   */
127  dtrace:::BEGIN /OPT_time == 1/
128  { 
129         printf("%-14s ","TIME");
130  }
131  dtrace:::BEGIN /OPT_timestr == 1/
132  { 
133         printf("%-20s ","STRTIME");
134  }
135  dtrace:::BEGIN /OPT_quiet == 0/
136  {
137         printf("%5s %5s %8s %3s  %s\n", "PID", "PPID", "CMD", "DIR", "TEXT");
138  }
139
140  /*
141   * Remember this PID is a shell child
142   */
143  syscall::exec:entry, syscall::exece:entry
144  /execname == "sh"   || execname == "ksh"  || execname == "csh"  || 
145   execname == "tcsh" || execname == "zsh"  || execname == "bash"/
146  {
147         child[pid] = 1;
148  
149         /* debug */
150         this->parent = (char *)curthread->t_procp->p_parent->p_user.u_comm;
151         OPT_debug == 1 ? printf("PID %d CMD %s started. (%s)\n",
152             pid, execname, stringof(this->parent)) : 1;
153  }
154  syscall::exec:entry, syscall::exece:entry
155  /(OPT_pid == 1 && PID != ppid) || (OPT_uid == 1 && UID != uid)/
156  {
157         /* forget if filtered */
158         child[pid] = 0;
159  }
160
161  /*
162   * Print shell keystrokes
163   */
164  syscall::write:entry, syscall::read:entry
165  /(execname == "sh"   || execname == "ksh"  || execname == "csh"  ||
166   execname == "tcsh" || execname == "zsh"  || execname == "bash")
167   && (arg0 >= 0 && arg0 <= 2)/
168  {
169         self->buf = arg1;
170  }
171  syscall::write:entry, syscall::read:entry
172  /(OPT_pid == 1 && PID != pid) || (OPT_uid == 1 && UID != uid)/
173  {
174         self->buf = 0;
175  }
176  syscall::write:return, syscall::read:return
177  /self->buf && child[pid] == 0 && OPT_time == 1/
178  {
179         printf("%-14d ", timestamp/1000);
180  }
181  syscall::write:return, syscall::read:return
182  /self->buf && child[pid] == 0 && OPT_timestr == 1/
183  {
184         printf("%-20Y ", walltimestamp);
185  }
186  syscall::write:return, syscall::read:return
187  /self->buf && child[pid] == 0 && OPT_quiet == 0/
188  {
189         this->text = (char *)copyin(self->buf, arg0);
190         this->text[arg0] = '\'\\0\'';
191  
192         printf("%5d %5d %8s %3s  %s\n", pid, curpsinfo->pr_ppid, execname, 
193             probefunc == "read" ? "R" : "W", stringof(this->text));
194  }
195  syscall::write:return
196  /self->buf && child[pid] == 0 && OPT_quiet == 1/
197  {
198         this->text = (char *)copyin(self->buf, arg0);
199         this->text[arg0] = '\'\\0\'';
200         printf("%s", stringof(this->text));
201  }
202  syscall::read:return
203  /self->buf && execname == "sh" && child[pid] == 0 && OPT_quiet == 1/
204  {
205         this->text = (char *)copyin(self->buf, arg0);
206         this->text[arg0] = '\'\\0\'';
207         printf("%s", stringof(this->text));
208  }
209  syscall::write:return, syscall::read:return
210  /self->buf && child[pid] == 0/
211  {
212         self->buf = 0;
213  }
214
215  /*
216   * Print command output
217   */
218  syscall::write:entry, syscall::read:entry
219  /child[pid] == 1 && (arg0 == 1 || arg0 == 2)/
220  {
221         self->buf = arg1;
222  }
223  syscall::write:return, syscall::read:return
224  /self->buf && OPT_time == 1/
225  {
226         printf("%-14d ", timestamp/1000);
227  }
228  syscall::write:return, syscall::read:return
229  /self->buf && OPT_timestr == 1/
230  {
231         printf("%-20Y ", walltimestamp);
232  }
233  syscall::write:return, syscall::read:return
234  /self->buf && OPT_quiet == 0/
235  {
236         this->text = (char *)copyin(self->buf, arg0);
237         this->text[arg0] = '\'\\0\'';
238  
239         printf("%5d %5d %8s %3s  %s", pid, curpsinfo->pr_ppid, execname,
240             probefunc == "read" ? "R" : "W", stringof(this->text));
241  
242         /* here we check if a newline is needed */
243         this->length = strlen(this->text);
244         printf("%s", this->text[this->length - 1] == '\'\\n\'' ? "" : "\n");
245         self->buf = 0;
246  }
247  syscall::write:return, syscall::read:return
248  /self->buf && OPT_quiet == 1/
249  {
250         this->text = (char *)copyin(self->buf, arg0);
251         this->text[arg0] = '\'\\0\'';
252         printf("%s", stringof(this->text));
253         self->buf = 0;
254  }
255
256  /*
257   *  Cleanup
258   */
259  syscall::rexit:entry
260  {
261         child[pid] = 0;
262
263         /* debug */
264         this->parent = (char *)curthread->t_procp->p_parent->p_user.u_comm;
265         OPT_debug == 1 ? printf("PID %d CMD %s exited. (%s)\n",
266          pid, execname, stringof(this->parent)) : 1;
267  }
268 '