1 /* $OpenBSD: shutdown.c,v 1.45 2016/09/01 09:50:38 deraadt 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 <setjmp.h> 44 #include <signal.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <time.h> 49 #include <unistd.h> 50 #include <limits.h> 51 #include <errno.h> 52 #include <err.h> 53 54 #include "pathnames.h" 55 56 #ifdef DEBUG 57 #undef _PATH_NOLOGIN 58 #define _PATH_NOLOGIN "./nologin" 59 #undef _PATH_FASTBOOT 60 #define _PATH_FASTBOOT "./fastboot" 61 #endif 62 63 #define H *60*60 64 #define M *60 65 #define S *1 66 #define NOLOG_TIME 5*60 67 struct interval { 68 int timeleft, timetowait; 69 } tlist[] = { 70 { 10 H, 5 H }, 71 { 5 H, 3 H }, 72 { 2 H, 1 H }, 73 { 1 H, 30 M }, 74 { 30 M, 10 M }, 75 { 20 M, 10 M }, 76 { 10 M, 5 M }, 77 { 5 M, 3 M }, 78 { 2 M, 1 M }, 79 { 1 M, 30 S }, 80 { 30 S, 30 S }, 81 { 0, 0 } 82 }; 83 #undef H 84 #undef M 85 #undef S 86 87 static time_t offset, shuttime; 88 static int dofast, dohalt, doreboot, dopower, dodump, mbuflen, nosync; 89 static sig_atomic_t killflg; 90 static char *whom, mbuf[BUFSIZ]; 91 92 void badtime(void); 93 void __dead die_you_gravy_sucking_pig_dog(void); 94 void doitfast(void); 95 void __dead finish(int); 96 void getoffset(char *); 97 void __dead loop(void); 98 void nolog(void); 99 void timeout(int); 100 void timewarn(int); 101 void usage(void); 102 103 int 104 main(int argc, char *argv[]) 105 { 106 int arglen, ch, len, readstdin = 0; 107 struct passwd *pw; 108 char *p, *endp; 109 pid_t forkpid; 110 111 if (pledge("stdio rpath wpath cpath getpw tty id proc exec", NULL) == -1) 112 err(1, "pledge"); 113 114 #ifndef DEBUG 115 if (geteuid()) 116 errx(1, "NOT super-user"); 117 #endif 118 while ((ch = getopt(argc, argv, "dfhknpr-")) != -1) 119 switch (ch) { 120 case '-': 121 readstdin = 1; 122 break; 123 case 'd': 124 dodump = 1; 125 break; 126 case 'f': 127 dofast = 1; 128 break; 129 case 'h': 130 dohalt = 1; 131 break; 132 case 'k': 133 killflg = 1; 134 break; 135 case 'n': 136 nosync = 1; 137 break; 138 case 'p': 139 dopower = 1; 140 break; 141 case 'r': 142 doreboot = 1; 143 break; 144 default: 145 usage(); 146 } 147 argc -= optind; 148 argv += optind; 149 150 if (argc < 1) 151 usage(); 152 153 if (dofast && nosync) { 154 (void)fprintf(stderr, 155 "shutdown: incompatible switches -f and -n.\n"); 156 usage(); 157 } 158 if (doreboot && dohalt) { 159 (void)fprintf(stderr, 160 "shutdown: incompatible switches -h and -r.\n"); 161 usage(); 162 } 163 if (doreboot && dopower) { 164 (void)fprintf(stderr, 165 "shutdown: incompatible switches -p and -r.\n"); 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 233 void 234 loop(void) 235 { 236 struct interval *tp; 237 u_int sltime; 238 int logged; 239 240 if (offset <= NOLOG_TIME) { 241 logged = 1; 242 nolog(); 243 } else 244 logged = 0; 245 tp = tlist; 246 if (tp->timeleft < offset) 247 (void)sleep((u_int)(offset - tp->timeleft)); 248 else { 249 while (offset < tp->timeleft) 250 ++tp; 251 /* 252 * Warn now, if going to sleep more than a fifth of 253 * the next wait time. 254 */ 255 if ((sltime = offset - tp->timeleft)) { 256 if (sltime > tp->timetowait / 5) 257 timewarn(offset); 258 (void)sleep(sltime); 259 } 260 } 261 for (;; ++tp) { 262 timewarn(tp->timeleft); 263 if (!logged && tp->timeleft <= NOLOG_TIME) { 264 logged = 1; 265 nolog(); 266 } 267 (void)sleep((u_int)tp->timetowait); 268 if (!tp->timeleft) 269 break; 270 } 271 die_you_gravy_sucking_pig_dog(); 272 } 273 274 static jmp_buf alarmbuf; 275 276 static char *restricted_environ[] = { 277 "PATH=" _PATH_STDPATH, 278 NULL 279 }; 280 281 void 282 timewarn(int timeleft) 283 { 284 static char hostname[HOST_NAME_MAX+1]; 285 char wcmd[PATH_MAX + 4]; 286 extern char **environ; 287 static int first; 288 FILE *pf; 289 290 if (!first++) 291 (void)gethostname(hostname, sizeof(hostname)); 292 293 /* undoc -n option to wall suppresses normal wall banner */ 294 (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 295 environ = restricted_environ; 296 if (!(pf = popen(wcmd, "w"))) { 297 syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 298 return; 299 } 300 301 (void)fprintf(pf, 302 "\007*** %sSystem shutdown message from %s@%s ***\007\n", 303 timeleft ? "": "FINAL ", whom, hostname); 304 305 if (timeleft > 10*60) { 306 struct tm *tm = localtime(&shuttime); 307 308 fprintf(pf, "System going down at %d:%02d\n\n", 309 tm->tm_hour, tm->tm_min); 310 } else if (timeleft > 59) 311 (void)fprintf(pf, "System going down in %d minute%s\n\n", 312 timeleft / 60, (timeleft > 60) ? "s" : ""); 313 else if (timeleft) 314 (void)fprintf(pf, "System going down in 30 seconds\n\n"); 315 else 316 (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 317 318 if (mbuflen) 319 (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 320 321 /* 322 * play some games, just in case wall doesn't come back 323 * probably unnecessary, given that wall is careful. 324 */ 325 if (!setjmp(alarmbuf)) { 326 (void)signal(SIGALRM, timeout); 327 (void)alarm((u_int)30); 328 (void)pclose(pf); 329 (void)alarm((u_int)0); 330 (void)signal(SIGALRM, SIG_DFL); 331 } 332 } 333 334 void 335 timeout(int signo) 336 { 337 longjmp(alarmbuf, 1); /* XXX signal/longjmp resource leaks */ 338 } 339 340 void 341 die_you_gravy_sucking_pig_dog(void) 342 { 343 344 syslog(LOG_NOTICE, "%s by %s: %s", 345 doreboot ? "reboot" : dopower ? "power-down" : dohalt ? "halt" : 346 "shutdown", whom, mbuf); 347 (void)sleep(2); 348 349 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 350 if (killflg) { 351 (void)printf("\rbut you'll have to do it yourself\r\n"); 352 finish(0); 353 } 354 if (dofast) 355 doitfast(); 356 357 if (pledge("stdio rpath wpath cpath tty id proc exec", NULL) == -1) 358 err(1, "pledge"); 359 360 #ifdef DEBUG 361 if (doreboot) 362 (void)printf("reboot"); 363 else if (dopower) 364 (void)printf("power-down"); 365 else if (dohalt) 366 (void)printf("halt"); 367 if (nosync) 368 (void)printf(" no sync"); 369 if (dofast) 370 (void)printf(" no fsck"); 371 if (dodump) 372 (void)printf(" with dump"); 373 (void)printf("\nkill -HUP 1\n"); 374 #else 375 if (dohalt || dopower || doreboot) { 376 char *args[10]; 377 char **arg, *path; 378 379 if (pledge("stdio exec", NULL) == -1) 380 err(1, "pledge"); 381 382 arg = &args[0]; 383 if (doreboot) { 384 path = _PATH_REBOOT; 385 *arg++ = "reboot"; 386 } else { 387 path = _PATH_HALT; 388 *arg++ = "halt"; 389 } 390 *arg++ = "-l"; 391 if (dopower) 392 *arg++ = "-p"; 393 if (nosync) 394 *arg++ = "-n"; 395 if (dodump) 396 *arg++ = "-d"; 397 *arg++ = NULL; 398 execve(path, args, NULL); 399 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", path); 400 warn(path); 401 } 402 if (access(_PATH_RC, R_OK) != -1) { 403 pid_t pid; 404 struct termios t; 405 int fd; 406 407 switch ((pid = fork())) { 408 case -1: 409 break; 410 case 0: 411 if (revoke(_PATH_CONSOLE) == -1) 412 perror("revoke"); 413 if (setsid() == -1) 414 perror("setsid"); 415 fd = open(_PATH_CONSOLE, O_RDWR); 416 if (fd == -1) 417 perror("open"); 418 dup2(fd, 0); 419 dup2(fd, 1); 420 dup2(fd, 2); 421 if (fd > 2) 422 close(fd); 423 424 /* At a minimum... */ 425 tcgetattr(0, &t); 426 t.c_oflag |= (ONLCR | OPOST); 427 tcsetattr(0, TCSANOW, &t); 428 429 execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL); 430 _exit(1); 431 default: 432 waitpid(pid, NULL, 0); 433 } 434 } 435 (void)kill(1, SIGTERM); /* to single user */ 436 #endif 437 finish(0); 438 } 439 440 #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 441 442 void 443 getoffset(char *timearg) 444 { 445 struct tm *lt; 446 int this_year; 447 time_t now; 448 char *p; 449 450 if (!strcasecmp(timearg, "now")) { /* now */ 451 offset = 0; 452 return; 453 } 454 455 (void)time(&now); 456 if (*timearg == '+') { /* +minutes */ 457 const char *errstr; 458 459 offset = strtonum(++timearg, 0, INT_MAX, &errstr); 460 if (errstr) 461 badtime(); 462 offset *= 60; 463 shuttime = now + offset; 464 return; 465 } 466 467 /* handle hh:mm by getting rid of the colon */ 468 for (p = timearg; *p; ++p) { 469 if (!isascii((unsigned char)*p) || !isdigit((unsigned char)*p)) { 470 if (*p == ':' && strlen(p) == 3) { 471 p[0] = p[1]; 472 p[1] = p[2]; 473 p[2] = '\0'; 474 } else 475 badtime(); 476 } 477 } 478 479 unsetenv("TZ"); /* OUR timezone */ 480 lt = localtime(&now); /* current time val */ 481 482 switch (strlen(timearg)) { 483 case 10: 484 this_year = lt->tm_year; 485 lt->tm_year = ATOI2(timearg); 486 /* 487 * check if the specified year is in the next century. 488 * allow for one year of user error as many people will 489 * enter n - 1 at the start of year n. 490 */ 491 if (lt->tm_year < (this_year % 100) - 1) 492 lt->tm_year += 100; 493 /* adjust for the year 2000 and beyond */ 494 lt->tm_year += (this_year - (this_year % 100)); 495 /* FALLTHROUGH */ 496 case 8: 497 lt->tm_mon = ATOI2(timearg); 498 if (--lt->tm_mon < 0 || lt->tm_mon > 11) 499 badtime(); 500 /* FALLTHROUGH */ 501 case 6: 502 lt->tm_mday = ATOI2(timearg); 503 if (lt->tm_mday < 1 || lt->tm_mday > 31) 504 badtime(); 505 /* FALLTHROUGH */ 506 case 4: 507 lt->tm_hour = ATOI2(timearg); 508 if (lt->tm_hour < 0 || lt->tm_hour > 23) 509 badtime(); 510 lt->tm_min = ATOI2(timearg); 511 if (lt->tm_min < 0 || lt->tm_min > 59) 512 badtime(); 513 lt->tm_sec = 0; 514 if ((shuttime = mktime(lt)) == -1) 515 badtime(); 516 if ((offset = shuttime - now) < 0) 517 errx(1, "that time is already past."); 518 break; 519 default: 520 badtime(); 521 } 522 } 523 524 void 525 doitfast(void) 526 { 527 int fastfd; 528 529 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 530 0664)) >= 0) { 531 dprintf(fastfd, "fastboot file for fsck\n"); 532 close(fastfd); 533 } 534 } 535 536 void 537 nolog(void) 538 { 539 int logfd; 540 struct tm *tm; 541 542 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 543 (void)signal(SIGINT, finish); 544 (void)signal(SIGHUP, finish); 545 (void)signal(SIGQUIT, finish); 546 (void)signal(SIGTERM, finish); 547 tm = localtime(&shuttime); 548 if (tm && (logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 549 0664)) >= 0) { 550 dprintf(logfd, 551 "\n\nNO LOGINS: System going down at %d:%02d\n\n", 552 tm->tm_hour, tm->tm_min); 553 close(logfd); 554 } 555 } 556 557 void 558 finish(int signo) 559 { 560 if (!killflg) 561 (void)unlink(_PATH_NOLOGIN); 562 if (signo == 0) 563 exit(0); 564 else 565 _exit(0); 566 } 567 568 void 569 badtime(void) 570 { 571 errx(1, "bad time format."); 572 } 573 574 void 575 usage(void) 576 { 577 fprintf(stderr, 578 "usage: shutdown [-] [-dfhknpr] time [warning-message ...]\n"); 579 exit(1); 580 } 581