]> git.saurik.com Git - apple/system_cmds.git/blobdiff - reboot.tproj/reboot.c
system_cmds-336.20.tar.gz
[apple/system_cmds.git] / reboot.tproj / reboot.c
index 294634c024689ed8bc69268dd79a5aa487ec0cc1..f970ca5252fec1e39aa6f074fee959cdb5f6ced2 100644 (file)
@@ -1,30 +1,6 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
 /*
  * Copyright (c) 1980, 1986, 1993
- *      The Regents of the University of California.  All rights reserved.
+ *     The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -36,8 +12,8 @@
  *    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.
+ *     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
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  * SUCH DAMAGE.
  */
 
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1986, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)reboot.c   8.1 (Berkeley) 6/5/93";
+#endif
+static const char rcsid[] =
+  "$FreeBSD: src/sbin/reboot/reboot.c,v 1.17 2002/10/06 16:24:36 thomas Exp $";
+#endif /* not lint */
+
 #include <sys/reboot.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
 #include <signal.h>
-#include <pwd.h>
+#include <err.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <util.h>
+#include <pwd.h>
 #include <syslog.h>
-#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+
+#ifdef __APPLE__
+#include "kextmanager.h"
+#include <IOKit/kext/kextmanager_types.h>
+#include <mach/mach_port.h>            // allocate
+#include <mach/mach.h>                 // task_self, etc
+#include <servers/bootstrap.h> // bootstrap
+#endif
 
-void err __P((const char *fmt, ...));
-void usage __P((void));
+void usage(void);
+u_int get_pageins(void);
+#ifdef __APPLE__
+int reserve_reboot(void);
+#endif
 
 int dohalt;
 
 int
