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