1 static char *sccsid = "@(#)shutdown.c 4.14 (Berkeley) 82/10/23"; 2 3 #include <stdio.h> 4 #include <ctype.h> 5 #include <signal.h> 6 #include <utmp.h> 7 #include <time.h> 8 #include <sys/types.h> 9 /* 10 * /etc/shutdown when [messages] 11 * 12 * allow super users to tell users and remind users 13 * of iminent shutdown of unix 14 * and shut it down automatically 15 * and even reboot or halt the machine if they desire 16 * 17 * Ian Johnstone, Sydney, 1977 18 * Robert Elz, Melbourne, 1978 19 * Peter Lamb, Melbourne, 1980 20 * William Joy, Berkeley, 1981 21 * Michael Toy, Berkeley, 1981 22 * Dave Presotto, Berkeley, 1981 23 */ 24 #ifdef DEBUG 25 #define LOGFILE "shutdown.log" 26 #else 27 #define LOGFILE "/usr/adm/shutdownlog" 28 #endif 29 #define REBOOT "/etc/reboot" 30 #define HALT "/etc/halt" 31 #define MAXINTS 20 32 #define HOURS *3600 33 #define MINUTES *60 34 #define SECONDS 35 #define NLOG 20 /* no of args possible for message */ 36 #define NOLOGTIME 5 MINUTES 37 #define IGNOREUSER "sleeper" 38 39 char hostname[32]; 40 41 int do_nothing(); 42 time_t getsdt(); 43 44 extern char *ctime(); 45 extern struct tm *localtime(); 46 47 struct utmp utmp; 48 int sint; 49 int stogo; 50 char tpath[] = "/dev/"; 51 int nlflag = 1; /* nolog yet to be done */ 52 int killflg = 1; 53 int reboot = 0; 54 int halt = 0; 55 char term[sizeof tpath + sizeof utmp.ut_line]; 56 char tbuf[BUFSIZ]; 57 char nolog1[] = "\n\nNO LOGINS: System going down at %5.5s\n\n"; 58 char *nolog2[NLOG+1]; 59 #ifdef DEBUG 60 char nologin[] = "nologin"; 61 #else 62 char nologin[] = "/etc/nologin"; 63 #endif 64 time_t nowtime; 65 66 struct interval { 67 int stogo; 68 int sint; 69 } interval[] = { 70 4 HOURS, 1 HOURS, 71 2 HOURS, 30 MINUTES, 72 1 HOURS, 15 MINUTES, 73 30 MINUTES, 10 MINUTES, 74 15 MINUTES, 5 MINUTES, 75 10 MINUTES, 5 MINUTES, 76 5 MINUTES, 3 MINUTES, 77 2 MINUTES, 1 MINUTES, 78 1 MINUTES, 30 SECONDS, 79 0 SECONDS, 0 SECONDS 80 }; 81 82 char *shutter, *getlogin(); 83 84 main(argc,argv) 85 int argc; 86 char **argv; 87 { 88 register i, ufd; 89 register char **mess, *f; 90 char *ts; 91 time_t sdt; 92 int h, m; 93 int first; 94 FILE *termf; 95 96 shutter = getlogin(); 97 gethostname(hostname, sizeof (hostname)); 98 argc--, argv++; 99 while (argc > 0 && (f = argv[0], *f++ == '-')) { 100 while (i = *f++) switch (i) { 101 case 'k': 102 killflg = 0; 103 continue; 104 case 'r': 105 reboot = 1; 106 continue; 107 case 'h': 108 halt = 1; 109 continue; 110 default: 111 fprintf(stderr, "shutdown: '%c' - unknown flag\n", i); 112 exit(1); 113 } 114 argc--, argv++; 115 } 116 if (argc < 1) { 117 printf("Usage: %s [ -krh ] shutdowntime [ message ]\n", 118 argv[0]); 119 finish(); 120 } 121 if (geteuid()) { 122 fprintf(stderr, "NOT super-user\n"); 123 finish(); 124 } 125 nowtime = time((time_t *)0); 126 sdt = getsdt(argv[0]); 127 argc--, argv++; 128 i = 0; 129 while (argc-- > 0) 130 if (i < NLOG) 131 nolog2[i++] = *argv++; 132 nolog2[i] = NULL; 133 m = ((stogo = sdt - nowtime) + 30)/60; 134 h = m/60; 135 m %= 60; 136 ts = ctime(&sdt); 137 printf("Shutdown at %5.5s (in ", ts+11); 138 if (h > 0) 139 printf("%d hour%s ", h, h != 1 ? "s" : ""); 140 printf("%d minute%s) ", m, m != 1 ? "s" : ""); 141 #ifndef DEBUG 142 signal(SIGHUP, SIG_IGN); 143 signal(SIGQUIT, SIG_IGN); 144 signal(SIGINT, SIG_IGN); 145 #endif 146 signal(SIGTERM, finish); 147 signal(SIGALRM, do_nothing); 148 nice(-20); 149 fflush(stdout); 150 #ifndef DEBUG 151 if (i = fork()) { 152 printf("[pid %d]\n", i); 153 exit(0); 154 } 155 #else 156 putc('\n', stdout); 157 #endif 158 sint = 1 HOURS; 159 f = ""; 160 ufd = open("/etc/utmp",0); 161 if (ufd < 0) { 162 perror("shutdown: /etc/utmp"); 163 exit(1); 164 } 165 first = 1; 166 for (;;) { 167 for (i = 0; stogo <= interval[i].stogo && interval[i].sint; i++) 168 sint = interval[i].sint; 169 if (stogo > 0 && (stogo-sint) < interval[i].stogo) 170 sint = stogo - interval[i].stogo; 171 if (stogo <= NOLOGTIME && nlflag) { 172 nlflag = 0; 173 nolog(sdt); 174 } 175 if (sint >= stogo || sint == 0) 176 f = "FINAL "; 177 nowtime = time((time_t *) 0); 178 lseek(ufd, 0L, 0); 179 while (read(ufd,&utmp,sizeof utmp)==sizeof utmp) 180 if (utmp.ut_name[0] && 181 strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name))) { 182 strcpy(term, tpath); 183 strncat(term, utmp.ut_line, sizeof utmp.ut_line); 184 alarm(3); 185 #ifdef DEBUG 186 if ((termf = stdout) != NULL) 187 #else 188 if ((termf = fopen(term, "w")) != NULL) 189 #endif 190 { 191 alarm(0); 192 setbuf(termf, tbuf); 193 fprintf(termf, "\n\r\n"); 194 warn(termf, sdt, nowtime, f); 195 if (first || sdt - nowtime > 1 MINUTES) { 196 if (*nolog2) 197 fprintf(termf, "\t..."); 198 for (mess = nolog2; *mess; mess++) 199 fprintf(termf, " %s", *mess); 200 } 201 fputc('\r', termf); 202 fputc('\n', termf); 203 alarm(5); 204 #ifdef DEBUG 205 fflush(termf); 206 #else 207 fclose(termf); 208 #endif 209 alarm(0); 210 } 211 } 212 if (stogo <= 0) { 213 printf("\n\007\007System shutdown time has arrived\007\007\n"); 214 log_entry(sdt); 215 unlink(nologin); 216 if (!killflg) { 217 printf("but you'll have to do it yourself\n"); 218 finish(); 219 } 220 #ifndef DEBUG 221 kill(-1, SIGTERM); /* terminate everyone */ 222 sleep(5); /* & wait while they die */ 223 if (reboot) 224 execle(REBOOT, "reboot", 0, 0); 225 if (halt) 226 execle(HALT, "halt", 0, 0); 227 kill(1, SIGTERM); /* sync */ 228 kill(1, SIGTERM); /* sync */ 229 sleep(20); 230 #else 231 printf("EXTERMINATE EXTERMINATE\n"); 232 #endif 233 finish(); 234 } 235 stogo = sdt - time((time_t *) 0); 236 if (stogo > 0 && sint > 0) 237 sleep(sint<stogo ? sint : stogo); 238 stogo -= sint; 239 first = 0; 240 } 241 } 242 243 time_t 244 getsdt(s) 245 register char *s; 246 { 247 time_t t, t1, tim; 248 register char c; 249 struct tm *lt; 250 251 if (strcmp(s, "now") == 0) 252 return(nowtime); 253 if (*s == '+') { 254 ++s; 255 t = 0; 256 for (;;) { 257 c = *s++; 258 if (!isdigit(c)) 259 break; 260 t = t * 10 + c - '0'; 261 } 262 if (t <= 0) 263 t = 5; 264 t *= 60; 265 tim = time((time_t *) 0) + t; 266 return(tim); 267 } 268 t = 0; 269 while (strlen(s) > 2 && isdigit(*s)) 270 t = t * 10 + *s++ - '0'; 271 if (*s == ':') 272 s++; 273 if (t > 23) 274 goto badform; 275 tim = t*60; 276 t = 0; 277 while (isdigit(*s)) 278 t = t * 10 + *s++ - '0'; 279 if (t > 59) 280 goto badform; 281 tim += t; 282 tim *= 60; 283 t1 = time((time_t *) 0); 284 lt = localtime(&t1); 285 t = lt->tm_sec + lt->tm_min*60 + lt->tm_hour*3600; 286 if (tim < t || tim >= (24*3600)) { 287 /* before now or after midnight */ 288 printf("That must be tomorrow\nCan't you wait till then?\n"); 289 finish(); 290 } 291 return (t1 + tim - t); 292 badform: 293 printf("Bad time format\n"); 294 finish(); 295 } 296 297 warn(term, sdt, now, type) 298 FILE *term; 299 time_t sdt, now; 300 char *type; 301 { 302 char *ts; 303 register delay = sdt - now; 304 305 if (delay > 8) 306 while (delay % 5) 307 delay++; 308 309 if (shutter) 310 fprintf(term, 311 "\007\007\t*** %sSystem shutdown message from %s!%s ***\r\n\n", 312 type, hostname, shutter); 313 else 314 fprintf(term, 315 "\007\007\t*** %sSystem shutdown message (%s) ***\r\n\n", 316 type, hostname); 317 318 ts = ctime(&sdt); 319 if (delay > 10 MINUTES) 320 fprintf(term, "System going down at %5.5s\r\n", ts+11); 321 else if (delay > 95 SECONDS) { 322 fprintf(term, "System going down in %d minute%s\r\n", 323 (delay+30)/60, (delay+30)/60 != 1 ? "s" : ""); 324 } else if (delay > 0) { 325 fprintf(term, "System going down in %d second%s\r\n", 326 delay, delay != 1 ? "s" : ""); 327 } else 328 fprintf(term, "System going down IMMEDIATELY\r\n"); 329 } 330 331 nolog(sdt) 332 time_t sdt; 333 { 334 FILE *nologf; 335 register char **mess; 336 337 unlink(nologin); /* in case linked to std file */ 338 if ((nologf = fopen(nologin, "w")) != NULL) { 339 fprintf(nologf, nolog1, (ctime(&sdt)) + 11); 340 putc('\t', nologf); 341 for (mess = nolog2; *mess; mess++) 342 fprintf(nologf, " %s", *mess); 343 putc('\n', nologf); 344 fclose(nologf); 345 } 346 } 347 348 finish() 349 { 350 signal(SIGTERM, SIG_IGN); 351 unlink(nologin); 352 exit(0); 353 } 354 355 do_nothing() 356 { 357 358 signal(SIGALRM, do_nothing); 359 } 360 361 /* 362 * make an entry in the shutdown log 363 */ 364 365 char *days[] = { 366 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 367 }; 368 369 char *months[] = { 370 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", 371 "Oct", "Nov", "Dec" 372 }; 373 374 log_entry(now) 375 time_t now; 376 { 377 register FILE *fp; 378 register char **mess; 379 struct tm *tm, *localtime(); 380 381 tm = localtime(&now); 382 fp = fopen(LOGFILE, "a"); 383 if (fp == NULL) { 384 printf("Shutdown: log entry failed\n"); 385 return; 386 } 387 fseek(fp, 0L, 2); 388 fprintf(fp, "%02d:%02d %s %s %2d, %4d. Shutdown:", tm->tm_hour, 389 tm->tm_min, days[tm->tm_wday], months[tm->tm_mon], 390 tm->tm_mday, tm->tm_year + 1900); 391 for (mess = nolog2; *mess; mess++) 392 fprintf(fp, " %s", *mess); 393 if (shutter) 394 fprintf(fp, " (by %s!%s)", hostname, shutter); 395 fputc('\n', fp); 396 fclose(fp); 397 } 398