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