1 /* $OpenBSD: shutdown.c,v 1.52 2018/08/03 17:09:22 deraadt 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) 67 struct 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 }; 85 const int tlistlen = sizeof(tlist) / sizeof(tlist[0]); 86 #undef H 87 #undef M 88 #undef S 89 90 static time_t offset, shuttime; 91 static int dofast, dohalt, doreboot, dopower, dodump, mbuflen, nosync; 92 static sig_atomic_t killflg, timed_out; 93 static char *whom, mbuf[BUFSIZ]; 94 95 void badtime(void); 96 void __dead die_you_gravy_sucking_pig_dog(void); 97 void doitfast(void); 98 void __dead finish(int); 99 void getoffset(char *); 100 void __dead loop(void); 101 void nolog(time_t); 102 void timeout(int); 103 void timewarn(time_t); 104 void usage(void); 105 106 int 107 main(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 #ifndef DEBUG 117 if (geteuid()) 118 errx(1, "NOT super-user"); 119 #endif 120 while ((ch = getopt(argc, argv, "dfhknpr-")) != -1) 121 switch (ch) { 122 case '-': 123 readstdin = 1; 124 break; 125 case 'd': 126 dodump = 1; 127 break; 128 case 'f': 129 dofast = 1; 130 break; 131 case 'h': 132 dohalt = 1; 133 break; 134 case 'k': 135 killflg = 1; 136 break; 137 case 'n': 138 nosync = 1; 139 break; 140 case 'p': 141 dopower = 1; 142 break; 143 case 'r': 144 doreboot = 1; 145 break; 146 default: 147 usage(); 148 } 149 argc -= optind; 150 argv += optind; 151 152 if (argc < 1) 153 usage(); 154 155 if (dofast && nosync) { 156 warnx("incompatible switches -f and -n."); 157 usage(); 158 } 159 if (doreboot && dohalt) { 160 warnx("incompatible switches -h and -r."); 161 usage(); 162 } 163 if (doreboot && dopower) { 164 warnx("incompatible switches -p and -r."); 165 usage(); 166 } 167 168 if (unveil(_PATH_CONSOLE, "rw") == -1) 169 err(1, "unveil"); 170 if (unveil(_PATH_RC, "r") == -1) 171 err(1, "unveil"); 172 if (unveil(_PATH_WALL, "x") == -1) 173 err(1, "unveil"); 174 if (unveil(_PATH_FASTBOOT, "wc") == -1) 175 err(1, "unveil"); 176 if (unveil(_PATH_NOLOGIN, "wc") == -1) 177 err(1, "unveil"); 178 if (dohalt || dopower) { 179 if (unveil(_PATH_HALT, "x") == -1) 180 err(1, "unveil"); 181 } else if (doreboot) { 182 if (unveil(_PATH_REBOOT, "x") == -1) 183 err(1, "unveil"); 184 } else { 185 if (unveil(_PATH_BSHELL, "x") == -1) 186 err(1, "unveil"); 187 } 188 if (pledge("stdio rpath wpath cpath getpw tty id proc exec", NULL) == -1) 189 err(1, "pledge"); 190 191 getoffset(*argv++); 192 193 if (*argv) { 194 for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 195 arglen = strlen(*argv); 196 if ((len -= arglen) <= 2) 197 break; 198 if (p != mbuf) 199 *p++ = ' '; 200 memcpy(p, *argv, arglen); 201 p += arglen; 202 } 203 *p = '\n'; 204 *++p = '\0'; 205 } 206 207 if (readstdin) { 208 p = mbuf; 209 endp = mbuf + sizeof(mbuf) - 2; 210 for (;;) { 211 if (!fgets(p, endp - p + 1, stdin)) 212 break; 213 for (; *p && p < endp; ++p) 214 ; 215 if (p == endp) { 216 *p = '\n'; 217 *++p = '\0'; 218 break; 219 } 220 } 221 } 222 mbuflen = strlen(mbuf); 223 224 if (offset > 0) { 225 shuttime = time(NULL) + offset; 226 lt = localtime(&shuttime); 227 if (lt != NULL) { 228 strftime(when, sizeof(when), "%a %b %e %T %Z %Y", lt); 229 printf("Shutdown at %s.\n", when); 230 } else 231 printf("Shutdown soon.\n"); 232 } else 233 printf("Shutdown NOW!\n"); 234 235 if (!(whom = getlogin())) 236 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 237 238 #ifdef DEBUG 239 (void)putc('\n', stdout); 240 #else 241 (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 242 243 forkpid = fork(); 244 if (forkpid == -1) 245 err(1, "fork"); 246 if (forkpid) { 247 (void)printf("shutdown: [pid %ld]\n", (long)forkpid); 248 exit(0); 249 } 250 setsid(); 251 #endif 252 openlog("shutdown", LOG_CONS, LOG_AUTH); 253 loop(); 254 /* NOTREACHED */ 255 } 256 257 void 258 loop(void) 259 { 260 struct timespec timeout; 261 int broadcast, i, logged; 262 263 broadcast = 1; 264 265 for (i = 0; i < tlistlen - 1; i++) { 266 if (offset > tlist[i + 1].timeleft) { 267 tlist[i].timeleft = offset; 268 tlist[i].timetowait = offset - tlist[i + 1].timeleft; 269 break; 270 } 271 } 272 273 /* 274 * Don't spam the users: skip our offset's warning broadcast if 275 * there's a broadcast scheduled after ours and it's relatively 276 * imminent. 277 */ 278 if (offset > TEN_HOURS || 279 (offset > 0 && tlist[i].timetowait < tlist[i+1].timetowait / 5)) 280 broadcast = 0; 281 282 for (logged = 0; i < tlistlen; i++) { 283 if (broadcast) 284 timewarn(tlist[i].timeleft); 285 broadcast = 1; 286 if (!logged && tlist[i].timeleft <= NOLOG_TIME) { 287 logged = 1; 288 nolog(tlist[i].timeleft); 289 } 290 timeout.tv_sec = tlist[i].timetowait; 291 timeout.tv_nsec = 0; 292 nanosleep(&timeout, NULL); 293 } 294 die_you_gravy_sucking_pig_dog(); 295 } 296 297 static char *restricted_environ[] = { 298 "PATH=" _PATH_STDPATH, 299 NULL 300 }; 301 302 void 303 timewarn(time_t timeleft) 304 { 305 static char hostname[HOST_NAME_MAX+1]; 306 char when[64]; 307 struct tm *lt; 308 static int first; 309 int fd[2]; 310 pid_t pid, wpid; 311 312 if (!first++) 313 (void)gethostname(hostname, sizeof(hostname)); 314 315 if (pipe(fd) == -1) { 316 syslog(LOG_ERR, "pipe: %m"); 317 return; 318 } 319 switch (pid = fork()) { 320 case -1: 321 syslog(LOG_ERR, "fork: %m"); 322 close(fd[0]); 323 close(fd[1]); 324 return; 325 case 0: 326 if (dup2(fd[0], STDIN_FILENO) == -1) { 327 syslog(LOG_ERR, "dup2: %m"); 328 _exit(1); 329 } 330 if (fd[0] != STDIN_FILENO) 331 close(fd[0]); 332 close(fd[1]); 333 /* wall(1)'s undocumented '-n' flag suppresses its banner. */ 334 execle(_PATH_WALL, _PATH_WALL, "-n", (char *)NULL, 335 restricted_environ); 336 syslog(LOG_ERR, "%s: %m", _PATH_WALL); 337 _exit(1); 338 default: 339 close(fd[0]); 340 } 341 342 dprintf(fd[1], 343 "\007*** %sSystem shutdown message from %s@%s ***\007\n", 344 timeleft ? "": "FINAL ", whom, hostname); 345 346 if (timeleft > 10 * 60) { 347 shuttime = time(NULL) + timeleft; 348 lt = localtime(&shuttime); 349 strftime(when, sizeof(when), "%H:%M %Z", lt); 350 dprintf(fd[1], "System going down at %s\n\n", when); 351 } else if (timeleft > 59) { 352 dprintf(fd[1], "System going down in %lld minute%s\n\n", 353 (long long)(timeleft / 60), (timeleft > 60) ? "s" : ""); 354 } else if (timeleft) 355 dprintf(fd[1], "System going down in 30 seconds\n\n"); 356 else 357 dprintf(fd[1], "System going down IMMEDIATELY\n\n"); 358 359 if (mbuflen) 360 (void)write(fd[1], mbuf, mbuflen); 361 close(fd[1]); 362 363 /* 364 * If we wait longer than 30 seconds for wall(1) to exit we'll 365 * throw off our schedule. 366 */ 367 signal(SIGALRM, timeout); 368 siginterrupt(SIGALRM, 1); 369 alarm(30); 370 while ((wpid = wait(NULL)) != pid && !timed_out) 371 continue; 372 alarm(0); 373 signal(SIGALRM, SIG_DFL); 374 if (timed_out) { 375 syslog(LOG_NOTICE, 376 "wall[%ld] is taking too long to exit; continuing", 377 (long)pid); 378 timed_out = 0; 379 } 380 } 381 382 void 383 timeout(int signo) 384 { 385 timed_out = 1; 386 } 387 388 void 389 die_you_gravy_sucking_pig_dog(void) 390 { 391 392 syslog(LOG_NOTICE, "%s by %s: %s", 393 doreboot ? "reboot" : dopower ? "power-down" : dohalt ? "halt" : 394 "shutdown", whom, mbuf); 395 (void)sleep(2); 396 397 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 398 if (killflg) { 399 (void)printf("\rbut you'll have to do it yourself\r\n"); 400 finish(0); 401 } 402 if (dofast) 403 doitfast(); 404 405 if (pledge("stdio rpath wpath cpath tty id proc exec", NULL) == -1) 406 err(1, "pledge"); 407 408 #ifdef DEBUG 409 if (doreboot) 410 (void)printf("reboot"); 411 else if (dopower) 412 (void)printf("power-down"); 413 else if (dohalt) 414 (void)printf("halt"); 415 if (nosync) 416 (void)printf(" no sync"); 417 if (dofast) 418 (void)printf(" no fsck"); 419 if (dodump) 420 (void)printf(" with dump"); 421 (void)printf("\nkill -HUP 1\n"); 422 #else 423 if (dohalt || dopower || doreboot) { 424 char *args[10]; 425 char **arg, *path; 426 427 if (pledge("stdio exec", NULL) == -1) 428 err(1, "pledge"); 429 430 arg = &args[0]; 431 if (doreboot) { 432 path = _PATH_REBOOT; 433 *arg++ = "reboot"; 434 } else { 435 path = _PATH_HALT; 436 *arg++ = "halt"; 437 } 438 *arg++ = "-l"; 439 if (dopower) 440 *arg++ = "-p"; 441 if (nosync) 442 *arg++ = "-n"; 443 if (dodump) 444 *arg++ = "-d"; 445 *arg++ = NULL; 446 execve(path, args, NULL); 447 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", path); 448 warn("%s", path); 449 } 450 if (access(_PATH_RC, R_OK) != -1) { 451 pid_t pid; 452 struct termios t; 453 int fd; 454 455 switch ((pid = fork())) { 456 case -1: 457 break; 458 case 0: 459 if (revoke(_PATH_CONSOLE) == -1) 460 perror("revoke"); 461 if (setsid() == -1) 462 perror("setsid"); 463 fd = open(_PATH_CONSOLE, O_RDWR); 464 if (fd == -1) 465 perror("open"); 466 dup2(fd, 0); 467 dup2(fd, 1); 468 dup2(fd, 2); 469 if (fd > 2) 470 close(fd); 471 472 /* At a minimum... */ 473 tcgetattr(0, &t); 474 t.c_oflag |= (ONLCR | OPOST); 475 tcsetattr(0, TCSANOW, &t); 476 477 execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL); 478 _exit(1); 479 default: 480 waitpid(pid, NULL, 0); 481 } 482 } 483 (void)kill(1, SIGTERM); /* to single user */ 484 #endif 485 finish(0); 486 } 487 488 #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 489 490 void 491 getoffset(char *timearg) 492 { 493 char when[64]; 494 const char *errstr; 495 struct tm *lt; 496 int this_year; 497 time_t minutes, now; 498 char *p; 499 500 if (!strcasecmp(timearg, "now")) { /* now */ 501 offset = 0; 502 return; 503 } 504 505 if (timearg[0] == '+') { /* +minutes */ 506 minutes = strtonum(timearg, 0, INT_MAX, &errstr); 507 if (errstr) 508 errx(1, "relative offset is %s: %s", errstr, timearg); 509 offset = minutes * 60; 510 return; 511 } 512 513 /* handle hh:mm by getting rid of the colon */ 514 for (p = timearg; *p; ++p) { 515 if (!isascii((unsigned char)*p) || !isdigit((unsigned char)*p)) { 516 if (*p == ':' && strlen(p) == 3) { 517 p[0] = p[1]; 518 p[1] = p[2]; 519 p[2] = '\0'; 520 } else 521 badtime(); 522 } 523 } 524 525 unsetenv("TZ"); /* OUR timezone */ 526 time(&now); 527 lt = localtime(&now); /* current time val */ 528 529 switch (strlen(timearg)) { 530 case 10: 531 this_year = lt->tm_year; 532 lt->tm_year = ATOI2(timearg); 533 /* 534 * check if the specified year is in the next century. 535 * allow for one year of user error as many people will 536 * enter n - 1 at the start of year n. 537 */ 538 if (lt->tm_year < (this_year % 100) - 1) 539 lt->tm_year += 100; 540 /* adjust for the year 2000 and beyond */ 541 lt->tm_year += (this_year - (this_year % 100)); 542 /* FALLTHROUGH */ 543 case 8: 544 lt->tm_mon = ATOI2(timearg); 545 if (--lt->tm_mon < 0 || lt->tm_mon > 11) 546 badtime(); 547 /* FALLTHROUGH */ 548 case 6: 549 lt->tm_mday = ATOI2(timearg); 550 if (lt->tm_mday < 1 || lt->tm_mday > 31) 551 badtime(); 552 /* FALLTHROUGH */ 553 case 4: 554 lt->tm_hour = ATOI2(timearg); 555 if (lt->tm_hour < 0 || lt->tm_hour > 23) 556 badtime(); 557 lt->tm_min = ATOI2(timearg); 558 if (lt->tm_min < 0 || lt->tm_min > 59) 559 badtime(); 560 lt->tm_sec = 0; 561 if ((shuttime = mktime(lt)) == -1) 562 badtime(); 563 if ((offset = shuttime - now) < 0) { 564 strftime(when, sizeof(when), "%a %b %e %T %Z %Y", lt); 565 errx(1, "time is already past: %s", when); 566 } 567 break; 568 default: 569 badtime(); 570 } 571 } 572 573 void 574 doitfast(void) 575 { 576 int fastfd; 577 578 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 579 0664)) >= 0) { 580 dprintf(fastfd, "fastboot file for fsck\n"); 581 close(fastfd); 582 } 583 } 584 585 void 586 nolog(time_t timeleft) 587 { 588 char when[64]; 589 struct tm *tm; 590 int logfd; 591 592 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 593 (void)signal(SIGINT, finish); 594 (void)signal(SIGHUP, finish); 595 (void)signal(SIGQUIT, finish); 596 (void)signal(SIGTERM, finish); 597 shuttime = time(NULL) + timeleft; 598 tm = localtime(&shuttime); 599 if (tm && (logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 600 0664)) >= 0) { 601 strftime(when, sizeof(when), "at %H:%M %Z", tm); 602 dprintf(logfd, "\n\nNO LOGINS: System going down %s\n\n", when); 603 close(logfd); 604 } 605 } 606 607 void 608 finish(int signo) 609 { 610 if (!killflg) 611 (void)unlink(_PATH_NOLOGIN); 612 if (signo == 0) 613 exit(0); 614 else 615 _exit(0); 616 } 617 618 void 619 badtime(void) 620 { 621 errx(1, "bad time format."); 622 } 623 624 void 625 usage(void) 626 { 627 fprintf(stderr, 628 "usage: shutdown [-] [-dfhknpr] time [warning-message ...]\n"); 629 exit(1); 630 } 631