Return to shutdown.c CVS log Up to [local] / src / sbin / shutdown

Annotation of src/sbin/shutdown/shutdown.c, Revision 1.36

1.36 ! sobrado 1: /* $OpenBSD: shutdown.c,v 1.35 2009/10/27 23:59:34 deraadt Exp $ */ 1.1 deraadt 2: /* $NetBSD: shutdown.c,v 1.9 1995/03/18 15:01:09 cgd Exp $ */ 3: 4: /* 5: * Copyright (c) 1988, 1990, 1993 6: * The Regents of the University of California. All rights reserved. 7: * 8: * Redistribution and use in source and binary forms, with or without 9: * modification, are permitted provided that the following conditions 10: * are met: 11: * 1. Redistributions of source code must retain the above copyright 12: * notice, this list of conditions and the following disclaimer. 13: * 2. Redistributions in binary form must reproduce the above copyright 14: * notice, this list of conditions and the following disclaimer in the 15: * documentation and/or other materials provided with the distribution. 1.29 millert 16: * 3. Neither the name of the University nor the names of its contributors 1.1 deraadt 17: * may be used to endorse or promote products derived from this software 18: * without specific prior written permission. 19: * 20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30: * SUCH DAMAGE. 31: */ 32: 33: #include <sys/param.h> 34: #include <sys/resource.h> 35: #include <sys/syslog.h> 1.18 ericj 36: #include <sys/types.h> 37: #include <sys/wait.h> 1.1 deraadt 38: 39: #include <ctype.h> 40: #include <fcntl.h> 1.16 deraadt 41: #include <sys/termios.h> 1.1 deraadt 42: #include <pwd.h> 43: #include <setjmp.h> 44: #include <signal.h> 45: #include <stdio.h> 46: #include <stdlib.h> 47: #include <string.h> 1.18 ericj 48: #include <time.h> 1.1 deraadt 49: #include <tzfile.h> 50: #include <unistd.h> 1.13 mickey 51: #include <errno.h> 52: #include <err.h> 1.1 deraadt 53: 54: #include "pathnames.h" 55: 56: #ifdef DEBUG 57: #undef _PATH_NOLOGIN 58: #define _PATH_NOLOGIN "./nologin" 59: #undef _PATH_FASTBOOT 60: #define _PATH_FASTBOOT "./fastboot" 61: #endif 62: 63: #define H *60*60 64: #define M *60 65: #define S *1 66: #define NOLOG_TIME 5*60 67: struct interval { 68: int timeleft, timetowait; 69: } tlist[] = { 1.10 mickey 70: { 10 H, 5 H }, 71: { 5 H, 3 H }, 72: { 2 H, 1 H }, 73: { 1 H, 30 M }, 74: { 30 M, 10 M }, 75: { 20 M, 10 M }, 76: { 10 M, 5 M }, 77: { 5 M, 3 M }, 78: { 2 M, 1 M }, 79: { 1 M, 30 S }, 80: { 30 S, 30 S }, 81: { 0, 0 } 1.1 deraadt 82: }; 83: #undef H 84: #undef M 85: #undef S 86: 87: static time_t offset, shuttime; 1.33 deraadt 88: static int dofast, dohalt, doreboot, dopower, dodump, mbuflen, nosync; 89: static sig_atomic_t killflg; 1.9 downsj 90: static char *whom, mbuf[BUFSIZ]; 1.1 deraadt 91: 1.24 millert 92: void badtime(void); 1.34 cloder 93: void __dead die_you_gravy_sucking_pig_dog(void); 1.24 millert 94: void doitfast(void); 1.34 cloder 95: void __dead finish(int); 1.24 millert 96: void getoffset(char *); 1.34 cloder 97: void __dead loop(void); 1.24 millert 98: void nolog(void); 99: void timeout(int); 100: void timewarn(int); 101: void usage(void); 1.1 deraadt 102: 103: int 1.26 deraadt 104: main(int argc, char *argv[]) 1.1 deraadt 105: { 1.31 deraadt 106: int arglen, ch, len, readstdin = 0; 107: struct passwd *pw; 1.23 mpech 108: char *p, *endp; 1.31 deraadt 109: pid_t forkpid; 1.1 deraadt 110: 111: #ifndef DEBUG 1.28 mickey 112: if (geteuid()) 113: errx(1, "NOT super-user"); 1.1 deraadt 114: #endif 1.27 millert 115: while ((ch = getopt(argc, argv, "dfhknpr-")) != -1) 1.1 deraadt 116: switch (ch) { 117: case '-': 118: readstdin = 1; 119: break; 1.11 mickey 120: case 'd': 121: dodump = 1; 122: break; 1.1 deraadt 123: case 'f': 124: dofast = 1; 125: break; 126: case 'h': 127: dohalt = 1; 128: break; 129: case 'k': 130: killflg = 1; 131: break; 132: case 'n': 1.9 downsj 133: nosync = 1; 1.1 deraadt 134: break; 1.8 downsj 135: case 'p': 136: dopower = 1; 137: break; 1.1 deraadt 138: case 'r': 139: doreboot = 1; 140: break; 141: default: 142: usage(); 143: } 144: argc -= optind; 145: argv += optind; 146: 147: if (argc < 1) 148: usage(); 149: 150: if (dofast && nosync) { 151: (void)fprintf(stderr, 152: "shutdown: incompatible switches -f and -n.

