diff --git a/README.md b/README.md index 75e4a0a..3006454 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ code between applets, making for an overall smaller binary. - chgrp - chmod - chown +- chroot - clear - cut - dirname @@ -35,6 +36,7 @@ code between applets, making for an overall smaller binary. - head - hostname - link +- logname - mkfifo - mknod - mountpoint diff --git a/src/cmd/chroot/mod.rs b/src/cmd/chroot/mod.rs new file mode 100644 index 0000000..3ad2e1c --- /dev/null +++ b/src/cmd/chroot/mod.rs @@ -0,0 +1,71 @@ +use super::Cmd; +use clap::{Arg, Command}; +use std::{ + env, io, + os::unix::{fs, process::CommandExt}, + process, +}; + +#[derive(Debug, Default)] +pub struct Chroot; + +impl Cmd for Chroot { + fn cli(&self) -> clap::Command { + Command::new("chroot") + .about("run command or interactive shell with special root directory") + .author("Nathan Fisher") + .version(env!("CARGO_PKG_VERSION")) + .args([ + Arg::new("dir") + .short('d') + .long("directory") + .help("change to this directory after performing the chroot instead of '/'") + .value_name("DIRECTORY"), + Arg::new("newroot") + .value_name("NEWROOT") + .num_args(1) + .required(true), + Arg::new("command") + .value_name("COMMAND") + .num_args(1) + .required(false), + Arg::new("arg") + .value_name("ARG") + .num_args(1..) + .requires("command") + .allow_hyphen_values(true) + .required(false), + ]) + } + + fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box> { + let Some(matches) = matches else { + return Err(io::Error::new(io::ErrorKind::Other, "no input").into()); + }; + let Some(newroot) = matches.get_one::("newroot") else { + return Err(io::Error::new(io::ErrorKind::Other, "no new root given").into()); + }; + let mut command = if let Some(c) = matches.get_one::("command") { + process::Command::new(c) + } else if let Ok(s) = env::var("SHELL") { + process::Command::new(s) + } else { + process::Command::new("/bin/sh") + }; + let mut command = &mut command; + if let Some(mut args) = matches.get_many::("arg") { + command = command.args(&mut args); + } + fs::chroot(newroot)?; + if let Some(d) = matches.get_one::("dir") { + env::set_current_dir(d)?; + } else { + env::set_current_dir("/")?; + } + return Err(command.exec().into()); + } + + fn path(&self) -> Option { + Some(crate::Path::UsrSbin) + } +} diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index ccb61d6..640d893 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -8,6 +8,7 @@ mod bootstrap; mod cat; mod chmod; mod chown; +mod chroot; mod clear; mod cp; mod cut; @@ -72,6 +73,7 @@ pub fn get(name: &str) -> Option> { "chmod" => Some(Box::new(chmod::Chmod::default())), "chgrp" => Some(Box::new(chown::Chgrp::default())), "chown" => Some(Box::new(chown::Chown::default())), + "chroot" => Some(Box::new(chroot::Chroot::default())), "clear" => Some(Box::new(clear::Clear::default())), "cut" => Some(Box::new(cut::Cut::default())), "dirname" => Some(Box::new(dirname::Dirname::default())), @@ -105,7 +107,7 @@ pub fn get(name: &str) -> Option> { } } -pub static COMMANDS: [&str; 37] = [ +pub static COMMANDS: [&str; 38] = [ "base32", "base64", "basename", @@ -113,6 +115,7 @@ pub static COMMANDS: [&str; 37] = [ "chgrp", "chmod", "chown", + "chroot", "clear", "cut", "dirname",