syscalls.c revision 101423
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 101423 2002-08-06 12:46:14Z 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> 4385292Sdes#include <sys/socket.h> 4485292Sdes#include <sys/un.h> 4585292Sdes#include <netinet/in.h> 4685292Sdes#include <arpa/inet.h> 4785292Sdes 4886138Sgreen#include <ctype.h> 4932275Scharnier#include <err.h> 5085292Sdes#include <signal.h> 5131567Ssef#include <stdio.h> 5231567Ssef#include <stdlib.h> 5331567Ssef#include <string.h> 54101423Smdodd#include <time.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 } } }, 106101289Smdodd { "execve", 1, 3, 107101289Smdodd { { String | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, 108101289Smdodd { "linux_execve", 1, 3, 109101289Smdodd { { String | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, 11032275Scharnier { 0, 0, 0, { { 0, 0 }}}, 11131567Ssef}; 11231567Ssef 11331567Ssef/* 11431567Ssef * If/when the list gets big, it might be desirable to do it 11531567Ssef * as a hash table or binary search. 11631567Ssef */ 11731567Ssef 11831567Ssefstruct syscall * 11931567Ssefget_syscall(const char *name) { 12031567Ssef struct syscall *sc = syscalls; 12131567Ssef 12231567Ssef while (sc->name) { 12331567Ssef if (!strcmp(name, sc->name)) 12431567Ssef return sc; 12531567Ssef sc++; 12631567Ssef } 12731567Ssef return NULL; 12831567Ssef} 12931567Ssef 13031567Ssef/* 13185292Sdes * get_struct 13285292Sdes * 13385292Sdes * Copy a fixed amount of bytes from the process. 13485292Sdes */ 13585292Sdes 13687703Smarkmstatic int 13785292Sdesget_struct(int procfd, void *offset, void *buf, int len) { 13885292Sdes char *pos; 13985292Sdes FILE *p; 14085292Sdes int c, fd; 14185292Sdes 14285292Sdes if ((fd = dup(procfd)) == -1) 14385292Sdes err(1, "dup"); 14485292Sdes if ((p = fdopen(fd, "r")) == NULL) 14585292Sdes err(1, "fdopen"); 14695225Sdwmalone fseeko(p, (uintptr_t)offset, SEEK_SET); 14785292Sdes for (pos = (char *)buf; len--; pos++) { 14885292Sdes if ((c = fgetc(p)) == EOF) 14985292Sdes return -1; 15085292Sdes *pos = c; 15185292Sdes } 15285292Sdes fclose(p); 15385292Sdes return 0; 15485292Sdes} 15585292Sdes 15685292Sdes/* 15731567Ssef * get_string 15831567Ssef * Copy a string from the process. Note that it is 15931567Ssef * expected to be a C string, but if max is set, it will 16031567Ssef * only get that much. 16131567Ssef */ 16231567Ssef 16331567Ssefchar * 16431567Ssefget_string(int procfd, void *offset, int max) { 16532275Scharnier char *buf; 16657245Ssef int size, len, c, fd; 16731567Ssef FILE *p; 16831567Ssef 16957245Ssef if ((fd = dup(procfd)) == -1) 17057245Ssef err(1, "dup"); 17157245Ssef if ((p = fdopen(fd, "r")) == NULL) 17232275Scharnier err(1, "fdopen"); 17331567Ssef buf = malloc( size = (max ? max : 64 ) ); 17431567Ssef len = 0; 17582471Sjoerg buf[0] = 0; 17695225Sdwmalone fseeko(p, (uintptr_t)offset, SEEK_SET); 17731567Ssef while ((c = fgetc(p)) != EOF) { 17831567Ssef buf[len++] = c; 17931567Ssef if (c == 0 || len == max) { 18031567Ssef buf[len] = 0; 18131567Ssef break; 18231567Ssef } 18331567Ssef if (len == size) { 18457245Ssef char *tmp; 18531567Ssef tmp = realloc(buf, size+64); 18631567Ssef if (tmp == NULL) { 18731567Ssef buf[len] = 0; 18857245Ssef fclose(p); 18931567Ssef return buf; 19031567Ssef } 19131567Ssef size += 64; 19257245Ssef buf = tmp; 19331567Ssef } 19431567Ssef } 19557245Ssef fclose(p); 19631567Ssef return buf; 19731567Ssef} 19831567Ssef 19931567Ssef 20031567Ssef/* 20131567Ssef * Gag. This is really unportable. Multiplication is more portable. 20231567Ssef * But slower, from the code I saw. 20331567Ssef */ 20431567Ssef 20531567Ssefstatic long long 20631567Ssefmake_quad(unsigned long p1, unsigned long p2) { 20731567Ssef union { 20831567Ssef long long ll; 20931567Ssef unsigned long l[2]; 21031567Ssef } t; 21131567Ssef t.l[0] = p1; 21231567Ssef t.l[1] = p2; 21331567Ssef return t.ll; 21431567Ssef} 21531567Ssef 21631567Ssef 21731567Ssef/* 21831567Ssef * print_arg 21931567Ssef * Converts a syscall argument into a string. Said string is 22031567Ssef * allocated via malloc(), so needs to be free()'d. The file 22131567Ssef * descriptor is for the process' memory (via /proc), and is used 22231567Ssef * to get any data (where the argument is a pointer). sc is 22331567Ssef * a pointer to the syscall description (see above); args is 22431567Ssef * an array of all of the system call arguments. 22531567Ssef */ 22631567Ssef 22731567Ssefchar * 22831567Ssefprint_arg(int fd, struct syscall_args *sc, unsigned long *args) { 22932275Scharnier char *tmp = NULL; 23031567Ssef switch (sc->type & ARG_MASK) { 23131567Ssef case Hex: 23231567Ssef tmp = malloc(12); 23337453Sbde sprintf(tmp, "0x%lx", args[sc->offset]); 23431567Ssef break; 23531567Ssef case Octal: 23631567Ssef tmp = malloc(13); 23737453Sbde sprintf(tmp, "0%lo", args[sc->offset]); 23831567Ssef break; 23931567Ssef case Int: 24031567Ssef tmp = malloc(12); 24137453Sbde sprintf(tmp, "%ld", args[sc->offset]); 24231567Ssef break; 24331567Ssef case String: 24431567Ssef { 24531567Ssef char *tmp2; 24631567Ssef tmp2 = get_string(fd, (void*)args[sc->offset], 0); 24731567Ssef tmp = malloc(strlen(tmp2) + 3); 24831567Ssef sprintf(tmp, "\"%s\"", tmp2); 24931567Ssef free(tmp2); 25031567Ssef } 25131567Ssef break; 252101289Smdodd case StringArray: 253101289Smdodd { 254101289Smdodd int num, size, i; 255101289Smdodd char *tmp2; 256101289Smdodd char *string; 257101289Smdodd char *strarray[100]; /* XXX This is ugly. */ 258101289Smdodd 259101289Smdodd if (get_struct(fd, (void *)args[sc->offset], (void *)&strarray, 260101289Smdodd sizeof(strarray)) == -1) { 261101289Smdodd err(1, "get_struct %p", (void *)args[sc->offset]); 262101289Smdodd } 263101289Smdodd num = 0; 264101289Smdodd size = 0; 265101289Smdodd 266101289Smdodd /* Find out how large of a buffer we'll need. */ 267101289Smdodd while (strarray[num] != NULL) { 268101289Smdodd string = get_string(fd, (void*)strarray[num], 0); 269101289Smdodd size += strlen(string); 270101289Smdodd free(string); 271101289Smdodd num++; 272101289Smdodd } 273101289Smdodd size += 4 + (num * 4); 274101289Smdodd tmp = (char *)malloc(size); 275101289Smdodd tmp2 = tmp; 276101289Smdodd 277101289Smdodd tmp2 += sprintf(tmp2, " ["); 278101289Smdodd for (i = 0; i < num; i++) { 279101289Smdodd string = get_string(fd, (void*)strarray[i], 0); 280101289Smdodd tmp2 += sprintf(tmp2, " \"%s\"%c", string, (i+1 == num) ? ' ' : ','); 281101289Smdodd free(string); 282101289Smdodd } 283101289Smdodd tmp2 += sprintf(tmp2, "]"); 284101289Smdodd } 285101289Smdodd break; 28631567Ssef case Quad: 28731567Ssef { 28831567Ssef unsigned long long t; 28931567Ssef unsigned long l1, l2; 29031567Ssef l1 = args[sc->offset]; 29131567Ssef l2 = args[sc->offset+1]; 29231567Ssef t = make_quad(l1, l2); 29331567Ssef tmp = malloc(24); 29431567Ssef sprintf(tmp, "0x%qx", t); 29531567Ssef break; 29631567Ssef } 29731567Ssef case Ptr: 29831567Ssef tmp = malloc(12); 29937453Sbde sprintf(tmp, "0x%lx", args[sc->offset]); 30031567Ssef break; 30131571Ssef case Ioctl: 30231571Ssef { 30387703Smarkm const char *temp = ioctlname(args[sc->offset]); 30431571Ssef if (temp) 30531571Ssef tmp = strdup(temp); 30631571Ssef else { 30731571Ssef tmp = malloc(12); 30837453Sbde sprintf(tmp, "0x%lx", args[sc->offset]); 30931571Ssef } 31031571Ssef } 31149609Sdes break; 31249609Sdes case Signal: 31349609Sdes { 31449609Sdes long sig; 31549609Sdes 31649609Sdes sig = args[sc->offset]; 31749609Sdes tmp = malloc(12); 31849609Sdes if (sig > 0 && sig < NSIG) { 31949609Sdes int i; 32049609Sdes sprintf(tmp, "sig%s", sys_signame[sig]); 32149609Sdes for (i = 0; tmp[i] != '\0'; ++i) 32249609Sdes tmp[i] = toupper(tmp[i]); 32349609Sdes } else { 32449609Sdes sprintf(tmp, "%ld", sig); 32549609Sdes } 32649609Sdes } 32749609Sdes break; 32885292Sdes case Sockaddr: 32985292Sdes { 33086138Sgreen struct sockaddr_storage ss; 33186138Sgreen char addr[64]; 33287703Smarkm struct sockaddr_in *lsin; 33387703Smarkm struct sockaddr_in6 *lsin6; 33486138Sgreen struct sockaddr_un *sun; 33585292Sdes struct sockaddr *sa; 33686138Sgreen char *p; 33786138Sgreen u_char *q; 33886138Sgreen int i; 33985292Sdes 34086138Sgreen /* yuck: get ss_len */ 34186138Sgreen if (get_struct(fd, (void *)args[sc->offset], (void *)&ss, 34286138Sgreen sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) 34386138Sgreen err(1, "get_struct %p", (void *)args[sc->offset]); 34486138Sgreen /* sockaddr_un never have the length filled in! */ 34586138Sgreen if (ss.ss_family == AF_UNIX) { 34686138Sgreen if (get_struct(fd, (void *)args[sc->offset], (void *)&ss, 34786138Sgreen sizeof(*sun)) 34886138Sgreen == -1) 34986138Sgreen err(2, "get_struct %p", (void *)args[sc->offset]); 35086138Sgreen } else { 35186138Sgreen if (get_struct(fd, (void *)args[sc->offset], (void *)&ss, ss.ss_len) 35286138Sgreen == -1) 35386138Sgreen err(2, "get_struct %p", (void *)args[sc->offset]); 35486138Sgreen } 35585292Sdes 35686138Sgreen switch (ss.ss_family) { 35786138Sgreen case AF_INET: 35887703Smarkm lsin = (struct sockaddr_in *)&ss; 35987703Smarkm inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr); 36087703Smarkm asprintf(&tmp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port)); 36186138Sgreen break; 36286138Sgreen case AF_INET6: 36387703Smarkm lsin6 = (struct sockaddr_in6 *)&ss; 36487703Smarkm inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, sizeof addr); 36587703Smarkm asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port)); 36686138Sgreen break; 36786138Sgreen case AF_UNIX: 36886138Sgreen sun = (struct sockaddr_un *)&ss; 36986138Sgreen asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path); 37086138Sgreen break; 37186138Sgreen default: 37286138Sgreen sa = (struct sockaddr *)&ss; 37386138Sgreen asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data = {%n%*s } }", 37486138Sgreen (int)sa->sa_len, (int)sa->sa_family, &i, 37586138Sgreen 6 * (int)(sa->sa_len - ((char *)&sa->sa_data - (char *)sa)), ""); 37686138Sgreen if (tmp != NULL) { 37786138Sgreen p = tmp + i; 37886138Sgreen for (q = (u_char *)&sa->sa_data; q < (u_char *)sa + sa->sa_len; q++) 37986138Sgreen p += sprintf(p, " %#02x,", *q); 38086138Sgreen } 38185292Sdes } 38285292Sdes } 38385292Sdes break; 38431567Ssef } 38531567Ssef return tmp; 38631567Ssef} 38731567Ssef 388101373Smdodd#define timespecsubt(tvp, uvp, vvp) \ 389101373Smdodd do { \ 390101373Smdodd (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ 391101373Smdodd (vvp)->tv_nsec = (tvp)->tv_nsec - (uvp)->tv_nsec; \ 392101373Smdodd if ((vvp)->tv_nsec < 0) { \ 393101373Smdodd (vvp)->tv_sec--; \ 394101373Smdodd (vvp)->tv_nsec += 1000000000; \ 395101373Smdodd } \ 396101373Smdodd } while (0) 397101373Smdodd 39831567Ssef/* 39931567Ssef * print_syscall 40031567Ssef * Print (to outfile) the system call and its arguments. Note that 40131567Ssef * nargs is the number of arguments (not the number of words; this is 40231567Ssef * potentially confusing, I know). 40331567Ssef */ 40431567Ssef 40531567Ssefvoid 406101282Smdoddprint_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args) { 40731567Ssef int i; 40858224Ssef int len = 0; 409101373Smdodd struct timespec timediff; 410101283Smdodd 411101283Smdodd if (trussinfo->flags & FOLLOWFORKS) 412101381Smdodd len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); 413101283Smdodd 414101285Smdodd if (!strcmp(name, "execve") || !strcmp(name, "exit")) { 415101373Smdodd clock_gettime(CLOCK_REALTIME, &trussinfo->after); 416101285Smdodd } 417101285Smdodd 418101285Smdodd if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 419101373Smdodd timespecsubt(&trussinfo->after, &trussinfo->start_time, &timediff); 420101423Smdodd len += fprintf(trussinfo->outfile, "%ld.%09ld ", 421101423Smdodd (long)timediff.tv_sec, (long)timediff.tv_nsec); 422101285Smdodd } 423101285Smdodd 424101285Smdodd if (trussinfo->flags & RELATIVETIMESTAMPS) { 425101373Smdodd timespecsubt(&trussinfo->after, &trussinfo->before, &timediff); 426101423Smdodd len += fprintf(trussinfo->outfile, "%ld.%09ld ", 427101423Smdodd (long)timediff.tv_sec, (long)timediff.tv_nsec); 428101285Smdodd } 429101285Smdodd 430101282Smdodd len += fprintf(trussinfo->outfile, "%s(", name); 431101283Smdodd 43231567Ssef for (i = 0; i < nargs; i++) { 43331567Ssef if (s_args[i]) 434101282Smdodd len += fprintf(trussinfo->outfile, "%s", s_args[i]); 43531567Ssef else 436101282Smdodd len += fprintf(trussinfo->outfile, "<missing argument>"); 437101282Smdodd len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : ""); 43831567Ssef } 439101282Smdodd len += fprintf(trussinfo->outfile, ")"); 44058224Ssef for (i = 0; i < 6 - (len / 8); i++) 441101282Smdodd fprintf(trussinfo->outfile, "\t"); 44231567Ssef} 44358224Ssef 44458224Ssefvoid 445101282Smdoddprint_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args, int errorp, int retval) { 446101282Smdodd print_syscall(trussinfo, name, nargs, s_args); 44758224Ssef if (errorp) { 448101282Smdodd fprintf(trussinfo->outfile, " ERR#%d '%s'\n", retval, strerror(retval)); 44958224Ssef } else { 450101282Smdodd fprintf(trussinfo->outfile, " = %d (0x%x)\n", retval, retval); 45158224Ssef } 45258224Ssef} 453