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