Commit 19b9563a authored by Mike Hibler's avatar Mike Hibler

/sbin/init changed significantly in 10.3.

parent d68e79f4
......@@ -4557,6 +4557,7 @@ outfiles="Makeconf GNUmakefile setversion \
tmcc/freebsd/init/8/GNUmakefile \
tmcc/freebsd/init/9/GNUmakefile \
tmcc/freebsd/init/10/GNUmakefile \
tmcc/freebsd/init/10.3/GNUmakefile \
tmcc/freebsd/init/11/GNUmakefile \
tmcc/freebsd/supfile tmcc/freebsd/sethostname \
tmcc/linux/GNUmakefile tmcc/linux/supfile \
......
#
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -284,6 +284,7 @@ outfiles="Makeconf GNUmakefile setversion \
tmcc/freebsd/init/8/GNUmakefile \
tmcc/freebsd/init/9/GNUmakefile \
tmcc/freebsd/init/10/GNUmakefile \
tmcc/freebsd/init/10.3/GNUmakefile \
tmcc/freebsd/init/11/GNUmakefile \
tmcc/freebsd/supfile tmcc/freebsd/sethostname \
tmcc/linux/GNUmakefile tmcc/linux/supfile \
......
#
# Insert Copyright Here.
#
SRCDIR = @srcdir@
TESTBED_SRCDIR = @top_srcdir@
OBJDIR = @top_builddir@
SUBDIR = $(subst $(TESTBED_SRCDIR)/,,$(SRCDIR))
include $(OBJDIR)/Makeconf
all:
client: init
include $(TESTBED_SRCDIR)/GNUmakerules
CFLAGS += -DDEBUGSHELL -DSECURE -DLOGIN_CAP -DCOMPAT_SYSV_INIT -DTESTBED
init.c: pathnames.h mntopts.h
getmntopts.c: mntopts.h
init: init.c getmntopts.c
$(CC) $(CFLAGS) -static -o init $^ -lutil -lcrypt
install:
client-install: client
install -s -o root -g wheel -m 555 -b -B.bak -fschg -S init $(DESTDIR)/sbin/init
clean:
rm -f *.o core init
/*-
* Copyright (c) 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#if 0
#ifndef lint
static char sccsid[] = "@(#)getmntopts.c 8.3 (Berkeley) 3/29/95";
#endif /* not lint */
#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: releng/10.3/sbin/mount/getmntopts.c 230372 2012-01-20 07:29:29Z jh $");
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <err.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mntopts.h"
int getmnt_silent = 0;
void
getmntopts(const char *options, const struct mntopt *m0, int *flagp,
int *altflagp)
{
const struct mntopt *m;
int negative, len;
char *opt, *optbuf, *p;
int *thisflagp;
/* Copy option string, since it is about to be torn asunder... */
if ((optbuf = strdup(options)) == NULL)
err(1, NULL);
for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
/* Check for "no" prefix. */
if (opt[0] == 'n' && opt[1] == 'o') {
negative = 1;
opt += 2;
} else
negative = 0;
/*
* for options with assignments in them (ie. quotas)
* ignore the assignment as it's handled elsewhere
*/
p = strchr(opt, '=');
if (p != NULL)
*++p = '\0';
/* Scan option table. */
for (m = m0; m->m_option != NULL; ++m) {
len = strlen(m->m_option);
if (strncasecmp(opt, m->m_option, len) == 0)
if (opt[len] == '\0' || opt[len] == '=')
break;
}
/* Save flag, or fail if option is not recognized. */
if (m->m_option) {
thisflagp = m->m_altloc ? altflagp : flagp;
if (negative == m->m_inverse)
*thisflagp |= m->m_flag;
else
*thisflagp &= ~m->m_flag;
} else if (!getmnt_silent) {
errx(1, "-o %s: option not supported", opt);
}
}
free(optbuf);
}
void
rmslashes(char *rrpin, char *rrpout)
{
char *rrpoutstart;
*rrpout = *rrpin;
for (rrpoutstart = rrpout; *rrpin != '\0'; *rrpout++ = *rrpin++) {
/* skip all double slashes */
while (*rrpin == '/' && *(rrpin + 1) == '/')
rrpin++;
}
/* remove trailing slash if necessary */
if (rrpout - rrpoutstart > 1 && *(rrpout - 1) == '/')
*(rrpout - 1) = '\0';
else
*rrpout = '\0';
}
int
checkpath(const char *path, char *resolved)
{
struct stat sb;
if (realpath(path, resolved) == NULL || stat(resolved, &sb) != 0)
return (1);
if (!S_ISDIR(sb.st_mode)) {
errno = ENOTDIR;
return (1);
}
return (0);
}
void
build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val,
size_t len)
{
int i;
if (*iovlen < 0)
return;
i = *iovlen;
*iov = realloc(*iov, sizeof **iov * (i + 2));
if (*iov == NULL) {
*iovlen = -1;
return;
}
(*iov)[i].iov_base = strdup(name);
(*iov)[i].iov_len = strlen(name) + 1;
i++;
(*iov)[i].iov_base = val;
if (len == (size_t)-1) {
if (val != NULL)
len = strlen(val) + 1;
else
len = 0;
}
(*iov)[i].iov_len = (int)len;
*iovlen = ++i;
}
/*
* This function is needed for compatibility with parameters
* which used to use the mount_argf() command for the old mount() syscall.
*/
void
build_iovec_argf(struct iovec **iov, int *iovlen, const char *name,
const char *fmt, ...)
{
va_list ap;
char val[255] = { 0 };
va_start(ap, fmt);
vsnprintf(val, sizeof(val), fmt, ap);
va_end(ap);
build_iovec(iov, iovlen, name, strdup(val), (size_t)-1);
}
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Donn Seeley at Berkeley Software Design, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1991, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93";
#endif
static const char rcsid[] =
"$FreeBSD: releng/10.3/sbin/init/init.c 293747 2016-01-12 10:24:08Z trasz $";
#endif /* not lint */
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <db.h>
#include <errno.h>
#include <fcntl.h>
#include <kenv.h>
#include <libutil.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <ttyent.h>
#include <unistd.h>
#include <sys/reboot.h>
#include <err.h>
#include <stdarg.h>
#ifdef SECURE
#include <pwd.h>
#endif
#ifdef LOGIN_CAP
#include <login_cap.h>
#endif
#include "mntopts.h"
#include "pathnames.h"
/*
* Sleep times; used to prevent thrashing.
*/
#define GETTY_SPACING 5 /* N secs minimum getty spacing */
#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */
#define GETTY_NSPACE 3 /* max. spacing count to bring reaction */
#define WINDOW_WAIT 3 /* wait N secs after starting window */
#define STALL_TIMEOUT 30 /* wait N secs after warning */
#define DEATH_WATCH 10 /* wait N secs for procs to die */
#define DEATH_SCRIPT 120 /* wait for 2min for /etc/rc.shutdown */
#define RESOURCE_RC "daemon"
#define RESOURCE_WINDOW "default"
#define RESOURCE_GETTY "default"
static void handle(sig_t, ...);
static void delset(sigset_t *, ...);
static void stall(const char *, ...) __printflike(1, 2);
static void warning(const char *, ...) __printflike(1, 2);
static void emergency(const char *, ...) __printflike(1, 2);
static void disaster(int);
static void badsys(int);
static void revoke_ttys(void);
static int runshutdown(void);
static char *strk(char *);
/*
* We really need a recursive typedef...
* The following at least guarantees that the return type of (*state_t)()
* is sufficiently wide to hold a function pointer.
*/
typedef long (*state_func_t)(void);
typedef state_func_t (*state_t)(void);
static state_func_t single_user(void);
static state_func_t runcom(void);
static state_func_t read_ttys(void);
static state_func_t multi_user(void);
static state_func_t clean_ttys(void);
static state_func_t catatonia(void);
static state_func_t death(void);
static state_func_t death_single(void);
static state_func_t reroot(void);
static state_func_t reroot_phase_two(void);
static state_func_t run_script(const char *);
static enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
#define FALSE 0
#define TRUE 1
static int Reboot = FALSE;
static int howto = RB_AUTOBOOT;
static int devfs;
static void transition(state_t);
static state_t requested_transition;
static state_t current_state = death_single;
static void open_console(void);
static const char *get_shell(void);
static void write_stderr(const char *message);
typedef struct init_session {
int se_index; /* index of entry in ttys file */
pid_t se_process; /* controlling process */
time_t se_started; /* used to avoid thrashing */
int se_flags; /* status of session */
#define SE_SHUTDOWN 0x1 /* session won't be restarted */
#define SE_PRESENT 0x2 /* session is in /etc/ttys */
int se_nspace; /* spacing count */
char *se_device; /* filename of port */
char *se_getty; /* what to run on that port */
char *se_getty_argv_space; /* pre-parsed argument array space */
char **se_getty_argv; /* pre-parsed argument array */
char *se_window; /* window system (started only once) */
char *se_window_argv_space; /* pre-parsed argument array space */
char **se_window_argv; /* pre-parsed argument array */
char *se_type; /* default terminal type */
struct init_session *se_prev;
struct init_session *se_next;
} session_t;
static void free_session(session_t *);
static session_t *new_session(session_t *, int, struct ttyent *);
static session_t *sessions;
static char **construct_argv(char *);
static void start_window_system(session_t *);
static void collect_child(pid_t);
static pid_t start_getty(session_t *);
static void transition_handler(int);
static void alrm_handler(int);
static void setsecuritylevel(int);
static int getsecuritylevel(void);
static int setupargv(session_t *, struct ttyent *);
#ifdef LOGIN_CAP
static void setprocresources(const char *);
#endif
static int clang;
static int start_session_db(void);
static void add_session(session_t *);
static void del_session(session_t *);
static session_t *find_session(pid_t);
static DB *session_db;
/*
* The mother of all processes.
*/
int
main(int argc, char *argv[])
{
state_t initial_transition = runcom;
char kenv_value[PATH_MAX];
int c, error;
struct sigaction sa;
sigset_t mask;
/* Dispose of random users. */
if (getuid() != 0)
errx(1, "%s", strerror(EPERM));
/* System V users like to reexec init. */
if (getpid() != 1) {
#ifdef COMPAT_SYSV_INIT
/* So give them what they want */
if (argc > 1) {
if (strlen(argv[1]) == 1) {
char runlevel = *argv[1];
int sig;
switch (runlevel) {
case '0': /* halt + poweroff */
sig = SIGUSR2;
break;
case '1': /* single-user */
sig = SIGTERM;
break;
case '6': /* reboot */
sig = SIGINT;
break;
case 'c': /* block further logins */
sig = SIGTSTP;
break;
case 'q': /* rescan /etc/ttys */
sig = SIGHUP;
break;
case 'r': /* remount root */
sig = SIGEMT;
break;
default:
goto invalid;
}
kill(1, sig);
_exit(0);
} else
invalid:
errx(1, "invalid run-level ``%s''", argv[1]);
} else
#endif
errx(1, "already running");
}
/*
* Note that this does NOT open a file...
* Does 'init' deserve its own facility number?
*/
openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
/*
* Create an initial session.
*/
if (setsid() < 0 && (errno != EPERM || getsid(0) != 1))
warning("initial setsid() failed: %m");
/*
* Establish an initial user so that programs running
* single user do not freak out and die (like passwd).
*/
if (setlogin("root") < 0)
warning("setlogin() failed: %m");
/*
* This code assumes that we always get arguments through flags,
* never through bits set in some random machine register.
*/
while ((c = getopt(argc, argv, "dsfr")) != -1)
switch (c) {
case 'd':
devfs = 1;
break;
case 's':
initial_transition = single_user;
break;
case 'f':
runcom_mode = FASTBOOT;
break;
case 'r':
initial_transition = reroot_phase_two;
break;
default:
warning("unrecognized flag '-%c'", c);
break;
}
if (optind != argc)
warning("ignoring excess arguments");
/*
* We catch or block signals rather than ignore them,
* so that they get reset on exec.
*/
handle(badsys, SIGSYS, 0);
handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU,
SIGXFSZ, 0);
handle(transition_handler, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP,
SIGUSR1, SIGUSR2, 0);
handle(alrm_handler, SIGALRM, 0);
sigfillset(&mask);
delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP,
SIGALRM, SIGUSR1, SIGUSR2, 0);
sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = SIG_IGN;
sigaction(SIGTTIN, &sa, (struct sigaction *)0);
sigaction(SIGTTOU, &sa, (struct sigaction *)0);
/*
* Paranoia.
*/
close(0);
close(1);
close(2);
if (kenv(KENV_GET, "init_script", kenv_value, sizeof(kenv_value)) > 0) {
state_func_t next_transition;
if ((next_transition = run_script(kenv_value)) != 0)
initial_transition = (state_t) next_transition;
}
if (kenv(KENV_GET, "init_chroot", kenv_value, sizeof(kenv_value)) > 0) {
if (chdir(kenv_value) != 0 || chroot(".") != 0)
warning("Can't chroot to %s: %m", kenv_value);
}
/*
* Additional check if devfs needs to be mounted:
* If "/" and "/dev" have the same device number,
* then it hasn't been mounted yet.
*/
if (!devfs) {
struct stat stst;
dev_t root_devno;
stat("/", &stst);
root_devno = stst.st_dev;
if (stat("/dev", &stst) != 0)
warning("Can't stat /dev: %m");
else if (stst.st_dev == root_devno)
devfs++;
}
if (devfs) {
struct iovec iov[4];
char *s;
int i;
char _fstype[] = "fstype";
char _devfs[] = "devfs";
char _fspath[] = "fspath";
char _path_dev[]= _PATH_DEV;
iov[0].iov_base = _fstype;
iov[0].iov_len = sizeof(_fstype);
iov[1].iov_base = _devfs;
iov[1].iov_len = sizeof(_devfs);
iov[2].iov_base = _fspath;
iov[2].iov_len = sizeof(_fspath);
/*
* Try to avoid the trailing slash in _PATH_DEV.
* Be *very* defensive.
*/
s = strdup(_PATH_DEV);
if (s != NULL) {
i = strlen(s);
if (i > 0 && s[i - 1] == '/')
s[i - 1] = '\0';
iov[3].iov_base = s;
iov[3].iov_len = strlen(s) + 1;
} else {
iov[3].iov_base = _path_dev;
iov[3].iov_len = sizeof(_path_dev);
}
nmount(iov, 4, 0);
if (s != NULL)
free(s);
}
if (initial_transition != reroot_phase_two) {
/*
* Unmount reroot leftovers. This runs after init(8)
* gets reexecuted after reroot_phase_two() is done.
*/
error = unmount(_PATH_REROOT, MNT_FORCE);
if (error != 0 && errno != EINVAL)
warning("Cannot unmount %s: %m", _PATH_REROOT);
}
/*
* Start the state machine.
*/
transition(initial_transition);
/*
* Should never reach here.
*/
return 1;
}
/*
* Associate a function with a signal handler.
*/
static void
handle(sig_t handler, ...)
{
int sig;
struct sigaction sa;
sigset_t mask_everything;
va_list ap;
va_start(ap, handler);
sa.sa_handler = handler;
sigfillset(&mask_everything);
while ((sig = va_arg(ap, int)) != 0) {
sa.sa_mask = mask_everything;
/* XXX SA_RESTART? */
sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
sigaction(sig, &sa, (struct sigaction *) 0);
}
va_end(ap);
}
/*
* Delete a set of signals from a mask.
*/
static void
delset(sigset_t *maskp, ...)
{
int sig;
va_list ap;
va_start(ap, maskp);
while ((sig = va_arg(ap, int)) != 0)
sigdelset(maskp, sig);
va_end(ap);
}
/*
* Log a message and sleep for a while (to give someone an opportunity
* to read it and to save log or hardcopy output if the problem is chronic).
* NB: should send a message to the session logger to avoid blocking.
*/
static void
stall(const char *message, ...)
{
va_list ap;
va_start(ap, message);
vsyslog(LOG_ALERT, message, ap);
va_end(ap);
sleep(STALL_TIMEOUT);
}
/*
* Like stall(), but doesn't sleep.
* If cpp had variadic macros, the two functions could be #defines for another.
* NB: should send a message to the session logger to avoid blocking.
*/
static void
warning(const char *message, ...)
{
va_list ap;
va_start(ap, message);
vsyslog(LOG_ALERT, message, ap);
va_end(ap);
}
/*
* Log an emergency message.
* NB: should send a message to the session logger to avoid blocking.
*/
static void
emergency(const char *message, ...)
{
va_list ap;
va_start(ap, message);