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