xref: /freebsd/sbin/reboot/reboot.c (revision 9461071d5cd901c968dcd7f4a114905995f90d66)
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