Commit | Line | Data |
---|---|---|
8c5eec2f | 1 | /* |
d6b981f6 KB |
2 | * Copyright (c) 1988, 1990, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
dc297c2a | 4 | * |
d60d530a | 5 | * %sccs.include.redist.c% |
8c5eec2f DF |
6 | */ |
7 | ||
37c640e2 | 8 | #ifndef lint |
d6b981f6 KB |
9 | static char copyright[] = |
10 | "@(#) Copyright (c) 1988, 1990, 1993\n\ | |
11 | The Regents of the University of California. All rights reserved.\n"; | |
dc297c2a | 12 | #endif /* not lint */ |
8c5eec2f DF |
13 | |
14 | #ifndef lint | |
35270711 | 15 | static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) %G%"; |
dc297c2a | 16 | #endif /* not lint */ |
954bb1fe | 17 | |
dc297c2a | 18 | #include <sys/param.h> |
2c5483d8 SL |
19 | #include <sys/time.h> |
20 | #include <sys/resource.h> | |
a96b688d | 21 | #include <sys/syslog.h> |
a4e77298 KB |
22 | |
23 | #include <ctype.h> | |
24 | #include <fcntl.h> | |
dc297c2a | 25 | #include <pwd.h> |
a4e77298 KB |
26 | #include <setjmp.h> |
27 | #include <signal.h> | |
dc297c2a | 28 | #include <stdio.h> |
a4e77298 KB |
29 | #include <stdlib.h> |
30 | #include <string.h> | |
31 | #include <tzfile.h> | |
32 | #include <unistd.h> | |
33 | ||
2da4f8e2 | 34 | #include "pathnames.h" |
a96b688d | 35 | |
dc297c2a | 36 | #ifdef DEBUG |
2da4f8e2 KB |
37 | #undef _PATH_NOLOGIN |
38 | #define _PATH_NOLOGIN "./nologin" | |
39 | #undef _PATH_FASTBOOT | |
40 | #define _PATH_FASTBOOT "./fastboot" | |
954bb1fe | 41 | #endif |
3ce26e8e | 42 | |
dc297c2a KB |
43 | #define H *60*60 |
44 | #define M *60 | |
45 | #define S *1 | |
46 | #define NOLOG_TIME 5*60 | |
954bb1fe | 47 | struct interval { |
dc297c2a KB |
48 | int timeleft, timetowait; |
49 | } tlist[] = { | |
50 | 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, | |
51 | 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, | |
52 | 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, | |
53 | 0, 0, | |
a4e77298 | 54 | }; |
dc297c2a KB |
55 | #undef H |
56 | #undef M | |
57 | #undef S | |
58 | ||
59 | static time_t offset, shuttime; | |
60 | static int dofast, dohalt, doreboot, killflg, mbuflen; | |
61 | static char *nosync, *whom, mbuf[BUFSIZ]; | |
62 | ||
a4e77298 KB |
63 | void badtime __P((void)); |
64 | void die_you_gravy_sucking_pig_dog __P((void)); | |
65 | void doitfast __P((void)); | |
66 | void finish __P((int)); | |
67 | void getoffset __P((char *)); | |
68 | void loop __P((void)); | |
69 | void nolog __P((void)); | |
70 | void timeout __P((int)); | |
71 | void timewarn __P((int)); | |
72 | void usage __P((void)); | |
73 | ||
74 | int | |
dc297c2a | 75 | main(argc, argv) |
954bb1fe | 76 | int argc; |
a4e77298 | 77 | char *argv[]; |
954bb1fe | 78 | { |
dc297c2a | 79 | extern int optind; |
30834fb6 | 80 | register char *p, *endp; |
f0d98f59 | 81 | struct passwd *pw; |
a4e77298 | 82 | int arglen, ch, len, readstdin; |
dc297c2a KB |
83 | |
84 | #ifndef DEBUG | |
85 | if (geteuid()) { | |
85c637cf | 86 | (void)fprintf(stderr, "shutdown: NOT super-user\n"); |
dc297c2a KB |
87 | exit(1); |
88 | } | |
89 | #endif | |
30834fb6 KB |
90 | nosync = NULL; |
91 | readstdin = 0; | |
92 | while ((ch = getopt(argc, argv, "-fhknr")) != EOF) | |
613c7e46 | 93 | switch (ch) { |
30834fb6 KB |
94 | case '-': |
95 | readstdin = 1; | |
96 | break; | |
dc297c2a KB |
97 | case 'f': |
98 | dofast = 1; | |
99 | break; | |
100 | case 'h': | |
101 | dohalt = 1; | |
102 | break; | |
954bb1fe | 103 | case 'k': |
dc297c2a KB |
104 | killflg = 1; |
105 | break; | |
8b3f4639 | 106 | case 'n': |
dc297c2a KB |
107 | nosync = "-n"; |
108 | break; | |
954bb1fe | 109 | case 'r': |
db1d8af0 | 110 | doreboot = 1; |
dc297c2a KB |
111 | break; |
112 | case '?': | |
954bb1fe | 113 | default: |
dc297c2a | 114 | usage(); |
954bb1fe | 115 | } |
dc297c2a KB |
116 | argc -= optind; |
117 | argv += optind; | |
118 | ||
119 | if (argc < 1) | |
120 | usage(); | |
121 | ||
30834fb6 | 122 | if (dofast && nosync) { |
85c637cf | 123 | (void)fprintf(stderr, |
dc297c2a KB |
124 | "shutdown: incompatible switches -f and -n.\n"); |
125 | usage(); | |
7b602762 | 126 | } |
dc297c2a | 127 | if (doreboot && dohalt) { |
85c637cf | 128 | (void)fprintf(stderr, |
dc297c2a KB |
129 | "shutdown: incompatible switches -h and -r.\n"); |
130 | usage(); | |
a96b688d | 131 | } |
dc297c2a | 132 | getoffset(*argv++); |
30834fb6 | 133 | |
dc297c2a | 134 | if (*argv) { |
30834fb6 KB |
135 | for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { |
136 | arglen = strlen(*argv); | |
137 | if ((len -= arglen) <= 2) | |
138 | break; | |
139 | if (p != mbuf) | |
140 | *p++ = ' '; | |
35270711 | 141 | memmove(p, *argv, arglen); |
30834fb6 KB |
142 | p += arglen; |
143 | } | |
144 | *p = '\n'; | |
145 | *++p = '\0'; | |
954bb1fe | 146 | } |
30834fb6 KB |
147 | |
148 | if (readstdin) { | |
149 | p = mbuf; | |
150 | endp = mbuf + sizeof(mbuf) - 2; | |
151 | for (;;) { | |
152 | if (!fgets(p, endp - p + 1, stdin)) | |
153 | break; | |
154 | for (; *p && p < endp; ++p); | |
155 | if (p == endp) { | |
156 | *p = '\n'; | |
157 | *++p = '\0'; | |
158 | break; | |
159 | } | |
160 | } | |
3ce26e8e | 161 | } |
dc297c2a KB |
162 | mbuflen = strlen(mbuf); |
163 | ||
164 | if (offset) | |
85c637cf | 165 | (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); |
dc297c2a | 166 | else |
85c637cf | 167 | (void)printf("Shutdown NOW!\n"); |
dc297c2a KB |
168 | |
169 | if (!(whom = getlogin())) | |
170 | whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; | |
171 | ||
8fa8eeab | 172 | #ifdef DEBUG |
dc297c2a | 173 | (void)putc('\n', stdout); |
8fa8eeab | 174 | #else |
dc297c2a KB |
175 | (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); |
176 | { | |
177 | int forkpid; | |
8b3f4639 | 178 | |
dc297c2a KB |
179 | forkpid = fork(); |
180 | if (forkpid == -1) { | |
181 | perror("shutdown: fork"); | |
182 | exit(1); | |
183 | } | |
184 | if (forkpid) { | |
85c637cf | 185 | (void)printf("shutdown: [pid %d]\n", forkpid); |
dc297c2a | 186 | exit(0); |
954bb1fe | 187 | } |
954bb1fe | 188 | } |
dc297c2a KB |
189 | #endif |
190 | openlog("shutdown", LOG_CONS, LOG_AUTH); | |
191 | loop(); | |
a4e77298 | 192 | /* NOTREACHED */ |
954bb1fe BJ |
193 | } |
194 | ||
a4e77298 | 195 | void |
dc297c2a | 196 | loop() |
954bb1fe | 197 | { |
a4e77298 | 198 | struct interval *tp; |
dc297c2a KB |
199 | u_int sltime; |
200 | int logged; | |
201 | ||
202 | if (offset <= NOLOG_TIME) { | |
203 | logged = 1; | |
204 | nolog(); | |
205 | } | |
206 | else | |
207 | logged = 0; | |
208 | tp = tlist; | |
209 | if (tp->timeleft < offset) | |
210 | (void)sleep((u_int)(offset - tp->timeleft)); | |
211 | else { | |
212 | while (offset < tp->timeleft) | |
213 | ++tp; | |
214 | /* | |
a4e77298 | 215 | * Warn now, if going to sleep more than a fifth of |
dc297c2a KB |
216 | * the next wait time. |
217 | */ | |
218 | if (sltime = offset - tp->timeleft) { | |
219 | if (sltime > tp->timetowait / 5) | |
a4e77298 | 220 | timewarn(offset); |
dc297c2a | 221 | (void)sleep(sltime); |
954bb1fe | 222 | } |
954bb1fe | 223 | } |
dc297c2a | 224 | for (;; ++tp) { |
a4e77298 | 225 | timewarn(tp->timeleft); |
dc297c2a KB |
226 | if (!logged && tp->timeleft <= NOLOG_TIME) { |
227 | logged = 1; | |
228 | nolog(); | |
229 | } | |
230 | (void)sleep((u_int)tp->timetowait); | |
231 | if (!tp->timeleft) | |
232 | break; | |
233 | } | |
234 | die_you_gravy_sucking_pig_dog(); | |
235 | } | |
236 | ||
237 | static jmp_buf alarmbuf; | |
238 | ||
a4e77298 KB |
239 | void |
240 | timewarn(timeleft) | |
241 | int timeleft; | |
dc297c2a KB |
242 | { |
243 | static int first; | |
613c7e46 | 244 | static char hostname[MAXHOSTNAMELEN + 1]; |
dc297c2a | 245 | FILE *pf; |
a4e77298 | 246 | char wcmd[MAXPATHLEN + 4]; |
dc297c2a | 247 | |
3f8356a8 | 248 | if (!first++) |
613c7e46 | 249 | (void)gethostname(hostname, sizeof(hostname)); |
dc297c2a | 250 | |
613c7e46 | 251 | /* undoc -n option to wall suppresses normal wall banner */ |
a4e77298 | 252 | (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); |
613c7e46 | 253 | if (!(pf = popen(wcmd, "w"))) { |
2da4f8e2 | 254 | syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); |
dc297c2a KB |
255 | return; |
256 | } | |
257 | ||
85c637cf KB |
258 | (void)fprintf(pf, |
259 | "\007*** %sSystem shutdown message from %s@%s ***\007\n", | |
a4e77298 | 260 | timeleft ? "": "FINAL ", whom, hostname); |
dc297c2a | 261 | |
a4e77298 | 262 | if (timeleft > 10*60) |
85c637cf | 263 | (void)fprintf(pf, "System going down at %5.5s\n\n", |
dc297c2a | 264 | ctime(&shuttime) + 11); |
a4e77298 | 265 | else if (timeleft > 59) |
85c637cf | 266 | (void)fprintf(pf, "System going down in %d minute%s\n\n", |
a4e77298 KB |
267 | timeleft / 60, (timeleft > 60) ? "s" : ""); |
268 | else if (timeleft) | |
85c637cf | 269 | (void)fprintf(pf, "System going down in 30 seconds\n\n"); |
dc297c2a | 270 | else |
85c637cf | 271 | (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); |
dc297c2a | 272 | |
30834fb6 KB |
273 | if (mbuflen) |
274 | (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); | |
dc297c2a KB |
275 | |
276 | /* | |
277 | * play some games, just in case wall doesn't come back | |
278 | * probably unecessary, given that wall is careful. | |
279 | */ | |
280 | if (!setjmp(alarmbuf)) { | |
613c7e46 | 281 | (void)signal(SIGALRM, timeout); |
dc297c2a KB |
282 | (void)alarm((u_int)30); |
283 | (void)pclose(pf); | |
284 | (void)alarm((u_int)0); | |
613c7e46 | 285 | (void)signal(SIGALRM, SIG_DFL); |
dc297c2a KB |
286 | } |
287 | } | |
288 | ||
85c637cf | 289 | void |
a4e77298 KB |
290 | timeout(signo) |
291 | int signo; | |
dc297c2a KB |
292 | { |
293 | longjmp(alarmbuf, 1); | |
294 | } | |
295 | ||
a4e77298 | 296 | void |
dc297c2a KB |
297 | die_you_gravy_sucking_pig_dog() |
298 | { | |
85c637cf | 299 | |
dc297c2a KB |
300 | syslog(LOG_NOTICE, "%s by %s: %s", |
301 | doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); | |
302 | (void)sleep(2); | |
303 | ||
85c637cf | 304 | (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); |
dc297c2a | 305 | if (killflg) { |
85c637cf | 306 | (void)printf("\rbut you'll have to do it yourself\r\n"); |
a4e77298 | 307 | finish(0); |
954bb1fe | 308 | } |
dc297c2a KB |
309 | if (dofast) |
310 | doitfast(); | |
311 | #ifdef DEBUG | |
312 | if (doreboot) | |
85c637cf | 313 | (void)printf("reboot"); |
30834fb6 | 314 | else if (dohalt) |
85c637cf | 315 | (void)printf("halt"); |
30834fb6 | 316 | if (nosync) |
85c637cf | 317 | (void)printf(" no sync"); |
dc297c2a | 318 | if (dofast) |
85c637cf KB |
319 | (void)printf(" no fsck"); |
320 | (void)printf("\nkill -HUP 1\n"); | |
dc297c2a KB |
321 | #else |
322 | if (doreboot) { | |
2da4f8e2 KB |
323 | execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); |
324 | syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); | |
dc297c2a KB |
325 | perror("shutdown"); |
326 | } | |
327 | else if (dohalt) { | |
2da4f8e2 KB |
328 | execle(_PATH_HALT, "halt", "-l", nosync, 0); |
329 | syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); | |
dc297c2a KB |
330 | perror("shutdown"); |
331 | } | |
332 | (void)kill(1, SIGTERM); /* to single user */ | |
333 | #endif | |
a4e77298 | 334 | finish(0); |
954bb1fe BJ |
335 | } |
336 | ||
dc297c2a | 337 | #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; |
dc297c2a | 338 | |
a4e77298 | 339 | void |
dc297c2a KB |
340 | getoffset(timearg) |
341 | register char *timearg; | |
954bb1fe | 342 | { |
dc297c2a KB |
343 | register struct tm *lt; |
344 | register char *p; | |
a4e77298 | 345 | time_t now; |
dc297c2a KB |
346 | |
347 | if (!strcasecmp(timearg, "now")) { /* now */ | |
348 | offset = 0; | |
349 | return; | |
350 | } | |
351 | ||
352 | (void)time(&now); | |
353 | if (*timearg == '+') { /* +minutes */ | |
354 | if (!isdigit(*++timearg)) | |
85c637cf KB |
355 | badtime(); |
356 | offset = atoi(timearg) * 60; | |
dc297c2a KB |
357 | shuttime = now + offset; |
358 | return; | |
359 | } | |
360 | ||
361 | /* handle hh:mm by getting rid of the colon */ | |
362 | for (p = timearg; *p; ++p) | |
363 | if (!isascii(*p) || !isdigit(*p)) | |
364 | if (*p == ':' && strlen(p) == 3) { | |
365 | p[0] = p[1]; | |
366 | p[1] = p[2]; | |
367 | p[2] = '\0'; | |
368 | } | |
369 | else | |
85c637cf | 370 | badtime(); |
dc297c2a KB |
371 | |
372 | unsetenv("TZ"); /* OUR timezone */ | |
85c637cf | 373 | lt = localtime(&now); /* current time val */ |
dc297c2a KB |
374 | |
375 | switch(strlen(timearg)) { | |
376 | case 10: | |
85c637cf | 377 | lt->tm_year = ATOI2(timearg); |
dc297c2a KB |
378 | /* FALLTHROUGH */ |
379 | case 8: | |
85c637cf KB |
380 | lt->tm_mon = ATOI2(timearg); |
381 | if (--lt->tm_mon < 0 || lt->tm_mon > 11) | |
382 | badtime(); | |
dc297c2a KB |
383 | /* FALLTHROUGH */ |
384 | case 6: | |
85c637cf KB |
385 | lt->tm_mday = ATOI2(timearg); |
386 | if (lt->tm_mday < 1 || lt->tm_mday > 31) | |
387 | badtime(); | |
dc297c2a KB |
388 | /* FALLTHROUGH */ |
389 | case 4: | |
85c637cf KB |
390 | lt->tm_hour = ATOI2(timearg); |
391 | if (lt->tm_hour < 0 || lt->tm_hour > 23) | |
392 | badtime(); | |
393 | lt->tm_min = ATOI2(timearg); | |
394 | if (lt->tm_min < 0 || lt->tm_min > 59) | |
395 | badtime(); | |
396 | lt->tm_sec = 0; | |
397 | if ((shuttime = mktime(lt)) == -1) | |
398 | badtime(); | |
399 | if ((offset = shuttime - now) < 0) { | |
400 | (void)fprintf(stderr, | |
401 | "shutdown: that time is already past.\n"); | |
402 | exit(1); | |
403 | } | |
404 | break; | |
dc297c2a | 405 | default: |
85c637cf | 406 | badtime(); |
dc297c2a | 407 | } |
954bb1fe BJ |
408 | } |
409 | ||
dc297c2a | 410 | #define FSMSG "fastboot file for fsck\n" |
a4e77298 | 411 | void |
8b3f4639 JB |
412 | doitfast() |
413 | { | |
dc297c2a | 414 | int fastfd; |
8b3f4639 | 415 | |
2da4f8e2 KB |
416 | if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, |
417 | 0664)) >= 0) { | |
dc297c2a KB |
418 | (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); |
419 | (void)close(fastfd); | |
8b3f4639 JB |
420 | } |
421 | } | |
422 | ||
dc297c2a | 423 | #define NOMSG "\n\nNO LOGINS: System going down at " |
a4e77298 | 424 | void |
dc297c2a | 425 | nolog() |
954bb1fe | 426 | { |
85c637cf | 427 | int logfd; |
a4e77298 | 428 | char *ct; |
dc297c2a | 429 | |
2da4f8e2 | 430 | (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ |
dc297c2a KB |
431 | (void)signal(SIGINT, finish); |
432 | (void)signal(SIGHUP, finish); | |
433 | (void)signal(SIGQUIT, finish); | |
434 | (void)signal(SIGTERM, finish); | |
2da4f8e2 KB |
435 | if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, |
436 | 0664)) >= 0) { | |
dc297c2a KB |
437 | (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); |
438 | ct = ctime(&shuttime); | |
439 | (void)write(logfd, ct + 11, 5); | |
440 | (void)write(logfd, "\n\n", 2); | |
441 | (void)write(logfd, mbuf, strlen(mbuf)); | |
442 | (void)close(logfd); | |
954bb1fe BJ |
443 | } |
444 | } | |
445 | ||
85c637cf | 446 | void |
a4e77298 KB |
447 | finish(signo) |
448 | int signo; | |
954bb1fe | 449 | { |
a5118597 KB |
450 | if (!killflg) |
451 | (void)unlink(_PATH_NOLOGIN); | |
954bb1fe BJ |
452 | exit(0); |
453 | } | |
454 | ||
a4e77298 | 455 | void |
85c637cf KB |
456 | badtime() |
457 | { | |
458 | (void)fprintf(stderr, "shutdown: bad time format.\n"); | |
459 | exit(1); | |
460 | } | |
461 | ||
a4e77298 | 462 | void |
dc297c2a | 463 | usage() |
954bb1fe | 464 | { |
dc297c2a KB |
465 | fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); |
466 | exit(1); | |
954bb1fe | 467 | } |