/*
 * Copyright (c) 1991, 1992 Paul Kranenburg <pk@cs.few.eur.nl>
 * Copyright (c) 1993 Branko Lankester <branko@hacktic.nl>
 * Copyright (c) 1993, 1994 Rick Sladkey <jrs@world.std.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Paul Kranenburg,
 *      Branko Lankester and Rick Sladkey.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *	process.c,v 2.25 1994/06/29 04:43:22 jrs Exp
 */

#include "defs.h"

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <sys/user.h>
#include <signal.h>
#ifdef SUNOS4
#include <machine/reg.h>
#endif

#ifndef WCOREDUMP
#define WCOREDUMP(status) ((status) & 0200)
#endif

int
sys_gethostid(tcp)
struct tcb *tcp;
{
	if (exiting(tcp))
		return RVAL_HEX;
	return 0;
}

int
sys_sethostname(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		printpathn(tcp, tcp->u_arg[0], tcp->u_arg[1]);
		tprintf(", %u", tcp->u_arg[1]);
	}
	return 0;
}

int
sys_gethostname(tcp)
struct tcb *tcp;
{
	if (exiting(tcp)) {
		if (syserror(tcp))
			tprintf("%#x", tcp->u_arg[0]);
		else
			printpath(tcp, tcp->u_arg[0]);
		tprintf(", %u", tcp->u_arg[1]);
	}
	return 0;
}

int
sys_setdomainname(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		printpathn(tcp, tcp->u_arg[0], tcp->u_arg[1]);
		tprintf(", %u", tcp->u_arg[1]);
	}
	return 0;
}

#ifndef LINUX

int
sys_getdomainname(tcp)
struct tcb *tcp;
{
	if (exiting(tcp)) {
		if (syserror(tcp))
			tprintf("%#x", tcp->u_arg[0]);
		else
			printpath(tcp, tcp->u_arg[0]);
		tprintf(", %u", tcp->u_arg[1]);
	}
	return 0;
}
#endif /* !LINUX */

int
sys_exit(tcp)
struct tcb *tcp;
{
	if (exiting(tcp)) {
		fprintf(stderr, "_exit returned!\n");
		return -1;
	}
	/* special case: we stop tracing this process, finish line now */
	tprintf("%d) ", tcp->u_arg[0]);
	tabto(acolumn);
	tprintf("= ?");
	printtrailer(tcp);
	tcp->flags |= TCB_EXITING;
	return 0;
}

#ifdef SVR4

int
sys_fork(tcp)
struct tcb *tcp;
{
	struct tcb *tcpchild;

	if (exiting(tcp)) {
		if (getrval2(tcp)) {
			tcp->auxstr = "child process";
			return RVAL_UDECIMAL | RVAL_STR;
		}
		if (!followfork)
			return 0;
		if (nprocs == MAX_PROCS) {
			tcp->flags &= ~TCB_FOLLOWFORK;
			fprintf(stderr, "sys_fork: tcb table full\n");
			return 0;
		}
		else
			tcp->flags |= TCB_FOLLOWFORK;
		if (syserror(tcp))
			return 0;
		if ((tcpchild = alloctcb(tcp->u_rval)) == NULL) {
			fprintf(stderr, "sys_fork: tcb table full\n");
			return 0;
		}
		proc_open(tcpchild, 1);
	}
	return 0;
}

#else /* !SVR4 */

static int vforking;

int
sys_fork(tcp)
struct tcb *tcp;
{
	struct tcb *tcpchild;
	int pid;

	if (entering(tcp)) {
		if (!followfork || vforking)
			return 0;
		if (nprocs == MAX_PROCS) {
			tcp->flags &= ~TCB_FOLLOWFORK;
			fprintf(stderr, "sys_fork: tcb table full\n");
			return 0;
		}
		tcp->flags |= TCB_FOLLOWFORK;
		if (setbpt(tcp) < 0)
			return 0;
	}
	else {
		int bpt = tcp->flags & TCB_BPTSET;

		if (!(tcp->flags & TCB_FOLLOWFORK))
			return 0;
		if (bpt)
			clearbpt(tcp);

		if (syserror(tcp))
			return 0;

		pid = tcp->u_rval;
		if ((tcpchild = alloctcb(pid)) == NULL) {
			fprintf(stderr, " [tcb table full]\n");
			kill(pid, SIGKILL); /* XXX */
			return 0;
		}
#ifdef LINUX
		if (ptrace(PTRACE_ATTACH, pid, (char *) 1, 0) < 0) {
			perror("PTRACE_ATTACH");
			fprintf(stderr, "Too late?\n");
			droptcb(tcpchild);
			return 0;
		}
#endif /* LINUX */
#ifdef SUNOS4
#ifdef oldway
		/* The child must have run before it can be attached. */
		{
			struct timeval tv;
			tv.tv_sec = 0;
			tv.tv_usec = 10000;
			select(0, NULL, NULL, NULL, &tv);
		}
		if (ptrace(PTRACE_ATTACH, pid, (char *)1, 0) < 0) {
			perror("PTRACE_ATTACH");
			fprintf(stderr, "Too late?\n");
			droptcb(tcpchild);
			return 0;
		}
#else /* !oldway */
		/* Try to catch the new process as soon as possible. */
		{
			int i;
			for (i = 0; i < 1024; i++)
				if (ptrace(PTRACE_ATTACH, pid, (char *) 1, 0) >= 0)
					break;
			if (i == 1024) {
				perror("PTRACE_ATTACH");
				fprintf(stderr, "Too late?\n");
				droptcb(tcpchild);
				return 0;
			}
		}
#endif /* !oldway */
#endif /* SUNOS4 */
		tcpchild->flags |= TCB_ATTACHED;
		/* Child has BPT too, must be removed on first occasion */
		if (bpt) {
			tcpchild->flags |= TCB_BPTSET;
			tcpchild->baddr = tcp->baddr;
			memcpy(tcpchild->inst, tcp->inst,
				sizeof tcpchild->inst);
		}
		newoutf(tcpchild);
		tcpchild->parent = tcp;
		tcp->nchildren++;
		if (!qflag)
			fprintf(stderr, "Process %d attached\n", pid);
	}
	return 0;
}

