shutdown.c revision 48956
11844Swollman/* 242450Sjdp * Copyright (c) 1988, 1990, 1993 31844Swollman * The Regents of the University of California. All rights reserved. 41638Srgrimes * 538183Speter * Redistribution and use in source and binary forms, with or without 638183Speter * modification, are permitted provided that the following conditions 71638Srgrimes * are met: 81638Srgrimes * 1. Redistributions of source code must retain the above copyright 91638Srgrimes * notice, this list of conditions and the following disclaimer. 1038183Speter * 2. Redistributions in binary form must reproduce the above copyright 111638Srgrimes * notice, this list of conditions and the following disclaimer in the 121844Swollman * documentation and/or other materials provided with the distribution. 131844Swollman * 3. All advertising materials mentioning features or use of this software 1438655Sjb * must display the following acknowledgement: 151844Swollman * This product includes software developed by the University of 161844Swollman * California, Berkeley and its contributors. 1728945Speter * 4. Neither the name of the University nor the names of its contributors 181844Swollman * may be used to endorse or promote products derived from this software 1938655Sjb * without specific prior written permission. 2029141Speter * 2129141Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2229141Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231844Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241844Swollman * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2526051Sasami * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261844Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 272353Sbde * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 282827Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 292827Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 302827Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 312827Sjkh * SUCH DAMAGE. 322827Sjkh */ 331638Srgrimes 342827Sjkh#ifndef lint 351638Srgrimesstatic const char copyright[] = 3638655Sjb"@(#) Copyright (c) 1988, 1990, 1993\n\ 3718529Sbde The Regents of the University of California. All rights reserved.\n"; 3831809Sbde#endif /* not lint */ 3918529Sbde 401638Srgrimes#ifndef lint 411638Srgrimes#if 0 421638Srgrimesstatic char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; 4342450Sjdp#endif 441638Srgrimesstatic const char rcsid[] = 4542450Sjdp "$Id: shutdown.c,v 1.19 1999/06/21 16:06:21 ru Exp $"; 461638Srgrimes#endif /* not lint */ 471638Srgrimes 481844Swollman#include <sys/param.h> 4938186Speter#include <sys/time.h> 5038186Speter#include <sys/resource.h> 511638Srgrimes#include <sys/syslog.h> 521638Srgrimes 5324761Sjdp#include <ctype.h> 5438186Speter#include <err.h> 5538186Speter#include <fcntl.h> 561638Srgrimes#include <pwd.h> 5742450Sjdp#include <setjmp.h> 581844Swollman#include <signal.h> 5938186Speter#include <stdio.h> 6038186Speter#include <stdlib.h> 611844Swollman#include <string.h> 6236673Sdt#include <unistd.h> 631844Swollman 6438186Speter#include "pathnames.h" 6538186Speter 661844Swollman#ifdef DEBUG 6736673Sdt#undef _PATH_NOLOGIN 6824761Sjdp#define _PATH_NOLOGIN "./nologin" 6938186Speter#endif 7038186Speter 711844Swollman#define H *60*60 7242450Sjdp#define M *60 731844Swollman#define S *1 7438186Speter#define NOLOG_TIME 5*60 7538186Speterstruct interval { 761844Swollman int timeleft, timetowait; 771844Swollman} tlist[] = { 781844Swollman { 10 H, 5 H }, 7938186Speter { 5 H, 3 H }, 8038186Speter { 2 H, 1 H }, 811844Swollman { 1 H, 30 M }, 821844Swollman { 30 M, 10 M }, 8324761Sjdp { 20 M, 10 M }, 8438186Speter { 10 M, 5 M }, 8538186Speter { 5 M, 3 M }, 861844Swollman { 2 M, 1 M }, 8742450Sjdp { 1 M, 30 S }, 881844Swollman { 30 S, 30 S }, 8938186Speter { 0 , 0 } 9038186Speter}; 911844Swollman#undef H 9236054Sbde#undef M 9336054Sbde#undef S 9438186Speter 9538186Speterstatic time_t offset, shuttime; 9636054Sbdestatic int dohalt, dopower, doreboot, killflg, mbuflen, oflag; 9736054Sbdestatic char *nosync, *whom, mbuf[BUFSIZ]; 9836054Sbde 9938186Spetervoid badtime __P((void)); 10038186Spetervoid die_you_gravy_sucking_pig_dog __P((void)); 10136054Sbdevoid finish __P((int)); 10242450Sjdpvoid getoffset __P((char *)); 10336054Sbdevoid loop __P((void)); 10438186Spetervoid nolog __P((void)); 10538186Spetervoid timeout __P((int)); 10636054Sbdevoid timewarn __P((int)); 1071638Srgrimesvoid usage __P((const char *)); 10826715Sasami 10917510Speterint 11038186Spetermain(argc, argv) 11138186Speter int argc; 1121638Srgrimes char *argv[]; 1131638Srgrimes{ 11426715Sasami register char *p, *endp; 11517510Speter struct passwd *pw; 11638186Speter int arglen, ch, len, readstdin; 11738186Speter 1181638Srgrimes#ifndef DEBUG 11942450Sjdp if (geteuid()) 12026715Sasami errx(1, "NOT super-user"); 12117510Speter#endif 12238186Speter nosync = NULL; 12338186Speter readstdin = 0; 1241638Srgrimes while ((ch = getopt(argc, argv, "-hknopr")) != -1) 1251844Swollman switch (ch) { 12626715Sasami case '-': 12738186Speter readstdin = 1; 12838186Speter break; 1291844Swollman case 'h': 1301844Swollman dohalt = 1; 13126715Sasami break; 13238186Speter case 'k': 13338186Speter killflg = 1; 1341844Swollman break; 13542450Sjdp case 'n': 13626715Sasami nosync = "-n"; 13738186Speter break; 13838186Speter case 'o': 1391844Swollman oflag = 1; 1402870Swollman break; 1412868Swollman case 'p': 1421638Srgrimes dopower = 1; 1431638Srgrimes break; 1441638Srgrimes case 'r': 1451638Srgrimes doreboot = 1; 1462870Swollman break; 1471638Srgrimes case '?': 1481844Swollman default: 14938655Sjb usage((char *)NULL); 1501844Swollman } 1511844Swollman argc -= optind; 1521638Srgrimes argv += optind; 15328945Speter 15428945Speter if (argc < 1) 15528945Speter usage((char *)NULL); 15628945Speter 15728945Speter if (killflg + doreboot + dohalt + dopower > 1) 1581844Swollman usage("incompatible switches -h, -k, -p and -r"); 1591844Swollman 1601844Swollman if (oflag && !(dohalt || dopower || doreboot)) 1611844Swollman usage("-o requires -h, -p or -r"); 1621638Srgrimes 1631844Swollman if (nosync != NULL && !oflag) 1641844Swollman usage("-n requires -o"); 1651844Swollman 1661638Srgrimes getoffset(*argv++); 16718340Sswallace 1681844Swollman if (*argv) { 1691844Swollman for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 1701844Swollman arglen = strlen(*argv); 1711638Srgrimes if ((len -= arglen) <= 2) 1722353Sbde break; 1731638Srgrimes if (p != mbuf) 17417400Sjkh *p++ = ' '; 1751844Swollman memmove(p, *argv, arglen); 1761638Srgrimes p += arglen; 17738187Speter } 1783859Sbde *p = '\n'; 1791638Srgrimes *++p = '\0'; 1802353Sbde } 1811638Srgrimes 18217400Sjkh if (readstdin) { 1831844Swollman p = mbuf; 1843859Sbde endp = mbuf + sizeof(mbuf) - 2; 1851638Srgrimes for (;;) { 1861844Swollman if (!fgets(p, endp - p + 1, stdin)) 18736640Speter break; 1881844Swollman for (; *p && p < endp; ++p); 1891844Swollman if (p == endp) { 19042450Sjdp *p = '\n'; 19128945Speter *++p = '\0'; 19238187Speter break; 19338655Sjb } 1941844Swollman } 1952353Sbde } 1961844Swollman mbuflen = strlen(mbuf); 19726073Sdfr 1981844Swollman if (offset) 19917400Sjkh (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 20028945Speter else 20128945Speter (void)printf("Shutdown NOW!\n"); 20228945Speter 20341218Sjdp if (!(whom = getlogin())) 20441218Sjdp whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 20528945Speter 20629141Speter#ifdef DEBUG 20728945Speter (void)putc('\n', stdout); 20828945Speter#else 2091844Swollman (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 2101844Swollman { 2112353Sbde int forkpid; 2121844Swollman 2135253Sache forkpid = fork(); 2141844Swollman if (forkpid == -1) 2153859Sbde err(1, "fork"); 2161844Swollman if (forkpid) 2171638Srgrimes errx(0, "[pid %d]", forkpid); 2181638Srgrimes } 2191638Srgrimes setsid(); 2201638Srgrimes#endif 22116663Sjkh openlog("shutdown", LOG_CONS, LOG_AUTH); 22238187Speter loop(); 22331809Sbde return(0); 22438187Speter} 22542450Sjdp 22642450Sjdpvoid 22716826Sphkloop() 22816437Sphk{ 2291638Srgrimes struct interval *tp; 23016437Sphk u_int sltime; 2311638Srgrimes int logged; 23234179Sbde 23324750Sbde if (offset <= NOLOG_TIME) { 23442450Sjdp logged = 1; 23524750Sbde nolog(); 23624750Sbde } 23734179Sbde else 23838655Sjb logged = 0; 23924750Sbde tp = tlist; 24025468Sjdp if (tp->timeleft < offset) 24125468Sjdp (void)sleep((u_int)(offset - tp->timeleft)); 24228945Speter else { 24331809Sbde while (tp->timeleft && offset < tp->timeleft) 24428945Speter ++tp; 24528945Speter /* 24627910Sasami * Warn now, if going to sleep more than a fifth of 24728945Speter * the next wait time. 24828945Speter */ 2491638Srgrimes if ((sltime = offset - tp->timeleft)) { 2501638Srgrimes if (sltime > tp->timetowait / 5) 2511638Srgrimes timewarn(offset); 2521638Srgrimes (void)sleep(sltime); 2531638Srgrimes } 2541638Srgrimes } 2552298Swollman for (;; ++tp) { 2562298Swollman timewarn(tp->timeleft); 2572298Swollman if (!logged && tp->timeleft <= NOLOG_TIME) { 2582298Swollman logged = 1; 2591638Srgrimes nolog(); 2602298Swollman } 2611996Swollman (void)sleep((u_int)tp->timetowait); 2621996Swollman if (!tp->timeleft) 2631638Srgrimes break; 2641844Swollman } 2651996Swollman die_you_gravy_sucking_pig_dog(); 2661638Srgrimes} 2672298Swollman 2681844Swollmanstatic jmp_buf alarmbuf; 26938655Sjb 2701844Swollmanstatic char *restricted_environ[] = { 2711844Swollman "PATH=" _PATH_STDPATH, 2722298Swollman NULL 2732298Swollman}; 2744450Sbde 2751844Swollmanvoid 27628945Spetertimewarn(timeleft) 27728945Speter int timeleft; 27828945Speter{ 27928945Speter static int first; 28028945Speter static char hostname[MAXHOSTNAMELEN + 1]; 28128945Speter FILE *pf; 28228945Speter char wcmd[MAXPATHLEN + 4]; 28328945Speter extern char **environ; 28428945Speter 28528945Speter if (!first++) 2861844Swollman (void)gethostname(hostname, sizeof(hostname)); 2871844Swollman 2881996Swollman /* undoc -n option to wall suppresses normal wall banner */ 2891844Swollman (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 2901844Swollman environ = restricted_environ; 2911638Srgrimes if (!(pf = popen(wcmd, "w"))) { 2921638Srgrimes syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 2931638Srgrimes return; 2941638Srgrimes } 2951638Srgrimes 2961638Srgrimes (void)fprintf(pf, 2971638Srgrimes "\007*** %sSystem shutdown message from %s@%s ***\007\n", 2982353Sbde timeleft ? "": "FINAL ", whom, hostname); 2991638Srgrimes 3006032Sjkh if (timeleft > 10*60) 3011638Srgrimes (void)fprintf(pf, "System going down at %5.5s\n\n", 3021638Srgrimes ctime(&shuttime) + 11); 3031638Srgrimes else if (timeleft > 59) 30416663Sjkh (void)fprintf(pf, "System going down in %d minute%s\n\n", 3051844Swollman timeleft / 60, (timeleft > 60) ? "s" : ""); 3061844Swollman else if (timeleft) 3071844Swollman (void)fprintf(pf, "System going down in 30 seconds\n\n"); 3081638Srgrimes else 3091638Srgrimes (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 3101638Srgrimes 3111638Srgrimes if (mbuflen) 31234528Seivind (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 31334528Seivind 31434528Seivind /* 31534528Seivind * play some games, just in case wall doesn't come back 3165585Sjkh * probably unnecessary, given that wall is careful. 3174442Sphk */ 31816663Sjkh if (!setjmp(alarmbuf)) { 31926760Sjkh (void)signal(SIGALRM, timeout); 32026760Sjkh (void)alarm((u_int)30); 32126760Sjkh (void)pclose(pf); 3224442Sphk (void)alarm((u_int)0); 3234442Sphk (void)signal(SIGALRM, SIG_DFL); 3241638Srgrimes } 3251638Srgrimes} 3261638Srgrimes 3271638Srgrimesvoid 3281844Swollmantimeout(signo) 3291638Srgrimes int signo; 3301844Swollman{ 3311844Swollman longjmp(alarmbuf, 1); 33211136Swollman} 3331844Swollman 3341844Swollmanvoid 3351844Swollmandie_you_gravy_sucking_pig_dog() 33634092Sbde{ 33734092Sbde char *empty_environ[] = { NULL }; 33834092Sbde 33934092Sbde syslog(LOG_NOTICE, "%s by %s: %s", 34034092Sbde doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : 34116663Sjkh "shutdown", whom, mbuf); 342 (void)sleep(2); 343 344 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 345 if (killflg) { 346 (void)printf("\rbut you'll have to do it yourself\r\n"); 347 exit(0); 348 } 349#ifdef DEBUG 350 if (doreboot) 351 (void)printf("reboot"); 352 else if (dohalt) 353 (void)printf("halt"); 354 else if (dopower) 355 (void)printf("power-down"); 356 if (nosync != NULL) 357 (void)printf(" no sync"); 358 (void)printf("\nkill -HUP 1\n"); 359#else 360 if (!oflag) { 361 (void)kill(1, doreboot ? SIGINT : /* reboot */ 362 dohalt ? SIGUSR1 : /* halt */ 363 dopower ? SIGUSR2 : /* power-down */ 364 SIGTERM); /* single-user */ 365 } else { 366 if (doreboot) { 367 execle(_PATH_REBOOT, "reboot", "-l", nosync, 368 (char *)NULL, empty_environ); 369 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 370 _PATH_REBOOT); 371 warn(_PATH_REBOOT); 372 } 373 else if (dohalt) { 374 execle(_PATH_HALT, "halt", "-l", nosync, 375 (char *)NULL, empty_environ); 376 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 377 _PATH_HALT); 378 warn(_PATH_HALT); 379 } 380 else if (dopower) { 381 execle(_PATH_HALT, "halt", "-l", "-p", nosync, 382 (char *)NULL, empty_environ); 383 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 384 _PATH_HALT); 385 warn(_PATH_HALT); 386 } 387 (void)kill(1, SIGTERM); /* to single-user */ 388 } 389#endif 390 finish(0); 391} 392 393#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 394 395void 396getoffset(timearg) 397 register char *timearg; 398{ 399 register struct tm *lt; 400 register char *p; 401 time_t now; 402 int this_year; 403 404 (void)time(&now); 405 406 if (!strcasecmp(timearg, "now")) { /* now */ 407 offset = 0; 408 shuttime = now; 409 return; 410 } 411 412 if (*timearg == '+') { /* +minutes */ 413 if (!isdigit(*++timearg)) 414 badtime(); 415 if ((offset = atoi(timearg) * 60) < 0) 416 badtime(); 417 shuttime = now + offset; 418 return; 419 } 420 421 /* handle hh:mm by getting rid of the colon */ 422 for (p = timearg; *p; ++p) 423 if (!isascii(*p) || !isdigit(*p)) { 424 if (*p == ':' && strlen(p) == 3) { 425 p[0] = p[1]; 426 p[1] = p[2]; 427 p[2] = '\0'; 428 } 429 else 430 badtime(); 431 } 432 433 unsetenv("TZ"); /* OUR timezone */ 434 lt = localtime(&now); /* current time val */ 435 436 switch(strlen(timearg)) { 437 case 10: 438 this_year = lt->tm_year; 439 lt->tm_year = ATOI2(timearg); 440 /* 441 * check if the specified year is in the next century. 442 * allow for one year of user error as many people will 443 * enter n - 1 at the start of year n. 444 */ 445 if (lt->tm_year < (this_year % 100) - 1) 446 lt->tm_year += 100; 447 /* adjust for the year 2000 and beyond */ 448 lt->tm_year += (this_year - (this_year % 100)); 449 /* FALLTHROUGH */ 450 case 8: 451 lt->tm_mon = ATOI2(timearg); 452 if (--lt->tm_mon < 0 || lt->tm_mon > 11) 453 badtime(); 454 /* FALLTHROUGH */ 455 case 6: 456 lt->tm_mday = ATOI2(timearg); 457 if (lt->tm_mday < 1 || lt->tm_mday > 31) 458 badtime(); 459 /* FALLTHROUGH */ 460 case 4: 461 lt->tm_hour = ATOI2(timearg); 462 if (lt->tm_hour < 0 || lt->tm_hour > 23) 463 badtime(); 464 lt->tm_min = ATOI2(timearg); 465 if (lt->tm_min < 0 || lt->tm_min > 59) 466 badtime(); 467 lt->tm_sec = 0; 468 if ((shuttime = mktime(lt)) == -1) 469 badtime(); 470 if ((offset = shuttime - now) < 0) 471 errx(1, "that time is already past."); 472 break; 473 default: 474 badtime(); 475 } 476} 477 478#define NOMSG "\n\nNO LOGINS: System going down at " 479void 480nolog() 481{ 482 int logfd; 483 char *ct; 484 485 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 486 (void)signal(SIGINT, finish); 487 (void)signal(SIGHUP, finish); 488 (void)signal(SIGQUIT, finish); 489 (void)signal(SIGTERM, finish); 490 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 491 0664)) >= 0) { 492 (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 493 ct = ctime(&shuttime); 494 (void)write(logfd, ct + 11, 5); 495 (void)write(logfd, "\n\n", 2); 496 (void)write(logfd, mbuf, strlen(mbuf)); 497 (void)close(logfd); 498 } 499} 500 501void 502finish(signo) 503 int signo; 504{ 505 if (!killflg) 506 (void)unlink(_PATH_NOLOGIN); 507 exit(0); 508} 509 510void 511badtime() 512{ 513 errx(1, "bad time format"); 514} 515 516void 517usage(cp) 518 const char *cp; 519{ 520 if (cp != NULL) 521 warnx("%s", cp); 522 (void)fprintf(stderr, 523 "usage: shutdown [-] [-h | -p | -r | -k] [-o [-n]]" 524 " time [warning-message ...]\n"); 525 exit(1); 526} 527