1 /* 2 * Copyright (c) 1988, 1990 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1988 Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)shutdown.c 5.14 (Berkeley) 06/01/90"; 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 <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, *getpwuid(); 66 char *strcat(), *getlogin(); 67 uid_t geteuid(); 68 69 #ifndef DEBUG 70 if (geteuid()) { 71 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 fprintf(stderr, 109 "shutdown: incompatible switches -f and -n.\n"); 110 usage(); 111 } 112 if (doreboot && dohalt) { 113 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 printf("Shutdown at %.24s.\n", ctime(&shuttime)); 151 else 152 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 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 int 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 fprintf(pf, "\007*** %sSystem shutdown message from %s@%s ***\007\n", 242 tp->timeleft ? "": "FINAL ", whom, hostname); 243 244 if (tp->timeleft > 10*60) 245 fprintf(pf, "System going down at %5.5s\n\n", 246 ctime(&shuttime) + 11); 247 else if (tp->timeleft > 59) 248 fprintf(pf, "System going down in %d minute%s\n\n", 249 tp->timeleft / 60, (tp->timeleft > 60) ? "s" : ""); 250 else if (tp->timeleft) 251 fprintf(pf, "System going down in 30 seconds\n\n"); 252 else 253 fprintf(pf, "System going down IMMEDIATELY\n\n"); 254 255 if (mbuflen) 256 (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 257 258 /* 259 * play some games, just in case wall doesn't come back 260 * probably unecessary, given that wall is careful. 261 */ 262 if (!setjmp(alarmbuf)) { 263 (void)signal(SIGALRM, timeout); 264 (void)alarm((u_int)30); 265 (void)pclose(pf); 266 (void)alarm((u_int)0); 267 (void)signal(SIGALRM, SIG_DFL); 268 } 269 } 270 271 timeout() 272 { 273 longjmp(alarmbuf, 1); 274 } 275 276 die_you_gravy_sucking_pig_dog() 277 { 278 syslog(LOG_NOTICE, "%s by %s: %s", 279 doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 280 (void)sleep(2); 281 282 printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 283 if (killflg) { 284 printf("\rbut you'll have to do it yourself\r\n"); 285 finish(); 286 } 287 if (dofast) 288 doitfast(); 289 #ifdef DEBUG 290 if (doreboot) 291 printf("reboot"); 292 else if (dohalt) 293 printf("halt"); 294 if (nosync) 295 printf(" no sync"); 296 if (dofast) 297 printf(" no fsck"); 298 printf("\nkill -HUP 1\n"); 299 #else 300 if (doreboot) { 301 execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); 302 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 303 perror("shutdown"); 304 } 305 else if (dohalt) { 306 execle(_PATH_HALT, "halt", "-l", nosync, 0); 307 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 308 perror("shutdown"); 309 } 310 (void)kill(1, SIGTERM); /* to single user */ 311 #endif 312 finish(); 313 } 314 315 #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 316 static int dmsize[] = 317 { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 318 319 getoffset(timearg) 320 register char *timearg; 321 { 322 register struct tm *lt; 323 register char *p; 324 time_t now, time(); 325 int year, month, day, hour, min; 326 327 if (!strcasecmp(timearg, "now")) { /* now */ 328 offset = 0; 329 return; 330 } 331 332 (void)time(&now); 333 if (*timearg == '+') { /* +minutes */ 334 if (!isdigit(*++timearg)) 335 goto badtime; 336 min = atoi(timearg); 337 offset = min * 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 goto badtime; 352 353 unsetenv("TZ"); /* OUR timezone */ 354 lt = localtime(&now); /* [yymmdd]hhmm */ 355 year = lt->tm_year; 356 month = lt->tm_mon + 1; 357 day = lt->tm_mday; 358 359 switch(strlen(timearg)) { 360 case 10: 361 year = ATOI2(timearg); 362 /* FALLTHROUGH */ 363 case 8: 364 month = ATOI2(timearg); 365 /* FALLTHROUGH */ 366 case 6: 367 day = ATOI2(timearg); 368 /* FALLTHROUGH */ 369 case 4: 370 hour = ATOI2(timearg); 371 min = ATOI2(timearg); 372 if (month < 1 || month > 12 || day < 1 || day > 31 || 373 hour < 0 || hour > 23 || min < 0 || min > 59) 374 goto badtime; 375 shuttime = 0; 376 year += TM_YEAR_BASE; 377 if (isleap(year) && month > 2) 378 ++shuttime; 379 for (--year; year >= EPOCH_YEAR; --year) 380 shuttime += isleap(year) ? 381 DAYSPERLYEAR : DAYSPERNYEAR; 382 while (--month) 383 shuttime += dmsize[month]; 384 shuttime += day - 1; 385 shuttime = HOURSPERDAY * shuttime + hour; 386 shuttime = MINSPERHOUR * shuttime + min; 387 shuttime *= SECSPERMIN; 388 shuttime -= lt->tm_gmtoff; 389 if ((offset = shuttime - now) >= 0) 390 break; 391 /* FALLTHROUGH */ 392 default: 393 badtime: fprintf(stderr, 394 "shutdown: bad time format, or already past.\n"); 395 exit(1); 396 } 397 } 398 399 #define FSMSG "fastboot file for fsck\n" 400 doitfast() 401 { 402 int fastfd; 403 404 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 405 0664)) >= 0) { 406 (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); 407 (void)close(fastfd); 408 } 409 } 410 411 #define NOMSG "\n\nNO LOGINS: System going down at " 412 nolog() 413 { 414 int logfd, finish(); 415 char *ct, *ctime(); 416 417 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 418 (void)signal(SIGINT, finish); 419 (void)signal(SIGHUP, finish); 420 (void)signal(SIGQUIT, finish); 421 (void)signal(SIGTERM, finish); 422 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 423 0664)) >= 0) { 424 (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 425 ct = ctime(&shuttime); 426 (void)write(logfd, ct + 11, 5); 427 (void)write(logfd, "\n\n", 2); 428 (void)write(logfd, mbuf, strlen(mbuf)); 429 (void)close(logfd); 430 } 431 } 432 433 finish() 434 { 435 (void)unlink(_PATH_NOLOGIN); 436 exit(0); 437 } 438 439 usage() 440 { 441 fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); 442 exit(1); 443 } 444