/*
* Copyright (c) 1980, 1986, 1993
* The Regents of the University of California. All rights reserved.
+ * Portions copyright (c) 2007 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
#include <unistd.h>
#ifdef __APPLE__
+#include <TargetConditionals.h>
+#if !TARGET_OS_EMBEDDED
#include "kextmanager.h"
#include <IOKit/kext/kextmanager_types.h>
+#endif
#include <mach/mach_port.h> // allocate
#include <mach/mach.h> // task_self, etc
#include <servers/bootstrap.h> // bootstrap
+#include <bootstrap_priv.h>
+#include <reboot2.h>
+#include <utmpx.h>
+#include <sys/time.h>
#endif
void usage(void);
u_int get_pageins(void);
-#ifdef __APPLE__
+#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
int reserve_reboot(void);
#endif
main(int argc, char *argv[])
{
struct passwd *pw;
- int ch, howto, i, fd, kflag, lflag, nflag, qflag, pflag, uflag, sverrno;
- u_int pageins;
- char *kernel, *p;
+ int ch, howto, kflag, lflag, nflag, qflag, uflag;
+ char *p;
const char *user;
+#ifndef __APPLE__
+ int i, fd, pflag, sverrno;
+ u_int pageins;
+ char *kernel;
+#endif
if (strstr((p = rindex(*argv, '/')) ? p + 1 : *argv, "halt")) {
dohalt = 1;
break;
case 'q':
qflag = 1;
+ howto |= RB_QUICK;
break;
case '?':
default:
err(1, NULL);
}
-#ifdef __APPLE__
- if (!lflag) { // shutdown(8) has already checked w/kextd
- if ((errno = reserve_reboot()) && !qflag)
+#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
+ if (!qflag && !lflag) { // shutdown(8) has already checked w/kextd
+ if ((errno = reserve_reboot()))
err(1, "couldn't lock for reboot");
}
#endif
syslog(LOG_CRIT, "rebooted by %s", user);
}
}
+#if defined(__APPLE__)
+ {
+ struct utmpx utx;
+ bzero(&utx, sizeof(utx));
+ utx.ut_type = SHUTDOWN_TIME;
+ gettimeofday(&utx.ut_tv, NULL);
+ pututxline(&utx);
+
+ int newvalue = 1;
+ sysctlbyname("kern.willshutdown", NULL, NULL, &newvalue, sizeof(newvalue));
+ }
+#else
logwtmp("~", "shutdown", "");
+#endif
/*
* Do a sync early on, so disks start transfers while we're off
if (!nflag)
sync();
+#ifndef __APPLE__
/* Just stop init -- if we fail, we'll restart it. */
if (kill(1, SIGTSTP) == -1)
err(1, "SIGTSTP init");
+#endif
/* Ignore the SIGHUP we get when our parent shell dies. */
(void)signal(SIGHUP, SIG_IGN);
}
#endif
+#ifdef __APPLE__
+ // launchd(8) handles reboot. This call returns NULL on success.
+ exit(reboot2(howto) == NULL ? EXIT_SUCCESS : EXIT_FAILURE);
+#else /* __APPLE__ */
reboot(howto);
/* FALLTHROUGH */
-#ifndef __APPLE__
restart:
-#endif
sverrno = errno;
errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
strerror(sverrno));
/* NOTREACHED */
+#endif /* __APPLE__ */
}
void
return pageins;
}
-#ifdef __APPLE__
-// XX another copy of this routine is in shutdown.c; it would be nice to share
+#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
+// XX this routine is also in shutdown.tproj; it would be nice to share
-#define LCK_MAXTRIES 10
-#define LCK_DELAY 30
+#define WAITFORLOCK 1
/*
* contact kextd to lock for reboot
*/
int
reserve_reboot()
{
- int rval = ELAST+1;
+ int rval = ELAST + 1;
kern_return_t macherr = KERN_FAILURE;
- mach_port_t tport, bsport, kxport, myport = MACH_PORT_NULL;
- int busyStatus, nretries = LCK_MAXTRIES;
- dev_path_t busyDev = "<unknown>";
+ mach_port_t kxport, tport = MACH_PORT_NULL, myport = MACH_PORT_NULL;
+ int busyStatus = ELAST + 1;
+ mountpoint_t busyVol;
- // find kextd
- tport = mach_task_self();
- if (tport == MACH_PORT_NULL) goto finish;
- macherr = task_get_bootstrap_port(tport, &bsport);
- if (macherr) goto finish;
- macherr = bootstrap_look_up(bsport, KEXTD_SERVER_NAME, &kxport);
+ macherr = bootstrap_look_up2(bootstrap_port, KEXTD_SERVER_NAME, &kxport, 0, BOOTSTRAP_PRIVILEGED_SERVER);
if (macherr) goto finish;
// allocate a port to pass to kextd (in case we die)
+ tport = mach_task_self();
+ if (tport == MACH_PORT_NULL) goto finish;
macherr = mach_port_allocate(tport, MACH_PORT_RIGHT_RECEIVE, &myport);
if (macherr) goto finish;
- // loop trying to lock for reboot (i.e. no volumes are busy)
- do {
- nretries--;
- macherr = kextmanager_lock_reboot(kxport, myport, busyDev, &busyStatus);
- if (macherr) goto finish;
-
- if (busyStatus == EBUSY) {
- if (*busyDev) {
- warnx("%s is busy updating; delaying reboot (%d retries left)",
- busyDev, nretries);
- } else
- warnx("kextd still starting up");
- if (nretries) sleep(LCK_DELAY); // don't sleep the last time
- }
- } while (busyStatus == EBUSY && nretries > 0);
+ // try to lock for reboot
+ macherr = kextmanager_lock_reboot(kxport, myport, !WAITFORLOCK, busyVol,
+ &busyStatus);
+ if (macherr) goto finish;
- rval = busyStatus;
+ if (busyStatus == EBUSY) {
+ warnx("%s is busy updating; waiting for lock", busyVol);
+ macherr = kextmanager_lock_reboot(kxport, myport, WAITFORLOCK,
+ busyVol, &busyStatus);
+ if (macherr) goto finish;
+ }
-finish:
- if (macherr == BOOTSTRAP_UNKNOWN_SERVICE) {
- mach_port_mod_refs(tport, myport, MACH_PORT_RIGHT_RECEIVE, -1);
+ if (busyStatus == EALREADY) {
+ // reboot already in progress
rval = 0;
- } else if (macherr) {
- warnx("couldn't lock kext manager for reboot: %s",
- mach_error_string(macherr));
- rval = ELAST + 1;
+ } else {
+ rval = busyStatus;
}
- if (rval && myport != MACH_PORT_NULL) {
+
+finish:
+ // in general, we want to err on the side of allowing the reboot
+ if (macherr) {
+ if (macherr != BOOTSTRAP_UNKNOWN_SERVICE)
+ warnx("WARNING: couldn't lock kext manager for reboot: %s",
+ mach_error_string(macherr));
+ rval = 0;
+ }
+ // unless we got the lock, clean up our port
+ if (busyStatus != 0 && myport != MACH_PORT_NULL)
mach_port_mod_refs(tport, myport, MACH_PORT_RIGHT_RECEIVE, -1);
- }
return rval;
}