1 /* $NetBSD: shutdown.c,v 1.11 1997/01/23 05:48:06 mikel 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 #ifndef lint 37 static char copyright[] = 38 "@(#) 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.2 (Berkeley) 2/16/94"; 45 #else 46 static char rcsid[] = "$NetBSD: shutdown.c,v 1.11 1997/01/23 05:48:06 mikel 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 <fcntl.h> 57 #include <pwd.h> 58 #include <setjmp.h> 59 #include <signal.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <tzfile.h> 64 #include <unistd.h> 65 66 #include "pathnames.h" 67 68 #ifdef DEBUG 69 #undef _PATH_NOLOGIN 70 #define _PATH_NOLOGIN "./nologin" 71 #undef _PATH_FASTBOOT 72 #define _PATH_FASTBOOT "./fastboot" 73 #endif 74 75 #define H *60*60 76 #define M *60 77 #define S *1 78 #define NOLOG_TIME 5*60 79 struct interval { 80 int timeleft, timetowait; 81 } tlist[] = { 82 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, 83 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, 84 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, 85 0, 0, 86 }; 87 #undef H 88 #undef M 89 #undef S 90 91 static time_t offset, shuttime; 92 static int dofast, dohalt, doreboot, killflg, mbuflen, nosync; 93 static char *whom, mbuf[BUFSIZ]; 94 95 void badtime __P((void)); 96 void die_you_gravy_sucking_pig_dog __P((void)); 97 void doitfast __P((void)); 98 void finish __P((int)); 99 void getoffset __P((char *)); 100 void loop __P((void)); 101 void nolog __P((void)); 102 void timeout __P((int)); 103 void timewarn __P((int)); 104 void usage __P((void)); 105 106 int 107 main(argc, argv) 108 int argc; 109 char *argv[]; 110 { 111 extern int optind; 112 register char *p, *endp; 113 struct passwd *pw; 114 int arglen, ch, len, readstdin; 115 116 #ifndef DEBUG 117 if (geteuid()) { 118 (void)fprintf(stderr, "shutdown: NOT super-user\n"); 119 exit(1); 120 } 121 #endif 122 readstdin = 0; 123 while ((ch = getopt(argc, argv, "-fhknr")) != EOF) 124 switch (ch) { 125 case '-': 126 readstdin = 1; 127 break; 128 case 'f': 129 dofast = 1; 130 break; 131 case 'h': 132 dohalt = 1; 133 break; 134 case 'k': 135 killflg = 1; 136 break; 137 case 'n': 138 nosync = 1; 139 break; 140 case 'r': 141 doreboot = 1; 142 break; 143 case '?': 144 default: 145 usage(); 146 } 147 argc -= optind; 148 argv += optind; 149 150 if (argc < 1) 151 usage(); 152 153 if (dofast && nosync) { 154 (void)fprintf(stderr, 155 "shutdown: incompatible switches -f and -n.\n"); 156 usage(); 157 } 158 if (doreboot && dohalt) { 159 (void)fprintf(stderr, 160 "shutdown: incompatible switches -h and -r.\n"); 161 usage(); 162 } 163 getoffset(*argv++); 164 165 if (*argv) { 166 for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 167 arglen = strlen(*argv); 168 if ((len -= arglen) <= 2) 169 break; 170 if (p != mbuf) 171 *p++ = ' '; 172 memcpy(p, *argv, arglen); 173 p += arglen; 174 } 175 *p = '\n'; 176 *++p = '\0'; 177 } 178 179 if (readstdin) { 180 p = mbuf; 181 endp = mbuf + sizeof(mbuf) - 2; 182 for (;;) { 183 if (!fgets(p, endp - p + 1, stdin)) 184 break; 185 for (; *p && p < endp; ++p); 186 if (p == endp) { 187 *p = '\n'; 188 *++p = '\0'; 189 break; 190 } 191 } 192 } 193 mbuflen = strlen(mbuf); 194 195 if (offset) 196 (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 197 else 198 (void)printf("Shutdown NOW!\n"); 199 200 if (!(whom = getlogin())) 201 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 202 203 #ifdef DEBUG 204 (void)putc('\n', stdout); 205 #else 206 (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 207 { 208 int forkpid; 209 210 forkpid = fork(); 211 if (forkpid == -1) { 212 perror("shutdown: fork"); 213 exit(1); 214 } 215 if (forkpid) { 216 (void)printf("shutdown: [pid %d]\n", forkpid); 217 exit(0); 218 } 219 } 220 #endif 221 openlog("shutdown", LOG_CONS, LOG_AUTH); 222 loop(); 223 /* NOTREACHED */ 224 } 225 226 void 227 loop() 228 { 229 struct interval *tp; 230 u_int sltime; 231 int logged; 232 233 if (offset <= NOLOG_TIME) { 234 logged = 1; 235 nolog(); 236 } 237 else 238 logged = 0; 239 tp = tlist; 240 if (tp->timeleft < offset) 241 (void)sleep((u_int)(offset - tp->timeleft)); 242 else { 243 while (offset < tp->timeleft) 244 ++tp; 245 /* 246 * Warn now, if going to sleep more than a fifth of 247 * the next wait time. 248 */ 249 if (sltime = offset - tp->timeleft) { 250 if (sltime > tp->timetowait / 5) 251 timewarn(offset); 252 (void)sleep(sltime); 253 } 254 } 255 for (;; ++tp) { 256 timewarn(tp->timeleft); 257 if (!logged && tp->timeleft <= NOLOG_TIME) { 258 logged = 1; 259 nolog(); 260 } 261 (void)sleep((u_int)tp->timetowait); 262 if (!tp->timeleft) 263 break; 264 } 265 die_you_gravy_sucking_pig_dog(); 266 } 267 268 static jmp_buf alarmbuf; 269 270 void 271 timewarn(timeleft) 272 int timeleft; 273 { 274 static int first; 275 static char hostname[MAXHOSTNAMELEN + 1]; 276 FILE *pf; 277 char wcmd[MAXPATHLEN + 4]; 278 279 if (!first++) 280 (void)gethostname(hostname, sizeof(hostname)); 281 282 /* undoc -n option to wall suppresses normal wall banner */ 283 (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 284 if (!(pf = popen(wcmd, "w"))) { 285 syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 286 return; 287 } 288 289 (void)fprintf(pf, 290 "\007*** %sSystem shutdown message from %s@%s ***\007\n", 291 timeleft ? "": "FINAL ", whom, hostname); 292 293 if (timeleft > 10*60) 294 (void)fprintf(pf, "System going down at %5.5s\n\n", 295 ctime(&shuttime) + 11); 296 else if (timeleft > 59) 297 (void)fprintf(pf, "System going down in %d minute%s\n\n", 298 timeleft / 60, (timeleft > 60) ? "s" : ""); 299 else if (timeleft) 300 (void)fprintf(pf, "System going down in 30 seconds\n\n"); 301 else 302 (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 303 304 if (mbuflen) 305 (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 306 307 /* 308 * play some games, just in case wall doesn't come back 309 * probably unecessary, given that wall is careful. 310 */ 311 if (!setjmp(alarmbuf)) { 312 (void)signal(SIGALRM, timeout); 313 (void)alarm((u_int)30); 314 (void)pclose(pf); 315 (void)alarm((u_int)0); 316 (void)signal(SIGALRM, SIG_DFL); 317 } 318 } 319 320 void 321 timeout(signo) 322 int signo; 323 { 324 longjmp(alarmbuf, 1); 325 } 326 327 void 328 die_you_gravy_sucking_pig_dog() 329 { 330 331 syslog(LOG_NOTICE, "%s by %s: %s", 332 doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 333 (void)sleep(2); 334 335 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 336 if (killflg) { 337 (void)printf("\rbut you'll have to do it yourself\r\n"); 338 finish(0); 339 } 340 if (dofast) 341 doitfast(); 342 #ifdef DEBUG 343 if (doreboot) 344 (void)printf("reboot"); 345 else if (dohalt) 346 (void)printf("halt"); 347 if (nosync) 348 (void)printf(" no sync"); 349 if (dofast) 350 (void)printf(" no fsck"); 351 (void)printf("\nkill -HUP 1\n"); 352 #else 353 if (doreboot) { 354 execle(_PATH_REBOOT, "reboot", nosync ? "-ln" : "-l", 355 (char *)0, (char **)0); 356 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 357 perror("shutdown"); 358 } 359 else if (dohalt) { 360 execle(_PATH_HALT, "halt", nosync ? "-ln" : "-l", 361 (char *)0, (char **)0); 362 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 363 perror("shutdown"); 364 } 365 (void)kill(1, SIGTERM); /* to single user */ 366 #endif 367 finish(0); 368 } 369 370 #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 371 372 void 373 getoffset(timearg) 374 register char *timearg; 375 { 376 register struct tm *lt; 377 register char *p; 378 time_t now; 379 380 if (!strcasecmp(timearg, "now")) { /* now */ 381 offset = 0; 382 return; 383 } 384 385 (void)time(&now); 386 if (*timearg == '+') { /* +minutes */ 387 if (!isdigit(*++timearg)) 388 badtime(); 389 offset = atoi(timearg) * 60; 390 shuttime = now + offset; 391 return; 392 } 393 394 /* handle hh:mm by getting rid of the colon */ 395 for (p = timearg; *p; ++p) 396 if (!isascii(*p) || !isdigit(*p)) 397 if (*p == ':' && strlen(p) == 3) { 398 p[0] = p[1]; 399 p[1] = p[2]; 400 p[2] = '\0'; 401 } 402 else 403 badtime(); 404 405 unsetenv("TZ"); /* OUR timezone */ 406 lt = localtime(&now); /* current time val */ 407 408 switch(strlen(timearg)) { 409 case 10: 410 lt->tm_year = ATOI2(timearg); 411 /* FALLTHROUGH */ 412 case 8: 413 lt->tm_mon = ATOI2(timearg); 414 if (--lt->tm_mon < 0 || lt->tm_mon > 11) 415 badtime(); 416 /* FALLTHROUGH */ 417 case 6: 418 lt->tm_mday = ATOI2(timearg); 419 if (lt->tm_mday < 1 || lt->tm_mday > 31) 420 badtime(); 421 /* FALLTHROUGH */ 422 case 4: 423 lt->tm_hour = ATOI2(timearg); 424 if (lt->tm_hour < 0 || lt->tm_hour > 23) 425 badtime(); 426 lt->tm_min = ATOI2(timearg); 427 if (lt->tm_min < 0 || lt->tm_min > 59) 428 badtime(); 429 lt->tm_sec = 0; 430 if ((shuttime = mktime(lt)) == -1) 431 badtime(); 432 if ((offset = shuttime - now) < 0) { 433 (void)fprintf(stderr, 434 "shutdown: that time is already past.\n"); 435 exit(1); 436 } 437 break; 438 default: 439 badtime(); 440 } 441 } 442 443 #define FSMSG "fastboot file for fsck\n" 444 void 445 doitfast() 446 { 447 int fastfd; 448 449 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 450 0664)) >= 0) { 451 (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); 452 (void)close(fastfd); 453 } 454 } 455 456 #define NOMSG "\n\nNO LOGINS: System going down at " 457 void 458 nolog() 459 { 460 int logfd; 461 char *ct; 462 463 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 464 (void)signal(SIGINT, finish); 465 (void)signal(SIGHUP, finish); 466 (void)signal(SIGQUIT, finish); 467 (void)signal(SIGTERM, finish); 468 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 469 0664)) >= 0) { 470 (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 471 ct = ctime(&shuttime); 472 (void)write(logfd, ct + 11, 5); 473 (void)write(logfd, "\n\n", 2); 474 (void)write(logfd, mbuf, strlen(mbuf)); 475 (void)close(logfd); 476 } 477 } 478 479 void 480 finish(signo) 481 int signo; 482 { 483 if (!killflg) 484 (void)unlink(_PATH_NOLOGIN); 485 exit(0); 486 } 487 488 void 489 badtime() 490 { 491 (void)fprintf(stderr, "shutdown: bad time format.\n"); 492 exit(1); 493 } 494 495 void 496 usage() 497 { 498 fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); 499 exit(1); 500 } 501