1/*
2 * Copyright (c) 1980, 1986, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 * Portions copyright (c) 2007 Apple Inc.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#ifndef lint
32static const char copyright[] =
33"@(#) Copyright (c) 1980, 1986, 1993\n\
34	The Regents of the University of California.  All rights reserved.\n";
35#endif /* not lint */
36
37#ifndef lint
38#if 0
39static char sccsid[] = "@(#)reboot.c	8.1 (Berkeley) 6/5/93";
40#endif
41static const char rcsid[] =
42  "$FreeBSD: src/sbin/reboot/reboot.c,v 1.17 2002/10/06 16:24:36 thomas Exp $";
43#endif /* not lint */
44
45#include <sys/reboot.h>
46#include <sys/types.h>
47#include <sys/sysctl.h>
48#include <signal.h>
49#include <err.h>
50#include <errno.h>
51#include <fcntl.h>
52#include <util.h>
53#include <pwd.h>
54#include <syslog.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <unistd.h>
59
60#ifdef __APPLE__
61#include <TargetConditionals.h>
62#if !TARGET_OS_EMBEDDED
63#include "kextmanager.h"
64#include <IOKit/kext/kextmanager_types.h>
65#endif
66#include <mach/mach_port.h>		// allocate
67#include <mach/mach.h>			// task_self, etc
68#include <servers/bootstrap.h>	// bootstrap
69#include <bootstrap_priv.h>
70#include <reboot2.h>
71#include <utmpx.h>
72#include <sys/time.h>
73#endif
74
75void usage(void);
76u_int get_pageins(void);
77#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
78int reserve_reboot(void);
79#endif
80
81int dohalt;
82
83int
84main(int argc, char *argv[])
85{
86	struct passwd *pw;
87	int ch, howto, kflag, lflag, nflag, qflag, uflag;
88	char *p;
89	const char *user;
90#ifndef __APPLE__
91	int i, fd, pflag, sverrno;
92	u_int pageins;
93	char *kernel;
94#endif
95
96	if (strstr((p = rindex(*argv, '/')) ? p + 1 : *argv, "halt")) {
97		dohalt = 1;
98		howto = RB_HALT;
99	} else
100		howto = 0;
101	kflag = lflag = nflag = qflag = 0;
102#ifndef __APPLE__
103	while ((ch = getopt(argc, argv, "dk:lnpq")) != -1)
104#else
105	while ((ch = getopt(argc, argv, "lnqu")) != -1)
106#endif
107		switch(ch) {
108#ifndef __APPLE__
109		case 'd':
110			howto |= RB_DUMP;
111			break;
112		case 'k':
113			kflag = 1;
114			kernel = optarg;
115			break;
116#endif
117		case 'l':
118			lflag = 1;
119			break;
120		case 'n':
121			nflag = 1;
122			howto |= RB_NOSYNC;
123			break;
124/* -p is irrelevant on OS X.  It does that anyway. */
125#ifndef __APPLE__
126		case 'p':
127			pflag = 1;
128			howto |= RB_POWEROFF;
129			break;
130#endif
131		case 'u':
132			uflag = 1;
133			howto |= RB_UPSDELAY;
134			break;
135		case 'q':
136			qflag = 1;
137			howto |= RB_QUICK;
138			break;
139		case '?':
140		default:
141			usage();
142		}
143	argc -= optind;
144	argv += optind;
145
146#ifndef __APPLE__
147	if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
148		errx(1, "cannot dump (-d) when halting; must reboot instead");
149#endif
150	if (geteuid()) {
151		errno = EPERM;
152		err(1, NULL);
153	}
154
155#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
156	if (!qflag && !lflag) {	// shutdown(8) has already checked w/kextd
157		if ((errno = reserve_reboot()))
158			err(1, "couldn't lock for reboot");
159	}
160#endif
161
162	if (qflag) {
163		reboot(howto);
164		err(1, NULL);
165	}
166
167#ifndef __APPLE__
168	if (kflag) {
169		fd = open("/boot/nextboot.conf", O_WRONLY | O_CREAT, 0444);
170		if (fd > -1) {
171			(void)write(fd, "nextboot_enable=\"YES\"\n", 22);
172			(void)write(fd, "kernel=\"", 8L);
173			(void)write(fd, kernel, strlen(kernel));
174			(void)write(fd, "\"\n", 2);
175			close(fd);
176		}
177	}
178#endif
179
180	/* Log the reboot. */
181	if (!lflag)  {
182		if ((user = getlogin()) == NULL)
183			user = (pw = getpwuid(getuid())) ?
184			    pw->pw_name : "???";
185		if (dohalt) {
186			openlog("halt", 0, LOG_AUTH | LOG_CONS);
187			syslog(LOG_CRIT, "halted by %s%s", user,
188			     (howto & RB_UPSDELAY) ? " with UPS delay":"");
189		} else {
190			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
191			syslog(LOG_CRIT, "rebooted by %s", user);
192		}
193	}
194#if defined(__APPLE__)
195	{
196		struct utmpx utx;
197		bzero(&utx, sizeof(utx));
198		utx.ut_type = SHUTDOWN_TIME;
199		gettimeofday(&utx.ut_tv, NULL);
200		pututxline(&utx);
201
202		int newvalue = 1;
203		sysctlbyname("kern.willshutdown", NULL, NULL, &newvalue, sizeof(newvalue));
204	}
205#else
206	logwtmp("~", "shutdown", "");
207#endif
208
209	/*
210	 * Do a sync early on, so disks start transfers while we're off
211	 * killing processes.  Don't worry about writes done before the
212	 * processes die, the reboot system call syncs the disks.
213	 */
214	if (!nflag)
215		sync();
216
217#ifndef __APPLE__
218	/* Just stop init -- if we fail, we'll restart it. */
219	if (kill(1, SIGTSTP) == -1)
220		err(1, "SIGTSTP init");
221#endif
222
223	/* Ignore the SIGHUP we get when our parent shell dies. */
224	(void)signal(SIGHUP, SIG_IGN);
225
226#ifndef __APPLE__
227	/* Send a SIGTERM first, a chance to save the buffers. */
228	if (kill(-1, SIGTERM) == -1)
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		if (kill(-1, SIGKILL) == -1) {
250			if (errno == ESRCH)
251				break;
252			goto restart;
253		}
254		if (i > 5) {
255			(void)fprintf(stderr,
256			    "WARNING: some process(es) wouldn't die\n");
257			break;
258		}
259		(void)sleep(2 * i);
260	}
261#endif
262
263#ifdef __APPLE__
264	// launchd(8) handles reboot.  This call returns NULL on success.
265	exit(reboot2(howto) == NULL ? EXIT_SUCCESS : EXIT_FAILURE);
266#else /* __APPLE__ */
267	reboot(howto);
268	/* FALLTHROUGH */
269
270restart:
271	sverrno = errno;
272	errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
273	    strerror(sverrno));
274	/* NOTREACHED */
275#endif /* __APPLE__ */
276}
277
278void
279usage()
280{
281#ifndef __APPLE__
282	(void)fprintf(stderr, "usage: %s [-dnpq] [-k kernel]\n",
283#else
284	(void)fprintf(stderr, "usage: %s [-lnq]\n",
285#endif
286	    dohalt ? "halt" : "reboot");
287	exit(1);
288}
289
290u_int
291get_pageins()
292{
293	u_int pageins;
294	size_t len;
295
296	len = sizeof(pageins);
297	if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
298	    != 0) {
299		warnx("v_swappgsin");
300		return (0);
301	}
302	return pageins;
303}
304
305#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
306// XX this routine is also in shutdown.tproj; it would be nice to share
307
308#define WAITFORLOCK 1
309/*
310 * contact kextd to lock for reboot
311 */
312int
313reserve_reboot()
314{
315	int rval = ELAST + 1;
316	kern_return_t macherr = KERN_FAILURE;
317	mach_port_t kxport, tport = MACH_PORT_NULL, myport = MACH_PORT_NULL;
318	int busyStatus = ELAST + 1;
319	mountpoint_t busyVol;
320
321	macherr = bootstrap_look_up2(bootstrap_port, KEXTD_SERVER_NAME, &kxport, 0, BOOTSTRAP_PRIVILEGED_SERVER);
322	if (macherr)  goto finish;
323
324	// allocate a port to pass to kextd (in case we die)
325	tport = mach_task_self();
326	if (tport == MACH_PORT_NULL)  goto finish;
327	macherr = mach_port_allocate(tport, MACH_PORT_RIGHT_RECEIVE, &myport);
328	if (macherr)  goto finish;
329
330	// try to lock for reboot
331	macherr = kextmanager_lock_reboot(kxport, myport, !WAITFORLOCK, busyVol,
332                                      &busyStatus);
333	if (macherr)  goto finish;
334
335	if (busyStatus == EBUSY) {
336		warnx("%s is busy updating; waiting for lock", busyVol);
337		macherr = kextmanager_lock_reboot(kxport, myport, WAITFORLOCK,
338										  busyVol, &busyStatus);
339		if (macherr)	goto finish;
340	}
341
342	if (busyStatus == EALREADY) {
343		// reboot already in progress
344		rval = 0;
345	} else {
346		rval = busyStatus;
347	}
348
349finish:
350	// in general, we want to err on the side of allowing the reboot
351	if (macherr) {
352		if (macherr != BOOTSTRAP_UNKNOWN_SERVICE)
353			warnx("WARNING: couldn't lock kext manager for reboot: %s",
354					mach_error_string(macherr));
355		rval = 0;
356	}
357	// unless we got the lock, clean up our port
358	if (busyStatus != 0 && myport != MACH_PORT_NULL)
359		mach_port_mod_refs(tport, myport, MACH_PORT_RIGHT_RECEIVE, -1);
360
361	return rval;
362}
363#endif
364