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