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