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