1 /* $OpenBSD: shutdown.c,v 1.37 2013/11/22 04:12:48 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/param.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 <tzfile.h> 50 #include <unistd.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 #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 (dopower && !dohalt) { 161 (void)fprintf(stderr, 162 "shutdown: switch -p must be used with -h.\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 225 void 226 loop(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 266 static jmp_buf alarmbuf; 267 268 static char *restricted_environ[] = { 269 "PATH=" _PATH_STDPATH, 270 NULL 271 }; 272 273 void 274 timewarn(int timeleft) 275 { 276 static char hostname[MAXHOSTNAMELEN]; 277 char wcmd[MAXPATHLEN + 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 324 void 325 timeout(int signo) 326 { 327 longjmp(alarmbuf, 1); /* XXX signal/longjmp resource leaks */ 328 } 329 330 void 331 die_you_gravy_sucking_pig_dog(void) 332 { 333 334 syslog(LOG_NOTICE, "%s by %s: %s", 335 doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 336 (void)sleep(2); 337 338 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 339 if (killflg) { 340 (void)printf("\rbut you'll have to do it yourself\r\n"); 341 finish(0); 342 } 343 if (dofast) 344 doitfast(); 345 #ifdef DEBUG 346 if (doreboot) 347 (void)printf("reboot"); 348 else if (dohalt) 349 (void)printf("halt"); 350 if (nosync) 351 (void)printf(" no sync"); 352 if (dofast) 353 (void)printf(" no fsck"); 354 if (dodump) 355 (void)printf(" with dump"); 356 (void)printf("\nkill -HUP 1\n"); 357 #else 358 if (doreboot) { 359 execle(_PATH_REBOOT, "reboot", "-l", 360 (nosync ? "-n" : (dodump ? "-d" : NULL)), 361 (dodump ? "-d" : NULL), (char *)NULL, (char *)NULL); 362 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 363 warn(_PATH_REBOOT); 364 } 365 else if (dohalt) { 366 execle(_PATH_HALT, "halt", "-l", 367 (dopower ? "-p" : (nosync ? "-n" : (dodump ? "-d" : NULL))), 368 (nosync ? "-n" : (dodump ? "-d" : NULL)), 369 (dodump ? "-d" : NULL), (char *)NULL, (char *)NULL); 370 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 371 warn(_PATH_HALT); 372 } 373 if (access(_PATH_RC, R_OK) != -1) { 374 pid_t pid; 375 struct termios t; 376 int fd; 377 378 switch ((pid = fork())) { 379 case -1: 380 break; 381 case 0: 382 if (revoke(_PATH_CONSOLE) == -1) 383 perror("revoke"); 384 if (setsid() == -1) 385 perror("setsid"); 386 fd = open(_PATH_CONSOLE, O_RDWR); 387 if (fd == -1) 388 perror("open"); 389 dup2(fd, 0); 390 dup2(fd, 1); 391 dup2(fd, 2); 392 if (fd > 2) 393 close(fd); 394 395 /* At a minimum... */ 396 tcgetattr(0, &t); 397 t.c_oflag |= (ONLCR | OPOST); 398 tcsetattr(0, TCSANOW, &t); 399 400 execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL); 401 _exit(1); 402 default: 403 waitpid(pid, NULL, 0); 404 } 405 } 406 (void)kill(1, SIGTERM); /* to single user */ 407 #endif 408 finish(0); 409 } 410 411 #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 412 413 void 414 getoffset(char *timearg) 415 { 416 struct tm *lt; 417 int this_year; 418 time_t now; 419 char *p; 420 421 if (!strcasecmp(timearg, "now")) { /* now */ 422 offset = 0; 423 return; 424 } 425 426 (void)time(&now); 427 if (*timearg == '+') { /* +minutes */ 428 if (!isdigit((unsigned char)*++timearg)) 429 badtime(); 430 offset = atoi(timearg) * 60; 431 shuttime = now + offset; 432 return; 433 } 434 435 /* handle hh:mm by getting rid of the colon */ 436 for (p = timearg; *p; ++p) { 437 if (!isascii((unsigned char)*p) || !isdigit((unsigned char)*p)) { 438 if (*p == ':' && strlen(p) == 3) { 439 p[0] = p[1]; 440 p[1] = p[2]; 441 p[2] = '\0'; 442 } else 443 badtime(); 444 } 445 } 446 447 unsetenv("TZ"); /* OUR timezone */ 448 lt = localtime(&now); /* current time val */ 449 450 switch (strlen(timearg)) { 451 case 10: 452 this_year = lt->tm_year; 453 lt->tm_year = ATOI2(timearg); 454 /* 455 * check if the specified year is in the next century. 456 * allow for one year of user error as many people will 457 * enter n - 1 at the start of year n. 458 */ 459 if (lt->tm_year < (this_year % 100) - 1) 460 lt->tm_year += 100; 461 /* adjust for the year 2000 and beyond */ 462 lt->tm_year += (this_year - (this_year % 100)); 463 /* FALLTHROUGH */ 464 case 8: 465 lt->tm_mon = ATOI2(timearg); 466 if (--lt->tm_mon < 0 || lt->tm_mon > 11) 467 badtime(); 468 /* FALLTHROUGH */ 469 case 6: 470 lt->tm_mday = ATOI2(timearg); 471 if (lt->tm_mday < 1 || lt->tm_mday > 31) 472 badtime(); 473 /* FALLTHROUGH */ 474 case 4: 475 lt->tm_hour = ATOI2(timearg); 476 if (lt->tm_hour < 0 || lt->tm_hour > 23) 477 badtime(); 478 lt->tm_min = ATOI2(timearg); 479 if (lt->tm_min < 0 || lt->tm_min > 59) 480 badtime(); 481 lt->tm_sec = 0; 482 if ((shuttime = mktime(lt)) == -1) 483 badtime(); 484 if ((offset = shuttime - now) < 0) 485 errx(1, "that time is already past."); 486 break; 487 default: 488 badtime(); 489 } 490 } 491 492 #define FSMSG "fastboot file for fsck\n" 493 void 494 doitfast(void) 495 { 496 int fastfd; 497 498 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 499 0664)) >= 0) { 500 (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); 501 (void)close(fastfd); 502 } 503 } 504 505 #define NOMSG "\n\nNO LOGINS: System going down at " 506 void 507 nolog(void) 508 { 509 int logfd; 510 char *ct; 511 512 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 513 (void)signal(SIGINT, finish); 514 (void)signal(SIGHUP, finish); 515 (void)signal(SIGQUIT, finish); 516 (void)signal(SIGTERM, finish); 517 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 518 0664)) >= 0) { 519 (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 520 ct = ctime(&shuttime); 521 (void)write(logfd, ct + 11, 5); 522 (void)write(logfd, "\n\n", 2); 523 (void)write(logfd, mbuf, strlen(mbuf)); 524 (void)close(logfd); 525 } 526 } 527 528 void 529 finish(int signo) 530 { 531 if (!killflg) 532 (void)unlink(_PATH_NOLOGIN); 533 if (signo == 0) 534 exit(0); 535 else 536 _exit(0); 537 } 538 539 void 540 badtime(void) 541 { 542 errx(1, "bad time format."); 543 } 544 545 void 546 usage(void) 547 { 548 fprintf(stderr, "usage: shutdown [-] [-dfhknpr] time [warning-message ...]\n"); 549 exit(1); 550 } 551