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