#endif /* !SVR4 */

#ifdef SUNOS4

int
sys_vfork(tcp)
struct tcb *tcp;
{
	/* XXX - cannot use the same trick for vfork */
	int res;

	vforking = 1;
	res = sys_fork(tcp);
	vforking = 0;
	return res;
}

#endif /* SUNOS4 */

#ifndef LINUX

static char idstr[16];

int
sys_getpid(tcp)
struct tcb *tcp;
{
	if (exiting(tcp)) {
		sprintf(idstr, "ppid %u", getrval2(tcp));
		tcp->auxstr = idstr;
		return RVAL_STR;
	}
	return 0;
}

int
sys_getuid(tcp)
struct tcb *tcp;
{
	if (exiting(tcp)) {
		sprintf(idstr, "euid %u", getrval2(tcp));
		tcp->auxstr = idstr;
		return RVAL_STR;
	}
	return 0;
}

int
sys_getgid(tcp)
struct tcb *tcp;
{
	if (exiting(tcp)) {
		sprintf(idstr, "egid %u", getrval2(tcp));
		tcp->auxstr = idstr;
		return RVAL_STR;
	}
	return 0;
}

#endif /* !LINUX */

#ifdef LINUX

int
sys_setuid(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		tprintf("%u", (uid_t) tcp->u_arg[0]);
	}
	return 0;
}

int
sys_setgid(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		tprintf("%u", (gid_t) tcp->u_arg[0]);
	}
	return 0;
}

#endif /* LINUX */

int
sys_setreuid(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		tprintf("%lu, %lu",
			(unsigned long) (uid_t) tcp->u_arg[0],
			(unsigned long) (uid_t) tcp->u_arg[1]);
	}
	return 0;
}

int
sys_setregid(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		tprintf("%lu, %lu",
			(unsigned long) (gid_t) tcp->u_arg[0],
			(unsigned long) (gid_t) tcp->u_arg[1]);
	}
	return 0;
}

int
sys_setgroups(tcp)
struct tcb *tcp;
{
	int i, len;
	GETGROUPS_T *gidset;

	if (entering(tcp)) {
		len = tcp->u_arg[0];
		tprintf("%u, ", len);
		if (len <= 0) {
			tprintf("[]");
			return 0;
		}
		gidset = (GETGROUPS_T *) malloc(len * sizeof(GETGROUPS_T));
		if (gidset == NULL) {
			fprintf(stderr, "sys_setgroups: out of memory\n");
			return -1;
		}
		if (!verbose(tcp))
			tprintf("%#x", tcp->u_arg[1]);
		else if (umoven(tcp, tcp->u_arg[1],
		    len * sizeof(GETGROUPS_T), (char *) gidset) < 0)
			tprintf("[?]");
		else {
			tprintf("[");
			for (i = 0; i < len; i++)
				tprintf("%s%lu", i ? ", " : "",
					(unsigned long) gidset[i]);
			tprintf("]");
		}
		free((char *) gidset);
	}
	return 0;
}

int
sys_getgroups(tcp)
struct tcb *tcp;
{
	int i, len;
	GETGROUPS_T *gidset;

	if (entering(tcp)) {
		len = tcp->u_arg[0];
		tprintf("%u, ", len);
	} else {
		len = tcp->u_rval;
		if (len <= 0) {
			tprintf("[]");
			return 0;
		}
		gidset = (GETGROUPS_T *) malloc(len * sizeof(GETGROUPS_T));
		if (gidset == NULL) {
			fprintf(stderr, "sys_getgroups: out of memory\n");
			return -1;
		}
		if (!tcp->u_arg[1])
			tprintf("NULL");
		else if (!verbose(tcp) || tcp->u_arg[0] == 0)
			tprintf("%#x", tcp->u_arg[1]);
		else if (umoven(tcp, tcp->u_arg[1],
		    len * sizeof(GETGROUPS_T), (char *) gidset) < 0)
			tprintf("[?]");
		else {
			tprintf("[");
			for (i = 0; i < len; i++)
				tprintf("%s%lu", i ? ", " : "",
					(unsigned long) gidset[i]);
			tprintf("]");
		}
		free((char *)gidset);
	}
	return 0;
}

