Make interactive-ness directly controllable via command line option
Defaults are often ok, but when they are not, people get confused.
"Why can't I kill strace?" and "Why strace dies on ^C when I want
to _tracee_ to die instead?" are typical complaints.
* strace.c: Replace 'interactive' variable with 'opt_intr' variable.
Define INTR_foo constants for its possible values.
Define 'interactive' as a macro.
(usage): Document -I n option.
(main): Parse -I n option, modify signal handling to accomidate new
-I 1 and -I 4 modes.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/strace.c b/strace.c
index 20a32b3..8eca57a 100644
--- a/strace.c
+++ b/strace.c
@@ -88,7 +88,20 @@
int dtime = 0, xflag = 0, qflag = 0;
cflag_t cflag = CFLAG_NONE;
static int iflag = 0, pflag_seen = 0, rflag = 0, tflag = 0;
-static int interactive = 1;
+
+/* -I n */
+enum {
+ INTR_NOT_SET = 0,
+ INTR_ANYWHERE = 1, /* don't block/ignore any signals */
+ INTR_WHILE_WAIT = 2, /* block fatal signals while decoding syscall. default */
+ INTR_NEVER = 3, /* block fatal signals. default if '-o FILE PROG' */
+ INTR_BLOCK_TSTP_TOO = 4, /* block fatal signals and SIGTSTP (^Z) */
+ NUM_INTR_OPTS
+};
+static int opt_intr;
+/* We play with signal mask only if this mode is active: */
+#define interactive (opt_intr == INTR_WHILE_WAIT)
+
/*
* daemonized_tracer supports -D option.
* With this option, strace forks twice.
@@ -187,16 +200,23 @@
usage(FILE *ofp, int exitval)
{
fprintf(ofp, "\
-usage: strace [-CdDffhiqrtttTvVxxy] [-a column] [-e expr] ... [-o file]\n\
- [-p pid] ... [-s strsize] [-u username] [-E var=val] ...\n\
- [-P path] [command [arg ...]]\n\
- or: strace -c [-D] [-e expr] ... [-O overhead] [-S sortby] [-E var=val] ...\n\
- [command [arg ...]]\n\
+usage: strace [-CdDffhiqrtttTvVxxy] [-I n] [-a column] [-e expr]... [-o file]\n\
+ [-p pid]... [-s strsize] [-u username] [-E var=val]...\n\
+ [-P path] [PROG [ARGS]]]\n\
+ or: strace -c [-D] [-I n] [-e expr]... [-O overhead] [-S sortby] [-E var=val]...\n\
+ [PROG [ARGS]]]\n\
-c -- count time, calls, and errors for each syscall and report summary\n\
-C -- like -c but also print regular output while processes are running\n\
+-D -- run tracer process as a detached grandchild, not as parent\n\
-f -- follow forks, -ff -- with output into separate files\n\
-F -- attempt to follow vforks, -h -- print help message\n\
-i -- print instruction pointer at time of syscall\n\
+-I interruptible\n\
+ 1: no signals are blocked\n\
+ 2: fatal signals are blocked while decoding syscall (default)\n\
+ 3: fatal signals are always blocked (default if '-o FILE PROG')\n\
+ 4: fatal signals and SIGTSTP (^Z) are always blocked\n\
+ (useful to make 'strace -o FILE PROG' not stop on ^Z)\n\
-q -- suppress messages about attaching, detaching, etc.\n\
-r -- print relative timestamp, -t -- absolute timestamp, -tt -- with usecs\n\
-T -- print time spent in each syscall, -V -- print version\n\
@@ -209,7 +229,6 @@
-o file -- send trace output to FILE instead of stderr\n\
-O overhead -- set overhead for tracing syscalls to OVERHEAD usecs\n\
-p pid -- trace process with process id PID, may be repeated\n\
--D -- run tracer process as a detached grandchild, not as parent\n\
-s strsize -- limit length of print strings to STRSIZE chars (default %d)\n\
-S sortby -- sort syscall counts by: time, calls, name, nothing (default %s)\n\
-u username -- run command as username handling setuid and/or setgid\n\
@@ -1119,7 +1138,7 @@
#ifndef USE_PROCFS
"D"
#endif
- "a:e:o:O:p:s:S:u:E:P:")) != EOF) {
+ "a:e:o:O:p:s:S:u:E:P:I:")) != EOF) {
switch (c) {
case 'c':
if (cflag == CFLAG_BOTH) {
@@ -1219,7 +1238,7 @@
case 's':
max_strlen = atoi(optarg);
if (max_strlen < 0) {
- error_msg_and_die("Invalid -s argument: '%s'", optarg);
+ error_msg_and_die("Invalid -%c argument: '%s'", c, optarg);
}
break;
case 'S':
@@ -1232,6 +1251,12 @@
if (putenv(optarg) < 0)
die_out_of_memory();
break;
+ case 'I':
+ opt_intr = atoi(optarg);
+ if (opt_intr <= 0 || opt_intr >= NUM_INTR_OPTS) {
+ error_msg_and_die("Invalid -%c argument: '%s'", c, optarg);
+ }
+ break;
default:
usage(stderr, 1);
break;
@@ -1310,16 +1335,18 @@
setvbuf(outf, buf, _IOLBF, BUFSIZ);
}
if (outfname && argv[0]) {
- interactive = 0;
+ if (!opt_intr)
+ opt_intr = INTR_NEVER;
qflag = 1;
}
+ if (!opt_intr)
+ opt_intr = INTR_WHILE_WAIT;
- /* Valid states here:
- argv[0] pflag_seen outfname interactive
- yes 0 0 1
- no 1 0 1
- yes 0 1 0
- no 1 1 1
+ /* argv[0] -pPID -oFILE Default interactive setting
+ * yes 0 0 INTR_WHILE_WAIT
+ * no 1 0 INTR_WHILE_WAIT
+ * yes 0 1 INTR_NEVER
+ * no 1 1 INTR_WHILE_WAIT
*/
/* STARTUP_CHILD must be called before the signal handlers get
@@ -1334,29 +1361,36 @@
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
- sigaction(SIGTTOU, &sa, NULL);
- sigaction(SIGTTIN, &sa, NULL);
- /* In interactive mode (if no -o OUTFILE, or -p PID is used),
- * fatal signals are blocked across syscall waits, and acted on
- * in between. In non-interactive mode, signals are ignored.
- */
- if (interactive) {
- sigaddset(&blocked_set, SIGHUP);
- sigaddset(&blocked_set, SIGINT);
- sigaddset(&blocked_set, SIGQUIT);
- sigaddset(&blocked_set, SIGPIPE);
- sigaddset(&blocked_set, SIGTERM);
- sa.sa_handler = interrupt;
+ sigaction(SIGTTOU, &sa, NULL); /* SIG_IGN */
+ sigaction(SIGTTIN, &sa, NULL); /* SIG_IGN */
+ if (opt_intr != INTR_ANYWHERE) {
+ if (opt_intr == INTR_BLOCK_TSTP_TOO)
+ sigaction(SIGTSTP, &sa, NULL); /* SIG_IGN */
+ /*
+ * In interactive mode (if no -o OUTFILE, or -p PID is used),
+ * fatal signals are blocked while syscall stop is processed,
+ * and acted on in between, when waiting for new syscall stops.
+ * In non-interactive mode, signals are ignored.
+ */
+ if (opt_intr == INTR_WHILE_WAIT) {
+ sigaddset(&blocked_set, SIGHUP);
+ sigaddset(&blocked_set, SIGINT);
+ sigaddset(&blocked_set, SIGQUIT);
+ sigaddset(&blocked_set, SIGPIPE);
+ sigaddset(&blocked_set, SIGTERM);
+ sa.sa_handler = interrupt;
#ifdef SUNOS4
- /* POSIX signals on sunos4.1 are a little broken. */
- sa.sa_flags = SA_INTERRUPT;
-#endif /* SUNOS4 */
+ /* POSIX signals on sunos4.1 are a little broken. */
+ sa.sa_flags = SA_INTERRUPT;
+#endif
+ }
+ /* SIG_IGN, or set handler for these */
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
}
- sigaction(SIGHUP, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGQUIT, &sa, NULL);
- sigaction(SIGPIPE, &sa, NULL);
- sigaction(SIGTERM, &sa, NULL);
#ifdef USE_PROCFS
sa.sa_handler = reaper;
sigaction(SIGCHLD, &sa, NULL);
@@ -1375,6 +1409,7 @@
if (trace() < 0)
exit(1);
+
cleanup();
fflush(NULL);
if (exit_code > 0xff) {