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