11558Srgrimes/*
21558Srgrimes * Copyright (c) 1980, 1986, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
30114589Sobrien#if 0
311558Srgrimes#ifndef lint
3237674Scharnierstatic const char copyright[] =
331558Srgrimes"@(#) Copyright (c) 1980, 1986, 1993\n\
341558Srgrimes	The Regents of the University of California.  All rights reserved.\n";
351558Srgrimes#endif /* not lint */
361558Srgrimes
371558Srgrimes#ifndef lint
381558Srgrimesstatic char sccsid[] = "@(#)reboot.c	8.1 (Berkeley) 6/5/93";
39114589Sobrien#endif /* not lint */
4037674Scharnier#endif
41114589Sobrien#include <sys/cdefs.h>
42114589Sobrien__FBSDID("$FreeBSD$");
431558Srgrimes
441558Srgrimes#include <sys/reboot.h>
45200161Sed#include <sys/time.h>
4637674Scharnier#include <sys/types.h>
4774525Siedowse#include <sys/sysctl.h>
481558Srgrimes#include <signal.h>
4937674Scharnier#include <err.h>
5037674Scharnier#include <errno.h>
5195485Swes#include <fcntl.h>
521558Srgrimes#include <pwd.h>
531558Srgrimes#include <syslog.h>
541558Srgrimes#include <stdio.h>
5578732Sdd#include <stdlib.h>
561558Srgrimes#include <string.h>
5737674Scharnier#include <unistd.h>
58202195Sed#include <utmpx.h>
591558Srgrimes
60140795Sdelphijstatic void usage(void);
61140795Sdelphijstatic u_int get_pageins(void);
621558Srgrimes
63228408Sedstatic int dohalt;
641558Srgrimes
651558Srgrimesint
6674525Siedowsemain(int argc, char *argv[])
671558Srgrimes{
68200161Sed	struct utmpx utx;
69140795Sdelphij	const struct passwd *pw;
70187956Sbz	int ch, howto, i, fd, lflag, nflag, qflag, sverrno;
7174525Siedowse	u_int pageins;
72228408Sed	const char *user, *kernel = NULL;
731558Srgrimes
74228408Sed	if (strcmp(getprogname(), "halt") == 0) {
751558Srgrimes		dohalt = 1;
761558Srgrimes		howto = RB_HALT;
771558Srgrimes	} else
781558Srgrimes		howto = 0;
79140794Sdelphij	lflag = nflag = qflag = 0;
8095485Swes	while ((ch = getopt(argc, argv, "dk:lnpq")) != -1)
811558Srgrimes		switch(ch) {
8253523Sjdp		case 'd':
8353523Sjdp			howto |= RB_DUMP;
8453523Sjdp			break;
8595485Swes		case 'k':
8695485Swes			kernel = optarg;
8795485Swes			break;
8877122Snik		case 'l':
891558Srgrimes			lflag = 1;
901558Srgrimes			break;
911558Srgrimes		case 'n':
921558Srgrimes			nflag = 1;
931558Srgrimes			howto |= RB_NOSYNC;
941558Srgrimes			break;
9517801Sjulian		case 'p':
96104572Sthomas			howto |= RB_POWEROFF;
9717801Sjulian			break;
981558Srgrimes		case 'q':
991558Srgrimes			qflag = 1;
1001558Srgrimes			break;
1011558Srgrimes		case '?':
1021558Srgrimes		default:
1031558Srgrimes			usage();
1041558Srgrimes		}
1051558Srgrimes	argc -= optind;
1061558Srgrimes	argv += optind;
1071558Srgrimes
10853523Sjdp	if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
10953523Sjdp		errx(1, "cannot dump (-d) when halting; must reboot instead");
11026676Scharnier	if (geteuid()) {
11126676Scharnier		errno = EPERM;
11226676Scharnier		err(1, NULL);
11326676Scharnier	}
1141558Srgrimes
1151558Srgrimes	if (qflag) {
1161558Srgrimes		reboot(howto);
11726676Scharnier		err(1, NULL);
1181558Srgrimes	}
1191558Srgrimes
120140794Sdelphij	if (kernel != NULL) {
121143948Spjd		fd = open("/boot/nextboot.conf", O_WRONLY | O_CREAT | O_TRUNC,
122143948Spjd		    0444);
12395485Swes		if (fd > -1) {
12497428Sgordon			(void)write(fd, "nextboot_enable=\"YES\"\n", 22);
12595485Swes			(void)write(fd, "kernel=\"", 8L);
12695485Swes			(void)write(fd, kernel, strlen(kernel));
12795485Swes			(void)write(fd, "\"\n", 2);
12895485Swes			close(fd);
12995485Swes		}
13095485Swes	}
13195485Swes
1321558Srgrimes	/* Log the reboot. */
1331558Srgrimes	if (!lflag)  {
1341558Srgrimes		if ((user = getlogin()) == NULL)
1351558Srgrimes			user = (pw = getpwuid(getuid())) ?
1361558Srgrimes			    pw->pw_name : "???";
1371558Srgrimes		if (dohalt) {
1381558Srgrimes			openlog("halt", 0, LOG_AUTH | LOG_CONS);
1391558Srgrimes			syslog(LOG_CRIT, "halted by %s", user);
1401558Srgrimes		} else {
1411558Srgrimes			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
1421558Srgrimes			syslog(LOG_CRIT, "rebooted by %s", user);
1431558Srgrimes		}
1441558Srgrimes	}
145200161Sed	utx.ut_type = SHUTDOWN_TIME;
146200161Sed	gettimeofday(&utx.ut_tv, NULL);
147200161Sed	pututxline(&utx);
1481558Srgrimes
1491558Srgrimes	/*
1501558Srgrimes	 * Do a sync early on, so disks start transfers while we're off
1511558Srgrimes	 * killing processes.  Don't worry about writes done before the
1521558Srgrimes	 * processes die, the reboot system call syncs the disks.
1531558Srgrimes	 */
1541558Srgrimes	if (!nflag)
1551558Srgrimes		sync();
1561558Srgrimes
157160916Sbms	/*
158160916Sbms	 * Ignore signals that we can get as a result of killing
159160916Sbms	 * parents, group leaders, etc.
160160916Sbms	 */
161160916Sbms	(void)signal(SIGHUP,  SIG_IGN);
162160916Sbms	(void)signal(SIGINT,  SIG_IGN);
163160916Sbms	(void)signal(SIGQUIT, SIG_IGN);
164160916Sbms	(void)signal(SIGTERM, SIG_IGN);
165160916Sbms	(void)signal(SIGTSTP, SIG_IGN);
166160914Sbms
167160916Sbms	/*
168160916Sbms	 * If we're running in a pipeline, we don't want to die
169160916Sbms	 * after killing whatever we're writing to.
170160916Sbms	 */
171160916Sbms	(void)signal(SIGPIPE, SIG_IGN);
172160916Sbms
1731558Srgrimes	/* Just stop init -- if we fail, we'll restart it. */
1741558Srgrimes	if (kill(1, SIGTSTP) == -1)
17526676Scharnier		err(1, "SIGTSTP init");
1761558Srgrimes
1771558Srgrimes	/* Send a SIGTERM first, a chance to save the buffers. */
178112991Srwatson	if (kill(-1, SIGTERM) == -1 && errno != ESRCH)
17926676Scharnier		err(1, "SIGTERM processes");
1801558Srgrimes
1811558Srgrimes	/*
1821558Srgrimes	 * After the processes receive the signal, start the rest of the
1831558Srgrimes	 * buffers on their way.  Wait 5 seconds between the SIGTERM and
18474525Siedowse	 * the SIGKILL to give everybody a chance. If there is a lot of
18574525Siedowse	 * paging activity then wait longer, up to a maximum of approx
18674525Siedowse	 * 60 seconds.
1871558Srgrimes	 */
1881558Srgrimes	sleep(2);
18974525Siedowse	for (i = 0; i < 20; i++) {
19074525Siedowse		pageins = get_pageins();
19174525Siedowse		if (!nflag)
19274525Siedowse			sync();
19374525Siedowse		sleep(3);
19474525Siedowse		if (get_pageins() == pageins)
19574525Siedowse			break;
19674525Siedowse	}
1971558Srgrimes
1981558Srgrimes	for (i = 1;; ++i) {
1991558Srgrimes		if (kill(-1, SIGKILL) == -1) {
2001558Srgrimes			if (errno == ESRCH)
2011558Srgrimes				break;
2021558Srgrimes			goto restart;
2031558Srgrimes		}
2041558Srgrimes		if (i > 5) {
2051558Srgrimes			(void)fprintf(stderr,
2061558Srgrimes			    "WARNING: some process(es) wouldn't die\n");
2071558Srgrimes			break;
2081558Srgrimes		}
2091558Srgrimes		(void)sleep(2 * i);
2101558Srgrimes	}
2111558Srgrimes
2121558Srgrimes	reboot(howto);
2131558Srgrimes	/* FALLTHROUGH */
2141558Srgrimes
2151558Srgrimesrestart:
2161558Srgrimes	sverrno = errno;
21726676Scharnier	errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
2181558Srgrimes	    strerror(sverrno));
2191558Srgrimes	/* NOTREACHED */
2201558Srgrimes}
2211558Srgrimes
222140795Sdelphijstatic void
223199130Sdesusage(void)
2241558Srgrimes{
225199130Sdes
226199130Sdes	(void)fprintf(stderr, dohalt ?
227199130Sdes	    "usage: halt [-lnpq] [-k kernel]\n" :
228199130Sdes	    "usage: reboot [-dlnpq] [-k kernel]\n");
2291558Srgrimes	exit(1);
2301558Srgrimes}
23174525Siedowse
232140795Sdelphijstatic u_int
233201182Sedget_pageins(void)
23474525Siedowse{
23574525Siedowse	u_int pageins;
23674525Siedowse	size_t len;
23774525Siedowse
23874525Siedowse	len = sizeof(pageins);
23974525Siedowse	if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
24074525Siedowse	    != 0) {
24174525Siedowse		warnx("v_swappgsin");
24274525Siedowse		return (0);
24374525Siedowse	}
24474525Siedowse	return pageins;
24574525Siedowse}
246