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