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