121179Sdist /* 221179Sdist * Copyright (c) 1983 Regents of the University of California. 321179Sdist * All rights reserved. The Berkeley software License Agreement 421179Sdist * specifies the terms and conditions for redistribution. 521179Sdist */ 621179Sdist 712682Ssam #ifndef lint 821179Sdist char copyright[] = 921179Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 1021179Sdist All rights reserved.\n"; 1121179Sdist #endif not lint 122373Swnj 1321179Sdist #ifndef lint 14*25366Sbloom static char sccsid[] = "@(#)shutdown.c 5.2 (Berkeley) 11/01/85"; 1521179Sdist #endif not lint 1621179Sdist 172373Swnj #include <stdio.h> 182373Swnj #include <ctype.h> 192373Swnj #include <signal.h> 2015920Skarels #include <setjmp.h> 212373Swnj #include <utmp.h> 2212897Ssam #include <sys/time.h> 2312897Ssam #include <sys/resource.h> 242373Swnj #include <sys/types.h> 252373Swnj /* 262373Swnj * /etc/shutdown when [messages] 272373Swnj * 282373Swnj * allow super users to tell users and remind users 292373Swnj * of iminent shutdown of unix 302373Swnj * and shut it down automatically 312373Swnj * and even reboot or halt the machine if they desire 322373Swnj */ 332818Swnj #ifdef DEBUG 342818Swnj #define LOGFILE "shutdown.log" 352818Swnj #else 362818Swnj #define LOGFILE "/usr/adm/shutdownlog" 372818Swnj #endif 382373Swnj #define REBOOT "/etc/reboot" 392373Swnj #define HALT "/etc/halt" 402373Swnj #define MAXINTS 20 412373Swnj #define HOURS *3600 422373Swnj #define MINUTES *60 432373Swnj #define SECONDS 443880Swnj #define NLOG 20 /* no of args possible for message */ 452373Swnj #define NOLOGTIME 5 MINUTES 463885Sroot #define IGNOREUSER "sleeper" 472373Swnj 486203Sroot char hostname[32]; 496203Sroot 5015920Skarels int timeout(); 512373Swnj time_t getsdt(); 522373Swnj 532373Swnj extern char *ctime(); 542373Swnj extern struct tm *localtime(); 552373Swnj 562373Swnj struct utmp utmp; 572373Swnj int sint; 582373Swnj int stogo; 592373Swnj char tpath[] = "/dev/"; 602373Swnj int nlflag = 1; /* nolog yet to be done */ 612373Swnj int killflg = 1; 622373Swnj int reboot = 0; 632373Swnj int halt = 0; 64*25366Sbloom int fast = 0; 65*25366Sbloom char *nosync = NULL; 66*25366Sbloom char nosyncflag[] = "-n"; 672373Swnj char term[sizeof tpath + sizeof utmp.ut_line]; 682373Swnj char tbuf[BUFSIZ]; 692373Swnj char nolog1[] = "\n\nNO LOGINS: System going down at %5.5s\n\n"; 702373Swnj char *nolog2[NLOG+1]; 712373Swnj #ifdef DEBUG 722373Swnj char nologin[] = "nologin"; 73*25366Sbloom char fastboot[] = "fastboot"; 742373Swnj #else 752373Swnj char nologin[] = "/etc/nologin"; 76*25366Sbloom char fastboot[] = "/fastboot"; 772373Swnj #endif 788841Smckusick time_t nowtime; 7915920Skarels jmp_buf alarmbuf; 808841Smckusick 812373Swnj struct interval { 822373Swnj int stogo; 832373Swnj int sint; 842373Swnj } interval[] = { 852373Swnj 4 HOURS, 1 HOURS, 862373Swnj 2 HOURS, 30 MINUTES, 872373Swnj 1 HOURS, 15 MINUTES, 882373Swnj 30 MINUTES, 10 MINUTES, 892373Swnj 15 MINUTES, 5 MINUTES, 902818Swnj 10 MINUTES, 5 MINUTES, 912818Swnj 5 MINUTES, 3 MINUTES, 928841Smckusick 2 MINUTES, 1 MINUTES, 938841Smckusick 1 MINUTES, 30 SECONDS, 942373Swnj 0 SECONDS, 0 SECONDS 952373Swnj }; 968841Smckusick 972818Swnj char *shutter, *getlogin(); 988841Smckusick 992373Swnj main(argc,argv) 1002373Swnj int argc; 1012373Swnj char **argv; 1022373Swnj { 1032373Swnj register i, ufd; 1042373Swnj register char **mess, *f; 1052373Swnj char *ts; 1068841Smckusick time_t sdt; 1072373Swnj int h, m; 1088841Smckusick int first; 1092373Swnj FILE *termf; 1102373Swnj 1112818Swnj shutter = getlogin(); 1126203Sroot gethostname(hostname, sizeof (hostname)); 1132373Swnj argc--, argv++; 1142373Swnj while (argc > 0 && (f = argv[0], *f++ == '-')) { 1152373Swnj while (i = *f++) switch (i) { 1162373Swnj case 'k': 1172373Swnj killflg = 0; 1182373Swnj continue; 119*25366Sbloom case 'n': 120*25366Sbloom nosync = nosyncflag; 121*25366Sbloom continue; 122*25366Sbloom case 'f': 123*25366Sbloom fast = 1; 124*25366Sbloom continue; 1252373Swnj case 'r': 1262373Swnj reboot = 1; 1272373Swnj continue; 1282373Swnj case 'h': 1292373Swnj halt = 1; 1302373Swnj continue; 1312373Swnj default: 1322373Swnj fprintf(stderr, "shutdown: '%c' - unknown flag\n", i); 1332373Swnj exit(1); 1342373Swnj } 1352373Swnj argc--, argv++; 1362373Swnj } 1372373Swnj if (argc < 1) { 138*25366Sbloom /* argv[0] is not available after the argument handling. */ 139*25366Sbloom printf("Usage: shutdown [ -krhfn ] shutdowntime [ message ]\n"); 1402373Swnj finish(); 1412373Swnj } 142*25366Sbloom if (fast && (nosync == nosyncflag)) { 143*25366Sbloom printf ("shutdown: Incompatible switches 'fast' & 'nosync'\n"); 144*25366Sbloom finish(); 145*25366Sbloom } 1463720Sroot if (geteuid()) { 1473720Sroot fprintf(stderr, "NOT super-user\n"); 1483648Stoy finish(); 1493648Stoy } 1508841Smckusick nowtime = time((time_t *)0); 1512373Swnj sdt = getsdt(argv[0]); 1522373Swnj argc--, argv++; 1532373Swnj i = 0; 1542373Swnj while (argc-- > 0) 1552373Swnj if (i < NLOG) 1562373Swnj nolog2[i++] = *argv++; 1572373Swnj nolog2[i] = NULL; 1582373Swnj m = ((stogo = sdt - nowtime) + 30)/60; 1592373Swnj h = m/60; 1602373Swnj m %= 60; 1612373Swnj ts = ctime(&sdt); 1622818Swnj printf("Shutdown at %5.5s (in ", ts+11); 1632373Swnj if (h > 0) 1642373Swnj printf("%d hour%s ", h, h != 1 ? "s" : ""); 1652818Swnj printf("%d minute%s) ", m, m != 1 ? "s" : ""); 1662373Swnj #ifndef DEBUG 1672373Swnj signal(SIGHUP, SIG_IGN); 1682373Swnj signal(SIGQUIT, SIG_IGN); 1692373Swnj signal(SIGINT, SIG_IGN); 1702373Swnj #endif 17112129Sedward signal(SIGTTOU, SIG_IGN); 1722373Swnj signal(SIGTERM, finish); 17315920Skarels signal(SIGALRM, timeout); 17412897Ssam setpriority(PRIO_PROCESS, 0, PRIO_MIN); 1753880Swnj fflush(stdout); 1762818Swnj #ifndef DEBUG 1772373Swnj if (i = fork()) { 1782818Swnj printf("[pid %d]\n", i); 1792373Swnj exit(0); 1802373Swnj } 1818841Smckusick #else 1828841Smckusick putc('\n', stdout); 1832818Swnj #endif 1842373Swnj sint = 1 HOURS; 1852373Swnj f = ""; 1868841Smckusick ufd = open("/etc/utmp",0); 1878841Smckusick if (ufd < 0) { 1888841Smckusick perror("shutdown: /etc/utmp"); 1898841Smckusick exit(1); 1908841Smckusick } 1918841Smckusick first = 1; 1922373Swnj for (;;) { 1932373Swnj for (i = 0; stogo <= interval[i].stogo && interval[i].sint; i++) 1942373Swnj sint = interval[i].sint; 1958841Smckusick if (stogo > 0 && (stogo-sint) < interval[i].stogo) 1968841Smckusick sint = stogo - interval[i].stogo; 1972373Swnj if (stogo <= NOLOGTIME && nlflag) { 1982373Swnj nlflag = 0; 1992373Swnj nolog(sdt); 2002373Swnj } 2012373Swnj if (sint >= stogo || sint == 0) 2022373Swnj f = "FINAL "; 2038841Smckusick nowtime = time((time_t *) 0); 2048841Smckusick lseek(ufd, 0L, 0); 2052373Swnj while (read(ufd,&utmp,sizeof utmp)==sizeof utmp) 2063885Sroot if (utmp.ut_name[0] && 2073885Sroot strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name))) { 20815920Skarels if (setjmp(alarmbuf)) 20915920Skarels continue; 2102373Swnj strcpy(term, tpath); 2112373Swnj strncat(term, utmp.ut_line, sizeof utmp.ut_line); 2122373Swnj alarm(3); 2132373Swnj #ifdef DEBUG 2142818Swnj if ((termf = stdout) != NULL) 2152373Swnj #else 2162373Swnj if ((termf = fopen(term, "w")) != NULL) 2172373Swnj #endif 2182373Swnj { 2192373Swnj alarm(0); 2202373Swnj setbuf(termf, tbuf); 2218841Smckusick fprintf(termf, "\n\r\n"); 2228841Smckusick warn(termf, sdt, nowtime, f); 2238841Smckusick if (first || sdt - nowtime > 1 MINUTES) { 2248841Smckusick if (*nolog2) 2258841Smckusick fprintf(termf, "\t..."); 2262373Swnj for (mess = nolog2; *mess; mess++) 2278841Smckusick fprintf(termf, " %s", *mess); 2288841Smckusick } 2298841Smckusick fputc('\r', termf); 2302373Swnj fputc('\n', termf); 2312818Swnj alarm(5); 2322818Swnj #ifdef DEBUG 2332818Swnj fflush(termf); 2342818Swnj #else 2352373Swnj fclose(termf); 2362818Swnj #endif 2372373Swnj alarm(0); 2382373Swnj } 2392373Swnj } 2408841Smckusick if (stogo <= 0) { 2412373Swnj printf("\n\007\007System shutdown time has arrived\007\007\n"); 2422818Swnj log_entry(sdt); 2432373Swnj unlink(nologin); 2442373Swnj if (!killflg) { 2452373Swnj printf("but you'll have to do it yourself\n"); 2462373Swnj finish(); 2472373Swnj } 248*25366Sbloom if (fast) 249*25366Sbloom doitfast(); 2502373Swnj #ifndef DEBUG 2518841Smckusick kill(-1, SIGTERM); /* terminate everyone */ 2528841Smckusick sleep(5); /* & wait while they die */ 2532373Swnj if (reboot) 254*25366Sbloom execle(REBOOT, "reboot", nosync, 0, 0); 2552373Swnj if (halt) 256*25366Sbloom execle(HALT, "halt", nosync, 0, 0); 2572373Swnj kill(1, SIGTERM); /* sync */ 2582373Swnj kill(1, SIGTERM); /* sync */ 2592373Swnj sleep(20); 2602373Swnj #else 2612373Swnj printf("EXTERMINATE EXTERMINATE\n"); 262*25366Sbloom if (reboot) 263*25366Sbloom printf("REBOOT"); 264*25366Sbloom if (halt) 265*25366Sbloom printf(" HALT"); 266*25366Sbloom if (fast) 267*25366Sbloom printf(" %s (without fsck's)\n", nosync); 268*25366Sbloom else 269*25366Sbloom printf(" %s\n", nosync); 270*25366Sbloom 2712373Swnj #endif 2722373Swnj finish(); 2732373Swnj } 2748841Smckusick stogo = sdt - time((time_t *) 0); 2758841Smckusick if (stogo > 0 && sint > 0) 2762373Swnj sleep(sint<stogo ? sint : stogo); 2772373Swnj stogo -= sint; 2788841Smckusick first = 0; 2792373Swnj } 2802373Swnj } 2812373Swnj 2822373Swnj time_t 2832373Swnj getsdt(s) 2848841Smckusick register char *s; 2852373Swnj { 2862373Swnj time_t t, t1, tim; 2872373Swnj register char c; 2882373Swnj struct tm *lt; 2892373Swnj 2908841Smckusick if (strcmp(s, "now") == 0) 2918841Smckusick return(nowtime); 2922373Swnj if (*s == '+') { 2932373Swnj ++s; 2942373Swnj t = 0; 2952373Swnj for (;;) { 2962373Swnj c = *s++; 2972373Swnj if (!isdigit(c)) 2982373Swnj break; 2992373Swnj t = t * 10 + c - '0'; 3002373Swnj } 3012373Swnj if (t <= 0) 3022373Swnj t = 5; 3032373Swnj t *= 60; 3048841Smckusick tim = time((time_t *) 0) + t; 3052373Swnj return(tim); 3062373Swnj } 3072373Swnj t = 0; 3082373Swnj while (strlen(s) > 2 && isdigit(*s)) 3092373Swnj t = t * 10 + *s++ - '0'; 3102373Swnj if (*s == ':') 3112373Swnj s++; 3122373Swnj if (t > 23) 3132373Swnj goto badform; 3142373Swnj tim = t*60; 3152373Swnj t = 0; 3162373Swnj while (isdigit(*s)) 3172373Swnj t = t * 10 + *s++ - '0'; 3182373Swnj if (t > 59) 3192373Swnj goto badform; 3202373Swnj tim += t; 3212373Swnj tim *= 60; 3228841Smckusick t1 = time((time_t *) 0); 3232373Swnj lt = localtime(&t1); 3242373Swnj t = lt->tm_sec + lt->tm_min*60 + lt->tm_hour*3600; 3252373Swnj if (tim < t || tim >= (24*3600)) { 3262373Swnj /* before now or after midnight */ 3272373Swnj printf("That must be tomorrow\nCan't you wait till then?\n"); 3282373Swnj finish(); 3292373Swnj } 3308841Smckusick return (t1 + tim - t); 3312373Swnj badform: 3322373Swnj printf("Bad time format\n"); 3332373Swnj finish(); 3342373Swnj } 3352373Swnj 3368841Smckusick warn(term, sdt, now, type) 3372373Swnj FILE *term; 3388841Smckusick time_t sdt, now; 3398841Smckusick char *type; 3402373Swnj { 3412373Swnj char *ts; 3428841Smckusick register delay = sdt - now; 3432373Swnj 3443880Swnj if (delay > 8) 3453880Swnj while (delay % 5) 3463880Swnj delay++; 3473880Swnj 3483880Swnj if (shutter) 3493880Swnj fprintf(term, 35013178Sroot "\007\007\t*** %sSystem shutdown message from %s@%s ***\r\n\n", 35113178Sroot type, shutter, hostname); 3523880Swnj else 3533880Swnj fprintf(term, 3548841Smckusick "\007\007\t*** %sSystem shutdown message (%s) ***\r\n\n", 3558841Smckusick type, hostname); 3568841Smckusick 3572373Swnj ts = ctime(&sdt); 3588841Smckusick if (delay > 10 MINUTES) 3598841Smckusick fprintf(term, "System going down at %5.5s\r\n", ts+11); 3608841Smckusick else if (delay > 95 SECONDS) { 3618841Smckusick fprintf(term, "System going down in %d minute%s\r\n", 3628841Smckusick (delay+30)/60, (delay+30)/60 != 1 ? "s" : ""); 3638841Smckusick } else if (delay > 0) { 3648841Smckusick fprintf(term, "System going down in %d second%s\r\n", 3658841Smckusick delay, delay != 1 ? "s" : ""); 3662373Swnj } else 3678841Smckusick fprintf(term, "System going down IMMEDIATELY\r\n"); 3682373Swnj } 3692373Swnj 370*25366Sbloom doitfast() 371*25366Sbloom { 372*25366Sbloom FILE *fastd; 373*25366Sbloom 374*25366Sbloom if ((fastd = fopen(fastboot, "w")) != NULL) { 375*25366Sbloom putc('\n', fastd); 376*25366Sbloom fclose(fastd); 377*25366Sbloom } 378*25366Sbloom } 379*25366Sbloom 3802373Swnj nolog(sdt) 3818841Smckusick time_t sdt; 3822373Swnj { 3832373Swnj FILE *nologf; 3842373Swnj register char **mess; 3852373Swnj 3868841Smckusick unlink(nologin); /* in case linked to std file */ 3872373Swnj if ((nologf = fopen(nologin, "w")) != NULL) { 3882373Swnj fprintf(nologf, nolog1, (ctime(&sdt)) + 11); 3893880Swnj putc('\t', nologf); 3902373Swnj for (mess = nolog2; *mess; mess++) 3913880Swnj fprintf(nologf, " %s", *mess); 3923880Swnj putc('\n', nologf); 3932373Swnj fclose(nologf); 3942373Swnj } 3952373Swnj } 3962373Swnj 3972373Swnj finish() 3982373Swnj { 3992373Swnj signal(SIGTERM, SIG_IGN); 4002373Swnj unlink(nologin); 4012373Swnj exit(0); 4022373Swnj } 4032373Swnj 40415920Skarels timeout() 4052373Swnj { 40615920Skarels longjmp(alarmbuf, 1); 4072373Swnj } 4082818Swnj 4092818Swnj /* 4102818Swnj * make an entry in the shutdown log 4112818Swnj */ 4122818Swnj 4133484Sroot char *days[] = { 4143484Sroot "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 4153484Sroot }; 4163484Sroot 4173484Sroot char *months[] = { 4183484Sroot "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", 4193484Sroot "Oct", "Nov", "Dec" 4203484Sroot }; 4213484Sroot 4223484Sroot log_entry(now) 4238841Smckusick time_t now; 4242818Swnj { 4253484Sroot register FILE *fp; 4263484Sroot register char **mess; 4273484Sroot struct tm *tm, *localtime(); 4282818Swnj 4293484Sroot tm = localtime(&now); 4303484Sroot fp = fopen(LOGFILE, "a"); 4318841Smckusick if (fp == NULL) { 4328841Smckusick printf("Shutdown: log entry failed\n"); 4333578Sroot return; 4348841Smckusick } 4353484Sroot fseek(fp, 0L, 2); 4363484Sroot fprintf(fp, "%02d:%02d %s %s %2d, %4d. Shutdown:", tm->tm_hour, 4373484Sroot tm->tm_min, days[tm->tm_wday], months[tm->tm_mon], 4383484Sroot tm->tm_mday, tm->tm_year + 1900); 4392818Swnj for (mess = nolog2; *mess; mess++) 4403484Sroot fprintf(fp, " %s", *mess); 4413880Swnj if (shutter) 4428841Smckusick fprintf(fp, " (by %s!%s)", hostname, shutter); 4433484Sroot fputc('\n', fp); 4443484Sroot fclose(fp); 4452818Swnj } 446