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