xref: /freebsd/sbin/reboot/reboot.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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
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
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 *
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
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
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 		if (unlink(PATH_NEXTBOOT) != 0 && errno != ENOENT)
312 			warn("unlink " PATH_NEXTBOOT);
313 		exit(0);
314 	}
315 
316 	if (!donextboot && geteuid() != 0) {
317 		errno = EPERM;
318 		err(1, NULL);
319 	}
320 
321 	if (qflag) {
322 		reboot(howto);
323 		err(1, NULL);
324 	}
325 
326 	if (kernel != NULL) {
327 		if (!fflag) {
328 			char *k;
329 			struct stat sb;
330 
331 			asprintf(&k, "/boot/%s/kernel", kernel);
332 			if (k == NULL)
333 				errx(1, "No memory to check %s", kernel);
334 			if (stat(k, &sb) != 0)
335 				err(1, "stat %s", k);
336 			if (!S_ISREG(sb.st_mode))
337 				errx(1, "%s is not a file", k);
338 			free(k);
339 		}
340 		add_env(&env, "kernel", kernel);
341 	}
342 
343 	if (env != NULL)
344 		write_nextboot(PATH_NEXTBOOT, env, fflag);
345 	if (donextboot)
346 		exit (0);
347 
348 	/* Log the reboot. */
349 	if (!lflag)  {
350 		if ((user = getlogin()) == NULL)
351 			user = (pw = getpwuid(getuid())) ?
352 			    pw->pw_name : "???";
353 		if (dohalt) {
354 			openlog("halt", 0, LOG_AUTH | LOG_CONS);
355 			syslog(LOG_CRIT, "halted by %s", user);
356 		} else if (howto & RB_REROOT) {
357 			openlog("reroot", 0, LOG_AUTH | LOG_CONS);
358 			syslog(LOG_CRIT, "rerooted by %s", user);
359 		} else if (howto & RB_POWEROFF) {
360 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
361 			syslog(LOG_CRIT, "powered off by %s", user);
362 		} else if (howto & RB_POWERCYCLE) {
363 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
364 			syslog(LOG_CRIT, "power cycled by %s", user);
365 		} else {
366 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
367 			syslog(LOG_CRIT, "rebooted by %s", user);
368 		}
369 	}
370 	utx.ut_type = SHUTDOWN_TIME;
371 	gettimeofday(&utx.ut_tv, NULL);
372 	pututxline(&utx);
373 
374 	/*
375 	 * Do a sync early on, so disks start transfers while we're off
376 	 * killing processes.  Don't worry about writes done before the
377 	 * processes die, the reboot system call syncs the disks.
378 	 */
379 	if (!nflag)
380 		sync();
381 
382 	/*
383 	 * Ignore signals that we can get as a result of killing
384 	 * parents, group leaders, etc.
385 	 */
386 	(void)signal(SIGHUP,  SIG_IGN);
387 	(void)signal(SIGINT,  SIG_IGN);
388 	(void)signal(SIGQUIT, SIG_IGN);
389 	(void)signal(SIGTERM, SIG_IGN);
390 	(void)signal(SIGTSTP, SIG_IGN);
391 
392 	/*
393 	 * If we're running in a pipeline, we don't want to die
394 	 * after killing whatever we're writing to.
395 	 */
396 	(void)signal(SIGPIPE, SIG_IGN);
397 
398 	/*
399 	 * Only init(8) can perform rerooting.
400 	 */
401 	if (howto & RB_REROOT) {
402 		if (kill(1, SIGEMT) == -1)
403 			err(1, "SIGEMT init");
404 
405 		return (0);
406 	}
407 
408 	/* Just stop init -- if we fail, we'll restart it. */
409 	BOOTTRACE("SIGTSTP to init(8)...");
410 	if (kill(1, SIGTSTP) == -1)
411 		err(1, "SIGTSTP init");
412 
413 	/* Send a SIGTERM first, a chance to save the buffers. */
414 	BOOTTRACE("SIGTERM to all other processes...");
415 	if (kill(-1, SIGTERM) == -1 && errno != ESRCH)
416 		err(1, "SIGTERM processes");
417 
418 	/*
419 	 * After the processes receive the signal, start the rest of the
420 	 * buffers on their way.  Wait 5 seconds between the SIGTERM and
421 	 * the SIGKILL to give everybody a chance. If there is a lot of
422 	 * paging activity then wait longer, up to a maximum of approx
423 	 * 60 seconds.
424 	 */
425 	sleep(2);
426 	for (i = 0; i < 20; i++) {
427 		pageins = get_pageins();
428 		if (!nflag)
429 			sync();
430 		sleep(3);
431 		if (get_pageins() == pageins)
432 			break;
433 	}
434 
435 	for (i = 1;; ++i) {
436 		BOOTTRACE("SIGKILL to all other processes(%d)...", i);
437 		if (kill(-1, SIGKILL) == -1) {
438 			if (errno == ESRCH)
439 				break;
440 			goto restart;
441 		}
442 		if (i > 5) {
443 			(void)fprintf(stderr,
444 			    "WARNING: some process(es) wouldn't die\n");
445 			break;
446 		}
447 		(void)sleep(2 * i);
448 	}
449 
450 	reboot(howto);
451 	/* FALLTHROUGH */
452 
453 restart:
454 	BOOTTRACE("SIGHUP to init(8)...");
455 	sverrno = errno;
456 	errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
457 	    strerror(sverrno));
458 	/* NOTREACHED */
459 }
460 
461 static void
462 usage(void)
463 {
464 
465 	(void)fprintf(stderr, dohalt ?
466 	    "usage: halt [-clNnpq] [-k kernel]\n" :
467 	    "usage: reboot [-cdlNnpqr] [-k kernel]\n");
468 	exit(1);
469 }
470 
471 static uint64_t
472 get_pageins(void)
473 {
474 	uint64_t pageins;
475 	size_t len;
476 
477 	len = sizeof(pageins);
478 	if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
479 	    != 0) {
480 		warn("v_swappgsin");
481 		return (0);
482 	}
483 	return (pageins);
484 }
485