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