shutdown.c revision 32399
11558Srgrimes/* 21558Srgrimes * Copyright (c) 1988, 1990, 1993 31558Srgrimes * The Regents of the University of California. All rights reserved. 41558Srgrimes * 51558Srgrimes * Redistribution and use in source and binary forms, with or without 61558Srgrimes * modification, are permitted provided that the following conditions 71558Srgrimes * are met: 81558Srgrimes * 1. Redistributions of source code must retain the above copyright 91558Srgrimes * notice, this list of conditions and the following disclaimer. 101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111558Srgrimes * notice, this list of conditions and the following disclaimer in the 121558Srgrimes * documentation and/or other materials provided with the distribution. 131558Srgrimes * 3. All advertising materials mentioning features or use of this software 141558Srgrimes * must display the following acknowledgement: 151558Srgrimes * This product includes software developed by the University of 161558Srgrimes * California, Berkeley and its contributors. 171558Srgrimes * 4. Neither the name of the University nor the names of its contributors 181558Srgrimes * may be used to endorse or promote products derived from this software 191558Srgrimes * without specific prior written permission. 201558Srgrimes * 211558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311558Srgrimes * SUCH DAMAGE. 3226737Scharnier * 3332399Salex * $Id: shutdown.c,v 1.11 1998/01/08 20:05:45 alex Exp $ 341558Srgrimes */ 351558Srgrimes 361558Srgrimes#ifndef lint 371558Srgrimesstatic char copyright[] = 381558Srgrimes"@(#) Copyright (c) 1988, 1990, 1993\n\ 391558Srgrimes The Regents of the University of California. All rights reserved.\n"; 401558Srgrimes#endif /* not lint */ 411558Srgrimes 421558Srgrimes#ifndef lint 431558Srgrimesstatic char sccsid[] = "@(#)shutdown.c 8.2 (Berkeley) 2/16/94"; 441558Srgrimes#endif /* not lint */ 451558Srgrimes 461558Srgrimes#include <sys/param.h> 471558Srgrimes#include <sys/time.h> 481558Srgrimes#include <sys/resource.h> 491558Srgrimes#include <sys/syslog.h> 501558Srgrimes 511558Srgrimes#include <ctype.h> 521558Srgrimes#include <fcntl.h> 531558Srgrimes#include <pwd.h> 541558Srgrimes#include <setjmp.h> 551558Srgrimes#include <signal.h> 561558Srgrimes#include <stdio.h> 571558Srgrimes#include <stdlib.h> 581558Srgrimes#include <string.h> 591558Srgrimes#include <unistd.h> 6026737Scharnier#include <err.h> 611558Srgrimes 621558Srgrimes#include "pathnames.h" 631558Srgrimes 641558Srgrimes#ifdef DEBUG 651558Srgrimes#undef _PATH_NOLOGIN 661558Srgrimes#define _PATH_NOLOGIN "./nologin" 671558Srgrimes#endif 681558Srgrimes 691558Srgrimes#define H *60*60 701558Srgrimes#define M *60 711558Srgrimes#define S *1 721558Srgrimes#define NOLOG_TIME 5*60 731558Srgrimesstruct interval { 741558Srgrimes int timeleft, timetowait; 751558Srgrimes} tlist[] = { 7632399Salex { 10 H, 5 H }, 7732399Salex { 5 H, 3 H }, 7832399Salex { 2 H, 1 H }, 7932399Salex { 1 H, 30 M }, 8032399Salex { 30 M, 10 M }, 8132399Salex { 20 M, 10 M }, 8232399Salex { 10 M, 5 M }, 8332399Salex { 5 M, 3 M }, 8432399Salex { 2 M, 1 M }, 8532399Salex { 1 M, 30 S }, 8632399Salex { 30 S, 30 S }, 8732399Salex { 0 , 0 } 881558Srgrimes}; 891558Srgrimes#undef H 901558Srgrimes#undef M 911558Srgrimes#undef S 921558Srgrimes 931558Srgrimesstatic time_t offset, shuttime; 946443Sdgstatic int dohalt, doreboot, killflg, mbuflen; 951558Srgrimesstatic char *nosync, *whom, mbuf[BUFSIZ]; 961558Srgrimes 971558Srgrimesvoid badtime __P((void)); 981558Srgrimesvoid die_you_gravy_sucking_pig_dog __P((void)); 991558Srgrimesvoid finish __P((int)); 1001558Srgrimesvoid getoffset __P((char *)); 1011558Srgrimesvoid loop __P((void)); 1021558Srgrimesvoid nolog __P((void)); 1031558Srgrimesvoid timeout __P((int)); 1041558Srgrimesvoid timewarn __P((int)); 1051558Srgrimesvoid usage __P((void)); 1061558Srgrimes 1071558Srgrimesint 1081558Srgrimesmain(argc, argv) 1091558Srgrimes int argc; 1101558Srgrimes char *argv[]; 1111558Srgrimes{ 1121558Srgrimes extern int optind; 1131558Srgrimes register char *p, *endp; 1141558Srgrimes struct passwd *pw; 1151558Srgrimes int arglen, ch, len, readstdin; 1161558Srgrimes 1171558Srgrimes#ifndef DEBUG 11826737Scharnier if (geteuid()) 11926737Scharnier errx(1, "NOT super-user"); 1201558Srgrimes#endif 1211558Srgrimes nosync = NULL; 1221558Srgrimes readstdin = 0; 12324359Simp while ((ch = getopt(argc, argv, "-hknr")) != -1) 1241558Srgrimes switch (ch) { 1251558Srgrimes case '-': 1261558Srgrimes readstdin = 1; 1271558Srgrimes break; 1281558Srgrimes case 'h': 1291558Srgrimes dohalt = 1; 1301558Srgrimes break; 1311558Srgrimes case 'k': 1321558Srgrimes killflg = 1; 1331558Srgrimes break; 1341558Srgrimes case 'n': 1351558Srgrimes nosync = "-n"; 1361558Srgrimes break; 1371558Srgrimes case 'r': 1381558Srgrimes doreboot = 1; 1391558Srgrimes break; 1401558Srgrimes case '?': 1411558Srgrimes default: 1421558Srgrimes usage(); 1431558Srgrimes } 1441558Srgrimes argc -= optind; 1451558Srgrimes argv += optind; 1461558Srgrimes 1471558Srgrimes if (argc < 1) 1481558Srgrimes usage(); 1491558Srgrimes 1501558Srgrimes if (doreboot && dohalt) { 15126737Scharnier warnx("incompatible switches -h and -r."); 1521558Srgrimes usage(); 1531558Srgrimes } 1541558Srgrimes getoffset(*argv++); 1551558Srgrimes 1561558Srgrimes if (*argv) { 1571558Srgrimes for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 1581558Srgrimes arglen = strlen(*argv); 1591558Srgrimes if ((len -= arglen) <= 2) 1601558Srgrimes break; 1611558Srgrimes if (p != mbuf) 1621558Srgrimes *p++ = ' '; 1631558Srgrimes bcopy(*argv, p, arglen); 1641558Srgrimes p += arglen; 1651558Srgrimes } 1661558Srgrimes *p = '\n'; 1671558Srgrimes *++p = '\0'; 1681558Srgrimes } 1691558Srgrimes 1701558Srgrimes if (readstdin) { 1711558Srgrimes p = mbuf; 1721558Srgrimes endp = mbuf + sizeof(mbuf) - 2; 1731558Srgrimes for (;;) { 1741558Srgrimes if (!fgets(p, endp - p + 1, stdin)) 1751558Srgrimes break; 1761558Srgrimes for (; *p && p < endp; ++p); 1771558Srgrimes if (p == endp) { 1781558Srgrimes *p = '\n'; 1791558Srgrimes *++p = '\0'; 1801558Srgrimes break; 1811558Srgrimes } 1821558Srgrimes } 1831558Srgrimes } 1841558Srgrimes mbuflen = strlen(mbuf); 1851558Srgrimes 1861558Srgrimes if (offset) 1871558Srgrimes (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 1881558Srgrimes else 1891558Srgrimes (void)printf("Shutdown NOW!\n"); 1901558Srgrimes 1911558Srgrimes if (!(whom = getlogin())) 1921558Srgrimes whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 1931558Srgrimes 1941558Srgrimes#ifdef DEBUG 1951558Srgrimes (void)putc('\n', stdout); 1961558Srgrimes#else 1971558Srgrimes (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 1981558Srgrimes { 1991558Srgrimes int forkpid; 2001558Srgrimes 2011558Srgrimes forkpid = fork(); 20226737Scharnier if (forkpid == -1) 20326737Scharnier err(1, "fork"); 20426737Scharnier if (forkpid) 20526737Scharnier errx(0, "[pid %d]", forkpid); 2061558Srgrimes } 20728613Sjoerg setsid(); 2081558Srgrimes#endif 2091558Srgrimes openlog("shutdown", LOG_CONS, LOG_AUTH); 2101558Srgrimes loop(); 2111558Srgrimes /* NOTREACHED */ 2121558Srgrimes} 2131558Srgrimes 2141558Srgrimesvoid 2151558Srgrimesloop() 2161558Srgrimes{ 2171558Srgrimes struct interval *tp; 2181558Srgrimes u_int sltime; 2191558Srgrimes int logged; 2201558Srgrimes 2211558Srgrimes if (offset <= NOLOG_TIME) { 2221558Srgrimes logged = 1; 2231558Srgrimes nolog(); 2241558Srgrimes } 2251558Srgrimes else 2261558Srgrimes logged = 0; 2271558Srgrimes tp = tlist; 2281558Srgrimes if (tp->timeleft < offset) 2291558Srgrimes (void)sleep((u_int)(offset - tp->timeleft)); 2301558Srgrimes else { 2311558Srgrimes while (offset < tp->timeleft) 2321558Srgrimes ++tp; 2331558Srgrimes /* 2341558Srgrimes * Warn now, if going to sleep more than a fifth of 2351558Srgrimes * the next wait time. 2361558Srgrimes */ 23732399Salex if ((sltime = offset - tp->timeleft)) { 2381558Srgrimes if (sltime > tp->timetowait / 5) 2391558Srgrimes timewarn(offset); 2401558Srgrimes (void)sleep(sltime); 2411558Srgrimes } 2421558Srgrimes } 2431558Srgrimes for (;; ++tp) { 2441558Srgrimes timewarn(tp->timeleft); 2451558Srgrimes if (!logged && tp->timeleft <= NOLOG_TIME) { 2461558Srgrimes logged = 1; 2471558Srgrimes nolog(); 2481558Srgrimes } 2491558Srgrimes (void)sleep((u_int)tp->timetowait); 2501558Srgrimes if (!tp->timeleft) 2511558Srgrimes break; 2521558Srgrimes } 2531558Srgrimes die_you_gravy_sucking_pig_dog(); 2541558Srgrimes} 2551558Srgrimes 2561558Srgrimesstatic jmp_buf alarmbuf; 2571558Srgrimes 25832399Salexstatic char *restricted_environ[] = { 25932399Salex "PATH=" _PATH_STDPATH, 26032399Salex NULL 26132399Salex}; 26232399Salex 2631558Srgrimesvoid 2641558Srgrimestimewarn(timeleft) 2651558Srgrimes int timeleft; 2661558Srgrimes{ 2671558Srgrimes static int first; 2681558Srgrimes static char hostname[MAXHOSTNAMELEN + 1]; 2691558Srgrimes FILE *pf; 2701558Srgrimes char wcmd[MAXPATHLEN + 4]; 27132399Salex extern char **environ; 2721558Srgrimes 2731558Srgrimes if (!first++) 2741558Srgrimes (void)gethostname(hostname, sizeof(hostname)); 2751558Srgrimes 2761558Srgrimes /* undoc -n option to wall suppresses normal wall banner */ 2771558Srgrimes (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 27832399Salex environ = restricted_environ; 2791558Srgrimes if (!(pf = popen(wcmd, "w"))) { 2801558Srgrimes syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 2811558Srgrimes return; 2821558Srgrimes } 2831558Srgrimes 2841558Srgrimes (void)fprintf(pf, 2851558Srgrimes "\007*** %sSystem shutdown message from %s@%s ***\007\n", 2861558Srgrimes timeleft ? "": "FINAL ", whom, hostname); 2871558Srgrimes 2881558Srgrimes if (timeleft > 10*60) 2891558Srgrimes (void)fprintf(pf, "System going down at %5.5s\n\n", 2901558Srgrimes ctime(&shuttime) + 11); 2911558Srgrimes else if (timeleft > 59) 2921558Srgrimes (void)fprintf(pf, "System going down in %d minute%s\n\n", 2931558Srgrimes timeleft / 60, (timeleft > 60) ? "s" : ""); 2941558Srgrimes else if (timeleft) 2951558Srgrimes (void)fprintf(pf, "System going down in 30 seconds\n\n"); 2961558Srgrimes else 2971558Srgrimes (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 2981558Srgrimes 2991558Srgrimes if (mbuflen) 3001558Srgrimes (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 3011558Srgrimes 3021558Srgrimes /* 3031558Srgrimes * play some games, just in case wall doesn't come back 3041558Srgrimes * probably unecessary, given that wall is careful. 3051558Srgrimes */ 3061558Srgrimes if (!setjmp(alarmbuf)) { 3071558Srgrimes (void)signal(SIGALRM, timeout); 3081558Srgrimes (void)alarm((u_int)30); 3091558Srgrimes (void)pclose(pf); 3101558Srgrimes (void)alarm((u_int)0); 3111558Srgrimes (void)signal(SIGALRM, SIG_DFL); 3121558Srgrimes } 3131558Srgrimes} 3141558Srgrimes 3151558Srgrimesvoid 3161558Srgrimestimeout(signo) 3171558Srgrimes int signo; 3181558Srgrimes{ 3191558Srgrimes longjmp(alarmbuf, 1); 3201558Srgrimes} 3211558Srgrimes 3221558Srgrimesvoid 3231558Srgrimesdie_you_gravy_sucking_pig_dog() 3241558Srgrimes{ 32532399Salex char *empty_environ[] = { NULL }; 3261558Srgrimes 3271558Srgrimes syslog(LOG_NOTICE, "%s by %s: %s", 3281558Srgrimes doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 3291558Srgrimes (void)sleep(2); 3301558Srgrimes 3311558Srgrimes (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 3321558Srgrimes if (killflg) { 3331558Srgrimes (void)printf("\rbut you'll have to do it yourself\r\n"); 3344844Sats exit(0); 3351558Srgrimes } 3361558Srgrimes#ifdef DEBUG 3371558Srgrimes if (doreboot) 3381558Srgrimes (void)printf("reboot"); 3391558Srgrimes else if (dohalt) 3401558Srgrimes (void)printf("halt"); 3411558Srgrimes if (nosync) 3421558Srgrimes (void)printf(" no sync"); 3431558Srgrimes (void)printf("\nkill -HUP 1\n"); 3441558Srgrimes#else 3451558Srgrimes if (doreboot) { 34632399Salex execle(_PATH_REBOOT, "reboot", "-l", nosync, 34732399Salex (char *)NULL, empty_environ); 3481558Srgrimes syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 34932399Salex warn(_PATH_REBOOT); 3501558Srgrimes } 3511558Srgrimes else if (dohalt) { 35232399Salex execle(_PATH_HALT, "halt", "-l", nosync, 35332399Salex (char *)NULL, empty_environ); 3541558Srgrimes syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 35532399Salex warn(_PATH_HALT); 3561558Srgrimes } 3571558Srgrimes (void)kill(1, SIGTERM); /* to single user */ 3581558Srgrimes#endif 3591558Srgrimes finish(0); 3601558Srgrimes} 3611558Srgrimes 3621558Srgrimes#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 3631558Srgrimes 3641558Srgrimesvoid 3651558Srgrimesgetoffset(timearg) 3661558Srgrimes register char *timearg; 3671558Srgrimes{ 3681558Srgrimes register struct tm *lt; 3691558Srgrimes register char *p; 3701558Srgrimes time_t now; 37132328Salex int this_year; 3721558Srgrimes 3731558Srgrimes if (!strcasecmp(timearg, "now")) { /* now */ 3741558Srgrimes offset = 0; 3751558Srgrimes return; 3761558Srgrimes } 3771558Srgrimes 3781558Srgrimes (void)time(&now); 3791558Srgrimes if (*timearg == '+') { /* +minutes */ 3801558Srgrimes if (!isdigit(*++timearg)) 3811558Srgrimes badtime(); 3821558Srgrimes offset = atoi(timearg) * 60; 3831558Srgrimes shuttime = now + offset; 3841558Srgrimes return; 3851558Srgrimes } 3861558Srgrimes 3871558Srgrimes /* handle hh:mm by getting rid of the colon */ 3881558Srgrimes for (p = timearg; *p; ++p) 3891558Srgrimes if (!isascii(*p) || !isdigit(*p)) 3901558Srgrimes if (*p == ':' && strlen(p) == 3) { 3911558Srgrimes p[0] = p[1]; 3921558Srgrimes p[1] = p[2]; 3931558Srgrimes p[2] = '\0'; 3941558Srgrimes } 3951558Srgrimes else 3961558Srgrimes badtime(); 3971558Srgrimes 3981558Srgrimes unsetenv("TZ"); /* OUR timezone */ 3991558Srgrimes lt = localtime(&now); /* current time val */ 4001558Srgrimes 4011558Srgrimes switch(strlen(timearg)) { 4021558Srgrimes case 10: 40332328Salex this_year = lt->tm_year; 4041558Srgrimes lt->tm_year = ATOI2(timearg); 40532328Salex /* 40632328Salex * check if the specified year is in the next century. 40732328Salex * allow for one year of user error as many people will 40832328Salex * enter n - 1 at the start of year n. 40932328Salex */ 41032328Salex if (lt->tm_year < (this_year % 100) - 1) 41132328Salex lt->tm_year += 100; 41232329Salex /* adjust for the year 2000 and beyond */ 41332328Salex lt->tm_year += (this_year - (this_year % 100)); 4141558Srgrimes /* FALLTHROUGH */ 4151558Srgrimes case 8: 4161558Srgrimes lt->tm_mon = ATOI2(timearg); 4171558Srgrimes if (--lt->tm_mon < 0 || lt->tm_mon > 11) 4181558Srgrimes badtime(); 4191558Srgrimes /* FALLTHROUGH */ 4201558Srgrimes case 6: 4211558Srgrimes lt->tm_mday = ATOI2(timearg); 4221558Srgrimes if (lt->tm_mday < 1 || lt->tm_mday > 31) 4231558Srgrimes badtime(); 4241558Srgrimes /* FALLTHROUGH */ 4251558Srgrimes case 4: 4261558Srgrimes lt->tm_hour = ATOI2(timearg); 4271558Srgrimes if (lt->tm_hour < 0 || lt->tm_hour > 23) 4281558Srgrimes badtime(); 4291558Srgrimes lt->tm_min = ATOI2(timearg); 4301558Srgrimes if (lt->tm_min < 0 || lt->tm_min > 59) 4311558Srgrimes badtime(); 4321558Srgrimes lt->tm_sec = 0; 4331558Srgrimes if ((shuttime = mktime(lt)) == -1) 4341558Srgrimes badtime(); 43526737Scharnier if ((offset = shuttime - now) < 0) 43626737Scharnier errx(1, "that time is already past."); 4371558Srgrimes break; 4381558Srgrimes default: 4391558Srgrimes badtime(); 4401558Srgrimes } 4411558Srgrimes} 4421558Srgrimes 4431558Srgrimes#define NOMSG "\n\nNO LOGINS: System going down at " 4441558Srgrimesvoid 4451558Srgrimesnolog() 4461558Srgrimes{ 4471558Srgrimes int logfd; 4481558Srgrimes char *ct; 4491558Srgrimes 4501558Srgrimes (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 4511558Srgrimes (void)signal(SIGINT, finish); 4521558Srgrimes (void)signal(SIGHUP, finish); 4531558Srgrimes (void)signal(SIGQUIT, finish); 4541558Srgrimes (void)signal(SIGTERM, finish); 4551558Srgrimes if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 4561558Srgrimes 0664)) >= 0) { 4571558Srgrimes (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 4581558Srgrimes ct = ctime(&shuttime); 4591558Srgrimes (void)write(logfd, ct + 11, 5); 4601558Srgrimes (void)write(logfd, "\n\n", 2); 4611558Srgrimes (void)write(logfd, mbuf, strlen(mbuf)); 4621558Srgrimes (void)close(logfd); 4631558Srgrimes } 4641558Srgrimes} 4651558Srgrimes 4661558Srgrimesvoid 4671558Srgrimesfinish(signo) 4681558Srgrimes int signo; 4691558Srgrimes{ 4701558Srgrimes (void)unlink(_PATH_NOLOGIN); 4711558Srgrimes exit(0); 4721558Srgrimes} 4731558Srgrimes 4741558Srgrimesvoid 4751558Srgrimesbadtime() 4761558Srgrimes{ 47726737Scharnier errx(1, "bad time format."); 4781558Srgrimes} 4791558Srgrimes 4801558Srgrimesvoid 4811558Srgrimesusage() 4821558Srgrimes{ 4836443Sdg fprintf(stderr, "usage: shutdown [-hknr] shutdowntime [ message ]\n"); 4841558Srgrimes exit(1); 4851558Srgrimes} 486