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