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::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))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(())
|
||||||
|
@ -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(())
|
||||||
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()?)
|
||||||
}
|
}
|
@ -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() {
|
||||||
|
@ -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);
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user