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