xref: /freebsd/usr.bin/truss/syscalls.c (revision 78007886c995898a9494648343e5236bca1cbba3)
1 /*
2  * Copryight 1997 Sean Eric Fagan
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. All advertising materials mentioning features or use of this software
13  *    must display the following acknowledgement:
14  *	This product includes software developed by Sean Eric Fagan
15  * 4. Neither the name of the author may be used to endorse or promote
16  *    products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifndef lint
33 static const char rcsid[] =
34   "$FreeBSD$";
35 #endif /* not lint */
36 
37 /*
38  * This file has routines used to print out system calls and their
39  * arguments.
40  */
41 
42 #include <sys/mman.h>
43 #include <sys/types.h>
44 #include <sys/ptrace.h>
45 #include <sys/socket.h>
46 #include <sys/time.h>
47 #include <sys/un.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #include <sys/ioccom.h>
51 #include <machine/atomic.h>
52 #include <errno.h>
53 #include <sys/umtx.h>
54 #include <sys/event.h>
55 #include <sys/stat.h>
56 #include <sys/resource.h>
57 
58 #include <ctype.h>
59 #include <err.h>
60 #include <fcntl.h>
61 #include <poll.h>
62 #include <signal.h>
63 #include <stdint.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <time.h>
68 #include <unistd.h>
69 #include <vis.h>
70 
71 #include "truss.h"
72 #include "extern.h"
73 #include "syscall.h"
74 
75 /*
76  * This should probably be in its own file, sorted alphabetically.
77  */
78 
79 struct syscall syscalls[] = {
80 	{ "fcntl", 1, 3,
81 	  { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 }}},
82 	{ "readlink", 1, 3,
83 	  { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 }}},
84 	{ "lseek", 2, 3,
85 #ifdef __LP64__
86 	  { { Int, 0 }, {Quad, 2 }, { Whence, 3 }}},
87 #else
88 	  { { Int, 0 }, {Quad, 2 }, { Whence, 4 }}},
89 #endif
90 	{ "linux_lseek", 2, 3,
91 	  { { Int, 0 }, {Int, 1 }, { Whence, 2 }}},
92 	{ "mmap", 2, 6,
93 #ifdef __LP64__
94 	  { { Ptr, 0 }, {Int, 1}, {Mprot, 2}, {Mmapflags, 3}, {Int, 4}, {Quad, 5}}},
95 #else
96 	  { { Ptr, 0 }, {Int, 1}, {Mprot, 2}, {Mmapflags, 3}, {Int, 4}, {Quad, 6}}},
97 #endif
98 	{ "mprotect", 1, 3,
99 	  { { Ptr, 0 }, {Int, 1}, {Mprot, 2}}},
100 	{ "open", 1, 3,
101 	  { { Name | IN, 0} , { Open, 1}, {Octal, 2}}},
102 	{ "mkdir", 1, 2,
103 	  { { Name, 0} , {Octal, 1}}},
104 	{ "linux_open", 1, 3,
105 	  { { Name, 0 }, { Hex, 1}, { Octal, 2 }}},
106 	{ "close", 1, 1,
107 	  { { Int, 0 } } },
108 	{ "link", 0, 2,
109 	  { { Name, 0 }, { Name, 1 }}},
110 	{ "unlink", 0, 1,
111 	  { { Name, 0 }}},
112 	{ "chdir", 0, 1,
113 	  { { Name, 0 }}},
114 	{ "chroot", 0, 1,
115 	  { { Name, 0 }}},
116 	{ "mknod", 0, 3,
117 	  { { Name, 0 }, { Octal, 1 }, { Int, 3 }}},
118 	{ "chmod", 0, 2,
119 	  { { Name, 0 }, { Octal, 1 }}},
120 	{ "chown", 0, 3,
121 	  { { Name, 0 }, { Int, 1 }, { Int, 2 }}},
122 	{ "mount", 0, 4,
123 	  { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 }}},
124 	{ "umount", 0, 2,
125 	  { { Name, 0 }, { Int, 2 }}},
126 	{ "fstat", 1, 2,
127 	  { { Int, 0}, { Stat | OUT , 1 }}},
128 	{ "stat", 1, 2,
129 	  { { Name | IN, 0 }, { Stat | OUT, 1 }}},
130 	{ "lstat", 1, 2,
131 	  { { Name | IN, 0 }, { Stat | OUT, 1 }}},
132 	{ "linux_newstat", 1, 2,
133 	  { { Name | IN, 0 }, { Ptr | OUT, 1 }}},
134 	{ "linux_newfstat", 1, 2,
135 	  { { Int, 0 }, { Ptr | OUT, 1 }}},
136 	{ "write", 1, 3,
137 	  { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }}},
138 	{ "ioctl", 1, 3,
139 	  { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 }}},
140 	{ "break", 1, 1, { { Hex, 0 }}},
141 	{ "exit", 0, 1, { { Hex, 0 }}},
142 	{ "access", 1, 2, { { Name | IN, 0 }, { Int, 1 }}},
143 	{ "sigaction", 1, 3,
144 	  { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 }}},
145 	{ "accept", 1, 3,
146 	  { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
147 	{ "bind", 1, 3,
148 	  { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
149 	{ "connect", 1, 3,
150 	  { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
151 	{ "getpeername", 1, 3,
152 	  { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
153 	{ "getsockname", 1, 3,
154 	  { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
155 	{ "recvfrom", 1, 6,
156 	  { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
157 	{ "sendto", 1, 6,
158 	  { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
159 	{ "execve", 1, 3,
160 	  { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
161 	{ "linux_execve", 1, 3,
162 	  { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
163 	{ "kldload", 0, 1, { { Name | IN, 0 }}},
164 	{ "kldunload", 0, 1, { { Int, 0 }}},
165 	{ "kldfind", 0, 1, { { Name | IN, 0 }}},
166 	{ "kldnext", 0, 1, { { Int, 0 }}},
167 	{ "kldstat", 0, 2, { { Int, 0 }, { Ptr, 1 }}},
168 	{ "kldfirstmod", 0, 1, { { Int, 0 }}},
169 	{ "nanosleep", 0, 1, { { Timespec, 0 }}},
170 	{ "select", 1, 5, { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 }}},
171 	{ "poll", 1, 3, { { Pollfd, 0 }, { Int, 1 }, { Int, 2 }}},
172 	{ "gettimeofday", 1, 2, { { Timeval | OUT, 0 }, { Ptr, 1 }}},
173 	{ "clock_gettime", 1, 2, { { Int, 0 }, { Timespec | OUT, 1 }}},
174 	{ "getitimer", 1, 2, { { Int, 0 }, { Itimerval | OUT, 2 }}},
175 	{ "setitimer", 1, 3, { { Int, 0 }, { Itimerval, 1} , { Itimerval | OUT, 2 }}},
176 	{ "kse_release", 0, 1, { { Timespec, 0 }}},
177 	{ "kevent", 0, 6, { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 }}},
178 	{ "_umtx_lock", 0, 1, { { Umtx, 0 }}},
179 	{ "_umtx_unlock", 0, 1, { { Umtx, 0 }}},
180 	{ "sigprocmask", 0, 3, { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 }}},
181 	{ "unmount", 1, 2, { { Name, 0 }, { Int, 1 }}},
182 	{ "socket", 1, 3, { { Sockdomain, 0}, { Socktype, 1}, {Int, 2 }}},
183 	{ "getrusage", 1, 2, { { Int, 0 }, { Rusage | OUT, 1 }}},
184 	{ "__getcwd", 1, 2, { { Name | OUT, 0}, { Int, 1 }}},
185 	{ "shutdown", 1, 2, { { Int, 0}, { Shutdown, 1}}},
186 	{ "getrlimit", 1, 2, { { Resource, 0}, {Rlimit | OUT, 1}}},
187 	{ "setrlimit", 1, 2, { { Resource, 0}, {Rlimit | IN, 1}}},
188 	{ "utimes", 1, 2,
189 		{ { Name | IN, 0 }, { Timeval2 | IN, 1 }}},
190 	{ "lutimes", 1, 2,
191 		{ { Name | IN, 0 }, { Timeval2 | IN, 1 }}},
192 	{ "futimes", 1, 2,
193 		{ { Int, 0 }, { Timeval | IN, 1 }}},
194 	{ "chflags", 1, 2,
195 		{ { Name | IN, 0 }, { Hex, 1 }}},
196 	{ "lchflags", 1, 2,
197 		{ { Name | IN, 0 }, { Hex, 1 }}},
198 	{ "pathconf", 1, 2,
199 		{ { Name | IN, 0 }, { Pathconf, 1 }}},
200 	{ "truncate", 1, 3,
201 		{ { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 }}},
202 	{ "ftruncate", 1, 3,
203 		{ { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 }}},
204 	{ "kill", 1, 2,
205 		{ { Int | IN, 0 }, { Signal | IN, 1}}},
206 	{ "munmap", 1, 2,
207 		{ { Ptr, 0 }, { Int, 1 }}},
208 	{ "read", 1, 3,
209 	  { { Int, 0}, { BinString | OUT, 1}, { Int, 2}}},
210 	{ "rename", 1, 2,
211 	  { { Name , 0} , { Name, 1}}},
212 	{ "symlink", 1, 2,
213 	  { { Name , 0} , { Name, 1}}},
214 	{ 0, 0, 0, { { 0, 0 }}},
215 };
216 
217 /* Xlat idea taken from strace */
218 struct xlat {
219 	int val;
220 	const char *str;
221 };
222 
223 #define X(a) { a, #a },
224 #define XEND { 0, NULL }
225 
226 static struct xlat kevent_filters[] = {
227 	X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE)
228 	X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER)
229 	X(EVFILT_NETDEV) X(EVFILT_FS) X(EVFILT_READ) XEND
230 };
231 
232 static struct xlat kevent_flags[] = {
233 	X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT)
234 	X(EV_CLEAR) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND
235 };
236 
237 struct xlat poll_flags[] = {
238 	X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR)
239 	X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND)
240 	X(POLLWRBAND) X(POLLINIGNEOF) XEND
241 };
242 
243 static struct xlat mmap_flags[] = {
244 	X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME)
245 	X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100)
246 	X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON)
247 	X(MAP_NOCORE) XEND
248 };
249 
250 static struct xlat mprot_flags[] = {
251 	X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND
252 };
253 
254 static struct xlat whence_arg[] = {
255 	X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) XEND
256 };
257 
258 static struct xlat sigaction_flags[] = {
259 	X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP)
260 	X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND
261 };
262 
263 static struct xlat fcntl_arg[] = {
264 	X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL)
265 	X(F_GETOWN) X(F_SETOWN) X(F_GETLK) X(F_SETLK) X(F_SETLKW) XEND
266 };
267 
268 static struct xlat fcntlfd_arg[] = {
269 	X(FD_CLOEXEC) XEND
270 };
271 
272 static struct xlat fcntlfl_arg[] = {
273 	X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW)
274 	X(O_DIRECT) XEND
275 };
276 
277 static struct xlat sockdomain_arg[] = {
278 	X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK)
279 	X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI)
280 	X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet)
281 	X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE)
282 	X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX)
283 	X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6)
284 	X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER)
285 	X(PF_ARP) X(PF_BLUETOOTH) XEND
286 };
287 
288 static struct xlat socktype_arg[] = {
289 	X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM)
290 	X(SOCK_SEQPACKET) XEND
291 };
292 
293 static struct xlat open_flags[] = {
294 	X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK)
295 	X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC)
296 	X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY)
297 	X(O_DIRECT) XEND
298 };
299 
300 static struct xlat shutdown_arg[] = {
301 	X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND
302 };
303 
304 static struct xlat resource_arg[] = {
305 	X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK)
306 	X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC)
307 	X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) XEND
308 };
309 
310 static struct xlat pathconf_arg[] = {
311 	X(_PC_LINK_MAX)  X(_PC_MAX_CANON)  X(_PC_MAX_INPUT)
312 	X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF)
313 	X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE)
314 	X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO)
315 	X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS)
316 	X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE)
317 	X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN)
318 	X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX)
319 	X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT)
320 	XEND
321 };
322 
323 #undef X
324 #undef XEND
325 
326 /* Searches an xlat array for a value, and returns it if found.  Otherwise
327    return a string representation. */
328 static const char
329 *lookup(struct xlat *xlat, int val, int base)
330 {
331 	static char tmp[16];
332 	for (; xlat->str != NULL; xlat++)
333 		if (xlat->val == val)
334 			return xlat->str;
335 	switch (base) {
336 		case 8:
337 			sprintf(tmp, "0%o", val);
338 			break;
339 		case 16:
340 			sprintf(tmp, "0x%x", val);
341 			break;
342 		case 10:
343 			sprintf(tmp, "%u", val);
344 			break;
345 		default:
346 			errx(1,"Unknown lookup base");
347 			break;
348 	}
349 	return tmp;
350 }
351 
352 static const char *
353 xlookup(struct xlat *xlat, int val)
354 {
355 	return lookup(xlat, val, 16);
356 }
357 
358 /* Searches an xlat array containing bitfield values.  Remaining bits
359    set after removing the known ones are printed at the end:
360    IN|0x400 */
361 static char
362 *xlookup_bits(struct xlat *xlat, int val)
363 {
364 	static char str[512];
365 	int len = 0;
366 	int rem = val;
367 
368 	for (; xlat->str != NULL; xlat++)
369 	{
370 		if ((xlat->val & rem) == xlat->val)
371 		{
372 			/* don't print the "all-bits-zero" string unless all
373 			   bits are really zero */
374 			if (xlat->val == 0 && val != 0)
375 				continue;
376 			len += sprintf(str + len, "%s|", xlat->str);
377 			rem &= ~(xlat->val);
378 		}
379 	}
380 	/* if we have leftover bits or didn't match anything */
381 	if (rem || len == 0)
382 		len += sprintf(str + len, "0x%x", rem);
383 	if (len && str[len - 1] == '|')
384 		len--;
385 	str[len] = 0;
386 	return str;
387 }
388 
389 /*
390  * If/when the list gets big, it might be desirable to do it
391  * as a hash table or binary search.
392  */
393 
394 struct syscall *
395 get_syscall(const char *name) {
396 	struct syscall *sc = syscalls;
397 
398 	if (name == NULL)
399 		return (NULL);
400 	while (sc->name) {
401 		if (!strcmp(name, sc->name))
402 			return sc;
403 		sc++;
404 	}
405 	return NULL;
406 }
407 
408 /*
409  * get_struct
410  *
411  * Copy a fixed amount of bytes from the process.
412  */
413 
414 static int
415 get_struct(int pid, void *offset, void *buf, int len) {
416 	struct ptrace_io_desc iorequest;
417 
418 	iorequest.piod_op = PIOD_READ_D;
419 	iorequest.piod_offs = offset;
420 	iorequest.piod_addr = buf;
421 	iorequest.piod_len = len;
422 	if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0)
423 		return -1;
424 	return 0;
425 }
426 
427 #define MAXSIZE 4096
428 #define BLOCKSIZE 1024
429 /*
430  * get_string
431  * Copy a string from the process.  Note that it is
432  * expected to be a C string, but if max is set, it will
433  * only get that much.
434  */
435 
436 static char *
437 get_string(pid_t pid, void *offset, int max) {
438 	char *buf;
439 	struct ptrace_io_desc iorequest;
440 	int totalsize, size;
441 	int diff = 0;
442 	int i;
443 
444 	totalsize = size = max ? (max + 1) : BLOCKSIZE;
445 	buf = malloc(totalsize);
446 	if (buf == NULL)
447 		return NULL;
448 	for(;;) {
449 		diff = totalsize - size;
450 		iorequest.piod_op = PIOD_READ_D;
451 		iorequest.piod_offs = (char *)offset + diff;
452 		iorequest.piod_addr = buf + diff;
453 		iorequest.piod_len = size;
454 		if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) {
455 			free(buf);
456 			return NULL;
457 		}
458 		for (i = 0 ; i < size; i++) {
459 			if (buf[diff + i] == '\0')
460 				return (buf);
461 		}
462 		if (totalsize < MAXSIZE - BLOCKSIZE && max == 0) {
463 			totalsize += BLOCKSIZE;
464 			buf = realloc(buf, totalsize);
465 			size = BLOCKSIZE;
466 		}
467 		else {
468 			buf[totalsize] = '\0';
469 			return buf;
470 		}
471 	}
472 }
473 
474 
475 /*
476  * print_arg
477  * Converts a syscall argument into a string.  Said string is
478  * allocated via malloc(), so needs to be free()'d.  The file
479  * descriptor is for the process' memory (via /proc), and is used
480  * to get any data (where the argument is a pointer).  sc is
481  * a pointer to the syscall description (see above); args is
482  * an array of all of the system call arguments.
483  */
484 
485 char *
486 print_arg(struct syscall_args *sc, unsigned long *args, long retval, struct trussinfo *trussinfo) {
487   char *tmp = NULL;
488   int pid = trussinfo->pid;
489   switch (sc->type & ARG_MASK) {
490   case Hex:
491     asprintf(&tmp, "0x%lx", args[sc->offset]);
492     break;
493   case Octal:
494     asprintf(&tmp, "0%lo", args[sc->offset]);
495     break;
496   case Int:
497     asprintf(&tmp, "%ld", args[sc->offset]);
498     break;
499   case Name:
500     {
501       /* NULL-terminated string. */
502       char *tmp2;
503       tmp2 = get_string(pid, (void*)args[sc->offset], 0);
504       asprintf(&tmp, "\"%s\"", tmp2);
505       free(tmp2);
506     }
507   break;
508   case BinString:
509     {
510       /* Binary block of data that might have printable characters.
511          XXX If type|OUT, assume that the length is the syscall's
512          return value.  Otherwise, assume that the length of the block
513          is in the next syscall argument. */
514       int max_string = trussinfo->strsize;
515       char tmp2[max_string+1], *tmp3;
516       int len;
517       int truncated = 0;
518 
519       if (sc->type & OUT)
520         len = retval;
521       else
522         len = args[sc->offset + 1];
523 
524       /* Don't print more than max_string characters, to avoid word
525          wrap.  If we have to truncate put some ... after the string.
526          */
527       if (len > max_string) {
528         len = max_string;
529         truncated = 1;
530       }
531       if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len) != -1) {
532         tmp3 = malloc(len * 4 + 1);
533         while (len) {
534           if (strvisx(tmp3, tmp2, len, VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string)
535             break;
536           len--;
537           truncated = 1;
538         };
539         asprintf(&tmp, "\"%s\"%s", tmp3, truncated?"...":"");
540         free(tmp3);
541       } else
542       	asprintf(&tmp, "0x%lx", args[sc->offset]);
543     }
544   break;
545   case StringArray:
546     {
547       int num, size, i;
548       char *tmp2;
549       char *string;
550       char *strarray[100];	/* XXX This is ugly. */
551 
552       if (get_struct(pid, (void *)args[sc->offset], (void *)&strarray,
553                      sizeof(strarray)) == -1) {
554 	err(1, "get_struct %p", (void *)args[sc->offset]);
555       }
556       num = 0;
557       size = 0;
558 
559       /* Find out how large of a buffer we'll need. */
560       while (strarray[num] != NULL) {
561 	string = get_string(pid, (void*)strarray[num], 0);
562         size += strlen(string);
563 	free(string);
564 	num++;
565       }
566       size += 4 + (num * 4);
567       tmp = (char *)malloc(size);
568       tmp2 = tmp;
569 
570       tmp2 += sprintf(tmp2, " [");
571       for (i = 0; i < num; i++) {
572 	string = get_string(pid, (void*)strarray[i], 0);
573         tmp2 += sprintf(tmp2, " \"%s\"%c", string, (i+1 == num) ? ' ' : ',');
574 	free(string);
575       }
576       tmp2 += sprintf(tmp2, "]");
577     }
578   break;
579 #ifdef __LP64__
580   case Quad:
581     asprintf(&tmp, "0x%lx", args[sc->offset]);
582     break;
583 #else
584   case Quad:
585     {
586       unsigned long long ll;
587       ll = *(unsigned long long *)(args + sc->offset);
588       asprintf(&tmp, "0x%llx", ll);
589       break;
590     }
591 #endif
592   case Ptr:
593     asprintf(&tmp, "0x%lx", args[sc->offset]);
594     break;
595   case Readlinkres:
596     {
597       char *tmp2;
598       if (retval == -1) {
599 	tmp = strdup("");
600 	break;
601       }
602       tmp2 = get_string(pid, (void*)args[sc->offset], retval);
603       asprintf(&tmp, "\"%s\"", tmp2);
604       free(tmp2);
605     }
606   break;
607   case Ioctl:
608     {
609       const char *temp = ioctlname(args[sc->offset]);
610       if (temp)
611         tmp = strdup(temp);
612       else
613       {
614         unsigned long arg = args[sc->offset];
615         asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu}", arg,
616           arg&IOC_OUT?"R":"", arg&IOC_IN?"W":"",
617           IOCGROUP(arg), isprint(IOCGROUP(arg))?(char)IOCGROUP(arg):'?',
618           arg & 0xFF, IOCPARM_LEN(arg));
619       }
620     }
621     break;
622   case Umtx:
623     {
624       struct umtx umtx;
625       if (get_struct(pid, (void *)args[sc->offset], &umtx, sizeof(umtx)) != -1)
626 	asprintf(&tmp, "{0x%lx}", (long)umtx.u_owner);
627       else
628 	asprintf(&tmp, "0x%lx", args[sc->offset]);
629     }
630     break;
631   case Timespec:
632     {
633       struct timespec ts;
634       if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts)) != -1)
635 	asprintf(&tmp, "{%ld.%09ld}", (long)ts.tv_sec, ts.tv_nsec);
636       else
637 	asprintf(&tmp, "0x%lx", args[sc->offset]);
638     }
639     break;
640   case Timeval:
641     {
642       struct timeval tv;
643       if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1)
644 	asprintf(&tmp, "{%ld.%06ld}", (long)tv.tv_sec, tv.tv_usec);
645       else
646 	asprintf(&tmp, "0x%lx", args[sc->offset]);
647     }
648     break;
649   case Timeval2:
650     {
651       struct timeval tv[2];
652       if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1)
653 	asprintf(&tmp, "{%ld.%06ld, %ld.%06ld}",
654 	  (long)tv[0].tv_sec, tv[0].tv_usec,
655 	  (long)tv[1].tv_sec, tv[1].tv_usec);
656       else
657 	asprintf(&tmp, "0x%lx", args[sc->offset]);
658     }
659     break;
660   case Itimerval:
661     {
662       struct itimerval itv;
663       if (get_struct(pid, (void *)args[sc->offset], &itv, sizeof(itv)) != -1)
664 	asprintf(&tmp, "{%ld.%06ld, %ld.%06ld}",
665 	    (long)itv.it_interval.tv_sec,
666 	    itv.it_interval.tv_usec,
667 	    (long)itv.it_value.tv_sec,
668 	    itv.it_value.tv_usec);
669       else
670 	asprintf(&tmp, "0x%lx", args[sc->offset]);
671     }
672     break;
673   case Pollfd:
674     {
675       /*
676        * XXX: A Pollfd argument expects the /next/ syscall argument to be
677        * the number of fds in the array. This matches the poll syscall.
678        */
679       struct pollfd *pfd;
680       int numfds = args[sc->offset+1];
681       int bytes = sizeof(struct pollfd) * numfds;
682       int i, tmpsize, u, used;
683       const int per_fd = 100;
684 
685       if ((pfd = malloc(bytes)) == NULL)
686 	err(1, "Cannot malloc %d bytes for pollfd array", bytes);
687       if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) != -1) {
688 
689 	used = 0;
690 	tmpsize = 1 + per_fd * numfds + 2;
691 	if ((tmp = malloc(tmpsize)) == NULL)
692 	  err(1, "Cannot alloc %d bytes for poll output", tmpsize);
693 
694 	tmp[used++] = '{';
695 	for (i = 0; i < numfds; i++) {
696 
697 	  u = snprintf(tmp + used, per_fd,
698 	    "%s%d/%s",
699 	    i > 0 ? " " : "",
700 	    pfd[i].fd,
701 	    xlookup_bits(poll_flags, pfd[i].events) );
702 	  if (u > 0)
703 	    used += u < per_fd ? u : per_fd;
704 	}
705 	tmp[used++] = '}';
706 	tmp[used++] = '\0';
707       } else
708 	asprintf(&tmp, "0x%lx", args[sc->offset]);
709       free(pfd);
710     }
711     break;
712   case Fd_set:
713     {
714       /*
715        * XXX: A Fd_set argument expects the /first/ syscall argument to be
716        * the number of fds in the array.  This matches the select syscall.
717        */
718       fd_set *fds;
719       int numfds = args[0];
720       int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS;
721       int i, tmpsize, u, used;
722       const int per_fd = 20;
723 
724       if ((fds = malloc(bytes)) == NULL)
725 	err(1, "Cannot malloc %d bytes for fd_set array", bytes);
726       if (get_struct(pid, (void *)args[sc->offset], fds, bytes) != -1) {
727 	used = 0;
728 	tmpsize = 1 + numfds * per_fd + 2;
729 	if ((tmp = malloc(tmpsize)) == NULL)
730 	  err(1, "Cannot alloc %d bytes for fd_set output", tmpsize);
731 
732 	tmp[used++] = '{';
733 	for (i = 0; i < numfds; i++) {
734 	  if (FD_ISSET(i, fds)) {
735 	    u = snprintf(tmp + used, per_fd, "%d ", i);
736 	    if (u > 0)
737 	      used += u < per_fd ? u : per_fd;
738 	  }
739 	}
740 	if (tmp[used-1] == ' ')
741 		used--;
742 	tmp[used++] = '}';
743 	tmp[used++] = '\0';
744       } else
745 	asprintf(&tmp, "0x%lx", args[sc->offset]);
746       free(fds);
747     }
748     break;
749   case Signal:
750     {
751       long sig;
752 
753       sig = args[sc->offset];
754       tmp = strsig(sig);
755       if (tmp == NULL)
756         asprintf(&tmp, "%ld", sig);
757     }
758     break;
759   case Sigset:
760     {
761       long sig;
762       sigset_t ss;
763       int i, used;
764 
765       sig = args[sc->offset];
766       if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
767           sizeof(ss)) == -1)
768       {
769 	asprintf(&tmp, "0x%lx", args[sc->offset]);
770 	break;
771       }
772       tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */
773       used = 0;
774       for (i = 1; i < sys_nsig; i++)
775       {
776       	if (sigismember(&ss, i))
777       	{
778       	  used += sprintf(tmp + used, "%s|", strsig(i));
779       	}
780       }
781       if(used)
782 	      tmp[used-1] = 0;
783 	  else
784 	      strcpy(tmp, "0x0");
785     }
786     break;
787   case Sigprocmask:
788     {
789 	switch (args[sc->offset]) {
790 #define S(a)	case a: tmp = strdup(#a); break;
791 	S(SIG_BLOCK);
792 	S(SIG_UNBLOCK);
793 	S(SIG_SETMASK);
794 #undef S
795 	}
796 	if (tmp == NULL)
797     		asprintf(&tmp, "0x%lx", args[sc->offset]);
798     }
799     break;
800 
801   case Fcntlflag:
802     {
803       /* XXX output depends on the value of the previous argument */
804       switch (args[sc->offset-1]) {
805         case F_SETFD:
806           tmp = strdup(xlookup_bits(fcntlfd_arg, args[sc->offset]));
807           break;
808         case F_SETFL:
809           tmp = strdup(xlookup_bits(fcntlfl_arg, args[sc->offset]));
810           break;
811         case F_GETFD:
812         case F_GETFL:
813         case F_GETOWN:
814           tmp = strdup("");
815           break;
816         default:
817           asprintf(&tmp, "0x%lx", args[sc->offset]);
818           break;
819       }
820     }
821     break;
822   case Open:
823     tmp = strdup(xlookup_bits(open_flags, args[sc->offset]));
824     break;
825   case Fcntl:
826     tmp = strdup(xlookup(fcntl_arg, args[sc->offset]));
827     break;
828   case Mprot:
829     tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset]));
830     break;
831   case Mmapflags:
832     tmp = strdup(xlookup_bits(mmap_flags, args[sc->offset]));
833     break;
834   case Whence:
835     tmp = strdup(xlookup(whence_arg, args[sc->offset]));
836     break;
837   case Sockdomain:
838     tmp = strdup(xlookup(sockdomain_arg, args[sc->offset]));
839     break;
840   case Socktype:
841     tmp = strdup(xlookup(socktype_arg, args[sc->offset]));
842     break;
843   case Shutdown:
844     tmp = strdup(xlookup(shutdown_arg, args[sc->offset]));
845     break;
846   case Resource:
847     tmp = strdup(xlookup(resource_arg, args[sc->offset]));
848     break;
849   case Pathconf:
850     tmp = strdup(xlookup(pathconf_arg, args[sc->offset]));
851     break;
852   case Sockaddr:
853     {
854       struct sockaddr_storage ss;
855       char addr[64];
856       struct sockaddr_in *lsin;
857       struct sockaddr_in6 *lsin6;
858       struct sockaddr_un *sun;
859       struct sockaddr *sa;
860       char *p;
861       u_char *q;
862       int i;
863 
864       if (args[sc->offset] == 0) {
865 	      asprintf(&tmp, "NULL");
866 	      break;
867       }
868 
869       /* yuck: get ss_len */
870       if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
871 	sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1)
872 	err(1, "get_struct %p", (void *)args[sc->offset]);
873       /*
874        * If ss_len is 0, then try to guess from the sockaddr type.
875        * AF_UNIX may be initialized incorrectly, so always frob
876        * it by using the "right" size.
877        */
878       if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) {
879 	      switch (ss.ss_family) {
880 	      case AF_INET:
881 		      ss.ss_len = sizeof(*lsin);
882 		      break;
883 	      case AF_UNIX:
884 		      ss.ss_len = sizeof(*sun);
885 		      break;
886 	      default:
887 		      /* hurrrr */
888 		      break;
889 	      }
890       }
891       if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, ss.ss_len)
892 	  == -1) {
893 	  err(2, "get_struct %p", (void *)args[sc->offset]);
894       }
895 
896       switch (ss.ss_family) {
897       case AF_INET:
898 	lsin = (struct sockaddr_in *)&ss;
899 	inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr);
900 	asprintf(&tmp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port));
901 	break;
902       case AF_INET6:
903 	lsin6 = (struct sockaddr_in6 *)&ss;
904 	inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, sizeof addr);
905 	asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port));
906 	break;
907       case AF_UNIX:
908         sun = (struct sockaddr_un *)&ss;
909         asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path);
910 	break;
911       default:
912 	sa = (struct sockaddr *)&ss;
913         asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data = {%n%*s } }",
914 	  (int)sa->sa_len, (int)sa->sa_family, &i,
915 	  6 * (int)(sa->sa_len - ((char *)&sa->sa_data - (char *)sa)), "");
916 	if (tmp != NULL) {
917 	  p = tmp + i;
918           for (q = (u_char *)&sa->sa_data; q < (u_char *)sa + sa->sa_len; q++)
919             p += sprintf(p, " %#02x,", *q);
920 	}
921       }
922     }
923     break;
924   case Sigaction:
925     {
926       struct sigaction sa;
927       char *hand;
928       const char *h;
929 
930       if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa)) != -1) {
931 
932 	asprintf(&hand, "%p", sa.sa_handler);
933 	if (sa.sa_handler == SIG_DFL)
934 	  h = "SIG_DFL";
935 	else if (sa.sa_handler == SIG_IGN)
936 	  h = "SIG_IGN";
937 	else
938 	  h = hand;
939 
940 	asprintf(&tmp, "{ %s %s ss_t }",
941 	    h,
942 	    xlookup_bits(sigaction_flags, sa.sa_flags));
943 	free(hand);
944       } else
945 	asprintf(&tmp, "0x%lx", args[sc->offset]);
946 
947     }
948     break;
949   case Kevent:
950     {
951       /*
952        * XXX XXX: the size of the array is determined by either the
953        * next syscall argument, or by the syscall returnvalue,
954        * depending on which argument number we are.  This matches the
955        * kevent syscall, but luckily that's the only syscall that uses
956        * them.
957        */
958       struct kevent *ke;
959       int numevents = -1;
960       int bytes = 0;
961       int i, tmpsize, u, used;
962       const int per_ke = 100;
963 
964       if (sc->offset == 1)
965       	numevents = args[sc->offset+1];
966       else if (sc->offset == 3 && retval != -1)
967         numevents = retval;
968 
969       if (numevents >= 0)
970       	bytes = sizeof(struct kevent) * numevents;
971       if ((ke = malloc(bytes)) == NULL)
972         err(1, "Cannot malloc %d bytes for kevent array", bytes);
973       if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset], ke, bytes) != -1) {
974 	used = 0;
975 	tmpsize = 1 + per_ke * numevents + 2;
976 	if ((tmp = malloc(tmpsize)) == NULL)
977 	  err(1, "Cannot alloc %d bytes for kevent output", tmpsize);
978 
979 	tmp[used++] = '{';
980 	for (i = 0; i < numevents; i++) {
981 	  u = snprintf(tmp + used, per_ke,
982 	    "%s%p,%s,%s,%d,%p,%p",
983 	    i > 0 ? " " : "",
984 	    (void *)ke[i].ident,
985 	    xlookup(kevent_filters, ke[i].filter),
986 	    xlookup_bits(kevent_flags, ke[i].flags),
987 	    ke[i].fflags,
988 	    (void *)ke[i].data,
989 	    (void *)ke[i].udata);
990 	  if (u > 0)
991 	    used += u < per_ke ? u : per_ke;
992 	}
993 	tmp[used++] = '}';
994 	tmp[used++] = '\0';
995       } else
996 	asprintf(&tmp, "0x%lx", args[sc->offset]);
997       free(ke);
998     }
999     break;
1000   case Stat:
1001     {
1002       struct stat st;
1003       if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st)) != -1) {
1004 	char mode[12];
1005 	strmode(st.st_mode, mode);
1006 	asprintf(&tmp, "{mode=%s,inode=%jd,size=%jd,blksize=%ld}",
1007 	  mode,
1008 	  (intmax_t)st.st_ino,(intmax_t)st.st_size,(long)st.st_blksize);
1009       } else
1010 	asprintf(&tmp, "0x%lx", args[sc->offset]);
1011     }
1012     break;
1013   case Rusage:
1014     {
1015       struct rusage ru;
1016       if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) != -1)
1017 	asprintf(&tmp, "{u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld}",
1018 	  (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
1019 	  (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
1020 	  ru.ru_inblock, ru.ru_oublock);
1021       else
1022 	asprintf(&tmp, "0x%lx", args[sc->offset]);
1023     }
1024     break;
1025   case Rlimit:
1026     {
1027       struct rlimit rl;
1028       if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl)) != -1)
1029 	asprintf(&tmp, "{cur=%ju,max=%ju}",
1030 	  rl.rlim_cur, rl.rlim_max);
1031       else
1032 	asprintf(&tmp, "0x%lx", args[sc->offset]);
1033     }
1034     break;
1035     default:
1036      errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
1037   }
1038   return tmp;
1039 }
1040 
1041 
1042 /*
1043  * print_syscall
1044  * Print (to outfile) the system call and its arguments.  Note that
1045  * nargs is the number of arguments (not the number of words; this is
1046  * potentially confusing, I know).
1047  */
1048 
1049 void
1050 print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args) {
1051   int i;
1052   int len = 0;
1053   struct timespec timediff;
1054 
1055   if (trussinfo->flags & FOLLOWFORKS)
1056     len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid);
1057 
1058   if (name != NULL && (!strcmp(name, "execve") || !strcmp(name, "exit"))) {
1059     clock_gettime(CLOCK_REALTIME, &trussinfo->after);
1060   }
1061 
1062   if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
1063     timespecsubt(&trussinfo->after, &trussinfo->start_time, &timediff);
1064     len += fprintf(trussinfo->outfile, "%ld.%09ld ",
1065 		   (long)timediff.tv_sec, timediff.tv_nsec);
1066   }
1067 
1068   if (trussinfo->flags & RELATIVETIMESTAMPS) {
1069     timespecsubt(&trussinfo->after, &trussinfo->before, &timediff);
1070     len += fprintf(trussinfo->outfile, "%ld.%09ld ",
1071 		   (long)timediff.tv_sec, timediff.tv_nsec);
1072   }
1073 
1074   len += fprintf(trussinfo->outfile, "%s(", name);
1075 
1076   for (i = 0; i < nargs; i++) {
1077     if (s_args[i])
1078       len += fprintf(trussinfo->outfile, "%s", s_args[i]);
1079     else
1080       len += fprintf(trussinfo->outfile, "<missing argument>");
1081     len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : "");
1082   }
1083   len += fprintf(trussinfo->outfile, ")");
1084   for (i = 0; i < 6 - (len / 8); i++)
1085 	fprintf(trussinfo->outfile, "\t");
1086 }
1087 
1088 void
1089 print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs,
1090     char **s_args, int errorp, long retval)
1091 {
1092   print_syscall(trussinfo, name, nargs, s_args);
1093   fflush(trussinfo->outfile);
1094   if (errorp) {
1095     fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval, strerror(retval));
1096   } else {
1097     fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval);
1098   }
1099 }
1100