Esempio n. 1
0
static void
parse_filter_chain(const char *expr, struct filter **retp)
{
	char *str = strdup(expr);
	if (str == NULL) {
		fprintf(stderr, "Filter '%s' will be ignored: %s.\n",
			expr, strerror(errno));
		return;
	}
	/* Support initial '!' for backward compatibility.  */
	if (str[0] == '!')
		str[0] = '-';

	*slist_chase_end(retp) = recursive_parse_chain(str, 1);
	free(str);
}
int
parse_colon_separated_list(const char *paths, struct vect *vec)
{
	/* PATHS contains a colon-separated list of directories and
	 * files to load.  It's modeled after shell PATH variable,
	 * which doesn't allow escapes.  PYTHONPATH in CPython behaves
	 * the same way.  So let's follow suit, it makes things easier
	 * to us.  */

	char *clone = strdup(paths);
	if (clone == NULL) {
		fprintf(stderr, "Couldn't parse argument %s: %s.\n",
			paths, strerror(errno));
		return -1;
	}

	/* It's undesirable to use strtok, because we want the string
	 * "a::b" to have three elements.  */
	char *tok = clone - 1;
	char *end = clone + strlen(clone);
	while (tok < end) {
		++tok;
		size_t len = strcspn(tok, ":");
		tok[len] = 0;

		struct opt_F_t arg = {
			.pathname = tok,
			.own_pathname = tok == clone,
		};
		if (VECT_PUSHBACK(vec, &arg) < 0)
			/* Presumably this is not a deal-breaker.  */
			fprintf(stderr, "Couldn't store component of %s: %s.\n",
				paths, strerror(errno));

		tok += len;
	}

	return 0;
}

void
opt_F_destroy(struct opt_F_t *entry)
{
	if (entry == NULL)
		return;
	if (entry->own_pathname)
		free(entry->pathname);
}

enum opt_F_kind
opt_F_get_kind(struct opt_F_t *entry)
{
	if (entry->kind == OPT_F_UNKNOWN) {
		struct stat st;
		if (lstat(entry->pathname, &st) < 0) {
			fprintf(stderr, "Couldn't stat %s: %s\n",
				entry->pathname, strerror(errno));
			entry->kind = OPT_F_BROKEN;
		} else if (S_ISDIR(st.st_mode)) {
			entry->kind = OPT_F_DIR;
		} else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
			entry->kind = OPT_F_FILE;
		} else {
			fprintf(stderr, "%s is neither a regular file, "
				"nor a directory.\n", entry->pathname);
			entry->kind = OPT_F_BROKEN;
		}
	}
	assert(entry->kind != OPT_F_UNKNOWN);
	return entry->kind;
}

