X-Git-Url: https://git.saurik.com/apple/system_cmds.git/blobdiff_plain/1815bff58803503e37009deeb85cfa8c22acf9d9..a8daac8f698fa38f1ca3aaa2a3ed6ac20e5128e2:/shutdown.tproj/shutdown.c diff --git a/shutdown.tproj/shutdown.c b/shutdown.tproj/shutdown.c index 889b0a0..e7a7928 100644 --- a/shutdown.tproj/shutdown.c +++ b/shutdown.tproj/shutdown.c @@ -1,29 +1,7 @@ -/* - * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * "Portions Copyright (c) 1999 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 1.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.apple.com/publicsource 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 OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License." - * - * @APPLE_LICENSE_HEADER_END@ - */ /* * Copyright (c) 1988, 1990, 1993 - * The Regents of the University of California. All rights reserved. + * 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 @@ -33,11 +11,7 @@ * 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. * @@ -54,29 +28,59 @@ * SUCH DAMAGE. */ +#if 0 +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1988, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; +#endif /* not lint */ +#endif +#include +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/sbin/shutdown/shutdown.c,v 1.28 2005/01/25 08:40:51 delphij Exp $"); +#endif + #include #include #include #include #include +#include #include +#include #include #include #include #include #include #include -#include #include +#ifdef __APPLE__ +#include +#include +#include +#include + +#include "kextmanager.h" +#include +#include +#include // allocate +#include // task_self, etc +#include // bootstrap +#include + #include "pathnames.h" +#endif /* __APPLE__ */ #ifdef DEBUG #undef _PATH_NOLOGIN #define _PATH_NOLOGIN "./nologin" -#undef _PATH_FASTBOOT -#define _PATH_FASTBOOT "./fastboot" #endif #define H *60*60 @@ -86,56 +90,77 @@ struct interval { int timeleft, timetowait; } tlist[] = { - 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, - 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, - 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, - 0, 0, + { 10 H, 5 H }, + { 5 H, 3 H }, + { 2 H, 1 H }, + { 1 H, 30 M }, + { 30 M, 10 M }, + { 20 M, 10 M }, + { 10 M, 5 M }, + { 5 M, 3 M }, + { 2 M, 1 M }, + { 1 M, 30 S }, + { 30 S, 30 S }, + { 0 , 0 } }; #undef H #undef M #undef S static time_t offset, shuttime; -static int dofast, dohalt, doreboot, killflg, mbuflen; -static char *nosync, *whom, mbuf[BUFSIZ]; - -void badtime __P((void)); -void die_you_gravy_sucking_pig_dog __P((void)); -void doitfast __P((void)); -void finish __P((int)); -void getoffset __P((char *)); -void loop __P((void)); -void nolog __P((void)); -void timeout __P((int)); -void timewarn __P((int)); -void usage __P((void)); +#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 log_and_exec_reboot_or_halt(void); +#else +void die_you_gravy_sucking_pig_dog(void); +#endif +void finish(int); +void getoffset(char *); +void loop(void); +void nolog(void); +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) { - extern int optind; - register char *p, *endp; + char *p, *endp; struct passwd *pw; int arglen, ch, len, readstdin; #ifndef DEBUG - if (geteuid()) { - (void)fprintf(stderr, "shutdown: NOT super-user\n"); - exit(1); - } + if (geteuid()) + errx(1, "NOT super-user"); #endif nosync = NULL; readstdin = 0; - while ((ch = getopt(argc, argv, "-fhknr")) != EOF) +#ifndef __APPLE__ + while ((ch = getopt(argc, argv, "-hknopr")) != -1) +#else + while ((ch = getopt(argc, argv, "-hknorsu")) != -1) +#endif switch (ch) { case '-': readstdin = 1; break; - case 'f': - dofast = 1; - break; case 'h': dohalt = 1; break; @@ -145,29 +170,55 @@ main(argc, argv) case 'n': nosync = "-n"; break; + case 'o': + oflag = 1; + break; +#ifndef __APPLE__ + case 'p': + 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(); + usage((char *)NULL); } argc -= optind; argv += optind; if (argc < 1) - usage(); + usage((char *)NULL); + +#ifndef __APPLE__ + if (killflg + doreboot + dohalt + dopower > 1) + usage("incompatible switches -h, -k, -p and -r"); + + if (oflag && !(dohalt || dopower || doreboot)) + usage("-o requires -h, -p or -r"); + + 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__ */ - if (dofast && nosync) { - (void)fprintf(stderr, - "shutdown: incompatible switches -f and -n.\n"); - usage(); - } - if (doreboot && dohalt) { - (void)fprintf(stderr, - "shutdown: incompatible switches -h and -r.\n"); - usage(); - } getoffset(*argv++); if (*argv) { @@ -209,26 +260,31 @@ main(argc, 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) { - perror("shutdown: fork"); - exit(1); - } - if (forkpid) { - (void)printf("shutdown: [pid %d]\n", forkpid); - exit(0); + audit_shutdown(1); + err(1, "fork"); } + if (forkpid) + errx(0, "[pid %d]", forkpid); } + audit_shutdown(0); + setsid(); #endif openlog("shutdown", LOG_CONS, LOG_AUTH); loop(); - /* NOTREACHED */ + return(0); } void @@ -248,14 +304,14 @@ loop() if (tp->timeleft < offset) (void)sleep((u_int)(offset - tp->timeleft)); else { - while (offset < tp->timeleft) + while (tp->timeleft && offset < tp->timeleft) ++tp; /* * Warn now, if going to sleep more than a fifth of * the next wait time. */ - if (sltime = offset - tp->timeleft) { - if (sltime > tp->timetowait / 5) + if ((sltime = offset - tp->timeleft)) { + if (sltime > (u_int)(tp->timetowait / 5)) timewarn(offset); (void)sleep(sltime); } @@ -270,25 +326,37 @@ loop() if (!tp->timeleft) break; } +#ifdef __APPLE__ + log_and_exec_reboot_or_halt(); +#else die_you_gravy_sucking_pig_dog(); +#endif } static jmp_buf alarmbuf; +static const char *restricted_environ[] = { + "PATH=" _PATH_STDPATH, + NULL +}; + void -timewarn(timeleft) - int timeleft; +timewarn(int timeleft) { static int first; static char hostname[MAXHOSTNAMELEN + 1]; FILE *pf; char wcmd[MAXPATHLEN + 4]; + /* wall is sometimes missing, e.g. on install media */ + if (access(_PATH_WALL, X_OK) == -1) return; + if (!first++) (void)gethostname(hostname, sizeof(hostname)); /* undoc -n option to wall suppresses normal wall banner */ (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); + environ = restricted_environ; if (!(pf = popen(wcmd, "w"))) { syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); return; @@ -314,7 +382,7 @@ timewarn(timeleft) /* * play some games, just in case wall doesn't come back - * probably unecessary, given that wall is careful. + * probably unnecessary, given that wall is careful. */ if (!setjmp(alarmbuf)) { (void)signal(SIGALRM, timeout); @@ -326,49 +394,118 @@ timewarn(timeleft) } void -timeout(signo) - int signo; +timeout(int signo __unused) { longjmp(alarmbuf, 1); } void +#ifdef __APPLE__ +log_and_exec_reboot_or_halt() +#else 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", - doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); + syslog(LOG_NOTICE, "%s%s by %s: %s", +#ifndef __APPLE__ + doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : +#else + doreboot ? "reboot" : dohalt ? "halt" : dosleep ? "sleep" : +#endif + "shutdown", doups?" with UPS delay":"", whom, mbuf); +#ifndef __APPLE__ (void)sleep(2); +#endif (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); if (killflg) { (void)printf("\rbut you'll have to do it yourself\r\n"); - finish(0); + exit(0); } - if (dofast) - doitfast(); #ifdef DEBUG if (doreboot) (void)printf("reboot"); else if (dohalt) (void)printf("halt"); - if (nosync) +#ifndef __APPLE__ + else if (dopower) + (void)printf("power-down"); + if (nosync != NULL) (void)printf(" no sync"); - if (dofast) - (void)printf(" no fsck"); +#else + else if (dosleep) + (void)printf("sleep"); +#endif (void)printf("\nkill -HUP 1\n"); #else - if (doreboot) { - execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); - syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); - perror("shutdown"); +#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); } - else if (dohalt) { - execle(_PATH_HALT, "halt", "-l", nosync, 0); - syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); - perror("shutdown"); + /* NOT-REACHED */ + +#else /* __APPLE__ */ + if (!oflag) { + (void)kill(1, doreboot ? SIGINT : /* reboot */ + dohalt ? SIGUSR1 : /* halt */ + dopower ? SIGUSR2 : /* power-down */ + SIGTERM); /* single-user */ + } else { + if (doreboot) { + execle(_PATH_REBOOT, "reboot", "-l", nosync, + (char *)NULL, empty_environ); + syslog(LOG_ERR, "shutdown: can't exec %s: %m.", + _PATH_REBOOT); + warn(_PATH_REBOOT); + } + else if (dohalt) { + execle(_PATH_HALT, "halt", "-l", nosync, + (char *)NULL, empty_environ); + syslog(LOG_ERR, "shutdown: can't exec %s: %m.", + _PATH_HALT); + warn(_PATH_HALT); + } + else if (dopower) { + execle(_PATH_HALT, "halt", "-l", "-p", nosync, + (char *)NULL, empty_environ); + syslog(LOG_ERR, "shutdown: can't exec %s: %m.", + _PATH_HALT); + warn(_PATH_HALT); + } + (void)kill(1, SIGTERM); /* to single-user */ } - (void)kill(1, SIGTERM); /* to single user */ +#endif /* __APPLE__ */ #endif finish(0); } @@ -376,30 +513,33 @@ die_you_gravy_sucking_pig_dog() #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; void -getoffset(timearg) - register char *timearg; +getoffset(char *timearg) { - register struct tm *lt; - register char *p; + struct tm *lt; + char *p; time_t now; + int this_year; + + (void)time(&now); if (!strcasecmp(timearg, "now")) { /* now */ offset = 0; + shuttime = now; return; } - (void)time(&now); if (*timearg == '+') { /* +minutes */ if (!isdigit(*++timearg)) badtime(); - offset = atoi(timearg) * 60; + if ((offset = atoi(timearg) * 60) < 0) + badtime(); shuttime = now + offset; return; } /* handle hh:mm by getting rid of the colon */ for (p = timearg; *p; ++p) - if (!isascii(*p) || !isdigit(*p)) + if (!isascii(*p) || !isdigit(*p)) { if (*p == ':' && strlen(p) == 3) { p[0] = p[1]; p[1] = p[2]; @@ -407,13 +547,24 @@ getoffset(timearg) } else badtime(); + } unsetenv("TZ"); /* OUR timezone */ lt = localtime(&now); /* current time val */ switch(strlen(timearg)) { case 10: + this_year = lt->tm_year; lt->tm_year = ATOI2(timearg); + /* + * check if the specified year is in the next century. + * allow for one year of user error as many people will + * enter n - 1 at the start of year n. + */ + if (lt->tm_year < (this_year % 100) - 1) + lt->tm_year += 100; + /* adjust for the year 2000 and beyond */ + lt->tm_year += (this_year - (this_year % 100)); /* FALLTHROUGH */ case 8: lt->tm_mon = ATOI2(timearg); @@ -435,34 +586,19 @@ getoffset(timearg) lt->tm_sec = 0; if ((shuttime = mktime(lt)) == -1) badtime(); - if ((offset = shuttime - now) < 0) { - (void)fprintf(stderr, - "shutdown: that time is already past.\n"); - exit(1); - } + if ((offset = shuttime - now) < 0) + errx(1, "that time is already past."); break; default: badtime(); } } -#define FSMSG "fastboot file for fsck\n" -void -doitfast() -{ - int fastfd; - - if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, - 0664)) >= 0) { - (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); - (void)close(fastfd); - } -} - #define NOMSG "\n\nNO LOGINS: System going down at " void nolog() { +#ifndef __APPLE__ int logfd; char *ct; @@ -480,27 +616,144 @@ nolog() (void)write(logfd, mbuf, strlen(mbuf)); (void)close(logfd); } +#endif /* !__APPLE__ */ } void -finish(signo) - int signo; +finish(int signo __unused) { +#ifndef __APPLE__ if (!killflg) (void)unlink(_PATH_NOLOGIN); +#endif exit(0); } void badtime() { - (void)fprintf(stderr, "shutdown: bad time format.\n"); - exit(1); + errx(1, "bad time format"); } void -usage() +usage(const char *cp) { - fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); + 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__ */ +