1 /* 2 * Copyright 1997 Sean Eric Fagan 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. All advertising materials mentioning features or use of this software 13 * must display the following acknowledgement: 14 * This product includes software developed by Sean Eric Fagan 15 * 4. Neither the name of the author may be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #ifndef lint 33 static const char rcsid[] = 34 "$FreeBSD$"; 35 #endif /* not lint */ 36 37 /* 38 * This file has routines used to print out system calls and their 39 * arguments. 40 */ 41 42 #include <sys/types.h> 43 #include <sys/mman.h> 44 #include <sys/procctl.h> 45 #include <sys/ptrace.h> 46 #include <sys/socket.h> 47 #include <sys/time.h> 48 #include <sys/un.h> 49 #include <sys/wait.h> 50 #include <netinet/in.h> 51 #include <arpa/inet.h> 52 #include <sys/ioccom.h> 53 #include <machine/atomic.h> 54 #include <errno.h> 55 #include <sys/umtx.h> 56 #include <sys/event.h> 57 #include <sys/stat.h> 58 #include <sys/resource.h> 59 60 #include <ctype.h> 61 #include <err.h> 62 #include <fcntl.h> 63 #include <poll.h> 64 #include <signal.h> 65 #include <stdint.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <time.h> 70 #include <unistd.h> 71 #include <vis.h> 72 73 #include "truss.h" 74 #include "extern.h" 75 #include "syscall.h" 76 77 /* 64-bit alignment on 32-bit platforms. */ 78 #ifdef __powerpc__ 79 #define QUAD_ALIGN 1 80 #else 81 #define QUAD_ALIGN 0 82 #endif 83 84 /* Number of slots needed for a 64-bit argument. */ 85 #ifdef __LP64__ 86 #define QUAD_SLOTS 1 87 #else 88 #define QUAD_SLOTS 2 89 #endif 90 91 /* 92 * This should probably be in its own file, sorted alphabetically. 93 */ 94 static struct syscall syscalls[] = { 95 { .name = "fcntl", .ret_type = 1, .nargs = 3, 96 .args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 2 } } }, 97 { .name = "fork", .ret_type = 1, .nargs = 0 }, 98 { .name = "vfork", .ret_type = 1, .nargs = 0 }, 99 { .name = "rfork", .ret_type = 1, .nargs = 1, 100 .args = { { Rforkflags, 0 } } }, 101 { .name = "getegid", .ret_type = 1, .nargs = 0 }, 102 { .name = "geteuid", .ret_type = 1, .nargs = 0 }, 103 { .name = "linux_readlink", .ret_type = 1, .nargs = 3, 104 .args = { { Name, 0 }, { Name | OUT, 1 }, { Int, 2 } } }, 105 { .name = "linux_socketcall", .ret_type = 1, .nargs = 2, 106 .args = { { Int, 0 }, { LinuxSockArgs, 1 } } }, 107 { .name = "getgid", .ret_type = 1, .nargs = 0 }, 108 { .name = "getpid", .ret_type = 1, .nargs = 0 }, 109 { .name = "getpgid", .ret_type = 1, .nargs = 1, 110 .args = { { Int, 0 } } }, 111 { .name = "getpgrp", .ret_type = 1, .nargs = 0 }, 112 { .name = "getppid", .ret_type = 1, .nargs = 0 }, 113 { .name = "getsid", .ret_type = 1, .nargs = 1, 114 .args = { { Int, 0 } } }, 115 { .name = "getuid", .ret_type = 1, .nargs = 0 }, 116 { .name = "readlink", .ret_type = 1, .nargs = 3, 117 .args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Int, 2 } } }, 118 { .name = "readlinkat", .ret_type = 1, .nargs = 4, 119 .args = { { Atfd, 0 }, { Name, 1 }, { Readlinkres | OUT, 2 }, 120 { Int, 3 } } }, 121 { .name = "lseek", .ret_type = 2, .nargs = 3, 122 .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN }, 123 { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } }, 124 { .name = "linux_lseek", .ret_type = 2, .nargs = 3, 125 .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } }, 126 { .name = "mmap", .ret_type = 2, .nargs = 6, 127 .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, 128 { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } }, 129 { .name = "linux_mkdir", .ret_type = 1, .nargs = 2, 130 .args = { { Name | IN, 0 }, { Int, 1 } } }, 131 { .name = "mprotect", .ret_type = 1, .nargs = 3, 132 .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } }, 133 { .name = "open", .ret_type = 1, .nargs = 3, 134 .args = { { Name | IN, 0 }, { Open, 1 }, { Octal, 2 } } }, 135 { .name = "openat", .ret_type = 1, .nargs = 4, 136 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Open, 2 }, 137 { Octal, 3 } } }, 138 { .name = "mkdir", .ret_type = 1, .nargs = 2, 139 .args = { { Name, 0 }, { Octal, 1 } } }, 140 { .name = "mkdirat", .ret_type = 1, .nargs = 3, 141 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } }, 142 { .name = "linux_open", .ret_type = 1, .nargs = 3, 143 .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } }, 144 { .name = "close", .ret_type = 1, .nargs = 1, 145 .args = { { Int, 0 } } }, 146 { .name = "link", .ret_type = 0, .nargs = 2, 147 .args = { { Name, 0 }, { Name, 1 } } }, 148 { .name = "linkat", .ret_type = 0, .nargs = 5, 149 .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 }, 150 { Atflags, 4 } } }, 151 { .name = "unlink", .ret_type = 0, .nargs = 1, 152 .args = { { Name, 0 } } }, 153 { .name = "unlinkat", .ret_type = 0, .nargs = 3, 154 .args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } }, 155 { .name = "chdir", .ret_type = 0, .nargs = 1, 156 .args = { { Name, 0 } } }, 157 { .name = "chroot", .ret_type = 0, .nargs = 1, 158 .args = { { Name, 0 } } }, 159 { .name = "mkfifo", .ret_type = 0, .nargs = 2, 160 .args = { { Name, 0 }, { Octal, 1 } } }, 161 { .name = "mkfifoat", .ret_type = 0, .nargs = 3, 162 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } }, 163 { .name = "mknod", .ret_type = 0, .nargs = 3, 164 .args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } }, 165 { .name = "mknodat", .ret_type = 0, .nargs = 4, 166 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } }, 167 { .name = "chmod", .ret_type = 0, .nargs = 2, 168 .args = { { Name, 0 }, { Octal, 1 } } }, 169 { .name = "fchmod", .ret_type = 0, .nargs = 2, 170 .args = { { Int, 0 }, { Octal, 1 } } }, 171 { .name = "lchmod", .ret_type = 0, .nargs = 2, 172 .args = { { Name, 0 }, { Octal, 1 } } }, 173 { .name = "fchmodat", .ret_type = 0, .nargs = 4, 174 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } }, 175 { .name = "chown", .ret_type = 0, .nargs = 3, 176 .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, 177 { .name = "fchown", .ret_type = 0, .nargs = 3, 178 .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } }, 179 { .name = "lchown", .ret_type = 0, .nargs = 3, 180 .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, 181 { .name = "fchownat", .ret_type = 0, .nargs = 5, 182 .args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 }, 183 { Atflags, 4 } } }, 184 { .name = "linux_stat64", .ret_type = 1, .nargs = 3, 185 .args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 } } }, 186 { .name = "mount", .ret_type = 0, .nargs = 4, 187 .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } }, 188 { .name = "umount", .ret_type = 0, .nargs = 2, 189 .args = { { Name, 0 }, { Int, 2 } } }, 190 { .name = "fstat", .ret_type = 1, .nargs = 2, 191 .args = { { Int, 0 }, { Stat | OUT, 1 } } }, 192 { .name = "fstatat", .ret_type = 1, .nargs = 4, 193 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat | OUT, 2 }, 194 { Atflags, 3 } } }, 195 { .name = "stat", .ret_type = 1, .nargs = 2, 196 .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, 197 { .name = "lstat", .ret_type = 1, .nargs = 2, 198 .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, 199 { .name = "linux_newstat", .ret_type = 1, .nargs = 2, 200 .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, 201 { .name = "linux_access", .ret_type = 1, .nargs = 2, 202 .args = { { Name, 0 }, { Accessmode, 1 } } }, 203 { .name = "linux_newfstat", .ret_type = 1, .nargs = 2, 204 .args = { { Int, 0 }, { Ptr | OUT, 1 } } }, 205 { .name = "write", .ret_type = 1, .nargs = 3, 206 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } }, 207 { .name = "ioctl", .ret_type = 1, .nargs = 3, 208 .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } }, 209 { .name = "break", .ret_type = 1, .nargs = 1, 210 .args = { { Ptr, 0 } } }, 211 { .name = "exit", .ret_type = 0, .nargs = 1, 212 .args = { { Hex, 0 } } }, 213 { .name = "access", .ret_type = 1, .nargs = 2, 214 .args = { { Name | IN, 0 }, { Accessmode, 1 } } }, 215 { .name = "eaccess", .ret_type = 1, .nargs = 2, 216 .args = { { Name | IN, 0 }, { Accessmode, 1 } } }, 217 { .name = "faccessat", .ret_type = 1, .nargs = 4, 218 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Accessmode, 2 }, 219 { Atflags, 3 } } }, 220 { .name = "sigaction", .ret_type = 1, .nargs = 3, 221 .args = { { Signal, 0 }, { Sigaction | IN, 1 }, 222 { Sigaction | OUT, 2 } } }, 223 { .name = "accept", .ret_type = 1, .nargs = 3, 224 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 225 { .name = "bind", .ret_type = 1, .nargs = 3, 226 .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 227 { .name = "bindat", .ret_type = 1, .nargs = 4, 228 .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 }, 229 { Int, 3 } } }, 230 { .name = "connect", .ret_type = 1, .nargs = 3, 231 .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 232 { .name = "connectat", .ret_type = 1, .nargs = 4, 233 .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 }, 234 { Int, 3 } } }, 235 { .name = "getpeername", .ret_type = 1, .nargs = 3, 236 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 237 { .name = "getsockname", .ret_type = 1, .nargs = 3, 238 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 239 { .name = "recvfrom", .ret_type = 1, .nargs = 6, 240 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, 241 { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, 242 { .name = "sendto", .ret_type = 1, .nargs = 6, 243 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, 244 { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, 245 { .name = "execve", .ret_type = 1, .nargs = 3, 246 .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, 247 { StringArray | IN, 2 } } }, 248 { .name = "linux_execve", .ret_type = 1, .nargs = 3, 249 .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, 250 { StringArray | IN, 2 } } }, 251 { .name = "kldload", .ret_type = 0, .nargs = 1, 252 .args = { { Name | IN, 0 } } }, 253 { .name = "kldunload", .ret_type = 0, .nargs = 1, 254 .args = { { Int, 0 } } }, 255 { .name = "kldfind", .ret_type = 0, .nargs = 1, 256 .args = { { Name | IN, 0 } } }, 257 { .name = "kldnext", .ret_type = 0, .nargs = 1, 258 .args = { { Int, 0 } } }, 259 { .name = "kldstat", .ret_type = 0, .nargs = 2, 260 .args = { { Int, 0 }, { Ptr, 1 } } }, 261 { .name = "kldfirstmod", .ret_type = 0, .nargs = 1, 262 .args = { { Int, 0 } } }, 263 { .name = "nanosleep", .ret_type = 0, .nargs = 1, 264 .args = { { Timespec, 0 } } }, 265 { .name = "select", .ret_type = 1, .nargs = 5, 266 .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, 267 { Timeval, 4 } } }, 268 { .name = "poll", .ret_type = 1, .nargs = 3, 269 .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } }, 270 { .name = "gettimeofday", .ret_type = 1, .nargs = 2, 271 .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } }, 272 { .name = "clock_gettime", .ret_type = 1, .nargs = 2, 273 .args = { { Int, 0 }, { Timespec | OUT, 1 } } }, 274 { .name = "getitimer", .ret_type = 1, .nargs = 2, 275 .args = { { Int, 0 }, { Itimerval | OUT, 2 } } }, 276 { .name = "setitimer", .ret_type = 1, .nargs = 3, 277 .args = { { Int, 0 }, { Itimerval, 1 }, { Itimerval | OUT, 2 } } }, 278 { .name = "kse_release", .ret_type = 0, .nargs = 1, 279 .args = { { Timespec, 0 } } }, 280 { .name = "kevent", .ret_type = 0, .nargs = 6, 281 .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, 282 { Int, 4 }, { Timespec, 5 } } }, 283 { .name = "sigprocmask", .ret_type = 0, .nargs = 3, 284 .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, 285 { .name = "unmount", .ret_type = 1, .nargs = 2, 286 .args = { { Name, 0 }, { Int, 1 } } }, 287 { .name = "socket", .ret_type = 1, .nargs = 3, 288 .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } }, 289 { .name = "getrusage", .ret_type = 1, .nargs = 2, 290 .args = { { Int, 0 }, { Rusage | OUT, 1 } } }, 291 { .name = "__getcwd", .ret_type = 1, .nargs = 2, 292 .args = { { Name | OUT, 0 }, { Int, 1 } } }, 293 { .name = "shutdown", .ret_type = 1, .nargs = 2, 294 .args = { { Int, 0 }, { Shutdown, 1 } } }, 295 { .name = "getrlimit", .ret_type = 1, .nargs = 2, 296 .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } }, 297 { .name = "setrlimit", .ret_type = 1, .nargs = 2, 298 .args = { { Resource, 0 }, { Rlimit | IN, 1 } } }, 299 { .name = "utimes", .ret_type = 1, .nargs = 2, 300 .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, 301 { .name = "lutimes", .ret_type = 1, .nargs = 2, 302 .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, 303 { .name = "futimes", .ret_type = 1, .nargs = 2, 304 .args = { { Int, 0 }, { Timeval2 | IN, 1 } } }, 305 { .name = "futimesat", .ret_type = 1, .nargs = 3, 306 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timeval2 | IN, 2 } } }, 307 { .name = "futimens", .ret_type = 1, .nargs = 2, 308 .args = { { Int, 0 }, { Timespec2 | IN, 1 } } }, 309 { .name = "utimensat", .ret_type = 1, .nargs = 4, 310 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timespec2 | IN, 2 }, 311 { Atflags, 3 } } }, 312 { .name = "chflags", .ret_type = 1, .nargs = 2, 313 .args = { { Name | IN, 0 }, { Hex, 1 } } }, 314 { .name = "lchflags", .ret_type = 1, .nargs = 2, 315 .args = { { Name | IN, 0 }, { Hex, 1 } } }, 316 { .name = "pathconf", .ret_type = 1, .nargs = 2, 317 .args = { { Name | IN, 0 }, { Pathconf, 1 } } }, 318 { .name = "pipe", .ret_type = 1, .nargs = 1, 319 .args = { { Ptr, 0 } } }, 320 { .name = "truncate", .ret_type = 1, .nargs = 3, 321 .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, 322 { .name = "ftruncate", .ret_type = 1, .nargs = 3, 323 .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, 324 { .name = "kill", .ret_type = 1, .nargs = 2, 325 .args = { { Int | IN, 0 }, { Signal | IN, 1 } } }, 326 { .name = "munmap", .ret_type = 1, .nargs = 2, 327 .args = { { Ptr, 0 }, { Int, 1 } } }, 328 { .name = "read", .ret_type = 1, .nargs = 3, 329 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } }, 330 { .name = "rename", .ret_type = 1, .nargs = 2, 331 .args = { { Name, 0 }, { Name, 1 } } }, 332 { .name = "renameat", .ret_type = 1, .nargs = 4, 333 .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 } } }, 334 { .name = "symlink", .ret_type = 1, .nargs = 2, 335 .args = { { Name, 0 }, { Name, 1 } } }, 336 { .name = "symlinkat", .ret_type = 1, .nargs = 3, 337 .args = { { Name, 0 }, { Atfd, 1 }, { Name, 2 } } }, 338 { .name = "posix_openpt", .ret_type = 1, .nargs = 1, 339 .args = { { Open, 0 } } }, 340 { .name = "wait4", .ret_type = 1, .nargs = 4, 341 .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 }, 342 { Rusage | OUT, 3 } } }, 343 { .name = "wait6", .ret_type = 1, .nargs = 6, 344 .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 }, 345 { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } }, 346 { .name = "procctl", .ret_type = 1, .nargs = 4, 347 .args = { { Idtype, 0 }, { Int, 1 }, { Procctl, 2 }, { Ptr, 3 } } }, 348 { .name = "_umtx_op", .ret_type = 1, .nargs = 5, 349 .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 }, 350 { Ptr, 4 } } }, 351 { .name = 0 }, 352 }; 353 354 /* Xlat idea taken from strace */ 355 struct xlat { 356 int val; 357 const char *str; 358 }; 359 360 #define X(a) { a, #a }, 361 #define XEND { 0, NULL } 362 363 static struct xlat kevent_filters[] = { 364 X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE) 365 X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER) 366 X(EVFILT_PROCDESC) X(EVFILT_FS) X(EVFILT_LIO) X(EVFILT_USER) 367 X(EVFILT_SENDFILE) XEND 368 }; 369 370 static struct xlat kevent_flags[] = { 371 X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT) 372 X(EV_CLEAR) X(EV_RECEIPT) X(EV_DISPATCH) X(EV_FORCEONESHOT) 373 X(EV_DROP) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND 374 }; 375 376 static struct xlat poll_flags[] = { 377 X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR) 378 X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND) 379 X(POLLWRBAND) X(POLLINIGNEOF) XEND 380 }; 381 382 static struct xlat mmap_flags[] = { 383 X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RESERVED0020) 384 X(MAP_RESERVED0040) X(MAP_RESERVED0080) X(MAP_RESERVED0100) 385 X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON) 386 X(MAP_EXCL) X(MAP_NOCORE) X(MAP_PREFAULT_READ) 387 #ifdef MAP_32BIT 388 X(MAP_32BIT) 389 #endif 390 XEND 391 }; 392 393 static struct xlat mprot_flags[] = { 394 X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND 395 }; 396 397 static struct xlat whence_arg[] = { 398 X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) X(SEEK_DATA) X(SEEK_HOLE) XEND 399 }; 400 401 static struct xlat sigaction_flags[] = { 402 X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP) 403 X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND 404 }; 405 406 static struct xlat fcntl_arg[] = { 407 X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL) 408 X(F_GETOWN) X(F_SETOWN) X(F_OGETLK) X(F_OSETLK) X(F_OSETLKW) 409 X(F_DUP2FD) X(F_GETLK) X(F_SETLK) X(F_SETLKW) X(F_SETLK_REMOTE) 410 X(F_READAHEAD) X(F_RDAHEAD) X(F_DUPFD_CLOEXEC) X(F_DUP2FD_CLOEXEC) 411 XEND 412 }; 413 414 static struct xlat fcntlfd_arg[] = { 415 X(FD_CLOEXEC) XEND 416 }; 417 418 static struct xlat fcntlfl_arg[] = { 419 X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW) 420 X(FRDAHEAD) X(O_DIRECT) XEND 421 }; 422 423 static struct xlat sockdomain_arg[] = { 424 X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK) 425 X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI) 426 X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet) 427 X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE) 428 X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX) 429 X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6) 430 X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER) 431 X(PF_ARP) X(PF_BLUETOOTH) X(PF_IEEE80211) X(PF_INET_SDP) 432 X(PF_INET6_SDP) XEND 433 }; 434 435 static struct xlat socktype_arg[] = { 436 X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM) 437 X(SOCK_SEQPACKET) XEND 438 }; 439 440 static struct xlat open_flags[] = { 441 X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK) 442 X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC) 443 X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY) 444 X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) 445 X(O_VERIFY) XEND 446 }; 447 448 static struct xlat shutdown_arg[] = { 449 X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND 450 }; 451 452 static struct xlat resource_arg[] = { 453 X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK) 454 X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC) 455 X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) X(RLIMIT_NPTS) 456 X(RLIMIT_SWAP) X(RLIMIT_KQUEUES) XEND 457 }; 458 459 static struct xlat pathconf_arg[] = { 460 X(_PC_LINK_MAX) X(_PC_MAX_CANON) X(_PC_MAX_INPUT) 461 X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF) 462 X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE) 463 X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO) 464 X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS) 465 X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE) 466 X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN) 467 X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX) 468 X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT) 469 X(_PC_ACL_NFS4) X(_PC_MIN_HOLE_SIZE) XEND 470 }; 471 472 static struct xlat rfork_flags[] = { 473 X(RFFDG) X(RFPROC) X(RFMEM) X(RFNOWAIT) X(RFCFDG) X(RFTHREAD) 474 X(RFSIGSHARE) X(RFLINUXTHPN) X(RFTSIGZMB) X(RFPPWAIT) XEND 475 }; 476 477 static struct xlat wait_options[] = { 478 X(WNOHANG) X(WUNTRACED) X(WCONTINUED) X(WNOWAIT) X(WEXITED) 479 X(WTRAPPED) XEND 480 }; 481 482 static struct xlat idtype_arg[] = { 483 X(P_PID) X(P_PPID) X(P_PGID) X(P_SID) X(P_CID) X(P_UID) X(P_GID) 484 X(P_ALL) X(P_LWPID) X(P_TASKID) X(P_PROJID) X(P_POOLID) X(P_JAILID) 485 X(P_CTID) X(P_CPUID) X(P_PSETID) XEND 486 }; 487 488 static struct xlat procctl_arg[] = { 489 X(PROC_SPROTECT) XEND 490 }; 491 492 static struct xlat umtx_ops[] = { 493 X(UMTX_OP_RESERVED0) X(UMTX_OP_RESERVED1) X(UMTX_OP_WAIT) 494 X(UMTX_OP_WAKE) X(UMTX_OP_MUTEX_TRYLOCK) X(UMTX_OP_MUTEX_LOCK) 495 X(UMTX_OP_MUTEX_UNLOCK) X(UMTX_OP_SET_CEILING) X(UMTX_OP_CV_WAIT) 496 X(UMTX_OP_CV_SIGNAL) X(UMTX_OP_CV_BROADCAST) X(UMTX_OP_WAIT_UINT) 497 X(UMTX_OP_RW_RDLOCK) X(UMTX_OP_RW_WRLOCK) X(UMTX_OP_RW_UNLOCK) 498 X(UMTX_OP_WAIT_UINT_PRIVATE) X(UMTX_OP_WAKE_PRIVATE) 499 X(UMTX_OP_MUTEX_WAIT) X(UMTX_OP_MUTEX_WAKE) X(UMTX_OP_SEM_WAIT) 500 X(UMTX_OP_SEM_WAKE) X(UMTX_OP_NWAKE_PRIVATE) X(UMTX_OP_MUTEX_WAKE2) 501 X(UMTX_OP_SEM2_WAIT) X(UMTX_OP_SEM2_WAKE) 502 XEND 503 }; 504 505 static struct xlat at_flags[] = { 506 X(AT_EACCESS) X(AT_SYMLINK_NOFOLLOW) X(AT_SYMLINK_FOLLOW) 507 X(AT_REMOVEDIR) XEND 508 }; 509 510 static struct xlat access_modes[] = { 511 X(R_OK) X(W_OK) X(X_OK) XEND 512 }; 513 514 #undef X 515 #undef XEND 516 517 /* 518 * Searches an xlat array for a value, and returns it if found. Otherwise 519 * return a string representation. 520 */ 521 static const char * 522 lookup(struct xlat *xlat, int val, int base) 523 { 524 static char tmp[16]; 525 526 for (; xlat->str != NULL; xlat++) 527 if (xlat->val == val) 528 return (xlat->str); 529 switch (base) { 530 case 8: 531 sprintf(tmp, "0%o", val); 532 break; 533 case 16: 534 sprintf(tmp, "0x%x", val); 535 break; 536 case 10: 537 sprintf(tmp, "%u", val); 538 break; 539 default: 540 errx(1,"Unknown lookup base"); 541 break; 542 } 543 return (tmp); 544 } 545 546 static const char * 547 xlookup(struct xlat *xlat, int val) 548 { 549 550 return (lookup(xlat, val, 16)); 551 } 552 553 /* Searches an xlat array containing bitfield values. Remaining bits 554 set after removing the known ones are printed at the end: 555 IN|0x400 */ 556 static char * 557 xlookup_bits(struct xlat *xlat, int val) 558 { 559 int len, rem; 560 static char str[512]; 561 562 len = 0; 563 rem = val; 564 for (; xlat->str != NULL; xlat++) { 565 if ((xlat->val & rem) == xlat->val) { 566 /* don't print the "all-bits-zero" string unless all 567 bits are really zero */ 568 if (xlat->val == 0 && val != 0) 569 continue; 570 len += sprintf(str + len, "%s|", xlat->str); 571 rem &= ~(xlat->val); 572 } 573 } 574 /* if we have leftover bits or didn't match anything */ 575 if (rem || len == 0) 576 len += sprintf(str + len, "0x%x", rem); 577 if (len && str[len - 1] == '|') 578 len--; 579 str[len] = 0; 580 return (str); 581 } 582 583 /* 584 * If/when the list gets big, it might be desirable to do it 585 * as a hash table or binary search. 586 */ 587 588 struct syscall * 589 get_syscall(const char *name) 590 { 591 struct syscall *sc; 592 593 sc = syscalls; 594 if (name == NULL) 595 return (NULL); 596 while (sc->name) { 597 if (strcmp(name, sc->name) == 0) 598 return (sc); 599 sc++; 600 } 601 return (NULL); 602 } 603 604 /* 605 * get_struct 606 * 607 * Copy a fixed amount of bytes from the process. 608 */ 609 610 static int 611 get_struct(pid_t pid, void *offset, void *buf, int len) 612 { 613 struct ptrace_io_desc iorequest; 614 615 iorequest.piod_op = PIOD_READ_D; 616 iorequest.piod_offs = offset; 617 iorequest.piod_addr = buf; 618 iorequest.piod_len = len; 619 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) 620 return (-1); 621 return (0); 622 } 623 624 #define MAXSIZE 4096 625 626 /* 627 * get_string 628 * Copy a string from the process. Note that it is 629 * expected to be a C string, but if max is set, it will 630 * only get that much. 631 */ 632 static char * 633 get_string(pid_t pid, void *addr, int max) 634 { 635 struct ptrace_io_desc iorequest; 636 char *buf, *nbuf; 637 size_t offset, size, totalsize; 638 639 offset = 0; 640 if (max) 641 size = max + 1; 642 else { 643 /* Read up to the end of the current page. */ 644 size = PAGE_SIZE - ((uintptr_t)addr % PAGE_SIZE); 645 if (size > MAXSIZE) 646 size = MAXSIZE; 647 } 648 totalsize = size; 649 buf = malloc(totalsize); 650 if (buf == NULL) 651 return (NULL); 652 for (;;) { 653 iorequest.piod_op = PIOD_READ_D; 654 iorequest.piod_offs = (char *)addr + offset; 655 iorequest.piod_addr = buf + offset; 656 iorequest.piod_len = size; 657 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) { 658 free(buf); 659 return (NULL); 660 } 661 if (memchr(buf + offset, '\0', size) != NULL) 662 return (buf); 663 offset += size; 664 if (totalsize < MAXSIZE && max == 0) { 665 size = MAXSIZE - totalsize; 666 if (size > PAGE_SIZE) 667 size = PAGE_SIZE; 668 nbuf = realloc(buf, totalsize + size); 669 if (nbuf == NULL) { 670 buf[totalsize - 1] = '\0'; 671 return (buf); 672 } 673 buf = nbuf; 674 totalsize += size; 675 } else { 676 buf[totalsize - 1] = '\0'; 677 return (buf); 678 } 679 } 680 } 681 682 static char * 683 strsig2(int sig) 684 { 685 char *tmp; 686 687 tmp = strsig(sig); 688 if (tmp == NULL) 689 asprintf(&tmp, "%d", sig); 690 return (tmp); 691 } 692 693 /* 694 * print_arg 695 * Converts a syscall argument into a string. Said string is 696 * allocated via malloc(), so needs to be free()'d. The file 697 * descriptor is for the process' memory (via /proc), and is used 698 * to get any data (where the argument is a pointer). sc is 699 * a pointer to the syscall description (see above); args is 700 * an array of all of the system call arguments. 701 */ 702 703 char * 704 print_arg(struct syscall_args *sc, unsigned long *args, long retval, 705 struct trussinfo *trussinfo) 706 { 707 char *tmp; 708 pid_t pid; 709 710 tmp = NULL; 711 pid = trussinfo->pid; 712 switch (sc->type & ARG_MASK) { 713 case Hex: 714 asprintf(&tmp, "0x%x", (int)args[sc->offset]); 715 break; 716 case Octal: 717 asprintf(&tmp, "0%o", (int)args[sc->offset]); 718 break; 719 case Int: 720 asprintf(&tmp, "%d", (int)args[sc->offset]); 721 break; 722 case LongHex: 723 asprintf(&tmp, "0x%lx", args[sc->offset]); 724 break; 725 case Name: { 726 /* NULL-terminated string. */ 727 char *tmp2; 728 tmp2 = get_string(pid, (void*)args[sc->offset], 0); 729 asprintf(&tmp, "\"%s\"", tmp2); 730 free(tmp2); 731 break; 732 } 733 case BinString: { 734 /* Binary block of data that might have printable characters. 735 XXX If type|OUT, assume that the length is the syscall's 736 return value. Otherwise, assume that the length of the block 737 is in the next syscall argument. */ 738 int max_string = trussinfo->strsize; 739 char tmp2[max_string+1], *tmp3; 740 int len; 741 int truncated = 0; 742 743 if (sc->type & OUT) 744 len = retval; 745 else 746 len = args[sc->offset + 1]; 747 748 /* Don't print more than max_string characters, to avoid word 749 wrap. If we have to truncate put some ... after the string. 750 */ 751 if (len > max_string) { 752 len = max_string; 753 truncated = 1; 754 } 755 if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len) 756 != -1) { 757 tmp3 = malloc(len * 4 + 1); 758 while (len) { 759 if (strvisx(tmp3, tmp2, len, 760 VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string) 761 break; 762 len--; 763 truncated = 1; 764 }; 765 asprintf(&tmp, "\"%s\"%s", tmp3, truncated ? 766 "..." : ""); 767 free(tmp3); 768 } else { 769 asprintf(&tmp, "0x%lx", args[sc->offset]); 770 } 771 break; 772 } 773 case StringArray: { 774 int num, size, i; 775 char *tmp2; 776 char *string; 777 char *strarray[100]; /* XXX This is ugly. */ 778 779 if (get_struct(pid, (void *)args[sc->offset], 780 (void *)&strarray, sizeof(strarray)) == -1) 781 err(1, "get_struct %p", (void *)args[sc->offset]); 782 num = 0; 783 size = 0; 784 785 /* Find out how large of a buffer we'll need. */ 786 while (strarray[num] != NULL) { 787 string = get_string(pid, (void*)strarray[num], 0); 788 size += strlen(string); 789 free(string); 790 num++; 791 } 792 size += 4 + (num * 4); 793 tmp = (char *)malloc(size); 794 tmp2 = tmp; 795 796 tmp2 += sprintf(tmp2, " ["); 797 for (i = 0; i < num; i++) { 798 string = get_string(pid, (void*)strarray[i], 0); 799 tmp2 += sprintf(tmp2, " \"%s\"%c", string, 800 (i + 1 == num) ? ' ' : ','); 801 free(string); 802 } 803 tmp2 += sprintf(tmp2, "]"); 804 break; 805 } 806 #ifdef __LP64__ 807 case Quad: 808 asprintf(&tmp, "0x%lx", args[sc->offset]); 809 break; 810 #else 811 case Quad: { 812 unsigned long long ll; 813 ll = *(unsigned long long *)(args + sc->offset); 814 asprintf(&tmp, "0x%llx", ll); 815 break; 816 } 817 #endif 818 case Ptr: 819 asprintf(&tmp, "0x%lx", args[sc->offset]); 820 break; 821 case Readlinkres: { 822 char *tmp2; 823 if (retval == -1) { 824 tmp = strdup(""); 825 break; 826 } 827 tmp2 = get_string(pid, (void*)args[sc->offset], retval); 828 asprintf(&tmp, "\"%s\"", tmp2); 829 free(tmp2); 830 break; 831 } 832 case Ioctl: { 833 const char *temp = ioctlname(args[sc->offset]); 834 if (temp) 835 tmp = strdup(temp); 836 else { 837 unsigned long arg = args[sc->offset]; 838 asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }", 839 arg, arg & IOC_OUT ? "R" : "", 840 arg & IOC_IN ? "W" : "", IOCGROUP(arg), 841 isprint(IOCGROUP(arg)) ? (char)IOCGROUP(arg) : '?', 842 arg & 0xFF, IOCPARM_LEN(arg)); 843 } 844 break; 845 } 846 case Timespec: { 847 struct timespec ts; 848 if (get_struct(pid, (void *)args[sc->offset], &ts, 849 sizeof(ts)) != -1) 850 asprintf(&tmp, "{ %ld.%09ld }", (long)ts.tv_sec, 851 ts.tv_nsec); 852 else 853 asprintf(&tmp, "0x%lx", args[sc->offset]); 854 break; 855 } 856 case Timespec2: { 857 struct timespec ts[2]; 858 FILE *fp; 859 size_t len; 860 const char *sep; 861 unsigned int i; 862 863 if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts)) 864 != -1) { 865 fp = open_memstream(&tmp, &len); 866 fputs("{ ", fp); 867 sep = ""; 868 for (i = 0; i < nitems(ts); i++) { 869 fputs(sep, fp); 870 sep = ", "; 871 switch (ts[i].tv_nsec) { 872 case UTIME_NOW: 873 fprintf(fp, "UTIME_NOW"); 874 break; 875 case UTIME_OMIT: 876 fprintf(fp, "UTIME_OMIT"); 877 break; 878 default: 879 fprintf(fp, "%ld.%09ld", 880 (long)ts[i].tv_sec, ts[i].tv_nsec); 881 break; 882 } 883 } 884 fputs(" }", fp); 885 fclose(fp); 886 } else 887 asprintf(&tmp, "0x%lx", args[sc->offset]); 888 break; 889 } 890 case Timeval: { 891 struct timeval tv; 892 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) 893 != -1) 894 asprintf(&tmp, "{ %ld.%06ld }", (long)tv.tv_sec, 895 tv.tv_usec); 896 else 897 asprintf(&tmp, "0x%lx", args[sc->offset]); 898 break; 899 } 900 case Timeval2: { 901 struct timeval tv[2]; 902 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) 903 != -1) 904 asprintf(&tmp, "{ %ld.%06ld, %ld.%06ld }", 905 (long)tv[0].tv_sec, tv[0].tv_usec, 906 (long)tv[1].tv_sec, tv[1].tv_usec); 907 else 908 asprintf(&tmp, "0x%lx", args[sc->offset]); 909 break; 910 } 911 case Itimerval: { 912 struct itimerval itv; 913 if (get_struct(pid, (void *)args[sc->offset], &itv, 914 sizeof(itv)) != -1) 915 asprintf(&tmp, "{ %ld.%06ld, %ld.%06ld }", 916 (long)itv.it_interval.tv_sec, 917 itv.it_interval.tv_usec, 918 (long)itv.it_value.tv_sec, 919 itv.it_value.tv_usec); 920 else 921 asprintf(&tmp, "0x%lx", args[sc->offset]); 922 break; 923 } 924 case LinuxSockArgs: 925 { 926 struct linux_socketcall_args largs; 927 if (get_struct(pid, (void *)args[sc->offset], (void *)&largs, 928 sizeof(largs)) == -1) { 929 err(1, "get_struct %p", (void *)args[sc->offset]); 930 } 931 const char *what; 932 char buf[30]; 933 934 switch (largs.what) { 935 case LINUX_SOCKET: 936 what = "LINUX_SOCKET"; 937 break; 938 case LINUX_BIND: 939 what = "LINUX_BIND"; 940 break; 941 case LINUX_CONNECT: 942 what = "LINUX_CONNECT"; 943 break; 944 case LINUX_LISTEN: 945 what = "LINUX_LISTEN"; 946 break; 947 case LINUX_ACCEPT: 948 what = "LINUX_ACCEPT"; 949 break; 950 case LINUX_GETSOCKNAME: 951 what = "LINUX_GETSOCKNAME"; 952 break; 953 case LINUX_GETPEERNAME: 954 what = "LINUX_GETPEERNAME"; 955 break; 956 case LINUX_SOCKETPAIR: 957 what = "LINUX_SOCKETPAIR"; 958 break; 959 case LINUX_SEND: 960 what = "LINUX_SEND"; 961 break; 962 case LINUX_RECV: 963 what = "LINUX_RECV"; 964 break; 965 case LINUX_SENDTO: 966 what = "LINUX_SENDTO"; 967 break; 968 case LINUX_RECVFROM: 969 what = "LINUX_RECVFROM"; 970 break; 971 case LINUX_SHUTDOWN: 972 what = "LINUX_SHUTDOWN"; 973 break; 974 case LINUX_SETSOCKOPT: 975 what = "LINUX_SETSOCKOPT"; 976 break; 977 case LINUX_GETSOCKOPT: 978 what = "LINUX_GETSOCKOPT"; 979 break; 980 case LINUX_SENDMSG: 981 what = "LINUX_SENDMSG"; 982 break; 983 case LINUX_RECVMSG: 984 what = "LINUX_RECVMSG"; 985 break; 986 default: 987 sprintf(buf, "%d", largs.what); 988 what = buf; 989 break; 990 } 991 asprintf(&tmp, "(0x%lx)%s, 0x%lx", args[sc->offset], what, 992 (long unsigned int)largs.args); 993 break; 994 } 995 case Pollfd: { 996 /* 997 * XXX: A Pollfd argument expects the /next/ syscall argument 998 * to be the number of fds in the array. This matches the poll 999 * syscall. 1000 */ 1001 struct pollfd *pfd; 1002 int numfds = args[sc->offset+1]; 1003 int bytes = sizeof(struct pollfd) * numfds; 1004 int i, tmpsize, u, used; 1005 const int per_fd = 100; 1006 1007 if ((pfd = malloc(bytes)) == NULL) 1008 err(1, "Cannot malloc %d bytes for pollfd array", 1009 bytes); 1010 if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) 1011 != -1) { 1012 used = 0; 1013 tmpsize = 1 + per_fd * numfds + 2; 1014 if ((tmp = malloc(tmpsize)) == NULL) 1015 err(1, "Cannot alloc %d bytes for poll output", 1016 tmpsize); 1017 1018 tmp[used++] = '{'; 1019 tmp[used++] = ' '; 1020 for (i = 0; i < numfds; i++) { 1021 1022 u = snprintf(tmp + used, per_fd, "%s%d/%s", 1023 i > 0 ? " " : "", pfd[i].fd, 1024 xlookup_bits(poll_flags, pfd[i].events)); 1025 if (u > 0) 1026 used += u < per_fd ? u : per_fd; 1027 } 1028 tmp[used++] = ' '; 1029 tmp[used++] = '}'; 1030 tmp[used++] = '\0'; 1031 } else { 1032 asprintf(&tmp, "0x%lx", args[sc->offset]); 1033 } 1034 free(pfd); 1035 break; 1036 } 1037 case Fd_set: { 1038 /* 1039 * XXX: A Fd_set argument expects the /first/ syscall argument 1040 * to be the number of fds in the array. This matches the 1041 * select syscall. 1042 */ 1043 fd_set *fds; 1044 int numfds = args[0]; 1045 int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; 1046 int i, tmpsize, u, used; 1047 const int per_fd = 20; 1048 1049 if ((fds = malloc(bytes)) == NULL) 1050 err(1, "Cannot malloc %d bytes for fd_set array", 1051 bytes); 1052 if (get_struct(pid, (void *)args[sc->offset], fds, bytes) 1053 != -1) { 1054 used = 0; 1055 tmpsize = 1 + numfds * per_fd + 2; 1056 if ((tmp = malloc(tmpsize)) == NULL) 1057 err(1, "Cannot alloc %d bytes for fd_set " 1058 "output", tmpsize); 1059 1060 tmp[used++] = '{'; 1061 tmp[used++] = ' '; 1062 for (i = 0; i < numfds; i++) { 1063 if (FD_ISSET(i, fds)) { 1064 u = snprintf(tmp + used, per_fd, "%d ", 1065 i); 1066 if (u > 0) 1067 used += u < per_fd ? u : per_fd; 1068 } 1069 } 1070 tmp[used++] = '}'; 1071 tmp[used++] = '\0'; 1072 } else 1073 asprintf(&tmp, "0x%lx", args[sc->offset]); 1074 free(fds); 1075 break; 1076 } 1077 case Signal: 1078 tmp = strsig2(args[sc->offset]); 1079 break; 1080 case Sigset: { 1081 long sig; 1082 sigset_t ss; 1083 int i, used; 1084 char *signame; 1085 1086 sig = args[sc->offset]; 1087 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 1088 sizeof(ss)) == -1) { 1089 asprintf(&tmp, "0x%lx", args[sc->offset]); 1090 break; 1091 } 1092 tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */ 1093 used = 0; 1094 for (i = 1; i < sys_nsig; i++) { 1095 if (sigismember(&ss, i)) { 1096 signame = strsig(i); 1097 used += sprintf(tmp + used, "%s|", signame); 1098 free(signame); 1099 } 1100 } 1101 if (used) 1102 tmp[used-1] = 0; 1103 else 1104 strcpy(tmp, "0x0"); 1105 break; 1106 } 1107 case Sigprocmask: { 1108 switch (args[sc->offset]) { 1109 #define S(a) case a: tmp = strdup(#a); break; 1110 S(SIG_BLOCK); 1111 S(SIG_UNBLOCK); 1112 S(SIG_SETMASK); 1113 #undef S 1114 } 1115 if (tmp == NULL) 1116 asprintf(&tmp, "0x%lx", args[sc->offset]); 1117 break; 1118 } 1119 case Fcntlflag: { 1120 /* XXX output depends on the value of the previous argument */ 1121 switch (args[sc->offset-1]) { 1122 case F_SETFD: 1123 tmp = strdup(xlookup_bits(fcntlfd_arg, 1124 args[sc->offset])); 1125 break; 1126 case F_SETFL: 1127 tmp = strdup(xlookup_bits(fcntlfl_arg, 1128 args[sc->offset])); 1129 break; 1130 case F_GETFD: 1131 case F_GETFL: 1132 case F_GETOWN: 1133 tmp = strdup(""); 1134 break; 1135 default: 1136 asprintf(&tmp, "0x%lx", args[sc->offset]); 1137 break; 1138 } 1139 break; 1140 } 1141 case Open: 1142 tmp = strdup(xlookup_bits(open_flags, args[sc->offset])); 1143 break; 1144 case Fcntl: 1145 tmp = strdup(xlookup(fcntl_arg, args[sc->offset])); 1146 break; 1147 case Mprot: 1148 tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset])); 1149 break; 1150 case Mmapflags: { 1151 char *base, *alignstr; 1152 int align, flags; 1153 1154 /* 1155 * MAP_ALIGNED can't be handled by xlookup_bits(), so 1156 * generate that string manually and prepend it to the 1157 * string from xlookup_bits(). Have to be careful to 1158 * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is 1159 * the only flag. 1160 */ 1161 flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK; 1162 align = args[sc->offset] & MAP_ALIGNMENT_MASK; 1163 if (align != 0) { 1164 if (align == MAP_ALIGNED_SUPER) 1165 alignstr = strdup("MAP_ALIGNED_SUPER"); 1166 else 1167 asprintf(&alignstr, "MAP_ALIGNED(%d)", 1168 align >> MAP_ALIGNMENT_SHIFT); 1169 if (flags == 0) { 1170 tmp = alignstr; 1171 break; 1172 } 1173 } else 1174 alignstr = NULL; 1175 base = strdup(xlookup_bits(mmap_flags, flags)); 1176 if (alignstr == NULL) { 1177 tmp = base; 1178 break; 1179 } 1180 asprintf(&tmp, "%s|%s", alignstr, base); 1181 free(alignstr); 1182 free(base); 1183 break; 1184 } 1185 case Whence: 1186 tmp = strdup(xlookup(whence_arg, args[sc->offset])); 1187 break; 1188 case Sockdomain: 1189 tmp = strdup(xlookup(sockdomain_arg, args[sc->offset])); 1190 break; 1191 case Socktype: 1192 tmp = strdup(xlookup(socktype_arg, args[sc->offset])); 1193 break; 1194 case Shutdown: 1195 tmp = strdup(xlookup(shutdown_arg, args[sc->offset])); 1196 break; 1197 case Resource: 1198 tmp = strdup(xlookup(resource_arg, args[sc->offset])); 1199 break; 1200 case Pathconf: 1201 tmp = strdup(xlookup(pathconf_arg, args[sc->offset])); 1202 break; 1203 case Rforkflags: 1204 tmp = strdup(xlookup_bits(rfork_flags, args[sc->offset])); 1205 break; 1206 case Sockaddr: { 1207 struct sockaddr_storage ss; 1208 char addr[64]; 1209 struct sockaddr_in *lsin; 1210 struct sockaddr_in6 *lsin6; 1211 struct sockaddr_un *sun; 1212 struct sockaddr *sa; 1213 char *p; 1214 u_char *q; 1215 int i; 1216 1217 if (args[sc->offset] == 0) { 1218 asprintf(&tmp, "NULL"); 1219 break; 1220 } 1221 1222 /* yuck: get ss_len */ 1223 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 1224 sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) 1225 err(1, "get_struct %p", (void *)args[sc->offset]); 1226 /* 1227 * If ss_len is 0, then try to guess from the sockaddr type. 1228 * AF_UNIX may be initialized incorrectly, so always frob 1229 * it by using the "right" size. 1230 */ 1231 if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) { 1232 switch (ss.ss_family) { 1233 case AF_INET: 1234 ss.ss_len = sizeof(*lsin); 1235 break; 1236 case AF_UNIX: 1237 ss.ss_len = sizeof(*sun); 1238 break; 1239 default: 1240 /* hurrrr */ 1241 break; 1242 } 1243 } 1244 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 1245 ss.ss_len) == -1) { 1246 err(2, "get_struct %p", (void *)args[sc->offset]); 1247 } 1248 1249 switch (ss.ss_family) { 1250 case AF_INET: 1251 lsin = (struct sockaddr_in *)&ss; 1252 inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr); 1253 asprintf(&tmp, "{ AF_INET %s:%d }", addr, 1254 htons(lsin->sin_port)); 1255 break; 1256 case AF_INET6: 1257 lsin6 = (struct sockaddr_in6 *)&ss; 1258 inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, 1259 sizeof addr); 1260 asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, 1261 htons(lsin6->sin6_port)); 1262 break; 1263 case AF_UNIX: 1264 sun = (struct sockaddr_un *)&ss; 1265 asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path); 1266 break; 1267 default: 1268 sa = (struct sockaddr *)&ss; 1269 asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data " 1270 "= { %n%*s } }", (int)sa->sa_len, 1271 (int)sa->sa_family, &i, 1272 6 * (int)(sa->sa_len - ((char *)&sa->sa_data - 1273 (char *)sa)), ""); 1274 if (tmp != NULL) { 1275 p = tmp + i; 1276 for (q = (u_char *)&sa->sa_data; 1277 q < (u_char *)sa + sa->sa_len; q++) 1278 p += sprintf(p, " %#02x,", *q); 1279 } 1280 } 1281 break; 1282 } 1283 case Sigaction: { 1284 struct sigaction sa; 1285 char *hand; 1286 const char *h; 1287 1288 if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa)) 1289 != -1) { 1290 asprintf(&hand, "%p", sa.sa_handler); 1291 if (sa.sa_handler == SIG_DFL) 1292 h = "SIG_DFL"; 1293 else if (sa.sa_handler == SIG_IGN) 1294 h = "SIG_IGN"; 1295 else 1296 h = hand; 1297 1298 asprintf(&tmp, "{ %s %s ss_t }", h, 1299 xlookup_bits(sigaction_flags, sa.sa_flags)); 1300 free(hand); 1301 } else 1302 asprintf(&tmp, "0x%lx", args[sc->offset]); 1303 break; 1304 } 1305 case Kevent: { 1306 /* 1307 * XXX XXX: the size of the array is determined by either the 1308 * next syscall argument, or by the syscall returnvalue, 1309 * depending on which argument number we are. This matches the 1310 * kevent syscall, but luckily that's the only syscall that uses 1311 * them. 1312 */ 1313 struct kevent *ke; 1314 int numevents = -1; 1315 int bytes = 0; 1316 int i, tmpsize, u, used; 1317 const int per_ke = 100; 1318 1319 if (sc->offset == 1) 1320 numevents = args[sc->offset+1]; 1321 else if (sc->offset == 3 && retval != -1) 1322 numevents = retval; 1323 1324 if (numevents >= 0) 1325 bytes = sizeof(struct kevent) * numevents; 1326 if ((ke = malloc(bytes)) == NULL) 1327 err(1, "Cannot malloc %d bytes for kevent array", 1328 bytes); 1329 if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset], 1330 ke, bytes) != -1) { 1331 used = 0; 1332 tmpsize = 1 + per_ke * numevents + 2; 1333 if ((tmp = malloc(tmpsize)) == NULL) 1334 err(1, "Cannot alloc %d bytes for kevent " 1335 "output", tmpsize); 1336 1337 tmp[used++] = '{'; 1338 tmp[used++] = ' '; 1339 for (i = 0; i < numevents; i++) { 1340 u = snprintf(tmp + used, per_ke, 1341 "%s%p,%s,%s,%d,%p,%p", 1342 i > 0 ? " " : "", 1343 (void *)ke[i].ident, 1344 xlookup(kevent_filters, ke[i].filter), 1345 xlookup_bits(kevent_flags, ke[i].flags), 1346 ke[i].fflags, 1347 (void *)ke[i].data, 1348 (void *)ke[i].udata); 1349 if (u > 0) 1350 used += u < per_ke ? u : per_ke; 1351 } 1352 tmp[used++] = ' '; 1353 tmp[used++] = '}'; 1354 tmp[used++] = '\0'; 1355 } else { 1356 asprintf(&tmp, "0x%lx", args[sc->offset]); 1357 } 1358 free(ke); 1359 break; 1360 } 1361 case Stat: { 1362 struct stat st; 1363 if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st)) 1364 != -1) { 1365 char mode[12]; 1366 strmode(st.st_mode, mode); 1367 asprintf(&tmp, 1368 "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode, 1369 (intmax_t)st.st_ino, (intmax_t)st.st_size, 1370 (long)st.st_blksize); 1371 } else { 1372 asprintf(&tmp, "0x%lx", args[sc->offset]); 1373 } 1374 break; 1375 } 1376 case Rusage: { 1377 struct rusage ru; 1378 if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) 1379 != -1) { 1380 asprintf(&tmp, 1381 "{ u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld }", 1382 (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, 1383 (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, 1384 ru.ru_inblock, ru.ru_oublock); 1385 } else 1386 asprintf(&tmp, "0x%lx", args[sc->offset]); 1387 break; 1388 } 1389 case Rlimit: { 1390 struct rlimit rl; 1391 if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl)) 1392 != -1) { 1393 asprintf(&tmp, "{ cur=%ju,max=%ju }", 1394 rl.rlim_cur, rl.rlim_max); 1395 } else 1396 asprintf(&tmp, "0x%lx", args[sc->offset]); 1397 break; 1398 } 1399 case ExitStatus: { 1400 char *signame; 1401 int status; 1402 signame = NULL; 1403 if (get_struct(pid, (void *)args[sc->offset], &status, 1404 sizeof(status)) != -1) { 1405 if (WIFCONTINUED(status)) 1406 tmp = strdup("{ CONTINUED }"); 1407 else if (WIFEXITED(status)) 1408 asprintf(&tmp, "{ EXITED,val=%d }", 1409 WEXITSTATUS(status)); 1410 else if (WIFSIGNALED(status)) 1411 asprintf(&tmp, "{ SIGNALED,sig=%s%s }", 1412 signame = strsig2(WTERMSIG(status)), 1413 WCOREDUMP(status) ? ",cored" : ""); 1414 else 1415 asprintf(&tmp, "{ STOPPED,sig=%s }", 1416 signame = strsig2(WTERMSIG(status))); 1417 } else 1418 asprintf(&tmp, "0x%lx", args[sc->offset]); 1419 free(signame); 1420 break; 1421 } 1422 case Waitoptions: 1423 tmp = strdup(xlookup_bits(wait_options, args[sc->offset])); 1424 break; 1425 case Idtype: 1426 tmp = strdup(xlookup(idtype_arg, args[sc->offset])); 1427 break; 1428 case Procctl: 1429 tmp = strdup(xlookup(procctl_arg, args[sc->offset])); 1430 break; 1431 case Umtxop: 1432 tmp = strdup(xlookup(umtx_ops, args[sc->offset])); 1433 break; 1434 case Atfd: 1435 if ((int)args[sc->offset] == AT_FDCWD) 1436 tmp = strdup("AT_FDCWD"); 1437 else 1438 asprintf(&tmp, "%d", (int)args[sc->offset]); 1439 break; 1440 case Atflags: 1441 tmp = strdup(xlookup_bits(at_flags, args[sc->offset])); 1442 break; 1443 case Accessmode: 1444 if (args[sc->offset] == F_OK) 1445 tmp = strdup("F_OK"); 1446 else 1447 tmp = strdup(xlookup_bits(access_modes, 1448 args[sc->offset])); 1449 break; 1450 default: 1451 errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); 1452 } 1453 return (tmp); 1454 } 1455 1456 /* 1457 * print_syscall 1458 * Print (to outfile) the system call and its arguments. Note that 1459 * nargs is the number of arguments (not the number of words; this is 1460 * potentially confusing, I know). 1461 */ 1462 1463 void 1464 print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, 1465 char **s_args) 1466 { 1467 struct timespec timediff; 1468 int i, len; 1469 1470 len = 0; 1471 if (trussinfo->flags & FOLLOWFORKS) 1472 len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); 1473 1474 if (name != NULL && (strcmp(name, "execve") == 0 || 1475 strcmp(name, "exit") == 0)) { 1476 clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); 1477 } 1478 1479 if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 1480 timespecsubt(&trussinfo->curthread->after, 1481 &trussinfo->start_time, &timediff); 1482 len += fprintf(trussinfo->outfile, "%ld.%09ld ", 1483 (long)timediff.tv_sec, timediff.tv_nsec); 1484 } 1485 1486 if (trussinfo->flags & RELATIVETIMESTAMPS) { 1487 timespecsubt(&trussinfo->curthread->after, 1488 &trussinfo->curthread->before, &timediff); 1489 len += fprintf(trussinfo->outfile, "%ld.%09ld ", 1490 (long)timediff.tv_sec, timediff.tv_nsec); 1491 } 1492 1493 len += fprintf(trussinfo->outfile, "%s(", name); 1494 1495 for (i = 0; i < nargs; i++) { 1496 if (s_args[i]) 1497 len += fprintf(trussinfo->outfile, "%s", s_args[i]); 1498 else 1499 len += fprintf(trussinfo->outfile, 1500 "<missing argument>"); 1501 len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? 1502 "," : ""); 1503 } 1504 len += fprintf(trussinfo->outfile, ")"); 1505 for (i = 0; i < 6 - (len / 8); i++) 1506 fprintf(trussinfo->outfile, "\t"); 1507 } 1508 1509 void 1510 print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, 1511 char **s_args, int errorp, long retval, struct syscall *sc) 1512 { 1513 struct timespec timediff; 1514 1515 if (trussinfo->flags & COUNTONLY) { 1516 if (!sc) 1517 return; 1518 clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); 1519 timespecsubt(&trussinfo->curthread->after, 1520 &trussinfo->curthread->before, &timediff); 1521 timespecadd(&sc->time, &timediff, &sc->time); 1522 sc->ncalls++; 1523 if (errorp) 1524 sc->nerror++; 1525 return; 1526 } 1527 1528 print_syscall(trussinfo, name, nargs, s_args); 1529 fflush(trussinfo->outfile); 1530 if (errorp) 1531 fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval, 1532 strerror(retval)); 1533 else { 1534 /* 1535 * Because pipe(2) has a special assembly glue to provide the 1536 * libc API, we have to adjust retval. 1537 */ 1538 if (name != NULL && strcmp(name, "pipe") == 0) 1539 retval = 0; 1540 fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval); 1541 } 1542 } 1543 1544 void 1545 print_summary(struct trussinfo *trussinfo) 1546 { 1547 struct timespec total = {0, 0}; 1548 struct syscall *sc; 1549 int ncall, nerror; 1550 1551 fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n", 1552 "syscall", "seconds", "calls", "errors"); 1553 ncall = nerror = 0; 1554 for (sc = syscalls; sc->name != NULL; sc++) 1555 if (sc->ncalls) { 1556 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", 1557 sc->name, (intmax_t)sc->time.tv_sec, 1558 sc->time.tv_nsec, sc->ncalls, sc->nerror); 1559 timespecadd(&total, &sc->time, &total); 1560 ncall += sc->ncalls; 1561 nerror += sc->nerror; 1562 } 1563 fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n", 1564 "", "-------------", "-------", "-------"); 1565 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", 1566 "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror); 1567 } 1568