/* execsnoop.c - snoop process execution. Uses DTrace for Solaris 10.
 *	This program was written as a short demostration of libdtrace.
 *
 * 09-May-2005,	ver 0.10	(first release. check for newer versions)
 *
 * COMPILE: cc -ldtrace -o execsnoop execsnoop.c
 *
 * NOTE! libdtrace is currently a private interface, and not intended
 *  for public use. Check the man pages libdtrace(3LIB) and dtrace(7D).
 *  Currently the dtrace(1M) compiler is the only supported interface.
 *
 * Standard Disclaimer: This is freeware, use at your own risk.
 *
 * 09-May-2005	Brendan Gregg	Created this.
 */

#include <stdio.h>
#include <stdarg.h>
#include <dtrace.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <strings.h>
#include <termio.h>
#include <signal.h>

/*
 * Global varibles
 */
static int g_debug = 0;			/* debugging output */
static dtrace_hdl_t *g_dtp;		/* dtrace handler */
static FILE *g_ofp = stdout;		/* dtrace output filedes */
static int g_intr = 0;			/* interrupt flag */
static int g_status = 0;		/* dtrace status */


/*
 * The DTrace script
 */
static const char *g_prog =
"syscall::exece:return"
"{"
"	printf(\"\%5d \%5d \%5d \%s\\n\",uid,pid,ppid,curpsinfo->pr_psargs);"
"}";


/*
 * Functions
 */

/* print header */
static void
doheader()
{
	fprintf(g_ofp,"%5s %5s %5s %s\n","UID","PID","PPID","ARGS");
}

/* customised error exit */
static int
die(int code, const char *fmt, const char *msg)
{
	fprintf(stderr,fmt,msg);
	exit(code);
}

/* interrupt trap */
static void
trapintr(int signo)
{
        g_intr++;
}

/* process probe */
static int
doprobe(const dtrace_probedata_t *data, void *arg)
{
	dtrace_probedesc_t *pd = data->dtpda_pdesc;
	if (g_debug) 
		fprintf(stderr,"debug2: %s:%s:%s:%s\n",pd->dtpd_provider,
		 pd->dtpd_mod, pd->dtpd_func,pd->dtpd_name);
	return (DTRACE_CONSUME_THIS);
}

/* process record */
static int
dorec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg)
{
	dtrace_actkind_t act;
	uintptr_t addr;

	if (rec == NULL) return (DTRACE_CONSUME_NEXT);
	act = rec->dtrd_action;
	addr = (uintptr_t)data->dtpda_data;

	if (act == DTRACEACT_EXIT) {
		g_status = *((uint32_t *)addr);
		return (DTRACE_CONSUME_NEXT);
	}

	return (DTRACE_CONSUME_THIS);
}


/* 
 * Main program
 */
int
main(int argc, char *argv[])
{
	/* Variable declerations */
	dtrace_prog_t *dpp;
	dtrace_proginfo_t dpi;
	struct sigaction act;
	int err,status,done;
	done = 0;

	/* Trap Ctrl-C */
	(void) sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	act.sa_handler = trapintr;
	(void) sigaction(SIGINT, &act, NULL);

	/* open dtrace */
	if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) 
		die(1,"ERROR1: opening dtrace: %s\n",dtrace_errmsg(NULL,err));

	/* set buffer size */
        (void) dtrace_setopt(g_dtp, "bufsize", "4m");

	/* compile dtrace script */
	if ((dpp = dtrace_program_strcompile(g_dtp, g_prog,
	 DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL)
		die(2,"ERROR2: bad program: %s\n",dtrace_errmsg(NULL,err));

	/* enable dtrace probes */
        if (dtrace_program_exec(g_dtp, dpp, &dpi) == -1)
		die(3,"ERROR3: enabling probes",NULL);

	if (g_debug) 
		fprintf(stderr,"debug1: matched %u probes\n",dpi.dpi_matches);

	if (dtrace_go(g_dtp) != 0)
		die(4,"ERROR4: dtrace go: %s\n",dtrace_errmsg(NULL,err));

	/* print header */
	doheader();

	/* main loop */
	do {
		dtrace_sleep(g_dtp);
		status = dtrace_work(g_dtp, g_ofp, doprobe, dorec, NULL);
		if (status == DTRACE_WORKSTATUS_DONE) done = 1;
                if (fflush(g_ofp) == EOF) clearerr(g_ofp);
	} while (!done && !g_intr);

	/* close dtrace */
	if (g_debug) fprintf(stderr,"debug3: goodbye.\n");
	dtrace_stop(g_dtp);
	dtrace_close(g_dtp);

	return(0);
}

