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