diff --git a/sysinstall/sysinstall.c b/sysinstall/sysinstall.c index fe27edf2..a7d3c5be 100644 --- a/sysinstall/sysinstall.c +++ b/sysinstall/sysinstall.c @@ -1751,7 +1751,7 @@ int main(void) unmount_all_but_root(); unsetenv("SYSINSTALL_TARGET"); unsetenv("SHLVL"); - exit(execute((const char*[]) { "chroot", "-d", fs, + exit(execute((const char*[]) { "chroot", "-dI", fs, "/sbin/init", NULL }, "f")); } } diff --git a/utils/chroot.8 b/utils/chroot.8 index fbcc69cd..aa6a15d7 100644 --- a/utils/chroot.8 +++ b/utils/chroot.8 @@ -6,7 +6,7 @@ .Nd run command with changed root directory .Sh SYNOPSIS .Nm -.Op Fl d +.Op Fl dI .Ar newroot .Oo .Ar command @@ -43,6 +43,12 @@ The mountpoint is removed when .Ar command completes. This option is useful for running installations. +.It Fl I , Fl \-init +Become an init process using +.Xr setinit 2 +and transfer the controlling terminal temporarily to the new session using the +.Sy TIOCSCTTY +.Xr ioctl 2 . .El .Sh ENVIRONMENT The environment is preserved. diff --git a/utils/chroot.c b/utils/chroot.c index 34e6fc8c..70a80fc5 100644 --- a/utils/chroot.c +++ b/utils/chroot.c @@ -17,6 +17,7 @@ * Runs a process with another root directory. */ +#include #include #include #include @@ -47,6 +48,7 @@ static void compact_arguments(int* argc, char*** argv) } static char* mount_point_dev; +static int tty_fd = -1; static void unmount_handler(int signum) { @@ -62,6 +64,7 @@ static void unmount_handler(int signum) int main(int argc, char* argv[]) { bool devices = false; + bool init = false; for ( int i = 1; i < argc; i++ ) { const char* arg = argv[i]; @@ -76,12 +79,15 @@ int main(int argc, char* argv[]) while ( (c = *++arg) ) switch ( c ) { case 'd': devices = true; break; + case 'I': init = true; break; default: errx(1, "unknown option -- '%c'", c); } } else if ( !strcmp(arg, "--devices") ) devices = true; + else if ( !strcmp(arg, "--init") ) + init = true; else errx(1, "unknown option: %s", arg); } @@ -91,7 +97,7 @@ int main(int argc, char* argv[]) if ( argc < 2 ) error(1, 0, "missing operand, expected new root directory"); - bool need_cleanup = devices; + bool need_cleanup = devices || init; // TODO: Why do we even have signal handling instead of just blocking the // signals and waiting for the subprocess to react? @@ -124,6 +130,9 @@ int main(int argc, char* argv[]) close(old_dev_fd); } + if ( init && (tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC)) < 0 ) + error(1, errno, "/dev/tty"); + sigset_t oldset, sigs; if ( need_cleanup ) { @@ -156,6 +165,14 @@ int main(int argc, char* argv[]) sigprocmask(SIG_SETMASK, &oldset, NULL); } + if ( init ) + { + if ( setinit() < 0 ) + error(1, errno, "setinit"); + if ( ioctl(tty_fd, TIOCSCTTY, 1) < 0 ) + error(1, errno, "TIOCSCTTY"); + } + if ( chroot(argv[1]) != 0 ) error(1, errno, "`%s'", argv[1]); @@ -175,8 +192,16 @@ int main(int argc, char* argv[]) int code; waitpid(child_pid, &code, 0); sigprocmask(SIG_BLOCK, &sigs, &oldset); + if ( init ) + { + if ( ioctl(tty_fd, TIOCSCTTY, 1) < 0 ) + error(0, errno, "TIOCSCTTY"); + } if ( devices ) - unmount(mount_point_dev, 0); + { + if ( unmount(mount_point_dev, 0) < 0 ) + error(0, errno, "unmount: %s", mount_point_dev); + } sigprocmask(SIG_SETMASK, &oldset, NULL); mount_point_dev = NULL; if ( WIFEXITED(code) )