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

View File

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

View File

@ -1,10 +1,10 @@
use std::path::Path; use std::path::Path;
use std::process::{Command,Stdio}; 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; 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())?; let mut p = choose_boot_partiton(true, CommandLine::revert_rootfs())?;
if CommandLine::noverity() { if CommandLine::noverity() {
setup_partition_unverified(&p) 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() { if CommandLine::noverity() {
setup_resource_unverified(&rootfs) setup_resource_unverified(&rootfs)
} else { } 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() { if img.is_compressed() {
img.decompress(false)?; img.decompress(false)?;
} }
@ -30,25 +30,31 @@ fn setup_resource_unverified(img: &ResourceImage) -> Result<()> {
setup_linear_mapping(loopdev.device()) 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()?; let _ = img.setup_verity_device()?;
Ok(()) 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)"); info!("Creating /dev/mapper/rootfs device with linear device mapping of partition (no verity)");
setup_linear_mapping(p.path()) 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"); info!("Creating /dev/mapper/rootfs dm-verity device");
if !CommandLine::nosignatures() { if !CommandLine::nosignatures() {
if !p.has_public_key() { 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() { if !p.is_signature_valid() {
p.write_status(ImageHeader::STATUS_BAD_SIG)?; 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()); info!("Image signature is valid for channel {}", p.metainfo().channel());
} }
@ -56,7 +62,7 @@ fn setup_partition_verified(p: &mut Partition) -> Result<()> {
Ok(()) 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 dev = BlockDev::open_ro(blockdev)?;
let table = format!("0 {} linear {} 0", dev.nsectors()?, blockdev.display()); let table = format!("0 {} linear {} 0", dev.nsectors()?, blockdev.display());
@ -70,7 +76,9 @@ fn setup_linear_mapping(blockdev: &Path) -> Result<()> {
.success(); .success();
if !ok { 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(()) Ok(())
} }
@ -94,7 +102,8 @@ fn choose_revert_partition(best: Option<Partition>) -> Option<Partition> {
best 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()?; let mut partitions = Partition::rootfs_partitions()?;
if scan { if scan {
@ -136,14 +145,17 @@ fn compare_boot_partitions(a: Option<Partition>, b: Partition) -> Option<Partiti
} }
// Compare versions and channels // Compare versions and channels
let a_v = a.metainfo().version(); let meta_a = a.metainfo();
let b_v = b.metainfo().version(); let meta_b = b.metainfo();
let ver_a = meta_a.version();
let ver_b = meta_b.version();
// Compare versions only if channels match // Compare versions only if channels match
if a.metainfo().channel() == b.metainfo().channel() { if a.metainfo().channel() == b.metainfo().channel() {
if a_v > b_v { if ver_a > ver_b {
return Some(a); return Some(a);
} else if b_v > a_v { } else if ver_b > ver_a {
return Some(b); return Some(b);
} }
} }

View File

@ -3,11 +3,10 @@ use std::process::exit;
use clap::{App,Arg,SubCommand,ArgMatches}; use clap::{App,Arg,SubCommand,ArgMatches};
use clap::AppSettings::*; use clap::AppSettings::*;
use libcitadel::{Result, ResourceImage, Logger, LogLevel, Partition, KeyPair, ImageHeader, util}; use libcitadel::{ResourceImage, Logger, LogLevel, Partition, KeyPair, ImageHeader, util};
use hex; 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") let app = App::new("citadel-image")
.about("Citadel update image builder") .about("Citadel update image builder")
.settings(&[ArgRequiredElseHelp,ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder]) .settings(&[ArgRequiredElseHelp,ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder])
@ -91,16 +90,18 @@ pub fn main(args: Vec<String>) {
println!("Error: {}", e); println!("Error: {}", e);
exit(1); 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)?; let img = load_image(arg_matches)?;
print!("{}", String::from_utf8(img.header().metainfo_bytes())?); print!("{}", String::from_utf8(img.header().metainfo_bytes())?);
info_signature(&img)?; info_signature(&img)?;
Ok(()) Ok(())
} }
fn info_signature(img: &ResourceImage) -> Result<()> { fn info_signature(img: &ResourceImage) -> Result<(), Box<dyn std::error::Error>> {
if img.header().has_signature() { if img.header().has_signature() {
println!("Signature: {}", hex::encode(&img.header().signature())); println!("Signature: {}", hex::encode(&img.header().signature()));
} else { } else {
@ -118,13 +119,13 @@ fn info_signature(img: &ResourceImage) -> Result<()> {
} }
Ok(()) Ok(())
} }
fn metainfo(arg_matches: &ArgMatches) -> Result<()> { fn metainfo(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
print!("{}", String::from_utf8(img.header().metainfo_bytes())?); print!("{}", String::from_utf8(img.header().metainfo_bytes())?);
Ok(()) 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)?; let img = load_image(arg_matches)?;
if img.has_verity_hashtree() { if img.has_verity_hashtree() {
info!("Image already has dm-verity hashtree appended, doing nothing."); info!("Image already has dm-verity hashtree appended, doing nothing.");
@ -134,7 +135,7 @@ fn generate_verity(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) 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 img = load_image(arg_matches)?;
let ok = img.verify_verity()?; let ok = img.verify_verity()?;
if ok { if ok {
@ -145,7 +146,7 @@ fn verify(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) 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 img = load_image(arg_matches)?;
let shasum = img.generate_shasum()?; let shasum = img.generate_shasum()?;
if shasum == img.metainfo().shasum() { if shasum == img.metainfo().shasum() {
@ -158,22 +159,22 @@ fn verify_shasum(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) 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"); let path = arg_matches.value_of("path").expect("path argument missing");
if !Path::new(path).exists() { 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)?; let img = ResourceImage::from_path(path)?;
if !img.is_valid_image() { 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) 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") { if arg_matches.is_present("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)?;
@ -182,7 +183,7 @@ fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> {
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() {
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(()) Ok(())
} }
fn clear_prefer_boot() -> Result<()> { fn clear_prefer_boot() -> Result<(), Box<dyn std::error::Error>> {
for mut p in Partition::rootfs_partitions()? { for mut p in Partition::rootfs_partitions()? {
if p.is_initialized() && p.header().has_flag(ImageHeader::FLAG_PREFER_BOOT) { if p.is_initialized() && p.header().has_flag(ImageHeader::FLAG_PREFER_BOOT) {
p.clear_flag_and_write(ImageHeader::FLAG_PREFER_BOOT)?; p.clear_flag_and_write(ImageHeader::FLAG_PREFER_BOOT)?;
@ -205,13 +206,13 @@ fn clear_prefer_boot() -> Result<()> {
Ok(()) 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)?; let _img = load_image(arg_matches)?;
info!("Not implemented yet"); info!("Not implemented yet");
Ok(()) 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 source = arg_matches.value_of("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();
@ -220,12 +221,12 @@ fn install_image(arg_matches: &ArgMatches) -> Result<()> {
// XXX verify signature? // XXX verify signature?
if !(metainfo.image_type() == "kernel" || metainfo.image_type() == "extra") { 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()?; let shasum = img.generate_shasum()?;
if shasum != img.metainfo().shasum() { if shasum != img.metainfo().shasum() {
bail!("Image shasum does not match metainfo"); panic!("Image shasum does not match metainfo");
} }
img.generate_verity_hashtree()?; img.generate_verity_hashtree()?;
@ -233,10 +234,10 @@ fn install_image(arg_matches: &ArgMatches) -> Result<()> {
let filename = if metainfo.image_type() == "kernel" { let filename = if metainfo.image_type() == "kernel" {
let kernel_version = match metainfo.kernel_version() { let kernel_version = match metainfo.kernel_version() {
Some(version) => 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 == '/') { 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()) format!("citadel-kernel-{}-{:03}.img", kernel_version, metainfo.version())
} else { } else {
@ -244,33 +245,38 @@ fn install_image(arg_matches: &ArgMatches) -> Result<()> {
}; };
if !metainfo.channel().chars().all(|c| c.is_ascii_lowercase()) { 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_dir = Path::new("/storage/resources").join(metainfo.channel());
let image_dest = image_dir.join(filename); let image_dest = image_dir.join(filename);
if image_dest.exists() { if image_dest.exists() {
rotate(&image_dest)?; 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() { if !path.exists() || path.file_name().is_none() {
return Ok(()); return Ok(());
} }
let filename = path.file_name().unwrap(); let filename = path.file_name().unwrap();
let dot_zero = path.with_file_name(format!("{}.0", filename.to_string_lossy())); let dot_zero = path.with_file_name(format!("{}.0", filename.to_string_lossy()));
util::remove_file(&dot_zero)?; 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(); let keypair = KeyPair::generate();
println!("keypair = \"{}\"", keypair.to_hex()); println!("keypair = \"{}\"", keypair.to_hex());
Ok(()) Ok(())
} }
fn decompress(arg_matches: &ArgMatches) -> Result<()> { fn decompress(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
if !img.is_compressed() { if !img.is_compressed() {
info!("Image is not compressed, not decompressing."); info!("Image is not compressed, not decompressing.");
@ -280,7 +286,7 @@ fn decompress(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) Ok(())
} }
fn bless() -> Result<()> { fn bless() -> Result<(), Box<dyn std::error::Error>> {
for mut p in Partition::rootfs_partitions()? { for mut p in Partition::rootfs_partitions()? {
if p.is_initialized() && p.is_mounted() { if p.is_initialized() && p.is_mounted() {
p.bless()?; 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()?; let partitions = Partition::rootfs_partitions()?;
if verbose { if verbose {
@ -314,9 +320,12 @@ fn choose_install_partition(verbose: bool) -> Result<Partition> {
for p in &partitions { for p in &partitions {
if !p.is_mounted() && !p.is_initialized() { if !p.is_mounted() && !p.is_initialized() {
if verbose { 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 { for p in &partitions {
@ -326,8 +335,8 @@ fn choose_install_partition(verbose: bool) -> Result<Partition> {
info!("Header metainfo:"); info!("Header metainfo:");
print!("{}", String::from_utf8(p.header().metainfo_bytes())?); 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::io::{self,Write};
use std::path::Path; use std::path::Path;
use libcitadel::Result;
use super::disk::Disk; use super::disk::Disk;
use rpassword; use rpassword;
use crate::install::installer::Installer; 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 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"; 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()? { let disk = match choose_disk()? {
Some(disk) => disk, Some(disk) => disk,
None => return Ok(false), None => return Ok(false),
@ -33,7 +32,7 @@ pub fn run_cli_install() -> Result<bool> {
Ok(true) 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())?; let disk = find_disk_by_path(target.as_ref())?;
display_disk(&disk); display_disk(&disk);
@ -55,11 +54,15 @@ pub fn run_cli_install_with<P: AsRef<Path>>(target: P) -> Result<bool> {
Ok(true) 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); let mut install = Installer::new(disk.path(), &citadel_passphrase, &passphrase);
install.set_install_syslinux(true); install.set_install_syslinux(true);
install.verify()?; install.verify()?;
install.run() Ok(install.run()?)
} }
fn display_disk(disk: &Disk) { fn display_disk(disk: &Disk) {
@ -70,22 +73,22 @@ fn display_disk(disk: &Disk) {
println!(); 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() { 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()? { for disk in Disk::probe_all()? {
if disk.path() == path { if disk.path() == path {
return Ok(disk.clone()); 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()?; let disks = Disk::probe_all()?;
if disks.is_empty() { if disks.is_empty() {
bail!("no disks found."); panic!("no disks found.");
} }
loop { loop {
@ -111,7 +114,7 @@ fn prompt_choose_disk(disks: &[Disk]) {
let _ = io::stdout().flush(); let _ = io::stdout().flush();
} }
fn read_line() -> Result<String> { fn read_line() -> Result<String, Box<dyn std::error::Error>> {
let mut input = String::new(); let mut input = String::new();
io::stdin().read_line(&mut input) io::stdin().read_line(&mut input)
.map_err(context!("error reading line from stdin"))?; .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!("Are you sure you want to completely erase this this device?");
println!(); println!();
println!(" Device: {}", disk.path().display()); println!(" Device: {}", disk.path().display());
@ -158,4 +161,3 @@ fn confirm_install(disk: &Disk) -> Result<bool> {
let answer = read_line()?; let answer = read_line()?;
Ok(answer == "YES") Ok(answer == "YES")
} }

View File

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

View File

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

View File

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

View File

@ -17,42 +17,37 @@ mod realmfs;
mod sync; mod sync;
mod update; mod update;
fn main() { fn main() -> Result<(), Box<dyn std::error::Error>> {
let exe = match env::current_exe() { let exe = env::current_exe()?;
Ok(path) => path,
Err(_e) => {
return;
},
};
let args = env::args().collect::<Vec<String>>(); let args = env::args().collect::<Vec<String>>();
if exe == Path::new("/usr/libexec/citadel-boot") { if exe == Path::new("/usr/libexec/citadel-boot") {
boot::main(args); boot::main(args)
} else if exe == Path::new("/usr/libexec/citadel-install") { } 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") { } 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(args)
} else if exe == Path::new("/usr/bin/citadel-realmfs") { } else if exe == Path::new("/usr/bin/citadel-realmfs") {
realmfs::main(args); realmfs::main(args)
} 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") {
sync::main(args); sync::main(args)
} else if exe == Path::new("/usr/libexec/citadel-run") { } 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")) { } 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")) { } else if exe.file_name() == Some(OsStr::new("citadel-tool")) {
dispatch_command(args); dispatch_command(args)
} else { } 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) { if let Some(command) = args.get(1) {
match command.as_str() { match command.as_str() {
"boot" => boot::main(rebuild_args("citadel-boot", args)), "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)), "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)),
"run" => do_citadel_run(rebuild_args("citadel-run", args)), "run" => do_citadel_run(rebuild_args("citadel-run", args)),
_ => println!("Error: unknown command {}", command), _ => throw_err(command),
} }
} else { } 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> { fn rebuild_args(command: &str, args: Vec<String>) -> Vec<String> {
iter::once(command.to_string()) iter::once(command.to_string())
.chain(args.into_iter().skip(2)) .chain(args.into_iter().skip(2))
.collect() .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) { 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 std::process::exit;
use libcitadel::Result;
mod config; mod config;
mod build; 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) { let config_path = match args.get(1) {
Some(arg) => arg, Some(arg) => arg,
None => { None => {
@ -21,11 +18,11 @@ pub fn main(args: Vec<String>) {
exit(1); 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 conf = config::BuildConfig::load(config_path)?;
let mut builder = build::UpdateBuilder::new(conf); let mut builder = build::UpdateBuilder::new(conf);
builder.build() Ok(builder.build()?)
} }

View File

@ -1,16 +1,14 @@
use clap::App; use clap::App;
use clap::ArgMatches; use clap::ArgMatches;
use libcitadel::{Result,RealmFS,Logger,LogLevel}; use libcitadel::{RealmFS, Logger, LogLevel};
use libcitadel::util::is_euid_root; use libcitadel::util::is_euid_root;
use clap::SubCommand; use clap::SubCommand;
use clap::AppSettings::*; use clap::AppSettings::*;
use clap::Arg; use clap::Arg;
use libcitadel::ResizeSize; 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); Logger::set_log_level(LogLevel::Debug);
let app = App::new("citadel-realmfs") let app = App::new("citadel-realmfs")
@ -83,18 +81,15 @@ is the final absolute size of the image.")
("activate", Some(m)) => activate(m), ("activate", Some(m)) => activate(m),
("deactivate", Some(m)) => deactivate(m), ("deactivate", Some(m)) => deactivate(m),
_ => image_info(&matches), _ => image_info(&matches),
}; }?;
if let Err(ref e) = result { Ok(())
eprintln!("Error: {}", e);
exit(1);
}
} }
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") { let image = match arg_matches.value_of("image") {
Some(s) => s, Some(s) => s,
None => bail!("Image argument required."), None => panic!("Image argument required."),
}; };
let realmfs = if RealmFS::is_valid_name(image) { 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) { } else if RealmFS::is_valid_realmfs_image(image) {
RealmFS::load_from_path(image)? RealmFS::load_from_path(image)?
} else { } 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) 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)?; let img = realmfs_image(arg_matches)?;
print!("{}", String::from_utf8(img.header().metainfo_bytes())?); print!("{}", String::from_utf8(img.header().metainfo_bytes())?);
Ok(()) 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 unit = s.chars().last().filter(|c| c.is_alphabetic());
let skip = if s.starts_with('+') { 1 } else { 0 }; let skip = if s.starts_with('+') { 1 } else { 0 };
let size = s.chars() let size = s
.chars()
.skip(skip) .skip(skip)
.take_while(|c| c.is_numeric()) .take_while(|c| c.is_numeric())
.collect::<String>() .collect::<String>()
@ -127,68 +126,71 @@ fn parse_resize_size(s: &str) -> Result<ResizeSize> {
let sz = match unit { let sz = match unit {
Some('g') | Some('G') => ResizeSize::gigs(size), Some('g') | Some('G') => ResizeSize::gigs(size),
Some('m') | Some('M') => ResizeSize::megs(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), None => ResizeSize::blocks(size),
}; };
Ok(sz) 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)?; 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.value_of("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('+');
let size = parse_resize_size(size_arg)?; let size = parse_resize_size(size_arg)?;
if mode_add { if mode_add {
img.resize_grow_by(size) img.resize_grow_by(size)?;
} else { } 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)?; let img = realmfs_image(arg_matches)?;
if let Some(size) = img.auto_resize_size() { if let Some(size) = img.auto_resize_size() {
img.resize_grow_to(size) img.resize_grow_to(size)?;
} else { } 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(()) 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 img = realmfs_image(arg_matches)?;
let forkname = match arg_matches.value_of("forkname") { let forkname = match arg_matches.value_of("forkname") {
Some(name) => name, Some(name) => name,
None => bail!("No fork name argument"), None => panic!("No fork name argument"),
}; };
if !RealmFS::is_valid_name(forkname) { 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) { 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)?; img.fork(forkname)?;
Ok(()) Ok(())
} }
fn update(arg_matches: &ArgMatches) -> Result<()> { fn update(arg_matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
if !is_euid_root() { 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)?; let img = realmfs_image(arg_matches)?;
img.interactive_update(Some("icy"))?; img.interactive_update(Some("icy"))?;
Ok(()) 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 = realmfs_image(arg_matches)?;
let img_arg = arg_matches.value_of("image").unwrap(); let img_arg = arg_matches.value_of("image").unwrap();
@ -201,7 +203,7 @@ fn activate(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) 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 = realmfs_image(arg_matches)?;
let img_arg = arg_matches.value_of("image").unwrap(); let img_arg = arg_matches.value_of("image").unwrap();
if !img.is_activated() { if !img.is_activated() {

View File

@ -3,7 +3,7 @@ use std::ffi::OsStr;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::SystemTime; use std::time::SystemTime;
use libcitadel::{Realm, Realms, Result, util}; use libcitadel::{Realm, Realms, util};
use crate::sync::parser::DesktopFileParser; use crate::sync::parser::DesktopFileParser;
use std::fs::DirEntry; use std::fs::DirEntry;
use crate::sync::desktop_file::DesktopFile; use crate::sync::desktop_file::DesktopFile;
@ -24,7 +24,6 @@ struct DesktopItem {
} }
impl DesktopItem { impl DesktopItem {
fn new(path: PathBuf, mtime: SystemTime) -> Self { fn new(path: PathBuf, mtime: SystemTime) -> Self {
DesktopItem { path, mtime } DesktopItem { path, mtime }
} }
@ -46,7 +45,7 @@ impl DesktopItem {
impl DesktopFileSync { impl DesktopFileSync {
pub const CITADEL_APPLICATIONS: &'static str = "/home/citadel/.local/share/applications"; 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()?; let realms = Realms::load()?;
for realm in realms.active(true) { for realm in realms.active(true) {
let mut sync = DesktopFileSync::new(realm); let mut sync = DesktopFileSync::new(realm);
@ -72,7 +71,7 @@ impl DesktopFileSync {
DesktopFileSync { realm, items: HashSet::new(), icons } 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()?; IconSync::ensure_theme_index_exists()?;
@ -98,7 +97,7 @@ impl DesktopFileSync {
Ok(()) 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()); let mut directory = Realms::current_realm_symlink().join(directory.as_ref());
directory.push("share/applications"); directory.push("share/applications");
if directory.exists() { if directory.exists() {
@ -119,16 +118,16 @@ impl DesktopFileSync {
} }
} }
pub fn clear_target_files() -> Result<()> { pub fn clear_target_files() -> Result<(), Box<dyn std::error::Error>> {
util::read_directory(Self::CITADEL_APPLICATIONS, |dent| { Ok(util::read_directory(Self::CITADEL_APPLICATIONS, |dent| {
util::remove_file(dent.path()) 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 sources = self.source_filenames();
let prefix = format!("realm-{}.", self.realm.name()); 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 let Some(filename) = dent.file_name().to_str() {
if filename.starts_with(&prefix) && !sources.contains(filename) { if filename.starts_with(&prefix) && !sources.contains(filename) {
let path = dent.path(); let path = dent.path();
@ -137,7 +136,7 @@ impl DesktopFileSync {
} }
} }
Ok(()) Ok(())
}) })?)
} }
fn mtime(path: &Path) -> Option<SystemTime> { fn mtime(path: &Path) -> Option<SystemTime> {
@ -156,7 +155,7 @@ impl DesktopFileSync {
.collect() .collect()
} }
fn synchronize_items(&self) -> Result<()> { fn synchronize_items(&self) -> Result<(), Box<dyn std::error::Error>> {
for item in &self.items { for item in &self.items {
let target = Path::new(Self::CITADEL_APPLICATIONS).join(item.filename()); let target = Path::new(Self::CITADEL_APPLICATIONS).join(item.filename());
if item.is_newer_than(&target) { 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 ")?; let mut dfp = DesktopFileParser::parse_from_path(&item.path, "/usr/libexec/citadel-run ")?;
if dfp.is_showable() { if dfp.is_showable() {
self.sync_item_icon(&mut dfp); 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 desktop_file;
mod parser; mod parser;
@ -19,8 +19,7 @@ pub const REALM_BASE_PATHS:&[&str] = &[
"home/.local/share/flatpak/exports" "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") { if has_arg(&args, "-v") {
Logger::set_log_level(LogLevel::Debug); Logger::set_log_level(LogLevel::Debug);
} else { } else {
@ -37,12 +36,14 @@ pub fn main(args: Vec<String>) {
println!("Desktop file sync failed: {}", e); 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() { if let Some(mut sync) = DesktopFileSync::new_current() {
sync.run_sync(clear) sync.run_sync(clear)?
} else { } else {
DesktopFileSync::clear_target_files() DesktopFileSync::clear_target_files()?
} }
Ok(())
} }

View File

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