shutdown.c revision 140796
1193640Sariff/* 2193640Sariff * Copyright (c) 1988, 1990, 1993 3193640Sariff * The Regents of the University of California. All rights reserved. 4193640Sariff * 5193640Sariff * Redistribution and use in source and binary forms, with or without 6193640Sariff * modification, are permitted provided that the following conditions 7193640Sariff * are met: 8193640Sariff * 1. Redistributions of source code must retain the above copyright 9193640Sariff * notice, this list of conditions and the following disclaimer. 10193640Sariff * 2. Redistributions in binary form must reproduce the above copyright 11193640Sariff * notice, this list of conditions and the following disclaimer in the 12193640Sariff * documentation and/or other materials provided with the distribution. 13193640Sariff * 4. Neither the name of the University nor the names of its contributors 14193640Sariff * may be used to endorse or promote products derived from this software 15193640Sariff * without specific prior written permission. 16193640Sariff * 17193640Sariff * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18193640Sariff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19193640Sariff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20193640Sariff * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21193640Sariff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22193640Sariff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23193640Sariff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24193640Sariff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25193640Sariff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26193640Sariff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27193640Sariff * SUCH DAMAGE. 28193640Sariff */ 29193640Sariff 30193640Sariff#if 0 31193640Sariff#ifndef lint 32193640Sariffstatic const char copyright[] = 33193640Sariff"@(#) Copyright (c) 1988, 1990, 1993\n\ 34193640Sariff The Regents of the University of California. All rights reserved.\n"; 35193640Sariff#endif /* not lint */ 36193640Sariff 37193640Sariff#ifndef lint 38193640Sariffstatic char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; 39193640Sariff#endif /* not lint */ 40193640Sariff#endif 41193640Sariff#include <sys/cdefs.h> 42193640Sariff__FBSDID("$FreeBSD: head/sbin/shutdown/shutdown.c 140796 2005-01-25 08:37:04Z delphij $"); 43193640Sariff 44193640Sariff#include <sys/param.h> 45193640Sariff#include <sys/time.h> 46193640Sariff#include <sys/resource.h> 47193640Sariff#include <sys/syslog.h> 48193640Sariff 49193640Sariff#include <ctype.h> 50193640Sariff#include <err.h> 51193640Sariff#include <fcntl.h> 52193640Sariff#include <paths.h> 53193640Sariff#include <pwd.h> 54193640Sariff#include <setjmp.h> 55193640Sariff#include <signal.h> 56193640Sariff#include <stdio.h> 57193640Sariff#include <stdlib.h> 58193640Sariff#include <string.h> 59193640Sariff#include <unistd.h> 60193640Sariff 61193640Sariff#ifdef DEBUG 62193640Sariff#undef _PATH_NOLOGIN 63193640Sariff#define _PATH_NOLOGIN "./nologin" 64193640Sariff#endif 65193640Sariff 66193640Sariff#define H *60*60 67193640Sariff#define M *60 68193640Sariff#define S *1 69193640Sariff#define NOLOG_TIME 5*60 70193640Sariffstruct interval { 71193640Sariff int timeleft, timetowait; 72193640Sariff} tlist[] = { 73193640Sariff { 10 H, 5 H }, 74193640Sariff { 5 H, 3 H }, 75193640Sariff { 2 H, 1 H }, 76193640Sariff { 1 H, 30 M }, 77193640Sariff { 30 M, 10 M }, 78193640Sariff { 20 M, 10 M }, 79193640Sariff { 10 M, 5 M }, 80193640Sariff { 5 M, 3 M }, 81193640Sariff { 2 M, 1 M }, 82193640Sariff { 1 M, 30 S }, 83193640Sariff { 30 S, 30 S }, 84193640Sariff { 0 , 0 } 85193640Sariff}; 86193640Sariff#undef H 87193640Sariff#undef M 88193640Sariff#undef S 89193640Sariff 90193640Sariffstatic time_t offset, shuttime; 91193640Sariffstatic int dohalt, dopower, doreboot, killflg, mbuflen, oflag; 92193640Sariffstatic char mbuf[BUFSIZ]; 93193640Sariffstatic const char *nosync, *whom; 94193640Sariff 95193640Sariffvoid badtime(void); 96193640Sariffvoid die_you_gravy_sucking_pig_dog(void); 97193640Sariffvoid finish(int); 98193640Sariffvoid getoffset(char *); 99193640Sariffvoid loop(void); 100193640Sariffvoid nolog(void); 101193640Sariffvoid timeout(int); 102193640Sariffvoid timewarn(int); 103193640Sariffvoid usage(const char *); 104193640Sariff 105193640Sariffextern const char **environ; 106193640Sariff 107193640Sariffint 108193640Sariffmain(int argc, char **argv) 109193640Sariff{ 110193640Sariff char *p, *endp; 111193640Sariff struct passwd *pw; 112193640Sariff int arglen, ch, len, readstdin; 113193640Sariff 114193640Sariff#ifndef DEBUG 115193640Sariff if (geteuid()) 116193640Sariff errx(1, "NOT super-user"); 117193640Sariff#endif 118193640Sariff nosync = NULL; 119193640Sariff readstdin = 0; 120193640Sariff while ((ch = getopt(argc, argv, "-hknopr")) != -1) 121193640Sariff switch (ch) { 122193640Sariff case '-': 123193640Sariff readstdin = 1; 124193640Sariff break; 125193640Sariff case 'h': 126193640Sariff dohalt = 1; 127193640Sariff break; 128193640Sariff case 'k': 129193640Sariff killflg = 1; 130193640Sariff break; 131193640Sariff case 'n': 132193640Sariff nosync = "-n"; 133193640Sariff break; 134193640Sariff case 'o': 135193640Sariff oflag = 1; 136193640Sariff break; 137193640Sariff case 'p': 138193640Sariff dopower = 1; 139193640Sariff break; 140193640Sariff case 'r': 141193640Sariff doreboot = 1; 142193640Sariff break; 143193640Sariff case '?': 144193640Sariff default: 145193640Sariff usage((char *)NULL); 146193640Sariff } 147193640Sariff argc -= optind; 148193640Sariff argv += optind; 149193640Sariff 150193640Sariff if (argc < 1) 151193640Sariff usage((char *)NULL); 152193640Sariff 153193640Sariff if (killflg + doreboot + dohalt + dopower > 1) 154193640Sariff usage("incompatible switches -h, -k, -p and -r"); 155193640Sariff 156193640Sariff if (oflag && !(dohalt || dopower || doreboot)) 157193640Sariff usage("-o requires -h, -p or -r"); 158193640Sariff 159193640Sariff if (nosync != NULL && !oflag) 160193640Sariff usage("-n requires -o"); 161193640Sariff 162193640Sariff getoffset(*argv++); 163193640Sariff 164193640Sariff if (*argv) { 165193640Sariff for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 166193640Sariff arglen = strlen(*argv); 167193640Sariff if ((len -= arglen) <= 2) 168193640Sariff break; 169193640Sariff if (p != mbuf) 170193640Sariff *p++ = ' '; 171193640Sariff memmove(p, *argv, arglen); 172193640Sariff p += arglen; 173193640Sariff } 174193640Sariff *p = '\n'; 175193640Sariff *++p = '\0'; 176193640Sariff } 177193640Sariff 178193640Sariff if (readstdin) { 179193640Sariff p = mbuf; 180193640Sariff endp = mbuf + sizeof(mbuf) - 2; 181193640Sariff for (;;) { 182193640Sariff if (!fgets(p, endp - p + 1, stdin)) 183193640Sariff break; 184193640Sariff for (; *p && p < endp; ++p); 185193640Sariff if (p == endp) { 186193640Sariff *p = '\n'; 187193640Sariff *++p = '\0'; 188193640Sariff break; 189193640Sariff } 190193640Sariff } 191193640Sariff } 192193640Sariff mbuflen = strlen(mbuf); 193193640Sariff 194193640Sariff if (offset) 195193640Sariff (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 196193640Sariff else 197193640Sariff (void)printf("Shutdown NOW!\n"); 198193640Sariff 199193640Sariff if (!(whom = getlogin())) 200193640Sariff whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 201193640Sariff 202193640Sariff#ifdef DEBUG 203193640Sariff (void)putc('\n', stdout); 204193640Sariff#else 205193640Sariff (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 206193640Sariff { 207193640Sariff int forkpid; 208193640Sariff 209193640Sariff forkpid = fork(); 210193640Sariff if (forkpid == -1) 211193640Sariff err(1, "fork"); 212193640Sariff if (forkpid) 213193640Sariff errx(0, "[pid %d]", forkpid); 214193640Sariff } 215193640Sariff setsid(); 216193640Sariff#endif 217193640Sariff openlog("shutdown", LOG_CONS, LOG_AUTH); 218193640Sariff loop(); 219193640Sariff return(0); 220193640Sariff} 221193640Sariff 222193640Sariffvoid 223193640Sariffloop() 224193640Sariff{ 225193640Sariff struct interval *tp; 226193640Sariff u_int sltime; 227193640Sariff int logged; 228193640Sariff 229193640Sariff if (offset <= NOLOG_TIME) { 230193640Sariff logged = 1; 231193640Sariff nolog(); 232193640Sariff } 233193640Sariff else 234193640Sariff logged = 0; 235193640Sariff tp = tlist; 236193640Sariff if (tp->timeleft < offset) 237193640Sariff (void)sleep((u_int)(offset - tp->timeleft)); 238193640Sariff else { 239193640Sariff while (tp->timeleft && offset < tp->timeleft) 240193640Sariff ++tp; 241193640Sariff /* 242193640Sariff * Warn now, if going to sleep more than a fifth of 243193640Sariff * the next wait time. 244193640Sariff */ 245193640Sariff if ((sltime = offset - tp->timeleft)) { 246193640Sariff if (sltime > (u_int)(tp->timetowait / 5)) 247193640Sariff timewarn(offset); 248193640Sariff (void)sleep(sltime); 249193640Sariff } 250193640Sariff } 251193640Sariff for (;; ++tp) { 252193640Sariff timewarn(tp->timeleft); 253193640Sariff if (!logged && tp->timeleft <= NOLOG_TIME) { 254193640Sariff logged = 1; 255193640Sariff nolog(); 256193640Sariff } 257193640Sariff (void)sleep((u_int)tp->timetowait); 258193640Sariff if (!tp->timeleft) 259193640Sariff break; 260193640Sariff } 261193640Sariff die_you_gravy_sucking_pig_dog(); 262193640Sariff} 263193640Sariff 264193640Sariffstatic jmp_buf alarmbuf; 265193640Sariff 266193640Sariffstatic const char *restricted_environ[] = { 267193640Sariff "PATH=" _PATH_STDPATH, 268193640Sariff NULL 269193640Sariff}; 270193640Sariff 271193640Sariffvoid 272193640Sarifftimewarn(timeleft) 273193640Sariff int timeleft; 274193640Sariff{ 275193640Sariff static int first; 276193640Sariff static char hostname[MAXHOSTNAMELEN + 1]; 277193640Sariff FILE *pf; 278193640Sariff char wcmd[MAXPATHLEN + 4]; 279193640Sariff 280193640Sariff if (!first++) 281193640Sariff (void)gethostname(hostname, sizeof(hostname)); 282193640Sariff 283193640Sariff /* undoc -n option to wall suppresses normal wall banner */ 284193640Sariff (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 285193640Sariff environ = restricted_environ; 286193640Sariff if (!(pf = popen(wcmd, "w"))) { 287193640Sariff syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 288193640Sariff return; 289193640Sariff } 290193640Sariff 291193640Sariff (void)fprintf(pf, 292193640Sariff "\007*** %sSystem shutdown message from %s@%s ***\007\n", 293193640Sariff timeleft ? "": "FINAL ", whom, hostname); 294193640Sariff 295193640Sariff if (timeleft > 10*60) 296193640Sariff (void)fprintf(pf, "System going down at %5.5s\n\n", 297193640Sariff ctime(&shuttime) + 11); 298193640Sariff else if (timeleft > 59) 299193640Sariff (void)fprintf(pf, "System going down in %d minute%s\n\n", 300193640Sariff timeleft / 60, (timeleft > 60) ? "s" : ""); 301193640Sariff else if (timeleft) 302193640Sariff (void)fprintf(pf, "System going down in 30 seconds\n\n"); 303193640Sariff else 304193640Sariff (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 305193640Sariff 306193640Sariff if (mbuflen) 307193640Sariff (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 308193640Sariff 309193640Sariff /* 310193640Sariff * play some games, just in case wall doesn't come back 311193640Sariff * probably unnecessary, given that wall is careful. 312193640Sariff */ 313193640Sariff if (!setjmp(alarmbuf)) { 314193640Sariff (void)signal(SIGALRM, timeout); 315193640Sariff (void)alarm((u_int)30); 316193640Sariff (void)pclose(pf); 317193640Sariff (void)alarm((u_int)0); 318193640Sariff (void)signal(SIGALRM, SIG_DFL); 319193640Sariff } 320193640Sariff} 321193640Sariff 322193640Sariffvoid 323193640Sarifftimeout(signo) 324193640Sariff int signo __unused; 325193640Sariff{ 326193640Sariff longjmp(alarmbuf, 1); 327193640Sariff} 328193640Sariff 329193640Sariffvoid 330193640Sariffdie_you_gravy_sucking_pig_dog() 331193640Sariff{ 332193640Sariff char *empty_environ[] = { NULL }; 333193640Sariff 334193640Sariff syslog(LOG_NOTICE, "%s by %s: %s", 335193640Sariff doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : 336193640Sariff "shutdown", whom, mbuf); 337193640Sariff (void)sleep(2); 338193640Sariff 339193640Sariff (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 340193640Sariff if (killflg) { 341193640Sariff (void)printf("\rbut you'll have to do it yourself\r\n"); 342193640Sariff exit(0); 343193640Sariff } 344193640Sariff#ifdef DEBUG 345193640Sariff if (doreboot) 346193640Sariff (void)printf("reboot"); 347193640Sariff else if (dohalt) 348193640Sariff (void)printf("halt"); 349193640Sariff else if (dopower) 350193640Sariff (void)printf("power-down"); 351193640Sariff if (nosync != NULL) 352193640Sariff (void)printf(" no sync"); 353193640Sariff (void)printf("\nkill -HUP 1\n"); 354193640Sariff#else 355193640Sariff if (!oflag) { 356193640Sariff (void)kill(1, doreboot ? SIGINT : /* reboot */ 357193640Sariff dohalt ? SIGUSR1 : /* halt */ 358193640Sariff dopower ? SIGUSR2 : /* power-down */ 359193640Sariff SIGTERM); /* single-user */ 360193640Sariff } else { 361193640Sariff if (doreboot) { 362193640Sariff execle(_PATH_REBOOT, "reboot", "-l", nosync, 363193640Sariff (char *)NULL, empty_environ); 364193640Sariff syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 365193640Sariff _PATH_REBOOT); 366193640Sariff warn(_PATH_REBOOT); 367193640Sariff } 368193640Sariff else if (dohalt) { 369193640Sariff execle(_PATH_HALT, "halt", "-l", nosync, 370193640Sariff (char *)NULL, empty_environ); 371193640Sariff syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 372193640Sariff _PATH_HALT); 373193640Sariff warn(_PATH_HALT); 374193640Sariff } 375193640Sariff else if (dopower) { 376193640Sariff execle(_PATH_HALT, "halt", "-l", "-p", nosync, 377193640Sariff (char *)NULL, empty_environ); 378193640Sariff syslog(LOG_ERR, "shutdown: can't exec %s: %m.", 379193640Sariff _PATH_HALT); 380193640Sariff warn(_PATH_HALT); 381193640Sariff } 382193640Sariff (void)kill(1, SIGTERM); /* to single-user */ 383193640Sariff } 384193640Sariff#endif 385193640Sariff finish(0); 386193640Sariff} 387193640Sariff 388193640Sariff#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 389193640Sariff 390193640Sariffvoid 391193640Sariffgetoffset(timearg) 392193640Sariff char *timearg; 393193640Sariff{ 394193640Sariff struct tm *lt; 395193640Sariff char *p; 396193640Sariff time_t now; 397193640Sariff int this_year; 398193640Sariff 399193640Sariff (void)time(&now); 400193640Sariff 401193640Sariff if (!strcasecmp(timearg, "now")) { /* now */ 402193640Sariff offset = 0; 403193640Sariff shuttime = now; 404193640Sariff return; 405193640Sariff } 406193640Sariff 407193640Sariff if (*timearg == '+') { /* +minutes */ 408193640Sariff if (!isdigit(*++timearg)) 409193640Sariff badtime(); 410193640Sariff if ((offset = atoi(timearg) * 60) < 0) 411193640Sariff badtime(); 412193640Sariff shuttime = now + offset; 413193640Sariff return; 414193640Sariff } 415193640Sariff 416193640Sariff /* handle hh:mm by getting rid of the colon */ 417193640Sariff for (p = timearg; *p; ++p) 418193640Sariff if (!isascii(*p) || !isdigit(*p)) { 419193640Sariff if (*p == ':' && strlen(p) == 3) { 420193640Sariff p[0] = p[1]; 421193640Sariff p[1] = p[2]; 422193640Sariff p[2] = '\0'; 423193640Sariff } 424193640Sariff else 425193640Sariff badtime(); 426193640Sariff } 427193640Sariff 428193640Sariff unsetenv("TZ"); /* OUR timezone */ 429193640Sariff lt = localtime(&now); /* current time val */ 430193640Sariff 431193640Sariff switch(strlen(timearg)) { 432193640Sariff case 10: 433193640Sariff this_year = lt->tm_year; 434193640Sariff lt->tm_year = ATOI2(timearg); 435193640Sariff /* 436193640Sariff * check if the specified year is in the next century. 437193640Sariff * allow for one year of user error as many people will 438193640Sariff * enter n - 1 at the start of year n. 439193640Sariff */ 440193640Sariff if (lt->tm_year < (this_year % 100) - 1) 441193640Sariff lt->tm_year += 100; 442193640Sariff /* adjust for the year 2000 and beyond */ 443193640Sariff lt->tm_year += (this_year - (this_year % 100)); 444193640Sariff /* FALLTHROUGH */ 445193640Sariff case 8: 446193640Sariff lt->tm_mon = ATOI2(timearg); 447193640Sariff if (--lt->tm_mon < 0 || lt->tm_mon > 11) 448193640Sariff badtime(); 449193640Sariff /* FALLTHROUGH */ 450193640Sariff case 6: 451193640Sariff lt->tm_mday = ATOI2(timearg); 452193640Sariff if (lt->tm_mday < 1 || lt->tm_mday > 31) 453193640Sariff badtime(); 454193640Sariff /* FALLTHROUGH */ 455193640Sariff case 4: 456193640Sariff lt->tm_hour = ATOI2(timearg); 457193640Sariff if (lt->tm_hour < 0 || lt->tm_hour > 23) 458193640Sariff badtime(); 459193640Sariff lt->tm_min = ATOI2(timearg); 460193640Sariff if (lt->tm_min < 0 || lt->tm_min > 59) 461193640Sariff badtime(); 462193640Sariff lt->tm_sec = 0; 463193640Sariff if ((shuttime = mktime(lt)) == -1) 464193640Sariff badtime(); 465193640Sariff if ((offset = shuttime - now) < 0) 466193640Sariff errx(1, "that time is already past."); 467193640Sariff break; 468 default: 469 badtime(); 470 } 471} 472 473#define NOMSG "\n\nNO LOGINS: System going down at " 474void 475nolog() 476{ 477 int logfd; 478 char *ct; 479 480 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 481 (void)signal(SIGINT, finish); 482 (void)signal(SIGHUP, finish); 483 (void)signal(SIGQUIT, finish); 484 (void)signal(SIGTERM, finish); 485 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 486 0664)) >= 0) { 487 (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 488 ct = ctime(&shuttime); 489 (void)write(logfd, ct + 11, 5); 490 (void)write(logfd, "\n\n", 2); 491 (void)write(logfd, mbuf, strlen(mbuf)); 492 (void)close(logfd); 493 } 494} 495 496void 497finish(signo) 498 int signo __unused; 499{ 500 if (!killflg) 501 (void)unlink(_PATH_NOLOGIN); 502 exit(0); 503} 504 505void 506badtime() 507{ 508 errx(1, "bad time format"); 509} 510 511void 512usage(cp) 513 const char *cp; 514{ 515 if (cp != NULL) 516 warnx("%s", cp); 517 (void)fprintf(stderr, 518 "usage: shutdown [-] [-h | -p | -r | -k] [-o [-n]]" 519 " time [warning-message ...]\n"); 520 exit(1); 521} 522