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