1 /*
2 * Check tracing of orphaned process group.
3 *
4 * Copyright (c) 2019-2020 The strace developers.
5 * All rights reserved.
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "tests.h"
11 #include <signal.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <sys/wait.h>
17
18 #define TIMEOUT 5
19
20 static void
21 alarm_handler(const int no)
22 {
23 error_msg_and_skip("Orphaned process group semantics"
24 " is not supported by the kernel");
25 }
26
27 int
28 main(void)
29 {
30 int status;
31
32 /*
33 * Unblock all signals.
34 */
35 static sigset_t mask;
36 if (sigprocmask(SIG_SETMASK, &mask, NULL))
37 perror_msg_and_fail("sigprocmask");
38
39 /*
40 * Create a pipe to track termination of processes.
41 */
42 int pipe_fds[2];
43 if (pipe(pipe_fds))
44 perror_msg_and_fail("pipe");
45
46 /*
47 * Create a leader for its own new process group.
48 */
49 pid_t leader = fork();
50 if (leader < 0)
51 perror_msg_and_fail("fork");
52
53 if (leader) {
54 /*
55 * Close the writing end of the pipe.
56 */
57 close(pipe_fds[1]);
58
59 /*
60 * Install the SIGALRM signal handler.
61 */
62 static const struct sigaction sa = {
63 .sa_handler = alarm_handler
64 };
65 if (sigaction(SIGALRM, &sa, NULL))
66 perror_msg_and_fail("sigaction");
67
68 /*
69 * Set an alarm clock.
70 */
71 alarm(TIMEOUT);
72
73 /*
74 * Wait for termination of the child process.
75 */
76 if (waitpid(leader, &status, 0) != leader)
77 perror_msg_and_fail("waitpid leader");
78 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
79 error_msg_and_fail("waitpid leader: "
80 "unexpected wait status %d",
81 status);
82
83 /*
84 * Wait for termination of all processes
85 * in the process group of the child process.
86 */
87 if (read(pipe_fds[0], &status, sizeof(status)) != 0)
88 perror_msg_and_fail("read");
89
90 /*
91 * At this point all processes are gone.
92 * Let the tracer time to catch up.
93 */
94 alarm(0);
95 sleep(1);
96 return 0;
97 }
98
99 /*
100 * Close the reading end of the pipe.
101 */
102 close(pipe_fds[0]);
103
104 /*
105 * Create a new process group.
106 */
107 if (setpgid(0, 0))
108 perror_msg_and_fail("setpgid");
109
110 /*
111 * When the leader process terminates, the process group becomes orphaned.
112 * If any member of the orphaned process group is stopped, then
113 * a SIGHUP signal followed by a SIGCONT signal is sent to each process
114 * in the orphaned process group.
115 * Create a process in a stopped state to activate this behaviour.
116 */
117 const pid_t stopped = fork();
118 if (stopped < 0)
119 perror_msg_and_fail("fork");
120 if (!stopped) {
121 static const struct sigaction sa = { .sa_handler = SIG_DFL };
122 if (sigaction(SIGHUP, &sa, NULL))
123 perror_msg_and_fail("sigaction");
124
125 raise(SIGSTOP);
126 _exit(0);
127 }
128
129 /*
130 * Wait for the process to stop.
131 */
132 if (waitpid(stopped, &status, WUNTRACED) != stopped)
133 perror_msg_and_fail("waitpid WUNTRACED");
134 if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
135 error_msg_and_fail("unexpected wait status %d", status);
136
137 /*
138 * Print the expected output.
139 */
140 leader = getpid();
141 printf("%-5d --- %s {si_signo=%s, si_code=SI_TKILL"
142 ", si_pid=%d, si_uid=%d} ---\n",
143 stopped, "SIGSTOP", "SIGSTOP", stopped, geteuid());
144 printf("%-5d --- stopped by SIGSTOP ---\n", stopped);
145 printf("%-5d +++ exited with 0 +++\n", leader);
146 printf("%-5d --- %s {si_signo=%s, si_code=SI_KERNEL} ---\n",
147 stopped, "SIGHUP", "SIGHUP");
148 printf("%-5d +++ killed by %s +++\n", stopped, "SIGHUP");
149 printf("%-5d +++ exited with 0 +++\n", getppid());
150
151 /*
152 * Make the process group orphaned.
153 */
154 return 0;
155 }