1595 lines
36 KiB
C
Executable File
1595 lines
36 KiB
C
Executable File
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
#pragma ident "@(#)main.c 1.25 95/07/29 SMI" /* SVr4.0 1.5 */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <memory.h>
|
|
#include <signal.h>
|
|
#include <wait.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/times.h>
|
|
#include <sys/fstyp.h>
|
|
#include <sys/fsid.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/fault.h>
|
|
#include <sys/syscall.h>
|
|
#include "pcontrol.h"
|
|
#include "ramdata.h"
|
|
#include "proto.h"
|
|
|
|
/* This module is carefully coded to contain only read-only data */
|
|
/* All read/write data is defined in ramdata.c (see also ramdata.h) */
|
|
|
|
extern time_t times();
|
|
extern long strtol();
|
|
|
|
extern int getopt();
|
|
extern char * optarg;
|
|
extern int optind;
|
|
|
|
/*
|
|
* Function prototypes for static routines in this module.
|
|
*/
|
|
#if defined(__STDC__)
|
|
|
|
static void make_pname( process_t * );
|
|
static int xcreat( char * );
|
|
static void setoutput( int );
|
|
static void isptraced();
|
|
static void report( time_t );
|
|
static void prtim( timestruc_t * );
|
|
static int pids( char * );
|
|
static void psargs( process_t * );
|
|
static int control( process_t * , pid_t );
|
|
static int grabit( process_t * , char * , pid_t );
|
|
static void release( pid_t );
|
|
static void intr( int );
|
|
static int wait4all( void );
|
|
static void letgo( process_t * );
|
|
static int prlstat( process_t * , char * , struct stat * );
|
|
|
|
#else /* defined(__STDC__) */
|
|
|
|
static void make_pname();
|
|
static int xcreat();
|
|
static void setoutput();
|
|
static void isptraced();
|
|
static void report();
|
|
static void prtim();
|
|
static int pids();
|
|
static void psargs();
|
|
static int control();
|
|
static int grabit();
|
|
static void release();
|
|
static void intr();
|
|
static int wait4all();
|
|
static void letgo();
|
|
static int prlstat();
|
|
|
|
#endif /* defined(__STDC__) */
|
|
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
struct tms tms;
|
|
time_t starttime;
|
|
int retc;
|
|
int ofd = -1;
|
|
int opt;
|
|
int i;
|
|
int what;
|
|
int first;
|
|
int errflg = FALSE;
|
|
int badname = FALSE;
|
|
int req_flag = 0;
|
|
int leave_hung = FALSE;
|
|
register process_t *Pr = &Proc;
|
|
|
|
/* make sure fd's 0, 1, and 2 are allocated */
|
|
/* just in case truss was invoked from init */
|
|
while ((i = open("/dev/null", O_RDWR)) >= 0 && i < 2)
|
|
;
|
|
if (i > 2)
|
|
(void) close(i);
|
|
|
|
starttime = times(&tms); /* for elapsed timing */
|
|
|
|
/* this should be per-traced-process */
|
|
pagesize = sysconf(_SC_PAGESIZE);
|
|
|
|
/* command name (e.g., "truss") */
|
|
if ((command = strrchr(argv[0], '/')) != NULL)
|
|
command++;
|
|
else
|
|
command = argv[0];
|
|
|
|
Euid = geteuid();
|
|
Egid = getegid();
|
|
Ruid = getuid();
|
|
Rgid = getgid();
|
|
ancestor = getpid();
|
|
|
|
prfillset(&trace); /* default: trace all system calls */
|
|
premptyset(&verbose); /* default: no syscall verbosity */
|
|
premptyset(&rawout); /* default: no raw syscall interpretation */
|
|
|
|
prfillset(&signals); /* default: trace all signals */
|
|
|
|
prfillset(&faults); /* default: trace all faults */
|
|
prdelset(&faults, FLTPAGE); /* except this one */
|
|
|
|
premptyset(&readfd); /* default: dump no buffers */
|
|
premptyset(&writefd);
|
|
|
|
premptyset(&syshang); /* default: hang on no system calls */
|
|
premptyset(&sighang); /* default: hang on no signals */
|
|
premptyset(&flthang); /* default: hang on no faults */
|
|
|
|
/* options */
|
|
while ((opt = getopt(argc, argv,
|
|
(CONST char *)"dZP:Fqpfcaeilt:T:v:x:s:S:m:M:r:w:o:")) != EOF) {
|
|
switch (opt) {
|
|
case 'd': /* debug */
|
|
debugflag = TRUE;
|
|
break;
|
|
case 'Z': /* slow mode */
|
|
slowmode = TRUE;
|
|
break;
|
|
case 'P': /* alternate /proc directory */
|
|
procdir = optarg;
|
|
break;
|
|
case 'F': /* force grabbing (no O_EXCL) */
|
|
Fflag = TRUE;
|
|
break;
|
|
case 'q': /* die on QUIT regardless */
|
|
qflag = TRUE;
|
|
break;
|
|
case 'p': /* grab processes */
|
|
pflag = TRUE;
|
|
break;
|
|
case 'f': /* follow children */
|
|
fflag = TRUE;
|
|
break;
|
|
case 'c': /* don't trace, just count */
|
|
cflag = TRUE;
|
|
iflag = TRUE; /* implies no interruptable syscalls */
|
|
break;
|
|
case 'a': /* display argument lists */
|
|
aflag = TRUE;
|
|
break;
|
|
case 'e': /* display environments */
|
|
eflag = TRUE;
|
|
break;
|
|
case 'i': /* don't show interruptable syscalls */
|
|
iflag = TRUE;
|
|
break;
|
|
case 'l': /* show lwp id for each syscall */
|
|
lflag = TRUE;
|
|
break;
|
|
case 't': /* system calls to trace */
|
|
if (syslist(optarg, &trace, &tflag))
|
|
badname = TRUE;
|
|
break;
|
|
case 'T': /* system calls to hang process */
|
|
if (syslist(optarg, &syshang, &Tflag))
|
|
badname = TRUE;
|
|
break;
|
|
case 'v': /* verbose interpretation of syscalls */
|
|
if (syslist(optarg, &verbose, &vflag))
|
|
badname = TRUE;
|
|
break;
|
|
case 'x': /* raw interpretation of syscalls */
|
|
if (syslist(optarg, &rawout, &xflag))
|
|
badname = TRUE;
|
|
break;
|
|
case 's': /* signals to trace */
|
|
if (siglist(optarg, &signals, &sflag))
|
|
badname = TRUE;
|
|
break;
|
|
case 'S': /* signals to hang process */
|
|
if (siglist(optarg, &sighang, &Sflag))
|
|
badname = TRUE;
|
|
break;
|
|
case 'm': /* machine faults to trace */
|
|
if (fltlist(optarg, &faults, &mflag))
|
|
badname = TRUE;
|
|
break;
|
|
case 'M': /* machine faults to hang process */
|
|
if (fltlist(optarg, &flthang, &Mflag))
|
|
badname = TRUE;
|
|
break;
|
|
case 'r': /* show contents of read(fd) */
|
|
if (fdlist(optarg, &readfd))
|
|
badname = TRUE;
|
|
break;
|
|
case 'w': /* show contents of write(fd) */
|
|
if (fdlist(optarg, &writefd))
|
|
badname = TRUE;
|
|
break;
|
|
case 'o': /* output file for trace */
|
|
oflag = TRUE;
|
|
if (ofd >= 0)
|
|
(void) close(ofd);
|
|
if ((ofd = xcreat(optarg)) < 0) {
|
|
perror(optarg);
|
|
badname = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
errflg = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* if -a or -e was specified, force tracing of exec() */
|
|
if (aflag || eflag) {
|
|
praddset(&trace, SYS_exec);
|
|
praddset(&trace, SYS_execve);
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
/* collect the specified process ids */
|
|
if (pflag) {
|
|
while (argc > 0) {
|
|
if (pids(*argv))
|
|
badname = TRUE;
|
|
argc--;
|
|
argv++;
|
|
}
|
|
}
|
|
|
|
if (badname)
|
|
exit(2);
|
|
|
|
if (errflg || (argc <= 0 && ngrab <= 0)) {
|
|
(void) fprintf(stderr,
|
|
"usage:\t%s [-fcaeil] [-[tvx] [!]syscalls] [-s [!]signals] \\\n",
|
|
command);
|
|
(void) fprintf(stderr,
|
|
"\t[-m [!]faults] [-[rw] [!]fds] [-o outfile] command | -p pid ...\n");
|
|
exit(2);
|
|
}
|
|
|
|
if (!isprocdir((process_t *)NULL, procdir)) {
|
|
(void) fprintf(stderr, "%s: %s is not a PROC directory\n",
|
|
command, procdir);
|
|
exit(2);
|
|
}
|
|
|
|
if ((sys_path = (char *)malloc(sys_psize = 16)) == NULL)
|
|
abend("cannot allocate memory for syscall pathname", 0);
|
|
if ((sys_string = (char *)malloc(sys_ssize = 32)) == NULL)
|
|
abend("cannot allocate memory for syscall string", 0);
|
|
|
|
if (argc > 0) { /* create the controlled process */
|
|
if (Pcreate(Pr, &argv[0]) != 0
|
|
|| Pr->state != PS_STOP) {
|
|
(void) Pterm(Pr);
|
|
abend("cannot create subject process", 0);
|
|
}
|
|
no_inherit = Ioctl(Pr->pfd, fflag? PIOCSFORK : PIOCRFORK, 0);
|
|
created = Pr->upid;
|
|
PR = Pr; /* for abend() */
|
|
}
|
|
|
|
setoutput(ofd); /* establish truss output */
|
|
istty = isatty(1);
|
|
|
|
#ifndef NOSETVBUF
|
|
if (setvbuf(stdout, (char *)NULL, _IOFBF, BUFSIZ) != 0) {
|
|
abend("setvbuf() failure", 0);
|
|
exit(2);
|
|
}
|
|
#else
|
|
{
|
|
extern unsigned char _sobuf[];
|
|
setbuf(stdout, (char *)_sobuf);
|
|
stdout->_flag &= ~_IOLBF;
|
|
}
|
|
#endif
|
|
if (stdout->_cnt == 0) { /* stdio bug, compensate */
|
|
/*
|
|
* Without this, the first write to stdout will be unbuffered.
|
|
*/
|
|
#if 0
|
|
(void) fprintf(stderr, "*** compensating for stdio bug\n");
|
|
#endif
|
|
stdout->_cnt = BUFSIZ;
|
|
}
|
|
|
|
/*
|
|
* Set up signal dispositions.
|
|
*/
|
|
if (created && (oflag || !istty)) { /* ignore interrupts */
|
|
(void) sigset(SIGHUP, SIG_IGN);
|
|
(void) sigset(SIGINT, SIG_IGN);
|
|
(void) sigset(SIGQUIT, SIG_IGN);
|
|
}
|
|
else { /* receive interrupts */
|
|
if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
|
|
(void) sigset(SIGHUP, intr);
|
|
if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
|
|
(void) sigset(SIGINT, intr);
|
|
if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
|
|
(void) sigset(SIGQUIT, intr);
|
|
}
|
|
if (qflag) /* -q : die on SIGQUIT with a core dump */
|
|
(void) sigset(SIGQUIT, SIG_DFL);
|
|
(void) sigset(SIGALRM, intr);
|
|
(void) sigset(SIGTERM, intr);
|
|
(void) sigset(SIGUSR1, intr);
|
|
(void) sigset(SIGPIPE, intr);
|
|
|
|
/* don't accumulate zombie children */
|
|
(void) sigset(SIGCLD, SIG_IGN);
|
|
|
|
if (fflag || ngrab > 1) { /* multiple processes */
|
|
int zfd;
|
|
|
|
if ((zfd = open("/dev/zero", O_RDWR)) < 0)
|
|
abend("cannot open /dev/zero for shared memory", 0);
|
|
else if ((Cp = (struct counts *)mmap(NULL,
|
|
sizeof(struct counts), PROT_READ|PROT_WRITE, MAP_SHARED,
|
|
zfd, (off_t)0)) == (struct counts *)(-1))
|
|
abend("cannot mmap /dev/zero for shared memory", 0);
|
|
(void) close(zfd);
|
|
}
|
|
if (Cp == (struct counts *)NULL)
|
|
Cp = (struct counts *)malloc(sizeof(struct counts));
|
|
if (Cp == (struct counts *)NULL)
|
|
abend("cannot allocate memory for counts", 0);
|
|
|
|
(void) memset((char *)Cp, 0, sizeof(struct counts));
|
|
Cp->mutex[0].flags.type = USYNC_PROCESS;
|
|
Cp->mutex[1].flags.type = USYNC_PROCESS;
|
|
|
|
if (created) {
|
|
procadd(Pr->upid);
|
|
show_cred(Pr, TRUE);
|
|
}
|
|
else { /* grab the specified processes */
|
|
int gotone = FALSE;
|
|
|
|
i = 0;
|
|
while (i < ngrab) { /* grab first process */
|
|
char * dir = grabdir[i];
|
|
pid_t pid = grab[i++];
|
|
|
|
if (grabit(Pr, dir, pid)) {
|
|
gotone = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!gotone)
|
|
abend(0, 0);
|
|
while (i < ngrab) { /* grab the remainder */
|
|
char * dir = grabdir[i];
|
|
pid_t pid = grab[i++];
|
|
|
|
switch (fork()) {
|
|
case -1:
|
|
(void) fprintf(stderr,
|
|
"%s: cannot fork to control process, pid# %ld\n",
|
|
command, pid);
|
|
default:
|
|
continue; /* parent carries on */
|
|
|
|
case 0: /* child grabs process */
|
|
(void) close(Pr->pfd);
|
|
descendent = TRUE;
|
|
if (grabit(Pr, dir, pid))
|
|
break;
|
|
exit(2);
|
|
}
|
|
break;
|
|
}
|
|
PR = Pr; /* for abend() */
|
|
}
|
|
|
|
/*
|
|
* If running setuid-root, become root for real to avoid
|
|
* affecting the per-user limitation on the maximum number
|
|
* of processes (one benefit of running setuid-root).
|
|
*/
|
|
if (Rgid != Egid)
|
|
(void) setgid(Egid);
|
|
if (Ruid != Euid)
|
|
(void) setuid(Euid);
|
|
|
|
/* catch these syscalls in order to get started */
|
|
(void) Psysentry(Pr, SYS_exit, TRUE);
|
|
(void) Psysentry(Pr, SYS_exec, TRUE);
|
|
(void) Psysentry(Pr, SYS_execve, TRUE);
|
|
(void) Psysexit(Pr, SYS_exec, TRUE);
|
|
(void) Psysexit(Pr, SYS_execve, TRUE);
|
|
|
|
while (created
|
|
&& Pr->state == PS_STOP
|
|
&& Pr->why.pr_why == PR_SYSENTRY
|
|
&& (Pr->why.pr_what == SYS_execve || Pr->why.pr_what == SYS_exec)) {
|
|
sys_nargs = -1;
|
|
(void) sysentry(Pr);
|
|
(void) Pstart(Pr, 0);
|
|
(void) Pwait(Pr);
|
|
while (Pr->state == PS_LOST) { /* we lost control */
|
|
if (Preopen(Pr, Fflag) == 0) /* we got it back */
|
|
continue;
|
|
|
|
/* we really lost it */
|
|
if (sys_valid)
|
|
(void) fprintf(stderr,
|
|
"%s: cannot trace set-id or unreadable object file: %s\n",
|
|
command, sys_path);
|
|
else
|
|
(void) fprintf(stderr,
|
|
"%s: lost control of process\n",
|
|
command);
|
|
(void) Pterm(Pr);
|
|
goto done;
|
|
}
|
|
if (Pr->state == PS_STOP
|
|
&& Pr->why.pr_why == PR_SYSEXIT
|
|
&& (Pr->why.pr_what==SYS_execve || Pr->why.pr_what==SYS_exec)) {
|
|
int ps;
|
|
|
|
(void) Pgetareg(Pr, R_PS);
|
|
ps = Pr->REG[R_PS];
|
|
if (ps & ERRBIT) { /* exec() failed */
|
|
(void) Pstart(Pr, 0);
|
|
(void) Pwait(Pr);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* successful exec() */
|
|
make_pname(Pr);
|
|
length = 0;
|
|
if (!cflag
|
|
&& prismember(&trace,SYS_execve)
|
|
&& Pr->state == PS_STOP) {
|
|
(void) strcpy(exec_string, pname);
|
|
(void) strcat(exec_string, sys_string);
|
|
length += strlen(sys_string);
|
|
sys_leng = 0;
|
|
*sys_string = '\0';
|
|
}
|
|
sysbegin = syslast = Pr->why.pr_stime;
|
|
usrbegin = usrlast = Pr->why.pr_utime;
|
|
break;
|
|
}
|
|
|
|
if (created
|
|
&& Pr->state == PS_STOP
|
|
&& Pr->why.pr_why == PR_SYSENTRY
|
|
&& Pr->why.pr_what == SYS_exit) {
|
|
(void) Pstart(Pr, 0);
|
|
abend("Cannot find program: ", argv[0]);
|
|
}
|
|
|
|
if (!created && aflag && prismember(&trace,SYS_execve)) {
|
|
psargs(Pr);
|
|
Flush();
|
|
}
|
|
|
|
PR = Pr; /* for abend() */
|
|
|
|
if (created && Pr->state != PS_STOP) /* assertion */
|
|
if (!(interrupt || sigusr1))
|
|
abend("ASSERT error: process is not stopped", 0);
|
|
|
|
traceeven = trace; /* trace these system calls */
|
|
|
|
/* trace these regardless, even if we don't report results */
|
|
praddset(&traceeven, SYS_exit);
|
|
praddset(&traceeven, SYS_exec);
|
|
praddset(&traceeven, SYS_execve);
|
|
praddset(&traceeven, SYS_open);
|
|
praddset(&traceeven, SYS_fork);
|
|
praddset(&traceeven, SYS_vfork);
|
|
praddset(&traceeven, SYS_fork1);
|
|
|
|
/* for I/O buffer dumps, force tracing of read()s and write()s */
|
|
if (!isemptyset(&readfd)) {
|
|
praddset(&traceeven, SYS_read);
|
|
praddset(&traceeven, SYS_readv);
|
|
praddset(&traceeven, SYS_pread);
|
|
}
|
|
if (!isemptyset(&writefd)) {
|
|
praddset(&traceeven, SYS_write);
|
|
praddset(&traceeven, SYS_writev);
|
|
praddset(&traceeven, SYS_pwrite);
|
|
}
|
|
|
|
Pr->sysexit = traceeven;
|
|
Pr->setexit = TRUE;
|
|
#if (defined(i386) || defined(sparc)) && !defined(PRSYSARGS)
|
|
/*
|
|
* Trace all system calls on entry, so we can
|
|
* grab their registers before r_o0 and r_o1 have
|
|
* been clobbered by the system, which uses these
|
|
* registers to return the result of the system call.
|
|
*/
|
|
Pr->sysentry = traceeven;
|
|
Pr->setentry = TRUE;
|
|
#endif
|
|
if (slowmode) {
|
|
/*
|
|
* Trace all system calls on entry, so we
|
|
* can print the entry information and
|
|
* have it seen even if the system panics
|
|
* before the syscall completes.
|
|
*/
|
|
Pr->sysentry = traceeven;
|
|
Pr->setentry = TRUE;
|
|
}
|
|
|
|
/* special case -- cannot trace sysexit because context is changed */
|
|
if (prismember(&trace, SYS_context)) {
|
|
(void) Psysentry(Pr, SYS_context, TRUE);
|
|
(void) Psysexit(Pr, SYS_context, FALSE);
|
|
prdelset(&traceeven, SYS_context);
|
|
}
|
|
|
|
/* special case -- sysexit not traced by OS */
|
|
if (prismember(&trace, SYS_evtrapret)) {
|
|
(void) Psysentry(Pr, SYS_evtrapret, TRUE);
|
|
(void) Psysexit(Pr, SYS_evtrapret, FALSE);
|
|
prdelset(&traceeven, SYS_evtrapret);
|
|
}
|
|
|
|
/* special case -- sysexit never reached */
|
|
if (prismember(&trace, SYS_lwp_exit)) {
|
|
(void) Psysentry(Pr, SYS_lwp_exit, TRUE);
|
|
(void) Psysexit(Pr, SYS_lwp_exit, FALSE);
|
|
prdelset(&traceeven, SYS_lwp_exit);
|
|
}
|
|
|
|
if (Pr->why.pr_flags&PR_PTRACE)
|
|
isptraced();
|
|
|
|
Pr->sigmask = signals; /* trace these signals */
|
|
Pr->setsig = TRUE;
|
|
|
|
Pr->faultmask = faults; /* trace these faults */
|
|
Pr->setfault = TRUE;
|
|
|
|
if (!created
|
|
&& Pr->state == PS_STOP
|
|
&& Pr->why.pr_why == PR_REQUESTED
|
|
&& Pstart(Pr, 0) == -1)
|
|
abend("cannot start subject process", 0);
|
|
|
|
for (first = TRUE;;first = FALSE) { /* run until termination */
|
|
if (timeout) {
|
|
(void) alarm(0);
|
|
timeout = FALSE;
|
|
}
|
|
if (interrupt || sigusr1) {
|
|
if (length)
|
|
(void) fputc('\n', stdout);
|
|
Flush();
|
|
if (sigusr1)
|
|
letgo(Pr);
|
|
else
|
|
(void) Prelease(Pr);
|
|
break;
|
|
}
|
|
if (Pr->state == PS_RUN) {
|
|
/* timeout is for sleeping syscalls */
|
|
unsigned tout = (iflag||req_flag)? 0 : 2;
|
|
|
|
(void) Pstatus(Pr, PIOCWSTOP, tout);
|
|
if (Pr->state == PS_RUN && !interrupt && !sigusr1) {
|
|
/* (void) Pstop(Pr); */
|
|
req_flag = requested(Pr, 0);
|
|
}
|
|
continue;
|
|
}
|
|
if (Pr->state == PS_DEAD) {
|
|
if (!exit_called && !cflag) {
|
|
if (length)
|
|
(void) fputc('\n', stdout);
|
|
(void) printf(
|
|
"%s\t*** process killed ***\n",
|
|
pname);
|
|
Flush();
|
|
}
|
|
break;
|
|
}
|
|
if (Pr->state == PS_LOST) { /* we lost control */
|
|
if (Preopen(Pr, Fflag) == 0) /* we got it back */
|
|
continue;
|
|
|
|
/* we really lost it */
|
|
if (length)
|
|
(void) fputc('\n', stdout);
|
|
if (sys_valid)
|
|
(void) printf(
|
|
"%s\t*** cannot trace across exec() of %s ***\n",
|
|
pname, sys_path);
|
|
else
|
|
(void) printf(
|
|
"%s\t*** lost control of process ***\n",
|
|
pname);
|
|
length = 0;
|
|
Flush();
|
|
(void) Prelease(Pr);
|
|
break;
|
|
}
|
|
if (Pr->state != PS_STOP) {
|
|
(void) fprintf(stderr,
|
|
"%s: state = %d\n", command, Pr->state);
|
|
abend(pname, "uncaught status of subject process");
|
|
}
|
|
if (!cflag && lflag)
|
|
make_pname(Pr);
|
|
|
|
what = Pr->why.pr_what;
|
|
switch (Pr->why.pr_why) {
|
|
case PR_REQUESTED:
|
|
/* avoid multiple alarm sigs */
|
|
req_flag = requested(Pr, req_flag);
|
|
break;
|
|
case PR_SIGNALLED:
|
|
req_flag = signalled(Pr);
|
|
if (Sflag && prismember(&sighang, what)) {
|
|
leave_hung = TRUE;
|
|
goto done;
|
|
}
|
|
break;
|
|
case PR_FAULTED:
|
|
req_flag = 0;
|
|
faulted(Pr);
|
|
if (Mflag && prismember(&flthang, what)) {
|
|
leave_hung = TRUE;
|
|
goto done;
|
|
}
|
|
break;
|
|
case PR_JOBCONTROL: /* can't happen except first time */
|
|
req_flag = jobcontrol(Pr);
|
|
break;
|
|
case PR_SYSENTRY:
|
|
req_flag = 0;
|
|
#ifndef PRSYSARGS
|
|
sys_nargs = -1;
|
|
setupsysargs(Pr);
|
|
#endif
|
|
/* protect ourself from operating system error */
|
|
if (what <= 0 || what > PRMAXSYS)
|
|
what = PRMAXSYS;
|
|
length = 0;
|
|
|
|
/*
|
|
* Special cases. Most syscalls are traced on exit.
|
|
*/
|
|
switch (what) {
|
|
case SYS_exit: /* exit() */
|
|
case SYS_lwp_exit: /* lwp_exit() */
|
|
case SYS_context: /* [get|set]context() */
|
|
case SYS_evtrapret: /* evtrapret() */
|
|
if (!cflag && prismember(&trace,what)) {
|
|
(void) sysentry(Pr);
|
|
if (pname[0])
|
|
(void) fputs(pname, stdout);
|
|
length += printf("%s\n", sys_string);
|
|
Flush();
|
|
}
|
|
sys_leng = 0;
|
|
*sys_string = '\0';
|
|
if (prismember(&trace,what)) {
|
|
Cp->syscount[what]++;
|
|
accumulate(&Cp->systime[what],
|
|
&Pr->why.pr_stime, &syslast);
|
|
}
|
|
syslast = Pr->why.pr_stime;
|
|
usrlast = Pr->why.pr_utime;
|
|
|
|
if (what == SYS_exit
|
|
|| (what == SYS_lwp_exit
|
|
&& Pr->why.pr_nlwp <= 1)) {
|
|
#if 0 /* try commenting this out, see what happens */
|
|
(void) Pstart(Pr, 0);
|
|
goto done;
|
|
#else
|
|
exit_called = TRUE;
|
|
#endif
|
|
}
|
|
break;
|
|
case SYS_exec:
|
|
case SYS_execve:
|
|
(void) sysentry(Pr);
|
|
if (!cflag && prismember(&trace,what)) {
|
|
(void) strcpy(exec_string, pname);
|
|
(void) strcat(exec_string, sys_string);
|
|
length += strlen(sys_string);
|
|
}
|
|
sys_leng = 0;
|
|
*sys_string = '\0';
|
|
break;
|
|
default:
|
|
if (slowmode
|
|
&& !cflag && prismember(&trace,what)) {
|
|
(void) sysentry(Pr);
|
|
if (pname[0])
|
|
(void) fputs(pname, stdout);
|
|
length += printf("%s", sys_string);
|
|
sys_leng = 0;
|
|
*sys_string = '\0';
|
|
}
|
|
break;
|
|
}
|
|
if (slowmode)
|
|
Flush();
|
|
if (Tflag && !first && prismember(&syshang, what)) {
|
|
leave_hung = TRUE;
|
|
goto done;
|
|
}
|
|
break;
|
|
case PR_SYSEXIT:
|
|
req_flag = 0;
|
|
sysexit(Pr);
|
|
if (what == SYS_open) {
|
|
/* check for r/w open of /proc */
|
|
if ((Errno == 0 || Errno == EBUSY)
|
|
&& sys_valid
|
|
&& (sys_nargs > 1
|
|
&& (sys_args[1]&0x3) != O_RDONLY)
|
|
&& checkproc(Pr, sys_path, Errno)) {
|
|
letgo(Pr);
|
|
goto done;
|
|
}
|
|
}
|
|
sys_nargs = -1;
|
|
Flush();
|
|
if (Tflag && !first && prismember(&syshang, what)) {
|
|
leave_hung = TRUE;
|
|
goto done;
|
|
}
|
|
break;
|
|
default:
|
|
req_flag = 0;
|
|
abend("unknown reason for stopping", 0);
|
|
}
|
|
|
|
if (child) { /* controlled process fork()ed */
|
|
if (!fflag) {
|
|
if (no_inherit)
|
|
release(child);
|
|
}
|
|
else if (control(Pr, child)) {
|
|
child = 0;
|
|
continue;
|
|
}
|
|
child = 0;
|
|
}
|
|
|
|
if ((Pr->why.pr_flags&PR_PTRACE)
|
|
&& !wasptraced
|
|
&& Pr->why.pr_why != PR_SYSENTRY) /* freshly ptrace()d */
|
|
isptraced();
|
|
|
|
if (!(Pr->why.pr_flags & PR_ISTOP))
|
|
Pr->state = PS_RUN;
|
|
else {
|
|
int runopt = 0;
|
|
|
|
switch (req_flag) {
|
|
case PTRACED:
|
|
case JOBSIG:
|
|
case JOBSTOP:
|
|
runopt = PRSTOP;
|
|
break;
|
|
}
|
|
if (Psetrun(Pr, 0, runopt) != 0)
|
|
abend("cannot start subject process", 0);
|
|
}
|
|
}
|
|
done:
|
|
Flush();
|
|
|
|
accumulate(&Cp->systotal, &syslast, &sysbegin);
|
|
accumulate(&Cp->usrtotal, &usrlast, &usrbegin);
|
|
procdel();
|
|
if (!leave_hung)
|
|
retc = wait4all();
|
|
else {
|
|
(void) Phang(Pr);
|
|
retc = 0;
|
|
}
|
|
if (!descendent) {
|
|
interrupt = FALSE; /* another interrupt kills the report */
|
|
if (cflag)
|
|
report(times(&tms) - starttime);
|
|
}
|
|
return retc; /* exit with exit status of created process, else 0 */
|
|
}
|
|
|
|
static void
|
|
make_pname(Pr)
|
|
register process_t *Pr;
|
|
{
|
|
pname[0] = '\0';
|
|
if (!cflag) {
|
|
register char * s = pname;
|
|
register int ff = (fflag || ngrab > 1);
|
|
register int lf = lflag;
|
|
|
|
if (ff) {
|
|
(void) sprintf(s, "%ld", Pr->why.pr_pid);
|
|
s += strlen(s);
|
|
}
|
|
if (lf) {
|
|
(void) sprintf(s, "/%ld", Pr->why.pr_who);
|
|
s += strlen(s);
|
|
}
|
|
if (ff || lf) {
|
|
(void) strcpy(s, ":\t");
|
|
s += 2;
|
|
}
|
|
if (ff && lf && s < pname + 9)
|
|
(void) strcpy(s, "\t");
|
|
}
|
|
}
|
|
|
|
static int
|
|
xcreat(path) /* create output file, being careful about */
|
|
register char * path; /* suid/sgid and fd 0, 1, 2 issues */
|
|
{
|
|
register int fd;
|
|
register int mode = 0666;
|
|
|
|
if (Euid == Ruid && Egid == Rgid) /* not set-id */
|
|
fd = creat(path, mode);
|
|
else if (access(path, 0) != 0) { /* file doesn't exist */
|
|
/* if directory permissions OK, create file & set ownership */
|
|
|
|
register char * dir;
|
|
register char * p;
|
|
char dot[4];
|
|
|
|
/* generate path for directory containing file */
|
|
if ((p = strrchr(path, '/')) == NULL) { /* no '/' */
|
|
p = dir = dot;
|
|
*p++ = '.'; /* current directory */
|
|
*p = '\0';
|
|
}
|
|
else if (p == path) { /* leading '/' */
|
|
p = dir = dot;
|
|
*p++ = '/'; /* root directory */
|
|
*p = '\0';
|
|
}
|
|
else { /* embedded '/' */
|
|
dir = path; /* directory path */
|
|
*p = '\0';
|
|
}
|
|
|
|
if (access(dir, 03) != 0) { /* not writeable/searchable */
|
|
*p = '/';
|
|
fd = -1;
|
|
}
|
|
else { /* create file and set ownership correctly */
|
|
*p = '/';
|
|
if ((fd = creat(path, mode)) >= 0)
|
|
(void) chown(path, (int)Ruid, (int)Rgid);
|
|
}
|
|
}
|
|
else if (access(path, 02) != 0) /* file not writeable */
|
|
fd = -1;
|
|
else
|
|
fd = creat(path, mode);
|
|
|
|
/* make sure it's not one of 0, 1, or 2 */
|
|
/* this allows truss to work when spawned by init(1m) */
|
|
if (0 <= fd && fd <= 2) {
|
|
int dfd = fcntl(fd, F_DUPFD, 3);
|
|
|
|
(void) close(fd);
|
|
fd = dfd;
|
|
}
|
|
|
|
/* mark it close-on-exec so created processes don't inherit it */
|
|
if (fd >= 0)
|
|
(void) fcntl(fd, F_SETFD, 1); /* close-on-exec */
|
|
|
|
return fd;
|
|
}
|
|
|
|
static void
|
|
setoutput(ofd)
|
|
register int ofd;
|
|
{
|
|
if (ofd < 0) {
|
|
(void) close(1);
|
|
(void) fcntl(2, F_DUPFD, 1);
|
|
}
|
|
else if (ofd != 1) {
|
|
(void) close(1);
|
|
(void) fcntl(ofd, F_DUPFD, 1);
|
|
(void) close(ofd);
|
|
/* if no stderr, make it the same file */
|
|
if ((ofd = dup(2)) < 0)
|
|
(void) fcntl(1, F_DUPFD, 2);
|
|
else
|
|
(void) close(ofd);
|
|
}
|
|
}
|
|
|
|
static void
|
|
isptraced() /* report newly ptraced() condition */
|
|
{
|
|
wasptraced = TRUE;
|
|
if (!cflag) {
|
|
(void) printf("%s\t*** process under ptrace() ***\n",
|
|
pname);
|
|
Flush();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Accumulate time differencies: a += e - s;
|
|
*/
|
|
void
|
|
accumulate(ap, ep, sp)
|
|
register timestruc_t *ap;
|
|
register timestruc_t *ep;
|
|
register timestruc_t *sp;
|
|
{
|
|
ap->tv_sec += ep->tv_sec - sp->tv_sec;
|
|
ap->tv_nsec += ep->tv_nsec - sp->tv_nsec;
|
|
if (ap->tv_nsec >= 1000000000) {
|
|
ap->tv_nsec -= 1000000000;
|
|
ap->tv_sec++;
|
|
}
|
|
else if (ap->tv_nsec < 0) {
|
|
ap->tv_nsec += 1000000000;
|
|
ap->tv_sec--;
|
|
}
|
|
}
|
|
|
|
static void
|
|
report(lapse)
|
|
time_t lapse; /* elapsed time, clock ticks */
|
|
{
|
|
register int i;
|
|
register long count;
|
|
register CONST char * name;
|
|
long error;
|
|
register long total;
|
|
long errtot;
|
|
timestruc_t tickzero;
|
|
timestruc_t ticks;
|
|
timestruc_t ticktot;
|
|
|
|
#if 1
|
|
if (descendent)
|
|
return;
|
|
|
|
for (i = 0, total = 0; i <= PRMAXFAULT && !interrupt; i++) {
|
|
if ((count = Cp->fltcount[i]) != 0) {
|
|
if (total == 0) /* produce header */
|
|
(void) printf("faults -------------\n");
|
|
name = fltname(i);
|
|
(void) printf("%s%s\t%4ld\n", name,
|
|
(((int)strlen(name) < 8)?
|
|
(CONST char *)"\t" : (CONST char *)""),
|
|
count);
|
|
total += count;
|
|
}
|
|
Flush();
|
|
}
|
|
if (total && !interrupt) {
|
|
(void) printf("total:\t\t%4ld\n\n", total);
|
|
Flush();
|
|
}
|
|
|
|
for (i = 0, total = 0; i <= PRMAXSIG && !interrupt; i++) {
|
|
if ((count = Cp->sigcount[i]) != 0) {
|
|
if (total == 0) /* produce header */
|
|
(void) printf("signals ------------\n");
|
|
name = signame(i);
|
|
(void) printf("%s%s\t%4ld\n", name,
|
|
(((int)strlen(name) < 8)?
|
|
(CONST char *)"\t" : (CONST char *)""),
|
|
count);
|
|
total += count;
|
|
}
|
|
Flush();
|
|
}
|
|
if (total && !interrupt) {
|
|
(void) printf("total:\t\t%4ld\n\n", total);
|
|
Flush();
|
|
}
|
|
|
|
if (!interrupt) {
|
|
(void) printf("syscall seconds calls errors\n");
|
|
Flush();
|
|
}
|
|
total = errtot = 0;
|
|
tickzero.tv_sec = ticks.tv_sec = ticktot.tv_sec = 0;
|
|
tickzero.tv_nsec = ticks.tv_nsec = ticktot.tv_nsec = 0;
|
|
for (i = 0; i <= PRMAXSYS && !interrupt; i++) {
|
|
if ((count = Cp->syscount[i]) != 0 || Cp->syserror[i]) {
|
|
(void) printf("%-12.12s", sysname(i,-1));
|
|
|
|
ticks = Cp->systime[i];
|
|
accumulate(&ticktot, &ticks, &tickzero);
|
|
prtim(&ticks);
|
|
|
|
(void) printf("%8ld", count);
|
|
if ((error = Cp->syserror[i]) != 0)
|
|
(void) printf(" %6ld", error);
|
|
(void) fputc('\n', stdout);
|
|
total += count;
|
|
errtot += error;
|
|
|
|
Flush();
|
|
}
|
|
}
|
|
|
|
if (!interrupt) {
|
|
(void) printf(" ---- --- ---\n");
|
|
(void) printf("sys totals: ");
|
|
prtim(&ticktot);
|
|
(void) printf("%8ld %6ld\n", total, errtot);
|
|
Flush();
|
|
}
|
|
|
|
if (!interrupt) {
|
|
(void) printf("usr time: ");
|
|
prtim(&Cp->usrtotal);
|
|
(void) fputc('\n', stdout);
|
|
Flush();
|
|
}
|
|
|
|
if (!interrupt) {
|
|
ticks.tv_sec = lapse/HZ;
|
|
ticks.tv_nsec = (lapse%HZ)*(1000000000/HZ);
|
|
(void) printf("elapsed: ");
|
|
prtim(&ticks);
|
|
(void) fputc('\n', stdout);
|
|
Flush();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
prtim(tp)
|
|
register timestruc_t *tp;
|
|
{
|
|
register unsigned long sec;
|
|
|
|
if ((sec = tp->tv_sec) != 0) /* whole seconds */
|
|
(void) printf("%5lu", sec);
|
|
else
|
|
(void) printf(" ");
|
|
|
|
(void) printf(".%2.2ld", tp->tv_nsec/10000000); /* fraction */
|
|
}
|
|
|
|
static int /* gather process id's */
|
|
pids(path) /* return 0 on success, != 0 on failure */
|
|
register char * path; /* number or /proc/nnn */
|
|
{
|
|
register char * name;
|
|
register pid_t pid;
|
|
register int i;
|
|
char *next;
|
|
int rc = 0;
|
|
|
|
if ((name = strrchr(path, '/')) != NULL) /* last component */
|
|
*name++ = '\0';
|
|
else {
|
|
name = path;
|
|
path = procdir;
|
|
}
|
|
|
|
pid = strtol(name, &next, 10);
|
|
if (isdigit(*name) && pid >= 0 && pid <= MAXPID && *next == '\0') {
|
|
for (i = 0; i < ngrab; i++)
|
|
if (grab[i] == pid /* duplicate */
|
|
&& strcmp(grabdir[i], path) == 0)
|
|
break;
|
|
if (i < ngrab) {
|
|
(void) fprintf(stderr,
|
|
"%s: duplicate process id ignored: %ld\n",
|
|
command, pid);
|
|
}
|
|
else if (ngrab < MAXGRAB) {
|
|
if (strcmp(procdir, path) != 0
|
|
&& !isprocdir((process_t *)NULL, path)) {
|
|
(void) fprintf(stderr,
|
|
"%s: %s is not a PROC directory\n",
|
|
command, path);
|
|
rc = -1;
|
|
}
|
|
else {
|
|
grabdir[ngrab] = path;
|
|
grab[ngrab++] = pid;
|
|
}
|
|
}
|
|
else {
|
|
if (ngrab++ == MAXGRAB) /* only one message */
|
|
(void) fprintf(stderr,
|
|
"%s: too many process id's, max is %d\n",
|
|
command, MAXGRAB);
|
|
rc = -1;
|
|
}
|
|
}
|
|
else {
|
|
(void) fprintf(stderr, "%s: invalid process id: %s\n",
|
|
command, name);
|
|
rc = -1;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void
|
|
psargs(Pr) /* report psargs string */
|
|
register process_t *Pr;
|
|
{
|
|
struct prpsinfo prpsinfo;
|
|
|
|
if (Ioctl(Pr->pfd, PIOCPSINFO, (int)&prpsinfo) == -1)
|
|
perror("PIOCPSINFO");
|
|
else {
|
|
prpsinfo.pr_psargs[PRARGSZ-16] = '\0';
|
|
(void) printf("%spsargs: %s\n", pname, prpsinfo.pr_psargs);
|
|
}
|
|
}
|
|
|
|
char *
|
|
fetchstring(addr, maxleng)
|
|
register long addr;
|
|
register int maxleng;
|
|
{
|
|
register process_t *Pr = &Proc;
|
|
register int nbyte;
|
|
register leng = 0;
|
|
char string[41];
|
|
string[40] = '\0';
|
|
|
|
if (str_bsize == 0) { /* initial allocation of string buffer */
|
|
str_buffer = (char *)malloc(str_bsize = 16);
|
|
if (str_buffer == NULL)
|
|
abend("cannot allocate string buffer", 0);
|
|
}
|
|
*str_buffer = '\0';
|
|
|
|
for (nbyte = 40; nbyte == 40 && leng < maxleng; addr += 40) {
|
|
if ((nbyte = Pread(Pr, addr, string, 40)) < 0)
|
|
return leng? str_buffer : NULL;
|
|
if (nbyte > 0
|
|
&& (nbyte = strlen(string)) > 0) {
|
|
while (leng+nbyte >= str_bsize) {
|
|
str_buffer = (char *)realloc(str_buffer, str_bsize *= 2);
|
|
if (str_buffer == NULL)
|
|
abend("cannot reallocate string buffer", 0);
|
|
}
|
|
(void) strcpy(str_buffer+leng, string);
|
|
leng += nbyte;
|
|
}
|
|
}
|
|
|
|
if (leng > maxleng)
|
|
leng = maxleng;
|
|
str_buffer[leng] = '\0';
|
|
|
|
return str_buffer;
|
|
}
|
|
|
|
void
|
|
show_cred(Pr, new)
|
|
register process_t *Pr;
|
|
int new;
|
|
{
|
|
prcred_t cred;
|
|
|
|
(void) Ioctl(Pr->pfd, PIOCCRED, (int)&cred);
|
|
|
|
if (!cflag && prismember(&trace,SYS_exec)) {
|
|
if (new)
|
|
credentials = cred;
|
|
if ((new && cred.pr_ruid != cred.pr_suid)
|
|
|| cred.pr_ruid != credentials.pr_ruid
|
|
|| cred.pr_suid != credentials.pr_suid)
|
|
(void) printf(
|
|
"%s *** SUID: ruid/euid/suid = %ld / %ld / %ld ***\n",
|
|
pname,
|
|
cred.pr_ruid,
|
|
cred.pr_euid,
|
|
cred.pr_suid);
|
|
if ((new && cred.pr_rgid != cred.pr_sgid)
|
|
|| cred.pr_rgid != credentials.pr_rgid
|
|
|| cred.pr_sgid != credentials.pr_sgid)
|
|
(void) printf(
|
|
"%s *** SGID: rgid/egid/sgid = %ld / %ld / %ld ***\n",
|
|
pname,
|
|
cred.pr_rgid,
|
|
cred.pr_egid,
|
|
cred.pr_sgid);
|
|
}
|
|
|
|
credentials = cred;
|
|
}
|
|
|
|
static int
|
|
control(Pr, pid) /* take control of a child process */
|
|
register process_t *Pr;
|
|
pid_t pid;
|
|
{
|
|
pid_t mypid = 0;
|
|
|
|
(void) sighold(SIGUSR1);
|
|
if (interrupt || sigusr1 || (mypid = fork()) == -1) {
|
|
if (mypid == -1)
|
|
(void) printf(
|
|
"%s\t*** Cannot fork() to control process #%ld\n",
|
|
pname, pid);
|
|
(void) sigrelse(SIGUSR1);
|
|
release(pid);
|
|
return FALSE;
|
|
}
|
|
|
|
if (mypid != 0) { /* parent carries on */
|
|
while (!interrupt && !sigusr1) {
|
|
(void) sigpause(SIGUSR1); /* after a brief pause */
|
|
(void) sighold(SIGUSR1);
|
|
}
|
|
(void) sigrelse(SIGUSR1);
|
|
sigusr1 = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
(void) sigrelse(SIGUSR1);
|
|
descendent = TRUE;
|
|
exit_called = FALSE;
|
|
(void) close(Pr->pfd); /* forget old process */
|
|
|
|
if (Pgrab(Pr, pid, FALSE) != 0) { /* child grabs new process */
|
|
(void) fprintf(stderr,
|
|
"%s: cannot control child process, pid# %ld\n",
|
|
command, pid);
|
|
(void) kill(getppid(), SIGUSR1); /* wake up parent */
|
|
exit(2);
|
|
}
|
|
|
|
if (!cflag)
|
|
make_pname(Pr);
|
|
|
|
if (Pr->why.pr_why != PR_SYSEXIT
|
|
|| (Pr->why.pr_what != SYS_fork
|
|
&& Pr->why.pr_what != SYS_vfork
|
|
&& Pr->why.pr_what != SYS_fork1))
|
|
(void) printf("%s\t*** Expected SYSEXIT, SYS_[v]fork\n", pname);
|
|
|
|
sysbegin = syslast = Pr->why.pr_stime;
|
|
usrbegin = usrlast = Pr->why.pr_utime;
|
|
|
|
wasptraced = FALSE; /* child is not ptrace()d */
|
|
|
|
no_inherit = Ioctl(Pr->pfd, PIOCSFORK, 0);
|
|
procadd(pid);
|
|
(void) kill(getppid(), SIGUSR1); /* wake up parent */
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
grabit(Pr, dir, pid) /* take control of an existing process */
|
|
register process_t *Pr;
|
|
char * dir;
|
|
pid_t pid;
|
|
{
|
|
procdir = dir; /* /proc directory */
|
|
|
|
/* don't force the takeover unless the -F option was specified */
|
|
switch (Pgrab(Pr, pid, Fflag)) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
(void) fprintf(stderr,
|
|
"%s: someone else is tracing process %ld\n",
|
|
command, pid);
|
|
return FALSE;
|
|
default:
|
|
goto bad;
|
|
}
|
|
|
|
if ((Pr->state != PS_STOP || Pr->why.pr_why != PR_REQUESTED)
|
|
&& !(Pr->why.pr_flags & PR_DSTOP)) {
|
|
#if 0
|
|
/* DEBUG */
|
|
(void) fprintf(stderr,
|
|
"%s: expected REQUESTED stop, pid# %d\n",
|
|
command, pid);
|
|
#endif
|
|
if (Pr->state != PS_STOP && !(Pr->why.pr_flags & PR_DSTOP)) {
|
|
(void) Prelease(Pr);
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
if (!cflag)
|
|
make_pname(Pr);
|
|
|
|
sysbegin = syslast = Pr->why.pr_stime;
|
|
usrbegin = usrlast = Pr->why.pr_utime;
|
|
|
|
no_inherit = Ioctl(Pr->pfd, fflag? PIOCSFORK : PIOCRFORK, 0);
|
|
procadd(pid);
|
|
show_cred(Pr, TRUE);
|
|
return TRUE;
|
|
|
|
bad:
|
|
if (errno == ENOENT)
|
|
(void) fprintf(stderr, "%s: no process %ld\n",
|
|
command, pid);
|
|
else
|
|
(void) fprintf(stderr, "%s: cannot control process %ld\n",
|
|
command, pid);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
release(pid) /* release process from control */
|
|
pid_t pid;
|
|
{
|
|
/* The process in question is the child of a traced process. */
|
|
/* We are here to turn off the inherited tracing flags. */
|
|
|
|
register int fd;
|
|
char procname[100];
|
|
|
|
/* open the /proc/upid file */
|
|
(void) sprintf(procname, "%s/%ld", procdir, pid);
|
|
|
|
/* this process is freshly forked, no need for exclusive open */
|
|
if ((fd = open(procname, O_RDWR)) < 0
|
|
|| Ioctl(fd, PIOCSRLC, 0) != 0) {
|
|
perror("release()");
|
|
(void) printf(
|
|
"%s\t*** Cannot release child process, pid# %ld\n",
|
|
pname, pid);
|
|
}
|
|
if (fd >= 0)
|
|
(void) close(fd);
|
|
}
|
|
|
|
static void
|
|
intr(sig)
|
|
register int sig;
|
|
{
|
|
/* SIGUSR1 is special. It is used by one truss process to tell */
|
|
/* another truss processes to release its controlled process. */
|
|
/* SIGALRM is used for timing out indefinite waits. */
|
|
if (sig == SIGUSR1) {
|
|
sigusr1 = TRUE;
|
|
} else if (sig == SIGALRM) { /* reset alarm clock */
|
|
timeout = TRUE;
|
|
(void) alarm(1);
|
|
} else {
|
|
interrupt = TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
errmsg(s,q)
|
|
register CONST char *s;
|
|
register CONST char *q;
|
|
{
|
|
char msg[200];
|
|
|
|
msg[0] = '\0';
|
|
if (command) {
|
|
(void) strcpy(msg, command);
|
|
(void) strcat(msg, ": ");
|
|
}
|
|
if (s) (void) strcat(msg, s);
|
|
if (q) (void) strcat(msg, q);
|
|
(void) strcat(msg, "\n");
|
|
(void) write(2, msg, (unsigned)strlen(msg));
|
|
}
|
|
|
|
void
|
|
abend(s,q)
|
|
register CONST char *s;
|
|
register CONST char *q;
|
|
{
|
|
Flush();
|
|
if (s || q)
|
|
errmsg(s, q);
|
|
if (PR) {
|
|
if (created)
|
|
(void) Pterm(PR);
|
|
else
|
|
(void) Prelease(PR);
|
|
}
|
|
procdel();
|
|
(void) wait4all();
|
|
exit(2);
|
|
}
|
|
|
|
static int
|
|
wait4all()
|
|
{
|
|
register int i;
|
|
register pid_t pid;
|
|
register int rc = 0;
|
|
int status;
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
while ((pid = wait(&status)) != -1) {
|
|
/* return exit() code of the created process */
|
|
if (pid == created) {
|
|
if ((rc = status&0xff) == 0)
|
|
rc = (status>>8)&0xff; /* exit()ed */
|
|
else
|
|
rc |= 0x80; /* killed */
|
|
}
|
|
}
|
|
if (errno != EINTR && errno != ERESTART)
|
|
break;
|
|
}
|
|
|
|
if (i >= 10) /* repeated interrupts */
|
|
rc = 2;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void
|
|
letgo(Pr)
|
|
register process_t *Pr;
|
|
{
|
|
(void) printf("%s\t*** process otherwise traced, releasing ...\n",
|
|
pname);
|
|
Flush();
|
|
(void) Prelease(Pr);
|
|
}
|
|
|
|
int
|
|
isprocdir(Pr, dir) /* return TRUE iff dir is a PROC directory */
|
|
process_t *Pr;
|
|
CONST char *dir; /* this is filthy */
|
|
{
|
|
/*
|
|
* This is based on the fact that "/proc/<n>" and "/proc/0<n>"
|
|
* are the same file, namely process <n>.
|
|
*/
|
|
|
|
struct stat stat1; /* dir/<pid> */
|
|
struct stat stat2; /* dir/0<pid> */
|
|
char path[200];
|
|
register char * p;
|
|
pid_t pid;
|
|
|
|
/* make a copy of the directory name without trailing '/'s */
|
|
if (dir == NULL)
|
|
(void) strcpy(path, ".");
|
|
else {
|
|
(void) strncpy(path, dir, (int)sizeof(path)-4);
|
|
path[sizeof(path)-4] = '\0';
|
|
p = path + strlen(path);
|
|
while (p > path && *--p == '/')
|
|
*p = '\0';
|
|
if (*path == '\0')
|
|
(void) strcpy(path, ".");
|
|
}
|
|
|
|
if (Pr == NULL)
|
|
pid = getpid();
|
|
else
|
|
pid = PR->upid;
|
|
|
|
/* append "/<pid>" to the directory path and lstat() the file */
|
|
p = path + strlen(path);
|
|
(void) sprintf(p, "/%ld", pid);
|
|
if (prlstat(Pr, path, &stat1) != 0) /* make process execute lstat */
|
|
return FALSE;
|
|
|
|
/* append "/0<pid>" to the directory path and lstat() the file */
|
|
(void) sprintf(p, "/0%ld", pid);
|
|
if (prlstat(Pr, path, &stat2) != 0) /* make process execute lstat */
|
|
return FALSE;
|
|
|
|
/* see if we ended up with the same file */
|
|
if (stat1.st_dev != stat2.st_dev
|
|
|| stat1.st_ino != stat2.st_ino
|
|
|| stat1.st_mode != stat2.st_mode
|
|
|| stat1.st_nlink != stat2.st_nlink
|
|
|| stat1.st_uid != stat2.st_uid
|
|
|| stat1.st_gid != stat2.st_gid
|
|
|| stat1.st_size != stat2.st_size)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int /* lstat() system call -- executed by subject process */
|
|
prlstat(Pr, path, buf)
|
|
process_t *Pr;
|
|
char * path;
|
|
struct stat * buf;
|
|
{
|
|
struct sysret rval; /* return value from lstat() */
|
|
struct argdes argd[3]; /* arg descriptors for lstat() */
|
|
register struct argdes *adp = &argd[0]; /* first argument */
|
|
register int syscall; /* which syscall, lstat or lxstat */
|
|
register int nargs; /* number of actual arguments */
|
|
|
|
if (Pr == (process_t *)NULL) /* no subject process */
|
|
return lstat(path, buf);
|
|
|
|
/*
|
|
* This is filthy, but truss(1) reveals everything about the
|
|
* system call interfaces, despite what the architects of the
|
|
* header files may desire. We have to know here whether we
|
|
* are calling the old or new lstat(2) syscall in the subject.
|
|
*/
|
|
#ifdef _STYPES /* old version of lstat(2) */
|
|
syscall = SYS_lstat;
|
|
nargs = 2;
|
|
#elif defined(_STAT_VER) || defined(STAT_VER)
|
|
/* new version of lstat(2) */
|
|
syscall = SYS_lxstat;
|
|
nargs = 3;
|
|
# ifdef _STAT_VER /* k18.2 */
|
|
adp->value = _STAT_VER;
|
|
# else
|
|
adp->value = STAT_VER;
|
|
# endif
|
|
adp->object = (char *)NULL;
|
|
adp->type = AT_BYVAL;
|
|
adp->inout = AI_INPUT;
|
|
adp->len = 0;
|
|
adp++; /* move to pathname argument */
|
|
#else /* newest version of lstat(2) */
|
|
syscall = SYS_lstat;
|
|
nargs = 2;
|
|
#endif
|
|
|
|
adp->value = 0;
|
|
adp->object = (char *)path;
|
|
adp->type = AT_BYREF;
|
|
adp->inout = AI_INPUT;
|
|
adp->len = strlen(path)+1;
|
|
adp++; /* move to buffer argument */
|
|
|
|
adp->value = 0;
|
|
adp->object = (char *)buf;
|
|
adp->type = AT_BYREF;
|
|
adp->inout = AI_OUTPUT;
|
|
adp->len = sizeof(struct stat);
|
|
|
|
rval = Psyscall(Pr, syscall, nargs, &argd[0]);
|
|
|
|
if (rval.errno < 0) {
|
|
(void) fprintf(stderr,
|
|
"%s\t*** error from Psyscall(SYS_lstat): %d\n",
|
|
pname, rval.errno);
|
|
rval.errno = EINVAL;
|
|
}
|
|
|
|
if (rval.errno == 0)
|
|
return 0;
|
|
errno = rval.errno;
|
|
return -1;
|
|
}
|