syscalls.c revision 101283
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 101283 2002-08-04 01:02:52Z mdodd $";
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/un.h>
45#include <netinet/in.h>
46#include <arpa/inet.h>
47
48#include <ctype.h>
49#include <err.h>
50#include <signal.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <unistd.h>
55
56#include "truss.h"
57#include "extern.h"
58#include "syscall.h"
59
60/*
61 * This should probably be in its own file.
62 */
63
64struct syscall syscalls[] = {
65	{ "readlink", 1, 3,
66	  { { String, 0 } , { String | OUT, 1 }, { Int, 2 }}},
67	{ "lseek", 2, 3,
68	  { { Int, 0 }, {Quad, 2 }, { Int, 4 }}},
69	{ "mmap", 2, 6,
70	  { { Hex, 0 }, {Int, 1}, {Hex, 2}, {Hex, 3}, {Int, 4}, {Quad, 6}}},
71	{ "open", 1, 3,
72	  { { String | IN, 0} , { Hex, 1}, {Octal, 2}}},
73	{ "linux_open", 1, 3,
74	  { { String, 0 }, { Hex, 1}, { Octal, 2 }}},
75	{ "close", 1, 1, { { Int, 0 } } },
76	{ "fstat", 1, 2,
77	  { { Int, 0},  {Ptr | OUT , 1 }}},
78	{ "stat", 1, 2,
79	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
80	{ "lstat", 1, 2,
81	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
82	{ "linux_newstat", 1, 2,
83	  { { String | IN, 0 }, { Ptr | OUT, 1 }}},
84	{ "linux_newfstat", 1, 2,
85	  { { Int, 0 }, { Ptr | OUT, 1 }}},
86	{ "write", 1, 3,
87	  { { Int, 0}, { Ptr | IN, 1 }, { Int, 2 }}},
88	{ "ioctl", 1, 3,
89	  { { Int, 0}, { Ioctl, 1 }, { Hex, 2 }}},
90	{ "break", 1, 1, { { Hex, 0 }}},
91	{ "exit", 0, 1, { { Hex, 0 }}},
92	{ "access", 1, 2, { { String | IN, 0 }, { Int, 1 }}},
93	{ "sigaction", 1, 3,
94	  { { Signal, 0 }, { Ptr | IN, 1 }, { Ptr | OUT, 2 }}},
95	{ "accept", 1, 3,
96	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
97	{ "bind", 1, 3,
98	  { { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
99	{ "connect", 1, 3,
100	  { { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
101	{ "getpeername", 1, 3,
102	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
103	{ "getsockname", 1, 3,
104	  { { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
105	{ 0, 0, 0, { { 0, 0 }}},
106};
107
108/*
109 * If/when the list gets big, it might be desirable to do it
110 * as a hash table or binary search.
111 */
112
113struct syscall *
114get_syscall(const char *name) {
115	struct syscall *sc = syscalls;
116
117	while (sc->name) {
118		if (!strcmp(name, sc->name))
119			return sc;
120		sc++;
121	}
122	return NULL;
123}
124
125/*
126 * get_struct
127 *
128 * Copy a fixed amount of bytes from the process.
129 */
130
131static int
132get_struct(int procfd, void *offset, void *buf, int len) {
133	char *pos;
134	FILE *p;
135	int c, fd;
136
137	if ((fd = dup(procfd)) == -1)
138		err(1, "dup");
139	if ((p = fdopen(fd, "r")) == NULL)
140		err(1, "fdopen");
141	fseeko(p, (uintptr_t)offset, SEEK_SET);
142	for (pos = (char *)buf; len--; pos++) {
143		if ((c = fgetc(p)) == EOF)
144			return -1;
145		*pos = c;
146	}
147	fclose(p);
148	return 0;
149}
150
151/*
152 * get_string
153 * Copy a string from the process.  Note that it is
154 * expected to be a C string, but if max is set, it will
155 * only get that much.
156 */
157
158char *
159get_string(int procfd, void *offset, int max) {
160	char *buf;
161	int size, len, c, fd;
162	FILE *p;
163
164	if ((fd = dup(procfd)) == -1)
165		err(1, "dup");
166	if ((p = fdopen(fd, "r")) == NULL)
167		err(1, "fdopen");
168	buf = malloc( size = (max ? max : 64 ) );
169	len = 0;
170	buf[0] = 0;
171	fseeko(p, (uintptr_t)offset, SEEK_SET);
172	while ((c = fgetc(p)) != EOF) {
173		buf[len++] = c;
174		if (c == 0 || len == max) {
175			buf[len] = 0;
176			break;
177		}
178		if (len == size) {
179			char *tmp;
180			tmp = realloc(buf, size+64);
181			if (tmp == NULL) {
182				buf[len] = 0;
183				fclose(p);
184				return buf;
185			}
186			size += 64;
187			buf = tmp;
188		}
189	}
190	fclose(p);
191	return buf;
192}
193
194
195/*
196 * Gag.  This is really unportable.  Multiplication is more portable.
197 * But slower, from the code I saw.
198 */
199
200static long long
201make_quad(unsigned long p1, unsigned long p2) {
202  union {
203    long long ll;
204    unsigned long l[2];
205  } t;
206  t.l[0] = p1;
207  t.l[1] = p2;
208  return t.ll;
209}
210
211
212/*
213 * print_arg
214 * Converts a syscall argument into a string.  Said string is
215 * allocated via malloc(), so needs to be free()'d.  The file
216 * descriptor is for the process' memory (via /proc), and is used
217 * to get any data (where the argument is a pointer).  sc is
218 * a pointer to the syscall description (see above); args is
219 * an array of all of the system call arguments.
220 */
221
222char *
223print_arg(int fd, struct syscall_args *sc, unsigned long *args) {
224  char *tmp = NULL;
225  switch (sc->type & ARG_MASK) {
226  case Hex:
227    tmp = malloc(12);
228    sprintf(tmp, "0x%lx", args[sc->offset]);
229    break;
230  case Octal:
231    tmp = malloc(13);
232    sprintf(tmp, "0%lo", args[sc->offset]);
233    break;
234  case Int:
235    tmp = malloc(12);
236    sprintf(tmp, "%ld", args[sc->offset]);
237    break;
238  case String:
239    {
240      char *tmp2;
241      tmp2 = get_string(fd, (void*)args[sc->offset], 0);
242      tmp = malloc(strlen(tmp2) + 3);
243      sprintf(tmp, "\"%s\"", tmp2);
244      free(tmp2);
245    }
246  break;
247  case Quad:
248    {
249      unsigned long long t;
250      unsigned long l1, l2;
251      l1 = args[sc->offset];
252      l2 = args[sc->offset+1];
253      t = make_quad(l1, l2);
254      tmp = malloc(24);
255      sprintf(tmp, "0x%qx", t);
256      break;
257    }
258  case Ptr:
259    tmp = malloc(12);
260    sprintf(tmp, "0x%lx", args[sc->offset]);
261    break;
262  case Ioctl:
263    {
264      const char *temp = ioctlname(args[sc->offset]);
265      if (temp)
266	tmp = strdup(temp);
267      else {
268	tmp = malloc(12);
269	sprintf(tmp, "0x%lx", args[sc->offset]);
270      }
271    }
272    break;
273  case Signal:
274    {
275      long sig;
276
277      sig = args[sc->offset];
278      tmp = malloc(12);
279      if (sig > 0 && sig < NSIG) {
280	int i;
281	sprintf(tmp, "sig%s", sys_signame[sig]);
282	for (i = 0; tmp[i] != '\0'; ++i)
283	  tmp[i] = toupper(tmp[i]);
284      } else {
285        sprintf(tmp, "%ld", sig);
286      }
287    }
288    break;
289  case Sockaddr:
290    {
291      struct sockaddr_storage ss;
292      char addr[64];
293      struct sockaddr_in *lsin;
294      struct sockaddr_in6 *lsin6;
295      struct sockaddr_un *sun;
296      struct sockaddr *sa;
297      char *p;
298      u_char *q;
299      int i;
300
301      /* yuck: get ss_len */
302      if (get_struct(fd, (void *)args[sc->offset], (void *)&ss,
303	sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1)
304	err(1, "get_struct %p", (void *)args[sc->offset]);
305      /* sockaddr_un never have the length filled in! */
306      if (ss.ss_family == AF_UNIX) {
307	if (get_struct(fd, (void *)args[sc->offset], (void *)&ss,
308	  sizeof(*sun))
309	  == -1)
310	  err(2, "get_struct %p", (void *)args[sc->offset]);
311      } else {
312	if (get_struct(fd, (void *)args[sc->offset], (void *)&ss, ss.ss_len)
313	  == -1)
314	  err(2, "get_struct %p", (void *)args[sc->offset]);
315      }
316
317      switch (ss.ss_family) {
318      case AF_INET:
319	lsin = (struct sockaddr_in *)&ss;
320	inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr);
321	asprintf(&tmp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port));
322	break;
323      case AF_INET6:
324	lsin6 = (struct sockaddr_in6 *)&ss;
325	inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, sizeof addr);
326	asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port));
327	break;
328      case AF_UNIX:
329        sun = (struct sockaddr_un *)&ss;
330        asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path);
331	break;
332      default:
333	sa = (struct sockaddr *)&ss;
334        asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data = {%n%*s } }",
335	  (int)sa->sa_len, (int)sa->sa_family, &i,
336	  6 * (int)(sa->sa_len - ((char *)&sa->sa_data - (char *)sa)), "");
337	if (tmp != NULL) {
338	  p = tmp + i;
339          for (q = (u_char *)&sa->sa_data; q < (u_char *)sa + sa->sa_len; q++)
340            p += sprintf(p, " %#02x,", *q);
341	}
342      }
343    }
344    break;
345  }
346  return tmp;
347}
348
349/*
350 * print_syscall
351 * Print (to outfile) the system call and its arguments.  Note that
352 * nargs is the number of arguments (not the number of words; this is
353 * potentially confusing, I know).
354 */
355
356void
357print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args) {
358  int i;
359  int len = 0;
360
361  if (trussinfo->flags & FOLLOWFORKS)
362    len += fprintf(trussinfo->outfile, "%5d:  ", trussinfo->pid);
363
364  len += fprintf(trussinfo->outfile, "%s(", name);
365
366  for (i = 0; i < nargs; i++) {
367    if (s_args[i])
368      len += fprintf(trussinfo->outfile, "%s", s_args[i]);
369    else
370      len += fprintf(trussinfo->outfile, "<missing argument>");
371    len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : "");
372  }
373  len += fprintf(trussinfo->outfile, ")");
374  for (i = 0; i < 6 - (len / 8); i++)
375	fprintf(trussinfo->outfile, "\t");
376}
377
378void
379print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args, int errorp, int retval) {
380  print_syscall(trussinfo, name, nargs, s_args);
381  if (errorp) {
382    fprintf(trussinfo->outfile, " ERR#%d '%s'\n", retval, strerror(retval));
383  } else {
384    fprintf(trussinfo->outfile, " = %d (0x%x)\n", retval, retval);
385  }
386}
387