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 #if 0 33 #ifndef lint 34 static const char copyright[] = 35 "@(#) Copyright (c) 1988, 1990, 1993\n\ 36 The Regents of the University of California. All rights reserved.\n"; 37 #endif /* not lint */ 38 39 #endif 40 #include <sys/cdefs.h> 41 #include <sys/param.h> 42 #include <sys/boottrace.h> 43 #include <sys/resource.h> 44 #include <sys/syslog.h> 45 #include <sys/time.h> 46 47 #include <ctype.h> 48 #include <err.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <paths.h> 52 #include <pwd.h> 53 #include <setjmp.h> 54 #include <signal.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 60 #ifdef DEBUG 61 #undef _PATH_NOLOGIN 62 #define _PATH_NOLOGIN "./nologin" 63 #endif 64 65 #define H *60*60 66 #define M *60 67 #define S *1 68 #define NOLOG_TIME 5*60 69 static struct interval { 70 int timeleft, timetowait; 71 } tlist[] = { 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 #undef H 86 #undef M 87 #undef S 88 89 static time_t offset, shuttime; 90 static int docycle, dohalt, dopower, doreboot, killflg, mbuflen, oflag; 91 static char mbuf[BUFSIZ]; 92 static const char *nosync, *whom; 93 94 static void badtime(void); 95 static void die_you_gravy_sucking_pig_dog(void); 96 static void finish(int); 97 static void getoffset(char *); 98 static void loop(void); 99 static void nolog(void); 100 static void timeout(int); 101 static void timewarn(int); 102 static void usage(const char *); 103 104 extern const char **environ; 105 106 int 107 main(int argc, char **argv) 108 { 109 char *p, *endp; 110 struct passwd *pw; 111 int arglen, ch, len, readstdin; 112 113 #ifndef DEBUG 114 if (geteuid()) 115 errx(1, "NOT super-user"); 116 #endif 117 118 nosync = NULL; 119 readstdin = 0; 120 121 /* 122 * Test for the special case where the utility is called as 123 * "poweroff", for which it runs 'shutdown -p now'. 124 */ 125 if ((p = strrchr(argv[0], '/')) == NULL) 126 p = argv[0]; 127 else 128 ++p; 129 if (strcmp(p, "poweroff") == 0) { 130 if (getopt(argc, argv, "") != -1) 131 usage((char *)NULL); 132 argc -= optind; 133 argv += optind; 134 if (argc != 0) 135 usage((char *)NULL); 136 dopower = 1; 137 offset = 0; 138 (void)time(&shuttime); 139 goto poweroff; 140 } 141 142 while ((ch = getopt(argc, argv, "-chknopr")) != -1) 143 switch (ch) { 144 case '-': 145 readstdin = 1; 146 break; 147 case 'c': 148 docycle = 1; 149 break; 150 case 'h': 151 dohalt = 1; 152 break; 153 case 'k': 154 killflg = 1; 155 break; 156 case 'n': 157 nosync = "-n"; 158 break; 159 case 'o': 160 oflag = 1; 161 break; 162 case 'p': 163 dopower = 1; 164 break; 165 case 'r': 166 doreboot = 1; 167 break; 168 case '?': 169 default: 170 usage((char *)NULL); 171 } 172 argc -= optind; 173 argv += optind; 174 175 if (argc < 1) 176 usage((char *)NULL); 177 178 if (killflg + doreboot + dohalt + dopower + docycle > 1) 179 usage("incompatible switches -c, -h, -k, -p and -r"); 180 181 if (oflag && !(dohalt || dopower || doreboot || docycle)) 182 usage("-o requires -c, -h, -p or -r"); 183 184 if (nosync != NULL && !oflag) 185 usage("-n requires -o"); 186 187 getoffset(*argv++); 188 189 poweroff: 190 if (*argv) { 191 for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 192 arglen = strlen(*argv); 193 if ((len -= arglen) <= 2) 194 break; 195 if (p != mbuf) 196 *p++ = ' '; 197 memmove(p, *argv, arglen); 198 p += arglen; 199 } 200 *p = '\n'; 201 *++p = '\0'; 202 } 203 204 if (readstdin) { 205 p = mbuf; 206 endp = mbuf + sizeof(mbuf) - 2; 207 for (;;) { 208 if (!fgets(p, endp - p + 1, stdin)) 209 break; 210 for (; *p && p < endp; ++p); 211 if (p == endp) { 212 *p = '\n'; 213 *++p = '\0'; 214 break; 215 } 216 } 217 } 218 mbuflen = strlen(mbuf); 219 220 if (offset) { 221 BOOTTRACE("Shutdown at %s", ctime(&shuttime)); 222 (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 223 } else { 224 BOOTTRACE("Shutdown NOW!"); 225 (void)printf("Shutdown NOW!\n"); 226 } 227 228 if (!(whom = getlogin())) 229 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 230 231 #ifdef DEBUG 232 (void)putc('\n', stdout); 233 #else 234 (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 235 { 236 int forkpid; 237 238 forkpid = fork(); 239 if (forkpid == -1) 240 err(1, "fork"); 241 if (forkpid) 242 errx(0, "[pid %d]", forkpid); 243 } 244 setsid(); 245 #endif 246 openlog("shutdown", LOG_CONS, LOG_AUTH); 247 loop(); 248 return(0); 249 } 250 251 static void 252 loop(void) 253 { 254 struct interval *tp; 255 u_int sltime; 256 int logged; 257 258 if (offset <= NOLOG_TIME) { 259 logged = 1; 260 nolog(); 261 } 262 else 263 logged = 0; 264 tp = tlist; 265 if (tp->timeleft < offset) 266 (void)sleep((u_int)(offset - tp->timeleft)); 267 else { 268 while (tp->timeleft && offset < tp->timeleft) 269 ++tp; 270 /* 271 * Warn now, if going to sleep more than a fifth of 272 * the next wait time. 273 */ 274 if ((sltime = offset - tp->timeleft)) { 275 if (sltime > (u_int)(tp->timetowait / 5)) 276 timewarn(offset); 277 (void)sleep(sltime); 278 } 279 } 280 for (;; ++tp) { 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]] time [warning-message ...]\n" 597 " poweroff\n"); 598 exit(1); 599 } 600