121179Sdist /* 235528Sbostic * Copyright (c) 1988 Regents of the University of California. 335528Sbostic * All rights reserved. 435528Sbostic * 535528Sbostic * Redistribution and use in source and binary forms are permitted 635528Sbostic * provided that the above copyright notice and this paragraph are 735528Sbostic * duplicated in all such forms and that any documentation, 835528Sbostic * advertising materials, and other materials related to such 935528Sbostic * distribution and use acknowledge that the software was developed 1035528Sbostic * by the University of California, Berkeley. The name of the 1135528Sbostic * University may not be used to endorse or promote products derived 1235528Sbostic * from this software without specific prior written permission. 1335528Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1435528Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1535528Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1621179Sdist */ 1721179Sdist 1812682Ssam #ifndef lint 1921179Sdist char copyright[] = 2035528Sbostic "@(#) Copyright (c) 1988 Regents of the University of California.\n\ 2121179Sdist All rights reserved.\n"; 2235528Sbostic #endif /* not lint */ 232373Swnj 2421179Sdist #ifndef lint 25*38990Sbostic static char sccsid[] = "@(#)shutdown.c 5.11 (Berkeley) 09/05/89"; 2635528Sbostic #endif /* not lint */ 2721179Sdist 2835528Sbostic #include <sys/param.h> 2935528Sbostic #include <sys/time.h> 3035528Sbostic #include <sys/file.h> 3135528Sbostic #include <sys/resource.h> 3235528Sbostic #include <sys/syslog.h> 332373Swnj #include <signal.h> 3415920Skarels #include <setjmp.h> 3535528Sbostic #include <tzfile.h> 3628802Skarels #include <pwd.h> 3735528Sbostic #include <stdio.h> 3835528Sbostic #include <ctype.h> 3937300Sbostic #include "pathnames.h" 4028024Seric 4135528Sbostic #ifdef DEBUG 4237300Sbostic #undef _PATH_NOLOGIN 4337300Sbostic #define _PATH_NOLOGIN "./nologin" 4437300Sbostic #undef _PATH_FASTBOOT 4537300Sbostic #define _PATH_FASTBOOT "./fastboot" 462373Swnj #endif 478841Smckusick 4835528Sbostic #define H *60*60 4935528Sbostic #define M *60 5035528Sbostic #define S *1 5135528Sbostic #define NOLOG_TIME 5*60 522373Swnj struct interval { 5335528Sbostic int timeleft, timetowait; 5435528Sbostic } tlist[] = { 5535528Sbostic 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, 5635528Sbostic 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, 5735528Sbostic 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, 5835528Sbostic 0, 0, 5935528Sbostic }, *tp = tlist; 6035528Sbostic #undef H 6135528Sbostic #undef M 6235528Sbostic #undef S 638841Smckusick 6435528Sbostic static time_t offset, shuttime; 6535528Sbostic static int dofast, dohalt, doreboot, killflg, mbuflen; 6635528Sbostic static char *nosync, *whom, mbuf[BUFSIZ]; 678841Smckusick 6835528Sbostic main(argc, argv) 692373Swnj int argc; 702373Swnj char **argv; 712373Swnj { 7235528Sbostic extern int optind; 7336433Sbostic register char *p, *endp; 7436433Sbostic int arglen, ch, len, readstdin; 7528802Skarels struct passwd *pw, *getpwuid(); 7635528Sbostic char *strcat(), *getlogin(); 7735528Sbostic uid_t geteuid(); 782373Swnj 7935528Sbostic #ifndef DEBUG 8035528Sbostic if (geteuid()) { 8135528Sbostic fprintf(stderr, "shutdown: NOT super-user\n"); 8235528Sbostic exit(1); 8335528Sbostic } 8435528Sbostic #endif 8536433Sbostic nosync = NULL; 8636433Sbostic readstdin = 0; 8736433Sbostic while ((ch = getopt(argc, argv, "-fhknr")) != EOF) 8835528Sbostic switch((char)ch) { 8936433Sbostic case '-': 9036433Sbostic readstdin = 1; 9136433Sbostic break; 9235528Sbostic case 'f': 9335528Sbostic dofast = 1; 9435528Sbostic break; 9535528Sbostic case 'h': 9635528Sbostic dohalt = 1; 9735528Sbostic break; 982373Swnj case 'k': 9935528Sbostic killflg = 1; 10035528Sbostic break; 10125366Sbloom case 'n': 10235528Sbostic nosync = "-n"; 10335528Sbostic break; 1042373Swnj case 'r': 10525408Sbloom doreboot = 1; 10635528Sbostic break; 10735528Sbostic case '?': 1082373Swnj default: 10935528Sbostic usage(); 1102373Swnj } 11135528Sbostic argc -= optind; 11235528Sbostic argv += optind; 11335528Sbostic 11435528Sbostic if (argc < 1) 11535528Sbostic usage(); 11635528Sbostic 11736433Sbostic if (dofast && nosync) { 11835528Sbostic fprintf(stderr, 11935528Sbostic "shutdown: incompatible switches -f and -n.\n"); 12035528Sbostic usage(); 1212373Swnj } 12235528Sbostic if (doreboot && dohalt) { 12335528Sbostic fprintf(stderr, 12435528Sbostic "shutdown: incompatible switches -h and -r.\n"); 12535528Sbostic usage(); 1262373Swnj } 12735528Sbostic getoffset(*argv++); 12836433Sbostic 12935528Sbostic if (*argv) { 13036433Sbostic for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 13136433Sbostic arglen = strlen(*argv); 13236433Sbostic if ((len -= arglen) <= 2) 13336433Sbostic break; 13436433Sbostic if (p != mbuf) 13536433Sbostic *p++ = ' '; 13636433Sbostic bcopy(*argv, p, arglen); 13736433Sbostic p += arglen; 13836433Sbostic } 13936433Sbostic *p = '\n'; 14036433Sbostic *++p = '\0'; 14125366Sbloom } 14236433Sbostic 14336433Sbostic if (readstdin) { 14436433Sbostic p = mbuf; 14536433Sbostic endp = mbuf + sizeof(mbuf) - 2; 14636433Sbostic for (;;) { 14736433Sbostic if (!fgets(p, endp - p + 1, stdin)) 14836433Sbostic break; 14936433Sbostic for (; *p && p < endp; ++p); 15036433Sbostic if (p == endp) { 15136433Sbostic *p = '\n'; 15236433Sbostic *++p = '\0'; 15336433Sbostic break; 15436433Sbostic } 15536433Sbostic } 1563648Stoy } 15735528Sbostic mbuflen = strlen(mbuf); 15835528Sbostic 15935528Sbostic if (offset) 160*38990Sbostic printf("Shutdown at %.24s.\n", ctime(&shuttime)); 16135528Sbostic else 16235528Sbostic printf("Shutdown NOW!\n"); 16335528Sbostic 16435528Sbostic if (!(whom = getlogin())) 16535528Sbostic whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 16635528Sbostic 16735528Sbostic #ifdef DEBUG 16835528Sbostic (void)putc('\n', stdout); 16935528Sbostic #else 17035528Sbostic (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 17135528Sbostic { 17235528Sbostic int forkpid; 17335528Sbostic 17435528Sbostic forkpid = fork(); 17535528Sbostic if (forkpid == -1) { 17635528Sbostic perror("shutdown: fork"); 17735528Sbostic exit(1); 17835528Sbostic } 17935528Sbostic if (forkpid) { 18035528Sbostic printf("shutdown: [pid %d]\n", forkpid); 18135528Sbostic exit(0); 18235528Sbostic } 18328024Seric } 1842373Swnj #endif 18535528Sbostic openlog("shutdown", LOG_CONS, LOG_AUTH); 18635528Sbostic loop(); 18735528Sbostic /*NOTREACHED*/ 18835528Sbostic } 18935528Sbostic 19035528Sbostic loop() 19135528Sbostic { 19235528Sbostic u_int sltime; 19335528Sbostic int logged; 19435528Sbostic 19535528Sbostic if (offset <= NOLOG_TIME) { 19635528Sbostic logged = 1; 19735528Sbostic nolog(); 1982373Swnj } 19935528Sbostic else 20035528Sbostic logged = 0; 20135528Sbostic tp = tlist; 20235528Sbostic if (tp->timeleft < offset) 20335528Sbostic (void)sleep((u_int)(offset - tp->timeleft)); 20435528Sbostic else { 20535528Sbostic while (offset < tp->timeleft) 20635528Sbostic ++tp; 20735528Sbostic /* 20835528Sbostic * warn now, if going to sleep more than a fifth of 20935528Sbostic * the next wait time. 21035528Sbostic */ 21135528Sbostic if (sltime = offset - tp->timeleft) { 21235528Sbostic if (sltime > tp->timetowait / 5) 21335528Sbostic warn(); 21435528Sbostic (void)sleep(sltime); 21535528Sbostic } 2168841Smckusick } 21735528Sbostic for (;; ++tp) { 21835528Sbostic warn(); 21935528Sbostic if (!logged && tp->timeleft <= NOLOG_TIME) { 22035528Sbostic logged = 1; 22135528Sbostic nolog(); 2222373Swnj } 22335528Sbostic (void)sleep((u_int)tp->timetowait); 22435528Sbostic if (!tp->timeleft) 22535528Sbostic break; 22635528Sbostic } 22735528Sbostic die_you_gravy_sucking_pig_dog(); 22835528Sbostic } 22925366Sbloom 23035528Sbostic static jmp_buf alarmbuf; 23135528Sbostic 23235528Sbostic warn() 23335528Sbostic { 23435528Sbostic static int first; 23535528Sbostic static char hostname[MAXHOSTNAMELEN]; 23635528Sbostic FILE *pf; 23735528Sbostic char *ctime(); 23835528Sbostic int timeout(); 23935528Sbostic 24035528Sbostic if (!first++) { 24135528Sbostic (void)signal(SIGALRM, timeout); 24235528Sbostic (void)gethostname(hostname, sizeof(hostname)); 2432373Swnj } 24435528Sbostic 24537300Sbostic if (!(pf = popen(_PATH_WALL, "w"))) { 24637300Sbostic syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 24735528Sbostic return; 24835528Sbostic } 24935528Sbostic 25035528Sbostic fprintf(pf, "*** %sSystem shutdown message ***\n", 25135528Sbostic tp->timeleft ? "": "FINAL "); 25235528Sbostic 25335528Sbostic if (tp->timeleft > 10*60) 25435528Sbostic fprintf(pf, "System going down at %5.5s\n\n", 25535528Sbostic ctime(&shuttime) + 11); 25635528Sbostic else if (tp->timeleft > 59) 25735528Sbostic fprintf(pf, "System going down in %d minute%s\n\n", 25835528Sbostic tp->timeleft / 60, (tp->timeleft > 60) ? "s" : ""); 25935528Sbostic else if (tp->timeleft) 26035528Sbostic fprintf(pf, "System going down in 30 seconds\n\n"); 26135528Sbostic else 26235528Sbostic fprintf(pf, "System going down IMMEDIATELY\n\n"); 26335528Sbostic 26436433Sbostic if (mbuflen) 26536433Sbostic (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 26635528Sbostic 26735528Sbostic /* 26835528Sbostic * play some games, just in case wall doesn't come back 26935528Sbostic * probably unecessary, given that wall is careful. 27035528Sbostic */ 27135528Sbostic if (!setjmp(alarmbuf)) { 27235528Sbostic (void)alarm((u_int)30); 27335528Sbostic (void)pclose(pf); 27435528Sbostic (void)alarm((u_int)0); 27535528Sbostic } 2762373Swnj } 2772373Swnj 27835528Sbostic timeout() 2792373Swnj { 28035528Sbostic longjmp(alarmbuf, 1); 28135528Sbostic } 2822373Swnj 28335528Sbostic die_you_gravy_sucking_pig_dog() 28435528Sbostic { 28535528Sbostic syslog(LOG_NOTICE, "%s by %s: %s", 28635528Sbostic doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 28735528Sbostic (void)sleep(2); 28835528Sbostic 28935528Sbostic printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 29035528Sbostic if (killflg) { 29135528Sbostic printf("\rbut you'll have to do it yourself\r\n"); 2922373Swnj finish(); 2932373Swnj } 29435528Sbostic if (dofast) 29535528Sbostic doitfast(); 29635528Sbostic #ifdef DEBUG 29735528Sbostic if (doreboot) 29836433Sbostic printf("reboot"); 29936433Sbostic else if (dohalt) 30036433Sbostic printf("halt"); 30136433Sbostic if (nosync) 30236433Sbostic printf(" no sync"); 30335528Sbostic if (dofast) 30436433Sbostic printf(" no fsck"); 30536433Sbostic printf("\nkill -HUP 1\n"); 30635528Sbostic #else 30735528Sbostic if (doreboot) { 30837300Sbostic execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); 30937300Sbostic syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 31035528Sbostic perror("shutdown"); 31135528Sbostic } 31235528Sbostic else if (dohalt) { 31337300Sbostic execle(_PATH_HALT, "halt", "-l", nosync, 0); 31437300Sbostic syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 31535528Sbostic perror("shutdown"); 31635528Sbostic } 31735528Sbostic (void)kill(1, SIGTERM); /* to single user */ 31835528Sbostic #endif 3192373Swnj finish(); 3202373Swnj } 3212373Swnj 32235528Sbostic #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 32335528Sbostic static int dmsize[] = 32435528Sbostic { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 32535528Sbostic 32635528Sbostic getoffset(timearg) 32735528Sbostic register char *timearg; 3282373Swnj { 32935528Sbostic register struct tm *lt; 33035528Sbostic register char *p; 33135528Sbostic time_t now, time(); 33235528Sbostic int year, month, day, hour, min; 3332373Swnj 33435528Sbostic if (!strcasecmp(timearg, "now")) { /* now */ 33535528Sbostic offset = 0; 33635528Sbostic return; 33735528Sbostic } 3383880Swnj 33935528Sbostic (void)time(&now); 34035528Sbostic if (*timearg == '+') { /* +minutes */ 34135528Sbostic if (!isdigit(*++timearg)) 34235528Sbostic goto badtime; 34335528Sbostic min = atoi(timearg); 34435528Sbostic offset = min * 60; 34535528Sbostic shuttime = now + offset; 34635528Sbostic return; 34735528Sbostic } 3488841Smckusick 34935528Sbostic /* handle hh:mm by getting rid of the colon */ 35035528Sbostic for (p = timearg; *p; ++p) 35135528Sbostic if (!isascii(*p) || !isdigit(*p)) 35235528Sbostic if (*p == ':' && strlen(p) == 3) { 35335528Sbostic p[0] = p[1]; 35435528Sbostic p[1] = p[2]; 35535528Sbostic p[2] = '\0'; 35635528Sbostic } 35735528Sbostic else 35835528Sbostic goto badtime; 35935528Sbostic 36035528Sbostic unsetenv("TZ"); /* OUR timezone */ 36135528Sbostic lt = localtime(&now); /* [yymmdd]hhmm */ 36235528Sbostic year = lt->tm_year; 36335528Sbostic month = lt->tm_mon + 1; 36435528Sbostic day = lt->tm_mday; 36535528Sbostic 36635528Sbostic switch(strlen(timearg)) { 36735528Sbostic case 10: 36835528Sbostic year = ATOI2(timearg); 36935528Sbostic /* FALLTHROUGH */ 37035528Sbostic case 8: 37135528Sbostic month = ATOI2(timearg); 37235528Sbostic /* FALLTHROUGH */ 37335528Sbostic case 6: 37435528Sbostic day = ATOI2(timearg); 37535528Sbostic /* FALLTHROUGH */ 37635528Sbostic case 4: 37735528Sbostic hour = ATOI2(timearg); 37835528Sbostic min = ATOI2(timearg); 37935528Sbostic if (month < 1 || month > 12 || day < 1 || day > 31 || 38035528Sbostic hour < 0 || hour > 23 || min < 0 || min > 59) 38135528Sbostic goto badtime; 38235528Sbostic shuttime = 0; 38335528Sbostic year += TM_YEAR_BASE; 38435528Sbostic if (isleap(year) && month > 2) 38535528Sbostic ++shuttime; 38635528Sbostic for (--year; year >= EPOCH_YEAR; --year) 38735528Sbostic shuttime += isleap(year) ? 38837300Sbostic DAYSPERLYEAR : DAYSPERNYEAR; 38935528Sbostic while (--month) 39035528Sbostic shuttime += dmsize[month]; 39135528Sbostic shuttime += day - 1; 39237300Sbostic shuttime = HOURSPERDAY * shuttime + hour; 39337300Sbostic shuttime = MINSPERHOUR * shuttime + min; 39437300Sbostic shuttime *= SECSPERMIN; 39535528Sbostic shuttime -= lt->tm_gmtoff; 39635528Sbostic if ((offset = shuttime - now) >= 0) 39735528Sbostic break; 39835528Sbostic /* FALLTHROUGH */ 39935528Sbostic default: 40035528Sbostic badtime: fprintf(stderr, 40135528Sbostic "shutdown: bad time format, or already past.\n"); 40235528Sbostic exit(1); 40335528Sbostic } 4042373Swnj } 4052373Swnj 40635528Sbostic #define FSMSG "fastboot file for fsck\n" 40725366Sbloom doitfast() 40825366Sbloom { 40935528Sbostic int fastfd; 41025366Sbloom 41137300Sbostic if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 41237300Sbostic 0664)) >= 0) { 41335528Sbostic (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); 41435528Sbostic (void)close(fastfd); 41525366Sbloom } 41625366Sbloom } 41725366Sbloom 41835528Sbostic #define NOMSG "\n\nNO LOGINS: System going down at " 41935528Sbostic nolog() 4202373Swnj { 42135528Sbostic int logfd, finish(); 42235528Sbostic char *ct, *ctime(); 4232373Swnj 42437300Sbostic (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 42535528Sbostic (void)signal(SIGINT, finish); 42635528Sbostic (void)signal(SIGHUP, finish); 42735528Sbostic (void)signal(SIGQUIT, finish); 42835528Sbostic (void)signal(SIGTERM, finish); 42937300Sbostic if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 43037300Sbostic 0664)) >= 0) { 43135528Sbostic (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 43235528Sbostic ct = ctime(&shuttime); 43335528Sbostic (void)write(logfd, ct + 11, 5); 43435528Sbostic (void)write(logfd, "\n\n", 2); 43535528Sbostic (void)write(logfd, mbuf, strlen(mbuf)); 43635528Sbostic (void)close(logfd); 4372373Swnj } 4382373Swnj } 4392373Swnj 4402373Swnj finish() 4412373Swnj { 44237300Sbostic (void)unlink(_PATH_NOLOGIN); 4432373Swnj exit(0); 4442373Swnj } 4452373Swnj 44635528Sbostic usage() 4472373Swnj { 44835528Sbostic fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); 44935528Sbostic exit(1); 4502373Swnj } 451