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