1
0
forked from brl/citadel-tools

Upgrade clap, rpassword and pwhash to prepare for new code using them

This commit is contained in:
isa 2024-08-30 13:11:47 -04:00
parent b7e6ee3b3c
commit 2a16bd4c41
7 changed files with 3562 additions and 907 deletions

1550
Cargo.lock generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,14 +8,14 @@ homepage = "https://subgraph.com"
[dependencies] [dependencies]
libcitadel = { path = "../libcitadel" } libcitadel = { path = "../libcitadel" }
rpassword = "4.0" rpassword = "7.3"
clap = "2.33" clap = { version = "4.5", features = ["cargo", "derive"] }
lazy_static = "1.4" lazy_static = "1.4"
serde_derive = "1.0" serde_derive = "1.0"
serde = "1.0" serde = "1.0"
toml = "0.5" toml = "0.8"
hex = "0.4" hex = "0.4"
byteorder = "1" byteorder = "1"
dbus = "0.8.4" dbus = "0.8.4"
pwhash = "0.3.1" pwhash = "1.0"
tempfile = "3" tempfile = "3"

View File

@ -1,89 +1,97 @@
use std::path::Path; use std::path::Path;
use std::process::exit; use std::process::exit;
use clap::{Arg,ArgMatches};
use clap::{App,Arg,SubCommand,ArgMatches}; use clap::{command, ArgAction, Command};
use clap::AppSettings::*;
use libcitadel::{Result, ResourceImage, Logger, LogLevel, Partition, KeyPair, ImageHeader, util};
use hex; use hex;
pub fn main(args: Vec<String>) { use libcitadel::{Result, ResourceImage, Logger, LogLevel, Partition, KeyPair, ImageHeader, util};
let app = App::new("citadel-image") pub fn main() {
let matches = command!()
.about("Citadel update image builder") .about("Citadel update image builder")
.settings(&[ArgRequiredElseHelp,ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder]) .arg_required_else_help(true)
.disable_help_subcommand(true)
.subcommand(SubCommand::with_name("metainfo") .subcommand(
Command::new("metainfo")
.about("Display metainfo variables for an image file") .about("Display metainfo variables for an image file")
.arg(Arg::with_name("path") .arg(Arg::new("path").required(true).help("Path to image file")),
.required(true) )
.help("Path to image file"))) .subcommand(
Command::new("info")
.subcommand(SubCommand::with_name("info")
.about("Display metainfo variables for an image file") .about("Display metainfo variables for an image file")
.arg(Arg::with_name("path") .arg(Arg::new("path").required(true).help("Path to image file")),
.required(true) )
.help("Path to image file"))) .subcommand(
Command::new("generate-verity")
.subcommand(SubCommand::with_name("generate-verity")
.about("Generate dm-verity hash tree for an image file") .about("Generate dm-verity hash tree for an image file")
.arg(Arg::with_name("path") .arg(Arg::new("path").required(true).help("Path to image file")),
.required(true) )
.help("Path to image file"))) .subcommand(
Command::new("verify")
.subcommand(SubCommand::with_name("verify")
.about("Verify dm-verity hash tree for an image file") .about("Verify dm-verity hash tree for an image file")
.arg(Arg::with_name("path") .arg(
Arg::new("option")
.long("option")
.required(true) .required(true)
.help("Path to image file"))) .help("Path to image file"),
),
.subcommand(SubCommand::with_name("install-rootfs") )
.subcommand(
Command::new("install-rootfs")
.about("Install rootfs image file to a partition") .about("Install rootfs image file to a partition")
.arg(Arg::with_name("choose") .arg(
Arg::new("choose")
.long("just-choose") .long("just-choose")
.help("Don't install anything, just show which partition would be chosen")) .action(ArgAction::SetTrue)
.arg(Arg::with_name("skip-sha") .help("Don't install anything, just show which partition would be chosen"),
)
.arg(
Arg::new("skip-sha")
.long("skip-sha") .long("skip-sha")
.help("Skip verification of header sha256 value")) .action(ArgAction::SetTrue)
.arg(Arg::with_name("no-prefer") .help("Skip verification of header sha256 value"),
)
.arg(
Arg::new("no-prefer")
.long("no-prefer") .long("no-prefer")
.help("Don't set PREFER_BOOT flag")) .action(ArgAction::SetTrue)
.arg(Arg::with_name("path") .help("Don't set PREFER_BOOT flag"),
.required_unless("choose") )
.help("Path to image file"))) .arg(
Arg::new("path")
.subcommand(SubCommand::with_name("genkeys") .required_unless_present("choose")
.about("Generate a pair of keys")) .help("Path to image file"),
),
.subcommand(SubCommand::with_name("decompress") )
.subcommand(Command::new("genkeys").about("Generate a pair of keys"))
.subcommand(
Command::new("decompress")
.about("Decompress a compressed image file") .about("Decompress a compressed image file")
.arg(Arg::with_name("path") .arg(Arg::new("path").required(true).help("Path to image file")),
.required(true) )
.help("Path to image file"))) .subcommand(
Command::new("bless")
.subcommand(SubCommand::with_name("bless") .about("Mark currently mounted rootfs partition as successfully booted"),
.about("Mark currently mounted rootfs partition as successfully booted")) )
.subcommand(
.subcommand(SubCommand::with_name("verify-shasum") Command::new("verify-shasum")
.about("Verify the sha256 sum of the image") .about("Verify the sha256 sum of the image")
.arg(Arg::with_name("path") .arg(Arg::new("path").required(true).help("Path to image file")),
.required(true) )
.help("Path to image file"))); .get_matches();
Logger::set_log_level(LogLevel::Debug);
let matches = app.get_matches_from(args);
let result = match matches.subcommand() { let result = match matches.subcommand() {
("metainfo", Some(m)) => metainfo(m), Some(("metainfo", sub_m)) => metainfo(sub_m),
("info", Some(m)) => info(m), Some(("info", sub_m)) => info(sub_m),
("generate-verity", Some(m)) => generate_verity(m), Some(("generate-verity", sub_m)) => generate_verity(sub_m),
("verify", Some(m)) => verify(m), Some(("verify", sub_m)) => verify(sub_m),
("sign-image", Some(m)) => sign_image(m), Some(("sign-image", sub_m)) => sign_image(sub_m),
("genkeys", Some(_)) => genkeys(), Some(("genkeys", _)) => genkeys(),
("decompress", Some(m)) => decompress(m), Some(("decompress", sub_m)) => decompress(sub_m),
("verify-shasum", Some(m)) => verify_shasum(m), Some(("verify-shasum", sub_m)) => verify_shasum(sub_m),
("install-rootfs", Some(m)) => install_rootfs(m), Some(("install-rootfs", sub_m)) => install_rootfs(sub_m),
("install", Some(m)) => install_image(m), Some(("install", sub_m)) => install_image(sub_m),
("bless", Some(_)) => bless(), Some(("bless", _)) => bless(),
_ => Ok(()), _ => Ok(()),
}; };
@ -159,7 +167,9 @@ fn verify_shasum(arg_matches: &ArgMatches) -> Result<()> {
} }
fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage> { fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage> {
let path = arg_matches.value_of("path").expect("path argument missing"); let path = arg_matches.get_one::<String>("path")
.expect("path argument missing");
if !Path::new(path).exists() { if !Path::new(path).exists() {
bail!("Cannot load image {}: File does not exist", path); bail!("Cannot load image {}: File does not exist", path);
} }
@ -171,14 +181,14 @@ fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage> {
} }
fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> { fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> {
if arg_matches.is_present("choose") { if arg_matches.get_flag("choose") {
let _ = choose_install_partition(true)?; let _ = choose_install_partition(true)?;
return Ok(()) return Ok(())
} }
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
if !arg_matches.is_present("skip-sha") { if !arg_matches.get_flag("skip-sha") {
info!("Verifying sha256 hash of image"); info!("Verifying sha256 hash of image");
let shasum = img.generate_shasum()?; let shasum = img.generate_shasum()?;
if shasum != img.metainfo().shasum() { if shasum != img.metainfo().shasum() {
@ -188,7 +198,7 @@ fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> {
let partition = choose_install_partition(true)?; let partition = choose_install_partition(true)?;
if !arg_matches.is_present("no-prefer") { if !arg_matches.get_flag("no-prefer") {
clear_prefer_boot()?; clear_prefer_boot()?;
img.header().set_flag(ImageHeader::FLAG_PREFER_BOOT); img.header().set_flag(ImageHeader::FLAG_PREFER_BOOT);
} }
@ -212,7 +222,9 @@ fn sign_image(arg_matches: &ArgMatches) -> Result<()> {
} }
fn install_image(arg_matches: &ArgMatches) -> Result<()> { fn install_image(arg_matches: &ArgMatches) -> Result<()> {
let source = arg_matches.value_of("path").expect("path argument missing"); let source = arg_matches.get_one::<String>("path")
.expect("path argument missing");
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
let _hdr = img.header(); let _hdr = img.header();
let metainfo = img.metainfo(); let metainfo = img.metainfo();

View File

@ -125,7 +125,7 @@ fn read_passphrase(prompt: &str) -> io::Result<Option<String>> {
loop { loop {
println!("{}", prompt); println!("{}", prompt);
println!(); println!();
let passphrase = rpassword::read_password_from_tty(Some(" Passphrase : "))?; let passphrase = rpassword::prompt_password(" Passphrase : ")?;
if passphrase.is_empty() { if passphrase.is_empty() {
println!("Passphrase cannot be empty"); println!("Passphrase cannot be empty");
continue; continue;
@ -133,7 +133,7 @@ fn read_passphrase(prompt: &str) -> io::Result<Option<String>> {
if passphrase == "q" || passphrase == "Q" { if passphrase == "q" || passphrase == "Q" {
return Ok(None); return Ok(None);
} }
let confirm = rpassword::read_password_from_tty(Some(" Confirm : "))?; let confirm = rpassword::prompt_password(" Confirm : ")?;
if confirm == "q" || confirm == "Q" { if confirm == "q" || confirm == "Q" {
return Ok(None); return Ok(None);
} }

View File

@ -34,9 +34,9 @@ fn main() {
} else if exe == Path::new("/usr/libexec/citadel-install-backend") { } else if exe == Path::new("/usr/libexec/citadel-install-backend") {
install_backend::main(); install_backend::main();
} else if exe == Path::new("/usr/bin/citadel-image") { } else if exe == Path::new("/usr/bin/citadel-image") {
image::main(args); image::main();
} else if exe == Path::new("/usr/bin/citadel-realmfs") { } else if exe == Path::new("/usr/bin/citadel-realmfs") {
realmfs::main(args); realmfs::main();
} else if exe == Path::new("/usr/bin/citadel-update") { } else if exe == Path::new("/usr/bin/citadel-update") {
update::main(args); update::main(args);
} else if exe == Path::new("/usr/libexec/citadel-desktop-sync") { } else if exe == Path::new("/usr/libexec/citadel-desktop-sync") {
@ -57,8 +57,8 @@ fn dispatch_command(args: Vec<String>) {
match command.as_str() { match command.as_str() {
"boot" => boot::main(rebuild_args("citadel-boot", args)), "boot" => boot::main(rebuild_args("citadel-boot", args)),
"install" => install::main(rebuild_args("citadel-install", args)), "install" => install::main(rebuild_args("citadel-install", args)),
"image" => image::main(rebuild_args("citadel-image", args)), "image" => image::main(),
"realmfs" => realmfs::main(rebuild_args("citadel-realmfs", args)), "realmfs" => realmfs::main(),
"update" => update::main(rebuild_args("citadel-update", args)), "update" => update::main(rebuild_args("citadel-update", args)),
"mkimage" => mkimage::main(rebuild_args("citadel-mkimage", args)), "mkimage" => mkimage::main(rebuild_args("citadel-mkimage", args)),
"sync" => sync::main(rebuild_args("citadel-desktop-sync", args)), "sync" => sync::main(rebuild_args("citadel-desktop-sync", args)),

View File

@ -1,28 +1,24 @@
use clap::App; use clap::{command, Command};
use clap::ArgMatches; use clap::{Arg, ArgMatches};
use libcitadel::{Result,RealmFS,Logger,LogLevel}; use libcitadel::{Result,RealmFS,Logger,LogLevel};
use libcitadel::util::is_euid_root; use libcitadel::util::is_euid_root;
use clap::SubCommand;
use clap::AppSettings::*;
use clap::Arg;
use libcitadel::ResizeSize; use libcitadel::ResizeSize;
use std::process::exit; use std::process::exit;
pub fn main(args: Vec<String>) { pub fn main() {
Logger::set_log_level(LogLevel::Debug); Logger::set_log_level(LogLevel::Debug);
let app = App::new("citadel-realmfs") let matches = command!()
.about("Citadel realmfs image tool") .about("citadel-realmfs")
.settings(&[ArgRequiredElseHelp,ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder,SubcommandsNegateReqs]) .arg_required_else_help(true)
.subcommand(Command::new("resize")
.subcommand(SubCommand::with_name("resize")
.about("Resize an existing RealmFS image. If the image is currently sealed, it will also be unsealed.") .about("Resize an existing RealmFS image. If the image is currently sealed, it will also be unsealed.")
.arg(Arg::with_name("image") .arg(Arg::new("image")
.help("Path or name of RealmFS image to resize") .help("Path or name of RealmFS image to resize")
.required(true)) .required(true))
.arg(Arg::with_name("size") .arg(Arg::new("size")
.help("Size to increase RealmFS image to (or by if prefixed with '+')") .help("Size to increase RealmFS image to (or by if prefixed with '+')")
.long_help("\ .long_help("\
The size can be followed by a 'g' or 'm' character \ The size can be followed by a 'g' or 'm' character \
@ -35,53 +31,53 @@ is the final absolute size of the image.")
.required(true))) .required(true)))
.subcommand(SubCommand::with_name("fork") .subcommand(Command::new("fork")
.about("Create a new RealmFS image as an unsealed copy of an existing image") .about("Create a new RealmFS image as an unsealed copy of an existing image")
.arg(Arg::with_name("image") .arg(Arg::new("image")
.help("Path or name of RealmFS image to fork") .help("Path or name of RealmFS image to fork")
.required(true)) .required(true))
.arg(Arg::with_name("forkname") .arg(Arg::new("forkname")
.help("Name of new image to create") .help("Name of new image to create")
.required(true))) .required(true)))
.subcommand(SubCommand::with_name("autoresize") .subcommand(Command::new("autoresize")
.about("Increase size of RealmFS image if not enough free space remains") .about("Increase size of RealmFS image if not enough free space remains")
.arg(Arg::with_name("image") .arg(Arg::new("image")
.help("Path or name of RealmFS image") .help("Path or name of RealmFS image")
.required(true))) .required(true)))
.subcommand(SubCommand::with_name("update") .subcommand(Command::new("update")
.about("Open an update shell on the image") .about("Open an update shell on the image")
.arg(Arg::with_name("image") .arg(Arg::new("image")
.help("Path or name of RealmFS image") .help("Path or name of RealmFS image")
.required(true))) .required(true)))
.subcommand(SubCommand::with_name("activate") .subcommand(Command::new("activate")
.about("Activate a RealmFS by creating a block device for the image and mounting it.") .about("Activate a RealmFS by creating a block device for the image and mounting it.")
.arg(Arg::with_name("image") .arg(Arg::new("image")
.help("Path or name of RealmFS image to activate") .help("Path or name of RealmFS image to activate")
.required(true))) .required(true)))
.subcommand(SubCommand::with_name("deactivate") .subcommand(Command::new("deactivate")
.about("Deactivate a RealmFS by unmounting it and removing block device created during activation.") .about("Deactivate a RealmFS by unmounting it and removing block device created during activation.")
.arg(Arg::with_name("image") .arg(Arg::new("image")
.help("Path or name of RealmFS image to deactivate") .help("Path or name of RealmFS image to deactivate")
.required(true))) .required(true)))
.arg(Arg::with_name("image") .arg(Arg::new("image")
.help("Name of or path to RealmFS image to display information about") .help("Name of or path to RealmFS image to display information about")
.required(true)); .required(true))
.get_matches();
let matches = app.get_matches_from(args);
let result = match matches.subcommand() { let result = match matches.subcommand() {
("resize", Some(m)) => resize(m), Some(("resize", m)) => resize(m),
("autoresize", Some(m)) => autoresize(m), Some(("autoresize", m)) => autoresize(m),
("fork", Some(m)) => fork(m), Some(("fork", m)) => fork(m),
("update", Some(m)) => update(m), Some(("update", m)) => update(m),
("activate", Some(m)) => activate(m), Some(("activate", m)) => activate(m),
("deactivate", Some(m)) => deactivate(m), Some(("deactivate", m)) => deactivate(m),
_ => image_info(&matches), _ => image_info(&matches),
}; };
@ -92,7 +88,7 @@ is the final absolute size of the image.")
} }
fn realmfs_image(arg_matches: &ArgMatches) -> Result<RealmFS> { fn realmfs_image(arg_matches: &ArgMatches) -> Result<RealmFS> {
let image = match arg_matches.value_of("image") { let image = match arg_matches.get_one::<String>("image") {
Some(s) => s, Some(s) => s,
None => bail!("Image argument required."), None => bail!("Image argument required."),
}; };
@ -136,10 +132,9 @@ fn parse_resize_size(s: &str) -> Result<ResizeSize> {
fn resize(arg_matches: &ArgMatches) -> Result<()> { fn resize(arg_matches: &ArgMatches) -> Result<()> {
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
info!("image is {}", img.path().display()); info!("image is {}", img.path().display());
let size_arg = match arg_matches.value_of("size") { let size_arg = match arg_matches.get_one::<String>("size") {
Some(size) => size, Some(size) => size,
None => "No size argument", None => "No size argument",
}; };
info!("Size is {}", size_arg); info!("Size is {}", size_arg);
let mode_add = size_arg.starts_with('+'); let mode_add = size_arg.starts_with('+');
@ -165,7 +160,7 @@ fn autoresize(arg_matches: &ArgMatches) -> Result<()> {
fn fork(arg_matches: &ArgMatches) -> Result<()> { fn fork(arg_matches: &ArgMatches) -> Result<()> {
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
let forkname = match arg_matches.value_of("forkname") { let forkname = match arg_matches.get_one::<String>("forkname") {
Some(name) => name, Some(name) => name,
None => bail!("No fork name argument"), None => bail!("No fork name argument"),
}; };
@ -190,7 +185,7 @@ fn update(arg_matches: &ArgMatches) -> Result<()> {
fn activate(arg_matches: &ArgMatches) -> Result<()> { fn activate(arg_matches: &ArgMatches) -> Result<()> {
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
let img_arg = arg_matches.value_of("image").unwrap(); let img_arg = arg_matches.get_one::<String>("image").unwrap();
if img.is_activated() { if img.is_activated() {
info!("RealmFS image {} is already activated", img_arg); info!("RealmFS image {} is already activated", img_arg);
@ -203,7 +198,7 @@ fn activate(arg_matches: &ArgMatches) -> Result<()> {
fn deactivate(arg_matches: &ArgMatches) -> Result<()> { fn deactivate(arg_matches: &ArgMatches) -> Result<()> {
let img = realmfs_image(arg_matches)?; let img = realmfs_image(arg_matches)?;
let img_arg = arg_matches.value_of("image").unwrap(); let img_arg = arg_matches.get_one::<String>("image").unwrap();
if !img.is_activated() { if !img.is_activated() {
info!("RealmFS image {} is not activated", img_arg); info!("RealmFS image {} is not activated", img_arg);
} else if img.is_in_use() { } else if img.is_in_use() {