xref: /freebsd/sbin/reboot/reboot.c (revision 2c479548119a11058fe8947ba021fd49d4169920)
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/types.h>
33 #include <sys/boottrace.h>
34 #include <sys/reboot.h>
35 #include <sys/stat.h>
36 #include <sys/sysctl.h>
37 #include <sys/time.h>
38 
39 #include <err.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <pwd.h>
43 #include <signal.h>
44 #include <stdbool.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <syslog.h>
49 #include <unistd.h>
50 #include <utmpx.h>
51 
52 #define PATH_NEXTBOOT "/boot/nextboot.conf"
53 
54 static void usage(void) __dead2;
55 static uint64_t get_pageins(void);
56 
57 static bool dohalt;
58 
59 static void
60 write_nextboot(const char *fn, const char *kernel, bool force)
61 {
62 	FILE *fp;
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 	fp = fopen(fn, "w");
73 	if (fp == NULL)
74 		E("Can't create %s to boot %s", fn, kernel);
75 
76 	if (fprintf(fp,
77 	    "nextboot_enable=\"YES\"\n"
78 	    "kernel=\"%s\"\n", kernel) < 0) {
79 		int e;
80 
81 		e = errno;
82 		fclose(fp);
83 		if (unlink(fn))
84 			warn("unlink %s", fn);
85 		errno = e;
86 		E("Can't write %s", fn);
87 	}
88 	fclose(fp);
89 #undef E
90 }
91 
92 int
93 main(int argc, char *argv[])
94 {
95 	struct utmpx utx;
96 	const struct passwd *pw;
97 	int ch, howto, i, sverrno;
98 	bool Dflag, fflag, lflag, Nflag, nflag, qflag;
99 	uint64_t pageins;
100 	const char *user, *kernel = NULL;
101 
102 	if (strstr(getprogname(), "halt") != NULL) {
103 		dohalt = true;
104 		howto = RB_HALT;
105 	} else
106 		howto = 0;
107 	Dflag = fflag = lflag = Nflag = nflag = qflag = false;
108 	while ((ch = getopt(argc, argv, "cDdk:lNnpqr")) != -1)
109 		switch(ch) {
110 		case 'c':
111 			howto |= RB_POWERCYCLE;
112 			break;
113 		case 'D':
114 			Dflag = true;
115 			break;
116 		case 'd':
117 			howto |= RB_DUMP;
118 			break;
119 		case 'f':
120 			fflag = true;
121 			break;
122 		case 'k':
123 			kernel = optarg;
124 			break;
125 		case 'l':
126 			lflag = true;
127 			break;
128 		case 'n':
129 			nflag = true;
130 			howto |= RB_NOSYNC;
131 			break;
132 		case 'N':
133 			nflag = true;
134 			Nflag = true;
135 			break;
136 		case 'p':
137 			howto |= RB_POWEROFF;
138 			break;
139 		case 'q':
140 			qflag = true;
141 			break;
142 		case 'r':
143 			howto |= RB_REROOT;
144 			break;
145 		case '?':
146 		default:
147 			usage();
148 		}
149 	argc -= optind;
150 	argv += optind;
151 	if (argc != 0)
152 		usage();
153 
154 	if (Dflag && ((howto & ~RB_HALT) != 0  || kernel != NULL))
155 		errx(1, "cannot delete existing nextboot config and do anything else");
156 	if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
157 		errx(1, "cannot dump (-d) when halting; must reboot instead");
158 	if (Nflag && (howto & RB_NOSYNC) != 0)
159 		errx(1, "-N cannot be used with -n");
160 	if ((howto & RB_POWEROFF) && (howto & RB_POWERCYCLE))
161 		errx(1, "-c and -p cannot be used together");
162 	if ((howto & RB_REROOT) != 0 && howto != RB_REROOT)
163 		errx(1, "-r cannot be used with -c, -d, -n, or -p");
164 	if ((howto & RB_REROOT) != 0 && kernel != NULL)
165 		errx(1, "-r and -k cannot be used together, there is no next kernel");
166 	if (geteuid()) {
167 		errno = EPERM;
168 		err(1, NULL);
169 	}
170 
171 	if (Dflag) {
172 		if (unlink(PATH_NEXTBOOT) != 0)
173 			err(1, "unlink %s", PATH_NEXTBOOT);
174 		exit(0);
175 	}
176 
177 	if (qflag) {
178 		reboot(howto);
179 		err(1, NULL);
180 	}
181 
182 	if (kernel != NULL) {
183 		if (!fflag) {
184 			char *k;
185 			struct stat sb;
186 
187 			asprintf(&k, "/boot/%s/kernel", kernel);
188 			if (k == NULL)
189 				errx(1, "No memory to check %s", kernel);
190 			if (stat(k, &sb) != 0)
191 				err(1, "stat %s", k);
192 			if (!S_ISREG(sb.st_mode))
193 				errx(1, "%s is not a file", k);
194 			free(k);
195 		}
196 		write_nextboot(PATH_NEXTBOOT, kernel, fflag);
197 	}
198 
199 	/* Log the reboot. */
200 	if (!lflag)  {
201 		if ((user = getlogin()) == NULL)
202 			user = (pw = getpwuid(getuid())) ?
203 			    pw->pw_name : "???";
204 		if (dohalt) {
205 			openlog("halt", 0, LOG_AUTH | LOG_CONS);
206 			syslog(LOG_CRIT, "halted by %s", user);
207 		} else if (howto & RB_REROOT) {
208 			openlog("reroot", 0, LOG_AUTH | LOG_CONS);
209 			syslog(LOG_CRIT, "rerooted by %s", user);
210 		} else if (howto & RB_POWEROFF) {
211 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
212 			syslog(LOG_CRIT, "powered off by %s", user);
213 		} else if (howto & RB_POWERCYCLE) {
214 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
215 			syslog(LOG_CRIT, "power cycled by %s", user);
216 		} else {
217 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
218 			syslog(LOG_CRIT, "rebooted by %s", user);
219 		}
220 	}
221 	utx.ut_type = SHUTDOWN_TIME;
222 	gettimeofday(&utx.ut_tv, NULL);
223 	pututxline(&utx);
224 
225 	/*
226 	 * Do a sync early on, so disks start transfers while we're off
227 	 * killing processes.  Don't worry about writes done before the
228 	 * processes die, the reboot system call syncs the disks.
229 	 */
230 	if (!nflag)
231 		sync();
232 
233 	/*
234 	 * Ignore signals that we can get as a result of killing
235 	 * parents, group leaders, etc.
236 	 */
237 	(void)signal(SIGHUP,  SIG_IGN);
238 	(void)signal(SIGINT,  SIG_IGN);
239 	(void)signal(SIGQUIT, SIG_IGN);
240 	(void)signal(SIGTERM, SIG_IGN);
241 	(void)signal(SIGTSTP, SIG_IGN);
242 
243 	/*
244 	 * If we're running in a pipeline, we don't want to die
245 	 * after killing whatever we're writing to.
246 	 */
247 	(void)signal(SIGPIPE, SIG_IGN);
248 
249 	/*
250 	 * Only init(8) can perform rerooting.
251 	 */
252 	if (howto & RB_REROOT) {
253 		if (kill(1, SIGEMT) == -1)
254 			err(1, "SIGEMT init");
255 
256 		return (0);
257 	}
258 
259 	/* Just stop init -- if we fail, we'll restart it. */
260 	BOOTTRACE("SIGTSTP to init(8)...");
261 	if (kill(1, SIGTSTP) == -1)
262 		err(1, "SIGTSTP init");
263 
264 	/* Send a SIGTERM first, a chance to save the buffers. */
265 	BOOTTRACE("SIGTERM to all other processes...");
266 	if (kill(-1, SIGTERM) == -1 && errno != ESRCH)
267 		err(1, "SIGTERM processes");
268 
269 	/*
270 	 * After the processes receive the signal, start the rest of the
271 	 * buffers on their way.  Wait 5 seconds between the SIGTERM and
272 	 * the SIGKILL to give everybody a chance. If there is a lot of
273 	 * paging activity then wait longer, up to a maximum of approx
274 	 * 60 seconds.
275 	 */
276 	sleep(2);
277 	for (i = 0; i < 20; i++) {
278 		pageins = get_pageins();
279 		if (!nflag)
280 			sync();
281 		sleep(3);
282 		if (get_pageins() == pageins)
283 			break;
284 	}
285 
286 	for (i = 1;; ++i) {
287 		BOOTTRACE("SIGKILL to all other processes(%d)...", i);
288 		if (kill(-1, SIGKILL) == -1) {
289 			if (errno == ESRCH)
290 				break;
291 			goto restart;
292 		}
293 		if (i > 5) {
294 			(void)fprintf(stderr,
295 			    "WARNING: some process(es) wouldn't die\n");
296 			break;
297 		}
298 		(void)sleep(2 * i);
299 	}
300 
301 	reboot(howto);
302 	/* FALLTHROUGH */
303 
304 restart:
305 	BOOTTRACE("SIGHUP to init(8)...");
306 	sverrno = errno;
307 	errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
308 	    strerror(sverrno));
309 	/* NOTREACHED */
310 }
311 
312 static void
313 usage(void)
314 {
315 
316 	(void)fprintf(stderr, dohalt ?
317 	    "usage: halt [-clNnpq] [-k kernel]\n" :
318 	    "usage: reboot [-cdlNnpqr] [-k kernel]\n");
319 	exit(1);
320 }
321 
322 static uint64_t
323 get_pageins(void)
324 {
325 	uint64_t pageins;
326 	size_t len;
327 
328 	len = sizeof(pageins);
329 	if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
330 	    != 0) {
331 		warn("v_swappgsin");
332 		return (0);
333 	}
334 	return (pageins);
335 }
336