1 /* 2 * Copryight 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/mman.h> 43 #include <sys/types.h> 44 #include <sys/ptrace.h> 45 #include <sys/socket.h> 46 #include <sys/time.h> 47 #include <sys/un.h> 48 #include <netinet/in.h> 49 #include <arpa/inet.h> 50 #include <sys/ioccom.h> 51 #include <machine/atomic.h> 52 #include <errno.h> 53 #include <sys/umtx.h> 54 #include <sys/event.h> 55 #include <sys/stat.h> 56 #include <sys/resource.h> 57 58 #include <ctype.h> 59 #include <err.h> 60 #include <fcntl.h> 61 #include <poll.h> 62 #include <signal.h> 63 #include <stdint.h> 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <time.h> 68 #include <unistd.h> 69 #include <vis.h> 70 71 #include "truss.h" 72 #include "extern.h" 73 #include "syscall.h" 74 75 /* 64-bit alignment on 32-bit platforms. */ 76 #ifdef __powerpc__ 77 #define QUAD_ALIGN 1 78 #else 79 #define QUAD_ALIGN 0 80 #endif 81 82 /* Number of slots needed for a 64-bit argument. */ 83 #ifdef __LP64__ 84 #define QUAD_SLOTS 1 85 #else 86 #define QUAD_SLOTS 2 87 #endif 88 89 /* 90 * This should probably be in its own file, sorted alphabetically. 91 */ 92 93 struct syscall syscalls[] = { 94 { "fcntl", 1, 3, 95 { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 }}}, 96 { "readlink", 1, 3, 97 { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 }}}, 98 { "lseek", 2, 3, 99 { {Int, 0}, {Quad, 1 + QUAD_ALIGN}, 100 {Whence, 1 + QUAD_SLOTS + QUAD_ALIGN}}}, 101 { "linux_lseek", 2, 3, 102 { { Int, 0 }, {Int, 1 }, { Whence, 2 }}}, 103 { "mmap", 2, 6, 104 { {Ptr, 0}, {Int, 1}, {Mprot, 2}, {Mmapflags, 3}, {Int, 4}, 105 {Quad, 5 + QUAD_ALIGN}}}, 106 { "mprotect", 1, 3, 107 { { Ptr, 0 }, {Int, 1}, {Mprot, 2}}}, 108 { "open", 1, 3, 109 { { Name | IN, 0} , { Open, 1}, {Octal, 2}}}, 110 { "mkdir", 1, 2, 111 { { Name, 0} , {Octal, 1}}}, 112 { "linux_open", 1, 3, 113 { { Name, 0 }, { Hex, 1}, { Octal, 2 }}}, 114 { "close", 1, 1, 115 { { Int, 0 } } }, 116 { "link", 0, 2, 117 { { Name, 0 }, { Name, 1 }}}, 118 { "unlink", 0, 1, 119 { { Name, 0 }}}, 120 { "chdir", 0, 1, 121 { { Name, 0 }}}, 122 { "chroot", 0, 1, 123 { { Name, 0 }}}, 124 { "mknod", 0, 3, 125 { { Name, 0 }, { Octal, 1 }, { Int, 3 }}}, 126 { "chmod", 0, 2, 127 { { Name, 0 }, { Octal, 1 }}}, 128 { "chown", 0, 3, 129 { { Name, 0 }, { Int, 1 }, { Int, 2 }}}, 130 { "mount", 0, 4, 131 { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 }}}, 132 { "umount", 0, 2, 133 { { Name, 0 }, { Int, 2 }}}, 134 { "fstat", 1, 2, 135 { { Int, 0}, { Stat | OUT , 1 }}}, 136 { "stat", 1, 2, 137 { { Name | IN, 0 }, { Stat | OUT, 1 }}}, 138 { "lstat", 1, 2, 139 { { Name | IN, 0 }, { Stat | OUT, 1 }}}, 140 { "linux_newstat", 1, 2, 141 { { Name | IN, 0 }, { Ptr | OUT, 1 }}}, 142 { "linux_newfstat", 1, 2, 143 { { Int, 0 }, { Ptr | OUT, 1 }}}, 144 { "write", 1, 3, 145 { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }}}, 146 { "ioctl", 1, 3, 147 { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 }}}, 148 { "break", 1, 1, { { Hex, 0 }}}, 149 { "exit", 0, 1, { { Hex, 0 }}}, 150 { "access", 1, 2, { { Name | IN, 0 }, { Int, 1 }}}, 151 { "sigaction", 1, 3, 152 { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 }}}, 153 { "accept", 1, 3, 154 { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 155 { "bind", 1, 3, 156 { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 157 { "connect", 1, 3, 158 { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 159 { "getpeername", 1, 3, 160 { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 161 { "getsockname", 1, 3, 162 { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 163 { "recvfrom", 1, 6, 164 { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, 165 { "sendto", 1, 6, 166 { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, 167 { "execve", 1, 3, 168 { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, 169 { "linux_execve", 1, 3, 170 { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, 171 { "kldload", 0, 1, { { Name | IN, 0 }}}, 172 { "kldunload", 0, 1, { { Int, 0 }}}, 173 { "kldfind", 0, 1, { { Name | IN, 0 }}}, 174 { "kldnext", 0, 1, { { Int, 0 }}}, 175 { "kldstat", 0, 2, { { Int, 0 }, { Ptr, 1 }}}, 176 { "kldfirstmod", 0, 1, { { Int, 0 }}}, 177 { "nanosleep", 0, 1, { { Timespec, 0 }}}, 178 { "select", 1, 5, { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 }}}, 179 { "poll", 1, 3, { { Pollfd, 0 }, { Int, 1 }, { Int, 2 }}}, 180 { "gettimeofday", 1, 2, { { Timeval | OUT, 0 }, { Ptr, 1 }}}, 181 { "clock_gettime", 1, 2, { { Int, 0 }, { Timespec | OUT, 1 }}}, 182 { "getitimer", 1, 2, { { Int, 0 }, { Itimerval | OUT, 2 }}}, 183 { "setitimer", 1, 3, { { Int, 0 }, { Itimerval, 1} , { Itimerval | OUT, 2 }}}, 184 { "kse_release", 0, 1, { { Timespec, 0 }}}, 185 { "kevent", 0, 6, { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 }}}, 186 { "_umtx_lock", 0, 1, { { Umtx, 0 }}}, 187 { "_umtx_unlock", 0, 1, { { Umtx, 0 }}}, 188 { "sigprocmask", 0, 3, { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 }}}, 189 { "unmount", 1, 2, { { Name, 0 }, { Int, 1 }}}, 190 { "socket", 1, 3, { { Sockdomain, 0}, { Socktype, 1}, {Int, 2 }}}, 191 { "getrusage", 1, 2, { { Int, 0 }, { Rusage | OUT, 1 }}}, 192 { "__getcwd", 1, 2, { { Name | OUT, 0}, { Int, 1 }}}, 193 { "shutdown", 1, 2, { { Int, 0}, { Shutdown, 1}}}, 194 { "getrlimit", 1, 2, { { Resource, 0}, {Rlimit | OUT, 1}}}, 195 { "setrlimit", 1, 2, { { Resource, 0}, {Rlimit | IN, 1}}}, 196 { "utimes", 1, 2, 197 { { Name | IN, 0 }, { Timeval2 | IN, 1 }}}, 198 { "lutimes", 1, 2, 199 { { Name | IN, 0 }, { Timeval2 | IN, 1 }}}, 200 { "futimes", 1, 2, 201 { { Int, 0 }, { Timeval | IN, 1 }}}, 202 { "chflags", 1, 2, 203 { { Name | IN, 0 }, { Hex, 1 }}}, 204 { "lchflags", 1, 2, 205 { { Name | IN, 0 }, { Hex, 1 }}}, 206 { "pathconf", 1, 2, 207 { { Name | IN, 0 }, { Pathconf, 1 }}}, 208 { "truncate", 1, 3, 209 { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 }}}, 210 { "ftruncate", 1, 3, 211 { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 }}}, 212 { "kill", 1, 2, 213 { { Int | IN, 0 }, { Signal | IN, 1}}}, 214 { "munmap", 1, 2, 215 { { Ptr, 0 }, { Int, 1 }}}, 216 { "read", 1, 3, 217 { { Int, 0}, { BinString | OUT, 1}, { Int, 2}}}, 218 { "rename", 1, 2, 219 { { Name , 0} , { Name, 1}}}, 220 { "symlink", 1, 2, 221 { { Name , 0} , { Name, 1}}}, 222 { 0, 0, 0, { { 0, 0 }}}, 223 }; 224 225 /* Xlat idea taken from strace */ 226 struct xlat { 227 int val; 228 const char *str; 229 }; 230 231 #define X(a) { a, #a }, 232 #define XEND { 0, NULL } 233 234 static struct xlat kevent_filters[] = { 235 X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE) 236 X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER) 237 X(EVFILT_NETDEV) X(EVFILT_FS) X(EVFILT_READ) XEND 238 }; 239 240 static struct xlat kevent_flags[] = { 241 X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT) 242 X(EV_CLEAR) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND 243 }; 244 245 struct xlat poll_flags[] = { 246 X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR) 247 X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND) 248 X(POLLWRBAND) X(POLLINIGNEOF) XEND 249 }; 250 251 static struct xlat mmap_flags[] = { 252 X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME) 253 X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100) 254 X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON) 255 X(MAP_NOCORE) XEND 256 }; 257 258 static struct xlat mprot_flags[] = { 259 X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND 260 }; 261 262 static struct xlat whence_arg[] = { 263 X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) XEND 264 }; 265 266 static struct xlat sigaction_flags[] = { 267 X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP) 268 X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND 269 }; 270 271 static struct xlat fcntl_arg[] = { 272 X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL) 273 X(F_GETOWN) X(F_SETOWN) X(F_GETLK) X(F_SETLK) X(F_SETLKW) XEND 274 }; 275 276 static struct xlat fcntlfd_arg[] = { 277 X(FD_CLOEXEC) XEND 278 }; 279 280 static struct xlat fcntlfl_arg[] = { 281 X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW) 282 X(O_DIRECT) XEND 283 }; 284 285 static struct xlat sockdomain_arg[] = { 286 X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK) 287 X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI) 288 X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet) 289 X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE) 290 X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX) 291 X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6) 292 X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER) 293 X(PF_ARP) X(PF_BLUETOOTH) XEND 294 }; 295 296 static struct xlat socktype_arg[] = { 297 X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM) 298 X(SOCK_SEQPACKET) XEND 299 }; 300 301 static struct xlat open_flags[] = { 302 X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK) 303 X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC) 304 X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY) 305 X(O_DIRECT) XEND 306 }; 307 308 static struct xlat shutdown_arg[] = { 309 X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND 310 }; 311 312 static struct xlat resource_arg[] = { 313 X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK) 314 X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC) 315 X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) XEND 316 }; 317 318 static struct xlat pathconf_arg[] = { 319 X(_PC_LINK_MAX) X(_PC_MAX_CANON) X(_PC_MAX_INPUT) 320 X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF) 321 X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE) 322 X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO) 323 X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS) 324 X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE) 325 X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN) 326 X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX) 327 X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT) 328 XEND 329 }; 330 331 #undef X 332 #undef XEND 333 334 /* Searches an xlat array for a value, and returns it if found. Otherwise 335 return a string representation. */ 336 static const char 337 *lookup(struct xlat *xlat, int val, int base) 338 { 339 static char tmp[16]; 340 for (; xlat->str != NULL; xlat++) 341 if (xlat->val == val) 342 return xlat->str; 343 switch (base) { 344 case 8: 345 sprintf(tmp, "0%o", val); 346 break; 347 case 16: 348 sprintf(tmp, "0x%x", val); 349 break; 350 case 10: 351 sprintf(tmp, "%u", val); 352 break; 353 default: 354 errx(1,"Unknown lookup base"); 355 break; 356 } 357 return tmp; 358 } 359 360 static const char * 361 xlookup(struct xlat *xlat, int val) 362 { 363 return lookup(xlat, val, 16); 364 } 365 366 /* Searches an xlat array containing bitfield values. Remaining bits 367 set after removing the known ones are printed at the end: 368 IN|0x400 */ 369 static char 370 *xlookup_bits(struct xlat *xlat, int val) 371 { 372 static char str[512]; 373 int len = 0; 374 int rem = val; 375 376 for (; xlat->str != NULL; xlat++) 377 { 378 if ((xlat->val & rem) == xlat->val) 379 { 380 /* don't print the "all-bits-zero" string unless all 381 bits are really zero */ 382 if (xlat->val == 0 && val != 0) 383 continue; 384 len += sprintf(str + len, "%s|", xlat->str); 385 rem &= ~(xlat->val); 386 } 387 } 388 /* if we have leftover bits or didn't match anything */ 389 if (rem || len == 0) 390 len += sprintf(str + len, "0x%x", rem); 391 if (len && str[len - 1] == '|') 392 len--; 393 str[len] = 0; 394 return str; 395 } 396 397 /* 398 * If/when the list gets big, it might be desirable to do it 399 * as a hash table or binary search. 400 */ 401 402 struct syscall * 403 get_syscall(const char *name) { 404 struct syscall *sc = syscalls; 405 406 if (name == NULL) 407 return (NULL); 408 while (sc->name) { 409 if (!strcmp(name, sc->name)) 410 return sc; 411 sc++; 412 } 413 return NULL; 414 } 415 416 /* 417 * get_struct 418 * 419 * Copy a fixed amount of bytes from the process. 420 */ 421 422 static int 423 get_struct(int pid, void *offset, void *buf, int len) { 424 struct ptrace_io_desc iorequest; 425 426 iorequest.piod_op = PIOD_READ_D; 427 iorequest.piod_offs = offset; 428 iorequest.piod_addr = buf; 429 iorequest.piod_len = len; 430 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) 431 return -1; 432 return 0; 433 } 434 435 #define MAXSIZE 4096 436 #define BLOCKSIZE 1024 437 /* 438 * get_string 439 * Copy a string from the process. Note that it is 440 * expected to be a C string, but if max is set, it will 441 * only get that much. 442 */ 443 444 static char * 445 get_string(pid_t pid, void *offset, int max) { 446 char *buf; 447 struct ptrace_io_desc iorequest; 448 int totalsize, size; 449 int diff = 0; 450 int i; 451 452 totalsize = size = max ? (max + 1) : BLOCKSIZE; 453 buf = malloc(totalsize); 454 if (buf == NULL) 455 return NULL; 456 for(;;) { 457 diff = totalsize - size; 458 iorequest.piod_op = PIOD_READ_D; 459 iorequest.piod_offs = (char *)offset + diff; 460 iorequest.piod_addr = buf + diff; 461 iorequest.piod_len = size; 462 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) { 463 free(buf); 464 return NULL; 465 } 466 for (i = 0 ; i < size; i++) { 467 if (buf[diff + i] == '\0') 468 return (buf); 469 } 470 if (totalsize < MAXSIZE - BLOCKSIZE && max == 0) { 471 totalsize += BLOCKSIZE; 472 buf = realloc(buf, totalsize); 473 size = BLOCKSIZE; 474 } 475 else { 476 buf[totalsize] = '\0'; 477 return buf; 478 } 479 } 480 } 481 482 483 /* 484 * print_arg 485 * Converts a syscall argument into a string. Said string is 486 * allocated via malloc(), so needs to be free()'d. The file 487 * descriptor is for the process' memory (via /proc), and is used 488 * to get any data (where the argument is a pointer). sc is 489 * a pointer to the syscall description (see above); args is 490 * an array of all of the system call arguments. 491 */ 492 493 char * 494 print_arg(struct syscall_args *sc, unsigned long *args, long retval, struct trussinfo *trussinfo) { 495 char *tmp = NULL; 496 int pid = trussinfo->pid; 497 switch (sc->type & ARG_MASK) { 498 case Hex: 499 asprintf(&tmp, "0x%x", (int)args[sc->offset]); 500 break; 501 case Octal: 502 asprintf(&tmp, "0%o", (int)args[sc->offset]); 503 break; 504 case Int: 505 asprintf(&tmp, "%d", (int)args[sc->offset]); 506 break; 507 case Name: 508 { 509 /* NULL-terminated string. */ 510 char *tmp2; 511 tmp2 = get_string(pid, (void*)args[sc->offset], 0); 512 asprintf(&tmp, "\"%s\"", tmp2); 513 free(tmp2); 514 } 515 break; 516 case BinString: 517 { 518 /* Binary block of data that might have printable characters. 519 XXX If type|OUT, assume that the length is the syscall's 520 return value. Otherwise, assume that the length of the block 521 is in the next syscall argument. */ 522 int max_string = trussinfo->strsize; 523 char tmp2[max_string+1], *tmp3; 524 int len; 525 int truncated = 0; 526 527 if (sc->type & OUT) 528 len = retval; 529 else 530 len = args[sc->offset + 1]; 531 532 /* Don't print more than max_string characters, to avoid word 533 wrap. If we have to truncate put some ... after the string. 534 */ 535 if (len > max_string) { 536 len = max_string; 537 truncated = 1; 538 } 539 if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len) != -1) { 540 tmp3 = malloc(len * 4 + 1); 541 while (len) { 542 if (strvisx(tmp3, tmp2, len, VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string) 543 break; 544 len--; 545 truncated = 1; 546 }; 547 asprintf(&tmp, "\"%s\"%s", tmp3, truncated?"...":""); 548 free(tmp3); 549 } else 550 asprintf(&tmp, "0x%lx", args[sc->offset]); 551 } 552 break; 553 case StringArray: 554 { 555 int num, size, i; 556 char *tmp2; 557 char *string; 558 char *strarray[100]; /* XXX This is ugly. */ 559 560 if (get_struct(pid, (void *)args[sc->offset], (void *)&strarray, 561 sizeof(strarray)) == -1) { 562 err(1, "get_struct %p", (void *)args[sc->offset]); 563 } 564 num = 0; 565 size = 0; 566 567 /* Find out how large of a buffer we'll need. */ 568 while (strarray[num] != NULL) { 569 string = get_string(pid, (void*)strarray[num], 0); 570 size += strlen(string); 571 free(string); 572 num++; 573 } 574 size += 4 + (num * 4); 575 tmp = (char *)malloc(size); 576 tmp2 = tmp; 577 578 tmp2 += sprintf(tmp2, " ["); 579 for (i = 0; i < num; i++) { 580 string = get_string(pid, (void*)strarray[i], 0); 581 tmp2 += sprintf(tmp2, " \"%s\"%c", string, (i+1 == num) ? ' ' : ','); 582 free(string); 583 } 584 tmp2 += sprintf(tmp2, "]"); 585 } 586 break; 587 #ifdef __LP64__ 588 case Quad: 589 asprintf(&tmp, "0x%lx", args[sc->offset]); 590 break; 591 #else 592 case Quad: 593 { 594 unsigned long long ll; 595 ll = *(unsigned long long *)(args + sc->offset); 596 asprintf(&tmp, "0x%llx", ll); 597 break; 598 } 599 #endif 600 case Ptr: 601 asprintf(&tmp, "0x%lx", args[sc->offset]); 602 break; 603 case Readlinkres: 604 { 605 char *tmp2; 606 if (retval == -1) { 607 tmp = strdup(""); 608 break; 609 } 610 tmp2 = get_string(pid, (void*)args[sc->offset], retval); 611 asprintf(&tmp, "\"%s\"", tmp2); 612 free(tmp2); 613 } 614 break; 615 case Ioctl: 616 { 617 const char *temp = ioctlname(args[sc->offset]); 618 if (temp) 619 tmp = strdup(temp); 620 else 621 { 622 unsigned long arg = args[sc->offset]; 623 asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu}", arg, 624 arg&IOC_OUT?"R":"", arg&IOC_IN?"W":"", 625 IOCGROUP(arg), isprint(IOCGROUP(arg))?(char)IOCGROUP(arg):'?', 626 arg & 0xFF, IOCPARM_LEN(arg)); 627 } 628 } 629 break; 630 case Umtx: 631 { 632 struct umtx umtx; 633 if (get_struct(pid, (void *)args[sc->offset], &umtx, sizeof(umtx)) != -1) 634 asprintf(&tmp, "{0x%lx}", (long)umtx.u_owner); 635 else 636 asprintf(&tmp, "0x%lx", args[sc->offset]); 637 } 638 break; 639 case Timespec: 640 { 641 struct timespec ts; 642 if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts)) != -1) 643 asprintf(&tmp, "{%ld.%09ld}", (long)ts.tv_sec, ts.tv_nsec); 644 else 645 asprintf(&tmp, "0x%lx", args[sc->offset]); 646 } 647 break; 648 case Timeval: 649 { 650 struct timeval tv; 651 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1) 652 asprintf(&tmp, "{%ld.%06ld}", (long)tv.tv_sec, tv.tv_usec); 653 else 654 asprintf(&tmp, "0x%lx", args[sc->offset]); 655 } 656 break; 657 case Timeval2: 658 { 659 struct timeval tv[2]; 660 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1) 661 asprintf(&tmp, "{%ld.%06ld, %ld.%06ld}", 662 (long)tv[0].tv_sec, tv[0].tv_usec, 663 (long)tv[1].tv_sec, tv[1].tv_usec); 664 else 665 asprintf(&tmp, "0x%lx", args[sc->offset]); 666 } 667 break; 668 case Itimerval: 669 { 670 struct itimerval itv; 671 if (get_struct(pid, (void *)args[sc->offset], &itv, sizeof(itv)) != -1) 672 asprintf(&tmp, "{%ld.%06ld, %ld.%06ld}", 673 (long)itv.it_interval.tv_sec, 674 itv.it_interval.tv_usec, 675 (long)itv.it_value.tv_sec, 676 itv.it_value.tv_usec); 677 else 678 asprintf(&tmp, "0x%lx", args[sc->offset]); 679 } 680 break; 681 case Pollfd: 682 { 683 /* 684 * XXX: A Pollfd argument expects the /next/ syscall argument to be 685 * the number of fds in the array. This matches the poll syscall. 686 */ 687 struct pollfd *pfd; 688 int numfds = args[sc->offset+1]; 689 int bytes = sizeof(struct pollfd) * numfds; 690 int i, tmpsize, u, used; 691 const int per_fd = 100; 692 693 if ((pfd = malloc(bytes)) == NULL) 694 err(1, "Cannot malloc %d bytes for pollfd array", bytes); 695 if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) != -1) { 696 697 used = 0; 698 tmpsize = 1 + per_fd * numfds + 2; 699 if ((tmp = malloc(tmpsize)) == NULL) 700 err(1, "Cannot alloc %d bytes for poll output", tmpsize); 701 702 tmp[used++] = '{'; 703 for (i = 0; i < numfds; i++) { 704 705 u = snprintf(tmp + used, per_fd, 706 "%s%d/%s", 707 i > 0 ? " " : "", 708 pfd[i].fd, 709 xlookup_bits(poll_flags, pfd[i].events) ); 710 if (u > 0) 711 used += u < per_fd ? u : per_fd; 712 } 713 tmp[used++] = '}'; 714 tmp[used++] = '\0'; 715 } else 716 asprintf(&tmp, "0x%lx", args[sc->offset]); 717 free(pfd); 718 } 719 break; 720 case Fd_set: 721 { 722 /* 723 * XXX: A Fd_set argument expects the /first/ syscall argument to be 724 * the number of fds in the array. This matches the select syscall. 725 */ 726 fd_set *fds; 727 int numfds = args[0]; 728 int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; 729 int i, tmpsize, u, used; 730 const int per_fd = 20; 731 732 if ((fds = malloc(bytes)) == NULL) 733 err(1, "Cannot malloc %d bytes for fd_set array", bytes); 734 if (get_struct(pid, (void *)args[sc->offset], fds, bytes) != -1) { 735 used = 0; 736 tmpsize = 1 + numfds * per_fd + 2; 737 if ((tmp = malloc(tmpsize)) == NULL) 738 err(1, "Cannot alloc %d bytes for fd_set output", tmpsize); 739 740 tmp[used++] = '{'; 741 for (i = 0; i < numfds; i++) { 742 if (FD_ISSET(i, fds)) { 743 u = snprintf(tmp + used, per_fd, "%d ", i); 744 if (u > 0) 745 used += u < per_fd ? u : per_fd; 746 } 747 } 748 if (tmp[used-1] == ' ') 749 used--; 750 tmp[used++] = '}'; 751 tmp[used++] = '\0'; 752 } else 753 asprintf(&tmp, "0x%lx", args[sc->offset]); 754 free(fds); 755 } 756 break; 757 case Signal: 758 { 759 long sig; 760 761 sig = args[sc->offset]; 762 tmp = strsig(sig); 763 if (tmp == NULL) 764 asprintf(&tmp, "%ld", sig); 765 } 766 break; 767 case Sigset: 768 { 769 long sig; 770 sigset_t ss; 771 int i, used; 772 773 sig = args[sc->offset]; 774 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 775 sizeof(ss)) == -1) 776 { 777 asprintf(&tmp, "0x%lx", args[sc->offset]); 778 break; 779 } 780 tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */ 781 used = 0; 782 for (i = 1; i < sys_nsig; i++) 783 { 784 if (sigismember(&ss, i)) 785 { 786 used += sprintf(tmp + used, "%s|", strsig(i)); 787 } 788 } 789 if(used) 790 tmp[used-1] = 0; 791 else 792 strcpy(tmp, "0x0"); 793 } 794 break; 795 case Sigprocmask: 796 { 797 switch (args[sc->offset]) { 798 #define S(a) case a: tmp = strdup(#a); break; 799 S(SIG_BLOCK); 800 S(SIG_UNBLOCK); 801 S(SIG_SETMASK); 802 #undef S 803 } 804 if (tmp == NULL) 805 asprintf(&tmp, "0x%lx", args[sc->offset]); 806 } 807 break; 808 809 case Fcntlflag: 810 { 811 /* XXX output depends on the value of the previous argument */ 812 switch (args[sc->offset-1]) { 813 case F_SETFD: 814 tmp = strdup(xlookup_bits(fcntlfd_arg, args[sc->offset])); 815 break; 816 case F_SETFL: 817 tmp = strdup(xlookup_bits(fcntlfl_arg, args[sc->offset])); 818 break; 819 case F_GETFD: 820 case F_GETFL: 821 case F_GETOWN: 822 tmp = strdup(""); 823 break; 824 default: 825 asprintf(&tmp, "0x%lx", args[sc->offset]); 826 break; 827 } 828 } 829 break; 830 case Open: 831 tmp = strdup(xlookup_bits(open_flags, args[sc->offset])); 832 break; 833 case Fcntl: 834 tmp = strdup(xlookup(fcntl_arg, args[sc->offset])); 835 break; 836 case Mprot: 837 tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset])); 838 break; 839 case Mmapflags: 840 tmp = strdup(xlookup_bits(mmap_flags, args[sc->offset])); 841 break; 842 case Whence: 843 tmp = strdup(xlookup(whence_arg, args[sc->offset])); 844 break; 845 case Sockdomain: 846 tmp = strdup(xlookup(sockdomain_arg, args[sc->offset])); 847 break; 848 case Socktype: 849 tmp = strdup(xlookup(socktype_arg, args[sc->offset])); 850 break; 851 case Shutdown: 852 tmp = strdup(xlookup(shutdown_arg, args[sc->offset])); 853 break; 854 case Resource: 855 tmp = strdup(xlookup(resource_arg, args[sc->offset])); 856 break; 857 case Pathconf: 858 tmp = strdup(xlookup(pathconf_arg, args[sc->offset])); 859 break; 860 case Sockaddr: 861 { 862 struct sockaddr_storage ss; 863 char addr[64]; 864 struct sockaddr_in *lsin; 865 struct sockaddr_in6 *lsin6; 866 struct sockaddr_un *sun; 867 struct sockaddr *sa; 868 char *p; 869 u_char *q; 870 int i; 871 872 if (args[sc->offset] == 0) { 873 asprintf(&tmp, "NULL"); 874 break; 875 } 876 877 /* yuck: get ss_len */ 878 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 879 sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) 880 err(1, "get_struct %p", (void *)args[sc->offset]); 881 /* 882 * If ss_len is 0, then try to guess from the sockaddr type. 883 * AF_UNIX may be initialized incorrectly, so always frob 884 * it by using the "right" size. 885 */ 886 if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) { 887 switch (ss.ss_family) { 888 case AF_INET: 889 ss.ss_len = sizeof(*lsin); 890 break; 891 case AF_UNIX: 892 ss.ss_len = sizeof(*sun); 893 break; 894 default: 895 /* hurrrr */ 896 break; 897 } 898 } 899 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, ss.ss_len) 900 == -1) { 901 err(2, "get_struct %p", (void *)args[sc->offset]); 902 } 903 904 switch (ss.ss_family) { 905 case AF_INET: 906 lsin = (struct sockaddr_in *)&ss; 907 inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr); 908 asprintf(&tmp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port)); 909 break; 910 case AF_INET6: 911 lsin6 = (struct sockaddr_in6 *)&ss; 912 inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, sizeof addr); 913 asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port)); 914 break; 915 case AF_UNIX: 916 sun = (struct sockaddr_un *)&ss; 917 asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path); 918 break; 919 default: 920 sa = (struct sockaddr *)&ss; 921 asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data = {%n%*s } }", 922 (int)sa->sa_len, (int)sa->sa_family, &i, 923 6 * (int)(sa->sa_len - ((char *)&sa->sa_data - (char *)sa)), ""); 924 if (tmp != NULL) { 925 p = tmp + i; 926 for (q = (u_char *)&sa->sa_data; q < (u_char *)sa + sa->sa_len; q++) 927 p += sprintf(p, " %#02x,", *q); 928 } 929 } 930 } 931 break; 932 case Sigaction: 933 { 934 struct sigaction sa; 935 char *hand; 936 const char *h; 937 938 if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa)) != -1) { 939 940 asprintf(&hand, "%p", sa.sa_handler); 941 if (sa.sa_handler == SIG_DFL) 942 h = "SIG_DFL"; 943 else if (sa.sa_handler == SIG_IGN) 944 h = "SIG_IGN"; 945 else 946 h = hand; 947 948 asprintf(&tmp, "{ %s %s ss_t }", 949 h, 950 xlookup_bits(sigaction_flags, sa.sa_flags)); 951 free(hand); 952 } else 953 asprintf(&tmp, "0x%lx", args[sc->offset]); 954 955 } 956 break; 957 case Kevent: 958 { 959 /* 960 * XXX XXX: the size of the array is determined by either the 961 * next syscall argument, or by the syscall returnvalue, 962 * depending on which argument number we are. This matches the 963 * kevent syscall, but luckily that's the only syscall that uses 964 * them. 965 */ 966 struct kevent *ke; 967 int numevents = -1; 968 int bytes = 0; 969 int i, tmpsize, u, used; 970 const int per_ke = 100; 971 972 if (sc->offset == 1) 973 numevents = args[sc->offset+1]; 974 else if (sc->offset == 3 && retval != -1) 975 numevents = retval; 976 977 if (numevents >= 0) 978 bytes = sizeof(struct kevent) * numevents; 979 if ((ke = malloc(bytes)) == NULL) 980 err(1, "Cannot malloc %d bytes for kevent array", bytes); 981 if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset], ke, bytes) != -1) { 982 used = 0; 983 tmpsize = 1 + per_ke * numevents + 2; 984 if ((tmp = malloc(tmpsize)) == NULL) 985 err(1, "Cannot alloc %d bytes for kevent output", tmpsize); 986 987 tmp[used++] = '{'; 988 for (i = 0; i < numevents; i++) { 989 u = snprintf(tmp + used, per_ke, 990 "%s%p,%s,%s,%d,%p,%p", 991 i > 0 ? " " : "", 992 (void *)ke[i].ident, 993 xlookup(kevent_filters, ke[i].filter), 994 xlookup_bits(kevent_flags, ke[i].flags), 995 ke[i].fflags, 996 (void *)ke[i].data, 997 (void *)ke[i].udata); 998 if (u > 0) 999 used += u < per_ke ? u : per_ke; 1000 } 1001 tmp[used++] = '}'; 1002 tmp[used++] = '\0'; 1003 } else 1004 asprintf(&tmp, "0x%lx", args[sc->offset]); 1005 free(ke); 1006 } 1007 break; 1008 case Stat: 1009 { 1010 struct stat st; 1011 if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st)) != -1) { 1012 char mode[12]; 1013 strmode(st.st_mode, mode); 1014 asprintf(&tmp, "{mode=%s,inode=%jd,size=%jd,blksize=%ld}", 1015 mode, 1016 (intmax_t)st.st_ino,(intmax_t)st.st_size,(long)st.st_blksize); 1017 } else 1018 asprintf(&tmp, "0x%lx", args[sc->offset]); 1019 } 1020 break; 1021 case Rusage: 1022 { 1023 struct rusage ru; 1024 if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) != -1) 1025 asprintf(&tmp, "{u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld}", 1026 (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, 1027 (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, 1028 ru.ru_inblock, ru.ru_oublock); 1029 else 1030 asprintf(&tmp, "0x%lx", args[sc->offset]); 1031 } 1032 break; 1033 case Rlimit: 1034 { 1035 struct rlimit rl; 1036 if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl)) != -1) 1037 asprintf(&tmp, "{cur=%ju,max=%ju}", 1038 rl.rlim_cur, rl.rlim_max); 1039 else 1040 asprintf(&tmp, "0x%lx", args[sc->offset]); 1041 } 1042 break; 1043 default: 1044 errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); 1045 } 1046 return tmp; 1047 } 1048 1049 1050 /* 1051 * print_syscall 1052 * Print (to outfile) the system call and its arguments. Note that 1053 * nargs is the number of arguments (not the number of words; this is 1054 * potentially confusing, I know). 1055 */ 1056 1057 void 1058 print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args) { 1059 int i; 1060 int len = 0; 1061 struct timespec timediff; 1062 1063 if (trussinfo->flags & FOLLOWFORKS) 1064 len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); 1065 1066 if (name != NULL && (!strcmp(name, "execve") || !strcmp(name, "exit"))) { 1067 clock_gettime(CLOCK_REALTIME, &trussinfo->after); 1068 } 1069 1070 if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 1071 timespecsubt(&trussinfo->after, &trussinfo->start_time, &timediff); 1072 len += fprintf(trussinfo->outfile, "%ld.%09ld ", 1073 (long)timediff.tv_sec, timediff.tv_nsec); 1074 } 1075 1076 if (trussinfo->flags & RELATIVETIMESTAMPS) { 1077 timespecsubt(&trussinfo->after, &trussinfo->before, &timediff); 1078 len += fprintf(trussinfo->outfile, "%ld.%09ld ", 1079 (long)timediff.tv_sec, timediff.tv_nsec); 1080 } 1081 1082 len += fprintf(trussinfo->outfile, "%s(", name); 1083 1084 for (i = 0; i < nargs; i++) { 1085 if (s_args[i]) 1086 len += fprintf(trussinfo->outfile, "%s", s_args[i]); 1087 else 1088 len += fprintf(trussinfo->outfile, "<missing argument>"); 1089 len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : ""); 1090 } 1091 len += fprintf(trussinfo->outfile, ")"); 1092 for (i = 0; i < 6 - (len / 8); i++) 1093 fprintf(trussinfo->outfile, "\t"); 1094 } 1095 1096 void 1097 print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, 1098 char **s_args, int errorp, long retval) 1099 { 1100 print_syscall(trussinfo, name, nargs, s_args); 1101 fflush(trussinfo->outfile); 1102 if (errorp) { 1103 fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval, strerror(retval)); 1104 } else { 1105 fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval); 1106 } 1107 } 1108