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 <limits.h>
44 #include <paths.h>
45 #include <pwd.h>
46 #include <signal.h>
47 #include <spawn.h>
48 #include <stdbool.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <syslog.h>
53 #include <unistd.h>
54 #include <utmpx.h>
55
56 extern char **environ;
57
58 #define PATH_NEXTBOOT "/boot/nextboot.conf"
59
60 static void usage(void) __dead2;
61 static uint64_t get_pageins(void);
62
63 static bool dofast;
64 static bool dohalt;
65 static bool donextboot;
66
67 #define E(...) do { \
68 if (force) { \
69 warn( __VA_ARGS__ ); \
70 return; \
71 } \
72 err(1, __VA_ARGS__); \
73 } while (0) \
74
75 static void
zfsbootcfg(const char * pool,bool force)76 zfsbootcfg(const char *pool, bool force)
77 {
78 const char * const av[] = {
79 "zfsbootcfg",
80 "-z",
81 pool,
82 "-n",
83 "freebsd:nvstore",
84 "-k",
85 "nextboot_enable",
86 "-v",
87 "YES",
88 NULL
89 };
90 int rv, status;
91 pid_t p;
92
93 rv = posix_spawnp(&p, av[0], NULL, NULL, __DECONST(char **, av),
94 environ);
95 if (rv == -1)
96 E("system zfsbootcfg");
97 if (waitpid(p, &status, WEXITED) < 0) {
98 if (errno == EINTR)
99 return;
100 E("waitpid zfsbootcfg");
101 }
102 if (WIFEXITED(status)) {
103 int e = WEXITSTATUS(status);
104
105 if (e == 0)
106 return;
107 if (e == 127)
108 E("zfsbootcfg not found in path");
109 E("zfsbootcfg returned %d", e);
110 }
111 if (WIFSIGNALED(status))
112 E("zfsbootcfg died with signal %d", WTERMSIG(status));
113 E("zfsbootcfg unexpected status %d", status);
114 }
115
116 static void
write_nextboot(const char * fn,const char * env,bool append,bool force)117 write_nextboot(const char *fn, const char *env, bool append, bool force)
118 {
119 char tmp[PATH_MAX];
120 FILE *fp;
121 struct statfs sfs;
122 ssize_t ret;
123 int fd, tmpfd;
124 bool supported = false;
125 bool zfs = false;
126
127 if (statfs("/boot", &sfs) != 0)
128 err(1, "statfs /boot");
129 if (strcmp(sfs.f_fstypename, "ufs") == 0) {
130 /*
131 * Only UFS supports the full nextboot protocol.
132 */
133 supported = true;
134 } else if (strcmp(sfs.f_fstypename, "zfs") == 0) {
135 zfs = true;
136 }
137
138 if (zfs) {
139 char *slash;
140
141 slash = strchr(sfs.f_mntfromname, '/');
142 if (slash != NULL)
143 *slash = '\0';
144 zfsbootcfg(sfs.f_mntfromname, force);
145 }
146
147 if (strlcpy(tmp, fn, sizeof(tmp)) >= sizeof(tmp))
148 E("Path too long %s", fn);
149 if (strlcat(tmp, ".XXXXXX", sizeof(tmp)) >= sizeof(tmp))
150 E("Path too long %s", fn);
151
152 tmpfd = mkstemp(tmp);
153 if (tmpfd == -1)
154 E("mkstemp %s", tmp);
155
156 fp = fdopen(tmpfd, "w");
157 if (fp == NULL)
158 E("fdopen %s", tmp);
159
160 if (append) {
161 if ((fd = open(fn, O_RDONLY)) < 0) {
162 if (errno != ENOENT)
163 E("open %s", fn);
164 } else {
165 do {
166 ret = copy_file_range(fd, NULL, tmpfd, NULL,
167 SSIZE_MAX, 0);
168 if (ret < 0)
169 E("copy %s to %s", fn, tmp);
170 } while (ret > 0);
171 close(fd);
172 }
173 }
174
175 if (fprintf(fp, "%s%s",
176 supported ? "nextboot_enable=\"YES\"\n" : "",
177 env != NULL ? env : "") < 0) {
178 int e;
179
180 e = errno;
181 if (unlink(tmp))
182 warn("unlink %s", tmp);
183 errno = e;
184 E("Can't write %s", tmp);
185 }
186 if (fsync(fileno(fp)) != 0)
187 E("Can't fsync %s", fn);
188 if (rename(tmp, fn) != 0) {
189 int e;
190
191 e = errno;
192 if (unlink(tmp))
193 warn("unlink %s", tmp);
194 errno = e;
195 E("Can't rename %s to %s", tmp, fn);
196 }
197 fclose(fp);
198 }
199
200 static char *
split_kv(char * raw)201 split_kv(char *raw)
202 {
203 char *eq;
204 int len;
205
206 eq = strchr(raw, '=');
207 if (eq == NULL)
208 errx(1, "No = in environment string %s", raw);
209 *eq++ = '\0';
210 len = strlen(eq);
211 if (len == 0)
212 errx(1, "Invalid null value %s=", raw);
213 if (eq[0] == '"') {
214 if (len < 2 || eq[len - 1] != '"')
215 errx(1, "Invalid string '%s'", eq);
216 eq[len - 1] = '\0';
217 return (eq + 1);
218 }
219 return (eq);
220 }
221
222 static void
add_env(char ** env,const char * key,const char * value)223 add_env(char **env, const char *key, const char *value)
224 {
225 char *oldenv;
226
227 oldenv = *env;
228 asprintf(env, "%s%s=\"%s\"\n", oldenv != NULL ? oldenv : "", key, value);
229 if (env == NULL)
230 errx(1, "No memory to build env array");
231 free(oldenv);
232 }
233
234 static void
shutdown(int howto)235 shutdown(int howto)
236 {
237 char sigstr[SIG2STR_MAX];
238 int signo =
239 howto & RB_POWERCYCLE ? SIGWINCH :
240 howto & RB_POWEROFF ? SIGUSR2 :
241 howto & RB_HALT ? SIGUSR1 :
242 howto & RB_REROOT ? SIGEMT :
243 SIGINT;
244
245 (void)sig2str(signo, sigstr);
246 BOOTTRACE("SIG%s to init(8)...", sigstr);
247 if (kill(1, signo) == -1)
248 err(1, "SIG%s init", sigstr);
249 exit(0);
250 }
251
252 /*
253 * Different options are valid for different programs.
254 */
255 #define GETOPT_REBOOT "cDde:fk:lNno:pqr"
256 #define GETOPT_NEXTBOOT "aDe:fk:o:"
257
258 int
main(int argc,char * argv[])259 main(int argc, char *argv[])
260 {
261 struct utmpx utx;
262 struct stat st;
263 const struct passwd *pw;
264 const char *progname, *user;
265 const char *kernel = NULL, *getopts = GETOPT_REBOOT;
266 char *env = NULL, *v;
267 uint64_t pageins;
268 int ch, howto = 0, i, sverrno;
269 bool aflag, Dflag, fflag, lflag, Nflag, nflag, qflag;
270
271 progname = getprogname();
272 if (strncmp(progname, "fast", 4) == 0) {
273 dofast = true;
274 progname += 4;
275 }
276 if (strcmp(progname, "halt") == 0) {
277 dohalt = true;
278 howto = RB_HALT;
279 } else if (strcmp(progname, "nextboot") == 0) {
280 donextboot = true;
281 getopts = GETOPT_NEXTBOOT; /* Note: reboot's extra opts return '?' */
282 } else {
283 /* reboot */
284 howto = 0;
285 }
286 aflag = Dflag = fflag = lflag = Nflag = nflag = qflag = false;
287 while ((ch = getopt(argc, argv, getopts)) != -1) {
288 switch(ch) {
289 case 'a':
290 aflag = true;
291 break;
292 case 'c':
293 howto |= RB_POWERCYCLE;
294 break;
295 case 'D':
296 Dflag = true;
297 break;
298 case 'd':
299 howto |= RB_DUMP;
300 break;
301 case 'e':
302 v = split_kv(optarg);
303 add_env(&env, optarg, v);
304 break;
305 case 'f':
306 fflag = true;
307 break;
308 case 'k':
309 kernel = optarg;
310 break;
311 case 'l':
312 lflag = true;
313 break;
314 case 'n':
315 nflag = true;
316 howto |= RB_NOSYNC;
317 break;
318 case 'N':
319 nflag = true;
320 Nflag = true;
321 break;
322 case 'o':
323 add_env(&env, "kernel_options", optarg);
324 break;
325 case 'p':
326 howto |= RB_POWEROFF;
327 break;
328 case 'q':
329 qflag = true;
330 break;
331 case 'r':
332 howto |= RB_REROOT;
333 break;
334 case '?':
335 default:
336 usage();
337 }
338 }
339
340 argc -= optind;
341 argv += optind;
342 if (argc != 0)
343 usage();
344
345 if (!donextboot && !fflag && stat(_PATH_NOSHUTDOWN, &st) == 0) {
346 errx(1, "Reboot cannot be done, " _PATH_NOSHUTDOWN
347 " is present");
348 }
349
350 if (Dflag && ((howto & ~RB_HALT) != 0 || kernel != NULL))
351 errx(1, "cannot delete existing nextboot config and do anything else");
352 if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
353 errx(1, "cannot dump (-d) when halting; must reboot instead");
354 if (Nflag && (howto & RB_NOSYNC) != 0)
355 errx(1, "-N cannot be used with -n");
356 if ((howto & RB_POWEROFF) && (howto & RB_POWERCYCLE))
357 errx(1, "-c and -p cannot be used together");
358 if ((howto & RB_REROOT) != 0 && howto != RB_REROOT)
359 errx(1, "-r cannot be used with -c, -d, -n, or -p");
360 if ((howto & RB_REROOT) != 0 && dofast)
361 errx(1, "-r cannot be performed in fast mode");
362 if ((howto & RB_REROOT) != 0 && kernel != NULL)
363 errx(1, "-r and -k cannot be used together, there is no next kernel");
364
365 if (Dflag) {
366 struct stat sb;
367
368 /*
369 * Break the rule about stat then doing
370 * something. When we're booting, there's no
371 * race. When we're a read-only root, though, the
372 * read-only error takes priority over the file not
373 * there error in unlink. So stat it first and exit
374 * with success if it isn't there. Otherwise, let
375 * unlink sort error reporting. POSIX-1.2024 suggests
376 * ENOENT should be preferred to EROFS for unlink,
377 * but FreeBSD historically has preferred EROFS.
378 */
379 if (stat(PATH_NEXTBOOT, &sb) != 0 && errno == ENOENT)
380 exit(0);
381 if (unlink(PATH_NEXTBOOT) != 0)
382 warn("unlink " PATH_NEXTBOOT);
383 exit(0);
384 }
385
386 if (!donextboot && geteuid() != 0) {
387 errno = EPERM;
388 err(1, NULL);
389 }
390
391 if (qflag) {
392 reboot(howto);
393 err(1, NULL);
394 }
395
396 if (kernel != NULL) {
397 if (!fflag) {
398 char *k;
399 struct stat sb;
400
401 asprintf(&k, "/boot/%s/kernel", kernel);
402 if (k == NULL)
403 errx(1, "No memory to check %s", kernel);
404 if (stat(k, &sb) != 0)
405 err(1, "stat %s", k);
406 if (!S_ISREG(sb.st_mode))
407 errx(1, "%s is not a file", k);
408 free(k);
409 }
410 add_env(&env, "kernel", kernel);
411 }
412
413 if (env != NULL)
414 write_nextboot(PATH_NEXTBOOT, env, aflag, fflag);
415 if (donextboot)
416 exit (0);
417
418 /* Log the reboot. */
419 if (!lflag) {
420 if ((user = getlogin()) == NULL)
421 user = (pw = getpwuid(getuid())) ?
422 pw->pw_name : "???";
423 if (dohalt) {
424 openlog("halt", 0, LOG_AUTH | LOG_CONS);
425 syslog(LOG_CRIT, "halted by %s", user);
426 } else if (howto & RB_REROOT) {
427 openlog("reroot", 0, LOG_AUTH | LOG_CONS);
428 syslog(LOG_CRIT, "rerooted by %s", user);
429 } else if (howto & RB_POWEROFF) {
430 openlog("reboot", 0, LOG_AUTH | LOG_CONS);
431 syslog(LOG_CRIT, "powered off by %s", user);
432 } else if (howto & RB_POWERCYCLE) {
433 openlog("reboot", 0, LOG_AUTH | LOG_CONS);
434 syslog(LOG_CRIT, "power cycled by %s", user);
435 } else {
436 openlog("reboot", 0, LOG_AUTH | LOG_CONS);
437 syslog(LOG_CRIT, "rebooted by %s", user);
438 }
439 }
440 utx.ut_type = SHUTDOWN_TIME;
441 gettimeofday(&utx.ut_tv, NULL);
442 pututxline(&utx);
443
444 /*
445 * Do a sync early on, so disks start transfers while we're off
446 * killing processes. Don't worry about writes done before the
447 * processes die, the reboot system call syncs the disks.
448 */
449 if (!nflag)
450 sync();
451
452 /*
453 * Ignore signals that we can get as a result of killing
454 * parents, group leaders, etc.
455 */
456 (void)signal(SIGHUP, SIG_IGN);
457 (void)signal(SIGINT, SIG_IGN);
458 (void)signal(SIGQUIT, SIG_IGN);
459 (void)signal(SIGTERM, SIG_IGN);
460 (void)signal(SIGTSTP, SIG_IGN);
461
462 /*
463 * If we're running in a pipeline, we don't want to die
464 * after killing whatever we're writing to.
465 */
466 (void)signal(SIGPIPE, SIG_IGN);
467
468 /*
469 * Common case: clean shutdown.
470 */
471 if (!dofast)
472 shutdown(howto);
473
474 /* Just stop init -- if we fail, we'll restart it. */
475 BOOTTRACE("SIGTSTP to init(8)...");
476 if (kill(1, SIGTSTP) == -1)
477 err(1, "SIGTSTP init");
478
479 /* Send a SIGTERM first, a chance to save the buffers. */
480 BOOTTRACE("SIGTERM to all other processes...");
481 if (kill(-1, SIGTERM) == -1 && errno != ESRCH)
482 err(1, "SIGTERM processes");
483
484 /*
485 * After the processes receive the signal, start the rest of the
486 * buffers on their way. Wait 5 seconds between the SIGTERM and
487 * the SIGKILL to give everybody a chance. If there is a lot of
488 * paging activity then wait longer, up to a maximum of approx
489 * 60 seconds.
490 */
491 sleep(2);
492 for (i = 0; i < 20; i++) {
493 pageins = get_pageins();
494 if (!nflag)
495 sync();
496 sleep(3);
497 if (get_pageins() == pageins)
498 break;
499 }
500
501 for (i = 1;; ++i) {
502 BOOTTRACE("SIGKILL to all other processes(%d)...", i);
503 if (kill(-1, SIGKILL) == -1) {
504 if (errno == ESRCH)
505 break;
506 goto restart;
507 }
508 if (i > 5) {
509 (void)fprintf(stderr,
510 "WARNING: some process(es) wouldn't die\n");
511 break;
512 }
513 (void)sleep(2 * i);
514 }
515
516 reboot(howto);
517 /* FALLTHROUGH */
518
519 restart:
520 BOOTTRACE("SIGHUP to init(8)...");
521 sverrno = errno;
522 errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
523 strerror(sverrno));
524 /* NOTREACHED */
525 }
526
527 static void
usage(void)528 usage(void)
529 {
530 if (donextboot) {
531 fprintf(stderr, "usage: nextboot [-aDf] "
532 "[-e name=value] [-k kernel] [-o options]\n");
533 } else {
534 fprintf(stderr, "usage: %s%s [-%sflNnpq%s] "
535 "[-e name=value] [-k kernel] [-o options]\n",
536 dofast ? "fast" : "",
537 dohalt ? "halt" : dofast ? "boot" : "reboot",
538 dohalt ? "D" : "cDd",
539 dohalt || dofast ? "" : "r");
540 }
541 exit(1);
542 }
543
544 static uint64_t
get_pageins(void)545 get_pageins(void)
546 {
547 uint64_t pageins;
548 size_t len;
549
550 len = sizeof(pageins);
551 if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
552 != 0) {
553 warn("v_swappgsin");
554 return (0);
555 }
556 return (pageins);
557 }
558