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