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