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