121179Sdist /* 2*35528Sbostic * Copyright (c) 1988 Regents of the University of California. 3*35528Sbostic * All rights reserved. 4*35528Sbostic * 5*35528Sbostic * Redistribution and use in source and binary forms are permitted 6*35528Sbostic * provided that the above copyright notice and this paragraph are 7*35528Sbostic * duplicated in all such forms and that any documentation, 8*35528Sbostic * advertising materials, and other materials related to such 9*35528Sbostic * distribution and use acknowledge that the software was developed 10*35528Sbostic * by the University of California, Berkeley. The name of the 11*35528Sbostic * University may not be used to endorse or promote products derived 12*35528Sbostic * from this software without specific prior written permission. 13*35528Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*35528Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*35528Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1621179Sdist */ 1721179Sdist 1812682Ssam #ifndef lint 1921179Sdist char copyright[] = 20*35528Sbostic "@(#) Copyright (c) 1988 Regents of the University of California.\n\ 2121179Sdist All rights reserved.\n"; 22*35528Sbostic #endif /* not lint */ 232373Swnj 2421179Sdist #ifndef lint 25*35528Sbostic static char sccsid[] = "@(#)shutdown.c 5.8 (Berkeley) 09/18/88"; 26*35528Sbostic #endif /* not lint */ 2721179Sdist 28*35528Sbostic #include <sys/param.h> 29*35528Sbostic #include <sys/time.h> 30*35528Sbostic #include <sys/file.h> 31*35528Sbostic #include <sys/resource.h> 32*35528Sbostic #include <sys/syslog.h> 332373Swnj #include <signal.h> 3415920Skarels #include <setjmp.h> 35*35528Sbostic #include <tzfile.h> 3628802Skarels #include <pwd.h> 37*35528Sbostic #include <stdio.h> 38*35528Sbostic #include <ctype.h> 3928024Seric 40*35528Sbostic #define REBOOT "/etc/reboot" 41*35528Sbostic #define HALT "/etc/halt" 4228024Seric 43*35528Sbostic #ifdef DEBUG 44*35528Sbostic #define NOLOGIN "./nologin" 45*35528Sbostic #define FASTBOOT "./fastboot" 462373Swnj #else 47*35528Sbostic #define NOLOGIN "/etc/nologin" 48*35528Sbostic #define FASTBOOT "/fastboot" 492373Swnj #endif 508841Smckusick 51*35528Sbostic #define H *60*60 52*35528Sbostic #define M *60 53*35528Sbostic #define S *1 54*35528Sbostic #define NOLOG_TIME 5*60 552373Swnj struct interval { 56*35528Sbostic int timeleft, timetowait; 57*35528Sbostic } tlist[] = { 58*35528Sbostic 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, 59*35528Sbostic 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, 60*35528Sbostic 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, 61*35528Sbostic 0, 0, 62*35528Sbostic }, *tp = tlist; 63*35528Sbostic #undef H 64*35528Sbostic #undef M 65*35528Sbostic #undef S 668841Smckusick 67*35528Sbostic static time_t offset, shuttime; 68*35528Sbostic static int dofast, dohalt, doreboot, killflg, mbuflen; 69*35528Sbostic static char *nosync, *whom, mbuf[BUFSIZ]; 708841Smckusick 71*35528Sbostic main(argc, argv) 722373Swnj int argc; 732373Swnj char **argv; 742373Swnj { 75*35528Sbostic extern int optind; 76*35528Sbostic register char *p; 77*35528Sbostic int arglen, ch, len; 7828802Skarels struct passwd *pw, *getpwuid(); 79*35528Sbostic char *strcat(), *getlogin(); 80*35528Sbostic uid_t geteuid(); 812373Swnj 82*35528Sbostic #ifndef DEBUG 83*35528Sbostic if (geteuid()) { 84*35528Sbostic fprintf(stderr, "shutdown: NOT super-user\n"); 85*35528Sbostic exit(1); 86*35528Sbostic } 87*35528Sbostic #endif 88*35528Sbostic nosync = ""; 89*35528Sbostic while ((ch = getopt(argc, argv, "fhknr")) != EOF) 90*35528Sbostic switch((char)ch) { 91*35528Sbostic case 'f': 92*35528Sbostic dofast = 1; 93*35528Sbostic break; 94*35528Sbostic case 'h': 95*35528Sbostic dohalt = 1; 96*35528Sbostic break; 972373Swnj case 'k': 98*35528Sbostic killflg = 1; 99*35528Sbostic break; 10025366Sbloom case 'n': 101*35528Sbostic nosync = "-n"; 102*35528Sbostic break; 1032373Swnj case 'r': 10425408Sbloom doreboot = 1; 105*35528Sbostic break; 106*35528Sbostic case '?': 1072373Swnj default: 108*35528Sbostic usage(); 1092373Swnj } 110*35528Sbostic argc -= optind; 111*35528Sbostic argv += optind; 112*35528Sbostic 113*35528Sbostic if (argc < 1) 114*35528Sbostic usage(); 115*35528Sbostic 116*35528Sbostic if (dofast && *nosync) { 117*35528Sbostic fprintf(stderr, 118*35528Sbostic "shutdown: incompatible switches -f and -n.\n"); 119*35528Sbostic usage(); 1202373Swnj } 121*35528Sbostic if (doreboot && dohalt) { 122*35528Sbostic fprintf(stderr, 123*35528Sbostic "shutdown: incompatible switches -h and -r.\n"); 124*35528Sbostic usage(); 1252373Swnj } 126*35528Sbostic getoffset(*argv++); 127*35528Sbostic if (*argv) { 128*35528Sbostic do { 129*35528Sbostic (void)strcat(mbuf, *argv); 130*35528Sbostic (void)strcat(mbuf, " "); 131*35528Sbostic } while(*++argv); 132*35528Sbostic (void)strcat(mbuf, "\n"); 13325366Sbloom } 134*35528Sbostic else for (len = sizeof(mbuf), p = mbuf; fgets(p, len, stdin);) { 135*35528Sbostic arglen = strlen(p); 136*35528Sbostic if (!(len -= arglen)) 137*35528Sbostic break; 138*35528Sbostic p += arglen; 1393648Stoy } 140*35528Sbostic mbuflen = strlen(mbuf); 141*35528Sbostic 142*35528Sbostic if (offset) 143*35528Sbostic printf("Shutdown at %.24s.", ctime(&shuttime)); 144*35528Sbostic else 145*35528Sbostic printf("Shutdown NOW!\n"); 146*35528Sbostic 147*35528Sbostic if (!(whom = getlogin())) 148*35528Sbostic whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 149*35528Sbostic 150*35528Sbostic #ifdef DEBUG 151*35528Sbostic (void)putc('\n', stdout); 152*35528Sbostic #else 153*35528Sbostic (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 154*35528Sbostic { 155*35528Sbostic int forkpid; 156*35528Sbostic 157*35528Sbostic forkpid = fork(); 158*35528Sbostic if (forkpid == -1) { 159*35528Sbostic perror("shutdown: fork"); 160*35528Sbostic exit(1); 161*35528Sbostic } 162*35528Sbostic if (forkpid) { 163*35528Sbostic printf("shutdown: [pid %d]\n", forkpid); 164*35528Sbostic exit(0); 165*35528Sbostic } 16628024Seric } 1672373Swnj #endif 168*35528Sbostic openlog("shutdown", LOG_CONS, LOG_AUTH); 169*35528Sbostic loop(); 170*35528Sbostic /*NOTREACHED*/ 171*35528Sbostic } 172*35528Sbostic 173*35528Sbostic #define WALL_CMD "/bin/wall" 174*35528Sbostic 175*35528Sbostic loop() 176*35528Sbostic { 177*35528Sbostic u_int sltime; 178*35528Sbostic int logged; 179*35528Sbostic 180*35528Sbostic if (offset <= NOLOG_TIME) { 181*35528Sbostic logged = 1; 182*35528Sbostic nolog(); 1832373Swnj } 184*35528Sbostic else 185*35528Sbostic logged = 0; 186*35528Sbostic tp = tlist; 187*35528Sbostic if (tp->timeleft < offset) 188*35528Sbostic (void)sleep((u_int)(offset - tp->timeleft)); 189*35528Sbostic else { 190*35528Sbostic while (offset < tp->timeleft) 191*35528Sbostic ++tp; 192*35528Sbostic /* 193*35528Sbostic * warn now, if going to sleep more than a fifth of 194*35528Sbostic * the next wait time. 195*35528Sbostic */ 196*35528Sbostic if (sltime = offset - tp->timeleft) { 197*35528Sbostic if (sltime > tp->timetowait / 5) 198*35528Sbostic warn(); 199*35528Sbostic (void)sleep(sltime); 200*35528Sbostic } 2018841Smckusick } 202*35528Sbostic for (;; ++tp) { 203*35528Sbostic warn(); 204*35528Sbostic if (!logged && tp->timeleft <= NOLOG_TIME) { 205*35528Sbostic logged = 1; 206*35528Sbostic nolog(); 2072373Swnj } 208*35528Sbostic (void)sleep((u_int)tp->timetowait); 209*35528Sbostic if (!tp->timeleft) 210*35528Sbostic break; 211*35528Sbostic } 212*35528Sbostic die_you_gravy_sucking_pig_dog(); 213*35528Sbostic } 21425366Sbloom 215*35528Sbostic static jmp_buf alarmbuf; 216*35528Sbostic 217*35528Sbostic warn() 218*35528Sbostic { 219*35528Sbostic static int first; 220*35528Sbostic static char hostname[MAXHOSTNAMELEN]; 221*35528Sbostic FILE *pf; 222*35528Sbostic char *ctime(); 223*35528Sbostic int timeout(); 224*35528Sbostic 225*35528Sbostic if (!first++) { 226*35528Sbostic (void)signal(SIGALRM, timeout); 227*35528Sbostic (void)gethostname(hostname, sizeof(hostname)); 2282373Swnj } 229*35528Sbostic 230*35528Sbostic if (!(pf = popen(WALL_CMD, "w"))) { 231*35528Sbostic syslog(LOG_ERR, "shutdown: can't find %s: %m", WALL_CMD); 232*35528Sbostic return; 233*35528Sbostic } 234*35528Sbostic 235*35528Sbostic fprintf(pf, "*** %sSystem shutdown message ***\n", 236*35528Sbostic tp->timeleft ? "": "FINAL "); 237*35528Sbostic 238*35528Sbostic if (tp->timeleft > 10*60) 239*35528Sbostic fprintf(pf, "System going down at %5.5s\n\n", 240*35528Sbostic ctime(&shuttime) + 11); 241*35528Sbostic else if (tp->timeleft > 59) 242*35528Sbostic fprintf(pf, "System going down in %d minute%s\n\n", 243*35528Sbostic tp->timeleft / 60, (tp->timeleft > 60) ? "s" : ""); 244*35528Sbostic else if (tp->timeleft) 245*35528Sbostic fprintf(pf, "System going down in 30 seconds\n\n"); 246*35528Sbostic else 247*35528Sbostic fprintf(pf, "System going down IMMEDIATELY\n\n"); 248*35528Sbostic 249*35528Sbostic (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 250*35528Sbostic 251*35528Sbostic /* 252*35528Sbostic * play some games, just in case wall doesn't come back 253*35528Sbostic * probably unecessary, given that wall is careful. 254*35528Sbostic */ 255*35528Sbostic if (!setjmp(alarmbuf)) { 256*35528Sbostic (void)alarm((u_int)30); 257*35528Sbostic (void)pclose(pf); 258*35528Sbostic (void)alarm((u_int)0); 259*35528Sbostic } 2602373Swnj } 2612373Swnj 262*35528Sbostic timeout() 2632373Swnj { 264*35528Sbostic longjmp(alarmbuf, 1); 265*35528Sbostic } 2662373Swnj 267*35528Sbostic die_you_gravy_sucking_pig_dog() 268*35528Sbostic { 269*35528Sbostic syslog(LOG_NOTICE, "%s by %s: %s", 270*35528Sbostic doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 271*35528Sbostic (void)sleep(2); 272*35528Sbostic 273*35528Sbostic printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 274*35528Sbostic if (killflg) { 275*35528Sbostic printf("\rbut you'll have to do it yourself\r\n"); 2762373Swnj finish(); 2772373Swnj } 278*35528Sbostic if (dofast) 279*35528Sbostic doitfast(); 280*35528Sbostic #ifdef DEBUG 281*35528Sbostic if (doreboot) 282*35528Sbostic printf("REBOOT"); 283*35528Sbostic if (dohalt) 284*35528Sbostic printf(" HALT"); 285*35528Sbostic if (dofast) 286*35528Sbostic printf(" -l %s (without fsck's)\n", nosync); 287*35528Sbostic else 288*35528Sbostic printf(" -l %s\n", nosync); 289*35528Sbostic printf("kill -HUP 1\n"); 290*35528Sbostic #else 291*35528Sbostic if (doreboot) { 292*35528Sbostic execle(REBOOT, "reboot", "-l", nosync, 0, 0); 293*35528Sbostic syslog(LOG_ERR, "shutdown: can't exec %s: %m.", REBOOT); 294*35528Sbostic perror("shutdown"); 295*35528Sbostic } 296*35528Sbostic else if (dohalt) { 297*35528Sbostic execle(HALT, "halt", "-l", nosync, 0, 0); 298*35528Sbostic syslog(LOG_ERR, "shutdown: can't exec %s: %m.", HALT); 299*35528Sbostic perror("shutdown"); 300*35528Sbostic } 301*35528Sbostic (void)kill(1, SIGTERM); /* to single user */ 302*35528Sbostic #endif 3032373Swnj finish(); 3042373Swnj } 3052373Swnj 306*35528Sbostic #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 307*35528Sbostic static int dmsize[] = 308*35528Sbostic { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 309*35528Sbostic 310*35528Sbostic getoffset(timearg) 311*35528Sbostic register char *timearg; 3122373Swnj { 313*35528Sbostic register struct tm *lt; 314*35528Sbostic register char *p; 315*35528Sbostic time_t now, time(); 316*35528Sbostic int year, month, day, hour, min; 3172373Swnj 318*35528Sbostic if (!strcasecmp(timearg, "now")) { /* now */ 319*35528Sbostic offset = 0; 320*35528Sbostic return; 321*35528Sbostic } 3223880Swnj 323*35528Sbostic (void)time(&now); 324*35528Sbostic if (*timearg == '+') { /* +minutes */ 325*35528Sbostic if (!isdigit(*++timearg)) 326*35528Sbostic goto badtime; 327*35528Sbostic min = atoi(timearg); 328*35528Sbostic offset = min * 60; 329*35528Sbostic shuttime = now + offset; 330*35528Sbostic return; 331*35528Sbostic } 3328841Smckusick 333*35528Sbostic /* handle hh:mm by getting rid of the colon */ 334*35528Sbostic for (p = timearg; *p; ++p) 335*35528Sbostic if (!isascii(*p) || !isdigit(*p)) 336*35528Sbostic if (*p == ':' && strlen(p) == 3) { 337*35528Sbostic p[0] = p[1]; 338*35528Sbostic p[1] = p[2]; 339*35528Sbostic p[2] = '\0'; 340*35528Sbostic } 341*35528Sbostic else 342*35528Sbostic goto badtime; 343*35528Sbostic 344*35528Sbostic unsetenv("TZ"); /* OUR timezone */ 345*35528Sbostic lt = localtime(&now); /* [yymmdd]hhmm */ 346*35528Sbostic year = lt->tm_year; 347*35528Sbostic month = lt->tm_mon + 1; 348*35528Sbostic day = lt->tm_mday; 349*35528Sbostic 350*35528Sbostic switch(strlen(timearg)) { 351*35528Sbostic case 10: 352*35528Sbostic year = ATOI2(timearg); 353*35528Sbostic /* FALLTHROUGH */ 354*35528Sbostic case 8: 355*35528Sbostic month = ATOI2(timearg); 356*35528Sbostic /* FALLTHROUGH */ 357*35528Sbostic case 6: 358*35528Sbostic day = ATOI2(timearg); 359*35528Sbostic /* FALLTHROUGH */ 360*35528Sbostic case 4: 361*35528Sbostic hour = ATOI2(timearg); 362*35528Sbostic min = ATOI2(timearg); 363*35528Sbostic if (month < 1 || month > 12 || day < 1 || day > 31 || 364*35528Sbostic hour < 0 || hour > 23 || min < 0 || min > 59) 365*35528Sbostic goto badtime; 366*35528Sbostic shuttime = 0; 367*35528Sbostic year += TM_YEAR_BASE; 368*35528Sbostic if (isleap(year) && month > 2) 369*35528Sbostic ++shuttime; 370*35528Sbostic for (--year; year >= EPOCH_YEAR; --year) 371*35528Sbostic shuttime += isleap(year) ? 372*35528Sbostic DAYS_PER_LYEAR : DAYS_PER_NYEAR; 373*35528Sbostic while (--month) 374*35528Sbostic shuttime += dmsize[month]; 375*35528Sbostic shuttime += day - 1; 376*35528Sbostic shuttime = HOURS_PER_DAY * shuttime + hour; 377*35528Sbostic shuttime = MINS_PER_HOUR * shuttime + min; 378*35528Sbostic shuttime *= SECS_PER_MIN; 379*35528Sbostic shuttime -= lt->tm_gmtoff; 380*35528Sbostic if ((offset = shuttime - now) >= 0) 381*35528Sbostic break; 382*35528Sbostic /* FALLTHROUGH */ 383*35528Sbostic default: 384*35528Sbostic badtime: fprintf(stderr, 385*35528Sbostic "shutdown: bad time format, or already past.\n"); 386*35528Sbostic exit(1); 387*35528Sbostic } 3882373Swnj } 3892373Swnj 390*35528Sbostic #define FSMSG "fastboot file for fsck\n" 39125366Sbloom doitfast() 39225366Sbloom { 393*35528Sbostic int fastfd; 39425366Sbloom 395*35528Sbostic if ((fastfd = open(FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 0664)) >= 0) { 396*35528Sbostic (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); 397*35528Sbostic (void)close(fastfd); 39825366Sbloom } 39925366Sbloom } 40025366Sbloom 401*35528Sbostic #define NOMSG "\n\nNO LOGINS: System going down at " 402*35528Sbostic nolog() 4032373Swnj { 404*35528Sbostic int logfd, finish(); 405*35528Sbostic char *ct, *ctime(); 4062373Swnj 407*35528Sbostic (void)unlink(NOLOGIN); /* in case linked to another file */ 408*35528Sbostic (void)signal(SIGINT, finish); 409*35528Sbostic (void)signal(SIGHUP, finish); 410*35528Sbostic (void)signal(SIGQUIT, finish); 411*35528Sbostic (void)signal(SIGTERM, finish); 412*35528Sbostic if ((logfd = open(NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 0664)) >= 0) { 413*35528Sbostic (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 414*35528Sbostic ct = ctime(&shuttime); 415*35528Sbostic (void)write(logfd, ct + 11, 5); 416*35528Sbostic (void)write(logfd, "\n\n", 2); 417*35528Sbostic (void)write(logfd, mbuf, strlen(mbuf)); 418*35528Sbostic (void)close(logfd); 4192373Swnj } 4202373Swnj } 4212373Swnj 4222373Swnj finish() 4232373Swnj { 424*35528Sbostic (void)unlink(NOLOGIN); 4252373Swnj exit(0); 4262373Swnj } 4272373Swnj 428*35528Sbostic usage() 4292373Swnj { 430*35528Sbostic fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); 431*35528Sbostic exit(1); 4322373Swnj } 433