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
54extern char **environ;
55
56#define PATH_NEXTBOOT "/boot/nextboot.conf"
57
58static void usage(void) __dead2;
59static uint64_t get_pageins(void);
60
61static bool dohalt;
62static 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
72static void
73zfsbootcfg(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
113static void
114write_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
180static char *
181split_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
202static void
203add_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
220int
221main(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
453restart:
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
461static void
462usage(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
471static uint64_t
472get_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