"); 153: usage(); 154: } 155: if (doreboot && dohalt) { 156: (void)fprintf(stderr, 157: "shutdown: incompatible switches -h and -r.

"); 158: usage(); 159: } 1.8 downsj 160: if (dopower && !dohalt) { 161: (void)fprintf(stderr, 162: "shutdown: switch -p must be used with -h.

"); 163: usage(); 164: } 1.1 deraadt 165: getoffset(*argv++); 166: 167: if (*argv) { 168: for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 169: arglen = strlen(*argv); 170: if ((len -= arglen) <= 2) 171: break; 172: if (p != mbuf) 173: *p++ = ' '; 174: memcpy(p, *argv, arglen); 175: p += arglen; 176: } 177: *p = '

'; 178: *++p = '\0'; 179: } 180: 181: if (readstdin) { 182: p = mbuf; 183: endp = mbuf + sizeof(mbuf) - 2; 184: for (;;) { 185: if (!fgets(p, endp - p + 1, stdin)) 186: break; 1.31 deraadt 187: for (; *p && p < endp; ++p) 188: ; 1.1 deraadt 189: if (p == endp) { 190: *p = '

'; 191: *++p = '\0'; 192: break; 193: } 194: } 195: } 196: mbuflen = strlen(mbuf); 197: 198: if (offset) 199: (void)printf("Shutdown at %.24s.

", ctime(&shuttime)); 200: else 201: (void)printf("Shutdown NOW!

"); 202: 203: if (!(whom = getlogin())) 204: whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 205: 206: #ifdef DEBUG 207: (void)putc('

', stdout); 208: #else 209: (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 210: 1.31 deraadt 211: forkpid = fork(); 212: if (forkpid == -1) 213: err(1, "fork"); 214: if (forkpid) { 215: (void)printf("shutdown: [pid %ld]

", (long)forkpid); 216: exit(0); 1.1 deraadt 217: } 1.12 deraadt 218: setsid(); 1.1 deraadt 219: #endif 220: openlog("shutdown", LOG_CONS, LOG_AUTH); 221: loop(); 222: /* NOTREACHED */ 223: } 224: 225: void 1.26 deraadt 226: loop(void) 1.1 deraadt 227: { 228: struct interval *tp; 229: u_int sltime; 230: int logged; 231: 232: if (offset <= NOLOG_TIME) { 233: logged = 1; 234: nolog(); 1.31 deraadt 235: } else 1.1 deraadt 236: logged = 0; 237: tp = tlist; 238: if (tp->timeleft < offset) 239: (void)sleep((u_int)(offset - tp->timeleft)); 240: else { 241: while (offset < tp->timeleft) 242: ++tp; 243: /* 244: * Warn now, if going to sleep more than a fifth of 245: * the next wait time. 246: */ 1.10 mickey 247: if ((sltime = offset - tp->timeleft)) { 1.1 deraadt 248: if (sltime > tp->timetowait / 5) 249: timewarn(offset); 250: (void)sleep(sltime); 251: } 252: } 253: for (;; ++tp) { 254: timewarn(tp->timeleft); 255: if (!logged && tp->timeleft <= NOLOG_TIME) { 256: logged = 1; 257: nolog(); 258: } 259: (void)sleep((u_int)tp->timetowait); 260: if (!tp->timeleft) 261: break; 262: } 263: die_you_gravy_sucking_pig_dog(); 264: } 265: 266: static jmp_buf alarmbuf; 267: 1.3 deraadt 268: static char *restricted_environ[] = { 269: "PATH=" _PATH_STDPATH, 270: NULL 271: }; 272: 1.1 deraadt 273: void 1.26 deraadt 274: timewarn(int timeleft) 1.1 deraadt 275: { 1.6 deraadt 276: static char hostname[MAXHOSTNAMELEN]; 1.1 deraadt 277: char wcmd[MAXPATHLEN + 4]; 1.4 deraadt 278: extern char **environ; 1.31 deraadt 279: static int first; 280: FILE *pf; 1.1 deraadt 281: 282: if (!first++) 283: (void)gethostname(hostname, sizeof(hostname)); 284: 285: /* undoc -n option to wall suppresses normal wall banner */ 286: (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 1.3 deraadt 287: environ = restricted_environ; 1.1 deraadt 288: if (!(pf = popen(wcmd, "w"))) { 289: syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 290: return; 291: } 292: 293: (void)fprintf(pf, 294: "\007*** %sSystem shutdown message from %s@%s ***\007

