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