Patches to allow one to allow one to specify a directory to chroot to.
This includes support for running a script to setup that directory. The kenv variables init_chroot and init_script control this behavior, and are documented in loader(8) that's about to be committed (along with the other variables like init_path...). Submitted by: Oliver Fromme Reviewed by: myself, jhb (earlier versions)
This commit is contained in:
parent
f3b179a4b1
commit
1a7bec91fb
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=166484
202
sbin/init/init.c
202
sbin/init/init.c
@ -55,6 +55,7 @@ static const char rcsid[] =
|
|||||||
#include <db.h>
|
#include <db.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <kenv.h>
|
||||||
#include <libutil.h>
|
#include <libutil.h>
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -121,6 +122,8 @@ state_func_t clean_ttys(void);
|
|||||||
state_func_t catatonia(void);
|
state_func_t catatonia(void);
|
||||||
state_func_t death(void);
|
state_func_t death(void);
|
||||||
|
|
||||||
|
state_func_t run_script(const char *);
|
||||||
|
|
||||||
enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
|
enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
|
||||||
#define FALSE 0
|
#define FALSE 0
|
||||||
#define TRUE 1
|
#define TRUE 1
|
||||||
@ -131,9 +134,11 @@ int howto = RB_AUTOBOOT;
|
|||||||
int devfs;
|
int devfs;
|
||||||
|
|
||||||
void transition(state_t);
|
void transition(state_t);
|
||||||
state_t requested_transition = runcom;
|
state_t requested_transition;
|
||||||
|
|
||||||
void setctty(const char *);
|
void setctty(const char *);
|
||||||
|
const char *get_shell(void);
|
||||||
|
void write_stderr(const char *message);
|
||||||
|
|
||||||
typedef struct init_session {
|
typedef struct init_session {
|
||||||
int se_index; /* index of entry in ttys file */
|
int se_index; /* index of entry in ttys file */
|
||||||
@ -187,6 +192,8 @@ DB *session_db;
|
|||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
state_t initial_transition = runcom;
|
||||||
|
char kenv_value[PATH_MAX];
|
||||||
int c;
|
int c;
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
sigset_t mask;
|
sigset_t mask;
|
||||||
@ -262,7 +269,7 @@ main(int argc, char *argv[])
|
|||||||
devfs = 1;
|
devfs = 1;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
requested_transition = single_user;
|
initial_transition = single_user;
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
runcom_mode = FASTBOOT;
|
runcom_mode = FASTBOOT;
|
||||||
@ -275,6 +282,63 @@ main(int argc, char *argv[])
|
|||||||
if (optind != argc)
|
if (optind != argc)
|
||||||
warning("ignoring excess arguments");
|
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, SIGTERM, SIGTSTP,
|
||||||
|
SIGUSR1, SIGUSR2, 0);
|
||||||
|
handle(alrm_handler, SIGALRM, 0);
|
||||||
|
sigfillset(&mask);
|
||||||
|
delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
|
||||||
|
SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, 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;
|
||||||
|
(void) sigaction(SIGTTIN, &sa, (struct sigaction *)0);
|
||||||
|
(void) 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) {
|
if (devfs) {
|
||||||
struct iovec iov[4];
|
struct iovec iov[4];
|
||||||
char *s;
|
char *s;
|
||||||
@ -311,38 +375,10 @@ main(int argc, char *argv[])
|
|||||||
free(s);
|
free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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, SIGTERM, SIGTSTP,
|
|
||||||
SIGUSR1, SIGUSR2, 0);
|
|
||||||
handle(alrm_handler, SIGALRM, 0);
|
|
||||||
sigfillset(&mask);
|
|
||||||
delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
|
|
||||||
SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, 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;
|
|
||||||
(void) sigaction(SIGTTIN, &sa, (struct sigaction *)0);
|
|
||||||
(void) sigaction(SIGTTOU, &sa, (struct sigaction *)0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Paranoia.
|
|
||||||
*/
|
|
||||||
close(0);
|
|
||||||
close(1);
|
|
||||||
close(2);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start the state machine.
|
* Start the state machine.
|
||||||
*/
|
*/
|
||||||
transition(requested_transition);
|
transition(initial_transition);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Should never reach here.
|
* Should never reach here.
|
||||||
@ -558,6 +594,23 @@ setctty(const char *name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
get_shell(void)
|
||||||
|
{
|
||||||
|
static char kenv_value[PATH_MAX];
|
||||||
|
|
||||||
|
if (kenv(KENV_GET, "init_shell", kenv_value, sizeof(kenv_value)) > 0)
|
||||||
|
return kenv_value;
|
||||||
|
else
|
||||||
|
return _PATH_BSHELL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write_stderr(const char *message)
|
||||||
|
{
|
||||||
|
write(STDERR_FILENO, message, strlen(message));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bring the system up single user.
|
* Bring the system up single user.
|
||||||
*/
|
*/
|
||||||
@ -567,7 +620,7 @@ single_user(void)
|
|||||||
pid_t pid, wpid;
|
pid_t pid, wpid;
|
||||||
int status;
|
int status;
|
||||||
sigset_t mask;
|
sigset_t mask;
|
||||||
const char *shell = _PATH_BSHELL;
|
const char *shell;
|
||||||
char *argv[2];
|
char *argv[2];
|
||||||
#ifdef SECURE
|
#ifdef SECURE
|
||||||
struct ttyent *typ;
|
struct ttyent *typ;
|
||||||
@ -589,6 +642,8 @@ single_user(void)
|
|||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shell = get_shell();
|
||||||
|
|
||||||
if ((pid = fork()) == 0) {
|
if ((pid = fork()) == 0) {
|
||||||
/*
|
/*
|
||||||
* Start the single user session.
|
* Start the single user session.
|
||||||
@ -605,7 +660,7 @@ single_user(void)
|
|||||||
pp = getpwnam("root");
|
pp = getpwnam("root");
|
||||||
if (typ && (typ->ty_status & TTY_SECURE) == 0 &&
|
if (typ && (typ->ty_status & TTY_SECURE) == 0 &&
|
||||||
pp && *pp->pw_passwd) {
|
pp && *pp->pw_passwd) {
|
||||||
write(STDERR_FILENO, banner, sizeof banner - 1);
|
write_stderr(banner);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
clear = getpass("Password:");
|
clear = getpass("Password:");
|
||||||
if (clear == 0 || *clear == '\0')
|
if (clear == 0 || *clear == '\0')
|
||||||
@ -626,10 +681,10 @@ single_user(void)
|
|||||||
char *cp = altshell;
|
char *cp = altshell;
|
||||||
int num;
|
int num;
|
||||||
|
|
||||||
#define SHREQUEST \
|
#define SHREQUEST "Enter full pathname of shell or RETURN for "
|
||||||
"Enter full pathname of shell or RETURN for " _PATH_BSHELL ": "
|
write_stderr(SHREQUEST);
|
||||||
(void)write(STDERR_FILENO,
|
write_stderr(shell);
|
||||||
SHREQUEST, sizeof(SHREQUEST) - 1);
|
write_stderr(": ");
|
||||||
while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
|
while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
|
||||||
num != 0 && *cp != '\n' && cp < &altshell[127])
|
num != 0 && *cp != '\n' && cp < &altshell[127])
|
||||||
cp++;
|
cp++;
|
||||||
@ -717,12 +772,36 @@ single_user(void)
|
|||||||
*/
|
*/
|
||||||
state_func_t
|
state_func_t
|
||||||
runcom(void)
|
runcom(void)
|
||||||
|
{
|
||||||
|
state_func_t next_transition;
|
||||||
|
|
||||||
|
if ((next_transition = run_script(_PATH_RUNCOM)) != 0)
|
||||||
|
return next_transition;
|
||||||
|
|
||||||
|
runcom_mode = AUTOBOOT; /* the default */
|
||||||
|
/* NB: should send a message to the session logger to avoid blocking. */
|
||||||
|
logwtmp("~", "reboot", "");
|
||||||
|
return (state_func_t) read_ttys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run a shell script.
|
||||||
|
* Returns 0 on success, otherwise the next transition to enter:
|
||||||
|
* - single_user if fork/execv/waitpid failed, or if the script
|
||||||
|
* terminated with a signal or exit code != 0.
|
||||||
|
* - death if a SIGTERM was delivered to init(8).
|
||||||
|
*/
|
||||||
|
state_func_t
|
||||||
|
run_script(const char *script)
|
||||||
{
|
{
|
||||||
pid_t pid, wpid;
|
pid_t pid, wpid;
|
||||||
int status;
|
int status;
|
||||||
char *argv[4];
|
char *argv[4];
|
||||||
|
const char *shell;
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
|
shell = get_shell();
|
||||||
|
|
||||||
if ((pid = fork()) == 0) {
|
if ((pid = fork()) == 0) {
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = 0;
|
sa.sa_flags = 0;
|
||||||
@ -733,11 +812,10 @@ runcom(void)
|
|||||||
setctty(_PATH_CONSOLE);
|
setctty(_PATH_CONSOLE);
|
||||||
|
|
||||||
char _sh[] = "sh";
|
char _sh[] = "sh";
|
||||||
char _path_runcom[] = _PATH_RUNCOM;
|
|
||||||
char _autoboot[] = "autoboot";
|
char _autoboot[] = "autoboot";
|
||||||
|
|
||||||
argv[0] = _sh;
|
argv[0] = _sh;
|
||||||
argv[1] = _path_runcom;
|
argv[1] = __DECONST(char *, script);
|
||||||
argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0;
|
argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0;
|
||||||
argv[3] = 0;
|
argv[3] = 0;
|
||||||
|
|
||||||
@ -746,14 +824,13 @@ runcom(void)
|
|||||||
#ifdef LOGIN_CAP
|
#ifdef LOGIN_CAP
|
||||||
setprocresources(RESOURCE_RC);
|
setprocresources(RESOURCE_RC);
|
||||||
#endif
|
#endif
|
||||||
execv(_PATH_BSHELL, argv);
|
execv(shell, argv);
|
||||||
stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
|
stall("can't exec %s for %s: %m", shell, script);
|
||||||
_exit(1); /* force single user mode */
|
_exit(1); /* force single user mode */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid == -1) {
|
if (pid == -1) {
|
||||||
emergency("can't fork for %s on %s: %m",
|
emergency("can't fork for %s on %s: %m", shell, script);
|
||||||
_PATH_BSHELL, _PATH_RUNCOM);
|
|
||||||
while (waitpid(-1, (int *) 0, WNOHANG) > 0)
|
while (waitpid(-1, (int *) 0, WNOHANG) > 0)
|
||||||
continue;
|
continue;
|
||||||
sleep(STALL_TIMEOUT);
|
sleep(STALL_TIMEOUT);
|
||||||
@ -772,13 +849,13 @@ runcom(void)
|
|||||||
return (state_func_t) death;
|
return (state_func_t) death;
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
warning("wait for %s on %s failed: %m; going to single user mode",
|
warning("wait for %s on %s failed: %m; going to "
|
||||||
_PATH_BSHELL, _PATH_RUNCOM);
|
"single user mode", shell, script);
|
||||||
return (state_func_t) single_user;
|
return (state_func_t) single_user;
|
||||||
}
|
}
|
||||||
if (wpid == pid && WIFSTOPPED(status)) {
|
if (wpid == pid && WIFSTOPPED(status)) {
|
||||||
warning("init: %s on %s stopped, restarting\n",
|
warning("init: %s on %s stopped, restarting\n",
|
||||||
_PATH_BSHELL, _PATH_RUNCOM);
|
shell, script);
|
||||||
kill(pid, SIGCONT);
|
kill(pid, SIGCONT);
|
||||||
wpid = -1;
|
wpid = -1;
|
||||||
}
|
}
|
||||||
@ -795,18 +872,15 @@ runcom(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!WIFEXITED(status)) {
|
if (!WIFEXITED(status)) {
|
||||||
warning("%s on %s terminated abnormally, going to single user mode",
|
warning("%s on %s terminated abnormally, going to single "
|
||||||
_PATH_BSHELL, _PATH_RUNCOM);
|
"user mode", shell, script);
|
||||||
return (state_func_t) single_user;
|
return (state_func_t) single_user;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WEXITSTATUS(status))
|
if (WEXITSTATUS(status))
|
||||||
return (state_func_t) single_user;
|
return (state_func_t) single_user;
|
||||||
|
|
||||||
runcom_mode = AUTOBOOT; /* the default */
|
return (state_func_t) 0;
|
||||||
/* NB: should send a message to the session logger to avoid blocking. */
|
|
||||||
logwtmp("~", "reboot", "");
|
|
||||||
return (state_func_t) read_ttys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1465,6 +1539,7 @@ runshutdown(void)
|
|||||||
int shutdowntimeout;
|
int shutdowntimeout;
|
||||||
size_t len;
|
size_t len;
|
||||||
char *argv[4];
|
char *argv[4];
|
||||||
|
const char *shell;
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
|
|
||||||
@ -1477,6 +1552,8 @@ runshutdown(void)
|
|||||||
if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT)
|
if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
shell = get_shell();
|
||||||
|
|
||||||
if ((pid = fork()) == 0) {
|
if ((pid = fork()) == 0) {
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
@ -1517,14 +1594,13 @@ runshutdown(void)
|
|||||||
#ifdef LOGIN_CAP
|
#ifdef LOGIN_CAP
|
||||||
setprocresources(RESOURCE_RC);
|
setprocresources(RESOURCE_RC);
|
||||||
#endif
|
#endif
|
||||||
execv(_PATH_BSHELL, argv);
|
execv(shell, argv);
|
||||||
warning("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNDOWN);
|
warning("can't exec %s for %s: %m", shell, _PATH_RUNDOWN);
|
||||||
_exit(1); /* force single user mode */
|
_exit(1); /* force single user mode */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid == -1) {
|
if (pid == -1) {
|
||||||
emergency("can't fork for %s on %s: %m",
|
emergency("can't fork for %s on %s: %m", shell, _PATH_RUNDOWN);
|
||||||
_PATH_BSHELL, _PATH_RUNDOWN);
|
|
||||||
while (waitpid(-1, (int *) 0, WNOHANG) > 0)
|
while (waitpid(-1, (int *) 0, WNOHANG) > 0)
|
||||||
continue;
|
continue;
|
||||||
sleep(STALL_TIMEOUT);
|
sleep(STALL_TIMEOUT);
|
||||||
@ -1548,20 +1624,20 @@ runshutdown(void)
|
|||||||
if (clang == 1) {
|
if (clang == 1) {
|
||||||
/* we were waiting for the sub-shell */
|
/* we were waiting for the sub-shell */
|
||||||
kill(wpid, SIGTERM);
|
kill(wpid, SIGTERM);
|
||||||
warning("timeout expired for %s on %s: %m; going to single user mode",
|
warning("timeout expired for %s on %s: %m; going to "
|
||||||
_PATH_BSHELL, _PATH_RUNDOWN);
|
"single user mode", shell, _PATH_RUNDOWN);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (wpid == -1) {
|
if (wpid == -1) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
warning("wait for %s on %s failed: %m; going to single user mode",
|
warning("wait for %s on %s failed: %m; going to "
|
||||||
_PATH_BSHELL, _PATH_RUNDOWN);
|
"single user mode", shell, _PATH_RUNDOWN);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (wpid == pid && WIFSTOPPED(status)) {
|
if (wpid == pid && WIFSTOPPED(status)) {
|
||||||
warning("init: %s on %s stopped, restarting\n",
|
warning("init: %s on %s stopped, restarting\n",
|
||||||
_PATH_BSHELL, _PATH_RUNDOWN);
|
shell, _PATH_RUNDOWN);
|
||||||
kill(pid, SIGCONT);
|
kill(pid, SIGCONT);
|
||||||
wpid = -1;
|
wpid = -1;
|
||||||
}
|
}
|
||||||
@ -1584,8 +1660,8 @@ runshutdown(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!WIFEXITED(status)) {
|
if (!WIFEXITED(status)) {
|
||||||
warning("%s on %s terminated abnormally, going to single user mode",
|
warning("%s on %s terminated abnormally, going to "
|
||||||
_PATH_BSHELL, _PATH_RUNDOWN);
|
"single user mode", shell, _PATH_RUNDOWN);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user