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