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