From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <pve-devel-bounces@lists.proxmox.com> Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 5CE241FF164 for <inbox@lore.proxmox.com>; Fri, 9 May 2025 14:11:24 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 716D53C6ED; Fri, 9 May 2025 14:11:37 +0200 (CEST) From: Christoph Heiss <c.heiss@proxmox.com> To: pve-devel@lists.proxmox.com Date: Fri, 9 May 2025 14:09:14 +0200 Message-ID: <20250509121007.1430080-3-c.heiss@proxmox.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250509121007.1430080-1-c.heiss@proxmox.com> References: <20250509121007.1430080-1-c.heiss@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.030 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [PATCH installer 2/5] chroot: replace clap with pico-args for command argument parsing X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion <pve-devel.lists.proxmox.com> List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pve-devel>, <mailto:pve-devel-request@lists.proxmox.com?subject=unsubscribe> List-Archive: <http://lists.proxmox.com/pipermail/pve-devel/> List-Post: <mailto:pve-devel@lists.proxmox.com> List-Help: <mailto:pve-devel-request@lists.proxmox.com?subject=help> List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel>, <mailto:pve-devel-request@lists.proxmox.com?subject=subscribe> Reply-To: Proxmox VE development discussion <pve-devel@lists.proxmox.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" <pve-devel-bounces@lists.proxmox.com> Drops clap for pico-args, as the former is pretty heavy-weight and completely overhauls its API with ~every major release. As for the resulting binary, there is a ~18% decrease in binary size, from 2.9M to 2.4MiB (after stripping). text data bss dec hex filename 2743595 274576 424 3018595 2e0f63 proxmox-chroot-clap 2208631 261224 408 2470263 25b177 proxmox-chroot-pico Further, the dependency tree goes from 40 to 29 dependencies, as well as the package compile time on my machine (on hot caches), roughly measured with `cargo clean && time cargo build --package proxmox-chroot --release` from ~20s to ~16s, so a nice little change w.r.t. to that aspect too. No functional changes intended. Signed-off-by: Christoph Heiss <c.heiss@proxmox.com> --- proxmox-chroot/Cargo.toml | 6 +- proxmox-chroot/src/main.rs | 159 +++++++++++++++++++++++++++---------- 2 files changed, 120 insertions(+), 45 deletions(-) diff --git a/proxmox-chroot/Cargo.toml b/proxmox-chroot/Cargo.toml index 7a1390a..c043938 100644 --- a/proxmox-chroot/Cargo.toml +++ b/proxmox-chroot/Cargo.toml @@ -9,8 +9,6 @@ homepage = "https://www.proxmox.com" [dependencies] anyhow.workspace = true -proxmox-installer-common.workspace = true -serde_json.workspace = true regex.workspace = true - -clap = { version = "4.0", features = ["derive"] } +proxmox-installer-common = { workspace = true, features = [ "cli" ] } +serde_json.workspace = true diff --git a/proxmox-chroot/src/main.rs b/proxmox-chroot/src/main.rs index ef339ed..037f079 100644 --- a/proxmox-chroot/src/main.rs +++ b/proxmox-chroot/src/main.rs @@ -1,13 +1,20 @@ +//! Tool to help prepare a successfully installed target environment for +//! chroot'ing. +//! +//! Can also be used in the rescue environment for mounting an existing system. + +#![forbid(unsafe_code)] + use std::{ - fs, io, + env, fs, io, path::{self, Path, PathBuf}, - process::Command, + process::{self, Command}, + str::FromStr, }; use anyhow::{Result, bail}; -use clap::{Args, Parser, Subcommand, ValueEnum}; use proxmox_installer_common::{ - RUNTIME_DIR, + RUNTIME_DIR, cli, options::FsType, setup::{InstallConfig, SetupInfo}, }; @@ -18,46 +25,87 @@ static BINDMOUNTS: [&str; 4] = ["dev", "proc", "run", "sys"]; const TARGET_DIR: &str = "/target"; const ZPOOL_NAME: &str = "rpool"; -/// Helper tool to prepare everything to `chroot` into an installation -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -struct Cli { - #[command(subcommand)] - command: Commands, -} - -#[derive(Subcommand, Debug)] -enum Commands { - Prepare(CommandPrepare), - Cleanup(CommandCleanup), -} - -/// Mount the root file system and bind mounts in preparation to chroot into the installation -#[derive(Args, Debug)] -struct CommandPrepare { +/// Arguments for the `prepare` command. +struct CommandPrepareArgs { /// Filesystem used for the installation. Will try to automatically detect it after a /// successful installation. - #[arg(short, long, value_enum)] filesystem: Option<Filesystems>, - /// Numerical ID of `rpool` ZFS pool to import. Needed if multiple pools of name `rpool` are present. - #[arg(long)] + /// Numerical ID of the `rpool` ZFS pool to import. Needed if multiple pools of name `rpool` + /// are present. rpool_id: Option<u64>, /// UUID of the BTRFS file system to mount. Needed if multiple BTRFS file systems are present. - #[arg(long)] btrfs_uuid: Option<String>, } -/// Unmount everything. Use once done with chroot. -#[derive(Args, Debug)] -struct CommandCleanup { +impl cli::Subcommand for CommandPrepareArgs { + fn parse(args: &mut cli::Arguments) -> Result<Self> { + Ok(Self { + filesystem: args.opt_value_from_str(["-f", "--filesystem"])?, + rpool_id: args.opt_value_from_fn("--rpool-id", str::parse)?, + btrfs_uuid: args.opt_value_from_str("--btrfs-uuid")?, + }) + } + + fn print_usage() { + eprintln!( + r#"Mount the root file system and bind mounts in preparation to chroot into the installation. + +USAGE: + {} prepare <OPTIONS> + +OPTIONS: + -f, --filesystem <FILESYSTEM> Filesystem used for the installation. Will try to automatically detect it after a successful installation. [possible values: zfs, ext4, xfs, btrfs] + --rpool-id <RPOOL_ID> Numerical ID of `rpool` ZFS pool to import. Needed if multiple pools of name `rpool` are present. + --btrfs-uuid <BTRFS_UUID> UUID of the BTRFS file system to mount. Needed if multiple BTRFS file systems are present. + -h, --help Print this help + -V, --version Print version +"#, + env!("CARGO_PKG_NAME") + ); + } + + fn run(&self) -> Result<()> { + prepare(self) + } +} + +/// Arguments for the `cleanup` command. +struct CommandCleanupArgs { /// Filesystem used for the installation. Will try to automatically detect it by default. - #[arg(short, long, value_enum)] filesystem: Option<Filesystems>, } -#[derive(Copy, Clone, Debug, ValueEnum)] +impl cli::Subcommand for CommandCleanupArgs { + fn parse(args: &mut cli::Arguments) -> Result<Self> { + Ok(Self { + filesystem: args.opt_value_from_str(["-f", "--filesystem"])?, + }) + } + + fn print_usage() { + eprintln!( + r#"Unmount everything. Use once done with the chroot. + +USAGE: + {} cleanup <OPTIONS> + +OPTIONS: + -f, --filesystem <FILESYSTEM> Filesystem used for the installation. Will try to automatically detect it after a successful installation. [possible values: zfs, ext4, xfs, btrfs] + -h, --help Print this help + -V, --version Print version +"#, + env!("CARGO_PKG_NAME") + ); + } + + fn run(&self) -> Result<()> { + cleanup(self) + } +} + +#[derive(Copy, Clone, Debug)] enum Filesystems { Zfs, Ext4, @@ -76,19 +124,48 @@ impl From<FsType> for Filesystems { } } -fn main() { - let args = Cli::parse(); - let res = match &args.command { - Commands::Prepare(args) => prepare(args), - Commands::Cleanup(args) => cleanup(args), - }; - if let Err(err) = res { - eprintln!("{err:#}"); - std::process::exit(1); +impl FromStr for Filesystems { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result<Self> { + match s { + "ext4" => Ok(Filesystems::Ext4), + "xfs" => Ok(Filesystems::Xfs), + _ if s.starts_with("zfs") => Ok(Filesystems::Zfs), + _ if s.starts_with("btrfs") => Ok(Filesystems::Btrfs), + _ => bail!("unknown filesystem"), + } } } -fn prepare(args: &CommandPrepare) -> Result<()> { +fn main() -> process::ExitCode { + cli::run(cli::AppInfo { + global_help: &format!( + r#"Helper tool to set up a `chroot` into an existing installation. + +USAGE: + {} <COMMAND> <OPTIONS> + +COMMANDS: + prepare Mount the root file system and bind mounts in preparation to chroot into the installation. + cleanup Unmount everything. Use once done with the chroot. + +GLOBAL OPTIONS: + -h, --help Print help + -V, --version Print version information +"#, + env!("CARGO_PKG_NAME") + ), + on_command: |s, args| match s { + Some("prepare") => cli::handle_command::<CommandPrepareArgs>(args), + Some("cleanup") => cli::handle_command::<CommandCleanupArgs>(args), + Some(s) => bail!("unknown subcommand '{s}'"), + None => bail!("subcommand required"), + }, + }) +} + +fn prepare(args: &CommandPrepareArgs) -> Result<()> { let fs = get_fs(args.filesystem)?; fs::create_dir_all(TARGET_DIR)?; @@ -108,7 +185,7 @@ fn prepare(args: &CommandPrepare) -> Result<()> { Ok(()) } -fn cleanup(args: &CommandCleanup) -> Result<()> { +fn cleanup(args: &CommandCleanupArgs) -> Result<()> { let fs = get_fs(args.filesystem)?; if let Err(e) = bind_umount() { -- 2.49.0 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel