#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/pioctl.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "truss.h"
#include "extern.h"
char procfs_path[FILENAME_MAX];
int have_procfs;
int Procfd;
static void
usage(void)
{
fprintf(stderr, "%s\n%s\n",
"usage: truss [-S] [-o file] -p pid",
" truss [-S] [-o file] command [args]");
exit(1);
}
struct ex_types {
const char *type;
void (*enter_syscall)(struct trussinfo *, int);
int (*exit_syscall)(struct trussinfo *, int);
} ex_types[] = {
#ifdef __x86_64__
{ "DragonFly ELF64", x86_64_syscall_entry, x86_64_syscall_exit },
{ "FreeBSD ELF64", x86_64_syscall_entry, x86_64_syscall_exit },
#endif
{ 0, 0, 0 },
};
static struct ex_types *
set_etype(struct trussinfo *trussinfo) {
struct ex_types *funcs;
char *etype;
char progt[32];
int fd;
asprintf(&etype, "%s/%d/etype", procfs_path, trussinfo->pid);
if (etype == NULL)
err(1, "Out of memory");
if ((fd = open(etype, O_RDONLY)) == -1) {
strcpy(progt, "FreeBSD a.out");
} else {
int len = read(fd, progt, sizeof(progt));
progt[len-1] = '\0';
close(fd);
}
free(etype);
for (funcs = ex_types; funcs->type; funcs++)
if (!strcmp(funcs->type, progt))
break;
if (funcs->type == NULL) {
funcs = &ex_types[0];
warn("Execution type %s is not supported -- using %s\n",
progt, funcs->type);
}
return funcs;
}
int
main(int ac, char **av) {
int c, i, mntsize;
char **command;
struct procfs_status pfs;
struct ex_types *funcs;
struct statfs *mntbuf;
int in_exec = 0;
char *fname = NULL;
int sigexit = 0;
struct trussinfo *trussinfo;
trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo));
if (trussinfo == NULL)
err(1, "calloc()");
trussinfo->outfile = stderr;
if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
err(1, "getmntinfo");
for (i = 0; i < mntsize; i++) {
if (strcasecmp(mntbuf[i].f_mntfromname, "procfs") == 0) {
strlcpy(procfs_path, mntbuf[i].f_mntonname, sizeof(procfs_path));
have_procfs = 1;
break;
}
}
if (!have_procfs) {
errno = 2;
err(1, "You must have a mounted procfs to use truss");
}
while ((c = getopt(ac, av, "p:o:S")) != -1) {
switch (c) {
case 'p':
trussinfo->pid = atoi(optarg);
if (trussinfo->pid == getpid()) {
errx(2, "truss: attempt to self trace: %d\n", trussinfo->pid);
}
break;
case 'o':
fname = optarg;
break;
case 'S':
trussinfo->flags |= NOSIGS;
break;
default:
usage();
}
}
ac -= optind; av += optind;
if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0))
usage();
if (fname != NULL) {
if ((trussinfo->outfile = fopen(fname, "w")) == NULL)
errx(1, "cannot open %s", fname);
}
if (trussinfo->pid == 0) {
command = av;
trussinfo->pid = setup_and_wait(command);
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
} else {
signal(SIGINT, restore_proc);
signal(SIGTERM, restore_proc);
signal(SIGQUIT, restore_proc);
}
Procfd = start_tracing(
trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT |
((trussinfo->flags & NOSIGS) ? 0 : S_SIG));
if (Procfd == -1)
return 0;
pfs.why = 0;
funcs = set_etype(trussinfo);
do {
int val = 0;
if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) {
if (errno == EPERM)
err(1, "PIOCWAIT top of loop");
else
warn("PIOCWAIT top of loop");
} else {
switch(i = pfs.why) {
case S_SCE:
funcs->enter_syscall(trussinfo, pfs.val);
break;
case S_SCX:
if (in_exec) {
in_exec = 0;
break;
}
funcs->exit_syscall(trussinfo, pfs.val);
break;
case S_SIG:
fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val);
sigexit = pfs.val;
break;
case S_EXIT:
fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val);
break;
case S_EXEC:
funcs = set_etype(trussinfo);
in_exec = 1;
break;
default:
fprintf (trussinfo->outfile, "Process stopped because of: %d\n", i);
break;
}
}
if (ioctl(Procfd, PIOCCONT, val) == -1) {
if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH)
break;
else
warn("PIOCCONT");
}
} while (pfs.why != S_EXIT);
fflush(trussinfo->outfile);
if (sigexit) {
if (sigexit == SIGQUIT)
exit(sigexit);
(void) signal(sigexit, SIG_DFL);
(void) kill(getpid(), sigexit);
}
return 0;
}