Commit | Line | Data |
---|---|---|
7b602762 | 1 | /* @(#)shutdown.c 4.5 (Berkeley/Melbourne) 81/05/04 */ |
954bb1fe BJ |
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 | |
8fa8eeab | 21 | * Michael Toy, Berkeley, 1981 |
954bb1fe | 22 | */ |
8fa8eeab BJ |
23 | #ifdef DEBUG |
24 | #define LOGFILE "shutdown.log" | |
25 | #else | |
26 | #define LOGFILE "/usr/adm/shutdownlog" | |
27 | #endif | |
954bb1fe BJ |
28 | #define REBOOT "/etc/reboot" |
29 | #define HALT "/etc/halt" | |
30 | #define MAXINTS 20 | |
31 | #define HOURS *3600 | |
32 | #define MINUTES *60 | |
33 | #define SECONDS | |
34 | #define NLOG 20 /* no of lines possible for message */ | |
35 | #define NOLOGTIME 5 MINUTES | |
36 | ||
37 | int do_nothing(); | |
38 | time_t getsdt(); | |
39 | ||
40 | extern char *ctime(); | |
41 | extern struct tm *localtime(); | |
42 | ||
43 | struct utmp utmp; | |
44 | int sint; | |
45 | int stogo; | |
46 | char tpath[] = "/dev/"; | |
47 | int nlflag = 1; /* nolog yet to be done */ | |
48 | int killflg = 1; | |
49 | int reboot = 0; | |
7b602762 | 50 | int noroot = 0; |
954bb1fe BJ |
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 | |
61 | int slots; | |
62 | struct interval { | |
63 | int stogo; | |
64 | int sint; | |
65 | } interval[] = { | |
66 | 4 HOURS, 1 HOURS, | |
67 | 2 HOURS, 30 MINUTES, | |
68 | 1 HOURS, 15 MINUTES, | |
69 | 30 MINUTES, 10 MINUTES, | |
70 | 15 MINUTES, 5 MINUTES, | |
8fa8eeab BJ |
71 | 10 MINUTES, 5 MINUTES, |
72 | 5 MINUTES, 3 MINUTES, | |
954bb1fe BJ |
73 | 2 MINUTES, 30 SECONDS, |
74 | 0 SECONDS, 0 SECONDS | |
75 | }; | |
8fa8eeab | 76 | char *shutter, *getlogin(); |
954bb1fe BJ |
77 | main(argc,argv) |
78 | int argc; | |
79 | char **argv; | |
80 | { | |
81 | register i, ufd; | |
82 | register char **mess, *f; | |
83 | char *ts; | |
84 | long sdt; | |
85 | int h, m; | |
86 | long nowtime; | |
87 | FILE *termf; | |
88 | ||
8fa8eeab | 89 | shutter = getlogin(); |
954bb1fe BJ |
90 | argc--, argv++; |
91 | while (argc > 0 && (f = argv[0], *f++ == '-')) { | |
92 | while (i = *f++) switch (i) { | |
93 | case 'k': | |
94 | killflg = 0; | |
95 | continue; | |
96 | case 'r': | |
97 | reboot = 1; | |
98 | continue; | |
99 | case 'h': | |
100 | halt = 1; | |
101 | continue; | |
7b602762 MT |
102 | case 'y': |
103 | noroot = 1; | |
104 | continue; | |
954bb1fe BJ |
105 | default: |
106 | fprintf(stderr, "shutdown: '%c' - unknown flag\n", i); | |
107 | exit(1); | |
108 | } | |
109 | argc--, argv++; | |
110 | } | |
111 | if (argc < 1) { | |
7b602762 | 112 | printf("Usage: %s [-krdy] shutdowntime [nologmessage]\n", |
954bb1fe BJ |
113 | argv[0]); |
114 | finish(); | |
115 | } | |
7b602762 MT |
116 | if (noroot == 0 && geteuid() != 0) |
117 | { | |
118 | printf("Must specify -y flag to run as normal user.\n"); | |
119 | finish(); | |
120 | } | |
954bb1fe BJ |
121 | sdt = getsdt(argv[0]); |
122 | argc--, argv++; | |
123 | i = 0; | |
124 | while (argc-- > 0) | |
125 | if (i < NLOG) | |
126 | nolog2[i++] = *argv++; | |
127 | nolog2[i] = NULL; | |
128 | nowtime = time((long *)0); | |
129 | m = ((stogo = sdt - nowtime) + 30)/60; | |
130 | h = m/60; | |
131 | m %= 60; | |
132 | ts = ctime(&sdt); | |
8fa8eeab | 133 | printf("Shutdown at %5.5s (in ", ts+11); |
954bb1fe BJ |
134 | if (h > 0) |
135 | printf("%d hour%s ", h, h != 1 ? "s" : ""); | |
8fa8eeab | 136 | printf("%d minute%s) ", m, m != 1 ? "s" : ""); |
954bb1fe BJ |
137 | #ifndef DEBUG |
138 | signal(SIGHUP, SIG_IGN); | |
139 | signal(SIGQUIT, SIG_IGN); | |
140 | signal(SIGINT, SIG_IGN); | |
141 | #endif | |
142 | signal(SIGTERM, finish); | |
143 | signal(SIGALRM, do_nothing); | |
144 | nice(-20); | |
8fa8eeab | 145 | #ifndef DEBUG |
954bb1fe | 146 | if (i = fork()) { |
8fa8eeab | 147 | printf("[pid %d]\n", i); |
954bb1fe BJ |
148 | exit(0); |
149 | } | |
8fa8eeab | 150 | #endif |
954bb1fe BJ |
151 | sint = 1 HOURS; |
152 | f = ""; | |
153 | for (;;) { | |
154 | for (i = 0; stogo <= interval[i].stogo && interval[i].sint; i++) | |
155 | sint = interval[i].sint; | |
156 | if (stogo <= NOLOGTIME && nlflag) { | |
157 | nlflag = 0; | |
158 | nolog(sdt); | |
159 | } | |
160 | if (sint >= stogo || sint == 0) | |
161 | f = "FINAL "; | |
162 | ufd = open("/etc/utmp",0); | |
163 | nowtime = time((long *) 0); | |
164 | while (read(ufd,&utmp,sizeof utmp)==sizeof utmp) | |
165 | if (utmp.ut_name[0]) { | |
166 | strcpy(term, tpath); | |
167 | strncat(term, utmp.ut_line, sizeof utmp.ut_line); | |
168 | alarm(3); | |
169 | #ifdef DEBUG | |
8fa8eeab | 170 | if ((termf = stdout) != NULL) |
954bb1fe BJ |
171 | #else |
172 | if ((termf = fopen(term, "w")) != NULL) | |
173 | #endif | |
174 | { | |
175 | alarm(0); | |
176 | setbuf(termf, tbuf); | |
177 | fprintf(termf, "\n\n"); | |
178 | warn(termf, sdt, nowtime); | |
179 | if (sdt - nowtime > 1 MINUTES) | |
180 | for (mess = nolog2; *mess; mess++) | |
181 | fprintf(termf, "%s ", *mess); | |
182 | fputc('\n', termf); | |
8fa8eeab BJ |
183 | alarm(5); |
184 | #ifdef DEBUG | |
185 | fflush(termf); | |
186 | #else | |
954bb1fe | 187 | fclose(termf); |
8fa8eeab | 188 | #endif |
954bb1fe BJ |
189 | alarm(0); |
190 | } | |
191 | } | |
192 | if (stogo < 0) { | |
193 | printf("\n\007\007System shutdown time has arrived\007\007\n"); | |
8fa8eeab | 194 | log_entry(sdt); |
954bb1fe BJ |
195 | unlink(nologin); |
196 | if (!killflg) { | |
197 | printf("but you'll have to do it yourself\n"); | |
198 | finish(); | |
199 | } | |
200 | #ifndef DEBUG | |
201 | if (reboot) | |
202 | execle(REBOOT, "reboot", 0, 0); | |
203 | if (halt) | |
204 | execle(HALT, "halt", 0, 0); | |
205 | kill(1, SIGTERM); /* sync */ | |
206 | kill(1, SIGTERM); /* sync */ | |
207 | sleep(20); | |
208 | #else | |
209 | printf("EXTERMINATE EXTERMINATE\n"); | |
210 | #endif | |
211 | finish(); | |
212 | } | |
213 | stogo = sdt - time((long *) 0); | |
214 | if (stogo > 0) | |
215 | sleep(sint<stogo ? sint : stogo); | |
216 | stogo -= sint; | |
217 | } | |
218 | } | |
219 | ||
220 | time_t | |
221 | getsdt(s) | |
222 | register char *s; | |
223 | { | |
224 | time_t t, t1, tim; | |
225 | register char c; | |
226 | struct tm *lt; | |
227 | ||
228 | if (*s == '+') { | |
229 | ++s; | |
230 | t = 0; | |
231 | for (;;) { | |
232 | c = *s++; | |
233 | if (!isdigit(c)) | |
234 | break; | |
235 | t = t * 10 + c - '0'; | |
236 | } | |
237 | if (t <= 0) | |
238 | t = 5; | |
239 | t *= 60; | |
240 | tim = time((long *) 0) + t; | |
241 | return(tim); | |
242 | } | |
243 | t = 0; | |
244 | while (strlen(s) > 2 && isdigit(*s)) | |
245 | t = t * 10 + *s++ - '0'; | |
246 | if (*s == ':') | |
247 | s++; | |
248 | if (t > 23) | |
249 | goto badform; | |
250 | tim = t*60; | |
251 | t = 0; | |
252 | while (isdigit(*s)) | |
253 | t = t * 10 + *s++ - '0'; | |
254 | if (t > 59) | |
255 | goto badform; | |
256 | tim += t; | |
257 | tim *= 60; | |
258 | t1 = time((long *) 0); | |
259 | lt = localtime(&t1); | |
260 | t = lt->tm_sec + lt->tm_min*60 + lt->tm_hour*3600; | |
261 | if (tim < t || tim >= (24*3600)) { | |
262 | /* before now or after midnight */ | |
263 | printf("That must be tomorrow\nCan't you wait till then?\n"); | |
264 | finish(); | |
265 | } | |
266 | return (t1 + tim -t); | |
267 | badform: | |
268 | printf("Bad time format\n"); | |
269 | finish(); | |
270 | } | |
271 | ||
272 | warn(term, sdt, nowtime) | |
273 | FILE *term; | |
274 | long sdt, nowtime; | |
275 | { | |
276 | char *ts; | |
277 | ||
8fa8eeab | 278 | fprintf(term, "\007\007*** System shutdown message from %s ***\n", shutter); |
954bb1fe BJ |
279 | ts = ctime(&sdt); |
280 | if (sdt - nowtime > 10 MINUTES) | |
281 | fprintf(term, "System going down at %5.5s\n", ts+11); | |
282 | else if ( sdt - nowtime > 60 SECONDS ) { | |
283 | fprintf(term, "System going down in %d minute%s\n", | |
284 | (sdt-nowtime+30)/60, (sdt-nowtime+30)/60 != 1 ? "s" : ""); | |
285 | } else if ( sdt - nowtime > 0 ) { | |
8fa8eeab | 286 | fprintf(term, "System going down in %d second%s\n", |
954bb1fe BJ |
287 | sdt-nowtime, sdt-nowtime != 1 ? "s" : ""); |
288 | } else | |
289 | fprintf(term, "System going down IMMEDIATELY\n"); | |
290 | } | |
291 | ||
292 | nolog(sdt) | |
293 | long sdt; | |
294 | { | |
295 | FILE *nologf; | |
296 | register char **mess; | |
297 | ||
298 | if ((nologf = fopen(nologin, "w")) != NULL) { | |
299 | fprintf(nologf, nolog1, (ctime(&sdt)) + 11); | |
300 | for (mess = nolog2; *mess; mess++) | |
301 | fprintf(nologf, "\t%s\n", *mess); | |
302 | fclose(nologf); | |
303 | } | |
304 | } | |
305 | ||
306 | finish() | |
307 | { | |
308 | signal(SIGTERM, SIG_IGN); | |
309 | unlink(nologin); | |
310 | exit(0); | |
311 | } | |
312 | ||
313 | do_nothing() | |
314 | { | |
315 | ||
316 | signal(SIGALRM, do_nothing); | |
317 | } | |
8fa8eeab BJ |
318 | |
319 | /* | |
320 | * make an entry in the shutdown log | |
321 | */ | |
322 | ||
f8969a9a BJ |
323 | char *days[] = { |
324 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | |
325 | }; | |
326 | ||
327 | char *months[] = { | |
328 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", | |
329 | "Oct", "Nov", "Dec" | |
330 | }; | |
331 | ||
332 | log_entry(now) | |
333 | time_t now; | |
8fa8eeab | 334 | { |
f8969a9a BJ |
335 | register FILE *fp; |
336 | register char **mess; | |
337 | struct tm *tm, *localtime(); | |
8fa8eeab | 338 | |
f8969a9a BJ |
339 | tm = localtime(&now); |
340 | fp = fopen(LOGFILE, "a"); | |
3f51f330 BJ |
341 | if (fp==0) |
342 | return; | |
f8969a9a BJ |
343 | fseek(fp, 0L, 2); |
344 | fprintf(fp, "%02d:%02d %s %s %2d, %4d. Shutdown:", tm->tm_hour, | |
345 | tm->tm_min, days[tm->tm_wday], months[tm->tm_mon], | |
346 | tm->tm_mday, tm->tm_year + 1900); | |
8fa8eeab | 347 | for (mess = nolog2; *mess; mess++) |
f8969a9a BJ |
348 | fprintf(fp, " %s", *mess); |
349 | fputc('\n', fp); | |
350 | fclose(fp); | |
8fa8eeab | 351 | } |