Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bsdinstall/runconsoles/child.c
105686 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2022 Jessica Clarke <[email protected]>
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
388