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