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