1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright 1997 Sean Eric Fagan
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by Sean Eric Fagan
17 * 4. Neither the name of the author may be used to endorse or promote
18 *    products derived from this software without specific prior written
19 *    permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35/*
36 * This file has routines used to print out system calls and their
37 * arguments.
38 */
39
40#include <sys/aio.h>
41#include <sys/capsicum.h>
42#include <sys/types.h>
43#define	_WANT_FREEBSD11_KEVENT
44#include <sys/event.h>
45#include <sys/ioccom.h>
46#include <sys/mman.h>
47#include <sys/mount.h>
48#include <sys/poll.h>
49#include <sys/procfs.h>
50#include <sys/ptrace.h>
51#include <sys/resource.h>
52#include <sys/sched.h>
53#include <sys/socket.h>
54#define _WANT_FREEBSD11_STAT
55#include <sys/stat.h>
56#include <sys/sysctl.h>
57#include <sys/time.h>
58#include <sys/un.h>
59#include <sys/wait.h>
60#include <netinet/in.h>
61#include <netinet/sctp.h>
62#include <arpa/inet.h>
63
64#include <assert.h>
65#include <ctype.h>
66#include <err.h>
67#define _WANT_KERNEL_ERRNO
68#include <errno.h>
69#include <fcntl.h>
70#include <signal.h>
71#include <stdbool.h>
72#include <stddef.h>
73#include <stdio.h>
74#include <stdlib.h>
75#include <string.h>
76#include <sysdecode.h>
77#include <unistd.h>
78#include <vis.h>
79
80#include "truss.h"
81#include "extern.h"
82#include "syscall.h"
83
84/*
85 * This should probably be in its own file, sorted alphabetically.
86 *
87 * Note: We only scan this table on the initial syscall number to calling
88 * convention lookup, i.e. once each time a new syscall is encountered. This
89 * is unlikely to be a performance issue, but if it is we could sort this array
90 * and use a binary search instead.
91 */
92static const struct syscall_decode decoded_syscalls[] = {
93	/* Native ABI */
94	{ .name = "__acl_aclcheck_fd", .ret_type = 1, .nargs = 3,
95	  .args = { { Int, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
96	{ .name = "__acl_aclcheck_file", .ret_type = 1, .nargs = 3,
97	  .args = { { Name, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
98	{ .name = "__acl_aclcheck_link", .ret_type = 1, .nargs = 3,
99	  .args = { { Name, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
100	{ .name = "__acl_delete_fd", .ret_type = 1, .nargs = 2,
101	  .args = { { Int, 0 }, { Acltype, 1 } } },
102	{ .name = "__acl_delete_file", .ret_type = 1, .nargs = 2,
103	  .args = { { Name, 0 }, { Acltype, 1 } } },
104	{ .name = "__acl_delete_link", .ret_type = 1, .nargs = 2,
105	  .args = { { Name, 0 }, { Acltype, 1 } } },
106	{ .name = "__acl_get_fd", .ret_type = 1, .nargs = 3,
107	  .args = { { Int, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
108	{ .name = "__acl_get_file", .ret_type = 1, .nargs = 3,
109	  .args = { { Name, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
110	{ .name = "__acl_get_link", .ret_type = 1, .nargs = 3,
111	  .args = { { Name, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
112	{ .name = "__acl_set_fd", .ret_type = 1, .nargs = 3,
113	  .args = { { Int, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
114	{ .name = "__acl_set_file", .ret_type = 1, .nargs = 3,
115	  .args = { { Name, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
116	{ .name = "__acl_set_link", .ret_type = 1, .nargs = 3,
117	  .args = { { Name, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
118	{ .name = "__cap_rights_get", .ret_type = 1, .nargs = 3,
119	  .args = { { Int, 0 }, { Int, 1 }, { CapRights | OUT, 2 } } },
120	{ .name = "__getcwd", .ret_type = 1, .nargs = 2,
121	  .args = { { Name | OUT, 0 }, { Int, 1 } } },
122	{ .name = "__realpathat", .ret_type = 1, .nargs = 5,
123	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Name | OUT, 2 },
124		    { Sizet, 3 }, { Int, 4} } },
125	{ .name = "_umtx_op", .ret_type = 1, .nargs = 5,
126	  .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 },
127		    { Ptr, 4 } } },
128	{ .name = "accept", .ret_type = 1, .nargs = 3,
129	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
130	{ .name = "access", .ret_type = 1, .nargs = 2,
131	  .args = { { Name | IN, 0 }, { Accessmode, 1 } } },
132	{ .name = "aio_cancel", .ret_type = 1, .nargs = 2,
133	  .args = { { Int, 0 }, { Aiocb, 1 } } },
134	{ .name = "aio_error", .ret_type = 1, .nargs = 1,
135	  .args = { { Aiocb, 0 } } },
136	{ .name = "aio_fsync", .ret_type = 1, .nargs = 2,
137	  .args = { { AiofsyncOp, 0 }, { Aiocb, 1 } } },
138	{ .name = "aio_mlock", .ret_type = 1, .nargs = 1,
139	  .args = { { Aiocb, 0 } } },
140	{ .name = "aio_read", .ret_type = 1, .nargs = 1,
141	  .args = { { Aiocb, 0 } } },
142	{ .name = "aio_return", .ret_type = 1, .nargs = 1,
143	  .args = { { Aiocb, 0 } } },
144	{ .name = "aio_suspend", .ret_type = 1, .nargs = 3,
145	  .args = { { AiocbArray, 0 }, { Int, 1 }, { Timespec, 2 } } },
146	{ .name = "aio_waitcomplete", .ret_type = 1, .nargs = 2,
147	  .args = { { AiocbPointer | OUT, 0 }, { Timespec, 1 } } },
148	{ .name = "aio_write", .ret_type = 1, .nargs = 1,
149	  .args = { { Aiocb, 0 } } },
150	{ .name = "bind", .ret_type = 1, .nargs = 3,
151	  .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Socklent, 2 } } },
152	{ .name = "bindat", .ret_type = 1, .nargs = 4,
153	  .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 },
154		    { Int, 3 } } },
155	{ .name = "break", .ret_type = 1, .nargs = 1,
156	  .args = { { Ptr, 0 } } },
157	{ .name = "cap_fcntls_get", .ret_type = 1, .nargs = 2,
158	  .args = { { Int, 0 }, { CapFcntlRights | OUT, 1 } } },
159	{ .name = "cap_fcntls_limit", .ret_type = 1, .nargs = 2,
160	  .args = { { Int, 0 }, { CapFcntlRights, 1 } } },
161	{ .name = "cap_getmode", .ret_type = 1, .nargs = 1,
162	  .args = { { PUInt | OUT, 0 } } },
163	{ .name = "cap_rights_limit", .ret_type = 1, .nargs = 2,
164	  .args = { { Int, 0 }, { CapRights, 1 } } },
165	{ .name = "chdir", .ret_type = 1, .nargs = 1,
166	  .args = { { Name, 0 } } },
167	{ .name = "chflags", .ret_type = 1, .nargs = 2,
168	  .args = { { Name | IN, 0 }, { FileFlags, 1 } } },
169	{ .name = "chflagsat", .ret_type = 1, .nargs = 4,
170	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { FileFlags, 2 },
171		    { Atflags, 3 } } },
172	{ .name = "chmod", .ret_type = 1, .nargs = 2,
173	  .args = { { Name, 0 }, { Octal, 1 } } },
174	{ .name = "chown", .ret_type = 1, .nargs = 3,
175	  .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
176	{ .name = "chroot", .ret_type = 1, .nargs = 1,
177	  .args = { { Name, 0 } } },
178	{ .name = "clock_gettime", .ret_type = 1, .nargs = 2,
179	  .args = { { Int, 0 }, { Timespec | OUT, 1 } } },
180	{ .name = "close", .ret_type = 1, .nargs = 1,
181	  .args = { { Int, 0 } } },
182	{ .name = "closefrom", .ret_type = 1, .nargs = 1,
183	  .args = { { Int, 0 } } },
184	{ .name = "close_range", .ret_type = 1, .nargs = 3,
185	  .args = { { Int, 0 }, { Int, 1 }, { Closerangeflags, 2 } } },
186	{ .name = "compat11.fstat", .ret_type = 1, .nargs = 2,
187	  .args = { { Int, 0 }, { Stat11 | OUT, 1 } } },
188	{ .name = "compat11.fstatat", .ret_type = 1, .nargs = 4,
189	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat11 | OUT, 2 },
190		    { Atflags, 3 } } },
191	{ .name = "compat11.kevent", .ret_type = 1, .nargs = 6,
192	  .args = { { Int, 0 }, { Kevent11, 1 }, { Int, 2 },
193		    { Kevent11 | OUT, 3 }, { Int, 4 }, { Timespec, 5 } } },
194	{ .name = "compat11.lstat", .ret_type = 1, .nargs = 2,
195	  .args = { { Name | IN, 0 }, { Stat11 | OUT, 1 } } },
196	{ .name = "compat11.mknod", .ret_type = 1, .nargs = 3,
197	  .args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } },
198	{ .name = "compat11.mknodat", .ret_type = 1, .nargs = 4,
199	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } },
200	{ .name = "compat11.stat", .ret_type = 1, .nargs = 2,
201	  .args = { { Name | IN, 0 }, { Stat11 | OUT, 1 } } },
202	{ .name = "connect", .ret_type = 1, .nargs = 3,
203	  .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Socklent, 2 } } },
204	{ .name = "connectat", .ret_type = 1, .nargs = 4,
205	  .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 },
206		    { Int, 3 } } },
207	{ .name = "dup", .ret_type = 1, .nargs = 1,
208	  .args = { { Int, 0 } } },
209	{ .name = "dup2", .ret_type = 1, .nargs = 2,
210	  .args = { { Int, 0 }, { Int, 1 } } },
211	{ .name = "eaccess", .ret_type = 1, .nargs = 2,
212	  .args = { { Name | IN, 0 }, { Accessmode, 1 } } },
213	{ .name = "execve", .ret_type = 1, .nargs = 3,
214	  .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 },
215		    { ExecEnv | IN, 2 } } },
216	{ .name = "exit", .ret_type = 0, .nargs = 1,
217	  .args = { { Hex, 0 } } },
218	{ .name = "extattr_delete_fd", .ret_type = 1, .nargs = 3,
219	  .args = { { Int, 0 }, { Extattrnamespace, 1 }, { Name, 2 } } },
220	{ .name = "extattr_delete_file", .ret_type = 1, .nargs = 3,
221	  .args = { { Name, 0 }, { Extattrnamespace, 1 }, { Name, 2 } } },
222	{ .name = "extattr_delete_link", .ret_type = 1, .nargs = 3,
223	  .args = { { Name, 0 }, { Extattrnamespace, 1 }, { Name, 2 } } },
224	{ .name = "extattr_get_fd", .ret_type = 1, .nargs = 5,
225	  .args = { { Int, 0 }, { Extattrnamespace, 1 }, { Name, 2 },
226		    { BinString | OUT, 3 }, { Sizet, 4 } } },
227	{ .name = "extattr_get_file", .ret_type = 1, .nargs = 5,
228	  .args = { { Name, 0 }, { Extattrnamespace, 1 }, { Name, 2 },
229		    { BinString | OUT, 3 }, { Sizet, 4 } } },
230	{ .name = "extattr_get_link", .ret_type = 1, .nargs = 5,
231	  .args = { { Name, 0 }, { Extattrnamespace, 1 }, { Name, 2 },
232		    { BinString | OUT, 3 }, { Sizet, 4 } } },
233	{ .name = "extattr_list_fd", .ret_type = 1, .nargs = 4,
234	  .args = { { Int, 0 }, { Extattrnamespace, 1 }, { BinString | OUT, 2 },
235		    { Sizet, 3 } } },
236	{ .name = "extattr_list_file", .ret_type = 1, .nargs = 4,
237	  .args = { { Name, 0 }, { Extattrnamespace, 1 }, { BinString | OUT, 2 },
238		    { Sizet, 3 } } },
239	{ .name = "extattr_list_link", .ret_type = 1, .nargs = 4,
240	  .args = { { Name, 0 }, { Extattrnamespace, 1 }, { BinString | OUT, 2 },
241		    { Sizet, 3 } } },
242	{ .name = "extattr_set_fd", .ret_type = 1, .nargs = 5,
243	  .args = { { Int, 0 }, { Extattrnamespace, 1 }, { Name, 2 },
244		    { BinString | IN, 3 }, { Sizet, 4 } } },
245	{ .name = "extattr_set_file", .ret_type = 1, .nargs = 5,
246	  .args = { { Name, 0 }, { Extattrnamespace, 1 }, { Name, 2 },
247		    { BinString | IN, 3 }, { Sizet, 4 } } },
248	{ .name = "extattr_set_link", .ret_type = 1, .nargs = 5,
249	  .args = { { Name, 0 }, { Extattrnamespace, 1 }, { Name, 2 },
250		    { BinString | IN, 3 }, { Sizet, 4 } } },
251	{ .name = "extattrctl", .ret_type = 1, .nargs = 5,
252	  .args = { { Name, 0 }, { Hex, 1 }, { Name, 2 },
253		    { Extattrnamespace, 3 }, { Name, 4 } } },
254	{ .name = "faccessat", .ret_type = 1, .nargs = 4,
255	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Accessmode, 2 },
256		    { Atflags, 3 } } },
257	{ .name = "fchflags", .ret_type = 1, .nargs = 2,
258	  .args = { { Int, 0 }, { FileFlags, 1 } } },
259	{ .name = "fchmod", .ret_type = 1, .nargs = 2,
260	  .args = { { Int, 0 }, { Octal, 1 } } },
261	{ .name = "fchmodat", .ret_type = 1, .nargs = 4,
262	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } },
263	{ .name = "fchown", .ret_type = 1, .nargs = 3,
264	  .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } },
265	{ .name = "fchownat", .ret_type = 1, .nargs = 5,
266	  .args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 },
267		    { Atflags, 4 } } },
268	{ .name = "fcntl", .ret_type = 1, .nargs = 3,
269	  .args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 2 } } },
270	{ .name = "fdatasync", .ret_type = 1, .nargs = 1,
271	  .args = { { Int, 0 } } },
272	{ .name = "flock", .ret_type = 1, .nargs = 2,
273	  .args = { { Int, 0 }, { Flockop, 1 } } },
274	{ .name = "fstat", .ret_type = 1, .nargs = 2,
275	  .args = { { Int, 0 }, { Stat | OUT, 1 } } },
276	{ .name = "fstatat", .ret_type = 1, .nargs = 4,
277	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat | OUT, 2 },
278		    { Atflags, 3 } } },
279	{ .name = "fstatfs", .ret_type = 1, .nargs = 2,
280	  .args = { { Int, 0 }, { StatFs | OUT, 1 } } },
281	{ .name = "fsync", .ret_type = 1, .nargs = 1,
282	  .args = { { Int, 0 } } },
283	{ .name = "ftruncate", .ret_type = 1, .nargs = 2,
284	  .args = { { Int | IN, 0 }, { QuadHex | IN, 1 } } },
285	{ .name = "futimens", .ret_type = 1, .nargs = 2,
286	  .args = { { Int, 0 }, { Timespec2 | IN, 1 } } },
287	{ .name = "futimes", .ret_type = 1, .nargs = 2,
288	  .args = { { Int, 0 }, { Timeval2 | IN, 1 } } },
289	{ .name = "futimesat", .ret_type = 1, .nargs = 3,
290	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timeval2 | IN, 2 } } },
291	{ .name = "getdirentries", .ret_type = 1, .nargs = 4,
292	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 },
293		    { PQuadHex | OUT, 3 } } },
294	{ .name = "getfsstat", .ret_type = 1, .nargs = 3,
295	  .args = { { Ptr, 0 }, { Long, 1 }, { Getfsstatmode, 2 } } },
296	{ .name = "getitimer", .ret_type = 1, .nargs = 2,
297	  .args = { { Itimerwhich, 0 }, { Itimerval | OUT, 2 } } },
298	{ .name = "getpeername", .ret_type = 1, .nargs = 3,
299	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
300	{ .name = "getpgid", .ret_type = 1, .nargs = 1,
301	  .args = { { Int, 0 } } },
302	{ .name = "getpriority", .ret_type = 1, .nargs = 2,
303	  .args = { { Priowhich, 0 }, { Int, 1 } } },
304	{ .name = "getrandom", .ret_type = 1, .nargs = 3,
305	  .args = { { BinString | OUT, 0 }, { Sizet, 1 }, { UInt, 2 } } },
306	{ .name = "getrlimit", .ret_type = 1, .nargs = 2,
307	  .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } },
308	{ .name = "getrusage", .ret_type = 1, .nargs = 2,
309	  .args = { { RusageWho, 0 }, { Rusage | OUT, 1 } } },
310	{ .name = "getsid", .ret_type = 1, .nargs = 1,
311	  .args = { { Int, 0 } } },
312	{ .name = "getsockname", .ret_type = 1, .nargs = 3,
313	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
314	{ .name = "getsockopt", .ret_type = 1, .nargs = 5,
315	  .args = { { Int, 0 }, { Sockoptlevel, 1 }, { Sockoptname, 2 },
316		    { Ptr | OUT, 3 }, { Ptr | OUT, 4 } } },
317	{ .name = "gettimeofday", .ret_type = 1, .nargs = 2,
318	  .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } },
319	{ .name = "ioctl", .ret_type = 1, .nargs = 3,
320	  .args = { { Int, 0 }, { Ioctl, 1 }, { Ptr, 2 } } },
321	{ .name = "kevent", .ret_type = 1, .nargs = 6,
322	  .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 },
323		    { Int, 4 }, { Timespec, 5 } } },
324	{ .name = "kill", .ret_type = 1, .nargs = 2,
325	  .args = { { Int | IN, 0 }, { Signal | IN, 1 } } },
326	{ .name = "kldfind", .ret_type = 1, .nargs = 1,
327	  .args = { { Name | IN, 0 } } },
328	{ .name = "kldfirstmod", .ret_type = 1, .nargs = 1,
329	  .args = { { Int, 0 } } },
330	{ .name = "kldload", .ret_type = 1, .nargs = 1,
331	  .args = { { Name | IN, 0 } } },
332	{ .name = "kldnext", .ret_type = 1, .nargs = 1,
333	  .args = { { Int, 0 } } },
334	{ .name = "kldstat", .ret_type = 1, .nargs = 2,
335	  .args = { { Int, 0 }, { Ptr, 1 } } },
336	{ .name = "kldsym", .ret_type = 1, .nargs = 3,
337	  .args = { { Int, 0 }, { Kldsymcmd, 1 }, { Ptr, 2 } } },
338	{ .name = "kldunload", .ret_type = 1, .nargs = 1,
339	  .args = { { Int, 0 } } },
340	{ .name = "kldunloadf", .ret_type = 1, .nargs = 2,
341	  .args = { { Int, 0 }, { Kldunloadflags, 1 } } },
342	{ .name = "kse_release", .ret_type = 0, .nargs = 1,
343	  .args = { { Timespec, 0 } } },
344	{ .name = "lchflags", .ret_type = 1, .nargs = 2,
345	  .args = { { Name | IN, 0 }, { FileFlags, 1 } } },
346	{ .name = "lchmod", .ret_type = 1, .nargs = 2,
347	  .args = { { Name, 0 }, { Octal, 1 } } },
348	{ .name = "lchown", .ret_type = 1, .nargs = 3,
349	  .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
350	{ .name = "link", .ret_type = 1, .nargs = 2,
351	  .args = { { Name, 0 }, { Name, 1 } } },
352	{ .name = "linkat", .ret_type = 1, .nargs = 5,
353	  .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 },
354		    { Atflags, 4 } } },
355	{ .name = "lio_listio", .ret_type = 1, .nargs = 4,
356	  .args = { { LioMode, 0 }, { AiocbArray, 1 }, { Int, 2 },
357		    { Sigevent, 3 } } },
358	{ .name = "listen", .ret_type = 1, .nargs = 2,
359	  .args = { { Int, 0 }, { Int, 1 } } },
360 	{ .name = "lseek", .ret_type = 2, .nargs = 3,
361	  .args = { { Int, 0 }, { QuadHex, 1 }, { Whence, 2 } } },
362	{ .name = "lstat", .ret_type = 1, .nargs = 2,
363	  .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
364	{ .name = "lutimes", .ret_type = 1, .nargs = 2,
365	  .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
366	{ .name = "madvise", .ret_type = 1, .nargs = 3,
367	  .args = { { Ptr, 0 }, { Sizet, 1 }, { Madvice, 2 } } },
368	{ .name = "minherit", .ret_type = 1, .nargs = 3,
369	  .args = { { Ptr, 0 }, { Sizet, 1 }, { Minherit, 2 } } },
370	{ .name = "mkdir", .ret_type = 1, .nargs = 2,
371	  .args = { { Name, 0 }, { Octal, 1 } } },
372	{ .name = "mkdirat", .ret_type = 1, .nargs = 3,
373	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
374	{ .name = "mkfifo", .ret_type = 1, .nargs = 2,
375	  .args = { { Name, 0 }, { Octal, 1 } } },
376	{ .name = "mkfifoat", .ret_type = 1, .nargs = 3,
377	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
378	{ .name = "mknod", .ret_type = 1, .nargs = 3,
379	  .args = { { Name, 0 }, { Octal, 1 }, { Quad, 2 } } },
380	{ .name = "mknodat", .ret_type = 1, .nargs = 4,
381	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Quad, 3 } } },
382	{ .name = "mlock", .ret_type = 1, .nargs = 2,
383	  .args = { { Ptr, 0 }, { Sizet, 1 } } },
384	{ .name = "mlockall", .ret_type = 1, .nargs = 1,
385	  .args = { { Mlockall, 0 } } },
386	{ .name = "mmap", .ret_type = 1, .nargs = 6,
387	  .args = { { Ptr, 0 }, { Sizet, 1 }, { Mprot, 2 }, { Mmapflags, 3 },
388		    { Int, 4 }, { QuadHex, 5 } } },
389	{ .name = "modfind", .ret_type = 1, .nargs = 1,
390	  .args = { { Name | IN, 0 } } },
391	{ .name = "mount", .ret_type = 1, .nargs = 4,
392	  .args = { { Name, 0 }, { Name, 1 }, { Mountflags, 2 }, { Ptr, 3 } } },
393	{ .name = "mprotect", .ret_type = 1, .nargs = 3,
394	  .args = { { Ptr, 0 }, { Sizet, 1 }, { Mprot, 2 } } },
395	{ .name = "msync", .ret_type = 1, .nargs = 3,
396	  .args = { { Ptr, 0 }, { Sizet, 1 }, { Msync, 2 } } },
397	{ .name = "munlock", .ret_type = 1, .nargs = 2,
398	  .args = { { Ptr, 0 }, { Sizet, 1 } } },
399	{ .name = "munmap", .ret_type = 1, .nargs = 2,
400	  .args = { { Ptr, 0 }, { Sizet, 1 } } },
401	{ .name = "nanosleep", .ret_type = 1, .nargs = 1,
402	  .args = { { Timespec, 0 } } },
403	{ .name = "nmount", .ret_type = 1, .nargs = 3,
404	  .args = { { Ptr, 0 }, { UInt, 1 }, { Mountflags, 2 } } },
405	{ .name = "open", .ret_type = 1, .nargs = 3,
406	  .args = { { Name | IN, 0 }, { Open, 1 }, { Octal, 2 } } },
407	{ .name = "openat", .ret_type = 1, .nargs = 4,
408	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Open, 2 },
409		    { Octal, 3 } } },
410	{ .name = "pathconf", .ret_type = 1, .nargs = 2,
411	  .args = { { Name | IN, 0 }, { Pathconf, 1 } } },
412	{ .name = "pipe", .ret_type = 1, .nargs = 1,
413	  .args = { { PipeFds | OUT, 0 } } },
414	{ .name = "pipe2", .ret_type = 1, .nargs = 2,
415	  .args = { { Ptr, 0 }, { Pipe2, 1 } } },
416	{ .name = "poll", .ret_type = 1, .nargs = 3,
417	  .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } },
418	{ .name = "posix_fadvise", .ret_type = 1, .nargs = 4,
419	  .args = { { Int, 0 }, { QuadHex, 1 }, { QuadHex, 2 },
420		    { Fadvice, 3 } } },
421	{ .name = "posix_openpt", .ret_type = 1, .nargs = 1,
422	  .args = { { Open, 0 } } },
423	{ .name = "ppoll", .ret_type = 1, .nargs = 4,
424	  .args = { { Pollfd, 0 }, { Int, 1 }, { Timespec | IN, 2 },
425 		    { Sigset | IN, 3 } } },
426	{ .name = "pread", .ret_type = 1, .nargs = 4,
427	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Sizet, 2 },
428		    { QuadHex, 3 } } },
429	{ .name = "preadv", .ret_type = 1, .nargs = 4,
430	  .args = { { Int, 0 }, { Iovec | OUT, 1 }, { Int, 2 },
431		    { QuadHex, 3 } } },
432	{ .name = "procctl", .ret_type = 1, .nargs = 4,
433	  .args = { { Idtype, 0 }, { Quad, 1 }, { Procctl, 2 }, { Ptr, 3 } } },
434	{ .name = "ptrace", .ret_type = 1, .nargs = 4,
435	  .args = { { Ptraceop, 0 }, { Int, 1 }, { Ptr, 2 }, { Int, 3 } } },
436	{ .name = "pwrite", .ret_type = 1, .nargs = 4,
437	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Sizet, 2 },
438		    { QuadHex, 3 } } },
439	{ .name = "pwritev", .ret_type = 1, .nargs = 4,
440	  .args = { { Int, 0 }, { Iovec | IN, 1 }, { Int, 2 },
441		    { QuadHex, 3 } } },
442	{ .name = "quotactl", .ret_type = 1, .nargs = 4,
443	  .args = { { Name, 0 }, { Quotactlcmd, 1 }, { Int, 2 }, { Ptr, 3 } } },
444	{ .name = "read", .ret_type = 1, .nargs = 3,
445	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Sizet, 2 } } },
446	{ .name = "readlink", .ret_type = 1, .nargs = 3,
447	  .args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Sizet, 2 } } },
448	{ .name = "readlinkat", .ret_type = 1, .nargs = 4,
449	  .args = { { Atfd, 0 }, { Name, 1 }, { Readlinkres | OUT, 2 },
450		    { Sizet, 3 } } },
451	{ .name = "readv", .ret_type = 1, .nargs = 3,
452	  .args = { { Int, 0 }, { Iovec | OUT, 1 }, { Int, 2 } } },
453	{ .name = "reboot", .ret_type = 1, .nargs = 1,
454	  .args = { { Reboothowto, 0 } } },
455	{ .name = "recvfrom", .ret_type = 1, .nargs = 6,
456	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Sizet, 2 },
457	            { Msgflags, 3 }, { Sockaddr | OUT, 4 },
458	            { Ptr | OUT, 5 } } },
459	{ .name = "recvmsg", .ret_type = 1, .nargs = 3,
460	  .args = { { Int, 0 }, { Msghdr | OUT, 1 }, { Msgflags, 2 } } },
461	{ .name = "rename", .ret_type = 1, .nargs = 2,
462	  .args = { { Name, 0 }, { Name, 1 } } },
463	{ .name = "renameat", .ret_type = 1, .nargs = 4,
464	  .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 } } },
465	{ .name = "rfork", .ret_type = 1, .nargs = 1,
466	  .args = { { Rforkflags, 0 } } },
467	{ .name = "rmdir", .ret_type = 1, .nargs = 1,
468	  .args = { { Name, 0 } } },
469	{ .name = "rtprio", .ret_type = 1, .nargs = 3,
470	  .args = { { Rtpriofunc, 0 }, { Int, 1 }, { Ptr, 2 } } },
471	{ .name = "rtprio_thread", .ret_type = 1, .nargs = 3,
472	  .args = { { Rtpriofunc, 0 }, { Int, 1 }, { Ptr, 2 } } },
473	{ .name = "sched_get_priority_max", .ret_type = 1, .nargs = 1,
474	  .args = { { Schedpolicy, 0 } } },
475	{ .name = "sched_get_priority_min", .ret_type = 1, .nargs = 1,
476	  .args = { { Schedpolicy, 0 } } },
477	{ .name = "sched_getparam", .ret_type = 1, .nargs = 2,
478	  .args = { { Int, 0 }, { Schedparam | OUT, 1 } } },
479	{ .name = "sched_getscheduler", .ret_type = 1, .nargs = 1,
480	  .args = { { Int, 0 } } },
481	{ .name = "sched_rr_get_interval", .ret_type = 1, .nargs = 2,
482	  .args = { { Int, 0 }, { Timespec | OUT, 1 } } },
483	{ .name = "sched_setparam", .ret_type = 1, .nargs = 2,
484	  .args = { { Int, 0 }, { Schedparam, 1 } } },
485	{ .name = "sched_setscheduler", .ret_type = 1, .nargs = 3,
486	  .args = { { Int, 0 }, { Schedpolicy, 1 }, { Schedparam, 2 } } },
487	{ .name = "sctp_generic_recvmsg", .ret_type = 1, .nargs = 7,
488	  .args = { { Int, 0 }, { Iovec | OUT, 1 }, { Int, 2 },
489	            { Sockaddr | OUT, 3 }, { Ptr | OUT, 4 },
490	            { Sctpsndrcvinfo | OUT, 5 }, { Ptr | OUT, 6 } } },
491	{ .name = "sctp_generic_sendmsg", .ret_type = 1, .nargs = 7,
492	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 },
493	            { Sockaddr | IN, 3 }, { Socklent, 4 },
494	            { Sctpsndrcvinfo | IN, 5 }, { Msgflags, 6 } } },
495	{ .name = "sctp_generic_sendmsg_iov", .ret_type = 1, .nargs = 7,
496	  .args = { { Int, 0 }, { Iovec | IN, 1 }, { Int, 2 },
497	            { Sockaddr | IN, 3 }, { Socklent, 4 },
498	            { Sctpsndrcvinfo | IN, 5 }, { Msgflags, 6 } } },
499	{ .name = "sendfile", .ret_type = 1, .nargs = 7,
500	  .args = { { Int, 0 }, { Int, 1 }, { QuadHex, 2 }, { Sizet, 3 },
501		    { Sendfilehdtr, 4 }, { QuadHex | OUT, 5 },
502		    { Sendfileflags, 6 } } },
503	{ .name = "select", .ret_type = 1, .nargs = 5,
504	  .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 },
505		    { Timeval, 4 } } },
506	{ .name = "sendmsg", .ret_type = 1, .nargs = 3,
507	  .args = { { Int, 0 }, { Msghdr | IN, 1 }, { Msgflags, 2 } } },
508	{ .name = "sendto", .ret_type = 1, .nargs = 6,
509	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Sizet, 2 },
510	            { Msgflags, 3 }, { Sockaddr | IN, 4 },
511	            { Socklent | IN, 5 } } },
512	{ .name = "setitimer", .ret_type = 1, .nargs = 3,
513	  .args = { { Itimerwhich, 0 }, { Itimerval, 1 },
514		    { Itimerval | OUT, 2 } } },
515	{ .name = "setpriority", .ret_type = 1, .nargs = 3,
516	  .args = { { Priowhich, 0 }, { Int, 1 }, { Int, 2 } } },
517	{ .name = "setrlimit", .ret_type = 1, .nargs = 2,
518	  .args = { { Resource, 0 }, { Rlimit | IN, 1 } } },
519	{ .name = "setsockopt", .ret_type = 1, .nargs = 5,
520	  .args = { { Int, 0 }, { Sockoptlevel, 1 }, { Sockoptname, 2 },
521		    { Ptr | IN, 3 }, { Socklent, 4 } } },
522	{ .name = "shm_open", .ret_type = 1, .nargs = 3,
523	  .args = { { ShmName | IN, 0 }, { Open, 1 }, { Octal, 2 } } },
524	{ .name = "shm_open2", .ret_type = 1, .nargs = 5,
525	  .args = { { ShmName | IN, 0 }, { Open, 1 }, { Octal, 2 },
526		    { ShmFlags, 3 }, { Name | IN, 4 } } },
527	{ .name = "shm_rename", .ret_type = 1, .nargs = 3,
528	  .args = { { Name | IN, 0 }, { Name | IN, 1 }, { Hex, 2 } } },
529	{ .name = "shm_unlink", .ret_type = 1, .nargs = 1,
530	  .args = { { Name | IN, 0 } } },
531	{ .name = "shutdown", .ret_type = 1, .nargs = 2,
532	  .args = { { Int, 0 }, { Shutdown, 1 } } },
533	{ .name = "sigaction", .ret_type = 1, .nargs = 3,
534	  .args = { { Signal, 0 }, { Sigaction | IN, 1 },
535		    { Sigaction | OUT, 2 } } },
536	{ .name = "sigpending", .ret_type = 1, .nargs = 1,
537	  .args = { { Sigset | OUT, 0 } } },
538	{ .name = "sigprocmask", .ret_type = 1, .nargs = 3,
539	  .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } },
540	{ .name = "sigqueue", .ret_type = 1, .nargs = 3,
541	  .args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } },
542	{ .name = "sigreturn", .ret_type = 1, .nargs = 1,
543	  .args = { { Ptr, 0 } } },
544	{ .name = "sigsuspend", .ret_type = 1, .nargs = 1,
545	  .args = { { Sigset | IN, 0 } } },
546	{ .name = "sigtimedwait", .ret_type = 1, .nargs = 3,
547	  .args = { { Sigset | IN, 0 }, { Siginfo | OUT, 1 },
548		    { Timespec | IN, 2 } } },
549	{ .name = "sigwait", .ret_type = 1, .nargs = 2,
550	  .args = { { Sigset | IN, 0 }, { PSig | OUT, 1 } } },
551	{ .name = "sigwaitinfo", .ret_type = 1, .nargs = 2,
552	  .args = { { Sigset | IN, 0 }, { Siginfo | OUT, 1 } } },
553	{ .name = "socket", .ret_type = 1, .nargs = 3,
554	  .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Sockprotocol, 2 } } },
555	{ .name = "stat", .ret_type = 1, .nargs = 2,
556	  .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
557	{ .name = "statfs", .ret_type = 1, .nargs = 2,
558	  .args = { { Name | IN, 0 }, { StatFs | OUT, 1 } } },
559	{ .name = "symlink", .ret_type = 1, .nargs = 2,
560	  .args = { { Name, 0 }, { Name, 1 } } },
561	{ .name = "symlinkat", .ret_type = 1, .nargs = 3,
562	  .args = { { Name, 0 }, { Atfd, 1 }, { Name, 2 } } },
563	{ .name = "sysarch", .ret_type = 1, .nargs = 2,
564	  .args = { { Sysarch, 0 }, { Ptr, 1 } } },
565	{ .name = "__sysctl", .ret_type = 1, .nargs = 6,
566	  .args = { { Sysctl, 0 }, { Sizet, 1 }, { Ptr, 2 }, { Ptr, 3 },
567	            { Ptr, 4 }, { Sizet, 5 } } },
568	{ .name = "__sysctlbyname", .ret_type = 1, .nargs = 6,
569	  .args = { { Name, 0 }, { Sizet, 1 }, { Ptr, 2 }, { Ptr, 3 },
570	            { Ptr, 4}, { Sizet, 5 } } },
571	{ .name = "thr_kill", .ret_type = 1, .nargs = 2,
572	  .args = { { Long, 0 }, { Signal, 1 } } },
573	{ .name = "thr_self", .ret_type = 1, .nargs = 1,
574	  .args = { { Ptr, 0 } } },
575	{ .name = "thr_set_name", .ret_type = 1, .nargs = 2,
576	  .args = { { Long, 0 }, { Name, 1 } } },
577	{ .name = "truncate", .ret_type = 1, .nargs = 2,
578	  .args = { { Name | IN, 0 }, { QuadHex | IN, 1 } } },
579	{ .name = "unlink", .ret_type = 1, .nargs = 1,
580	  .args = { { Name, 0 } } },
581	{ .name = "unlinkat", .ret_type = 1, .nargs = 3,
582	  .args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } },
583	{ .name = "unmount", .ret_type = 1, .nargs = 2,
584	  .args = { { Name, 0 }, { Mountflags, 1 } } },
585	{ .name = "utimensat", .ret_type = 1, .nargs = 4,
586	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timespec2 | IN, 2 },
587		    { Atflags, 3 } } },
588	{ .name = "utimes", .ret_type = 1, .nargs = 2,
589	  .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
590	{ .name = "utrace", .ret_type = 1, .nargs = 1,
591	  .args = { { Utrace, 0 } } },
592	{ .name = "wait4", .ret_type = 1, .nargs = 4,
593	  .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 },
594		    { Rusage | OUT, 3 } } },
595	{ .name = "wait6", .ret_type = 1, .nargs = 6,
596	  .args = { { Idtype, 0 }, { Quad, 1 }, { ExitStatus | OUT, 2 },
597		    { Waitoptions, 3 }, { Rusage | OUT, 4 },
598		    { Siginfo | OUT, 5 } } },
599	{ .name = "write", .ret_type = 1, .nargs = 3,
600	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Sizet, 2 } } },
601	{ .name = "writev", .ret_type = 1, .nargs = 3,
602	  .args = { { Int, 0 }, { Iovec | IN, 1 }, { Int, 2 } } },
603
604	/* Linux ABI */
605	{ .name = "linux_access", .ret_type = 1, .nargs = 2,
606	  .args = { { Name, 0 }, { Accessmode, 1 } } },
607	{ .name = "linux_execve", .ret_type = 1, .nargs = 3,
608	  .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 },
609		    { ExecEnv | IN, 2 } } },
610	{ .name = "linux_getitimer", .ret_type = 1, .nargs = 2,
611	  .args = { { Itimerwhich, 0 }, { Itimerval | OUT, 2 } } },
612	{ .name = "linux_lseek", .ret_type = 2, .nargs = 3,
613	  .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } },
614	{ .name = "linux_mkdir", .ret_type = 1, .nargs = 2,
615	  .args = { { Name | IN, 0 }, { Int, 1 } } },
616	{ .name = "linux_newfstat", .ret_type = 1, .nargs = 2,
617	  .args = { { Int, 0 }, { Ptr | OUT, 1 } } },
618	{ .name = "linux_newlstat", .ret_type = 1, .nargs = 2,
619	  .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } },
620	{ .name = "linux_newstat", .ret_type = 1, .nargs = 2,
621	  .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } },
622	{ .name = "linux_open", .ret_type = 1, .nargs = 3,
623	  .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } },
624	{ .name = "linux_readlink", .ret_type = 1, .nargs = 3,
625	  .args = { { Name, 0 }, { Name | OUT, 1 }, { Sizet, 2 } } },
626	{ .name = "linux_setitimer", .ret_type = 1, .nargs = 3,
627	  .args = { { Itimerwhich, 0 }, { Itimerval, 1 },
628		    { Itimerval | OUT, 2 } } },
629	{ .name = "linux_socketcall", .ret_type = 1, .nargs = 2,
630	  .args = { { Int, 0 }, { LinuxSockArgs, 1 } } },
631	{ .name = "linux_stat64", .ret_type = 1, .nargs = 2,
632	  .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } },
633};
634static STAILQ_HEAD(, syscall) seen_syscalls;
635
636/* Xlat idea taken from strace */
637struct xlat {
638	int val;
639	const char *str;
640};
641
642#define	X(a)	{ a, #a },
643#define	XEND	{ 0, NULL }
644
645static struct xlat poll_flags[] = {
646	X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR)
647	X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND)
648	X(POLLWRBAND) X(POLLINIGNEOF) X(POLLRDHUP) XEND
649};
650
651static struct xlat sigaction_flags[] = {
652	X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP)
653	X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND
654};
655
656static struct xlat linux_socketcall_ops[] = {
657	X(LINUX_SOCKET) X(LINUX_BIND) X(LINUX_CONNECT) X(LINUX_LISTEN)
658	X(LINUX_ACCEPT) X(LINUX_GETSOCKNAME) X(LINUX_GETPEERNAME)
659	X(LINUX_SOCKETPAIR) X(LINUX_SEND) X(LINUX_RECV) X(LINUX_SENDTO)
660	X(LINUX_RECVFROM) X(LINUX_SHUTDOWN) X(LINUX_SETSOCKOPT)
661	X(LINUX_GETSOCKOPT) X(LINUX_SENDMSG) X(LINUX_RECVMSG)
662	XEND
663};
664
665static struct xlat lio_modes[] = {
666	X(LIO_WAIT) X(LIO_NOWAIT)
667	XEND
668};
669
670static struct xlat lio_opcodes[] = {
671	X(LIO_WRITE) X(LIO_READ) X(LIO_READV) X(LIO_WRITEV) X(LIO_NOP)
672	XEND
673};
674
675static struct xlat aio_fsync_ops[] = {
676	X(O_SYNC)
677	XEND
678};
679
680#undef X
681#undef XEND
682
683/*
684 * Searches an xlat array for a value, and returns it if found.  Otherwise
685 * return a string representation.
686 */
687static const char *
688lookup(struct xlat *xlat, int val, int base)
689{
690	static char tmp[16];
691
692	for (; xlat->str != NULL; xlat++)
693		if (xlat->val == val)
694			return (xlat->str);
695	switch (base) {
696	case 8:
697		sprintf(tmp, "0%o", val);
698		break;
699	case 16:
700		sprintf(tmp, "0x%x", val);
701		break;
702	case 10:
703		sprintf(tmp, "%u", val);
704		break;
705	default:
706		errx(1, "Unknown lookup base");
707	}
708	return (tmp);
709}
710
711static const char *
712xlookup(struct xlat *xlat, int val)
713{
714
715	return (lookup(xlat, val, 16));
716}
717
718/*
719 * Searches an xlat array containing bitfield values.  Remaining bits
720 * set after removing the known ones are printed at the end:
721 * IN|0x400.
722 */
723static char *
724xlookup_bits(struct xlat *xlat, int val)
725{
726	int len, rem;
727	static char str[512];
728
729	len = 0;
730	rem = val;
731	for (; xlat->str != NULL; xlat++) {
732		if ((xlat->val & rem) == xlat->val) {
733			/*
734			 * Don't print the "all-bits-zero" string unless all
735			 * bits are really zero.
736			 */
737			if (xlat->val == 0 && val != 0)
738				continue;
739			len += sprintf(str + len, "%s|", xlat->str);
740			rem &= ~(xlat->val);
741		}
742	}
743
744	/*
745	 * If we have leftover bits or didn't match anything, print
746	 * the remainder.
747	 */
748	if (rem || len == 0)
749		len += sprintf(str + len, "0x%x", rem);
750	if (len && str[len - 1] == '|')
751		len--;
752	str[len] = 0;
753	return (str);
754}
755
756static void
757print_integer_arg(const char *(*decoder)(int), FILE *fp, int value)
758{
759	const char *str;
760
761	str = decoder(value);
762	if (str != NULL)
763		fputs(str, fp);
764	else
765		fprintf(fp, "%d", value);
766}
767
768static bool
769print_mask_arg_part(bool (*decoder)(FILE *, int, int *), FILE *fp, int value,
770    int *rem)
771{
772
773	return (decoder(fp, value, rem));
774}
775
776static void
777print_mask_arg(bool (*decoder)(FILE *, int, int *), FILE *fp, int value)
778{
779	int rem;
780
781	if (!print_mask_arg_part(decoder, fp, value, &rem))
782		fprintf(fp, "0x%x", rem);
783	else if (rem != 0)
784		fprintf(fp, "|0x%x", rem);
785}
786
787static void
788print_mask_arg32(bool (*decoder)(FILE *, uint32_t, uint32_t *), FILE *fp,
789    uint32_t value)
790{
791	uint32_t rem;
792
793	if (!decoder(fp, value, &rem))
794		fprintf(fp, "0x%x", rem);
795	else if (rem != 0)
796		fprintf(fp, "|0x%x", rem);
797}
798
799/*
800 * Add argument padding to subsequent system calls after Quad
801 * syscall arguments as needed.  This used to be done by hand in the
802 * decoded_syscalls table which was ugly and error prone.  It is
803 * simpler to do the fixup of offsets at initialization time than when
804 * decoding arguments.
805 */
806static void
807quad_fixup(struct syscall_decode *sc)
808{
809	int offset, prev;
810	u_int i;
811
812	offset = 0;
813	prev = -1;
814	for (i = 0; i < sc->nargs; i++) {
815		/* This arg type is a dummy that doesn't use offset. */
816		if ((sc->args[i].type & ARG_MASK) == PipeFds)
817			continue;
818
819		assert(prev < sc->args[i].offset);
820		prev = sc->args[i].offset;
821		sc->args[i].offset += offset;
822		switch (sc->args[i].type & ARG_MASK) {
823		case Quad:
824		case QuadHex:
825#if defined(__powerpc__) || defined(__arm__) || defined(__aarch64__)
826			/*
827			 * 64-bit arguments on 32-bit powerpc and arm must be
828			 * 64-bit aligned.  If the current offset is
829			 * not aligned, the calling convention inserts
830			 * a 32-bit pad argument that should be skipped.
831			 */
832			if (sc->args[i].offset % 2 == 1) {
833				sc->args[i].offset++;
834				offset++;
835			}
836#endif
837			offset++;
838		default:
839			break;
840		}
841	}
842}
843
844static struct syscall *
845find_syscall(struct procabi *abi, u_int number)
846{
847	struct extra_syscall *es;
848
849	if (number < nitems(abi->syscalls))
850		return (abi->syscalls[number]);
851	STAILQ_FOREACH(es, &abi->extra_syscalls, entries) {
852		if (es->number == number)
853			return (es->sc);
854	}
855	return (NULL);
856}
857
858static void
859add_syscall(struct procabi *abi, u_int number, struct syscall *sc)
860{
861	struct extra_syscall *es;
862
863	/*
864	 * quad_fixup() is currently needed for all 32-bit ABIs.
865	 * TODO: This should probably be a function pointer inside struct
866	 *  procabi instead.
867	 */
868	if (abi->pointer_size == 4)
869		quad_fixup(&sc->decode);
870
871	if (number < nitems(abi->syscalls)) {
872		assert(abi->syscalls[number] == NULL);
873		abi->syscalls[number] = sc;
874	} else {
875		es = malloc(sizeof(*es));
876		es->sc = sc;
877		es->number = number;
878		STAILQ_INSERT_TAIL(&abi->extra_syscalls, es, entries);
879	}
880
881	STAILQ_INSERT_HEAD(&seen_syscalls, sc, entries);
882}
883
884/*
885 * If/when the list gets big, it might be desirable to do it
886 * as a hash table or binary search.
887 */
888struct syscall *
889get_syscall(struct threadinfo *t, u_int number, u_int nargs)
890{
891	struct syscall *sc;
892	struct procabi *procabi;
893	const char *sysdecode_name;
894	const char *lookup_name;
895	const char *name;
896	u_int i;
897
898	procabi = t->proc->abi;
899	sc = find_syscall(procabi, number);
900	if (sc != NULL)
901		return (sc);
902
903	/* Memory is not explicitly deallocated, it's released on exit(). */
904	sysdecode_name = sysdecode_syscallname(procabi->abi, number);
905	if (sysdecode_name == NULL)
906		asprintf(__DECONST(char **, &name), "#%d", number);
907	else
908		name = sysdecode_name;
909
910	sc = calloc(1, sizeof(*sc));
911	sc->name = name;
912
913	/* Also decode compat syscalls arguments by stripping the prefix. */
914	lookup_name = name;
915	if (procabi->compat_prefix != NULL && strncmp(procabi->compat_prefix,
916	    name, strlen(procabi->compat_prefix)) == 0)
917		lookup_name += strlen(procabi->compat_prefix);
918
919	for (i = 0; i < nitems(decoded_syscalls); i++) {
920		if (strcmp(lookup_name, decoded_syscalls[i].name) == 0) {
921			sc->decode = decoded_syscalls[i];
922			add_syscall(t->proc->abi, number, sc);
923			return (sc);
924		}
925	}
926
927	/* It is unknown.  Add it into the list. */
928#if DEBUG
929	fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name,
930	    nargs);
931#endif
932	sc->unknown = sysdecode_name == NULL;
933	sc->decode.ret_type = 1; /* Assume 1 return value. */
934	sc->decode.nargs = nargs;
935	for (i = 0; i < nargs; i++) {
936		sc->decode.args[i].offset = i;
937		/* Treat all unknown arguments as LongHex. */
938		sc->decode.args[i].type = LongHex;
939	}
940	add_syscall(t->proc->abi, number, sc);
941	return (sc);
942}
943
944/*
945 * Copy a fixed amount of bytes from the process.
946 */
947static int
948get_struct(pid_t pid, psaddr_t offset, void *buf, size_t len)
949{
950	struct ptrace_io_desc iorequest;
951
952	iorequest.piod_op = PIOD_READ_D;
953	iorequest.piod_offs = (void *)(uintptr_t)offset;
954	iorequest.piod_addr = buf;
955	iorequest.piod_len = len;
956	if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0)
957		return (-1);
958	return (0);
959}
960
961#define	MAXSIZE		4096
962
963/*
964 * Copy a string from the process.  Note that it is
965 * expected to be a C string, but if max is set, it will
966 * only get that much.
967 */
968static char *
969get_string(pid_t pid, psaddr_t addr, int max)
970{
971	struct ptrace_io_desc iorequest;
972	char *buf, *nbuf;
973	size_t offset, size, totalsize;
974
975	offset = 0;
976	if (max)
977		size = max + 1;
978	else {
979		/* Read up to the end of the current page. */
980		size = PAGE_SIZE - (addr % PAGE_SIZE);
981		if (size > MAXSIZE)
982			size = MAXSIZE;
983	}
984	totalsize = size;
985	buf = malloc(totalsize);
986	if (buf == NULL)
987		return (NULL);
988	for (;;) {
989		iorequest.piod_op = PIOD_READ_D;
990		iorequest.piod_offs = (void *)((uintptr_t)addr + offset);
991		iorequest.piod_addr = buf + offset;
992		iorequest.piod_len = size;
993		if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) {
994			free(buf);
995			return (NULL);
996		}
997		if (memchr(buf + offset, '\0', size) != NULL)
998			return (buf);
999		offset += size;
1000		if (totalsize < MAXSIZE && max == 0) {
1001			size = MAXSIZE - totalsize;
1002			if (size > PAGE_SIZE)
1003				size = PAGE_SIZE;
1004			nbuf = realloc(buf, totalsize + size);
1005			if (nbuf == NULL) {
1006				buf[totalsize - 1] = '\0';
1007				return (buf);
1008			}
1009			buf = nbuf;
1010			totalsize += size;
1011		} else {
1012			buf[totalsize - 1] = '\0';
1013			return (buf);
1014		}
1015	}
1016}
1017
1018static const char *
1019strsig2(int sig)
1020{
1021	static char tmp[32];
1022	const char *signame;
1023
1024	signame = sysdecode_signal(sig);
1025	if (signame == NULL) {
1026		snprintf(tmp, sizeof(tmp), "%d", sig);
1027		signame = tmp;
1028	}
1029	return (signame);
1030}
1031
1032static void
1033print_kevent(FILE *fp, struct kevent *ke)
1034{
1035
1036	switch (ke->filter) {
1037	case EVFILT_READ:
1038	case EVFILT_WRITE:
1039	case EVFILT_VNODE:
1040	case EVFILT_PROC:
1041	case EVFILT_TIMER:
1042	case EVFILT_PROCDESC:
1043	case EVFILT_EMPTY:
1044		fprintf(fp, "%ju", (uintmax_t)ke->ident);
1045		break;
1046	case EVFILT_SIGNAL:
1047		fputs(strsig2(ke->ident), fp);
1048		break;
1049	default:
1050		fprintf(fp, "%p", (void *)ke->ident);
1051	}
1052	fprintf(fp, ",");
1053	print_integer_arg(sysdecode_kevent_filter, fp, ke->filter);
1054	fprintf(fp, ",");
1055	print_mask_arg(sysdecode_kevent_flags, fp, ke->flags);
1056	fprintf(fp, ",");
1057	sysdecode_kevent_fflags(fp, ke->filter, ke->fflags, 16);
1058	fprintf(fp, ",%#jx,%p", (uintmax_t)ke->data, ke->udata);
1059}
1060
1061static void
1062print_utrace(FILE *fp, void *utrace_addr, size_t len)
1063{
1064	unsigned char *utrace_buffer;
1065
1066	fprintf(fp, "{ ");
1067	if (sysdecode_utrace(fp, utrace_addr, len)) {
1068		fprintf(fp, " }");
1069		return;
1070	}
1071
1072	utrace_buffer = utrace_addr;
1073	fprintf(fp, "%zu:", len);
1074	while (len--)
1075		fprintf(fp, " %02x", *utrace_buffer++);
1076	fprintf(fp, " }");
1077}
1078
1079static void
1080print_pointer(FILE *fp, uintptr_t arg)
1081{
1082
1083	fprintf(fp, "%p", (void *)arg);
1084}
1085
1086static void
1087print_sockaddr(FILE *fp, struct trussinfo *trussinfo, uintptr_t arg,
1088    socklen_t len)
1089{
1090	char addr[64];
1091	struct sockaddr_in *lsin;
1092	struct sockaddr_in6 *lsin6;
1093	struct sockaddr_un *sun;
1094	struct sockaddr *sa;
1095	u_char *q;
1096	pid_t pid = trussinfo->curthread->proc->pid;
1097
1098	if (arg == 0) {
1099		fputs("NULL", fp);
1100		return;
1101	}
1102	/* If the length is too small, just bail. */
1103	if (len < sizeof(*sa)) {
1104		print_pointer(fp, arg);
1105		return;
1106	}
1107
1108	sa = calloc(1, len);
1109	if (get_struct(pid, arg, sa, len) == -1) {
1110		free(sa);
1111		print_pointer(fp, arg);
1112		return;
1113	}
1114
1115	switch (sa->sa_family) {
1116	case AF_INET:
1117		if (len < sizeof(*lsin))
1118			goto sockaddr_short;
1119		lsin = (struct sockaddr_in *)(void *)sa;
1120		inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof(addr));
1121		fprintf(fp, "{ AF_INET %s:%d }", addr,
1122		    htons(lsin->sin_port));
1123		break;
1124	case AF_INET6:
1125		if (len < sizeof(*lsin6))
1126			goto sockaddr_short;
1127		lsin6 = (struct sockaddr_in6 *)(void *)sa;
1128		inet_ntop(AF_INET6, &lsin6->sin6_addr, addr,
1129		    sizeof(addr));
1130		fprintf(fp, "{ AF_INET6 [%s]:%d }", addr,
1131		    htons(lsin6->sin6_port));
1132		break;
1133	case AF_UNIX:
1134		sun = (struct sockaddr_un *)sa;
1135		fprintf(fp, "{ AF_UNIX \"%.*s\" }",
1136		    (int)(len - offsetof(struct sockaddr_un, sun_path)),
1137		    sun->sun_path);
1138		break;
1139	default:
1140	sockaddr_short:
1141		fprintf(fp,
1142		    "{ sa_len = %d, sa_family = %d, sa_data = {",
1143		    (int)sa->sa_len, (int)sa->sa_family);
1144		for (q = (u_char *)sa->sa_data;
1145		     q < (u_char *)sa + len; q++)
1146			fprintf(fp, "%s 0x%02x",
1147			    q == (u_char *)sa->sa_data ? "" : ",",
1148			    *q);
1149		fputs(" } }", fp);
1150	}
1151	free(sa);
1152}
1153
1154#define IOV_LIMIT 16
1155
1156static void
1157print_iovec(FILE *fp, struct trussinfo *trussinfo, uintptr_t arg, int iovcnt)
1158{
1159	struct iovec iov[IOV_LIMIT];
1160	size_t max_string = trussinfo->strsize;
1161	char tmp2[max_string + 1], *tmp3;
1162	size_t len;
1163	pid_t pid = trussinfo->curthread->proc->pid;
1164	int i;
1165	bool buf_truncated, iov_truncated;
1166
1167	if (iovcnt <= 0) {
1168		print_pointer(fp, arg);
1169		return;
1170	}
1171	if (iovcnt > IOV_LIMIT) {
1172		iovcnt = IOV_LIMIT;
1173		iov_truncated = true;
1174	} else {
1175		iov_truncated = false;
1176	}
1177	if (get_struct(pid, arg, &iov, iovcnt * sizeof(struct iovec)) == -1) {
1178		print_pointer(fp, arg);
1179		return;
1180	}
1181
1182	fputs("[", fp);
1183	for (i = 0; i < iovcnt; i++) {
1184		len = iov[i].iov_len;
1185		if (len > max_string) {
1186			len = max_string;
1187			buf_truncated = true;
1188		} else {
1189			buf_truncated = false;
1190		}
1191		fprintf(fp, "%s{", (i > 0) ? "," : "");
1192		if (len && get_struct(pid, (uintptr_t)iov[i].iov_base, &tmp2, len) != -1) {
1193			tmp3 = malloc(len * 4 + 1);
1194			while (len) {
1195				if (strvisx(tmp3, tmp2, len,
1196				    VIS_CSTYLE|VIS_TAB|VIS_NL) <=
1197				    (int)max_string)
1198					break;
1199				len--;
1200				buf_truncated = true;
1201			}
1202			fprintf(fp, "\"%s\"%s", tmp3,
1203			    buf_truncated ? "..." : "");
1204			free(tmp3);
1205		} else {
1206			print_pointer(fp, (uintptr_t)iov[i].iov_base);
1207		}
1208		fprintf(fp, ",%zu}", iov[i].iov_len);
1209	}
1210	fprintf(fp, "%s%s", iov_truncated ? ",..." : "", "]");
1211}
1212
1213static void
1214print_sigval(FILE *fp, union sigval *sv)
1215{
1216	fprintf(fp, "{ %d, %p }", sv->sival_int, sv->sival_ptr);
1217}
1218
1219static void
1220print_sigevent(FILE *fp, struct sigevent *se)
1221{
1222	fputs("{ sigev_notify=", fp);
1223	switch (se->sigev_notify) {
1224	case SIGEV_NONE:
1225		fputs("SIGEV_NONE", fp);
1226		break;
1227	case SIGEV_SIGNAL:
1228		fprintf(fp, "SIGEV_SIGNAL, sigev_signo=%s, sigev_value=",
1229				strsig2(se->sigev_signo));
1230		print_sigval(fp, &se->sigev_value);
1231		break;
1232	case SIGEV_THREAD:
1233		fputs("SIGEV_THREAD, sigev_value=", fp);
1234		print_sigval(fp, &se->sigev_value);
1235		break;
1236	case SIGEV_KEVENT:
1237		fprintf(fp, "SIGEV_KEVENT, sigev_notify_kqueue=%d, sigev_notify_kevent_flags=",
1238				se->sigev_notify_kqueue);
1239		print_mask_arg(sysdecode_kevent_flags, fp, se->sigev_notify_kevent_flags);
1240		break;
1241	case SIGEV_THREAD_ID:
1242		fprintf(fp, "SIGEV_THREAD_ID, sigev_notify_thread_id=%d, sigev_signo=%s, sigev_value=",
1243				se->sigev_notify_thread_id, strsig2(se->sigev_signo));
1244		print_sigval(fp, &se->sigev_value);
1245		break;
1246	default:
1247		fprintf(fp, "%d", se->sigev_notify);
1248		break;
1249	}
1250	fputs(" }", fp);
1251}
1252
1253static void
1254print_aiocb(FILE *fp, struct aiocb *cb)
1255{
1256	fprintf(fp, "{ %d,%jd,%p,%zu,%s,",
1257			cb->aio_fildes,
1258			cb->aio_offset,
1259			cb->aio_buf,
1260			cb->aio_nbytes,
1261			xlookup(lio_opcodes, cb->aio_lio_opcode));
1262	print_sigevent(fp, &cb->aio_sigevent);
1263	fputs(" }", fp);
1264}
1265
1266static void
1267print_gen_cmsg(FILE *fp, struct cmsghdr *cmsghdr)
1268{
1269	u_char *q;
1270
1271	fputs("{", fp);
1272	for (q = CMSG_DATA(cmsghdr);
1273	     q < (u_char *)cmsghdr + cmsghdr->cmsg_len; q++) {
1274		fprintf(fp, "%s0x%02x", q == CMSG_DATA(cmsghdr) ? "" : ",", *q);
1275	}
1276	fputs("}", fp);
1277}
1278
1279static void
1280print_sctp_initmsg(FILE *fp, struct sctp_initmsg *init)
1281{
1282	fprintf(fp, "{out=%u,", init->sinit_num_ostreams);
1283	fprintf(fp, "in=%u,", init->sinit_max_instreams);
1284	fprintf(fp, "max_rtx=%u,", init->sinit_max_attempts);
1285	fprintf(fp, "max_rto=%u}", init->sinit_max_init_timeo);
1286}
1287
1288static void
1289print_sctp_sndrcvinfo(FILE *fp, bool receive, struct sctp_sndrcvinfo *info)
1290{
1291	fprintf(fp, "{sid=%u,", info->sinfo_stream);
1292	if (receive) {
1293		fprintf(fp, "ssn=%u,", info->sinfo_ssn);
1294	}
1295	fputs("flgs=", fp);
1296	sysdecode_sctp_sinfo_flags(fp, info->sinfo_flags);
1297	fprintf(fp, ",ppid=%u,", ntohl(info->sinfo_ppid));
1298	if (!receive) {
1299		fprintf(fp, "ctx=%u,", info->sinfo_context);
1300		fprintf(fp, "ttl=%u,", info->sinfo_timetolive);
1301	}
1302	if (receive) {
1303		fprintf(fp, "tsn=%u,", info->sinfo_tsn);
1304		fprintf(fp, "cumtsn=%u,", info->sinfo_cumtsn);
1305	}
1306	fprintf(fp, "id=%u}", info->sinfo_assoc_id);
1307}
1308
1309static void
1310print_sctp_sndinfo(FILE *fp, struct sctp_sndinfo *info)
1311{
1312	fprintf(fp, "{sid=%u,", info->snd_sid);
1313	fputs("flgs=", fp);
1314	print_mask_arg(sysdecode_sctp_snd_flags, fp, info->snd_flags);
1315	fprintf(fp, ",ppid=%u,", ntohl(info->snd_ppid));
1316	fprintf(fp, "ctx=%u,", info->snd_context);
1317	fprintf(fp, "id=%u}", info->snd_assoc_id);
1318}
1319
1320static void
1321print_sctp_rcvinfo(FILE *fp, struct sctp_rcvinfo *info)
1322{
1323	fprintf(fp, "{sid=%u,", info->rcv_sid);
1324	fprintf(fp, "ssn=%u,", info->rcv_ssn);
1325	fputs("flgs=", fp);
1326	print_mask_arg(sysdecode_sctp_rcv_flags, fp, info->rcv_flags);
1327	fprintf(fp, ",ppid=%u,", ntohl(info->rcv_ppid));
1328	fprintf(fp, "tsn=%u,", info->rcv_tsn);
1329	fprintf(fp, "cumtsn=%u,", info->rcv_cumtsn);
1330	fprintf(fp, "ctx=%u,", info->rcv_context);
1331	fprintf(fp, "id=%u}", info->rcv_assoc_id);
1332}
1333
1334static void
1335print_sctp_nxtinfo(FILE *fp, struct sctp_nxtinfo *info)
1336{
1337	fprintf(fp, "{sid=%u,", info->nxt_sid);
1338	fputs("flgs=", fp);
1339	print_mask_arg(sysdecode_sctp_nxt_flags, fp, info->nxt_flags);
1340	fprintf(fp, ",ppid=%u,", ntohl(info->nxt_ppid));
1341	fprintf(fp, "len=%u,", info->nxt_length);
1342	fprintf(fp, "id=%u}", info->nxt_assoc_id);
1343}
1344
1345static void
1346print_sctp_prinfo(FILE *fp, struct sctp_prinfo *info)
1347{
1348	fputs("{pol=", fp);
1349	print_integer_arg(sysdecode_sctp_pr_policy, fp, info->pr_policy);
1350	fprintf(fp, ",val=%u}", info->pr_value);
1351}
1352
1353static void
1354print_sctp_authinfo(FILE *fp, struct sctp_authinfo *info)
1355{
1356	fprintf(fp, "{num=%u}", info->auth_keynumber);
1357}
1358
1359static void
1360print_sctp_ipv4_addr(FILE *fp, struct in_addr *addr)
1361{
1362	char buf[INET_ADDRSTRLEN];
1363	const char *s;
1364
1365	s = inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN);
1366	if (s != NULL)
1367		fprintf(fp, "{addr=%s}", s);
1368	else
1369		fputs("{addr=???}", fp);
1370}
1371
1372static void
1373print_sctp_ipv6_addr(FILE *fp, struct in6_addr *addr)
1374{
1375	char buf[INET6_ADDRSTRLEN];
1376	const char *s;
1377
1378	s = inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN);
1379	if (s != NULL)
1380		fprintf(fp, "{addr=%s}", s);
1381	else
1382		fputs("{addr=???}", fp);
1383}
1384
1385static void
1386print_sctp_cmsg(FILE *fp, bool receive, struct cmsghdr *cmsghdr)
1387{
1388	void *data;
1389	socklen_t len;
1390
1391	len = cmsghdr->cmsg_len;
1392	data = CMSG_DATA(cmsghdr);
1393	switch (cmsghdr->cmsg_type) {
1394	case SCTP_INIT:
1395		if (len == CMSG_LEN(sizeof(struct sctp_initmsg)))
1396			print_sctp_initmsg(fp, (struct sctp_initmsg *)data);
1397		else
1398			print_gen_cmsg(fp, cmsghdr);
1399		break;
1400	case SCTP_SNDRCV:
1401		if (len == CMSG_LEN(sizeof(struct sctp_sndrcvinfo)))
1402			print_sctp_sndrcvinfo(fp, receive,
1403			    (struct sctp_sndrcvinfo *)data);
1404		else
1405			print_gen_cmsg(fp, cmsghdr);
1406		break;
1407#if 0
1408	case SCTP_EXTRCV:
1409		if (len == CMSG_LEN(sizeof(struct sctp_extrcvinfo)))
1410			print_sctp_extrcvinfo(fp,
1411			    (struct sctp_extrcvinfo *)data);
1412		else
1413			print_gen_cmsg(fp, cmsghdr);
1414		break;
1415#endif
1416	case SCTP_SNDINFO:
1417		if (len == CMSG_LEN(sizeof(struct sctp_sndinfo)))
1418			print_sctp_sndinfo(fp, (struct sctp_sndinfo *)data);
1419		else
1420			print_gen_cmsg(fp, cmsghdr);
1421		break;
1422	case SCTP_RCVINFO:
1423		if (len == CMSG_LEN(sizeof(struct sctp_rcvinfo)))
1424			print_sctp_rcvinfo(fp, (struct sctp_rcvinfo *)data);
1425		else
1426			print_gen_cmsg(fp, cmsghdr);
1427		break;
1428	case SCTP_NXTINFO:
1429		if (len == CMSG_LEN(sizeof(struct sctp_nxtinfo)))
1430			print_sctp_nxtinfo(fp, (struct sctp_nxtinfo *)data);
1431		else
1432			print_gen_cmsg(fp, cmsghdr);
1433		break;
1434	case SCTP_PRINFO:
1435		if (len == CMSG_LEN(sizeof(struct sctp_prinfo)))
1436			print_sctp_prinfo(fp, (struct sctp_prinfo *)data);
1437		else
1438			print_gen_cmsg(fp, cmsghdr);
1439		break;
1440	case SCTP_AUTHINFO:
1441		if (len == CMSG_LEN(sizeof(struct sctp_authinfo)))
1442			print_sctp_authinfo(fp, (struct sctp_authinfo *)data);
1443		else
1444			print_gen_cmsg(fp, cmsghdr);
1445		break;
1446	case SCTP_DSTADDRV4:
1447		if (len == CMSG_LEN(sizeof(struct in_addr)))
1448			print_sctp_ipv4_addr(fp, (struct in_addr *)data);
1449		else
1450			print_gen_cmsg(fp, cmsghdr);
1451		break;
1452	case SCTP_DSTADDRV6:
1453		if (len == CMSG_LEN(sizeof(struct in6_addr)))
1454			print_sctp_ipv6_addr(fp, (struct in6_addr *)data);
1455		else
1456			print_gen_cmsg(fp, cmsghdr);
1457		break;
1458	default:
1459		print_gen_cmsg(fp, cmsghdr);
1460	}
1461}
1462
1463static void
1464print_cmsgs(FILE *fp, pid_t pid, bool receive, struct msghdr *msghdr)
1465{
1466	struct cmsghdr *cmsghdr;
1467	char *cmsgbuf;
1468	const char *temp;
1469	socklen_t len;
1470	int level, type;
1471	bool first;
1472
1473	len = msghdr->msg_controllen;
1474	if (len == 0) {
1475		fputs("{}", fp);
1476		return;
1477	}
1478	cmsgbuf = calloc(1, len);
1479	if (get_struct(pid, (uintptr_t)msghdr->msg_control, cmsgbuf, len) == -1) {
1480		print_pointer(fp, (uintptr_t)msghdr->msg_control);
1481		free(cmsgbuf);
1482		return;
1483	}
1484	msghdr->msg_control = cmsgbuf;
1485	first = true;
1486	fputs("{", fp);
1487	for (cmsghdr = CMSG_FIRSTHDR(msghdr);
1488	   cmsghdr != NULL;
1489	   cmsghdr = CMSG_NXTHDR(msghdr, cmsghdr)) {
1490		if (cmsghdr->cmsg_len < sizeof(*cmsghdr)) {
1491			fprintf(fp, "{<invalid cmsg, len=%u>}",
1492			    cmsghdr->cmsg_len);
1493			if (cmsghdr->cmsg_len == 0) {
1494				/* Avoid looping forever. */
1495				break;
1496			}
1497			continue;
1498		}
1499
1500		level = cmsghdr->cmsg_level;
1501		type = cmsghdr->cmsg_type;
1502		len = cmsghdr->cmsg_len;
1503		fprintf(fp, "%s{level=", first ? "" : ",");
1504		print_integer_arg(sysdecode_sockopt_level, fp, level);
1505		fputs(",type=", fp);
1506		temp = sysdecode_cmsg_type(level, type);
1507		if (temp) {
1508			fputs(temp, fp);
1509		} else {
1510			fprintf(fp, "%d", type);
1511		}
1512		fputs(",data=", fp);
1513		switch (level) {
1514		case IPPROTO_SCTP:
1515			print_sctp_cmsg(fp, receive, cmsghdr);
1516			break;
1517		default:
1518			print_gen_cmsg(fp, cmsghdr);
1519			break;
1520		}
1521		fputs("}", fp);
1522		first = false;
1523	}
1524	fputs("}", fp);
1525	free(cmsgbuf);
1526}
1527
1528static void
1529print_sysctl_oid(FILE *fp, int *oid, size_t len)
1530{
1531	size_t i;
1532	bool first;
1533
1534	first = true;
1535	fprintf(fp, "{ ");
1536	for (i = 0; i < len; i++) {
1537		fprintf(fp, "%s%d", first ? "" : ".", oid[i]);
1538		first = false;
1539	}
1540	fprintf(fp, " }");
1541}
1542
1543static void
1544print_sysctl(FILE *fp, int *oid, size_t len)
1545{
1546	char name[BUFSIZ];
1547	int qoid[CTL_MAXNAME + 2];
1548	size_t i;
1549
1550	qoid[0] = CTL_SYSCTL;
1551	qoid[1] = CTL_SYSCTL_NAME;
1552	memcpy(qoid + 2, oid, len * sizeof(int));
1553	i = sizeof(name);
1554	if (sysctl(qoid, len + 2, name, &i, 0, 0) == -1)
1555		print_sysctl_oid(fp, oid, len);
1556	else
1557		fprintf(fp, "%s", name);
1558}
1559
1560/*
1561 * Convert a 32-bit user-space pointer to psaddr_t by zero-extending.
1562 */
1563static psaddr_t
1564user_ptr32_to_psaddr(int32_t user_pointer)
1565{
1566	return ((psaddr_t)(uintptr_t)user_pointer);
1567}
1568
1569/*
1570 * Converts a syscall argument into a string.  Said string is
1571 * allocated via malloc(), so needs to be free()'d.  sc is
1572 * a pointer to the syscall description (see above); args is
1573 * an array of all of the system call arguments.
1574 */
1575char *
1576print_arg(struct syscall_arg *sc, syscallarg_t *args, syscallarg_t *retval,
1577    struct trussinfo *trussinfo)
1578{
1579	FILE *fp;
1580	char *tmp;
1581	size_t tmplen;
1582	pid_t pid;
1583
1584	fp = open_memstream(&tmp, &tmplen);
1585	pid = trussinfo->curthread->proc->pid;
1586	switch (sc->type & ARG_MASK) {
1587	case Hex:
1588		fprintf(fp, "0x%x", (int)args[sc->offset]);
1589		break;
1590	case Octal:
1591		fprintf(fp, "0%o", (int)args[sc->offset]);
1592		break;
1593	case Int:
1594		fprintf(fp, "%d", (int)args[sc->offset]);
1595		break;
1596	case UInt:
1597		fprintf(fp, "%u", (unsigned int)args[sc->offset]);
1598		break;
1599	case PUInt: {
1600		unsigned int val;
1601
1602		if (get_struct(pid, args[sc->offset], &val,
1603		    sizeof(val)) == 0)
1604			fprintf(fp, "{ %u }", val);
1605		else
1606			print_pointer(fp, args[sc->offset]);
1607		break;
1608	}
1609	case LongHex:
1610		fprintf(fp, "0x%lx", (long)args[sc->offset]);
1611		break;
1612	case Long:
1613		fprintf(fp, "%ld", (long)args[sc->offset]);
1614		break;
1615	case Sizet:
1616		fprintf(fp, "%zu", (size_t)args[sc->offset]);
1617		break;
1618	case ShmName:
1619		/* Handle special SHM_ANON value. */
1620		if ((char *)(uintptr_t)args[sc->offset] == SHM_ANON) {
1621			fprintf(fp, "SHM_ANON");
1622			break;
1623		}
1624		/* FALLTHROUGH */
1625	case Name: {
1626		/* NULL-terminated string. */
1627		char *tmp2;
1628
1629		tmp2 = get_string(pid, args[sc->offset], 0);
1630		fprintf(fp, "\"%s\"", tmp2);
1631		free(tmp2);
1632		break;
1633	}
1634	case BinString: {
1635		/*
1636		 * Binary block of data that might have printable characters.
1637		 * XXX If type|OUT, assume that the length is the syscall's
1638		 * return value.  Otherwise, assume that the length of the block
1639		 * is in the next syscall argument.
1640		 */
1641		int max_string = trussinfo->strsize;
1642		char tmp2[max_string + 1], *tmp3;
1643		int len;
1644		int truncated = 0;
1645
1646		if (sc->type & OUT)
1647			len = retval[0];
1648		else
1649			len = args[sc->offset + 1];
1650
1651		/*
1652		 * Don't print more than max_string characters, to avoid word
1653		 * wrap.  If we have to truncate put some ... after the string.
1654		 */
1655		if (len > max_string) {
1656			len = max_string;
1657			truncated = 1;
1658		}
1659		if (len && get_struct(pid, args[sc->offset], &tmp2, len)
1660		    != -1) {
1661			tmp3 = malloc(len * 4 + 1);
1662			while (len) {
1663				if (strvisx(tmp3, tmp2, len,
1664				    VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string)
1665					break;
1666				len--;
1667				truncated = 1;
1668			}
1669			fprintf(fp, "\"%s\"%s", tmp3, truncated ?
1670			    "..." : "");
1671			free(tmp3);
1672		} else {
1673			print_pointer(fp, args[sc->offset]);
1674		}
1675		break;
1676	}
1677	case ExecArgs:
1678	case ExecEnv:
1679	case StringArray: {
1680		psaddr_t addr;
1681		union {
1682			int32_t strarray32[PAGE_SIZE / sizeof(int32_t)];
1683			int64_t strarray64[PAGE_SIZE / sizeof(int64_t)];
1684			char buf[PAGE_SIZE];
1685		} u;
1686		char *string;
1687		size_t len;
1688		u_int first, i;
1689		size_t pointer_size =
1690		    trussinfo->curthread->proc->abi->pointer_size;
1691
1692		/*
1693		 * Only parse argv[] and environment arrays from exec calls
1694		 * if requested.
1695		 */
1696		if (((sc->type & ARG_MASK) == ExecArgs &&
1697		    (trussinfo->flags & EXECVEARGS) == 0) ||
1698		    ((sc->type & ARG_MASK) == ExecEnv &&
1699		    (trussinfo->flags & EXECVEENVS) == 0)) {
1700			print_pointer(fp, args[sc->offset]);
1701			break;
1702		}
1703
1704		/*
1705		 * Read a page of pointers at a time.  Punt if the top-level
1706		 * pointer is not aligned.  Note that the first read is of
1707		 * a partial page.
1708		 */
1709		addr = args[sc->offset];
1710		if (!__is_aligned(addr, pointer_size)) {
1711			print_pointer(fp, args[sc->offset]);
1712			break;
1713		}
1714
1715		len = PAGE_SIZE - (addr & PAGE_MASK);
1716		if (get_struct(pid, addr, u.buf, len) == -1) {
1717			print_pointer(fp, args[sc->offset]);
1718			break;
1719		}
1720		assert(len > 0);
1721
1722		fputc('[', fp);
1723		first = 1;
1724		i = 0;
1725		for (;;) {
1726			psaddr_t straddr;
1727			if (pointer_size == 4) {
1728				straddr = user_ptr32_to_psaddr(u.strarray32[i]);
1729			} else if (pointer_size == 8) {
1730				straddr = (psaddr_t)u.strarray64[i];
1731			} else {
1732				errx(1, "Unsupported pointer size: %zu",
1733				    pointer_size);
1734			}
1735
1736			/* Stop once we read the first NULL pointer. */
1737			if (straddr == 0)
1738				break;
1739			string = get_string(pid, straddr, 0);
1740			fprintf(fp, "%s \"%s\"", first ? "" : ",", string);
1741			free(string);
1742			first = 0;
1743
1744			i++;
1745			if (i == len / pointer_size) {
1746				addr += len;
1747				len = PAGE_SIZE;
1748				if (get_struct(pid, addr, u.buf, len) == -1) {
1749					fprintf(fp, ", <inval>");
1750					break;
1751				}
1752				i = 0;
1753			}
1754		}
1755		fputs(" ]", fp);
1756		break;
1757	}
1758	case Quad:
1759	case QuadHex: {
1760		uint64_t value;
1761		size_t pointer_size =
1762		    trussinfo->curthread->proc->abi->pointer_size;
1763
1764		if (pointer_size == 4) {
1765#if _BYTE_ORDER == _LITTLE_ENDIAN
1766			value = (uint64_t)args[sc->offset + 1] << 32 |
1767			    args[sc->offset];
1768#else
1769			value = (uint64_t)args[sc->offset] << 32 |
1770			    args[sc->offset + 1];
1771#endif
1772		} else {
1773			value = (uint64_t)args[sc->offset];
1774		}
1775		if ((sc->type & ARG_MASK) == Quad)
1776			fprintf(fp, "%jd", (intmax_t)value);
1777		else
1778			fprintf(fp, "0x%jx", (intmax_t)value);
1779		break;
1780	}
1781	case PQuadHex: {
1782		uint64_t val;
1783
1784		if (get_struct(pid, args[sc->offset], &val,
1785		    sizeof(val)) == 0)
1786			fprintf(fp, "{ 0x%jx }", (uintmax_t)val);
1787		else
1788			print_pointer(fp, args[sc->offset]);
1789		break;
1790	}
1791	case Ptr:
1792		print_pointer(fp, args[sc->offset]);
1793		break;
1794	case Readlinkres: {
1795		char *tmp2;
1796
1797		if (retval[0] == -1)
1798			break;
1799		tmp2 = get_string(pid, args[sc->offset], retval[0]);
1800		fprintf(fp, "\"%s\"", tmp2);
1801		free(tmp2);
1802		break;
1803	}
1804	case Ioctl: {
1805		const char *temp;
1806		unsigned long cmd;
1807
1808		cmd = args[sc->offset];
1809		temp = sysdecode_ioctlname(cmd);
1810		if (temp)
1811			fputs(temp, fp);
1812		else {
1813			fprintf(fp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }",
1814			    cmd, cmd & IOC_OUT ? "R" : "",
1815			    cmd & IOC_IN ? "W" : "", IOCGROUP(cmd),
1816			    isprint(IOCGROUP(cmd)) ? (char)IOCGROUP(cmd) : '?',
1817			    cmd & 0xFF, IOCPARM_LEN(cmd));
1818		}
1819		break;
1820	}
1821	case Timespec: {
1822		struct timespec ts;
1823
1824		if (get_struct(pid, args[sc->offset], &ts, sizeof(ts)) != -1)
1825			fprintf(fp, "{ %jd.%09ld }", (intmax_t)ts.tv_sec,
1826			    ts.tv_nsec);
1827		else
1828			print_pointer(fp, args[sc->offset]);
1829		break;
1830	}
1831	case Timespec2: {
1832		struct timespec ts[2];
1833		const char *sep;
1834		unsigned int i;
1835
1836		if (get_struct(pid, args[sc->offset], &ts, sizeof(ts)) != -1) {
1837			fputs("{ ", fp);
1838			sep = "";
1839			for (i = 0; i < nitems(ts); i++) {
1840				fputs(sep, fp);
1841				sep = ", ";
1842				switch (ts[i].tv_nsec) {
1843				case UTIME_NOW:
1844					fprintf(fp, "UTIME_NOW");
1845					break;
1846				case UTIME_OMIT:
1847					fprintf(fp, "UTIME_OMIT");
1848					break;
1849				default:
1850					fprintf(fp, "%jd.%09ld",
1851					    (intmax_t)ts[i].tv_sec,
1852					    ts[i].tv_nsec);
1853					break;
1854				}
1855			}
1856			fputs(" }", fp);
1857		} else
1858			print_pointer(fp, args[sc->offset]);
1859		break;
1860	}
1861	case Timeval: {
1862		struct timeval tv;
1863
1864		if (get_struct(pid, args[sc->offset], &tv, sizeof(tv)) != -1)
1865			fprintf(fp, "{ %jd.%06ld }", (intmax_t)tv.tv_sec,
1866			    tv.tv_usec);
1867		else
1868			print_pointer(fp, args[sc->offset]);
1869		break;
1870	}
1871	case Timeval2: {
1872		struct timeval tv[2];
1873
1874		if (get_struct(pid, args[sc->offset], &tv, sizeof(tv)) != -1)
1875			fprintf(fp, "{ %jd.%06ld, %jd.%06ld }",
1876			    (intmax_t)tv[0].tv_sec, tv[0].tv_usec,
1877			    (intmax_t)tv[1].tv_sec, tv[1].tv_usec);
1878		else
1879			print_pointer(fp, args[sc->offset]);
1880		break;
1881	}
1882	case Itimerval: {
1883		struct itimerval itv;
1884
1885		if (get_struct(pid, args[sc->offset], &itv, sizeof(itv)) != -1)
1886			fprintf(fp, "{ %jd.%06ld, %jd.%06ld }",
1887			    (intmax_t)itv.it_interval.tv_sec,
1888			    itv.it_interval.tv_usec,
1889			    (intmax_t)itv.it_value.tv_sec,
1890			    itv.it_value.tv_usec);
1891		else
1892			print_pointer(fp, args[sc->offset]);
1893		break;
1894	}
1895	case LinuxSockArgs:
1896	{
1897		struct linux_socketcall_args largs;
1898
1899		if (get_struct(pid, args[sc->offset], (void *)&largs,
1900		    sizeof(largs)) != -1)
1901			fprintf(fp, "{ %s, 0x%lx }",
1902			    lookup(linux_socketcall_ops, largs.what, 10),
1903			    (long unsigned int)largs.args);
1904		else
1905			print_pointer(fp, args[sc->offset]);
1906		break;
1907	}
1908	case Pollfd: {
1909		/*
1910		 * XXX: A Pollfd argument expects the /next/ syscall argument
1911		 * to be the number of fds in the array. This matches the poll
1912		 * syscall.
1913		 */
1914		struct pollfd *pfd;
1915		int numfds = args[sc->offset + 1];
1916		size_t bytes = sizeof(struct pollfd) * numfds;
1917		int i;
1918
1919		if ((pfd = malloc(bytes)) == NULL)
1920			err(1, "Cannot malloc %zu bytes for pollfd array",
1921			    bytes);
1922		if (get_struct(pid, args[sc->offset], pfd, bytes) != -1) {
1923			fputs("{", fp);
1924			for (i = 0; i < numfds; i++) {
1925				fprintf(fp, " %d/%s", pfd[i].fd,
1926				    xlookup_bits(poll_flags, pfd[i].events));
1927			}
1928			fputs(" }", fp);
1929		} else {
1930			print_pointer(fp, args[sc->offset]);
1931		}
1932		free(pfd);
1933		break;
1934	}
1935	case Fd_set: {
1936		/*
1937		 * XXX: A Fd_set argument expects the /first/ syscall argument
1938		 * to be the number of fds in the array.  This matches the
1939		 * select syscall.
1940		 */
1941		fd_set *fds;
1942		int numfds = args[0];
1943		size_t bytes = _howmany(numfds, _NFDBITS) * _NFDBITS;
1944		int i;
1945
1946		if ((fds = malloc(bytes)) == NULL)
1947			err(1, "Cannot malloc %zu bytes for fd_set array",
1948			    bytes);
1949		if (get_struct(pid, args[sc->offset], fds, bytes) != -1) {
1950			fputs("{", fp);
1951			for (i = 0; i < numfds; i++) {
1952				if (FD_ISSET(i, fds))
1953					fprintf(fp, " %d", i);
1954			}
1955			fputs(" }", fp);
1956		} else
1957			print_pointer(fp, args[sc->offset]);
1958		free(fds);
1959		break;
1960	}
1961	case Signal:
1962		fputs(strsig2(args[sc->offset]), fp);
1963		break;
1964	case Sigset: {
1965		sigset_t ss;
1966		int i, first;
1967
1968		if (get_struct(pid, args[sc->offset], (void *)&ss,
1969		    sizeof(ss)) == -1) {
1970			print_pointer(fp, args[sc->offset]);
1971			break;
1972		}
1973		fputs("{ ", fp);
1974		first = 1;
1975		for (i = 1; i < sys_nsig; i++) {
1976			if (sigismember(&ss, i)) {
1977				fprintf(fp, "%s%s", !first ? "|" : "",
1978				    strsig2(i));
1979				first = 0;
1980			}
1981		}
1982		if (!first)
1983			fputc(' ', fp);
1984		fputc('}', fp);
1985		break;
1986	}
1987	case Sigprocmask:
1988		print_integer_arg(sysdecode_sigprocmask_how, fp,
1989		    args[sc->offset]);
1990		break;
1991	case Fcntlflag:
1992		/* XXX: Output depends on the value of the previous argument. */
1993		if (sysdecode_fcntl_arg_p(args[sc->offset - 1]))
1994			sysdecode_fcntl_arg(fp, args[sc->offset - 1],
1995			    args[sc->offset], 16);
1996		break;
1997	case Open:
1998		print_mask_arg(sysdecode_open_flags, fp, args[sc->offset]);
1999		break;
2000	case Fcntl:
2001		print_integer_arg(sysdecode_fcntl_cmd, fp, args[sc->offset]);
2002		break;
2003	case Closerangeflags:
2004		print_mask_arg(sysdecode_close_range_flags, fp, args[sc->offset]);
2005		break;
2006	case Mprot:
2007		print_mask_arg(sysdecode_mmap_prot, fp, args[sc->offset]);
2008		break;
2009	case Mmapflags:
2010		print_mask_arg(sysdecode_mmap_flags, fp, args[sc->offset]);
2011		break;
2012	case Whence:
2013		print_integer_arg(sysdecode_whence, fp, args[sc->offset]);
2014		break;
2015	case ShmFlags:
2016		print_mask_arg(sysdecode_shmflags, fp, args[sc->offset]);
2017		break;
2018	case Sockdomain:
2019		print_integer_arg(sysdecode_socketdomain, fp, args[sc->offset]);
2020		break;
2021	case Socktype:
2022		print_mask_arg(sysdecode_socket_type, fp, args[sc->offset]);
2023		break;
2024	case Shutdown:
2025		print_integer_arg(sysdecode_shutdown_how, fp, args[sc->offset]);
2026		break;
2027	case Resource:
2028		print_integer_arg(sysdecode_rlimit, fp, args[sc->offset]);
2029		break;
2030	case RusageWho:
2031		print_integer_arg(sysdecode_getrusage_who, fp, args[sc->offset]);
2032		break;
2033	case Pathconf:
2034		print_integer_arg(sysdecode_pathconf_name, fp, args[sc->offset]);
2035		break;
2036	case Rforkflags:
2037		print_mask_arg(sysdecode_rfork_flags, fp, args[sc->offset]);
2038		break;
2039	case Sockaddr: {
2040		socklen_t len;
2041
2042		if (args[sc->offset] == 0) {
2043			fputs("NULL", fp);
2044			break;
2045		}
2046
2047		/*
2048		 * Extract the address length from the next argument.  If
2049		 * this is an output sockaddr (OUT is set), then the
2050		 * next argument is a pointer to a socklen_t.  Otherwise
2051		 * the next argument contains a socklen_t by value.
2052		 */
2053		if (sc->type & OUT) {
2054			if (get_struct(pid, args[sc->offset + 1], &len,
2055			    sizeof(len)) == -1) {
2056				print_pointer(fp, args[sc->offset]);
2057				break;
2058			}
2059		} else
2060			len = args[sc->offset + 1];
2061
2062		print_sockaddr(fp, trussinfo, args[sc->offset], len);
2063		break;
2064	}
2065	case Sigaction: {
2066		struct sigaction sa;
2067
2068		if (get_struct(pid, args[sc->offset], &sa, sizeof(sa)) != -1) {
2069			fputs("{ ", fp);
2070			if (sa.sa_handler == SIG_DFL)
2071				fputs("SIG_DFL", fp);
2072			else if (sa.sa_handler == SIG_IGN)
2073				fputs("SIG_IGN", fp);
2074			else
2075				fprintf(fp, "%p", sa.sa_handler);
2076			fprintf(fp, " %s ss_t }",
2077			    xlookup_bits(sigaction_flags, sa.sa_flags));
2078		} else
2079			print_pointer(fp, args[sc->offset]);
2080		break;
2081	}
2082	case Sigevent: {
2083		struct sigevent se;
2084
2085		if (get_struct(pid, args[sc->offset], &se, sizeof(se)) != -1)
2086			print_sigevent(fp, &se);
2087		else
2088			print_pointer(fp, args[sc->offset]);
2089		break;
2090	}
2091	case Kevent: {
2092		/*
2093		 * XXX XXX: The size of the array is determined by either the
2094		 * next syscall argument, or by the syscall return value,
2095		 * depending on which argument number we are.  This matches the
2096		 * kevent syscall, but luckily that's the only syscall that uses
2097		 * them.
2098		 */
2099		struct kevent *ke;
2100		int numevents = -1;
2101		size_t bytes;
2102		int i;
2103
2104		if (sc->offset == 1)
2105			numevents = args[sc->offset+1];
2106		else if (sc->offset == 3 && retval[0] != -1)
2107			numevents = retval[0];
2108
2109		if (numevents >= 0) {
2110			bytes = sizeof(struct kevent) * numevents;
2111			if ((ke = malloc(bytes)) == NULL)
2112				err(1,
2113				    "Cannot malloc %zu bytes for kevent array",
2114				    bytes);
2115		} else
2116			ke = NULL;
2117		if (numevents >= 0 && get_struct(pid, args[sc->offset],
2118		    ke, bytes) != -1) {
2119			fputc('{', fp);
2120			for (i = 0; i < numevents; i++) {
2121				fputc(' ', fp);
2122				print_kevent(fp, &ke[i]);
2123			}
2124			fputs(" }", fp);
2125		} else {
2126			print_pointer(fp, args[sc->offset]);
2127		}
2128		free(ke);
2129		break;
2130	}
2131	case Kevent11: {
2132		struct freebsd11_kevent *ke11;
2133		struct kevent ke;
2134		int numevents = -1;
2135		size_t bytes;
2136		int i;
2137
2138		if (sc->offset == 1)
2139			numevents = args[sc->offset+1];
2140		else if (sc->offset == 3 && retval[0] != -1)
2141			numevents = retval[0];
2142
2143		if (numevents >= 0) {
2144			bytes = sizeof(struct freebsd11_kevent) * numevents;
2145			if ((ke11 = malloc(bytes)) == NULL)
2146				err(1,
2147				    "Cannot malloc %zu bytes for kevent array",
2148				    bytes);
2149		} else
2150			ke11 = NULL;
2151		memset(&ke, 0, sizeof(ke));
2152		if (numevents >= 0 && get_struct(pid, args[sc->offset],
2153		    ke11, bytes) != -1) {
2154			fputc('{', fp);
2155			for (i = 0; i < numevents; i++) {
2156				fputc(' ', fp);
2157				ke.ident = ke11[i].ident;
2158				ke.filter = ke11[i].filter;
2159				ke.flags = ke11[i].flags;
2160				ke.fflags = ke11[i].fflags;
2161				ke.data = ke11[i].data;
2162				ke.udata = ke11[i].udata;
2163				print_kevent(fp, &ke);
2164			}
2165			fputs(" }", fp);
2166		} else {
2167			print_pointer(fp, args[sc->offset]);
2168		}
2169		free(ke11);
2170		break;
2171	}
2172	case Stat: {
2173		struct stat st;
2174
2175		if (get_struct(pid, args[sc->offset], &st, sizeof(st))
2176		    != -1) {
2177			char mode[12];
2178
2179			strmode(st.st_mode, mode);
2180			fprintf(fp,
2181			    "{ mode=%s,inode=%ju,size=%jd,blksize=%ld }", mode,
2182			    (uintmax_t)st.st_ino, (intmax_t)st.st_size,
2183			    (long)st.st_blksize);
2184		} else {
2185			print_pointer(fp, args[sc->offset]);
2186		}
2187		break;
2188	}
2189	case Stat11: {
2190		struct freebsd11_stat st;
2191
2192		if (get_struct(pid, args[sc->offset], &st, sizeof(st))
2193		    != -1) {
2194			char mode[12];
2195
2196			strmode(st.st_mode, mode);
2197			fprintf(fp,
2198			    "{ mode=%s,inode=%ju,size=%jd,blksize=%ld }", mode,
2199			    (uintmax_t)st.st_ino, (intmax_t)st.st_size,
2200			    (long)st.st_blksize);
2201		} else {
2202			print_pointer(fp, args[sc->offset]);
2203		}
2204		break;
2205	}
2206	case StatFs: {
2207		unsigned int i;
2208		struct statfs buf;
2209
2210		if (get_struct(pid, args[sc->offset], &buf,
2211		    sizeof(buf)) != -1) {
2212			char fsid[17];
2213
2214			bzero(fsid, sizeof(fsid));
2215			if (buf.f_fsid.val[0] != 0 || buf.f_fsid.val[1] != 0) {
2216			        for (i = 0; i < sizeof(buf.f_fsid); i++)
2217					snprintf(&fsid[i*2],
2218					    sizeof(fsid) - (i*2), "%02x",
2219					    ((u_char *)&buf.f_fsid)[i]);
2220			}
2221			fprintf(fp,
2222			    "{ fstypename=%s,mntonname=%s,mntfromname=%s,"
2223			    "fsid=%s }", buf.f_fstypename, buf.f_mntonname,
2224			    buf.f_mntfromname, fsid);
2225		} else
2226			print_pointer(fp, args[sc->offset]);
2227		break;
2228	}
2229
2230	case Rusage: {
2231		struct rusage ru;
2232
2233		if (get_struct(pid, args[sc->offset], &ru, sizeof(ru))
2234		    != -1) {
2235			fprintf(fp,
2236			    "{ u=%jd.%06ld,s=%jd.%06ld,in=%ld,out=%ld }",
2237			    (intmax_t)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
2238			    (intmax_t)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
2239			    ru.ru_inblock, ru.ru_oublock);
2240		} else
2241			print_pointer(fp, args[sc->offset]);
2242		break;
2243	}
2244	case Rlimit: {
2245		struct rlimit rl;
2246
2247		if (get_struct(pid, args[sc->offset], &rl, sizeof(rl))
2248		    != -1) {
2249			fprintf(fp, "{ cur=%ju,max=%ju }",
2250			    rl.rlim_cur, rl.rlim_max);
2251		} else
2252			print_pointer(fp, args[sc->offset]);
2253		break;
2254	}
2255	case ExitStatus: {
2256		int status;
2257
2258		if (get_struct(pid, args[sc->offset], &status,
2259		    sizeof(status)) != -1) {
2260			fputs("{ ", fp);
2261			if (WIFCONTINUED(status))
2262				fputs("CONTINUED", fp);
2263			else if (WIFEXITED(status))
2264				fprintf(fp, "EXITED,val=%d",
2265				    WEXITSTATUS(status));
2266			else if (WIFSIGNALED(status))
2267				fprintf(fp, "SIGNALED,sig=%s%s",
2268				    strsig2(WTERMSIG(status)),
2269				    WCOREDUMP(status) ? ",cored" : "");
2270			else
2271				fprintf(fp, "STOPPED,sig=%s",
2272				    strsig2(WTERMSIG(status)));
2273			fputs(" }", fp);
2274		} else
2275			print_pointer(fp, args[sc->offset]);
2276		break;
2277	}
2278	case Waitoptions:
2279		print_mask_arg(sysdecode_wait6_options, fp, args[sc->offset]);
2280		break;
2281	case Idtype:
2282		print_integer_arg(sysdecode_idtype, fp, args[sc->offset]);
2283		break;
2284	case Procctl:
2285		print_integer_arg(sysdecode_procctl_cmd, fp, args[sc->offset]);
2286		break;
2287	case Umtxop: {
2288		int rem;
2289
2290		if (print_mask_arg_part(sysdecode_umtx_op_flags, fp,
2291		    args[sc->offset], &rem))
2292			fprintf(fp, "|");
2293		print_integer_arg(sysdecode_umtx_op, fp, rem);
2294		break;
2295	}
2296	case Atfd:
2297		print_integer_arg(sysdecode_atfd, fp, args[sc->offset]);
2298		break;
2299	case Atflags:
2300		print_mask_arg(sysdecode_atflags, fp, args[sc->offset]);
2301		break;
2302	case Accessmode:
2303		print_mask_arg(sysdecode_access_mode, fp, args[sc->offset]);
2304		break;
2305	case Sysarch:
2306		print_integer_arg(sysdecode_sysarch_number, fp,
2307		    args[sc->offset]);
2308		break;
2309	case Sysctl: {
2310		char name[BUFSIZ];
2311		int oid[CTL_MAXNAME + 2];
2312		size_t len;
2313
2314		memset(name, 0, sizeof(name));
2315		len = args[sc->offset + 1];
2316		if (get_struct(pid, args[sc->offset], oid,
2317		    len * sizeof(oid[0])) != -1) {
2318		    	fprintf(fp, "\"");
2319			if (oid[0] == CTL_SYSCTL) {
2320				fprintf(fp, "sysctl.");
2321				switch (oid[1]) {
2322				case CTL_SYSCTL_DEBUG:
2323					fprintf(fp, "debug");
2324					break;
2325				case CTL_SYSCTL_NAME:
2326					fprintf(fp, "name ");
2327					print_sysctl_oid(fp, oid + 2, len - 2);
2328					break;
2329				case CTL_SYSCTL_NEXT:
2330					fprintf(fp, "next");
2331					break;
2332				case CTL_SYSCTL_NAME2OID:
2333					fprintf(fp, "name2oid %s",
2334					    get_string(pid,
2335					        args[sc->offset + 4],
2336						args[sc->offset + 5]));
2337					break;
2338				case CTL_SYSCTL_OIDFMT:
2339					fprintf(fp, "oidfmt ");
2340					print_sysctl(fp, oid + 2, len - 2);
2341					break;
2342				case CTL_SYSCTL_OIDDESCR:
2343					fprintf(fp, "oiddescr ");
2344					print_sysctl(fp, oid + 2, len - 2);
2345					break;
2346				case CTL_SYSCTL_OIDLABEL:
2347					fprintf(fp, "oidlabel ");
2348					print_sysctl(fp, oid + 2, len - 2);
2349					break;
2350				case CTL_SYSCTL_NEXTNOSKIP:
2351					fprintf(fp, "nextnoskip");
2352					break;
2353				default:
2354					print_sysctl(fp, oid + 1, len - 1);
2355				}
2356			} else {
2357				print_sysctl(fp, oid, len);
2358			}
2359		    	fprintf(fp, "\"");
2360		}
2361		break;
2362	}
2363	case PipeFds:
2364		/*
2365		 * The pipe() system call in the kernel returns its
2366		 * two file descriptors via return values.  However,
2367		 * the interface exposed by libc is that pipe()
2368		 * accepts a pointer to an array of descriptors.
2369		 * Format the output to match the libc API by printing
2370		 * the returned file descriptors as a fake argument.
2371		 *
2372		 * Overwrite the first retval to signal a successful
2373		 * return as well.
2374		 */
2375		fprintf(fp, "{ %d, %d }", (int)retval[0], (int)retval[1]);
2376		retval[0] = 0;
2377		break;
2378	case Utrace: {
2379		size_t len;
2380		void *utrace_addr;
2381
2382		len = args[sc->offset + 1];
2383		utrace_addr = calloc(1, len);
2384		if (get_struct(pid, args[sc->offset],
2385		    (void *)utrace_addr, len) != -1)
2386			print_utrace(fp, utrace_addr, len);
2387		else
2388			print_pointer(fp, args[sc->offset]);
2389		free(utrace_addr);
2390		break;
2391	}
2392	case IntArray: {
2393		int descriptors[16];
2394		unsigned long i, ndescriptors;
2395		bool truncated;
2396
2397		ndescriptors = args[sc->offset + 1];
2398		truncated = false;
2399		if (ndescriptors > nitems(descriptors)) {
2400			ndescriptors = nitems(descriptors);
2401			truncated = true;
2402		}
2403		if (get_struct(pid, args[sc->offset],
2404		    descriptors, ndescriptors * sizeof(descriptors[0])) != -1) {
2405			fprintf(fp, "{");
2406			for (i = 0; i < ndescriptors; i++)
2407				fprintf(fp, i == 0 ? " %d" : ", %d",
2408				    descriptors[i]);
2409			fprintf(fp, truncated ? ", ... }" : " }");
2410		} else
2411			print_pointer(fp, args[sc->offset]);
2412		break;
2413	}
2414	case Pipe2:
2415		print_mask_arg(sysdecode_pipe2_flags, fp, args[sc->offset]);
2416		break;
2417	case CapFcntlRights: {
2418		uint32_t rights;
2419
2420		if (sc->type & OUT) {
2421			if (get_struct(pid, args[sc->offset], &rights,
2422			    sizeof(rights)) == -1) {
2423				print_pointer(fp, args[sc->offset]);
2424				break;
2425			}
2426		} else
2427			rights = args[sc->offset];
2428		print_mask_arg32(sysdecode_cap_fcntlrights, fp, rights);
2429		break;
2430	}
2431	case Fadvice:
2432		print_integer_arg(sysdecode_fadvice, fp, args[sc->offset]);
2433		break;
2434	case FileFlags: {
2435		fflags_t rem;
2436
2437		if (!sysdecode_fileflags(fp, args[sc->offset], &rem))
2438			fprintf(fp, "0x%x", rem);
2439		else if (rem != 0)
2440			fprintf(fp, "|0x%x", rem);
2441		break;
2442	}
2443	case Flockop:
2444		print_mask_arg(sysdecode_flock_operation, fp, args[sc->offset]);
2445		break;
2446	case Getfsstatmode:
2447		print_integer_arg(sysdecode_getfsstat_mode, fp,
2448		    args[sc->offset]);
2449		break;
2450	case Itimerwhich:
2451		print_integer_arg(sysdecode_itimer, fp, args[sc->offset]);
2452		break;
2453	case Kldsymcmd:
2454		print_integer_arg(sysdecode_kldsym_cmd, fp, args[sc->offset]);
2455		break;
2456	case Kldunloadflags:
2457		print_integer_arg(sysdecode_kldunload_flags, fp,
2458		    args[sc->offset]);
2459		break;
2460	case AiofsyncOp:
2461		fputs(xlookup(aio_fsync_ops, args[sc->offset]), fp);
2462		break;
2463	case LioMode:
2464		fputs(xlookup(lio_modes, args[sc->offset]), fp);
2465		break;
2466	case Madvice:
2467		print_integer_arg(sysdecode_madvice, fp, args[sc->offset]);
2468		break;
2469	case Socklent:
2470		fprintf(fp, "%u", (socklen_t)args[sc->offset]);
2471		break;
2472	case Sockprotocol: {
2473		const char *temp;
2474		int domain, protocol;
2475
2476		domain = args[sc->offset - 2];
2477		protocol = args[sc->offset];
2478		if (protocol == 0) {
2479			fputs("0", fp);
2480		} else {
2481			temp = sysdecode_socket_protocol(domain, protocol);
2482			if (temp) {
2483				fputs(temp, fp);
2484			} else {
2485				fprintf(fp, "%d", protocol);
2486			}
2487		}
2488		break;
2489	}
2490	case Sockoptlevel:
2491		print_integer_arg(sysdecode_sockopt_level, fp,
2492		    args[sc->offset]);
2493		break;
2494	case Sockoptname: {
2495		const char *temp;
2496		int level, name;
2497
2498		level = args[sc->offset - 1];
2499		name = args[sc->offset];
2500		temp = sysdecode_sockopt_name(level, name);
2501		if (temp) {
2502			fputs(temp, fp);
2503		} else {
2504			fprintf(fp, "%d", name);
2505		}
2506		break;
2507	}
2508	case Msgflags:
2509		print_mask_arg(sysdecode_msg_flags, fp, args[sc->offset]);
2510		break;
2511	case CapRights: {
2512		cap_rights_t rights;
2513
2514		if (get_struct(pid, args[sc->offset], &rights,
2515		    sizeof(rights)) != -1) {
2516			fputs("{ ", fp);
2517			sysdecode_cap_rights(fp, &rights);
2518			fputs(" }", fp);
2519		} else
2520			print_pointer(fp, args[sc->offset]);
2521		break;
2522	}
2523	case Acltype:
2524		print_integer_arg(sysdecode_acltype, fp, args[sc->offset]);
2525		break;
2526	case Extattrnamespace:
2527		print_integer_arg(sysdecode_extattrnamespace, fp,
2528		    args[sc->offset]);
2529		break;
2530	case Minherit:
2531		print_integer_arg(sysdecode_minherit_inherit, fp,
2532		    args[sc->offset]);
2533		break;
2534	case Mlockall:
2535		print_mask_arg(sysdecode_mlockall_flags, fp, args[sc->offset]);
2536		break;
2537	case Mountflags:
2538		print_mask_arg(sysdecode_mount_flags, fp, args[sc->offset]);
2539		break;
2540	case Msync:
2541		print_mask_arg(sysdecode_msync_flags, fp, args[sc->offset]);
2542		break;
2543	case Priowhich:
2544		print_integer_arg(sysdecode_prio_which, fp, args[sc->offset]);
2545		break;
2546	case Ptraceop:
2547		print_integer_arg(sysdecode_ptrace_request, fp,
2548		    args[sc->offset]);
2549		break;
2550	case Sendfileflags:
2551		print_mask_arg(sysdecode_sendfile_flags, fp, args[sc->offset]);
2552		break;
2553	case Sendfilehdtr: {
2554		struct sf_hdtr hdtr;
2555
2556		if (get_struct(pid, args[sc->offset], &hdtr, sizeof(hdtr)) !=
2557		    -1) {
2558			fprintf(fp, "{");
2559			print_iovec(fp, trussinfo, (uintptr_t)hdtr.headers,
2560			    hdtr.hdr_cnt);
2561			print_iovec(fp, trussinfo, (uintptr_t)hdtr.trailers,
2562			    hdtr.trl_cnt);
2563			fprintf(fp, "}");
2564		} else
2565			print_pointer(fp, args[sc->offset]);
2566		break;
2567	}
2568	case Quotactlcmd:
2569		if (!sysdecode_quotactl_cmd(fp, args[sc->offset]))
2570			fprintf(fp, "%#x", (int)args[sc->offset]);
2571		break;
2572	case Reboothowto:
2573		print_mask_arg(sysdecode_reboot_howto, fp, args[sc->offset]);
2574		break;
2575	case Rtpriofunc:
2576		print_integer_arg(sysdecode_rtprio_function, fp,
2577		    args[sc->offset]);
2578		break;
2579	case Schedpolicy:
2580		print_integer_arg(sysdecode_scheduler_policy, fp,
2581		    args[sc->offset]);
2582		break;
2583	case Schedparam: {
2584		struct sched_param sp;
2585
2586		if (get_struct(pid, args[sc->offset], &sp, sizeof(sp)) != -1)
2587			fprintf(fp, "{ %d }", sp.sched_priority);
2588		else
2589			print_pointer(fp, args[sc->offset]);
2590		break;
2591	}
2592	case PSig: {
2593		int sig;
2594
2595		if (get_struct(pid, args[sc->offset], &sig, sizeof(sig)) == 0)
2596			fprintf(fp, "{ %s }", strsig2(sig));
2597		else
2598			print_pointer(fp, args[sc->offset]);
2599		break;
2600	}
2601	case Siginfo: {
2602		siginfo_t si;
2603
2604		if (get_struct(pid, args[sc->offset], &si, sizeof(si)) != -1) {
2605			fprintf(fp, "{ signo=%s", strsig2(si.si_signo));
2606			decode_siginfo(fp, &si);
2607			fprintf(fp, " }");
2608		} else
2609			print_pointer(fp, args[sc->offset]);
2610		break;
2611	}
2612	case Iovec:
2613		/*
2614		 * Print argument as an array of struct iovec, where the next
2615		 * syscall argument is the number of elements of the array.
2616		 */
2617
2618		print_iovec(fp, trussinfo, args[sc->offset],
2619		    (int)args[sc->offset + 1]);
2620		break;
2621	case Aiocb: {
2622		struct aiocb cb;
2623
2624		if (get_struct(pid, args[sc->offset], &cb, sizeof(cb)) != -1)
2625			print_aiocb(fp, &cb);
2626		else
2627			print_pointer(fp, args[sc->offset]);
2628		break;
2629	}
2630	case AiocbArray: {
2631		/*
2632		 * Print argment as an array of pointers to struct aiocb, where
2633		 * the next syscall argument is the number of elements.
2634		 */
2635		uintptr_t cbs[16];
2636		unsigned int nent;
2637		bool truncated;
2638
2639		nent = args[sc->offset + 1];
2640		truncated = false;
2641		if (nent > nitems(cbs)) {
2642			nent = nitems(cbs);
2643			truncated = true;
2644		}
2645
2646		if (get_struct(pid, args[sc->offset], cbs, sizeof(uintptr_t) * nent) != -1) {
2647			unsigned int i;
2648			fputs("[", fp);
2649			for (i = 0; i < nent; ++i) {
2650				struct aiocb cb;
2651				if (i > 0)
2652					fputc(',', fp);
2653				if (get_struct(pid, cbs[i], &cb, sizeof(cb)) != -1)
2654					print_aiocb(fp, &cb);
2655				else
2656					print_pointer(fp, cbs[i]);
2657			}
2658			if (truncated)
2659				fputs(",...", fp);
2660			fputs("]", fp);
2661		} else
2662			print_pointer(fp, args[sc->offset]);
2663		break;
2664	}
2665	case AiocbPointer: {
2666		/*
2667		 * aio_waitcomplete(2) assigns a pointer to a pointer to struct
2668		 * aiocb, so we need to handle the extra layer of indirection.
2669		 */
2670		uintptr_t cbp;
2671		struct aiocb cb;
2672
2673		if (get_struct(pid, args[sc->offset], &cbp, sizeof(cbp)) != -1) {
2674			if (get_struct(pid, cbp, &cb, sizeof(cb)) != -1)
2675				print_aiocb(fp, &cb);
2676			else
2677				print_pointer(fp, cbp);
2678		} else
2679			print_pointer(fp, args[sc->offset]);
2680		break;
2681	}
2682	case Sctpsndrcvinfo: {
2683		struct sctp_sndrcvinfo info;
2684
2685		if (get_struct(pid, args[sc->offset],
2686		    &info, sizeof(struct sctp_sndrcvinfo)) == -1) {
2687			print_pointer(fp, args[sc->offset]);
2688			break;
2689		}
2690		print_sctp_sndrcvinfo(fp, sc->type & OUT, &info);
2691		break;
2692	}
2693	case Msghdr: {
2694		struct msghdr msghdr;
2695
2696		if (get_struct(pid, args[sc->offset],
2697		    &msghdr, sizeof(struct msghdr)) == -1) {
2698			print_pointer(fp, args[sc->offset]);
2699			break;
2700		}
2701		fputs("{", fp);
2702		print_sockaddr(fp, trussinfo, (uintptr_t)msghdr.msg_name, msghdr.msg_namelen);
2703		fprintf(fp, ",%d,", msghdr.msg_namelen);
2704		print_iovec(fp, trussinfo, (uintptr_t)msghdr.msg_iov, msghdr.msg_iovlen);
2705		fprintf(fp, ",%d,", msghdr.msg_iovlen);
2706		print_cmsgs(fp, pid, sc->type & OUT, &msghdr);
2707		fprintf(fp, ",%u,", msghdr.msg_controllen);
2708		print_mask_arg(sysdecode_msg_flags, fp, msghdr.msg_flags);
2709		fputs("}", fp);
2710		break;
2711	}
2712
2713	default:
2714		errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
2715	}
2716	fclose(fp);
2717	return (tmp);
2718}
2719
2720/*
2721 * Print (to outfile) the system call and its arguments.
2722 */
2723void
2724print_syscall(struct trussinfo *trussinfo)
2725{
2726	struct threadinfo *t;
2727	const char *name;
2728	char **s_args;
2729	int i, len, nargs;
2730
2731	t = trussinfo->curthread;
2732
2733	name = t->cs.sc->name;
2734	nargs = t->cs.nargs;
2735	s_args = t->cs.s_args;
2736
2737	len = print_line_prefix(trussinfo);
2738	len += fprintf(trussinfo->outfile, "%s(", name);
2739
2740	for (i = 0; i < nargs; i++) {
2741		if (s_args[i] != NULL)
2742			len += fprintf(trussinfo->outfile, "%s", s_args[i]);
2743		else
2744			len += fprintf(trussinfo->outfile,
2745			    "<missing argument>");
2746		len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ?
2747		    "," : "");
2748	}
2749	len += fprintf(trussinfo->outfile, ")");
2750	for (i = 0; i < 6 - (len / 8); i++)
2751		fprintf(trussinfo->outfile, "\t");
2752}
2753
2754void
2755print_syscall_ret(struct trussinfo *trussinfo, int error, syscallarg_t *retval)
2756{
2757	struct timespec timediff;
2758	struct threadinfo *t;
2759	struct syscall *sc;
2760
2761	t = trussinfo->curthread;
2762	sc = t->cs.sc;
2763	if (trussinfo->flags & COUNTONLY) {
2764		timespecsub(&t->after, &t->before, &timediff);
2765		timespecadd(&sc->time, &timediff, &sc->time);
2766		sc->ncalls++;
2767		if (error != 0)
2768			sc->nerror++;
2769		return;
2770	}
2771
2772	print_syscall(trussinfo);
2773	fflush(trussinfo->outfile);
2774
2775	if (retval == NULL) {
2776		/*
2777		 * This system call resulted in the current thread's exit,
2778		 * so there is no return value or error to display.
2779		 */
2780		fprintf(trussinfo->outfile, "\n");
2781		return;
2782	}
2783
2784	if (error == ERESTART)
2785		fprintf(trussinfo->outfile, " ERESTART\n");
2786	else if (error == EJUSTRETURN)
2787		fprintf(trussinfo->outfile, " EJUSTRETURN\n");
2788	else if (error != 0) {
2789		fprintf(trussinfo->outfile, " ERR#%d '%s'\n",
2790		    sysdecode_freebsd_to_abi_errno(t->proc->abi->abi, error),
2791		    strerror(error));
2792	} else if (sc->decode.ret_type == 2 &&
2793	    t->proc->abi->pointer_size == 4) {
2794		off_t off;
2795#if _BYTE_ORDER == _LITTLE_ENDIAN
2796		off = (off_t)retval[1] << 32 | retval[0];
2797#else
2798		off = (off_t)retval[0] << 32 | retval[1];
2799#endif
2800		fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", (intmax_t)off,
2801		    (intmax_t)off);
2802	} else {
2803		fprintf(trussinfo->outfile, " = %jd (0x%jx)\n",
2804		    (intmax_t)retval[0], (intmax_t)retval[0]);
2805	}
2806}
2807
2808void
2809print_summary(struct trussinfo *trussinfo)
2810{
2811	struct timespec total = {0, 0};
2812	struct syscall *sc;
2813	int ncall, nerror;
2814
2815	fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n",
2816	    "syscall", "seconds", "calls", "errors");
2817	ncall = nerror = 0;
2818	STAILQ_FOREACH(sc, &seen_syscalls, entries) {
2819		if (sc->ncalls) {
2820			fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
2821			    sc->name, (intmax_t)sc->time.tv_sec,
2822			    sc->time.tv_nsec, sc->ncalls, sc->nerror);
2823			timespecadd(&total, &sc->time, &total);
2824			ncall += sc->ncalls;
2825			nerror += sc->nerror;
2826		}
2827	}
2828	fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n",
2829	    "", "-------------", "-------", "-------");
2830	fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
2831	    "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror);
2832}
2833