xref: /original-bsd/sbin/shutdown/shutdown.c (revision 065e4d72)
1 /*
2  * Copyright (c) 1988, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1988, 1990, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)shutdown.c	8.4 (Berkeley) 04/28/95";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/time.h>
20 #include <sys/resource.h>
21 #include <sys/syslog.h>
22 
23 #include <ctype.h>
24 #include <fcntl.h>
25 #include <pwd.h>
26 #include <setjmp.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <tzfile.h>
32 #include <unistd.h>
33 
34 #include "pathnames.h"
35 
36 #ifdef DEBUG
37 #undef _PATH_NOLOGIN
38 #define	_PATH_NOLOGIN	"./nologin"
39 #undef _PATH_FASTBOOT
40 #define	_PATH_FASTBOOT	"./fastboot"
41 #endif
42 
43 #define	H		*60*60
44 #define	M		*60
45 #define	S		*1
46 #define	NOLOG_TIME	5*60
47 struct interval {
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,
54 };
55 #undef H
56 #undef M
57 #undef S
58 
59 static time_t offset, shuttime;
60 static int dofast, dohalt, doreboot, killflg, mbuflen;
61 static char *nosync, *whom, mbuf[BUFSIZ];
62 
63 void badtime __P((void));
64 void die_you_gravy_sucking_pig_dog __P((void));
65 void doitfast __P((void));
66 void finish __P((int));
67 void getoffset __P((char *));
68 void loop __P((void));
69 void nolog __P((void));
70 void timeout __P((int));
71 void timewarn __P((int));
72 void usage __P((void));
73 
74 int
75 main(argc, argv)
76 	int argc;
77 	char *argv[];
78 {
79 	extern int optind;
80 	register char *p, *endp;
81 	struct passwd *pw;
82 	int arglen, ch, len, readstdin;
83 
84 #ifndef DEBUG
85 	if (geteuid()) {
86 		(void)fprintf(stderr, "shutdown: NOT super-user\n");
87 		exit(1);
88 	}
89 #endif
90 	nosync = NULL;
91 	readstdin = 0;
92 	while ((ch = getopt(argc, argv, "-fhknr")) != EOF)
93 		switch (ch) {
94 		case '-':
95 			readstdin = 1;
96 			break;
97 		case 'f':
98 			dofast = 1;
99 			break;
100 		case 'h':
101 			dohalt = 1;
102 			break;
103 		case 'k':
104 			killflg = 1;
105 			break;
106 		case 'n':
107 			nosync = "-n";
108 			break;
109 		case 'r':
110 			doreboot = 1;
111 			break;
112 		case '?':
113 		default:
114 			usage();
115 		}
116 	argc -= optind;
117 	argv += optind;
118 
119 	if (argc < 1)
120 		usage();
121 
122 	if (dofast && nosync) {
123 		(void)fprintf(stderr,
124 		    "shutdown: incompatible switches -f and -n.\n");
125 		usage();
126 	}
127 	if (doreboot && dohalt) {
128 		(void)fprintf(stderr,
129 		    "shutdown: incompatible switches -h and -r.\n");
130 		usage();
131 	}
132 	getoffset(*argv++);
133 
134 	if (*argv) {
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++ = ' ';
141 			memmove(p, *argv, arglen);
142 			p += arglen;
143 		}
144 		*p = '\n';
145 		*++p = '\0';
146 	}
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 		}
161 	}
162 	mbuflen = strlen(mbuf);
163 
164 	if (offset)
165 		(void)printf("Shutdown at %.24s.\n", ctime(&shuttime));
166 	else
167 		(void)printf("Shutdown NOW!\n");
168 
169 	if (!(whom = getlogin()))
170 		whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
171 
172 #ifdef DEBUG
173 	(void)putc('\n', stdout);
174 #else
175 	(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
176 	{
177 		int forkpid;
178 
179 		forkpid = fork();
180 		if (forkpid == -1) {
181 			perror("shutdown: fork");
182 			exit(1);
183 		}
184 		if (forkpid) {
185 			(void)printf("shutdown: [pid %d]\n", forkpid);
186 			exit(0);
187 		}
188 	}
189 #endif
190 	openlog("shutdown", LOG_CONS, LOG_AUTH);
191 	loop();
192 	/* NOTREACHED */
193 }
194 
195 void
196 loop()
197 {
198 	struct interval *tp;
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 		/*
215 		 * Warn now, if going to sleep more than a fifth of
216 		 * the next wait time.
217 		 */
218 		if (sltime = offset - tp->timeleft) {
219 			if (sltime > tp->timetowait / 5)
220 				timewarn(offset);
221 			(void)sleep(sltime);
222 		}
223 	}
224 	for (;; ++tp) {
225 		timewarn(tp->timeleft);
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 
237 static jmp_buf alarmbuf;
238 
239 void
240 timewarn(timeleft)
241 	int timeleft;
242 {
243 	static int first;
244 	static char hostname[MAXHOSTNAMELEN + 1];
245 	FILE *pf;
246 	char wcmd[MAXPATHLEN + 4];
247 
248 	if (!first++)
249 		(void)gethostname(hostname, sizeof(hostname));
250 
251 	/* undoc -n option to wall suppresses normal wall banner */
252 	(void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL);
253 	if (!(pf = popen(wcmd, "w"))) {
254 		syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
255 		return;
256 	}
257 
258 	(void)fprintf(pf,
259 	    "\007*** %sSystem shutdown message from %s@%s ***\007\n",
260 	    timeleft ? "": "FINAL ", whom, hostname);
261 
262 	if (timeleft > 10*60)
263 		(void)fprintf(pf, "System going down at %5.5s\n\n",
264 		    ctime(&shuttime) + 11);
265 	else if (timeleft > 59)
266 		(void)fprintf(pf, "System going down in %d minute%s\n\n",
267 		    timeleft / 60, (timeleft > 60) ? "s" : "");
268 	else if (timeleft)
269 		(void)fprintf(pf, "System going down in 30 seconds\n\n");
270 	else
271 		(void)fprintf(pf, "System going down IMMEDIATELY\n\n");
272 
273 	if (mbuflen)
274 		(void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
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)) {
281 		(void)signal(SIGALRM, timeout);
282 		(void)alarm((u_int)30);
283 		(void)pclose(pf);
284 		(void)alarm((u_int)0);
285 		(void)signal(SIGALRM, SIG_DFL);
286 	}
287 }
288 
289 void
290 timeout(signo)
291 	int signo;
292 {
293 	longjmp(alarmbuf, 1);
294 }
295 
296 void
297 die_you_gravy_sucking_pig_dog()
298 {
299 
300 	syslog(LOG_NOTICE, "%s by %s: %s",
301 	    doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
302 	(void)sleep(2);
303 
304 	(void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
305 	if (killflg) {
306 		(void)printf("\rbut you'll have to do it yourself\r\n");
307 		finish(0);
308 	}
309 	if (dofast)
310 		doitfast();
311 #ifdef DEBUG
312 	if (doreboot)
313 		(void)printf("reboot");
314 	else if (dohalt)
315 		(void)printf("halt");
316 	if (nosync)
317 		(void)printf(" no sync");
318 	if (dofast)
319 		(void)printf(" no fsck");
320 	(void)printf("\nkill -HUP 1\n");
321 #else
322 	if (doreboot) {
323 		execle(_PATH_REBOOT, "reboot", "-l", nosync, 0);
324 		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT);
325 		perror("shutdown");
326 	}
327 	else if (dohalt) {
328 		execle(_PATH_HALT, "halt", "-l", nosync, 0);
329 		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT);
330 		perror("shutdown");
331 	}
332 	(void)kill(1, SIGTERM);		/* to single user */
333 #endif
334 	finish(0);
335 }
336 
337 #define	ATOI2(p)	(p[0] - '0') * 10 + (p[1] - '0'); p += 2;
338 
339 void
340 getoffset(timearg)
341 	register char *timearg;
342 {
343 	register struct tm *lt;
344 	register char *p;
345 	time_t now;
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))
355 			badtime();
356 		offset = atoi(timearg) * 60;
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
370 				badtime();
371 
372 	unsetenv("TZ");					/* OUR timezone */
373 	lt = localtime(&now);				/* current time val */
374 
375 	switch(strlen(timearg)) {
376 	case 10:
377 		lt->tm_year = ATOI2(timearg);
378 		/* FALLTHROUGH */
379 	case 8:
380 		lt->tm_mon = ATOI2(timearg);
381 		if (--lt->tm_mon < 0 || lt->tm_mon > 11)
382 			badtime();
383 		/* FALLTHROUGH */
384 	case 6:
385 		lt->tm_mday = ATOI2(timearg);
386 		if (lt->tm_mday < 1 || lt->tm_mday > 31)
387 			badtime();
388 		/* FALLTHROUGH */
389 	case 4:
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;
405 	default:
406 		badtime();
407 	}
408 }
409 
410 #define	FSMSG	"fastboot file for fsck\n"
411 void
412 doitfast()
413 {
414 	int fastfd;
415 
416 	if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
417 	    0664)) >= 0) {
418 		(void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
419 		(void)close(fastfd);
420 	}
421 }
422 
423 #define	NOMSG	"\n\nNO LOGINS: System going down at "
424 void
425 nolog()
426 {
427 	int logfd;
428 	char *ct;
429 
430 	(void)unlink(_PATH_NOLOGIN);	/* in case linked to another file */
431 	(void)signal(SIGINT, finish);
432 	(void)signal(SIGHUP, finish);
433 	(void)signal(SIGQUIT, finish);
434 	(void)signal(SIGTERM, finish);
435 	if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
436 	    0664)) >= 0) {
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);
443 	}
444 }
445 
446 void
447 finish(signo)
448 	int signo;
449 {
450 	if (!killflg)
451 		(void)unlink(_PATH_NOLOGIN);
452 	exit(0);
453 }
454 
455 void
456 badtime()
457 {
458 	(void)fprintf(stderr, "shutdown: bad time format.\n");
459 	exit(1);
460 }
461 
462 void
463 usage()
464 {
465 	fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n");
466 	exit(1);
467 }
468