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