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