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