]> git.saurik.com Git - apple/system_cmds.git/blame_incremental - shutdown.tproj/shutdown.c
system_cmds-336.23.tar.gz
[apple/system_cmds.git] / shutdown.tproj / shutdown.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1988, 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1988, 1990, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95";
43#endif
44static const char rcsid[] =
45 "$FreeBSD: src/sbin/shutdown/shutdown.c,v 1.23 2002/03/21 13:20:48 imp Exp $";
46#endif /* not lint */
47
48#include <sys/param.h>
49#include <sys/time.h>
50#include <sys/resource.h>
51#include <sys/syslog.h>
52
53#include <ctype.h>
54#include <err.h>
55#include <fcntl.h>
56#include <pwd.h>
57#include <setjmp.h>
58#include <signal.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <unistd.h>
63
64#include <errno.h>
65#include <bsm/libbsm.h>
66#include <bsm/audit_uevents.h>
67
68#ifdef __APPLE__
69#include "kextmanager.h"
70#include <IOKit/kext/kextmanager_types.h>
71#include <mach/mach_port.h> // allocate
72#include <mach/mach.h> // task_self, etc
73#include <servers/bootstrap.h> // bootstrap
74#endif
75
76#include "pathnames.h"
77
78#ifdef __APPLE__
79#define __unused
80#endif
81
82#ifdef DEBUG
83#undef _PATH_NOLOGIN
84#define _PATH_NOLOGIN "./nologin"
85#endif
86
87#define H *60*60
88#define M *60
89#define S *1
90#define NOLOG_TIME 5*60
91struct interval {
92 int timeleft, timetowait;
93} tlist[] = {
94 { 10 H, 5 H },
95 { 5 H, 3 H },
96 { 2 H, 1 H },
97 { 1 H, 30 M },
98 { 30 M, 10 M },
99 { 20 M, 10 M },
100 { 10 M, 5 M },
101 { 5 M, 3 M },
102 { 2 M, 1 M },
103 { 1 M, 30 S },
104 { 30 S, 30 S },
105 { 0 , 0 }
106};
107#undef H
108#undef M
109#undef S
110
111static time_t offset, shuttime;
112static int dohalt, dopower, doreboot, doups, killflg, mbuflen, oflag = 1;
113static char mbuf[BUFSIZ];
114static const char *nosync, *whom;
115
116void badtime(void);
117#ifdef __APPLE__
118void log_and_exec_reboot_or_halt(void);
119#else
120void die_you_gravy_sucking_pig_dog(void);
121#endif
122void finish(int);
123void getoffset(char *);
124void loop(void);
125void nolog(void);
126void timeout(int);
127void timewarn(int);
128void usage(const char *);
129int audit_shutdown(int);
130#ifdef __APPLE__
131int reserve_reboot(void);
132#endif
133
134int
135main(argc, argv)
136 int argc;
137 char *argv[];
138{
139 char *p, *endp;
140 struct passwd *pw;
141 int arglen, ch, len, readstdin;
142
143#ifndef DEBUG
144 if (geteuid())
145 errx(1, "NOT super-user");
146#endif
147 nosync = NULL;
148 readstdin = 0;
149#ifndef __APPLE__
150 while ((ch = getopt(argc, argv, "-hknopr")) != -1)
151#else
152 while ((ch = getopt(argc, argv, "-hknoru")) != -1)
153#endif
154 switch (ch) {
155 case '-':
156 readstdin = 1;
157 break;
158 case 'h':
159 dohalt = 1;
160 break;
161 case 'k':
162 killflg = 1;
163 oflag = 0;
164 break;
165 case 'n':
166 nosync = "-n";
167 break;
168 case 'o':
169 oflag = 1;
170 break;
171#ifndef __APPLE__
172 case 'p':
173 dopower = 1;
174 break;
175#endif
176 case 'u':
177 doups = 1;
178 break;
179 case 'r':
180 doreboot = 1;
181 break;
182 case '?':
183 default:
184 usage((char *)NULL);
185 }
186 argc -= optind;
187 argv += optind;
188
189 if (argc < 1)
190 usage((char *)NULL);
191
192#ifndef __APPLE__
193 if (killflg + doreboot + dohalt + dopower > 1)
194 usage("incompatible switches -h, -k, -p and -r");
195
196 if (oflag && !(dohalt || dopower || doreboot))
197 usage("-o requires -h, -p or -r");
198#else
199 if (killflg + doreboot + dohalt > 1)
200 usage("incompatible switches -h, -k, and -r");
201
202 if (oflag && !(dohalt || doreboot))
203 usage("-o requires -h or -r");
204
205 if (doups && !dohalt)
206 usage("-u requires -h");
207#endif
208
209 if (nosync != NULL && !oflag)
210 usage("-n requires -o");
211
212 getoffset(*argv++);
213
214 if (*argv) {
215 for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
216 arglen = strlen(*argv);
217 if ((len -= arglen) <= 2)
218 break;
219 if (p != mbuf)
220 *p++ = ' ';
221 memmove(p, *argv, arglen);
222 p += arglen;
223 }
224 *p = '\n';
225 *++p = '\0';
226 }
227
228 if (readstdin) {
229 p = mbuf;
230 endp = mbuf + sizeof(mbuf) - 2;
231 for (;;) {
232 if (!fgets(p, endp - p + 1, stdin))
233 break;
234 for (; *p && p < endp; ++p);
235 if (p == endp) {
236 *p = '\n';
237 *++p = '\0';
238 break;
239 }
240 }
241 }
242 mbuflen = strlen(mbuf);
243
244 if (offset)
245 (void)printf("Shutdown at %.24s.\n", ctime(&shuttime));
246 else
247 (void)printf("Shutdown NOW!\n");
248
249 if (!(whom = getlogin()))
250 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
251
252#ifdef DEBUG
253 audit_shutdown(0);
254 (void)putc('\n', stdout);
255#else
256 (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
257 {
258 int forkpid;
259
260 forkpid = fork();
261 if (forkpid == -1) {
262 audit_shutdown(1);
263 err(1, "fork");
264 }
265 if (forkpid) {
266 errx(0, "[pid %d]", forkpid);
267 }
268 }
269 audit_shutdown(0);
270 setsid();
271#endif
272 openlog("shutdown", LOG_CONS, LOG_AUTH);
273 loop();
274 return(0);
275}
276
277void
278loop()
279{
280 struct interval *tp;
281 u_int sltime;
282 int logged;
283
284 if (offset <= NOLOG_TIME) {
285 logged = 1;
286 nolog();
287 }
288 else
289 logged = 0;
290 tp = tlist;
291 if (tp->timeleft < offset)
292 (void)sleep((u_int)(offset - tp->timeleft));
293 else {
294 while (tp->timeleft && offset < tp->timeleft)
295 ++tp;
296 /*
297 * Warn now, if going to sleep more than a fifth of
298 * the next wait time.
299 */
300 if ((sltime = offset - tp->timeleft)) {
301 if (sltime > (u_int)(tp->timetowait / 5))
302 timewarn(offset);
303 (void)sleep(sltime);
304 }
305 }
306 for (;; ++tp) {
307 timewarn(tp->timeleft);
308 if (!logged && tp->timeleft <= NOLOG_TIME) {
309 logged = 1;
310 nolog();
311 }
312 (void)sleep((u_int)tp->timetowait);
313 if (!tp->timeleft)
314 break;
315 }
316#ifdef __APPLE__
317 log_and_exec_reboot_or_halt();
318#else
319 die_you_gravy_sucking_pig_dog();
320#endif
321}
322
323static jmp_buf alarmbuf;
324
325static const char *restricted_environ[] = {
326 "PATH=" _PATH_STDPATH,
327 NULL
328};
329
330void
331timewarn(timeleft)
332 int timeleft;
333{
334 static int first;
335 static char hostname[MAXHOSTNAMELEN + 1];
336 FILE *pf;
337 char wcmd[MAXPATHLEN + 4];
338 extern const char **environ;
339
340 if (!first++)
341 (void)gethostname(hostname, sizeof(hostname));
342
343 /* undoc -n option to wall suppresses normal wall banner */
344 (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL);
345 environ = restricted_environ;
346 if (!(pf = popen(wcmd, "w"))) {
347 syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
348 return;
349 }
350
351 (void)fprintf(pf,
352 "\007*** %sSystem shutdown message from %s@%s ***\007\n",
353 timeleft ? "": "FINAL ", whom, hostname);
354
355 if (timeleft > 10*60)
356 (void)fprintf(pf, "System going down at %5.5s\n\n",
357 ctime(&shuttime) + 11);
358 else if (timeleft > 59)
359 (void)fprintf(pf, "System going down in %d minute%s\n\n",
360 timeleft / 60, (timeleft > 60) ? "s" : "");
361 else if (timeleft)
362 (void)fprintf(pf, "System going down in 30 seconds\n\n");
363 else
364 (void)fprintf(pf, "System going down IMMEDIATELY\n\n");
365
366 if (mbuflen)
367 (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
368
369 /*
370 * play some games, just in case wall doesn't come back
371 * probably unnecessary, given that wall is careful.
372 */
373 if (!setjmp(alarmbuf)) {
374 (void)signal(SIGALRM, timeout);
375 (void)alarm((u_int)30);
376 (void)pclose(pf);
377 (void)alarm((u_int)0);
378 (void)signal(SIGALRM, SIG_DFL);
379 }
380}
381
382void
383timeout(signo)
384 int signo __unused;
385{
386 longjmp(alarmbuf, 1);
387}
388
389void
390#ifdef __APPLE__
391log_and_exec_reboot_or_halt()
392#else
393die_you_gravy_sucking_pig_dog()
394#endif
395{
396 char *empty_environ[] = { NULL };
397
398#ifdef __APPLE__
399 if ((errno = reserve_reboot()))
400 err(1, "couldn't lock for reboot");
401#endif
402
403 syslog(LOG_NOTICE, "%s%s by %s: %s",
404#ifndef __APPLE__
405 doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" :
406#else
407 doreboot ? "reboot" : dohalt ? "halt" :
408#endif
409 "shutdown", doups?"with UPS delay":"", whom, mbuf);
410#ifndef __APPLE__
411 (void)sleep(2);
412#endif
413
414 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
415 if (killflg) {
416 (void)printf("\rbut you'll have to do it yourself\r\n");
417 exit(0);
418 }
419#ifdef DEBUG
420 if (doreboot)
421 (void)printf("reboot");
422 else if (dohalt)
423 (void)printf("halt");
424#ifndef __APPLE__
425 else if (dopower)
426 (void)printf("power-down");
427#endif
428 if (nosync != NULL)
429 (void)printf(" no sync");
430 (void)printf("\nkill -HUP 1\n");
431#else
432#ifdef __APPLE__
433 {
434 int ws = 0;
435 int fp = fork();
436 if (fp == 0)
437 execl(_PATH_BSHELL, _PATH_BSHELL, "/etc/rc.shutdown", NULL);
438 else if (fp > 0)
439 waitpid(fp, &ws, 0);
440 }
441#endif
442 if (!oflag) {
443 (void)kill(1, doreboot ? SIGINT : /* reboot */
444 dohalt ? SIGUSR1 : /* halt */
445#ifndef __APPLE__
446 dopower ? SIGUSR2 : /* power-down */
447#endif
448 SIGTERM); /* single-user */
449 } else {
450 if (doreboot) {
451 execle(_PATH_REBOOT, "reboot", "-l", nosync,
452 (char *)NULL, empty_environ);
453 syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
454 _PATH_REBOOT);
455 warn(_PATH_REBOOT);
456 }
457 else if (dohalt) {
458 char *halt_args;
459 if(doups) {
460 halt_args = "-lu";
461 } else {
462 halt_args = "-l";
463 }
464 execle(_PATH_HALT, "halt", halt_args, nosync,
465 (char *)NULL, empty_environ);
466 syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
467 _PATH_HALT);
468 warn(_PATH_HALT);
469 }
470#ifndef __APPLE__
471 else if (dopower) {
472 execle(_PATH_HALT, "halt", "-l", "-p", nosync,
473 (char *)NULL, empty_environ);
474 syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
475 _PATH_HALT);
476 warn(_PATH_HALT);
477 }
478#endif
479 (void)kill(1, SIGTERM); /* to single-user */
480 }
481#endif
482 finish(0);
483}
484
485#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2;
486
487void
488getoffset(timearg)
489 char *timearg;
490{
491 struct tm *lt;
492 char *p;
493 time_t now;
494 int this_year;
495
496 (void)time(&now);
497
498 if (!strcasecmp(timearg, "now")) { /* now */
499 offset = 0;
500 shuttime = now;
501 return;
502 }
503
504 if (*timearg == '+') { /* +minutes */
505 if (!isdigit(*++timearg))
506 badtime();
507 if ((offset = atoi(timearg) * 60) < 0)
508 badtime();
509 shuttime = now + offset;
510 return;
511 }
512
513 /* handle hh:mm by getting rid of the colon */
514 for (p = timearg; *p; ++p)
515 if (!isascii(*p) || !isdigit(*p)) {
516 if (*p == ':' && strlen(p) == 3) {
517 p[0] = p[1];
518 p[1] = p[2];
519 p[2] = '\0';
520 }
521 else
522 badtime();
523 }
524
525 unsetenv("TZ"); /* OUR timezone */
526 lt = localtime(&now); /* current time val */
527
528 switch(strlen(timearg)) {
529 case 10:
530 this_year = lt->tm_year;
531 lt->tm_year = ATOI2(timearg);
532 /*
533 * check if the specified year is in the next century.
534 * allow for one year of user error as many people will
535 * enter n - 1 at the start of year n.
536 */
537 if (lt->tm_year < (this_year % 100) - 1)
538 lt->tm_year += 100;
539 /* adjust for the year 2000 and beyond */
540 lt->tm_year += (this_year - (this_year % 100));
541 /* FALLTHROUGH */
542 case 8:
543 lt->tm_mon = ATOI2(timearg);
544 if (--lt->tm_mon < 0 || lt->tm_mon > 11)
545 badtime();
546 /* FALLTHROUGH */
547 case 6:
548 lt->tm_mday = ATOI2(timearg);
549 if (lt->tm_mday < 1 || lt->tm_mday > 31)
550 badtime();
551 /* FALLTHROUGH */
552 case 4:
553 lt->tm_hour = ATOI2(timearg);
554 if (lt->tm_hour < 0 || lt->tm_hour > 23)
555 badtime();
556 lt->tm_min = ATOI2(timearg);
557 if (lt->tm_min < 0 || lt->tm_min > 59)
558 badtime();
559 lt->tm_sec = 0;
560 if ((shuttime = mktime(lt)) == -1)
561 badtime();
562 if ((offset = shuttime - now) < 0)
563 errx(1, "that time is already past.");
564 break;
565 default:
566 badtime();
567 }
568}
569
570#define NOMSG "\n\nNO LOGINS: System going down at "
571void
572nolog()
573{
574#ifndef __APPLE__
575 int logfd;
576 char *ct;
577
578 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */
579 (void)signal(SIGINT, finish);
580 (void)signal(SIGHUP, finish);
581 (void)signal(SIGQUIT, finish);
582 (void)signal(SIGTERM, finish);
583 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
584 0664)) >= 0) {
585 (void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
586 ct = ctime(&shuttime);
587 (void)write(logfd, ct + 11, 5);
588 (void)write(logfd, "\n\n", 2);
589 (void)write(logfd, mbuf, strlen(mbuf));
590 (void)close(logfd);
591 }
592#endif
593}
594
595void
596finish(signo)
597 int signo __unused;
598{
599#ifndef __APPLE__
600 if (!killflg)
601 (void)unlink(_PATH_NOLOGIN);
602#endif
603 exit(0);
604}
605
606void
607badtime()
608{
609 errx(1, "bad time format");
610}
611
612void
613usage(cp)
614 const char *cp;
615{
616 if (cp != NULL)
617 warnx("%s", cp);
618 (void)fprintf(stderr,
619 "usage: shutdown [-] [-h [-u] | -r | -k] [-o [-n]]"
620 " time [warning-message ...]\n");
621 exit(1);
622}
623
624/*
625 * The following tokens are included in the audit record for shutdown
626 * header
627 * subject
628 * return
629 */
630int audit_shutdown(int exitstatus)
631{
632 int aufd;
633 token_t *tok;
634 long au_cond;
635
636 /* If we are not auditing, don't cut an audit record; just return */
637 if (auditon(A_GETCOND, &au_cond, sizeof(long)) < 0) {
638 fprintf(stderr, "shutdown: Could not determine audit condition\n");
639 return 0;
640 }
641 if (au_cond == AUC_NOAUDIT)
642 return 0;
643
644 if((aufd = au_open()) == -1) {
645 fprintf(stderr, "shutdown: Audit Error: au_open() failed\n");
646 exit(1);
647 }
648
649 /* The subject that performed the operation */
650 if((tok = au_to_me()) == NULL) {
651 fprintf(stderr, "shutdown: Audit Error: au_to_me() failed\n");
652 exit(1);
653 }
654 au_write(aufd, tok);
655
656 /* success and failure status */
657 if((tok = au_to_return32(exitstatus, errno)) == NULL) {
658 fprintf(stderr, "shutdown: Audit Error: au_to_return32() failed\n");
659 exit(1);
660 }
661 au_write(aufd, tok);
662
663 if(au_close(aufd, 1, AUE_shutdown) == -1) {
664 fprintf(stderr, "shutdown: Audit Error: au_close() failed\n");
665 exit(1);
666 }
667 return 1;
668}
669
670
671#ifdef __APPLE__
672// XX copied from reboot.c; would be nice to share the code
673
674#define LCK_MAXTRIES 10
675#define LCK_DELAY 30
676/*
677 * contact kextd to lock for reboot
678 */
679int
680reserve_reboot()
681{
682 int rval = ELAST+1;
683 kern_return_t macherr = KERN_FAILURE;
684 mach_port_t tport, bsport, kxport, myport = MACH_PORT_NULL;
685 int busyStatus, nretries = LCK_MAXTRIES;
686 dev_path_t busyDev = "<unknown>";
687
688 // find kextd
689 tport = mach_task_self();
690 if (tport == MACH_PORT_NULL) goto finish;
691 macherr = task_get_bootstrap_port(tport, &bsport);
692 if (macherr) goto finish;
693 macherr = bootstrap_look_up(bsport, KEXTD_SERVER_NAME, &kxport);
694 if (macherr) goto finish;
695
696 // allocate a port to pass to kextd (in case we die)
697 macherr = mach_port_allocate(tport, MACH_PORT_RIGHT_RECEIVE, &myport);
698 if (macherr) goto finish;
699
700 // loop trying to lock for reboot (i.e. no volumes are busy)
701 do {
702 nretries--;
703 macherr = kextmanager_lock_reboot(kxport, myport, busyDev, &busyStatus);
704 if (macherr) goto finish;
705
706 if (busyStatus == EBUSY) {
707 if (*busyDev) {
708 warnx("%s is busy updating; delaying reboot (%d retries left)",
709 busyDev, nretries);
710 } else
711 warnx("kextd still starting up");
712 if (nretries) sleep(LCK_DELAY); // don't sleep the last time
713 }
714 } while (busyStatus == EBUSY && nretries > 0);
715
716 rval = busyStatus;
717
718finish:
719 if (macherr == BOOTSTRAP_UNKNOWN_SERVICE) {
720 mach_port_mod_refs(tport, myport, MACH_PORT_RIGHT_RECEIVE, -1);
721 rval = 0;
722 } else if (macherr) {
723 warnx("couldn't lock kext manager for reboot: %s",
724 mach_error_string(macherr));
725 rval = ELAST + 1;
726 }
727 if (rval && myport != MACH_PORT_NULL) {
728 mach_port_mod_refs(tport, myport, MACH_PORT_RIGHT_RECEIVE, -1);
729 }
730
731 return rval;
732}
733#endif
734