shutdown.c revision 1.51
1/* $OpenBSD: shutdown.c,v 1.51 2018/04/07 19:08:13 cheloha Exp $ */ 2/* $NetBSD: shutdown.c,v 1.9 1995/03/18 15:01:09 cgd Exp $ */ 3 4/* 5 * Copyright (c) 1988, 1990, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include <sys/types.h> 34#include <sys/resource.h> 35#include <sys/syslog.h> 36#include <sys/types.h> 37#include <sys/wait.h> 38 39#include <ctype.h> 40#include <fcntl.h> 41#include <sys/termios.h> 42#include <pwd.h> 43#include <signal.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <time.h> 48#include <unistd.h> 49#include <limits.h> 50#include <errno.h> 51#include <err.h> 52 53#include "pathnames.h" 54 55#ifdef DEBUG 56#undef _PATH_NOLOGIN 57#define _PATH_NOLOGIN "./nologin" 58#undef _PATH_FASTBOOT 59#define _PATH_FASTBOOT "./fastboot" 60#endif 61 62#define H *60*60LL 63#define M *60LL 64#define S *1LL 65#define TEN_HOURS (10*60*60) 66#define NOLOG_TIME (5*60) 67struct interval { 68 time_t timeleft; 69 time_t timetowait; 70} tlist[] = { 71 { 0, 0 }, 72 { 10 H, 5 H }, 73 { 5 H, 3 H }, 74 { 2 H, 1 H }, 75 { 1 H, 30 M }, 76 { 30 M, 10 M }, 77 { 20 M, 10 M }, 78 { 10 M, 5 M }, 79 { 5 M, 3 M }, 80 { 2 M, 1 M }, 81 { 1 M, 30 S }, 82 { 30 S, 30 S }, 83 { 0, 0 } 84}; 85const int tlistlen = sizeof(tlist) / sizeof(tlist[0]); 86#undef H 87#undef M 88#undef S 89 90static time_t offset, shuttime; 91static int dofast, dohalt, doreboot, dopower, dodump, mbuflen, nosync; 92static sig_atomic_t killflg, timed_out; 93static char *whom, mbuf[BUFSIZ]; 94 95void badtime(void); 96void __dead die_you_gravy_sucking_pig_dog(void); 97void doitfast(void); 98void __dead finish(int); 99void getoffset(char *); 100void __dead loop(void); 101void nolog(time_t); 102void timeout(int); 103void timewarn(time_t); 104void usage(void); 105 106int 107main(int argc, char *argv[]) 108{ 109 char when[64]; 110 char *p, *endp; 111 struct passwd *pw; 112 struct tm *lt; 113 int arglen, ch, len, readstdin = 0; 114 pid_t forkpid; 115 116 if (pledge("stdio rpath wpath cpath getpw tty id proc exec", NULL) == -1) 117 err(1, "pledge"); 118 119#ifndef DEBUG 120 if (geteuid()) 121 errx(1, "NOT super-user"); 122#endif 123 while ((ch = getopt(argc, argv, "dfhknpr-")) != -1) 124 switch (ch) { 125 case '-': 126 readstdin = 1; 127 break; 128 case 'd': 129 dodump = 1; 130 break; 131 case 'f': 132 dofast = 1; 133 break; 134 case 'h': 135 dohalt = 1; 136 break; 137 case 'k': 138 killflg = 1; 139 break; 140 case 'n': 141 nosync = 1; 142 break; 143 case 'p': 144 dopower = 1; 145 break; 146 case 'r': 147 doreboot = 1; 148 break; 149 default: 150 usage(); 151 } 152 argc -= optind; 153 argv += optind; 154 155 if (argc < 1) 156 usage(); 157 158 if (dofast && nosync) { 159 warnx("incompatible switches -f and -n."); 160 usage(); 161 } 162 if (doreboot && dohalt) { 163 warnx("incompatible switches -h and -r."); 164 usage(); 165 } 166 if (doreboot && dopower) { 167 warnx("incompatible switches -p and -r."); 168 usage(); 169 } 170 getoffset(*argv++); 171 172 if (*argv) { 173 for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 174 arglen = strlen(*argv); 175 if ((len -= arglen) <= 2) 176 break; 177 if (p != mbuf) 178 *p++ = ' '; 179 memcpy(p, *argv, arglen); 180 p += arglen; 181 } 182 *p = '\n'; 183 *++p = '\0'; 184 } 185 186 if (readstdin) { 187 p = mbuf; 188 endp = mbuf + sizeof(mbuf) - 2; 189 for (;;) { 190 if (!fgets(p, endp - p + 1, stdin)) 191 break; 192 for (; *p && p < endp; ++p) 193 ; 194 if (p == endp) { 195 *p = '\n'; 196 *++p = '\0'; 197 break; 198 } 199 } 200 } 201 mbuflen = strlen(mbuf); 202 203 if (offset > 0) { 204 shuttime = time(NULL) + offset; 205 lt = localtime(&shuttime); 206 if (lt != NULL) { 207 strftime(when, sizeof(when), "%a %b %e %T %Z %Y", lt); 208 printf("Shutdown at %s.\n", when); 209 } else 210 printf("Shutdown soon.\n"); 211 } else 212 printf("Shutdown NOW!\n"); 213 214 if (!(whom = getlogin())) 215 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 216 217#ifdef DEBUG 218 (void)putc('\n', stdout); 219#else 220 (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 221 222 forkpid = fork(); 223 if (forkpid == -1) 224 err(1, "fork"); 225 if (forkpid) { 226 (void)printf("shutdown: [pid %ld]\n", (long)forkpid); 227 exit(0); 228 } 229 setsid(); 230#endif 231 openlog("shutdown", LOG_CONS, LOG_AUTH); 232 loop(); 233 /* NOTREACHED */ 234} 235 236void 237loop(void) 238{ 239 struct timespec timeout; 240 int broadcast, i, logged; 241 242 broadcast = 1; 243 244 for (i = 0; i < tlistlen - 1; i++) { 245 if (offset > tlist[i + 1].timeleft) { 246 tlist[i].timeleft = offset; 247 tlist[i].timetowait = offset - tlist[i + 1].timeleft; 248 break; 249 } 250 } 251 252 /* 253 * Don't spam the users: skip our offset's warning broadcast if 254 * there's a broadcast scheduled after ours and it's relatively 255 * imminent. 256 */ 257 if (offset > TEN_HOURS || 258 (offset > 0 && tlist[i].timetowait < tlist[i+1].timetowait / 5)) 259 broadcast = 0; 260 261 for (logged = 0; i < tlistlen; i++) { 262 if (broadcast) 263 timewarn(tlist[i].timeleft); 264 broadcast = 1; 265 if (!logged && tlist[i].timeleft <= NOLOG_TIME) { 266 logged = 1; 267 nolog(tlist[i].timeleft); 268 } 269 timeout.tv_sec = tlist[i].timetowait; 270 timeout.tv_nsec = 0; 271 nanosleep(&timeout, NULL); 272 } 273 die_you_gravy_sucking_pig_dog(); 274} 275 276static char *restricted_environ[] = { 277 "PATH=" _PATH_STDPATH, 278 NULL 279}; 280 281void 282timewarn(time_t timeleft) 283{ 284 static char hostname[HOST_NAME_MAX+1]; 285 char when[64]; 286 struct tm *lt; 287 static int first; 288 int fd[2]; 289 pid_t pid, wpid; 290 291 if (!first++) 292 (void)gethostname(hostname, sizeof(hostname)); 293 294 if (pipe(fd) == -1) { 295 syslog(LOG_ERR, "pipe: %m"); 296 return; 297 } 298 switch (pid = fork()) { 299 case -1: 300 syslog(LOG_ERR, "fork: %m"); 301 close(fd[0]); 302 close(fd[1]); 303 return; 304 case 0: 305 if (dup2(fd[0], STDIN_FILENO) == -1) { 306 syslog(LOG_ERR, "dup2: %m"); 307 _exit(1); 308 } 309 if (fd[0] != STDIN_FILENO) 310 close(fd[0]); 311 close(fd[1]); 312 /* wall(1)'s undocumented '-n' flag suppresses its banner. */ 313 execle(_PATH_WALL, _PATH_WALL, "-n", (char *)NULL, 314 restricted_environ); 315 syslog(LOG_ERR, "%s: %m", _PATH_WALL); 316 _exit(1); 317 default: 318 close(fd[0]); 319 } 320 321 dprintf(fd[1], 322 "\007*** %sSystem shutdown message from %s@%s ***\007\n", 323 timeleft ? "": "FINAL ", whom, hostname); 324 325 if (timeleft > 10 * 60) { 326 shuttime = time(NULL) + timeleft; 327 lt = localtime(&shuttime); 328 strftime(when, sizeof(when), "%H:%M %Z", lt); 329 dprintf(fd[1], "System going down at %s\n\n", when); 330 } else if (timeleft > 59) { 331 dprintf(fd[1], "System going down in %lld minute%s\n\n", 332 (long long)(timeleft / 60), (timeleft > 60) ? "s" : ""); 333 } else if (timeleft) 334 dprintf(fd[1], "System going down in 30 seconds\n\n"); 335 else 336 dprintf(fd[1], "System going down IMMEDIATELY\n\n"); 337 338 if (mbuflen) 339 (void)write(fd[1], mbuf, mbuflen); 340 close(fd[1]); 341 342 /* 343 * If we wait longer than 30 seconds for wall(1) to exit we'll 344 * throw off our schedule. 345 */ 346 signal(SIGALRM, timeout); 347 siginterrupt(SIGALRM, 1); 348 alarm(30); 349 while ((wpid = wait(NULL)) != pid && !timed_out) 350 continue; 351 alarm(0); 352 signal(SIGALRM, SIG_DFL); 353 if (timed_out) { 354 syslog(LOG_NOTICE, 355 "wall[%ld] is taking too long to exit; continuing", 356 (long)pid); 357 timed_out = 0; 358 } 359} 360 361void 362timeout(int signo) 363{ 364 timed_out = 1; 365} 366 367void 368die_you_gravy_sucking_pig_dog(void) 369{ 370 371 syslog(LOG_NOTICE, "%s by %s: %s", 372 doreboot ? "reboot" : dopower ? "power-down" : dohalt ? "halt" : 373 "shutdown", whom, mbuf); 374 (void)sleep(2); 375 376 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 377 if (killflg) { 378 (void)printf("\rbut you'll have to do it yourself\r\n"); 379 finish(0); 380 } 381 if (dofast) 382 doitfast(); 383 384 if (pledge("stdio rpath wpath cpath tty id proc exec", NULL) == -1) 385 err(1, "pledge"); 386 387#ifdef DEBUG 388 if (doreboot) 389 (void)printf("reboot"); 390 else if (dopower) 391 (void)printf("power-down"); 392 else if (dohalt) 393 (void)printf("halt"); 394 if (nosync) 395 (void)printf(" no sync"); 396 if (dofast) 397 (void)printf(" no fsck"); 398 if (dodump) 399 (void)printf(" with dump"); 400 (void)printf("\nkill -HUP 1\n"); 401#else 402 if (dohalt || dopower || doreboot) { 403 char *args[10]; 404 char **arg, *path; 405 406 if (pledge("stdio exec", NULL) == -1) 407 err(1, "pledge"); 408 409 arg = &args[0]; 410 if (doreboot) { 411 path = _PATH_REBOOT; 412 *arg++ = "reboot"; 413 } else { 414 path = _PATH_HALT; 415 *arg++ = "halt"; 416 } 417 *arg++ = "-l"; 418 if (dopower) 419 *arg++ = "-p"; 420 if (nosync) 421 *arg++ = "-n"; 422 if (dodump) 423 *arg++ = "-d"; 424 *arg++ = NULL; 425 execve(path, args, NULL); 426 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", path); 427 warn("%s", path); 428 } 429 if (access(_PATH_RC, R_OK) != -1) { 430 pid_t pid; 431 struct termios t; 432 int fd; 433 434 switch ((pid = fork())) { 435 case -1: 436 break; 437 case 0: 438 if (revoke(_PATH_CONSOLE) == -1) 439 perror("revoke"); 440 if (setsid() == -1) 441 perror("setsid"); 442 fd = open(_PATH_CONSOLE, O_RDWR); 443 if (fd == -1) 444 perror("open"); 445 dup2(fd, 0); 446 dup2(fd, 1); 447 dup2(fd, 2); 448 if (fd > 2) 449 close(fd); 450 451 /* At a minimum... */ 452 tcgetattr(0, &t); 453 t.c_oflag |= (ONLCR | OPOST); 454 tcsetattr(0, TCSANOW, &t); 455 456 execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL); 457 _exit(1); 458 default: 459 waitpid(pid, NULL, 0); 460 } 461 } 462 (void)kill(1, SIGTERM); /* to single user */ 463#endif 464 finish(0); 465} 466 467#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 468 469void 470getoffset(char *timearg) 471{ 472 char when[64]; 473 const char *errstr; 474 struct tm *lt; 475 int this_year; 476 time_t minutes, now; 477 char *p; 478 479 if (!strcasecmp(timearg, "now")) { /* now */ 480 offset = 0; 481 return; 482 } 483 484 if (timearg[0] == '+') { /* +minutes */ 485 minutes = strtonum(timearg, 0, INT_MAX, &errstr); 486 if (errstr) 487 errx(1, "relative offset is %s: %s", errstr, timearg); 488 offset = minutes * 60; 489 return; 490 } 491 492 /* handle hh:mm by getting rid of the colon */ 493 for (p = timearg; *p; ++p) { 494 if (!isascii((unsigned char)*p) || !isdigit((unsigned char)*p)) { 495 if (*p == ':' && strlen(p) == 3) { 496 p[0] = p[1]; 497 p[1] = p[2]; 498 p[2] = '\0'; 499 } else 500 badtime(); 501 } 502 } 503 504 unsetenv("TZ"); /* OUR timezone */ 505 time(&now); 506 lt = localtime(&now); /* current time val */ 507 508 switch (strlen(timearg)) { 509 case 10: 510 this_year = lt->tm_year; 511 lt->tm_year = ATOI2(timearg); 512 /* 513 * check if the specified year is in the next century. 514 * allow for one year of user error as many people will 515 * enter n - 1 at the start of year n. 516 */ 517 if (lt->tm_year < (this_year % 100) - 1) 518 lt->tm_year += 100; 519 /* adjust for the year 2000 and beyond */ 520 lt->tm_year += (this_year - (this_year % 100)); 521 /* FALLTHROUGH */ 522 case 8: 523 lt->tm_mon = ATOI2(timearg); 524 if (--lt->tm_mon < 0 || lt->tm_mon > 11) 525 badtime(); 526 /* FALLTHROUGH */ 527 case 6: 528 lt->tm_mday = ATOI2(timearg); 529 if (lt->tm_mday < 1 || lt->tm_mday > 31) 530 badtime(); 531 /* FALLTHROUGH */ 532 case 4: 533 lt->tm_hour = ATOI2(timearg); 534 if (lt->tm_hour < 0 || lt->tm_hour > 23) 535 badtime(); 536 lt->tm_min = ATOI2(timearg); 537 if (lt->tm_min < 0 || lt->tm_min > 59) 538 badtime(); 539 lt->tm_sec = 0; 540 if ((shuttime = mktime(lt)) == -1) 541 badtime(); 542 if ((offset = shuttime - now) < 0) { 543 strftime(when, sizeof(when), "%a %b %e %T %Z %Y", lt); 544 errx(1, "time is already past: %s", when); 545 } 546 break; 547 default: 548 badtime(); 549 } 550} 551 552void 553doitfast(void) 554{ 555 int fastfd; 556 557 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 558 0664)) >= 0) { 559 dprintf(fastfd, "fastboot file for fsck\n"); 560 close(fastfd); 561 } 562} 563 564void 565nolog(time_t timeleft) 566{ 567 char when[64]; 568 struct tm *tm; 569 int logfd; 570 571 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 572 (void)signal(SIGINT, finish); 573 (void)signal(SIGHUP, finish); 574 (void)signal(SIGQUIT, finish); 575 (void)signal(SIGTERM, finish); 576 shuttime = time(NULL) + timeleft; 577 tm = localtime(&shuttime); 578 if (tm && (logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 579 0664)) >= 0) { 580 strftime(when, sizeof(when), "at %H:%M %Z", tm); 581 dprintf(logfd, "\n\nNO LOGINS: System going down %s\n\n", when); 582 close(logfd); 583 } 584} 585 586void 587finish(int signo) 588{ 589 if (!killflg) 590 (void)unlink(_PATH_NOLOGIN); 591 if (signo == 0) 592 exit(0); 593 else 594 _exit(0); 595} 596 597void 598badtime(void) 599{ 600 errx(1, "bad time format."); 601} 602 603void 604usage(void) 605{ 606 fprintf(stderr, 607 "usage: shutdown [-] [-dfhknpr] time [warning-message ...]\n"); 608 exit(1); 609} 610