shutdown.c revision 1.23 1 /* $NetBSD: shutdown.c,v 1.23 1998/01/21 00:32:53 mycroft Exp $ */
2
3 #define DEBUG
4
5 /*
6 * Copyright (c) 1988, 1990, 1993
7 * The Regents of the University of California. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the University of
20 * California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38 #include <sys/cdefs.h>
39 #ifndef lint
40 __COPYRIGHT("@(#) Copyright (c) 1988, 1990, 1993\n\
41 The Regents of the University of California. All rights reserved.\n");
42 #endif /* not lint */
43
44 #ifndef lint
45 #if 0
46 static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95";
47 #else
48 __RCSID("$NetBSD: shutdown.c,v 1.23 1998/01/21 00:32:53 mycroft Exp $");
49 #endif
50 #endif /* not lint */
51
52 #include <sys/param.h>
53 #include <sys/time.h>
54 #include <sys/resource.h>
55 #include <sys/syslog.h>
56
57 #include <ctype.h>
58 #include <err.h>
59 #include <fcntl.h>
60 #include <pwd.h>
61 #include <setjmp.h>
62 #include <signal.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <tzfile.h>
67 #include <unistd.h>
68
69 #include "pathnames.h"
70
71 #ifdef DEBUG
72 #undef _PATH_NOLOGIN
73 #define _PATH_NOLOGIN "./nologin"
74 #undef _PATH_FASTBOOT
75 #define _PATH_FASTBOOT "./fastboot"
76 #endif
77
78 #define H *60*60
79 #define M *60
80 #define S *1
81 #define NOLOG_TIME 5*60
82 struct interval {
83 int timeleft, timetowait;
84 } tlist[] = {
85 { 10 H, 5 H }, { 5 H, 3 H }, { 2 H, 1 H }, { 1 H, 30 M },
86 { 30 M, 10 M }, { 20 M, 10 M }, { 10 M, 5 M }, { 5 M, 3 M },
87 { 2 M, 1 M }, { 1 M, 30 S }, { 30 S, 30 S },
88 { 0, 0 }
89 };
90 #undef H
91 #undef M
92 #undef S
93
94 static time_t offset, shuttime;
95 static int dofast, dohalt, doreboot, killflg, mbuflen, nosync, dodump;
96 static char *whom, mbuf[BUFSIZ];
97
98 void badtime __P((void));
99 void die_you_gravy_sucking_pig_dog __P((void));
100 void doitfast __P((void));
101 void finish __P((int));
102 void getoffset __P((char *));
103 void loop __P((void));
104 int main __P((int, char *[]));
105 void nolog __P((void));
106 void timeout __P((int));
107 void timewarn __P((int));
108 void usage __P((void));
109
110 int
111 main(argc, argv)
112 int argc;
113 char *argv[];
114 {
115 char *p, *endp;
116 struct passwd *pw;
117 int arglen, ch, len;
118
119 #ifndef DEBUG
120 if (geteuid())
121 errx(1, "NOT super-user");
122 #endif
123 while ((ch = getopt(argc, argv, "dfhknr")) != -1)
124 switch (ch) {
125 case 'd':
126 dodump = 1;
127 break;
128 case 'f':
129 dofast = 1;
130 break;
131 case 'h':
132 dohalt = 1;
133 break;
134 case 'k':
135 killflg = 1;
136 break;
137 case 'n':
138 nosync = 1;
139 break;
140 case 'r':
141 doreboot = 1;
142 break;
143 case '?':
144 default:
145 usage();
146 }
147 argc -= optind;
148 argv += optind;
149
150 if (argc < 1)
151 usage();
152
153 if (dodump && !dohalt && !doreboot)
154 doreboot = 1;
155
156 if (dofast && nosync) {
157 warnx("incompatible switches -f and -n");
158 usage();
159 }
160 if (dohalt && doreboot) {
161 warnx("incompatible switches -h and -r");
162 usage();
163 }
164
165 getoffset(*argv++);
166
167 if (argv[0])
168 if (strcmp(argv[0], "-") || argv[1]) {
169 for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
170 arglen = strlen(*argv);
171 if ((len -= arglen) <= 2)
172 break;
173 if (p != mbuf)
174 *p++ = ' ';
175 memmove(p, *argv, arglen);
176 p += arglen;
177 }
178 *p = '\n';
179 *++p = '\0';
180 } else {
181 p = mbuf;
182 endp = mbuf + sizeof(mbuf) - 2;
183 for (;;) {
184 if (!fgets(p, endp - p + 1, stdin))
185 break;
186 for (; *p && p < endp; ++p);
187 if (p == endp) {
188 *p = '\n';
189 *++p = '\0';
190 break;
191 }
192 }
193 }
194 mbuflen = strlen(mbuf);
195
196 if (offset)
197 (void)printf("Shutdown at %.24s.\n", ctime(&shuttime));
198 else
199 (void)printf("Shutdown NOW!\n");
200
201 if (!(whom = getlogin()))
202 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
203
204 #ifdef DEBUG
205 (void)putc('\n', stdout);
206 #else
207 (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
208 {
209 int forkpid;
210
211 forkpid = fork();
212 if (forkpid == -1) {
213 perror("shutdown: fork");
214 exit(1);
215 }
216 if (forkpid) {
217 (void)printf("shutdown: [pid %d]\n", forkpid);
218 exit(0);
219 }
220 }
221 #endif
222 openlog("shutdown", LOG_CONS, LOG_AUTH);
223 loop();
224 /* NOTREACHED */
225 #ifdef __GNUC__
226 return 1;
227 #endif
228 }
229
230 void
231 loop()
232 {
233 struct interval *tp;
234 u_int sltime;
235 int logged;
236
237 if (offset <= NOLOG_TIME) {
238 logged = 1;
239 nolog();
240 }
241 else
242 logged = 0;
243 tp = tlist;
244 if (tp->timeleft < offset)
245 (void)sleep((u_int)(offset - tp->timeleft));
246 else {
247 while (offset < tp->timeleft)
248 ++tp;
249 /*
250 * Warn now, if going to sleep more than a fifth of
251 * the next wait time.
252 */
253 if ((sltime = offset - tp->timeleft) != 0) {
254 if (sltime > tp->timetowait / 5)
255 timewarn(offset);
256 (void)sleep(sltime);
257 }
258 }
259 for (;; ++tp) {
260 timewarn(tp->timeleft);
261 if (!logged && tp->timeleft <= NOLOG_TIME) {
262 logged = 1;
263 nolog();
264 }
265 (void)sleep((u_int)tp->timetowait);
266 if (!tp->timeleft)
267 break;
268 }
269 die_you_gravy_sucking_pig_dog();
270 }
271
272 static jmp_buf alarmbuf;
273
274 void
275 timewarn(timeleft)
276 int timeleft;
277 {
278 static int first;
279 static char hostname[MAXHOSTNAMELEN + 1];
280 FILE *pf;
281 char wcmd[MAXPATHLEN + 4];
282
283 if (!first++)
284 (void)gethostname(hostname, sizeof(hostname));
285
286 /* undoc -n option to wall suppresses normal wall banner */
287 (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL);
288 if (!(pf = popen(wcmd, "w"))) {
289 syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
290 return;
291 }
292
293 (void)fprintf(pf,
294 "\007*** %sSystem shutdown message from %s@%s ***\007\n",
295 timeleft ? "": "FINAL ", whom, hostname);
296
297 if (timeleft > 10*60)
298 (void)fprintf(pf, "System going down at %5.5s\n\n",
299 ctime(&shuttime) + 11);
300 else if (timeleft > 59)
301 (void)fprintf(pf, "System going down in %d minute%s\n\n",
302 timeleft / 60, (timeleft > 60) ? "s" : "");
303 else if (timeleft)
304 (void)fprintf(pf, "System going down in 30 seconds\n\n");
305 else
306 (void)fprintf(pf, "System going down IMMEDIATELY\n\n");
307
308 if (mbuflen)
309 (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
310
311 /*
312 * play some games, just in case wall doesn't come back
313 * probably unecessary, given that wall is careful.
314 */
315 if (!setjmp(alarmbuf)) {
316 (void)signal(SIGALRM, timeout);
317 (void)alarm((u_int)30);
318 (void)pclose(pf);
319 (void)alarm((u_int)0);
320 (void)signal(SIGALRM, SIG_DFL);
321 }
322 }
323
324 void
325 timeout(signo)
326 int signo;
327 {
328 longjmp(alarmbuf, 1);
329 }
330
331 void
332 die_you_gravy_sucking_pig_dog()
333 {
334
335 syslog(LOG_NOTICE, "%s by %s: %s",
336 doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
337 (void)sleep(2);
338
339 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
340 if (killflg) {
341 (void)printf("\rbut you'll have to do it yourself\r\n");
342 finish(0);
343 }
344 if (dofast)
345 doitfast();
346 if (doreboot || dohalt) {
347 char *args[8], **arg, *path;
348
349 arg = &args[0];
350 if (doreboot) {
351 path = _PATH_REBOOT;
352 *arg++ = "reboot";
353 } else {
354 path = _PATH_HALT;
355 *arg++ = "halt";
356 }
357 if (dodump)
358 *arg++ = "-d";
359 if (nosync)
360 *arg++ = "-n";
361 *arg++ = "-l";
362 *arg++ = 0;
363 #ifndef DEBUG
364 exect(path, args, (char **)0);
365 syslog(LOG_ERR, "shutdown: can't exec %s: %m", path);
366 perror("shutdown");
367 #else
368 printf("%s", path);
369 for (arg = &args[0]; *arg; arg++)
370 printf(" %s", *arg);
371 printf("\n");
372 #endif
373 } else {
374 #ifndef DEBUG
375 (void)kill(1, SIGTERM); /* to single user */
376 #else
377 printf("kill 1\n");
378 #endif
379 }
380 finish(0);
381 }
382
383 #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
384
385 void
386 getoffset(timearg)
387 char *timearg;
388 {
389 struct tm *lt;
390 char *p;
391 time_t now;
392 int yearset;
393
394 if (!strcasecmp(timearg, "now")) { /* now */
395 offset = 0;
396 return;
397 }
398
399 (void)time(&now);
400 if (*timearg == '+') { /* +minutes */
401 if (!isdigit(*++timearg))
402 badtime();
403 offset = atoi(timearg) * 60;
404 shuttime = now + offset;
405 return;
406 }
407
408 /* handle hh:mm by getting rid of the colon */
409 for (p = timearg; *p; ++p)
410 if (!isascii(*p) || !isdigit(*p))
411 if (*p == ':' && strlen(p) == 3) {
412 p[0] = p[1];
413 p[1] = p[2];
414 p[2] = '\0';
415 }
416 else
417 badtime();
418
419 unsetenv("TZ"); /* OUR timezone */
420 lt = localtime(&now); /* current time val */
421
422 lt->tm_sec = 0;
423
424 yearset = 0;
425 switch (strlen(timearg)) {
426 case 12:
427 lt->tm_year = ATOI2(timearg) * 100 - TM_YEAR_BASE;
428 yearset = 1;
429 /* FALLTHROUGH */
430 case 10:
431 if (yearset) {
432 lt->tm_year += ATOI2(timearg);
433 } else {
434 yearset = ATOI2(timearg);
435 if (yearset < 69)
436 lt->tm_year = yearset + 2000 - TM_YEAR_BASE;
437 else
438 lt->tm_year = yearset + 1900 - TM_YEAR_BASE;
439 }
440 /* FALLTHROUGH */
441 case 8:
442 lt->tm_mon = ATOI2(timearg);
443 --lt->tm_mon;
444 /* FALLTHROUGH */
445 case 6:
446 lt->tm_mday = ATOI2(timearg);
447 /* FALLTHROUGH */
448 case 4:
449 lt->tm_hour = ATOI2(timearg);
450 /* FALLTHROUGH */
451 case 2:
452 lt->tm_min = ATOI2(timearg);
453 break;
454 default:
455 badtime();
456 }
457
458 if ((shuttime = mktime(lt)) == -1)
459 badtime();
460 if ((offset = shuttime - now) < 0)
461 errx(1, "time is already past");
462 }
463
464 #define FSMSG "fastboot file for fsck\n"
465 void
466 doitfast()
467 {
468 int fastfd;
469
470 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
471 0664)) >= 0) {
472 (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
473 (void)close(fastfd);
474 }
475 }
476
477 #define NOMSG "\n\nNO LOGINS: System going down at "
478 void
479 nolog()
480 {
481 int logfd;
482 char *ct;
483
484 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */
485 (void)signal(SIGINT, finish);
486 (void)signal(SIGHUP, finish);
487 (void)signal(SIGQUIT, finish);
488 (void)signal(SIGTERM, finish);
489 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
490 0664)) >= 0) {
491 (void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
492 ct = ctime(&shuttime);
493 (void)write(logfd, ct + 11, 5);
494 (void)write(logfd, "\n\n", 2);
495 (void)write(logfd, mbuf, strlen(mbuf));
496 (void)close(logfd);
497 }
498 }
499
500 void
501 finish(signo)
502 int signo;
503 {
504 if (!killflg)
505 (void)unlink(_PATH_NOLOGIN);
506 exit(0);
507 }
508
509 void
510 badtime()
511 {
512 warnx("illegal time format");
513 usage();
514 }
515
516 void
517 usage()
518 {
519 (void)fprintf(stderr,
520 "usage: shutdown [-dfhknr] time [message ... | -]\n");
521 exit(1);
522 }
523