options.c revision aee093126654f722523b47848a0c5449e39cf4bb
1#if HAVE_CONFIG_H 2#include "config.h" 3#endif 4 5#include <string.h> 6#include <stdlib.h> 7#include <unistd.h> 8#include <fcntl.h> 9#include <errno.h> 10#include <limits.h> 11 12#if HAVE_GETOPT_H 13#include <getopt.h> 14#endif 15 16#include "ltrace.h" 17#include "options.h" 18#include "defs.h" 19 20#ifndef SYSCONFDIR 21#define SYSCONFDIR "/etc" 22#endif 23 24#define SYSTEM_CONFIG_FILE SYSCONFDIR "/ltrace.conf" 25#define USER_CONFIG_FILE "~/.ltrace.conf" 26 27#define MAX_LIBRARY 30 28char *library[MAX_LIBRARY]; 29int library_num = 0; 30static char *progname; /* Program name (`ltrace') */ 31FILE *output; 32int opt_a = DEFAULT_ACOLUMN; /* alignment column for results */ 33int opt_A = DEFAULT_ARRAYLEN; /* maximum # array elements to print */ 34int opt_c = 0; /* Report a summary on program exit */ 35int opt_d = 0; /* debug */ 36int opt_i = 0; /* instruction pointer */ 37int opt_s = DEFAULT_STRLEN; /* maximum # of bytes printed in strings */ 38int opt_S = 0; /* display syscalls */ 39int opt_L = 1; /* display library calls */ 40int opt_f = 0; /* trace child processes as they are created */ 41char *opt_u = NULL; /* username to run command as */ 42int opt_r = 0; /* print relative timestamp */ 43int opt_t = 0; /* print absolute timestamp */ 44#ifdef USE_DEMANGLE 45int opt_C = 0; /* Demangle low-level symbol names */ 46#endif 47int opt_n = 0; /* indent output according to program flow */ 48int opt_T = 0; /* show the time spent inside each call */ 49int opt_o = 0; /* output to a specific file */ 50 51/* List of pids given to option -p: */ 52struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */ 53 54/* List of function names given to option -e: */ 55struct opt_e_t *opt_e = NULL; 56int opt_e_enable = 1; 57 58/* List of global function names given to -x: */ 59struct opt_x_t *opt_x = NULL; 60 61/* List of filenames give to option -F: */ 62struct opt_F_t *opt_F = NULL; /* alternate configuration file(s) */ 63 64#ifdef PLT_REINITALISATION_BP 65/* Set a break on the routine named here in order to re-initialize breakpoints 66 after all the PLTs have been initialzed */ 67char *PLTs_initialized_by_here = PLT_REINITALISATION_BP; 68#endif 69 70static void usage(void) 71{ 72#if !(HAVE_GETOPT || HAVE_GETOPT_LONG) 73 fprintf(stdout, "Usage: %s [command [arg ...]]\n" 74 "Trace library calls of a given program.\n\n", progname); 75#else 76 fprintf(stdout, "Usage: %s [option ...] [command [arg ...]]\n" 77 "Trace library calls of a given program.\n\n" 78# if HAVE_GETOPT_LONG 79 " -a, --align=COLUMN align return values in a secific column.\n" 80# else 81 " -a COLUMN align return values in a secific column.\n" 82# endif 83 " -A ARRAYLEN maximum number of array elements to print.\n" 84 " -c count time and calls, and report a summary on exit.\n" 85# ifdef USE_DEMANGLE 86# if HAVE_GETOPT_LONG 87 " -C, --demangle decode low-level symbol names into user-level names.\n" 88# else 89 " -C decode low-level symbol names into user-level names.\n" 90# endif 91# endif 92# if HAVE_GETOPT_LONG 93 " -d, --debug print debugging info.\n" 94# else 95 " -d print debugging info.\n" 96# endif 97 " -e expr modify which events to trace.\n" 98 " -f follow forks.\n" 99# if HAVE_GETOPT_LONG 100 " -F, --config=FILE load alternate configuration file\n" 101# else 102 " -F FILE load alternate configuration file\n" 103# endif 104 " (can be repeated).\n" 105# if HAVE_GETOPT_LONG 106 " -h, --help display this help and exit.\n" 107# else 108 " -h display this help and exit.\n" 109# endif 110 " -i print instruction pointer at time of library call.\n" 111# if HAVE_GETOPT_LONG 112 " -l, --library=FILE print library calls from this library only.\n" 113# else 114 " -l FILE print library calls from this library only.\n" 115# endif 116 " -L do NOT display library calls.\n" 117# if HAVE_GETOPT_LONG 118 " -n, --indent=NR indent output by NR spaces for each call level nesting.\n" 119# else 120 " -n NR indent output by NR spaces for each call level nesting.\n" 121# endif 122# if HAVE_GETOPT_LONG 123 " -o, --output=FILE write the trace output to that file.\n" 124# else 125 " -o FILE write the trace output to that file.\n" 126# endif 127 " -p PID attach to the process with the process ID pid.\n" 128 " -r print relative timestamps.\n" 129 " -s STRLEN specify the maximum string size to print.\n" 130 " -S display system calls.\n" 131 " -t, -tt, -ttt print absolute timestamps.\n" 132 " -T show the time spent inside each call.\n" 133 " -u USERNAME run command with the userid, groupid of username.\n" 134# if HAVE_GETOPT_LONG 135 " -V, --version output version information and exit.\n" 136# else 137 " -V output version information and exit.\n" 138# endif 139 " -x NAME treat the global NAME like a library subroutine.\n" 140#ifdef PLT_REINITALISATION_BP 141 " -X NAME same as -x; and PLT's will be initialized by here.\n" 142#endif 143 "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n", 144 progname); 145#endif 146} 147 148static char *search_for_command(char *filename) 149{ 150 static char pathname[PATH_MAX]; 151 char *path; 152 int m, n; 153 154 if (strchr(filename, '/')) { 155 return filename; 156 } 157 for (path = getenv("PATH"); path && *path; path += m) { 158 if (strchr(path, ':')) { 159 n = strchr(path, ':') - path; 160 m = n + 1; 161 } else { 162 m = n = strlen(path); 163 } 164 if (n + strlen(filename) + 1 >= PATH_MAX) { 165 fprintf(stderr, "Error: filename too long\n"); 166 exit(1); 167 } 168 strncpy(pathname, path, n); 169 if (n && pathname[n - 1] != '/') { 170 pathname[n++] = '/'; 171 } 172 strcpy(pathname + n, filename); 173 if (!access(pathname, X_OK)) { 174 return pathname; 175 } 176 } 177 return filename; 178} 179 180char **process_options(int argc, char **argv) 181{ 182 progname = argv[0]; 183 output = stderr; 184 185#if HAVE_GETOPT || HAVE_GETOPT_LONG 186 while (1) { 187 int c; 188#if HAVE_GETOPT_LONG 189 int option_index = 0; 190 static struct option long_options[] = { 191 {"align", 1, 0, 'a'}, 192 {"config", 1, 0, 'F'}, 193 {"debug", 0, 0, 'd'}, 194# ifdef USE_DEMANGLE 195 {"demangle", 0, 0, 'C'}, 196#endif 197 {"indent", 1, 0, 'n'}, 198 {"help", 0, 0, 'h'}, 199 {"library", 1, 0, 'l'}, 200 {"output", 1, 0, 'o'}, 201 {"version", 0, 0, 'V'}, 202 {0, 0, 0, 0} 203 }; 204 c = getopt_long(argc, argv, "+cdfhiLrStTV" 205# ifdef USE_DEMANGLE 206 "C" 207# endif 208 "a:A:e:F:l:n:o:p:s:u:x:X:", long_options, 209 &option_index); 210#else 211 c = getopt(argc, argv, "+cdfhiLrStTV" 212# ifdef USE_DEMANGLE 213 "C" 214# endif 215 "a:A:e:F:l:n:o:p:s:u:x:X:"); 216#endif 217 if (c == -1) { 218 break; 219 } 220 switch (c) { 221 case 'a': 222 opt_a = atoi(optarg); 223 break; 224 case 'A': 225 opt_A = atoi(optarg); 226 break; 227 case 'c': 228 opt_c++; 229 break; 230#ifdef USE_DEMANGLE 231 case 'C': 232 opt_C++; 233 break; 234#endif 235 case 'd': 236 opt_d++; 237 break; 238 case 'e': 239 { 240 char *str_e = strdup(optarg); 241 if (!str_e) { 242 perror("ltrace: strdup"); 243 exit(1); 244 } 245 if (str_e[0] == '!') { 246 opt_e_enable = 0; 247 str_e++; 248 } 249 while (*str_e) { 250 struct opt_e_t *tmp; 251 char *str2 = strchr(str_e, ','); 252 if (str2) { 253 *str2 = '\0'; 254 } 255 tmp = malloc(sizeof(struct opt_e_t)); 256 if (!tmp) { 257 perror("ltrace: malloc"); 258 exit(1); 259 } 260 tmp->name = str_e; 261 tmp->next = opt_e; 262 opt_e = tmp; 263 if (str2) { 264 str_e = str2 + 1; 265 } else { 266 break; 267 } 268 } 269 break; 270 } 271 case 'f': 272 opt_f = 1; 273 break; 274 case 'F': 275 { 276 struct opt_F_t *tmp = malloc(sizeof(struct opt_F_t)); 277 if (!tmp) { 278 perror("ltrace: malloc"); 279 exit(1); 280 } 281 tmp->filename = strdup(optarg); 282 tmp->next = opt_F; 283 opt_F = tmp; 284 break; 285 } 286 case 'h': 287 usage(); 288 exit(0); 289 case 'i': 290 opt_i++; 291 break; 292 case 'l': 293 if (library_num == MAX_LIBRARY) { 294 fprintf(stderr, 295 "Too many libraries. Maximum is %i.\n", 296 MAX_LIBRARY); 297 exit(1); 298 } 299 library[library_num++] = optarg; 300 break; 301 case 'L': 302 opt_L = 0; 303 break; 304 case 'n': 305 opt_n = atoi(optarg); 306 break; 307 case 'o': 308 opt_o++; 309 output = fopen(optarg, "w"); 310 if (!output) { 311 fprintf(stderr, 312 "Can't open %s for output: %s\n", 313 optarg, strerror(errno)); 314 exit(1); 315 } 316 setvbuf(output, (char *)NULL, _IOLBF, 0); 317 fcntl(fileno(output), F_SETFD, FD_CLOEXEC); 318 break; 319 case 'p': 320 { 321 struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t)); 322 if (!tmp) { 323 perror("ltrace: malloc"); 324 exit(1); 325 } 326 tmp->pid = atoi(optarg); 327 tmp->next = opt_p; 328 opt_p = tmp; 329 break; 330 } 331 case 'r': 332 opt_r++; 333 break; 334 case 's': 335 opt_s = atoi(optarg); 336 break; 337 case 'S': 338 opt_S = 1; 339 break; 340 case 't': 341 opt_t++; 342 break; 343 case 'T': 344 opt_T++; 345 break; 346 case 'u': 347 opt_u = optarg; 348 break; 349 case 'V': 350 printf("ltrace version " PACKAGE_VERSION ".\n" 351 "Copyright (C) 1997-2007 Juan Cespedes <cespedes@debian.org>.\n" 352 "This is free software; see the GNU General Public Licence\n" 353 "version 2 or later for copying conditions. There is NO warranty.\n"); 354 exit(0); 355 case 'X': 356#ifdef PLT_REINITALISATION_BP 357 PLTs_initialized_by_here = optarg; 358#else 359 fprintf(stderr, "WARNING: \"-X\" not used for this " 360 "architecture: assuming you meant \"-x\"\n"); 361#endif 362 /* Fall Thru */ 363 364 case 'x': 365 { 366 struct opt_x_t *p = opt_x; 367 368 /* First, check for duplicate. */ 369 while (p && strcmp(p->name, optarg)) { 370 p = p->next; 371 } 372 if (p) { 373 break; 374 } 375 376 /* If not duplicate, add to list. */ 377 p = malloc(sizeof(struct opt_x_t)); 378 if (!p) { 379 perror("ltrace: malloc"); 380 exit(1); 381 } 382 p->name = optarg; 383 p->found = 0; 384 p->next = opt_x; 385 opt_x = p; 386 break; 387 } 388 389 default: 390#if HAVE_GETOPT_LONG 391 fprintf(stderr, 392 "Try `%s --help' for more information\n", 393 progname); 394#else 395 fprintf(stderr, "Try `%s -h' for more information\n", 396 progname); 397#endif 398 exit(1); 399 } 400 } 401 argc -= optind; 402 argv += optind; 403#endif 404 405 if (!opt_F) { 406 opt_F = malloc(sizeof(struct opt_F_t)); 407 opt_F->next = malloc(sizeof(struct opt_F_t)); 408 opt_F->next->next = NULL; 409 opt_F->filename = USER_CONFIG_FILE; 410 opt_F->next->filename = SYSTEM_CONFIG_FILE; 411 } 412 /* Reverse the config file list since it was built by 413 * prepending, and it would make more sense to process the 414 * files in the order they were given. Probably it would make 415 * more sense to keep a tail pointer instead? */ 416 { 417 struct opt_F_t *egg = NULL; 418 struct opt_F_t *chicken; 419 while (opt_F) { 420 chicken = opt_F->next; 421 opt_F->next = egg; 422 egg = opt_F; 423 opt_F = chicken; 424 } 425 opt_F = egg; 426 } 427 428 if (!opt_p && argc < 1) { 429 fprintf(stderr, "%s: too few arguments\n", progname); 430 usage(); 431 exit(1); 432 } 433 if (opt_r && opt_t) { 434 fprintf(stderr, "%s: Incompatible options -r and -t\n", 435 progname); 436 exit(1); 437 } 438 if (argc > 0) { 439 command = search_for_command(argv[0]); 440 } 441 return &argv[0]; 442} 443