Browse Source

strace: expand -D option

As of now, despite of being stated that -D option runs strace as a "detached"
grandchild (and the option name being named after "daemon"), strace
still runs in the same process group and session, thus not being
"detached" in a common sense and being subjected to process group kill
and session termination kill. Quoting[1]:

    I stumble upon unexpected behavior: if strace is used with option '-D'
    (tracer as a detached grandchild) and process (leader) kills whole
    process group, it will kill strace too.

    It can be easily reproduced by `timeout` from "coreutils":

      # timeout -s KILL 2 strace -D -o ./strace-inside.log /bin/sleep 10 &

    Here we can see, that `strace` didn't finished its output (because it
    was killed):

      # tail -n 1 ./strace-inside.log
      nanosleep({tv_sec=10, tv_nsec=0},

    If `timeout` is not run in '--foreground' mode, it changes process group
    and after "timeout" it sends two kills:

      setpgid(0, 0)                           = 0
      kill(37337, SIGKILL)                    = 0
      kill(0, SIGKILL)                        = ?

    The first kill is for the `sleep` and the second one is for the process
    group (which is `strace` part of). PIDs and their relations are:

      timeout  pid=30595   [ppid=476   bash   ]     pgrp=30595
      sleep    pid=37337   [ppid=30595 timeout]     pgrp=30595
      strace   pid=30603   [ppid=1     systemd]     pgrp=30595

    Here is "strace log" of `strace` inside `timeout`:

      strace: Process 30603 attached
      wait4(-1,  <unfinished ...>)            = ?
      +++ killed by SIGKILL +++

    I think that detached `strace` should not be killed like that -- it
    should not be part of former grandparents' "job pipeline".

While this behaviour is not exactly intuitive, it is implemented this
way for quite some time, so it might be relied upon by some of strace
users.  In order to address this issue, two new levels of
"daemonisation" are added, that put strace in a separate process group
and session, respectively.

[1] https://lists.strace.io/pipermail/strace-devel/2019-October/009160.html

* strace.1.in (.SH SYNOPSIS): Update.
(.SS Tracing): Document -DD and -DDD.
* strace.c (DAEMONIZE_NONE, DAEMONIZE_GRANDCHILD, DAEMONIZE_NEW_PGROUP,
* DAEMONIZE_NEW_SESSION, DAEMONIZE_OPTS_GUARD__, MAX_DAEMONIZE_OPTS):
* New enumeration entities.
(daemonized_tracer): Change type to unsigned int.
(usage): Document -DD and -DDD.
(startup_attach) <daemonized_tracer == DAEMONIZE_NEW_PGROUP>: Call
setpgid.
<daemonized_tracer == DAEMONIZE_NEW_SESSION>: Call setsid.
(init) <case 'D'>: Increase daemonized_tracer instead of setting to 1.
(init): Bail out if too many -D's are given.
* NEWS: Mention this improvement.
* tests/options-syntax.test: Add checks for -D option usage.

Co-authored-by: Eugene Syromyatnikov <evgsyr@gmail.com>
Co-authored-by: Dmitry V. Levin <ldv@altlinux.org>
Fanda Uchytil 1 month ago
parent
commit
1f58235b13
4 changed files with 72 additions and 8 deletions
  1. 4
    0
      NEWS
  2. 22
    3
      strace.1.in
  3. 42
    5
      strace.c
  4. 4
    0
      tests/options-syntax.test

+ 4
- 0
NEWS View File

@@ -1,6 +1,10 @@
1 1
 Noteworthy changes in release ?.? (????-??-??)
2 2
 ==============================================
3 3
 
4
+* Improvements
5
+  * Implemented -DD and -DDD options that move strace into a separate
6
+    process group and session, respectively.
7
+
4 8
 * Bug fixes
5 9
   * Fixed -b execve when --seccomp-bpf option is specified.
6 10
   * Fixed build on no-MMU architectures.

+ 22
- 3
strace.1.in View File

@@ -53,7 +53,7 @@ strace \- trace system calls and signals
53 53
 .BR "" {
54 54
 .OR \-p pid
55 55
 .BR "" |
56
-.OP \-D
56
+.OP \-DDD
57 57
 .OM \-E var\fR[=\fIval\fR]
58 58
 .OP \-u username
59 59
 .IR command " [" args ]
@@ -73,7 +73,7 @@ strace \- trace system calls and signals
73 73
 .BR "" {
74 74
 .OR \-p pid
75 75
 .BR "" |
76
-.OP \-D
76
+.OP \-DDD
77 77
 .OM \-E var\fR[=\fIval\fR]
78 78
 .OP -u username
79 79
 .IR command " [" args ]
@@ -338,11 +338,30 @@ multi-threaded process and therefore require
338 338
 but don't want to trace its (potentially very complex) children.
339 339
 .TP
340 340
 .B \-D
341
-Run tracer process as a detached grandchild, not as parent of the
341
+Run tracer process as a grandchild, not as the parent of the
342 342
 tracee.  This reduces the visible effect of
343 343
 .B strace
344 344
 by keeping the tracee a direct child of the calling process.
345 345
 .TP
346
+.B \-DD
347
+Run tracer process as tracee's grandchild in a separate process group.
348
+In addition to reduction of the visible effect of
349
+.BR strace ,
350
+it also avoids killing of
351
+.B strace
352
+with
353
+.BR kill (2)
354
+issued to the whole process group.
355
+.TP
356
+.B \-DDD
357
+Run tracer process as tracee's grandchild in a separate session
358
+("true daemonisation").
359
+In addition to reduction of the visible effect of
360
+.BR strace ,
361
+it also avoids killing of
362
+.B strace
363
+upon session termination.
364
+.TP
346 365
 .B \-f
347 366
 Trace child processes as they are created by currently traced
348 367
 processes as a result of the

+ 42
- 5
strace.c View File

@@ -93,6 +93,15 @@ static int opt_intr;
93 93
 /* We play with signal mask only if this mode is active: */
94 94
 #define interactive (opt_intr == INTR_WHILE_WAIT)
95 95
 
96
+enum {
97
+	DAEMONIZE_NONE        = 0,
98
+	DAEMONIZE_GRANDCHILD  = 1,
99
+	DAEMONIZE_NEW_PGROUP  = 2,
100
+	DAEMONIZE_NEW_SESSION = 3,
101
+
102
+	DAEMONIZE_OPTS_GUARD__,
103
+	MAX_DAEMONIZE_OPTS    = DAEMONIZE_OPTS_GUARD__ - 1
104
+};
96 105
 /*
97 106
  * daemonized_tracer supports -D option.
98 107
  * With this option, strace forks twice.
@@ -105,7 +114,7 @@ static int opt_intr;
105 114
  * wait() etc. Without -D, strace process gets lodged in between,
106 115
  * disrupting parent<->child link.
107 116
  */
108
-static bool daemonized_tracer;
117
+static unsigned int daemonized_tracer;
109 118
 
110 119
 static int post_attach_sigstop = TCB_IGNORE_ONE_SIGSTOP;
111 120
 #define use_seize (post_attach_sigstop == 0)
@@ -241,10 +250,10 @@ usage(void)
241 250
 usage: strace [-ACdffhi" K_OPT "qqrtttTvVwxxyyzZ] [-I n] [-b execve] [-e expr]...\n\
242 251
               [-a column] [-o file] [-s strsize] [-X format] [-P path]...\n\
243 252
               [-p pid]... [--seccomp-bpf]\n\
244
-	      { -p pid | [-D] [-E var=val]... [-u username] PROG [ARGS] }\n\
253
+              { -p pid | [-DDD] [-E var=val]... [-u username] PROG [ARGS] }\n\
245 254
    or: strace -c[dfwzZ] [-I n] [-b execve] [-e expr]... [-O overhead]\n\
246 255
               [-S sortby] [-P path]... [-p pid]... [--seccomp-bpf]\n\
247
-              { -p pid | [-D] [-E var=val]... [-u username] PROG [ARGS] }\n\
256
+              { -p pid | [-DDD] [-E var=val]... [-u username] PROG [ARGS] }\n\
248 257
 \n\
249 258
 Output format:\n\
250 259
   -A             open the file provided in the -o option in append mode\n\
@@ -292,7 +301,9 @@ Filtering:\n\
292 301
 \n\
293 302
 Tracing:\n\
294 303
   -b execve      detach on execve syscall\n\
295
-  -D             run tracer process as a detached grandchild, not as parent\n\
304
+  -D             run tracer process as a grandchild, not as a parent\n\
305
+  -DD            run tracer process in a separate process group\n\
306
+  -DDD           run tracer process in a separate session\n\
296 307
   -f             follow forks\n\
297 308
   -ff            follow forks with output into separate files\n\
298 309
   -I interruptible\n\
@@ -1133,6 +1144,27 @@ startup_attach(void)
1133 1144
 		/* grandchild */
1134 1145
 		/* We will be the tracer process. Remember our new pid: */
1135 1146
 		strace_tracer_pid = getpid();
1147
+
1148
+		switch (daemonized_tracer) {
1149
+		case DAEMONIZE_NEW_PGROUP:
1150
+			/*
1151
+			 * If -D is passed twice, create a new process group,
1152
+			 * so we won't be killed by kill(0, ...).
1153
+			 */
1154
+			if (setpgid(0, 0) < 0)
1155
+				perror_msg_and_die("Cannot create a new"
1156
+						   " process group");
1157
+			break;
1158
+		case DAEMONIZE_NEW_SESSION:
1159
+			/*
1160
+			 * If -D is passed thrice, create a new session,
1161
+			 * so we won't be killed upon session termination.
1162
+			 */
1163
+			if (setsid() < 0)
1164
+				perror_msg_and_die("Cannot create a new"
1165
+						   " session");
1166
+			break;
1167
+		}
1136 1168
 	}
