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