options.c revision 5b3ffdf2e696273d38434ff7b3c26349fff5a0ea
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
20static char *progname;		/* Program name (`ltrace') */
21FILE * output;
22int opt_a = DEFAULT_ACOLUMN;	/* default alignment column for results */
23int opt_d = 0;			/* debug */
24int opt_i = 0;			/* instruction pointer */
25int opt_s = DEFAULT_STRLEN;	/* default maximum # of bytes printed in strings */
26int opt_S = 0;			/* display syscalls */
27int opt_L = 1;			/* display library calls */
28int opt_f = 0;			/* trace child processes as they are created */
29char * opt_u = NULL;		/* username to run command as */
30int opt_r = 0;			/* print relative timestamp */
31int opt_t = 0;			/* print absolute timestamp */
32#if HAVE_LIBIBERTY
33int opt_C = 0;			/* Demangle low-level symbol names into user-level names */
34#endif
35int opt_n = 0;			/* indent trace output according to program flow */
36
37/* List of pids given to option -p: */
38struct opt_p_t * opt_p = NULL;	/* attach to process with a given pid */
39
40/* List of function names given to option -e: */
41struct opt_e_t * opt_e = NULL;
42int opt_e_enable=1;
43
44static void usage(void)
45{
46#if !(HAVE_GETOPT || HAVE_GETOPT_LONG)
47	fprintf(stdout, "Usage: %s [command [arg ...]]\n"
48"Trace library calls of a given program.\n\n", progname);
49#else
50	fprintf(stdout, "Usage: %s [option ...] [command [arg ...]]\n"
51"Trace library calls of a given program.\n\n"
52
53# if HAVE_GETOPT_LONG
54"  -d, --debug         print debugging info.\n"
55# else
56"  -d                  print debugging info.\n"
57# endif
58"  -f                  follow forks.\n"
59"  -i                  print instruction pointer at time of library call.\n"
60"  -L                  do NOT display library calls.\n"
61"  -S                  display system calls.\n"
62"  -r                  print relative timestamps.\n"
63"  -t, -tt, -ttt       print absolute timestamps.\n"
64# if HAVE_LIBIBERTY
65#  if HAVE_GETOPT_LONG
66"  -C, --demangle      decode low-level symbol names into user-level names.\n"
67#  else
68"  -C                  decode low-level symbol names into user-level names.\n"
69#  endif
70# endif
71# if HAVE_GETOPT_LONG
72"  -a, --align=COLUMN  align return values in a secific column.\n"
73# else
74"  -a COLUMN           align return values in a secific column.\n"
75# endif
76"  -s STRLEN           specify the maximum string size to print.\n"
77# if HAVE_GETOPT_LONG
78"  -o, --output=FILE   write the trace output to that file.\n"
79# else
80"  -o FILE             write the trace output to that file.\n"
81# endif
82"  -u USERNAME         run command with the userid, groupid of username.\n"
83"  -p PID              attach to the process with the process ID pid.\n"
84"  -e expr             modify which events to trace.\n"
85# if HAVE_GETOPT_LONG
86"  -n, --indent=NR     indent output by NR spaces for each call level nesting.\n"
87# else
88"  -n NR               indent output by NR spaces for each call level nesting.\n"
89# endif
90# if HAVE_GETOPT_LONG
91"  -h, --help          display this help and exit.\n"
92# else
93"  -h                  display this help and exit.\n"
94# endif
95# if HAVE_GETOPT_LONG
96"  -V, --version       output version information and exit.\n"
97# else
98"  -V                  output version information and exit.\n"
99# endif
100"\nReport bugs to Juan Cespedes <cespedes@debian.org>\n"
101		, progname);
102#endif
103}
104
105static char * search_for_command(char * filename)
106{
107	static char pathname[1024];
108	char *path;
109	int m, n;
110
111	if (strchr(filename, '/')) {
112		return filename;
113	}
114	for (path = getenv("PATH"); path && *path; path += m) {
115		if (strchr(path, ':')) {
116			n = strchr(path, ':') - path;
117			m = n + 1;
118		} else {
119			m = n = strlen(path);
120		}
121		strncpy(pathname, path, n);     /* Possible buffer overrun */
122		if (n && pathname[n - 1] != '/') {
123			pathname[n++] = '/';
124		}
125		strcpy(pathname + n, filename);
126		if (!access(pathname, X_OK)) {
127			return pathname;
128		}
129	}
130	return filename;
131}
132
133char ** process_options(int argc, char **argv)
134{
135	progname = argv[0];
136	output = stderr;
137
138#if HAVE_GETOPT || HAVE_GETOPT_LONG
139	while(1) {
140		int c;
141#if HAVE_GETOPT_LONG
142		int option_index = 0;
143		static struct option long_options[] = {
144			{ "align", 1, 0, 'a'},
145			{ "debug", 0, 0, 'd'},
146# if HAVE_LIBIBERTY
147			{ "demangle", 0, 0, 'C'},
148#endif
149			{ "indent", 0, 0, 'n'},
150			{ "help", 0, 0, 'h'},
151			{ "output", 1, 0, 'o'},
152			{ "version", 0, 0, 'V'},
153			{ 0, 0, 0, 0}
154		};
155		c = getopt_long(argc, argv, "+dfiLSrthV"
156# if HAVE_LIBIBERTY
157			"C"
158# endif
159			"a:s:o:u:p:e:n:", long_options, &option_index);
160#else
161		c = getopt(argc, argv, "+dfiLSrthV"
162# if HAVE_LIBIBERTY
163			"C"
164# endif
165			"a:s:o:u:p:e:n:");
166#endif
167		if (c==-1) {
168			break;
169		}
170		switch(c) {
171			case 'd':	opt_d++;
172					break;
173			case 'f':	opt_f = 1;
174					break;
175			case 'i':	opt_i++;
176					break;
177			case 'L':	opt_L = 0;
178					break;
179			case 'S':	opt_S = 1;
180					break;
181			case 'r':	opt_r++;
182					break;
183			case 't':	opt_t++;
184					break;
185#if HAVE_LIBIBERTY
186			case 'C':	opt_C++;
187					break;
188#endif
189			case 'a':	opt_a = atoi(optarg);
190					break;
191			case 's':	opt_s = atoi(optarg);
192					break;
193			case 'n':	opt_n = atoi(optarg);
194					break;
195			case 'h':	usage();
196					exit(0);
197			case 'o':	output = fopen(optarg, "w");
198					if (!output) {
199						fprintf(stderr, "Can't open %s for output: %s\n", optarg, strerror(errno));
200						exit(1);
201					}
202					setvbuf(output, (char *)NULL, _IOLBF, 0);
203					fcntl(fileno(output), F_SETFD, FD_CLOEXEC);
204					break;
205			case 'u':	opt_u = optarg;
206					break;
207			case 'p':
208				{
209					struct opt_p_t * tmp = malloc(sizeof(struct opt_p_t));
210					if (!tmp) {
211						perror("ltrace: malloc");
212						exit(1);
213					}
214					tmp->pid = atoi(optarg);
215					tmp->next = opt_p;
216					opt_p = tmp;
217					break;
218				}
219			case 'e':
220				{
221					char * str_e = strdup(optarg);
222					if (!str_e) {
223						perror("ltrace: strdup");
224						exit(1);
225					}
226					if (str_e[0]=='!') {
227						opt_e_enable=0;
228						str_e++;
229					}
230					while(*str_e) {
231						struct opt_e_t * tmp;
232						char *str2 = strchr(str_e,',');
233						if (str2) {
234							*str2 = '\0';
235						}
236						tmp = malloc(sizeof(struct opt_e_t));
237						if (!tmp) {
238							perror("ltrace: malloc");
239							exit(1);
240						}
241						tmp->name = str_e;
242						tmp->next = opt_e;
243						opt_e = tmp;
244						if (str2) {
245							str_e = str2+1;
246						} else {
247							break;
248						}
249					}
250					break;
251				}
252			case 'V':	printf("ltrace version "
253#ifdef VERSION
254					VERSION
255#else
256					"???"
257#endif
258					".\n"
259"Copyright (C) 1997-1999 Juan Cespedes <cespedes@debian.org>.\n"
260"This is free software; see the GNU General Public Licence\n"
261"version 2 or later for copying conditions.  There is NO warranty.\n");
262					exit(0);
263
264			default:
265#if HAVE_GETOPT_LONG
266				fprintf(stderr, "Try `%s --help' for more information\n", progname);
267#else
268				fprintf(stderr, "Try `%s -h' for more information\n", progname);
269#endif
270					exit(1);
271		}
272	}
273	argc -= optind; argv += optind;
274#endif
275
276	if (!opt_p && argc<1) {
277		fprintf(stderr, "%s: too few arguments\n", progname);
278#if HAVE_GETOPT_LONG
279		fprintf(stderr, "Try `%s --help' for more information\n", progname);
280#elif HAVE_GETOPT
281		fprintf(stderr, "Try `%s -h' for more information\n", progname);
282#endif
283		exit(1);
284	}
285	if (opt_r && opt_t) {
286		fprintf(stderr, "%s: Incompatible options -r and -t\n", progname);
287		exit(1);
288	}
289	if (argc>0) {
290		command = search_for_command(argv[0]);
291	}
292	return &argv[0];
293}
294