shutdown.c revision 140797
1160996Ssam/* 2160996Ssam * Copyright (c) 1988, 1990, 1993 3160996Ssam * The Regents of the University of California. All rights reserved. 4160996Ssam * 5160996Ssam * Redistribution and use in source and binary forms, with or without 6160996Ssam * modification, are permitted provided that the following conditions 7160996Ssam * are met: 8160996Ssam * 1. Redistributions of source code must retain the above copyright 9160996Ssam * notice, this list of conditions and the following disclaimer. 10160996Ssam * 2. Redistributions in binary form must reproduce the above copyright 11160996Ssam * notice, this list of conditions and the following disclaimer in the 12160996Ssam * documentation and/or other materials provided with the distribution. 13160996Ssam * 4. Neither the name of the University nor the names of its contributors 14160996Ssam * may be used to endorse or promote products derived from this software 15160996Ssam * without specific prior written permission. 16160996Ssam * 17160996Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18160996Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19160996Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20160996Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21160996Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22160996Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23160996Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24160996Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25160996Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26160996Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27160996Ssam * SUCH DAMAGE. 28160996Ssam */ 29160996Ssam 30160996Ssam#if 0 31160996Ssam#ifndef lint 32160996Ssamstatic const char copyright[] = 33160996Ssam"@(#) Copyright (c) 1988, 1990, 1993\n\ 34160996Ssam The Regents of the University of California. All rights reserved.\n"; 35160996Ssam#endif /* not lint */ 36160996Ssam 37160996Ssam#ifndef lint 38160996Ssamstatic char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; 39160996Ssam#endif /* not lint */ 40160996Ssam#endif 41160996Ssam#include <sys/cdefs.h> 42160996Ssam__FBSDID("$FreeBSD: head/sbin/shutdown/shutdown.c 140797 2005-01-25 08:40:51Z delphij $"); 43160996Ssam 44160996Ssam#include <sys/param.h> 45160996Ssam#include <sys/time.h> 46160996Ssam#include <sys/resource.h> 47160996Ssam#include <sys/syslog.h> 48160996Ssam 49160996Ssam#include <ctype.h> 50160996Ssam#include <err.h> 51160996Ssam#include <fcntl.h> 52160996Ssam#include <paths.h> 53160996Ssam#include <pwd.h> 54160996Ssam#include <setjmp.h> 55160996Ssam#include <signal.h> 56160996Ssam#include <stdio.h> 57160996Ssam#include <stdlib.h> 58160996Ssam#include <string.h> 59160996Ssam#include <unistd.h> 60160996Ssam 61160996Ssam#ifdef DEBUG 62160996Ssam#undef _PATH_NOLOGIN 63160996Ssam#define _PATH_NOLOGIN "./nologin" 64160996Ssam#endif 65160996Ssam 66160996Ssam#define H *60*60 67160996Ssam#define M *60 68160996Ssam#define S *1 69160996Ssam#define NOLOG_TIME 5*60 70160996Ssamstruct interval { 71160996Ssam int timeleft, timetowait; 72160996Ssam} tlist[] = { 73160996Ssam { 10 H, 5 H }, 74160996Ssam { 5 H, 3 H }, 75160996Ssam { 2 H, 1 H }, 76160996Ssam { 1 H, 30 M }, 77160996Ssam { 30 M, 10 M }, 78160996Ssam { 20 M, 10 M }, 79160996Ssam { 10 M, 5 M }, 80160996Ssam { 5 M, 3 M }, 81160996Ssam { 2 M, 1 M }, 82160996Ssam { 1 M, 30 S }, 83160996Ssam { 30 S, 30 S }, 84160996Ssam { 0 , 0 } 85160996Ssam}; 86160996Ssam#undef H 87160996Ssam#undef M 88160996Ssam#undef S 89160996Ssam 90160996Ssamstatic time_t offset, shuttime; 91160996Ssamstatic int dohalt, dopower, doreboot, killflg, mbuflen, oflag; 92160996Ssamstatic char mbuf[BUFSIZ]; 93160996Ssamstatic const char *nosync, *whom; 94160996Ssam 95160996Ssamvoid badtime(void); 96160996Ssamvoid die_you_gravy_sucking_pig_dog(void); 97160996Ssamvoid finish(int); 98160996Ssamvoid getoffset(char *); 99160996Ssamvoid loop(void); 100160996Ssamvoid nolog(void); 101160996Ssamvoid timeout(int); 102160996Ssamvoid timewarn(int); 103160996Ssamvoid usage(const char *); 104160996Ssam 105160996Ssamextern const char **environ; 106160996Ssam 107160996Ssamint 108160996Ssammain(int argc, char **argv) 109160996Ssam{ 110160996Ssam char *p, *endp; 111160996Ssam struct passwd *pw; 112160996Ssam int arglen, ch, len, readstdin; 113160996Ssam 114160996Ssam#ifndef DEBUG 115160996Ssam if (geteuid()) 116160996Ssam errx(1, "NOT super-user"); 117160996Ssam#endif 118160996Ssam nosync = NULL; 119160996Ssam readstdin = 0; 120160996Ssam while ((ch = getopt(argc, argv, "-hknopr")) != -1) 121160996Ssam switch (ch) { 122160996Ssam case '-': 123160996Ssam readstdin = 1; 124160996Ssam break; 125160996Ssam case 'h': 126160996Ssam dohalt = 1; 127160996Ssam break; 128160996Ssam case 'k': 129160996Ssam killflg = 1; 130160996Ssam break; 131160996Ssam case 'n': 132160996Ssam nosync = "-n"; 133160996Ssam break; 134160996Ssam case 'o': 135160996Ssam oflag = 1; 136160996Ssam break; 137160996Ssam case 'p': 138160996Ssam dopower = 1; 139160996Ssam break; 140160996Ssam case 'r': 141160996Ssam doreboot = 1; 142160996Ssam break; 143160996Ssam case '?': 144160996Ssam default: 145160996Ssam usage((char *)NULL); 146160996Ssam } 147160996Ssam argc -= optind; 148160996Ssam argv += optind; 149160996Ssam 150160996Ssam if (argc < 1) 151160996Ssam usage((char *)NULL); 152160996Ssam 153160996Ssam if (killflg + doreboot + dohalt + dopower > 1) 154160996Ssam usage("incompatible switches -h, -k, -p and -r"); 155160996Ssam 156160996Ssam if (oflag && !(dohalt || dopower || doreboot)) 157160996Ssam usage("-o requires -h, -p or -r"); 158160996Ssam 159160996Ssam if (nosync != NULL && !oflag) 160160996Ssam usage("-n requires -o"); 161160996Ssam 162160996Ssam getoffset(*argv++); 163160996Ssam 164160996Ssam if (*argv) { 165160996Ssam for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 166160996Ssam arglen = strlen(*argv); 167160996Ssam if ((len -= arglen) <= 2) 168160996Ssam break; 169160996Ssam if (p != mbuf) 170160996Ssam *p++ = ' '; 171160996Ssam memmove(p, *argv, arglen); 172160996Ssam p += arglen; 173160996Ssam } 174160996Ssam *p = '\n'; 175160996Ssam *++p = '\0'; 176160996Ssam } 177160996Ssam 178160996Ssam if (readstdin) { 179160996Ssam p = mbuf; 180160996Ssam endp = mbuf + sizeof(mbuf) - 2; 181160996Ssam for (;;) { 182160996Ssam if (!fgets(p, endp - p + 1, stdin)) 183160996Ssam break; 184160996Ssam for (; *p && p < endp; ++p); 185160996Ssam if (p == endp) { 186160996Ssam *p = '\n'; 187160996Ssam *++p = '\0'; 188160996Ssam break; 189160996Ssam } 190160996Ssam } 191160996Ssam } 192160996Ssam mbuflen = strlen(mbuf); 193160996Ssam 194160996Ssam if (offset) 195160996Ssam (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 196160996Ssam else 197160996Ssam (void)printf("Shutdown NOW!\n"); 198160996Ssam 199160996Ssam if (!(whom = getlogin())) 200160996Ssam whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 201160996Ssam 202160996Ssam#ifdef DEBUG 203160996Ssam (void)putc('\n', stdout); 204160996Ssam#else 205160996Ssam (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 206160996Ssam { 207160996Ssam int forkpid; 208160996Ssam 209160996Ssam forkpid = fork(); 210160996Ssam if (forkpid == -1) 211160996Ssam err(1, "fork"); 212160996Ssam if (forkpid) 213160996Ssam errx(0, "[pid %d]", forkpid); 214160996Ssam } 215160996Ssam setsid(); 216160996Ssam#endif 217160996Ssam openlog("shutdown", LOG_CONS, LOG_AUTH); 218160996Ssam loop(); 219160996Ssam return(0); 220160996Ssam} 221160996Ssam 222160996Ssamvoid 223160996Ssamloop() 224160996Ssam{ 225160996Ssam struct interval *tp; 226160996Ssam u_int sltime; 227160996Ssam int logged; 228160996Ssam 229160996Ssam if (offset <= NOLOG_TIME) { 230160996Ssam logged = 1; 231160996Ssam nolog(); 232160996Ssam } 233160996Ssam else 234160996Ssam logged = 0; 235160996Ssam tp = tlist; 236160996Ssam if (tp->timeleft < offset) 237160996Ssam (void)sleep((u_int)(offset - tp->timeleft)); 238160996Ssam else { 239160996Ssam while (tp->timeleft && offset < tp->timeleft) 240160996Ssam ++tp; 241160996Ssam /* 242160996Ssam * Warn now, if going to sleep more than a fifth of 243160996Ssam * the next wait time. 244160996Ssam */ 245160996Ssam if ((sltime = offset - tp->timeleft)) { 246160996Ssam if (sltime > (u_int)(tp->timetowait / 5)) 247160996Ssam timewarn(offset); 248160996Ssam (void)sleep(sltime); 249160996Ssam } 250160996Ssam } 251160996Ssam for (;; ++tp) { 252160996Ssam timewarn(tp->timeleft); 253160996Ssam if (!logged && tp->timeleft <= NOLOG_TIME) { 254160996Ssam logged = 1; 255160996Ssam nolog(); 256160996Ssam } 257160996Ssam (void)sleep((u_int)tp->timetowait); 258160996Ssam if (!tp->timeleft) 259160996Ssam break; 260160996Ssam } 261160996Ssam die_you_gravy_sucking_pig_dog(); 262160996Ssam} 263160996Ssam 264160996Ssamstatic jmp_buf alarmbuf; 265160996Ssam 266160996Ssamstatic const char *restricted_environ[] = { 267160996Ssam "PATH=" _PATH_STDPATH, 268160996Ssam NULL 269160996Ssam}; 270160996Ssam 271160996Ssamvoid 272160996Ssamtimewarn(int timeleft) 273160996Ssam{ 274160996Ssam static int first; 275160996Ssam static char hostname[MAXHOSTNAMELEN + 1]; 276160996Ssam FILE *pf; 277160996Ssam char wcmd[MAXPATHLEN + 4]; 278160996Ssam 279160996Ssam if (!first++) 280160996Ssam (void)gethostname(hostname, sizeof(hostname)); 281160996Ssam 282160996Ssam /* undoc -n option to wall suppresses normal wall banner */ 283160996Ssam (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 284160996Ssam environ = restricted_environ; 285160996Ssam if (!(pf = popen(wcmd, "w"))) { 286160996Ssam syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 287160996Ssam return; 288160996Ssam } 289160996Ssam 290160996Ssam (void)fprintf(pf, 291160996Ssam "\007*** %sSystem shutdown message from %s@%s ***\007\n", 292160996Ssam timeleft ? "": "FINAL ", whom, hostname); 293160996Ssam 294160996Ssam if (timeleft > 10*60) 295160996Ssam (void)fprintf(pf, "System going down at %5.5s\n\n", 296160996Ssam ctime(&shuttime) + 11); 297160996Ssam else if (timeleft > 59) 298160996Ssam (void)fprintf(pf, "System going down in %d minute%s\n\n", 299160996Ssam timeleft / 60, (timeleft > 60) ? "s" : ""); 300160996Ssam else if (timeleft) 301160996Ssam (void)fprintf(pf, "System going down in 30 seconds\n\n"); 302160996Ssam else 303160996Ssam (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 304160996Ssam 305160996Ssam if (mbuflen) 306160996Ssam (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 307160996Ssam 308160996Ssam /* 309160996Ssam * play some games, just in case wall doesn't come back 310160996Ssam * probably unnecessary, given that wall is careful. 311160996Ssam */ 312160996Ssam if (!setjmp(alarmbuf)) { 313160996Ssam (void)signal(SIGALRM, timeout); 314160996Ssam (void)alarm((u_int)30); 315160996Ssam (void)pclose(pf); 316160996Ssam (void)alarm((u_int)0); 317160996Ssam (void)signal(SIGALRM, SIG_DFL); 318160996Ssam } 319160996Ssam} 320160996Ssam 321160996Ssamvoid 322160996Ssamtimeout(int signo __unused) 323160996Ssam{ 324160996Ssam longjmp(alarmbuf, 1); 325160996Ssam} 326160996Ssam 327160996Ssamvoid 328160996Ssamdie_you_gravy_sucking_pig_dog() 329160996Ssam{ 330160996Ssam char *empty_environ[] = { NULL }; 331160996Ssam 332160996Ssam syslog(LOG_NOTICE, "%s by %s: %s", 333160996Ssam doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : 334160996Ssam "shutdown", whom, mbuf); 335160996Ssam (void)sleep(2); 336160996Ssam 337160996Ssam (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 338160996Ssam if (killflg) { 339160996Ssam (void)printf("\rbut you'll have to do it yourself\r\n"); 340160996Ssam exit(0); 341160996Ssam } 342160996Ssam#ifdef DEBUG 343160996Ssam if (doreboot) 344160996Ssam (void)printf("reboot"); 345160996Ssam else if (dohalt) 346160996Ssam (void)printf("halt"); 347160996Ssam else if (dopower) 348160996Ssam (void)printf("power-down"); 349160996Ssam if (nosync != NULL) 350160996Ssam (void)printf(" no sync"); 351160996Ssam (void)printf("\nkill -HUP 1\n"); 352160996Ssam#else 353160996Ssam if (!oflag) { 354160996Ssam (void)kill(1, doreboot ? SIGINT : /* reboot */ 355160996Ssam dohalt ? SIGUSR1 : /* halt */ 356160996Ssam dopower ? SIGUSR2 : /* power-down */ 357160996Ssam SIGTERM); /* single-user */ 358160996Ssam } else { 359160996Ssam if (doreboot) { 360160996Ssam execle(_PATH_REBOOT, "reboot", "-l", nosync, 361160996Ssam (char *)NULL, empty_environ); 362160996Ssam syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 363160996Ssam _PATH_REBOOT); 364160996Ssam warn(_PATH_REBOOT); 365160996Ssam } 366160996Ssam else if (dohalt) { 367160996Ssam execle(_PATH_HALT, "halt", "-l", nosync, 368160996Ssam (char *)NULL, empty_environ); 369160996Ssam syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 370160996Ssam _PATH_HALT); 371160996Ssam warn(_PATH_HALT); 372160996Ssam } 373160996Ssam else if (dopower) { 374160996Ssam execle(_PATH_HALT, "halt", "-l", "-p", nosync, 375160996Ssam (char *)NULL, empty_environ); 376160996Ssam syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 377160996Ssam _PATH_HALT); 378160996Ssam warn(_PATH_HALT); 379160996Ssam } 380160996Ssam (void)kill(1, SIGTERM); /* to single-user */ 381160996Ssam } 382160996Ssam#endif 383160996Ssam finish(0); 384160996Ssam} 385160996Ssam 386160996Ssam#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 387160996Ssam 388160996Ssamvoid 389160996Ssamgetoffset(char *timearg) 390160996Ssam{ 391160996Ssam struct tm *lt; 392160996Ssam char *p; 393160996Ssam time_t now; 394160996Ssam int this_year; 395160996Ssam 396160996Ssam (void)time(&now); 397160996Ssam 398160996Ssam if (!strcasecmp(timearg, "now")) { /* now */ 399160996Ssam offset = 0; 400160996Ssam shuttime = now; 401160996Ssam return; 402160996Ssam } 403160996Ssam 404160996Ssam if (*timearg == '+') { /* +minutes */ 405160996Ssam if (!isdigit(*++timearg)) 406160996Ssam badtime(); 407160996Ssam if ((offset = atoi(timearg) * 60) < 0) 408160996Ssam badtime(); 409160996Ssam shuttime = now + offset; 410160996Ssam return; 411160996Ssam } 412160996Ssam 413160996Ssam /* handle hh:mm by getting rid of the colon */ 414160996Ssam for (p = timearg; *p; ++p) 415160996Ssam if (!isascii(*p) || !isdigit(*p)) { 416160996Ssam if (*p == ':' && strlen(p) == 3) { 417160996Ssam p[0] = p[1]; 418160996Ssam p[1] = p[2]; 419160996Ssam p[2] = '\0'; 420160996Ssam } 421160996Ssam else 422160996Ssam badtime(); 423160996Ssam } 424195848Ssam 425160996Ssam unsetenv("TZ"); /* OUR timezone */ 426160996Ssam lt = localtime(&now); /* current time val */ 427160996Ssam 428160996Ssam switch(strlen(timearg)) { 429160996Ssam case 10: 430160996Ssam this_year = lt->tm_year; 431160996Ssam lt->tm_year = ATOI2(timearg); 432160996Ssam /* 433160996Ssam * check if the specified year is in the next century. 434160996Ssam * allow for one year of user error as many people will 435160996Ssam * enter n - 1 at the start of year n. 436160996Ssam */ 437160996Ssam if (lt->tm_year < (this_year % 100) - 1) 438160996Ssam lt->tm_year += 100; 439160996Ssam /* adjust for the year 2000 and beyond */ 440160996Ssam lt->tm_year += (this_year - (this_year % 100)); 441160996Ssam /* FALLTHROUGH */ 442160996Ssam case 8: 443160996Ssam lt->tm_mon = ATOI2(timearg); 444160996Ssam if (--lt->tm_mon < 0 || lt->tm_mon > 11) 445160996Ssam badtime(); 446160996Ssam /* FALLTHROUGH */ 447160996Ssam case 6: 448160996Ssam lt->tm_mday = ATOI2(timearg); 449160996Ssam if (lt->tm_mday < 1 || lt->tm_mday > 31) 450160996Ssam badtime(); 451160996Ssam /* FALLTHROUGH */ 452160996Ssam case 4: 453160996Ssam lt->tm_hour = ATOI2(timearg); 454160996Ssam if (lt->tm_hour < 0 || lt->tm_hour > 23) 455160996Ssam badtime(); 456160996Ssam lt->tm_min = ATOI2(timearg); 457160996Ssam if (lt->tm_min < 0 || lt->tm_min > 59) 458160996Ssam badtime(); 459160996Ssam lt->tm_sec = 0; 460160996Ssam if ((shuttime = mktime(lt)) == -1) 461160996Ssam badtime(); 462160996Ssam if ((offset = shuttime - now) < 0) 463160996Ssam errx(1, "that time is already past."); 464160996Ssam break; 465160996Ssam default: 466160996Ssam badtime(); 467160996Ssam } 468160996Ssam} 469 470#define NOMSG "\n\nNO LOGINS: System going down at " 471void 472nolog() 473{ 474 int logfd; 475 char *ct; 476 477 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 478 (void)signal(SIGINT, finish); 479 (void)signal(SIGHUP, finish); 480 (void)signal(SIGQUIT, finish); 481 (void)signal(SIGTERM, finish); 482 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 483 0664)) >= 0) { 484 (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 485 ct = ctime(&shuttime); 486 (void)write(logfd, ct + 11, 5); 487 (void)write(logfd, "\n\n", 2); 488 (void)write(logfd, mbuf, strlen(mbuf)); 489 (void)close(logfd); 490 } 491} 492 493void 494finish(int signo __unused) 495{ 496 if (!killflg) 497 (void)unlink(_PATH_NOLOGIN); 498 exit(0); 499} 500 501void 502badtime() 503{ 504 errx(1, "bad time format"); 505} 506 507void 508usage(const char *cp) 509{ 510 if (cp != NULL) 511 warnx("%s", cp); 512 (void)fprintf(stderr, 513 "usage: shutdown [-] [-h | -p | -r | -k] [-o [-n]]" 514 " time [warning-message ...]\n"); 515 exit(1); 516} 517