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