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