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