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 sc->trace = syscall_filter_match(name, number);
923
924 for (i = 0; i < nitems(decoded_syscalls); i++) {
925 if (strcmp(lookup_name, decoded_syscalls[i].name) == 0) {
926 sc->decode = decoded_syscalls[i];
927 add_syscall(t->proc->abi, number, sc);
928 return (sc);
929 }
930 }
931
932 /* It is unknown. Add it into the list. */
933 #if DEBUG
934 fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name,
935 nargs);
936 #endif
937 sc->unknown = sysdecode_name == NULL;
938 sc->decode.ret_type = 1; /* Assume 1 return value. */
939 sc->decode.nargs = nargs;
940 for (i = 0; i < nargs; i++) {
941 sc->decode.args[i].offset = i;
942 /* Treat all unknown arguments as LongHex. */
943 sc->decode.args[i].type = LongHex;
944 }
945 add_syscall(t->proc->abi, number, sc);
946 return (sc);
947 }
948
949 /*
950 * Copy a fixed amount of bytes from the process.
951 */
952 static int
get_struct(struct trussinfo * info,struct procinfo * p,psaddr_t offset,void * buf,size_t len)953 get_struct(struct trussinfo *info, struct procinfo *p, psaddr_t offset,
954 void *buf, size_t len)
955 {
956 struct ptrace_io_desc iorequest;
957
958 iorequest.piod_op = PIOD_READ_D;
959 iorequest.piod_offs = (void *)(uintptr_t)offset;
960 iorequest.piod_addr = buf;
961 iorequest.piod_len = len;
962 if (truss_ptrace(info, PT_IO, p, (caddr_t)&iorequest, 0) < 0)
963 return (-1);
964 return (0);
965 }
966
967 #define MAXSIZE 4096
968
969 /*
970 * Copy a string from the process. Note that it is
971 * expected to be a C string, but if max is set, it will
972 * only get that much.
973 */
974 static char *
get_string(struct trussinfo * info,struct procinfo * p,psaddr_t addr,int max)975 get_string(struct trussinfo *info, struct procinfo *p, psaddr_t addr, int max)
976 {
977 struct ptrace_io_desc iorequest;
978 char *buf, *nbuf;
979 size_t offset, size, totalsize;
980
981 offset = 0;
982 if (max)
983 size = max + 1;
984 else {
985 /* Read up to the end of the current page. */
986 size = PAGE_SIZE - (addr % PAGE_SIZE);
987 if (size > MAXSIZE)
988 size = MAXSIZE;
989 }
990 totalsize = size;
991 buf = malloc(totalsize);
992 if (buf == NULL)
993 return (NULL);
994 for (;;) {
995 iorequest.piod_op = PIOD_READ_D;
996 iorequest.piod_offs = (void *)((uintptr_t)addr + offset);
997 iorequest.piod_addr = buf + offset;
998 iorequest.piod_len = size;
999 if (truss_ptrace(info, PT_IO, p, (caddr_t)&iorequest, 0) < 0) {
1000 free(buf);
1001 return (NULL);
1002 }
1003 if (memchr(buf + offset, '\0', size) != NULL)
1004 return (buf);
1005 offset += size;
1006 if (totalsize < MAXSIZE && max == 0) {
1007 size = MAXSIZE - totalsize;
1008 if (size > PAGE_SIZE)
1009 size = PAGE_SIZE;
1010 nbuf = realloc(buf, totalsize + size);
1011 if (nbuf == NULL) {
1012 buf[totalsize - 1] = '\0';
1013 return (buf);
1014 }
1015 buf = nbuf;
1016 totalsize += size;
1017 } else {
1018 buf[totalsize - 1] = '\0';
1019 return (buf);
1020 }
1021 }
1022 }
1023
1024 static const char *
strsig2(int sig)1025 strsig2(int sig)
1026 {
1027 static char tmp[32];
1028 const char *signame;
1029
1030 signame = sysdecode_signal(sig);
1031 if (signame == NULL) {
1032 snprintf(tmp, sizeof(tmp), "%d", sig);
1033 signame = tmp;
1034 }
1035 return (signame);
1036 }
1037
1038 static void
print_kevent(FILE * fp,struct kevent * ke)1039 print_kevent(FILE *fp, struct kevent *ke)
1040 {
1041
1042 switch (ke->filter) {
1043 case EVFILT_READ:
1044 case EVFILT_WRITE:
1045 case EVFILT_VNODE:
1046 case EVFILT_PROC:
1047 case EVFILT_TIMER:
1048 case EVFILT_PROCDESC:
1049 case EVFILT_EMPTY:
1050 fprintf(fp, "%ju", (uintmax_t)ke->ident);
1051 break;
1052 case EVFILT_SIGNAL:
1053 fputs(strsig2(ke->ident), fp);
1054 break;
1055 default:
1056 fprintf(fp, "%p", (void *)ke->ident);
1057 }
1058 fprintf(fp, ",");
1059 print_integer_arg(sysdecode_kevent_filter, fp, ke->filter);
1060 fprintf(fp, ",");
1061 print_mask_arg(sysdecode_kevent_flags, fp, ke->flags);
1062 fprintf(fp, ",");
1063 sysdecode_kevent_fflags(fp, ke->filter, ke->fflags, 16);
1064 fprintf(fp, ",%#jx,%p", (uintmax_t)ke->data, ke->udata);
1065 }
1066
1067 static void
print_utrace(FILE * fp,void * utrace_addr,size_t len)1068 print_utrace(FILE *fp, void *utrace_addr, size_t len)
1069 {
1070 unsigned char *utrace_buffer;
1071
1072 fprintf(fp, "{ ");
1073 if (sysdecode_utrace(fp, utrace_addr, len)) {
1074 fprintf(fp, " }");
1075 return;
1076 }
1077
1078 utrace_buffer = utrace_addr;
1079 fprintf(fp, "%zu:", len);
1080 while (len--)
1081 fprintf(fp, " %02x", *utrace_buffer++);
1082 fprintf(fp, " }");
1083 }
1084
1085 static void
print_pointer(FILE * fp,uintptr_t arg)1086 print_pointer(FILE *fp, uintptr_t arg)
1087 {
1088
1089 fprintf(fp, "%p", (void *)arg);
1090 }
1091
1092 static void
print_sockaddr(FILE * fp,struct trussinfo * trussinfo,uintptr_t arg,socklen_t len)1093 print_sockaddr(FILE *fp, struct trussinfo *trussinfo, uintptr_t arg,
1094 socklen_t len)
1095 {
1096 char addr[64];
1097 struct sockaddr_in *lsin;
1098 struct sockaddr_in6 *lsin6;
1099 struct sockaddr_un *sun;
1100 struct sockaddr *sa;
1101 u_char *q;
1102 struct procinfo *p;
1103
1104 p = trussinfo->curthread->proc;
1105
1106 if (arg == 0) {
1107 fputs("NULL", fp);
1108 return;
1109 }
1110 /* If the length is too small, just bail. */
1111 if (len < sizeof(*sa)) {
1112 print_pointer(fp, arg);
1113 return;
1114 }
1115
1116 sa = calloc(1, len);
1117 if (get_struct(trussinfo, p, arg, sa, len) == -1) {
1118 free(sa);
1119 print_pointer(fp, arg);
1120 return;
1121 }
1122
1123 switch (sa->sa_family) {
1124 case AF_INET:
1125 if (len < sizeof(*lsin))
1126 goto sockaddr_short;
1127 lsin = (struct sockaddr_in *)(void *)sa;
1128 inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof(addr));
1129 fprintf(fp, "{ AF_INET %s:%d }", addr,
1130 htons(lsin->sin_port));
1131 break;
1132 case AF_INET6:
1133 if (len < sizeof(*lsin6))
1134 goto sockaddr_short;
1135 lsin6 = (struct sockaddr_in6 *)(void *)sa;
1136 inet_ntop(AF_INET6, &lsin6->sin6_addr, addr,
1137 sizeof(addr));
1138 fprintf(fp, "{ AF_INET6 [%s]:%d }", addr,
1139 htons(lsin6->sin6_port));
1140 break;
1141 case AF_UNIX:
1142 sun = (struct sockaddr_un *)sa;
1143 fprintf(fp, "{ AF_UNIX \"%.*s\" }",
1144 (int)(len - offsetof(struct sockaddr_un, sun_path)),
1145 sun->sun_path);
1146 break;
1147 default:
1148 sockaddr_short:
1149 fprintf(fp,
1150 "{ sa_len = %d, sa_family = %d, sa_data = {",
1151 (int)sa->sa_len, (int)sa->sa_family);
1152 for (q = (u_char *)sa->sa_data;
1153 q < (u_char *)sa + len; q++)
1154 fprintf(fp, "%s 0x%02x",
1155 q == (u_char *)sa->sa_data ? "" : ",",
1156 *q);
1157 fputs(" } }", fp);
1158 }
1159 free(sa);
1160 }
1161
1162 #define IOV_LIMIT 16
1163
1164 static void
print_iovec(FILE * fp,struct trussinfo * trussinfo,uintptr_t arg,int iovcnt)1165 print_iovec(FILE *fp, struct trussinfo *trussinfo, uintptr_t arg, int iovcnt)
1166 {
1167 struct procinfo *p;
1168 struct iovec iov[IOV_LIMIT];
1169 size_t max_string = trussinfo->strsize;
1170 char tmp2[max_string + 1], *tmp3;
1171 size_t len;
1172 int i;
1173 bool buf_truncated, iov_truncated;
1174
1175 if (iovcnt <= 0) {
1176 print_pointer(fp, arg);
1177 return;
1178 }
1179 if (iovcnt > IOV_LIMIT) {
1180 iovcnt = IOV_LIMIT;
1181 iov_truncated = true;
1182 } else {
1183 iov_truncated = false;
1184 }
1185 p = trussinfo->curthread->proc;
1186 if (get_struct(trussinfo, p, arg, &iov, iovcnt *
1187 sizeof(struct iovec)) == -1) {
1188 print_pointer(fp, arg);
1189 return;
1190 }
1191
1192 fputs("[", fp);
1193 for (i = 0; i < iovcnt; i++) {
1194 len = iov[i].iov_len;
1195 if (len > max_string) {
1196 len = max_string;
1197 buf_truncated = true;
1198 } else {
1199 buf_truncated = false;
1200 }
1201 fprintf(fp, "%s{", (i > 0) ? "," : "");
1202 if (len && get_struct(trussinfo, p, (uintptr_t)iov[i].iov_base,
1203 &tmp2, len) != -1) {
1204 tmp3 = malloc(len * 4 + 1);
1205 while (len) {
1206 if (strvisx(tmp3, tmp2, len,
1207 VIS_CSTYLE|VIS_TAB|VIS_NL) <=
1208 (int)max_string)
1209 break;
1210 len--;
1211 buf_truncated = true;
1212 }
1213 fprintf(fp, "\"%s\"%s", tmp3,
1214 buf_truncated ? "..." : "");
1215 free(tmp3);
1216 } else {
1217 print_pointer(fp, (uintptr_t)iov[i].iov_base);
1218 }
1219 fprintf(fp, ",%zu}", iov[i].iov_len);
1220 }
1221 fprintf(fp, "%s%s", iov_truncated ? ",..." : "", "]");
1222 }
1223
1224 static void
print_sigval(FILE * fp,union sigval * sv)1225 print_sigval(FILE *fp, union sigval *sv)
1226 {
1227 fprintf(fp, "{ %d, %p }", sv->sival_int, sv->sival_ptr);
1228 }
1229
1230 static void
print_sigevent(FILE * fp,struct sigevent * se)1231 print_sigevent(FILE *fp, struct sigevent *se)
1232 {
1233 fputs("{ sigev_notify=", fp);
1234 switch (se->sigev_notify) {
1235 case SIGEV_NONE:
1236 fputs("SIGEV_NONE", fp);
1237 break;
1238 case SIGEV_SIGNAL:
1239 fprintf(fp, "SIGEV_SIGNAL, sigev_signo=%s, sigev_value=",
1240 strsig2(se->sigev_signo));
1241 print_sigval(fp, &se->sigev_value);
1242 break;
1243 case SIGEV_THREAD:
1244 fputs("SIGEV_THREAD, sigev_value=", fp);
1245 print_sigval(fp, &se->sigev_value);
1246 break;
1247 case SIGEV_KEVENT:
1248 fprintf(fp, "SIGEV_KEVENT, sigev_notify_kqueue=%d, sigev_notify_kevent_flags=",
1249 se->sigev_notify_kqueue);
1250 print_mask_arg(sysdecode_kevent_flags, fp, se->sigev_notify_kevent_flags);
1251 break;
1252 case SIGEV_THREAD_ID:
1253 fprintf(fp, "SIGEV_THREAD_ID, sigev_notify_thread_id=%d, sigev_signo=%s, sigev_value=",
1254 se->sigev_notify_thread_id, strsig2(se->sigev_signo));
1255 print_sigval(fp, &se->sigev_value);
1256 break;
1257 default:
1258 fprintf(fp, "%d", se->sigev_notify);
1259 break;
1260 }
1261 fputs(" }", fp);
1262 }
1263
1264 static void
print_aiocb(FILE * fp,struct aiocb * cb)1265 print_aiocb(FILE *fp, struct aiocb *cb)
1266 {
1267 fprintf(fp, "{ %d,%jd,%p,%zu,%s,",
1268 cb->aio_fildes,
1269 cb->aio_offset,
1270 cb->aio_buf,
1271 cb->aio_nbytes,
1272 xlookup(lio_opcodes, cb->aio_lio_opcode));
1273 print_sigevent(fp, &cb->aio_sigevent);
1274 fputs(" }", fp);
1275 }
1276
1277 static void
print_gen_cmsg(FILE * fp,struct cmsghdr * cmsghdr)1278 print_gen_cmsg(FILE *fp, struct cmsghdr *cmsghdr)
1279 {
1280 u_char *q;
1281
1282 fputs("{", fp);
1283 for (q = CMSG_DATA(cmsghdr);
1284 q < (u_char *)cmsghdr + cmsghdr->cmsg_len; q++) {
1285 fprintf(fp, "%s0x%02x", q == CMSG_DATA(cmsghdr) ? "" : ",", *q);
1286 }
1287 fputs("}", fp);
1288 }
1289
1290 static void
print_sctp_initmsg(FILE * fp,struct sctp_initmsg * init)1291 print_sctp_initmsg(FILE *fp, struct sctp_initmsg *init)
1292 {
1293 fprintf(fp, "{out=%u,", init->sinit_num_ostreams);
1294 fprintf(fp, "in=%u,", init->sinit_max_instreams);
1295 fprintf(fp, "max_rtx=%u,", init->sinit_max_attempts);
1296 fprintf(fp, "max_rto=%u}", init->sinit_max_init_timeo);
1297 }
1298
1299 static void
print_sctp_sndrcvinfo(FILE * fp,bool receive,struct sctp_sndrcvinfo * info)1300 print_sctp_sndrcvinfo(FILE *fp, bool receive, struct sctp_sndrcvinfo *info)
1301 {
1302 fprintf(fp, "{sid=%u,", info->sinfo_stream);
1303 if (receive) {
1304 fprintf(fp, "ssn=%u,", info->sinfo_ssn);
1305 }
1306 fputs("flgs=", fp);
1307 sysdecode_sctp_sinfo_flags(fp, info->sinfo_flags);
1308 fprintf(fp, ",ppid=%u,", ntohl(info->sinfo_ppid));
1309 if (!receive) {
1310 fprintf(fp, "ctx=%u,", info->sinfo_context);
1311 fprintf(fp, "ttl=%u,", info->sinfo_timetolive);
1312 }
1313 if (receive) {
1314 fprintf(fp, "tsn=%u,", info->sinfo_tsn);
1315 fprintf(fp, "cumtsn=%u,", info->sinfo_cumtsn);
1316 }
1317 fprintf(fp, "id=%u}", info->sinfo_assoc_id);
1318 }
1319
1320 static void
print_sctp_sndinfo(FILE * fp,struct sctp_sndinfo * info)1321 print_sctp_sndinfo(FILE *fp, struct sctp_sndinfo *info)
1322 {
1323 fprintf(fp, "{sid=%u,", info->snd_sid);
1324 fputs("flgs=", fp);
1325 print_mask_arg(sysdecode_sctp_snd_flags, fp, info->snd_flags);
1326 fprintf(fp, ",ppid=%u,", ntohl(info->snd_ppid));
1327 fprintf(fp, "ctx=%u,", info->snd_context);
1328 fprintf(fp, "id=%u}", info->snd_assoc_id);
1329 }
1330
1331 static void
print_sctp_rcvinfo(FILE * fp,struct sctp_rcvinfo * info)1332 print_sctp_rcvinfo(FILE *fp, struct sctp_rcvinfo *info)
1333 {
1334 fprintf(fp, "{sid=%u,", info->rcv_sid);
1335 fprintf(fp, "ssn=%u,", info->rcv_ssn);
1336 fputs("flgs=", fp);
1337 print_mask_arg(sysdecode_sctp_rcv_flags, fp, info->rcv_flags);
1338 fprintf(fp, ",ppid=%u,", ntohl(info->rcv_ppid));
1339 fprintf(fp, "tsn=%u,", info->rcv_tsn);
1340 fprintf(fp, "cumtsn=%u,", info->rcv_cumtsn);
1341 fprintf(fp, "ctx=%u,", info->rcv_context);
1342 fprintf(fp, "id=%u}", info->rcv_assoc_id);
1343 }
1344
1345 static void
print_sctp_nxtinfo(FILE * fp,struct sctp_nxtinfo * info)1346 print_sctp_nxtinfo(FILE *fp, struct sctp_nxtinfo *info)
1347 {
1348 fprintf(fp, "{sid=%u,", info->nxt_sid);
1349 fputs("flgs=", fp);
1350 print_mask_arg(sysdecode_sctp_nxt_flags, fp, info->nxt_flags);
1351 fprintf(fp, ",ppid=%u,", ntohl(info->nxt_ppid));
1352 fprintf(fp, "len=%u,", info->nxt_length);
1353 fprintf(fp, "id=%u}", info->nxt_assoc_id);
1354 }
1355
1356 static void
print_sctp_prinfo(FILE * fp,struct sctp_prinfo * info)1357 print_sctp_prinfo(FILE *fp, struct sctp_prinfo *info)
1358 {
1359 fputs("{pol=", fp);
1360 print_integer_arg(sysdecode_sctp_pr_policy, fp, info->pr_policy);
1361 fprintf(fp, ",val=%u}", info->pr_value);
1362 }
1363
1364 static void
print_sctp_authinfo(FILE * fp,struct sctp_authinfo * info)1365 print_sctp_authinfo(FILE *fp, struct sctp_authinfo *info)
1366 {
1367 fprintf(fp, "{num=%u}", info->auth_keynumber);
1368 }
1369
1370 static void
print_sctp_ipv4_addr(FILE * fp,struct in_addr * addr)1371 print_sctp_ipv4_addr(FILE *fp, struct in_addr *addr)
1372 {
1373 char buf[INET_ADDRSTRLEN];
1374 const char *s;
1375
1376 s = inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN);
1377 if (s != NULL)
1378 fprintf(fp, "{addr=%s}", s);
1379 else
1380 fputs("{addr=???}", fp);
1381 }
1382
1383 static void
print_sctp_ipv6_addr(FILE * fp,struct in6_addr * addr)1384 print_sctp_ipv6_addr(FILE *fp, struct in6_addr *addr)
1385 {
1386 char buf[INET6_ADDRSTRLEN];
1387 const char *s;
1388
1389 s = inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN);
1390 if (s != NULL)
1391 fprintf(fp, "{addr=%s}", s);
1392 else
1393 fputs("{addr=???}", fp);
1394 }
1395
1396 static void
print_sctp_cmsg(FILE * fp,bool receive,struct cmsghdr * cmsghdr)1397 print_sctp_cmsg(FILE *fp, bool receive, struct cmsghdr *cmsghdr)
1398 {
1399 void *data;
1400 socklen_t len;
1401
1402 len = cmsghdr->cmsg_len;
1403 data = CMSG_DATA(cmsghdr);
1404 switch (cmsghdr->cmsg_type) {
1405 case SCTP_INIT:
1406 if (len == CMSG_LEN(sizeof(struct sctp_initmsg)))
1407 print_sctp_initmsg(fp, (struct sctp_initmsg *)data);
1408 else
1409 print_gen_cmsg(fp, cmsghdr);
1410 break;
1411 case SCTP_SNDRCV:
1412 if (len == CMSG_LEN(sizeof(struct sctp_sndrcvinfo)))
1413 print_sctp_sndrcvinfo(fp, receive,
1414 (struct sctp_sndrcvinfo *)data);
1415 else
1416 print_gen_cmsg(fp, cmsghdr);
1417 break;
1418 #if 0
1419 case SCTP_EXTRCV:
1420 if (len == CMSG_LEN(sizeof(struct sctp_extrcvinfo)))
1421 print_sctp_extrcvinfo(fp,
1422 (struct sctp_extrcvinfo *)data);
1423 else
1424 print_gen_cmsg(fp, cmsghdr);
1425 break;
1426 #endif
1427 case SCTP_SNDINFO:
1428 if (len == CMSG_LEN(sizeof(struct sctp_sndinfo)))
1429 print_sctp_sndinfo(fp, (struct sctp_sndinfo *)data);
1430 else
1431 print_gen_cmsg(fp, cmsghdr);
1432 break;
1433 case SCTP_RCVINFO:
1434 if (len == CMSG_LEN(sizeof(struct sctp_rcvinfo)))
1435 print_sctp_rcvinfo(fp, (struct sctp_rcvinfo *)data);
1436 else
1437 print_gen_cmsg(fp, cmsghdr);
1438 break;
1439 case SCTP_NXTINFO:
1440 if (len == CMSG_LEN(sizeof(struct sctp_nxtinfo)))
1441 print_sctp_nxtinfo(fp, (struct sctp_nxtinfo *)data);
1442 else
1443 print_gen_cmsg(fp, cmsghdr);
1444 break;
1445 case SCTP_PRINFO:
1446 if (len == CMSG_LEN(sizeof(struct sctp_prinfo)))
1447 print_sctp_prinfo(fp, (struct sctp_prinfo *)data);
1448 else
1449 print_gen_cmsg(fp, cmsghdr);
1450 break;
1451 case SCTP_AUTHINFO:
1452 if (len == CMSG_LEN(sizeof(struct sctp_authinfo)))
1453 print_sctp_authinfo(fp, (struct sctp_authinfo *)data);
1454 else
1455 print_gen_cmsg(fp, cmsghdr);
1456 break;
1457 case SCTP_DSTADDRV4:
1458 if (len == CMSG_LEN(sizeof(struct in_addr)))
1459 print_sctp_ipv4_addr(fp, (struct in_addr *)data);
1460 else
1461 print_gen_cmsg(fp, cmsghdr);
1462 break;
1463 case SCTP_DSTADDRV6:
1464 if (len == CMSG_LEN(sizeof(struct in6_addr)))
1465 print_sctp_ipv6_addr(fp, (struct in6_addr *)data);
1466 else
1467 print_gen_cmsg(fp, cmsghdr);
1468 break;
1469 default:
1470 print_gen_cmsg(fp, cmsghdr);
1471 }
1472 }
1473
1474 static void
print_cmsgs(FILE * fp,struct trussinfo * info,struct procinfo * p,bool receive,struct msghdr * msghdr)1475 print_cmsgs(FILE *fp, struct trussinfo *info, struct procinfo *p,
1476 bool receive, struct msghdr *msghdr)
1477 {
1478 struct cmsghdr *cmsghdr;
1479 char *cmsgbuf;
1480 const char *temp;
1481 socklen_t len;
1482 int level, type;
1483 bool first;
1484
1485 len = msghdr->msg_controllen;
1486 if (len == 0) {
1487 fputs("{}", fp);
1488 return;
1489 }
1490 cmsgbuf = calloc(1, len);
1491 if (get_struct(info, p, (uintptr_t)msghdr->msg_control, cmsgbuf,
1492 len) == -1) {
1493 print_pointer(fp, (uintptr_t)msghdr->msg_control);
1494 free(cmsgbuf);
1495 return;
1496 }
1497 msghdr->msg_control = cmsgbuf;
1498 first = true;
1499 fputs("{", fp);
1500 for (cmsghdr = CMSG_FIRSTHDR(msghdr);
1501 cmsghdr != NULL;
1502 cmsghdr = CMSG_NXTHDR(msghdr, cmsghdr)) {
1503 if (cmsghdr->cmsg_len < sizeof(*cmsghdr)) {
1504 fprintf(fp, "{<invalid cmsg, len=%u>}",
1505 cmsghdr->cmsg_len);
1506 if (cmsghdr->cmsg_len == 0) {
1507 /* Avoid looping forever. */
1508 break;
1509 }
1510 continue;
1511 }
1512
1513 level = cmsghdr->cmsg_level;
1514 type = cmsghdr->cmsg_type;
1515 len = cmsghdr->cmsg_len;
1516 fprintf(fp, "%s{level=", first ? "" : ",");
1517 print_integer_arg(sysdecode_sockopt_level, fp, level);
1518 fputs(",type=", fp);
1519 temp = sysdecode_cmsg_type(level, type);
1520 if (temp) {
1521 fputs(temp, fp);
1522 } else {
1523 fprintf(fp, "%d", type);
1524 }
1525 fputs(",data=", fp);
1526 switch (level) {
1527 case IPPROTO_SCTP:
1528 print_sctp_cmsg(fp, receive, cmsghdr);
1529 break;
1530 default:
1531 print_gen_cmsg(fp, cmsghdr);
1532 break;
1533 }
1534 fputs("}", fp);
1535 first = false;
1536 }
1537 fputs("}", fp);
1538 free(cmsgbuf);
1539 }
1540
1541 static void
print_sysctl_oid(FILE * fp,int * oid,size_t len)1542 print_sysctl_oid(FILE *fp, int *oid, size_t len)
1543 {
1544 size_t i;
1545 bool first;
1546
1547 first = true;
1548 fprintf(fp, "{ ");
1549 for (i = 0; i < len; i++) {
1550 fprintf(fp, "%s%d", first ? "" : ".", oid[i]);
1551 first = false;
1552 }
1553 fprintf(fp, " }");
1554 }
1555
1556 static void
print_sysctl(FILE * fp,int * oid,size_t len)1557 print_sysctl(FILE *fp, int *oid, size_t len)
1558 {
1559 char name[BUFSIZ];
1560 int qoid[CTL_MAXNAME + 2];
1561 size_t i;
1562
1563 qoid[0] = CTL_SYSCTL;
1564 qoid[1] = CTL_SYSCTL_NAME;
1565 memcpy(qoid + 2, oid, len * sizeof(int));
1566 i = sizeof(name);
1567 if (sysctl(qoid, len + 2, name, &i, 0, 0) == -1)
1568 print_sysctl_oid(fp, oid, len);
1569 else
1570 fprintf(fp, "%s", name);
1571 }
1572
1573 /*
1574 * Convert a 32-bit user-space pointer to psaddr_t by zero-extending.
1575 */
1576 static psaddr_t
user_ptr32_to_psaddr(int32_t user_pointer)1577 user_ptr32_to_psaddr(int32_t user_pointer)
1578 {
1579 return ((psaddr_t)(uintptr_t)user_pointer);
1580 }
1581
1582 /*
1583 * Check whether a file descriptor belongs to an AF_NETLINK socket
1584 * for the current traced process.
1585 */
1586 static int
get_netlink_protocol(struct trussinfo * trussinfo,int num_fd)1587 get_netlink_protocol(struct trussinfo *trussinfo, int num_fd)
1588 {
1589 struct procinfo *p = trussinfo->curthread->proc;
1590 struct fd_domain *f;
1591
1592 LIST_FOREACH(f, &p->fdlist, entries) {
1593 if (f->fd == num_fd && f->domain == AF_NETLINK)
1594 return (f->protocol);
1595 }
1596 return (-1);
1597 }
1598
1599 #define NETLINK_MAX_DECODE 4096
1600
1601 /*
1602 * Copy a potential Netlink message buffer from the traced process
1603 * and attempt to decode and print it.
1604 */
1605 static bool
print_netlink(FILE * fp,struct trussinfo * trussinfo,void * msg,size_t len,int protocol)1606 print_netlink(FILE *fp, struct trussinfo *trussinfo, void *msg, size_t len,
1607 int protocol)
1608 {
1609 char *buf;
1610 struct procinfo *p;
1611 bool success = false;
1612
1613 if (msg == NULL || len == 0)
1614 return (false);
1615
1616 size_t read_len = MIN(len, NETLINK_MAX_DECODE);
1617 if (read_len == 0)
1618 return (false);
1619
1620 buf = malloc(read_len);
1621 if (buf == NULL)
1622 return (false);
1623
1624 p = trussinfo->curthread->proc;
1625 if (get_struct(trussinfo, p, (uintptr_t)msg, buf, read_len) == -1) {
1626 free(buf);
1627 return (false);
1628 }
1629
1630 if (sysdecode_netlink(fp, buf, read_len, protocol)) {
1631 success = true;
1632 }
1633 free(buf);
1634
1635 return (success);
1636 }
1637
1638 /*
1639 * Converts a syscall argument into a string. Said string is
1640 * allocated via malloc(), so needs to be free()'d. sc is
1641 * a pointer to the syscall description (see above); args is
1642 * an array of all of the system call arguments.
1643 */
1644 char *
print_arg(struct syscall_arg * sc,syscallarg_t * args,syscallarg_t * retval,struct trussinfo * trussinfo,struct syscall_decode * decode)1645 print_arg(struct syscall_arg *sc, syscallarg_t *args, syscallarg_t *retval,
1646 struct trussinfo *trussinfo, struct syscall_decode *decode)
1647 {
1648 FILE *fp;
1649 struct procinfo *p;
1650 char *tmp;
1651 size_t tmplen;
1652
1653 fp = open_memstream(&tmp, &tmplen);
1654 p = trussinfo->curthread->proc;
1655 switch (sc->type & ARG_MASK) {
1656 case Hex:
1657 fprintf(fp, "0x%x", (int)args[sc->offset]);
1658 break;
1659 case Octal:
1660 fprintf(fp, "0%o", (int)args[sc->offset]);
1661 break;
1662 case Int:
1663 fprintf(fp, "%d", (int)args[sc->offset]);
1664 break;
1665 case UInt:
1666 fprintf(fp, "%u", (unsigned int)args[sc->offset]);
1667 break;
1668 case PUInt: {
1669 unsigned int val;
1670
1671 if (get_struct(trussinfo, p, args[sc->offset], &val,
1672 sizeof(val)) == 0)
1673 fprintf(fp, "{ %u }", val);
1674 else
1675 print_pointer(fp, args[sc->offset]);
1676 break;
1677 }
1678 case LongHex:
1679 fprintf(fp, "0x%lx", (long)args[sc->offset]);
1680 break;
1681 case Long:
1682 fprintf(fp, "%ld", (long)args[sc->offset]);
1683 break;
1684 case Sizet:
1685 fprintf(fp, "%zu", (size_t)args[sc->offset]);
1686 break;
1687 case ShmName:
1688 /* Handle special SHM_ANON value. */
1689 if ((char *)(uintptr_t)args[sc->offset] == SHM_ANON) {
1690 fprintf(fp, "SHM_ANON");
1691 break;
1692 }
1693 /* FALLTHROUGH */
1694 case Name: {
1695 /* NULL-terminated string. */
1696 char *tmp2;
1697
1698 tmp2 = get_string(trussinfo, p, args[sc->offset], 0);
1699 fprintf(fp, "\"%s\"", tmp2);
1700 free(tmp2);
1701 break;
1702 }
1703 case BinString: {
1704 if ((strcmp(decode->name, "sendto") == 0 ||
1705 strcmp(decode->name, "recvfrom") == 0)) {
1706
1707 int protocol =
1708 get_netlink_protocol(trussinfo, (int)args[0]);
1709
1710 if ((protocol != -1) &&
1711 print_netlink(fp, trussinfo,
1712 (void *)args[sc->offset],
1713 (size_t)args[sc->offset + 1],
1714 protocol))
1715 break;
1716 }
1717 /*
1718 * Binary block of data that might have printable characters.
1719 * XXX If type|OUT, assume that the length is the syscall's
1720 * return value. Otherwise, assume that the length of the block
1721 * is in the next syscall argument.
1722 */
1723 int max_string = trussinfo->strsize;
1724 char tmp2[max_string + 1], *tmp3;
1725 int len;
1726 int truncated = 0;
1727
1728 if (sc->type & OUT)
1729 len = retval[0];
1730 else
1731 len = args[sc->offset + 1];
1732
1733 /*
1734 * Don't print more than max_string characters, to avoid word
1735 * wrap. If we have to truncate put some ... after the string.
1736 */
1737 if (len > max_string) {
1738 len = max_string;
1739 truncated = 1;
1740 }
1741 if (len && get_struct(trussinfo, p, args[sc->offset], &tmp2,
1742 len) != -1) {
1743 tmp3 = malloc(len * 4 + 1);
1744 while (len) {
1745 if (strvisx(tmp3, tmp2, len,
1746 VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string)
1747 break;
1748 len--;
1749 truncated = 1;
1750 }
1751 fprintf(fp, "\"%s\"%s", tmp3, truncated ?
1752 "..." : "");
1753 free(tmp3);
1754 } else {
1755 print_pointer(fp, args[sc->offset]);
1756 }
1757 break;
1758 }
1759 case ExecArgs:
1760 case ExecEnv:
1761 case StringArray: {
1762 psaddr_t addr;
1763 union {
1764 int32_t strarray32[PAGE_SIZE / sizeof(int32_t)];
1765 int64_t strarray64[PAGE_SIZE / sizeof(int64_t)];
1766 char buf[PAGE_SIZE];
1767 } u;
1768 char *string;
1769 size_t len;
1770 u_int first, i;
1771 size_t pointer_size =
1772 trussinfo->curthread->proc->abi->pointer_size;
1773
1774 /*
1775 * Only parse argv[] and environment arrays from exec calls
1776 * if requested.
1777 */
1778 if (((sc->type & ARG_MASK) == ExecArgs &&
1779 (trussinfo->flags & EXECVEARGS) == 0) ||
1780 ((sc->type & ARG_MASK) == ExecEnv &&
1781 (trussinfo->flags & EXECVEENVS) == 0)) {
1782 print_pointer(fp, args[sc->offset]);
1783 break;
1784 }
1785
1786 /*
1787 * Read a page of pointers at a time. Punt if the top-level
1788 * pointer is not aligned. Note that the first read is of
1789 * a partial page.
1790 */
1791 addr = args[sc->offset];
1792 if (!__is_aligned(addr, pointer_size)) {
1793 print_pointer(fp, args[sc->offset]);
1794 break;
1795 }
1796
1797 len = PAGE_SIZE - (addr & PAGE_MASK);
1798 if (get_struct(trussinfo, p, addr, u.buf, len) == -1) {
1799 print_pointer(fp, args[sc->offset]);
1800 break;
1801 }
1802 assert(len > 0);
1803
1804 fputc('[', fp);
1805 first = 1;
1806 i = 0;
1807 for (;;) {
1808 psaddr_t straddr;
1809 if (pointer_size == 4) {
1810 straddr = user_ptr32_to_psaddr(u.strarray32[i]);
1811 } else if (pointer_size == 8) {
1812 straddr = (psaddr_t)u.strarray64[i];
1813 } else {
1814 errx(1, "Unsupported pointer size: %zu",
1815 pointer_size);
1816 }
1817
1818 /* Stop once we read the first NULL pointer. */
1819 if (straddr == 0)
1820 break;
1821 string = get_string(trussinfo, p, straddr, 0);
1822 fprintf(fp, "%s \"%s\"", first ? "" : ",", string);
1823 free(string);
1824 first = 0;
1825
1826 i++;
1827 if (i == len / pointer_size) {
1828 addr += len;
1829 len = PAGE_SIZE;
1830 if (get_struct(trussinfo, p, addr, u.buf,
1831 len) == -1) {
1832 fprintf(fp, ", <inval>");
1833 break;
1834 }
1835 i = 0;
1836 }
1837 }
1838 fputs(" ]", fp);
1839 break;
1840 }
1841 case Quad:
1842 case QuadHex: {
1843 uint64_t value;
1844 size_t pointer_size =
1845 trussinfo->curthread->proc->abi->pointer_size;
1846
1847 if (pointer_size == 4) {
1848 #if _BYTE_ORDER == _LITTLE_ENDIAN
1849 value = (uint64_t)args[sc->offset + 1] << 32 |
1850 args[sc->offset];
1851 #else
1852 value = (uint64_t)args[sc->offset] << 32 |
1853 args[sc->offset + 1];
1854 #endif
1855 } else {
1856 value = (uint64_t)args[sc->offset];
1857 }
1858 if ((sc->type & ARG_MASK) == Quad)
1859 fprintf(fp, "%jd", (intmax_t)value);
1860 else
1861 fprintf(fp, "0x%jx", (intmax_t)value);
1862 break;
1863 }
1864 case PQuadHex: {
1865 uint64_t val;
1866
1867 if (get_struct(trussinfo, p, args[sc->offset], &val,
1868 sizeof(val)) == 0)
1869 fprintf(fp, "{ 0x%jx }", (uintmax_t)val);
1870 else
1871 print_pointer(fp, args[sc->offset]);
1872 break;
1873 }
1874 case Ptr:
1875 print_pointer(fp, args[sc->offset]);
1876 break;
1877 case Readlinkres: {
1878 char *tmp2;
1879
1880 if (retval[0] == -1)
1881 break;
1882 tmp2 = get_string(trussinfo, p, args[sc->offset], retval[0]);
1883 fprintf(fp, "\"%s\"", tmp2);
1884 free(tmp2);
1885 break;
1886 }
1887 case Ioctl: {
1888 const char *temp;
1889 unsigned long cmd;
1890
1891 cmd = args[sc->offset];
1892 temp = sysdecode_ioctlname(cmd);
1893 if (temp)
1894 fputs(temp, fp);
1895 else {
1896 fprintf(fp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }",
1897 cmd, cmd & IOC_OUT ? "R" : "",
1898 cmd & IOC_IN ? "W" : "", IOCGROUP(cmd),
1899 isprint(IOCGROUP(cmd)) ? (char)IOCGROUP(cmd) : '?',
1900 cmd & 0xFF, IOCPARM_LEN(cmd));
1901 }
1902 break;
1903 }
1904 case Timespec: {
1905 struct timespec ts;
1906
1907 if (get_struct(trussinfo, p, args[sc->offset], &ts, sizeof(ts))
1908 != -1)
1909 fprintf(fp, "{ %jd.%09ld }", (intmax_t)ts.tv_sec,
1910 ts.tv_nsec);
1911 else
1912 print_pointer(fp, args[sc->offset]);
1913 break;
1914 }
1915 case Timespec2: {
1916 struct timespec ts[2];
1917 const char *sep;
1918 unsigned int i;
1919
1920 if (get_struct(trussinfo, p, args[sc->offset], &ts, sizeof(ts))
1921 != -1) {
1922 fputs("{ ", fp);
1923 sep = "";
1924 for (i = 0; i < nitems(ts); i++) {
1925 fputs(sep, fp);
1926 sep = ", ";
1927 switch (ts[i].tv_nsec) {
1928 case UTIME_NOW:
1929 fprintf(fp, "UTIME_NOW");
1930 break;
1931 case UTIME_OMIT:
1932 fprintf(fp, "UTIME_OMIT");
1933 break;
1934 default:
1935 fprintf(fp, "%jd.%09ld",
1936 (intmax_t)ts[i].tv_sec,
1937 ts[i].tv_nsec);
1938 break;
1939 }
1940 }
1941 fputs(" }", fp);
1942 } else
1943 print_pointer(fp, args[sc->offset]);
1944 break;
1945 }
1946 case Timeval: {
1947 struct timeval tv;
1948
1949 if (get_struct(trussinfo, p, args[sc->offset], &tv, sizeof(tv))
1950 != -1)
1951 fprintf(fp, "{ %jd.%06ld }", (intmax_t)tv.tv_sec,
1952 tv.tv_usec);
1953 else
1954 print_pointer(fp, args[sc->offset]);
1955 break;
1956 }
1957 case Timeval2: {
1958 struct timeval tv[2];
1959
1960 if (get_struct(trussinfo, p, args[sc->offset], &tv, sizeof(tv))
1961 != -1)
1962 fprintf(fp, "{ %jd.%06ld, %jd.%06ld }",
1963 (intmax_t)tv[0].tv_sec, tv[0].tv_usec,
1964 (intmax_t)tv[1].tv_sec, tv[1].tv_usec);
1965 else
1966 print_pointer(fp, args[sc->offset]);
1967 break;
1968 }
1969 case Itimerval: {
1970 struct itimerval itv;
1971
1972 if (get_struct(trussinfo, p, args[sc->offset], &itv, sizeof(itv))
1973 != -1)
1974 fprintf(fp, "{ %jd.%06ld, %jd.%06ld }",
1975 (intmax_t)itv.it_interval.tv_sec,
1976 itv.it_interval.tv_usec,
1977 (intmax_t)itv.it_value.tv_sec,
1978 itv.it_value.tv_usec);
1979 else
1980 print_pointer(fp, args[sc->offset]);
1981 break;
1982 }
1983 case LinuxSockArgs:
1984 {
1985 struct linux_socketcall_args largs;
1986
1987 if (get_struct(trussinfo, p, args[sc->offset], (void *)&largs,
1988 sizeof(largs)) != -1)
1989 fprintf(fp, "{ %s, 0x%lx }",
1990 lookup(linux_socketcall_ops, largs.what, 10),
1991 (long unsigned int)largs.args);
1992 else
1993 print_pointer(fp, args[sc->offset]);
1994 break;
1995 }
1996 case Pollfd: {
1997 /*
1998 * XXX: A Pollfd argument expects the /next/ syscall argument
1999 * to be the number of fds in the array. This matches the poll
2000 * syscall.
2001 */
2002 struct pollfd *pfd;
2003 int numfds = args[sc->offset + 1];
2004 size_t bytes = sizeof(struct pollfd) * numfds;
2005 int i;
2006
2007 if ((pfd = malloc(bytes)) == NULL)
2008 err(1, "Cannot malloc %zu bytes for pollfd array",
2009 bytes);
2010 if (get_struct(trussinfo, p, args[sc->offset], pfd, bytes)
2011 != -1) {
2012 fputs("{", fp);
2013 for (i = 0; i < numfds; i++) {
2014 fprintf(fp, " %d/%s", pfd[i].fd,
2015 xlookup_bits(poll_flags, pfd[i].events));
2016 }
2017 fputs(" }", fp);
2018 } else {
2019 print_pointer(fp, args[sc->offset]);
2020 }
2021 free(pfd);
2022 break;
2023 }
2024 case Fd_set: {
2025 /*
2026 * XXX: A Fd_set argument expects the /first/ syscall argument
2027 * to be the number of fds in the array. This matches the
2028 * select syscall.
2029 */
2030 fd_set *fds;
2031 int numfds = args[0];
2032 size_t bytes = _howmany(numfds, _NFDBITS) * _NFDBITS;
2033 int i;
2034
2035 if ((fds = malloc(bytes)) == NULL)
2036 err(1, "Cannot malloc %zu bytes for fd_set array",
2037 bytes);
2038 if (get_struct(trussinfo, p, args[sc->offset], fds, bytes)
2039 != -1) {
2040 fputs("{", fp);
2041 for (i = 0; i < numfds; i++) {
2042 if (FD_ISSET(i, fds))
2043 fprintf(fp, " %d", i);
2044 }
2045 fputs(" }", fp);
2046 } else
2047 print_pointer(fp, args[sc->offset]);
2048 free(fds);
2049 break;
2050 }
2051 case Signal:
2052 fputs(strsig2(args[sc->offset]), fp);
2053 break;
2054 case Sigset: {
2055 sigset_t ss;
2056 int i, first;
2057
2058 if (get_struct(trussinfo, p, args[sc->offset], (void *)&ss,
2059 sizeof(ss)) == -1) {
2060 print_pointer(fp, args[sc->offset]);
2061 break;
2062 }
2063 fputs("{ ", fp);
2064 first = 1;
2065 for (i = 1; i < sys_nsig; i++) {
2066 if (sigismember(&ss, i)) {
2067 fprintf(fp, "%s%s", !first ? "|" : "",
2068 strsig2(i));
2069 first = 0;
2070 }
2071 }
2072 if (!first)
2073 fputc(' ', fp);
2074 fputc('}', fp);
2075 break;
2076 }
2077 case Sigprocmask:
2078 print_integer_arg(sysdecode_sigprocmask_how, fp,
2079 args[sc->offset]);
2080 break;
2081 case Fcntlflag:
2082 /* XXX: Output depends on the value of the previous argument. */
2083 if (sysdecode_fcntl_arg_p(args[sc->offset - 1]))
2084 sysdecode_fcntl_arg(fp, args[sc->offset - 1],
2085 args[sc->offset], 16);
2086 break;
2087 case Open:
2088 print_mask_arg(sysdecode_open_flags, fp, args[sc->offset]);
2089 break;
2090 case Fcntl:
2091 print_integer_arg(sysdecode_fcntl_cmd, fp, args[sc->offset]);
2092 break;
2093 case Closerangeflags:
2094 print_mask_arg(sysdecode_close_range_flags, fp, args[sc->offset]);
2095 break;
2096 case Mprot:
2097 print_mask_arg(sysdecode_mmap_prot, fp, args[sc->offset]);
2098 break;
2099 case Mmapflags:
2100 print_mask_arg(sysdecode_mmap_flags, fp, args[sc->offset]);
2101 break;
2102 case Whence:
2103 print_integer_arg(sysdecode_whence, fp, args[sc->offset]);
2104 break;
2105 case ShmFlags:
2106 print_mask_arg(sysdecode_shmflags, fp, args[sc->offset]);
2107 break;
2108 case Sockdomain:
2109 print_integer_arg(sysdecode_socketdomain, fp, args[sc->offset]);
2110 break;
2111 case Socktype:
2112 print_mask_arg(sysdecode_socket_type, fp, args[sc->offset]);
2113 break;
2114 case Shutdown:
2115 print_integer_arg(sysdecode_shutdown_how, fp, args[sc->offset]);
2116 break;
2117 case Resource:
2118 print_integer_arg(sysdecode_rlimit, fp, args[sc->offset]);
2119 break;
2120 case RusageWho:
2121 print_integer_arg(sysdecode_getrusage_who, fp, args[sc->offset]);
2122 break;
2123 case Pathconf:
2124 print_integer_arg(sysdecode_pathconf_name, fp, args[sc->offset]);
2125 break;
2126 case Rforkflags:
2127 print_mask_arg(sysdecode_rfork_flags, fp, args[sc->offset]);
2128 break;
2129 case Sockaddr: {
2130 socklen_t len;
2131
2132 if (args[sc->offset] == 0) {
2133 fputs("NULL", fp);
2134 break;
2135 }
2136
2137 /*
2138 * Extract the address length from the next argument. If
2139 * this is an output sockaddr (OUT is set), then the
2140 * next argument is a pointer to a socklen_t. Otherwise
2141 * the next argument contains a socklen_t by value.
2142 */
2143 if (sc->type & OUT) {
2144 if (get_struct(trussinfo, p, args[sc->offset + 1],
2145 &len, sizeof(len)) == -1) {
2146 print_pointer(fp, args[sc->offset]);
2147 break;
2148 }
2149 } else
2150 len = args[sc->offset + 1];
2151
2152 print_sockaddr(fp, trussinfo, args[sc->offset], len);
2153 break;
2154 }
2155 case Sigaction: {
2156 struct sigaction sa;
2157
2158 if (get_struct(trussinfo, p, args[sc->offset], &sa, sizeof(sa))
2159 != -1) {
2160 fputs("{ ", fp);
2161 if (sa.sa_handler == SIG_DFL)
2162 fputs("SIG_DFL", fp);
2163 else if (sa.sa_handler == SIG_IGN)
2164 fputs("SIG_IGN", fp);
2165 else
2166 fprintf(fp, "%p", sa.sa_handler);
2167 fprintf(fp, " %s ss_t }",
2168 xlookup_bits(sigaction_flags, sa.sa_flags));
2169 } else
2170 print_pointer(fp, args[sc->offset]);
2171 break;
2172 }
2173 case Sigevent: {
2174 struct sigevent se;
2175
2176 if (get_struct(trussinfo, p, args[sc->offset], &se, sizeof(se))
2177 != -1)
2178 print_sigevent(fp, &se);
2179 else
2180 print_pointer(fp, args[sc->offset]);
2181 break;
2182 }
2183 case Kevent: {
2184 /*
2185 * XXX XXX: The size of the array is determined by either the
2186 * next syscall argument, or by the syscall return value,
2187 * depending on which argument number we are. This matches the
2188 * kevent syscall, but luckily that's the only syscall that uses
2189 * them.
2190 */
2191 struct kevent *ke;
2192 int numevents = -1;
2193 size_t bytes;
2194 int i;
2195
2196 if (sc->offset == 1)
2197 numevents = args[sc->offset+1];
2198 else if (sc->offset == 3 && retval[0] != -1)
2199 numevents = retval[0];
2200
2201 if (numevents >= 0) {
2202 bytes = sizeof(struct kevent) * numevents;
2203 if ((ke = malloc(bytes)) == NULL)
2204 err(1,
2205 "Cannot malloc %zu bytes for kevent array",
2206 bytes);
2207 } else
2208 ke = NULL;
2209 if (numevents >= 0 && get_struct(trussinfo, p, args[sc->offset],
2210 ke, bytes) != -1) {
2211 fputc('{', fp);
2212 for (i = 0; i < numevents; i++) {
2213 fputc(' ', fp);
2214 print_kevent(fp, &ke[i]);
2215 }
2216 fputs(" }", fp);
2217 } else {
2218 print_pointer(fp, args[sc->offset]);
2219 }
2220 free(ke);
2221 break;
2222 }
2223 case Kevent11: {
2224 struct freebsd11_kevent *ke11;
2225 struct kevent ke;
2226 int numevents = -1;
2227 size_t bytes;
2228 int i;
2229
2230 if (sc->offset == 1)
2231 numevents = args[sc->offset+1];
2232 else if (sc->offset == 3 && retval[0] != -1)
2233 numevents = retval[0];
2234
2235 if (numevents >= 0) {
2236 bytes = sizeof(struct freebsd11_kevent) * numevents;
2237 if ((ke11 = malloc(bytes)) == NULL)
2238 err(1,
2239 "Cannot malloc %zu bytes for kevent array",
2240 bytes);
2241 } else
2242 ke11 = NULL;
2243 memset(&ke, 0, sizeof(ke));
2244 if (numevents >= 0 && get_struct(trussinfo, p, args[sc->offset],
2245 ke11, bytes) != -1) {
2246 fputc('{', fp);
2247 for (i = 0; i < numevents; i++) {
2248 fputc(' ', fp);
2249 ke.ident = ke11[i].ident;
2250 ke.filter = ke11[i].filter;
2251 ke.flags = ke11[i].flags;
2252 ke.fflags = ke11[i].fflags;
2253 ke.data = ke11[i].data;
2254 ke.udata = ke11[i].udata;
2255 print_kevent(fp, &ke);
2256 }
2257 fputs(" }", fp);
2258 } else {
2259 print_pointer(fp, args[sc->offset]);
2260 }
2261 free(ke11);
2262 break;
2263 }
2264 case Stat: {
2265 struct stat st;
2266
2267 if (get_struct(trussinfo, p, args[sc->offset], &st, sizeof(st))
2268 != -1) {
2269 char mode[12];
2270
2271 strmode(st.st_mode, mode);
2272 fprintf(fp,
2273 "{ mode=%s,inode=%ju,size=%jd,blksize=%ld }", mode,
2274 (uintmax_t)st.st_ino, (intmax_t)st.st_size,
2275 (long)st.st_blksize);
2276 } else {
2277 print_pointer(fp, args[sc->offset]);
2278 }
2279 break;
2280 }
2281 case Stat11: {
2282 struct freebsd11_stat st;
2283
2284 if (get_struct(trussinfo, p, args[sc->offset], &st, sizeof(st))
2285 != -1) {
2286 char mode[12];
2287
2288 strmode(st.st_mode, mode);
2289 fprintf(fp,
2290 "{ mode=%s,inode=%ju,size=%jd,blksize=%ld }", mode,
2291 (uintmax_t)st.st_ino, (intmax_t)st.st_size,
2292 (long)st.st_blksize);
2293 } else {
2294 print_pointer(fp, args[sc->offset]);
2295 }
2296 break;
2297 }
2298 case StatFs: {
2299 unsigned int i;
2300 struct statfs buf;
2301
2302 if (get_struct(trussinfo, p, args[sc->offset], &buf,
2303 sizeof(buf)) != -1) {
2304 char fsid[17];
2305
2306 bzero(fsid, sizeof(fsid));
2307 if (buf.f_fsid.val[0] != 0 || buf.f_fsid.val[1] != 0) {
2308 for (i = 0; i < sizeof(buf.f_fsid); i++)
2309 snprintf(&fsid[i*2],
2310 sizeof(fsid) - (i*2), "%02x",
2311 ((u_char *)&buf.f_fsid)[i]);
2312 }
2313 fprintf(fp,
2314 "{ fstypename=%s,mntonname=%s,mntfromname=%s,"
2315 "fsid=%s }", buf.f_fstypename, buf.f_mntonname,
2316 buf.f_mntfromname, fsid);
2317 } else
2318 print_pointer(fp, args[sc->offset]);
2319 break;
2320 }
2321
2322 case Rusage: {
2323 struct rusage ru;
2324
2325 if (get_struct(trussinfo, p, args[sc->offset], &ru, sizeof(ru))
2326 != -1) {
2327 fprintf(fp,
2328 "{ u=%jd.%06ld,s=%jd.%06ld,in=%ld,out=%ld }",
2329 (intmax_t)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
2330 (intmax_t)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
2331 ru.ru_inblock, ru.ru_oublock);
2332 } else
2333 print_pointer(fp, args[sc->offset]);
2334 break;
2335 }
2336 case Rlimit: {
2337 struct rlimit rl;
2338
2339 if (get_struct(trussinfo, p, args[sc->offset], &rl, sizeof(rl))
2340 != -1) {
2341 fprintf(fp, "{ cur=%ju,max=%ju }",
2342 rl.rlim_cur, rl.rlim_max);
2343 } else
2344 print_pointer(fp, args[sc->offset]);
2345 break;
2346 }
2347 case ExitStatus: {
2348 int status;
2349
2350 if (get_struct(trussinfo, p, args[sc->offset], &status,
2351 sizeof(status)) != -1) {
2352 fputs("{ ", fp);
2353 if (WIFCONTINUED(status))
2354 fputs("CONTINUED", fp);
2355 else if (WIFEXITED(status))
2356 fprintf(fp, "EXITED,val=%d",
2357 WEXITSTATUS(status));
2358 else if (WIFSIGNALED(status))
2359 fprintf(fp, "SIGNALED,sig=%s%s",
2360 strsig2(WTERMSIG(status)),
2361 WCOREDUMP(status) ? ",cored" : "");
2362 else
2363 fprintf(fp, "STOPPED,sig=%s",
2364 strsig2(WTERMSIG(status)));
2365 fputs(" }", fp);
2366 } else
2367 print_pointer(fp, args[sc->offset]);
2368 break;
2369 }
2370 case Waitoptions:
2371 print_mask_arg(sysdecode_wait6_options, fp, args[sc->offset]);
2372 break;
2373 case Idtype:
2374 print_integer_arg(sysdecode_idtype, fp, args[sc->offset]);
2375 break;
2376 case Procctl:
2377 print_integer_arg(sysdecode_procctl_cmd, fp, args[sc->offset]);
2378 break;
2379 case Umtxop: {
2380 int rem;
2381
2382 if (print_mask_arg_part(sysdecode_umtx_op_flags, fp,
2383 args[sc->offset], &rem))
2384 fprintf(fp, "|");
2385 print_integer_arg(sysdecode_umtx_op, fp, rem);
2386 break;
2387 }
2388 case Atfd:
2389 print_integer_arg(sysdecode_atfd, fp, args[sc->offset]);
2390 break;
2391 case Atflags:
2392 print_mask_arg(sysdecode_atflags, fp, args[sc->offset]);
2393 break;
2394 case Accessmode:
2395 print_mask_arg(sysdecode_access_mode, fp, args[sc->offset]);
2396 break;
2397 case Sysarch:
2398 print_integer_arg(sysdecode_sysarch_number, fp,
2399 args[sc->offset]);
2400 break;
2401 case Sysctl: {
2402 char name[BUFSIZ];
2403 int oid[CTL_MAXNAME + 2];
2404 size_t len;
2405
2406 memset(name, 0, sizeof(name));
2407 len = args[sc->offset + 1];
2408 if (get_struct(trussinfo, p, args[sc->offset], oid,
2409 len * sizeof(oid[0])) != -1) {
2410 fprintf(fp, "\"");
2411 if (oid[0] == CTL_SYSCTL) {
2412 fprintf(fp, "sysctl.");
2413 switch (oid[1]) {
2414 case CTL_SYSCTL_DEBUG:
2415 fprintf(fp, "debug");
2416 break;
2417 case CTL_SYSCTL_NAME:
2418 fprintf(fp, "name ");
2419 print_sysctl_oid(fp, oid + 2, len - 2);
2420 break;
2421 case CTL_SYSCTL_NEXT:
2422 fprintf(fp, "next");
2423 break;
2424 case CTL_SYSCTL_NAME2OID:
2425 fprintf(fp, "name2oid %s",
2426 get_string(trussinfo, p,
2427 args[sc->offset + 4],
2428 args[sc->offset + 5]));
2429 break;
2430 case CTL_SYSCTL_OIDFMT:
2431 fprintf(fp, "oidfmt ");
2432 print_sysctl(fp, oid + 2, len - 2);
2433 break;
2434 case CTL_SYSCTL_OIDDESCR:
2435 fprintf(fp, "oiddescr ");
2436 print_sysctl(fp, oid + 2, len - 2);
2437 break;
2438 case CTL_SYSCTL_OIDLABEL:
2439 fprintf(fp, "oidlabel ");
2440 print_sysctl(fp, oid + 2, len - 2);
2441 break;
2442 case CTL_SYSCTL_NEXTNOSKIP:
2443 fprintf(fp, "nextnoskip");
2444 break;
2445 default:
2446 print_sysctl(fp, oid + 1, len - 1);
2447 }
2448 } else {
2449 print_sysctl(fp, oid, len);
2450 }
2451 fprintf(fp, "\"");
2452 }
2453 break;
2454 }
2455 case PipeFds:
2456 /*
2457 * The pipe() system call in the kernel returns its
2458 * two file descriptors via return values. However,
2459 * the interface exposed by libc is that pipe()
2460 * accepts a pointer to an array of descriptors.
2461 * Format the output to match the libc API by printing
2462 * the returned file descriptors as a fake argument.
2463 *
2464 * Overwrite the first retval to signal a successful
2465 * return as well.
2466 */
2467 fprintf(fp, "{ %d, %d }", (int)retval[0], (int)retval[1]);
2468 retval[0] = 0;
2469 break;
2470 case Utrace: {
2471 size_t len;
2472 void *utrace_addr;
2473
2474 len = args[sc->offset + 1];
2475 utrace_addr = calloc(1, len);
2476 if (get_struct(trussinfo, p, args[sc->offset],
2477 (void *)utrace_addr, len) != -1)
2478 print_utrace(fp, utrace_addr, len);
2479 else
2480 print_pointer(fp, args[sc->offset]);
2481 free(utrace_addr);
2482 break;
2483 }
2484 case IntArray: {
2485 int descriptors[16];
2486 unsigned long i, ndescriptors;
2487 bool truncated;
2488
2489 ndescriptors = args[sc->offset + 1];
2490 truncated = false;
2491 if (ndescriptors > nitems(descriptors)) {
2492 ndescriptors = nitems(descriptors);
2493 truncated = true;
2494 }
2495 if (get_struct(trussinfo, p, args[sc->offset],
2496 descriptors, ndescriptors * sizeof(descriptors[0])) != -1) {
2497 fprintf(fp, "{");
2498 for (i = 0; i < ndescriptors; i++)
2499 fprintf(fp, i == 0 ? " %d" : ", %d",
2500 descriptors[i]);
2501 fprintf(fp, truncated ? ", ... }" : " }");
2502 } else
2503 print_pointer(fp, args[sc->offset]);
2504 break;
2505 }
2506 case Pipe2:
2507 print_mask_arg(sysdecode_pipe2_flags, fp, args[sc->offset]);
2508 break;
2509 case CapFcntlRights: {
2510 uint32_t rights;
2511
2512 if (sc->type & OUT) {
2513 if (get_struct(trussinfo, p, args[sc->offset], &rights,
2514 sizeof(rights)) == -1) {
2515 print_pointer(fp, args[sc->offset]);
2516 break;
2517 }
2518 } else
2519 rights = args[sc->offset];
2520 print_mask_arg32(sysdecode_cap_fcntlrights, fp, rights);
2521 break;
2522 }
2523 case Fadvice:
2524 print_integer_arg(sysdecode_fadvice, fp, args[sc->offset]);
2525 break;
2526 case FileFlags: {
2527 fflags_t rem;
2528
2529 if (!sysdecode_fileflags(fp, args[sc->offset], &rem))
2530 fprintf(fp, "0x%x", rem);
2531 else if (rem != 0)
2532 fprintf(fp, "|0x%x", rem);
2533 break;
2534 }
2535 case Flockop:
2536 print_mask_arg(sysdecode_flock_operation, fp, args[sc->offset]);
2537 break;
2538 case Getfsstatmode:
2539 print_integer_arg(sysdecode_getfsstat_mode, fp,
2540 args[sc->offset]);
2541 break;
2542 case Inotifyflags:
2543 print_mask_arg(sysdecode_inotifyflags, fp, args[sc->offset]);
2544 break;
2545 case Itimerwhich:
2546 print_integer_arg(sysdecode_itimer, fp, args[sc->offset]);
2547 break;
2548 case Kldsymcmd:
2549 print_integer_arg(sysdecode_kldsym_cmd, fp, args[sc->offset]);
2550 break;
2551 case Kldunloadflags:
2552 print_integer_arg(sysdecode_kldunload_flags, fp,
2553 args[sc->offset]);
2554 break;
2555 case AiofsyncOp:
2556 fputs(xlookup(aio_fsync_ops, args[sc->offset]), fp);
2557 break;
2558 case LioMode:
2559 fputs(xlookup(lio_modes, args[sc->offset]), fp);
2560 break;
2561 case Madvice:
2562 print_integer_arg(sysdecode_madvice, fp, args[sc->offset]);
2563 break;
2564 case Socklent:
2565 fprintf(fp, "%u", (socklen_t)args[sc->offset]);
2566 break;
2567 case Sockprotocol: {
2568 const char *temp;
2569 int domain, protocol;
2570
2571 domain = args[sc->offset - 2];
2572 protocol = args[sc->offset];
2573 if (protocol == 0) {
2574 fputs("0", fp);
2575 } else {
2576 temp = sysdecode_socket_protocol(domain, protocol);
2577 if (temp) {
2578 fputs(temp, fp);
2579 } else {
2580 fprintf(fp, "%d", protocol);
2581 }
2582 }
2583 break;
2584 }
2585 case Sockoptlevel:
2586 print_integer_arg(sysdecode_sockopt_level, fp,
2587 args[sc->offset]);
2588 break;
2589 case Sockoptname: {
2590 const char *temp;
2591 int level, name;
2592
2593 level = args[sc->offset - 1];
2594 name = args[sc->offset];
2595 temp = sysdecode_sockopt_name(level, name);
2596 if (temp) {
2597 fputs(temp, fp);
2598 } else {
2599 fprintf(fp, "%d", name);
2600 }
2601 break;
2602 }
2603 case Msgflags:
2604 print_mask_arg(sysdecode_msg_flags, fp, args[sc->offset]);
2605 break;
2606 case CapRights: {
2607 cap_rights_t rights;
2608
2609 if (get_struct(trussinfo, p, args[sc->offset], &rights,
2610 sizeof(rights)) != -1) {
2611 fputs("{ ", fp);
2612 sysdecode_cap_rights(fp, &rights);
2613 fputs(" }", fp);
2614 } else
2615 print_pointer(fp, args[sc->offset]);
2616 break;
2617 }
2618 case Acltype:
2619 print_integer_arg(sysdecode_acltype, fp, args[sc->offset]);
2620 break;
2621 case Extattrnamespace:
2622 print_integer_arg(sysdecode_extattrnamespace, fp,
2623 args[sc->offset]);
2624 break;
2625 case Minherit:
2626 print_integer_arg(sysdecode_minherit_inherit, fp,
2627 args[sc->offset]);
2628 break;
2629 case Mlockall:
2630 print_mask_arg(sysdecode_mlockall_flags, fp, args[sc->offset]);
2631 break;
2632 case Mountflags:
2633 print_mask_arg(sysdecode_mount_flags, fp, args[sc->offset]);
2634 break;
2635 case Msync:
2636 print_mask_arg(sysdecode_msync_flags, fp, args[sc->offset]);
2637 break;
2638 case Priowhich:
2639 print_integer_arg(sysdecode_prio_which, fp, args[sc->offset]);
2640 break;
2641 case Ptraceop:
2642 print_integer_arg(sysdecode_ptrace_request, fp,
2643 args[sc->offset]);
2644 break;
2645 case Sendfileflags:
2646 print_mask_arg(sysdecode_sendfile_flags, fp, args[sc->offset]);
2647 break;
2648 case Sendfilehdtr: {
2649 struct sf_hdtr hdtr;
2650
2651 if (get_struct(trussinfo, p, args[sc->offset], &hdtr,
2652 sizeof(hdtr)) != -1) {
2653 fprintf(fp, "{");
2654 print_iovec(fp, trussinfo, (uintptr_t)hdtr.headers,
2655 hdtr.hdr_cnt);
2656 print_iovec(fp, trussinfo, (uintptr_t)hdtr.trailers,
2657 hdtr.trl_cnt);
2658 fprintf(fp, "}");
2659 } else
2660 print_pointer(fp, args[sc->offset]);
2661 break;
2662 }
2663 case Quotactlcmd:
2664 if (!sysdecode_quotactl_cmd(fp, args[sc->offset]))
2665 fprintf(fp, "%#x", (int)args[sc->offset]);
2666 break;
2667 case Reboothowto:
2668 print_mask_arg(sysdecode_reboot_howto, fp, args[sc->offset]);
2669 break;
2670 case Rtpriofunc:
2671 print_integer_arg(sysdecode_rtprio_function, fp,
2672 args[sc->offset]);
2673 break;
2674 case Schedpolicy:
2675 print_integer_arg(sysdecode_scheduler_policy, fp,
2676 args[sc->offset]);
2677 break;
2678 case Schedparam: {
2679 struct sched_param sp;
2680
2681 if (get_struct(trussinfo, p, args[sc->offset], &sp,
2682 sizeof(sp)) != -1)
2683 fprintf(fp, "{ %d }", sp.sched_priority);
2684 else
2685 print_pointer(fp, args[sc->offset]);
2686 break;
2687 }
2688 case PSig: {
2689 int sig;
2690
2691 if (get_struct(trussinfo, p, args[sc->offset], &sig,
2692 sizeof(sig)) == 0)
2693 fprintf(fp, "{ %s }", strsig2(sig));
2694 else
2695 print_pointer(fp, args[sc->offset]);
2696 break;
2697 }
2698 case Siginfo: {
2699 siginfo_t si;
2700
2701 if (get_struct(trussinfo, p, args[sc->offset], &si,
2702 sizeof(si)) != -1) {
2703 fprintf(fp, "{ signo=%s", strsig2(si.si_signo));
2704 decode_siginfo(fp, &si);
2705 fprintf(fp, " }");
2706 } else
2707 print_pointer(fp, args[sc->offset]);
2708 break;
2709 }
2710 case Iovec:
2711 /*
2712 * Print argument as an array of struct iovec, where the next
2713 * syscall argument is the number of elements of the array.
2714 */
2715
2716 print_iovec(fp, trussinfo, args[sc->offset],
2717 (int)args[sc->offset + 1]);
2718 break;
2719 case Aiocb: {
2720 struct aiocb cb;
2721
2722 if (get_struct(trussinfo, p, args[sc->offset], &cb,
2723 sizeof(cb)) != -1)
2724 print_aiocb(fp, &cb);
2725 else
2726 print_pointer(fp, args[sc->offset]);
2727 break;
2728 }
2729 case AiocbArray: {
2730 /*
2731 * Print argment as an array of pointers to struct aiocb, where
2732 * the next syscall argument is the number of elements.
2733 */
2734 uintptr_t cbs[16];
2735 unsigned int nent;
2736 bool truncated;
2737
2738 nent = args[sc->offset + 1];
2739 truncated = false;
2740 if (nent > nitems(cbs)) {
2741 nent = nitems(cbs);
2742 truncated = true;
2743 }
2744
2745 if (get_struct(trussinfo, p, args[sc->offset], cbs,
2746 sizeof(uintptr_t) * nent) != -1) {
2747 unsigned int i;
2748 fputs("[", fp);
2749 for (i = 0; i < nent; ++i) {
2750 struct aiocb cb;
2751 if (i > 0)
2752 fputc(',', fp);
2753 if (get_struct(trussinfo, p, cbs[i], &cb,
2754 sizeof(cb)) != -1)
2755 print_aiocb(fp, &cb);
2756 else
2757 print_pointer(fp, cbs[i]);
2758 }
2759 if (truncated)
2760 fputs(",...", fp);
2761 fputs("]", fp);
2762 } else
2763 print_pointer(fp, args[sc->offset]);
2764 break;
2765 }
2766 case AiocbPointer: {
2767 /*
2768 * aio_waitcomplete(2) assigns a pointer to a pointer to struct
2769 * aiocb, so we need to handle the extra layer of indirection.
2770 */
2771 uintptr_t cbp;
2772 struct aiocb cb;
2773
2774 if (get_struct(trussinfo, p, args[sc->offset], &cbp,
2775 sizeof(cbp)) != -1) {
2776 if (get_struct(trussinfo, p, cbp, &cb, sizeof(cb))
2777 != -1)
2778 print_aiocb(fp, &cb);
2779 else
2780 print_pointer(fp, cbp);
2781 } else
2782 print_pointer(fp, args[sc->offset]);
2783 break;
2784 }
2785 case Sctpsndrcvinfo: {
2786 struct sctp_sndrcvinfo info;
2787
2788 if (get_struct(trussinfo, p, args[sc->offset],
2789 &info, sizeof(struct sctp_sndrcvinfo)) == -1) {
2790 print_pointer(fp, args[sc->offset]);
2791 break;
2792 }
2793 print_sctp_sndrcvinfo(fp, sc->type & OUT, &info);
2794 break;
2795 }
2796 case Msghdr: {
2797 struct msghdr msghdr;
2798 struct iovec iov;
2799
2800 if (get_struct(trussinfo, p, args[sc->offset],
2801 &msghdr, sizeof(struct msghdr)) == -1) {
2802 print_pointer(fp, args[sc->offset]);
2803 break;
2804 }
2805 fputs("{", fp);
2806 print_sockaddr(fp, trussinfo, (uintptr_t)msghdr.msg_name, msghdr.msg_namelen);
2807 fprintf(fp, ",%d,", msghdr.msg_namelen);
2808 /* Attempt Netlink decode; fallback to standard iovec if it fails. */
2809 int protocol = get_netlink_protocol(trussinfo, (int)args[0]);
2810 if ((protocol == -1) ||
2811 (get_struct(trussinfo, p, (uintptr_t)msghdr.msg_iov,
2812 &iov, sizeof(iov)) == -1) ||
2813 (!print_netlink(fp, trussinfo, (void *)iov.iov_base,
2814 (size_t)iov.iov_len, protocol))) {
2815 print_iovec(fp, trussinfo, (uintptr_t)msghdr.msg_iov,
2816 msghdr.msg_iovlen);
2817 }
2818 fprintf(fp, ",%d,", msghdr.msg_iovlen);
2819 print_cmsgs(fp, trussinfo, p, sc->type & OUT, &msghdr);
2820 fprintf(fp, ",%u,", msghdr.msg_controllen);
2821 print_mask_arg(sysdecode_msg_flags, fp, msghdr.msg_flags);
2822 fputs("}", fp);
2823 break;
2824 }
2825
2826 default:
2827 errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
2828 }
2829 fclose(fp);
2830 return (tmp);
2831 }
2832
2833 /*
2834 * Print (to outfile) the system call and its arguments.
2835 */
2836 void
print_syscall(struct trussinfo * trussinfo)2837 print_syscall(struct trussinfo *trussinfo)
2838 {
2839 struct threadinfo *t;
2840 const char *name;
2841 char **s_args;
2842 int i, len, nargs;
2843
2844 t = trussinfo->curthread;
2845
2846 name = t->cs.sc->name;
2847 nargs = t->cs.nargs;
2848 s_args = t->cs.s_args;
2849
2850 len = print_line_prefix(trussinfo);
2851 len += fprintf(trussinfo->outfile, "%s(", name);
2852
2853 for (i = 0; i < nargs; i++) {
2854 if (s_args[i] != NULL)
2855 len += fprintf(trussinfo->outfile, "%s", s_args[i]);
2856 else
2857 len += fprintf(trussinfo->outfile,
2858 "<missing argument>");
2859 len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ?
2860 "," : "");
2861 }
2862 len += fprintf(trussinfo->outfile, ")");
2863 for (i = 0; i < 6 - (len / 8); i++)
2864 fprintf(trussinfo->outfile, "\t");
2865 }
2866
2867 void
print_syscall_ret(struct trussinfo * trussinfo,int error,syscallarg_t * retval)2868 print_syscall_ret(struct trussinfo *trussinfo, int error, syscallarg_t *retval)
2869 {
2870 struct timespec timediff;
2871 struct threadinfo *t;
2872 struct syscall *sc;
2873
2874 t = trussinfo->curthread;
2875 sc = t->cs.sc;
2876 if (!sc->trace)
2877 return;
2878 if (trussinfo->flags & COUNTONLY) {
2879 timespecsub(&t->after, &t->before, &timediff);
2880 timespecadd(&sc->time, &timediff, &sc->time);
2881 sc->ncalls++;
2882 if (error != 0)
2883 sc->nerror++;
2884 return;
2885 }
2886
2887 print_syscall(trussinfo);
2888 fflush(trussinfo->outfile);
2889
2890 if (retval == NULL) {
2891 /*
2892 * This system call resulted in the current thread's exit,
2893 * so there is no return value or error to display.
2894 */
2895 fprintf(trussinfo->outfile, "\n");
2896 return;
2897 }
2898
2899 if (error == ERESTART)
2900 fprintf(trussinfo->outfile, " ERESTART\n");
2901 else if (error == EJUSTRETURN)
2902 fprintf(trussinfo->outfile, " EJUSTRETURN\n");
2903 else if (error != 0) {
2904 fprintf(trussinfo->outfile, " ERR#%d '%s'\n",
2905 sysdecode_freebsd_to_abi_errno(t->proc->abi->abi, error),
2906 strerror(error));
2907 } else if (sc->decode.ret_type == 2 &&
2908 t->proc->abi->pointer_size == 4) {
2909 off_t off;
2910 #if _BYTE_ORDER == _LITTLE_ENDIAN
2911 off = (off_t)retval[1] << 32 | retval[0];
2912 #else
2913 off = (off_t)retval[0] << 32 | retval[1];
2914 #endif
2915 fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", (intmax_t)off,
2916 (intmax_t)off);
2917 } else {
2918 fprintf(trussinfo->outfile, " = %jd (0x%jx)\n",
2919 (intmax_t)retval[0], (intmax_t)retval[0]);
2920 }
2921 }
2922
2923 void
print_summary(struct trussinfo * trussinfo)2924 print_summary(struct trussinfo *trussinfo)
2925 {
2926 struct timespec total = {0, 0};
2927 struct syscall *sc;
2928 int ncall, nerror;
2929
2930 fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n",
2931 "syscall", "seconds", "calls", "errors");
2932 ncall = nerror = 0;
2933 STAILQ_FOREACH(sc, &seen_syscalls, entries) {
2934 if (sc->ncalls) {
2935 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
2936 sc->name, (intmax_t)sc->time.tv_sec,
2937 sc->time.tv_nsec, sc->ncalls, sc->nerror);
2938 timespecadd(&total, &sc->time, &total);
2939 ncall += sc->ncalls;
2940 nerror += sc->nerror;
2941 }
2942 }
2943 fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n",
2944 "", "-------------", "-------", "-------");
2945 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
2946 "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror);
2947 }
2948