X-Git-Url: https://git.saurik.com/apple/system_cmds.git/blobdiff_plain/1815bff58803503e37009deeb85cfa8c22acf9d9..b180c247da3427572377f1dda16026ceef181844:/shutdown.tproj/shutdown.c diff --git a/shutdown.tproj/shutdown.c b/shutdown.tproj/shutdown.c index 889b0a0..91ea293 100644 --- a/shutdown.tproj/shutdown.c +++ b/shutdown.tproj/shutdown.c @@ -1,29 +1,6 @@ -/* - * 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. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,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. @@ -54,12 +31,27 @@ * SUCH DAMAGE. */ +#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 +#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 */ + #include #include #include #include #include +#include #include #include #include @@ -67,16 +59,29 @@ #include #include #include -#include #include +#include +#include +#include + +#ifdef __APPLE__ +#include "kextmanager.h" +#include +#include // allocate +#include // task_self, etc +#include // bootstrap +#endif + #include "pathnames.h" +#ifdef __APPLE__ +#define __unused +#endif + #ifdef DEBUG #undef _PATH_NOLOGIN #define _PATH_NOLOGIN "./nologin" -#undef _PATH_FASTBOOT -#define _PATH_FASTBOOT "./fastboot" #endif #define H *60*60 @@ -86,88 +91,124 @@ 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)); +static int dohalt, dopower, doreboot, doups, killflg, mbuflen, oflag = 1; +static char mbuf[BUFSIZ]; +static const char *nosync, *whom; + +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 *); +int audit_shutdown(int); +#ifdef __APPLE__ +int reserve_reboot(void); +#endif int main(argc, argv) 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, "-hknoru")) != -1) +#endif switch (ch) { case '-': readstdin = 1; break; - case 'f': - dofast = 1; - break; case 'h': dohalt = 1; break; case 'k': killflg = 1; + oflag = 0; break; 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; 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"); +#else + if (killflg + doreboot + dohalt > 1) + usage("incompatible switches -h, -k, and -r"); + + if (oflag && !(dohalt || doreboot)) + usage("-o requires -h or -r"); + + if (doups && !dohalt) + usage("-u requires -h"); +#endif + + if (nosync != NULL && !oflag) + usage("-n requires -o"); - 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,6 +250,7 @@ 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); @@ -217,18 +259,19 @@ main(argc, argv) forkpid = fork(); if (forkpid == -1) { - perror("shutdown: fork"); - exit(1); + audit_shutdown(1); + err(1, "fork"); } if (forkpid) { - (void)printf("shutdown: [pid %d]\n", forkpid); - exit(0); + errx(0, "[pid %d]", forkpid); } } + audit_shutdown(0); + setsid(); #endif openlog("shutdown", LOG_CONS, LOG_AUTH); loop(); - /* NOTREACHED */ + return(0); } void @@ -248,14 +291,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,11 +313,20 @@ 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; @@ -283,12 +335,14 @@ timewarn(timeleft) static char hostname[MAXHOSTNAMELEN + 1]; FILE *pf; char wcmd[MAXPATHLEN + 4]; + extern const char **environ; 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 +368,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); @@ -327,48 +381,103 @@ timewarn(timeleft) void timeout(signo) - int signo; + int signo __unused; { longjmp(alarmbuf, 1); } void +#ifdef __APPLE__ +log_and_exec_reboot_or_halt() +#else die_you_gravy_sucking_pig_dog() +#endif { + char *empty_environ[] = { NULL }; + +#ifdef __APPLE__ + 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" : +#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"); +#endif + if (nosync != NULL) (void)printf(" no sync"); - if (dofast) - (void)printf(" no fsck"); (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__ + { + int ws = 0; + int fp = fork(); + if (fp == 0) + execl(_PATH_BSHELL, _PATH_BSHELL, "/etc/rc.shutdown", NULL); + else if (fp > 0) + waitpid(fp, &ws, 0); } - else if (dohalt) { - execle(_PATH_HALT, "halt", "-l", nosync, 0); - syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); - perror("shutdown"); +#endif + if (!oflag) { + (void)kill(1, doreboot ? SIGINT : /* reboot */ + dohalt ? SIGUSR1 : /* halt */ +#ifndef __APPLE__ + dopower ? SIGUSR2 : /* power-down */ +#endif + 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) { + char *halt_args; + if(doups) { + halt_args = "-lu"; + } else { + halt_args = "-l"; + } + execle(_PATH_HALT, "halt", halt_args, nosync, + (char *)NULL, empty_environ); + syslog(LOG_ERR, "shutdown: can't exec %s: %m.", + _PATH_HALT); + warn(_PATH_HALT); + } +#ifndef __APPLE__ + 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); + } +#endif + (void)kill(1, SIGTERM); /* to single-user */ } - (void)kill(1, SIGTERM); /* to single user */ #endif finish(0); } @@ -377,29 +486,33 @@ die_you_gravy_sucking_pig_dog() void getoffset(timearg) - register char *timearg; + 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 +520,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 +559,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 +589,146 @@ nolog() (void)write(logfd, mbuf, strlen(mbuf)); (void)close(logfd); } +#endif } void finish(signo) - int signo; + 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(cp) + const char *cp; { - fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); + if (cp != NULL) + warnx("%s", cp); + (void)fprintf(stderr, + "usage: shutdown [-] [-h [-u] | -r | -k] [-o [-n]]" + " time [warning-message ...]\n"); exit(1); } + +/* + * 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; +} + + +#ifdef __APPLE__ +// XX copied from reboot.c; would be nice to share the code + +#define LCK_MAXTRIES 10 +#define LCK_DELAY 30 +/* + * contact kextd to lock for reboot + */ +int +reserve_reboot() +{ + 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 = ""; + + // 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 +