xref: /freebsd/usr.sbin/bsdinstall/runconsoles/child.c (revision 058ac3e8063366dafa634d9107642e12b038bf09)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Jessica Clarke <jrtc27@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/errno.h>
30 #include <sys/procctl.h>
31 #include <sys/queue.h>
32 #include <sys/resource.h>
33 #include <sys/sysctl.h>
34 #include <sys/wait.h>
35 
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <signal.h>
40 #include <stdarg.h>
41 #include <stdbool.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sysexits.h>
46 #include <termios.h>
47 #include <ttyent.h>
48 #include <unistd.h>
49 
50 #include "common.h"
51 #include "child.h"
52 
53 /* -1: not started, 0: reaped */
54 static volatile pid_t grandchild_pid = -1;
55 static volatile int grandchild_status;
56 
57 static struct pipe_barrier wait_grandchild_barrier;
58 static struct pipe_barrier wait_all_descendants_barrier;
59 
60 static void
61 kill_descendants(int sig)
62 {
63 	struct procctl_reaper_kill rk;
64 
65 	rk.rk_sig = sig;
66 	rk.rk_flags = 0;
67 	procctl(P_PID, getpid(), PROC_REAP_KILL, &rk);
68 }
69 
70 static void
71 sigalrm_handler(int sig __unused)
72 {
73 	int saved_errno;
74 
75 	saved_errno = errno;
76 	kill_descendants(SIGKILL);
77 	errno = saved_errno;
78 }
79 
80 static void
81 wait_all_descendants(void)
82 {
83 	sigset_t set, oset;
84 
85 	err_set_exit(NULL);
86 
87 	/*
88 	 * We may be run in a context where SIGALRM is blocked; temporarily
89 	 * unblock so we can SIGKILL. Similarly, SIGCHLD may be blocked, but if
90 	 * we're waiting on the pipe we need to make sure it's not.
91 	 */
92 	sigemptyset(&set);
93 	sigaddset(&set, SIGALRM);
94 	sigaddset(&set, SIGCHLD);
95 	sigprocmask(SIG_UNBLOCK, &set, &oset);
96 	alarm(KILL_TIMEOUT);
97 	pipe_barrier_wait(&wait_all_descendants_barrier);
98 	alarm(0);
99 	sigprocmask(SIG_SETMASK, &oset, NULL);
100 }
101 
102 static void
103 sigchld_handler(int sig __unused)
104 {
105 	int status, saved_errno;
106 	pid_t pid;
107 
108 	saved_errno = errno;
109 
110 	while ((void)(pid = waitpid(-1, &status, WNOHANG)),
111 	    pid != -1 && pid != 0) {
112 		/* NB: No need to check grandchild_pid due to the pid checks */
113 		if (pid == grandchild_pid) {
114 			grandchild_status = status;
115 			grandchild_pid = 0;
116 			pipe_barrier_ready(&wait_grandchild_barrier);
117 		}
118 	}
119 
120 	/*
121 	 * Another process calling kill(..., SIGCHLD) could cause us to get
122 	 * here before we've spawned the grandchild; only ready when we have no
123 	 * children if the grandchild has been reaped.
124 	 */
125 	if (pid == -1 && errno == ECHILD && grandchild_pid == 0)
126 		pipe_barrier_ready(&wait_all_descendants_barrier);
127 
128 	errno = saved_errno;
129 }
130 
131 static void
132 exit_signal_handler(int sig)
133 {
134 	int saved_errno;
135 
136 	/*
137 	 * If we get killed before we've started the grandchild then just exit
138 	 * with that signal, otherwise kill all our descendants with that
139 	 * signal and let the main program pick up the grandchild's death.
140 	 */
141 	if (grandchild_pid == -1) {
142 		reproduce_signal_death(sig);
143 		exit(EXIT_FAILURE);
144 	}
145 
146 	saved_errno = errno;
147 	kill_descendants(sig);
148 	errno = saved_errno;
149 }
150 
151 static void
152 kill_wait_all_descendants(int sig)
153 {
154 	kill_descendants(sig);
155 	wait_all_descendants();
156 }
157 
158 static void
159 kill_wait_all_descendants_err_exit(int eval __unused)
160 {
161 	kill_wait_all_descendants(SIGTERM);
162 }
163 
164 static void __dead2
165 grandchild_run(const char **argv, const sigset_t *oset)
166 {
167 	sig_t orig;
168 
169 	/* Restore signals */
170 	orig = signal(SIGALRM, SIG_DFL);
171 	if (orig == SIG_ERR)
172 		err(EX_OSERR, "could not restore SIGALRM");
173 	orig = signal(SIGCHLD, SIG_DFL);
174 	if (orig == SIG_ERR)
175 		err(EX_OSERR, "could not restore SIGCHLD");
176 	orig = signal(SIGTERM, SIG_DFL);
177 	if (orig == SIG_ERR)
178 		err(EX_OSERR, "could not restore SIGTERM");
179 	orig = signal(SIGINT, SIG_DFL);
180 	if (orig == SIG_ERR)
181 		err(EX_OSERR, "could not restore SIGINT");
182 	orig = signal(SIGQUIT, SIG_DFL);
183 	if (orig == SIG_ERR)
184 		err(EX_OSERR, "could not restore SIGQUIT");
185 	orig = signal(SIGPIPE, SIG_DFL);
186 	if (orig == SIG_ERR)
187 		err(EX_OSERR, "could not restore SIGPIPE");
188 	orig = signal(SIGTTOU, SIG_DFL);
189 	if (orig == SIG_ERR)
190 		err(EX_OSERR, "could not restore SIGTTOU");
191 
192 	/* Now safe to unmask signals */
193 	sigprocmask(SIG_SETMASK, oset, NULL);
194 
195 	/* Only run with stdin/stdout/stderr */
196 	closefrom(3);
197 
198 	/* Ready to execute the requested program */
199 	execvp(argv[0], __DECONST(char * const *, argv));
200 	err(EX_OSERR, "cannot execvp %s", argv[0]);
201 }
202 
203 static int
204 wait_grandchild_descendants(void)
205 {
206 	pipe_barrier_wait(&wait_grandchild_barrier);
207 
208 	/*
209 	 * Once the grandchild itself has exited, kill any lingering
210 	 * descendants and wait until we've reaped them all.
211 	 */
212 	kill_wait_all_descendants(SIGTERM);
213 
214 	if (grandchild_pid != 0)
215 		errx(EX_SOFTWARE, "failed to reap grandchild");
216 
217 	return (grandchild_status);
218 }
219 
220 void
221 child_leader_run(const char *name, int fd, bool new_session, const char **argv,
222     const sigset_t *oset, struct pipe_barrier *start_children_barrier)
223 {
224 	struct pipe_barrier start_grandchild_barrier;
225 	pid_t pid, sid, pgid;
226 	struct sigaction sa;
227 	int error, status;
228 	sigset_t set;
229 
230 	setproctitle("%s [%s]", getprogname(), name);
231 
232 	error = procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL);
233 	if (error != 0)
234 		err(EX_OSERR, "could not acquire reaper status");
235 
236 	/*
237 	 * Set up our own signal handlers for everything the parent overrides
238 	 * other than SIGPIPE and SIGTTOU which we leave as ignored, since we
239 	 * also use pipe-based synchronisation and want to be able to print
240 	 * errors.
241 	 */
242 	sa.sa_flags = SA_RESTART;
243 	sa.sa_handler = sigchld_handler;
244 	sigfillset(&sa.sa_mask);
245 	error = sigaction(SIGCHLD, &sa, NULL);
246 	if (error != 0)
247 		err(EX_OSERR, "could not enable SIGCHLD handler");
248 	sa.sa_handler = sigalrm_handler;
249 	error = sigaction(SIGALRM, &sa, NULL);
250 	if (error != 0)
251 		err(EX_OSERR, "could not enable SIGALRM handler");
252 	sa.sa_handler = exit_signal_handler;
253 	error = sigaction(SIGTERM, &sa, NULL);
254 	if (error != 0)
255 		err(EX_OSERR, "could not enable SIGTERM handler");
256 	error = sigaction(SIGINT, &sa, NULL);
257 	if (error != 0)
258 		err(EX_OSERR, "could not enable SIGINT handler");
259 	error = sigaction(SIGQUIT, &sa, NULL);
260 	if (error != 0)
261 		err(EX_OSERR, "could not enable SIGQUIT handler");
262 
263 	/*
264 	 * Now safe to unmask signals. Note that creating the barriers used by
265 	 * the SIGCHLD handler with signals unmasked is safe since they won't
266 	 * be used if the grandchild hasn't been forked (and reaped), which
267 	 * comes later.
268 	 */
269 	sigprocmask(SIG_SETMASK, oset, NULL);
270 
271 	error = pipe_barrier_init(&start_grandchild_barrier);
272 	if (error != 0)
273 		err(EX_OSERR, "could not create start grandchild barrier");
274 
275 	error = pipe_barrier_init(&wait_grandchild_barrier);
276 	if (error != 0)
277 		err(EX_OSERR, "could not create wait grandchild barrier");
278 
279 	error = pipe_barrier_init(&wait_all_descendants_barrier);
280 	if (error != 0)
281 		err(EX_OSERR, "could not create wait all descendants barrier");
282 
283 	/*
284 	 * Create a new session if this is on a different terminal to
285 	 * the current one, otherwise just create a new process group to keep
286 	 * things as similar as possible between the two cases.
287 	 */
288 	if (new_session) {
289 		sid = setsid();
290 		pgid = sid;
291 		if (sid == -1)
292 			err(EX_OSERR, "could not create session");
293 	} else {
294 		sid = -1;
295 		pgid = getpid();
296 		error = setpgid(0, pgid);
297 		if (error == -1)
298 			err(EX_OSERR, "could not create process group");
299 	}
300 
301 	/* Wait until parent is ready for us to start */
302 	pipe_barrier_destroy_ready(start_children_barrier);
303 	pipe_barrier_wait(start_children_barrier);
304 
305 	/*
306 	 * Use the console for stdin/stdout/stderr.
307 	 *
308 	 * NB: dup2(2) is a no-op if the two fds are equal, and the call to
309 	 * closefrom(2) later in the grandchild will close the fd if it isn't
310 	 * one of stdin/stdout/stderr already. This means we do not need to
311 	 * handle that special case differently.
312 	 */
313 	error = dup2(fd, STDIN_FILENO);
314 	if (error == -1)
315 		err(EX_IOERR, "could not dup %s to stdin", name);
316 	error = dup2(fd, STDOUT_FILENO);
317 	if (error == -1)
318 		err(EX_IOERR, "could not dup %s to stdout", name);
319 	error = dup2(fd, STDERR_FILENO);
320 	if (error == -1)
321 		err(EX_IOERR, "could not dup %s to stderr", name);
322 
323 	/*
324 	 * If we created a new session, make the console our controlling
325 	 * terminal. Either way, also make this the foreground process group.
326 	 */
327 	if (new_session) {
328 		error = tcsetsid(STDIN_FILENO, sid);
329 		if (error != 0)
330 			err(EX_IOERR, "could not set session for %s", name);
331 	} else {
332 		error = tcsetpgrp(STDIN_FILENO, pgid);
333 		if (error != 0)
334 			err(EX_IOERR, "could not set process group for %s",
335 			    name);
336 	}
337 
338 	/*
339 	 * Temporarily block signals again; forking, setting grandchild_pid and
340 	 * calling err_set_exit need to all be atomic for similar reasons as
341 	 * the parent when forking us.
342 	 */
343 	sigfillset(&set);
344 	sigprocmask(SIG_BLOCK, &set, NULL);
345 	pid = fork();
346 	if (pid == -1)
347 		err(EX_OSERR, "could not fork");
348 
349 	if (pid == 0) {
350 		/*
351 		 * We need to destroy the ready ends so we don't block these
352 		 * child leader-only self-pipes, and might as well destroy the
353 		 * wait ends too given we're not going to use them.
354 		 */
355 		pipe_barrier_destroy(&wait_grandchild_barrier);
356 		pipe_barrier_destroy(&wait_all_descendants_barrier);
357 
358 		/* Wait until the parent has put us in a new process group */
359 		pipe_barrier_destroy_ready(&start_grandchild_barrier);
360 		pipe_barrier_wait(&start_grandchild_barrier);
361 		grandchild_run(argv, oset);
362 	}
363 
364 	grandchild_pid = pid;
365 
366 	/*
367 	 * Now the grandchild exists make sure to clean it up, and any of its
368 	 * descendants, on exit.
369 	 */
370 	err_set_exit(kill_wait_all_descendants_err_exit);
371 
372 	sigprocmask(SIG_SETMASK, oset, NULL);
373 
374 	/* Start the grandchild and wait for it and its descendants to exit */
375 	pipe_barrier_ready(&start_grandchild_barrier);
376 
377 	status = wait_grandchild_descendants();
378 
379 	if (WIFSIGNALED(status))
380 		reproduce_signal_death(WTERMSIG(status));
381 
382 	if (WIFEXITED(status))
383 		exit(WEXITSTATUS(status));
384 
385 	exit(EXIT_FAILURE);
386 }
387