syscalls.c revision 101282
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 101282 2002-08-04 00:46:48Z 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>
5431567Ssef#include <unistd.h>
5585292Sdes
56101282Smdodd#include "truss.h"
5787703Smarkm#include "extern.h"
5831567Ssef#include "syscall.h"
5931567Ssef
6031567Ssef/*
6131567Ssef * This should probably be in its own file.
6231567Ssef */
6331567Ssef
6431567Ssefstruct syscall syscalls[] = {
6531567Ssef	{ "readlink", 1, 3,
6631567Ssef	  { { String, 0 } , { String | OUT, 1 }, { Int, 2 }}},
6731567Ssef	{ "lseek", 2, 3,
6831567Ssef	  { { Int, 0 }, {Quad, 2 }, { Int, 4 }}},
6931567Ssef	{ "mmap", 2, 6,
7031567Ssef	  { { Hex, 0 }, {Int, 1}, {Hex, 2}, {Hex, 3}, {Int, 4}, {Quad, 6}}},
7131567Ssef	{ "open", 1, 3,
7288726Salfred	  { { String | IN, 0} , { Hex, 1}, {Octal, 2}}},
7331567Ssef	{ "linux_open", 1, 3,
7488726Salfred	  { { String, 0 }, { Hex, 1}, { Octal, 2 }}},
7531567Ssef	{ "close", 1, 1, { { Int, 0 } } },
7631567Ssef	{ "fstat", 1, 2,
7731567Ssef	  { { Int, 0},  {Ptr | OUT , 1 }}},
7831567Ssef	{ "stat", 1, 2,
7931567Ssef	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
8040370Ssef	{ "lstat", 1, 2,
8140370Ssef	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
8231567Ssef	{ "linux_newstat", 1, 2,
8331567Ssef	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
8431567Ssef	{ "linux_newfstat", 1, 2,
8531567Ssef	  { { Int, 0 }, { Ptr | OUT, 1 }}},
8631567Ssef	{ "write", 1, 3,
8731567Ssef	  { { Int, 0}, { Ptr | IN, 1 }, { Int, 2 }}},
8831571Ssef	{ "ioctl", 1, 3,
8931571Ssef	  { { Int, 0}, { Ioctl, 1 }, { Hex, 2 }}},
9031567Ssef	{ "break", 1, 1, { { Hex, 0 }}},
9131567Ssef	{ "exit", 0, 1, { { Hex, 0 }}},
9249436Sdes	{ "access", 1, 2, { { String | IN, 0 }, { Int, 1 }}},
9349609Sdes	{ "sigaction", 1, 3,
9449609Sdes	  { { Signal, 0 }, { Ptr | IN, 1 }, { Ptr | OUT, 2 }}},
9585292Sdes	{ "accept", 1, 3,
9685292Sdes	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
9785292Sdes	{ "bind", 1, 3,
9885292Sdes	  { { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
9985292Sdes	{ "connect", 1, 3,
10085292Sdes	  { { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
10185292Sdes	{ "getpeername", 1, 3,
10285292Sdes	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
10385292Sdes	{ "getsockname", 1, 3,
10485292Sdes	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
10532275Scharnier	{ 0, 0, 0, { { 0, 0 }}},
10631567Ssef};
10731567Ssef
10831567Ssef/*
10931567Ssef * If/when the list gets big, it might be desirable to do it
11031567Ssef * as a hash table or binary search.
11131567Ssef */
11231567Ssef
11331567Ssefstruct syscall *
11431567Ssefget_syscall(const char *name) {
11531567Ssef	struct syscall *sc = syscalls;
11631567Ssef
11731567Ssef	while (sc->name) {
11831567Ssef		if (!strcmp(name, sc->name))
11931567Ssef			return sc;
12031567Ssef		sc++;
12131567Ssef	}
12231567Ssef	return NULL;
12331567Ssef}
12431567Ssef
12531567Ssef/*
12685292Sdes * get_struct
12785292Sdes *
12885292Sdes * Copy a fixed amount of bytes from the process.
12985292Sdes */
13085292Sdes
13187703Smarkmstatic int
13285292Sdesget_struct(int procfd, void *offset, void *buf, int len) {
13385292Sdes	char *pos;
13485292Sdes	FILE *p;
13585292Sdes	int c, fd;
13685292Sdes
13785292Sdes	if ((fd = dup(procfd)) == -1)
13885292Sdes		err(1, "dup");
13985292Sdes	if ((p = fdopen(fd, "r")) == NULL)
14085292Sdes		err(1, "fdopen");
14195225Sdwmalone	fseeko(p, (uintptr_t)offset, SEEK_SET);
14285292Sdes	for (pos = (char *)buf; len--; pos++) {
14385292Sdes		if ((c = fgetc(p)) == EOF)
14485292Sdes			return -1;
14585292Sdes		*pos = c;
14685292Sdes	}
14785292Sdes	fclose(p);
14885292Sdes	return 0;
14985292Sdes}
15085292Sdes
15185292Sdes/*
15231567Ssef * get_string
15331567Ssef * Copy a string from the process.  Note that it is
15431567Ssef * expected to be a C string, but if max is set, it will
15531567Ssef * only get that much.
15631567Ssef */
15731567Ssef
15831567Ssefchar *
15931567Ssefget_string(int procfd, void *offset, int max) {
16032275Scharnier	char *buf;
16157245Ssef	int size, len, c, fd;
16231567Ssef	FILE *p;
16331567Ssef
16457245Ssef	if ((fd = dup(procfd)) == -1)
16557245Ssef		err(1, "dup");
16657245Ssef	if ((p = fdopen(fd, "r")) == NULL)
16732275Scharnier		err(1, "fdopen");
16831567Ssef	buf = malloc( size = (max ? max : 64 ) );
16931567Ssef	len = 0;
17082471Sjoerg	buf[0] = 0;
17195225Sdwmalone	fseeko(p, (uintptr_t)offset, SEEK_SET);
17231567Ssef	while ((c = fgetc(p)) != EOF) {
17331567Ssef		buf[len++] = c;
17431567Ssef		if (c == 0 || len == max) {
17531567Ssef			buf[len] = 0;
17631567Ssef			break;
17731567Ssef		}
17831567Ssef		if (len == size) {
17957245Ssef			char *tmp;
18031567Ssef			tmp = realloc(buf, size+64);
18131567Ssef			if (tmp == NULL) {
18231567Ssef				buf[len] = 0;
18357245Ssef				fclose(p);
18431567Ssef				return buf;
18531567Ssef			}
18631567Ssef			size += 64;
18757245Ssef			buf = tmp;
18831567Ssef		}
18931567Ssef	}
19057245Ssef	fclose(p);
19131567Ssef	return buf;
19231567Ssef}
19331567Ssef
19431567Ssef
19531567Ssef/*
19631567Ssef * Gag.  This is really unportable.  Multiplication is more portable.
19731567Ssef * But slower, from the code I saw.
19831567Ssef */
19931567Ssef
20031567Ssefstatic long long
20131567Ssefmake_quad(unsigned long p1, unsigned long p2) {
20231567Ssef  union {
20331567Ssef    long long ll;
20431567Ssef    unsigned long l[2];
20531567Ssef  } t;
20631567Ssef  t.l[0] = p1;
20731567Ssef  t.l[1] = p2;
20831567Ssef  return t.ll;
20931567Ssef}
21031567Ssef
21131567Ssef
21231567Ssef/*
21331567Ssef * print_arg
21431567Ssef * Converts a syscall argument into a string.  Said string is
21531567Ssef * allocated via malloc(), so needs to be free()'d.  The file
21631567Ssef * descriptor is for the process' memory (via /proc), and is used
21731567Ssef * to get any data (where the argument is a pointer).  sc is
21831567Ssef * a pointer to the syscall description (see above); args is
21931567Ssef * an array of all of the system call arguments.
22031567Ssef */
22131567Ssef
22231567Ssefchar *
22331567Ssefprint_arg(int fd, struct syscall_args *sc, unsigned long *args) {
22432275Scharnier  char *tmp = NULL;
22531567Ssef  switch (sc->type & ARG_MASK) {
22631567Ssef  case Hex:
22731567Ssef    tmp = malloc(12);
22837453Sbde    sprintf(tmp, "0x%lx", args[sc->offset]);
22931567Ssef    break;
23031567Ssef  case Octal:
23131567Ssef    tmp = malloc(13);
23237453Sbde    sprintf(tmp, "0%lo", args[sc->offset]);
23331567Ssef    break;
23431567Ssef  case Int:
23531567Ssef    tmp = malloc(12);
23637453Sbde    sprintf(tmp, "%ld", args[sc->offset]);
23731567Ssef    break;
23831567Ssef  case String:
23931567Ssef    {
24031567Ssef      char *tmp2;
24131567Ssef      tmp2 = get_string(fd, (void*)args[sc->offset], 0);
24231567Ssef      tmp = malloc(strlen(tmp2) + 3);
24331567Ssef      sprintf(tmp, "\"%s\"", tmp2);
24431567Ssef      free(tmp2);
24531567Ssef    }
24631567Ssef  break;
24731567Ssef  case Quad:
24831567Ssef    {
24931567Ssef      unsigned long long t;
25031567Ssef      unsigned long l1, l2;
25131567Ssef      l1 = args[sc->offset];
25231567Ssef      l2 = args[sc->offset+1];
25331567Ssef      t = make_quad(l1, l2);
25431567Ssef      tmp = malloc(24);
25531567Ssef      sprintf(tmp, "0x%qx", t);
25631567Ssef      break;
25731567Ssef    }
25831567Ssef  case Ptr:
25931567Ssef    tmp = malloc(12);
26037453Sbde    sprintf(tmp, "0x%lx", args[sc->offset]);
26131567Ssef    break;
26231571Ssef  case Ioctl:
26331571Ssef    {
26487703Smarkm      const char *temp = ioctlname(args[sc->offset]);
26531571Ssef      if (temp)
26631571Ssef	tmp = strdup(temp);
26731571Ssef      else {
26831571Ssef	tmp = malloc(12);
26937453Sbde	sprintf(tmp, "0x%lx", args[sc->offset]);
27031571Ssef      }
27131571Ssef    }
27249609Sdes    break;
27349609Sdes  case Signal:
27449609Sdes    {
27549609Sdes      long sig;
27649609Sdes
27749609Sdes      sig = args[sc->offset];
27849609Sdes      tmp = malloc(12);
27949609Sdes      if (sig > 0 && sig < NSIG) {
28049609Sdes	int i;
28149609Sdes	sprintf(tmp, "sig%s", sys_signame[sig]);
28249609Sdes	for (i = 0; tmp[i] != '\0'; ++i)
28349609Sdes	  tmp[i] = toupper(tmp[i]);
28449609Sdes      } else {
28549609Sdes        sprintf(tmp, "%ld", sig);
28649609Sdes      }
28749609Sdes    }
28849609Sdes    break;
28985292Sdes  case Sockaddr:
29085292Sdes    {
29186138Sgreen      struct sockaddr_storage ss;
29286138Sgreen      char addr[64];
29387703Smarkm      struct sockaddr_in *lsin;
29487703Smarkm      struct sockaddr_in6 *lsin6;
29586138Sgreen      struct sockaddr_un *sun;
29685292Sdes      struct sockaddr *sa;
29786138Sgreen      char *p;
29886138Sgreen      u_char *q;
29986138Sgreen      int i;
30085292Sdes
30186138Sgreen      /* yuck: get ss_len */
30286138Sgreen      if (get_struct(fd, (void *)args[sc->offset], (void *)&ss,
30386138Sgreen	sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1)
30486138Sgreen	err(1, "get_struct %p", (void *)args[sc->offset]);
30586138Sgreen      /* sockaddr_un never have the length filled in! */
30686138Sgreen      if (ss.ss_family == AF_UNIX) {
30786138Sgreen	if (get_struct(fd, (void *)args[sc->offset], (void *)&ss,
30886138Sgreen	  sizeof(*sun))
30986138Sgreen	  == -1)
31086138Sgreen	  err(2, "get_struct %p", (void *)args[sc->offset]);
31186138Sgreen      } else {
31286138Sgreen	if (get_struct(fd, (void *)args[sc->offset], (void *)&ss, ss.ss_len)
31386138Sgreen	  == -1)
31486138Sgreen	  err(2, "get_struct %p", (void *)args[sc->offset]);
31586138Sgreen      }
31685292Sdes
31786138Sgreen      switch (ss.ss_family) {
31886138Sgreen      case AF_INET:
31987703Smarkm	lsin = (struct sockaddr_in *)&ss;
32087703Smarkm	inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr);
32187703Smarkm	asprintf(&tmp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port));
32286138Sgreen	break;
32386138Sgreen      case AF_INET6:
32487703Smarkm	lsin6 = (struct sockaddr_in6 *)&ss;
32587703Smarkm	inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, sizeof addr);
32687703Smarkm	asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port));
32786138Sgreen	break;
32886138Sgreen      case AF_UNIX:
32986138Sgreen        sun = (struct sockaddr_un *)&ss;
33086138Sgreen        asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path);
33186138Sgreen	break;
33286138Sgreen      default:
33386138Sgreen	sa = (struct sockaddr *)&ss;
33486138Sgreen        asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data = {%n%*s } }",
33586138Sgreen	  (int)sa->sa_len, (int)sa->sa_family, &i,
33686138Sgreen	  6 * (int)(sa->sa_len - ((char *)&sa->sa_data - (char *)sa)), "");
33786138Sgreen	if (tmp != NULL) {
33886138Sgreen	  p = tmp + i;
33986138Sgreen          for (q = (u_char *)&sa->sa_data; q < (u_char *)sa + sa->sa_len; q++)
34086138Sgreen            p += sprintf(p, " %#02x,", *q);
34186138Sgreen	}
34285292Sdes      }
34385292Sdes    }
34485292Sdes    break;
34531567Ssef  }
34631567Ssef  return tmp;
34731567Ssef}
34831567Ssef
34931567Ssef/*
35031567Ssef * print_syscall
35131567Ssef * Print (to outfile) the system call and its arguments.  Note that
35231567Ssef * nargs is the number of arguments (not the number of words; this is
35331567Ssef * potentially confusing, I know).
35431567Ssef */
35531567Ssef
35631567Ssefvoid
357101282Smdoddprint_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args) {
35831567Ssef  int i;
35958224Ssef  int len = 0;
360101282Smdodd  len += fprintf(trussinfo->outfile, "%s(", name);
36131567Ssef  for (i = 0; i < nargs; i++) {
36231567Ssef    if (s_args[i])
363101282Smdodd      len += fprintf(trussinfo->outfile, "%s", s_args[i]);
36431567Ssef    else
365101282Smdodd      len += fprintf(trussinfo->outfile, "<missing argument>");
366101282Smdodd    len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : "");
36731567Ssef  }
368101282Smdodd  len += fprintf(trussinfo->outfile, ")");
36958224Ssef  for (i = 0; i < 6 - (len / 8); i++)
370101282Smdodd	fprintf(trussinfo->outfile, "\t");
37131567Ssef}
37258224Ssef
37358224Ssefvoid
374101282Smdoddprint_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args, int errorp, int retval) {
375101282Smdodd  print_syscall(trussinfo, name, nargs, s_args);
37658224Ssef  if (errorp) {
377101282Smdodd    fprintf(trussinfo->outfile, " ERR#%d '%s'\n", retval, strerror(retval));
37858224Ssef  } else {
379101282Smdodd    fprintf(trussinfo->outfile, " = %d (0x%x)\n", retval, retval);
38058224Ssef  }
38158224Ssef}
382