shutdown.c revision 28613
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 * 3328613Sjoerg * $Id: shutdown.c,v 1.7 1997/06/19 14:28:32 charnier 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[] = { 761558Srgrimes 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, 771558Srgrimes 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, 781558Srgrimes 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, 791558Srgrimes 0, 0, 801558Srgrimes}; 811558Srgrimes#undef H 821558Srgrimes#undef M 831558Srgrimes#undef S 841558Srgrimes 851558Srgrimesstatic time_t offset, shuttime; 866443Sdgstatic int dohalt, doreboot, killflg, mbuflen; 871558Srgrimesstatic char *nosync, *whom, mbuf[BUFSIZ]; 881558Srgrimes 891558Srgrimesvoid badtime __P((void)); 901558Srgrimesvoid die_you_gravy_sucking_pig_dog __P((void)); 911558Srgrimesvoid finish __P((int)); 921558Srgrimesvoid getoffset __P((char *)); 931558Srgrimesvoid loop __P((void)); 941558Srgrimesvoid nolog __P((void)); 951558Srgrimesvoid timeout __P((int)); 961558Srgrimesvoid timewarn __P((int)); 971558Srgrimesvoid usage __P((void)); 981558Srgrimes 991558Srgrimesint 1001558Srgrimesmain(argc, argv) 1011558Srgrimes int argc; 1021558Srgrimes char *argv[]; 1031558Srgrimes{ 1041558Srgrimes extern int optind; 1051558Srgrimes register char *p, *endp; 1061558Srgrimes struct passwd *pw; 1071558Srgrimes int arglen, ch, len, readstdin; 1081558Srgrimes 1091558Srgrimes#ifndef DEBUG 11026737Scharnier if (geteuid()) 11126737Scharnier errx(1, "NOT super-user"); 1121558Srgrimes#endif 1131558Srgrimes nosync = NULL; 1141558Srgrimes readstdin = 0; 11524359Simp while ((ch = getopt(argc, argv, "-hknr")) != -1) 1161558Srgrimes switch (ch) { 1171558Srgrimes case '-': 1181558Srgrimes readstdin = 1; 1191558Srgrimes break; 1201558Srgrimes case 'h': 1211558Srgrimes dohalt = 1; 1221558Srgrimes break; 1231558Srgrimes case 'k': 1241558Srgrimes killflg = 1; 1251558Srgrimes break; 1261558Srgrimes case 'n': 1271558Srgrimes nosync = "-n"; 1281558Srgrimes break; 1291558Srgrimes case 'r': 1301558Srgrimes doreboot = 1; 1311558Srgrimes break; 1321558Srgrimes case '?': 1331558Srgrimes default: 1341558Srgrimes usage(); 1351558Srgrimes } 1361558Srgrimes argc -= optind; 1371558Srgrimes argv += optind; 1381558Srgrimes 1391558Srgrimes if (argc < 1) 1401558Srgrimes usage(); 1411558Srgrimes 1421558Srgrimes if (doreboot && dohalt) { 14326737Scharnier warnx("incompatible switches -h and -r."); 1441558Srgrimes usage(); 1451558Srgrimes } 1461558Srgrimes getoffset(*argv++); 1471558Srgrimes 1481558Srgrimes if (*argv) { 1491558Srgrimes for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 1501558Srgrimes arglen = strlen(*argv); 1511558Srgrimes if ((len -= arglen) <= 2) 1521558Srgrimes break; 1531558Srgrimes if (p != mbuf) 1541558Srgrimes *p++ = ' '; 1551558Srgrimes bcopy(*argv, p, arglen); 1561558Srgrimes p += arglen; 1571558Srgrimes } 1581558Srgrimes *p = '\n'; 1591558Srgrimes *++p = '\0'; 1601558Srgrimes } 1611558Srgrimes 1621558Srgrimes if (readstdin) { 1631558Srgrimes p = mbuf; 1641558Srgrimes endp = mbuf + sizeof(mbuf) - 2; 1651558Srgrimes for (;;) { 1661558Srgrimes if (!fgets(p, endp - p + 1, stdin)) 1671558Srgrimes break; 1681558Srgrimes for (; *p && p < endp; ++p); 1691558Srgrimes if (p == endp) { 1701558Srgrimes *p = '\n'; 1711558Srgrimes *++p = '\0'; 1721558Srgrimes break; 1731558Srgrimes } 1741558Srgrimes } 1751558Srgrimes } 1761558Srgrimes mbuflen = strlen(mbuf); 1771558Srgrimes 1781558Srgrimes if (offset) 1791558Srgrimes (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 1801558Srgrimes else 1811558Srgrimes (void)printf("Shutdown NOW!\n"); 1821558Srgrimes 1831558Srgrimes if (!(whom = getlogin())) 1841558Srgrimes whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 1851558Srgrimes 1861558Srgrimes#ifdef DEBUG 1871558Srgrimes (void)putc('\n', stdout); 1881558Srgrimes#else 1891558Srgrimes (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 1901558Srgrimes { 1911558Srgrimes int forkpid; 1921558Srgrimes 1931558Srgrimes forkpid = fork(); 19426737Scharnier if (forkpid == -1) 19526737Scharnier err(1, "fork"); 19626737Scharnier if (forkpid) 19726737Scharnier errx(0, "[pid %d]", forkpid); 1981558Srgrimes } 19928613Sjoerg setsid(); 2001558Srgrimes#endif 2011558Srgrimes openlog("shutdown", LOG_CONS, LOG_AUTH); 2021558Srgrimes loop(); 2031558Srgrimes /* NOTREACHED */ 2041558Srgrimes} 2051558Srgrimes 2061558Srgrimesvoid 2071558Srgrimesloop() 2081558Srgrimes{ 2091558Srgrimes struct interval *tp; 2101558Srgrimes u_int sltime; 2111558Srgrimes int logged; 2121558Srgrimes 2131558Srgrimes if (offset <= NOLOG_TIME) { 2141558Srgrimes logged = 1; 2151558Srgrimes nolog(); 2161558Srgrimes } 2171558Srgrimes else 2181558Srgrimes logged = 0; 2191558Srgrimes tp = tlist; 2201558Srgrimes if (tp->timeleft < offset) 2211558Srgrimes (void)sleep((u_int)(offset - tp->timeleft)); 2221558Srgrimes else { 2231558Srgrimes while (offset < tp->timeleft) 2241558Srgrimes ++tp; 2251558Srgrimes /* 2261558Srgrimes * Warn now, if going to sleep more than a fifth of 2271558Srgrimes * the next wait time. 2281558Srgrimes */ 2291558Srgrimes if (sltime = offset - tp->timeleft) { 2301558Srgrimes if (sltime > tp->timetowait / 5) 2311558Srgrimes timewarn(offset); 2321558Srgrimes (void)sleep(sltime); 2331558Srgrimes } 2341558Srgrimes } 2351558Srgrimes for (;; ++tp) { 2361558Srgrimes timewarn(tp->timeleft); 2371558Srgrimes if (!logged && tp->timeleft <= NOLOG_TIME) { 2381558Srgrimes logged = 1; 2391558Srgrimes nolog(); 2401558Srgrimes } 2411558Srgrimes (void)sleep((u_int)tp->timetowait); 2421558Srgrimes if (!tp->timeleft) 2431558Srgrimes break; 2441558Srgrimes } 2451558Srgrimes die_you_gravy_sucking_pig_dog(); 2461558Srgrimes} 2471558Srgrimes 2481558Srgrimesstatic jmp_buf alarmbuf; 2491558Srgrimes 2501558Srgrimesvoid 2511558Srgrimestimewarn(timeleft) 2521558Srgrimes int timeleft; 2531558Srgrimes{ 2541558Srgrimes static int first; 2551558Srgrimes static char hostname[MAXHOSTNAMELEN + 1]; 2561558Srgrimes FILE *pf; 2571558Srgrimes char wcmd[MAXPATHLEN + 4]; 2581558Srgrimes 2591558Srgrimes if (!first++) 2601558Srgrimes (void)gethostname(hostname, sizeof(hostname)); 2611558Srgrimes 2621558Srgrimes /* undoc -n option to wall suppresses normal wall banner */ 2631558Srgrimes (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 2641558Srgrimes if (!(pf = popen(wcmd, "w"))) { 2651558Srgrimes syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 2661558Srgrimes return; 2671558Srgrimes } 2681558Srgrimes 2691558Srgrimes (void)fprintf(pf, 2701558Srgrimes "\007*** %sSystem shutdown message from %s@%s ***\007\n", 2711558Srgrimes timeleft ? "": "FINAL ", whom, hostname); 2721558Srgrimes 2731558Srgrimes if (timeleft > 10*60) 2741558Srgrimes (void)fprintf(pf, "System going down at %5.5s\n\n", 2751558Srgrimes ctime(&shuttime) + 11); 2761558Srgrimes else if (timeleft > 59) 2771558Srgrimes (void)fprintf(pf, "System going down in %d minute%s\n\n", 2781558Srgrimes timeleft / 60, (timeleft > 60) ? "s" : ""); 2791558Srgrimes else if (timeleft) 2801558Srgrimes (void)fprintf(pf, "System going down in 30 seconds\n\n"); 2811558Srgrimes else 2821558Srgrimes (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 2831558Srgrimes 2841558Srgrimes if (mbuflen) 2851558Srgrimes (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 2861558Srgrimes 2871558Srgrimes /* 2881558Srgrimes * play some games, just in case wall doesn't come back 2891558Srgrimes * probably unecessary, given that wall is careful. 2901558Srgrimes */ 2911558Srgrimes if (!setjmp(alarmbuf)) { 2921558Srgrimes (void)signal(SIGALRM, timeout); 2931558Srgrimes (void)alarm((u_int)30); 2941558Srgrimes (void)pclose(pf); 2951558Srgrimes (void)alarm((u_int)0); 2961558Srgrimes (void)signal(SIGALRM, SIG_DFL); 2971558Srgrimes } 2981558Srgrimes} 2991558Srgrimes 3001558Srgrimesvoid 3011558Srgrimestimeout(signo) 3021558Srgrimes int signo; 3031558Srgrimes{ 3041558Srgrimes longjmp(alarmbuf, 1); 3051558Srgrimes} 3061558Srgrimes 3071558Srgrimesvoid 3081558Srgrimesdie_you_gravy_sucking_pig_dog() 3091558Srgrimes{ 3101558Srgrimes 3111558Srgrimes syslog(LOG_NOTICE, "%s by %s: %s", 3121558Srgrimes doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 3131558Srgrimes (void)sleep(2); 3141558Srgrimes 3151558Srgrimes (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 3161558Srgrimes if (killflg) { 3171558Srgrimes (void)printf("\rbut you'll have to do it yourself\r\n"); 3184844Sats exit(0); 3191558Srgrimes } 3201558Srgrimes#ifdef DEBUG 3211558Srgrimes if (doreboot) 3221558Srgrimes (void)printf("reboot"); 3231558Srgrimes else if (dohalt) 3241558Srgrimes (void)printf("halt"); 3251558Srgrimes if (nosync) 3261558Srgrimes (void)printf(" no sync"); 3271558Srgrimes (void)printf("\nkill -HUP 1\n"); 3281558Srgrimes#else 3291558Srgrimes if (doreboot) { 3301558Srgrimes execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); 3311558Srgrimes syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 3321558Srgrimes perror("shutdown"); 3331558Srgrimes } 3341558Srgrimes else if (dohalt) { 3351558Srgrimes execle(_PATH_HALT, "halt", "-l", nosync, 0); 3361558Srgrimes syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 3371558Srgrimes perror("shutdown"); 3381558Srgrimes } 3391558Srgrimes (void)kill(1, SIGTERM); /* to single user */ 3401558Srgrimes#endif 3411558Srgrimes finish(0); 3421558Srgrimes} 3431558Srgrimes 3441558Srgrimes#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 3451558Srgrimes 3461558Srgrimesvoid 3471558Srgrimesgetoffset(timearg) 3481558Srgrimes register char *timearg; 3491558Srgrimes{ 3501558Srgrimes register struct tm *lt; 3511558Srgrimes register char *p; 3521558Srgrimes time_t now; 3531558Srgrimes 3541558Srgrimes if (!strcasecmp(timearg, "now")) { /* now */ 3551558Srgrimes offset = 0; 3561558Srgrimes return; 3571558Srgrimes } 3581558Srgrimes 3591558Srgrimes (void)time(&now); 3601558Srgrimes if (*timearg == '+') { /* +minutes */ 3611558Srgrimes if (!isdigit(*++timearg)) 3621558Srgrimes badtime(); 3631558Srgrimes offset = atoi(timearg) * 60; 3641558Srgrimes shuttime = now + offset; 3651558Srgrimes return; 3661558Srgrimes } 3671558Srgrimes 3681558Srgrimes /* handle hh:mm by getting rid of the colon */ 3691558Srgrimes for (p = timearg; *p; ++p) 3701558Srgrimes if (!isascii(*p) || !isdigit(*p)) 3711558Srgrimes if (*p == ':' && strlen(p) == 3) { 3721558Srgrimes p[0] = p[1]; 3731558Srgrimes p[1] = p[2]; 3741558Srgrimes p[2] = '\0'; 3751558Srgrimes } 3761558Srgrimes else 3771558Srgrimes badtime(); 3781558Srgrimes 3791558Srgrimes unsetenv("TZ"); /* OUR timezone */ 3801558Srgrimes lt = localtime(&now); /* current time val */ 3811558Srgrimes 3821558Srgrimes switch(strlen(timearg)) { 3831558Srgrimes case 10: 3841558Srgrimes lt->tm_year = ATOI2(timearg); 3851558Srgrimes /* FALLTHROUGH */ 3861558Srgrimes case 8: 3871558Srgrimes lt->tm_mon = ATOI2(timearg); 3881558Srgrimes if (--lt->tm_mon < 0 || lt->tm_mon > 11) 3891558Srgrimes badtime(); 3901558Srgrimes /* FALLTHROUGH */ 3911558Srgrimes case 6: 3921558Srgrimes lt->tm_mday = ATOI2(timearg); 3931558Srgrimes if (lt->tm_mday < 1 || lt->tm_mday > 31) 3941558Srgrimes badtime(); 3951558Srgrimes /* FALLTHROUGH */ 3961558Srgrimes case 4: 3971558Srgrimes lt->tm_hour = ATOI2(timearg); 3981558Srgrimes if (lt->tm_hour < 0 || lt->tm_hour > 23) 3991558Srgrimes badtime(); 4001558Srgrimes lt->tm_min = ATOI2(timearg); 4011558Srgrimes if (lt->tm_min < 0 || lt->tm_min > 59) 4021558Srgrimes badtime(); 4031558Srgrimes lt->tm_sec = 0; 4041558Srgrimes if ((shuttime = mktime(lt)) == -1) 4051558Srgrimes badtime(); 40626737Scharnier if ((offset = shuttime - now) < 0) 40726737Scharnier errx(1, "that time is already past."); 4081558Srgrimes break; 4091558Srgrimes default: 4101558Srgrimes badtime(); 4111558Srgrimes } 4121558Srgrimes} 4131558Srgrimes 4141558Srgrimes#define NOMSG "\n\nNO LOGINS: System going down at " 4151558Srgrimesvoid 4161558Srgrimesnolog() 4171558Srgrimes{ 4181558Srgrimes int logfd; 4191558Srgrimes char *ct; 4201558Srgrimes 4211558Srgrimes (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 4221558Srgrimes (void)signal(SIGINT, finish); 4231558Srgrimes (void)signal(SIGHUP, finish); 4241558Srgrimes (void)signal(SIGQUIT, finish); 4251558Srgrimes (void)signal(SIGTERM, finish); 4261558Srgrimes if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 4271558Srgrimes 0664)) >= 0) { 4281558Srgrimes (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 4291558Srgrimes ct = ctime(&shuttime); 4301558Srgrimes (void)write(logfd, ct + 11, 5); 4311558Srgrimes (void)write(logfd, "\n\n", 2); 4321558Srgrimes (void)write(logfd, mbuf, strlen(mbuf)); 4331558Srgrimes (void)close(logfd); 4341558Srgrimes } 4351558Srgrimes} 4361558Srgrimes 4371558Srgrimesvoid 4381558Srgrimesfinish(signo) 4391558Srgrimes int signo; 4401558Srgrimes{ 4411558Srgrimes (void)unlink(_PATH_NOLOGIN); 4421558Srgrimes exit(0); 4431558Srgrimes} 4441558Srgrimes 4451558Srgrimesvoid 4461558Srgrimesbadtime() 4471558Srgrimes{ 44826737Scharnier errx(1, "bad time format."); 4491558Srgrimes} 4501558Srgrimes 4511558Srgrimesvoid 4521558Srgrimesusage() 4531558Srgrimes{ 4546443Sdg fprintf(stderr, "usage: shutdown [-hknr] shutdowntime [ message ]\n"); 4551558Srgrimes exit(1); 4561558Srgrimes} 457