shutdown.c revision 1.47
1/* $OpenBSD: shutdown.c,v 1.47 2018/02/04 04:28:41 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 <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 67struct 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 87static time_t offset, shuttime; 88static int dofast, dohalt, doreboot, dopower, dodump, mbuflen, nosync; 89static sig_atomic_t killflg; 90static char *whom, mbuf[BUFSIZ]; 91 92void badtime(void); 93void __dead die_you_gravy_sucking_pig_dog(void); 94void doitfast(void); 95void __dead finish(int); 96void getoffset(char *); 97void __dead loop(void); 98void nolog(void); 99void timeout(int); 100void timewarn(int); 101void usage(void); 102 103int 104main(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 warnx("incompatible switches -f and -n."); 155 usage(); 156 } 157 if (doreboot && dohalt) { 158 warnx("incompatible switches -h and -r."); 159 usage(); 160 } 161 if (doreboot && dopower) { 162 warnx("incompatible switches -p and -r."); 163 usage(); 164 } 165 getoffset(*argv++); 166 167 if (*argv) { 168 for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 169 arglen = strlen(*argv); 170 if ((len -= arglen) <= 2) 171 break; 172 if (p != mbuf) 173 *p++ = ' '; 174 memcpy(p, *argv, arglen); 175 p += arglen; 176 } 177 *p = '\n'; 178 *++p = '\0'; 179 } 180 181 if (readstdin) { 182 p = mbuf; 183 endp = mbuf + sizeof(mbuf) - 2; 184 for (;;) { 185 if (!fgets(p, endp - p + 1, stdin)) 186 break; 187 for (; *p && p < endp; ++p) 188 ; 189 if (p == endp) { 190 *p = '\n'; 191 *++p = '\0'; 192 break; 193 } 194 } 195 } 196 mbuflen = strlen(mbuf); 197 198 if (offset) { 199 char *ct = ctime(&shuttime); 200 201 if (ct) 202 printf("Shutdown at %.24s.\n", ct); 203 else 204 printf("Shutdown soon.\n"); 205 } else 206 (void)printf("Shutdown NOW!\n"); 207 208 if (!(whom = getlogin())) 209 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 210 211#ifdef DEBUG 212 (void)putc('\n', stdout); 213#else 214 (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 215 216 forkpid = fork(); 217 if (forkpid == -1) 218 err(1, "fork"); 219 if (forkpid) { 220 (void)printf("shutdown: [pid %ld]\n", (long)forkpid); 221 exit(0); 222 } 223 setsid(); 224#endif 225 openlog("shutdown", LOG_CONS, LOG_AUTH); 226 loop(); 227 /* NOTREACHED */ 228} 229 230void 231loop(void) 232{ 233 struct interval *tp; 234 u_int sltime; 235 int logged; 236 237 if (offset <= NOLOG_TIME) { 238 logged = 1; 239 nolog(); 240 } else 241 logged = 0; 242 tp = tlist; 243 if (tp->timeleft < offset) 244 (void)sleep((u_int)(offset - tp->timeleft)); 245 else { 246 while (offset < tp->timeleft) 247 ++tp; 248 /* 249 * Warn now, if going to sleep more than a fifth of 250 * the next wait time. 251 */ 252 if ((sltime = offset - tp->timeleft)) { 253 if (sltime > tp->timetowait / 5) 254 timewarn(offset); 255 (void)sleep(sltime); 256 } 257 } 258 for (;; ++tp) { 259 timewarn(tp->timeleft); 260 if (!logged && tp->timeleft <= NOLOG_TIME) { 261 logged = 1; 262 nolog(); 263 } 264 (void)sleep((u_int)tp->timetowait); 265 if (!tp->timeleft) 266 break; 267 } 268 die_you_gravy_sucking_pig_dog(); 269} 270 271static jmp_buf alarmbuf; 272 273static char *restricted_environ[] = { 274 "PATH=" _PATH_STDPATH, 275 NULL 276}; 277 278void 279timewarn(int timeleft) 280{ 281 static char hostname[HOST_NAME_MAX+1]; 282 char wcmd[PATH_MAX + 4]; 283 extern char **environ; 284 static int first; 285 FILE *pf; 286 287 if (!first++) 288 (void)gethostname(hostname, sizeof(hostname)); 289 290 /* undoc -n option to wall suppresses normal wall banner */ 291 (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 292 environ = restricted_environ; 293 if (!(pf = popen(wcmd, "w"))) { 294 syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 295 return; 296 } 297 298 (void)fprintf(pf, 299 "\007*** %sSystem shutdown message from %s@%s ***\007\n", 300 timeleft ? "": "FINAL ", whom, hostname); 301 302 if (timeleft > 10*60) { 303 struct tm *tm = localtime(&shuttime); 304 305 fprintf(pf, "System going down at %d:%02d\n\n", 306 tm->tm_hour, tm->tm_min); 307 } else if (timeleft > 59) 308 (void)fprintf(pf, "System going down in %d minute%s\n\n", 309 timeleft / 60, (timeleft > 60) ? "s" : ""); 310 else if (timeleft) 311 (void)fprintf(pf, "System going down in 30 seconds\n\n"); 312 else 313 (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 314 315 if (mbuflen) 316 (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 317 318 /* 319 * play some games, just in case wall doesn't come back 320 * probably unnecessary, given that wall is careful. 321 */ 322 if (!setjmp(alarmbuf)) { 323 (void)signal(SIGALRM, timeout); 324 (void)alarm((u_int)30); 325 (void)pclose(pf); 326 (void)alarm((u_int)0); 327 (void)signal(SIGALRM, SIG_DFL); 328 } 329} 330 331void 332timeout(int signo) 333{ 334 longjmp(alarmbuf, 1); /* XXX signal/longjmp resource leaks */ 335} 336 337void 338die_you_gravy_sucking_pig_dog(void) 339{ 340 341 syslog(LOG_NOTICE, "%s by %s: %s", 342 doreboot ? "reboot" : dopower ? "power-down" : dohalt ? "halt" : 343 "shutdown", whom, mbuf); 344 (void)sleep(2); 345 346 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 347 if (killflg) { 348 (void)printf("\rbut you'll have to do it yourself\r\n"); 349 finish(0); 350 } 351 if (dofast) 352 doitfast(); 353 354 if (pledge("stdio rpath wpath cpath tty id proc exec", NULL) == -1) 355 err(1, "pledge"); 356 357#ifdef DEBUG 358 if (doreboot) 359 (void)printf("reboot"); 360 else if (dopower) 361 (void)printf("power-down"); 362 else if (dohalt) 363 (void)printf("halt"); 364 if (nosync) 365 (void)printf(" no sync"); 366 if (dofast) 367 (void)printf(" no fsck"); 368 if (dodump) 369 (void)printf(" with dump"); 370 (void)printf("\nkill -HUP 1\n"); 371#else 372 if (dohalt || dopower || doreboot) { 373 char *args[10]; 374 char **arg, *path; 375 376 if (pledge("stdio exec", NULL) == -1) 377 err(1, "pledge"); 378 379 arg = &args[0]; 380 if (doreboot) { 381 path = _PATH_REBOOT; 382 *arg++ = "reboot"; 383 } else { 384 path = _PATH_HALT; 385 *arg++ = "halt"; 386 } 387 *arg++ = "-l"; 388 if (dopower) 389 *arg++ = "-p"; 390 if (nosync) 391 *arg++ = "-n"; 392 if (dodump) 393 *arg++ = "-d"; 394 *arg++ = NULL; 395 execve(path, args, NULL); 396 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", path); 397 warn("%s", path); 398 } 399 if (access(_PATH_RC, R_OK) != -1) { 400 pid_t pid; 401 struct termios t; 402 int fd; 403 404 switch ((pid = fork())) { 405 case -1: 406 break; 407 case 0: 408 if (revoke(_PATH_CONSOLE) == -1) 409 perror("revoke"); 410 if (setsid() == -1) 411 perror("setsid"); 412 fd = open(_PATH_CONSOLE, O_RDWR); 413 if (fd == -1) 414 perror("open"); 415 dup2(fd, 0); 416 dup2(fd, 1); 417 dup2(fd, 2); 418 if (fd > 2) 419 close(fd); 420 421 /* At a minimum... */ 422 tcgetattr(0, &t); 423 t.c_oflag |= (ONLCR | OPOST); 424 tcsetattr(0, TCSANOW, &t); 425 426 execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL); 427 _exit(1); 428 default: 429 waitpid(pid, NULL, 0); 430 } 431 } 432 (void)kill(1, SIGTERM); /* to single user */ 433#endif 434 finish(0); 435} 436 437#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 438 439void 440getoffset(char *timearg) 441{ 442 struct tm *lt; 443 int this_year; 444 time_t now; 445 char *p; 446 447 if (!strcasecmp(timearg, "now")) { /* now */ 448 offset = 0; 449 return; 450 } 451 452 (void)time(&now); 453 if (*timearg == '+') { /* +minutes */ 454 const char *errstr; 455 456 offset = strtonum(++timearg, 0, INT_MAX, &errstr); 457 if (errstr) 458 badtime(); 459 offset *= 60; 460 shuttime = now + offset; 461 return; 462 } 463 464 /* handle hh:mm by getting rid of the colon */ 465 for (p = timearg; *p; ++p) { 466 if (!isascii((unsigned char)*p) || !isdigit((unsigned char)*p)) { 467 if (*p == ':' && strlen(p) == 3) { 468 p[0] = p[1]; 469 p[1] = p[2]; 470 p[2] = '\0'; 471 } else 472 badtime(); 473 } 474 } 475 476 unsetenv("TZ"); /* OUR timezone */ 477 lt = localtime(&now); /* current time val */ 478 479 switch (strlen(timearg)) { 480 case 10: 481 this_year = lt->tm_year; 482 lt->tm_year = ATOI2(timearg); 483 /* 484 * check if the specified year is in the next century. 485 * allow for one year of user error as many people will 486 * enter n - 1 at the start of year n. 487 */ 488 if (lt->tm_year < (this_year % 100) - 1) 489 lt->tm_year += 100; 490 /* adjust for the year 2000 and beyond */ 491 lt->tm_year += (this_year - (this_year % 100)); 492 /* FALLTHROUGH */ 493 case 8: 494 lt->tm_mon = ATOI2(timearg); 495 if (--lt->tm_mon < 0 || lt->tm_mon > 11) 496 badtime(); 497 /* FALLTHROUGH */ 498 case 6: 499 lt->tm_mday = ATOI2(timearg); 500 if (lt->tm_mday < 1 || lt->tm_mday > 31) 501 badtime(); 502 /* FALLTHROUGH */ 503 case 4: 504 lt->tm_hour = ATOI2(timearg); 505 if (lt->tm_hour < 0 || lt->tm_hour > 23) 506 badtime(); 507 lt->tm_min = ATOI2(timearg); 508 if (lt->tm_min < 0 || lt->tm_min > 59) 509 badtime(); 510 lt->tm_sec = 0; 511 if ((shuttime = mktime(lt)) == -1) 512 badtime(); 513 if ((offset = shuttime - now) < 0) 514 errx(1, "that time is already past."); 515 break; 516 default: 517 badtime(); 518 } 519} 520 521void 522doitfast(void) 523{ 524 int fastfd; 525 526 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 527 0664)) >= 0) { 528 dprintf(fastfd, "fastboot file for fsck\n"); 529 close(fastfd); 530 } 531} 532 533void 534nolog(void) 535{ 536 int logfd; 537 struct tm *tm; 538 539 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 540 (void)signal(SIGINT, finish); 541 (void)signal(SIGHUP, finish); 542 (void)signal(SIGQUIT, finish); 543 (void)signal(SIGTERM, finish); 544 tm = localtime(&shuttime); 545 if (tm && (logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 546 0664)) >= 0) { 547 dprintf(logfd, 548 "\n\nNO LOGINS: System going down at %d:%02d\n\n", 549 tm->tm_hour, tm->tm_min); 550 close(logfd); 551 } 552} 553 554void 555finish(int signo) 556{ 557 if (!killflg) 558 (void)unlink(_PATH_NOLOGIN); 559 if (signo == 0) 560 exit(0); 561 else 562 _exit(0); 563} 564 565void 566badtime(void) 567{ 568 errx(1, "bad time format."); 569} 570 571void 572usage(void) 573{ 574 fprintf(stderr, 575 "usage: shutdown [-] [-dfhknpr] time [warning-message ...]\n"); 576 exit(1); 577} 578