syscalls.c revision 122348
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 122348 2003-11-09 03:48:13Z marcel $";
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>
44104581Smike#include <sys/time.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>
55101423Smdodd#include <time.h>
5631567Ssef#include <unistd.h>
5785292Sdes
58101282Smdodd#include "truss.h"
5987703Smarkm#include "extern.h"
6031567Ssef#include "syscall.h"
6131567Ssef
6231567Ssef/*
6331567Ssef * This should probably be in its own file.
6431567Ssef */
6531567Ssef
6631567Ssefstruct syscall syscalls[] = {
6731567Ssef	{ "readlink", 1, 3,
6831567Ssef	  { { String, 0 } , { String | OUT, 1 }, { Int, 2 }}},
6931567Ssef	{ "lseek", 2, 3,
7031567Ssef	  { { Int, 0 }, {Quad, 2 }, { Int, 4 }}},
7131567Ssef	{ "mmap", 2, 6,
7231567Ssef	  { { Hex, 0 }, {Int, 1}, {Hex, 2}, {Hex, 3}, {Int, 4}, {Quad, 6}}},
7331567Ssef	{ "open", 1, 3,
7488726Salfred	  { { String | IN, 0} , { Hex, 1}, {Octal, 2}}},
7531567Ssef	{ "linux_open", 1, 3,
7688726Salfred	  { { String, 0 }, { Hex, 1}, { Octal, 2 }}},
77113501Smdodd	{ "close", 1, 1,
78113501Smdodd	  { { Int, 0 } } },
79113501Smdodd	{ "link", 0, 2,
80113501Smdodd	  { { String, 0 }, { String, 1 }}},
81113501Smdodd	{ "unlink", 0, 1,
82113501Smdodd	  { { String, 0 }}},
83113501Smdodd	{ "chdir", 0, 1,
84113501Smdodd	  { { String, 0 }}},
85113501Smdodd	{ "mknod", 0, 3,
86113501Smdodd	  { { String, 0 }, { Octal, 1 }, { Int, 3 }}},
87113501Smdodd	{ "chmod", 0, 2,
88113501Smdodd	  { { String, 0 }, { Octal, 1 }}},
89113501Smdodd	{ "chown", 0, 3,
90113501Smdodd	  { { String, 0 }, { Int, 1 }, { Int, 2 }}},
91113501Smdodd	{ "mount", 0, 4,
92113501Smdodd	  { { String, 0 }, { String, 1 }, { Int, 2 }, { Ptr, 3 }}},
93113501Smdodd	{ "umount", 0, 2,
94113501Smdodd	  { { String, 0 }, { Int, 2 }}},
9531567Ssef	{ "fstat", 1, 2,
9631567Ssef	  { { Int, 0},  {Ptr | OUT , 1 }}},
9731567Ssef	{ "stat", 1, 2,
9831567Ssef	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
9940370Ssef	{ "lstat", 1, 2,
10040370Ssef	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
10131567Ssef	{ "linux_newstat", 1, 2,
10231567Ssef	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
10331567Ssef	{ "linux_newfstat", 1, 2,
10431567Ssef	  { { Int, 0 }, { Ptr | OUT, 1 }}},
10531567Ssef	{ "write", 1, 3,
106118483Sdes	  { { Int, 0 }, { Ptr | IN, 1 }, { Int, 2 }}},
10731571Ssef	{ "ioctl", 1, 3,
108118483Sdes	  { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 }}},
10931567Ssef	{ "break", 1, 1, { { Hex, 0 }}},
11031567Ssef	{ "exit", 0, 1, { { Hex, 0 }}},
11149436Sdes	{ "access", 1, 2, { { String | IN, 0 }, { Int, 1 }}},
11249609Sdes	{ "sigaction", 1, 3,
11349609Sdes	  { { Signal, 0 }, { Ptr | IN, 1 }, { Ptr | OUT, 2 }}},
11485292Sdes	{ "accept", 1, 3,
11585292Sdes	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
11685292Sdes	{ "bind", 1, 3,
11785292Sdes	  { { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
11885292Sdes	{ "connect", 1, 3,
11985292Sdes	  { { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
12085292Sdes	{ "getpeername", 1, 3,
12185292Sdes	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
12285292Sdes	{ "getsockname", 1, 3,
12385292Sdes	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
124118483Sdes	{ "recvfrom", 1, 6,
125118483Sdes	  { { Hex, 0 }, { Ptr | IN, 1 }, { Int, 3 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
126118483Sdes	{ "sendto", 1, 6,
127118483Sdes	  { { Hex, 0 }, { Ptr | IN, 1 }, { Int, 3 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
128101289Smdodd	{ "execve", 1, 3,
129101289Smdodd	  { { String | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
130101289Smdodd	{ "linux_execve", 1, 3,
131101289Smdodd	  { { String | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
132113501Smdodd	{ "kldload", 0, 1, { { String | IN, 0 }}},
133113501Smdodd	{ "kldunload", 0, 1, { { Int, 0 }}},
134113501Smdodd	{ "kldfind", 0, 1, { { String | IN, 0 }}},
135113501Smdodd	{ "kldnext", 0, 1, { { Int, 0 }}},
136113501Smdodd	{ "kldstat", 0, 2, { { Int, 0 }, { Ptr, 1 }}},
137113501Smdodd	{ "kldfirstmod", 0, 1, { { Int, 0 }}},
13832275Scharnier	{ 0, 0, 0, { { 0, 0 }}},
13931567Ssef};
14031567Ssef
14131567Ssef/*
14231567Ssef * If/when the list gets big, it might be desirable to do it
14331567Ssef * as a hash table or binary search.
14431567Ssef */
14531567Ssef
14631567Ssefstruct syscall *
14731567Ssefget_syscall(const char *name) {
14831567Ssef	struct syscall *sc = syscalls;
14931567Ssef
15031567Ssef	while (sc->name) {
15131567Ssef		if (!strcmp(name, sc->name))
15231567Ssef			return sc;
15331567Ssef		sc++;
15431567Ssef	}
15531567Ssef	return NULL;
15631567Ssef}
15731567Ssef
15831567Ssef/*
15985292Sdes * get_struct
16085292Sdes *
16185292Sdes * Copy a fixed amount of bytes from the process.
16285292Sdes */
16385292Sdes
16487703Smarkmstatic int
16585292Sdesget_struct(int procfd, void *offset, void *buf, int len) {
16685292Sdes	char *pos;
16785292Sdes	FILE *p;
16885292Sdes	int c, fd;
16985292Sdes
17085292Sdes	if ((fd = dup(procfd)) == -1)
17185292Sdes		err(1, "dup");
17285292Sdes	if ((p = fdopen(fd, "r")) == NULL)
17385292Sdes		err(1, "fdopen");
17495225Sdwmalone	fseeko(p, (uintptr_t)offset, SEEK_SET);
17585292Sdes	for (pos = (char *)buf; len--; pos++) {
17685292Sdes		if ((c = fgetc(p)) == EOF)
17785292Sdes			return -1;
17885292Sdes		*pos = c;
17985292Sdes	}
18085292Sdes	fclose(p);
18185292Sdes	return 0;
18285292Sdes}
18385292Sdes
18485292Sdes/*
18531567Ssef * get_string
18631567Ssef * Copy a string from the process.  Note that it is
18731567Ssef * expected to be a C string, but if max is set, it will
18831567Ssef * only get that much.
18931567Ssef */
19031567Ssef
19131567Ssefchar *
19231567Ssefget_string(int procfd, void *offset, int max) {
19332275Scharnier	char *buf;
19457245Ssef	int size, len, c, fd;
19531567Ssef	FILE *p;
19631567Ssef
19757245Ssef	if ((fd = dup(procfd)) == -1)
19857245Ssef		err(1, "dup");
19957245Ssef	if ((p = fdopen(fd, "r")) == NULL)
20032275Scharnier		err(1, "fdopen");
20131567Ssef	buf = malloc( size = (max ? max : 64 ) );
20231567Ssef	len = 0;
20382471Sjoerg	buf[0] = 0;
20495225Sdwmalone	fseeko(p, (uintptr_t)offset, SEEK_SET);
20531567Ssef	while ((c = fgetc(p)) != EOF) {
20631567Ssef		buf[len++] = c;
20731567Ssef		if (c == 0 || len == max) {
20831567Ssef			buf[len] = 0;
20931567Ssef			break;
21031567Ssef		}
21131567Ssef		if (len == size) {
21257245Ssef			char *tmp;
21331567Ssef			tmp = realloc(buf, size+64);
21431567Ssef			if (tmp == NULL) {
21531567Ssef				buf[len] = 0;
21657245Ssef				fclose(p);
21731567Ssef				return buf;
21831567Ssef			}
21931567Ssef			size += 64;
22057245Ssef			buf = tmp;
22131567Ssef		}
22231567Ssef	}
22357245Ssef	fclose(p);
22431567Ssef	return buf;
22531567Ssef}
22631567Ssef
22731567Ssef
22831567Ssef/*
22931567Ssef * Gag.  This is really unportable.  Multiplication is more portable.
23031567Ssef * But slower, from the code I saw.
23131567Ssef */
23231567Ssef
23331567Ssefstatic long long
23431567Ssefmake_quad(unsigned long p1, unsigned long p2) {
23531567Ssef  union {
23631567Ssef    long long ll;
23731567Ssef    unsigned long l[2];
23831567Ssef  } t;
23931567Ssef  t.l[0] = p1;
24031567Ssef  t.l[1] = p2;
24131567Ssef  return t.ll;
24231567Ssef}
24331567Ssef
24431567Ssef
24531567Ssef/*
24631567Ssef * print_arg
24731567Ssef * Converts a syscall argument into a string.  Said string is
24831567Ssef * allocated via malloc(), so needs to be free()'d.  The file
24931567Ssef * descriptor is for the process' memory (via /proc), and is used
25031567Ssef * to get any data (where the argument is a pointer).  sc is
25131567Ssef * a pointer to the syscall description (see above); args is
25231567Ssef * an array of all of the system call arguments.
25331567Ssef */
25431567Ssef
25531567Ssefchar *
25631567Ssefprint_arg(int fd, struct syscall_args *sc, unsigned long *args) {
25732275Scharnier  char *tmp = NULL;
25831567Ssef  switch (sc->type & ARG_MASK) {
25931567Ssef  case Hex:
260122348Smarcel    asprintf(&tmp, "0x%lx", args[sc->offset]);
26131567Ssef    break;
26231567Ssef  case Octal:
263122348Smarcel    asprintf(&tmp, "0%lo", args[sc->offset]);
26431567Ssef    break;
26531567Ssef  case Int:
266122348Smarcel    asprintf(&tmp, "%ld", args[sc->offset]);
26731567Ssef    break;
26831567Ssef  case String:
26931567Ssef    {
27031567Ssef      char *tmp2;
27131567Ssef      tmp2 = get_string(fd, (void*)args[sc->offset], 0);
272122348Smarcel      asprintf(&tmp, "\"%s\"", tmp2);
27331567Ssef      free(tmp2);
27431567Ssef    }
27531567Ssef  break;
276101289Smdodd  case StringArray:
277101289Smdodd    {
278101289Smdodd      int num, size, i;
279101289Smdodd      char *tmp2;
280101289Smdodd      char *string;
281101289Smdodd      char *strarray[100];	/* XXX This is ugly. */
282101289Smdodd
283101289Smdodd      if (get_struct(fd, (void *)args[sc->offset], (void *)&strarray,
284101289Smdodd                     sizeof(strarray)) == -1) {
285101289Smdodd	err(1, "get_struct %p", (void *)args[sc->offset]);
286101289Smdodd      }
287101289Smdodd      num = 0;
288101289Smdodd      size = 0;
289101289Smdodd
290101289Smdodd      /* Find out how large of a buffer we'll need. */
291101289Smdodd      while (strarray[num] != NULL) {
292101289Smdodd	string = get_string(fd, (void*)strarray[num], 0);
293101289Smdodd        size += strlen(string);
294101289Smdodd	free(string);
295101289Smdodd	num++;
296101289Smdodd      }
297101289Smdodd      size += 4 + (num * 4);
298101289Smdodd      tmp = (char *)malloc(size);
299101289Smdodd      tmp2 = tmp;
300101289Smdodd
301101289Smdodd      tmp2 += sprintf(tmp2, " [");
302101289Smdodd      for (i = 0; i < num; i++) {
303101289Smdodd	string = get_string(fd, (void*)strarray[i], 0);
304101289Smdodd        tmp2 += sprintf(tmp2, " \"%s\"%c", string, (i+1 == num) ? ' ' : ',');
305101289Smdodd	free(string);
306101289Smdodd      }
307101289Smdodd      tmp2 += sprintf(tmp2, "]");
308101289Smdodd    }
309101289Smdodd  break;
31031567Ssef  case Quad:
31131567Ssef    {
31231567Ssef      unsigned long long t;
31331567Ssef      unsigned long l1, l2;
31431567Ssef      l1 = args[sc->offset];
31531567Ssef      l2 = args[sc->offset+1];
31631567Ssef      t = make_quad(l1, l2);
317122348Smarcel      asprintf(&tmp, "0x%qx", t);
31831567Ssef      break;
31931567Ssef    }
32031567Ssef  case Ptr:
321122348Smarcel    asprintf(&tmp, "0x%lx", args[sc->offset]);
32231567Ssef    break;
32331571Ssef  case Ioctl:
32431571Ssef    {
32587703Smarkm      const char *temp = ioctlname(args[sc->offset]);
32631571Ssef      if (temp)
32731571Ssef	tmp = strdup(temp);
328122348Smarcel      else
329122348Smarcel	asprintf(&tmp, "0x%lx", args[sc->offset]);
33031571Ssef    }
33149609Sdes    break;
33249609Sdes  case Signal:
33349609Sdes    {
33449609Sdes      long sig;
33549609Sdes
33649609Sdes      sig = args[sc->offset];
33749609Sdes      if (sig > 0 && sig < NSIG) {
33849609Sdes	int i;
339122348Smarcel	asprintf(&tmp, "sig%s", sys_signame[sig]);
34049609Sdes	for (i = 0; tmp[i] != '\0'; ++i)
34149609Sdes	  tmp[i] = toupper(tmp[i]);
342122348Smarcel      } else
343122348Smarcel        asprintf(&tmp, "%ld", sig);
34449609Sdes    }
34549609Sdes    break;
34685292Sdes  case Sockaddr:
34785292Sdes    {
34886138Sgreen      struct sockaddr_storage ss;
34986138Sgreen      char addr[64];
35087703Smarkm      struct sockaddr_in *lsin;
35187703Smarkm      struct sockaddr_in6 *lsin6;
35286138Sgreen      struct sockaddr_un *sun;
35385292Sdes      struct sockaddr *sa;
35486138Sgreen      char *p;
35586138Sgreen      u_char *q;
35686138Sgreen      int i;
35785292Sdes
358121606Smarcel      if (args[sc->offset] == 0) {
359121606Smarcel	      asprintf(&tmp, "NULL");
360121606Smarcel	      break;
361121606Smarcel      }
362121606Smarcel
36386138Sgreen      /* yuck: get ss_len */
36486138Sgreen      if (get_struct(fd, (void *)args[sc->offset], (void *)&ss,
36586138Sgreen	sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1)
36686138Sgreen	err(1, "get_struct %p", (void *)args[sc->offset]);
36786138Sgreen      /* sockaddr_un never have the length filled in! */
36886138Sgreen      if (ss.ss_family == AF_UNIX) {
36986138Sgreen	if (get_struct(fd, (void *)args[sc->offset], (void *)&ss,
37086138Sgreen	  sizeof(*sun))
37186138Sgreen	  == -1)
37286138Sgreen	  err(2, "get_struct %p", (void *)args[sc->offset]);
37386138Sgreen      } else {
37486138Sgreen	if (get_struct(fd, (void *)args[sc->offset], (void *)&ss, ss.ss_len)
37586138Sgreen	  == -1)
37686138Sgreen	  err(2, "get_struct %p", (void *)args[sc->offset]);
37786138Sgreen      }
37885292Sdes
37986138Sgreen      switch (ss.ss_family) {
38086138Sgreen      case AF_INET:
38187703Smarkm	lsin = (struct sockaddr_in *)&ss;
38287703Smarkm	inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr);
38387703Smarkm	asprintf(&tmp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port));
38486138Sgreen	break;
38586138Sgreen      case AF_INET6:
38687703Smarkm	lsin6 = (struct sockaddr_in6 *)&ss;
38787703Smarkm	inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, sizeof addr);
38887703Smarkm	asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port));
38986138Sgreen	break;
39086138Sgreen      case AF_UNIX:
39186138Sgreen        sun = (struct sockaddr_un *)&ss;
39286138Sgreen        asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path);
39386138Sgreen	break;
39486138Sgreen      default:
39586138Sgreen	sa = (struct sockaddr *)&ss;
39686138Sgreen        asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data = {%n%*s } }",
39786138Sgreen	  (int)sa->sa_len, (int)sa->sa_family, &i,
39886138Sgreen	  6 * (int)(sa->sa_len - ((char *)&sa->sa_data - (char *)sa)), "");
39986138Sgreen	if (tmp != NULL) {
40086138Sgreen	  p = tmp + i;
40186138Sgreen          for (q = (u_char *)&sa->sa_data; q < (u_char *)sa + sa->sa_len; q++)
40286138Sgreen            p += sprintf(p, " %#02x,", *q);
40386138Sgreen	}
40485292Sdes      }
40585292Sdes    }
40685292Sdes    break;
40731567Ssef  }
40831567Ssef  return tmp;
40931567Ssef}
41031567Ssef
411101373Smdodd#define timespecsubt(tvp, uvp, vvp)					\
412101373Smdodd	do {								\
413101373Smdodd		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
414101373Smdodd		(vvp)->tv_nsec = (tvp)->tv_nsec - (uvp)->tv_nsec;	\
415101373Smdodd		if ((vvp)->tv_nsec < 0) {				\
416101373Smdodd			(vvp)->tv_sec--;				\
417101373Smdodd			(vvp)->tv_nsec += 1000000000;			\
418101373Smdodd		}							\
419101373Smdodd	} while (0)
420101373Smdodd
42131567Ssef/*
42231567Ssef * print_syscall
42331567Ssef * Print (to outfile) the system call and its arguments.  Note that
42431567Ssef * nargs is the number of arguments (not the number of words; this is
42531567Ssef * potentially confusing, I know).
42631567Ssef */
42731567Ssef
42831567Ssefvoid
429101282Smdoddprint_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args) {
43031567Ssef  int i;
43158224Ssef  int len = 0;
432101373Smdodd  struct timespec timediff;
433101283Smdodd
434101283Smdodd  if (trussinfo->flags & FOLLOWFORKS)
435101381Smdodd    len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid);
436101283Smdodd
437106712Sdwmalone  if (name != NULL && (!strcmp(name, "execve") || !strcmp(name, "exit"))) {
438101373Smdodd    clock_gettime(CLOCK_REALTIME, &trussinfo->after);
439101285Smdodd  }
440101285Smdodd
441101285Smdodd  if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
442101373Smdodd    timespecsubt(&trussinfo->after, &trussinfo->start_time, &timediff);
443101423Smdodd    len += fprintf(trussinfo->outfile, "%ld.%09ld ",
444101458Smdodd		   (long)timediff.tv_sec, timediff.tv_nsec);
445101285Smdodd  }
446101285Smdodd
447101285Smdodd  if (trussinfo->flags & RELATIVETIMESTAMPS) {
448101373Smdodd    timespecsubt(&trussinfo->after, &trussinfo->before, &timediff);
449101423Smdodd    len += fprintf(trussinfo->outfile, "%ld.%09ld ",
450101458Smdodd		   (long)timediff.tv_sec, timediff.tv_nsec);
451101285Smdodd  }
452101285Smdodd
453101282Smdodd  len += fprintf(trussinfo->outfile, "%s(", name);
454101283Smdodd
45531567Ssef  for (i = 0; i < nargs; i++) {
45631567Ssef    if (s_args[i])
457101282Smdodd      len += fprintf(trussinfo->outfile, "%s", s_args[i]);
45831567Ssef    else
459101282Smdodd      len += fprintf(trussinfo->outfile, "<missing argument>");
460101282Smdodd    len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : "");
46131567Ssef  }
462101282Smdodd  len += fprintf(trussinfo->outfile, ")");
46358224Ssef  for (i = 0; i < 6 - (len / 8); i++)
464101282Smdodd	fprintf(trussinfo->outfile, "\t");
46531567Ssef}
46658224Ssef
46758224Ssefvoid
468122348Smarcelprint_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs,
469122348Smarcel    char **s_args, int errorp, long retval)
470122348Smarcel{
471101282Smdodd  print_syscall(trussinfo, name, nargs, s_args);
47258224Ssef  if (errorp) {
473101282Smdodd    fprintf(trussinfo->outfile, " ERR#%d '%s'\n", retval, strerror(retval));
47458224Ssef  } else {
475122348Smarcel    fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval);
47658224Ssef  }
47758224Ssef}
478