1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/boottrace.h>
33 #include <sys/mount.h>
34 #include <sys/reboot.h>
35 #include <sys/stat.h>
36 #include <sys/sysctl.h>
37 #include <sys/time.h>
38 #include <sys/wait.h>
39
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <pwd.h>
44 #include <signal.h>
45 #include <spawn.h>
46 #include <stdbool.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <syslog.h>
51 #include <unistd.h>
52 #include <utmpx.h>
53
54 extern char **environ;
55
56 #define PATH_NEXTBOOT "/boot/nextboot.conf"
57
58 static void usage(void) __dead2;
59 static uint64_t get_pageins(void);
60
61 static bool dohalt;
62 static bool donextboot;
63
64 #define E(...) do { \
65 if (force) { \
66 warn( __VA_ARGS__ ); \
67 return; \
68 } \
69 err(1, __VA_ARGS__); \
70 } while (0) \
71
72 static void
zfsbootcfg(const char * pool,bool force)73 zfsbootcfg(const char *pool, bool force)
74 {
75 const char * const av[] = {
76 "zfsbootcfg",
77 "-z",
78 pool,
79 "-n",
80 "freebsd:nvstore",
81 "-k",
82 "nextboot_enable",
83 "-v",
84 "YES",
85 NULL
86 };
87 int rv, status;
88 pid_t p;
89
90 rv = posix_spawnp(&p, av[0], NULL, NULL, __DECONST(char **, av),
91 environ);
92 if (rv == -1)
93 E("system zfsbootcfg");
94 if (waitpid(p, &status, WEXITED) < 0) {
95 if (errno == EINTR)
96 return;
97 E("waitpid zfsbootcfg");
98 }
99 if (WIFEXITED(status)) {
100 int e = WEXITSTATUS(status);
101
102 if (e == 0)
103 return;
104 if (e == 127)
105 E("zfsbootcfg not found in path");
106 E("zfsbootcfg returned %d", e);
107 }
108 if (WIFSIGNALED(status))
109 E("zfsbootcfg died with signal %d", WTERMSIG(status));
110 E("zfsbootcfg unexpected status %d", status);
111 }
112
113 static void
write_nextboot(const char * fn,const char * env,bool force)114 write_nextboot(const char *fn, const char *env, bool force)
115 {
116 char tmp[PATH_MAX];
117 FILE *fp;
118 struct statfs sfs;
119 int tmpfd;
120 bool supported = false;
121 bool zfs = false;
122
123 if (statfs("/boot", &sfs) != 0)
124 err(1, "statfs /boot");
125 if (strcmp(sfs.f_fstypename, "ufs") == 0) {
126 /*
127 * Only UFS supports the full nextboot protocol.
128 */
129 supported = true;
130 } else if (strcmp(sfs.f_fstypename, "zfs") == 0) {
131 zfs = true;
132 }
133
134 if (zfs) {
135 char *slash;
136
137 slash = strchr(sfs.f_mntfromname, '/');
138 if (slash != NULL)
139 *slash = '\0';
140 zfsbootcfg(sfs.f_mntfromname, force);
141 }
142
143 if (strlcpy(tmp, fn, sizeof(tmp)) >= sizeof(tmp))
144 E("Path too long %s", fn);
145 if (strlcat(tmp, ".XXXXXX", sizeof(tmp)) >= sizeof(tmp))
146 E("Path too long %s", fn);
147 tmpfd = mkstemp(tmp);
148 if (tmpfd == -1)
149 E("mkstemp %s", tmp);
150
151 fp = fdopen(tmpfd, "w");
152 if (fp == NULL)
153 E("fdopen %s", tmp);
154
155 if (fprintf(fp, "%s%s",
156 supported ? "nextboot_enable=\"YES\"\n" : "",
157 env != NULL ? env : "") < 0) {
158 int e;
159
160 e = errno;
161 if (unlink(tmp))
162 warn("unlink %s", tmp);
163 errno = e;
164 E("Can't write %s", tmp);
165 }
166 if (fsync(fileno(fp)) != 0)
167 E("Can't fsync %s", fn);
168 if (rename(tmp, fn) != 0) {
169 int e;
170
171 e = errno;
172 if (unlink(tmp))
173 warn("unlink %s", tmp);
174 errno = e;
175 E("Can't rename %s to %s", tmp, fn);
176 }
177 fclose(fp);
178 }
179
180 static char *
split_kv(char * raw)181 split_kv(char *raw)
182 {
183 char *eq;
184 int len;
185
186 eq = strchr(raw, '=');
187 if (eq == NULL)
188 errx(1, "No = in environment string %s", raw);
189 *eq++ = '\0';
190 len = strlen(eq);
191 if (len == 0)
192 errx(1, "Invalid null value %s=", raw);
193 if (eq[0] == '"') {
194 if (len < 2 || eq[len - 1] != '"')
195 errx(1, "Invalid string '%s'", eq);
196 eq[len - 1] = '\0';
197 return (eq + 1);
198 }
199 return (eq);
200 }
201
202 static void
add_env(char ** env,const char * key,const char * value)203 add_env(char **env, const char *key, const char *value)
204 {
205 char *oldenv;
206
207 oldenv = *env;
208 asprintf(env, "%s%s=\"%s\"\n", oldenv != NULL ? oldenv : "", key, value);
209 if (env == NULL)
210 errx(1, "No memory to build env array");
211 free(oldenv);
212 }
213
214 /*
215 * Different options are valid for different programs.
216 */
217 #define GETOPT_REBOOT "cDde:fk:lNno:pqr"
218 #define GETOPT_NEXTBOOT "De:fk:o:"
219
220 int
main(int argc,char * argv[])221 main(int argc, char *argv[])
222 {
223 struct utmpx utx;
224 const struct passwd *pw;
225 int ch, howto = 0, i, sverrno;
226 bool Dflag, fflag, lflag, Nflag, nflag, qflag;
227 uint64_t pageins;
228 const char *user, *kernel = NULL, *getopts = GETOPT_REBOOT;
229 char *env = NULL, *v;
230
231 if (strstr(getprogname(), "halt") != NULL) {
232 dohalt = true;
233 howto = RB_HALT;
234 } else if (strcmp(getprogname(), "nextboot") == 0) {
235 donextboot = true;
236 getopts = GETOPT_NEXTBOOT; /* Note: reboot's extra opts return '?' */
237 } else {
238 /* reboot */
239 howto = 0;
240 }
241 Dflag = fflag = lflag = Nflag = nflag = qflag = false;
242 while ((ch = getopt(argc, argv, getopts)) != -1) {
243 switch(ch) {
244 case 'c':
245 howto |= RB_POWERCYCLE;
246 break;
247 case 'D':
248 Dflag = true;
249 break;
250 case 'd':
251 howto |= RB_DUMP;
252 break;
253 case 'e':
254 v = split_kv(optarg);
255 add_env(&env, optarg, v);
256 break;
257 case 'f':
258 fflag = true;
259 break;
260 case 'k':
261 kernel = optarg;
262 break;
263 case 'l':
264 lflag = true;
265 break;
266 case 'n':
267 nflag = true;
268 howto |= RB_NOSYNC;
269 break;
270 case 'N':
271 nflag = true;
272 Nflag = true;
273 break;
274 case 'o':
275 add_env(&env, "kernel_options", optarg);
276 break;
277 case 'p':
278 howto |= RB_POWEROFF;
279 break;
280 case 'q':
281 qflag = true;
282 break;
283 case 'r':
284 howto |= RB_REROOT;
285 break;
286 case '?':
287 default:
288 usage();
289 }
290 }
291
292 argc -= optind;
293 argv += optind;
294 if (argc != 0)
295 usage();
296
297 if (Dflag && ((howto & ~RB_HALT) != 0 || kernel != NULL))
298 errx(1, "cannot delete existing nextboot config and do anything else");
299 if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
300 errx(1, "cannot dump (-d) when halting; must reboot instead");
301 if (Nflag && (howto & RB_NOSYNC) != 0)
302 errx(1, "-N cannot be used with -n");
303 if ((howto & RB_POWEROFF) && (howto & RB_POWERCYCLE))
304 errx(1, "-c and -p cannot be used together");
305 if ((howto & RB_REROOT) != 0 && howto != RB_REROOT)
306 errx(1, "-r cannot be used with -c, -d, -n, or -p");
307 if ((howto & RB_REROOT) != 0 && kernel != NULL)
308 errx(1, "-r and -k cannot be used together, there is no next kernel");
309
310 if (Dflag) {
311 struct stat sb;
312
313 /*
314 * Break the rule about stat then doing
315 * something. When we're booting, there's no
316 * race. When we're a read-only root, though, the
317 * read-only error takes priority over the file not
318 * there error in unlink. So stat it first and exit
319 * with success if it isn't there. Otherwise, let
320 * unlink sort error reporting. POSIX-1.2024 suggests
321 * ENOENT should be preferred to EROFS for unlink,
322 * but FreeBSD historically has preferred EROFS.
323 */
324 if (stat(PATH_NEXTBOOT, &sb) != 0 && errno == ENOENT)
325 exit(0);
326 if (unlink(PATH_NEXTBOOT) != 0)
327 warn("unlink " PATH_NEXTBOOT);
328 exit(0);
329 }
330
331 if (!donextboot && geteuid() != 0) {
332 errno = EPERM;
333 err(1, NULL);
334 }
335
336 if (qflag) {
337 reboot(howto);
338 err(1, NULL);
339 }
340
341 if (kernel != NULL) {
342 if (!fflag) {
343 char *k;
344 struct stat sb;
345
346 asprintf(&k, "/boot/%s/kernel", kernel);
347 if (k == NULL)
348 errx(1, "No memory to check %s", kernel);
349 if (stat(k, &sb) != 0)
350 err(1, "stat %s", k);
351 if (!S_ISREG(sb.st_mode))
352 errx(1, "%s is not a file", k);
353 free(k);
354 }
355 add_env(&env, "kernel", kernel);
356 }
357
358 if (env != NULL)
359 write_nextboot(PATH_NEXTBOOT, env, fflag);
360 if (donextboot)
361 exit (0);
362
363 /* Log the reboot. */
364 if (!lflag) {
365 if ((user = getlogin()) == NULL)
366 user = (pw = getpwuid(getuid())) ?
367 pw->pw_name : "???";
368 if (dohalt) {
369 openlog("halt", 0, LOG_AUTH | LOG_CONS);
370 syslog(LOG_CRIT, "halted by %s", user);
371 } else if (howto & RB_REROOT) {
372 openlog("reroot", 0, LOG_AUTH | LOG_CONS);
373 syslog(LOG_CRIT, "rerooted by %s", user);
374 } else if (howto & RB_POWEROFF) {
375 openlog("reboot", 0, LOG_AUTH | LOG_CONS);
376 syslog(LOG_CRIT, "powered off by %s", user);
377 } else if (howto & RB_POWERCYCLE) {
378 openlog("reboot", 0, LOG_AUTH | LOG_CONS);
379 syslog(LOG_CRIT, "power cycled by %s", user);
380 } else {
381 openlog("reboot", 0, LOG_AUTH | LOG_CONS);
382 syslog(LOG_CRIT, "rebooted by %s", user);
383 }
384 }
385 utx.ut_type = SHUTDOWN_TIME;
386 gettimeofday(&utx.ut_tv, NULL);
387 pututxline(&utx);
388
389 /*
390 * Do a sync early on, so disks start transfers while we're off
391 * killing processes. Don't worry about writes done before the
392 * processes die, the reboot system call syncs the disks.
393 */
394 if (!nflag)
395 sync();
396
397 /*
398 * Ignore signals that we can get as a result of killing
399 * parents, group leaders, etc.
400 */
401 (void)signal(SIGHUP, SIG_IGN);
402 (void)signal(SIGINT, SIG_IGN);
403 (void)signal(SIGQUIT, SIG_IGN);
404 (void)signal(SIGTERM, SIG_IGN);
405 (void)signal(SIGTSTP, SIG_IGN);
406
407 /*
408 * If we're running in a pipeline, we don't want to die
409 * after killing whatever we're writing to.
410 */
411 (void)signal(SIGPIPE, SIG_IGN);
412
413 /*
414 * Only init(8) can perform rerooting.
415 */
416 if (howto & RB_REROOT) {
417 if (kill(1, SIGEMT) == -1)
418 err(1, "SIGEMT init");
419
420 return (0);
421 }
422
423 /* Just stop init -- if we fail, we'll restart it. */
424 BOOTTRACE("SIGTSTP to init(8)...");
425 if (kill(1, SIGTSTP) == -1)
426 err(1, "SIGTSTP init");
427
428 /* Send a SIGTERM first, a chance to save the buffers. */
429 BOOTTRACE("SIGTERM to all other processes...");
430 if (kill(-1, SIGTERM) == -1 && errno != ESRCH)
431 err(1, "SIGTERM processes");
432
433 /*
434 * After the processes receive the signal, start the rest of the
435 * buffers on their way. Wait 5 seconds between the SIGTERM and
436 * the SIGKILL to give everybody a chance. If there is a lot of
437 * paging activity then wait longer, up to a maximum of approx
438 * 60 seconds.
439 */
440 sleep(2);
441 for (i = 0; i < 20; i++) {
442 pageins = get_pageins();
443 if (!nflag)
444 sync();
445 sleep(3);
446 if (get_pageins() == pageins)
447 break;
448 }
449
450 for (i = 1;; ++i) {
451 BOOTTRACE("SIGKILL to all other processes(%d)...", i);
452 if (kill(-1, SIGKILL) == -1) {
453 if (errno == ESRCH)
454 break;
455 goto restart;
456 }
457 if (i > 5) {
458 (void)fprintf(stderr,
459 "WARNING: some process(es) wouldn't die\n");
460 break;
461 }
462 (void)sleep(2 * i);
463 }
464
465 reboot(howto);
466 /* FALLTHROUGH */
467
468 restart:
469 BOOTTRACE("SIGHUP to init(8)...");
470 sverrno = errno;
471 errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
472 strerror(sverrno));
473 /* NOTREACHED */
474 }
475
476 static void
usage(void)477 usage(void)
478 {
479
480 (void)fprintf(stderr, dohalt ?
481 "usage: halt [-clNnpq] [-k kernel]\n" :
482 "usage: reboot [-cdlNnpqr] [-k kernel]\n");
483 exit(1);
484 }
485
486 static uint64_t
get_pageins(void)487 get_pageins(void)
488 {
489 uint64_t pageins;
490 size_t len;
491
492 len = sizeof(pageins);
493 if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
494 != 0) {
495 warn("v_swappgsin");
496 return (0);
497 }
498 return (pageins);
499 }
500