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