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