int
sys_setpgrp(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
#ifndef SVR4
		tprintf("%u, %u", tcp->u_arg[0], tcp->u_arg[1]);
#endif /* !SVR4 */
	}
	return 0;
}

int
sys_getpgrp(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
#ifndef SVR4
		tprintf("%u", tcp->u_arg[0]);
#endif /* !SVR4 */
	}
	return 0;
}

int
sys_getsid(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		tprintf("%u", tcp->u_arg[0]);
	}
	return 0;
}

int
sys_setsid(tcp)
struct tcb *tcp;
{
	return 0;
}

int
sys_getpgid(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		tprintf("%u", tcp->u_arg[0]);
	}
	return 0;
}

int
sys_setpgid(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		tprintf("%u, %u", tcp->u_arg[0], tcp->u_arg[1]);
	}
	return 0;
}

static void
printargv(tcp, addr)
struct tcb *tcp;
int addr;
{
	int cp;
	char *sep;
	int max = max_strlen / 2;

	for (sep = ""; --max >= 0; sep = ", ") {
		if (umove(tcp, addr, &cp) < 0) {
			tprintf("%#x", addr);
			return;
		}
		if (cp == 0)
			break;
		tprintf(sep);
		printstr(tcp, cp, -1);
		addr += sizeof(char *);
	}
	if (cp)
		tprintf(", ...");
}

static void
printargc(fmt, tcp, addr)
char *fmt;
struct tcb *tcp;
int addr;
{
	int cp;
	int count;

	for (count = 0; ; count++) {
		if (umove(tcp, addr, &cp) < 0)
			break;
		if (cp == 0)
			break;
		addr += sizeof(char *);
	}
	tprintf(fmt, count, count == 1 ? "" : "s");
}

int
sys_execv(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		printpath(tcp, tcp->u_arg[0]);
		if (!verbose(tcp))
			tprintf(", %#x", tcp->u_arg[1]);
#if 0
		else if (abbrev(tcp))
			printargc(", [%d arg%s]", tcp, tcp->u_arg[1]);
#endif
		else {
			tprintf(", [");
			printargv(tcp, tcp->u_arg[1]);
			tprintf("]");
		}
	}
#ifdef SUNOS4
	else if (!syserror(tcp) && followfork)
		fixvfork(tcp);
#endif /* SUNOS4 */
	return 0;
}

int
sys_execve(tcp)
struct tcb *tcp;
{
	if (entering(tcp)) {
		printpath(tcp, tcp->u_arg[0]);
		if (!verbose(tcp))
			tprintf(", %#x", tcp->u_arg[1]);
#if 0
		else if (abbrev(tcp))
			printargc(", [%d arg%s]", tcp, tcp->u_arg[1]);
#endif
		else {
			tprintf(", [");
			printargv(tcp, tcp->u_arg[1]);
			tprintf("]");
		}
		if (!verbose(tcp))
			tprintf(", %#x", tcp->u_arg[2]);
		else if (abbrev(tcp))
			printargc(", [%d var%s]", tcp, tcp->u_arg[2]);
		else {
			tprintf("[");
			printargv(tcp, tcp->u_arg[2]);
			tprintf("]");
		}
	}
#ifdef SUNOS4
	else if (!syserror(tcp) && followfork)
		fixvfork(tcp);
#endif /* SUNOS4 */
	return 0;
}

#ifdef LINUX
#ifndef __WCLONE
#define __WCLONE	0x8000000
#endif
#endif /* LINUX */

static struct xlat wait4_options[] = {
	{ WNOHANG,	"WNOHANG"	},
#ifndef WSTOPPED
	{ WUNTRACED,	"WUNTRACED"	},
#endif
#ifdef WEXITED
	{ WEXITED,	"WEXITED"	},
#endif
#ifdef WTRAPPED
	{ WTRAPPED,	"WTRAPPED"	},
#endif
#ifdef WSTOPPED
	{ WSTOPPED,	"WSTOPPED"	},
#endif
#ifdef WCONTINUED
	{ WCONTINUED,	"WCONTINUED"	},
#endif
#ifdef WNOWAIT
	{ WNOWAIT,	"WNOWAIT"	},
#endif
#ifdef __WCLONE
	{ __WCLONE,	"__WCLONE"	},
#endif
	{ 0,		NULL		},
};