char **
process_options(int argc, char **argv)
{
	VECT_INIT(&opt_F, struct opt_F_t);

	progname = argv[0];
	options.output = stderr;
	options.no_signals = 0;
#if defined(HAVE_UNWINDER)
	options.bt_depth = -1;
#endif /* defined(HAVE_UNWINDER) */

	guess_cols();

	int libcalls = 1;

	while (1) {
		int c;
		char *p;
#ifdef HAVE_GETOPT_LONG
		int option_index = 0;
		static struct option long_options[] = {
			{"align", 1, 0, 'a'},
			{"config", 1, 0, 'F'},
			{"debug", 1, 0, 'D'},
# ifdef USE_DEMANGLE
			{"demangle", 0, 0, 'C'},
# endif
			{"indent", 1, 0, 'n'},
			{"help", 0, 0, 'h'},
			{"library", 1, 0, 'l'},
			{"output", 1, 0, 'o'},
			{"version", 0, 0, 'V'},
			{"no-signals", 0, 0, 'b'},
# if defined(HAVE_UNWINDER)
			{"where", 1, 0, 'w'},
# endif /* defined(HAVE_UNWINDER) */
			{0, 0, 0, 0}
		};
#endif

		const char *opts = "+"
#ifdef USE_DEMANGLE
			"C"
#endif
#if defined(HAVE_UNWINDER)
			"w:"
#endif
			"cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:";

#ifdef HAVE_GETOPT_LONG
		c = getopt_long(argc, argv, opts, long_options, &option_index);
#else
		c = getopt(argc, argv, opts);
#endif
		if (c == -1) {
			break;
		}
		switch (c) {
		case 'a':
			options.align = parse_int(optarg, 'a', 0, 0);
			break;
		case 'A':
			options.arraylen = parse_int(optarg, 'A', 0, 0);
			break;
		case 'b':
			options.no_signals = 1;
			break;
		case 'c':
			options.summary++;
			break;
#ifdef USE_DEMANGLE
		case 'C':
			options.demangle++;
			break;
#endif
		case 'D':
			if (optarg[0]=='h') {
				usage_debug();
				exit(0);
			}
			options.debug = strtoul(optarg,&p,8);
			if (*p) {
				fprintf(stderr, "%s: --debug requires an octal argument\n", progname);
				err_usage();
			}
			break;

		case 'e':
			parse_filter_chain(optarg, &options.plt_filter);
			break;

		case 'f':
			options.follow = 1;
			break;
		case 'F':
			parse_colon_separated_list(optarg, &opt_F);
			break;
		case 'h':
			usage();
			exit(0);
		case 'i':
			opt_i++;
			break;

		case 'l': {
			size_t patlen = strlen(optarg);
			char buf[patlen + 2];
			sprintf(buf, "@%s", optarg);
			*slist_chase_end(&options.export_filter)
				= recursive_parse_chain(buf, buf, 0);
			break;
		}

		case 'L':
			libcalls = 0;
			break;
		case 'n':
			options.indent = parse_int(optarg, 'n', 0, 20);
			break;
		case 'o':
			options.output = fopen(optarg, "w");
			if (!options.output) {
				fprintf(stderr,
					"can't open %s for writing: %s\n",
					optarg, strerror(errno));
				exit(1);
			}
			setvbuf(options.output, (char *)NULL, _IOLBF, 0);
			fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC);
			break;
		case 'p':
			{
				struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t));
				if (!tmp) {
					perror("ltrace: malloc");
					exit(1);
				}
				tmp->pid = parse_int(optarg, 'p', 1, 0);
				tmp->next = opt_p;
				opt_p = tmp;
				break;
			}
		case 'r':
			opt_r++;
			break;
		case 's':
			options.strlen = parse_int(optarg, 's', 0, 0);
			break;
		case 'S':
			options.syscalls = 1;
			break;
		case 't':
			opt_t++;
			break;
		case 'T':
			opt_T++;
			break;
		case 'u':
			options.user = optarg;
			break;
		case 'V':
			printf("ltrace " PACKAGE_VERSION "\n"
			       "Copyright (C) 2010-2013 Petr Machata, Red Hat Inc.\n"
			       "Copyright (C) 1997-2009 Juan Cespedes <*****@*****.**>.\n"
			       "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
			       "This is free software: you are free to change and redistribute it.\n"
			       "There is NO WARRANTY, to the extent permitted by law.\n");
			exit(0);
			break;
#if defined(HAVE_UNWINDER)
		case 'w':
			options.bt_depth = parse_int(optarg, 'w', 1, 0);
			break;
#endif /* defined(HAVE_UNWINDER) */

		case 'x':
			parse_filter_chain(optarg, &options.static_filter);
			break;

		default:
			err_usage();
		}
	}
	argc -= optind;
	argv += optind;

	/* If neither -e, nor -l, nor -L are used, set default -e.
	 * Use @MAIN for now, as that's what ltrace used to have in
	 * the past.  XXX Maybe we should make this "*" instead.  */
	if (libcalls
	    && options.plt_filter == NULL
	    && options.export_filter == NULL) {
		parse_filter_chain("@MAIN", &options.plt_filter);
		options.hide_caller = 1;
	}
	if (!libcalls && options.plt_filter != NULL) {
		fprintf(stderr,
			"%s: Option -L can't be used with -e or -l.\n",
			progname);
		err_usage();
	}

	if (!opt_p && argc < 1) {
		fprintf(stderr, "%s: too few arguments\n", progname);
		err_usage();
	}
	if (opt_r && opt_t) {
		fprintf(stderr,
			"%s: Options -r and -t can't be used together\n",
			progname);
		err_usage();
	}
	if (argc > 0) {
		command = search_for_command(argv[0]);
	}
	return &argv[0];
}