shutdown.c revision 1.19 1 /* $NetBSD: shutdown.c,v 1.19 1998/01/20 22:30:15 mycroft 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.19 1998/01/20 22:30:15 mycroft 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 <tzfile.h>
65 #include <unistd.h>
66
67 #include "pathnames.h"
68
69 #ifdef DEBUG
70 #undef _PATH_NOLOGIN
71 #define _PATH_NOLOGIN "./nologin"
72 #undef _PATH_FASTBOOT
73 #define _PATH_FASTBOOT "./fastboot"
74 #endif
75
76 #define H *60*60
77 #define M *60
78 #define S *1
79 #define NOLOG_TIME 5*60
80 struct interval {
81 int timeleft, timetowait;
82 } tlist[] = {
83 { 10 H, 5 H }, { 5 H, 3 H }, { 2 H, 1 H }, { 1 H, 30 M },
84 { 30 M, 10 M }, { 20 M, 10 M }, { 10 M, 5 M }, { 5 M, 3 M },
85 { 2 M, 1 M }, { 1 M, 30 S }, { 30 S, 30 S },
86 { 0, 0 }
87 };
88 #undef H
89 #undef M
90 #undef S
91
92 static time_t offset, shuttime;
93 static int dofast, dohalt, doreboot, killflg, mbuflen, nosync, dodump;
94 static char *whom, mbuf[BUFSIZ];
95
96 void badtime __P((void));
97 void die_you_gravy_sucking_pig_dog __P((void));
98 void doitfast __P((void));
99 void finish __P((int));
100 void getoffset __P((char *));
101 void loop __P((void));
102 int main __P((int, char *[]));
103 void nolog __P((void));
104 void timeout __P((int));
105 void timewarn __P((int));
106 void usage __P((void));
107
108 int
109 main(argc, argv)
110 int argc;
111 char *argv[];
112 {
113 char *p, *endp;
114 struct passwd *pw;
115 int arglen, ch, len;
116
117 #ifndef DEBUG
118 if (geteuid())
119 errx(1, "NOT super-user");
120 #endif
121 while ((ch = getopt(argc, argv, "dfhknr")) != -1)
122 switch (ch) {
123 case 'd':
124 dodump = 1;
125 break;
126 case 'f':
127 dofast = 1;
128 break;
129 case 'h':
130 dohalt = 1;
131 break;
132 case 'k':
133 killflg = 1;
134 break;
135 case 'n':
136 nosync = 1;
137 break;
138 case 'r':
139 doreboot = 1;
140 break;
141 case '?':
142 default:
143 usage();
144 }
145 argc -= optind;
146 argv += optind;
147
148 if (argc < 1)
149 usage();
150
151 if (dofast && nosync) {
152 warnx("incompatible switches -f and -n\n");
153 usage();
154 }
155 if (doreboot && dohalt) {
156 warnx("incompatible switches -h and -r\n");
157 usage();
158 }
159
160 getoffset(*argv++);
161
162 if (*argv)
163 if (strcmp(*argv, "-")) {
164 for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
165 arglen = strlen(*argv);
166 if ((len -= arglen) <= 2)
167 break;
168 if (p != mbuf)
169 *p++ = ' ';
170 memmove(p, *argv, arglen);
171 p += arglen;
172 }
173 *p = '\n';
174 *++p = '\0';
175 } else {
176 p = mbuf;
177 endp = mbuf + sizeof(mbuf) - 2;
178 for (;;) {
179 if (!fgets(p, endp - p + 1, stdin))
180 break;
181 for (; *p && p < endp; ++p);
182 if (p == endp) {
183 *p = '\n';
184 *++p = '\0';
185 break;
186 }
187 }
188 }
189 mbuflen = strlen(mbuf);
190
191 if (offset)
192 (void)printf("Shutdown at %.24s.\n", ctime(&shuttime));
193 else
194 (void)printf("Shutdown NOW!\n");
195
196 if (!(whom = getlogin()))
197 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
198
199 #ifdef DEBUG
200 (void)putc('\n', stdout);
201 #else
202 (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
203 {
204 int forkpid;
205
206 forkpid = fork();
207 if (forkpid == -1) {
208 perror("shutdown: fork");
209 exit(1);
210 }
211 if (forkpid) {
212 (void)printf("shutdown: [pid %d]\n", forkpid);
213 exit(0);
214 }
215 }
216 #endif
217 openlog("shutdown", LOG_CONS, LOG_AUTH);
218 loop();
219 /* NOTREACHED */
220 #ifdef __GNUC__
221 return 1;
222 #endif
223 }
224
225 void
226 loop()
227 {
228 struct interval *tp;
229 u_int sltime;
230 int logged;
231
232 if (offset <= NOLOG_TIME) {
233 logged = 1;
234 nolog();
235 }
236 else
237 logged = 0;
238 tp = tlist;
239 if (tp->timeleft < offset)
240 (void)sleep((u_int)(offset - tp->timeleft));
241 else {
242 while (offset < tp->timeleft)
243 ++tp;
244 /*
245 * Warn now, if going to sleep more than a fifth of
246 * the next wait time.
247 */
248 if ((sltime = offset - tp->timeleft) != 0) {
249 if (sltime > tp->timetowait / 5)
250 timewarn(offset);
251 (void)sleep(sltime);
252 }
253 }
254 for (;; ++tp) {
255 timewarn(tp->timeleft);
256 if (!logged && tp->timeleft <= NOLOG_TIME) {
257 logged = 1;
258 nolog();
259 }
260 (void)sleep((u_int)tp->timetowait);
261 if (!tp->timeleft)
262 break;
263 }
264 die_you_gravy_sucking_pig_dog();
265 }
266
267 static jmp_buf alarmbuf;
268
269 void
270 timewarn(timeleft)
271 int timeleft;
272 {
273 static int first;
274 static char hostname[MAXHOSTNAMELEN + 1];
275 FILE *pf;
276 char wcmd[MAXPATHLEN + 4];
277
278 if (!first++)
279 (void)gethostname(hostname, sizeof(hostname));
280
281 /* undoc -n option to wall suppresses normal wall banner */
282 (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL);
283 if (!(pf = popen(wcmd, "w"))) {
284 syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
285 return;
286 }
287
288 (void)fprintf(pf,
289 "\007*** %sSystem shutdown message from %s@%s ***\007\n",
290 timeleft ? "": "FINAL ", whom, hostname);
291
292 if (timeleft > 10*60)
293 (void)fprintf(pf, "System going down at %5.5s\n\n",
294 ctime(&shuttime) + 11);
295 else if (timeleft > 59)
296 (void)fprintf(pf, "System going down in %d minute%s\n\n",
297 timeleft / 60, (timeleft > 60) ? "s" : "");
298 else if (timeleft)
299 (void)fprintf(pf, "System going down in 30 seconds\n\n");
300 else
301 (void)fprintf(pf, "System going down IMMEDIATELY\n\n");
302
303 if (mbuflen)
304 (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
305
306 /*
307 * play some games, just in case wall doesn't come back
308 * probably unecessary, given that wall is careful.
309 */
310 if (!setjmp(alarmbuf)) {
311 (void)signal(SIGALRM, timeout);
312 (void)alarm((u_int)30);
313 (void)pclose(pf);
314 (void)alarm((u_int)0);
315 (void)signal(SIGALRM, SIG_DFL);
316 }
317 }
318
319 void
320 timeout(signo)
321 int signo;
322 {
323 longjmp(alarmbuf, 1);
324 }
325
326 void
327 die_you_gravy_sucking_pig_dog()
328 {
329
330 syslog(LOG_NOTICE, "%s by %s: %s",
331 doreboot || dodump ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
332 (void)sleep(2);
333
334 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
335 if (killflg) {
336 (void)printf("\rbut you'll have to do it yourself\r\n");
337 finish(0);
338 }
339 if (dofast)
340 doitfast();
341 #ifdef DEBUG
342 if (doreboot || dodump)
343 (void)printf("reboot%s", dodump ? " -d" : "");
344 else if (dohalt)
345 (void)printf("halt");
346 if (nosync)
347 (void)printf(" no sync");
348 if (dofast)
349 (void)printf(" no fsck");
350 (void)printf("\nkill -HUP 1\n");
351 #else
352 if (doreboot || dodump) {
353 execle(_PATH_REBOOT, "reboot",
354 dodump ? (nosync ? "-ldn" : "-ld") :
355 (nosync ? "-ln" : "-l"), 0, (char **)0);
356 syslog(LOG_ERR, "shutdown: can't exec %s: %m", _PATH_REBOOT);
357 perror("shutdown");
358 }
359 else if (dohalt) {
360 execle(_PATH_HALT, "halt", nosync ? "-ln" : "-l",
361 (char *)0, (char **)0);
362 syslog(LOG_ERR, "shutdown: can't exec %s: %m", _PATH_HALT);
363 perror("shutdown");
364 }
365 (void)kill(1, SIGTERM); /* to single user */
366 #endif
367 finish(0);
368 }
369
370 #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2;
371
372 void
373 getoffset(timearg)
374 char *timearg;
375 {
376 struct tm *lt;
377 char *p;
378 time_t now;
379 int yearset;
380
381 if (!strcasecmp(timearg, "now")) { /* now */
382 offset = 0;
383 return;
384 }
385
386 (void)time(&now);
387 if (*timearg == '+') { /* +minutes */
388 if (!isdigit(*++timearg))
389 badtime();
390 offset = atoi(timearg) * 60;
391 shuttime = now + offset;
392 return;
393 }
394
395 /* handle hh:mm by getting rid of the colon */
396 for (p = timearg; *p; ++p)
397 if (!isascii(*p) || !isdigit(*p))
398 if (*p == ':' && strlen(p) == 3) {
399 p[0] = p[1];
400 p[1] = p[2];
401 p[2] = '\0';
402 }
403 else
404 badtime();
405
406 unsetenv("TZ"); /* OUR timezone */
407 lt = localtime(&now); /* current time val */
408
409 lt->tm_sec = 0;
410
411 yearset = 0;
412 switch (strlen(timearg)) {
413 case 12:
414 lt->tm_year = ATOI2(timearg);
415 lt->tm_year *= 100;
416 yearset = 1;
417 /* FALLTHROUGH */
418 case 10:
419 if (yearset) {
420 yearset = ATOI2(timearg);
421 lt->tm_year += yearset;
422 } else {
423 yearset = ATOI2(timearg);
424 if (yearset < 69)
425 lt->tm_year = yearset + 2000;
426 else
427 lt->tm_year = yearset + 1900;
428 }
429 lt->tm_year -= TM_YEAR_BASE;
430 /* FALLTHROUGH */
431 case 8:
432 lt->tm_mon = ATOI2(timearg);
433 --lt->tm_mon;
434 /* FALLTHROUGH */
435 case 6:
436 lt->tm_mday = ATOI2(timearg);
437 /* FALLTHROUGH */
438 case 4:
439 lt->tm_hour = ATOI2(timearg);
440 /* FALLTHROUGH */
441 case 2:
442 lt->tm_min = ATOI2(timearg);
443 break;
444 default:
445 badtime();
446 }
447
448 if ((shuttime = mktime(lt)) == -1)
449 badtime();
450 if ((offset = shuttime - now) < 0)
451 errx(1, "time is already past");
452 }
453
454 #define FSMSG "fastboot file for fsck\n"
455 void
456 doitfast()
457 {
458 int fastfd;
459
460 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
461 0664)) >= 0) {
462 (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
463 (void)close(fastfd);
464 }
465 }
466
467 #define NOMSG "\n\nNO LOGINS: System going down at "
468 void
469 nolog()
470 {
471 int logfd;
472 char *ct;
473
474 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */
475 (void)signal(SIGINT, finish);
476 (void)signal(SIGHUP, finish);
477 (void)signal(SIGQUIT, finish);
478 (void)signal(SIGTERM, finish);
479 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
480 0664)) >= 0) {
481 (void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
482 ct = ctime(&shuttime);
483 (void)write(logfd, ct + 11, 5);
484 (void)write(logfd, "\n\n", 2);
485 (void)write(logfd, mbuf, strlen(mbuf));
486 (void)close(logfd);
487 }
488 }
489
490 void
491 finish(signo)
492 int signo;
493 {
494 if (!killflg)
495 (void)unlink(_PATH_NOLOGIN);
496 exit(0);
497 }
498
499 void
500 badtime()
501 {
502 warnx("illegal time format");
503 usage();
504 }
505
506 void
507 usage()
508 {
509 (void)fprintf(stderr,
510 "usage: shutdown [-dfhknr] [[[[[cc]yy]mm]dd]hh]mm [message|-]\n");
511 (void)fprintf(stderr,
512 " shutdown [-dfhknr] +time [message|-]\n");
513 exit(1);
514 }
515