", 295: timeleft ? "": "FINAL ", whom, hostname); 296: 297: if (timeleft > 10*60) 298: (void)fprintf(pf, "System going down at %5.5s



", 299: ctime(&shuttime) + 11); 300: else if (timeleft > 59) 301: (void)fprintf(pf, "System going down in %d minute%s



", 302: timeleft / 60, (timeleft > 60) ? "s" : ""); 303: else if (timeleft) 304: (void)fprintf(pf, "System going down in 30 seconds



"); 305: else 306: (void)fprintf(pf, "System going down IMMEDIATELY



"); 307: 308: if (mbuflen) 309: (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 310: 311: /* 312: * play some games, just in case wall doesn't come back 1.36 ! sobrado 313: * probably unnecessary, given that wall is careful. 1.1 deraadt 314: */ 315: if (!setjmp(alarmbuf)) { 316: (void)signal(SIGALRM, timeout); 317: (void)alarm((u_int)30); 318: (void)pclose(pf); 319: (void)alarm((u_int)0); 320: (void)signal(SIGALRM, SIG_DFL); 321: } 322: } 323: 324: void 1.26 deraadt 325: timeout(int signo) 1.1 deraadt 326: { 1.20 deraadt 327: longjmp(alarmbuf, 1); /* XXX signal/longjmp resource leaks */ 1.1 deraadt 328: } 329: 330: void 1.26 deraadt 331: die_you_gravy_sucking_pig_dog(void) 1.1 deraadt 332: { 333: 334: syslog(LOG_NOTICE, "%s by %s: %s", 335: doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 336: (void)sleep(2); 337: 338: (void)printf("\r

System shutdown time has arrived\007\007\r

"); 339: if (killflg) { 340: (void)printf("\rbut you'll have to do it yourself\r

"); 341: finish(0); 342: } 343: if (dofast) 344: doitfast(); 345: #ifdef DEBUG 346: if (doreboot) 347: (void)printf("reboot"); 348: else if (dohalt) 349: (void)printf("halt"); 350: if (nosync) 351: (void)printf(" no sync"); 352: if (dofast) 353: (void)printf(" no fsck"); 1.11 mickey 354: if (dodump) 355: (void)printf(" with dump"); 1.1 deraadt 356: (void)printf("

kill -HUP 1

