shutdown.c revision 21931
1258945Sroberto/* 2280849Scy * Copyright (c) 1988, 1990, 1993 3258945Sroberto * The Regents of the University of California. All rights reserved. 4258945Sroberto * 5258945Sroberto * Redistribution and use in source and binary forms, with or without 6258945Sroberto * modification, are permitted provided that the following conditions 7258945Sroberto * are met: 8258945Sroberto * 1. Redistributions of source code must retain the above copyright 9258945Sroberto * notice, this list of conditions and the following disclaimer. 10258945Sroberto * 2. Redistributions in binary form must reproduce the above copyright 11258945Sroberto * notice, this list of conditions and the following disclaimer in the 12258945Sroberto * documentation and/or other materials provided with the distribution. 13258945Sroberto * 3. All advertising materials mentioning features or use of this software 14258945Sroberto * must display the following acknowledgement: 15258945Sroberto * This product includes software developed by the University of 16258945Sroberto * California, Berkeley and its contributors. 17258945Sroberto * 4. Neither the name of the University nor the names of its contributors 18280849Scy * may be used to endorse or promote products derived from this software 19258945Sroberto * without specific prior written permission. 20258945Sroberto * 21258945Sroberto * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22258945Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23258945Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24258945Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25258945Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26258945Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27258945Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28258945Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29258945Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30258945Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31258945Sroberto * SUCH DAMAGE. 32258945Sroberto */ 33258945Sroberto 34280849Scy#ifndef lint 35258945Srobertostatic char copyright[] = 36258945Sroberto"@(#) Copyright (c) 1988, 1990, 1993\n\ 37258945Sroberto The Regents of the University of California. All rights reserved.\n"; 38258945Sroberto#endif /* not lint */ 39258945Sroberto 40258945Sroberto#ifndef lint 41258945Srobertostatic char sccsid[] = "@(#)shutdown.c 8.2 (Berkeley) 2/16/94"; 42258945Sroberto#endif /* not lint */ 43258945Sroberto 44258945Sroberto#include <sys/param.h> 45258945Sroberto#include <sys/time.h> 46258945Sroberto#include <sys/resource.h> 47258945Sroberto#include <sys/syslog.h> 48258945Sroberto 49258945Sroberto#include <ctype.h> 50258945Sroberto#include <fcntl.h> 51258945Sroberto#include <pwd.h> 52258945Sroberto#include <setjmp.h> 53258945Sroberto#include <signal.h> 54258945Sroberto#include <stdio.h> 55258945Sroberto#include <stdlib.h> 56258945Sroberto#include <string.h> 57258945Sroberto#include <unistd.h> 58258945Sroberto 59258945Sroberto#include "pathnames.h" 60258945Sroberto 61258945Sroberto#ifdef DEBUG 62258945Sroberto#undef _PATH_NOLOGIN 63258945Sroberto#define _PATH_NOLOGIN "./nologin" 64258945Sroberto#endif 65258945Sroberto 66258945Sroberto#define H *60*60 67258945Sroberto#define M *60 68258945Sroberto#define S *1 69258945Sroberto#define NOLOG_TIME 5*60 70258945Srobertostruct interval { 71258945Sroberto int timeleft, timetowait; 72258945Sroberto} tlist[] = { 73280849Scy 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, 74280849Scy 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, 75280849Scy 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, 76280849Scy 0, 0, 77258945Sroberto}; 78258945Sroberto#undef H 79258945Sroberto#undef M 80258945Sroberto#undef S 81258945Sroberto 82258945Srobertostatic time_t offset, shuttime; 83258945Srobertostatic int dohalt, doreboot, killflg, mbuflen; 84258945Srobertostatic char *nosync, *whom, mbuf[BUFSIZ]; 85258945Sroberto 86258945Srobertovoid badtime __P((void)); 87258945Srobertovoid die_you_gravy_sucking_pig_dog __P((void)); 88258945Srobertovoid finish __P((int)); 89258945Srobertovoid getoffset __P((char *)); 90258945Srobertovoid loop __P((void)); 91258945Srobertovoid nolog __P((void)); 92258945Srobertovoid timeout __P((int)); 93258945Srobertovoid timewarn __P((int)); 94258945Srobertovoid usage __P((void)); 95258945Sroberto 96258945Srobertoint 97258945Srobertomain(argc, argv) 98258945Sroberto int argc; 99258945Sroberto char *argv[]; 100258945Sroberto{ 101258945Sroberto extern int optind; 102258945Sroberto register char *p, *endp; 103258945Sroberto struct passwd *pw; 104258945Sroberto int arglen, ch, len, readstdin; 105258945Sroberto 106258945Sroberto#ifndef DEBUG 107258945Sroberto if (geteuid()) { 108258945Sroberto (void)fprintf(stderr, "shutdown: NOT super-user\n"); 109258945Sroberto exit(1); 110258945Sroberto } 111258945Sroberto#endif 112258945Sroberto nosync = NULL; 113258945Sroberto readstdin = 0; 114258945Sroberto while ((ch = getopt(argc, argv, "-hknr")) != EOF) 115258945Sroberto switch (ch) { 116258945Sroberto case '-': 117258945Sroberto readstdin = 1; 118258945Sroberto break; 119258945Sroberto case 'h': 120258945Sroberto dohalt = 1; 121258945Sroberto break; 122258945Sroberto case 'k': 123258945Sroberto killflg = 1; 124258945Sroberto break; 125258945Sroberto case 'n': 126258945Sroberto nosync = "-n"; 127258945Sroberto break; 128258945Sroberto case 'r': 129258945Sroberto doreboot = 1; 130258945Sroberto break; 131258945Sroberto case '?': 132258945Sroberto default: 133258945Sroberto usage(); 134258945Sroberto } 135258945Sroberto argc -= optind; 136258945Sroberto argv += optind; 137258945Sroberto 138258945Sroberto if (argc < 1) 139258945Sroberto usage(); 140258945Sroberto 141258945Sroberto if (doreboot && dohalt) { 142258945Sroberto (void)fprintf(stderr, 143258945Sroberto "shutdown: incompatible switches -h and -r.\n"); 144258945Sroberto usage(); 145258945Sroberto } 146258945Sroberto getoffset(*argv++); 147258945Sroberto 148258945Sroberto if (*argv) { 149258945Sroberto for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 150258945Sroberto arglen = strlen(*argv); 151258945Sroberto if ((len -= arglen) <= 2) 152258945Sroberto break; 153258945Sroberto if (p != mbuf) 154258945Sroberto *p++ = ' '; 155258945Sroberto bcopy(*argv, p, arglen); 156258945Sroberto p += arglen; 157258945Sroberto } 158258945Sroberto *p = '\n'; 159258945Sroberto *++p = '\0'; 160258945Sroberto } 161258945Sroberto 162293893Sglebius if (readstdin) { 163258945Sroberto p = mbuf; 164258945Sroberto endp = mbuf + sizeof(mbuf) - 2; 165258945Sroberto for (;;) { 166258945Sroberto if (!fgets(p, endp - p + 1, stdin)) 167258945Sroberto break; 168258945Sroberto for (; *p && p < endp; ++p); 169258945Sroberto if (p == endp) { 170258945Sroberto *p = '\n'; 171258945Sroberto *++p = '\0'; 172258945Sroberto break; 173258945Sroberto } 174258945Sroberto } 175258945Sroberto } 176258945Sroberto mbuflen = strlen(mbuf); 177258945Sroberto 178258945Sroberto if (offset) 179258945Sroberto (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 180258945Sroberto else 181258945Sroberto (void)printf("Shutdown NOW!\n"); 182258945Sroberto 183258945Sroberto if (!(whom = getlogin())) 184258945Sroberto whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 185258945Sroberto 186258945Sroberto#ifdef DEBUG 187258945Sroberto (void)putc('\n', stdout); 188258945Sroberto#else 189258945Sroberto (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 190280849Scy { 191280849Scy int forkpid; 192280849Scy 193258945Sroberto forkpid = fork(); 194258945Sroberto if (forkpid == -1) { 195258945Sroberto perror("shutdown: fork"); 196258945Sroberto exit(1); 197258945Sroberto } 198258945Sroberto if (forkpid) { 199258945Sroberto (void)printf("shutdown: [pid %d]\n", forkpid); 200258945Sroberto exit(0); 201258945Sroberto } 202258945Sroberto } 203258945Sroberto#endif 204258945Sroberto openlog("shutdown", LOG_CONS, LOG_AUTH); 205280849Scy loop(); 206258945Sroberto /* NOTREACHED */ 207258945Sroberto} 208280849Scy 209258945Srobertovoid 210258945Srobertoloop() 211258945Sroberto{ 212258945Sroberto struct interval *tp; 213258945Sroberto u_int sltime; 214258945Sroberto int logged; 215258945Sroberto 216258945Sroberto if (offset <= NOLOG_TIME) { 217258945Sroberto logged = 1; 218280849Scy nolog(); 219258945Sroberto } 220258945Sroberto else 221258945Sroberto logged = 0; 222258945Sroberto tp = tlist; 223258945Sroberto if (tp->timeleft < offset) 224258945Sroberto (void)sleep((u_int)(offset - tp->timeleft)); 225258945Sroberto else { 226258945Sroberto while (offset < tp->timeleft) 227258945Sroberto ++tp; 228258945Sroberto /* 229258945Sroberto * Warn now, if going to sleep more than a fifth of 230258945Sroberto * the next wait time. 231258945Sroberto */ 232258945Sroberto if (sltime = offset - tp->timeleft) { 233258945Sroberto if (sltime > tp->timetowait / 5) 234258945Sroberto timewarn(offset); 235258945Sroberto (void)sleep(sltime); 236258945Sroberto } 237258945Sroberto } 238258945Sroberto for (;; ++tp) { 239258945Sroberto timewarn(tp->timeleft); 240258945Sroberto if (!logged && tp->timeleft <= NOLOG_TIME) { 241258945Sroberto logged = 1; 242258945Sroberto nolog(); 243258945Sroberto } 244258945Sroberto (void)sleep((u_int)tp->timetowait); 245258945Sroberto if (!tp->timeleft) 246258945Sroberto break; 247258945Sroberto } 248258945Sroberto die_you_gravy_sucking_pig_dog(); 249258945Sroberto} 250258945Sroberto 251280849Scystatic jmp_buf alarmbuf; 252258945Sroberto 253258945Srobertovoid 254258945Srobertotimewarn(timeleft) 255258945Sroberto int timeleft; 256258945Sroberto{ 257258945Sroberto static int first; 258258945Sroberto static char hostname[MAXHOSTNAMELEN + 1]; 259258945Sroberto FILE *pf; 260258945Sroberto char wcmd[MAXPATHLEN + 4]; 261258945Sroberto 262258945Sroberto if (!first++) 263258945Sroberto (void)gethostname(hostname, sizeof(hostname)); 264258945Sroberto 265258945Sroberto /* undoc -n option to wall suppresses normal wall banner */ 266258945Sroberto (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 267258945Sroberto if (!(pf = popen(wcmd, "w"))) { 268258945Sroberto syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 269258945Sroberto return; 270258945Sroberto } 271258945Sroberto 272258945Sroberto (void)fprintf(pf, 273258945Sroberto "\007*** %sSystem shutdown message from %s@%s ***\007\n", 274258945Sroberto timeleft ? "": "FINAL ", whom, hostname); 275258945Sroberto 276258945Sroberto if (timeleft > 10*60) 277258945Sroberto (void)fprintf(pf, "System going down at %5.5s\n\n", 278258945Sroberto ctime(&shuttime) + 11); 279258945Sroberto else if (timeleft > 59) 280258945Sroberto (void)fprintf(pf, "System going down in %d minute%s\n\n", 281258945Sroberto timeleft / 60, (timeleft > 60) ? "s" : ""); 282258945Sroberto else if (timeleft) 283258945Sroberto (void)fprintf(pf, "System going down in 30 seconds\n\n"); 284258945Sroberto else 285258945Sroberto (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 286258945Sroberto 287258945Sroberto if (mbuflen) 288258945Sroberto (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 289258945Sroberto 290258945Sroberto /* 291258945Sroberto * play some games, just in case wall doesn't come back 292258945Sroberto * probably unecessary, given that wall is careful. 293258945Sroberto */ 294258945Sroberto if (!setjmp(alarmbuf)) { 295258945Sroberto (void)signal(SIGALRM, timeout); 296258945Sroberto (void)alarm((u_int)30); 297258945Sroberto (void)pclose(pf); 298258945Sroberto (void)alarm((u_int)0); 299258945Sroberto (void)signal(SIGALRM, SIG_DFL); 300258945Sroberto } 301258945Sroberto} 302258945Sroberto 303258945Srobertovoid 304258945Srobertotimeout(signo) 305280849Scy int signo; 306280849Scy{ 307258945Sroberto longjmp(alarmbuf, 1); 308258945Sroberto} 309258945Sroberto 310280849Scyvoid 311258945Srobertodie_you_gravy_sucking_pig_dog() 312258945Sroberto{ 313258945Sroberto 314258945Sroberto syslog(LOG_NOTICE, "%s by %s: %s", 315258945Sroberto doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 316280849Scy (void)sleep(2); 317258945Sroberto 318258945Sroberto (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 319258945Sroberto if (killflg) { 320258945Sroberto (void)printf("\rbut you'll have to do it yourself\r\n"); 321258945Sroberto exit(0); 322258945Sroberto } 323258945Sroberto#ifdef DEBUG 324258945Sroberto if (doreboot) 325258945Sroberto (void)printf("reboot"); 326258945Sroberto else if (dohalt) 327258945Sroberto (void)printf("halt"); 328258945Sroberto if (nosync) 329258945Sroberto (void)printf(" no sync"); 330258945Sroberto (void)printf("\nkill -HUP 1\n"); 331258945Sroberto#else 332258945Sroberto if (doreboot) { 333258945Sroberto execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); 334258945Sroberto syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 335258945Sroberto perror("shutdown"); 336258945Sroberto } 337258945Sroberto else if (dohalt) { 338258945Sroberto execle(_PATH_HALT, "halt", "-l", nosync, 0); 339258945Sroberto syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 340258945Sroberto perror("shutdown"); 341258945Sroberto } 342258945Sroberto (void)kill(1, SIGTERM); /* to single user */ 343258945Sroberto#endif 344258945Sroberto finish(0); 345258945Sroberto} 346258945Sroberto 347258945Sroberto#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 348258945Sroberto 349258945Srobertovoid 350258945Srobertogetoffset(timearg) 351258945Sroberto register char *timearg; 352258945Sroberto{ 353258945Sroberto register struct tm *lt; 354258945Sroberto register char *p; 355258945Sroberto time_t now; 356258945Sroberto 357258945Sroberto if (!strcasecmp(timearg, "now")) { /* now */ 358258945Sroberto offset = 0; 359258945Sroberto return; 360258945Sroberto } 361258945Sroberto 362258945Sroberto (void)time(&now); 363258945Sroberto if (*timearg == '+') { /* +minutes */ 364258945Sroberto if (!isdigit(*++timearg)) 365258945Sroberto badtime(); 366258945Sroberto offset = atoi(timearg) * 60; 367258945Sroberto shuttime = now + offset; 368258945Sroberto return; 369258945Sroberto } 370258945Sroberto 371258945Sroberto /* handle hh:mm by getting rid of the colon */ 372258945Sroberto for (p = timearg; *p; ++p) 373258945Sroberto if (!isascii(*p) || !isdigit(*p)) 374258945Sroberto if (*p == ':' && strlen(p) == 3) { 375258945Sroberto p[0] = p[1]; 376258945Sroberto p[1] = p[2]; 377258945Sroberto p[2] = '\0'; 378258945Sroberto } 379258945Sroberto else 380258945Sroberto badtime(); 381258945Sroberto 382258945Sroberto unsetenv("TZ"); /* OUR timezone */ 383258945Sroberto lt = localtime(&now); /* current time val */ 384258945Sroberto 385258945Sroberto switch(strlen(timearg)) { 386258945Sroberto case 10: 387258945Sroberto lt->tm_year = ATOI2(timearg); 388258945Sroberto /* FALLTHROUGH */ 389258945Sroberto case 8: 390258945Sroberto lt->tm_mon = ATOI2(timearg); 391258945Sroberto if (--lt->tm_mon < 0 || lt->tm_mon > 11) 392258945Sroberto badtime(); 393258945Sroberto /* FALLTHROUGH */ 394258945Sroberto case 6: 395258945Sroberto lt->tm_mday = ATOI2(timearg); 396258945Sroberto if (lt->tm_mday < 1 || lt->tm_mday > 31) 397258945Sroberto badtime(); 398258945Sroberto /* FALLTHROUGH */ 399258945Sroberto case 4: 400258945Sroberto lt->tm_hour = ATOI2(timearg); 401258945Sroberto if (lt->tm_hour < 0 || lt->tm_hour > 23) 402258945Sroberto badtime(); 403258945Sroberto lt->tm_min = ATOI2(timearg); 404258945Sroberto if (lt->tm_min < 0 || lt->tm_min > 59) 405258945Sroberto badtime(); 406258945Sroberto lt->tm_sec = 0; 407258945Sroberto if ((shuttime = mktime(lt)) == -1) 408258945Sroberto badtime(); 409258945Sroberto if ((offset = shuttime - now) < 0) { 410258945Sroberto (void)fprintf(stderr, 411258945Sroberto "shutdown: that time is already past.\n"); 412258945Sroberto exit(1); 413258945Sroberto } 414258945Sroberto break; 415258945Sroberto default: 416258945Sroberto badtime(); 417258945Sroberto } 418258945Sroberto} 419258945Sroberto 420258945Sroberto#define NOMSG "\n\nNO LOGINS: System going down at " 421258945Srobertovoid 422258945Srobertonolog() 423258945Sroberto{ 424258945Sroberto int logfd; 425258945Sroberto char *ct; 426258945Sroberto 427258945Sroberto (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 428258945Sroberto (void)signal(SIGINT, finish); 429258945Sroberto (void)signal(SIGHUP, finish); 430258945Sroberto (void)signal(SIGQUIT, finish); 431258945Sroberto (void)signal(SIGTERM, finish); 432258945Sroberto if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 433258945Sroberto 0664)) >= 0) { 434258945Sroberto (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 435258945Sroberto ct = ctime(&shuttime); 436 (void)write(logfd, ct + 11, 5); 437 (void)write(logfd, "\n\n", 2); 438 (void)write(logfd, mbuf, strlen(mbuf)); 439 (void)close(logfd); 440 } 441} 442 443void 444finish(signo) 445 int signo; 446{ 447 (void)unlink(_PATH_NOLOGIN); 448 exit(0); 449} 450 451void 452badtime() 453{ 454 (void)fprintf(stderr, "shutdown: bad time format.\n"); 455 exit(1); 456} 457 458void 459usage() 460{ 461 fprintf(stderr, "usage: shutdown [-hknr] shutdowntime [ message ]\n"); 462 exit(1); 463} 464