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