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