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