forked from brl/citadel-tools
Improve error handling by using std lib rust code
This commit is contained in:
parent
b7e6ee3b3c
commit
04457a47ef
@ -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,9 +166,11 @@ fn decompress_images(sync: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decompress_one_image_sync(image: ResourceImage) -> Result<()> {
|
||||
let start = Instant::now();
|
||||
info!("Decompressing {}", image.path().display());
|
||||
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)
|
||||
.map_err(|e| format_err!("Failed to decompress image file {}: {}", image.path().display(), e))?;
|
||||
cmd!("/usr/bin/du", "-h {}", image.path().display())?;
|
||||
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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())?);
|
||||
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 {
|
||||
@ -116,15 +117,15 @@ fn info_signature(img: &ResourceImage) -> Result<()> {
|
||||
},
|
||||
None => { println!("No public key found for channel '{}'", img.metainfo().channel()) },
|
||||
}
|
||||
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)?;
|
||||
print!("{}",String::from_utf8(img.header().metainfo_bytes())?);
|
||||
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 {
|
||||
@ -324,10 +333,10 @@ fn choose_install_partition(verbose: bool) -> Result<Partition> {
|
||||
if verbose {
|
||||
info!("Choosing {} because it is not mounted", p.path().display());
|
||||
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")
|
||||
}
|
||||
|
@ -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 {
|
||||
@ -96,7 +99,7 @@ fn choose_disk() -> Result<Option<Disk>> {
|
||||
}
|
||||
if let Ok(n) = line.parse::<usize>() {
|
||||
if n > 0 && n <= disks.len() {
|
||||
return Ok(Some(disks[n-1].clone()));
|
||||
return Ok(Some(disks[n - 1].clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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()?)
|
||||
}
|
@ -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,93 +97,100 @@ 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>()
|
||||
.parse::<usize>()
|
||||
.map_err(|_| format_err!("Unable to parse size value '{}'",s))?;
|
||||
.map_err(|_| format_err!("Unable to parse size value '{}'", s))?;
|
||||
|
||||
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());
|
||||
Ok(())
|
||||
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() {
|
||||
|
@ -1,9 +1,9 @@
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::{Path,PathBuf};
|
||||
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;
|
||||
@ -17,14 +17,13 @@ pub struct DesktopFileSync {
|
||||
icons: Option<IconSync>,
|
||||
}
|
||||
|
||||
#[derive(Eq,PartialEq,Hash)]
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
struct DesktopItem {
|
||||
path: PathBuf,
|
||||
mtime: SystemTime,
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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)?;
|
||||
@ -113,14 +117,14 @@ fn install_image(path: &Path, flags: u32) -> Result<()> {
|
||||
match image.metainfo().image_type() {
|
||||
"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),
|
||||
"rootfs" => install_rootfs_image(&image, flags),
|
||||
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")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user