static int
printstatus(status)
int status;
{
	int exited = 0;

	/*
	 * Here is a tricky presentation problem.  This solution
	 * is still not entirely satisfactory because there
	 * are no wait status constructors.
	 */
	if (WIFSTOPPED(status))
		tprintf("[WIFSTOPPED(s) && WSTOPSIG(s) == %s]",
			signalent[WSTOPSIG(status)]);
	else if WIFSIGNALED(status)
		tprintf("[WIFSTOPPED(s) && WTERMSIG(s) == %s%s]",
			signalent[WTERMSIG(status)],
			WCOREDUMP(status) ? " && WCOREDUMP(s)" : "");
	else if WIFEXITED(status) {
		tprintf("[WIFEXITED(s) && WEXITSTATUS(s) == %d]",
			WEXITSTATUS(status));
		exited = 1;
	}
	else
		tprintf("[%#x]", status);
	return exited;
}

static int
printwaitn(tcp, n)
struct tcb *tcp;
int n;
{
	int status;
	int exited;

	if (entering(tcp)) {
		tprintf("%d, ", tcp->u_arg[0]);
		if (tcp->nchildren > 0) {
			/* There are traced children */
			tcp->flags |= TCB_SUSPENDED;
			tcp->waitpid = tcp->u_arg[0];
		}
	} else {
		/* status */
		if (!tcp->u_arg[1])
			tprintf("NULL");
		else if (syserror(tcp) || tcp->u_rval == 0)
			tprintf("%#x", tcp->u_arg[1]);
		else if (umove(tcp, tcp->u_arg[1], &status) < 0)
			tprintf("[?]");
		else
			exited = printstatus(status);
		/* options */
		tprintf(", ");
		if (!printflags(wait4_options, tcp->u_arg[2]))
			tprintf("0");
		if (n == 4) {
			tprintf(", ");
			/* usage */
			if (!tcp->u_arg[3])
				tprintf("NULL");
#ifdef LINUX
			else if (tcp->u_rval > 0)
				printrusage(tcp, tcp->u_arg[3]);
#endif /* LINUX */
#ifdef SUNOS4
			else if (tcp->u_rval > 0 && exited)
				printrusage(tcp, tcp->u_arg[3]);
#endif /* SUNOS4 */
			else
				tprintf("%#x", tcp->u_arg[3]);
		}
	}
	return 0;
}

#ifdef SVR4

int
sys_wait(tcp)
struct tcb *tcp;
{
	if (exiting(tcp)) {
		/* The library wrapper stuffs this into the user variable. */
		if (!syserror(tcp))
			printstatus(getrval2(tcp));
	}
	return 0;
}

#endif /* SVR4 */

int
sys_waitpid(tcp)
struct tcb *tcp;
{
	return printwaitn(tcp, 3);
}

int
sys_wait4(tcp)
struct tcb *tcp;
{
	return printwaitn(tcp, 4);
}

#ifdef SVR4

static struct xlat waitid_types[] = {
	{ P_PID,	"P_PID"		},
	{ P_PPID,	"P_PPID"	},
	{ P_PGID,	"P_PGID"	},
	{ P_SID,	"P_SID"		},
	{ P_CID,	"P_CID"		},
	{ P_UID,	"P_UID"		},
	{ P_GID,	"P_GID"		},
	{ P_ALL,	"P_ALL"		},
#ifdef P_LWPID
	{ P_LWPID,	"P_LWPID"	},
#endif
	{ 0,		NULL		},
};

static struct xlat siginfo_codes[] = {
#ifdef SI_NOINFO
	{ SI_NOINFO,	"SI_NOINFO"	},
#endif
#ifdef SI_USER
	{ SI_USER,	"SI_USER"	},
#endif
#ifdef SI_LWP
	{ SI_LWP,	"SI_LWP"	},
#endif
#ifdef SI_QUEUE
	{ SI_QUEUE,	"SI_QUEUE"	},
#endif
#ifdef SI_TIMER
	{ SI_TIMER,	"SI_TIMER"	},
#endif
#ifdef SI_ASYNCIO
	{ SI_ASYNCIO,	"SI_ASYNCIO"	},
#endif
#ifdef SI_MESGQ
	{ SI_MESGQ,	"SI_MESGQ"	},
#endif
	{ 0,		NULL		},
};

static struct xlat sigtrap_codes[] = {
	{ TRAP_BRKPT,	"TRAP_BRKPT"	},
	{ TRAP_TRACE,	"TRAP_TRACE"	},
	{ 0,		NULL		},
};

static struct xlat sigcld_codes[] = {
	{ CLD_EXITED,	"CLD_EXITED"	},
	{ CLD_KILLED,	"CLD_KILLED"	},
	{ CLD_DUMPED,	"CLD_DUMPED"	},
	{ CLD_TRAPPED,	"CLD_TRAPPED"	},
	{ CLD_STOPPED,	"CLD_STOPPED"	},
	{ CLD_CONTINUED,"CLD_CONTINUED"	},
	{ 0,		NULL		},
};

static struct xlat sigpoll_codes[] = {
	{ POLL_IN,	"POLL_IN"	},
	{ POLL_OUT,	"POLL_OUT"	},
	{ POLL_MSG,	"POLL_MSG"	},
	{ POLL_ERR,	"POLL_ERR"	},
	{ POLL_PRI,	"POLL_PRI"	},
	{ POLL_HUP,	"POLL_HUP"	},
	{ 0,		NULL		},
};

