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