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