syscalls.c revision 121606
1/*
2 * Copryight 1997 Sean Eric Fagan
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. All advertising materials mentioning features or use of this software
13 *    must display the following acknowledgement:
14 *	This product includes software developed by Sean Eric Fagan
15 * 4. Neither the name of the author may be used to endorse or promote
16 *    products derived from this software without specific prior written
17 *    permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#ifndef lint
33static const char rcsid[] =
34  "$FreeBSD: head/usr.bin/truss/syscalls.c 121606 2003-10-27 06:50:57Z marcel $";
35#endif /* not lint */
36
37/*
38 * This file has routines used to print out system calls and their
39 * arguments.
40 */
41
42#include <sys/types.h>
43#include <sys/socket.h>
44#include <sys/time.h>
45#include <sys/un.h>
46#include <netinet/in.h>
47#include <arpa/inet.h>
48
49#include <ctype.h>
50#include <err.h>
51#include <signal.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <time.h>
56#include <unistd.h>
57
58#include "truss.h"
59#include "extern.h"
60#include "syscall.h"
61
62/*
63 * This should probably be in its own file.
64 */
65
66struct syscall syscalls[] = {
67	{ "readlink", 1, 3,
68	  { { String, 0 } , { String | OUT, 1 }, { Int, 2 }}},
69	{ "lseek", 2, 3,
70	  { { Int, 0 }, {Quad, 2 }, { Int, 4 }}},
71	{ "mmap", 2, 6,
72	  { { Hex, 0 }, {Int, 1}, {Hex, 2}, {Hex, 3}, {Int, 4}, {Quad, 6}}},
73	{ "open", 1, 3,
74	  { { String | IN, 0} , { Hex, 1}, {Octal, 2}}},
75	{ "linux_open", 1, 3,
76	  { { String, 0 }, { Hex, 1}, { Octal, 2 }}},
77	{ "close", 1, 1,
78	  { { Int, 0 } } },
79	{ "link", 0, 2,
80	  { { String, 0 }, { String, 1 }}},
81	{ "unlink", 0, 1,
82	  { { String, 0 }}},
83	{ "chdir", 0, 1,
84	  { { String, 0 }}},
85	{ "mknod", 0, 3,
86	  { { String, 0 }, { Octal, 1 }, { Int, 3 }}},
87	{ "chmod", 0, 2,
88	  { { String, 0 }, { Octal, 1 }}},
89	{ "chown", 0, 3,
90	  { { String, 0 }, { Int, 1 }, { Int, 2 }}},
91	{ "mount", 0, 4,
92	  { { String, 0 }, { String, 1 }, { Int, 2 }, { Ptr, 3 }}},
93	{ "umount", 0, 2,
94	  { { String, 0 }, { Int, 2 }}},
95	{ "fstat", 1, 2,
96	  { { Int, 0},  {Ptr | OUT , 1 }}},
97	{ "stat", 1, 2,
98	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
99	{ "lstat", 1, 2,
100	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
101	{ "linux_newstat", 1, 2,
102	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
103	{ "linux_newfstat", 1, 2,
104	  { { Int, 0 }, { Ptr | OUT, 1 }}},
105	{ "write", 1, 3,
106	  { { Int, 0 }, { Ptr | IN, 1 }, { Int, 2 }}},
107	{ "ioctl", 1, 3,
108	  { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 }}},
109	{ "break", 1, 1, { { Hex, 0 }}},
110	{ "exit", 0, 1, { { Hex, 0 }}},
111	{ "access", 1, 2, { { String | IN, 0 }, { Int, 1 }}},
112	{ "sigaction", 1, 3,
113	  { { Signal, 0 }, { Ptr | IN, 1 }, { Ptr | OUT, 2 }}},
114	{ "accept", 1, 3,
115	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
116	{ "bind", 1, 3,
117	  { { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
118	{ "connect", 1, 3,
119	  { { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
120	{ "getpeername", 1, 3,
121	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
122	{ "getsockname", 1, 3,
123	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
124	{ "recvfrom", 1, 6,
125	  { { Hex, 0 }, { Ptr | IN, 1 }, { Int, 3 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
126	{ "sendto", 1, 6,
127	  { { Hex, 0 }, { Ptr | IN, 1 }, { Int, 3 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
128	{ "execve", 1, 3,
129	  { { String | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
130	{ "linux_execve", 1, 3,
131	  { { String | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
132	{ "kldload", 0, 1, { { String | IN, 0 }}},
133	{ "kldunload", 0, 1, { { Int, 0 }}},
134	{ "kldfind", 0, 1, { { String | IN, 0 }}},
135	{ "kldnext", 0, 1, { { Int, 0 }}},
136	{ "kldstat", 0, 2, { { Int, 0 }, { Ptr, 1 }}},
137	{ "kldfirstmod", 0, 1, { { Int, 0 }}},
138	{ 0, 0, 0, { { 0, 0 }}},
139};
140
141/*
142 * If/when the list gets big, it might be desirable to do it
143 * as a hash table or binary search.
144 */
145
146struct syscall *
147get_syscall(const char *name) {
148	struct syscall *sc = syscalls;
149
150	while (sc->name) {
151		if (!strcmp(name, sc->name))
152			return sc;
153		sc++;
154	}
155	return NULL;
156}
157
158/*
159 * get_struct
160 *
161 * Copy a fixed amount of bytes from the process.
162 */
163
164static int
165get_struct(int procfd, void *offset, void *buf, int len) {
166	char *pos;
167	FILE *p;
168	int c, fd;
169
170	if ((fd = dup(procfd)) == -1)
171		err(1, "dup");
172	if ((p = fdopen(fd, "r")) == NULL)
173		err(1, "fdopen");
174	fseeko(p, (uintptr_t)offset, SEEK_SET);
175	for (pos = (char *)buf; len--; pos++) {
176		if ((c = fgetc(p)) == EOF)
177			return -1;
178		*pos = c;
179	}
180	fclose(p);
181	return 0;
182}
183
184/*
185 * get_string
186 * Copy a string from the process.  Note that it is
187 * expected to be a C string, but if max is set, it will
188 * only get that much.
189 */
190
191char *
192get_string(int procfd, void *offset, int max) {
193	char *buf;
194	int size, len, c, fd;
195	FILE *p;
196
197	if ((fd = dup(procfd)) == -1)
198		err(1, "dup");
199	if ((p = fdopen(fd, "r")) == NULL)
200		err(1, "fdopen");
201	buf = malloc( size = (max ? max : 64 ) );
202	len = 0;
203	buf[0] = 0;
204	fseeko(p, (uintptr_t)offset, SEEK_SET);
205	while ((c = fgetc(p)) != EOF) {
206		buf[len++] = c;
207		if (c == 0 || len == max) {
208			buf[len] = 0;
209			break;
210		}
211		if (len == size) {
212			char *tmp;
213			tmp = realloc(buf, size+64);
214			if (tmp == NULL) {
215				buf[len] = 0;
216				fclose(p);
217				return buf;
218			}
219			size += 64;
220			buf = tmp;
221		}
222	}
223	fclose(p);
224	return buf;
225}
226
227
228/*
229 * Gag.  This is really unportable.  Multiplication is more portable.
230 * But slower, from the code I saw.
231 */
232
233static long long
234make_quad(unsigned long p1, unsigned long p2) {
235  union {
236    long long ll;
237    unsigned long l[2];
238  } t;
239  t.l[0] = p1;
240  t.l[1] = p2;
241  return t.ll;
242}
243
244
245/*
246 * print_arg
247 * Converts a syscall argument into a string.  Said string is
248 * allocated via malloc(), so needs to be free()'d.  The file
249 * descriptor is for the process' memory (via /proc), and is used
250 * to get any data (where the argument is a pointer).  sc is
251 * a pointer to the syscall description (see above); args is
252 * an array of all of the system call arguments.
253 */
254
255char *
256print_arg(int fd, struct syscall_args *sc, unsigned long *args) {
257  char *tmp = NULL;
258  switch (sc->type & ARG_MASK) {
259  case Hex:
260    tmp = malloc(12);
261    sprintf(tmp, "0x%lx", args[sc->offset]);
262    break;
263  case Octal:
264    tmp = malloc(13);
265    sprintf(tmp, "0%lo", args[sc->offset]);
266    break;
267  case Int:
268    tmp = malloc(12);
269    sprintf(tmp, "%ld", args[sc->offset]);
270    break;
271  case String:
272    {
273      char *tmp2;
274      tmp2 = get_string(fd, (void*)args[sc->offset], 0);
275      tmp = malloc(strlen(tmp2) + 3);
276      sprintf(tmp, "\"%s\"", tmp2);
277      free(tmp2);
278    }
279  break;
280  case StringArray:
281    {
282      int num, size, i;
283      char *tmp2;
284      char *string;
285      char *strarray[100];	/* XXX This is ugly. */
286
287      if (get_struct(fd, (void *)args[sc->offset], (void *)&strarray,
288                     sizeof(strarray)) == -1) {
289	err(1, "get_struct %p", (void *)args[sc->offset]);
290      }
291      num = 0;
292      size = 0;
293
294      /* Find out how large of a buffer we'll need. */
295      while (strarray[num] != NULL) {
296	string = get_string(fd, (void*)strarray[num], 0);
297        size += strlen(string);
298	free(string);
299	num++;
300      }
301      size += 4 + (num * 4);
302      tmp = (char *)malloc(size);
303      tmp2 = tmp;
304
305      tmp2 += sprintf(tmp2, " [");
306      for (i = 0; i < num; i++) {
307	string = get_string(fd, (void*)strarray[i], 0);
308        tmp2 += sprintf(tmp2, " \"%s\"%c", string, (i+1 == num) ? ' ' : ',');
309	free(string);
310      }
311      tmp2 += sprintf(tmp2, "]");
312    }
313  break;
314  case Quad:
315    {
316      unsigned long long t;
317      unsigned long l1, l2;
318      l1 = args[sc->offset];
319      l2 = args[sc->offset+1];
320      t = make_quad(l1, l2);
321      tmp = malloc(24);
322      sprintf(tmp, "0x%qx", t);
323      break;
324    }
325  case Ptr:
326    tmp = malloc(12);
327    sprintf(tmp, "0x%lx", args[sc->offset]);
328    break;
329  case Ioctl:
330    {
331      const char *temp = ioctlname(args[sc->offset]);
332      if (temp)
333	tmp = strdup(temp);
334      else {
335	tmp = malloc(12);
336	sprintf(tmp, "0x%lx", args[sc->offset]);
337      }
338    }
339    break;
340  case Signal:
341    {
342      long sig;
343
344      sig = args[sc->offset];
345      tmp = malloc(12);
346      if (sig > 0 && sig < NSIG) {
347	int i;
348	sprintf(tmp, "sig%s", sys_signame[sig]);
349	for (i = 0; tmp[i] != '\0'; ++i)
350	  tmp[i] = toupper(tmp[i]);
351      } else {
352        sprintf(tmp, "%ld", sig);
353      }
354    }
355    break;
356  case Sockaddr:
357    {
358      struct sockaddr_storage ss;
359      char addr[64];
360      struct sockaddr_in *lsin;
361      struct sockaddr_in6 *lsin6;
362      struct sockaddr_un *sun;
363      struct sockaddr *sa;
364      char *p;
365      u_char *q;
366      int i;
367
368      if (args[sc->offset] == 0) {
369	      asprintf(&tmp, "NULL");
370	      break;
371      }
372
373      /* yuck: get ss_len */
374      if (get_struct(fd, (void *)args[sc->offset], (void *)&ss,
375	sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1)
376	err(1, "get_struct %p", (void *)args[sc->offset]);
377      /* sockaddr_un never have the length filled in! */
378      if (ss.ss_family == AF_UNIX) {
379	if (get_struct(fd, (void *)args[sc->offset], (void *)&ss,
380	  sizeof(*sun))
381	  == -1)
382	  err(2, "get_struct %p", (void *)args[sc->offset]);
383      } else {
384	if (get_struct(fd, (void *)args[sc->offset], (void *)&ss, ss.ss_len)
385	  == -1)
386	  err(2, "get_struct %p", (void *)args[sc->offset]);
387      }
388
389      switch (ss.ss_family) {
390      case AF_INET:
391	lsin = (struct sockaddr_in *)&ss;
392	inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr);
393	asprintf(&tmp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port));
394	break;
395      case AF_INET6:
396	lsin6 = (struct sockaddr_in6 *)&ss;
397	inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, sizeof addr);
398	asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port));
399	break;
400      case AF_UNIX:
401        sun = (struct sockaddr_un *)&ss;
402        asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path);
403	break;
404      default:
405	sa = (struct sockaddr *)&ss;
406        asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data = {%n%*s } }",
407	  (int)sa->sa_len, (int)sa->sa_family, &i,
408	  6 * (int)(sa->sa_len - ((char *)&sa->sa_data - (char *)sa)), "");
409	if (tmp != NULL) {
410	  p = tmp + i;
411          for (q = (u_char *)&sa->sa_data; q < (u_char *)sa + sa->sa_len; q++)
412            p += sprintf(p, " %#02x,", *q);
413	}
414      }
415    }
416    break;
417  }
418  return tmp;
419}
420
421#define timespecsubt(tvp, uvp, vvp)					\
422	do {								\
423		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
424		(vvp)->tv_nsec = (tvp)->tv_nsec - (uvp)->tv_nsec;	\
425		if ((vvp)->tv_nsec < 0) {				\
426			(vvp)->tv_sec--;				\
427			(vvp)->tv_nsec += 1000000000;			\
428		}							\
429	} while (0)
430
431/*
432 * print_syscall
433 * Print (to outfile) the system call and its arguments.  Note that
434 * nargs is the number of arguments (not the number of words; this is
435 * potentially confusing, I know).
436 */
437
438void
439print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args) {
440  int i;
441  int len = 0;
442  struct timespec timediff;
443
444  if (trussinfo->flags & FOLLOWFORKS)
445    len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid);
446
447  if (name != NULL && (!strcmp(name, "execve") || !strcmp(name, "exit"))) {
448    clock_gettime(CLOCK_REALTIME, &trussinfo->after);
449  }
450
451  if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
452    timespecsubt(&trussinfo->after, &trussinfo->start_time, &timediff);
453    len += fprintf(trussinfo->outfile, "%ld.%09ld ",
454		   (long)timediff.tv_sec, timediff.tv_nsec);
455  }
456
457  if (trussinfo->flags & RELATIVETIMESTAMPS) {
458    timespecsubt(&trussinfo->after, &trussinfo->before, &timediff);
459    len += fprintf(trussinfo->outfile, "%ld.%09ld ",
460		   (long)timediff.tv_sec, timediff.tv_nsec);
461  }
462
463  len += fprintf(trussinfo->outfile, "%s(", name);
464
465  for (i = 0; i < nargs; i++) {
466    if (s_args[i])
467      len += fprintf(trussinfo->outfile, "%s", s_args[i]);
468    else
469      len += fprintf(trussinfo->outfile, "<missing argument>");
470    len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : "");
471  }
472  len += fprintf(trussinfo->outfile, ")");
473  for (i = 0; i < 6 - (len / 8); i++)
474	fprintf(trussinfo->outfile, "\t");
475}
476
477void
478print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args, int errorp, int retval) {
479  print_syscall(trussinfo, name, nargs, s_args);
480  if (errorp) {
481    fprintf(trussinfo->outfile, " ERR#%d '%s'\n", retval, strerror(retval));
482  } else {
483    fprintf(trussinfo->outfile, " = %d (0x%x)\n", retval, retval);
484  }
485}
486