shutdown.c revision 1.53
1/*	$OpenBSD: shutdown.c,v 1.53 2021/07/12 15:09:19 beck 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#ifndef DEBUG
117	if (geteuid())
118		errx(1, "NOT super-user");
119#endif
120	while ((ch = getopt(argc, argv, "dfhknpr-")) != -1)
121		switch (ch) {
122		case '-':
123			readstdin = 1;
124			break;
125		case 'd':
126			dodump = 1;
127			break;
128		case 'f':
129			dofast = 1;
130			break;
131		case 'h':
132			dohalt = 1;
133			break;
134		case 'k':
135			killflg = 1;
136			break;
137		case 'n':
138			nosync = 1;
139			break;
140		case 'p':
141			dopower = 1;
142			break;
143		case 'r':
144			doreboot = 1;
145			break;
146		default:
147			usage();
148		}
149	argc -= optind;
150	argv += optind;
151
152	if (argc < 1)
153		usage();
154
155	if (dofast && nosync) {
156		warnx("incompatible switches -f and -n.");
157		usage();
158	}
159	if (doreboot && dohalt) {
160		warnx("incompatible switches -h and -r.");
161		usage();
162	}
163	if (doreboot && dopower) {
164		warnx("incompatible switches -p and -r.");
165		usage();
166	}
167
168	if (unveil(_PATH_CONSOLE, "rw") == -1)
169		err(1, "unveil %s", _PATH_CONSOLE);
170	if (unveil(_PATH_RC, "r") == -1)
171		err(1, "unveil %s", _PATH_RC);
172	if (unveil(_PATH_WALL, "x") == -1)
173		err(1, "unveil %s", _PATH_WALL);
174	if (unveil(_PATH_FASTBOOT, "wc") == -1)
175		err(1, "unveil %s", _PATH_FASTBOOT);
176	if (unveil(_PATH_NOLOGIN, "wc") == -1)
177		err(1, "unveil %s", _PATH_NOLOGIN);
178	if (dohalt || dopower) {
179		if (unveil(_PATH_HALT, "x") == -1)
180			err(1, "unveil %s", _PATH_HALT);
181	} else if (doreboot) {
182		if (unveil(_PATH_REBOOT, "x") == -1)
183			err(1, "unveil %s", _PATH_REBOOT);
184	} else {
185		if (unveil(_PATH_BSHELL, "x") == -1)
186			err(1, "unveil %s", _PATH_BSHELL);
187	}
188	if (pledge("stdio rpath wpath cpath getpw tty id proc exec", NULL) == -1)
189		err(1, "pledge");
190
191	getoffset(*argv++);
192
193	if (*argv) {
194		for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
195			arglen = strlen(*argv);
196			if ((len -= arglen) <= 2)
197				break;
198			if (p != mbuf)
199				*p++ = ' ';
200			memcpy(p, *argv, arglen);
201			p += arglen;
202		}
203		*p = '\n';
204		*++p = '\0';
205	}
206
207	if (readstdin) {
208		p = mbuf;
209		endp = mbuf + sizeof(mbuf) - 2;
210		for (;;) {
211			if (!fgets(p, endp - p + 1, stdin))
212				break;
213			for (; *p &&  p < endp; ++p)
214				;
215			if (p == endp) {
216				*p = '\n';
217				*++p = '\0';
218				break;
219			}
220		}
221	}
222	mbuflen = strlen(mbuf);
223
224	if (offset > 0) {
225		shuttime = time(NULL) + offset;
226		lt = localtime(&shuttime);
227		if (lt != NULL) {
228			strftime(when, sizeof(when), "%a %b %e %T %Z %Y", lt);
229			printf("Shutdown at %s.\n", when);
230		} else
231			printf("Shutdown soon.\n");
232	} else
233		printf("Shutdown NOW!\n");
234
235	if (!(whom = getlogin()))
236		whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
237
238#ifdef DEBUG
239	(void)putc('\n', stdout);
240#else
241	(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
242
243	forkpid = fork();
244	if (forkpid == -1)
245		err(1, "fork");
246	if (forkpid) {
247		(void)printf("shutdown: [pid %ld]\n", (long)forkpid);
248		exit(0);
249	}
250	setsid();
251#endif
252	openlog("shutdown", LOG_CONS, LOG_AUTH);
253	loop();
254	/* NOTREACHED */
255}
256
257void
258loop(void)
259{
260	struct timespec timeout;
261	int broadcast, i, logged;
262
263	broadcast = 1;
264
265	for (i = 0; i < tlistlen - 1; i++) {
266		if (offset > tlist[i + 1].timeleft) {
267			tlist[i].timeleft = offset;
268			tlist[i].timetowait = offset - tlist[i + 1].timeleft;
269			break;
270		}
271	}
272
273	/*
274	 * Don't spam the users: skip our offset's warning broadcast if
275	 * there's a broadcast scheduled after ours and it's relatively
276	 * imminent.
277	 */
278	if (offset > TEN_HOURS ||
279	    (offset > 0 && tlist[i].timetowait < tlist[i+1].timetowait / 5))
280		broadcast = 0;
281
282	for (logged = 0; i < tlistlen; i++) {
283		if (broadcast)
284			timewarn(tlist[i].timeleft);
285		broadcast = 1;
286		if (!logged && tlist[i].timeleft <= NOLOG_TIME) {
287			logged = 1;
288			nolog(tlist[i].timeleft);
289		}
290		timeout.tv_sec = tlist[i].timetowait;
291		timeout.tv_nsec = 0;
292		nanosleep(&timeout, NULL);
293	}
294	die_you_gravy_sucking_pig_dog();
295}
296
297static char *restricted_environ[] = {
298	"PATH=" _PATH_STDPATH,
299	NULL
300};
301
302void
303timewarn(time_t timeleft)
304{
305	static char hostname[HOST_NAME_MAX+1];
306	char when[64];
307	struct tm *lt;
308	static int first;
309	int fd[2];
310	pid_t pid, wpid;
311
312	if (!first++)
313		(void)gethostname(hostname, sizeof(hostname));
314
315	if (pipe(fd) == -1) {
316		syslog(LOG_ERR, "pipe: %m");
317		return;
318	}
319	switch (pid = fork()) {
320	case -1:
321		syslog(LOG_ERR, "fork: %m");
322		close(fd[0]);
323		close(fd[1]);
324		return;
325	case 0:
326		if (dup2(fd[0], STDIN_FILENO) == -1) {
327			syslog(LOG_ERR, "dup2: %m");
328			_exit(1);
329		}
330		if (fd[0] != STDIN_FILENO)
331			close(fd[0]);
332		close(fd[1]);
333		/* wall(1)'s undocumented '-n' flag suppresses its banner. */
334		execle(_PATH_WALL, _PATH_WALL, "-n", (char *)NULL,
335		    restricted_environ);
336		syslog(LOG_ERR, "%s: %m", _PATH_WALL);
337		_exit(1);
338	default:
339		close(fd[0]);
340	}
341
342	dprintf(fd[1],
343	    "\007*** %sSystem shutdown message from %s@%s ***\007\n",
344	    timeleft ? "": "FINAL ", whom, hostname);
345
346	if (timeleft > 10 * 60) {
347		shuttime = time(NULL) + timeleft;
348		lt = localtime(&shuttime);
349		strftime(when, sizeof(when), "%H:%M %Z", lt);
350		dprintf(fd[1], "System going down at %s\n\n", when);
351	} else if (timeleft > 59) {
352		dprintf(fd[1], "System going down in %lld minute%s\n\n",
353		    (long long)(timeleft / 60), (timeleft > 60) ? "s" : "");
354	} else if (timeleft)
355		dprintf(fd[1], "System going down in 30 seconds\n\n");
356	else
357		dprintf(fd[1], "System going down IMMEDIATELY\n\n");
358
359	if (mbuflen)
360		(void)write(fd[1], mbuf, mbuflen);
361	close(fd[1]);
362
363	/*
364	 * If we wait longer than 30 seconds for wall(1) to exit we'll
365	 * throw off our schedule.
366	 */
367	signal(SIGALRM, timeout);
368	siginterrupt(SIGALRM, 1);
369	alarm(30);
370	while ((wpid = wait(NULL)) != pid && !timed_out)
371		continue;
372	alarm(0);
373	signal(SIGALRM, SIG_DFL);
374	if (timed_out) {
375		syslog(LOG_NOTICE,
376		    "wall[%ld] is taking too long to exit; continuing",
377		    (long)pid);
378		timed_out = 0;
379	}
380}
381
382void
383timeout(int signo)
384{
385	timed_out = 1;
386}
387
388void
389die_you_gravy_sucking_pig_dog(void)
390{
391
392	syslog(LOG_NOTICE, "%s by %s: %s",
393	    doreboot ? "reboot" : dopower ? "power-down" : dohalt ? "halt" :
394	    "shutdown", whom, mbuf);
395	(void)sleep(2);
396
397	(void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
398	if (killflg) {
399		(void)printf("\rbut you'll have to do it yourself\r\n");
400		finish(0);
401	}
402	if (dofast)
403		doitfast();
404
405	if (pledge("stdio rpath wpath cpath tty id proc exec", NULL) == -1)
406		err(1, "pledge");
407
408#ifdef DEBUG
409	if (doreboot)
410		(void)printf("reboot");
411	else if (dopower)
412		(void)printf("power-down");
413	else if (dohalt)
414		(void)printf("halt");
415	if (nosync)
416		(void)printf(" no sync");
417	if (dofast)
418		(void)printf(" no fsck");
419	if (dodump)
420		(void)printf(" with dump");
421	(void)printf("\nkill -HUP 1\n");
422#else
423	if (dohalt || dopower || doreboot) {
424		char *args[10];
425		char **arg, *path;
426
427		if (pledge("stdio exec", NULL) == -1)
428			err(1, "pledge");
429
430		arg = &args[0];
431		if (doreboot) {
432			path = _PATH_REBOOT;
433			*arg++ = "reboot";
434		} else {
435			path = _PATH_HALT;
436			*arg++ = "halt";
437		}
438		*arg++ = "-l";
439		if (dopower)
440			*arg++ = "-p";
441		if (nosync)
442			*arg++ = "-n";
443		if (dodump)
444			*arg++ = "-d";
445		*arg++ = NULL;
446		execve(path, args, NULL);
447		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", path);
448		warn("%s", path);
449	}
450	if (access(_PATH_RC, R_OK) != -1) {
451		pid_t pid;
452		struct termios t;
453		int fd;
454
455		switch ((pid = fork())) {
456		case -1:
457			break;
458		case 0:
459			if (revoke(_PATH_CONSOLE) == -1)
460				perror("revoke");
461			if (setsid() == -1)
462				perror("setsid");
463			fd = open(_PATH_CONSOLE, O_RDWR);
464			if (fd == -1)
465				perror("open");
466			dup2(fd, 0);
467			dup2(fd, 1);
468			dup2(fd, 2);
469			if (fd > 2)
470				close(fd);
471
472			/* At a minimum... */
473			tcgetattr(0, &t);
474			t.c_oflag |= (ONLCR | OPOST);
475			tcsetattr(0, TCSANOW, &t);
476
477			execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL);
478			_exit(1);
479		default:
480			waitpid(pid, NULL, 0);
481		}
482	}
483	(void)kill(1, SIGTERM);		/* to single user */
484#endif
485	finish(0);
486}
487
488#define	ATOI2(p)	(p[0] - '0') * 10 + (p[1] - '0'); p += 2;
489
490void
491getoffset(char *timearg)
492{
493	char when[64];
494	const char *errstr;
495	struct tm *lt;
496	int this_year;
497	time_t minutes, now;
498	char *p;
499
500	if (!strcasecmp(timearg, "now")) {		/* now */
501		offset = 0;
502		return;
503	}
504
505	if (timearg[0] == '+') {			/* +minutes */
506		minutes = strtonum(timearg, 0, INT_MAX, &errstr);
507		if (errstr)
508			errx(1, "relative offset is %s: %s", errstr, timearg);
509		offset = minutes * 60;
510		return;
511	}
512
513	/* handle hh:mm by getting rid of the colon */
514	for (p = timearg; *p; ++p) {
515		if (!isascii((unsigned char)*p) || !isdigit((unsigned char)*p)) {
516			if (*p == ':' && strlen(p) == 3) {
517				p[0] = p[1];
518				p[1] = p[2];
519				p[2] = '\0';
520			} else
521				badtime();
522		}
523	}
524
525	unsetenv("TZ");					/* OUR timezone */
526	time(&now);
527	lt = localtime(&now);				/* current time val */
528
529	switch (strlen(timearg)) {
530	case 10:
531		this_year = lt->tm_year;
532		lt->tm_year = ATOI2(timearg);
533		/*
534		 * check if the specified year is in the next century.
535		 * allow for one year of user error as many people will
536		 * enter n - 1 at the start of year n.
537		 */
538		if (lt->tm_year < (this_year % 100) - 1)
539			lt->tm_year += 100;
540		/* adjust for the year 2000 and beyond */
541		lt->tm_year += (this_year - (this_year % 100));
542		/* FALLTHROUGH */
543	case 8:
544		lt->tm_mon = ATOI2(timearg);
545		if (--lt->tm_mon < 0 || lt->tm_mon > 11)
546			badtime();
547		/* FALLTHROUGH */
548	case 6:
549		lt->tm_mday = ATOI2(timearg);
550		if (lt->tm_mday < 1 || lt->tm_mday > 31)
551			badtime();
552		/* FALLTHROUGH */
553	case 4:
554		lt->tm_hour = ATOI2(timearg);
555		if (lt->tm_hour < 0 || lt->tm_hour > 23)
556			badtime();
557		lt->tm_min = ATOI2(timearg);
558		if (lt->tm_min < 0 || lt->tm_min > 59)
559			badtime();
560		lt->tm_sec = 0;
561		if ((shuttime = mktime(lt)) == -1)
562			badtime();
563		if ((offset = shuttime - now) < 0) {
564			strftime(when, sizeof(when), "%a %b %e %T %Z %Y", lt);
565			errx(1, "time is already past: %s", when);
566		}
567		break;
568	default:
569		badtime();
570	}
571}
572
573void
574doitfast(void)
575{
576	int fastfd;
577
578	if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
579	    0664)) >= 0) {
580		dprintf(fastfd, "fastboot file for fsck\n");
581		close(fastfd);
582	}
583}
584
585void
586nolog(time_t timeleft)
587{
588	char when[64];
589	struct tm *tm;
590	int logfd;
591
592	(void)unlink(_PATH_NOLOGIN);	/* in case linked to another file */
593	(void)signal(SIGINT, finish);
594	(void)signal(SIGHUP, finish);
595	(void)signal(SIGQUIT, finish);
596	(void)signal(SIGTERM, finish);
597	shuttime = time(NULL) + timeleft;
598	tm = localtime(&shuttime);
599	if (tm && (logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
600	    0664)) >= 0) {
601		strftime(when, sizeof(when), "at %H:%M %Z", tm);
602		dprintf(logfd, "\n\nNO LOGINS: System going down %s\n\n", when);
603		close(logfd);
604	}
605}
606
607void
608finish(int signo)
609{
610	if (!killflg)
611		(void)unlink(_PATH_NOLOGIN);
612	if (signo == 0)
613		exit(0);
614	else
615		_exit(0);
616}
617
618void
619badtime(void)
620{
621	errx(1, "bad time format.");
622}
623
624void
625usage(void)
626{
627	fprintf(stderr,
628	    "usage: shutdown [-] [-dfhknpr] time [warning-message ...]\n");
629	exit(1);
630}
631