shutdown.c revision 238968
1251875Speter/* 2251875Speter * Copyright (c) 1988, 1990, 1993 3251875Speter * The Regents of the University of California. All rights reserved. 4251875Speter * 5251875Speter * Redistribution and use in source and binary forms, with or without 6251875Speter * modification, are permitted provided that the following conditions 7251875Speter * are met: 8251875Speter * 1. Redistributions of source code must retain the above copyright 9251875Speter * notice, this list of conditions and the following disclaimer. 10251875Speter * 2. Redistributions in binary form must reproduce the above copyright 11251875Speter * notice, this list of conditions and the following disclaimer in the 12251875Speter * documentation and/or other materials provided with the distribution. 13251875Speter * 4. Neither the name of the University nor the names of its contributors 14251875Speter * may be used to endorse or promote products derived from this software 15251875Speter * without specific prior written permission. 16251875Speter * 17251875Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18251875Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19251875Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20251875Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21251875Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22251875Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23251875Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24251875Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25251875Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26251875Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27251875Speter * SUCH DAMAGE. 28251875Speter */ 29251875Speter 30251875Speter#if 0 31251875Speter#ifndef lint 32251875Speterstatic const char copyright[] = 33251875Speter"@(#) Copyright (c) 1988, 1990, 1993\n\ 34251875Speter The Regents of the University of California. All rights reserved.\n"; 35251875Speter#endif /* not lint */ 36251875Speter 37251875Speter#ifndef lint 38251875Speterstatic char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; 39251875Speter#endif /* not lint */ 40251875Speter#endif 41251875Speter#include <sys/cdefs.h> 42251875Speter__FBSDID("$FreeBSD: head/sbin/shutdown/shutdown.c 238968 2012-08-01 09:10:21Z des $"); 43251875Speter 44251875Speter#include <sys/param.h> 45251875Speter#include <sys/time.h> 46251875Speter#include <sys/resource.h> 47251875Speter#include <sys/syslog.h> 48251875Speter 49251875Speter#include <ctype.h> 50251875Speter#include <err.h> 51251875Speter#include <fcntl.h> 52251875Speter#include <paths.h> 53251875Speter#include <pwd.h> 54251875Speter#include <setjmp.h> 55251875Speter#include <signal.h> 56251875Speter#include <stdio.h> 57251875Speter#include <stdlib.h> 58251875Speter#include <string.h> 59251875Speter#include <unistd.h> 60251875Speter 61251875Speter#ifdef DEBUG 62251875Speter#undef _PATH_NOLOGIN 63251875Speter#define _PATH_NOLOGIN "./nologin" 64251875Speter#endif 65251875Speter 66251875Speter#define H *60*60 67251875Speter#define M *60 68251875Speter#define S *1 69251875Speter#define NOLOG_TIME 5*60 70251875Speterstatic struct interval { 71251875Speter int timeleft, timetowait; 72251875Speter} tlist[] = { 73251875Speter { 10 H, 5 H }, 74251875Speter { 5 H, 3 H }, 75251875Speter { 2 H, 1 H }, 76251875Speter { 1 H, 30 M }, 77251875Speter { 30 M, 10 M }, 78251875Speter { 20 M, 10 M }, 79251875Speter { 10 M, 5 M }, 80251875Speter { 5 M, 3 M }, 81251875Speter { 2 M, 1 M }, 82251875Speter { 1 M, 30 S }, 83251875Speter { 30 S, 30 S }, 84251875Speter { 0 , 0 } 85251875Speter}; 86251875Speter#undef H 87251875Speter#undef M 88251875Speter#undef S 89251875Speter 90251875Speterstatic time_t offset, shuttime; 91251875Speterstatic int dohalt, dopower, doreboot, killflg, mbuflen, oflag; 92251875Speterstatic char mbuf[BUFSIZ]; 93251875Speterstatic const char *nosync, *whom; 94251875Speter 95251875Speterstatic void badtime(void); 96251875Speterstatic void die_you_gravy_sucking_pig_dog(void); 97251875Speterstatic void finish(int); 98251875Speterstatic void getoffset(char *); 99251875Speterstatic void loop(void); 100251875Speterstatic void nolog(void); 101251875Speterstatic void timeout(int); 102251875Speterstatic void timewarn(int); 103251875Speterstatic void usage(const char *); 104251875Speter 105251875Speterextern const char **environ; 106251875Speter 107251875Speterint 108251875Spetermain(int argc, char **argv) 109251875Speter{ 110251875Speter char *p, *endp; 111251875Speter struct passwd *pw; 112251875Speter int arglen, ch, len, readstdin; 113251875Speter 114251875Speter#ifndef DEBUG 115251875Speter if (geteuid()) 116251875Speter errx(1, "NOT super-user"); 117251875Speter#endif 118251875Speter 119251875Speter nosync = NULL; 120251875Speter readstdin = 0; 121251875Speter 122251875Speter /* 123251875Speter * Test for the special case where the utility is called as 124251875Speter * "poweroff", for which it runs 'shutdown -p now'. 125251875Speter */ 126251875Speter if ((p = strrchr(argv[0], '/')) == NULL) 127251875Speter p = argv[0]; 128251875Speter else 129251875Speter ++p; 130251875Speter if (strcmp(p, "poweroff") == 0) { 131251875Speter if (getopt(argc, argv, "") != -1) 132251875Speter usage((char *)NULL); 133251875Speter argc -= optind; 134251875Speter argv += optind; 135251875Speter if (argc != 0) 136251875Speter usage((char *)NULL); 137251875Speter dopower = 1; 138251875Speter offset = 0; 139251875Speter (void)time(&shuttime); 140251875Speter goto poweroff; 141251875Speter } 142251875Speter 143251875Speter while ((ch = getopt(argc, argv, "-hknopr")) != -1) 144251875Speter switch (ch) { 145251875Speter case '-': 146251875Speter readstdin = 1; 147251875Speter break; 148251875Speter case 'h': 149251875Speter dohalt = 1; 150251875Speter break; 151251875Speter case 'k': 152251875Speter killflg = 1; 153251875Speter break; 154251875Speter case 'n': 155251875Speter nosync = "-n"; 156251875Speter break; 157251875Speter case 'o': 158251875Speter oflag = 1; 159251875Speter break; 160251875Speter case 'p': 161251875Speter dopower = 1; 162251875Speter break; 163251875Speter case 'r': 164251875Speter doreboot = 1; 165251875Speter break; 166251875Speter case '?': 167251875Speter default: 168251875Speter usage((char *)NULL); 169251875Speter } 170251875Speter argc -= optind; 171251875Speter argv += optind; 172251875Speter 173251875Speter if (argc < 1) 174251875Speter usage((char *)NULL); 175251875Speter 176251875Speter if (killflg + doreboot + dohalt + dopower > 1) 177251875Speter usage("incompatible switches -h, -k, -p and -r"); 178251875Speter 179251875Speter if (oflag && !(dohalt || dopower || doreboot)) 180251875Speter usage("-o requires -h, -p or -r"); 181251875Speter 182251875Speter if (nosync != NULL && !oflag) 183251875Speter usage("-n requires -o"); 184251875Speter 185251875Speter getoffset(*argv++); 186251875Speter 187251875Speterpoweroff: 188251875Speter if (*argv) { 189251875Speter for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 190251875Speter arglen = strlen(*argv); 191251875Speter if ((len -= arglen) <= 2) 192251875Speter break; 193251875Speter if (p != mbuf) 194251875Speter *p++ = ' '; 195251875Speter memmove(p, *argv, arglen); 196251875Speter p += arglen; 197251875Speter } 198251875Speter *p = '\n'; 199251875Speter *++p = '\0'; 200251875Speter } 201251875Speter 202251875Speter if (readstdin) { 203251875Speter p = mbuf; 204251875Speter endp = mbuf + sizeof(mbuf) - 2; 205251875Speter for (;;) { 206251875Speter if (!fgets(p, endp - p + 1, stdin)) 207251875Speter break; 208251875Speter for (; *p && p < endp; ++p); 209251875Speter if (p == endp) { 210251875Speter *p = '\n'; 211251875Speter *++p = '\0'; 212251875Speter break; 213251875Speter } 214251875Speter } 215251875Speter } 216251875Speter mbuflen = strlen(mbuf); 217251875Speter 218251875Speter if (offset) 219251875Speter (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 220251875Speter else 221251875Speter (void)printf("Shutdown NOW!\n"); 222251875Speter 223251875Speter if (!(whom = getlogin())) 224251875Speter whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 225251875Speter 226251875Speter#ifdef DEBUG 227251875Speter (void)putc('\n', stdout); 228251875Speter#else 229251875Speter (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 230251875Speter { 231251875Speter int forkpid; 232251875Speter 233251875Speter forkpid = fork(); 234251875Speter if (forkpid == -1) 235251875Speter err(1, "fork"); 236251875Speter if (forkpid) 237251875Speter errx(0, "[pid %d]", forkpid); 238251875Speter } 239251875Speter setsid(); 240251875Speter#endif 241251875Speter openlog("shutdown", LOG_CONS, LOG_AUTH); 242251875Speter loop(); 243251875Speter return(0); 244251875Speter} 245251875Speter 246251875Speterstatic void 247251875Speterloop(void) 248251875Speter{ 249251875Speter struct interval *tp; 250251875Speter u_int sltime; 251251875Speter int logged; 252251875Speter 253251875Speter if (offset <= NOLOG_TIME) { 254251875Speter logged = 1; 255251875Speter nolog(); 256251875Speter } 257251875Speter else 258251875Speter logged = 0; 259251875Speter tp = tlist; 260251875Speter if (tp->timeleft < offset) 261251875Speter (void)sleep((u_int)(offset - tp->timeleft)); 262251875Speter else { 263251875Speter while (tp->timeleft && offset < tp->timeleft) 264251875Speter ++tp; 265251875Speter /* 266251875Speter * Warn now, if going to sleep more than a fifth of 267251875Speter * the next wait time. 268251875Speter */ 269251875Speter if ((sltime = offset - tp->timeleft)) { 270251875Speter if (sltime > (u_int)(tp->timetowait / 5)) 271251875Speter timewarn(offset); 272251875Speter (void)sleep(sltime); 273251875Speter } 274251875Speter } 275251875Speter for (;; ++tp) { 276251875Speter timewarn(tp->timeleft); 277251875Speter if (!logged && tp->timeleft <= NOLOG_TIME) { 278251875Speter logged = 1; 279251875Speter nolog(); 280251875Speter } 281251875Speter (void)sleep((u_int)tp->timetowait); 282251875Speter if (!tp->timeleft) 283251875Speter break; 284251875Speter } 285251875Speter die_you_gravy_sucking_pig_dog(); 286251875Speter} 287251875Speter 288251875Speterstatic jmp_buf alarmbuf; 289251875Speter 290251875Speterstatic const char *restricted_environ[] = { 291251875Speter "PATH=" _PATH_STDPATH, 292251875Speter NULL 293251875Speter}; 294251875Speter 295251875Speterstatic void 296251875Spetertimewarn(int timeleft) 297251875Speter{ 298251875Speter static int first; 299251875Speter static char hostname[MAXHOSTNAMELEN + 1]; 300251875Speter FILE *pf; 301251875Speter char wcmd[MAXPATHLEN + 4]; 302251875Speter 303251875Speter if (!first++) 304251875Speter (void)gethostname(hostname, sizeof(hostname)); 305251875Speter 306251875Speter /* undoc -n option to wall suppresses normal wall banner */ 307251875Speter (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 308251875Speter environ = restricted_environ; 309251875Speter if (!(pf = popen(wcmd, "w"))) { 310251875Speter syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 311251875Speter return; 312251875Speter } 313251875Speter 314251875Speter (void)fprintf(pf, 315251875Speter "\007*** %sSystem shutdown message from %s@%s ***\007\n", 316251875Speter timeleft ? "": "FINAL ", whom, hostname); 317251875Speter 318251875Speter if (timeleft > 10*60) 319251875Speter (void)fprintf(pf, "System going down at %5.5s\n\n", 320251875Speter ctime(&shuttime) + 11); 321251875Speter else if (timeleft > 59) 322251875Speter (void)fprintf(pf, "System going down in %d minute%s\n\n", 323251875Speter timeleft / 60, (timeleft > 60) ? "s" : ""); 324251875Speter else if (timeleft) 325251875Speter (void)fprintf(pf, "System going down in 30 seconds\n\n"); 326251875Speter else 327251875Speter (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 328251875Speter 329251875Speter if (mbuflen) 330251875Speter (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 331251875Speter 332251875Speter /* 333251875Speter * play some games, just in case wall doesn't come back 334251875Speter * probably unnecessary, given that wall is careful. 335251875Speter */ 336251875Speter if (!setjmp(alarmbuf)) { 337251875Speter (void)signal(SIGALRM, timeout); 338251875Speter (void)alarm((u_int)30); 339251875Speter (void)pclose(pf); 340251875Speter (void)alarm((u_int)0); 341251875Speter (void)signal(SIGALRM, SIG_DFL); 342251875Speter } 343251875Speter} 344251875Speter 345251875Speterstatic void 346251875Spetertimeout(int signo __unused) 347251875Speter{ 348251875Speter longjmp(alarmbuf, 1); 349251875Speter} 350251875Speter 351251875Speterstatic void 352251875Speterdie_you_gravy_sucking_pig_dog(void) 353251875Speter{ 354251875Speter char *empty_environ[] = { NULL }; 355251875Speter 356251875Speter syslog(LOG_NOTICE, "%s by %s: %s", 357251875Speter doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : 358251875Speter "shutdown", whom, mbuf); 359251875Speter 360251875Speter (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 361251875Speter if (killflg) { 362251875Speter (void)printf("\rbut you'll have to do it yourself\r\n"); 363251875Speter exit(0); 364251875Speter } 365251875Speter#ifdef DEBUG 366251875Speter if (doreboot) 367251875Speter (void)printf("reboot"); 368251875Speter else if (dohalt) 369251875Speter (void)printf("halt"); 370251875Speter else if (dopower) 371251875Speter (void)printf("power-down"); 372251875Speter if (nosync != NULL) 373251875Speter (void)printf(" no sync"); 374251875Speter (void)printf("\nkill -HUP 1\n"); 375251875Speter#else 376251875Speter if (!oflag) { 377251875Speter (void)kill(1, doreboot ? SIGINT : /* reboot */ 378251875Speter dohalt ? SIGUSR1 : /* halt */ 379251875Speter dopower ? SIGUSR2 : /* power-down */ 380251875Speter SIGTERM); /* single-user */ 381251875Speter } else { 382251875Speter if (doreboot) { 383251875Speter execle(_PATH_REBOOT, "reboot", "-l", nosync, 384251875Speter (char *)NULL, empty_environ); 385251875Speter syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 386251875Speter _PATH_REBOOT); 387251875Speter warn(_PATH_REBOOT); 388251875Speter } 389251875Speter else if (dohalt) { 390251875Speter execle(_PATH_HALT, "halt", "-l", nosync, 391251875Speter (char *)NULL, empty_environ); 392251875Speter syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 393251875Speter _PATH_HALT); 394251875Speter warn(_PATH_HALT); 395251875Speter } 396251875Speter else if (dopower) { 397251875Speter execle(_PATH_HALT, "halt", "-l", "-p", nosync, 398251875Speter (char *)NULL, empty_environ); 399251875Speter syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 400251875Speter _PATH_HALT); 401251875Speter warn(_PATH_HALT); 402251875Speter } 403251875Speter (void)kill(1, SIGTERM); /* to single-user */ 404251875Speter } 405251875Speter#endif 406251875Speter finish(0); 407251875Speter} 408251875Speter 409251875Speter#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 410251875Speter 411251875Speterstatic void 412251875Spetergetoffset(char *timearg) 413251875Speter{ 414251875Speter struct tm *lt; 415251875Speter char *p; 416251875Speter time_t now; 417251875Speter int this_year; 418251875Speter 419251875Speter (void)time(&now); 420251875Speter 421251875Speter if (!strcasecmp(timearg, "now")) { /* now */ 422251875Speter offset = 0; 423251875Speter shuttime = now; 424251875Speter return; 425251875Speter } 426251875Speter 427251875Speter if (*timearg == '+') { /* +minutes */ 428251875Speter if (!isdigit(*++timearg)) 429251875Speter badtime(); 430251875Speter if ((offset = atoi(timearg) * 60) < 0) 431251875Speter badtime(); 432251875Speter shuttime = now + offset; 433251875Speter return; 434251875Speter } 435251875Speter 436251875Speter /* handle hh:mm by getting rid of the colon */ 437251875Speter for (p = timearg; *p; ++p) 438251875Speter if (!isascii(*p) || !isdigit(*p)) { 439251875Speter if (*p == ':' && strlen(p) == 3) { 440251875Speter p[0] = p[1]; 441251875Speter p[1] = p[2]; 442251875Speter p[2] = '\0'; 443251875Speter } 444251875Speter else 445251875Speter badtime(); 446251875Speter } 447251875Speter 448251875Speter unsetenv("TZ"); /* OUR timezone */ 449251875Speter lt = localtime(&now); /* current time val */ 450251875Speter 451251875Speter switch(strlen(timearg)) { 452251875Speter case 10: 453251875Speter this_year = lt->tm_year; 454251875Speter lt->tm_year = ATOI2(timearg); 455251875Speter /* 456251875Speter * check if the specified year is in the next century. 457251875Speter * allow for one year of user error as many people will 458251875Speter * enter n - 1 at the start of year n. 459251875Speter */ 460251875Speter if (lt->tm_year < (this_year % 100) - 1) 461251875Speter lt->tm_year += 100; 462251875Speter /* adjust for the year 2000 and beyond */ 463251875Speter lt->tm_year += (this_year - (this_year % 100)); 464251875Speter /* FALLTHROUGH */ 465251875Speter case 8: 466251875Speter lt->tm_mon = ATOI2(timearg); 467251875Speter if (--lt->tm_mon < 0 || lt->tm_mon > 11) 468251875Speter badtime(); 469251875Speter /* FALLTHROUGH */ 470251875Speter case 6: 471251875Speter lt->tm_mday = ATOI2(timearg); 472251875Speter if (lt->tm_mday < 1 || lt->tm_mday > 31) 473251875Speter badtime(); 474251875Speter /* FALLTHROUGH */ 475251875Speter case 4: 476251875Speter lt->tm_hour = ATOI2(timearg); 477251875Speter if (lt->tm_hour < 0 || lt->tm_hour > 23) 478251875Speter badtime(); 479251875Speter lt->tm_min = ATOI2(timearg); 480251875Speter if (lt->tm_min < 0 || lt->tm_min > 59) 481251875Speter badtime(); 482251875Speter lt->tm_sec = 0; 483251875Speter if ((shuttime = mktime(lt)) == -1) 484251875Speter badtime(); 485251875Speter if ((offset = shuttime - now) < 0) 486251875Speter errx(1, "that time is already past."); 487251875Speter break; 488251875Speter default: 489251875Speter badtime(); 490251875Speter } 491251875Speter} 492251875Speter 493251875Speter#define NOMSG "\n\nNO LOGINS: System going down at " 494251875Speterstatic void 495251875Speternolog(void) 496251875Speter{ 497251875Speter int logfd; 498251875Speter char *ct; 499251875Speter 500251875Speter (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 501251875Speter (void)signal(SIGINT, finish); 502251875Speter (void)signal(SIGHUP, finish); 503251875Speter (void)signal(SIGQUIT, finish); 504251875Speter (void)signal(SIGTERM, finish); 505251875Speter if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 506251875Speter 0664)) >= 0) { 507251875Speter (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 508251875Speter ct = ctime(&shuttime); 509251875Speter (void)write(logfd, ct + 11, 5); 510251875Speter (void)write(logfd, "\n\n", 2); 511251875Speter (void)write(logfd, mbuf, strlen(mbuf)); 512251875Speter (void)close(logfd); 513251875Speter } 514251875Speter} 515251875Speter 516251875Speterstatic void 517251875Speterfinish(int signo __unused) 518251875Speter{ 519251875Speter if (!killflg) 520251875Speter (void)unlink(_PATH_NOLOGIN); 521251875Speter exit(0); 522251875Speter} 523251875Speter 524251875Speterstatic void 525251875Speterbadtime(void) 526251875Speter{ 527251875Speter errx(1, "bad time format"); 528251875Speter} 529251875Speter 530251875Speterstatic void 531251875Speterusage(const char *cp) 532251875Speter{ 533251875Speter if (cp != NULL) 534251875Speter warnx("%s", cp); 535251875Speter (void)fprintf(stderr, 536251875Speter "usage: shutdown [-] [-h | -p | -r | -k] [-o [-n]] time [warning-message ...]\n" 537251875Speter " poweroff\n"); 538251875Speter exit(1); 539251875Speter} 540251875Speter