diff --git a/osdk/Cargo.lock b/osdk/Cargo.lock index e0758b7ac..05fba2e90 100644 --- a/osdk/Cargo.lock +++ b/osdk/Cargo.lock @@ -203,6 +203,7 @@ dependencies = [ "shlex", "syn", "toml", + "which", ] [[package]] @@ -380,6 +381,12 @@ dependencies = [ "regex", ] +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + [[package]] name = "env_logger" version = "0.11.8" @@ -399,6 +406,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "getrandom" version = "0.3.3" @@ -608,6 +625,12 @@ dependencies = [ "xmas-elf", ] +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "lock_api" version = "0.4.12" @@ -811,6 +834,19 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustversion" version = "1.0.20" @@ -1063,6 +1099,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" +dependencies = [ + "env_home", + "rustix", + "winsafe", +] + [[package]] name = "windows-core" version = "0.61.0" @@ -1204,6 +1251,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/osdk/Cargo.toml b/osdk/Cargo.toml index 3075cab5e..1c05a9f2c 100644 --- a/osdk/Cargo.toml +++ b/osdk/Cargo.toml @@ -26,6 +26,7 @@ serde_json = "1.0.111" shlex = "1.3.0" syn = { version = "2.0.52", features = ["extra-traits", "full", "parsing", "printing"] } toml = { version = "0.8.8", features = ["preserve_order"] } +which = "8.0.0" [dev-dependencies] assert_cmd = "2.0.13" diff --git a/osdk/src/arch.rs b/osdk/src/arch.rs index 348ced52b..53771ed12 100644 --- a/osdk/src/arch.rs +++ b/osdk/src/arch.rs @@ -80,7 +80,7 @@ impl Display for Arch { /// Get the default architecture implied by the host rustc's default architecture. pub fn get_default_arch() -> Arch { - let output = std::process::Command::new("rustc") + let output = crate::util::new_command_checked_exists("rustc") .arg("-vV") .output() .expect("Failed to run rustc to get the host target"); diff --git a/osdk/src/bundle/mod.rs b/osdk/src/bundle/mod.rs index fa4d33017..841f045d2 100644 --- a/osdk/src/bundle/mod.rs +++ b/osdk/src/bundle/mod.rs @@ -11,7 +11,6 @@ use vm_image::{AsterVmImage, AsterVmImageType}; use std::{ path::{Path, PathBuf}, - process::Command, time::SystemTime, }; @@ -22,7 +21,7 @@ use crate::{ }, error::Errno, error_msg, - util::DirGuard, + util::{new_command_checked_exists, DirGuard}, }; /// The osdk bundle artifact that stores as `bundle` directory. @@ -199,8 +198,8 @@ impl Bundle { ActionChoice::Run => &config.run, ActionChoice::Test => &config.test, }; - let mut qemu_cmd = Command::new(&action.qemu.path); + let mut qemu_cmd = new_command_checked_exists(&action.qemu.path); qemu_cmd.current_dir(&config.work_dir); match action.boot.method { diff --git a/osdk/src/commands/build/bin.rs b/osdk/src/commands/build/bin.rs index 6e1d5c0f7..abd6d2f5b 100644 --- a/osdk/src/commands/build/bin.rs +++ b/osdk/src/commands/build/bin.rs @@ -4,7 +4,6 @@ use std::{ fs::{File, OpenOptions}, io::{Read, Seek, SeekFrom, Write}, path::{Path, PathBuf}, - process::Command, }; use linux_bzimage_builder::{ @@ -17,7 +16,7 @@ use crate::{ bin::{AsterBin, AsterBinType, AsterBzImageMeta, AsterElfMeta}, file::BundleFile, }, - util::{get_current_crates, hard_link_or_copy}, + util::{get_current_crates, hard_link_or_copy, new_command_checked_exists}, }; pub fn make_install_bzimage( @@ -95,7 +94,7 @@ pub fn make_elf_for_qemu(install_dir: impl AsRef, elf: &AsterBin, strip: b if strip { // We use rust-strip to reduce the kernel image size. - let status = Command::new("rust-strip") + let status = new_command_checked_exists("rust-strip") .arg(elf.path()) .arg("-o") .arg(result_elf_path.as_os_str()) @@ -170,7 +169,7 @@ fn install_setup_with_arch( } let target_dir = std::fs::canonicalize(target_dir).unwrap(); - let mut cmd = Command::new("cargo"); + let mut cmd = new_command_checked_exists("cargo"); let mut rustflags = vec![ "-Cdebuginfo=2", "-Ccode-model=kernel", diff --git a/osdk/src/commands/build/grub.rs b/osdk/src/commands/build/grub.rs index 059e4a5f1..507dba630 100644 --- a/osdk/src/commands/build/grub.rs +++ b/osdk/src/commands/build/grub.rs @@ -16,7 +16,7 @@ use crate::{ scheme::{ActionChoice, BootProtocol}, Config, }, - util::{get_current_crates, hard_link_or_copy}, + util::{get_current_crates, hard_link_or_copy, new_command_checked_exists}, }; pub fn create_bootdev_image( @@ -84,7 +84,7 @@ pub fn create_bootdev_image( // Make the boot device CDROM image using `grub-mkrescue`. let iso_path = &target_dir.as_ref().join(target_name.to_string() + ".iso"); - let mut grub_mkrescue_cmd = std::process::Command::new(action.grub.grub_mkrescue.as_os_str()); + let mut grub_mkrescue_cmd = new_command_checked_exists(action.grub.grub_mkrescue.as_os_str()); grub_mkrescue_cmd .arg(iso_root.as_os_str()) .arg("-o") @@ -166,7 +166,7 @@ fn generate_grub_cfg( } fn get_grub_mkrescue_version(grub_mkrescue: &PathBuf) -> String { - let mut cmd = std::process::Command::new(grub_mkrescue); + let mut cmd = new_command_checked_exists(grub_mkrescue); cmd.arg("--version"); let output = cmd.output().unwrap(); String::from_utf8(output.stdout).unwrap() diff --git a/osdk/src/commands/build/qcow2.rs b/osdk/src/commands/build/qcow2.rs index 39418b7a6..952c4d4f1 100644 --- a/osdk/src/commands/build/qcow2.rs +++ b/osdk/src/commands/build/qcow2.rs @@ -8,6 +8,7 @@ use crate::{ vm_image::{AsterQcow2ImageMeta, AsterVmImage, AsterVmImageType}, }, error_msg, + util::new_command_checked_exists, }; pub fn convert_iso_to_qcow2(iso: AsterVmImage) -> AsterVmImage { @@ -17,7 +18,7 @@ pub fn convert_iso_to_qcow2(iso: AsterVmImage) -> AsterVmImage { let iso_path = iso.path(); let qcow2_path = iso_path.with_extension("qcow2"); // Convert the ISO to QCOW2 using `qemu-img`. - let mut qemu_img = process::Command::new("qemu-img"); + let mut qemu_img = new_command_checked_exists("qemu-img"); qemu_img.args([ "convert", "-O", diff --git a/osdk/src/commands/debug.rs b/osdk/src/commands/debug.rs index 30961ae5e..4e6b89b9c 100644 --- a/osdk/src/commands/debug.rs +++ b/osdk/src/commands/debug.rs @@ -3,9 +3,8 @@ use crate::{ cli::DebugArgs, commands::util::bin_file_name, - util::{get_kernel_crate, get_target_directory}, + util::{get_kernel_crate, get_target_directory, new_command_checked_exists}, }; -use std::process::Command; pub fn execute_debug_command(_profile: &str, args: &DebugArgs) { let remote = &args.remote; @@ -16,7 +15,7 @@ pub fn execute_debug_command(_profile: &str, args: &DebugArgs) { .join(bin_file_name()); println!("Debugging {}", file_path.display()); - let mut gdb = Command::new("gdb"); + let mut gdb = new_command_checked_exists("gdb"); gdb.args([ format!("{}", file_path.display()).as_str(), "-ex", @@ -27,7 +26,7 @@ pub fn execute_debug_command(_profile: &str, args: &DebugArgs) { #[test] fn have_gdb_installed() { - let output = Command::new("gdb").arg("--version").output(); + let output = new_command_checked_exists("gdb").arg("--version").output(); assert!(output.is_ok(), "Failed to run gdb"); let stdout = String::from_utf8_lossy(&output.unwrap().stdout).to_string(); assert!(stdout.contains("GNU gdb")); diff --git a/osdk/src/commands/profile.rs b/osdk/src/commands/profile.rs index 73bb2d80a..12e8c31da 100644 --- a/osdk/src/commands/profile.rs +++ b/osdk/src/commands/profile.rs @@ -13,7 +13,7 @@ use inferno::flamegraph; use crate::{ cli::{ProfileArgs, ProfileFormat}, commands::util::bin_file_name, - util::{get_kernel_crate, get_target_directory}, + util::{get_kernel_crate, get_target_directory, new_command_checked_exists}, }; use regex::Regex; use std::{ @@ -21,7 +21,7 @@ use std::{ fs::File, io::{BufRead, Write}, path::PathBuf, - process::{Command, Stdio}, + process::Stdio, thread, time, }; @@ -92,7 +92,7 @@ fn do_collect_stack_traces(args: &ProfileArgs) { ]; gdb_args.append(&mut vec![backtrace_cmd_seq; *samples].concat()); - Command::new("gdb") + new_command_checked_exists("gdb") .args(gdb_args) .stdout(Stdio::piped()) .spawn() @@ -119,7 +119,7 @@ fn do_collect_stack_traces(args: &ProfileArgs) { } gdb_output.clear(); thread::sleep(time::Duration::from_secs_f64(*interval)); - let _ = Command::new("kill") + let _ = new_command_checked_exists("kill") .args(["-INT", &format!("{}", gdb_process.id())]) .output(); } diff --git a/osdk/src/commands/util.rs b/osdk/src/commands/util.rs index e21dd53ae..1b521791c 100644 --- a/osdk/src/commands/util.rs +++ b/osdk/src/commands/util.rs @@ -2,7 +2,7 @@ use std::process::Command; -use crate::util::get_kernel_crate; +use crate::util::{get_kernel_crate, new_command_checked_exists}; pub const COMMON_CARGO_ARGS: &[&str] = &[ "-Zbuild-std=core,alloc,compiler_builtins", @@ -12,7 +12,7 @@ pub const COMMON_CARGO_ARGS: &[&str] = &[ pub const DEFAULT_TARGET_RELPATH: &str = "osdk"; pub fn cargo() -> Command { - Command::new("cargo") + new_command_checked_exists("cargo") } pub fn profile_name_adapter(profile: &str) -> &str { diff --git a/osdk/src/config/mod.rs b/osdk/src/config/mod.rs index 29631e1f4..6c55ea4c9 100644 --- a/osdk/src/config/mod.rs +++ b/osdk/src/config/mod.rs @@ -29,6 +29,7 @@ use crate::{ config::unix_args::apply_kv_array, error::Errno, error_msg, + util::new_command_checked_exists, }; /// The global configuration for the OSDK actions. @@ -155,7 +156,7 @@ fn canonicalize_and_eval(action_scheme: &mut ActionScheme, workdir: &PathBuf) { /// This function is used to evaluate the string using the host's shell recursively /// in order. pub fn eval(cwd: impl AsRef, s: &String) -> io::Result { - let mut eval = process::Command::new("bash"); + let mut eval = new_command_checked_exists("bash"); eval.arg("-c"); eval.arg(format!("echo \"{}\"", s)); eval.current_dir(cwd.as_ref()); diff --git a/osdk/src/error.rs b/osdk/src/error.rs index 679535225..0534e7336 100644 --- a/osdk/src/error.rs +++ b/osdk/src/error.rs @@ -14,6 +14,7 @@ pub enum Errno { BadCrateName = 9, NoKernelCrate = 10, TooManyCrates = 11, + ExecutableNotFound = 12, } /// Print error message to console diff --git a/osdk/src/util.rs b/osdk/src/util.rs index ad8a645dd..59eb26677 100644 --- a/osdk/src/util.rs +++ b/osdk/src/util.rs @@ -23,7 +23,7 @@ pub fn ostd_dep() -> String { } fn cargo() -> Command { - Command::new("cargo") + new_command_checked_exists("cargo") } /// Create a new library crate with cargo @@ -163,6 +163,22 @@ pub fn get_kernel_crate() -> CrateInfo { } } +/// Check if an OSDK dependent executable (e.g. QEMU) exists. +/// +/// If it exists, create a command. If not, print an error message and exit the +/// process. +pub fn new_command_checked_exists(executable: impl AsRef) -> Command { + let executable = executable.as_ref(); + if which::which(executable).is_err() { + error_msg!( + "Executable {:?} cannot be found in PATH, please install the corresponding package", + executable + ); + std::process::exit(Errno::ExecutableNotFound as _); + } + Command::new(executable) +} + fn package_contains_ostd_main(package: &serde_json::Value) -> bool { let src_path = { let targets = package.get("targets").unwrap().as_array().unwrap(); @@ -262,13 +278,15 @@ pub fn trace_panic_from_log(qemu_log: File, bin_path: PathBuf) { let mut stack_num = 0; let pc_matcher = regex::Regex::new(r" - pc (0x[0-9a-fA-F]+)").unwrap(); let exe = bin_path.to_string_lossy(); - let mut addr2line = Command::new("addr2line"); + + let mut addr2line = new_command_checked_exists("addr2line"); addr2line.args(["-e", &exe]); let mut addr2line_proc = addr2line .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) .spawn() .unwrap(); + for line in lines.into_iter().rev() { if line.contains("Printing stack trace:") { println!("[OSDK] The kernel seems panicked. Parsing stack trace for source lines:");