static struct xlat sigprof_codes[] = {
#ifdef PROF_SIG
	{ PROF_SIG,	"PROF_SIG"	},
#endif
	{ 0,		NULL		},
};

static struct xlat sigill_codes[] = {
	{ ILL_ILLOPC,	"ILL_ILLOPC"	},
	{ ILL_ILLOPN,	"ILL_ILLOPN"	},
	{ ILL_ILLADR,	"ILL_ILLADR"	},
	{ ILL_ILLTRP,	"ILL_ILLTRP"	},
	{ ILL_PRVOPC,	"ILL_PRVOPC"	},
	{ ILL_PRVREG,	"ILL_PRVREG"	},
	{ ILL_COPROC,	"ILL_COPROC"	},
	{ ILL_BADSTK,	"ILL_BADSTK"	},
	{ 0,		NULL		},
};

static struct xlat sigemt_codes[] = {
#ifdef EMT_TAGOVF
	{ EMT_TAGOVF,	"EMT_TAGOVF"	},
#endif
	{ 0,		NULL		},
};

static struct xlat sigfpe_codes[] = {
	{ FPE_INTDIV,	"FPE_INTDIV"	},
	{ FPE_INTOVF,	"FPE_INTOVF"	},
	{ FPE_FLTDIV,	"FPE_FLTDIV"	},
	{ FPE_FLTOVF,	"FPE_FLTOVF"	},
	{ FPE_FLTUND,	"FPE_FLTUND"	},
	{ FPE_FLTRES,	"FPE_FLTRES"	},
	{ FPE_FLTINV,	"FPE_FLTINV"	},
	{ FPE_FLTSUB,	"FPE_FLTSUB"	},
	{ 0,		NULL		},
};

static struct xlat sigsegv_codes[] = {
	{ SEGV_MAPERR,	"SEGV_MAPERR"	},
	{ SEGV_ACCERR,	"SEGV_ACCERR"	},
	{ 0,		NULL		},
};

static struct xlat sigbus_codes[] = {
	{ BUS_ADRALN,	"BUS_ADRALN"	},
	{ BUS_ADRERR,	"BUS_ADRERR"	},
	{ BUS_OBJERR,	"BUS_OBJERR"	},
	{ 0,		NULL		},
};

void
printsiginfo(sip)
siginfo_t *sip;
{
	char *code;

	tprintf("{si_signo=");
	printsignal(sip->si_signo);
	code = xlookup(siginfo_codes, sip->si_code);
	if (!code) {
		switch (sip->si_signo) {
		case SIGTRAP:
			code = xlookup(sigtrap_codes, sip->si_code);
			break;
		case SIGCHLD:
			code = xlookup(sigcld_codes, sip->si_code);
			break;
		case SIGPOLL:
			code = xlookup(sigpoll_codes, sip->si_code);
			break;
		case SIGPROF:
			code = xlookup(sigprof_codes, sip->si_code);
			break;
		case SIGILL:
			code = xlookup(sigill_codes, sip->si_code);
			break;
		case SIGEMT:
			code = xlookup(sigemt_codes, sip->si_code);
			break;
		case SIGFPE:
			code = xlookup(sigfpe_codes, sip->si_code);
			break;
		case SIGSEGV:
			code = xlookup(sigsegv_codes, sip->si_code);
			break;
		case SIGBUS:
			code = xlookup(sigbus_codes, sip->si_code);
			break;
		}
	}
	if (code)
		tprintf(", si_code=%s", code);
	else
		tprintf(", si_code=%#x", sip->si_code);
#ifdef SI_NOINFO
	if (sip->si_code != SI_NOINFO) {
#endif
		if (sip->si_errno) {
			if (sip->si_errno < 0 || sip->si_errno >= nerrnos)
				tprintf(", si_errno=%d", sip->si_errno);
			else
				tprintf(", si_errno=%s",
					errnoent[sip->si_errno]);
		}
		if (SI_FROMUSER(sip)) {
#ifdef SI_QUEUE
			tprintf(", si_pid=%ld, si_uid=%ld",
				sip->si_pid, sip->si_uid);
			switch (sip->si_code) {
			case SI_QUEUE: case SI_TIMER:
			case SI_ASYNCIO: case SI_MESGQ:
				tprintf(", si_value=%d",
					sip->si_value.sival_int);
				break;
			}
#endif /* SI_QUEUE */
		}
		else {
			switch (sip->si_signo) {
			case SIGCHLD:
				tprintf(", si_pid=%ld, si_status=",
					sip->si_pid);
				if (sip->si_code == CLD_EXITED)
					tprintf("%d", sip->si_status);
				else
					printsignal(sip->si_status);
				break;
			case SIGILL: case SIGFPE:
			case SIGSEGV: case SIGBUS:
				tprintf(", si_addr=%#lx",
					(unsigned long) sip->si_addr);
				break;
			case SIGPOLL:
				switch (sip->si_code) {
				case POLL_IN: case POLL_OUT: case POLL_MSG:
					tprintf(", si_band=%ld", sip->si_band);
					break;
				}
				break;
			}
		}
		tprintf(", ...");
#ifdef SI_NOINFO
	}
#endif
	tprintf("}");
}
				
