xref: /original-bsd/sbin/shutdown/shutdown.c (revision c73c5200)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
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.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1988 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)shutdown.c	5.10 (Berkeley) 04/02/89";
26 #endif /* not lint */
27 
28 #include <sys/param.h>
29 #include <sys/time.h>
30 #include <sys/file.h>
31 #include <sys/resource.h>
32 #include <sys/syslog.h>
33 #include <signal.h>
34 #include <setjmp.h>
35 #include <tzfile.h>
36 #include <pwd.h>
37 #include <stdio.h>
38 #include <ctype.h>
39 #include "pathnames.h"
40 
41 #ifdef DEBUG
42 #undef _PATH_NOLOGIN
43 #define	_PATH_NOLOGIN	"./nologin"
44 #undef _PATH_FASTBOOT
45 #define	_PATH_FASTBOOT	"./fastboot"
46 #endif
47 
48 #define	H		*60*60
49 #define	M		*60
50 #define	S		*1
51 #define	NOLOG_TIME	5*60
52 struct interval {
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 
64 static time_t offset, shuttime;
65 static int dofast, dohalt, doreboot, killflg, mbuflen;
66 static char *nosync, *whom, mbuf[BUFSIZ];
67 
68 main(argc, argv)
69 	int argc;
70 	char **argv;
71 {
72 	extern int optind;
73 	register char *p, *endp;
74 	int arglen, ch, len, readstdin;
75 	struct passwd *pw, *getpwuid();
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
85 	nosync = NULL;
86 	readstdin = 0;
87 	while ((ch = getopt(argc, argv, "-fhknr")) != EOF)
88 		switch((char)ch) {
89 		case '-':
90 			readstdin = 1;
91 			break;
92 		case 'f':
93 			dofast = 1;
94 			break;
95 		case 'h':
96 			dohalt = 1;
97 			break;
98 		case 'k':
99 			killflg = 1;
100 			break;
101 		case 'n':
102 			nosync = "-n";
103 			break;
104 		case 'r':
105 			doreboot = 1;
106 			break;
107 		case '?':
108 		default:
109 			usage();
110 		}
111 	argc -= optind;
112 	argv += optind;
113 
114 	if (argc < 1)
115 		usage();
116 
117 	if (dofast && nosync) {
118 		fprintf(stderr,
119 		    "shutdown: incompatible switches -f and -n.\n");
120 		usage();
121 	}
122 	if (doreboot && dohalt) {
123 		fprintf(stderr,
124 		    "shutdown: incompatible switches -h and -r.\n");
125 		usage();
126 	}
127 	getoffset(*argv++);
128 
129 	if (*argv) {
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';
141 	}
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 		}
156 	}
157 	mbuflen = strlen(mbuf);
158 
159 	if (offset)
160 		printf("Shutdown at %.24s.", ctime(&shuttime));
161 	else
162 		printf("Shutdown NOW!\n");
163 
164 	if (!(whom = getlogin()))
165 		whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
166 
167 #ifdef DEBUG
168 	(void)putc('\n', stdout);
169 #else
170 	(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
171 	{
172 		int forkpid;
173 
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);
182 		}
183 	}
184 #endif
185 	openlog("shutdown", LOG_CONS, LOG_AUTH);
186 	loop();
187 	/*NOTREACHED*/
188 }
189 
190 loop()
191 {
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);
215 		}
216 	}
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 
230 static jmp_buf alarmbuf;
231 
232 warn()
233 {
234 	static int first;
235 	static char hostname[MAXHOSTNAMELEN];
236 	FILE *pf;
237 	char *ctime();
238 	int timeout();
239 
240 	if (!first++) {
241 		(void)signal(SIGALRM, timeout);
242 		(void)gethostname(hostname, sizeof(hostname));
243 	}
244 
245 	if (!(pf = popen(_PATH_WALL, "w"))) {
246 		syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
247 		return;
248 	}
249 
250 	fprintf(pf, "*** %sSystem shutdown message ***\n",
251 	    tp->timeleft ? "": "FINAL ");
252 
253 	if (tp->timeleft > 10*60)
254 		fprintf(pf, "System going down at %5.5s\n\n",
255 		    ctime(&shuttime) + 11);
256 	else if (tp->timeleft > 59)
257 		fprintf(pf, "System going down in %d minute%s\n\n",
258 		    tp->timeleft / 60, (tp->timeleft > 60) ? "s" : "");
259 	else if (tp->timeleft)
260 		fprintf(pf, "System going down in 30 seconds\n\n");
261 	else
262 		fprintf(pf, "System going down IMMEDIATELY\n\n");
263 
264 	if (mbuflen)
265 		(void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
266 
267 	/*
268 	 * play some games, just in case wall doesn't come back
269 	 * probably unecessary, given that wall is careful.
270 	 */
271 	if (!setjmp(alarmbuf)) {
272 		(void)alarm((u_int)30);
273 		(void)pclose(pf);
274 		(void)alarm((u_int)0);
275 	}
276 }
277 
278 timeout()
279 {
280 	longjmp(alarmbuf, 1);
281 }
282 
283 die_you_gravy_sucking_pig_dog()
284 {
285 	syslog(LOG_NOTICE, "%s by %s: %s",
286 	    doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
287 	(void)sleep(2);
288 
289 	printf("\r\nSystem shutdown time has arrived\007\007\r\n");
290 	if (killflg) {
291 		printf("\rbut you'll have to do it yourself\r\n");
292 		finish();
293 	}
294 	if (dofast)
295 		doitfast();
296 #ifdef DEBUG
297 	if (doreboot)
298 		printf("reboot");
299 	else if (dohalt)
300 		printf("halt");
301 	if (nosync)
302 		printf(" no sync");
303 	if (dofast)
304 		printf(" no fsck");
305 	printf("\nkill -HUP 1\n");
306 #else
307 	if (doreboot) {
308 		execle(_PATH_REBOOT, "reboot", "-l", nosync, 0);
309 		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT);
310 		perror("shutdown");
311 	}
312 	else if (dohalt) {
313 		execle(_PATH_HALT, "halt", "-l", nosync, 0);
314 		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT);
315 		perror("shutdown");
316 	}
317 	(void)kill(1, SIGTERM);		/* to single user */
318 #endif
319 	finish();
320 }
321 
322 #define	ATOI2(p)	(p[0] - '0') * 10 + (p[1] - '0'); p += 2;
323 static int dmsize[] =
324 	{ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
325 
326 getoffset(timearg)
327 	register char *timearg;
328 {
329 	register struct tm *lt;
330 	register char *p;
331 	time_t now, time();
332 	int year, month, day, hour, min;
333 
334 	if (!strcasecmp(timearg, "now")) {		/* now */
335 		offset = 0;
336 		return;
337 	}
338 
339 	(void)time(&now);
340 	if (*timearg == '+') {				/* +minutes */
341 		if (!isdigit(*++timearg))
342 			goto badtime;
343 		min = atoi(timearg);
344 		offset = min * 60;
345 		shuttime = now + offset;
346 		return;
347 	}
348 
349 	/* handle hh:mm by getting rid of the colon */
350 	for (p = timearg; *p; ++p)
351 		if (!isascii(*p) || !isdigit(*p))
352 			if (*p == ':' && strlen(p) == 3) {
353 				p[0] = p[1];
354 				p[1] = p[2];
355 				p[2] = '\0';
356 			}
357 			else
358 				goto badtime;
359 
360 	unsetenv("TZ");					/* OUR timezone */
361 	lt = localtime(&now);				/* [yymmdd]hhmm */
362 	year = lt->tm_year;
363 	month = lt->tm_mon + 1;
364 	day = lt->tm_mday;
365 
366 	switch(strlen(timearg)) {
367 	case 10:
368 		year = ATOI2(timearg);
369 		/* FALLTHROUGH */
370 	case 8:
371 		month = ATOI2(timearg);
372 		/* FALLTHROUGH */
373 	case 6:
374 		day = ATOI2(timearg);
375 		/* FALLTHROUGH */
376 	case 4:
377 		hour = ATOI2(timearg);
378 		min = ATOI2(timearg);
379 		if (month < 1 || month > 12 || day < 1 || day > 31 ||
380 		    hour < 0 || hour > 23 || min < 0 || min > 59)
381 			goto badtime;
382 		shuttime = 0;
383 		year += TM_YEAR_BASE;
384 		if (isleap(year) && month > 2)
385 			++shuttime;
386 		for (--year; year >= EPOCH_YEAR; --year)
387 			shuttime += isleap(year) ?
388 			    DAYSPERLYEAR : DAYSPERNYEAR;
389 		while (--month)
390 			shuttime += dmsize[month];
391 		shuttime += day - 1;
392 		shuttime = HOURSPERDAY * shuttime + hour;
393 		shuttime = MINSPERHOUR * shuttime + min;
394 		shuttime *= SECSPERMIN;
395 		shuttime -= lt->tm_gmtoff;
396 		if ((offset = shuttime - now) >= 0)
397 			break;
398 		/* FALLTHROUGH */
399 	default:
400 badtime:	fprintf(stderr,
401 		    "shutdown: bad time format, or already past.\n");
402 		exit(1);
403 	}
404 }
405 
406 #define	FSMSG	"fastboot file for fsck\n"
407 doitfast()
408 {
409 	int fastfd;
410 
411 	if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
412 	    0664)) >= 0) {
413 		(void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
414 		(void)close(fastfd);
415 	}
416 }
417 
418 #define	NOMSG	"\n\nNO LOGINS: System going down at "
419 nolog()
420 {
421 	int logfd, finish();
422 	char *ct, *ctime();
423 
424 	(void)unlink(_PATH_NOLOGIN);	/* in case linked to another file */
425 	(void)signal(SIGINT, finish);
426 	(void)signal(SIGHUP, finish);
427 	(void)signal(SIGQUIT, finish);
428 	(void)signal(SIGTERM, finish);
429 	if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
430 	    0664)) >= 0) {
431 		(void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
432 		ct = ctime(&shuttime);
433 		(void)write(logfd, ct + 11, 5);
434 		(void)write(logfd, "\n\n", 2);
435 		(void)write(logfd, mbuf, strlen(mbuf));
436 		(void)close(logfd);
437 	}
438 }
439 
440 finish()
441 {
442 	(void)unlink(_PATH_NOLOGIN);
443 	exit(0);
444 }
445 
446 usage()
447 {
448 	fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n");
449 	exit(1);
450 }
451