/*
* Copyright (c) 1988, 1990, 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.
*
* SUCH DAMAGE.
*/
+#if 0
#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1988, 1990, 1993\n\
#endif /* not lint */
#ifndef lint
-#if 0
static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95";
-#endif
-static const char rcsid[] =
- "$FreeBSD: src/sbin/shutdown/shutdown.c,v 1.23 2002/03/21 13:20:48 imp Exp $";
#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+#ifndef __APPLE__
+__FBSDID("$FreeBSD: src/sbin/shutdown/shutdown.c,v 1.28 2005/01/25 08:40:51 delphij Exp $");
+#endif
#include <sys/param.h>
#include <sys/time.h>
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
+#include <paths.h>
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
-#include "pathnames.h"
-
#ifdef __APPLE__
-#define __unused
-#endif
+#include <errno.h>
+#include <util.h>
+#include <bsm/libbsm.h>
+#include <bsm/audit_uevents.h>
+
+#include "kextmanager.h"
+#include <IOKit/kext/kextmanager_types.h>
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#include <mach/mach_port.h> // allocate
+#include <mach/mach.h> // task_self, etc
+#include <servers/bootstrap.h> // bootstrap
+#include <reboot2.h>
+
+#include "pathnames.h"
+#endif /* __APPLE__ */
#ifdef DEBUG
#undef _PATH_NOLOGIN
#undef S
static time_t offset, shuttime;
-static int dohalt, dopower, doreboot, killflg, mbuflen, oflag = 1;
+#ifdef __APPLE__
+static int dohalt, doreboot, doups, killflg, mbuflen, oflag;
+#else
+static int dohalt, dopower, doreboot, killflg, mbuflen, oflag;
+#endif
static char mbuf[BUFSIZ];
static const char *nosync, *whom;
+#ifdef __APPLE__
+static int dosleep;
+#endif
void badtime(void);
#ifdef __APPLE__
void timeout(int);
void timewarn(int);
void usage(const char *);
+#ifdef __APPLE__
+int audit_shutdown(int);
+int reserve_reboot(void);
+#endif
+
+extern const char **environ;
int
-main(argc, argv)
- int argc;
- char *argv[];
+main(int argc, char **argv)
{
char *p, *endp;
struct passwd *pw;
#ifndef __APPLE__
while ((ch = getopt(argc, argv, "-hknopr")) != -1)
#else
- while ((ch = getopt(argc, argv, "-hknor")) != -1)
+ while ((ch = getopt(argc, argv, "-hknorsu")) != -1)
#endif
switch (ch) {
case '-':
dopower = 1;
break;
#endif
+ case 'u':
+ doups = 1;
+ break;
case 'r':
doreboot = 1;
break;
+#ifdef __APPLE__
+ case 's':
+ dosleep = 1;
+ break;
+#endif
case '?':
default:
usage((char *)NULL);
if (oflag && !(dohalt || dopower || doreboot))
usage("-o requires -h, -p or -r");
-#else
- if (killflg + doreboot + dohalt > 1)
- usage("incompatible switches -h, -k, and -r");
-
- if (oflag && !(dohalt || doreboot))
- usage("-o requires -h or -r");
-#endif
if (nosync != NULL && !oflag)
usage("-n requires -o");
+#else /* !__APPLE__ */
+ if (killflg + doreboot + dohalt + dosleep > 1)
+ usage("incompatible switches -h, -k, -r, and -s");
+
+ if (!(dohalt || doreboot || dosleep || killflg))
+ usage("-h, -r, -s, or -k is required");
+
+ if (doups && !dohalt)
+ usage("-u requires -h");
+#endif /* !__APPLE__ */
getoffset(*argv++);
whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
#ifdef DEBUG
+ audit_shutdown(0);
(void)putc('\n', stdout);
#else
(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
+#ifdef __APPLE__
+ if (offset) {
+#else
{
+#endif
int forkpid;
forkpid = fork();
- if (forkpid == -1)
+ if (forkpid == -1) {
+ audit_shutdown(1);
err(1, "fork");
+ }
if (forkpid)
errx(0, "[pid %d]", forkpid);
}
+ audit_shutdown(0);
setsid();
#endif
openlog("shutdown", LOG_CONS, LOG_AUTH);
};
void
-timewarn(timeleft)
- int timeleft;
+timewarn(int timeleft)
{
static int first;
static char hostname[MAXHOSTNAMELEN + 1];
FILE *pf;
char wcmd[MAXPATHLEN + 4];
- extern const char **environ;
+
+ /* wall is sometimes missing, e.g. on install media */
+ if (access(_PATH_WALL, X_OK) == -1) return;
if (!first++)
(void)gethostname(hostname, sizeof(hostname));
}
void
-timeout(signo)
- int signo __unused;
+timeout(int signo __unused)
{
longjmp(alarmbuf, 1);
}
die_you_gravy_sucking_pig_dog()
#endif
{
+#ifndef __APPLE__
char *empty_environ[] = { NULL };
+#else
+ if ((errno = reserve_reboot()))
+ err(1, "couldn't lock for reboot");
+#endif
- syslog(LOG_NOTICE, "%s by %s: %s",
+ syslog(LOG_NOTICE, "%s%s by %s: %s",
#ifndef __APPLE__
doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" :
#else
- doreboot ? "reboot" : dohalt ? "halt" :
+ doreboot ? "reboot" : dohalt ? "halt" : dosleep ? "sleep" :
#endif
- "shutdown", whom, mbuf);
+ "shutdown", doups?" with UPS delay":"", whom, mbuf);
#ifndef __APPLE__
(void)sleep(2);
#endif
#ifndef __APPLE__
else if (dopower)
(void)printf("power-down");
-#endif
if (nosync != NULL)
(void)printf(" no sync");
+#else
+ else if (dosleep)
+ (void)printf("sleep");
+#endif
(void)printf("\nkill -HUP 1\n");
#else
+#ifdef __APPLE__
+ if (dosleep) {
+ mach_port_t mp;
+ io_connect_t fb;
+ kern_return_t kr = IOMasterPort(bootstrap_port, &mp);
+ if (kr == kIOReturnSuccess) {
+ fb = IOPMFindPowerManagement(mp);
+ if (fb != IO_OBJECT_NULL) {
+ IOReturn err = IOPMSleepSystem(fb);
+ if (err != kIOReturnSuccess) {
+ fprintf(stderr, "shutdown: sleep failed (0x%08x)\n", err);
+ kr = -1;
+ }
+ }
+ }
+ exit((kr == kIOReturnSuccess) ? 0 : 1);
+ } else {
+ int howto = 0;
+
+ logwtmp("~", "shutdown", "");
+
+ if (dohalt) howto |= RB_HALT;
+ if (doups) howto |= RB_UPSDELAY;
+ if (nosync) howto |= RB_NOSYNC;
+
+ // launchd(8) handles reboot. This call returns NULL on success.
+ exit(reboot2(howto) == NULL ? EXIT_SUCCESS : EXIT_FAILURE);
+ }
+ /* NOT-REACHED */
+
+#else /* __APPLE__ */
if (!oflag) {
(void)kill(1, doreboot ? SIGINT : /* reboot */
dohalt ? SIGUSR1 : /* halt */
-#ifndef __APPLE__
dopower ? SIGUSR2 : /* power-down */
-#endif
SIGTERM); /* single-user */
} else {
if (doreboot) {
_PATH_HALT);
warn(_PATH_HALT);
}
-#ifndef __APPLE__
else if (dopower) {
execle(_PATH_HALT, "halt", "-l", "-p", nosync,
(char *)NULL, empty_environ);
_PATH_HALT);
warn(_PATH_HALT);
}
-#endif
(void)kill(1, SIGTERM); /* to single-user */
}
+#endif /* __APPLE__ */
#endif
finish(0);
}
#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2;
void
-getoffset(timearg)
- char *timearg;
+getoffset(char *timearg)
{
struct tm *lt;
char *p;
(void)write(logfd, mbuf, strlen(mbuf));
(void)close(logfd);
}
-#endif
+#endif /* !__APPLE__ */
}
void
-finish(signo)
- int signo __unused;
+finish(int signo __unused)
{
#ifndef __APPLE__
if (!killflg)
}
void
-usage(cp)
- const char *cp;
+usage(const char *cp)
{
if (cp != NULL)
warnx("%s", cp);
(void)fprintf(stderr,
+#ifdef __APPLE__
+ "usage: shutdown [-] [-h [-u] [-n] | -r [-n] | -s | -k]"
+#else
"usage: shutdown [-] [-h | -p | -r | -k] [-o [-n]]"
+#endif
" time [warning-message ...]\n");
exit(1);
}
+
+#ifdef __APPLE__
+/*
+ * The following tokens are included in the audit record for shutdown
+ * header
+ * subject
+ * return
+ */
+int audit_shutdown(int exitstatus)
+{
+ int aufd;
+ token_t *tok;
+ long au_cond;
+
+ /* If we are not auditing, don't cut an audit record; just return */
+ if (auditon(A_GETCOND, &au_cond, sizeof(long)) < 0) {
+ fprintf(stderr, "shutdown: Could not determine audit condition\n");
+ return 0;
+ }
+ if (au_cond == AUC_NOAUDIT)
+ return 0;
+
+ if((aufd = au_open()) == -1) {
+ fprintf(stderr, "shutdown: Audit Error: au_open() failed\n");
+ exit(1);
+ }
+
+ /* The subject that performed the operation */
+ if((tok = au_to_me()) == NULL) {
+ fprintf(stderr, "shutdown: Audit Error: au_to_me() failed\n");
+ exit(1);
+ }
+ au_write(aufd, tok);
+
+ /* success and failure status */
+ if((tok = au_to_return32(exitstatus, errno)) == NULL) {
+ fprintf(stderr, "shutdown: Audit Error: au_to_return32() failed\n");
+ exit(1);
+ }
+ au_write(aufd, tok);
+
+ if(au_close(aufd, 1, AUE_shutdown) == -1) {
+ fprintf(stderr, "shutdown: Audit Error: au_close() failed\n");
+ exit(1);
+ }
+ return 1;
+}
+
+
+// XX copied from reboot.tproj/reboot.c; it would be nice to share the code
+
+#define WAITFORLOCK 1
+/*
+ * contact kextd to lock for reboot
+ */
+int
+reserve_reboot()
+{
+ int rval = ELAST + 1;
+ kern_return_t macherr = KERN_FAILURE;
+ mach_port_t kxport, tport = MACH_PORT_NULL, myport = MACH_PORT_NULL;
+ int busyStatus = ELAST + 1;
+ mountpoint_t busyVol;
+
+ macherr = bootstrap_look_up(bootstrap_port, KEXTD_SERVER_NAME, &kxport);
+ 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;
+
+ // try to lock for reboot
+ macherr = kextmanager_lock_reboot(kxport, myport, !WAITFORLOCK, busyVol,
+ &busyStatus);
+ if (macherr) goto finish;
+
+ 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;
+ }
+
+ if (busyStatus == EALREADY) {
+ // reboot already in progress
+ rval = 0;
+ } else {
+ rval = busyStatus;
+ }
+
+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;
+}
+#endif /* __APPLE__ */
+