121179Sdist /* 240224Skarels * Copyright (c) 1988, 1990 Regents of the University of California. 335528Sbostic * All rights reserved. 435528Sbostic * 5*42712Sbostic * %sccs.include.redist.c% 621179Sdist */ 721179Sdist 812682Ssam #ifndef lint 921179Sdist char copyright[] = 1035528Sbostic "@(#) Copyright (c) 1988 Regents of the University of California.\n\ 1121179Sdist All rights reserved.\n"; 1235528Sbostic #endif /* not lint */ 132373Swnj 1421179Sdist #ifndef lint 15*42712Sbostic static char sccsid[] = "@(#)shutdown.c 5.14 (Berkeley) 06/01/90"; 1635528Sbostic #endif /* not lint */ 1721179Sdist 1835528Sbostic #include <sys/param.h> 1935528Sbostic #include <sys/time.h> 2035528Sbostic #include <sys/file.h> 2135528Sbostic #include <sys/resource.h> 2235528Sbostic #include <sys/syslog.h> 232373Swnj #include <signal.h> 2415920Skarels #include <setjmp.h> 2535528Sbostic #include <tzfile.h> 2628802Skarels #include <pwd.h> 2735528Sbostic #include <stdio.h> 2835528Sbostic #include <ctype.h> 2937300Sbostic #include "pathnames.h" 3028024Seric 3135528Sbostic #ifdef DEBUG 3237300Sbostic #undef _PATH_NOLOGIN 3337300Sbostic #define _PATH_NOLOGIN "./nologin" 3437300Sbostic #undef _PATH_FASTBOOT 3537300Sbostic #define _PATH_FASTBOOT "./fastboot" 362373Swnj #endif 378841Smckusick 3835528Sbostic #define H *60*60 3935528Sbostic #define M *60 4035528Sbostic #define S *1 4135528Sbostic #define NOLOG_TIME 5*60 422373Swnj struct interval { 4335528Sbostic int timeleft, timetowait; 4435528Sbostic } tlist[] = { 4535528Sbostic 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, 4635528Sbostic 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, 4735528Sbostic 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, 4835528Sbostic 0, 0, 4935528Sbostic }, *tp = tlist; 5035528Sbostic #undef H 5135528Sbostic #undef M 5235528Sbostic #undef S 538841Smckusick 5435528Sbostic static time_t offset, shuttime; 5535528Sbostic static int dofast, dohalt, doreboot, killflg, mbuflen; 5635528Sbostic static char *nosync, *whom, mbuf[BUFSIZ]; 578841Smckusick 5835528Sbostic main(argc, argv) 592373Swnj int argc; 602373Swnj char **argv; 612373Swnj { 6235528Sbostic extern int optind; 6336433Sbostic register char *p, *endp; 6436433Sbostic int arglen, ch, len, readstdin; 6528802Skarels struct passwd *pw, *getpwuid(); 6635528Sbostic char *strcat(), *getlogin(); 6735528Sbostic uid_t geteuid(); 682373Swnj 6935528Sbostic #ifndef DEBUG 7035528Sbostic if (geteuid()) { 7135528Sbostic fprintf(stderr, "shutdown: NOT super-user\n"); 7235528Sbostic exit(1); 7335528Sbostic } 7435528Sbostic #endif 7536433Sbostic nosync = NULL; 7636433Sbostic readstdin = 0; 7736433Sbostic while ((ch = getopt(argc, argv, "-fhknr")) != EOF) 7840224Skarels switch (ch) { 7936433Sbostic case '-': 8036433Sbostic readstdin = 1; 8136433Sbostic break; 8235528Sbostic case 'f': 8335528Sbostic dofast = 1; 8435528Sbostic break; 8535528Sbostic case 'h': 8635528Sbostic dohalt = 1; 8735528Sbostic break; 882373Swnj case 'k': 8935528Sbostic killflg = 1; 9035528Sbostic break; 9125366Sbloom case 'n': 9235528Sbostic nosync = "-n"; 9335528Sbostic break; 942373Swnj case 'r': 9525408Sbloom doreboot = 1; 9635528Sbostic break; 9735528Sbostic case '?': 982373Swnj default: 9935528Sbostic usage(); 1002373Swnj } 10135528Sbostic argc -= optind; 10235528Sbostic argv += optind; 10335528Sbostic 10435528Sbostic if (argc < 1) 10535528Sbostic usage(); 10635528Sbostic 10736433Sbostic if (dofast && nosync) { 10835528Sbostic fprintf(stderr, 10935528Sbostic "shutdown: incompatible switches -f and -n.\n"); 11035528Sbostic usage(); 1112373Swnj } 11235528Sbostic if (doreboot && dohalt) { 11335528Sbostic fprintf(stderr, 11435528Sbostic "shutdown: incompatible switches -h and -r.\n"); 11535528Sbostic usage(); 1162373Swnj } 11735528Sbostic getoffset(*argv++); 11836433Sbostic 11935528Sbostic if (*argv) { 12036433Sbostic for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 12136433Sbostic arglen = strlen(*argv); 12236433Sbostic if ((len -= arglen) <= 2) 12336433Sbostic break; 12436433Sbostic if (p != mbuf) 12536433Sbostic *p++ = ' '; 12636433Sbostic bcopy(*argv, p, arglen); 12736433Sbostic p += arglen; 12836433Sbostic } 12936433Sbostic *p = '\n'; 13036433Sbostic *++p = '\0'; 13125366Sbloom } 13236433Sbostic 13336433Sbostic if (readstdin) { 13436433Sbostic p = mbuf; 13536433Sbostic endp = mbuf + sizeof(mbuf) - 2; 13636433Sbostic for (;;) { 13736433Sbostic if (!fgets(p, endp - p + 1, stdin)) 13836433Sbostic break; 13936433Sbostic for (; *p && p < endp; ++p); 14036433Sbostic if (p == endp) { 14136433Sbostic *p = '\n'; 14236433Sbostic *++p = '\0'; 14336433Sbostic break; 14436433Sbostic } 14536433Sbostic } 1463648Stoy } 14735528Sbostic mbuflen = strlen(mbuf); 14835528Sbostic 14935528Sbostic if (offset) 15038990Sbostic printf("Shutdown at %.24s.\n", ctime(&shuttime)); 15135528Sbostic else 15235528Sbostic printf("Shutdown NOW!\n"); 15335528Sbostic 15435528Sbostic if (!(whom = getlogin())) 15535528Sbostic whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 15635528Sbostic 15735528Sbostic #ifdef DEBUG 15835528Sbostic (void)putc('\n', stdout); 15935528Sbostic #else 16035528Sbostic (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 16135528Sbostic { 16235528Sbostic int forkpid; 16335528Sbostic 16435528Sbostic forkpid = fork(); 16535528Sbostic if (forkpid == -1) { 16635528Sbostic perror("shutdown: fork"); 16735528Sbostic exit(1); 16835528Sbostic } 16935528Sbostic if (forkpid) { 17035528Sbostic printf("shutdown: [pid %d]\n", forkpid); 17135528Sbostic exit(0); 17235528Sbostic } 17328024Seric } 1742373Swnj #endif 17535528Sbostic openlog("shutdown", LOG_CONS, LOG_AUTH); 17635528Sbostic loop(); 17735528Sbostic /*NOTREACHED*/ 17835528Sbostic } 17935528Sbostic 18035528Sbostic loop() 18135528Sbostic { 18235528Sbostic u_int sltime; 18335528Sbostic int logged; 18435528Sbostic 18535528Sbostic if (offset <= NOLOG_TIME) { 18635528Sbostic logged = 1; 18735528Sbostic nolog(); 1882373Swnj } 18935528Sbostic else 19035528Sbostic logged = 0; 19135528Sbostic tp = tlist; 19235528Sbostic if (tp->timeleft < offset) 19335528Sbostic (void)sleep((u_int)(offset - tp->timeleft)); 19435528Sbostic else { 19535528Sbostic while (offset < tp->timeleft) 19635528Sbostic ++tp; 19735528Sbostic /* 19835528Sbostic * warn now, if going to sleep more than a fifth of 19935528Sbostic * the next wait time. 20035528Sbostic */ 20135528Sbostic if (sltime = offset - tp->timeleft) { 20235528Sbostic if (sltime > tp->timetowait / 5) 20335528Sbostic warn(); 20435528Sbostic (void)sleep(sltime); 20535528Sbostic } 2068841Smckusick } 20735528Sbostic for (;; ++tp) { 20835528Sbostic warn(); 20935528Sbostic if (!logged && tp->timeleft <= NOLOG_TIME) { 21035528Sbostic logged = 1; 21135528Sbostic nolog(); 2122373Swnj } 21335528Sbostic (void)sleep((u_int)tp->timetowait); 21435528Sbostic if (!tp->timeleft) 21535528Sbostic break; 21635528Sbostic } 21735528Sbostic die_you_gravy_sucking_pig_dog(); 21835528Sbostic } 21925366Sbloom 22035528Sbostic static jmp_buf alarmbuf; 22135528Sbostic 22235528Sbostic warn() 22335528Sbostic { 22435528Sbostic static int first; 22540224Skarels static char hostname[MAXHOSTNAMELEN + 1]; 22640224Skarels char wcmd[MAXPATHLEN + 4]; 22735528Sbostic FILE *pf; 22835528Sbostic char *ctime(); 22935528Sbostic int timeout(); 23035528Sbostic 23139421Sbostic if (!first++) 23240224Skarels (void)gethostname(hostname, sizeof(hostname)); 23335528Sbostic 23440224Skarels /* undoc -n option to wall suppresses normal wall banner */ 23540224Skarels (void) sprintf(wcmd, "%s -n", _PATH_WALL); 23640224Skarels if (!(pf = popen(wcmd, "w"))) { 23737300Sbostic syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 23835528Sbostic return; 23935528Sbostic } 24035528Sbostic 24140224Skarels fprintf(pf, "\007*** %sSystem shutdown message from %s@%s ***\007\n", 24240224Skarels tp->timeleft ? "": "FINAL ", whom, hostname); 24335528Sbostic 24435528Sbostic if (tp->timeleft > 10*60) 24535528Sbostic fprintf(pf, "System going down at %5.5s\n\n", 24635528Sbostic ctime(&shuttime) + 11); 24735528Sbostic else if (tp->timeleft > 59) 24835528Sbostic fprintf(pf, "System going down in %d minute%s\n\n", 24935528Sbostic tp->timeleft / 60, (tp->timeleft > 60) ? "s" : ""); 25035528Sbostic else if (tp->timeleft) 25135528Sbostic fprintf(pf, "System going down in 30 seconds\n\n"); 25235528Sbostic else 25335528Sbostic fprintf(pf, "System going down IMMEDIATELY\n\n"); 25435528Sbostic 25536433Sbostic if (mbuflen) 25636433Sbostic (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 25735528Sbostic 25835528Sbostic /* 25935528Sbostic * play some games, just in case wall doesn't come back 26035528Sbostic * probably unecessary, given that wall is careful. 26135528Sbostic */ 26235528Sbostic if (!setjmp(alarmbuf)) { 26340224Skarels (void)signal(SIGALRM, timeout); 26435528Sbostic (void)alarm((u_int)30); 26535528Sbostic (void)pclose(pf); 26635528Sbostic (void)alarm((u_int)0); 26740224Skarels (void)signal(SIGALRM, SIG_DFL); 26835528Sbostic } 2692373Swnj } 2702373Swnj 27135528Sbostic timeout() 2722373Swnj { 27335528Sbostic longjmp(alarmbuf, 1); 27435528Sbostic } 2752373Swnj 27635528Sbostic die_you_gravy_sucking_pig_dog() 27735528Sbostic { 27835528Sbostic syslog(LOG_NOTICE, "%s by %s: %s", 27935528Sbostic doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 28035528Sbostic (void)sleep(2); 28135528Sbostic 28235528Sbostic printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 28335528Sbostic if (killflg) { 28435528Sbostic printf("\rbut you'll have to do it yourself\r\n"); 2852373Swnj finish(); 2862373Swnj } 28735528Sbostic if (dofast) 28835528Sbostic doitfast(); 28935528Sbostic #ifdef DEBUG 29035528Sbostic if (doreboot) 29136433Sbostic printf("reboot"); 29236433Sbostic else if (dohalt) 29336433Sbostic printf("halt"); 29436433Sbostic if (nosync) 29536433Sbostic printf(" no sync"); 29635528Sbostic if (dofast) 29736433Sbostic printf(" no fsck"); 29836433Sbostic printf("\nkill -HUP 1\n"); 29935528Sbostic #else 30035528Sbostic if (doreboot) { 30137300Sbostic execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); 30237300Sbostic syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 30335528Sbostic perror("shutdown"); 30435528Sbostic } 30535528Sbostic else if (dohalt) { 30637300Sbostic execle(_PATH_HALT, "halt", "-l", nosync, 0); 30737300Sbostic syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 30835528Sbostic perror("shutdown"); 30935528Sbostic } 31035528Sbostic (void)kill(1, SIGTERM); /* to single user */ 31135528Sbostic #endif 3122373Swnj finish(); 3132373Swnj } 3142373Swnj 31535528Sbostic #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 31635528Sbostic static int dmsize[] = 31735528Sbostic { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 31835528Sbostic 31935528Sbostic getoffset(timearg) 32035528Sbostic register char *timearg; 3212373Swnj { 32235528Sbostic register struct tm *lt; 32335528Sbostic register char *p; 32435528Sbostic time_t now, time(); 32535528Sbostic int year, month, day, hour, min; 3262373Swnj 32735528Sbostic if (!strcasecmp(timearg, "now")) { /* now */ 32835528Sbostic offset = 0; 32935528Sbostic return; 33035528Sbostic } 3313880Swnj 33235528Sbostic (void)time(&now); 33335528Sbostic if (*timearg == '+') { /* +minutes */ 33435528Sbostic if (!isdigit(*++timearg)) 33535528Sbostic goto badtime; 33635528Sbostic min = atoi(timearg); 33735528Sbostic offset = min * 60; 33835528Sbostic shuttime = now + offset; 33935528Sbostic return; 34035528Sbostic } 3418841Smckusick 34235528Sbostic /* handle hh:mm by getting rid of the colon */ 34335528Sbostic for (p = timearg; *p; ++p) 34435528Sbostic if (!isascii(*p) || !isdigit(*p)) 34535528Sbostic if (*p == ':' && strlen(p) == 3) { 34635528Sbostic p[0] = p[1]; 34735528Sbostic p[1] = p[2]; 34835528Sbostic p[2] = '\0'; 34935528Sbostic } 35035528Sbostic else 35135528Sbostic goto badtime; 35235528Sbostic 35335528Sbostic unsetenv("TZ"); /* OUR timezone */ 35435528Sbostic lt = localtime(&now); /* [yymmdd]hhmm */ 35535528Sbostic year = lt->tm_year; 35635528Sbostic month = lt->tm_mon + 1; 35735528Sbostic day = lt->tm_mday; 35835528Sbostic 35935528Sbostic switch(strlen(timearg)) { 36035528Sbostic case 10: 36135528Sbostic year = ATOI2(timearg); 36235528Sbostic /* FALLTHROUGH */ 36335528Sbostic case 8: 36435528Sbostic month = ATOI2(timearg); 36535528Sbostic /* FALLTHROUGH */ 36635528Sbostic case 6: 36735528Sbostic day = ATOI2(timearg); 36835528Sbostic /* FALLTHROUGH */ 36935528Sbostic case 4: 37035528Sbostic hour = ATOI2(timearg); 37135528Sbostic min = ATOI2(timearg); 37235528Sbostic if (month < 1 || month > 12 || day < 1 || day > 31 || 37335528Sbostic hour < 0 || hour > 23 || min < 0 || min > 59) 37435528Sbostic goto badtime; 37535528Sbostic shuttime = 0; 37635528Sbostic year += TM_YEAR_BASE; 37735528Sbostic if (isleap(year) && month > 2) 37835528Sbostic ++shuttime; 37935528Sbostic for (--year; year >= EPOCH_YEAR; --year) 38035528Sbostic shuttime += isleap(year) ? 38137300Sbostic DAYSPERLYEAR : DAYSPERNYEAR; 38235528Sbostic while (--month) 38335528Sbostic shuttime += dmsize[month]; 38435528Sbostic shuttime += day - 1; 38537300Sbostic shuttime = HOURSPERDAY * shuttime + hour; 38637300Sbostic shuttime = MINSPERHOUR * shuttime + min; 38737300Sbostic shuttime *= SECSPERMIN; 38835528Sbostic shuttime -= lt->tm_gmtoff; 38935528Sbostic if ((offset = shuttime - now) >= 0) 39035528Sbostic break; 39135528Sbostic /* FALLTHROUGH */ 39235528Sbostic default: 39335528Sbostic badtime: fprintf(stderr, 39435528Sbostic "shutdown: bad time format, or already past.\n"); 39535528Sbostic exit(1); 39635528Sbostic } 3972373Swnj } 3982373Swnj 39935528Sbostic #define FSMSG "fastboot file for fsck\n" 40025366Sbloom doitfast() 40125366Sbloom { 40235528Sbostic int fastfd; 40325366Sbloom 40437300Sbostic if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 40537300Sbostic 0664)) >= 0) { 40635528Sbostic (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); 40735528Sbostic (void)close(fastfd); 40825366Sbloom } 40925366Sbloom } 41025366Sbloom 41135528Sbostic #define NOMSG "\n\nNO LOGINS: System going down at " 41235528Sbostic nolog() 4132373Swnj { 41435528Sbostic int logfd, finish(); 41535528Sbostic char *ct, *ctime(); 4162373Swnj 41737300Sbostic (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 41835528Sbostic (void)signal(SIGINT, finish); 41935528Sbostic (void)signal(SIGHUP, finish); 42035528Sbostic (void)signal(SIGQUIT, finish); 42135528Sbostic (void)signal(SIGTERM, finish); 42237300Sbostic if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 42337300Sbostic 0664)) >= 0) { 42435528Sbostic (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 42535528Sbostic ct = ctime(&shuttime); 42635528Sbostic (void)write(logfd, ct + 11, 5); 42735528Sbostic (void)write(logfd, "\n\n", 2); 42835528Sbostic (void)write(logfd, mbuf, strlen(mbuf)); 42935528Sbostic (void)close(logfd); 4302373Swnj } 4312373Swnj } 4322373Swnj 4332373Swnj finish() 4342373Swnj { 43537300Sbostic (void)unlink(_PATH_NOLOGIN); 4362373Swnj exit(0); 4372373Swnj } 4382373Swnj 43935528Sbostic usage() 4402373Swnj { 44135528Sbostic fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); 44235528Sbostic exit(1); 4432373Swnj } 444