"); 357: #else 358: if (doreboot) { 1.11 mickey 359: execle(_PATH_REBOOT, "reboot", "-l", 360: (nosync ? "-n" : (dodump ? "-d" : NULL)), 1.30 avsm 361: (dodump ? "-d" : NULL), (char *)NULL, (char *)NULL); 1.1 deraadt 362: syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 1.13 mickey 363: warn(_PATH_REBOOT); 1.1 deraadt 364: } 365: else if (dohalt) { 1.9 downsj 366: execle(_PATH_HALT, "halt", "-l", 1.11 mickey 367: (dopower ? "-p" : (nosync ? "-n" : (dodump ? "-d" : NULL))), 368: (nosync ? "-n" : (dodump ? "-d" : NULL)), 1.30 avsm 369: (dodump ? "-d" : NULL), (char *)NULL, (char *)NULL); 1.1 deraadt 370: syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 1.13 mickey 371: warn(_PATH_HALT); 1.14 millert 372: } 1.17 deraadt 373: if (access(_PATH_RC, R_OK) != -1) { 1.14 millert 374: pid_t pid; 1.16 deraadt 375: struct termios t; 376: int fd; 1.14 millert 377: 378: switch ((pid = fork())) { 379: case -1: 380: break; 381: case 0: 1.16 deraadt 382: if (revoke(_PATH_CONSOLE) == -1) 383: perror("revoke"); 384: if (setsid() == -1) 385: perror("setsid"); 386: fd = open(_PATH_CONSOLE, O_RDWR); 387: if (fd == -1) 388: perror("open"); 389: dup2(fd, 0); 390: dup2(fd, 1); 391: dup2(fd, 2); 392: if (fd > 2) 393: close(fd); 394: 395: /* At a minimum... */ 396: tcgetattr(0, &t); 397: t.c_oflag |= (ONLCR | OPOST); 398: tcsetattr(0, TCSANOW, &t); 399: 1.22 deraadt 400: execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL); 1.16 deraadt 401: _exit(1); 1.14 millert 402: default: 403: waitpid(pid, NULL, 0); 404: } 1.1 deraadt 405: } 406: (void)kill(1, SIGTERM); /* to single user */ 407: #endif 408: finish(0); 409: } 410: 411: #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 412: 413: void 1.26 deraadt 414: getoffset(char *timearg) 1.1 deraadt 415: { 1.23 mpech 416: struct tm *lt; 1.31 deraadt 417: int this_year; 418: time_t now; 1.23 mpech 419: char *p; 1.1 deraadt 420: 421: if (!strcasecmp(timearg, "now")) { /* now */ 422: offset = 0; 423: return; 424: } 425: 426: (void)time(&now); 427: if (*timearg == '+') { /* +minutes */ 428: if (!isdigit(*++timearg)) 429: badtime(); 430: offset = atoi(timearg) * 60; 431: shuttime = now + offset; 432: return; 433: } 434: 435: /* handle hh:mm by getting rid of the colon */ 1.21 deraadt 436: for (p = timearg; *p; ++p) { 437: if (!isascii(*p) || !isdigit(*p)) { 1.1 deraadt 438: if (*p == ':' && strlen(p) == 3) { 439: p[0] = p[1]; 440: p[1] = p[2]; 441: p[2] = '\0'; 1.21 deraadt 442: } else 1.1 deraadt 443: badtime(); 1.21 deraadt 444: } 445: } 1.1 deraadt 446: 447: unsetenv("TZ"); /* OUR timezone */ 448: lt = localtime(&now); /* current time val */ 449: 1.31 deraadt 450: switch (strlen(timearg)) { 1.1 deraadt 451: case 10: 1.15 alex 452: this_year = lt->tm_year; 1.1 deraadt 453: lt->tm_year = ATOI2(timearg); 1.15 alex 454: /* 455: * check if the specified year is in the next century. 456: * allow for one year of user error as many people will 457: * enter n - 1 at the start of year n. 458: */ 459: if (lt->tm_year < (this_year % 100) - 1) 460: lt->tm_year += 100; 461: /* adjust for the year 2000 and beyond */ 462: lt->tm_year += (this_year - (this_year % 100)); 1.1 deraadt 463: /* FALLTHROUGH */ 464: case 8: 465: lt->tm_mon = ATOI2(timearg); 466: if (--lt->tm_mon < 0 || lt->tm_mon > 11) 467: badtime(); 468: /* FALLTHROUGH */ 469: case 6: 470: lt->tm_mday = ATOI2(timearg); 471: if (lt->tm_mday < 1 || lt->tm_mday > 31) 472: badtime(); 473: /* FALLTHROUGH */ 474: case 4: 475: lt->tm_hour = ATOI2(timearg); 476: if (lt->tm_hour < 0 || lt->tm_hour > 23) 477: badtime(); 478: lt->tm_min = ATOI2(timearg); 479: if (lt->tm_min < 0 || lt->tm_min > 59) 480: badtime(); 481: lt->tm_sec = 0; 482: if ((shuttime = mktime(lt)) == -1) 483: badtime(); 1.28 mickey 484: if ((offset = shuttime - now) < 0) 485: errx(1, "that time is already past."); 1.1 deraadt 486: break; 487: default: 488: badtime(); 489: } 490: } 491: 492: #define FSMSG "fastboot file for fsck

" 493: void 1.26 deraadt 494: doitfast(void) 1.1 deraadt 495: { 496: int fastfd; 497: 498: if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 499: 0664)) >= 0) { 500: (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); 501: (void)close(fastfd); 502: } 503: } 504: 505: #define NOMSG "



NO LOGINS: System going down at " 506: void 1.26 deraadt 507: nolog(void) 1.1 deraadt 508: { 509: int logfd; 510: char *ct; 511: 512: (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 513: (void)signal(SIGINT, finish); 514: (void)signal(SIGHUP, finish); 515: (void)signal(SIGQUIT, finish); 516: (void)signal(SIGTERM, finish); 517: if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 518: 0664)) >= 0) { 519: (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 520: ct = ctime(&shuttime); 521: (void)write(logfd, ct + 11, 5); 522: (void)write(logfd, "



", 2); 523: (void)write(logfd, mbuf, strlen(mbuf)); 524: (void)close(logfd); 525: } 526: } 527: 528: void 1.26 deraadt 529: finish(int signo) 1.1 deraadt 530: { 531: if (!killflg) 532: (void)unlink(_PATH_NOLOGIN); 1.19 deraadt 533: if (signo == 0) 534: exit(0); 535: else 536: _exit(0); 1.1 deraadt 537: } 538: 539: void 1.26 deraadt 540: badtime(void) 1.1 deraadt 541: { 1.13 mickey 542: errx(1, "bad time format."); 1.1 deraadt 543: } 544: 545: void 1.26 deraadt 546: usage(void) 1.1 deraadt 547: { 1.32 jmc 548: fprintf(stderr, "usage: shutdown [-] [-dfhknpr] time [warning-message ...]

"); 1.1 deraadt 549: exit(1); 550: }