syscalls.c revision 101285
131567Ssef/* 231899Ssef * Copryight 1997 Sean Eric Fagan 331899Ssef * 431899Ssef * Redistribution and use in source and binary forms, with or without 531899Ssef * modification, are permitted provided that the following conditions 631899Ssef * are met: 731899Ssef * 1. Redistributions of source code must retain the above copyright 831899Ssef * notice, this list of conditions and the following disclaimer. 931899Ssef * 2. Redistributions in binary form must reproduce the above copyright 1031899Ssef * notice, this list of conditions and the following disclaimer in the 1131899Ssef * documentation and/or other materials provided with the distribution. 1231899Ssef * 3. All advertising materials mentioning features or use of this software 1331899Ssef * must display the following acknowledgement: 1431899Ssef * This product includes software developed by Sean Eric Fagan 1531899Ssef * 4. Neither the name of the author may be used to endorse or promote 1631899Ssef * products derived from this software without specific prior written 1731899Ssef * permission. 1831899Ssef * 1931899Ssef * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2031899Ssef * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2131899Ssef * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2231899Ssef * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2331899Ssef * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2431899Ssef * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2531899Ssef * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2631899Ssef * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2731899Ssef * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2831899Ssef * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2931899Ssef * SUCH DAMAGE. 3031899Ssef */ 3131899Ssef 3232275Scharnier#ifndef lint 3332275Scharnierstatic const char rcsid[] = 3450477Speter "$FreeBSD: head/usr.bin/truss/syscalls.c 101285 2002-08-04 01:27:31Z mdodd $"; 3532275Scharnier#endif /* not lint */ 3632275Scharnier 3731899Ssef/* 3831567Ssef * This file has routines used to print out system calls and their 3931567Ssef * arguments. 4031567Ssef */ 4131567Ssef 4285292Sdes#include <sys/types.h> 43101285Smdodd#include <sys/time.h> 4485292Sdes#include <sys/socket.h> 4585292Sdes#include <sys/un.h> 4685292Sdes#include <netinet/in.h> 4785292Sdes#include <arpa/inet.h> 4885292Sdes 4986138Sgreen#include <ctype.h> 5032275Scharnier#include <err.h> 5185292Sdes#include <signal.h> 5231567Ssef#include <stdio.h> 5331567Ssef#include <stdlib.h> 5431567Ssef#include <string.h> 5531567Ssef#include <unistd.h> 5685292Sdes 57101282Smdodd#include "truss.h" 5887703Smarkm#include "extern.h" 5931567Ssef#include "syscall.h" 6031567Ssef 6131567Ssef/* 6231567Ssef * This should probably be in its own file. 6331567Ssef */ 6431567Ssef 6531567Ssefstruct syscall syscalls[] = { 6631567Ssef { "readlink", 1, 3, 6731567Ssef { { String, 0 } , { String | OUT, 1 }, { Int, 2 }}}, 6831567Ssef { "lseek", 2, 3, 6931567Ssef { { Int, 0 }, {Quad, 2 }, { Int, 4 }}}, 7031567Ssef { "mmap", 2, 6, 7131567Ssef { { Hex, 0 }, {Int, 1}, {Hex, 2}, {Hex, 3}, {Int, 4}, {Quad, 6}}}, 7231567Ssef { "open", 1, 3, 7388726Salfred { { String | IN, 0} , { Hex, 1}, {Octal, 2}}}, 7431567Ssef { "linux_open", 1, 3, 7588726Salfred { { String, 0 }, { Hex, 1}, { Octal, 2 }}}, 7631567Ssef { "close", 1, 1, { { Int, 0 } } }, 7731567Ssef { "fstat", 1, 2, 7831567Ssef { { Int, 0}, {Ptr | OUT , 1 }}}, 7931567Ssef { "stat", 1, 2, 8031567Ssef { { String | IN, 0 }, { Ptr | OUT, 1 }}}, 8140370Ssef { "lstat", 1, 2, 8240370Ssef { { String | IN, 0 }, { Ptr | OUT, 1 }}}, 8331567Ssef { "linux_newstat", 1, 2, 8431567Ssef { { String | IN, 0 }, { Ptr | OUT, 1 }}}, 8531567Ssef { "linux_newfstat", 1, 2, 8631567Ssef { { Int, 0 }, { Ptr | OUT, 1 }}}, 8731567Ssef { "write", 1, 3, 8831567Ssef { { Int, 0}, { Ptr | IN, 1 }, { Int, 2 }}}, 8931571Ssef { "ioctl", 1, 3, 9031571Ssef { { Int, 0}, { Ioctl, 1 }, { Hex, 2 }}}, 9131567Ssef { "break", 1, 1, { { Hex, 0 }}}, 9231567Ssef { "exit", 0, 1, { { Hex, 0 }}}, 9349436Sdes { "access", 1, 2, { { String | IN, 0 }, { Int, 1 }}}, 9449609Sdes { "sigaction", 1, 3, 9549609Sdes { { Signal, 0 }, { Ptr | IN, 1 }, { Ptr | OUT, 2 }}}, 9685292Sdes { "accept", 1, 3, 9785292Sdes { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 9885292Sdes { "bind", 1, 3, 9985292Sdes { { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 10085292Sdes { "connect", 1, 3, 10185292Sdes { { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 10285292Sdes { "getpeername", 1, 3, 10385292Sdes { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 10485292Sdes { "getsockname", 1, 3, 10585292Sdes { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 10632275Scharnier { 0, 0, 0, { { 0, 0 }}}, 10731567Ssef}; 10831567Ssef 10931567Ssef/* 11031567Ssef * If/when the list gets big, it might be desirable to do it 11131567Ssef * as a hash table or binary search. 11231567Ssef */ 11331567Ssef 11431567Ssefstruct syscall * 11531567Ssefget_syscall(const char *name) { 11631567Ssef struct syscall *sc = syscalls; 11731567Ssef 11831567Ssef while (sc->name) { 11931567Ssef if (!strcmp(name, sc->name)) 12031567Ssef return sc; 12131567Ssef sc++; 12231567Ssef } 12331567Ssef return NULL; 12431567Ssef} 12531567Ssef 12631567Ssef/* 12785292Sdes * get_struct 12885292Sdes * 12985292Sdes * Copy a fixed amount of bytes from the process. 13085292Sdes */ 13185292Sdes 13287703Smarkmstatic int 13385292Sdesget_struct(int procfd, void *offset, void *buf, int len) { 13485292Sdes char *pos; 13585292Sdes FILE *p; 13685292Sdes int c, fd; 13785292Sdes 13885292Sdes if ((fd = dup(procfd)) == -1) 13985292Sdes err(1, "dup"); 14085292Sdes if ((p = fdopen(fd, "r")) == NULL) 14185292Sdes err(1, "fdopen"); 14295225Sdwmalone fseeko(p, (uintptr_t)offset, SEEK_SET); 14385292Sdes for (pos = (char *)buf; len--; pos++) { 14485292Sdes if ((c = fgetc(p)) == EOF) 14585292Sdes return -1; 14685292Sdes *pos = c; 14785292Sdes } 14885292Sdes fclose(p); 14985292Sdes return 0; 15085292Sdes} 15185292Sdes 15285292Sdes/* 15331567Ssef * get_string 15431567Ssef * Copy a string from the process. Note that it is 15531567Ssef * expected to be a C string, but if max is set, it will 15631567Ssef * only get that much. 15731567Ssef */ 15831567Ssef 15931567Ssefchar * 16031567Ssefget_string(int procfd, void *offset, int max) { 16132275Scharnier char *buf; 16257245Ssef int size, len, c, fd; 16331567Ssef FILE *p; 16431567Ssef 16557245Ssef if ((fd = dup(procfd)) == -1) 16657245Ssef err(1, "dup"); 16757245Ssef if ((p = fdopen(fd, "r")) == NULL) 16832275Scharnier err(1, "fdopen"); 16931567Ssef buf = malloc( size = (max ? max : 64 ) ); 17031567Ssef len = 0; 17182471Sjoerg buf[0] = 0; 17295225Sdwmalone fseeko(p, (uintptr_t)offset, SEEK_SET); 17331567Ssef while ((c = fgetc(p)) != EOF) { 17431567Ssef buf[len++] = c; 17531567Ssef if (c == 0 || len == max) { 17631567Ssef buf[len] = 0; 17731567Ssef break; 17831567Ssef } 17931567Ssef if (len == size) { 18057245Ssef char *tmp; 18131567Ssef tmp = realloc(buf, size+64); 18231567Ssef if (tmp == NULL) { 18331567Ssef buf[len] = 0; 18457245Ssef fclose(p); 18531567Ssef return buf; 18631567Ssef } 18731567Ssef size += 64; 18857245Ssef buf = tmp; 18931567Ssef } 19031567Ssef } 19157245Ssef fclose(p); 19231567Ssef return buf; 19331567Ssef} 19431567Ssef 19531567Ssef 19631567Ssef/* 19731567Ssef * Gag. This is really unportable. Multiplication is more portable. 19831567Ssef * But slower, from the code I saw. 19931567Ssef */ 20031567Ssef 20131567Ssefstatic long long 20231567Ssefmake_quad(unsigned long p1, unsigned long p2) { 20331567Ssef union { 20431567Ssef long long ll; 20531567Ssef unsigned long l[2]; 20631567Ssef } t; 20731567Ssef t.l[0] = p1; 20831567Ssef t.l[1] = p2; 20931567Ssef return t.ll; 21031567Ssef} 21131567Ssef 21231567Ssef 21331567Ssef/* 21431567Ssef * print_arg 21531567Ssef * Converts a syscall argument into a string. Said string is 21631567Ssef * allocated via malloc(), so needs to be free()'d. The file 21731567Ssef * descriptor is for the process' memory (via /proc), and is used 21831567Ssef * to get any data (where the argument is a pointer). sc is 21931567Ssef * a pointer to the syscall description (see above); args is 22031567Ssef * an array of all of the system call arguments. 22131567Ssef */ 22231567Ssef 22331567Ssefchar * 22431567Ssefprint_arg(int fd, struct syscall_args *sc, unsigned long *args) { 22532275Scharnier char *tmp = NULL; 22631567Ssef switch (sc->type & ARG_MASK) { 22731567Ssef case Hex: 22831567Ssef tmp = malloc(12); 22937453Sbde sprintf(tmp, "0x%lx", args[sc->offset]); 23031567Ssef break; 23131567Ssef case Octal: 23231567Ssef tmp = malloc(13); 23337453Sbde sprintf(tmp, "0%lo", args[sc->offset]); 23431567Ssef break; 23531567Ssef case Int: 23631567Ssef tmp = malloc(12); 23737453Sbde sprintf(tmp, "%ld", args[sc->offset]); 23831567Ssef break; 23931567Ssef case String: 24031567Ssef { 24131567Ssef char *tmp2; 24231567Ssef tmp2 = get_string(fd, (void*)args[sc->offset], 0); 24331567Ssef tmp = malloc(strlen(tmp2) + 3); 24431567Ssef sprintf(tmp, "\"%s\"", tmp2); 24531567Ssef free(tmp2); 24631567Ssef } 24731567Ssef break; 24831567Ssef case Quad: 24931567Ssef { 25031567Ssef unsigned long long t; 25131567Ssef unsigned long l1, l2; 25231567Ssef l1 = args[sc->offset]; 25331567Ssef l2 = args[sc->offset+1]; 25431567Ssef t = make_quad(l1, l2); 25531567Ssef tmp = malloc(24); 25631567Ssef sprintf(tmp, "0x%qx", t); 25731567Ssef break; 25831567Ssef } 25931567Ssef case Ptr: 26031567Ssef tmp = malloc(12); 26137453Sbde sprintf(tmp, "0x%lx", args[sc->offset]); 26231567Ssef break; 26331571Ssef case Ioctl: 26431571Ssef { 26587703Smarkm const char *temp = ioctlname(args[sc->offset]); 26631571Ssef if (temp) 26731571Ssef tmp = strdup(temp); 26831571Ssef else { 26931571Ssef tmp = malloc(12); 27037453Sbde sprintf(tmp, "0x%lx", args[sc->offset]); 27131571Ssef } 27231571Ssef } 27349609Sdes break; 27449609Sdes case Signal: 27549609Sdes { 27649609Sdes long sig; 27749609Sdes 27849609Sdes sig = args[sc->offset]; 27949609Sdes tmp = malloc(12); 28049609Sdes if (sig > 0 && sig < NSIG) { 28149609Sdes int i; 28249609Sdes sprintf(tmp, "sig%s", sys_signame[sig]); 28349609Sdes for (i = 0; tmp[i] != '\0'; ++i) 28449609Sdes tmp[i] = toupper(tmp[i]); 28549609Sdes } else { 28649609Sdes sprintf(tmp, "%ld", sig); 28749609Sdes } 28849609Sdes } 28949609Sdes break; 29085292Sdes case Sockaddr: 29185292Sdes { 29286138Sgreen struct sockaddr_storage ss; 29386138Sgreen char addr[64]; 29487703Smarkm struct sockaddr_in *lsin; 29587703Smarkm struct sockaddr_in6 *lsin6; 29686138Sgreen struct sockaddr_un *sun; 29785292Sdes struct sockaddr *sa; 29886138Sgreen char *p; 29986138Sgreen u_char *q; 30086138Sgreen int i; 30185292Sdes 30286138Sgreen /* yuck: get ss_len */ 30386138Sgreen if (get_struct(fd, (void *)args[sc->offset], (void *)&ss, 30486138Sgreen sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) 30586138Sgreen err(1, "get_struct %p", (void *)args[sc->offset]); 30686138Sgreen /* sockaddr_un never have the length filled in! */ 30786138Sgreen if (ss.ss_family == AF_UNIX) { 30886138Sgreen if (get_struct(fd, (void *)args[sc->offset], (void *)&ss, 30986138Sgreen sizeof(*sun)) 31086138Sgreen == -1) 31186138Sgreen err(2, "get_struct %p", (void *)args[sc->offset]); 31286138Sgreen } else { 31386138Sgreen if (get_struct(fd, (void *)args[sc->offset], (void *)&ss, ss.ss_len) 31486138Sgreen == -1) 31586138Sgreen err(2, "get_struct %p", (void *)args[sc->offset]); 31686138Sgreen } 31785292Sdes 31886138Sgreen switch (ss.ss_family) { 31986138Sgreen case AF_INET: 32087703Smarkm lsin = (struct sockaddr_in *)&ss; 32187703Smarkm inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr); 32287703Smarkm asprintf(&tmp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port)); 32386138Sgreen break; 32486138Sgreen case AF_INET6: 32587703Smarkm lsin6 = (struct sockaddr_in6 *)&ss; 32687703Smarkm inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, sizeof addr); 32787703Smarkm asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port)); 32886138Sgreen break; 32986138Sgreen case AF_UNIX: 33086138Sgreen sun = (struct sockaddr_un *)&ss; 33186138Sgreen asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path); 33286138Sgreen break; 33386138Sgreen default: 33486138Sgreen sa = (struct sockaddr *)&ss; 33586138Sgreen asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data = {%n%*s } }", 33686138Sgreen (int)sa->sa_len, (int)sa->sa_family, &i, 33786138Sgreen 6 * (int)(sa->sa_len - ((char *)&sa->sa_data - (char *)sa)), ""); 33886138Sgreen if (tmp != NULL) { 33986138Sgreen p = tmp + i; 34086138Sgreen for (q = (u_char *)&sa->sa_data; q < (u_char *)sa + sa->sa_len; q++) 34186138Sgreen p += sprintf(p, " %#02x,", *q); 34286138Sgreen } 34385292Sdes } 34485292Sdes } 34585292Sdes break; 34631567Ssef } 34731567Ssef return tmp; 34831567Ssef} 34931567Ssef 35031567Ssef/* 35131567Ssef * print_syscall 35231567Ssef * Print (to outfile) the system call and its arguments. Note that 35331567Ssef * nargs is the number of arguments (not the number of words; this is 35431567Ssef * potentially confusing, I know). 35531567Ssef */ 35631567Ssef 35731567Ssefvoid 358101282Smdoddprint_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args) { 35931567Ssef int i; 36058224Ssef int len = 0; 361101283Smdodd 362101285Smdodd struct timeval timediff; 363101285Smdodd 364101283Smdodd if (trussinfo->flags & FOLLOWFORKS) 365101283Smdodd len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); 366101283Smdodd 367101285Smdodd if (!strcmp(name, "execve") || !strcmp(name, "exit")) { 368101285Smdodd gettimeofday(&trussinfo->after, (struct timezone *)NULL); 369101285Smdodd } 370101285Smdodd 371101285Smdodd if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 372101285Smdodd timersub(&trussinfo->after, &trussinfo->start_time, &timediff); 373101285Smdodd len += fprintf(trussinfo->outfile, "%d.%0.7d ", 374101285Smdodd timediff.tv_sec, timediff.tv_usec); 375101285Smdodd } 376101285Smdodd 377101285Smdodd if (trussinfo->flags & RELATIVETIMESTAMPS) { 378101285Smdodd timersub(&trussinfo->after, &trussinfo->before, &timediff); 379101285Smdodd len += fprintf(trussinfo->outfile, "%d.%0.7d ", 380101285Smdodd timediff.tv_sec, timediff.tv_usec); 381101285Smdodd } 382101285Smdodd 383101282Smdodd len += fprintf(trussinfo->outfile, "%s(", name); 384101283Smdodd 38531567Ssef for (i = 0; i < nargs; i++) { 38631567Ssef if (s_args[i]) 387101282Smdodd len += fprintf(trussinfo->outfile, "%s", s_args[i]); 38831567Ssef else 389101282Smdodd len += fprintf(trussinfo->outfile, "<missing argument>"); 390101282Smdodd len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : ""); 39131567Ssef } 392101282Smdodd len += fprintf(trussinfo->outfile, ")"); 39358224Ssef for (i = 0; i < 6 - (len / 8); i++) 394101282Smdodd fprintf(trussinfo->outfile, "\t"); 39531567Ssef} 39658224Ssef 39758224Ssefvoid 398101282Smdoddprint_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args, int errorp, int retval) { 399101282Smdodd print_syscall(trussinfo, name, nargs, s_args); 40058224Ssef if (errorp) { 401101282Smdodd fprintf(trussinfo->outfile, " ERR#%d '%s'\n", retval, strerror(retval)); 40258224Ssef } else { 403101282Smdodd fprintf(trussinfo->outfile, " = %d (0x%x)\n", retval, retval); 40458224Ssef } 40558224Ssef} 406