#!/usr/sbin/dtrace -s
/*
 * shellsnoop.d - A program to print read/write details from shells, 
 *	such as keystrokes and command outputs.
 *	Written in DTrace (Solaris 10 build 51).
 *
 * NOTE: This version is deprecated. See "shellsnoop",
 * 	http://www.brendangregg.com/dtrace.html
 *
 * 05-May-2004, ver 0.71.
 *
 *
 * USAGE:       ./shellsnoop.d
 *
 * This program is somewhat experimental.
 *
 *
 * FIELDS:
 *              PID     process ID
 *              PPID    parent process ID
 *              CMD     command name
 *              DIR     direction (R read, W write)
 *              TEXT    text contained in the read/write
 *
 * Standard Disclaimer: This is freeware, use at your own risk.
 *
 * 28-Mar-2004  Brendan Gregg   Created this.
 *
 */


#pragma D option quiet
#pragma D option strsize=2048

inline int DEBUG = 0;			/* extra debug output */

dtrace:::BEGIN
{
	/* print header */
	printf("%5s %5s %8s %3s  %s\n","PID","PPID","CMD","DIR","TEXT");
}


syscall::exec:entry, syscall::exece:entry
/execname == "sh"   || execname == "ksh"  || execname == "csh"  || 
 execname == "tcsh" || execname == "zsh"  || execname == "bash"/
{
	/*
	 * Remember that this PID was invoked by a shell
	 */
	child[pid] = 1;

	/* debug */
	this->parent = (char *)curthread->t_procp->p_parent->p_user.u_comm;
	DEBUG == 1 ? printf("PID %d CMD %s started. (%s)\n",
	 pid,execname, stringof(this->parent)) : 1;
}


syscall::write:entry, syscall::read:entry
/execname == "sh"   || execname == "ksh"  || execname == "csh"  ||
 execname == "tcsh" || execname == "zsh"  || execname == "bash"/
{
	self->buf = arg1;
	self->len = arg2;
}

syscall::write:return, syscall::read:return
/child[pid] == 0 && self->buf != NULL/
{
	/*
	 * Print shell keystrokes
	 */
	this->text = (char *)copyin(self->buf, self->len);

	printf("%5d %5d %8s %3s  %s\n", pid, curpsinfo->pr_ppid, execname, 
	    probefunc == "read" ? "R" : "W", stringof(this->text));
	self->buf = NULL;
	self->len = 0;
}

syscall::write:entry, syscall::read:entry
/child[pid] == 1 && (arg0 == 1 || arg0 == 2)/
{
	self->buf = arg1;
	self->len = arg2;
}

syscall::write:return, syscall::read:return
/self->buf != NULL/
{
	/*
	 * Print command output
	 */
	this->text = (char *)copyin(self->buf, self->len);

	printf("%5d %5d %8s %3s  %s", pid, curpsinfo->pr_ppid, execname,
	    probefunc == "read" ? "R" : "W", stringof(this->text));

	/* here we check if a newline is needed */
	this->length = strlen(this->text);
	printf("%s", this->text[this->length - 1] == '\n' ? "" : "\n");
	self->buf = NULL;
	self->len = 0;
}


fbt:genunix:exit:entry
{
	/*
	 * Forget this PID
	 */
	child[pid] = 0;

	/* debug */
	this->parent = (char *)curthread->t_procp->p_parent->p_user.u_comm;
	DEBUG == 1 ? printf("PID %d CMD %s exited. (%s)\n",
	 pid,execname, stringof(this->parent)) : 1;
}