int
sys_waitid(tcp)
struct tcb *tcp;
{
	siginfo_t si;
	int exited;

	if (entering(tcp)) {
		printxval(waitid_types, tcp->u_arg[0], "P_???");
		tprintf(", %d, ", tcp->u_arg[1]);
		if (tcp->nchildren > 0) {
			/* There are traced children */
			tcp->flags |= TCB_SUSPENDED;
			tcp->waitpid = tcp->u_arg[0];
		}
	}
	else {
		/* siginfo */
		exited = 0;
		if (!tcp->u_arg[2])
			tprintf("NULL");
		else if (syserror(tcp))
			tprintf("%#x", tcp->u_arg[2]);
		else if (umove(tcp, tcp->u_arg[2], &si) < 0)
			tprintf("{???}");
		else
			printsiginfo(&si);
		/* options */
		tprintf(", ");
		if (!printflags(wait4_options, tcp->u_arg[3]))
			tprintf("0");
	}
	return 0;
}

#endif /* SVR4 */

int
sys_alarm(tcp)
struct tcb *tcp;
{
	if (entering(tcp))
		tprintf("%u", tcp->u_arg[0]);
	return 0;
}

int
sys_uname(tcp)
struct tcb *tcp;
{
	struct utsname uname;

	if (exiting(tcp)) {
		if (syserror(tcp) || !verbose(tcp))
			tprintf("%#x", tcp->u_arg[0]);
		else if (umove(tcp, tcp->u_arg[0], &uname) < 0)
			tprintf("{...}");
		else if (!abbrev(tcp)) {

			tprintf("{sysname=\"%s\", nodename=\"%s\", ",
				uname.sysname, uname.nodename);
			tprintf("release=\"%s\", version=\"%s\", ",
				uname.release, uname.version);
			tprintf("machine=\"%s\"", uname.machine);
#ifdef LINUX
			tprintf(", domainname=\"%s\"", uname.domainname);
#endif /* LINUX */
			tprintf("}");
		}
		else
			tprintf("{sys=\"%s\", node=\"%s\", ...}",
				uname.sysname, uname.nodename);
	}
	return 0;
}

#ifndef SVR4

static struct xlat ptrace_cmds[] = {
	{ PTRACE_TRACEME,	"PTRACE_TRACEME"	},
	{ PTRACE_PEEKTEXT,	"PTRACE_PEEKTEXT",	},
	{ PTRACE_PEEKDATA,	"PTRACE_PEEKDATA",	},
	{ PTRACE_PEEKUSER,	"PTRACE_PEEKUSER",	},
	{ PTRACE_POKETEXT,	"PTRACE_POKETEXT",	},
	{ PTRACE_POKEDATA,	"PTRACE_POKEDATA",	},
	{ PTRACE_POKEUSER,	"PTRACE_POKEUSER",	},
	{ PTRACE_CONT,		"PTRACE_CONT"		},
	{ PTRACE_KILL,		"PTRACE_KILL"		},
	{ PTRACE_SINGLESTEP,	"PTRACE_SINGLESTEP"	},
	{ PTRACE_ATTACH,	"PTRACE_ATTACH"		},
	{ PTRACE_DETACH,	"PTRACE_DETACH"		},
#ifdef SUNOS4
	{ PTRACE_GETREGS,	"PTRACE_GETREGS"	},
	{ PTRACE_SETREGS,	"PTRACE_SETREGS"	},
	{ PTRACE_GETFPREGS,	"PTRACE_GETFPREGS",	},
	{ PTRACE_SETFPREGS,	"PTRACE_SETFPREGS",	},
	{ PTRACE_READDATA,	"PTRACE_READDATA"	},
	{ PTRACE_WRITEDATA,	"PTRACE_WRITEDATA"	},
	{ PTRACE_READTEXT,	"PTRACE_READTEXT"	},
	{ PTRACE_WRITETEXT,	"PTRACE_WRITETEXT"	},
	{ PTRACE_GETFPAREGS,	"PTRACE_GETFPAREGS"	},
	{ PTRACE_SETFPAREGS,	"PTRACE_SETFPAREGS"	},
#ifdef SPARC
	{ PTRACE_GETWINDOW,	"PTRACE_GETWINDOW"	},
	{ PTRACE_SETWINDOW,	"PTRACE_SETWINDOW"	},
#else /* !SPARC */
	{ PTRACE_22,		"PTRACE_PTRACE_22"	},
	{ PTRACE_23,		"PTRACE_PTRACE_23"	},
#endif /* !SPARC */
#endif /* SUNOS4 */
	{ PTRACE_SYSCALL,	"PTRACE_SYSCALL"	},
#ifdef SUNOS4
	{ PTRACE_DUMPCORE,	"PTRACE_DUMPCORE"	},
#ifdef I386
	{ PTRACE_SETWRBKPT,	"PTRACE_SETWRBKPT"	},
	{ PTRACE_SETACBKPT,	"PTRACE_SETACBKPT"	},
	{ PTRACE_CLRDR7,	"PTRACE_CLRDR7"		},
#else /* !I386 */
	{ PTRACE_26,		"PTRACE_26"		},
	{ PTRACE_27,		"PTRACE_27"		},
	{ PTRACE_28,		"PTRACE_28"		},
#endif /* !I386 */
	{ PTRACE_GETUCODE,	"PTRACE_GETUCODE"	},
#endif /* SUNOS4 */
	{ 0,			NULL			},
};

