Annotation of src/sbin/shutdown/shutdown.c, Revision 1.55
1.55 ! joerg 1: /* $NetBSD: shutdown.c,v 1.54 2011/02/16 19:33:48 wiz Exp $ */
1.9 cgd 2:
1.1 cgd 3: /*
1.7 mycroft 4: * Copyright (c) 1988, 1990, 1993
5: * The Regents of the University of California. All rights reserved.
1.1 cgd 6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
1.40 agc 15: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 16: * may be used to endorse or promote products derived from this software
17: * without specific prior written permission.
18: *
19: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29: * SUCH DAMAGE.
30: */
31:
1.14 lukem 32: #include <sys/cdefs.h>
1.1 cgd 33: #ifndef lint
1.51 lukem 34: __COPYRIGHT("@(#) Copyright (c) 1988, 1990, 1993\
35: The Regents of the University of California. All rights reserved.");
1.1 cgd 36: #endif /* not lint */
37:
38: #ifndef lint
1.9 cgd 39: #if 0
1.15 lukem 40: static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95";
1.9 cgd 41: #else
1.55 ! joerg 42: __RCSID("$NetBSD: shutdown.c,v 1.54 2011/02/16 19:33:48 wiz Exp $");
1.9 cgd 43: #endif
1.1 cgd 44: #endif /* not lint */
45:
46: #include <sys/param.h>
47: #include <sys/time.h>
48: #include <sys/resource.h>
49: #include <sys/syslog.h>
1.7 mycroft 50:
51: #include <ctype.h>
1.19 mycroft 52: #include <err.h>
1.7 mycroft 53: #include <fcntl.h>
54: #include <pwd.h>
1.1 cgd 55: #include <setjmp.h>
1.7 mycroft 56: #include <signal.h>
57: #include <stdio.h>
58: #include <stdlib.h>
59: #include <string.h>
1.26 kleink 60: #include <time.h>
1.1 cgd 61: #include <tzfile.h>
1.7 mycroft 62: #include <unistd.h>
1.47 christos 63: #include <errno.h>
1.7 mycroft 64:
1.1 cgd 65: #include "pathnames.h"
66:
67: #ifdef DEBUG
68: #undef _PATH_NOLOGIN
69: #define _PATH_NOLOGIN "./nologin"
70: #undef _PATH_FASTBOOT
71: #define _PATH_FASTBOOT "./fastboot"
72: #endif
73:
74: #define H *60*60
75: #define M *60
76: #define S *1
77: #define NOLOG_TIME 5*60
1.47 christos 78: static const struct interval {
79: time_t timeleft, timetowait;
1.1 cgd 80: } tlist[] = {
1.13 mikel 81: { 10 H, 5 H }, { 5 H, 3 H }, { 2 H, 1 H }, { 1 H, 30 M },
82: { 30 M, 10 M }, { 20 M, 10 M }, { 10 M, 5 M }, { 5 M, 3 M },
83: { 2 M, 1 M }, { 1 M, 30 S }, { 30 S, 30 S },
84: { 0, 0 }
1.7 mycroft 85: };
1.1 cgd 86: #undef H
87: #undef M
88: #undef S
89:
90: static time_t offset, shuttime;
1.47 christos 91: static int dofast, dohalt, doreboot, killflg, nofork, nosync, dodump;
92: static size_t mbuflen;
1.27 thorpej 93: static int dopowerdown;
1.53 dyoung 94: static int dodebug, dosilent, doverbose;
1.29 mycroft 95: static const char *whom;
96: static char mbuf[BUFSIZ];
1.47 christos 97: static char *bootstr;
1.1 cgd 98:
1.49 perry 99: static void badtime(void) __dead;
100: static void die_you_gravy_sucking_pig_dog(void) __dead;
1.47 christos 101: static void doitfast(void);
1.55 ! joerg 102: static void dorcshutdown(void);
1.49 perry 103: static void finish(int) __dead;
1.47 christos 104: static void getoffset(char *);
1.55 ! joerg 105: static void loop(void) __dead;
1.47 christos 106: static void nolog(void);
1.55 ! joerg 107: static void timeout(int) __dead;
1.47 christos 108: static void timewarn(time_t);
1.49 perry 109: static void usage(void) __dead;
1.5 cgd 110:
1.7 mycroft 111: int
1.43 xtraeme 112: main(int argc, char *argv[])
1.1 cgd 113: {
1.14 lukem 114: char *p, *endp;
1.7 mycroft 115: struct passwd *pw;
1.47 christos 116: size_t arglen, len;
117: int ch;
1.1 cgd 118:
1.47 christos 119: (void)setprogname(argv[0]);
1.1 cgd 120: #ifndef DEBUG
1.19 mycroft 121: if (geteuid())
1.50 dholland 122: errx(1, "%s: Not super-user", strerror(EPERM));
1.1 cgd 123: #endif
1.53 dyoung 124: while ((ch = getopt(argc, argv, "b:Ddfhknprvxz")) != -1)
1.1 cgd 125: switch (ch) {
1.39 atatat 126: case 'b':
127: bootstr = optarg;
128: break;
1.19 mycroft 129: case 'd':
130: dodump = 1;
1.1 cgd 131: break;
1.25 fair 132: case 'D':
133: nofork = 1;
134: break;
1.1 cgd 135: case 'f':
136: dofast = 1;
137: break;
1.27 thorpej 138: case 'p':
139: dopowerdown = 1;
140: /* FALLTHROUGH */
1.1 cgd 141: case 'h':
142: dohalt = 1;
143: break;
144: case 'k':
145: killflg = 1;
146: break;
147: case 'n':
1.11 mikel 148: nosync = 1;
1.1 cgd 149: break;
150: case 'r':
151: doreboot = 1;
152: break;
1.53 dyoung 153: case 'v':
154: doverbose = 1;
155: break;
156: case 'x':
157: dodebug = 1;
158: break;
159: case 'z':
160: dosilent = 1;
161: break;
1.1 cgd 162: case '?':
163: default:
164: usage();
165: }
166: argc -= optind;
167: argv += optind;
168:
169: if (argc < 1)
170: usage();
171:
1.20 mycroft 172: if (dodump && !dohalt && !doreboot)
173: doreboot = 1;
174:
1.1 cgd 175: if (dofast && nosync) {
1.50 dholland 176: warnx("Incompatible options -f and -n");
1.1 cgd 177: usage();
178: }
1.20 mycroft 179: if (dohalt && doreboot) {
1.27 thorpej 180: const char *which_flag = dopowerdown ? "p" : "h";
181:
1.50 dholland 182: warnx("Incompatible options -%s and -r", which_flag);
1.12 jtk 183: usage();
184: }
1.19 mycroft 185:
1.1 cgd 186: getoffset(*argv++);
187:
1.30 ross 188: if (argv[0]) {
1.21 mycroft 189: if (strcmp(argv[0], "-") || argv[1]) {
1.19 mycroft 190: for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
191: arglen = strlen(*argv);
192: if ((len -= arglen) <= 2)
193: break;
194: if (p != mbuf)
195: *p++ = ' ';
1.47 christos 196: (void)memmove(p, *argv, arglen);
1.19 mycroft 197: p += arglen;
198: }
199: *p = '\n';
200: *++p = '\0';
201: } else {
202: p = mbuf;
203: endp = mbuf + sizeof(mbuf) - 2;
204: for (;;) {
205: if (!fgets(p, endp - p + 1, stdin))
206: break;
207: for (; *p && p < endp; ++p);
208: if (p == endp) {
209: *p = '\n';
210: *++p = '\0';
211: break;
212: }
1.1 cgd 213: }
214: }
1.30 ross 215: }
1.1 cgd 216: mbuflen = strlen(mbuf);
217:
218: if (offset)
219: (void)printf("Shutdown at %.24s.\n", ctime(&shuttime));
220: else
221: (void)printf("Shutdown NOW!\n");
222:
223: if (!(whom = getlogin()))
224: whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
225:
226: #ifdef DEBUG
227: (void)putc('\n', stdout);
228: #else
229: (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
1.25 fair 230: if (nofork == 0) {
1.1 cgd 231: int forkpid;
232:
233: forkpid = fork();
234: if (forkpid == -1) {
235: perror("shutdown: fork");
236: exit(1);
237: }
238: if (forkpid) {
239: (void)printf("shutdown: [pid %d]\n", forkpid);
240: exit(0);
241: }
1.46 jnemeth 242: (void)setsid();
1.1 cgd 243: }
244: #endif
245: openlog("shutdown", LOG_CONS, LOG_AUTH);
246: loop();
1.7 mycroft 247: /* NOTREACHED */
1.13 mikel 248: #ifdef __GNUC__
249: return 1;
250: #endif
1.1 cgd 251: }
252:
1.55 ! joerg 253: static void
1.43 xtraeme 254: loop(void)
1.1 cgd 255: {
1.47 christos 256: const struct interval *tp;
1.1 cgd 257: u_int sltime;
258: int logged;
259:
260: if (offset <= NOLOG_TIME) {
261: logged = 1;
262: nolog();
263: }
264: else
265: logged = 0;
266: tp = tlist;
267: if (tp->timeleft < offset)
268: (void)sleep((u_int)(offset - tp->timeleft));
269: else {
270: while (offset < tp->timeleft)
271: ++tp;
272: /*
1.7 mycroft 273: * Warn now, if going to sleep more than a fifth of
1.1 cgd 274: * the next wait time.
275: */
1.13 mikel 276: if ((sltime = offset - tp->timeleft) != 0) {
1.1 cgd 277: if (sltime > tp->timetowait / 5)
1.7 mycroft 278: timewarn(offset);
1.1 cgd 279: (void)sleep(sltime);
280: }
281: }
282: for (;; ++tp) {
1.7 mycroft 283: timewarn(tp->timeleft);
1.1 cgd 284: if (!logged && tp->timeleft <= NOLOG_TIME) {
285: logged = 1;
286: nolog();
287: }
288: (void)sleep((u_int)tp->timetowait);
289: if (!tp->timeleft)
290: break;
291: }
292: die_you_gravy_sucking_pig_dog();
293: }
294:
295: static jmp_buf alarmbuf;
296:
1.55 ! joerg 297: static void
1.47 christos 298: timewarn(time_t timeleft)
1.1 cgd 299: {
300: static int first;
301: static char hostname[MAXHOSTNAMELEN + 1];
1.7 mycroft 302: FILE *pf;
1.1 cgd 303: char wcmd[MAXPATHLEN + 4];
304:
1.28 mrg 305: if (!first++) {
1.1 cgd 306: (void)gethostname(hostname, sizeof(hostname));
1.28 mrg 307: hostname[sizeof(hostname) - 1] = '\0';
308: }
1.1 cgd 309:
310: /* undoc -n option to wall suppresses normal wall banner */
1.28 mrg 311: (void)snprintf(wcmd, sizeof wcmd, "%s -n", _PATH_WALL);
1.47 christos 312: if ((pf = popen(wcmd, "w")) == NULL) {
313: syslog(LOG_ERR, "%s: Can't find `%s' (%m)", getprogname(),
314: _PATH_WALL);
1.1 cgd 315: return;
316: }
317:
318: (void)fprintf(pf,
319: "\007*** %sSystem shutdown message from %s@%s ***\007\n",
1.5 cgd 320: timeleft ? "": "FINAL ", whom, hostname);
1.1 cgd 321:
1.5 cgd 322: if (timeleft > 10*60)
1.1 cgd 323: (void)fprintf(pf, "System going down at %5.5s\n\n",
324: ctime(&shuttime) + 11);
1.5 cgd 325: else if (timeleft > 59)
1.47 christos 326: (void)fprintf(pf, "System going down in %ld minute%s\n\n",
327: (long)timeleft / 60, (timeleft > 60) ? "s" : "");
1.5 cgd 328: else if (timeleft)
1.1 cgd 329: (void)fprintf(pf, "System going down in 30 seconds\n\n");
330: else
331: (void)fprintf(pf, "System going down IMMEDIATELY\n\n");
332:
333: if (mbuflen)
334: (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
335:
336: /*
337: * play some games, just in case wall doesn't come back
1.41 dsainty 338: * probably unnecessary, given that wall is careful.
1.1 cgd 339: */
340: if (!setjmp(alarmbuf)) {
341: (void)signal(SIGALRM, timeout);
342: (void)alarm((u_int)30);
343: (void)pclose(pf);
344: (void)alarm((u_int)0);
345: (void)signal(SIGALRM, SIG_DFL);
346: }
347: }
348:
1.47 christos 349: static void
350: /*ARGSUSED*/
1.43 xtraeme 351: timeout(int signo)
1.1 cgd 352: {
353: longjmp(alarmbuf, 1);
354: }
355:
1.47 christos 356: static void
1.44 xtraeme 357: die_you_gravy_sucking_pig_dog(void)
1.1 cgd 358: {
1.50 dholland 359: const char *what;
1.1 cgd 360:
1.50 dholland 361: if (doreboot) {
362: what = "reboot";
363: } else if (dohalt && dopowerdown) {
364: what = "poweroff";
365: } else if (dohalt) {
366: what = "halt";
367: } else {
368: what = "shutdown";
369: }
370:
371: syslog(LOG_NOTICE, "%s by %s: %s", what, whom, mbuf);
1.1 cgd 372: (void)sleep(2);
373:
374: (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
375: if (killflg) {
376: (void)printf("\rbut you'll have to do it yourself\r\n");
1.7 mycroft 377: finish(0);
1.1 cgd 378: }
379: if (dofast)
380: doitfast();
1.33 bad 381: dorcshutdown();
1.20 mycroft 382: if (doreboot || dohalt) {
1.53 dyoung 383: const char *args[20];
1.45 christos 384: const char **arg, *path;
1.48 jnemeth 385: #ifndef DEBUG
1.47 christos 386: int serrno;
1.48 jnemeth 387: #endif
1.20 mycroft 388:
389: arg = &args[0];
390: if (doreboot) {
391: path = _PATH_REBOOT;
392: *arg++ = "reboot";
393: } else {
394: path = _PATH_HALT;
395: *arg++ = "halt";
396: }
1.53 dyoung 397: if (doverbose)
398: *arg++ = "-v";
399: if (dodebug)
400: *arg++ = "-x";
401: if (dosilent)
402: *arg++ = "-z";
1.20 mycroft 403: if (dodump)
404: *arg++ = "-d";
405: if (nosync)
406: *arg++ = "-n";
1.27 thorpej 407: if (dopowerdown)
408: *arg++ = "-p";
1.20 mycroft 409: *arg++ = "-l";
1.39 atatat 410: if (bootstr)
411: *arg++ = bootstr;
1.20 mycroft 412: *arg++ = 0;
413: #ifndef DEBUG
1.52 riz 414: (void)unlink(_PATH_NOLOGIN);
1.47 christos 415: (void)execve(path, __UNCONST(args), NULL);
416: serrno = errno;
1.50 dholland 417: syslog(LOG_ERR, "Can't exec `%s' (%m)", path);
1.47 christos 418: errno = serrno;
419: warn("Can't exec `%s'", path);
1.20 mycroft 420: #else
421: printf("%s", path);
422: for (arg = &args[0]; *arg; arg++)
423: printf(" %s", *arg);
424: printf("\n");
425: #endif
426: } else {
427: #ifndef DEBUG
428: (void)kill(1, SIGTERM); /* to single user */
1.1 cgd 429: #else
1.20 mycroft 430: printf("kill 1\n");
431: #endif
1.1 cgd 432: }
1.7 mycroft 433: finish(0);
1.1 cgd 434: }
435:
1.23 mycroft 436: #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
1.1 cgd 437:
1.55 ! joerg 438: static void
1.43 xtraeme 439: getoffset(char *timearg)
1.1 cgd 440: {
1.14 lukem 441: struct tm *lt;
442: char *p;
1.7 mycroft 443: time_t now;
1.18 mycroft 444: int yearset;
1.1 cgd 445:
1.32 enami 446: (void)time(&now);
1.1 cgd 447: if (!strcasecmp(timearg, "now")) { /* now */
448: offset = 0;
1.32 enami 449: shuttime = now;
1.1 cgd 450: return;
451: }
452:
453: if (*timearg == '+') { /* +minutes */
1.42 dsl 454: if (!isdigit((unsigned char)*++timearg))
1.1 cgd 455: badtime();
456: offset = atoi(timearg) * 60;
457: shuttime = now + offset;
458: return;
459: }
460:
461: /* handle hh:mm by getting rid of the colon */
462: for (p = timearg; *p; ++p)
1.42 dsl 463: if (!isascii(*p) || !isdigit((unsigned char)*p)) {
1.1 cgd 464: if (*p == ':' && strlen(p) == 3) {
465: p[0] = p[1];
466: p[1] = p[2];
467: p[2] = '\0';
468: }
469: else
470: badtime();
1.30 ross 471: }
1.1 cgd 472:
1.47 christos 473: (void)unsetenv("TZ"); /* OUR timezone */
1.1 cgd 474: lt = localtime(&now); /* current time val */
475:
1.18 mycroft 476: lt->tm_sec = 0;
477:
478: yearset = 0;
479: switch (strlen(timearg)) {
480: case 12:
1.23 mycroft 481: lt->tm_year = ATOI2(timearg) * 100 - TM_YEAR_BASE;
1.18 mycroft 482: yearset = 1;
483: /* FALLTHROUGH */
1.1 cgd 484: case 10:
1.18 mycroft 485: if (yearset) {
1.23 mycroft 486: lt->tm_year += ATOI2(timearg);
1.18 mycroft 487: } else {
488: yearset = ATOI2(timearg);
489: if (yearset < 69)
1.23 mycroft 490: lt->tm_year = yearset + 2000 - TM_YEAR_BASE;
1.18 mycroft 491: else
1.23 mycroft 492: lt->tm_year = yearset + 1900 - TM_YEAR_BASE;
1.18 mycroft 493: }
1.1 cgd 494: /* FALLTHROUGH */
495: case 8:
496: lt->tm_mon = ATOI2(timearg);
1.18 mycroft 497: --lt->tm_mon;
1.1 cgd 498: /* FALLTHROUGH */
499: case 6:
500: lt->tm_mday = ATOI2(timearg);
501: /* FALLTHROUGH */
502: case 4:
503: lt->tm_hour = ATOI2(timearg);
1.18 mycroft 504: /* FALLTHROUGH */
505: case 2:
1.1 cgd 506: lt->tm_min = ATOI2(timearg);
507: break;
508: default:
509: badtime();
1.18 mycroft 510: }
511:
512: if ((shuttime = mktime(lt)) == -1)
513: badtime();
1.19 mycroft 514: if ((offset = shuttime - now) < 0)
515: errx(1, "time is already past");
1.33 bad 516: }
517:
1.55 ! joerg 518: static void
1.43 xtraeme 519: dorcshutdown(void)
1.33 bad 520: {
521: (void)printf("\r\nAbout to run shutdown hooks...\r\n");
1.47 christos 522: #ifndef DEBUG
523: (void)setuid(0);
1.38 cgd 524: (void)system(". " _PATH_RCSHUTDOWN);
1.47 christos 525: #endif
1.37 jdolecek 526: (void)sleep(5); /* Give operator a chance to abort this. */
1.36 jdolecek 527: (void)printf("\r\nDone running shutdown hooks.\r\n");
1.1 cgd 528: }
529:
530: #define FSMSG "fastboot file for fsck\n"
1.55 ! joerg 531: static void
1.43 xtraeme 532: doitfast(void)
1.1 cgd 533: {
534: int fastfd;
535:
536: if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
537: 0664)) >= 0) {
538: (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
539: (void)close(fastfd);
540: }
541: }
542:
543: #define NOMSG "\n\nNO LOGINS: System going down at "
1.55 ! joerg 544: static void
1.43 xtraeme 545: nolog(void)
1.1 cgd 546: {
547: int logfd;
1.7 mycroft 548: char *ct;
1.1 cgd 549:
550: (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */
551: (void)signal(SIGINT, finish);
552: (void)signal(SIGHUP, finish);
553: (void)signal(SIGQUIT, finish);
554: (void)signal(SIGTERM, finish);
555: if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
556: 0664)) >= 0) {
557: (void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
558: ct = ctime(&shuttime);
559: (void)write(logfd, ct + 11, 5);
560: (void)write(logfd, "\n\n", 2);
561: (void)write(logfd, mbuf, strlen(mbuf));
562: (void)close(logfd);
563: }
564: }
565:
1.47 christos 566: static void
567: /*ARGSUSED*/
1.43 xtraeme 568: finish(int signo)
1.1 cgd 569: {
1.28 mrg 570:
1.6 mycroft 571: if (!killflg)
572: (void)unlink(_PATH_NOLOGIN);
1.1 cgd 573: exit(0);
574: }
575:
1.47 christos 576: static void
1.43 xtraeme 577: badtime(void)
1.1 cgd 578: {
1.28 mrg 579:
1.19 mycroft 580: warnx("illegal time format");
581: usage();
1.1 cgd 582: }
583:
1.47 christos 584: static void
1.43 xtraeme 585: usage(void)
1.1 cgd 586: {
1.28 mrg 587:
1.19 mycroft 588: (void)fprintf(stderr,
1.54 wiz 589: "Usage: %s [-Ddfhknprvxz] [-b bootstr] time [message ... | -]\n",
1.47 christos 590: getprogname());
1.1 cgd 591: exit(1);
592: }
CVSweb <webmaster@jp.NetBSD.org>