1
0
forked from brl/citadel-tools

Improve error handling by using std lib rust code

This commit is contained in:
isa 2024-08-29 14:54:31 -04:00
parent b7e6ee3b3c
commit 04457a47ef
15 changed files with 372 additions and 317 deletions

View File

@ -4,7 +4,7 @@ use std::fs;
use std::thread::{self,JoinHandle};
use std::time::{self,Instant};
use libcitadel::{Result, UtsName, util};
use libcitadel::{UtsName, util};
use libcitadel::ResourceImage;
use crate::boot::disks;
@ -13,34 +13,33 @@ use crate::install::installer::Installer;
const IMAGE_DIRECTORY: &str = "/run/citadel/images";
pub fn live_rootfs() -> Result<()> {
pub fn live_rootfs() -> Result<(), Box<dyn std::error::Error>> {
copy_artifacts()?;
let rootfs = find_rootfs_image()?;
setup_rootfs_resource(&rootfs)
}
pub fn live_setup() -> Result<()> {
pub fn live_setup() -> Result<(), Box<dyn std::error::Error>> {
decompress_images(true)?;
info!("Starting live setup");
let live = Installer::new_livesetup();
live.run()
}
fn copy_artifacts() -> Result<()> {
fn copy_artifacts() -> Result<(), Box<dyn std::error::Error>> {
for _ in 0..3 {
if try_copy_artifacts()? {
//decompress_images()?;
return Ok(())
return Ok(());
}
// Try again after waiting for more devices to be discovered
info!("Failed to find partition with images, trying again in 2 seconds");
thread::sleep(time::Duration::from_secs(2));
}
bail!("could not find partition containing resource images")
Result::Err("could not find partition containing resource images".into())
}
fn try_copy_artifacts() -> Result<bool> {
fn try_copy_artifacts() -> Result<bool, Box<dyn std::error::Error>> {
let rootfs_image = Path::new("/boot/images/citadel-rootfs.img");
// Already mounted?
if rootfs_image.exists() {
@ -60,13 +59,13 @@ fn try_copy_artifacts() -> Result<bool> {
Ok(false)
}
fn kernel_version() -> String {
fn kernel_version() -> Result<String, Box<dyn std::error::Error>> {
let utsname = UtsName::uname();
let v = utsname.release().split('-').collect::<Vec<_>>();
v[0].to_string()
Ok(v[0].to_string())
}
fn deploy_artifacts() -> Result<()> {
fn deploy_artifacts() -> Result<(), Box<dyn std::error::Error>> {
let run_images = Path::new(IMAGE_DIRECTORY);
if !run_images.exists() {
util::create_dir(run_images)?;
@ -78,7 +77,7 @@ fn deploy_artifacts() -> Result<()> {
util::copy_file(dent.path(), run_images.join(dent.file_name()))
})?;
let kv = kernel_version();
let kv = kernel_version()?;
println!("Copying bzImage-{} to /run/citadel/images", kv);
let from = format!("/boot/bzImage-{}", kv);
let to = format!("/run/citadel/images/bzImage-{}", kv);
@ -92,7 +91,7 @@ fn deploy_artifacts() -> Result<()> {
Ok(())
}
fn deploy_syslinux_artifacts() -> Result<()> {
fn deploy_syslinux_artifacts() -> Result<(), Box<dyn std::error::Error>> {
let boot_syslinux = Path::new("/boot/syslinux");
if !boot_syslinux.exists() {
@ -112,10 +111,11 @@ fn deploy_syslinux_artifacts() -> Result<()> {
}
}
Ok(())
})
})?;
Ok(())
}
fn find_rootfs_image() -> Result<ResourceImage> {
fn find_rootfs_image() -> Result<ResourceImage, Box<dyn std::error::Error>> {
let entries = fs::read_dir(IMAGE_DIRECTORY)
.map_err(context!("error reading directory {}", IMAGE_DIRECTORY))?;
for entry in entries {
@ -123,15 +123,21 @@ fn find_rootfs_image() -> Result<ResourceImage> {
if entry.path().extension() == Some(OsStr::new("img")) {
if let Ok(image) = ResourceImage::from_path(&entry.path()) {
if image.metainfo().image_type() == "rootfs" {
return Ok(image)
return Ok(image);
}
}
}
}
bail!("unable to find rootfs resource image in {}", IMAGE_DIRECTORY)
Result::Err(
format!(
"unable to find rootfs resource image in {}",
IMAGE_DIRECTORY
)
.into(),
)
}
fn decompress_images(sync: bool) -> Result<()> {
fn decompress_images(sync: bool) -> Result<(), Box<dyn std::error::Error>> {
info!("Decompressing images");
let mut threads = Vec::new();
util::read_directory("/run/citadel/images", |dent| {
@ -140,9 +146,8 @@ fn decompress_images(sync: bool) -> Result<()> {
if image.is_compressed() {
if sync {
if let Err(err) = decompress_one_image_sync(image) {
warn!("Error: {}", err);
warn!("Error decompressing image: {}", err);
}
} else {
threads.push(decompress_one_image(image));
}
@ -161,7 +166,9 @@ fn decompress_images(sync: bool) -> Result<()> {
Ok(())
}
fn decompress_one_image_sync(image: ResourceImage) -> Result<()> {
fn decompress_one_image_sync(
image: ResourceImage,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let start = Instant::now();
info!("Decompressing {}", image.path().display());
image.decompress(true)
@ -173,8 +180,7 @@ fn decompress_one_image_sync(image: ResourceImage) -> Result<()> {
Ok(())
}
fn decompress_one_image(image: ResourceImage) -> JoinHandle<Result<()>> {
thread::spawn(move || {
decompress_one_image_sync(image)
})
fn decompress_one_image(image: ResourceImage,) ->
JoinHandle<Result<(), Box<dyn std::error::Error + Send + Sync>>> {
thread::spawn(move || decompress_one_image_sync(image))
}

View File

@ -1,7 +1,6 @@
use std::fs;
use std::process::exit;
use libcitadel::{Result, ResourceImage, CommandLine, KeyRing, LogLevel, Logger, util};
use libcitadel::{ResourceImage, CommandLine, KeyRing, LogLevel, Logger, util};
use libcitadel::RealmManager;
use crate::boot::disks::DiskPartition;
use std::path::Path;
@ -10,44 +9,40 @@ mod live;
mod disks;
mod rootfs;
pub fn main(args: Vec<String>) {
pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
if CommandLine::debug() {
Logger::set_log_level(LogLevel::Debug);
} else if CommandLine::verbose() {
Logger::set_log_level(LogLevel::Info);
}
let result = match args.get(1) {
match args.get(1) {
Some(s) if s == "rootfs" => do_rootfs(),
Some(s) if s == "setup" => do_setup(),
Some(s) if s == "boot-automount" => do_boot_automount(),
Some(s) if s == "start-realms" => do_start_realms(),
_ => Err(format_err!("Bad or missing argument").into()),
};
}?;
if let Err(ref e) = result {
warn!("Failed: {}", e);
exit(1);
}
Ok(())
}
fn do_rootfs() -> Result<()> {
fn do_rootfs() -> Result<(), Box<dyn std::error::Error>> {
if CommandLine::live_mode() || CommandLine::install_mode() {
live::live_rootfs()
} else {
rootfs::setup_rootfs()
Ok(rootfs::setup_rootfs()?)
}
}
fn setup_keyring() -> Result<()> {
fn setup_keyring() -> Result<(), Box<dyn std::error::Error>> {
ResourceImage::ensure_storage_mounted()?;
let keyring = KeyRing::load_with_cryptsetup_passphrase("/sysroot/storage/keyring")?;
keyring.add_keys_to_kernel()?;
Ok(())
}
fn do_setup() -> Result<()> {
fn do_setup() -> Result<(), Box<dyn std::error::Error>> {
if CommandLine::live_mode() || CommandLine::install_mode() {
live::live_setup()?;
} else if let Err(err) = setup_keyring() {
@ -64,8 +59,7 @@ fn do_setup() -> Result<()> {
Ok(())
}
fn mount_overlay() -> Result<()> {
fn mount_overlay() -> Result<(), Box<dyn std::error::Error>> {
info!("Creating rootfs overlay");
info!("Moving /sysroot mount to /rootfs.ro");
@ -89,13 +83,13 @@ fn mount_overlay() -> Result<()> {
Ok(())
}
fn do_start_realms() -> Result<()> {
fn do_start_realms() -> Result<(), Box<dyn std::error::Error>> {
let manager = RealmManager::load()?;
manager.start_boot_realms()
Ok(manager.start_boot_realms()?)
}
// Write automount unit for /boot partition
fn do_boot_automount() -> Result<()> {
fn do_boot_automount() -> Result<(), Box<dyn std::error::Error>> {
Logger::set_log_level(LogLevel::Info);
if CommandLine::live_mode() || CommandLine::install_mode() {
@ -105,10 +99,10 @@ fn do_boot_automount() -> Result<()> {
let boot_partition = find_boot_partition()?;
info!("Creating /boot automount units for boot partition {}", boot_partition);
cmd!("/usr/bin/systemd-mount", "-A --timeout-idle-sec=300 {} /boot", boot_partition)
Ok(cmd!("/usr/bin/systemd-mount", "-A --timeout-idle-sec=300 {} /boot", boot_partition)?)
}
fn find_boot_partition() -> Result<String> {
fn find_boot_partition() -> Result<String, Box<dyn std::error::Error>> {
let loader_dev = read_loader_dev_efi_var()?;
let boot_partitions = DiskPartition::boot_partitions(true)?
.into_iter()
@ -116,7 +110,7 @@ fn find_boot_partition() -> Result<String> {
.collect::<Vec<_>>();
if boot_partitions.len() != 1 {
return Err(format_err!("Cannot uniquely determine boot partition"));
return Result::Err("Cannot uniquely determine boot partition".into());
}
Ok(boot_partitions[0].path().display().to_string())
@ -141,7 +135,7 @@ fn matches_loader_dev(partition: &DiskPartition, dev: &Option<String>) -> bool {
const LOADER_EFI_VAR_PATH: &str =
"/sys/firmware/efi/efivars/LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f";
fn read_loader_dev_efi_var() -> Result<Option<String>> {
fn read_loader_dev_efi_var() -> Result<Option<String>, Box<dyn std::error::Error>> {
let efi_var = Path::new(LOADER_EFI_VAR_PATH);
if efi_var.exists() {
let s = fs::read(efi_var)

View File

@ -1,10 +1,10 @@
use std::path::Path;
use std::process::{Command,Stdio};
use libcitadel::{BlockDev, ResourceImage, CommandLine, ImageHeader, Partition, Result, LoopDevice};
use libcitadel::{BlockDev, ResourceImage, CommandLine, ImageHeader, Partition, LoopDevice};
use libcitadel::verity::Verity;
pub fn setup_rootfs() -> Result<()> {
pub fn setup_rootfs() -> Result<(), Box<dyn std::error::Error>> {
let mut p = choose_boot_partiton(true, CommandLine::revert_rootfs())?;
if CommandLine::noverity() {
setup_partition_unverified(&p)
@ -13,7 +13,7 @@ pub fn setup_rootfs() -> Result<()> {
}
}
pub fn setup_rootfs_resource(rootfs: &ResourceImage) -> Result<()> {
pub fn setup_rootfs_resource(rootfs: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
if CommandLine::noverity() {
setup_resource_unverified(&rootfs)
} else {
@ -21,7 +21,7 @@ pub fn setup_rootfs_resource(rootfs: &ResourceImage) -> Result<()> {
}
}
fn setup_resource_unverified(img: &ResourceImage) -> Result<()> {
fn setup_resource_unverified(img: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
if img.is_compressed() {
img.decompress(false)?;
}
@ -30,25 +30,31 @@ fn setup_resource_unverified(img: &ResourceImage) -> Result<()> {
setup_linear_mapping(loopdev.device())
}
fn setup_resource_verified(img: &ResourceImage) -> Result<()> {
fn setup_resource_verified(img: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
let _ = img.setup_verity_device()?;
Ok(())
}
fn setup_partition_unverified(p: &Partition) -> Result<()> {
fn setup_partition_unverified(p: &Partition) -> Result<(), Box<dyn std::error::Error>> {
info!("Creating /dev/mapper/rootfs device with linear device mapping of partition (no verity)");
setup_linear_mapping(p.path())
}
fn setup_partition_verified(p: &mut Partition) -> Result<()> {
fn setup_partition_verified(p: &mut Partition) -> Result<(), Box<dyn std::error::Error>> {
info!("Creating /dev/mapper/rootfs dm-verity device");
if !CommandLine::nosignatures() {
if !p.has_public_key() {
bail!("no public key available for channel {}", p.metainfo().channel())
return Result::Err(
format!(
"no public key available for channel {}",
p.metainfo().channel()
)
.into(),
);
}
if !p.is_signature_valid() {
p.write_status(ImageHeader::STATUS_BAD_SIG)?;
bail!("signature verification failed on partition");
return Result::Err("signature verification failed on partition".into());
}
info!("Image signature is valid for channel {}", p.metainfo().channel());
}
@ -56,7 +62,7 @@ fn setup_partition_verified(p: &mut Partition) -> Result<()> {
Ok(())
}
fn setup_linear_mapping(blockdev: &Path) -> Result<()> {
fn setup_linear_mapping(blockdev: &Path) -> Result<(), Box<dyn std::error::Error>> {
let dev = BlockDev::open_ro(blockdev)?;
let table = format!("0 {} linear {} 0", dev.nsectors()?, blockdev.display());
@ -70,7 +76,9 @@ fn setup_linear_mapping(blockdev: &Path) -> Result<()> {
.success();
if !ok {
bail!("failed to set up linear identity mapping with /usr/sbin/dmsetup");
return Result::Err(
"failed to set up linear identity mapping with /usr/sbin/dmsetup".into(),
);
}
Ok(())
}
@ -94,7 +102,8 @@ fn choose_revert_partition(best: Option<Partition>) -> Option<Partition> {
best
}
fn choose_boot_partiton(scan: bool, revert_rootfs: bool) -> Result<Partition> {
fn choose_boot_partiton(scan: bool, revert_rootfs: bool,
) -> Result<Partition, Box<dyn std::error::Error>> {
let mut partitions = Partition::rootfs_partitions()?;
if scan {
@ -136,14 +145,17 @@ fn compare_boot_partitions(a: Option<Partition>, b: Partition) -> Option<Partiti
}
// Compare versions and channels
let a_v = a.metainfo().version();
let b_v = b.metainfo().version();
let meta_a = a.metainfo();
let meta_b = b.metainfo();
let ver_a = meta_a.version();
let ver_b = meta_b.version();
// Compare versions only if channels match
if a.metainfo().channel() == b.metainfo().channel() {
if a_v > b_v {
if ver_a > ver_b {
return Some(a);
} else if b_v > a_v {
} else if ver_b > ver_a {
return Some(b);
}
}

View File

@ -3,11 +3,10 @@ use std::process::exit;
use clap::{App,Arg,SubCommand,ArgMatches};
use clap::AppSettings::*;
use libcitadel::{Result, ResourceImage, Logger, LogLevel, Partition, KeyPair, ImageHeader, util};
use libcitadel::{ResourceImage, Logger, LogLevel, Partition, KeyPair, ImageHeader, util};
use hex;
pub fn main(args: Vec<String>) {
pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
let app = App::new("citadel-image")
.about("Citadel update image builder")
.settings(&[ArgRequiredElseHelp,ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder])
@ -91,16 +90,18 @@ pub fn main(args: Vec<String>) {
println!("Error: {}", e);
exit(1);
}
Ok(())
}
fn info(arg_matches: &ArgMatches) -> Result<()> {
fn info(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?;
print!("{}", String::from_utf8(img.header().metainfo_bytes())?);
info_signature(&img)?;
Ok(())
}
fn info_signature(img: &ResourceImage) -> Result<()> {
fn info_signature(img: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
if img.header().has_signature() {
println!("Signature: {}", hex::encode(&img.header().signature()));
} else {
@ -118,13 +119,13 @@ fn info_signature(img: &ResourceImage) -> Result<()> {
}
Ok(())
}
fn metainfo(arg_matches: &ArgMatches) -> Result<()> {
fn metainfo(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?;
print!("{}", String::from_utf8(img.header().metainfo_bytes())?);
Ok(())
}
fn generate_verity(arg_matches: &ArgMatches) -> Result<()> {
fn generate_verity(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?;
if img.has_verity_hashtree() {
info!("Image already has dm-verity hashtree appended, doing nothing.");
@ -134,7 +135,7 @@ fn generate_verity(arg_matches: &ArgMatches) -> Result<()> {
Ok(())
}
fn verify(arg_matches: &ArgMatches) -> Result<()> {
fn verify(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?;
let ok = img.verify_verity()?;
if ok {
@ -145,7 +146,7 @@ fn verify(arg_matches: &ArgMatches) -> Result<()> {
Ok(())
}
fn verify_shasum(arg_matches: &ArgMatches) -> Result<()> {
fn verify_shasum(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?;
let shasum = img.generate_shasum()?;
if shasum == img.metainfo().shasum() {
@ -158,22 +159,22 @@ fn verify_shasum(arg_matches: &ArgMatches) -> Result<()> {
Ok(())
}
fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage> {
fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage, Box<dyn std::error::Error>> {
let path = arg_matches.value_of("path").expect("path argument missing");
if !Path::new(path).exists() {
bail!("Cannot load image {}: File does not exist", path);
panic!("Cannot load image {}: File does not exist", path);
}
let img = ResourceImage::from_path(path)?;
if !img.is_valid_image() {
bail!("File {} is not a valid image file", path);
panic!("File {} is not a valid image file", path);
}
Ok(img)
}
fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> {
fn install_rootfs(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
if arg_matches.is_present("choose") {
let _ = choose_install_partition(true)?;
return Ok(())
return Ok(());
}
let img = load_image(arg_matches)?;
@ -182,7 +183,7 @@ fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> {
info!("Verifying sha256 hash of image");
let shasum = img.generate_shasum()?;
if shasum != img.metainfo().shasum() {
bail!("image file does not have expected sha256 value");
panic!("image file does not have expected sha256 value");
}
}
@ -196,7 +197,7 @@ fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> {
Ok(())
}
fn clear_prefer_boot() -> Result<()> {
fn clear_prefer_boot() -> Result<(), Box<dyn std::error::Error>> {
for mut p in Partition::rootfs_partitions()? {
if p.is_initialized() && p.header().has_flag(ImageHeader::FLAG_PREFER_BOOT) {
p.clear_flag_and_write(ImageHeader::FLAG_PREFER_BOOT)?;
@ -205,13 +206,13 @@ fn clear_prefer_boot() -> Result<()> {
Ok(())
}
fn sign_image(arg_matches: &ArgMatches) -> Result<()> {
fn sign_image(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let _img = load_image(arg_matches)?;
info!("Not implemented yet");
Ok(())
}
fn install_image(arg_matches: &ArgMatches) -> Result<()> {
fn install_image(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let source = arg_matches.value_of("path").expect("path argument missing");
let img = load_image(arg_matches)?;
let _hdr = img.header();
@ -220,12 +221,12 @@ fn install_image(arg_matches: &ArgMatches) -> Result<()> {
// XXX verify signature?
if !(metainfo.image_type() == "kernel" || metainfo.image_type() == "extra") {
bail!("Cannot install image type {}", metainfo.image_type());
panic!("Cannot install image type {}", metainfo.image_type());
}
let shasum = img.generate_shasum()?;
if shasum != img.metainfo().shasum() {
bail!("Image shasum does not match metainfo");
panic!("Image shasum does not match metainfo");
}
img.generate_verity_hashtree()?;
@ -233,10 +234,10 @@ fn install_image(arg_matches: &ArgMatches) -> Result<()> {
let filename = if metainfo.image_type() == "kernel" {
let kernel_version = match metainfo.kernel_version() {
Some(version) => version,
None => bail!("Kernel image does not have a kernel version field in metainfo"),
None => panic!("Kernel image does not have a kernel version field in metainfo"),
};
if kernel_version.chars().any(|c| c == '/') {
bail!("Kernel version field has / char");
panic!("Kernel version field has / char");
}
format!("citadel-kernel-{}-{:03}.img", kernel_version, metainfo.version())
} else {
@ -244,33 +245,38 @@ fn install_image(arg_matches: &ArgMatches) -> Result<()> {
};
if !metainfo.channel().chars().all(|c| c.is_ascii_lowercase()) {
bail!("Refusing to build path from strange channel name {}", metainfo.channel());
panic!(
"Refusing to build path from strange channel name {}",
metainfo.channel()
);
}
let image_dir = Path::new("/storage/resources").join(metainfo.channel());
let image_dest = image_dir.join(filename);
if image_dest.exists() {
rotate(&image_dest)?;
}
util::rename(source, &image_dest)
util::rename(source, &image_dest);
Ok(())
}
fn rotate(path: &Path) -> Result<()> {
fn rotate(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
if !path.exists() || path.file_name().is_none() {
return Ok(());
}
let filename = path.file_name().unwrap();
let dot_zero = path.with_file_name(format!("{}.0", filename.to_string_lossy()));
util::remove_file(&dot_zero)?;
util::rename(path, &dot_zero)
util::rename(path, &dot_zero).unwrap();
Ok(())
}
fn genkeys() -> Result<()> {
fn genkeys() -> Result<(), Box<dyn std::error::Error>> {
let keypair = KeyPair::generate();
println!("keypair = \"{}\"", keypair.to_hex());
Ok(())
}
fn decompress(arg_matches: &ArgMatches) -> Result<()> {
fn decompress(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?;
if !img.is_compressed() {
info!("Image is not compressed, not decompressing.");
@ -280,7 +286,7 @@ fn decompress(arg_matches: &ArgMatches) -> Result<()> {
Ok(())
}
fn bless() -> Result<()> {
fn bless() -> Result<(), Box<dyn std::error::Error>> {
for mut p in Partition::rootfs_partitions()? {
if p.is_initialized() && p.is_mounted() {
p.bless()?;
@ -299,7 +305,7 @@ fn bool_to_yesno(val: bool) -> &'static str {
}
}
fn choose_install_partition(verbose: bool) -> Result<Partition> {
fn choose_install_partition(verbose: bool) -> Result<Partition, Box<dyn std::error::Error>> {
let partitions = Partition::rootfs_partitions()?;
if verbose {
@ -314,9 +320,12 @@ fn choose_install_partition(verbose: bool) -> Result<Partition> {
for p in &partitions {
if !p.is_mounted() && !p.is_initialized() {
if verbose {
info!("Choosing {} because it is empty and not mounted", p.path().display());
info!(
"Choosing {} because it is empty and not mounted",
p.path().display()
);
}
return Ok(p.clone())
return Ok(p.clone());
}
}
for p in &partitions {
@ -326,8 +335,8 @@ fn choose_install_partition(verbose: bool) -> Result<Partition> {
info!("Header metainfo:");
print!("{}", String::from_utf8(p.header().metainfo_bytes())?);
}
return Ok(p.clone())
return Ok(p.clone());
}
}
bail!("No suitable install partition found")
panic!("No suitable install partition found")
}

View File

@ -1,6 +1,5 @@
use std::io::{self,Write};
use std::path::Path;
use libcitadel::Result;
use super::disk::Disk;
use rpassword;
use crate::install::installer::Installer;
@ -8,7 +7,7 @@ use crate::install::installer::Installer;
const CITADEL_PASSPHRASE_PROMPT: &str = "Enter a password for the Citadel user (or 'q' to quit)";
const LUKS_PASSPHRASE_PROMPT: &str = "Enter a disk encryption passphrase (or 'q' to quit";
pub fn run_cli_install() -> Result<bool> {
pub fn run_cli_install() -> Result<bool, Box<dyn std::error::Error>> {
let disk = match choose_disk()? {
Some(disk) => disk,
None => return Ok(false),
@ -33,7 +32,7 @@ pub fn run_cli_install() -> Result<bool> {
Ok(true)
}
pub fn run_cli_install_with<P: AsRef<Path>>(target: P) -> Result<bool> {
pub fn run_cli_install_with<P: AsRef<Path>>(target: P) -> Result<bool, Box<dyn std::error::Error>> {
let disk = find_disk_by_path(target.as_ref())?;
display_disk(&disk);
@ -55,11 +54,15 @@ pub fn run_cli_install_with<P: AsRef<Path>>(target: P) -> Result<bool> {
Ok(true)
}
fn run_install(disk: Disk, citadel_passphrase: String, passphrase: String) -> Result<()> {
fn run_install(
disk: Disk,
citadel_passphrase: String,
passphrase: String,
) -> Result<(), Box<dyn std::error::Error>> {
let mut install = Installer::new(disk.path(), &citadel_passphrase, &passphrase);
install.set_install_syslinux(true);
install.verify()?;
install.run()
Ok(install.run()?)
}
fn display_disk(disk: &Disk) {
@ -70,22 +73,22 @@ fn display_disk(disk: &Disk) {
println!();
}
fn find_disk_by_path(path: &Path) -> Result<Disk> {
fn find_disk_by_path(path: &Path) -> Result<Disk, Box<dyn std::error::Error>> {
if !path.exists() {
bail!("Target disk path {} does not exist", path.display());
panic!("Target disk path {} does not exist", path.display());
}
for disk in Disk::probe_all()? {
if disk.path() == path {
return Ok(disk.clone());
}
}
bail!("installation target {} is not a valid disk", path.display())
panic!("installation target {} is not a valid disk", path.display())
}
fn choose_disk() -> Result<Option<Disk>> {
fn choose_disk() -> Result<Option<Disk>, Box<dyn std::error::Error>> {
let disks = Disk::probe_all()?;
if disks.is_empty() {
bail!("no disks found.");
panic!("no disks found.");
}
loop {
@ -111,7 +114,7 @@ fn prompt_choose_disk(disks: &[Disk]) {
let _ = io::stdout().flush();
}
fn read_line() -> Result<String> {
fn read_line() -> Result<String, Box<dyn std::error::Error>> {
let mut input = String::new();
io::stdin().read_line(&mut input)
.map_err(context!("error reading line from stdin"))?;
@ -146,7 +149,7 @@ fn read_passphrase(prompt: &str) -> io::Result<Option<String>> {
}
}
fn confirm_install(disk: &Disk) -> Result<bool> {
fn confirm_install(disk: &Disk) -> Result<bool, Box<dyn std::error::Error>> {
println!("Are you sure you want to completely erase this this device?");
println!();
println!(" Device: {}", disk.path().display());
@ -158,4 +161,3 @@ fn confirm_install(disk: &Disk) -> Result<bool> {
let answer = read_line()?;
Ok(answer == "YES")
}

View File

@ -9,7 +9,6 @@ use pwhash::sha512_crypt;
use libcitadel::util;
use libcitadel::RealmFS;
use libcitadel::Result;
use libcitadel::OsRelease;
use libcitadel::KeyRing;
use libcitadel::terminal::Base16Scheme;
@ -183,7 +182,7 @@ impl Installer {
self.install_syslinux = val;
}
pub fn verify(&self) -> Result<()> {
pub fn verify(&self) -> Result<(), Box<dyn std::error::Error>> {
let kernel_img = self.kernel_imagename();
let bzimage = format!("bzImage-{}", self.kernel_version());
let artifacts = vec![
@ -192,26 +191,29 @@ impl Installer {
];
if !self.target().exists() {
bail!("target device {:?} does not exist", self.target());
panic!("target device {:?} does not exist", self.target());
}
for a in artifacts {
if !self.artifact_path(a).exists() {
bail!("required install artifact {} does not exist in {}", a, self.artifact_directory);
panic!(
"required install artifact {} does not exist in {}",
a, self.artifact_directory
);
}
}
Ok(())
}
pub fn run(&self) -> Result<()> {
pub fn run(&self) -> Result<(), Box<dyn std::error::Error>> {
match self._type {
InstallType::Install => self.run_install(),
InstallType::LiveSetup => self.run_live_setup(),
}
}
pub fn run_install(&self) -> Result<()> {
pub fn run_install(&self) -> Result<(), Box<dyn std::error::Error>> {
let start = Instant::now();
self.partition_disk()?;
self.setup_luks()?;
@ -224,7 +226,7 @@ impl Installer {
Ok(())
}
pub fn run_live_setup(&self) -> Result<()> {
pub fn run_live_setup(&self) -> Result<(), Box<dyn std::error::Error>> {
self.cmd_list(&[
"/bin/mount -t tmpfs var-tmpfs /sysroot/var",
"/bin/mount -t tmpfs home-tmpfs /sysroot/home",
@ -242,8 +244,7 @@ impl Installer {
Ok(())
}
fn setup_live_realm(&self) -> Result<()> {
fn setup_live_realm(&self) -> Result<(), Box<dyn std::error::Error>> {
let realmfs_dir = self.storage().join("realms/realmfs-images");
let base_realmfs = realmfs_dir.join("base-realmfs.img");
@ -261,14 +262,14 @@ impl Installer {
Ok(())
}
pub fn partition_disk(&self) -> Result<()> {
pub fn partition_disk(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Partitioning target disk")?;
self.cmd_list(PARTITION_COMMANDS, &[
("$TARGET", self.target_str())
])
}
pub fn setup_luks(&self) -> Result<()> {
pub fn setup_luks(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Setting up LUKS disk encryption")?;
util::create_dir(INSTALL_MOUNT)?;
util::write_file(LUKS_PASSPHRASE_FILE, self.passphrase().as_bytes())?;
@ -281,15 +282,16 @@ impl Installer {
("$LUKS_PASSFILE", LUKS_PASSPHRASE_FILE),
])?;
util::remove_file(LUKS_PASSPHRASE_FILE)
util::remove_file(LUKS_PASSPHRASE_FILE)?;
Ok(())
}
pub fn setup_lvm(&self) -> Result<()> {
pub fn setup_lvm(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Setting up LVM volumes")?;
self.cmd_list(LVM_COMMANDS, &[])
}
pub fn setup_boot(&self) -> Result<()> {
pub fn setup_boot(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Setting up /boot partition")?;
let boot_partition = self.target_partition(1);
self.cmd(format!("/sbin/mkfs.vfat -F 32 {}", boot_partition))?;
@ -323,11 +325,11 @@ impl Installer {
Ok(())
}
fn setup_syslinux(&self) -> Result<()> {
fn setup_syslinux(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Installing syslinux")?;
let syslinux_src = self.artifact_path("syslinux");
if !syslinux_src.exists() {
bail!("no syslinux directory found in artifact directory, cannot install syslinux");
panic!("no syslinux directory found in artifact directory, cannot install syslinux");
}
let dst = Path::new(INSTALL_MOUNT).join("syslinux");
util::create_dir(&dst)?;
@ -345,17 +347,17 @@ impl Installer {
self.cmd(format!("/sbin/extlinux --install {}", dst.display()))
}
fn setup_syslinux_post_umount(&self) -> Result<()> {
fn setup_syslinux_post_umount(&self) -> Result<(), Box<dyn std::error::Error>> {
let mbrbin = self.artifact_path("syslinux/gptmbr.bin");
if !mbrbin.exists() {
bail!("could not find MBR image: {:?}", mbrbin);
panic!("could not find MBR image: {:?}", mbrbin);
}
self.cmd(format!("/bin/dd bs=440 count=1 conv=notrunc if={} of={}", mbrbin.display(), self.target().display()))?;
self.cmd(format!("/sbin/parted -s {} set 1 legacy_boot on", self.target_str()))
}
pub fn create_storage(&self) -> Result<()> {
pub fn create_storage(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Setting up /storage partition")?;
self.cmd_list(CREATE_STORAGE_COMMANDS,
@ -365,7 +367,7 @@ impl Installer {
self.cmd(format!("/bin/umount {}", INSTALL_MOUNT))
}
fn setup_storage(&self) -> Result<()> {
fn setup_storage(&self) -> Result<(), Box<dyn std::error::Error>> {
if self._type == InstallType::Install {
self.create_keyring()?;
self.setup_storage_resources()?;
@ -389,33 +391,33 @@ impl Installer {
Ok(())
}
fn create_keyring(&self) -> Result<()> {
fn create_keyring(&self) -> Result<(), Box<dyn std::error::Error>> {
self.info("Creating initial keyring")?;
let keyring = KeyRing::create_new();
keyring.write(self.storage().join("keyring"), self.passphrase.as_ref().unwrap())
Ok(keyring.write(self.storage().join("keyring"), self.passphrase.as_ref().unwrap())?)
}
fn setup_base_realmfs(&self) -> Result<()> {
fn setup_base_realmfs(&self) -> Result<(), Box<dyn std::error::Error>> {
let realmfs_dir = self.storage().join("realms/realmfs-images");
util::create_dir(&realmfs_dir)?;
self.sparse_copy_artifact("base-realmfs.img", &realmfs_dir)?;
self.cmd(format!("/usr/bin/citadel-image decompress {}/base-realmfs.img", realmfs_dir.display()))
}
fn setup_realm_skel(&self) -> Result<()> {
fn setup_realm_skel(&self) -> Result<(), Box<dyn std::error::Error>> {
let realm_skel = self.storage().join("realms/skel");
util::create_dir(&realm_skel)?;
util::copy_tree_with_chown(&self.skel(), &realm_skel, (1000,1000))
util::copy_tree_with_chown(&self.skel(), &realm_skel, (1000, 1000))?;
Ok(())
}
fn create_realmlock(&self, dir: &Path) -> Result<()> {
fn create_realmlock(&self, dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
fs::File::create(dir.join(".realmlock"))
.map_err(context!("failed to create {:?}/.realmlock file", dir))?;
Ok(())
}
fn setup_main_realm(&self) -> Result<()> {
fn setup_main_realm(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Creating main realm")?;
let realm = self.storage().join("realms/realm-main");
@ -440,7 +442,7 @@ impl Installer {
self.create_realmlock(&realm)
}
fn setup_apt_cacher_realm(&self) -> Result<()> {
fn setup_apt_cacher_realm(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Creating apt-cacher realm")?;
let realm_base = self.storage().join("realms/realm-apt-cacher");
@ -460,7 +462,7 @@ impl Installer {
self.create_realmlock(&realm_base)
}
fn setup_storage_resources(&self) -> Result<()> {
fn setup_storage_resources(&self) -> Result<(), Box<dyn std::error::Error>> {
let channel = match OsRelease::citadel_channel() {
Some(channel) => channel,
None => "dev",
@ -474,7 +476,7 @@ impl Installer {
self.sparse_copy_artifact(&kernel_img, &resources)
}
fn setup_citadel_passphrase(&self) -> Result<()> {
fn setup_citadel_passphrase(&self) -> Result<(), Box<dyn std::error::Error>> {
if self._type == InstallType::LiveSetup {
self.info("Creating temporary citadel passphrase file for live mode")?;
let path = self.storage().join("citadel-state/passwd");
@ -497,17 +499,15 @@ impl Installer {
Ok(())
}
pub fn install_rootfs_partitions(&self) -> Result<()> {
pub fn install_rootfs_partitions(&self) -> Result<(), Box<dyn std::error::Error>> {
self.header("Installing rootfs partitions")?;
let rootfs = self.artifact_path("citadel-rootfs.img");
self.cmd(format!("/usr/bin/citadel-image install-rootfs --skip-sha {}", rootfs.display()))?;
self.cmd(format!("/usr/bin/citadel-image install-rootfs --skip-sha --no-prefer {}", rootfs.display()))
}
pub fn finish_install(&self) -> Result<()> {
self.cmd_list(FINISH_COMMANDS, &[
("$TARGET", self.target_str())
])
pub fn finish_install(&self) -> Result<(), Box<dyn std::error::Error>> {
self.cmd_list(FINISH_COMMANDS, &[("$TARGET", self.target_str())])
}
fn global_realm_config(&self) -> &str {
@ -546,16 +546,33 @@ impl Installer {
Path::new(&self.artifact_directory).join(filename)
}
fn copy_artifact<P: AsRef<Path>>(&self, filename: &str, target: P) -> Result<()> {
fn copy_artifact<P: AsRef<Path>>(
&self,
filename: &str,
target: P,
) -> Result<(), Box<dyn std::error::Error>> {
self._copy_artifact(filename, target, false)
}
fn sparse_copy_artifact<P: AsRef<Path>>(&self, filename: &str, target: P) -> Result<()> {
fn sparse_copy_artifact<P: AsRef<Path>>(
&self,
filename: &str,
target: P,
) -> Result<(), Box<dyn std::error::Error>> {
self._copy_artifact(filename, target, true)
}
fn _copy_artifact<P: AsRef<Path>>(&self, filename: &str, target: P, sparse: bool) -> Result<()> {
self.info(format!("Copying {} to {}", filename, target.as_ref().display()))?;
fn _copy_artifact<P: AsRef<Path>>(
&self,
filename: &str,
target: P,
sparse: bool,
) -> Result<(), Box<dyn std::error::Error>> {
self.info(format!(
"Copying {} to {}",
filename,
target.as_ref().display()
))?;
let src = self.artifact_path(filename);
let target = target.as_ref();
util::create_dir(target)?;
@ -568,20 +585,19 @@ impl Installer {
Ok(())
}
fn header<S: AsRef<str>>(&self, s: S) -> Result<()> {
fn header<S: AsRef<str>>(&self, s: S) -> Result<(), Box<dyn std::error::Error>> {
self.output(format!("\n[+] {}\n", s.as_ref()))
}
fn info<S: AsRef<str>>(&self, s: S) -> Result<()> {
fn info<S: AsRef<str>>(&self, s: S) -> Result<(), Box<dyn std::error::Error>> {
self.output(format!(" [>] {}", s.as_ref()))
}
fn output<S: AsRef<str>>(&self, s: S) -> Result<()> {
self.write_output(s.as_ref()).map_err(context!("error writing output"))
fn output<S: AsRef<str>>(&self, s: S) -> Result<(), Box<dyn std::error::Error>> {
self.write_output(s.as_ref())
}
fn write_output(&self, s: &str) -> io::Result<()> {
fn write_output(&self, s: &str) -> Result<(), Box<dyn std::error::Error>> {
println!("{}", s);
io::stdout().flush()?;
@ -592,7 +608,11 @@ impl Installer {
Ok(())
}
fn cmd_list<I: IntoIterator<Item=S>, S: AsRef<str>>(&self, cmd_lines: I, subs: &[(&str,&str)]) -> Result<()> {
fn cmd_list<I: IntoIterator<Item = S>, S: AsRef<str>>(
&self,
cmd_lines: I,
subs: &[(&str, &str)],
) -> Result<(), Box<dyn std::error::Error>> {
for line in cmd_lines {
let line = line.as_ref();
let line = subs.iter().fold(line.to_string(), |acc, (from,to)| acc.replace(from,to));
@ -602,12 +622,12 @@ impl Installer {
Ok(())
}
fn cmd<S: AsRef<str>>(&self, args: S) -> Result<()> {
fn cmd<S: AsRef<str>>(&self, args: S) -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
self.run_cmd(args, false)
}
fn run_cmd(&self, args: Vec<&str>, as_user: bool) -> Result<()> {
fn run_cmd(&self, args: Vec<&str>, as_user: bool) -> Result<(), Box<dyn std::error::Error>> {
self.output(format!(" # {}", args.join(" ")))?;
let mut command = Command::new(args[0]);
@ -632,8 +652,8 @@ impl Installer {
if !result.status.success() {
match result.status.code() {
Some(code) => bail!("command {} failed with exit code: {}", args[0], code),
None => bail!("command {} failed with no exit code", args[0]),
Some(code) => panic!("command {} failed with exit code: {}", args[0], code),
None => panic!("command {} failed with no exit code", args[0]),
}
}
Ok(())

View File

@ -4,7 +4,7 @@ pub(crate) mod installer;
mod cli;
mod disk;
pub fn main(args: Vec<String>) {
pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
let mut args = args.iter().skip(1);
let result = if let Some(dev) = args.next() {
cli::run_cli_install_with(dev)
@ -17,10 +17,11 @@ pub fn main(args: Vec<String>) {
Err(ref err) => {
println!("Install failed: {}", err);
exit(1);
},
}
};
if !ok {
println!("Install cancelled...");
}
}
Ok(())
}

View File

@ -7,7 +7,6 @@ use std::sync::mpsc::{Sender};
use dbus::tree::{self, Factory, MTFn, MethodResult, Tree};
use dbus::{Message};
use dbus::blocking::LocalConnection;
use libcitadel::{Result};
// Use local version of disk.rs since we added some methods
use crate::install_backend::disk::*;
use crate::install::installer::*;
@ -15,7 +14,6 @@ use std::fmt;
type MethodInfo<'a> = tree::MethodInfo<'a, MTFn<TData>, TData>;
const OBJECT_PATH: &str = "/com/subgraph/installer";
const INTERFACE_NAME: &str = "com.subgraph.installer.Manager";
const BUS_NAME: &str = "com.subgraph.installer";
@ -37,8 +35,7 @@ pub struct DbusServer {
}
impl DbusServer {
pub fn connect() -> Result<DbusServer> {
pub fn connect() -> Result<DbusServer, Box<dyn std::error::Error>> {
let connection = LocalConnection::new_system()
.map_err(|e| format_err!("Failed to connect to DBUS system bus: {}", e))?;
let connection = Arc::new(connection);
@ -80,7 +77,7 @@ impl DbusServer {
Ok(vec![m.msg.method_return().append1(list)])
}
fn run_install(path: String, citadel_passphrase: String, luks_passphrase: String, sender: Sender<Msg>) -> Result<()> {
fn run_install(path: String, citadel_passphrase: String, luks_passphrase: String, sender: Sender<Msg>) -> Result<(), Box<dyn std::error::Error>> {
let mut install = Installer::new(path, &citadel_passphrase, &luks_passphrase);
install.set_install_syslinux(true);
install.verify()?;
@ -174,12 +171,12 @@ impl DbusServer {
Message::signal(&path, &iface, &member).append1(text)
}
pub fn start(&self) -> Result<()> {
pub fn start(&self) -> Result<(), Box<dyn std::error::Error>> {
let (sender, receiver) = mpsc::channel::<Msg>();
let sender_clone = sender.clone();
let tree = self.build_tree(sender);
if let Err(_err) = self.connection.request_name(BUS_NAME, false, true, false) {
bail!("Failed to request name");
panic!("Failed to request name");
}
tree.start_receive(self.connection.as_ref());
@ -231,7 +228,6 @@ impl DbusServer {
}
}
}
}
#[derive(Clone)]
@ -243,7 +239,6 @@ impl TreeData {
TreeData {}
}
fn disks(&self) -> HashMap<String, Vec<String>> {
let disks = Disk::probe_all().unwrap();
@ -257,7 +252,6 @@ impl TreeData {
}
disk_map
}
}
impl fmt::Debug for TreeData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

View File

@ -1,24 +1,20 @@
use libcitadel::Result;
use std::process::exit;
mod disk;
mod dbus;
use libcitadel::CommandLine;
pub fn main() {
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
if CommandLine::install_mode() {
if let Err(e) = run_dbus_server() {
warn!("Error: {}", e);
}
run_dbus_server()
} else {
println!("Citadel installer backend will only run in install or live mode");
exit(1);
}
}
fn run_dbus_server() -> Result<()> {
fn run_dbus_server() -> Result<(), Box<dyn std::error::Error>> {
let server = dbus::DbusServer::connect()?;
server.start()?;
Ok(())
}

View File

@ -17,42 +17,37 @@ mod realmfs;
mod sync;
mod update;
fn main() {
let exe = match env::current_exe() {
Ok(path) => path,
Err(_e) => {
return;
},
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let exe = env::current_exe()?;
let args = env::args().collect::<Vec<String>>();
if exe == Path::new("/usr/libexec/citadel-boot") {
boot::main(args);
boot::main(args)
} else if exe == Path::new("/usr/libexec/citadel-install") {
install::main(args);
install::main(args)
} 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") {
image::main(args);
image::main(args)
} else if exe == Path::new("/usr/bin/citadel-realmfs") {
realmfs::main(args);
realmfs::main(args)
} 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") {
sync::main(args);
sync::main(args)
} else if exe == Path::new("/usr/libexec/citadel-run") {
do_citadel_run(args);
do_citadel_run(args)
} else if exe.file_name() == Some(OsStr::new("citadel-mkimage")) {
mkimage::main(args);
mkimage::main(args)
} else if exe.file_name() == Some(OsStr::new("citadel-tool")) {
dispatch_command(args);
dispatch_command(args)
} else {
println!("Error: unknown executable {}", exe.display());
Result::Err(format!("Error: unknown executable {}", exe.display()).into())
}
}
fn dispatch_command(args: Vec<String>) {
fn dispatch_command(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
if let Some(command) = args.get(1) {
match command.as_str() {
"boot" => boot::main(rebuild_args("citadel-boot", args)),
@ -63,22 +58,33 @@ fn dispatch_command(args: Vec<String>) {
"mkimage" => mkimage::main(rebuild_args("citadel-mkimage", args)),
"sync" => sync::main(rebuild_args("citadel-desktop-sync", args)),
"run" => do_citadel_run(rebuild_args("citadel-run", args)),
_ => println!("Error: unknown command {}", command),
_ => throw_err(command),
}
} else {
println!("Must provide an argument");
Result::Err("Must provide an argument".into())
}
}
fn throw_err(command: &String) -> Result<(), Box<dyn std::error::Error>> {
Result::Err(format!("Error: unknown command {}", command).into())
}
fn rebuild_args(command: &str, args: Vec<String>) -> Vec<String> {
iter::once(command.to_string())
.chain(args.into_iter().skip(2))
.collect()
}
fn do_citadel_run(args: Vec<String>) {
fn do_citadel_run(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
if let Err(e) = RealmManager::run_in_current(&args[1..], true) {
println!("RealmManager::run_in_current({:?}) failed: {}", &args[1..], e);
return Result::Err(
format!(
"RealmManager::run_in_current({:?}) failed: {}",
&args[1..],
e
)
.into(),
);
}
Ok(())
}

View File

@ -1,13 +1,10 @@
use std::process::exit;
use libcitadel::Result;
mod config;
mod build;
pub fn main(args: Vec<String>) {
pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
let config_path = match args.get(1) {
Some(arg) => arg,
None => {
@ -21,11 +18,11 @@ pub fn main(args: Vec<String>) {
exit(1);
}
Ok(())
}
fn build_image(config_path: &str) -> Result<()> {
fn build_image(config_path: &str) -> Result<(), Box<dyn std::error::Error>> {
let conf = config::BuildConfig::load(config_path)?;
let mut builder = build::UpdateBuilder::new(conf);
builder.build()
Ok(builder.build()?)
}

View File

@ -1,16 +1,14 @@
use clap::App;
use clap::ArgMatches;
use libcitadel::{Result,RealmFS,Logger,LogLevel};
use libcitadel::{RealmFS, Logger, LogLevel};
use libcitadel::util::is_euid_root;
use clap::SubCommand;
use clap::AppSettings::*;
use clap::Arg;
use libcitadel::ResizeSize;
use std::process::exit;
pub fn main(args: Vec<String>) {
pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
Logger::set_log_level(LogLevel::Debug);
let app = App::new("citadel-realmfs")
@ -83,18 +81,15 @@ is the final absolute size of the image.")
("activate", Some(m)) => activate(m),
("deactivate", Some(m)) => deactivate(m),
_ => image_info(&matches),
};
}?;
if let Err(ref e) = result {
eprintln!("Error: {}", e);
exit(1);
}
Ok(())
}
fn realmfs_image(arg_matches: &ArgMatches) -> Result<RealmFS> {
fn realmfs_image(arg_matches: &ArgMatches) -> Result<RealmFS, Box<dyn std::error::Error>> {
let image = match arg_matches.value_of("image") {
Some(s) => s,
None => bail!("Image argument required."),
None => panic!("Image argument required."),
};
let realmfs = if RealmFS::is_valid_name(image) {
@ -102,22 +97,26 @@ fn realmfs_image(arg_matches: &ArgMatches) -> Result<RealmFS> {
} 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);
panic!(
"Not a valid realmfs name or path to realmfs image file: {}",
image
);
};
Ok(realmfs)
}
fn image_info(arg_matches: &ArgMatches) -> Result<()> {
fn image_info(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?;
print!("{}", String::from_utf8(img.header().metainfo_bytes())?);
Ok(())
}
fn parse_resize_size(s: &str) -> Result<ResizeSize> {
fn parse_resize_size(s: &str) -> Result<ResizeSize, Box<dyn std::error::Error>> {
let unit = s.chars().last().filter(|c| c.is_alphabetic());
let skip = if s.starts_with('+') { 1 } else { 0 };
let size = s.chars()
let size = s
.chars()
.skip(skip)
.take_while(|c| c.is_numeric())
.collect::<String>()
@ -127,68 +126,71 @@ fn parse_resize_size(s: &str) -> Result<ResizeSize> {
let sz = match unit {
Some('g') | Some('G') => ResizeSize::gigs(size),
Some('m') | Some('M') => ResizeSize::megs(size),
Some(c) => bail!("Unknown size unit '{}'", c),
Some(c) => panic!("Unknown size unit '{}'", c),
None => ResizeSize::blocks(size),
};
Ok(sz)
}
fn resize(arg_matches: &ArgMatches) -> Result<()> {
fn resize(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
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)
img.resize_grow_by(size)?;
} else {
img.resize_grow_to(size)
img.resize_grow_to(size)?;
}
Ok(())
}
fn autoresize(arg_matches: &ArgMatches) -> Result<()> {
fn autoresize(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?;
if let Some(size) = img.auto_resize_size() {
img.resize_grow_to(size)
img.resize_grow_to(size)?;
} else {
info!("RealmFS image {} has sufficient free space, doing nothing", img.path().display());
info!(
"RealmFS image {} has sufficient free space, doing nothing",
img.path().display()
);
}
Ok(())
}
}
fn fork(arg_matches: &ArgMatches) -> Result<()> {
fn fork(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?;
let forkname = match arg_matches.value_of("forkname") {
Some(name) => name,
None => bail!("No fork name argument"),
None => panic!("No fork name argument"),
};
if !RealmFS::is_valid_name(forkname) {
bail!("Not a valid RealmFS image name '{}'", forkname);
panic!("Not a valid RealmFS image name '{}'", forkname);
}
if RealmFS::named_image_exists(forkname) {
bail!("A RealmFS image named '{}' already exists", forkname);
panic!("A RealmFS image named '{}' already exists", forkname);
}
img.fork(forkname)?;
Ok(())
}
fn update(arg_matches: &ArgMatches) -> Result<()> {
fn update(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
if !is_euid_root() {
bail!("RealmFS updates must be run as root");
panic!("RealmFS updates must be run as root");
}
let img = realmfs_image(arg_matches)?;
img.interactive_update(Some("icy"))?;
Ok(())
}
fn activate(arg_matches: &ArgMatches) -> Result<()> {
fn activate(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?;
let img_arg = arg_matches.value_of("image").unwrap();
@ -201,7 +203,7 @@ fn activate(arg_matches: &ArgMatches) -> Result<()> {
Ok(())
}
fn deactivate(arg_matches: &ArgMatches) -> Result<()> {
fn deactivate(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = realmfs_image(arg_matches)?;
let img_arg = arg_matches.value_of("image").unwrap();
if !img.is_activated() {

View File

@ -3,7 +3,7 @@ use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use libcitadel::{Realm, Realms, Result, util};
use libcitadel::{Realm, Realms, util};
use crate::sync::parser::DesktopFileParser;
use std::fs::DirEntry;
use crate::sync::desktop_file::DesktopFile;
@ -24,7 +24,6 @@ struct DesktopItem {
}
impl DesktopItem {
fn new(path: PathBuf, mtime: SystemTime) -> Self {
DesktopItem { path, mtime }
}
@ -46,7 +45,7 @@ impl DesktopItem {
impl DesktopFileSync {
pub const CITADEL_APPLICATIONS: &'static str = "/home/citadel/.local/share/applications";
pub fn sync_active_realms() -> Result<()> {
pub fn sync_active_realms() -> Result<(), Box<dyn std::error::Error>> {
let realms = Realms::load()?;
for realm in realms.active(true) {
let mut sync = DesktopFileSync::new(realm);
@ -72,7 +71,7 @@ impl DesktopFileSync {
DesktopFileSync { realm, items: HashSet::new(), icons }
}
pub fn run_sync(&mut self, clear: bool) -> Result<()> {
pub fn run_sync(&mut self, clear: bool) -> Result<(), Box<dyn std::error::Error>> {
IconSync::ensure_theme_index_exists()?;
@ -98,7 +97,7 @@ impl DesktopFileSync {
Ok(())
}
fn collect_source_files(&mut self, directory: impl AsRef<Path>) -> Result<()> {
fn collect_source_files(&mut self, directory: impl AsRef<Path>) -> Result<(), Box<dyn std::error::Error>> {
let mut directory = Realms::current_realm_symlink().join(directory.as_ref());
directory.push("share/applications");
if directory.exists() {
@ -119,16 +118,16 @@ impl DesktopFileSync {
}
}
pub fn clear_target_files() -> Result<()> {
util::read_directory(Self::CITADEL_APPLICATIONS, |dent| {
pub fn clear_target_files() -> Result<(), Box<dyn std::error::Error>> {
Ok(util::read_directory(Self::CITADEL_APPLICATIONS, |dent| {
util::remove_file(dent.path())
})
})?)
}
fn remove_missing_target_files(&mut self) -> Result<()> {
fn remove_missing_target_files(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let sources = self.source_filenames();
let prefix = format!("realm-{}.", self.realm.name());
util::read_directory(Self::CITADEL_APPLICATIONS, |dent| {
Ok(util::read_directory(Self::CITADEL_APPLICATIONS, |dent| {
if let Some(filename) = dent.file_name().to_str() {
if filename.starts_with(&prefix) && !sources.contains(filename) {
let path = dent.path();
@ -137,7 +136,7 @@ impl DesktopFileSync {
}
}
Ok(())
})
})?)
}
fn mtime(path: &Path) -> Option<SystemTime> {
@ -156,7 +155,7 @@ impl DesktopFileSync {
.collect()
}
fn synchronize_items(&self) -> Result<()> {
fn synchronize_items(&self) -> Result<(), Box<dyn std::error::Error>> {
for item in &self.items {
let target = Path::new(Self::CITADEL_APPLICATIONS).join(item.filename());
if item.is_newer_than(&target) {
@ -180,7 +179,7 @@ impl DesktopFileSync {
}
}
fn sync_item(&self, item: &DesktopItem) -> Result<()> {
fn sync_item(&self, item: &DesktopItem) -> Result<(), Box<dyn std::error::Error>> {
let mut dfp = DesktopFileParser::parse_from_path(&item.path, "/usr/libexec/citadel-run ")?;
if dfp.is_showable() {
self.sync_item_icon(&mut dfp);

View File

@ -1,4 +1,4 @@
use libcitadel::{Result, Logger, LogLevel};
use libcitadel::{Logger, LogLevel};
mod desktop_file;
mod parser;
@ -19,8 +19,7 @@ pub const REALM_BASE_PATHS:&[&str] = &[
"home/.local/share/flatpak/exports"
];
pub fn main(args: Vec<String>) {
pub fn main(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
if has_arg(&args, "-v") {
Logger::set_log_level(LogLevel::Debug);
} else {
@ -37,12 +36,14 @@ pub fn main(args: Vec<String>) {
println!("Desktop file sync failed: {}", e);
}
}
Ok(())
}
fn sync(clear: bool) -> Result<()> {
fn sync(clear: bool) -> Result<(), Box<dyn std::error::Error>> {
if let Some(mut sync) = DesktopFileSync::new_current() {
sync.run_sync(clear)
sync.run_sync(clear)?
} else {
DesktopFileSync::clear_target_files()
DesktopFileSync::clear_target_files()?
}
Ok(())
}

View File

@ -1,6 +1,6 @@
use std::path::{Path, PathBuf};
use libcitadel::{Result, Partition, ResourceImage, ImageHeader, LogLevel, Logger, util};
use libcitadel::{Partition, ResourceImage, ImageHeader, LogLevel, Logger, util};
use crate::update::kernel::{KernelInstaller, KernelVersion};
use std::collections::HashSet;
use std::fs::{DirEntry, File};
@ -16,7 +16,7 @@ const FLAG_QUIET: u32 = 0x04;
const RESOURCES_DIRECTORY: &str = "/storage/resources";
const TEMP_DIRECTORY: &str = "/storage/resources/tmp";
pub fn main(args: Vec<String>) {
pub fn main(args: Vec<String>) -> std::result::Result<(), Box<dyn std::error::Error>> {
let mut args = args.iter().skip(1);
let mut flags = 0;
@ -34,7 +34,7 @@ pub fn main(args: Vec<String>) {
Logger::set_log_level(LogLevel::Debug);
} else if arg == "--choose-rootfs" {
let _ = choose_install_partition(true);
return;
return Ok(())
} else {
let path = Path::new(arg);
if let Err(e) = install_image(path, flags) {
@ -42,12 +42,13 @@ pub fn main(args: Vec<String>) {
}
}
}
Ok(())
}
// Search directory containing installed image files for an
// image file that has an identical shasum and abort the installation
// if a duplicate is found.
fn detect_duplicates(header: &ImageHeader) -> Result<()> {
fn detect_duplicates(header: &ImageHeader) -> Result<(), Box<dyn std::error::Error>> {
let metainfo = header.metainfo();
let channel = metainfo.channel();
let shasum = metainfo.shasum();
@ -61,17 +62,20 @@ fn detect_duplicates(header: &ImageHeader) -> Result<()> {
return Ok(())
}
util::read_directory(&resource_dir, |dent| {
Ok(util::read_directory(&resource_dir, |dent| {
if let Ok(hdr) = ImageHeader::from_file(dent.path()) {
if hdr.metainfo().shasum() == shasum {
bail!("A duplicate image file with the same shasum already exists at {}", dent.path().display());
panic!(
"A duplicate image file with the same shasum already exists at {}",
dent.path().display()
);
}
}
Ok(())
})
})?)
}
fn create_tmp_copy(path: &Path) -> Result<PathBuf> {
fn create_tmp_copy(path: &Path) -> Result<PathBuf, Box<dyn std::error::Error>> {
if !Path::new(TEMP_DIRECTORY).exists() {
util::create_dir(TEMP_DIRECTORY)?;
}
@ -93,12 +97,12 @@ fn create_tmp_copy(path: &Path) -> Result<PathBuf> {
Ok(path)
}
fn install_image(path: &Path, flags: u32) -> Result<()> {
fn install_image(path: &Path, flags: u32) -> Result<(), Box<dyn std::error::Error>> {
if !path.exists() || path.file_name().is_none() {
bail!("file path {} does not exist", path.display());
panic!("file path {} does not exist", path.display());
}
if !util::is_euid_root() {
bail!("Image updates must be installed by root user");
panic!("Image updates must be installed by root user");
}
let header = ImageHeader::from_file(path)?;
@ -114,13 +118,13 @@ fn install_image(path: &Path, flags: u32) -> Result<()> {
"kernel" => install_kernel_image(&mut image),
"extra" => install_extra_image(&image),
"rootfs" => install_rootfs_image(&image, flags),
image_type => bail!("Unknown image type: {}", image_type),
image_type => panic!("Unknown image type: {}", image_type),
}
}
// Prepare the image file for installation by decompressing and generating
// dmverity hash tree.
fn prepare_image(image: &ResourceImage, flags: u32) -> Result<()> {
fn prepare_image(image: &ResourceImage, flags: u32) -> Result<(), Box<dyn std::error::Error>> {
if image.is_compressed() {
image.decompress(false)?;
}
@ -129,7 +133,7 @@ fn prepare_image(image: &ResourceImage, flags: u32) -> Result<()> {
info!("Verifying sha256 hash of image");
let shasum = image.generate_shasum()?;
if shasum != image.metainfo().shasum() {
bail!("image file does not have expected sha256 value");
panic!("image file does not have expected sha256 value");
}
}
@ -139,24 +143,28 @@ fn prepare_image(image: &ResourceImage, flags: u32) -> Result<()> {
Ok(())
}
fn install_extra_image(image: &ResourceImage) -> Result<()> {
fn install_extra_image(image: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
let filename = format!("citadel-extra-{:03}.img", image.header().metainfo().version());
install_image_file(image, filename.as_str())?;
remove_old_extra_images(image)?;
Ok(())
}
fn remove_old_extra_images(image: &ResourceImage) -> Result<()> {
fn remove_old_extra_images(image: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
let new_meta = image.header().metainfo();
let shasum = new_meta.shasum();
let target_dir = target_directory(image)?;
util::read_directory(&target_dir, |dent| {
Ok(util::read_directory(&target_dir, |dent| {
let path = dent.path();
maybe_remove_old_extra_image(&path, shasum)
})
maybe_remove_old_extra_image(&path, shasum).unwrap();
Ok(())
})?)
}
fn maybe_remove_old_extra_image(path: &Path, shasum: &str) -> Result<()> {
fn maybe_remove_old_extra_image(
path: &Path,
shasum: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let header = ImageHeader::from_file(&path)?;
if !header.is_magic_valid() {
return Ok(());
@ -172,16 +180,16 @@ fn maybe_remove_old_extra_image(path: &Path, shasum: &str) -> Result<()> {
Ok(())
}
fn install_kernel_image(image: &mut ResourceImage) -> Result<()> {
fn install_kernel_image(image: &mut ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
if !Path::new("/boot/loader/loader.conf").exists() {
bail!("failed to automount /boot partition. Please manually mount correct partition.");
panic!("failed to automount /boot partition. Please manually mount correct partition.");
}
let metainfo = image.header().metainfo();
let version = metainfo.version();
let kernel_version = match metainfo.kernel_version() {
Some(kv) => kv,
None => bail!("kernel image does not have kernel version field"),
None => panic!("kernel image does not have kernel version field"),
};
info!("kernel version is {}", kernel_version);
install_kernel_file(image, &kernel_version)?;
@ -194,7 +202,7 @@ fn install_kernel_image(image: &mut ResourceImage) -> Result<()> {
let mut remove_paths = Vec::new();
util::read_directory(&image_dir, |dent| {
let path = dent.path();
if is_unused_kernel_image(&path, &all_versions)? {
if is_unused_kernel_image(&path, &all_versions).unwrap() {
remove_paths.push(path);
}
Ok(())
@ -206,7 +214,7 @@ fn install_kernel_image(image: &mut ResourceImage) -> Result<()> {
Ok(())
}
fn is_unused_kernel_image(path: &Path, versions: &HashSet<String>) -> Result<bool> {
fn is_unused_kernel_image(path: &Path, versions: &HashSet<String>) -> Result<bool, Box<dyn std::error::Error>> {
let header = ImageHeader::from_file(path)?;
if !header.is_magic_valid() {
return Ok(false);
@ -226,23 +234,25 @@ fn is_unused_kernel_image(path: &Path, versions: &HashSet<String>) -> Result<boo
Ok(false)
}
fn install_kernel_file(image: &mut ResourceImage, kernel_version: &str) -> Result<()> {
fn install_kernel_file(
image: &mut ResourceImage,
kernel_version: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let mountpoint = Path::new("/run/citadel/images/kernel-install.mountpoint");
info!("Temporarily mounting kernel resource image");
let mut handle = image.mount_at(mountpoint)?;
let kernel_path = mountpoint.join("kernel/bzImage");
if !kernel_path.exists() {
handle.unmount()?;
bail!("kernel not found in kernel resource image at /kernel/bzImage")
panic!("kernel not found in kernel resource image at /kernel/bzImage")
}
let result = KernelInstaller::install_kernel(&kernel_path, kernel_version);
KernelInstaller::install_kernel(&kernel_path, kernel_version)?;
info!("Unmounting kernel resource image");
handle.unmount()?;
result
Ok(handle.unmount()?)
}
fn all_boot_kernel_versions() -> Result<HashSet<String>> {
fn all_boot_kernel_versions() -> Result<HashSet<String>, Box<dyn std::error::Error>> {
let mut result = HashSet::new();
util::read_directory("/boot", |dent| {
if is_kernel_dirent(&dent) {
@ -264,7 +274,10 @@ fn is_kernel_dirent(dirent: &DirEntry) -> bool {
}
}
fn install_image_file(image: &ResourceImage, filename: &str) -> Result<()> {
fn install_image_file(
image: &ResourceImage,
filename: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let image_dir = target_directory(image)?;
let image_dest = image_dir.join(filename);
if image_dest.exists() {
@ -275,14 +288,14 @@ fn install_image_file(image: &ResourceImage, filename: &str) -> Result<()> {
Ok(())
}
fn target_directory(image: &ResourceImage) -> Result<PathBuf> {
fn target_directory(image: &ResourceImage) -> Result<PathBuf, Box<dyn std::error::Error>> {
let metainfo = image.header().metainfo();
let channel = metainfo.channel();
validate_channel_name(channel)?;
Ok(Path::new("/storage/resources").join(channel))
}
fn rotate(path: &Path) -> Result<()> {
fn rotate(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
if !path.exists() || path.file_name().is_none() {
return Ok(());
}
@ -293,14 +306,17 @@ fn rotate(path: &Path) -> Result<()> {
Ok(())
}
fn validate_channel_name(channel: &str) -> Result<()> {
fn validate_channel_name(channel: &str) -> Result<(), Box<dyn std::error::Error>> {
if !channel.chars().all(|c| c.is_ascii_lowercase()) {
bail!("image has invalid channel name '{}'", channel);
panic!("image has invalid channel name '{}'", channel);
}
Ok(())
}
fn install_rootfs_image(image: &ResourceImage, flags: u32) -> Result<()> {
fn install_rootfs_image(
image: &ResourceImage,
flags: u32,
) -> Result<(), Box<dyn std::error::Error>> {
let quiet = flags & FLAG_QUIET != 0;
let partition = choose_install_partition(!quiet)?;
@ -315,7 +331,7 @@ fn install_rootfs_image(image: &ResourceImage, flags: u32) -> Result<()> {
Ok(())
}
fn clear_prefer_boot() -> Result<()> {
fn clear_prefer_boot() -> Result<(), Box<dyn std::error::Error>> {
for mut p in Partition::rootfs_partitions()? {
if p.is_initialized() && p.header().has_flag(ImageHeader::FLAG_PREFER_BOOT) {
p.clear_flag_and_write(ImageHeader::FLAG_PREFER_BOOT)?;
@ -332,7 +348,7 @@ fn bool_to_yesno(val: bool) -> &'static str {
}
}
fn choose_install_partition(verbose: bool) -> Result<Partition> {
fn choose_install_partition(verbose: bool) -> Result<Partition, Box<dyn std::error::Error>> {
let partitions = Partition::rootfs_partitions()?;
if verbose {
@ -362,5 +378,5 @@ fn choose_install_partition(verbose: bool) -> Result<Partition> {
return Ok(p.clone())
}
}
bail!("no suitable install partition found")
panic!("no suitable install partition found")
}