citadel-realmfs moved into citadel-tool binary
This commit is contained in:
parent
a984632123
commit
ce10df3dfc
244
citadel-tool/src/realmfs/mod.rs
Normal file
244
citadel-tool/src/realmfs/mod.rs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
use clap::App;
|
||||||
|
use clap::ArgMatches;
|
||||||
|
|
||||||
|
use libcitadel::{Result,RealmFS,Logger,LogLevel};
|
||||||
|
use clap::SubCommand;
|
||||||
|
use clap::AppSettings::*;
|
||||||
|
use clap::Arg;
|
||||||
|
use libcitadel::ResizeSize;
|
||||||
|
use libcitadel::format_error;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
pub fn main(args: Vec<String>) {
|
||||||
|
|
||||||
|
Logger::set_log_level(LogLevel::Debug);
|
||||||
|
|
||||||
|
let app = App::new("citadel-realmfs")
|
||||||
|
.about("Citadel realmfs image tool")
|
||||||
|
.settings(&[ArgRequiredElseHelp,ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder,SubcommandsNegateReqs])
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("resize")
|
||||||
|
.about("Resize an existing RealmFS image. If the image is currently sealed, it will also be unsealed.")
|
||||||
|
.arg(Arg::with_name("image")
|
||||||
|
.help("Path or name of RealmFS image to resize")
|
||||||
|
.required(true))
|
||||||
|
.arg(Arg::with_name("size")
|
||||||
|
.help("Size to increase RealmFS image to (or by if prefixed with '+')")
|
||||||
|
.long_help("\
|
||||||
|
The size can be followed by a 'g' or 'm' character \
|
||||||
|
to indicate a quantity of gigabytes or megabytes. If no size unit \
|
||||||
|
is provided the size is measured in blocks (of 4096 bytes). \
|
||||||
|
\
|
||||||
|
If the size is prefixed with a '+' character it is understood \
|
||||||
|
as a quantity to increase the current size by. Otherwise the size \
|
||||||
|
is the final absolute size of the image.")
|
||||||
|
.required(true)))
|
||||||
|
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("fork")
|
||||||
|
.about("Create a new RealmFS image as an unsealed copy of an existing image")
|
||||||
|
.arg(Arg::with_name("image")
|
||||||
|
.help("Path or name of RealmFS image to fork")
|
||||||
|
.required(true))
|
||||||
|
|
||||||
|
.arg(Arg::with_name("forkname")
|
||||||
|
.help("Name of new image to create")
|
||||||
|
.required(true)))
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("seal")
|
||||||
|
.about("Seal an unsealed RealmFS image")
|
||||||
|
.arg(Arg::with_name("image")
|
||||||
|
.help("Path or name of RealmFS image to seal")
|
||||||
|
.required(true)))
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("autoresize")
|
||||||
|
.about("Increase size of RealmFS image if not enough free space remains")
|
||||||
|
.arg(Arg::with_name("image")
|
||||||
|
.help("Path or name of RealmFS image")
|
||||||
|
.required(true)))
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("update")
|
||||||
|
.about("Open an update shell on the image")
|
||||||
|
.arg(Arg::with_name("image")
|
||||||
|
.help("Path or name of RealmFS image")
|
||||||
|
.required(true)))
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("activate")
|
||||||
|
.about("Activate a RealmFS by creating a block device for the image and mounting it.")
|
||||||
|
.arg(Arg::with_name("image")
|
||||||
|
.help("Path or name of RealmFS image to activate")
|
||||||
|
.required(true)))
|
||||||
|
|
||||||
|
.subcommand(SubCommand::with_name("deactivate")
|
||||||
|
.about("Deactivate a RealmFS by unmounting it and removing block device created during activation.")
|
||||||
|
.arg(Arg::with_name("image")
|
||||||
|
.help("Path or name of RealmFS image to deactivate")
|
||||||
|
.required(true)))
|
||||||
|
|
||||||
|
|
||||||
|
.arg(Arg::with_name("image")
|
||||||
|
.help("Name of or path to RealmFS image to display information about")
|
||||||
|
.required(true));
|
||||||
|
|
||||||
|
let matches = app.get_matches_from(args);
|
||||||
|
let result = match matches.subcommand() {
|
||||||
|
("resize", Some(m)) => resize(m),
|
||||||
|
("autoresize", Some(m)) => autoresize(m),
|
||||||
|
("fork", Some(m)) => fork(m),
|
||||||
|
("seal", Some(m)) => seal(m),
|
||||||
|
("update", Some(m)) => update(m),
|
||||||
|
("activate", Some(m)) => activate(m),
|
||||||
|
("deactivate", Some(m)) => deactivate(m),
|
||||||
|
_ => image_info(&matches),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(ref e) = result {
|
||||||
|
eprintln!("Error: {}", format_error(e));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn realmfs_image(arg_matches: &ArgMatches) -> Result<RealmFS> {
|
||||||
|
let image = match arg_matches.value_of("image") {
|
||||||
|
Some(s) => s,
|
||||||
|
None => bail!("Image argument required."),
|
||||||
|
};
|
||||||
|
|
||||||
|
let realmfs = if RealmFS::is_valid_name(image) {
|
||||||
|
RealmFS::load_by_name(image)?
|
||||||
|
} else if RealmFS::is_valid_realmfs_image(image) {
|
||||||
|
RealmFS::load_from_path(image)?
|
||||||
|
} else {
|
||||||
|
bail!("Not a valid realmfs name or path to realmfs image file: {}", image);
|
||||||
|
};
|
||||||
|
Ok(realmfs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn image_info(arg_matches: &ArgMatches) -> Result<()> {
|
||||||
|
let img = realmfs_image(arg_matches)?;
|
||||||
|
print!("{}", String::from_utf8(img.header().metainfo_bytes())?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_resize_size(s: &str) -> Result<ResizeSize> {
|
||||||
|
let unit = s.chars().last().filter(|c| c.is_alphabetic());
|
||||||
|
|
||||||
|
let skip = if s.starts_with("+") { 1 } else { 0 };
|
||||||
|
let size = s.chars()
|
||||||
|
.skip(skip)
|
||||||
|
.take_while(|c| c.is_numeric())
|
||||||
|
.collect::<String>()
|
||||||
|
.parse::<usize>()
|
||||||
|
.map_err(|_| format_err!("Unable to parse size value '{}'",s))?;
|
||||||
|
|
||||||
|
match unit {
|
||||||
|
Some('g') | Some('G') => Ok(ResizeSize::gigs(size)),
|
||||||
|
Some('m') | Some('M') => Ok(ResizeSize::megs(size)),
|
||||||
|
Some(c) => Err(format_err!("Unknown size unit '{}'", c)),
|
||||||
|
None => Ok(ResizeSize::blocks(size)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(arg_matches: &ArgMatches) -> Result<()> {
|
||||||
|
let img = realmfs_image(arg_matches)?;
|
||||||
|
info!("image is {}", img.path().display());
|
||||||
|
let size_arg = match arg_matches.value_of("size") {
|
||||||
|
Some(size) => size,
|
||||||
|
None => "No size argument",
|
||||||
|
|
||||||
|
};
|
||||||
|
info!("Size is {}", size_arg);
|
||||||
|
let mode_add = size_arg.starts_with("+");
|
||||||
|
let size = parse_resize_size(size_arg)?;
|
||||||
|
|
||||||
|
if mode_add {
|
||||||
|
img.resize_grow_by(size)
|
||||||
|
} else {
|
||||||
|
img.resize_grow_to(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn autoresize(arg_matches: &ArgMatches) -> Result<()> {
|
||||||
|
let img = realmfs_image(arg_matches)?;
|
||||||
|
|
||||||
|
if let Some(size) = img.auto_resize_size() {
|
||||||
|
img.resize_grow_to(size)
|
||||||
|
} else {
|
||||||
|
info!("RealmFS image {} has sufficient free space, doing nothing", img.path().display());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fork(arg_matches: &ArgMatches) -> Result<()> {
|
||||||
|
let img = realmfs_image(arg_matches)?;
|
||||||
|
let forkname = match arg_matches.value_of("forkname") {
|
||||||
|
Some(name) => name,
|
||||||
|
None => bail!("No fork name argument"),
|
||||||
|
};
|
||||||
|
if !RealmFS::is_valid_name(forkname) {
|
||||||
|
bail!("Not a valid RealmFS image name '{}'", forkname);
|
||||||
|
}
|
||||||
|
if RealmFS::named_image_exists(forkname) {
|
||||||
|
bail!("A RealmFS image named '{}' already exists", forkname);
|
||||||
|
}
|
||||||
|
img.fork(forkname)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seal(arg_matches: &ArgMatches) -> Result<()> {
|
||||||
|
let img = realmfs_image(arg_matches)?;
|
||||||
|
let img_arg = arg_matches.value_of("image").unwrap();
|
||||||
|
|
||||||
|
if img.is_sealed() {
|
||||||
|
info!("RealmFS image {} is already sealed", img_arg);
|
||||||
|
} else if img.is_activated() {
|
||||||
|
info!("RealmFS image {} cannot be sealed because it is currently activated", img_arg);
|
||||||
|
} else {
|
||||||
|
img.seal(None)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(arg_matches: &ArgMatches) -> Result<()> {
|
||||||
|
let img = realmfs_image(arg_matches)?;
|
||||||
|
let mut update = img.update();
|
||||||
|
update.setup()?;
|
||||||
|
update.open_update_shell()?;
|
||||||
|
update.apply_update()?;
|
||||||
|
update.cleanup()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn activate(arg_matches: &ArgMatches) -> Result<()> {
|
||||||
|
let img = realmfs_image(arg_matches)?;
|
||||||
|
let img_arg = arg_matches.value_of("image").unwrap();
|
||||||
|
|
||||||
|
let activation = if let Some(activation) = img.activation() {
|
||||||
|
info!("RealmFS image {} is already activated", img_arg);
|
||||||
|
activation
|
||||||
|
} else {
|
||||||
|
info!("Activating {}", img_arg);
|
||||||
|
img.activate()?
|
||||||
|
};
|
||||||
|
info!("Read-Only mountpoint: {}", activation.mountpoint());
|
||||||
|
if let Some(rw) = activation.mountpoint_rw() {
|
||||||
|
info!("Read-Write mountpoint: {}", rw);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deactivate(arg_matches: &ArgMatches) -> Result<()> {
|
||||||
|
let img = realmfs_image(arg_matches)?;
|
||||||
|
let img_arg = arg_matches.value_of("image").unwrap();
|
||||||
|
if !img.is_activated() {
|
||||||
|
info!("RealmFS image {} is not activated", img_arg);
|
||||||
|
} else if img.is_in_use() {
|
||||||
|
info!("Cannot deactivate RealmFS image {} because it is currently in use", img_arg);
|
||||||
|
} else {
|
||||||
|
info!("Deactivating {}", img_arg);
|
||||||
|
img.deactivate()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user