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