shutdown.c revision 1559
156043Syokota/* 256043Syokota * Copyright (c) 1988, 1990, 1993 356043Syokota * The Regents of the University of California. All rights reserved. 456043Syokota * 556043Syokota * Redistribution and use in source and binary forms, with or without 656043Syokota * modification, are permitted provided that the following conditions 756043Syokota * are met: 856043Syokota * 1. Redistributions of source code must retain the above copyright 956043Syokota * notice, this list of conditions and the following disclaimer. 1056043Syokota * 2. Redistributions in binary form must reproduce the above copyright 1156043Syokota * notice, this list of conditions and the following disclaimer in the 1256043Syokota * documentation and/or other materials provided with the distribution. 1356043Syokota * 3. All advertising materials mentioning features or use of this software 1456043Syokota * must display the following acknowledgement: 1556043Syokota * This product includes software developed by the University of 1656043Syokota * California, Berkeley and its contributors. 1756043Syokota * 4. Neither the name of the University nor the names of its contributors 1856043Syokota * may be used to endorse or promote products derived from this software 1956043Syokota * without specific prior written permission. 2056043Syokota * 2156043Syokota * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2256043Syokota * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2356043Syokota * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2456043Syokota * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2556043Syokota * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2656043Syokota * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2756043Syokota * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2856043Syokota * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2956043Syokota * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3056043Syokota * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3156043Syokota * SUCH DAMAGE. 3256043Syokota */ 3356043Syokota 3456043Syokota#ifndef lint 3556043Syokotastatic char copyright[] = 3656043Syokota"@(#) Copyright (c) 1988, 1990, 1993\n\ 3756043Syokota The Regents of the University of California. All rights reserved.\n"; 3856043Syokota#endif /* not lint */ 3956043Syokota 4056043Syokota#ifndef lint 4156043Syokotastatic char sccsid[] = "@(#)shutdown.c 8.2 (Berkeley) 2/16/94"; 4256043Syokota#endif /* not lint */ 4356043Syokota 4456043Syokota#include <sys/param.h> 4556043Syokota#include <sys/time.h> 4656043Syokota#include <sys/resource.h> 4756043Syokota#include <sys/syslog.h> 4856043Syokota 4956043Syokota#include <ctype.h> 5056043Syokota#include <fcntl.h> 5156043Syokota#include <pwd.h> 5256043Syokota#include <setjmp.h> 5356043Syokota#include <signal.h> 5456043Syokota#include <stdio.h> 5556043Syokota#include <stdlib.h> 5656043Syokota#include <string.h> 5756043Syokota#include <tzfile.h> 5856043Syokota#include <unistd.h> 5956043Syokota 6056043Syokota#include "pathnames.h" 6156043Syokota 6256043Syokota#ifdef DEBUG 6356043Syokota#undef _PATH_NOLOGIN 6456043Syokota#define _PATH_NOLOGIN "./nologin" 6556043Syokota#undef _PATH_FASTBOOT 6656043Syokota#define _PATH_FASTBOOT "./fastboot" 6756043Syokota#endif 6856043Syokota 6956043Syokota#define H *60*60 7056043Syokota#define M *60 7156043Syokota#define S *1 7256043Syokota#define NOLOG_TIME 5*60 7356043Syokotastruct interval { 7456043Syokota int timeleft, timetowait; 7556043Syokota} tlist[] = { 7656043Syokota 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, 7756043Syokota 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, 7856043Syokota 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, 7956043Syokota 0, 0, 8056043Syokota}; 8156043Syokota#undef H 8256043Syokota#undef M 8356043Syokota#undef S 8456043Syokota 8556043Syokotastatic time_t offset, shuttime; 8656043Syokotastatic int dofast, dohalt, doreboot, killflg, mbuflen; 8756043Syokotastatic char *nosync, *whom, mbuf[BUFSIZ]; 8856043Syokota 8956043Syokotavoid badtime __P((void)); 9056043Syokotavoid die_you_gravy_sucking_pig_dog __P((void)); 9156043Syokotavoid doitfast __P((void)); 9256043Syokotavoid finish __P((int)); 9356043Syokotavoid getoffset __P((char *)); 9456043Syokotavoid loop __P((void)); 9556043Syokotavoid nolog __P((void)); 9656043Syokotavoid timeout __P((int)); 9756043Syokotavoid timewarn __P((int)); 9856043Syokotavoid usage __P((void)); 9956043Syokota 10056043Syokotaint 10156043Syokotamain(argc, argv) 10256043Syokota int argc; 10356043Syokota char *argv[]; 10456043Syokota{ 10556043Syokota extern int optind; 10656043Syokota register char *p, *endp; 10756043Syokota struct passwd *pw; 10856043Syokota int arglen, ch, len, readstdin; 10956043Syokota 11056043Syokota#ifndef DEBUG 11156043Syokota if (geteuid()) { 11256043Syokota (void)fprintf(stderr, "shutdown: NOT super-user\n"); 11356043Syokota exit(1); 11456043Syokota } 11556043Syokota#endif 11656043Syokota nosync = NULL; 11756043Syokota readstdin = 0; 11856043Syokota while ((ch = getopt(argc, argv, "-fhknr")) != EOF) 11956043Syokota switch (ch) { 12056043Syokota case '-': 12156043Syokota readstdin = 1; 12256043Syokota break; 12356043Syokota case 'f': 12456043Syokota dofast = 1; 12556043Syokota break; 12656043Syokota case 'h': 12756043Syokota dohalt = 1; 12856043Syokota break; 12956043Syokota case 'k': 13056043Syokota killflg = 1; 13156043Syokota break; 13256043Syokota case 'n': 13356043Syokota nosync = "-n"; 13456043Syokota break; 13556043Syokota case 'r': 13656043Syokota doreboot = 1; 13756043Syokota break; 13856043Syokota case '?': 13956043Syokota default: 14056043Syokota usage(); 14156043Syokota } 14256043Syokota argc -= optind; 14356043Syokota argv += optind; 14456043Syokota 14556043Syokota if (argc < 1) 14656043Syokota usage(); 14756043Syokota 14856043Syokota if (dofast && nosync) { 14956043Syokota (void)fprintf(stderr, 15056043Syokota "shutdown: incompatible switches -f and -n.\n"); 15156043Syokota usage(); 15256043Syokota } 15356043Syokota if (doreboot && dohalt) { 15456043Syokota (void)fprintf(stderr, 15556043Syokota "shutdown: incompatible switches -h and -r.\n"); 15656043Syokota usage(); 15756043Syokota } 15856043Syokota getoffset(*argv++); 15956043Syokota 16056043Syokota if (*argv) { 16156043Syokota for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 16256043Syokota arglen = strlen(*argv); 16356043Syokota if ((len -= arglen) <= 2) 16456043Syokota break; 16556043Syokota if (p != mbuf) 16656043Syokota *p++ = ' '; 16756043Syokota bcopy(*argv, p, arglen); 16856043Syokota p += arglen; 16956043Syokota } 17056043Syokota *p = '\n'; 17156043Syokota *++p = '\0'; 17256043Syokota } 17356043Syokota 17456043Syokota if (readstdin) { 17556043Syokota p = mbuf; 17656043Syokota endp = mbuf + sizeof(mbuf) - 2; 17756043Syokota for (;;) { 17856043Syokota if (!fgets(p, endp - p + 1, stdin)) 17956043Syokota break; 18056043Syokota for (; *p && p < endp; ++p); 18156043Syokota if (p == endp) { 18256043Syokota *p = '\n'; 18356043Syokota *++p = '\0'; 18456043Syokota break; 18556043Syokota } 18656043Syokota } 18756043Syokota } 18856043Syokota mbuflen = strlen(mbuf); 18956043Syokota 19056043Syokota if (offset) 19156043Syokota (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 19256043Syokota else 19356043Syokota (void)printf("Shutdown NOW!\n"); 19456043Syokota 19556043Syokota if (!(whom = getlogin())) 19656043Syokota whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 19756043Syokota 19856043Syokota#ifdef DEBUG 19956043Syokota (void)putc('\n', stdout); 20056043Syokota#else 20156043Syokota (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 20256043Syokota { 20356043Syokota int forkpid; 20456043Syokota 20556043Syokota forkpid = fork(); 20656043Syokota if (forkpid == -1) { 20756043Syokota perror("shutdown: fork"); 20856043Syokota exit(1); 20956043Syokota } 21056043Syokota if (forkpid) { 21156043Syokota (void)printf("shutdown: [pid %d]\n", forkpid); 21256043Syokota exit(0); 21356043Syokota } 21456043Syokota } 21556043Syokota#endif 21656043Syokota openlog("shutdown", LOG_CONS, LOG_AUTH); 21756043Syokota loop(); 21856043Syokota /* NOTREACHED */ 21956043Syokota} 22056043Syokota 22156043Syokotavoid 22256043Syokotaloop() 22356043Syokota{ 22480044Syokota struct interval *tp; 22556043Syokota u_int sltime; 22656043Syokota int logged; 22756043Syokota 22856043Syokota if (offset <= NOLOG_TIME) { 22956043Syokota logged = 1; 23056043Syokota nolog(); 23156043Syokota } 23256043Syokota else 23356043Syokota logged = 0; 23456043Syokota tp = tlist; 23556043Syokota if (tp->timeleft < offset) 23656043Syokota (void)sleep((u_int)(offset - tp->timeleft)); 23756043Syokota else { 23856043Syokota while (offset < tp->timeleft) 23956043Syokota ++tp; 24056043Syokota /* 24156043Syokota * Warn now, if going to sleep more than a fifth of 24256043Syokota * the next wait time. 24356043Syokota */ 24456043Syokota if (sltime = offset - tp->timeleft) { 24556043Syokota if (sltime > tp->timetowait / 5) 24656043Syokota timewarn(offset); 24756043Syokota (void)sleep(sltime); 24856043Syokota } 24956043Syokota } 25056043Syokota for (;; ++tp) { 25156043Syokota timewarn(tp->timeleft); 25256043Syokota if (!logged && tp->timeleft <= NOLOG_TIME) { 25356043Syokota logged = 1; 25456043Syokota nolog(); 25556043Syokota } 25656043Syokota (void)sleep((u_int)tp->timetowait); 25756043Syokota if (!tp->timeleft) 25856043Syokota break; 25956043Syokota } 26056043Syokota die_you_gravy_sucking_pig_dog(); 26156043Syokota} 26256043Syokota 26356043Syokotastatic jmp_buf alarmbuf; 26456043Syokota 26556043Syokotavoid 26656043Syokotatimewarn(timeleft) 26756043Syokota int timeleft; 26856043Syokota{ 26956043Syokota static int first; 27056043Syokota static char hostname[MAXHOSTNAMELEN + 1]; 27156043Syokota FILE *pf; 27261072Sache char wcmd[MAXPATHLEN + 4]; 27361072Sache 27461072Sache if (!first++) 27561072Sache (void)gethostname(hostname, sizeof(hostname)); 27661072Sache 27761072Sache /* undoc -n option to wall suppresses normal wall banner */ 27861072Sache (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 27961072Sache if (!(pf = popen(wcmd, "w"))) { 28056043Syokota syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 28156043Syokota return; 28256043Syokota } 28356043Syokota 28456043Syokota (void)fprintf(pf, 28556043Syokota "\007*** %sSystem shutdown message from %s@%s ***\007\n", 28656043Syokota timeleft ? "": "FINAL ", whom, hostname); 28756043Syokota 28856043Syokota if (timeleft > 10*60) 28956043Syokota (void)fprintf(pf, "System going down at %5.5s\n\n", 29056043Syokota ctime(&shuttime) + 11); 29156043Syokota else if (timeleft > 59) 29256043Syokota (void)fprintf(pf, "System going down in %d minute%s\n\n", 29356043Syokota timeleft / 60, (timeleft > 60) ? "s" : ""); 29456043Syokota else if (timeleft) 29556043Syokota (void)fprintf(pf, "System going down in 30 seconds\n\n"); 29656043Syokota else 29756043Syokota (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 29856043Syokota 29956043Syokota if (mbuflen) 30056043Syokota (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 30156043Syokota 30256043Syokota /* 30356043Syokota * play some games, just in case wall doesn't come back 30456043Syokota * probably unecessary, given that wall is careful. 30556043Syokota */ 30656043Syokota if (!setjmp(alarmbuf)) { 30756043Syokota (void)signal(SIGALRM, timeout); 30856043Syokota (void)alarm((u_int)30); 30956043Syokota (void)pclose(pf); 31056043Syokota (void)alarm((u_int)0); 31156043Syokota (void)signal(SIGALRM, SIG_DFL); 31256043Syokota } 31356043Syokota} 31456043Syokota 31556043Syokotavoid 31656043Syokotatimeout(signo) 31756043Syokota int signo; 31856043Syokota{ 31956043Syokota longjmp(alarmbuf, 1); 32056043Syokota} 32156043Syokota 32256043Syokotavoid 32356043Syokotadie_you_gravy_sucking_pig_dog() 32456043Syokota{ 325105584Srobert 32656043Syokota syslog(LOG_NOTICE, "%s by %s: %s", 32756043Syokota doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 32856043Syokota (void)sleep(2); 32956043Syokota 33056043Syokota (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 33156043Syokota if (killflg) { 33256043Syokota (void)printf("\rbut you'll have to do it yourself\r\n"); 33356043Syokota finish(0); 33456043Syokota } 33556043Syokota if (dofast) 33656043Syokota doitfast(); 33756043Syokota#ifdef DEBUG 33856043Syokota if (doreboot) 33956043Syokota (void)printf("reboot"); 34056043Syokota else if (dohalt) 34156043Syokota (void)printf("halt"); 34256043Syokota if (nosync) 34356043Syokota (void)printf(" no sync"); 34456043Syokota if (dofast) 34556043Syokota (void)printf(" no fsck"); 34656043Syokota (void)printf("\nkill -HUP 1\n"); 34756043Syokota#else 34856043Syokota if (doreboot) { 34956043Syokota execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); 35056043Syokota syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 35156043Syokota perror("shutdown"); 35256043Syokota } 35356043Syokota else if (dohalt) { 35456043Syokota execle(_PATH_HALT, "halt", "-l", nosync, 0); 35556043Syokota syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 35656043Syokota perror("shutdown"); 35756043Syokota } 35856043Syokota (void)kill(1, SIGTERM); /* to single user */ 35956043Syokota#endif 36056043Syokota finish(0); 36156043Syokota} 36256043Syokota 36356043Syokota#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 36456043Syokota 36556043Syokotavoid 36656043Syokotagetoffset(timearg) 36756043Syokota register char *timearg; 36856043Syokota{ 36956043Syokota register struct tm *lt; 37056043Syokota register char *p; 37156043Syokota time_t now; 37256043Syokota 37356043Syokota if (!strcasecmp(timearg, "now")) { /* now */ 37456043Syokota offset = 0; 37556043Syokota return; 37656043Syokota } 37756043Syokota 37856043Syokota (void)time(&now); 37956043Syokota if (*timearg == '+') { /* +minutes */ 38056043Syokota if (!isdigit(*++timearg)) 38156043Syokota badtime(); 38256043Syokota offset = atoi(timearg) * 60; 38356043Syokota shuttime = now + offset; 38456043Syokota return; 38556043Syokota } 38656043Syokota 38756043Syokota /* handle hh:mm by getting rid of the colon */ 38856043Syokota for (p = timearg; *p; ++p) 38956043Syokota if (!isascii(*p) || !isdigit(*p)) 39056043Syokota if (*p == ':' && strlen(p) == 3) { 39156043Syokota p[0] = p[1]; 39256043Syokota p[1] = p[2]; 39356043Syokota p[2] = '\0'; 39456043Syokota } 39556043Syokota else 39656043Syokota badtime(); 39756043Syokota 39856043Syokota unsetenv("TZ"); /* OUR timezone */ 39956043Syokota lt = localtime(&now); /* current time val */ 40056043Syokota 40156043Syokota switch(strlen(timearg)) { 40256043Syokota case 10: 40356043Syokota lt->tm_year = ATOI2(timearg); 40456043Syokota /* FALLTHROUGH */ 40556043Syokota case 8: 40656043Syokota lt->tm_mon = ATOI2(timearg); 40756043Syokota if (--lt->tm_mon < 0 || lt->tm_mon > 11) 40856043Syokota badtime(); 40956043Syokota /* FALLTHROUGH */ 41056043Syokota case 6: 41156043Syokota lt->tm_mday = ATOI2(timearg); 41256043Syokota if (lt->tm_mday < 1 || lt->tm_mday > 31) 41356043Syokota badtime(); 41456043Syokota /* FALLTHROUGH */ 41556043Syokota case 4: 41656043Syokota lt->tm_hour = ATOI2(timearg); 41756043Syokota if (lt->tm_hour < 0 || lt->tm_hour > 23) 41856043Syokota badtime(); 41956043Syokota lt->tm_min = ATOI2(timearg); 42056043Syokota if (lt->tm_min < 0 || lt->tm_min > 59) 42156043Syokota badtime(); 42256043Syokota lt->tm_sec = 0; 42356043Syokota if ((shuttime = mktime(lt)) == -1) 42456043Syokota badtime(); 42556043Syokota if ((offset = shuttime - now) < 0) { 42656043Syokota (void)fprintf(stderr, 42756043Syokota "shutdown: that time is already past.\n"); 42856043Syokota exit(1); 42956043Syokota } 43056043Syokota break; 43156043Syokota default: 43256043Syokota badtime(); 433 } 434} 435 436#define FSMSG "fastboot file for fsck\n" 437void 438doitfast() 439{ 440 int fastfd; 441 442 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 443 0664)) >= 0) { 444 (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); 445 (void)close(fastfd); 446 } 447} 448 449#define NOMSG "\n\nNO LOGINS: System going down at " 450void 451nolog() 452{ 453 int logfd; 454 char *ct; 455 456 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 457 (void)signal(SIGINT, finish); 458 (void)signal(SIGHUP, finish); 459 (void)signal(SIGQUIT, finish); 460 (void)signal(SIGTERM, finish); 461 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 462 0664)) >= 0) { 463 (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 464 ct = ctime(&shuttime); 465 (void)write(logfd, ct + 11, 5); 466 (void)write(logfd, "\n\n", 2); 467 (void)write(logfd, mbuf, strlen(mbuf)); 468 (void)close(logfd); 469 } 470} 471 472void 473finish(signo) 474 int signo; 475{ 476 (void)unlink(_PATH_NOLOGIN); 477 exit(0); 478} 479 480void 481badtime() 482{ 483 (void)fprintf(stderr, "shutdown: bad time format.\n"); 484 exit(1); 485} 486 487void 488usage() 489{ 490 fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); 491 exit(1); 492} 493