diff --git a/Cargo.lock b/Cargo.lock index bd73a42..1be127f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,6 +152,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + [[package]] name = "base64" version = "0.21.0" @@ -232,6 +238,7 @@ dependencies = [ "dirs", "dotenv", "dotenv-parser", + "grep", "haikunator", "lazy_static", "libc", @@ -252,14 +259,21 @@ dependencies = [ [[package]] name = "bstr" -version = "1.2.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" +checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" dependencies = [ "memchr", + "regex-automata", "serde", ] +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + [[package]] name = "bytemuck" version = "1.13.0" @@ -593,6 +607,24 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "encoding_rs_io" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83" +dependencies = [ + "encoding_rs", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -725,6 +757,89 @@ dependencies = [ "regex", ] +[[package]] +name = "grep" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d913342e9820e3e4d095746caa468ede633a2382abba069b1d12619fe2b6171e" +dependencies = [ + "grep-cli", + "grep-matcher", + "grep-printer", + "grep-regex", + "grep-searcher", +] + +[[package]] +name = "grep-cli" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe4bdbf4300c8b039f5d7eec7fbc6760d2c85bb15ac7499c4d235667f6d747a" +dependencies = [ + "bstr", + "globset", + "lazy_static", + "log", + "regex", + "same-file", + "termcolor", + "winapi-util", +] + +[[package]] +name = "grep-matcher" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3902ca28f26945fe35cad349d776f163981d777fee382ccd6ef451126f51b319" +dependencies = [ + "memchr", +] + +[[package]] +name = "grep-printer" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14551578f49da1f774b70da5bd1b8c20bbbead01620c426cb0a217536d95a6a" +dependencies = [ + "base64 0.20.0", + "bstr", + "grep-matcher", + "grep-searcher", + "serde", + "serde_json", + "termcolor", +] + +[[package]] +name = "grep-regex" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "997598b41d53a37a2e3fc5300d5c11d825368c054420a9c65125b8fe1078463f" +dependencies = [ + "aho-corasick 0.7.20", + "bstr", + "grep-matcher", + "log", + "regex", + "regex-syntax 0.6.29", + "thread_local", +] + +[[package]] +name = "grep-searcher" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5601c4b9f480f0c9ebb40b1f6cbf447b8a50c5369223937a6c5214368c58779f" +dependencies = [ + "bstr", + "bytecount", + "encoding_rs", + "encoding_rs_io", + "grep-matcher", + "log", + "memmap2", +] + [[package]] name = "haikunator" version = "0.1.2" @@ -917,6 +1032,15 @@ version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + [[package]] name = "minimal-lexical" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index d0be766..9d4254b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ daemonize = "0.5.0" dirs = "5.0.1" dotenv = "0.15.0" dotenv-parser = "0.1.3" +grep = "0.2.12" haikunator = "0.1.2" lazy_static = "1.4.0" libc = "0.2.149" diff --git a/README.md b/README.md index 8814f2b..fd1666a 100644 --- a/README.md +++ b/README.md @@ -158,3 +158,7 @@ rules: - bind-mount rule mounts rw so that target programs can use them - remount `/` ro - run! + +## credits + +- `fixtures/helloworld-appimage-x86_84.AppImage`: https://github.com/ClonedRepos/hello-world-appimage diff --git a/fixtures/helloworld-appimage-x86_64.AppImage b/fixtures/helloworld-appimage-x86_64.AppImage new file mode 100755 index 0000000..76074d9 Binary files /dev/null and b/fixtures/helloworld-appimage-x86_64.AppImage differ diff --git a/src/enclosure/mod.rs b/src/enclosure/mod.rs index a0d7876..aef88a6 100644 --- a/src/enclosure/mod.rs +++ b/src/enclosure/mod.rs @@ -13,6 +13,7 @@ use daemonize::Daemonize; use dotenv_parser::parse_dotenv; use haikunator::Haikunator; use log::*; +use nix::errno::Errno; use nix::mount::{umount2, MntFlags}; use nix::sched::{clone, CloneFlags}; use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus}; @@ -65,7 +66,13 @@ impl Enclosure { self.set_up_temporary_files(applicable_rules)?; // Set up the container: callback, stack, etc. - let callback = || self.run_in_container(applicable_rules).unwrap(); + let callback = || match self.run_in_container(applicable_rules) { + Ok(exit_code) => exit_code, + Err(err) => { + error!("{}", err); + -1isize + } + }; let stack_size = match Resource::STACK.get() { Ok((soft, _hard)) => soft as usize, @@ -152,8 +159,16 @@ impl Enclosure { if self.config.trace { self.run_with_tracing(pid)?; } else { - ptrace::detach(pid, None)?; - self.run_without_tracing(pid)?; + match ptrace::detach(pid, None) { + Ok(_) => { + self.run_without_tracing(pid)?; + } + Err(Errno::ESRCH) => { + error!("child exited early (ESRCH)! try running boxxy with `-l debug` or `-l trace` if it isn't obvious why"); + return Ok(()); + } + err => return Ok(err?), + } } Ok(()) @@ -287,7 +302,7 @@ impl Enclosure { debug!("loaded env var: {}=********", key); } if !rule.env.is_empty() { - info!( + debug!( "loaded {} env vars from rule '{}'", rule.env.len(), rule.name @@ -372,6 +387,61 @@ impl Enclosure { } fn run_in_container(&mut self, applicable_rules: &[Rule]) -> Result { + // TODO: There HAS to be a better way than this... + let mut grep = grep::searcher::SearcherBuilder::new().build(); + + let path_to_input_binary = { + let program = self.config.command.get_program(); + match which::which(program) { + Ok(path) => path, + Err(_) => { + // Check if it's a path we can resolve + let path = PathBuf::from(program); + if path.exists() { + path + } else { + return Err(color_eyre::eyre::eyre!( + "could not resolve binary: {program:?}" + )); + } + } + } + }; + + // Search input binary for `--appimage-help` `--appimage-mount` and + // `--appimage-extract`. + // If it has all of these, it's PROBABLY an AppImage, and we should + // warn the end-user that they need to extract it first. + // TODO: Could we do this automatically? + let mut found_appimage_help = false; + let mut found_appimage_mount = false; + let mut found_appimage_extract = false; + let matcher = grep::regex::RegexMatcher::new( + r"(--appimage-help|--appimage-mount|--appimage-extract)", + )?; + grep.search_path( + matcher, + path_to_input_binary, + // TODO: Write a sink that doesn't care about line numbers and won't raise + grep::searcher::sinks::UTF8(|_, line| { + if line.contains("--appimage-help") { + found_appimage_help = true; + } else if line.contains("--appimage-mount") { + found_appimage_mount = true; + } else if line.contains("--appimage-extract") { + found_appimage_extract = true; + } + Ok(true) + }), + )?; + + if found_appimage_extract && found_appimage_help && found_appimage_mount { + return Err(color_eyre::eyre::eyre!( + "{program:?} is an AppImage! Please extract it first with --appimage-extract. For more information, see https://github.com/AppImage/AppImageKit/wiki/FUSE#fallback", + program = self.config.command.get_program() + )); + } + self.set_up_container(applicable_rules)?; let pwd = std::env::current_dir()?; @@ -406,7 +476,7 @@ impl Enclosure { debug!("setting CHILD_SUBREAPER to {}", getpid()); unsafe { libc::prctl(libc::PR_SET_CHILD_SUBREAPER, getpid()) }; - // Do the needful! + // Do the thing! debug!("running command: {:?}", self.config.command.get_program()); info!( "{}",