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