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 33static 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 */ 94static struct syscall syscalls[] = { 95 { .name = "fcntl", .ret_type = 1, .nargs = 3, 96 .args = { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 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 = "lseek", .ret_type = 2, .nargs = 3, 119 .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN }, { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } }, 120 { .name = "linux_lseek", .ret_type = 2, .nargs = 3, 121 .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } }, 122 { .name = "mmap", .ret_type = 2, .nargs = 6, 123 .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } }, 124 { .name = "linux_mkdir", .ret_type = 1, .nargs = 2, 125 .args = { { Name | IN, 0} , {Int, 1}}}, 126 { .name = "mprotect", .ret_type = 1, .nargs = 3, 127 .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } }, 128 { .name = "open", .ret_type = 1, .nargs = 3, 129 .args = { { Name | IN, 0 } , { Open, 1 }, { Octal, 2 } } }, 130 { .name = "mkdir", .ret_type = 1, .nargs = 2, 131 .args = { { Name, 0 } , { Octal, 1 } } }, 132 { .name = "linux_open", .ret_type = 1, .nargs = 3, 133 .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } }, 134 { .name = "close", .ret_type = 1, .nargs = 1, 135 .args = { { Int, 0 } } }, 136 { .name = "link", .ret_type = 0, .nargs = 2, 137 .args = { { Name, 0 }, { Name, 1 } } }, 138 { .name = "unlink", .ret_type = 0, .nargs = 1, 139 .args = { { Name, 0 } } }, 140 { .name = "chdir", .ret_type = 0, .nargs = 1, 141 .args = { { Name, 0 } } }, 142 { .name = "chroot", .ret_type = 0, .nargs = 1, 143 .args = { { Name, 0 } } }, 144 { .name = "mknod", .ret_type = 0, .nargs = 3, 145 .args = { { Name, 0 }, { Octal, 1 }, { Int, 3 } } }, 146 { .name = "chmod", .ret_type = 0, .nargs = 2, 147 .args = { { Name, 0 }, { Octal, 1 } } }, 148 { .name = "chown", .ret_type = 0, .nargs = 3, 149 .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, 150 { .name = "linux_stat64", .ret_type = 1, .nargs = 3, 151 .args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 }}}, 152 { .name = "mount", .ret_type = 0, .nargs = 4, 153 .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } }, 154 { .name = "umount", .ret_type = 0, .nargs = 2, 155 .args = { { Name, 0 }, { Int, 2 } } }, 156 { .name = "fstat", .ret_type = 1, .nargs = 2, 157 .args = { { Int, 0 }, { Stat | OUT , 1 } } }, 158 { .name = "stat", .ret_type = 1, .nargs = 2, 159 .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, 160 { .name = "lstat", .ret_type = 1, .nargs = 2, 161 .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, 162 { .name = "linux_newstat", .ret_type = 1, .nargs = 2, 163 .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, 164 { .name = "linux_access", .ret_type = 1, .nargs = 2, 165 .args = { { Name, 0 }, { Int, 1 }}}, 166 { .name = "linux_newfstat", .ret_type = 1, .nargs = 2, 167 .args = { { Int, 0 }, { Ptr | OUT, 1 } } }, 168 { .name = "write", .ret_type = 1, .nargs = 3, 169 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } }, 170 { .name = "ioctl", .ret_type = 1, .nargs = 3, 171 .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } }, 172 { .name = "break", .ret_type = 1, .nargs = 1, 173 .args = { { Ptr, 0 } } }, 174 { .name = "exit", .ret_type = 0, .nargs = 1, 175 .args = { { Hex, 0 } } }, 176 { .name = "access", .ret_type = 1, .nargs = 2, 177 .args = { { Name | IN, 0 }, { Int, 1 } } }, 178 { .name = "sigaction", .ret_type = 1, .nargs = 3, 179 .args = { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 } } }, 180 { .name = "accept", .ret_type = 1, .nargs = 3, 181 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 182 { .name = "bind", .ret_type = 1, .nargs = 3, 183 .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 184 { .name = "connect", .ret_type = 1, .nargs = 3, 185 .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 186 { .name = "getpeername", .ret_type = 1, .nargs = 3, 187 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 188 { .name = "getsockname", .ret_type = 1, .nargs = 3, 189 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 190 { .name = "recvfrom", .ret_type = 1, .nargs = 6, 191 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, 192 { .name = "sendto", .ret_type = 1, .nargs = 6, 193 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, 194 { .name = "execve", .ret_type = 1, .nargs = 3, 195 .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, 196 { .name = "linux_execve", .ret_type = 1, .nargs = 3, 197 .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, 198 { .name = "kldload", .ret_type = 0, .nargs = 1, 199 .args = { { Name | IN, 0 } } }, 200 { .name = "kldunload", .ret_type = 0, .nargs = 1, 201 .args = { { Int, 0 } } }, 202 { .name = "kldfind", .ret_type = 0, .nargs = 1, 203 .args = { { Name | IN, 0 } } }, 204 { .name = "kldnext", .ret_type = 0, .nargs = 1, 205 .args = { { Int, 0 } } }, 206 { .name = "kldstat", .ret_type = 0, .nargs = 2, 207 .args = { { Int, 0 }, { Ptr, 1 } } }, 208 { .name = "kldfirstmod", .ret_type = 0, .nargs = 1, 209 .args = { { Int, 0 } } }, 210 { .name = "nanosleep", .ret_type = 0, .nargs = 1, 211 .args = { { Timespec, 0 } } }, 212 { .name = "select", .ret_type = 1, .nargs = 5, 213 .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 } } }, 214 { .name = "poll", .ret_type = 1, .nargs = 3, 215 .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } }, 216 { .name = "gettimeofday", .ret_type = 1, .nargs = 2, 217 .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } }, 218 { .name = "clock_gettime", .ret_type = 1, .nargs = 2, 219 .args = { { Int, 0 }, { Timespec | OUT, 1 } } }, 220 { .name = "getitimer", .ret_type = 1, .nargs = 2, 221 .args = { { Int, 0 }, { Itimerval | OUT, 2 } } }, 222 { .name = "setitimer", .ret_type = 1, .nargs = 3, 223 .args = { { Int, 0 }, { Itimerval, 1 } , { Itimerval | OUT, 2 } } }, 224 { .name = "kse_release", .ret_type = 0, .nargs = 1, 225 .args = { { Timespec, 0 } } }, 226 { .name = "kevent", .ret_type = 0, .nargs = 6, 227 .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 } } }, 228 { .name = "_umtx_lock", .ret_type = 0, .nargs = 1, 229 .args = { { Umtx, 0 } } }, 230 { .name = "_umtx_unlock", .ret_type = 0, .nargs = 1, 231 .args = { { Umtx, 0 } } }, 232 { .name = "sigprocmask", .ret_type = 0, .nargs = 3, 233 .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, 234 { .name = "unmount", .ret_type = 1, .nargs = 2, 235 .args = { { Name, 0 }, { Int, 1 } } }, 236 { .name = "socket", .ret_type = 1, .nargs = 3, 237 .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } }, 238 { .name = "getrusage", .ret_type = 1, .nargs = 2, 239 .args = { { Int, 0 }, { Rusage | OUT, 1 } } }, 240 { .name = "__getcwd", .ret_type = 1, .nargs = 2, 241 .args = { { Name | OUT, 0 }, { Int, 1 } } }, 242 { .name = "shutdown", .ret_type = 1, .nargs = 2, 243 .args = { { Int, 0 }, { Shutdown, 1 } } }, 244 { .name = "getrlimit", .ret_type = 1, .nargs = 2, 245 .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } }, 246 { .name = "setrlimit", .ret_type = 1, .nargs = 2, 247 .args = { { Resource, 0 }, { Rlimit | IN, 1 } } }, 248 { .name = "utimes", .ret_type = 1, .nargs = 2, 249 .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, 250 { .name = "lutimes", .ret_type = 1, .nargs = 2, 251 .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, 252 { .name = "futimes", .ret_type = 1, .nargs = 2, 253 .args = { { Int, 0 }, { Timeval | IN, 1 } } }, 254 { .name = "chflags", .ret_type = 1, .nargs = 2, 255 .args = { { Name | IN, 0 }, { Hex, 1 } } }, 256 { .name = "lchflags", .ret_type = 1, .nargs = 2, 257 .args = { { Name | IN, 0 }, { Hex, 1 } } }, 258 { .name = "pathconf", .ret_type = 1, .nargs = 2, 259 .args = { { Name | IN, 0 }, { Pathconf, 1 } } }, 260 { .name = "pipe", .ret_type = 1, .nargs = 1, 261 .args = { { Ptr, 0 } } }, 262 { .name = "truncate", .ret_type = 1, .nargs = 3, 263 .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, 264 { .name = "ftruncate", .ret_type = 1, .nargs = 3, 265 .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, 266 { .name = "kill", .ret_type = 1, .nargs = 2, 267 .args = { { Int | IN, 0 }, { Signal | IN, 1 } } }, 268 { .name = "munmap", .ret_type = 1, .nargs = 2, 269 .args = { { Ptr, 0 }, { Int, 1 } } }, 270 { .name = "read", .ret_type = 1, .nargs = 3, 271 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } }, 272 { .name = "rename", .ret_type = 1, .nargs = 2, 273 .args = { { Name , 0 } , { Name, 1 } } }, 274 { .name = "symlink", .ret_type = 1, .nargs = 2, 275 .args = { { Name , 0 } , { Name, 1 } } }, 276 { .name = "posix_openpt", .ret_type = 1, .nargs = 1, 277 .args = { { Open, 0 } } }, 278 { .name = "wait4", .ret_type = 1, .nargs = 4, 279 .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 }, 280 { Rusage | OUT, 3 } } }, 281 { .name = "wait6", .ret_type = 1, .nargs = 6, 282 .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 }, 283 { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } }, 284 { .name = "procctl", .ret_type = 1, .nargs = 4, 285 .args = { { Idtype, 0 }, { Int, 1 }, { Procctl, 2 }, { Ptr, 3 } } }, 286 { .name = 0 }, 287}; 288 289/* Xlat idea taken from strace */ 290struct xlat { 291 int val; 292 const char *str; 293}; 294 295#define X(a) { a, #a }, 296#define XEND { 0, NULL } 297 298static struct xlat kevent_filters[] = { 299 X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE) 300 X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER) 301 X(EVFILT_FS) X(EVFILT_READ) XEND 302}; 303 304static struct xlat kevent_flags[] = { 305 X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT) 306 X(EV_CLEAR) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND 307}; 308 309static struct xlat poll_flags[] = { 310 X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR) 311 X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND) 312 X(POLLWRBAND) X(POLLINIGNEOF) XEND 313}; 314 315static struct xlat mmap_flags[] = { 316 X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME) 317 X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100) 318 X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON) 319 X(MAP_NOCORE) X(MAP_PREFAULT_READ) 320#ifdef MAP_32BIT 321 X(MAP_32BIT) 322#endif 323 XEND 324}; 325 326static struct xlat mprot_flags[] = { 327 X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND 328}; 329 330static struct xlat whence_arg[] = { 331 X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) XEND 332}; 333 334static struct xlat sigaction_flags[] = { 335 X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP) 336 X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND 337}; 338 339static struct xlat fcntl_arg[] = { 340 X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL) 341 X(F_GETOWN) X(F_SETOWN) X(F_GETLK) X(F_SETLK) X(F_SETLKW) XEND 342}; 343 344static struct xlat fcntlfd_arg[] = { 345 X(FD_CLOEXEC) XEND 346}; 347 348static struct xlat fcntlfl_arg[] = { 349 X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW) 350 X(O_DIRECT) XEND 351}; 352 353static struct xlat sockdomain_arg[] = { 354 X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK) 355 X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI) 356 X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet) 357 X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE) 358 X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX) 359 X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6) 360 X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER) 361 X(PF_ARP) X(PF_BLUETOOTH) XEND 362}; 363 364static struct xlat socktype_arg[] = { 365 X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM) 366 X(SOCK_SEQPACKET) XEND 367}; 368 369static struct xlat open_flags[] = { 370 X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK) 371 X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC) 372 X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY) 373 X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) XEND 374}; 375 376static struct xlat shutdown_arg[] = { 377 X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND 378}; 379 380static struct xlat resource_arg[] = { 381 X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK) 382 X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC) 383 X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) XEND 384}; 385 386static struct xlat pathconf_arg[] = { 387 X(_PC_LINK_MAX) X(_PC_MAX_CANON) X(_PC_MAX_INPUT) 388 X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF) 389 X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE) 390 X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO) 391 X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS) 392 X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE) 393 X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN) 394 X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX) 395 X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT) 396 XEND 397}; 398 399static struct xlat rfork_flags[] = { 400 X(RFPROC) X(RFNOWAIT) X(RFFDG) X(RFCFDG) X(RFTHREAD) X(RFMEM) 401 X(RFSIGSHARE) X(RFTSIGZMB) X(RFLINUXTHPN) XEND 402}; 403 404static struct xlat wait_options[] = { 405 X(WNOHANG) X(WUNTRACED) X(WCONTINUED) X(WNOWAIT) X(WEXITED) 406 X(WTRAPPED) XEND 407}; 408 409static struct xlat idtype_arg[] = { 410 X(P_PID) X(P_PPID) X(P_PGID) X(P_SID) X(P_CID) X(P_UID) X(P_GID) 411 X(P_ALL) X(P_LWPID) X(P_TASKID) X(P_PROJID) X(P_POOLID) X(P_JAILID) 412 X(P_CTID) X(P_CPUID) X(P_PSETID) XEND 413}; 414 415static struct xlat procctl_arg[] = { 416 X(PROC_SPROTECT) XEND 417}; 418 419#undef X 420#undef XEND 421 422/* 423 * Searches an xlat array for a value, and returns it if found. Otherwise 424 * return a string representation. 425 */ 426static const char * 427lookup(struct xlat *xlat, int val, int base) 428{ 429 static char tmp[16]; 430 431 for (; xlat->str != NULL; xlat++) 432 if (xlat->val == val) 433 return (xlat->str); 434 switch (base) { 435 case 8: 436 sprintf(tmp, "0%o", val); 437 break; 438 case 16: 439 sprintf(tmp, "0x%x", val); 440 break; 441 case 10: 442 sprintf(tmp, "%u", val); 443 break; 444 default: 445 errx(1,"Unknown lookup base"); 446 break; 447 } 448 return (tmp); 449} 450 451static const char * 452xlookup(struct xlat *xlat, int val) 453{ 454 455 return (lookup(xlat, val, 16)); 456} 457 458/* Searches an xlat array containing bitfield values. Remaining bits 459 set after removing the known ones are printed at the end: 460 IN|0x400 */ 461static char * 462xlookup_bits(struct xlat *xlat, int val) 463{ 464 int len, rem; 465 static char str[512]; 466 467 len = 0; 468 rem = val; 469 for (; xlat->str != NULL; xlat++) { 470 if ((xlat->val & rem) == xlat->val) { 471 /* don't print the "all-bits-zero" string unless all 472 bits are really zero */ 473 if (xlat->val == 0 && val != 0) 474 continue; 475 len += sprintf(str + len, "%s|", xlat->str); 476 rem &= ~(xlat->val); 477 } 478 } 479 /* if we have leftover bits or didn't match anything */ 480 if (rem || len == 0) 481 len += sprintf(str + len, "0x%x", rem); 482 if (len && str[len - 1] == '|') 483 len--; 484 str[len] = 0; 485 return (str); 486} 487 488/* 489 * If/when the list gets big, it might be desirable to do it 490 * as a hash table or binary search. 491 */ 492 493struct syscall * 494get_syscall(const char *name) 495{ 496 struct syscall *sc; 497 498 sc = syscalls; 499 if (name == NULL) 500 return (NULL); 501 while (sc->name) { 502 if (strcmp(name, sc->name) == 0) 503 return (sc); 504 sc++; 505 } 506 return (NULL); 507} 508 509/* 510 * get_struct 511 * 512 * Copy a fixed amount of bytes from the process. 513 */ 514 515static int 516get_struct(pid_t pid, void *offset, void *buf, int len) 517{ 518 struct ptrace_io_desc iorequest; 519 520 iorequest.piod_op = PIOD_READ_D; 521 iorequest.piod_offs = offset; 522 iorequest.piod_addr = buf; 523 iorequest.piod_len = len; 524 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) 525 return (-1); 526 return (0); 527} 528 529#define MAXSIZE 4096 530#define BLOCKSIZE 1024 531/* 532 * get_string 533 * Copy a string from the process. Note that it is 534 * expected to be a C string, but if max is set, it will 535 * only get that much. 536 */ 537 538static char * 539get_string(pid_t pid, void *offset, int max) 540{ 541 struct ptrace_io_desc iorequest; 542 char *buf; 543 int diff, i, size, totalsize; 544 545 diff = 0; 546 totalsize = size = max ? (max + 1) : BLOCKSIZE; 547 buf = malloc(totalsize); 548 if (buf == NULL) 549 return (NULL); 550 for (;;) { 551 diff = totalsize - size; 552 iorequest.piod_op = PIOD_READ_D; 553 iorequest.piod_offs = (char *)offset + diff; 554 iorequest.piod_addr = buf + diff; 555 iorequest.piod_len = size; 556 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) { 557 free(buf); 558 return (NULL); 559 } 560 for (i = 0 ; i < size; i++) { 561 if (buf[diff + i] == '\0') 562 return (buf); 563 } 564 if (totalsize < MAXSIZE - BLOCKSIZE && max == 0) { 565 totalsize += BLOCKSIZE; 566 buf = realloc(buf, totalsize); 567 size = BLOCKSIZE; 568 } else { 569 buf[totalsize - 1] = '\0'; 570 return (buf); 571 } 572 } 573} 574 575static char * 576strsig2(int sig) 577{ 578 char *tmp; 579 580 tmp = strsig(sig); 581 if (tmp == NULL) 582 asprintf(&tmp, "%d", sig); 583 return (tmp); 584} 585 586/* 587 * print_arg 588 * Converts a syscall argument into a string. Said string is 589 * allocated via malloc(), so needs to be free()'d. The file 590 * descriptor is for the process' memory (via /proc), and is used 591 * to get any data (where the argument is a pointer). sc is 592 * a pointer to the syscall description (see above); args is 593 * an array of all of the system call arguments. 594 */ 595 596char * 597print_arg(struct syscall_args *sc, unsigned long *args, long retval, 598 struct trussinfo *trussinfo) 599{ 600 char *tmp; 601 pid_t pid; 602 603 tmp = NULL; 604 pid = trussinfo->pid; 605 switch (sc->type & ARG_MASK) { 606 case Hex: 607 asprintf(&tmp, "0x%x", (int)args[sc->offset]); 608 break; 609 case Octal: 610 asprintf(&tmp, "0%o", (int)args[sc->offset]); 611 break; 612 case Int: 613 asprintf(&tmp, "%d", (int)args[sc->offset]); 614 break; 615 case Name: { 616 /* NULL-terminated string. */ 617 char *tmp2; 618 tmp2 = get_string(pid, (void*)args[sc->offset], 0); 619 asprintf(&tmp, "\"%s\"", tmp2); 620 free(tmp2); 621 break; 622 } 623 case BinString: { 624 /* Binary block of data that might have printable characters. 625 XXX If type|OUT, assume that the length is the syscall's 626 return value. Otherwise, assume that the length of the block 627 is in the next syscall argument. */ 628 int max_string = trussinfo->strsize; 629 char tmp2[max_string+1], *tmp3; 630 int len; 631 int truncated = 0; 632 633 if (sc->type & OUT) 634 len = retval; 635 else 636 len = args[sc->offset + 1]; 637 638 /* Don't print more than max_string characters, to avoid word 639 wrap. If we have to truncate put some ... after the string. 640 */ 641 if (len > max_string) { 642 len = max_string; 643 truncated = 1; 644 } 645 if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len) 646 != -1) { 647 tmp3 = malloc(len * 4 + 1); 648 while (len) { 649 if (strvisx(tmp3, tmp2, len, 650 VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string) 651 break; 652 len--; 653 truncated = 1; 654 }; 655 asprintf(&tmp, "\"%s\"%s", tmp3, truncated ? 656 "..." : ""); 657 free(tmp3); 658 } else { 659 asprintf(&tmp, "0x%lx", args[sc->offset]); 660 } 661 break; 662 } 663 case StringArray: { 664 int num, size, i; 665 char *tmp2; 666 char *string; 667 char *strarray[100]; /* XXX This is ugly. */ 668 669 if (get_struct(pid, (void *)args[sc->offset], 670 (void *)&strarray, sizeof(strarray)) == -1) 671 err(1, "get_struct %p", (void *)args[sc->offset]); 672 num = 0; 673 size = 0; 674 675 /* Find out how large of a buffer we'll need. */ 676 while (strarray[num] != NULL) { 677 string = get_string(pid, (void*)strarray[num], 0); 678 size += strlen(string); 679 free(string); 680 num++; 681 } 682 size += 4 + (num * 4); 683 tmp = (char *)malloc(size); 684 tmp2 = tmp; 685 686 tmp2 += sprintf(tmp2, " ["); 687 for (i = 0; i < num; i++) { 688 string = get_string(pid, (void*)strarray[i], 0); 689 tmp2 += sprintf(tmp2, " \"%s\"%c", string, 690 (i + 1 == num) ? ' ' : ','); 691 free(string); 692 } 693 tmp2 += sprintf(tmp2, "]"); 694 break; 695 } 696#ifdef __LP64__ 697 case Quad: 698 asprintf(&tmp, "0x%lx", args[sc->offset]); 699 break; 700#else 701 case Quad: { 702 unsigned long long ll; 703 ll = *(unsigned long long *)(args + sc->offset); 704 asprintf(&tmp, "0x%llx", ll); 705 break; 706 } 707#endif 708 case Ptr: 709 asprintf(&tmp, "0x%lx", args[sc->offset]); 710 break; 711 case Readlinkres: { 712 char *tmp2; 713 if (retval == -1) { 714 tmp = strdup(""); 715 break; 716 } 717 tmp2 = get_string(pid, (void*)args[sc->offset], retval); 718 asprintf(&tmp, "\"%s\"", tmp2); 719 free(tmp2); 720 break; 721 } 722 case Ioctl: { 723 const char *temp = ioctlname(args[sc->offset]); 724 if (temp) 725 tmp = strdup(temp); 726 else { 727 unsigned long arg = args[sc->offset]; 728 asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }", 729 arg, arg & IOC_OUT ? "R" : "", 730 arg & IOC_IN ? "W" : "", IOCGROUP(arg), 731 isprint(IOCGROUP(arg)) ? (char)IOCGROUP(arg) : '?', 732 arg & 0xFF, IOCPARM_LEN(arg)); 733 } 734 break; 735 } 736 case Umtx: { 737 struct umtx umtx; 738 if (get_struct(pid, (void *)args[sc->offset], &umtx, 739 sizeof(umtx)) != -1) 740 asprintf(&tmp, "{ 0x%lx }", (long)umtx.u_owner); 741 else 742 asprintf(&tmp, "0x%lx", args[sc->offset]); 743 break; 744 } 745 case Timespec: { 746 struct timespec ts; 747 if (get_struct(pid, (void *)args[sc->offset], &ts, 748 sizeof(ts)) != -1) 749 asprintf(&tmp, "{%ld.%09ld }", (long)ts.tv_sec, 750 ts.tv_nsec); 751 else 752 asprintf(&tmp, "0x%lx", args[sc->offset]); 753 break; 754 } 755 case Timeval: { 756 struct timeval tv; 757 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) 758 != -1) 759 asprintf(&tmp, "{%ld.%06ld }", (long)tv.tv_sec, 760 tv.tv_usec); 761 else 762 asprintf(&tmp, "0x%lx", args[sc->offset]); 763 break; 764 } 765 case Timeval2: { 766 struct timeval tv[2]; 767 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) 768 != -1) 769 asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", 770 (long)tv[0].tv_sec, tv[0].tv_usec, 771 (long)tv[1].tv_sec, tv[1].tv_usec); 772 else 773 asprintf(&tmp, "0x%lx", args[sc->offset]); 774 break; 775 } 776 case Itimerval: { 777 struct itimerval itv; 778 if (get_struct(pid, (void *)args[sc->offset], &itv, 779 sizeof(itv)) != -1) 780 asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", 781 (long)itv.it_interval.tv_sec, 782 itv.it_interval.tv_usec, 783 (long)itv.it_value.tv_sec, 784 itv.it_value.tv_usec); 785 else 786 asprintf(&tmp, "0x%lx", args[sc->offset]); 787 break; 788 } 789 case LinuxSockArgs: 790 { 791 struct linux_socketcall_args largs; 792 if (get_struct(pid, (void *)args[sc->offset], (void *)&largs, 793 sizeof(largs)) == -1) { 794 err(1, "get_struct %p", (void *)args[sc->offset]); 795 } 796 const char *what; 797 char buf[30]; 798 799 switch (largs.what) { 800 case LINUX_SOCKET: 801 what = "LINUX_SOCKET"; 802 break; 803 case LINUX_BIND: 804 what = "LINUX_BIND"; 805 break; 806 case LINUX_CONNECT: 807 what = "LINUX_CONNECT"; 808 break; 809 case LINUX_LISTEN: 810 what = "LINUX_LISTEN"; 811 break; 812 case LINUX_ACCEPT: 813 what = "LINUX_ACCEPT"; 814 break; 815 case LINUX_GETSOCKNAME: 816 what = "LINUX_GETSOCKNAME"; 817 break; 818 case LINUX_GETPEERNAME: 819 what = "LINUX_GETPEERNAME"; 820 break; 821 case LINUX_SOCKETPAIR: 822 what = "LINUX_SOCKETPAIR"; 823 break; 824 case LINUX_SEND: 825 what = "LINUX_SEND"; 826 break; 827 case LINUX_RECV: 828 what = "LINUX_RECV"; 829 break; 830 case LINUX_SENDTO: 831 what = "LINUX_SENDTO"; 832 break; 833 case LINUX_RECVFROM: 834 what = "LINUX_RECVFROM"; 835 break; 836 case LINUX_SHUTDOWN: 837 what = "LINUX_SHUTDOWN"; 838 break; 839 case LINUX_SETSOCKOPT: 840 what = "LINUX_SETSOCKOPT"; 841 break; 842 case LINUX_GETSOCKOPT: 843 what = "LINUX_GETSOCKOPT"; 844 break; 845 case LINUX_SENDMSG: 846 what = "LINUX_SENDMSG"; 847 break; 848 case LINUX_RECVMSG: 849 what = "LINUX_RECVMSG"; 850 break; 851 default: 852 sprintf(buf, "%d", largs.what); 853 what = buf; 854 break; 855 } 856 asprintf(&tmp, "(0x%lx)%s, 0x%lx", args[sc->offset], what, (long unsigned int)largs.args); 857 break; 858 } 859 case Pollfd: { 860 /* 861 * XXX: A Pollfd argument expects the /next/ syscall argument 862 * to be the number of fds in the array. This matches the poll 863 * syscall. 864 */ 865 struct pollfd *pfd; 866 int numfds = args[sc->offset+1]; 867 int bytes = sizeof(struct pollfd) * numfds; 868 int i, tmpsize, u, used; 869 const int per_fd = 100; 870 871 if ((pfd = malloc(bytes)) == NULL) 872 err(1, "Cannot malloc %d bytes for pollfd array", 873 bytes); 874 if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) 875 != -1) { 876 used = 0; 877 tmpsize = 1 + per_fd * numfds + 2; 878 if ((tmp = malloc(tmpsize)) == NULL) 879 err(1, "Cannot alloc %d bytes for poll output", 880 tmpsize); 881 882 tmp[used++] = '{'; 883 for (i = 0; i < numfds; i++) { 884 885 u = snprintf(tmp + used, per_fd, "%s%d/%s", 886 i > 0 ? " " : "", pfd[i].fd, 887 xlookup_bits(poll_flags, pfd[i].events)); 888 if (u > 0) 889 used += u < per_fd ? u : per_fd; 890 } 891 tmp[used++] = '}'; 892 tmp[used++] = '\0'; 893 } else { 894 asprintf(&tmp, "0x%lx", args[sc->offset]); 895 } 896 free(pfd); 897 break; 898 } 899 case Fd_set: { 900 /* 901 * XXX: A Fd_set argument expects the /first/ syscall argument 902 * to be the number of fds in the array. This matches the 903 * select syscall. 904 */ 905 fd_set *fds; 906 int numfds = args[0]; 907 int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; 908 int i, tmpsize, u, used; 909 const int per_fd = 20; 910 911 if ((fds = malloc(bytes)) == NULL) 912 err(1, "Cannot malloc %d bytes for fd_set array", 913 bytes); 914 if (get_struct(pid, (void *)args[sc->offset], fds, bytes) 915 != -1) { 916 used = 0; 917 tmpsize = 1 + numfds * per_fd + 2; 918 if ((tmp = malloc(tmpsize)) == NULL) 919 err(1, "Cannot alloc %d bytes for fd_set " 920 "output", tmpsize); 921 922 tmp[used++] = '{'; 923 for (i = 0; i < numfds; i++) { 924 if (FD_ISSET(i, fds)) { 925 u = snprintf(tmp + used, per_fd, "%d ", 926 i); 927 if (u > 0) 928 used += u < per_fd ? u : per_fd; 929 } 930 } 931 if (tmp[used-1] == ' ') 932 used--; 933 tmp[used++] = '}'; 934 tmp[used++] = '\0'; 935 } else 936 asprintf(&tmp, "0x%lx", args[sc->offset]); 937 free(fds); 938 break; 939 } 940 case Signal: 941 tmp = strsig2(args[sc->offset]); 942 break; 943 case Sigset: { 944 long sig; 945 sigset_t ss; 946 int i, used; 947 char *signame; 948 949 sig = args[sc->offset]; 950 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 951 sizeof(ss)) == -1) { 952 asprintf(&tmp, "0x%lx", args[sc->offset]); 953 break; 954 } 955 tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */ 956 used = 0; 957 for (i = 1; i < sys_nsig; i++) { 958 if (sigismember(&ss, i)) { 959 signame = strsig(i); 960 used += sprintf(tmp + used, "%s|", signame); 961 free(signame); 962 } 963 } 964 if (used) 965 tmp[used-1] = 0; 966 else 967 strcpy(tmp, "0x0"); 968 break; 969 } 970 case Sigprocmask: { 971 switch (args[sc->offset]) { 972#define S(a) case a: tmp = strdup(#a); break; 973 S(SIG_BLOCK); 974 S(SIG_UNBLOCK); 975 S(SIG_SETMASK); 976#undef S 977 } 978 if (tmp == NULL) 979 asprintf(&tmp, "0x%lx", args[sc->offset]); 980 break; 981 } 982 case Fcntlflag: { 983 /* XXX output depends on the value of the previous argument */ 984 switch (args[sc->offset-1]) { 985 case F_SETFD: 986 tmp = strdup(xlookup_bits(fcntlfd_arg, 987 args[sc->offset])); 988 break; 989 case F_SETFL: 990 tmp = strdup(xlookup_bits(fcntlfl_arg, 991 args[sc->offset])); 992 break; 993 case F_GETFD: 994 case F_GETFL: 995 case F_GETOWN: 996 tmp = strdup(""); 997 break; 998 default: 999 asprintf(&tmp, "0x%lx", args[sc->offset]); 1000 break; 1001 } 1002 break; 1003 } 1004 case Open: 1005 tmp = strdup(xlookup_bits(open_flags, args[sc->offset])); 1006 break; 1007 case Fcntl: 1008 tmp = strdup(xlookup(fcntl_arg, args[sc->offset])); 1009 break; 1010 case Mprot: 1011 tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset])); 1012 break; 1013 case Mmapflags: { 1014 char *base, *alignstr; 1015 int align, flags; 1016 1017 /* 1018 * MAP_ALIGNED can't be handled by xlookup_bits(), so 1019 * generate that string manually and prepend it to the 1020 * string from xlookup_bits(). Have to be careful to 1021 * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is 1022 * the only flag. 1023 */ 1024 flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK; 1025 align = args[sc->offset] & MAP_ALIGNMENT_MASK; 1026 if (align != 0) { 1027 if (align == MAP_ALIGNED_SUPER) 1028 alignstr = strdup("MAP_ALIGNED_SUPER"); 1029 else 1030 asprintf(&alignstr, "MAP_ALIGNED(%d)", 1031 align >> MAP_ALIGNMENT_SHIFT); 1032 if (flags == 0) { 1033 tmp = alignstr; 1034 break; 1035 } 1036 } else 1037 alignstr = NULL; 1038 base = strdup(xlookup_bits(mmap_flags, flags)); 1039 if (alignstr == NULL) { 1040 tmp = base; 1041 break; 1042 } 1043 asprintf(&tmp, "%s|%s", alignstr, base); 1044 free(alignstr); 1045 free(base); 1046 break; 1047 } 1048 case Whence: 1049 tmp = strdup(xlookup(whence_arg, args[sc->offset])); 1050 break; 1051 case Sockdomain: 1052 tmp = strdup(xlookup(sockdomain_arg, args[sc->offset])); 1053 break; 1054 case Socktype: 1055 tmp = strdup(xlookup(socktype_arg, args[sc->offset])); 1056 break; 1057 case Shutdown: 1058 tmp = strdup(xlookup(shutdown_arg, args[sc->offset])); 1059 break; 1060 case Resource: 1061 tmp = strdup(xlookup(resource_arg, args[sc->offset])); 1062 break; 1063 case Pathconf: 1064 tmp = strdup(xlookup(pathconf_arg, args[sc->offset])); 1065 break; 1066 case Rforkflags: 1067 tmp = strdup(xlookup_bits(rfork_flags, args[sc->offset])); 1068 break; 1069 case Sockaddr: { 1070 struct sockaddr_storage ss; 1071 char addr[64]; 1072 struct sockaddr_in *lsin; 1073 struct sockaddr_in6 *lsin6; 1074 struct sockaddr_un *sun; 1075 struct sockaddr *sa; 1076 char *p; 1077 u_char *q; 1078 int i; 1079 1080 if (args[sc->offset] == 0) { 1081 asprintf(&tmp, "NULL"); 1082 break; 1083 } 1084 1085 /* yuck: get ss_len */ 1086 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 1087 sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) 1088 err(1, "get_struct %p", (void *)args[sc->offset]); 1089 /* 1090 * If ss_len is 0, then try to guess from the sockaddr type. 1091 * AF_UNIX may be initialized incorrectly, so always frob 1092 * it by using the "right" size. 1093 */ 1094 if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) { 1095 switch (ss.ss_family) { 1096 case AF_INET: 1097 ss.ss_len = sizeof(*lsin); 1098 break; 1099 case AF_UNIX: 1100 ss.ss_len = sizeof(*sun); 1101 break; 1102 default: 1103 /* hurrrr */ 1104 break; 1105 } 1106 } 1107 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 1108 ss.ss_len) == -1) { 1109 err(2, "get_struct %p", (void *)args[sc->offset]); 1110 } 1111 1112 switch (ss.ss_family) { 1113 case AF_INET: 1114 lsin = (struct sockaddr_in *)&ss; 1115 inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr); 1116 asprintf(&tmp, "{ AF_INET %s:%d }", addr, 1117 htons(lsin->sin_port)); 1118 break; 1119 case AF_INET6: 1120 lsin6 = (struct sockaddr_in6 *)&ss; 1121 inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, 1122 sizeof addr); 1123 asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, 1124 htons(lsin6->sin6_port)); 1125 break; 1126 case AF_UNIX: 1127 sun = (struct sockaddr_un *)&ss; 1128 asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path); 1129 break; 1130 default: 1131 sa = (struct sockaddr *)&ss; 1132 asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data " 1133 "= {%n%*s } }", (int)sa->sa_len, (int)sa->sa_family, 1134 &i, 6 * (int)(sa->sa_len - ((char *)&sa->sa_data - 1135 (char *)sa)), ""); 1136 if (tmp != NULL) { 1137 p = tmp + i; 1138 for (q = (u_char *)&sa->sa_data; 1139 q < (u_char *)sa + sa->sa_len; q++) 1140 p += sprintf(p, " %#02x,", *q); 1141 } 1142 } 1143 break; 1144 } 1145 case Sigaction: { 1146 struct sigaction sa; 1147 char *hand; 1148 const char *h; 1149 1150 if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa)) 1151 != -1) { 1152 asprintf(&hand, "%p", sa.sa_handler); 1153 if (sa.sa_handler == SIG_DFL) 1154 h = "SIG_DFL"; 1155 else if (sa.sa_handler == SIG_IGN) 1156 h = "SIG_IGN"; 1157 else 1158 h = hand; 1159 1160 asprintf(&tmp, "{ %s %s ss_t }", h, 1161 xlookup_bits(sigaction_flags, sa.sa_flags)); 1162 free(hand); 1163 } else 1164 asprintf(&tmp, "0x%lx", args[sc->offset]); 1165 break; 1166 } 1167 case Kevent: { 1168 /* 1169 * XXX XXX: the size of the array is determined by either the 1170 * next syscall argument, or by the syscall returnvalue, 1171 * depending on which argument number we are. This matches the 1172 * kevent syscall, but luckily that's the only syscall that uses 1173 * them. 1174 */ 1175 struct kevent *ke; 1176 int numevents = -1; 1177 int bytes = 0; 1178 int i, tmpsize, u, used; 1179 const int per_ke = 100; 1180 1181 if (sc->offset == 1) 1182 numevents = args[sc->offset+1]; 1183 else if (sc->offset == 3 && retval != -1) 1184 numevents = retval; 1185 1186 if (numevents >= 0) 1187 bytes = sizeof(struct kevent) * numevents; 1188 if ((ke = malloc(bytes)) == NULL) 1189 err(1, "Cannot malloc %d bytes for kevent array", 1190 bytes); 1191 if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset], 1192 ke, bytes) != -1) { 1193 used = 0; 1194 tmpsize = 1 + per_ke * numevents + 2; 1195 if ((tmp = malloc(tmpsize)) == NULL) 1196 err(1, "Cannot alloc %d bytes for kevent " 1197 "output", tmpsize); 1198 1199 tmp[used++] = '{'; 1200 for (i = 0; i < numevents; i++) { 1201 u = snprintf(tmp + used, per_ke, 1202 "%s%p,%s,%s,%d,%p,%p", 1203 i > 0 ? " " : "", 1204 (void *)ke[i].ident, 1205 xlookup(kevent_filters, ke[i].filter), 1206 xlookup_bits(kevent_flags, ke[i].flags), 1207 ke[i].fflags, 1208 (void *)ke[i].data, 1209 (void *)ke[i].udata); 1210 if (u > 0) 1211 used += u < per_ke ? u : per_ke; 1212 } 1213 tmp[used++] = '}'; 1214 tmp[used++] = '\0'; 1215 } else { 1216 asprintf(&tmp, "0x%lx", args[sc->offset]); 1217 } 1218 free(ke); 1219 break; 1220 } 1221 case Stat: { 1222 struct stat st; 1223 if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st)) 1224 != -1) { 1225 char mode[12]; 1226 strmode(st.st_mode, mode); 1227 asprintf(&tmp, 1228 "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode, 1229 (intmax_t)st.st_ino, (intmax_t)st.st_size, 1230 (long)st.st_blksize); 1231 } else { 1232 asprintf(&tmp, "0x%lx", args[sc->offset]); 1233 } 1234 break; 1235 } 1236 case Rusage: { 1237 struct rusage ru; 1238 if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) 1239 != -1) { 1240 asprintf(&tmp, 1241 "{ u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld }", 1242 (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, 1243 (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, 1244 ru.ru_inblock, ru.ru_oublock); 1245 } else 1246 asprintf(&tmp, "0x%lx", args[sc->offset]); 1247 break; 1248 } 1249 case Rlimit: { 1250 struct rlimit rl; 1251 if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl)) 1252 != -1) { 1253 asprintf(&tmp, "{ cur=%ju,max=%ju }", 1254 rl.rlim_cur, rl.rlim_max); 1255 } else 1256 asprintf(&tmp, "0x%lx", args[sc->offset]); 1257 break; 1258 } 1259 case ExitStatus: { 1260 char *signame; 1261 int status; 1262 signame = NULL; 1263 if (get_struct(pid, (void *)args[sc->offset], &status, 1264 sizeof(status)) != -1) { 1265 if (WIFCONTINUED(status)) 1266 tmp = strdup("{ CONTINUED }"); 1267 else if (WIFEXITED(status)) 1268 asprintf(&tmp, "{ EXITED,val=%d }", 1269 WEXITSTATUS(status)); 1270 else if (WIFSIGNALED(status)) 1271 asprintf(&tmp, "{ SIGNALED,sig=%s%s }", 1272 signame = strsig2(WTERMSIG(status)), 1273 WCOREDUMP(status) ? ",cored" : ""); 1274 else 1275 asprintf(&tmp, "{ STOPPED,sig=%s }", 1276 signame = strsig2(WTERMSIG(status))); 1277 } else 1278 asprintf(&tmp, "0x%lx", args[sc->offset]); 1279 free(signame); 1280 break; 1281 } 1282 case Waitoptions: 1283 tmp = strdup(xlookup_bits(wait_options, args[sc->offset])); 1284 break; 1285 case Idtype: 1286 tmp = strdup(xlookup(idtype_arg, args[sc->offset])); 1287 break; 1288 case Procctl: 1289 tmp = strdup(xlookup(procctl_arg, args[sc->offset])); 1290 break; 1291 default: 1292 errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); 1293 } 1294 return (tmp); 1295} 1296 1297/* 1298 * print_syscall 1299 * Print (to outfile) the system call and its arguments. Note that 1300 * nargs is the number of arguments (not the number of words; this is 1301 * potentially confusing, I know). 1302 */ 1303 1304void 1305print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, 1306 char **s_args) 1307{ 1308 struct timespec timediff; 1309 int i, len; 1310 1311 len = 0; 1312 if (trussinfo->flags & FOLLOWFORKS) 1313 len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); 1314 1315 if (name != NULL && (strcmp(name, "execve") == 0 || 1316 strcmp(name, "exit") == 0)) { 1317 clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); 1318 } 1319 1320 if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 1321 timespecsubt(&trussinfo->curthread->after, 1322 &trussinfo->start_time, &timediff); 1323 len += fprintf(trussinfo->outfile, "%ld.%09ld ", 1324 (long)timediff.tv_sec, timediff.tv_nsec); 1325 } 1326 1327 if (trussinfo->flags & RELATIVETIMESTAMPS) { 1328 timespecsubt(&trussinfo->curthread->after, 1329 &trussinfo->curthread->before, &timediff); 1330 len += fprintf(trussinfo->outfile, "%ld.%09ld ", 1331 (long)timediff.tv_sec, timediff.tv_nsec); 1332 } 1333 1334 len += fprintf(trussinfo->outfile, "%s(", name); 1335 1336 for (i = 0; i < nargs; i++) { 1337 if (s_args[i]) 1338 len += fprintf(trussinfo->outfile, "%s", s_args[i]); 1339 else 1340 len += fprintf(trussinfo->outfile, 1341 "<missing argument>"); 1342 len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? 1343 "," : ""); 1344 } 1345 len += fprintf(trussinfo->outfile, ")"); 1346 for (i = 0; i < 6 - (len / 8); i++) 1347 fprintf(trussinfo->outfile, "\t"); 1348} 1349 1350void 1351print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, 1352 char **s_args, int errorp, long retval, struct syscall *sc) 1353{ 1354 struct timespec timediff; 1355 1356 if (trussinfo->flags & COUNTONLY) { 1357 if (!sc) 1358 return; 1359 clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); 1360 timespecsubt(&trussinfo->curthread->after, 1361 &trussinfo->curthread->before, &timediff); 1362 timespecadd(&sc->time, &timediff, &sc->time); 1363 sc->ncalls++; 1364 if (errorp) 1365 sc->nerror++; 1366 return; 1367 } 1368 1369 print_syscall(trussinfo, name, nargs, s_args); 1370 fflush(trussinfo->outfile); 1371 if (errorp) 1372 fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval, 1373 strerror(retval)); 1374 else { 1375 /* 1376 * Because pipe(2) has a special assembly glue to provide the 1377 * libc API, we have to adjust retval. 1378 */ 1379 if (name != NULL && strcmp(name, "pipe") == 0) 1380 retval = 0; 1381 fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval); 1382 } 1383} 1384 1385void 1386print_summary(struct trussinfo *trussinfo) 1387{ 1388 struct timespec total = {0, 0}; 1389 struct syscall *sc; 1390 int ncall, nerror; 1391 1392 fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n", 1393 "syscall", "seconds", "calls", "errors"); 1394 ncall = nerror = 0; 1395 for (sc = syscalls; sc->name != NULL; sc++) 1396 if (sc->ncalls) { 1397 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", 1398 sc->name, (intmax_t)sc->time.tv_sec, 1399 sc->time.tv_nsec, sc->ncalls, sc->nerror); 1400 timespecadd(&total, &sc->time, &total); 1401 ncall += sc->ncalls; 1402 nerror += sc->nerror; 1403 } 1404 fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n", 1405 "", "-------------", "-------", "-------"); 1406 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", 1407 "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror); 1408} 1409