2  * Copyright (c) 1988, 1990, 1993 
   3  *      The Regents of the University of California.  All rights reserved. 
   4  * Portions copyright (c) 2007 Apple Inc.  All rights reserved. 
   6  * Redistribution and use in source and binary forms, with or without 
   7  * modification, are permitted provided that the following conditions 
   9  * 1. Redistributions of source code must retain the above copyright 
  10  *    notice, this list of conditions and the following disclaimer. 
  11  * 2. Redistributions in binary form must reproduce the above copyright 
  12  *    notice, this list of conditions and the following disclaimer in the 
  13  *    documentation and/or other materials provided with the distribution. 
  14  * 3. Neither the name of the University nor the names of its contributors 
  15  *    may be used to endorse or promote products derived from this software 
  16  *    without specific prior written permission. 
  18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 
  19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
  20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 
  22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
  23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
  24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
  25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
  26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
  27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
  33 static const char copyright
[] = 
  34 "@(#) Copyright (c) 1988, 1990, 1993\n\ 
  35         The Regents of the University of California.  All rights reserved.\n"; 
  39 static char sccsid
[] = "@(#)shutdown.c  8.4 (Berkeley) 4/28/95"; 
  42 #include <sys/cdefs.h> 
  44 __FBSDID("$FreeBSD: src/sbin/shutdown/shutdown.c,v 1.28 2005/01/25 08:40:51 delphij Exp $"); 
  47 #include <sys/param.h> 
  49 #include <sys/resource.h> 
  50 #include <sys/syslog.h> 
  67 #include <bsm/libbsm.h> 
  68 #include <bsm/audit_uevents.h> 
  70 #include "kextmanager.h" 
  71 #include <IOKit/kext/kextmanager_types.h> 
  72 #include <IOKit/pwr_mgt/IOPMLib.h> 
  73 #include <mach/mach_port.h>             // allocate 
  74 #include <mach/mach.h>                  // task_self, etc 
  75 #include <servers/bootstrap.h>  // bootstrap 
  78 #include "pathnames.h" 
  79 #endif /* __APPLE__ */ 
  83 #define _PATH_NOLOGIN   "./nologin" 
  89 #define NOLOG_TIME      5*60 
  91         int timeleft
, timetowait
; 
 110 static time_t offset
, shuttime
; 
 112 static int dohalt
, doreboot
, doups
, killflg
, mbuflen
, oflag
; 
 114 static int dohalt
, dopower
, doreboot
, killflg
, mbuflen
, oflag
; 
 116 static char mbuf
[BUFSIZ
]; 
 117 static const char *nosync
, *whom
; 
 124 void log_and_exec_reboot_or_halt(void); 
 126 void die_you_gravy_sucking_pig_dog(void); 
 129 void getoffset(char *); 
 134 void usage(const char *); 
 136 int audit_shutdown(int); 
 137 int reserve_reboot(void); 
 140 extern const char **environ
; 
 143 main(int argc
, char **argv
) 
 147         int arglen