#ifndef SUNOS4_KERNEL_ARCH_KLUDGE
static
#endif /* !SUNOS4_KERNEL_ARCH_KLUDGE */
struct xlat struct_user_offsets[] = {
#ifdef LINUX
	{ 4*EBX,		"4*EBX"					},
	{ 4*ECX,		"4*ECX"					},
	{ 4*EDX,		"4*EDX"					},
	{ 4*ESI,		"4*ESI"					},
	{ 4*EDI,		"4*EDI"					},
	{ 4*EBP,		"4*EBP"					},
	{ 4*EAX,		"4*EAX"					},
	{ 4*DS,			"4*DS"					},
	{ 4*ES,			"4*ES"					},
	{ 4*FS,			"4*FS"					},
	{ 4*GS,			"4*GS"					},
	{ 4*ORIG_EAX,		"4*ORIG_EAX"				},
	{ 4*EIP,		"4*EIP"					},
	{ 4*CS,			"4*CS"					},
	{ 4*EFL,		"4*EFL"					},
	{ 4*UESP,		"4*UESP"				},
	{ 4*SS,			"4*SS"					},
	{ uoff(u_fpvalid),	"offsetof(struct user, u_fpvalid)"	},
	{ uoff(i387),		"offsetof(struct user, i387)"		},
	{ uoff(u_tsize),	"offsetof(struct user, u_tsize)"	},
	{ uoff(u_dsize),	"offsetof(struct user, u_dsize)"	},
	{ uoff(u_ssize),	"offsetof(struct user, u_ssize)"	},
	{ uoff(start_code),	"offsetof(struct user, start_code)"	},
	{ uoff(start_stack),	"offsetof(struct user, start_stack)"	},
	{ uoff(signal),		"offsetof(struct user, signal)"		},
	{ uoff(reserved),	"offsetof(struct user, reserved)"	},
	{ uoff(u_ar0),		"offsetof(struct user, u_ar0)"		},
	{ uoff(u_fpstate),	"offsetof(struct user, u_fpstate)"	},
	{ uoff(magic),		"offsetof(struct user, magic)"		},
	{ uoff(u_comm),		"offsetof(struct user, u_comm)"		},
	{ uoff(u_debugreg),	"offsetof(struct user, u_debugreg)"	},
#endif /* LINUX */
#ifdef SUNOS4
	{ uoff(u_pcb),		"offsetof(struct user, u_pcb)"		},
	{ uoff(u_procp),	"offsetof(struct user, u_procp)"	},
	{ uoff(u_ar0),		"offsetof(struct user, u_ar0)"		},
	{ uoff(u_comm[0]),	"offsetof(struct user, u_comm[0])"	},
	{ uoff(u_arg[0]),	"offsetof(struct user, u_arg[0])"	},
	{ uoff(u_ap),		"offsetof(struct user, u_ap)"		},
	{ uoff(u_qsave),	"offsetof(struct user, u_qsave)"	},
	{ uoff(u_rval1),	"offsetof(struct user, u_rval1)"	},
	{ uoff(u_rval2),	"offsetof(struct user, u_rval2)"	},
	{ uoff(u_error),	"offsetof(struct user, u_error)"	},
	{ uoff(u_eosys),	"offsetof(struct user, u_eosys)"	},
	{ uoff(u_ssave),	"offsetof(struct user, u_ssave)"	},
	{ uoff(u_signal[0]),	"offsetof(struct user, u_signal)"	},
	{ uoff(u_sigmask[0]),	"offsetof(struct user, u_sigmask)"	},
	{ uoff(u_sigonstack),	"offsetof(struct user, u_sigonstack)"	},
	{ uoff(u_sigintr),	"offsetof(struct user, u_sigintr)"	},
	{ uoff(u_sigreset),	"offsetof(struct user, u_sigreset)"	},
	{ uoff(u_oldmask),	"offsetof(struct user, u_oldmask)"	},
	{ uoff(u_code),		"offsetof(struct user, u_code)"		},
	{ uoff(u_addr),		"offsetof(struct user, u_addr)"		},
	{ uoff(u_sigstack),	"offsetof(struct user, u_sigstack)"	},
	{ uoff(u_ofile),	"offsetof(struct user, u_ofile)"	},
	{ uoff(u_pofile),	"offsetof(struct user, u_pofile)"	},
	{ uoff(u_ofile_arr[0]),	"offsetof(struct user, u_ofile_arr[0])"	},
	{ uoff(u_pofile_arr[0]),"offsetof(struct user, u_pofile_arr[0])"},
	{ uoff(u_lastfile),	"offsetof(struct user, u_lastfile)"	},
	{ uoff(u_cwd),		"offsetof(struct user, u_cwd)"		},
	{ uoff(u_cdir),		"offsetof(struct user, u_cdir)"		},
	{ uoff(u_rdir),		"offsetof(struct user, u_rdir)"		},
	{ uoff(u_cmask),	"offsetof(struct user, u_cmask)"	},
	{ uoff(u_ru),		"offsetof(struct user, u_ru)"		},
	{ uoff(u_cru),		"offsetof(struct user, u_cru)"		},
	{ uoff(u_timer[0]),	"offsetof(struct user, u_timer[0])"	},
	{ uoff(u_XXX[0]),	"offsetof(struct user, u_XXX[0])"	},
	{ uoff(u_ioch),		"offsetof(struct user, u_ioch)"		},
	{ uoff(u_start),	"offsetof(struct user, u_start)"	},
	{ uoff(u_acflag),	"offsetof(struct user, u_acflag)"	},
	{ uoff(u_prof.pr_base),	"offsetof(struct user, u_prof.pr_base)"	},
	{ uoff(u_prof.pr_size),	"offsetof(struct user, u_prof.pr_size)"	},
	{ uoff(u_prof.pr_off),	"offsetof(struct user, u_prof.pr_off)"	},
	{ uoff(u_prof.pr_scale),"offsetof(struct user, u_prof.pr_scale)"},
	{ uoff(u_rlimit[0]),	"offsetof(struct user, u_rlimit)"	},
	{ uoff(u_exdata.Ux_A),	"offsetof(struct user, u_exdata.Ux_A)"	},
	{ uoff(u_exdata.ux_shell[0]),"offsetof(struct user, u_exdata.ux_shell[0])"},
	{ uoff(u_lofault),	"offsetof(struct user, u_lofault)"	},
#endif /* SUNOS4 */
	{ sizeof(struct user),	"sizeof(struct user)"			},
	{ 0,			NULL					},
};

