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