shutdown.c revision 41683
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 341556Srgrimes#ifndef lint 351556Srgrimesstatic const char copyright[] = 361556Srgrimes"@(#) Copyright (c) 1988, 1990, 1993\n\ 371556Srgrimes The Regents of the University of California. All rights reserved.\n"; 3836150Scharnier#endif /* not lint */ 3936150Scharnier 4036150Scharnier#ifndef lint 4136150Scharnier#if 0 4236150Scharnierstatic char sccsid[] = "@(#)shutdown.c 8.2 (Berkeley) 2/16/94"; 431556Srgrimes#endif 441556Srgrimesstatic const char rcsid[] = 4517987Speter "$Id: shutdown.c,v 1.14 1998/12/10 23:54:02 msmith Exp $"; 4617987Speter#endif /* not lint */ 471556Srgrimes 481556Srgrimes#include <sys/param.h> 491556Srgrimes#include <sys/time.h> 501556Srgrimes#include <sys/resource.h> 511556Srgrimes#include <sys/syslog.h> 521556Srgrimes 531556Srgrimes#include <ctype.h> 541556Srgrimes#include <err.h> 551556Srgrimes#include <fcntl.h> 561556Srgrimes#include <pwd.h> 571556Srgrimes#include <setjmp.h> 581556Srgrimes#include <signal.h> 591556Srgrimes#include <stdio.h> 601556Srgrimes#include <stdlib.h> 6117987Speter#include <string.h> 6217987Speter#include <unistd.h> 631556Srgrimes 6417987Speter#include "pathnames.h" 651556Srgrimes 661556Srgrimes#ifdef DEBUG 671556Srgrimes#undef _PATH_NOLOGIN 681556Srgrimes#define _PATH_NOLOGIN "./nologin" 691556Srgrimes#endif 701556Srgrimes 711556Srgrimes#define H *60*60 721556Srgrimes#define M *60 7317987Speter#define S *1 741556Srgrimes#define NOLOG_TIME 5*60 751556Srgrimesstruct interval { 761556Srgrimes int timeleft, timetowait; 771556Srgrimes} tlist[] = { 781556Srgrimes { 10 H, 5 H }, 791556Srgrimes { 5 H, 3 H }, 801556Srgrimes { 2 H, 1 H }, 811556Srgrimes { 1 H, 30 M }, 821556Srgrimes { 30 M, 10 M }, 831556Srgrimes { 20 M, 10 M }, 841556Srgrimes { 10 M, 5 M }, 851556Srgrimes { 5 M, 3 M }, 861556Srgrimes { 2 M, 1 M }, 871556Srgrimes { 1 M, 30 S }, 881556Srgrimes { 30 S, 30 S }, 891556Srgrimes { 0 , 0 } 901556Srgrimes}; 911556Srgrimes#undef H 921556Srgrimes#undef M 931556Srgrimes#undef S 941556Srgrimes 951556Srgrimesstatic time_t offset, shuttime; 961556Srgrimesstatic int dohalt, dopower, doreboot, killflg, mbuflen; 971556Srgrimesstatic char *nosync, *whom, mbuf[BUFSIZ]; 981556Srgrimes 991556Srgrimesvoid badtime __P((void)); 10018018Spetervoid die_you_gravy_sucking_pig_dog __P((void)); 10118018Spetervoid finish __P((int)); 1021556Srgrimesvoid getoffset __P((char *)); 1031556Srgrimesvoid loop __P((void)); 1041556Srgrimesvoid nolog __P((void)); 1051556Srgrimesvoid timeout __P((int)); 1061556Srgrimesvoid timewarn __P((int)); 1071556Srgrimesvoid usage __P((void)); 1081556Srgrimes 1091556Srgrimesint 1101556Srgrimesmain(argc, argv) 1111556Srgrimes int argc; 1121556Srgrimes char *argv[]; 1131556Srgrimes{ 1141556Srgrimes register char *p, *endp; 11517987Speter struct passwd *pw; 1161556Srgrimes int arglen, ch, len, readstdin; 1171556Srgrimes 11817987Speter#ifndef DEBUG 1191556Srgrimes if (geteuid()) 12017987Speter errx(1, "NOT super-user"); 1211556Srgrimes#endif 1221556Srgrimes nosync = NULL; 1231556Srgrimes readstdin = 0; 1241556Srgrimes while ((ch = getopt(argc, argv, "-hknpr")) != -1) 12520425Ssteve switch (ch) { 1261556Srgrimes case '-': 12717987Speter readstdin = 1; 1281556Srgrimes break; 1291556Srgrimes case 'h': 1301556Srgrimes dohalt = 1; 1311556Srgrimes break; 1321556Srgrimes case 'k': 1331556Srgrimes killflg = 1; 13420425Ssteve break; 13517987Speter case 'n': 13617987Speter nosync = "-n"; 1371556Srgrimes break; 1381556Srgrimes case 'p': 1391556Srgrimes dopower = 1; 1401556Srgrimes break; 1411556Srgrimes case 'r': 1421556Srgrimes doreboot = 1; 1431556Srgrimes break; 1441556Srgrimes case '?': 1451556Srgrimes default: 1461556Srgrimes usage(); 1471556Srgrimes } 1481556Srgrimes argc -= optind; 1491556Srgrimes argv += optind; 1501556Srgrimes 1511556Srgrimes if (argc < 1) 1521556Srgrimes usage(); 1531556Srgrimes 1541556Srgrimes if (doreboot + dohalt + dopower > 1) { 1551556Srgrimes warnx("incompatible switches -h, -p and -r"); 15620425Ssteve usage(); 15717987Speter } 15817987Speter getoffset(*argv++); 1591556Srgrimes 16017987Speter if (*argv) { 1611556Srgrimes for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 1621556Srgrimes arglen = strlen(*argv); 1631556Srgrimes if ((len -= arglen) <= 2) 1641556Srgrimes break; 16517987Speter if (p != mbuf) 1661556Srgrimes *p++ = ' '; 16717987Speter bcopy(*argv, p, arglen); 16817987Speter p += arglen; 16917987Speter } 17017987Speter *p = '\n'; 17117987Speter *++p = '\0'; 17217987Speter } 17317987Speter 17417987Speter if (readstdin) { 17517987Speter p = mbuf; 17617987Speter endp = mbuf + sizeof(mbuf) - 2; 17717987Speter for (;;) { 17817987Speter if (!fgets(p, endp - p + 1, stdin)) 17917987Speter break; 18017987Speter for (; *p && p < endp; ++p); 18117987Speter if (p == endp) { 18217987Speter *p = '\n'; 18317987Speter *++p = '\0'; 18417987Speter break; 18517987Speter } 18617987Speter } 18717987Speter } 18817987Speter mbuflen = strlen(mbuf); 18917987Speter 19017987Speter if (offset) 19117987Speter (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 19217987Speter else 19313882Sjoerg (void)printf("Shutdown NOW!\n"); 19417987Speter 19517987Speter if (!(whom = getlogin())) 19617987Speter whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 1971556Srgrimes 19817987Speter#ifdef DEBUG 19917987Speter (void)putc('\n', stdout); 20017987Speter#else 20117987Speter (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 20217987Speter { 20317987Speter int forkpid; 20417987Speter 2051556Srgrimes forkpid = fork(); 2061556Srgrimes if (forkpid == -1) 2071556Srgrimes err(1, "fork"); 2081556Srgrimes if (forkpid) 2091556Srgrimes errx(0, "[pid %d]", forkpid); 2101556Srgrimes } 2111556Srgrimes setsid(); 2121556Srgrimes#endif 2131556Srgrimes openlog("shutdown", LOG_CONS, LOG_AUTH); 2141556Srgrimes loop(); 2151556Srgrimes return(0); 2161556Srgrimes} 2171556Srgrimes 2181556Srgrimesvoid 2191556Srgrimesloop() 2201556Srgrimes{ 2211556Srgrimes struct interval *tp; 2221556Srgrimes u_int sltime; 2231556Srgrimes int logged; 2241556Srgrimes 2251556Srgrimes if (offset <= NOLOG_TIME) { 2261556Srgrimes logged = 1; 2271556Srgrimes nolog(); 2281556Srgrimes } 2291556Srgrimes else 2301556Srgrimes logged = 0; 2311556Srgrimes tp = tlist; 2321556Srgrimes if (tp->timeleft < offset) 2331556Srgrimes (void)sleep((u_int)(offset - tp->timeleft)); 2341556Srgrimes else { 2351556Srgrimes while (offset < tp->timeleft) 2361556Srgrimes ++tp; 2371556Srgrimes /* 2381556Srgrimes * Warn now, if going to sleep more than a fifth of 2391556Srgrimes * the next wait time. 2401556Srgrimes */ 2411556Srgrimes if ((sltime = offset - tp->timeleft)) { 2421556Srgrimes if (sltime > tp->timetowait / 5) 2431556Srgrimes timewarn(offset); 2441556Srgrimes (void)sleep(sltime); 2451556Srgrimes } 2461556Srgrimes } 2471556Srgrimes for (;; ++tp) { 2481556Srgrimes timewarn(tp->timeleft); 2491556Srgrimes if (!logged && tp->timeleft <= NOLOG_TIME) { 2501556Srgrimes logged = 1; 2511556Srgrimes nolog(); 2521556Srgrimes } 2531556Srgrimes (void)sleep((u_int)tp->timetowait); 25425230Ssteve if (!tp->timeleft) 2551556Srgrimes break; 25625230Ssteve } 2571556Srgrimes die_you_gravy_sucking_pig_dog(); 2581556Srgrimes} 25925230Ssteve 26025230Sstevestatic jmp_buf alarmbuf; 26125230Ssteve 26225230Sstevestatic char *restricted_environ[] = { 26325230Ssteve "PATH=" _PATH_STDPATH, 2641556Srgrimes NULL 2651556Srgrimes}; 2661556Srgrimes 2671556Srgrimesvoid 2681556Srgrimestimewarn(timeleft) 2691556Srgrimes int timeleft; 2701556Srgrimes{ 2711556Srgrimes static int first; 2721556Srgrimes static char hostname[MAXHOSTNAMELEN + 1]; 2731556Srgrimes FILE *pf; 2741556Srgrimes char wcmd[MAXPATHLEN + 4]; 2751556Srgrimes extern char **environ; 2761556Srgrimes 2771556Srgrimes if (!first++) 2781556Srgrimes (void)gethostname(hostname, sizeof(hostname)); 2791556Srgrimes 2801556Srgrimes /* undoc -n option to wall suppresses normal wall banner */ 2811556Srgrimes (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 28225230Ssteve environ = restricted_environ; 28325230Ssteve if (!(pf = popen(wcmd, "w"))) { 28425230Ssteve syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 28525230Ssteve return; 28625230Ssteve } 28725230Ssteve 2881556Srgrimes (void)fprintf(pf, 2891556Srgrimes "\007*** %sSystem shutdown message from %s@%s ***\007\n", 2901556Srgrimes timeleft ? "": "FINAL ", whom, hostname); 2911556Srgrimes 2921556Srgrimes if (timeleft > 10*60) 2931556Srgrimes (void)fprintf(pf, "System going down at %5.5s\n\n", 2941556Srgrimes ctime(&shuttime) + 11); 2951556Srgrimes else if (timeleft > 59) 2961556Srgrimes (void)fprintf(pf, "System going down in %d minute%s\n\n", 2971556Srgrimes timeleft / 60, (timeleft > 60) ? "s" : ""); 2981556Srgrimes else if (timeleft) 29925230Ssteve (void)fprintf(pf, "System going down in 30 seconds\n\n"); 3001556Srgrimes else 3011556Srgrimes (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 30217987Speter 30317987Speter if (mbuflen) 3041556Srgrimes (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 30520425Ssteve 3061556Srgrimes /* 3071556Srgrimes * play some games, just in case wall doesn't come back 3081556Srgrimes * probably unnecessary, given that wall is careful. 3091556Srgrimes */ 3101556Srgrimes if (!setjmp(alarmbuf)) { 3111556Srgrimes (void)signal(SIGALRM, timeout); 3121556Srgrimes (void)alarm((u_int)30); 3131556Srgrimes (void)pclose(pf); 3141556Srgrimes (void)alarm((u_int)0); 3151556Srgrimes (void)signal(SIGALRM, SIG_DFL); 3161556Srgrimes } 3171556Srgrimes} 3181556Srgrimes 3191556Srgrimesvoid 3201556Srgrimestimeout(signo) 3211556Srgrimes int signo; 3221556Srgrimes{ 3231556Srgrimes longjmp(alarmbuf, 1); 3241556Srgrimes} 3251556Srgrimes 3261556Srgrimesvoid 3271556Srgrimesdie_you_gravy_sucking_pig_dog() 3281556Srgrimes{ 3291556Srgrimes char *empty_environ[] = { NULL }; 3301556Srgrimes 3311556Srgrimes syslog(LOG_NOTICE, "%s by %s: %s", 3321556Srgrimes doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : 3331556Srgrimes "shutdown", whom, mbuf); 3341556Srgrimes (void)sleep(2); 3351556Srgrimes 3361556Srgrimes (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 3371556Srgrimes if (killflg) { 3381556Srgrimes (void)printf("\rbut you'll have to do it yourself\r\n"); 3391556Srgrimes exit(0); 3401556Srgrimes } 3411556Srgrimes#ifdef DEBUG 3421556Srgrimes if (doreboot) 3431556Srgrimes (void)printf("reboot"); 3441556Srgrimes else if (dohalt) 3451556Srgrimes (void)printf("halt"); 3461556Srgrimes else if (dopower) 3471556Srgrimes (void)printf("power-down"); 3481556Srgrimes if (nosync) 3491556Srgrimes (void)printf(" no sync"); 3501556Srgrimes (void)printf("\nkill -HUP 1\n"); 3511556Srgrimes#else 3521556Srgrimes if (doreboot) { 3531556Srgrimes execle(_PATH_REBOOT, "reboot", "-l", nosync, 3541556Srgrimes (char *)NULL, empty_environ); 3551556Srgrimes syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 3561556Srgrimes warn(_PATH_REBOOT); 3571556Srgrimes } 3581556Srgrimes else if (dohalt) { 3591556Srgrimes execle(_PATH_HALT, "halt", "-l", nosync, 3601556Srgrimes (char *)NULL, empty_environ); 3611556Srgrimes syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 3621556Srgrimes warn(_PATH_HALT); 3631556Srgrimes } 3641556Srgrimes else if (dopower) { 3651556Srgrimes execle(_PATH_HALT, "halt", "-l", "-p", nosync, 3661556Srgrimes (char *)NULL, empty_environ); 3671556Srgrimes syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 3681556Srgrimes warn(_PATH_HALT); 3691556Srgrimes } 3701556Srgrimes (void)kill(1, SIGTERM); /* to single user */ 3711556Srgrimes#endif 3721556Srgrimes finish(0); 3731556Srgrimes} 3741556Srgrimes 3751556Srgrimes#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 3761556Srgrimes 3771556Srgrimesvoid 3781556Srgrimesgetoffset(timearg) 3791556Srgrimes register char *timearg; 3801556Srgrimes{ 3811556Srgrimes register struct tm *lt; 3821556Srgrimes register char *p; 3831556Srgrimes time_t now; 3841556Srgrimes int this_year; 3851556Srgrimes 3861556Srgrimes if (!strcasecmp(timearg, "now")) { /* now */ 3871556Srgrimes offset = 0; 3881556Srgrimes return; 3891556Srgrimes } 3901556Srgrimes 3911556Srgrimes (void)time(&now); 3921556Srgrimes if (*timearg == '+') { /* +minutes */ 3931556Srgrimes if (!isdigit(*++timearg)) 3941556Srgrimes badtime(); 3951556Srgrimes offset = atoi(timearg) * 60; 3961556Srgrimes shuttime = now + offset; 3971556Srgrimes return; 3981556Srgrimes } 3991556Srgrimes 4001556Srgrimes /* handle hh:mm by getting rid of the colon */ 4011556Srgrimes for (p = timearg; *p; ++p) 4021556Srgrimes if (!isascii(*p) || !isdigit(*p)) 4031556Srgrimes if (*p == ':' && strlen(p) == 3) { 4041556Srgrimes p[0] = p[1]; 4051556Srgrimes p[1] = p[2]; 4061556Srgrimes p[2] = '\0'; 4071556Srgrimes } 4081556Srgrimes else 4091556Srgrimes badtime(); 4101556Srgrimes 4111556Srgrimes unsetenv("TZ"); /* OUR timezone */ 4121556Srgrimes lt = localtime(&now); /* current time val */ 4131556Srgrimes 4141556Srgrimes switch(strlen(timearg)) { 4151556Srgrimes case 10: 4161556Srgrimes this_year = lt->tm_year; 4171556Srgrimes lt->tm_year = ATOI2(timearg); 4181556Srgrimes /* 4191556Srgrimes * check if the specified year is in the next century. 4201556Srgrimes * allow for one year of user error as many people will 4211556Srgrimes * enter n - 1 at the start of year n. 42218018Speter */ 4232760Ssef if (lt->tm_year < (this_year % 100) - 1) 4242760Ssef lt->tm_year += 100; 4251556Srgrimes /* adjust for the year 2000 and beyond */ 4261556Srgrimes lt->tm_year += (this_year - (this_year % 100)); 4271556Srgrimes /* FALLTHROUGH */ 4281556Srgrimes case 8: 4291556Srgrimes lt->tm_mon = ATOI2(timearg); 4301556Srgrimes if (--lt->tm_mon < 0 || lt->tm_mon > 11) 4311556Srgrimes badtime(); 4321556Srgrimes /* FALLTHROUGH */ 4332760Ssef case 6: 4341556Srgrimes lt->tm_mday = ATOI2(timearg); 4351556Srgrimes if (lt->tm_mday < 1 || lt->tm_mday > 31) 4362760Ssef badtime(); 4371556Srgrimes /* FALLTHROUGH */ 4381556Srgrimes case 4: 4391556Srgrimes lt->tm_hour = ATOI2(timearg); 44018018Speter if (lt->tm_hour < 0 || lt->tm_hour > 23) 4411556Srgrimes badtime(); 4422760Ssef lt->tm_min = ATOI2(timearg); 4432760Ssef if (lt->tm_min < 0 || lt->tm_min > 59) 4442760Ssef badtime(); 4452760Ssef lt->tm_sec = 0; 44618018Speter if ((shuttime = mktime(lt)) == -1) 4472760Ssef badtime(); 4482760Ssef if ((offset = shuttime - now) < 0) 4492760Ssef errx(1, "that time is already past."); 4501556Srgrimes break; 4512760Ssef default: 45218018Speter badtime(); 4531556Srgrimes } 4541556Srgrimes} 4551556Srgrimes 4561556Srgrimes#define NOMSG "\n\nNO LOGINS: System going down at " 4571556Srgrimesvoid 4581556Srgrimesnolog() 4591556Srgrimes{ 4601556Srgrimes int logfd; 4611556Srgrimes char *ct; 4621556Srgrimes 4631556Srgrimes (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 4641556Srgrimes (void)signal(SIGINT, finish); 4651556Srgrimes (void)signal(SIGHUP, finish); 4661556Srgrimes (void)signal(SIGQUIT, finish); 4671556Srgrimes (void)signal(SIGTERM, finish); 4681556Srgrimes if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 4691556Srgrimes 0664)) >= 0) { 4701556Srgrimes (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 4711556Srgrimes ct = ctime(&shuttime); 47217987Speter (void)write(logfd, ct + 11, 5); 47317987Speter (void)write(logfd, "\n\n", 2); 47417987Speter (void)write(logfd, mbuf, strlen(mbuf)); 47517987Speter (void)close(logfd); 47617987Speter } 47717987Speter} 47817987Speter 47920425Sstevevoid 48020425Sstevefinish(signo) 4811556Srgrimes int signo; 48210399Sjoerg{ 4831556Srgrimes (void)unlink(_PATH_NOLOGIN); 48417987Speter exit(0); 4851556Srgrimes} 48625230Ssteve 4871556Srgrimesvoid 4881556Srgrimesbadtime() 4891556Srgrimes{ 4901556Srgrimes errx(1, "bad time format"); 4911556Srgrimes} 4921556Srgrimes 4931556Srgrimesvoid 4941556Srgrimesusage() 4951556Srgrimes{ 4961556Srgrimes fprintf(stderr, 4971556Srgrimes "usage: shutdown [-] [-hknpr] time [warning-message ...]\n"); 4981556Srgrimes exit(1); 4991556Srgrimes} 5001556Srgrimes