int
sys_ptrace(tcp)
struct tcb *tcp;
{
	char *cmd;
	struct xlat *x;
	int addr;

	cmd = xlookup(ptrace_cmds, tcp->u_arg[0]);
	if (!cmd)
		cmd = "PTRACE_???";
	if (entering(tcp)) {
		tprintf("%s, %u, ", cmd, tcp->u_arg[1]);
		addr = tcp->u_arg[2];
		if (tcp->u_arg[0] == PTRACE_PEEKUSER
			|| tcp->u_arg[0] == PTRACE_POKEUSER) {
			for (x = struct_user_offsets; x->str; x++) {
				if (x->val >= addr)
					break;
			}
			if (!x->str)
				tprintf("%#x, ", addr);
			else if (x->val > addr && x != struct_user_offsets) {
				x--;
				tprintf("%s + %d, ", x->str, addr - x->val);
			}
			else
				tprintf("%s, ", x->str);
		}
		else
			tprintf("%#x, ", tcp->u_arg[2]);
#ifdef LINUX
		switch (tcp->u_arg[0]) {
		case PTRACE_PEEKDATA:
		case PTRACE_PEEKTEXT:
		case PTRACE_PEEKUSER:
			break;
		case PTRACE_CONT:
		case PTRACE_SINGLESTEP:
		case PTRACE_SYSCALL:
		case PTRACE_DETACH:
			printsignal(tcp->u_arg[3]);
			break;
		default:
			tprintf("%#x", tcp->u_arg[3]);
			break;
		}
	} else {
		switch (tcp->u_arg[0]) {
		case PTRACE_PEEKDATA:
		case PTRACE_PEEKTEXT:
		case PTRACE_PEEKUSER:
			printnum(tcp, tcp->u_arg[3], "%#x");
			break;
		}
	}
#endif /* LINUX */
#ifdef SUNOS4
		if (tcp->u_arg[0] == PTRACE_WRITEDATA ||
			tcp->u_arg[0] == PTRACE_WRITETEXT) {
			tprintf("%u, ", tcp->u_arg[3]);
			printstr(tcp, tcp->u_arg[4], tcp->u_arg[3]);
		} else if (tcp->u_arg[0] != PTRACE_READDATA &&
				tcp->u_arg[0] != PTRACE_READTEXT) {
			tprintf("%#x", tcp->u_arg[3]);
		}
	} else {
		if (tcp->u_arg[0] == PTRACE_READDATA ||
			tcp->u_arg[0] == PTRACE_READTEXT) {
			tprintf("%u, ", tcp->u_arg[3]);
			printstr(tcp, tcp->u_arg[4], tcp->u_arg[3]);
		}
	}
#endif /* SUNOS4 */
	return 0;
}

#endif /* !SVR4 */
