blob: 5c3441dcb1019c7bb0b9c5237ec578fc20f7a204 [file] [log] [blame]
Petr Machatae99af272012-10-26 00:29:52 +02001/*
2 * This file is part of ltrace.
Petr Machata94773bf2014-01-14 16:01:35 +01003 * Copyright (C) 2012, 2013, 2014 Petr Machata, Red Hat Inc.
Petr Machatae99af272012-10-26 00:29:52 +02004 * Copyright (C) 2009,2010 Joe Damato
5 * Copyright (C) 1998,1999,2002,2003,2004,2007,2008,2009 Juan Cespedes
6 * Copyright (C) 2006 Ian Wienand
7 * Copyright (C) 2006 Steve Fink
8 * Copyright (C) 2006 Paul Gilliam, IBM Corporation
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 * 02110-1301 USA
24 */
25
Juan Cespedesac3db291998-04-25 14:31:58 +020026#include "config.h"
Juan Cespedesac3db291998-04-25 14:31:58 +020027
Juan Cespedesce377d52008-12-16 19:38:10 +010028#include <sys/ioctl.h>
Petr Machata418584d2012-12-04 17:53:56 +010029#include <sys/stat.h>
30#include <sys/types.h>
Petr Machata2b46cfc2012-02-18 11:17:29 +010031#include <assert.h>
Petr Machata1e4fed22012-04-01 00:45:22 +020032#include <errno.h>
Petr Machata1e4fed22012-04-01 00:45:22 +020033#include <fcntl.h>
34#include <getopt.h>
35#include <limits.h>
Petr Machatacc0e1e42012-04-25 13:42:07 +020036#include <stdio.h>
Petr Machata1e4fed22012-04-01 00:45:22 +020037#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
Juan Cespedesac3db291998-04-25 14:31:58 +020040
Juan Cespedesf7281232009-06-25 16:11:21 +020041#include "common.h"
Petr Machata1e4fed22012-04-01 00:45:22 +020042#include "filter.h"
43#include "glob.h"
Petr Machata3ac8db62012-11-23 18:43:10 +010044#include "demangle.h"
Juan Cespedes5e01f651998-03-08 22:31:44 +010045
Juan Cespedesce377d52008-12-16 19:38:10 +010046struct options_t options = {
Juan Cespedesda9b9532009-04-07 15:33:50 +020047 .align = DEFAULT_ALIGN, /* alignment column for results */
48 .user = NULL, /* username to run command as */
49 .syscalls = 0, /* display syscalls */
Juan Cespedesce377d52008-12-16 19:38:10 +010050#ifdef USE_DEMANGLE
Juan Cespedesda9b9532009-04-07 15:33:50 +020051 .demangle = 0, /* Demangle low-level symbol names */
Juan Cespedesce377d52008-12-16 19:38:10 +010052#endif
Juan Cespedesda9b9532009-04-07 15:33:50 +020053 .indent = 0, /* indent output according to program flow */
54 .output = NULL, /* output to a specific file */
Juan Cespedescc813cd2009-04-07 15:45:53 +020055 .summary = 0, /* Report a summary on program exit */
56 .debug = 0, /* debug */
57 .arraylen = DEFAULT_ARRAYLEN, /* maximum # array elements to print */
58 .strlen = DEFAULT_STRLEN, /* maximum # of bytes printed in strings */
59 .follow = 0, /* trace child processes */
Juan Cespedesce377d52008-12-16 19:38:10 +010060};
61
Juan Cespedesac3db291998-04-25 14:31:58 +020062static char *progname; /* Program name (`ltrace') */
Juan Cespedes5e01f651998-03-08 22:31:44 +010063int opt_i = 0; /* instruction pointer */
Juan Cespedesf666d191998-09-20 23:04:34 +020064int opt_r = 0; /* print relative timestamp */
Juan Cespedes5e0acdb1998-04-04 08:34:07 +020065int opt_t = 0; /* print absolute timestamp */
Juan Cespedesd65efa32003-02-03 00:22:30 +010066int opt_T = 0; /* show the time spent inside each call */
Juan Cespedes5e01f651998-03-08 22:31:44 +010067
68/* List of pids given to option -p: */
Ian Wienand2d45b1a2006-02-20 22:48:07 +010069struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */
Juan Cespedes5e01f651998-03-08 22:31:44 +010070
Petr Machata418584d2012-12-04 17:53:56 +010071/* Vector of struct opt_F_t. */
72struct vect opt_F;
Steve Fink58c73a72006-07-17 23:18:35 +020073
Juan Cespedesf1350522008-12-16 18:19:58 +010074static void
Juan Cespedesc5c744a2009-07-23 18:22:58 +020075err_usage(void) {
Petr Machata20a411d2012-09-25 22:45:26 +020076 fprintf(stderr, "Try `%s --help' for more information.\n", progname);
Juan Cespedesc5c744a2009-07-23 18:22:58 +020077 exit(1);
78}
79
80static void
Juan Cespedesf1350522008-12-16 18:19:58 +010081usage(void) {
Juan Cespedesac3db291998-04-25 14:31:58 +020082 fprintf(stdout, "Usage: %s [option ...] [command [arg ...]]\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010083 "Trace library calls of a given program.\n\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010084 " -a, --align=COLUMN align return values in a secific column.\n"
Petr Machata4b3bda42013-01-11 23:32:17 +010085 " -A MAXELTS maximum number of array elements to print.\n"
Joe Damato59e3fb12009-11-06 19:45:10 -080086 " -b, --no-signals don't print signals.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010087 " -c count time and calls, and report a summary on exit.\n"
Juan Cespedesd914a202004-11-10 00:15:33 +010088# ifdef USE_DEMANGLE
Ian Wienand2d45b1a2006-02-20 22:48:07 +010089 " -C, --demangle decode low-level symbol names into user-level names.\n"
Juan Cespedesac3db291998-04-25 14:31:58 +020090# endif
Petr Machata4b3bda42013-01-11 23:32:17 +010091 " -D, --debug=MASK enable debugging (see -Dh or --debug=help).\n"
Juan Cespedesc5c744a2009-07-23 18:22:58 +020092 " -Dh, --debug=help show help on debugging.\n"
Petr Machata4b3bda42013-01-11 23:32:17 +010093 " -e FILTER modify which library calls to trace.\n"
Juan Cespedesc4e53a92009-05-06 20:36:42 +020094 " -f trace children (fork() and clone()).\n"
Juan Cespedescc813cd2009-04-07 15:45:53 +020095 " -F, --config=FILE load alternate configuration file (may be repeated).\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010096 " -h, --help display this help and exit.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010097 " -i print instruction pointer at time of library call.\n"
Petr Machata4b3bda42013-01-11 23:32:17 +010098 " -l, --library=LIBRARY_PATTERN only trace symbols implemented by this library.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010099 " -L do NOT display library calls.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100100 " -n, --indent=NR indent output by NR spaces for each call level nesting.\n"
Petr Machata4b3bda42013-01-11 23:32:17 +0100101 " -o, --output=FILENAME write the trace output to file with given name.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100102 " -p PID attach to the process with the process ID pid.\n"
103 " -r print relative timestamps.\n"
Petr Machata4b3bda42013-01-11 23:32:17 +0100104 " -s STRSIZE specify the maximum string size to print.\n"
105 " -S trace system calls as well as library calls.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100106 " -t, -tt, -ttt print absolute timestamps.\n"
107 " -T show the time spent inside each call.\n"
108 " -u USERNAME run command with the userid, groupid of username.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100109 " -V, --version output version information and exit.\n"
Mark Wielaarddfefa9f2014-01-07 21:00:44 +0100110#if defined(HAVE_UNWINDER)
Petr Machata68818a82012-11-30 21:42:59 +0100111 " -w, --where=NR print backtrace showing NR stack frames at most.\n"
Mark Wielaarddfefa9f2014-01-07 21:00:44 +0100112#endif /* defined(HAVE_UNWINDER) */
Petr Machata4b3bda42013-01-11 23:32:17 +0100113 " -x FILTER modify which static functions to trace.\n"
Paul Gilliambe320772006-04-24 22:06:23 +0200114 "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n",
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100115 progname);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100116}
117
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200118static void
119usage_debug(void) {
120 fprintf(stdout, "%s debugging option, --debug=<octal> or -D<octal>:\n", progname);
121 fprintf(stdout,
122 "\n"
123 " number ref. in source description\n"
124 " 1 general Generally helpful progress information\n"
125 " 10 event Shows every event received by a traced process\n"
126 " 20 process Shows actions carried upon a traced processes\n"
127 " 40 function Shows every entry to internal functions\n"
128 "\n"
129 "Debugging options are mixed using bitwise-or.\n"
130 "Note that the meanings and values are subject to change.\n"
Petr Machata94773bf2014-01-14 16:01:35 +0100131 "Also note that these values are used inconsistently in ltrace, and the\n"
132 "only debuglevel that you can rely on is -D77 that will show everything.\n"
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200133 );
134}
135
Juan Cespedesf1350522008-12-16 18:19:58 +0100136static char *
137search_for_command(char *filename) {
Juan Cespedesf1bfe202002-03-27 00:22:23 +0100138 static char pathname[PATH_MAX];
Juan Cespedes5e01f651998-03-08 22:31:44 +0100139 char *path;
140 int m, n;
141
142 if (strchr(filename, '/')) {
143 return filename;
144 }
145 for (path = getenv("PATH"); path && *path; path += m) {
146 if (strchr(path, ':')) {
147 n = strchr(path, ':') - path;
148 m = n + 1;
149 } else {
150 m = n = strlen(path);
151 }
Juan Cespedesf1bfe202002-03-27 00:22:23 +0100152 if (n + strlen(filename) + 1 >= PATH_MAX) {
Petr Machata20a411d2012-09-25 22:45:26 +0200153 fprintf(stderr, "Error: filename too long.\n");
Juan Cespedesf1bfe202002-03-27 00:22:23 +0100154 exit(1);
155 }
156 strncpy(pathname, path, n);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100157 if (n && pathname[n - 1] != '/') {
158 pathname[n++] = '/';
159 }
160 strcpy(pathname + n, filename);
161 if (!access(pathname, X_OK)) {
162 return pathname;
163 }
164 }
165 return filename;
166}
167
Juan Cespedesce377d52008-12-16 19:38:10 +0100168static void
169guess_cols(void) {
170 struct winsize ws;
171 char *c;
172
173 options.align = DEFAULT_ALIGN;
174 c = getenv("COLUMNS");
175 if (c && *c) {
176 char *endptr;
177 int cols;
178 cols = strtol(c, &endptr, 0);
179 if (cols > 0 && !*endptr) {
180 options.align = cols * 5 / 8;
181 }
182 } else if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) {
183 options.align = ws.ws_col * 5 / 8;
Juan Cespedes43739a62009-04-07 13:10:08 +0200184 } else if (ioctl(2, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) {
185 options.align = ws.ws_col * 5 / 8;
Juan Cespedesce377d52008-12-16 19:38:10 +0100186 }
187}
188
Petr Machata02b96072012-09-25 23:10:14 +0200189static int
190compile_libname(const char *expr, const char *a_lib, int lib_re_p,
191 struct filter_lib_matcher *matcher)
192{
193 if (strcmp(a_lib, "MAIN") == 0) {
194 filter_lib_matcher_main_init(matcher);
195 } else {
196 /* Add ^ and $ to the library expression as well. */
197 char lib[strlen(a_lib) + 3];
198 sprintf(lib, "^%s$", a_lib);
199
200 enum filter_lib_matcher_type type
201 = lib[0] == '/' ? FLM_PATHNAME : FLM_SONAME;
202
203 regex_t lib_re;
204 int status = (lib_re_p ? regcomp : globcomp)(&lib_re, lib, 0);
Petr Machatab6c5c8c2012-12-08 03:23:39 +0100205 if (status != 0) {
Petr Machata02b96072012-09-25 23:10:14 +0200206 char buf[100];
207 regerror(status, &lib_re, buf, sizeof buf);
Petr Machataec446692013-01-06 17:14:09 +0100208 fprintf(stderr, "Couldn't compile '%s': %s.\n",
Petr Machata02b96072012-09-25 23:10:14 +0200209 expr, buf);
210 return -1;
211 }
212 filter_lib_matcher_name_init(matcher, type, lib_re);
213 }
214 return 0;
215}
216
Petr Machataec446692013-01-06 17:14:09 +0100217static int
Petr Machata1e4fed22012-04-01 00:45:22 +0200218add_filter_rule(struct filter *filt, const char *expr,
219 enum filter_rule_type type,
Petr Machata5bc4e7f2012-04-06 22:28:24 +0200220 const char *a_sym, int sym_re_p,
221 const char *a_lib, int lib_re_p)
Petr Machata1e4fed22012-04-01 00:45:22 +0200222{
Petr Machata1e4fed22012-04-01 00:45:22 +0200223 struct filter_rule *rule = malloc(sizeof(*rule));
224 struct filter_lib_matcher *matcher = malloc(sizeof(*matcher));
225
226 if (rule == NULL || matcher == NULL) {
Petr Machata1e4fed22012-04-01 00:45:22 +0200227 fail:
228 free(rule);
229 free(matcher);
Petr Machataec446692013-01-06 17:14:09 +0100230 return -1;
Petr Machata1e4fed22012-04-01 00:45:22 +0200231 }
232
233 regex_t symbol_re;
Petr Machata5bc4e7f2012-04-06 22:28:24 +0200234 {
235 /* Add ^ to the start of expression and $ to the end, so that
236 * we match the whole symbol name. Let the user write the "*"
237 * explicitly if they wish. */
238 char sym[strlen(a_sym) + 3];
239 sprintf(sym, "^%s$", a_sym);
Petr Machata02b96072012-09-25 23:10:14 +0200240 int status = (sym_re_p ? regcomp : globcomp)
241 (&symbol_re, sym, 0);
Petr Machata5bc4e7f2012-04-06 22:28:24 +0200242 if (status != 0) {
243 char buf[100];
244 regerror(status, &symbol_re, buf, sizeof buf);
Petr Machataec446692013-01-06 17:14:09 +0100245 fprintf(stderr, "Couldn't compile '%s': %s.\n",
Petr Machatacc0e1e42012-04-25 13:42:07 +0200246 expr, buf);
Petr Machata5bc4e7f2012-04-06 22:28:24 +0200247 goto fail;
248 }
Petr Machata1e4fed22012-04-01 00:45:22 +0200249 }
250
Petr Machata02b96072012-09-25 23:10:14 +0200251 if (compile_libname(expr, a_lib, lib_re_p, matcher) < 0) {
252 regfree(&symbol_re);
253 goto fail;
Petr Machata1e4fed22012-04-01 00:45:22 +0200254 }
255
Petr Machata1e4fed22012-04-01 00:45:22 +0200256 filter_rule_init(rule, type, matcher, symbol_re);
Petr Machatae0973cb2012-04-01 19:36:41 +0200257 filter_add_rule(filt, rule);
Petr Machataec446692013-01-06 17:14:09 +0100258 return 0;
Petr Machata1e4fed22012-04-01 00:45:22 +0200259}
260
261static int
Petr Machata02b96072012-09-25 23:10:14 +0200262grok_libname_pattern(char **libnamep, char **libendp)
263{
264 char *libname = *libnamep;
265 char *libend = *libendp;
266
267 if (libend[0] != '/')
268 return 0;
269
270 *libend-- = 0;
271 if (libname != libend && libname[0] == '/')
272 ++libname;
273 else
274 fprintf(stderr, "Unmatched '/' in library name.\n");
275
276 *libendp = libend;
277 *libnamep = libname;
278 return 1;
279}
280
281static int
Petr Machata51e74ac2012-09-27 23:43:25 +0200282parse_filter(struct filter *filt, char *expr, int operators)
Petr Machata1e4fed22012-04-01 00:45:22 +0200283{
Petr Machata02b96072012-09-25 23:10:14 +0200284 /* Filter is a chain of sym@lib rules separated by '-' or '+'.
285 * If the filter expression starts with '-', the missing
286 * initial rule is implicitly *@*. */
Petr Machata1e4fed22012-04-01 00:45:22 +0200287
288 enum filter_rule_type type = FR_ADD;
289
290 while (*expr != 0) {
Andrey Zonov6bb42012013-02-14 12:32:06 +0100291 size_t s = strcspn(expr, &"-+@"[operators ? 0 : 2]);
Petr Machata1e4fed22012-04-01 00:45:22 +0200292 char *symname = expr;
293 char *libname;
294 char *next = expr + s + 1;
295 enum filter_rule_type this_type = type;
296
297 if (expr[s] == 0) {
298 libname = "*";
299 expr = next - 1;
300
Petr Machata050fa7f2012-04-23 23:44:36 +0200301 } else if (expr[s] == '-' || expr[s] == '+') {
302 type = expr[s] == '-' ? FR_SUBTRACT : FR_ADD;
303 expr[s] = 0;
Petr Machata1e4fed22012-04-01 00:45:22 +0200304 libname = "*";
305 expr = next;
Petr Machata1e4fed22012-04-01 00:45:22 +0200306
307 } else {
308 assert(expr[s] == '@');
309 expr[s] = 0;
Andrey Zonov6bb42012013-02-14 12:32:06 +0100310 s = strcspn(next, &"-+"[operators ? 0 : 2]);
Petr Machata1e4fed22012-04-01 00:45:22 +0200311 if (s == 0) {
312 libname = "*";
313 expr = next;
314 } else if (next[s] == 0) {
315 expr = next + s;
316 libname = next;
Petr Machata1e4fed22012-04-01 00:45:22 +0200317 } else {
Petr Machata050fa7f2012-04-23 23:44:36 +0200318 assert(next[s] == '-' || next[s] == '+');
319 type = next[s] == '-' ? FR_SUBTRACT : FR_ADD;
Petr Machata1e4fed22012-04-01 00:45:22 +0200320 next[s] = 0;
321 expr = next + s + 1;
322 libname = next;
323 }
324 }
325
326 assert(*libname != 0);
327 char *symend = symname + strlen(symname) - 1;
328 char *libend = libname + strlen(libname) - 1;
329 int sym_is_re = 0;
330 int lib_is_re = 0;
331
332 /*
333 * /xxx/@... and ...@/xxx/ means that xxx are regular
334 * expressions. They are globs otherwise.
335 *
336 * /xxx@yyy/ is the same as /xxx/@/yyy/
337 *
338 * @/xxx matches library path name
339 * @.xxx matches library relative path name
340 */
341 if (symname[0] == '/') {
342 if (symname != symend && symend[0] == '/') {
343 ++symname;
344 *symend-- = 0;
345 sym_is_re = 1;
346
347 } else {
348 sym_is_re = 1;
349 lib_is_re = 1;
350 ++symname;
351
352 /* /XXX@YYY/ is the same as
353 * /XXX/@/YYY/. */
354 if (libend[0] != '/')
Petr Machata20a411d2012-09-25 22:45:26 +0200355 fprintf(stderr, "Unmatched '/'"
356 " in symbol name.\n");
Petr Machata1e4fed22012-04-01 00:45:22 +0200357 else
358 *libend-- = 0;
359 }
360 }
361
362 /* If libname ends in '/', then we expect '/' in the
363 * beginning too. Otherwise the initial '/' is part
364 * of absolute file name. */
Petr Machata02b96072012-09-25 23:10:14 +0200365 if (!lib_is_re)
366 lib_is_re = grok_libname_pattern(&libname, &libend);
Petr Machata1e4fed22012-04-01 00:45:22 +0200367
368 if (*symname == 0) /* /@AA/ */
369 symname = "*";
370 if (*libname == 0) /* /aa@/ */
371 libname = "*";
372
Petr Machataa43b2a02013-03-11 21:21:22 -0400373 add_filter_rule(filt, expr, this_type,
374 symname, sym_is_re,
375 libname, lib_is_re);
Petr Machata1e4fed22012-04-01 00:45:22 +0200376 }
377
378 return 0;
379}
380
381static struct filter *
Petr Machataec446692013-01-06 17:14:09 +0100382recursive_parse_chain(const char *orig, char *expr, int operators)
Petr Machata1e4fed22012-04-01 00:45:22 +0200383{
Petr Machata1e4fed22012-04-01 00:45:22 +0200384 struct filter *filt = malloc(sizeof(*filt));
385 if (filt == NULL) {
Petr Machata20a411d2012-09-25 22:45:26 +0200386 fprintf(stderr, "(Part of) filter will be ignored: '%s': %s.\n",
Petr Machatacc0e1e42012-04-25 13:42:07 +0200387 expr, strerror(errno));
Petr Machata1e4fed22012-04-01 00:45:22 +0200388 return NULL;
389 }
390
Petr Machata35c88142012-04-04 00:54:43 +0200391 filter_init(filt);
Petr Machata51e74ac2012-09-27 23:43:25 +0200392 if (parse_filter(filt, expr, operators) < 0) {
Petr Machataec446692013-01-06 17:14:09 +0100393 fprintf(stderr, "Filter '%s' will be ignored.\n", orig);
Petr Machata050fa7f2012-04-23 23:44:36 +0200394 free(filt);
395 filt = NULL;
Petr Machata1e4fed22012-04-01 00:45:22 +0200396 }
Petr Machata050fa7f2012-04-23 23:44:36 +0200397
398 return filt;
Petr Machata1e4fed22012-04-01 00:45:22 +0200399}
400
Petr Machata51e74ac2012-09-27 23:43:25 +0200401static struct filter **
402slist_chase_end(struct filter **begin)
403{
404 for (; *begin != NULL; begin = &(*begin)->next)
405 ;
406 return begin;
407}
408
Petr Machata1e4fed22012-04-01 00:45:22 +0200409static void
Petr Machatab5f80ac2012-04-04 01:46:18 +0200410parse_filter_chain(const char *expr, struct filter **retp)
Petr Machata1e4fed22012-04-01 00:45:22 +0200411{
412 char *str = strdup(expr);
413 if (str == NULL) {
Petr Machata20a411d2012-09-25 22:45:26 +0200414 fprintf(stderr, "Filter '%s' will be ignored: %s.\n",
Petr Machatacc0e1e42012-04-25 13:42:07 +0200415 expr, strerror(errno));
Petr Machata1e4fed22012-04-01 00:45:22 +0200416 return;
417 }
Petr Machata25f319e2012-04-23 23:45:21 +0200418 /* Support initial '!' for backward compatibility. */
419 if (str[0] == '!')
420 str[0] = '-';
421
Petr Machataec446692013-01-06 17:14:09 +0100422 *slist_chase_end(retp) = recursive_parse_chain(expr, str, 1);
Petr Machatadd1ec842012-10-27 00:34:10 +0200423 free(str);
Petr Machata1e4fed22012-04-01 00:45:22 +0200424}
425
Petr Machata68818a82012-11-30 21:42:59 +0100426static int
427parse_int(const char *optarg, char opt, int min, int max)
428{
429 char *endptr;
430 long int l = strtol(optarg, &endptr, 0);
431 if (l < min || (max != 0 && l > max)
432 || *optarg == 0 || *endptr != 0) {
433 const char *fmt = max != 0
434 ? "Invalid argument to -%c: '%s'. Use integer %d..%d.\n"
435 : "Invalid argument to -%c: '%s'. Use integer >=%d.\n";
436 fprintf(stderr, fmt, opt, optarg, min, max);
437 exit(1);
438 }
439 return (int)l;
440}
441
Petr Machata418584d2012-12-04 17:53:56 +0100442int
443parse_colon_separated_list(const char *paths, struct vect *vec)
444{
445 /* PATHS contains a colon-separated list of directories and
446 * files to load. It's modeled after shell PATH variable,
447 * which doesn't allow escapes. PYTHONPATH in CPython behaves
448 * the same way. So let's follow suit, it makes things easier
449 * to us. */
450
451 char *clone = strdup(paths);
452 if (clone == NULL) {
453 fprintf(stderr, "Couldn't parse argument %s: %s.\n",
454 paths, strerror(errno));
455 return -1;
456 }
457
458 /* It's undesirable to use strtok, because we want the string
459 * "a::b" to have three elements. */
460 char *tok = clone - 1;
461 char *end = clone + strlen(clone);
462 while (tok < end) {
463 ++tok;
464 size_t len = strcspn(tok, ":");
465 tok[len] = 0;
466
467 struct opt_F_t arg = {
468 .pathname = tok,
469 .own_pathname = tok == clone,
470 };
471 if (VECT_PUSHBACK(vec, &arg) < 0)
472 /* Presumably this is not a deal-breaker. */
473 fprintf(stderr, "Couldn't store component of %s: %s.\n",
474 paths, strerror(errno));
475
476 tok += len;
477 }
478
479 return 0;
480}
481
482void
483opt_F_destroy(struct opt_F_t *entry)
484{
485 if (entry == NULL)
486 return;
487 if (entry->own_pathname)
488 free(entry->pathname);
489}
490
491enum opt_F_kind
492opt_F_get_kind(struct opt_F_t *entry)
493{
494 if (entry->kind == OPT_F_UNKNOWN) {
495 struct stat st;
496 if (lstat(entry->pathname, &st) < 0) {
497 fprintf(stderr, "Couldn't stat %s: %s\n",
498 entry->pathname, strerror(errno));
499 entry->kind = OPT_F_BROKEN;
500 } else if (S_ISDIR(st.st_mode)) {
501 entry->kind = OPT_F_DIR;
Petr Machatae7be3902012-12-05 01:21:03 +0100502 } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
Petr Machata418584d2012-12-04 17:53:56 +0100503 entry->kind = OPT_F_FILE;
504 } else {
505 fprintf(stderr, "%s is neither a regular file, "
506 "nor a directory.\n", entry->pathname);
507 entry->kind = OPT_F_BROKEN;
508 }
509 }
510 assert(entry->kind != OPT_F_UNKNOWN);
511 return entry->kind;
512}
513
Juan Cespedesf1350522008-12-16 18:19:58 +0100514char **
Petr Machata67fa52f2012-04-05 02:11:39 +0200515process_options(int argc, char **argv)
516{
Petr Machata418584d2012-12-04 17:53:56 +0100517 VECT_INIT(&opt_F, struct opt_F_t);
518
Juan Cespedesac3db291998-04-25 14:31:58 +0200519 progname = argv[0];
Juan Cespedesb65bdc52008-12-16 19:50:16 +0100520 options.output = stderr;
Joe Damato59e3fb12009-11-06 19:45:10 -0800521 options.no_signals = 0;
Mark Wielaarddfefa9f2014-01-07 21:00:44 +0100522#if defined(HAVE_UNWINDER)
Joe Damatoab3b72c2010-10-31 00:21:53 -0700523 options.bt_depth = -1;
Mark Wielaarddfefa9f2014-01-07 21:00:44 +0100524#endif /* defined(HAVE_UNWINDER) */
Juan Cespedes5e01f651998-03-08 22:31:44 +0100525
Juan Cespedesce377d52008-12-16 19:38:10 +0100526 guess_cols();
527
Petr Machata67fa52f2012-04-05 02:11:39 +0200528 int libcalls = 1;
529
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100530 while (1) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200531 int c;
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200532 char *p;
Petr Machata35d07302012-12-09 11:38:56 +0100533#ifdef HAVE_GETOPT_LONG
Juan Cespedesac3db291998-04-25 14:31:58 +0200534 int option_index = 0;
535 static struct option long_options[] = {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100536 {"align", 1, 0, 'a'},
Juan Cespedesaee09312007-08-31 18:49:48 +0200537 {"config", 1, 0, 'F'},
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200538 {"debug", 1, 0, 'D'},
Juan Cespedesd914a202004-11-10 00:15:33 +0100539# ifdef USE_DEMANGLE
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100540 {"demangle", 0, 0, 'C'},
Petr Machata35d07302012-12-09 11:38:56 +0100541# endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100542 {"indent", 1, 0, 'n'},
543 {"help", 0, 0, 'h'},
544 {"library", 1, 0, 'l'},
545 {"output", 1, 0, 'o'},
546 {"version", 0, 0, 'V'},
Joe Damato59e3fb12009-11-06 19:45:10 -0800547 {"no-signals", 0, 0, 'b'},
Mark Wielaarddfefa9f2014-01-07 21:00:44 +0100548# if defined(HAVE_UNWINDER)
Joe Damatoab3b72c2010-10-31 00:21:53 -0700549 {"where", 1, 0, 'w'},
Mark Wielaarddfefa9f2014-01-07 21:00:44 +0100550# endif /* defined(HAVE_UNWINDER) */
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100551 {0, 0, 0, 0}
Juan Cespedesac3db291998-04-25 14:31:58 +0200552 };
Joe Damatoab3b72c2010-10-31 00:21:53 -0700553#endif
Petr Machata35d07302012-12-09 11:38:56 +0100554
555 const char *opts = "+"
556#ifdef USE_DEMANGLE
557 "C"
558#endif
Mark Wielaarddfefa9f2014-01-07 21:00:44 +0100559#if defined(HAVE_UNWINDER)
Petr Machata35d07302012-12-09 11:38:56 +0100560 "w:"
561#endif
Petr Machata5da68e92014-02-28 12:37:15 +0100562 "cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:";
Petr Machata35d07302012-12-09 11:38:56 +0100563
564#ifdef HAVE_GETOPT_LONG
565 c = getopt_long(argc, argv, opts, long_options, &option_index);
566#else
567 c = getopt(argc, argv, opts);
568#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100569 if (c == -1) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200570 break;
Juan Cespedes5e01f651998-03-08 22:31:44 +0100571 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100572 switch (c) {
573 case 'a':
Petr Machata68818a82012-11-30 21:42:59 +0100574 options.align = parse_int(optarg, 'a', 0, 0);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100575 break;
Juan Cespedesaee09312007-08-31 18:49:48 +0200576 case 'A':
Petr Machata68818a82012-11-30 21:42:59 +0100577 options.arraylen = parse_int(optarg, 'A', 0, 0);
Steve Fink1150bc42006-08-07 06:04:43 +0200578 break;
Joe Damato535e7382010-11-08 15:47:43 -0800579 case 'b':
580 options.no_signals = 1;
581 break;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100582 case 'c':
Juan Cespedesda9b9532009-04-07 15:33:50 +0200583 options.summary++;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100584 break;
Juan Cespedesd914a202004-11-10 00:15:33 +0100585#ifdef USE_DEMANGLE
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100586 case 'C':
Juan Cespedesce377d52008-12-16 19:38:10 +0100587 options.demangle++;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100588 break;
Juan Cespedesac3db291998-04-25 14:31:58 +0200589#endif
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200590 case 'D':
591 if (optarg[0]=='h') {
592 usage_debug();
593 exit(0);
594 }
595 options.debug = strtoul(optarg,&p,8);
596 if (*p) {
597 fprintf(stderr, "%s: --debug requires an octal argument\n", progname);
598 err_usage();
599 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100600 break;
Petr Machata1e4fed22012-04-01 00:45:22 +0200601
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100602 case 'e':
Petr Machatab5f80ac2012-04-04 01:46:18 +0200603 parse_filter_chain(optarg, &options.plt_filter);
Petr Machata1e4fed22012-04-01 00:45:22 +0200604 break;
605
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100606 case 'f':
Juan Cespedescc813cd2009-04-07 15:45:53 +0200607 options.follow = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100608 break;
Juan Cespedesaee09312007-08-31 18:49:48 +0200609 case 'F':
Petr Machata418584d2012-12-04 17:53:56 +0100610 parse_colon_separated_list(optarg, &opt_F);
611 break;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100612 case 'h':
613 usage();
614 exit(0);
615 case 'i':
616 opt_i++;
617 break;
Petr Machata51e74ac2012-09-27 23:43:25 +0200618
619 case 'l': {
620 size_t patlen = strlen(optarg);
621 char buf[patlen + 2];
622 sprintf(buf, "@%s", optarg);
623 *slist_chase_end(&options.export_filter)
Petr Machataec446692013-01-06 17:14:09 +0100624 = recursive_parse_chain(buf, buf, 0);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100625 break;
Petr Machata51e74ac2012-09-27 23:43:25 +0200626 }
627
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100628 case 'L':
Petr Machata67fa52f2012-04-05 02:11:39 +0200629 libcalls = 0;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100630 break;
Petr Machata68818a82012-11-30 21:42:59 +0100631 case 'n':
632 options.indent = parse_int(optarg, 'n', 0, 20);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100633 break;
634 case 'o':
Juan Cespedesb65bdc52008-12-16 19:50:16 +0100635 options.output = fopen(optarg, "w");
636 if (!options.output) {
Petr Machatacc0e1e42012-04-25 13:42:07 +0200637 fprintf(stderr,
638 "can't open %s for writing: %s\n",
639 optarg, strerror(errno));
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100640 exit(1);
641 }
Juan Cespedesb65bdc52008-12-16 19:50:16 +0100642 setvbuf(options.output, (char *)NULL, _IOLBF, 0);
643 fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100644 break;
645 case 'p':
646 {
Juan Cespedesaee09312007-08-31 18:49:48 +0200647 struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t));
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100648 if (!tmp) {
649 perror("ltrace: malloc");
650 exit(1);
651 }
Petr Machata68818a82012-11-30 21:42:59 +0100652 tmp->pid = parse_int(optarg, 'p', 1, 0);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100653 tmp->next = opt_p;
654 opt_p = tmp;
655 break;
656 }
657 case 'r':
658 opt_r++;
659 break;
660 case 's':
Petr Machata68818a82012-11-30 21:42:59 +0100661 options.strlen = parse_int(optarg, 's', 0, 0);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100662 break;
663 case 'S':
Juan Cespedesce377d52008-12-16 19:38:10 +0100664 options.syscalls = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100665 break;
666 case 't':
667 opt_t++;
668 break;
669 case 'T':
670 opt_T++;
671 break;
672 case 'u':
Juan Cespedesce377d52008-12-16 19:38:10 +0100673 options.user = optarg;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100674 break;
675 case 'V':
Petr Machata5cde20b2012-12-15 23:54:54 +0100676 printf("ltrace " PACKAGE_VERSION "\n"
Petr Machata3992a0b2013-10-23 00:44:52 +0200677 "Copyright (C) 2010-2013 Petr Machata, Red Hat Inc.\n"
Petr Machata5cde20b2012-12-15 23:54:54 +0100678 "Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org>.\n"
679 "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
680 "This is free software: you are free to change and redistribute it.\n"
681 "There is NO WARRANTY, to the extent permitted by law.\n");
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100682 exit(0);
Joe Damatofa2aefc2010-10-30 19:56:50 -0700683 break;
Mark Wielaarddfefa9f2014-01-07 21:00:44 +0100684#if defined(HAVE_UNWINDER)
Joe Damato535e7382010-11-08 15:47:43 -0800685 case 'w':
Petr Machata68818a82012-11-30 21:42:59 +0100686 options.bt_depth = parse_int(optarg, 'w', 1, 0);
Joe Damato59e3fb12009-11-06 19:45:10 -0800687 break;
Mark Wielaarddfefa9f2014-01-07 21:00:44 +0100688#endif /* defined(HAVE_UNWINDER) */
Juan Cespedesac3db291998-04-25 14:31:58 +0200689
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100690 case 'x':
Petr Machatada3edbf2012-04-04 02:20:21 +0200691 parse_filter_chain(optarg, &options.static_filter);
692 break;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100693
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100694 default:
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200695 err_usage();
Juan Cespedes5e01f651998-03-08 22:31:44 +0100696 }
697 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100698 argc -= optind;
699 argv += optind;
Juan Cespedes5e01f651998-03-08 22:31:44 +0100700
Petr Machata51e74ac2012-09-27 23:43:25 +0200701 /* If neither -e, nor -l, nor -L are used, set default -e.
702 * Use @MAIN for now, as that's what ltrace used to have in
703 * the past. XXX Maybe we should make this "*" instead. */
704 if (libcalls
705 && options.plt_filter == NULL
706 && options.export_filter == NULL) {
Petr Machatab5f80ac2012-04-04 01:46:18 +0200707 parse_filter_chain("@MAIN", &options.plt_filter);
Petr Machata03673892012-04-03 13:51:09 +0200708 options.hide_caller = 1;
709 }
Petr Machata51e74ac2012-09-27 23:43:25 +0200710 if (!libcalls && options.plt_filter != NULL) {
711 fprintf(stderr,
712 "%s: Option -L can't be used with -e or -l.\n",
713 progname);
714 err_usage();
715 }
Petr Machatae0973cb2012-04-01 19:36:41 +0200716
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100717 if (!opt_p && argc < 1) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200718 fprintf(stderr, "%s: too few arguments\n", progname);
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200719 err_usage();
Juan Cespedes5e01f651998-03-08 22:31:44 +0100720 }
Juan Cespedesf666d191998-09-20 23:04:34 +0200721 if (opt_r && opt_t) {
Petr Machata20a411d2012-09-25 22:45:26 +0200722 fprintf(stderr,
723 "%s: Options -r and -t can't be used together\n",
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100724 progname);
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200725 err_usage();
Juan Cespedesf666d191998-09-20 23:04:34 +0200726 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100727 if (argc > 0) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200728 command = search_for_command(argv[0]);
Juan Cespedes1fe93d51998-03-13 00:29:21 +0100729 }
Juan Cespedesac3db291998-04-25 14:31:58 +0200730 return &argv[0];
Juan Cespedes5e01f651998-03-08 22:31:44 +0100731}