-main(argc, argv)
-       int argc;
-       char *argv[];
+main(int argc, char *argv[])
 {
-       register int i;
        struct passwd *pw;
-       int ch, howto, lflag, nflag, qflag, sverrno;
-       char *p, *user;
+       int ch, howto, i, fd, kflag, lflag, nflag, qflag, pflag, uflag, sverrno;
+       u_int pageins;
+       char *kernel, *p;
+       const char *user;
 
-       if (!strcmp((p = rindex(*argv, '/')) ? p + 1 : *argv, "halt")) {
+       if (strstr((p = rindex(*argv, '/')) ? p + 1 : *argv, "halt")) {
                dohalt = 1;
                howto = RB_HALT;
        } else
                howto = 0;
-       lflag = nflag = qflag = 0;
-       while ((ch = getopt(argc, argv, "lnq")) != EOF)
+       kflag = lflag = nflag = qflag = 0;
+#ifndef __APPLE__
+       while ((ch = getopt(argc, argv, "dk:lnpq")) != -1)
+#else
+       while ((ch = getopt(argc, argv, "lnqu")) != -1)
+#endif
                switch(ch) {
-               case 'l':               /* Undocumented; used by shutdown. */
+#ifndef __APPLE__
+               case 'd':
+                       howto |= RB_DUMP;
+                       break;
+               case 'k':
+                       kflag = 1;
+                       kernel = optarg;
+                       break;
+#endif
+               case 'l':
                        lflag = 1;
                        break;
                case 'n':
                        nflag = 1;
                        howto |= RB_NOSYNC;
                        break;
+/* -p is irrelevant on OS X.  It does that anyway. */
+#ifndef __APPLE__
+               case 'p':
+                       pflag = 1;
+                       howto |= RB_POWEROFF;
+                       break;
+#endif
+               case 'u':
+                       uflag = 1;
+                       howto |= RB_UPSDELAY;
+                       break;
                case 'q':
                        qflag = 1;
                        break;
@@ -105,13 +134,39 @@ main(argc, argv)
        argc -= optind;
        argv += optind;
 
-       if (geteuid())
-               err("%s", strerror(EPERM));
+#ifndef __APPLE__
+       if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
+               errx(1, "cannot dump (-d) when halting; must reboot instead");
+#endif
+       if (geteuid()) {
+               errno = EPERM;
+               err(1, NULL);
+       }
+
+#ifdef __APPLE__
+       if (!lflag) {   // shutdown(8) has already checked w/kextd
+               if ((errno = reserve_reboot()) && !qflag)
+                       err(1, "couldn't lock for reboot");
+       }
+#endif
 
        if (qflag) {
                reboot(howto);
-               err("%s", strerror(errno));
+               err(1, NULL);
+       }
+
+#ifndef __APPLE__
+       if (kflag) {
+               fd = open("/boot/nextboot.conf", O_WRONLY | O_CREAT, 0444);
+               if (fd > -1) {
+                       (void)write(fd, "nextboot_enable=\"YES\"\n", 22);
+                       (void)write(fd, "kernel=\"", 8L);
+                       (void)write(fd, kernel, strlen(kernel));
+                       (void)write(fd, "\"\n", 2);
+                       close(fd);
+               }
        }
+#endif
 
        /* Log the reboot. */
        if (!lflag)  {
@@ -120,7 +175,8 @@ main(argc, argv)
                            pw->pw_name : "???";
                if (dohalt) {
                        openlog("halt", 0, LOG_AUTH | LOG_CONS);
-                       syslog(LOG_CRIT, "halted by %s", user);
+                       syslog(LOG_CRIT, "halted by %s%s", user, 
+                            (howto & RB_UPSDELAY) ? " with UPS delay":"");
                } else {
                        openlog("reboot", 0, LOG_AUTH | LOG_CONS);
                        syslog(LOG_CRIT, "rebooted by %s", user);
@@ -138,25 +194,32 @@ main(argc, argv)
 
        /* Just stop init -- if we fail, we'll restart it. */
        if (kill(1, SIGTSTP) == -1)
-               err("SIGTSTP init: %s", strerror(errno));
+               err(1, "SIGTSTP init");
 
        /* Ignore the SIGHUP we get when our parent shell dies. */
        (void)signal(SIGHUP, SIG_IGN);
 
-#if 0
+#ifndef __APPLE__
        /* Send a SIGTERM first, a chance to save the buffers. */
        if (kill(-1, SIGTERM) == -1)
-               err("SIGTERM processes: %s", strerror(errno));
+               err(1, "SIGTERM processes");
 
        /*
         * After the processes receive the signal, start the rest of the
         * buffers on their way.  Wait 5 seconds between the SIGTERM and
-        * the SIGKILL to give everybody a chance.
+        * the SIGKILL to give everybody a chance. If there is a lot of
+        * paging activity then wait longer, up to a maximum of approx
+        * 60 seconds.
         */
        sleep(2);
-       if (!nflag)
-               sync();
-       sleep(3);
+       for (i = 0; i < 20; i++) {
+               pageins = get_pageins();
+               if (!nflag)
+                       sync();
+               sleep(3);
+               if (get_pageins() == pageins)
+                       break;
+       }
 
        for (i = 1;; ++i) {
                if (kill(-1, SIGKILL) == -1) {
@@ -172,12 +235,15 @@ main(argc, argv)
                (void)sleep(2 * i);
        }
 #endif
+
        reboot(howto);
        /* FALLTHROUGH */
 
+#ifndef __APPLE__
 restart:
+#endif
        sverrno = errno;
-       err("%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
+       errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
            strerror(sverrno));
        /* NOTREACHED */
 }
@@ -185,35 +251,90 @@ restart:
 void
 usage()
 {
-       (void)fprintf(stderr, "usage: %s [-nq]\n", dohalt ? "halt" : "reboot");
+#ifndef __APPLE__
+       (void)fprintf(stderr, "usage: %s [-dnpq] [-k kernel]\n",
+#else
+       (void)fprintf(stderr, "usage: %s [-lnq]\n",
+#endif
+           dohalt ? "halt" : "reboot");
        exit(1);
 }
 
-#if __STDC__
-#include <stdarg.h>
-#else
-#include <varargs.h>
-#endif
+u_int
+get_pageins()
+{
+       u_int pageins;
+       size_t len;
 
-void
-#if __STDC__
-err(const char *fmt, ...)
-#else
-err(fmt, va_alist)
-       char *fmt;
-        va_dcl
-#endif
+       len = sizeof(pageins);
+       if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
+           != 0) {
+               warnx("v_swappgsin");
+               return (0);
+       }
+       return pageins;
+}
+
+#ifdef __APPLE__
+// XX another copy of this routine is in shutdown.c; it would be nice to share
+
+#define LCK_MAXTRIES 10
+#define LCK_DELAY       30
+/*
+ * contact kextd to lock for reboot
+ */
+int
+reserve_reboot()
 {
-       va_list ap;
-#if __STDC__
-       va_start(ap, fmt);
-#else
-       va_start(ap);
-#endif
-       (void)fprintf(stderr, "%s: ", dohalt ? "halt" : "reboot");
-       (void)vfprintf(stderr, fmt, ap);
-       va_end(ap);
-       (void)fprintf(stderr, "\n");
-       exit(1);
-       /* NOTREACHED */
+       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>";
+
+       // 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);
+       if (macherr)  goto finish;
+
+       // allocate a port to pass to kextd (in case we die)
+       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);
+
+       rval = busyStatus;
+
+finish:
+       if (macherr == BOOTSTRAP_UNKNOWN_SERVICE) {
+               mach_port_mod_refs(tport, myport, MACH_PORT_RIGHT_RECEIVE, -1);
+               rval = 0;
+       } else if (macherr) {
+               warnx("couldn't lock kext manager for reboot: %s",
+                               mach_error_string(macherr));
+               rval = ELAST + 1;
+       }
+       if (rval && myport != MACH_PORT_NULL) {
+               mach_port_mod_refs(tport, myport, MACH_PORT_RIGHT_RECEIVE, -1);
+       }
+
+       return rval;
 }
+#endif