shutdown.c revision 1.51
1/*	$OpenBSD: shutdown.c,v 1.51 2018/04/07 19:08:13 cheloha Exp $	*/
2/*	$NetBSD: shutdown.c,v 1.9 1995/03/18 15:01:09 cgd Exp $	*/
3
4/*
5 * Copyright (c) 1988, 1990, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/types.h>
34#include <sys/resource.h>
35#include <sys/syslog.h>
36#include <sys/types.h>
37#include <sys/wait.h>
38
39#include <ctype.h>
40#include <fcntl.h>
41#include <sys/termios.h>
42#include <pwd.h>
43#include <signal.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <time.h>
48#include <unistd.h>
49#include <limits.h>
50#include <errno.h>
51#include <err.h>
52
53#include "pathnames.h"
54
55#ifdef DEBUG
56#undef _PATH_NOLOGIN
57#define	_PATH_NOLOGIN	"./nologin"
58#undef _PATH_FASTBOOT
59#define	_PATH_FASTBOOT	"./fastboot"
60#endif
61
62#define	H		*60*60LL
63#define	M		*60LL
64#define	S		*1LL
65#define	TEN_HOURS	(10*60*60)
66#define	NOLOG_TIME	(5*60)
67struct interval {
68	time_t timeleft;
69	time_t timetowait;
70} tlist[] = {
71	{    0,    0 },
72	{ 10 H,  5 H },
73	{  5 H,  3 H },
74	{  2 H,  1 H },
75	{  1 H, 30 M },
76	{ 30 M, 10 M },
77	{ 20 M, 10 M },
78	{ 10 M,  5 M },
79	{  5 M,  3 M },
80	{  2 M,  1 M },
81	{  1 M, 30 S },
82	{ 30 S, 30 S },
83	{    0,    0 }
84};
85const int tlistlen = sizeof(tlist) / sizeof(tlist[0]);
86#undef H
87#undef M
88#undef S
89
90static time_t offset, shuttime;
91static int dofast, dohalt, doreboot, dopower, dodump, mbuflen, nosync;
92static sig_atomic_t killflg, timed_out;
93static char *whom, mbuf[BUFSIZ];
94
95void badtime(void);
96void __dead die_you_gravy_sucking_pig_dog(void);
97void doitfast(void);
98void __dead finish(int);
99void getoffset(char *);
100void __dead loop(void);
101void nolog(time_t);
102void timeout(int);
103void timewarn(time_t);
104void usage(void);
105
106int
107main(int argc, char *argv[])
108{
109	char when[64];
110	char *p, *endp;
111	struct passwd *pw;
112	struct tm *lt;
113	int arglen, ch, len, readstdin = 0;
114	pid_t forkpid;
115
116	if (pledge("stdio rpath wpath cpath getpw tty id proc exec", NULL) == -1)
117		err(1, "pledge");
118
119#ifndef DEBUG
120	if (geteuid())
121		errx(1, "NOT super-user");
122#endif
123	while ((ch = getopt(argc, argv, "dfhknpr-")) != -1)
124		switch (ch) {
125		case '-':
126			readstdin = 1;
127			break;
128		case 'd':
129			dodump = 1;
130			break;
131		case 'f':
132			dofast = 1;
133			break;
134		case 'h':
135			dohalt = 1;
136			break;
137		case 'k':
138			killflg = 1;
139			break;
140		case 'n':
141			nosync = 1;
142			break;
143		case 'p':
144			dopower = 1;
145			break;
146		case 'r':
147			doreboot = 1;
148			break;
149		default:
150			usage();
151		}
152	argc -= optind;
153	argv += optind;
154
155	if (argc < 1)
156		usage();
157
158	if (dofast && nosync) {
159		warnx("incompatible switches -f and -n.");
160		usage();
161	}
162	if (doreboot && dohalt) {
163		warnx("incompatible switches -h and -r.");
164		usage();
165	}
166	if (doreboot && dopower) {
167		warnx("incompatible switches -p and -r.");
168		usage();
169	}
170	getoffset(*argv++);
171
172	if (*argv) {
173		for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
174			arglen = strlen(*argv);
175			if ((len -= arglen) <= 2)
176				break;
177			if (p != mbuf)
178				*p++ = ' ';
179			memcpy(p, *argv, arglen);
180			p += arglen;
181		}
182		*p = '\n';
183		*++p = '\0';
184	}
185
186	if (readstdin) {
187		p = mbuf;
188		endp = mbuf + sizeof(mbuf) - 2;
189		for (;;) {
190			if (!fgets(p, endp - p + 1, stdin))
191				break;
192			for (; *p &&  p < endp; ++p)
193				;
194			if (p == endp) {
195				*p = '\n';
196				*++p = '\0';
197				break;
198			}
199		}
200	}
201	mbuflen = strlen(mbuf);
202
203	if (offset > 0) {
204		shuttime = time(NULL) + offset;
205		lt = localtime(&shuttime);
206		if (lt != NULL) {
207			strftime(when, sizeof(when), "%a %b %e %T %Z %Y", lt);
208			printf("Shutdown at %s.\n", when);
209		} else
210			printf("Shutdown soon.\n");
211	} else
212		printf("Shutdown NOW!\n");
213
214	if (!(whom = getlogin()))
215		whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
216
217#ifdef DEBUG
218	(void)putc('\n', stdout);
219#else
220	(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
221
222	forkpid = fork();
223	if (forkpid == -1)
224		err(1, "fork");
225	if (forkpid) {
226		(void)printf("shutdown: [pid %ld]\n", (long)forkpid);
227		exit(0);
228	}
229	setsid();
230#endif
231	openlog("shutdown", LOG_CONS, LOG_AUTH);
232	loop();
233	/* NOTREACHED */
234}
235
236void
237loop(void)
238{
239	struct timespec timeout;
240	int broadcast, i, logged;
241
242	broadcast = 1;
243
244	for (i = 0; i < tlistlen - 1; i++) {
245		if (offset > tlist[i + 1].timeleft) {
246			tlist[i].timeleft = offset;
247			tlist[i].timetowait = offset - tlist[i + 1].timeleft;
248			break;
249		}
250	}
251
252	/*
253	 * Don't spam the users: skip our offset's warning broadcast if
254	 * there's a broadcast scheduled after ours and it's relatively
255	 * imminent.
256	 */
257	if (offset > TEN_HOURS ||
258	    (offset > 0 && tlist[i].timetowait < tlist[i+1].timetowait / 5))
259		broadcast = 0;
260
261	for (logged = 0; i < tlistlen; i++) {
262		if (broadcast)
263			timewarn(tlist[i].timeleft);
264		broadcast = 1;
265		if (!logged && tlist[i].timeleft <= NOLOG_TIME) {
266			logged = 1;
267			nolog(tlist[i].timeleft);
268		}
269		timeout.tv_sec = tlist[i].timetowait;
270		timeout.tv_nsec = 0;
271		nanosleep(&timeout, NULL);
272	}
273	die_you_gravy_sucking_pig_dog();
274}
275
276static char *restricted_environ[] = {
277	"PATH=" _PATH_STDPATH,
278	NULL
279};
280
281void
282timewarn(time_t timeleft)
283{
284	static char hostname[HOST_NAME_MAX+1];
285	char when[64];
286	struct tm *lt;
287	static int first;
288	int fd[2];
289	pid_t pid, wpid;
290
291	if (!first++)
292		(void)gethostname(hostname, sizeof(hostname));
293
294	if (pipe(fd) == -1) {
295		syslog(LOG_ERR, "pipe: %m");
296		return;
297	}
298	switch (pid = fork()) {
299	case -1:
300		syslog(LOG_ERR, "fork: %m");
301		close(fd[0]);
302		close(fd[1]);
303		return;
304	case 0:
305		if (dup2(fd[0], STDIN_FILENO) == -1) {
306			syslog(LOG_ERR, "dup2: %m");
307			_exit(1);
308		}
309		if (fd[0] != STDIN_FILENO)
310			close(fd[0]);
311		close(fd[1]);
312		/* wall(1)'s undocumented '-n' flag suppresses its banner. */
313		execle(_PATH_WALL, _PATH_WALL, "-n", (char *)NULL,
314		    restricted_environ);
315		syslog(LOG_ERR, "%s: %m", _PATH_WALL);
316		_exit(1);
317	default:
318		close(fd[0]);
319	}
320
321	dprintf(fd[1],
322	    "\007*** %sSystem shutdown message from %s@%s ***\007\n",
323	    timeleft ? "": "FINAL ", whom, hostname);
324
325	if (timeleft > 10 * 60) {
326		shuttime = time(NULL) + timeleft;
327		lt = localtime(&shuttime);
328		strftime(when, sizeof(when), "%H:%M %Z", lt);
329		dprintf(fd[1], "System going down at %s\n\n", when);
330	} else if (timeleft > 59) {
331		dprintf(fd[1], "System going down in %lld minute%s\n\n",
332		    (long long)(timeleft / 60), (timeleft > 60) ? "s" : "");
333	} else if (timeleft)
334		dprintf(fd[1], "System going down in 30 seconds\n\n");
335	else
336		dprintf(fd[1], "System going down IMMEDIATELY\n\n");
337
338	if (mbuflen)
339		(void)write(fd[1], mbuf, mbuflen);
340	close(fd[1]);
341
342	/*
343	 * If we wait longer than 30 seconds for wall(1) to exit we'll
344	 * throw off our schedule.
345	 */
346	signal(SIGALRM, timeout);
347	siginterrupt(SIGALRM, 1);
348	alarm(30);
349	while ((wpid = wait(NULL)) != pid && !timed_out)
350		continue;
351	alarm(0);
352	signal(SIGALRM, SIG_DFL);
353	if (timed_out) {
354		syslog(LOG_NOTICE,
355		    "wall[%ld] is taking too long to exit; continuing",
356		    (long)pid);
357		timed_out = 0;
358	}
359}
360
361void
362timeout(int signo)
363{
364	timed_out = 1;
365}
366
367void
368die_you_gravy_sucking_pig_dog(void)
369{
370
371	syslog(LOG_NOTICE, "%s by %s: %s",
372	    doreboot ? "reboot" : dopower ? "power-down" : dohalt ? "halt" :
373	    "shutdown", whom, mbuf);
374	(void)sleep(2);
375
376	(void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
377	if (killflg) {
378		(void)printf("\rbut you'll have to do it yourself\r\n");
379		finish(0);
380	}
381	if (dofast)
382		doitfast();
383
384	if (pledge("stdio rpath wpath cpath tty id proc exec", NULL) == -1)
385		err(1, "pledge");
386
387#ifdef DEBUG
388	if (doreboot)
389		(void)printf("reboot");
390	else if (dopower)
391		(void)printf("power-down");
392	else if (dohalt)
393		(void)printf("halt");
394	if (nosync)
395		(void)printf(" no sync");
396	if (dofast)
397		(void)printf(" no fsck");
398	if (dodump)
399		(void)printf(" with dump");
400	(void)printf("\nkill -HUP 1\n");
401#else
402	if (dohalt || dopower || doreboot) {
403		char *args[10];
404		char **arg, *path;
405
406		if (pledge("stdio exec", NULL) == -1)
407			err(1, "pledge");
408
409		arg = &args[0];
410		if (doreboot) {
411			path = _PATH_REBOOT;
412			*arg++ = "reboot";
413		} else {
414			path = _PATH_HALT;
415			*arg++ = "halt";
416		}
417		*arg++ = "-l";
418		if (dopower)
419			*arg++ = "-p";
420		if (nosync)
421			*arg++ = "-n";
422		if (dodump)
423			*arg++ = "-d";
424		*arg++ = NULL;
425		execve(path, args, NULL);
426		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", path);
427		warn("%s", path);
428	}
429	if (access(_PATH_RC, R_OK) != -1) {
430		pid_t pid;
431		struct termios t;
432		int fd;
433
434		switch ((pid = fork())) {
435		case -1:
436			break;
437		case 0:
438			if (revoke(_PATH_CONSOLE) == -1)
439				perror("revoke");
440			if (setsid() == -1)
441				perror("setsid");
442			fd = open(_PATH_CONSOLE, O_RDWR);
443			if (fd == -1)
444				perror("open");
445			dup2(fd, 0);
446			dup2(fd, 1);
447			dup2(fd, 2);
448			if (fd > 2)
449				close(fd);
450
451			/* At a minimum... */
452			tcgetattr(0, &t);
453			t.c_oflag |= (ONLCR | OPOST);
454			tcsetattr(0, TCSANOW, &t);
455
456			execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL);
457			_exit(1);
458		default:
459			waitpid(pid, NULL, 0);
460		}
461	}
462	(void)kill(1, SIGTERM);		/* to single user */
463#endif
464	finish(0);
465}
466
467#define	ATOI2(p)	(p[0] - '0') * 10 + (p[1] - '0'); p += 2;
468
469void
470getoffset(char *timearg)
471{
472	char when[64];
473	const char *errstr;
474	struct tm *lt;
475	int this_year;
476	time_t minutes, now;
477	char *p;
478
479	if (!strcasecmp(timearg, "now")) {		/* now */
480		offset = 0;
481		return;
482	}
483
484	if (timearg[0] == '+') {			/* +minutes */
485		minutes = strtonum(timearg, 0, INT_MAX, &errstr);
486		if (errstr)
487			errx(1, "relative offset is %s: %s", errstr, timearg);
488		offset = minutes * 60;
489		return;
490	}
491
492	/* handle hh:mm by getting rid of the colon */
493	for (p = timearg; *p; ++p) {
494		if (!isascii((unsigned char)*p) || !isdigit((unsigned char)*p)) {
495			if (*p == ':' && strlen(p) == 3) {
496				p[0] = p[1];
497				p[1] = p[2];
498				p[2] = '\0';
499			} else
500				badtime();
501		}
502	}
503
504	unsetenv("TZ");					/* OUR timezone */
505	time(&now);
506	lt = localtime(&now);				/* current time val */
507
508	switch (strlen(timearg)) {
509	case 10:
510		this_year = lt->tm_year;
511		lt->tm_year = ATOI2(timearg);
512		/*
513		 * check if the specified year is in the next century.
514		 * allow for one year of user error as many people will
515		 * enter n - 1 at the start of year n.
516		 */
517		if (lt->tm_year < (this_year % 100) - 1)
518			lt->tm_year += 100;
519		/* adjust for the year 2000 and beyond */
520		lt->tm_year += (this_year - (this_year % 100));
521		/* FALLTHROUGH */
522	case 8:
523		lt->tm_mon = ATOI2(timearg);
524		if (--lt->tm_mon < 0 || lt->tm_mon > 11)
525			badtime();
526		/* FALLTHROUGH */
527	case 6:
528		lt->tm_mday = ATOI2(timearg);
529		if (lt->tm_mday < 1 || lt->tm_mday > 31)
530			badtime();
531		/* FALLTHROUGH */
532	case 4:
533		lt->tm_hour = ATOI2(timearg);
534		if (lt->tm_hour < 0 || lt->tm_hour > 23)
535			badtime();
536		lt->tm_min = ATOI2(timearg);
537		if (lt->tm_min < 0 || lt->tm_min > 59)
538			badtime();
539		lt->tm_sec = 0;
540		if ((shuttime = mktime(lt)) == -1)
541			badtime();
542		if ((offset = shuttime - now) < 0) {
543			strftime(when, sizeof(when), "%a %b %e %T %Z %Y", lt);
544			errx(1, "time is already past: %s", when);
545		}
546		break;
547	default:
548		badtime();
549	}
550}
551
552void
553doitfast(void)
554{
555	int fastfd;
556
557	if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
558	    0664)) >= 0) {
559		dprintf(fastfd, "fastboot file for fsck\n");
560		close(fastfd);
561	}
562}
563
564void
565nolog(time_t timeleft)
566{
567	char when[64];
568	struct tm *tm;
569	int logfd;
570
571	(void)unlink(_PATH_NOLOGIN);	/* in case linked to another file */
572	(void)signal(SIGINT, finish);
573	(void)signal(SIGHUP, finish);
574	(void)signal(SIGQUIT, finish);
575	(void)signal(SIGTERM, finish);
576	shuttime = time(NULL) + timeleft;
577	tm = localtime(&shuttime);
578	if (tm && (logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
579	    0664)) >= 0) {
580		strftime(when, sizeof(when), "at %H:%M %Z", tm);
581		dprintf(logfd, "\n\nNO LOGINS: System going down %s\n\n", when);
582		close(logfd);
583	}
584}
585
586void
587finish(int signo)
588{
589	if (!killflg)
590		(void)unlink(_PATH_NOLOGIN);
591	if (signo == 0)
592		exit(0);
593	else
594		_exit(0);
595}
596
597void
598badtime(void)
599{
600	errx(1, "bad time format.");
601}
602
603void
604usage(void)
605{
606	fprintf(stderr,
607	    "usage: shutdown [-] [-dfhknpr] time [warning-message ...]\n");
608	exit(1);
609}
610