xref: /freebsd/sbin/reboot/reboot.c (revision 783d3ff6d7fae619db8a7990b8a6387de0c677b5)
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 	FILE *fp;
117 	struct statfs sfs;
118 	bool supported = false;
119 	bool zfs = false;
120 
121 	if (statfs("/boot", &sfs) != 0)
122 		err(1, "statfs /boot");
123 	if (strcmp(sfs.f_fstypename, "ufs") == 0) {
124 		/*
125 		 * Only UFS supports the full nextboot protocol.
126 		 */
127 		supported = true;
128 	} else if (strcmp(sfs.f_fstypename, "zfs") == 0) {
129 		zfs = true;
130 	}
131 
132 	if (zfs) {
133 		char *slash;
134 
135 		if ((slash = strchr(sfs.f_mntfromname, '/')) == NULL)
136 			E("Can't find ZFS pool name in %s", sfs.f_mntfromname);
137 		*slash = '\0';
138 		zfsbootcfg(sfs.f_mntfromname, force);
139 	}
140 
141 	fp = fopen(fn, "w");
142 	if (fp == NULL)
143 		E("Can't create %s", fn);
144 
145 	if (fprintf(fp,"%s%s",
146 	    supported ? "nextboot_enable=\"YES\"\n" : "",
147 	    env != NULL ? env : "") < 0) {
148 		int e;
149 
150 		e = errno;
151 		fclose(fp);
152 		if (unlink(fn))
153 			warn("unlink %s", fn);
154 		errno = e;
155 		E("Can't write %s", fn);
156 	}
157 	fclose(fp);
158 }
159 
160 static char *
161 split_kv(char *raw)
162 {
163 	char *eq;
164 	int len;
165 
166 	eq = strchr(raw, '=');
167 	if (eq == NULL)
168 		errx(1, "No = in environment string %s", raw);
169 	*eq++ = '\0';
170 	len = strlen(eq);
171 	if (len == 0)
172 		errx(1, "Invalid null value %s=", raw);
173 	if (eq[0] == '"') {
174 		if (len < 2 || eq[len - 1] != '"')
175 			errx(1, "Invalid string '%s'", eq);
176 		eq[len - 1] = '\0';
177 		return (eq + 1);
178 	}
179 	return (eq);
180 }
181 
182 static void
183 add_env(char **env, const char *key, const char *value)
184 {
185 	char *oldenv;
186 
187 	oldenv = *env;
188 	asprintf(env, "%s%s=\"%s\"\n", oldenv != NULL ? oldenv : "", key, value);
189 	if (env == NULL)
190 		errx(1, "No memory to build env array");
191 	free(oldenv);
192 }
193 
194 /*
195  * Different options are valid for different programs.
196  */
197 #define GETOPT_REBOOT "cDde:k:lNno:pqr"
198 #define GETOPT_NEXTBOOT "De:k:o:"
199 
200 int
201 main(int argc, char *argv[])
202 {
203 	struct utmpx utx;
204 	const struct passwd *pw;
205 	int ch, howto = 0, i, sverrno;
206 	bool Dflag, fflag, lflag, Nflag, nflag, qflag;
207 	uint64_t pageins;
208 	const char *user, *kernel = NULL, *getopts = GETOPT_REBOOT;
209 	char *env = NULL, *v;
210 
211 	if (strstr(getprogname(), "halt") != NULL) {
212 		dohalt = true;
213 		howto = RB_HALT;
214 	} else if (strcmp(getprogname(), "nextboot") == 0) {
215 		donextboot = true;
216 		getopts = GETOPT_NEXTBOOT; /* Note: reboot's extra opts return '?' */
217 	} else {
218 		/* reboot */
219 		howto = 0;
220 	}
221 	Dflag = fflag = lflag = Nflag = nflag = qflag = false;
222 	while ((ch = getopt(argc, argv, getopts)) != -1) {
223 		switch(ch) {
224 		case 'c':
225 			howto |= RB_POWERCYCLE;
226 			break;
227 		case 'D':
228 			Dflag = true;
229 			break;
230 		case 'd':
231 			howto |= RB_DUMP;
232 			break;
233 		case 'e':
234 			v = split_kv(optarg);
235 			add_env(&env, optarg, v);
236 			break;
237 		case 'f':
238 			fflag = true;
239 			break;
240 		case 'k':
241 			kernel = optarg;
242 			break;
243 		case 'l':
244 			lflag = true;
245 			break;
246 		case 'n':
247 			nflag = true;
248 			howto |= RB_NOSYNC;
249 			break;
250 		case 'N':
251 			nflag = true;
252 			Nflag = true;
253 			break;
254 		case 'o':
255 			add_env(&env, "kernel_options", optarg);
256 			break;
257 		case 'p':
258 			howto |= RB_POWEROFF;
259 			break;
260 		case 'q':
261 			qflag = true;
262 			break;
263 		case 'r':
264 			howto |= RB_REROOT;
265 			break;
266 		case '?':
267 		default:
268 			usage();
269 		}
270 	}
271 
272 	argc -= optind;
273 	argv += optind;
274 	if (argc != 0)
275 		usage();
276 
277 	if (Dflag && ((howto & ~RB_HALT) != 0  || kernel != NULL))
278 		errx(1, "cannot delete existing nextboot config and do anything else");
279 	if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
280 		errx(1, "cannot dump (-d) when halting; must reboot instead");
281 	if (Nflag && (howto & RB_NOSYNC) != 0)
282 		errx(1, "-N cannot be used with -n");
283 	if ((howto & RB_POWEROFF) && (howto & RB_POWERCYCLE))
284 		errx(1, "-c and -p cannot be used together");
285 	if ((howto & RB_REROOT) != 0 && howto != RB_REROOT)
286 		errx(1, "-r cannot be used with -c, -d, -n, or -p");
287 	if ((howto & RB_REROOT) != 0 && kernel != NULL)
288 		errx(1, "-r and -k cannot be used together, there is no next kernel");
289 
290 	if (Dflag) {
291 		if (unlink(PATH_NEXTBOOT) != 0 && errno != ENOENT)
292 			warn("unlink " PATH_NEXTBOOT);
293 		exit(0);
294 	}
295 
296 	if (!donextboot && geteuid() != 0) {
297 		errno = EPERM;
298 		err(1, NULL);
299 	}
300 
301 	if (qflag) {
302 		reboot(howto);
303 		err(1, NULL);
304 	}
305 
306 	if (kernel != NULL) {
307 		if (!fflag) {
308 			char *k;
309 			struct stat sb;
310 
311 			asprintf(&k, "/boot/%s/kernel", kernel);
312 			if (k == NULL)
313 				errx(1, "No memory to check %s", kernel);
314 			if (stat(k, &sb) != 0)
315 				err(1, "stat %s", k);
316 			if (!S_ISREG(sb.st_mode))
317 				errx(1, "%s is not a file", k);
318 			free(k);
319 		}
320 		add_env(&env, "kernel", kernel);
321 	}
322 
323 	if (env != NULL)
324 		write_nextboot(PATH_NEXTBOOT, env, fflag);
325 	if (donextboot)
326 		exit (0);
327 
328 	/* Log the reboot. */
329 	if (!lflag)  {
330 		if ((user = getlogin()) == NULL)
331 			user = (pw = getpwuid(getuid())) ?
332 			    pw->pw_name : "???";
333 		if (dohalt) {
334 			openlog("halt", 0, LOG_AUTH | LOG_CONS);
335 			syslog(LOG_CRIT, "halted by %s", user);
336 		} else if (howto & RB_REROOT) {
337 			openlog("reroot", 0, LOG_AUTH | LOG_CONS);
338 			syslog(LOG_CRIT, "rerooted by %s", user);
339 		} else if (howto & RB_POWEROFF) {
340 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
341 			syslog(LOG_CRIT, "powered off by %s", user);
342 		} else if (howto & RB_POWERCYCLE) {
343 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
344 			syslog(LOG_CRIT, "power cycled by %s", user);
345 		} else {
346 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
347 			syslog(LOG_CRIT, "rebooted by %s", user);
348 		}
349 	}
350 	utx.ut_type = SHUTDOWN_TIME;
351 	gettimeofday(&utx.ut_tv, NULL);
352 	pututxline(&utx);
353 
354 	/*
355 	 * Do a sync early on, so disks start transfers while we're off
356 	 * killing processes.  Don't worry about writes done before the
357 	 * processes die, the reboot system call syncs the disks.
358 	 */
359 	if (!nflag)
360 		sync();
361 
362 	/*
363 	 * Ignore signals that we can get as a result of killing
364 	 * parents, group leaders, etc.
365 	 */
366 	(void)signal(SIGHUP,  SIG_IGN);
367 	(void)signal(SIGINT,  SIG_IGN);
368 	(void)signal(SIGQUIT, SIG_IGN);
369 	(void)signal(SIGTERM, SIG_IGN);
370 	(void)signal(SIGTSTP, SIG_IGN);
371 
372 	/*
373 	 * If we're running in a pipeline, we don't want to die
374 	 * after killing whatever we're writing to.
375 	 */
376 	(void)signal(SIGPIPE, SIG_IGN);
377 
378 	/*
379 	 * Only init(8) can perform rerooting.
380 	 */
381 	if (howto & RB_REROOT) {
382 		if (kill(1, SIGEMT) == -1)
383 			err(1, "SIGEMT init");
384 
385 		return (0);
386 	}
387 
388 	/* Just stop init -- if we fail, we'll restart it. */
389 	BOOTTRACE("SIGTSTP to init(8)...");
390 	if (kill(1, SIGTSTP) == -1)
391 		err(1, "SIGTSTP init");
392 
393 	/* Send a SIGTERM first, a chance to save the buffers. */
394 	BOOTTRACE("SIGTERM to all other processes...");
395 	if (kill(-1, SIGTERM) == -1 && errno != ESRCH)
396 		err(1, "SIGTERM processes");
397 
398 	/*
399 	 * After the processes receive the signal, start the rest of the
400 	 * buffers on their way.  Wait 5 seconds between the SIGTERM and
401 	 * the SIGKILL to give everybody a chance. If there is a lot of
402 	 * paging activity then wait longer, up to a maximum of approx
403 	 * 60 seconds.
404 	 */
405 	sleep(2);
406 	for (i = 0; i < 20; i++) {
407 		pageins = get_pageins();
408 		if (!nflag)
409 			sync();
410 		sleep(3);
411 		if (get_pageins() == pageins)
412 			break;
413 	}
414 
415 	for (i = 1;; ++i) {
416 		BOOTTRACE("SIGKILL to all other processes(%d)...", i);
417 		if (kill(-1, SIGKILL) == -1) {
418 			if (errno == ESRCH)
419 				break;
420 			goto restart;
421 		}
422 		if (i > 5) {
423 			(void)fprintf(stderr,
424 			    "WARNING: some process(es) wouldn't die\n");
425 			break;
426 		}
427 		(void)sleep(2 * i);
428 	}
429 
430 	reboot(howto);
431 	/* FALLTHROUGH */
432 
433 restart:
434 	BOOTTRACE("SIGHUP to init(8)...");
435 	sverrno = errno;
436 	errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
437 	    strerror(sverrno));
438 	/* NOTREACHED */
439 }
440 
441 static void
442 usage(void)
443 {
444 
445 	(void)fprintf(stderr, dohalt ?
446 	    "usage: halt [-clNnpq] [-k kernel]\n" :
447 	    "usage: reboot [-cdlNnpqr] [-k kernel]\n");
448 	exit(1);
449 }
450 
451 static uint64_t
452 get_pageins(void)
453 {
454 	uint64_t pageins;
455 	size_t len;
456 
457 	len = sizeof(pageins);
458 	if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
459 	    != 0) {
460 		warn("v_swappgsin");
461 		return (0);
462 	}
463 	return (pageins);
464 }
465