shutdown.c revision 41684
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. 321558Srgrimes */ 331558Srgrimes 341558Srgrimes#ifndef lint 3538036Scharnierstatic const char copyright[] = 361558Srgrimes"@(#) Copyright (c) 1988, 1990, 1993\n\ 371558Srgrimes The Regents of the University of California. All rights reserved.\n"; 381558Srgrimes#endif /* not lint */ 391558Srgrimes 401558Srgrimes#ifndef lint 4138036Scharnier#if 0 4241684Sbdestatic char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; 4338036Scharnier#endif 4438036Scharnierstatic const char rcsid[] = 4541684Sbde "$Id: shutdown.c,v 1.15 1998/12/11 11:04:19 bde Exp $"; 461558Srgrimes#endif /* not lint */ 471558Srgrimes 481558Srgrimes#include <sys/param.h> 491558Srgrimes#include <sys/time.h> 501558Srgrimes#include <sys/resource.h> 511558Srgrimes#include <sys/syslog.h> 521558Srgrimes 531558Srgrimes#include <ctype.h> 5438036Scharnier#include <err.h> 551558Srgrimes#include <fcntl.h> 561558Srgrimes#include <pwd.h> 571558Srgrimes#include <setjmp.h> 581558Srgrimes#include <signal.h> 591558Srgrimes#include <stdio.h> 601558Srgrimes#include <stdlib.h> 611558Srgrimes#include <string.h> 621558Srgrimes#include <unistd.h> 631558Srgrimes 641558Srgrimes#include "pathnames.h" 651558Srgrimes 661558Srgrimes#ifdef DEBUG 671558Srgrimes#undef _PATH_NOLOGIN 681558Srgrimes#define _PATH_NOLOGIN "./nologin" 691558Srgrimes#endif 701558Srgrimes 711558Srgrimes#define H *60*60 721558Srgrimes#define M *60 731558Srgrimes#define S *1 741558Srgrimes#define NOLOG_TIME 5*60 751558Srgrimesstruct interval { 761558Srgrimes int timeleft, timetowait; 771558Srgrimes} tlist[] = { 7832399Salex { 10 H, 5 H }, 7932399Salex { 5 H, 3 H }, 8032399Salex { 2 H, 1 H }, 8132399Salex { 1 H, 30 M }, 8232399Salex { 30 M, 10 M }, 8332399Salex { 20 M, 10 M }, 8432399Salex { 10 M, 5 M }, 8532399Salex { 5 M, 3 M }, 8632399Salex { 2 M, 1 M }, 8732399Salex { 1 M, 30 S }, 8832399Salex { 30 S, 30 S }, 8932399Salex { 0 , 0 } 901558Srgrimes}; 911558Srgrimes#undef H 921558Srgrimes#undef M 931558Srgrimes#undef S 941558Srgrimes 951558Srgrimesstatic time_t offset, shuttime; 9641666Smsmithstatic int dohalt, dopower, doreboot, killflg, mbuflen; 971558Srgrimesstatic char *nosync, *whom, mbuf[BUFSIZ]; 981558Srgrimes 991558Srgrimesvoid badtime __P((void)); 1001558Srgrimesvoid die_you_gravy_sucking_pig_dog __P((void)); 1011558Srgrimesvoid finish __P((int)); 1021558Srgrimesvoid getoffset __P((char *)); 1031558Srgrimesvoid loop __P((void)); 1041558Srgrimesvoid nolog __P((void)); 1051558Srgrimesvoid timeout __P((int)); 1061558Srgrimesvoid timewarn __P((int)); 1071558Srgrimesvoid usage __P((void)); 1081558Srgrimes 1091558Srgrimesint 1101558Srgrimesmain(argc, argv) 1111558Srgrimes int argc; 1121558Srgrimes char *argv[]; 1131558Srgrimes{ 1141558Srgrimes register char *p, *endp; 1151558Srgrimes struct passwd *pw; 1161558Srgrimes int arglen, ch, len, readstdin; 1171558Srgrimes 1181558Srgrimes#ifndef DEBUG 11926737Scharnier if (geteuid()) 12026737Scharnier errx(1, "NOT super-user"); 1211558Srgrimes#endif 1221558Srgrimes nosync = NULL; 1231558Srgrimes readstdin = 0; 12441666Smsmith while ((ch = getopt(argc, argv, "-hknpr")) != -1) 1251558Srgrimes switch (ch) { 1261558Srgrimes case '-': 1271558Srgrimes readstdin = 1; 1281558Srgrimes break; 1291558Srgrimes case 'h': 1301558Srgrimes dohalt = 1; 1311558Srgrimes break; 1321558Srgrimes case 'k': 1331558Srgrimes killflg = 1; 1341558Srgrimes break; 1351558Srgrimes case 'n': 1361558Srgrimes nosync = "-n"; 1371558Srgrimes break; 13841666Smsmith case 'p': 13941666Smsmith dopower = 1; 14041666Smsmith break; 1411558Srgrimes case 'r': 1421558Srgrimes doreboot = 1; 1431558Srgrimes break; 1441558Srgrimes case '?': 1451558Srgrimes default: 1461558Srgrimes usage(); 1471558Srgrimes } 1481558Srgrimes argc -= optind; 1491558Srgrimes argv += optind; 1501558Srgrimes 1511558Srgrimes if (argc < 1) 1521558Srgrimes usage(); 1531558Srgrimes 15441683Sbde if (doreboot + dohalt + dopower > 1) { 15541683Sbde warnx("incompatible switches -h, -p and -r"); 1561558Srgrimes usage(); 1571558Srgrimes } 1581558Srgrimes getoffset(*argv++); 1591558Srgrimes 1601558Srgrimes if (*argv) { 1611558Srgrimes for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 1621558Srgrimes arglen = strlen(*argv); 1631558Srgrimes if ((len -= arglen) <= 2) 1641558Srgrimes break; 1651558Srgrimes if (p != mbuf) 1661558Srgrimes *p++ = ' '; 16741684Sbde memmove(p, *argv, arglen); 1681558Srgrimes p += arglen; 1691558Srgrimes } 1701558Srgrimes *p = '\n'; 1711558Srgrimes *++p = '\0'; 1721558Srgrimes } 1731558Srgrimes 1741558Srgrimes if (readstdin) { 1751558Srgrimes p = mbuf; 1761558Srgrimes endp = mbuf + sizeof(mbuf) - 2; 1771558Srgrimes for (;;) { 1781558Srgrimes if (!fgets(p, endp - p + 1, stdin)) 1791558Srgrimes break; 1801558Srgrimes for (; *p && p < endp; ++p); 1811558Srgrimes if (p == endp) { 1821558Srgrimes *p = '\n'; 1831558Srgrimes *++p = '\0'; 1841558Srgrimes break; 1851558Srgrimes } 1861558Srgrimes } 1871558Srgrimes } 1881558Srgrimes mbuflen = strlen(mbuf); 1891558Srgrimes 1901558Srgrimes if (offset) 1911558Srgrimes (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 1921558Srgrimes else 1931558Srgrimes (void)printf("Shutdown NOW!\n"); 1941558Srgrimes 1951558Srgrimes if (!(whom = getlogin())) 1961558Srgrimes whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 1971558Srgrimes 1981558Srgrimes#ifdef DEBUG 1991558Srgrimes (void)putc('\n', stdout); 2001558Srgrimes#else 2011558Srgrimes (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 2021558Srgrimes { 2031558Srgrimes int forkpid; 2041558Srgrimes 2051558Srgrimes forkpid = fork(); 20626737Scharnier if (forkpid == -1) 20726737Scharnier err(1, "fork"); 20826737Scharnier if (forkpid) 20926737Scharnier errx(0, "[pid %d]", forkpid); 2101558Srgrimes } 21128613Sjoerg setsid(); 2121558Srgrimes#endif 2131558Srgrimes openlog("shutdown", LOG_CONS, LOG_AUTH); 2141558Srgrimes loop(); 21538036Scharnier return(0); 2161558Srgrimes} 2171558Srgrimes 2181558Srgrimesvoid 2191558Srgrimesloop() 2201558Srgrimes{ 2211558Srgrimes struct interval *tp; 2221558Srgrimes u_int sltime; 2231558Srgrimes int logged; 2241558Srgrimes 2251558Srgrimes if (offset <= NOLOG_TIME) { 2261558Srgrimes logged = 1; 2271558Srgrimes nolog(); 2281558Srgrimes } 2291558Srgrimes else 2301558Srgrimes logged = 0; 2311558Srgrimes tp = tlist; 2321558Srgrimes if (tp->timeleft < offset) 2331558Srgrimes (void)sleep((u_int)(offset - tp->timeleft)); 2341558Srgrimes else { 2351558Srgrimes while (offset < tp->timeleft) 2361558Srgrimes ++tp; 2371558Srgrimes /* 2381558Srgrimes * Warn now, if going to sleep more than a fifth of 2391558Srgrimes * the next wait time. 2401558Srgrimes */ 24132399Salex if ((sltime = offset - tp->timeleft)) { 2421558Srgrimes if (sltime > tp->timetowait / 5) 2431558Srgrimes timewarn(offset); 2441558Srgrimes (void)sleep(sltime); 2451558Srgrimes } 2461558Srgrimes } 2471558Srgrimes for (;; ++tp) { 2481558Srgrimes timewarn(tp->timeleft); 2491558Srgrimes if (!logged && tp->timeleft <= NOLOG_TIME) { 2501558Srgrimes logged = 1; 2511558Srgrimes nolog(); 2521558Srgrimes } 2531558Srgrimes (void)sleep((u_int)tp->timetowait); 2541558Srgrimes if (!tp->timeleft) 2551558Srgrimes break; 2561558Srgrimes } 2571558Srgrimes die_you_gravy_sucking_pig_dog(); 2581558Srgrimes} 2591558Srgrimes 2601558Srgrimesstatic jmp_buf alarmbuf; 2611558Srgrimes 26232399Salexstatic char *restricted_environ[] = { 26332399Salex "PATH=" _PATH_STDPATH, 26432399Salex NULL 26532399Salex}; 26632399Salex 2671558Srgrimesvoid 2681558Srgrimestimewarn(timeleft) 2691558Srgrimes int timeleft; 2701558Srgrimes{ 2711558Srgrimes static int first; 2721558Srgrimes static char hostname[MAXHOSTNAMELEN + 1]; 2731558Srgrimes FILE *pf; 2741558Srgrimes char wcmd[MAXPATHLEN + 4]; 27532399Salex extern char **environ; 2761558Srgrimes 2771558Srgrimes if (!first++) 2781558Srgrimes (void)gethostname(hostname, sizeof(hostname)); 2791558Srgrimes 2801558Srgrimes /* undoc -n option to wall suppresses normal wall banner */ 2811558Srgrimes (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 28232399Salex environ = restricted_environ; 2831558Srgrimes if (!(pf = popen(wcmd, "w"))) { 2841558Srgrimes syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 2851558Srgrimes return; 2861558Srgrimes } 2871558Srgrimes 2881558Srgrimes (void)fprintf(pf, 2891558Srgrimes "\007*** %sSystem shutdown message from %s@%s ***\007\n", 2901558Srgrimes timeleft ? "": "FINAL ", whom, hostname); 2911558Srgrimes 2921558Srgrimes if (timeleft > 10*60) 2931558Srgrimes (void)fprintf(pf, "System going down at %5.5s\n\n", 2941558Srgrimes ctime(&shuttime) + 11); 2951558Srgrimes else if (timeleft > 59) 2961558Srgrimes (void)fprintf(pf, "System going down in %d minute%s\n\n", 2971558Srgrimes timeleft / 60, (timeleft > 60) ? "s" : ""); 2981558Srgrimes else if (timeleft) 2991558Srgrimes (void)fprintf(pf, "System going down in 30 seconds\n\n"); 3001558Srgrimes else 3011558Srgrimes (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 3021558Srgrimes 3031558Srgrimes if (mbuflen) 3041558Srgrimes (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 3051558Srgrimes 3061558Srgrimes /* 3071558Srgrimes * play some games, just in case wall doesn't come back 30838036Scharnier * probably unnecessary, given that wall is careful. 3091558Srgrimes */ 3101558Srgrimes if (!setjmp(alarmbuf)) { 3111558Srgrimes (void)signal(SIGALRM, timeout); 3121558Srgrimes (void)alarm((u_int)30); 3131558Srgrimes (void)pclose(pf); 3141558Srgrimes (void)alarm((u_int)0); 3151558Srgrimes (void)signal(SIGALRM, SIG_DFL); 3161558Srgrimes } 3171558Srgrimes} 3181558Srgrimes 3191558Srgrimesvoid 3201558Srgrimestimeout(signo) 3211558Srgrimes int signo; 3221558Srgrimes{ 3231558Srgrimes longjmp(alarmbuf, 1); 3241558Srgrimes} 3251558Srgrimes 3261558Srgrimesvoid 3271558Srgrimesdie_you_gravy_sucking_pig_dog() 3281558Srgrimes{ 32932399Salex char *empty_environ[] = { NULL }; 3301558Srgrimes 3311558Srgrimes syslog(LOG_NOTICE, "%s by %s: %s", 33241666Smsmith doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : 33341666Smsmith "shutdown", whom, mbuf); 3341558Srgrimes (void)sleep(2); 3351558Srgrimes 3361558Srgrimes (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 3371558Srgrimes if (killflg) { 3381558Srgrimes (void)printf("\rbut you'll have to do it yourself\r\n"); 3394844Sats exit(0); 3401558Srgrimes } 3411558Srgrimes#ifdef DEBUG 3421558Srgrimes if (doreboot) 3431558Srgrimes (void)printf("reboot"); 3441558Srgrimes else if (dohalt) 3451558Srgrimes (void)printf("halt"); 34641666Smsmith else if (dopower) 34741666Smsmith (void)printf("power-down"); 3481558Srgrimes if (nosync) 3491558Srgrimes (void)printf(" no sync"); 3501558Srgrimes (void)printf("\nkill -HUP 1\n"); 3511558Srgrimes#else 3521558Srgrimes if (doreboot) { 35332399Salex execle(_PATH_REBOOT, "reboot", "-l", nosync, 35432399Salex (char *)NULL, empty_environ); 3551558Srgrimes syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 35632399Salex warn(_PATH_REBOOT); 3571558Srgrimes } 3581558Srgrimes else if (dohalt) { 35932399Salex execle(_PATH_HALT, "halt", "-l", nosync, 36032399Salex (char *)NULL, empty_environ); 3611558Srgrimes syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 36232399Salex warn(_PATH_HALT); 3631558Srgrimes } 36441666Smsmith else if (dopower) { 36541666Smsmith execle(_PATH_HALT, "halt", "-l", "-p", nosync, 36641666Smsmith (char *)NULL, empty_environ); 36741666Smsmith syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 36841666Smsmith warn(_PATH_HALT); 36941666Smsmith } 3701558Srgrimes (void)kill(1, SIGTERM); /* to single user */ 3711558Srgrimes#endif 3721558Srgrimes finish(0); 3731558Srgrimes} 3741558Srgrimes 3751558Srgrimes#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 3761558Srgrimes 3771558Srgrimesvoid 3781558Srgrimesgetoffset(timearg) 3791558Srgrimes register char *timearg; 3801558Srgrimes{ 3811558Srgrimes register struct tm *lt; 3821558Srgrimes register char *p; 3831558Srgrimes time_t now; 38432328Salex int this_year; 3851558Srgrimes 3861558Srgrimes if (!strcasecmp(timearg, "now")) { /* now */ 3871558Srgrimes offset = 0; 3881558Srgrimes return; 3891558Srgrimes } 3901558Srgrimes 3911558Srgrimes (void)time(&now); 3921558Srgrimes if (*timearg == '+') { /* +minutes */ 3931558Srgrimes if (!isdigit(*++timearg)) 3941558Srgrimes badtime(); 3951558Srgrimes offset = atoi(timearg) * 60; 3961558Srgrimes shuttime = now + offset; 3971558Srgrimes return; 3981558Srgrimes } 3991558Srgrimes 4001558Srgrimes /* handle hh:mm by getting rid of the colon */ 4011558Srgrimes for (p = timearg; *p; ++p) 4021558Srgrimes if (!isascii(*p) || !isdigit(*p)) 4031558Srgrimes if (*p == ':' && strlen(p) == 3) { 4041558Srgrimes p[0] = p[1]; 4051558Srgrimes p[1] = p[2]; 4061558Srgrimes p[2] = '\0'; 4071558Srgrimes } 4081558Srgrimes else 4091558Srgrimes badtime(); 4101558Srgrimes 4111558Srgrimes unsetenv("TZ"); /* OUR timezone */ 4121558Srgrimes lt = localtime(&now); /* current time val */ 4131558Srgrimes 4141558Srgrimes switch(strlen(timearg)) { 4151558Srgrimes case 10: 41632328Salex this_year = lt->tm_year; 4171558Srgrimes lt->tm_year = ATOI2(timearg); 41832328Salex /* 41932328Salex * check if the specified year is in the next century. 42032328Salex * allow for one year of user error as many people will 42132328Salex * enter n - 1 at the start of year n. 42232328Salex */ 42332328Salex if (lt->tm_year < (this_year % 100) - 1) 42432328Salex lt->tm_year += 100; 42532329Salex /* adjust for the year 2000 and beyond */ 42632328Salex lt->tm_year += (this_year - (this_year % 100)); 4271558Srgrimes /* FALLTHROUGH */ 4281558Srgrimes case 8: 4291558Srgrimes lt->tm_mon = ATOI2(timearg); 4301558Srgrimes if (--lt->tm_mon < 0 || lt->tm_mon > 11) 4311558Srgrimes badtime(); 4321558Srgrimes /* FALLTHROUGH */ 4331558Srgrimes case 6: 4341558Srgrimes lt->tm_mday = ATOI2(timearg); 4351558Srgrimes if (lt->tm_mday < 1 || lt->tm_mday > 31) 4361558Srgrimes badtime(); 4371558Srgrimes /* FALLTHROUGH */ 4381558Srgrimes case 4: 4391558Srgrimes lt->tm_hour = ATOI2(timearg); 4401558Srgrimes if (lt->tm_hour < 0 || lt->tm_hour > 23) 4411558Srgrimes badtime(); 4421558Srgrimes lt->tm_min = ATOI2(timearg); 4431558Srgrimes if (lt->tm_min < 0 || lt->tm_min > 59) 4441558Srgrimes badtime(); 4451558Srgrimes lt->tm_sec = 0; 4461558Srgrimes if ((shuttime = mktime(lt)) == -1) 4471558Srgrimes badtime(); 44826737Scharnier if ((offset = shuttime - now) < 0) 44926737Scharnier errx(1, "that time is already past."); 4501558Srgrimes break; 4511558Srgrimes default: 4521558Srgrimes badtime(); 4531558Srgrimes } 4541558Srgrimes} 4551558Srgrimes 4561558Srgrimes#define NOMSG "\n\nNO LOGINS: System going down at " 4571558Srgrimesvoid 4581558Srgrimesnolog() 4591558Srgrimes{ 4601558Srgrimes int logfd; 4611558Srgrimes char *ct; 4621558Srgrimes 4631558Srgrimes (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 4641558Srgrimes (void)signal(SIGINT, finish); 4651558Srgrimes (void)signal(SIGHUP, finish); 4661558Srgrimes (void)signal(SIGQUIT, finish); 4671558Srgrimes (void)signal(SIGTERM, finish); 4681558Srgrimes if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 4691558Srgrimes 0664)) >= 0) { 4701558Srgrimes (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 4711558Srgrimes ct = ctime(&shuttime); 4721558Srgrimes (void)write(logfd, ct + 11, 5); 4731558Srgrimes (void)write(logfd, "\n\n", 2); 4741558Srgrimes (void)write(logfd, mbuf, strlen(mbuf)); 4751558Srgrimes (void)close(logfd); 4761558Srgrimes } 4771558Srgrimes} 4781558Srgrimes 4791558Srgrimesvoid 4801558Srgrimesfinish(signo) 4811558Srgrimes int signo; 4821558Srgrimes{ 48341684Sbde if (!killflg) 48441684Sbde (void)unlink(_PATH_NOLOGIN); 4851558Srgrimes exit(0); 4861558Srgrimes} 4871558Srgrimes 4881558Srgrimesvoid 4891558Srgrimesbadtime() 4901558Srgrimes{ 49138036Scharnier errx(1, "bad time format"); 4921558Srgrimes} 4931558Srgrimes 4941558Srgrimesvoid 4951558Srgrimesusage() 4961558Srgrimes{ 49741683Sbde fprintf(stderr, 49841683Sbde "usage: shutdown [-] [-hknpr] time [warning-message ...]\n"); 4991558Srgrimes exit(1); 5001558Srgrimes} 501