Add chroot(8) -I option.

This commit is contained in:
Jonas 'Sortie' Termansen 2024-06-16 20:42:30 +00:00
parent 49bf6298a7
commit ed8bb024c5
3 changed files with 35 additions and 4 deletions

View File

@ -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"));
}
}

View File

@ -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.

View File

@ -17,6 +17,7 @@
* Runs a process with another root directory.
*/
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
@ -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) )