, ch
, len
, readstdin
; 
 151                 errx(1, "NOT super-user"); 
 156         while ((ch 
= getopt(argc
, argv
, "-hknopr")) != -1) 
 158         while ((ch 
= getopt(argc
, argv
, "-hknorsu")) != -1) 
 203         if (killflg 
+ doreboot 
+ dohalt 
+ dopower 
> 1) 
 204                 usage("incompatible switches -h, -k, -p and -r"); 
 206         if (oflag 
&& !(dohalt 
|| dopower 
|| doreboot
)) 
 207                 usage("-o requires -h, -p or -r"); 
 209         if (nosync 
!= NULL 
&& !oflag
) 
 210                 usage("-n requires -o"); 
 211 #else /* !__APPLE__ */ 
 212         if (killflg 
+ doreboot 
+ dohalt 
+ dosleep 
> 1) 
 213                 usage("incompatible switches -h, -k, -r, and -s"); 
 215         if (!(dohalt 
|| doreboot 
|| dosleep 
|| killflg
)) 
 216                 usage("-h, -r, -s, or -k is required"); 
 218         if (doups 
&& !dohalt
) 
 219                 usage("-u requires -h"); 
 220 #endif /* !__APPLE__ */ 
 225                 for (p 
= mbuf
, len 
= sizeof(mbuf
); *argv
; ++argv
) { 
 226                         arglen 
= strlen(*argv
); 
 227                         if ((len 
-= arglen
) <= 2) 
 231                         memmove(p
, *argv
, arglen
); 
 240                 endp 
= mbuf 
+ sizeof(mbuf
) - 2; 
 242                         if (!fgets(p
, endp 
- p 
+ 1, stdin
)) 
 244                         for (; *p 
&&  p 
< endp
; ++p
); 
 252         mbuflen 
= strlen(mbuf
); 
 255                 (void)printf("Shutdown at %.24s.\n", ctime(&shuttime
)); 
 257                 (void)printf("Shutdown NOW!\n"); 
 259         if (!(whom 
= getlogin())) 
 260                 whom 
= (pw 
= getpwuid(getuid())) ? pw
->pw_name 
: "???"; 
 264         (void)putc('\n', stdout
); 
 266         (void)setpriority(PRIO_PROCESS
, 0, PRIO_MIN
); 
 280                         errx(0, "[pid %d]", forkpid
); 
 285         openlog("shutdown", LOG_CONS
, LOG_AUTH
); 
 297         if (offset 
<= NOLOG_TIME
) { 
 304         if (tp
->timeleft 
< offset
) 
 305                 (void)sleep((u_int
)(offset 
- tp
->timeleft
)); 
 307                 while (tp
->timeleft 
&& offset 
< tp
->timeleft
) 
 310                  * Warn now, if going to sleep more than a fifth of 
 311                  * the next wait time. 
 313                 if ((sltime 
= offset 
- tp
->timeleft
)) { 
 314                         if (sltime 
> (u_int
)(tp
->timetowait 
/ 5)) 
 320                 timewarn(tp
->timeleft
); 
 321                 if (!logged 
&& tp
->timeleft 
<= NOLOG_TIME
) { 
 325                 (void)sleep((u_int
)tp
->timetowait
); 
 330         log_and_exec_reboot_or_halt(); 
 332         die_you_gravy_sucking_pig_dog(); 
 336 static jmp_buf alarmbuf
; 
 338 static const char *restricted_environ
[] = { 
 339         "PATH=" _PATH_STDPATH
, 
 344 timewarn(int timeleft
) 
 347         static char hostname
[MAXHOSTNAMELEN 
+ 1]; 
 349         char wcmd
[MAXPATHLEN 
+ 4]; 
 351         /* wall is sometimes missing, e.g. on install media */ 
 352         if (access(_PATH_WALL
, X_OK
) == -1) return; 
 355                 (void)gethostname(hostname
, sizeof(hostname
)); 
 357         /* undoc -n option to wall suppresses normal wall banner */ 
 358         (void)snprintf(wcmd
, sizeof(wcmd
), "%s -n", _PATH_WALL
); 
 359         environ 
= restricted_environ
; 
 360         if (!(pf 
= popen(wcmd
, "w"))) { 
 361                 syslog(LOG_ERR
, "shutdown: can't find %s: %m", _PATH_WALL
); 
 366             "\007*** %sSystem shutdown message from %s@%s ***\007\n", 
 367             timeleft 
? "": "FINAL ", whom
, hostname
); 
 369         if (timeleft 
> 10*60) 
 370                 (void)fprintf(pf
, "System going down at %5.5s\n\n", 
 371                     ctime(&shuttime
) + 11); 
 372         else if (timeleft 
> 59) 
 373                 (void)fprintf(pf
, "System going down in %d minute%s\n\n", 
 374                     timeleft 
/ 60, (timeleft 
> 60) ? "s" : ""); 
 376                 (void)fprintf(pf
, "System going down in 30 seconds\n\n"); 
 378                 (void)fprintf(pf
, "System going down IMMEDIATELY\n\n"); 
 381                 (void)fwrite(mbuf
, sizeof(*mbuf
), mbuflen
, pf
); 
 384          * play some games, just in case wall doesn't come back 
 385          * probably unnecessary, given that wall is careful. 
 387         if (!setjmp(alarmbuf
)) { 
 388                 (void)signal(SIGALRM
, timeout
); 
 389                 (void)alarm((u_int
)30); 
 391                 (void)alarm((u_int
)0); 
 392                 (void)signal(SIGALRM
, SIG_DFL
); 
 397 timeout(int signo __unused
) 
 399         longjmp(alarmbuf
, 1); 
 404 log_and_exec_reboot_or_halt() 
 406 die_you_gravy_sucking_pig_dog() 
 410         char *empty_environ
[] = { NULL 
}; 
 412         if ((errno 
= reserve_reboot())) 
 413                 err(1, "couldn't lock for reboot"); 
 416         syslog(LOG_NOTICE
, "%s%s by %s: %s", 
 418             doreboot 
? "reboot" : dohalt 
? "halt" : dopower 
? "power-down" :  
 420             doreboot 
? "reboot" : dohalt 
? "halt" : dosleep 
? "sleep" : 
 422             "shutdown", doups
?" with UPS delay":"", whom
, mbuf
); 
 427         (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 
 429                 (void)printf("\rbut you'll have to do it yourself\r\n"); 
 434                 (void)printf("reboot"); 
 436                 (void)printf("halt"); 
 439                 (void)printf("power-down"); 
 441                 (void)printf(" no sync"); 
 444                 (void)printf("sleep"); 
 446         (void)printf("\nkill -HUP 1\n"); 
 452                 kern_return_t kr 
= IOMasterPort(bootstrap_port
, &mp
); 
 453                 if (kr 
== kIOReturnSuccess
) { 
 454                         fb 
= IOPMFindPowerManagement(mp
); 
 455                         if (fb 
!= IO_OBJECT_NULL
) { 
 456                                 IOReturn err 
= IOPMSleepSystem(fb
); 
 457                                 if (err 
!= kIOReturnSuccess
) { 
 458                                         fprintf(stderr
, "shutdown: sleep failed (0x%08x)\n", err
); 
 463                 exit((kr 
== kIOReturnSuccess
) ? 0 : 1); 
 467                 logwtmp("~", "shutdown", ""); 
 469                 if (dohalt
) howto 
|= RB_HALT
; 
 470                 if (doups
) howto 
|= RB_UPSDELAY
; 
 471                 if (nosync
) howto 
|= RB_NOSYNC
; 
 473                 // launchd(8) handles reboot.  This call returns NULL on success. 
 474                 exit(reboot2(howto
) == NULL 
? EXIT_SUCCESS 
: EXIT_FAILURE
); 
 478 #else /* __APPLE__ */ 
 480                 (void)kill(1, doreboot 
? SIGINT 
:       /* reboot */ 
 481                               dohalt 
? SIGUSR1 
:        /* halt */ 
 482                               dopower 
? SIGUSR2 
:       /* power-down */ 
 483                               SIGTERM
);                 /* single-user */ 
 486                         execle(_PATH_REBOOT
, "reboot", "-l", nosync
,  
 487                                 (char *)NULL
, empty_environ
); 
 488                         syslog(LOG_ERR
, "shutdown: can't exec %s: %m.", 
 493                         execle(_PATH_HALT
, "halt", "-l", nosync
, 
 494                                 (char *)NULL
, empty_environ
); 
 495                         syslog(LOG_ERR
, "shutdown: can't exec %s: %m.", 
 500                         execle(_PATH_HALT
, "halt", "-l", "-p", nosync
, 
 501                                 (char *)NULL
, empty_environ
); 
 502                         syslog(LOG_ERR
, "shutdown: can't exec %s: %m.", 
 506                 (void)kill(1, SIGTERM
);         /* to single-user */ 
 508 #endif /* __APPLE__ */ 
 513 #define ATOI2(p)        (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 
 516 getoffset(char *timearg
) 
 525         if (!strcasecmp(timearg
, "now")) {              /* now */ 
 531         if (*timearg 
== '+') {                          /* +minutes */ 
 532                 if (!isdigit(*++timearg
)) 
 534                 if ((offset 
= atoi(timearg
) * 60) < 0) 
 536                 shuttime 
= now 
+ offset
; 
 540         /* handle hh:mm by getting rid of the colon */ 
 541         for (p 
= timearg
; *p
; ++p
) 
 542                 if (!isascii(*p
) || !isdigit(*p
)) { 
 543                         if (*p 
== ':' && strlen(p
) == 3) { 
 552         unsetenv("TZ");                                 /* OUR timezone */ 
 553         lt 
= localtime(&now
);                           /* current time val */ 
 555         switch(strlen(timearg
)) { 
 557                 this_year 
= lt
->tm_year
; 
 558                 lt
->tm_year 
= ATOI2(timearg
); 
 560                  * check if the specified year is in the next century. 
 561                  * allow for one year of user error as many people will 
 562                  * enter n - 1 at the start of year n. 
 564                 if (lt
->tm_year 
< (this_year 
% 100) - 1) 
 566                 /* adjust for the year 2000 and beyond */ 
 567                 lt
->tm_year 
+= (this_year 
- (this_year 
% 100)); 
 570                 lt
->tm_mon 
= ATOI2(timearg
); 
 571                 if (--lt
->tm_mon 
< 0 || lt
->tm_mon 
> 11) 
 575                 lt
->tm_mday 
= ATOI2(timearg
); 
 576                 if (lt
->tm_mday 
< 1 || lt
->tm_mday 
> 31) 
 580                 lt
->tm_hour 
= ATOI2(timearg
); 
 581                 if (lt
->tm_hour 
< 0 || lt
->tm_hour 
> 23) 
 583                 lt
->tm_min 
= ATOI2(timearg
); 
 584                 if (lt
->tm_min 
< 0 || lt
->tm_min 
> 59) 
 587                 if ((shuttime 
= mktime(lt
)) == -1) 
 589                 if ((offset 
= shuttime 
- now
) < 0) 
 590                         errx(1, "that time is already past."); 
 597 #define NOMSG   "\n\nNO LOGINS: System going down at " 
 605         (void)unlink(_PATH_NOLOGIN
);    /* in case linked to another file */ 
 606         (void)signal(SIGINT
, finish
); 
 607         (void)signal(SIGHUP
, finish
); 
 608         (void)signal(SIGQUIT
, finish
); 
 609         (void)signal(SIGTERM
, finish
); 
 610         if ((logfd 
= open(_PATH_NOLOGIN
, O_WRONLY
|O_CREAT
|O_TRUNC
, 
 612                 (void)write(logfd
, NOMSG
, sizeof(NOMSG
) - 1); 
 613                 ct 
= ctime(&shuttime
); 
 614                 (void)write(logfd
, ct 
+ 11, 5); 
 615                 (void)write(logfd
, "\n\n", 2); 
 616                 (void)write(logfd
, mbuf
, strlen(mbuf
)); 
 619 #endif /* !__APPLE__ */ 
 623 finish(int signo __unused
) 
 627                 (void)unlink(_PATH_NOLOGIN
); 
 635         errx(1, "bad time format"); 
 639 usage(const char *cp
) 
 643         (void)fprintf(stderr
, 
 645             "usage: shutdown [-] [-h [-u] [-n] | -r [-n] | -s | -k]" 
 647             "usage: shutdown [-] [-h | -p | -r | -k] [-o [-n]]" 
 649             " time [warning-message ...]\n"); 
 655  * The following tokens are included in the audit record for shutdown 
 660 int audit_shutdown(int exitstatus
) 
 666         /* If we are not auditing, don't cut an audit record; just return */ 
 667         if (auditon(A_GETCOND
, &au_cond
, sizeof(long)) < 0) { 
 668                 fprintf(stderr
, "shutdown: Could not determine audit condition\n"); 
 671         if (au_cond 
== AUC_NOAUDIT
) 
 674         if((aufd 
= au_open()) == -1) { 
 675                 fprintf(stderr
, "shutdown: Audit Error: au_open() failed\n"); 
 679         /* The subject that performed the operation */ 
 680         if((tok 
= au_to_me()) == NULL
) { 
 681                 fprintf(stderr
, "shutdown: Audit Error: au_to_me() failed\n"); 
 686         /* success and failure status */ 
 687         if((tok 
= au_to_return32(exitstatus
, errno
)) == NULL
) { 
 688                 fprintf(stderr
, "shutdown: Audit Error: au_to_return32() failed\n"); 
 693         if(au_close(aufd
, 1, AUE_shutdown
) == -1) { 
 694                 fprintf(stderr
, "shutdown: Audit Error: au_close() failed\n"); 
 701 // XX copied from reboot.tproj/reboot.c; it would be nice to share the code 
 703 #define WAITFORLOCK 1 
 705  * contact kextd to lock for reboot 
 710     int rval 
= ELAST 
+ 1; 
 711     kern_return_t macherr 
= KERN_FAILURE
; 
 712     mach_port_t kxport
, tport 
= MACH_PORT_NULL
, myport 
= MACH_PORT_NULL
; 
 713     int busyStatus 
= ELAST 
+ 1; 
 714     mountpoint_t busyVol
; 
 716     macherr 
= bootstrap_look_up(bootstrap_port
, KEXTD_SERVER_NAME
, &kxport
); 
 717     if (macherr
)  goto finish
; 
 719     // allocate a port to pass to kextd (in case we die) 
 720     tport 
= mach_task_self(); 
 721     if (tport 
== MACH_PORT_NULL
)  goto finish
; 
 722     macherr 
= mach_port_allocate(tport
, MACH_PORT_RIGHT_RECEIVE
, &myport
); 
 723     if (macherr
)  goto finish
; 
 725     // try to lock for reboot 
 726     macherr 
= kextmanager_lock_reboot(kxport
, myport
, !WAITFORLOCK
, busyVol
, 
 728     if (macherr
)  goto finish
; 
 730     if (busyStatus 
== EBUSY
) { 
 731         warnx("%s is busy updating; waiting for lock", busyVol
); 
 732         macherr 
= kextmanager_lock_reboot(kxport
, myport
, WAITFORLOCK
, 
 733                                           busyVol
, &busyStatus
); 
 734         if (macherr
)    goto finish
; 
 737     if (busyStatus 
== EALREADY
) { 
 738         // reboot already in progress 
 745     // in general, we want to err on the side of allowing the reboot 
 747         if (macherr 
!= BOOTSTRAP_UNKNOWN_SERVICE
) 
 748             warnx("WARNING: couldn't lock kext manager for reboot: %s", 
 749                     mach_error_string(macherr
)); 
 752     // unless we got the lock, clean up our port 
 753     if (busyStatus 
!= 0 && myport 
!= MACH_PORT_NULL
) 
 754         mach_port_mod_refs(tport
, myport
, MACH_PORT_RIGHT_RECEIVE
, -1); 
 758 #endif /* __APPLE__ */