BSD 4_3_Net_2 release
[unix-history] / usr / src / sbin / shutdown / shutdown.c
CommitLineData
8c5eec2f 1/*
613c7e46 2 * Copyright (c) 1988, 1990 Regents of the University of California.
dc297c2a
KB
3 * All rights reserved.
4 *
af359dea
C
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.
8c5eec2f
DF
32 */
33
37c640e2 34#ifndef lint
8c5eec2f 35char copyright[] =
dc297c2a 36"@(#) Copyright (c) 1988 Regents of the University of California.\n\
8c5eec2f 37 All rights reserved.\n";
dc297c2a 38#endif /* not lint */
8c5eec2f
DF
39
40#ifndef lint
af359dea 41static char sccsid[] = "@(#)shutdown.c 5.16 (Berkeley) 2/3/91";
dc297c2a 42#endif /* not lint */
954bb1fe 43
dc297c2a 44#include <sys/param.h>
2c5483d8 45#include <sys/time.h>
dc297c2a 46#include <sys/file.h>
2c5483d8 47#include <sys/resource.h>
a96b688d 48#include <sys/syslog.h>
85c637cf 49#include <sys/signal.h>
dc297c2a
KB
50#include <setjmp.h>
51#include <tzfile.h>
52#include <pwd.h>
53#include <stdio.h>
54#include <ctype.h>
2da4f8e2 55#include "pathnames.h"
a96b688d 56
dc297c2a 57#ifdef DEBUG
2da4f8e2
KB
58#undef _PATH_NOLOGIN
59#define _PATH_NOLOGIN "./nologin"
60#undef _PATH_FASTBOOT
61#define _PATH_FASTBOOT "./fastboot"
954bb1fe 62#endif
3ce26e8e 63
dc297c2a
KB
64#define H *60*60
65#define M *60
66#define S *1
67#define NOLOG_TIME 5*60
954bb1fe 68struct interval {
dc297c2a
KB
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)
954bb1fe
BJ
85 int argc;
86 char **argv;
87{
dc297c2a 88 extern int optind;
30834fb6
KB
89 register char *p, *endp;
90 int arglen, ch, len, readstdin;
f0d98f59 91 struct passwd *pw;
dc297c2a
KB
92 char *strcat(), *getlogin();
93 uid_t geteuid();
94
95#ifndef DEBUG
96 if (geteuid()) {
85c637cf 97 (void)fprintf(stderr, "shutdown: NOT super-user\n");
dc297c2a
KB
98 exit(1);
99 }
100#endif
30834fb6
KB
101 nosync = NULL;
102 readstdin = 0;
103 while ((ch = getopt(argc, argv, "-fhknr")) != EOF)
613c7e46 104 switch (ch) {
30834fb6
KB
105 case '-':
106 readstdin = 1;
107 break;
dc297c2a
KB
108 case 'f':
109 dofast = 1;
110 break;
111 case 'h':
112 dohalt = 1;
113 break;
954bb1fe 114 case 'k':
dc297c2a
KB
115 killflg = 1;
116 break;
8b3f4639 117 case 'n':
dc297c2a
KB
118 nosync = "-n";
119 break;
954bb1fe 120 case 'r':
db1d8af0 121 doreboot = 1;
dc297c2a
KB
122 break;
123 case '?':
954bb1fe 124 default:
dc297c2a 125 usage();
954bb1fe 126 }
dc297c2a
KB
127 argc -= optind;
128 argv += optind;
129
130 if (argc < 1)
131 usage();
132
30834fb6 133 if (dofast && nosync) {
85c637cf 134 (void)fprintf(stderr,
dc297c2a
KB
135 "shutdown: incompatible switches -f and -n.\n");
136 usage();
7b602762 137 }
dc297c2a 138 if (doreboot && dohalt) {
85c637cf 139 (void)fprintf(stderr,
dc297c2a
KB
140 "shutdown: incompatible switches -h and -r.\n");
141 usage();
a96b688d 142 }
dc297c2a 143 getoffset(*argv++);
30834fb6 144
dc297c2a 145 if (*argv) {
30834fb6
KB
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';
954bb1fe 157 }
30834fb6
KB
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 }
3ce26e8e 172 }
dc297c2a
KB
173 mbuflen = strlen(mbuf);
174
175 if (offset)
85c637cf 176 (void)printf("Shutdown at %.24s.\n", ctime(&shuttime));
dc297c2a 177 else
85c637cf 178 (void)printf("Shutdown NOW!\n");
dc297c2a
KB
179
180 if (!(whom = getlogin()))
181 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
182
8fa8eeab 183#ifdef DEBUG
dc297c2a 184 (void)putc('\n', stdout);
8fa8eeab 185#else
dc297c2a
KB
186 (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
187 {
188 int forkpid;
8b3f4639 189
dc297c2a
KB
190 forkpid = fork();
191 if (forkpid == -1) {
192 perror("shutdown: fork");
193 exit(1);
194 }
195 if (forkpid) {
85c637cf 196 (void)printf("shutdown: [pid %d]\n", forkpid);
dc297c2a 197 exit(0);
954bb1fe 198 }
954bb1fe 199 }
dc297c2a
KB
200#endif
201 openlog("shutdown", LOG_CONS, LOG_AUTH);
202 loop();
203 /*NOTREACHED*/
954bb1fe
BJ
204}
205
dc297c2a 206loop()
954bb1fe 207{
dc297c2a
KB
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);
954bb1fe 231 }
954bb1fe 232 }
dc297c2a
KB
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;
613c7e46
MK
251 static char hostname[MAXHOSTNAMELEN + 1];
252 char wcmd[MAXPATHLEN + 4];
dc297c2a
KB
253 FILE *pf;
254 char *ctime();
85c637cf 255 void timeout();
dc297c2a 256
3f8356a8 257 if (!first++)
613c7e46 258 (void)gethostname(hostname, sizeof(hostname));
dc297c2a 259
613c7e46 260 /* undoc -n option to wall suppresses normal wall banner */
85c637cf 261 (void)sprintf(wcmd, "%s -n", _PATH_WALL);
613c7e46 262 if (!(pf = popen(wcmd, "w"))) {
2da4f8e2 263 syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
dc297c2a
KB
264 return;
265 }
266
85c637cf
KB
267 (void)fprintf(pf,
268 "\007*** %sSystem shutdown message from %s@%s ***\007\n",
613c7e46 269 tp->timeleft ? "": "FINAL ", whom, hostname);
dc297c2a
KB
270
271 if (tp->timeleft > 10*60)
85c637cf 272 (void)fprintf(pf, "System going down at %5.5s\n\n",
dc297c2a
KB
273 ctime(&shuttime) + 11);
274 else if (tp->timeleft > 59)
85c637cf 275 (void)fprintf(pf, "System going down in %d minute%s\n\n",
dc297c2a
KB
276 tp->timeleft / 60, (tp->timeleft > 60) ? "s" : "");
277 else if (tp->timeleft)
85c637cf 278 (void)fprintf(pf, "System going down in 30 seconds\n\n");
dc297c2a 279 else
85c637cf 280 (void)fprintf(pf, "System going down IMMEDIATELY\n\n");
dc297c2a 281
30834fb6
KB
282 if (mbuflen)
283 (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
dc297c2a
KB
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)) {
613c7e46 290 (void)signal(SIGALRM, timeout);
dc297c2a
KB
291 (void)alarm((u_int)30);
292 (void)pclose(pf);
293 (void)alarm((u_int)0);
613c7e46 294 (void)signal(SIGALRM, SIG_DFL);
dc297c2a
KB
295 }
296}
297
85c637cf 298void
dc297c2a
KB
299timeout()
300{
301 longjmp(alarmbuf, 1);
302}
303
304die_you_gravy_sucking_pig_dog()
305{
85c637cf
KB
306 void finish();
307
dc297c2a
KB
308 syslog(LOG_NOTICE, "%s by %s: %s",
309 doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
310 (void)sleep(2);
311
85c637cf 312 (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
dc297c2a 313 if (killflg) {
85c637cf 314 (void)printf("\rbut you'll have to do it yourself\r\n");
954bb1fe
BJ
315 finish();
316 }
dc297c2a
KB
317 if (dofast)
318 doitfast();
319#ifdef DEBUG
320 if (doreboot)
85c637cf 321 (void)printf("reboot");
30834fb6 322 else if (dohalt)
85c637cf 323 (void)printf("halt");
30834fb6 324 if (nosync)
85c637cf 325 (void)printf(" no sync");
dc297c2a 326 if (dofast)
85c637cf
KB
327 (void)printf(" no fsck");
328 (void)printf("\nkill -HUP 1\n");
dc297c2a
KB
329#else
330 if (doreboot) {
2da4f8e2
KB
331 execle(_PATH_REBOOT, "reboot", "-l", nosync, 0);
332 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT);
dc297c2a
KB
333 perror("shutdown");
334 }
335 else if (dohalt) {
2da4f8e2
KB
336 execle(_PATH_HALT, "halt", "-l", nosync, 0);
337 syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT);
dc297c2a
KB
338 perror("shutdown");
339 }
340 (void)kill(1, SIGTERM); /* to single user */
341#endif
954bb1fe
BJ
342 finish();
343}
344
dc297c2a 345#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2;
dc297c2a
KB
346
347getoffset(timearg)
348 register char *timearg;
954bb1fe 349{
dc297c2a
KB
350 register struct tm *lt;
351 register char *p;
352 time_t now, time();
dc297c2a
KB
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))
85c637cf
KB
362 badtime();
363 offset = atoi(timearg) * 60;
dc297c2a
KB
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
85c637cf 377 badtime();
dc297c2a
KB
378
379 unsetenv("TZ"); /* OUR timezone */
85c637cf 380 lt = localtime(&now); /* current time val */
dc297c2a
KB
381
382 switch(strlen(timearg)) {
383 case 10:
85c637cf 384 lt->tm_year = ATOI2(timearg);
dc297c2a
KB
385 /* FALLTHROUGH */
386 case 8:
85c637cf
KB
387 lt->tm_mon = ATOI2(timearg);
388 if (--lt->tm_mon < 0 || lt->tm_mon > 11)
389 badtime();
dc297c2a
KB
390 /* FALLTHROUGH */
391 case 6:
85c637cf
KB
392 lt->tm_mday = ATOI2(timearg);
393 if (lt->tm_mday < 1 || lt->tm_mday > 31)
394 badtime();
dc297c2a
KB
395 /* FALLTHROUGH */
396 case 4:
85c637cf
KB
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;
dc297c2a 412 default:
85c637cf 413 badtime();
dc297c2a 414 }
954bb1fe
BJ
415}
416
dc297c2a 417#define FSMSG "fastboot file for fsck\n"
8b3f4639
JB
418doitfast()
419{
dc297c2a 420 int fastfd;
8b3f4639 421
2da4f8e2
KB
422 if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
423 0664)) >= 0) {
dc297c2a
KB
424 (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
425 (void)close(fastfd);
8b3f4639
JB
426 }
427}
428
dc297c2a
KB
429#define NOMSG "\n\nNO LOGINS: System going down at "
430nolog()
954bb1fe 431{
85c637cf 432 int logfd;
dc297c2a 433 char *ct, *ctime();
85c637cf 434 void finish();
dc297c2a 435
2da4f8e2 436 (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */
dc297c2a
KB
437 (void)signal(SIGINT, finish);
438 (void)signal(SIGHUP, finish);
439 (void)signal(SIGQUIT, finish);
440 (void)signal(SIGTERM, finish);
2da4f8e2
KB
441 if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
442 0664)) >= 0) {
dc297c2a
KB
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);
954bb1fe
BJ
449 }
450}
451
85c637cf 452void
954bb1fe
BJ
453finish()
454{
2da4f8e2 455 (void)unlink(_PATH_NOLOGIN);
954bb1fe
BJ
456 exit(0);
457}
458
85c637cf
KB
459badtime()
460{
461 (void)fprintf(stderr, "shutdown: bad time format.\n");
462 exit(1);
463}
464
dc297c2a 465usage()
954bb1fe 466{
dc297c2a
KB
467 fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n");
468 exit(1);
954bb1fe 469}