1137 1169
 
1138 1170
 	for (tcbi = 0; tcbi < tcbtabsize; tcbi++) {
@@ -1662,7 +1694,7 @@ init(int argc, char *argv[])
1662 1694
 			debug_flag = 1;
1663 1695
 			break;
1664 1696
 		case 'D':
1665
-			daemonized_tracer = 1;
1697
+			daemonized_tracer++;
1666 1698
 			break;
1667 1699
 		case 'e':
1668 1700
 			qualify(optarg);
@@ -1786,6 +1818,11 @@ init(int argc, char *argv[])
1786 1818
 		error_msg_and_help("PROG [ARGS] must be specified with -D");
1787 1819
 	}
1788 1820
 
1821
+	if (daemonized_tracer > (unsigned int) MAX_DAEMONIZE_OPTS)
1822
+		error_msg_and_help("Too many -D's (%u), maximum supported -D "
1823
+				   "count is %d",
1824
+				   daemonized_tracer, MAX_DAEMONIZE_OPTS);
1825
+
1789 1826
 	if (seccomp_filtering && detach_on_execve) {
1790 1827
 		error_msg("--seccomp-bpf is not enabled because"
1791 1828
 			  " it is not compatible with -b");

+ 4
- 0
tests/options-syntax.test View File

@@ -24,6 +24,10 @@ check_e_using_grep "$ff_name: File *name too long" -ff -o "$ff_name" true
24 24
 
25 25
 check_h 'must have PROG [ARGS] or -p PID'
26 26
 check_h 'PROG [ARGS] must be specified with -D' -D -p $$
27
+check_h 'PROG [ARGS] must be specified with -D' -DD -p $$
28
+check_h 'PROG [ARGS] must be specified with -D' -DDD -p $$
29
+check_h 'PROG [ARGS] must be specified with -D' -DDDD -p $$
30
+check_h 'Too many -D'\''s (4), maximum supported -D count is 3' -DDDD /bin/true
27 31
 check_h '-c and -C are mutually exclusive' -c -C true
28 32
 check_h '-c and -C are mutually exclusive' -C -c true
29 33
 check_h '(-c or -C) and -ff are mutually exclusive' -c -ff true

Loading…
Cancel
Save