shutdown.c revision 26737
11556Srgrimes/* 21556Srgrimes * Copyright (c) 1988, 1990, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * Redistribution and use in source and binary forms, with or without 61556Srgrimes * modification, are permitted provided that the following conditions 71556Srgrimes * are met: 81556Srgrimes * 1. Redistributions of source code must retain the above copyright 91556Srgrimes * notice, this list of conditions and the following disclaimer. 101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111556Srgrimes * notice, this list of conditions and the following disclaimer in the 121556Srgrimes * documentation and/or other materials provided with the distribution. 131556Srgrimes * 3. All advertising materials mentioning features or use of this software 141556Srgrimes * must display the following acknowledgement: 151556Srgrimes * This product includes software developed by the University of 161556Srgrimes * California, Berkeley and its contributors. 171556Srgrimes * 4. Neither the name of the University nor the names of its contributors 181556Srgrimes * may be used to endorse or promote products derived from this software 191556Srgrimes * without specific prior written permission. 201556Srgrimes * 211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311556Srgrimes * SUCH DAMAGE. 321556Srgrimes * 331556Srgrimes * $Id$ 3436150Scharnier */ 3536150Scharnier 3636150Scharnier#ifndef lint 371556Srgrimesstatic char copyright[] = 3899110Sobrien"@(#) Copyright (c) 1988, 1990, 1993\n\ 3999110Sobrien The Regents of the University of California. All rights reserved.\n"; 401556Srgrimes#endif /* not lint */ 4117987Speter 4217987Speter#ifndef lint 4317987Speterstatic char sccsid[] = "@(#)shutdown.c 8.2 (Berkeley) 2/16/94"; 4417987Speter#endif /* not lint */ 4517987Speter 4617987Speter#include <sys/param.h> 4717987Speter#include <sys/time.h> 481556Srgrimes#include <sys/resource.h> 491556Srgrimes#include <sys/syslog.h> 501556Srgrimes 511556Srgrimes#include <ctype.h> 521556Srgrimes#include <fcntl.h> 5317987Speter#include <pwd.h> 541556Srgrimes#include <setjmp.h> 551556Srgrimes#include <signal.h> 561556Srgrimes#include <stdio.h> 571556Srgrimes#include <stdlib.h> 581556Srgrimes#include <string.h> 591556Srgrimes#include <unistd.h> 601556Srgrimes#include <err.h> 611556Srgrimes 621556Srgrimes#include "pathnames.h" 63100588Stjr 641556Srgrimes#ifdef DEBUG 651556Srgrimes#undef _PATH_NOLOGIN 661556Srgrimes#define _PATH_NOLOGIN "./nologin" 671556Srgrimes#endif 681556Srgrimes 691556Srgrimes#define H *60*60 701556Srgrimes#define M *60 711556Srgrimes#define S *1 7212043Speter#define NOLOG_TIME 5*60 731556Srgrimesstruct interval { 741556Srgrimes int timeleft, timetowait; 751556Srgrimes} tlist[] = { 761556Srgrimes 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, 771556Srgrimes 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, 781556Srgrimes 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, 791556Srgrimes 0, 0, 801556Srgrimes}; 811556Srgrimes#undef H 821556Srgrimes#undef M 831556Srgrimes#undef S 841556Srgrimes 851556Srgrimesstatic time_t offset, shuttime; 8620425Sstevestatic int dohalt, doreboot, killflg, mbuflen; 8720425Sstevestatic char *nosync, *whom, mbuf[BUFSIZ]; 881556Srgrimes 891556Srgrimesvoid badtime __P((void)); 901556Srgrimesvoid die_you_gravy_sucking_pig_dog __P((void)); 911556Srgrimesvoid finish __P((int)); 921556Srgrimesvoid getoffset __P((char *)); 931556Srgrimesvoid loop __P((void)); 941556Srgrimesvoid nolog __P((void)); 951556Srgrimesvoid timeout __P((int)); 961556Srgrimesvoid timewarn __P((int)); 9712043Spetervoid usage __P((void)); 981556Srgrimes 991556Srgrimesint 1001556Srgrimesmain(argc, argv) 101117261Sdds int argc; 1021556Srgrimes char *argv[]; 1031556Srgrimes{ 1041556Srgrimes extern int optind; 1051556Srgrimes register char *p, *endp; 1061556Srgrimes struct passwd *pw; 10790111Simp int arglen, ch, len, readstdin; 10890111Simp 1091556Srgrimes#ifndef DEBUG 1101556Srgrimes if (geteuid()) 1111556Srgrimes errx(1, "NOT super-user"); 1121556Srgrimes#endif 1131556Srgrimes nosync = NULL; 1141556Srgrimes readstdin = 0; 1151556Srgrimes while ((ch = getopt(argc, argv, "-hknr")) != -1) 1161556Srgrimes switch (ch) { 1171556Srgrimes case '-': 1181556Srgrimes readstdin = 1; 1191556Srgrimes break; 1201556Srgrimes case 'h': 121194406Sjilles dohalt = 1; 1221556Srgrimes break; 12312043Speter case 'k': 1241556Srgrimes killflg = 1; 1251556Srgrimes break; 1261556Srgrimes case 'n': 1271556Srgrimes nosync = "-n"; 1281556Srgrimes break; 1291556Srgrimes case 'r': 1301556Srgrimes doreboot = 1; 1311556Srgrimes break; 1321556Srgrimes case '?': 1331556Srgrimes default: 1341556Srgrimes usage(); 1351556Srgrimes } 1361556Srgrimes argc -= optind; 13790111Simp argv += optind; 13817987Speter 13925225Ssteve if (argc < 1) 1401556Srgrimes usage(); 1411556Srgrimes 1421556Srgrimes if (doreboot && dohalt) { 1431556Srgrimes warnx("incompatible switches -h and -r."); 1441556Srgrimes usage(); 1451556Srgrimes } 1461556Srgrimes getoffset(*argv++); 1471556Srgrimes 1481556Srgrimes if (*argv) { 1491556Srgrimes for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 1501556Srgrimes arglen = strlen(*argv); 1511556Srgrimes if ((len -= arglen) <= 2) 1521556Srgrimes break; 1531556Srgrimes if (p != mbuf) 1541556Srgrimes *p++ = ' '; 1551556Srgrimes bcopy(*argv, p, arglen); 1561556Srgrimes p += arglen; 1571556Srgrimes } 1581556Srgrimes *p = '\n'; 1591556Srgrimes *++p = '\0'; 1601556Srgrimes } 1611556Srgrimes 1621556Srgrimes if (readstdin) { 1631556Srgrimes p = mbuf; 1641556Srgrimes endp = mbuf + sizeof(mbuf) - 2; 1651556Srgrimes for (;;) { 16690111Simp if (!fgets(p, endp - p + 1, stdin)) 16720425Ssteve break; 1681556Srgrimes for (; *p && p < endp; ++p); 1691556Srgrimes if (p == endp) { 1701556Srgrimes *p = '\n'; 17120425Ssteve *++p = '\0'; 17212043Speter break; 17390111Simp } 17412043Speter } 17512043Speter } 17620425Ssteve mbuflen = strlen(mbuf); 1771556Srgrimes 178100588Stjr if (offset) 179100588Stjr (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 180100588Stjr else 181100588Stjr (void)printf("Shutdown NOW!\n"); 182100588Stjr 183100588Stjr if (!(whom = getlogin())) 18412043Speter whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 18525225Ssteve 18612043Speter#ifdef DEBUG 187158143Sstefanf (void)putc('\n', stdout); 188158143Sstefanf#else 18912043Speter (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 19012043Speter { 191158143Sstefanf int forkpid; 192158143Sstefanf 19312043Speter forkpid = fork(); 19412043Speter if (forkpid == -1) 195158143Sstefanf err(1, "fork"); 196158143Sstefanf if (forkpid) 197158143Sstefanf errx(0, "[pid %d]", forkpid); 198158143Sstefanf } 199158143Sstefanf#endif 200158143Sstefanf openlog("shutdown", LOG_CONS, LOG_AUTH); 201158143Sstefanf loop(); 202158143Sstefanf /* NOTREACHED */ 203158143Sstefanf} 20412043Speter 20525225Sstevevoid 20625225Ssteveloop() 20712043Speter{ 20812043Speter struct interval *tp; 20912043Speter u_int sltime; 21012043Speter int logged; 21112043Speter 21212043Speter if (offset <= NOLOG_TIME) { 21312043Speter logged = 1; 21412043Speter nolog(); 21512043Speter } 21612043Speter else 21712043Speter logged = 0; 218199629Sjilles tp = tlist; 21912043Speter if (tp->timeleft < offset) 22012043Speter (void)sleep((u_int)(offset - tp->timeleft)); 22112043Speter else { 22212043Speter while (offset < tp->timeleft) 22312043Speter ++tp; 22420425Ssteve /* 22512043Speter * Warn now, if going to sleep more than a fifth of 22612043Speter * the next wait time. 22712043Speter */ 22812043Speter if (sltime = offset - tp->timeleft) { 2291556Srgrimes if (sltime > tp->timetowait / 5) 2301556Srgrimes timewarn(offset); 2311556Srgrimes (void)sleep(sltime); 2321556Srgrimes } 2331556Srgrimes } 2341556Srgrimes for (;; ++tp) { 23520425Ssteve timewarn(tp->timeleft); 23620425Ssteve if (!logged && tp->timeleft <= NOLOG_TIME) { 2371556Srgrimes logged = 1; 2381556Srgrimes nolog(); 2391556Srgrimes } 24090111Simp (void)sleep((u_int)tp->timetowait); 24120425Ssteve if (!tp->timeleft) 24212043Speter break; 24312043Speter } 24412043Speter die_you_gravy_sucking_pig_dog(); 24512043Speter} 2461556Srgrimes 2471556Srgrimesstatic jmp_buf alarmbuf; 2481556Srgrimes 2491556Srgrimesvoid 2501556Srgrimestimewarn(timeleft) 2511556Srgrimes int timeleft; 2521556Srgrimes{ 2531556Srgrimes static int first; 2541556Srgrimes static char hostname[MAXHOSTNAMELEN + 1]; 2551556Srgrimes FILE *pf; 2561556Srgrimes char wcmd[MAXPATHLEN + 4]; 25712043Speter 25812043Speter if (!first++) 25925225Ssteve (void)gethostname(hostname, sizeof(hostname)); 26012043Speter 26112043Speter /* undoc -n option to wall suppresses normal wall banner */ 2621556Srgrimes (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 2631556Srgrimes if (!(pf = popen(wcmd, "w"))) { 2641556Srgrimes syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 26512043Speter return; 26612043Speter } 2671556Srgrimes 2681556Srgrimes (void)fprintf(pf, 26912043Speter "\007*** %sSystem shutdown message from %s@%s ***\007\n", 27012043Speter timeleft ? "": "FINAL ", whom, hostname); 27112043Speter 27212043Speter if (timeleft > 10*60) 27312043Speter (void)fprintf(pf, "System going down at %5.5s\n\n", 27412043Speter ctime(&shuttime) + 11); 27512043Speter else if (timeleft > 59) 27612043Speter (void)fprintf(pf, "System going down in %d minute%s\n\n", 2771556Srgrimes timeleft / 60, (timeleft > 60) ? "s" : ""); 27812043Speter else if (timeleft) 27912043Speter (void)fprintf(pf, "System going down in 30 seconds\n\n"); 28012043Speter else 28112043Speter (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 28212043Speter 28312043Speter if (mbuflen) 28412043Speter (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 2851556Srgrimes 28612043Speter /* 2871556Srgrimes * play some games, just in case wall doesn't come back 28812043Speter * probably unecessary, given that wall is careful. 28912043Speter */ 29012043Speter if (!setjmp(alarmbuf)) { 29112043Speter (void)signal(SIGALRM, timeout); 29212043Speter (void)alarm((u_int)30); 29312043Speter (void)pclose(pf); 29412043Speter (void)alarm((u_int)0); 29512043Speter (void)signal(SIGALRM, SIG_DFL); 29612043Speter } 29712043Speter} 2981556Srgrimes 29912043Spetervoid 30012043Spetertimeout(signo) 3011556Srgrimes int signo; 3021556Srgrimes{ 30318018Speter longjmp(alarmbuf, 1); 3041556Srgrimes} 30584261Sobrien 3061556Srgrimesvoid 30784261Sobriendie_you_gravy_sucking_pig_dog() 30884261Sobrien{ 3091556Srgrimes 3101556Srgrimes syslog(LOG_NOTICE, "%s by %s: %s", 31118018Speter doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 31212043Speter (void)sleep(2); 3131556Srgrimes 31412043Speter (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 3151556Srgrimes if (killflg) { 3161556Srgrimes (void)printf("\rbut you'll have to do it yourself\r\n"); 31712043Speter exit(0); 31812043Speter } 31912043Speter#ifdef DEBUG 3201556Srgrimes if (doreboot) 3211556Srgrimes (void)printf("reboot"); 3221556Srgrimes else if (dohalt) 3231556Srgrimes (void)printf("halt"); 324194128Sjilles if (nosync) 325194128Sjilles (void)printf(" no sync"); 326194128Sjilles (void)printf("\nkill -HUP 1\n"); 327194128Sjilles#else 328194128Sjilles if (doreboot) { 329194128Sjilles execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); 330194128Sjilles syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 331194128Sjilles perror("shutdown"); 332194128Sjilles } 333194128Sjilles else if (dohalt) { 334194128Sjilles execle(_PATH_HALT, "halt", "-l", nosync, 0); 335194128Sjilles syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 336194128Sjilles perror("shutdown"); 337194128Sjilles } 338194128Sjilles (void)kill(1, SIGTERM); /* to single user */ 339194128Sjilles#endif 340194128Sjilles finish(0); 3411556Srgrimes} 3421556Srgrimes 3431556Srgrimes#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 3441556Srgrimes 3451556Srgrimesvoid 34690111Simpgetoffset(timearg) 34790111Simp register char *timearg; 3481556Srgrimes{ 3491556Srgrimes register struct tm *lt; 3501556Srgrimes register char *p; 3511556Srgrimes time_t now; 3521556Srgrimes 3531556Srgrimes if (!strcasecmp(timearg, "now")) { /* now */ 3541556Srgrimes offset = 0; 3551556Srgrimes return; 3561556Srgrimes } 35790111Simp 35890111Simp (void)time(&now); 3591556Srgrimes if (*timearg == '+') { /* +minutes */ 3601556Srgrimes if (!isdigit(*++timearg)) 3611556Srgrimes badtime(); 362199629Sjilles offset = atoi(timearg) * 60; 3631556Srgrimes shuttime = now + offset; 3641556Srgrimes return; 3651556Srgrimes } 3661556Srgrimes 3671556Srgrimes /* handle hh:mm by getting rid of the colon */ 3681556Srgrimes for (p = timearg; *p; ++p) 3691556Srgrimes if (!isascii(*p) || !isdigit(*p)) 3701556Srgrimes if (*p == ':' && strlen(p) == 3) { 37112043Speter p[0] = p[1]; 3721556Srgrimes p[1] = p[2]; 3731556Srgrimes p[2] = '\0'; 3741556Srgrimes } 3751556Srgrimes else 3761556Srgrimes badtime(); 3771556Srgrimes 3781556Srgrimes unsetenv("TZ"); /* OUR timezone */ 3791556Srgrimes lt = localtime(&now); /* current time val */ 38017987Speter 38190111Simp switch(strlen(timearg)) { 3821556Srgrimes case 10: 3831556Srgrimes lt->tm_year = ATOI2(timearg); 3841556Srgrimes /* FALLTHROUGH */ 3851556Srgrimes case 8: 3861556Srgrimes lt->tm_mon = ATOI2(timearg); 3871556Srgrimes if (--lt->tm_mon < 0 || lt->tm_mon > 11) 38812043Speter badtime(); 389199629Sjilles /* FALLTHROUGH */ 3901556Srgrimes case 6: 3911556Srgrimes lt->tm_mday = ATOI2(timearg); 3921556Srgrimes if (lt->tm_mday < 1 || lt->tm_mday > 31) 3931556Srgrimes badtime(); 3941556Srgrimes /* FALLTHROUGH */ 3951556Srgrimes case 4: 3961556Srgrimes lt->tm_hour = ATOI2(timearg); 3971556Srgrimes if (lt->tm_hour < 0 || lt->tm_hour > 23) 3981556Srgrimes badtime(); 3991556Srgrimes lt->tm_min = ATOI2(timearg); 4001556Srgrimes if (lt->tm_min < 0 || lt->tm_min > 59) 4011556Srgrimes badtime(); 4021556Srgrimes lt->tm_sec = 0; 4031556Srgrimes if ((shuttime = mktime(lt)) == -1) 40490111Simp badtime(); 40517987Speter if ((offset = shuttime - now) < 0) 4061556Srgrimes errx(1, "that time is already past."); 4071556Srgrimes break; 4081556Srgrimes default: 4091556Srgrimes badtime(); 4101556Srgrimes } 41153891Scracauer} 4121556Srgrimes 413124780Sdes#define NOMSG "\n\nNO LOGINS: System going down at " 4141556Srgrimesvoid 4151556Srgrimesnolog() 4161556Srgrimes{ 4171556Srgrimes int logfd; 4181556Srgrimes char *ct; 4191556Srgrimes 4201556Srgrimes (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 4211556Srgrimes (void)signal(SIGINT, finish); 4221556Srgrimes (void)signal(SIGHUP, finish); 4231556Srgrimes (void)signal(SIGQUIT, finish); 4241556Srgrimes (void)signal(SIGTERM, finish); 4251556Srgrimes if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 4261556Srgrimes 0664)) >= 0) { 4271556Srgrimes (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 4281556Srgrimes ct = ctime(&shuttime); 4291556Srgrimes (void)write(logfd, ct + 11, 5); 43090111Simp (void)write(logfd, "\n\n", 2); 43117987Speter (void)write(logfd, mbuf, strlen(mbuf)); 43225225Ssteve (void)close(logfd); 4331556Srgrimes } 4341556Srgrimes} 4351556Srgrimes 4361556Srgrimesvoid 4371556Srgrimesfinish(signo) 4381556Srgrimes int signo; 4391556Srgrimes{ 4401556Srgrimes (void)unlink(_PATH_NOLOGIN); 4411556Srgrimes exit(0); 44212043Speter} 4431556Srgrimes 4441556Srgrimesvoid 4451556Srgrimesbadtime() 4461556Srgrimes{ 4471556Srgrimes errx(1, "bad time format."); 4481556Srgrimes} 4491556Srgrimes 4501556Srgrimesvoid 4511556Srgrimesusage() 45290111Simp{ 45390111Simp fprintf(stderr, "usage: shutdown [-hknr] shutdowntime [ message ]\n"); 4541556Srgrimes exit(1); 4551556Srgrimes} 4561556Srgrimes