xref: /freebsd/sbin/reboot/reboot.c (revision cfeedadfbde084e25ace99a370f3f417b78f5df7)
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 fflag, lflag, nflag, qflag, Nflag;
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 	fflag = lflag = nflag = qflag = Nflag = false;
108 	while ((ch = getopt(argc, argv, "cdk:lNnpqr")) != -1)
109 		switch(ch) {
110 		case 'c':
111 			howto |= RB_POWERCYCLE;
112 			break;
113 		case 'd':
114 			howto |= RB_DUMP;
115 			break;
116 		case 'f':
117 			fflag = true;
118 			break;
119 		case 'k':
120 			kernel = optarg;
121 			break;
122 		case 'l':
123 			lflag = true;
124 			break;
125 		case 'n':
126 			nflag = true;
127 			howto |= RB_NOSYNC;
128 			break;
129 		case 'N':
130 			nflag = true;
131 			Nflag = true;
132 			break;
133 		case 'p':
134 			howto |= RB_POWEROFF;
135 			break;
136 		case 'q':
137 			qflag = true;
138 			break;
139 		case 'r':
140 			howto |= RB_REROOT;
141 			break;
142 		case '?':
143 		default:
144 			usage();
145 		}
146 	argc -= optind;
147 	argv += optind;
148 	if (argc != 0)
149 		usage();
150 
151 	if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
152 		errx(1, "cannot dump (-d) when halting; must reboot instead");
153 	if (Nflag && (howto & RB_NOSYNC) != 0)
154 		errx(1, "-N cannot be used with -n");
155 	if ((howto & RB_POWEROFF) && (howto & RB_POWERCYCLE))
156 		errx(1, "-c and -p cannot be used together");
157 	if ((howto & RB_REROOT) != 0 && howto != RB_REROOT)
158 		errx(1, "-r cannot be used with -c, -d, -n, or -p");
159 	if ((howto & RB_REROOT) != 0 && kernel != NULL)
160 		errx(1, "-r and -k cannot be used together, there is no next kernel");
161 	if (geteuid()) {
162 		errno = EPERM;
163 		err(1, NULL);
164 	}
165 
166 	if (qflag) {
167 		reboot(howto);
168 		err(1, NULL);
169 	}
170 
171 	if (kernel != NULL) {
172 		if (!fflag) {
173 			char *k;
174 			struct stat sb;
175 
176 			asprintf(&k, "/boot/%s/kernel", kernel);
177 			if (k == NULL)
178 				errx(1, "No memory to check %s", kernel);
179 			if (stat(k, &sb) != 0)
180 				err(1, "stat %s", k);
181 			if (!S_ISREG(sb.st_mode))
182 				errx(1, "%s is not a file", k);
183 			free(k);
184 		}
185 		write_nextboot(PATH_NEXTBOOT, kernel, fflag);
186 	}
187 
188 	/* Log the reboot. */
189 	if (!lflag)  {
190 		if ((user = getlogin()) == NULL)
191 			user = (pw = getpwuid(getuid())) ?
192 			    pw->pw_name : "???";
193 		if (dohalt) {
194 			openlog("halt", 0, LOG_AUTH | LOG_CONS);
195 			syslog(LOG_CRIT, "halted by %s", user);
196 		} else if (howto & RB_REROOT) {
197 			openlog("reroot", 0, LOG_AUTH | LOG_CONS);
198 			syslog(LOG_CRIT, "rerooted by %s", user);
199 		} else if (howto & RB_POWEROFF) {
200 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
201 			syslog(LOG_CRIT, "powered off by %s", user);
202 		} else if (howto & RB_POWERCYCLE) {
203 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
204 			syslog(LOG_CRIT, "power cycled by %s", user);
205 		} else {
206 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
207 			syslog(LOG_CRIT, "rebooted by %s", user);
208 		}
209 	}
210 	utx.ut_type = SHUTDOWN_TIME;
211 	gettimeofday(&utx.ut_tv, NULL);
212 	pututxline(&utx);
213 
214 	/*
215 	 * Do a sync early on, so disks start transfers while we're off
216 	 * killing processes.  Don't worry about writes done before the
217 	 * processes die, the reboot system call syncs the disks.
218 	 */
219 	if (!nflag)
220 		sync();
221 
222 	/*
223 	 * Ignore signals that we can get as a result of killing
224 	 * parents, group leaders, etc.
225 	 */
226 	(void)signal(SIGHUP,  SIG_IGN);
227 	(void)signal(SIGINT,  SIG_IGN);
228 	(void)signal(SIGQUIT, SIG_IGN);
229 	(void)signal(SIGTERM, SIG_IGN);
230 	(void)signal(SIGTSTP, SIG_IGN);
231 
232 	/*
233 	 * If we're running in a pipeline, we don't want to die
234 	 * after killing whatever we're writing to.
235 	 */
236 	(void)signal(SIGPIPE, SIG_IGN);
237 
238 	/*
239 	 * Only init(8) can perform rerooting.
240 	 */
241 	if (howto & RB_REROOT) {
242 		if (kill(1, SIGEMT) == -1)
243 			err(1, "SIGEMT init");
244 
245 		return (0);
246 	}
247 
248 	/* Just stop init -- if we fail, we'll restart it. */
249 	BOOTTRACE("SIGTSTP to init(8)...");
250 	if (kill(1, SIGTSTP) == -1)
251 		err(1, "SIGTSTP init");
252 
253 	/* Send a SIGTERM first, a chance to save the buffers. */
254 	BOOTTRACE("SIGTERM to all other processes...");
255 	if (kill(-1, SIGTERM) == -1 && errno != ESRCH)
256 		err(1, "SIGTERM processes");
257 
258 	/*
259 	 * After the processes receive the signal, start the rest of the
260 	 * buffers on their way.  Wait 5 seconds between the SIGTERM and
261 	 * the SIGKILL to give everybody a chance. If there is a lot of
262 	 * paging activity then wait longer, up to a maximum of approx
263 	 * 60 seconds.
264 	 */
265 	sleep(2);
266 	for (i = 0; i < 20; i++) {
267 		pageins = get_pageins();
268 		if (!nflag)
269 			sync();
270 		sleep(3);
271 		if (get_pageins() == pageins)
272 			break;
273 	}
274 
275 	for (i = 1;; ++i) {
276 		BOOTTRACE("SIGKILL to all other processes(%d)...", i);
277 		if (kill(-1, SIGKILL) == -1) {
278 			if (errno == ESRCH)
279 				break;
280 			goto restart;
281 		}
282 		if (i > 5) {
283 			(void)fprintf(stderr,
284 			    "WARNING: some process(es) wouldn't die\n");
285 			break;
286 		}
287 		(void)sleep(2 * i);
288 	}
289 
290 	reboot(howto);
291 	/* FALLTHROUGH */
292 
293 restart:
294 	BOOTTRACE("SIGHUP to init(8)...");
295 	sverrno = errno;
296 	errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
297 	    strerror(sverrno));
298 	/* NOTREACHED */
299 }
300 
301 static void
302 usage(void)
303 {
304 
305 	(void)fprintf(stderr, dohalt ?
306 	    "usage: halt [-clNnpq] [-k kernel]\n" :
307 	    "usage: reboot [-cdlNnpqr] [-k kernel]\n");
308 	exit(1);
309 }
310 
311 static uint64_t
312 get_pageins(void)
313 {
314 	uint64_t pageins;
315 	size_t len;
316 
317 	len = sizeof(pageins);
318 	if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
319 	    != 0) {
320 		warn("v_swappgsin");
321 		return (0);
322 	}
323 	return (pageins);
324 }
325