Refactor multiple tools into a single binary.

citadel-tool now installed with a hardlink for each binary tool and
dispatches on the exe path to the tool implementation. This makes
the build faster, uses less disk space, and makes it easier to
create new small tools.
This commit is contained in:
Bruce Leidl 2019-01-30 21:31:13 -05:00
parent dbdf0d4035
commit 2dc32d1f20
22 changed files with 320 additions and 361 deletions

44
Cargo.lock generated
View File

@ -95,37 +95,6 @@ dependencies = [
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "citadel-image"
version = "0.1.0"
dependencies = [
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"libcitadel 0.1.0",
"serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "citadel-install"
version = "0.1.0"
dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)",
"libcitadel 0.1.0",
"rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "citadel-mount"
version = "0.1.0"
dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)",
"libcitadel 0.1.0",
]
[[package]] [[package]]
name = "citadel-realms" name = "citadel-realms"
version = "0.1.0" version = "0.1.0"
@ -142,6 +111,19 @@ dependencies = [
"walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "citadel-tool"
version = "0.1.0"
dependencies = [
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"libcitadel 0.1.0",
"rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.32.0" version = "2.32.0"

View File

@ -1,2 +1,6 @@
[workspace] [workspace]
members = ["citadel-desktopd", "citadel-image", "citadel-install", "citadel-mount", "citadel-realms"] members = ["citadel-desktopd", "citadel-realms", "citadel-tool" ]
[profile.release]
lto = true
codegen-units = 1
incremental = false

View File

@ -1,12 +0,0 @@
[package]
name = "citadel-install"
version = "0.1.0"
authors = ["Bruce Leidl <bruce@subgraph.com>"]
homepage = "http://github.com/subgraph/citadel"
edition = "2018"
[dependencies]
libcitadel = { path = "../libcitadel" }
failure = "0.1.3"
libc = "0.2"
rpassword = "2.1.0"

View File

@ -1,10 +0,0 @@
[package]
name = "citadel-mount"
version = "0.1.0"
authors = ["Bruce Leidl <bruce@subgraph.com>"]
homepage = "http://github.com/subgraph/citadel"
[dependencies]
libcitadel = { path = "../libcitadel" }
failure = "0.1.3"
libc = "0.2"

View File

@ -1,94 +0,0 @@
#[macro_use] extern crate failure;
#[macro_use] extern crate libcitadel;
use std::process::exit;
use std::env;
use std::fs;
use libcitadel::{Result,CommandLine,set_verbose,format_error,ResourceImage,util};
mod rootfs;
/// mount command supports 4 subcommands
///
/// citadel-mount rootfs
/// citadel-mount kernel
/// citadel-mount extra
/// citadel-mount overlay
///
/// 'rootfs' creates the /dev/mapper/rootfs device which will be mounted as root filesystem
///
/// 'kernel' mounts a resource bundle containing kernel modules
/// 'extra' mounts a resource bundle containing extra files
/// 'overlay' mounts a tmpfs overlay over rootfs filesystem only if citadel.overlay is set
///
fn main() {
if CommandLine::verbose() {
set_verbose(true);
}
let mut args = env::args();
args.next();
let result = match args.next() {
Some(ref s) if s == "rootfs" => mount_rootfs(),
Some(ref s) if s == "kernel" => mount_kernel(),
Some(ref s) if s == "extra" => mount_extra(),
Some(ref s) if s == "overlay" => mount_overlay(),
_ => Err(format_err!("Bad or missing argument")),
};
if let Err(ref e) = result {
warn!("Failed: {}", format_error(e));
exit(1);
}
}
fn mount_rootfs() -> Result<()> {
info!("citadel-mount rootfs");
rootfs::setup_rootfs()
}
fn mount_kernel() -> Result<()> {
info!("citadel-mount kernel");
let mut image = ResourceImage::find("kernel")?;
image.mount()?;
Ok(())
}
fn mount_extra() -> Result<()> {
info!("citadel-mount extra");
let mut image = ResourceImage::find("extra")?;
image.mount()?;
Ok(())
}
fn mount_overlay() -> Result<()> {
if !CommandLine::overlay() {
info!("Not mounting rootfs overlay because citadel.overlay is not enabled");
return Ok(())
}
info!("Creating rootfs overlay");
info!("Moving /sysroot mount to /rootfs.ro");
fs::create_dir_all("/rootfs.ro")?;
util::exec_cmdline("/usr/bin/mount", "--make-private /")?;
util::exec_cmdline("/usr/bin/mount", "--move /sysroot /rootfs.ro")?;
info!("Mounting tmpfs on /rootfs.rw");
fs::create_dir_all("/rootfs.rw")?;
util::exec_cmdline("/usr/bin/mount", "-t tmpfs -orw,noatime,mode=755 rootfs.rw /rootfs.rw")?;
info!("Creating /rootfs.rw/work /rootfs.rw/upperdir");
fs::create_dir_all("/rootfs.rw/upperdir")?;
fs::create_dir_all("/rootfs.rw/work")?;
info!("Mounting overlay on /sysroot");
util::exec_cmdline("/usr/bin/mount", "-t overlay overlay -olowerdir=/rootfs.ro,upperdir=/rootfs.rw/upperdir,workdir=/rootfs.rw/work /sysroot")?;
info!("Moving /rootfs.ro and /rootfs.rw to new root");
fs::create_dir_all("/sysroot/rootfs.ro")?;
fs::create_dir_all("/sysroot/rootfs.rw")?;
util::exec_cmdline("/usr/bin/mount", "--move /rootfs.ro /sysroot/rootfs.ro")?;
util::exec_cmdline("/usr/bin/mount", "--move /rootfs.rw /sysroot/rootfs.rw")?;
Ok(())
}

View File

@ -1,14 +1,14 @@
[package] [package]
name = "citadel-image" name = "citadel-tool"
version = "0.1.0" version = "0.1.0"
authors = ["Bruce Leidl <bruce@subgraph.com>"] authors = ["Bruce Leidl <bruce@subgraph.com>"]
homepage = "https://github.com/subgraph/citadel"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
libcitadel = { path = "../libcitadel" } libcitadel = { path = "../libcitadel" }
failure = "0.1"
rpassword = "2.1.0"
clap = "2.32.0" clap = "2.32.0"
failure = "0.1.3"
serde_derive = "1.0.82" serde_derive = "1.0.82"
serde = "1.0.82" serde = "1.0.82"
toml = "0.4.10" toml = "0.4.10"

View File

@ -1,8 +1,8 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::fs; use std::fs;
use crate::Result; use libcitadel::Result;
use crate::util; use libcitadel::util;
/// ///
/// Represents a disk partition device on the system /// Represents a disk partition device on the system

View File

@ -1,71 +1,43 @@
#[macro_use] extern crate failure;
mod installer;
mod cli;
mod util;
mod disks;
use std::result;
use std::path::Path;
use std::env;
use std::time;
use std::fs;
use std::ffi::OsStr;
use std::thread::{self,JoinHandle}; use std::thread::{self,JoinHandle};
use std::process::exit; use std::time;
use failure::Error; use std::path::Path;
use std::ffi::OsStr;
use std::fs;
use libcitadel::Result;
use libcitadel::util;
use libcitadel::ResourceImage; use libcitadel::ResourceImage;
use crate::boot::disks;
use crate::boot::rootfs::setup_rootfs_resource;
use crate::install::installer::Installer;
pub type Result<T> = result::Result<T,Error>; const IMAGE_DIRECTORY: &str = "/run/citadel/images";
fn main() { pub fn live_rootfs() -> Result<()> {
copy_artifacts()?;
let mut args = env::args(); let rootfs = find_rootfs_image()?;
args.next(); setup_rootfs_resource(&rootfs)
let result = match args.next() {
Some(ref s) if s == "live-setup" => live_setup(),
Some(ref s) if s == "copy-artifacts" => copy_artifacts(),
Some(ref s) => cli_install_to(s),
None => cli_install(),
};
if let Err(ref e) = result {
println!("Failed: {}", format_error(e));
exit(1);
}
} }
pub fn format_error(err: &Error) -> String { pub fn live_setup() -> Result<()> {
let mut output = err.to_string(); decompress_images()?;
let mut prev = err.as_fail(); let live = Installer::new_livesetup();
while let Some(next) = prev.cause() { live.run()
output.push_str(": ");
output.push_str(&next.to_string());
prev = next;
}
output
}
fn live_setup() -> Result<()> {
if !Path::new("/etc/initrd-release").exists() {
bail!("Not running in initramfs, cannot do live-setup");
}
let installer = installer::Installer::new_livesetup();
installer.run()
} }
fn copy_artifacts() -> Result<()> { fn copy_artifacts() -> Result<()> {
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
println!("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));
} }
Err(format_err!("Could not find partition containing resource images")) Err(format_err!("Could not find partition containing resource images"))
} }
fn try_copy_artifacts() -> Result<bool> { fn try_copy_artifacts() -> Result<bool> {
@ -89,22 +61,22 @@ fn try_copy_artifacts() -> Result<bool> {
} }
fn deploy_artifacts() -> Result<()> { fn deploy_artifacts() -> Result<()> {
let run_images = Path::new("/run/images"); let run_images = Path::new(IMAGE_DIRECTORY);
if !run_images.exists() { if !run_images.exists() {
fs::create_dir_all(run_images)?; fs::create_dir_all(run_images)?;
util::exec_cmdline("/bin/mount", "-t tmpfs -o size=4g images /run/images")?; util::exec_cmdline("/bin/mount", "-t tmpfs -o size=4g images /run/citadel/images")?;
} }
for entry in fs::read_dir("/boot/images")? { for entry in fs::read_dir("/boot/images")? {
let entry = entry?; let entry = entry?;
println!("Copying {:?} from /boot/images to /run/images", entry.file_name()); println!("Copying {:?} from /boot/images to /run/citadel/images", entry.file_name());
fs::copy(entry.path(), run_images.join(entry.file_name()))?; fs::copy(entry.path(), run_images.join(entry.file_name()))?;
} }
println!("Copying bzImage to /run/images"); println!("Copying bzImage to /run/citadel/images");
fs::copy("/boot/bzImage", "/run/images/bzImage")?; fs::copy("/boot/bzImage", "/run/citadel/images/bzImage")?;
println!("Copying bootx64.efi to /run/images"); println!("Copying bootx64.efi to /run/citadel/images");
fs::copy("/boot/EFI/BOOT/bootx64.efi", "/run/images/bootx64.efi")?; fs::copy("/boot/EFI/BOOT/bootx64.efi", "/run/citadel/images/bootx64.efi")?;
deploy_syslinux_artifacts()?; deploy_syslinux_artifacts()?;
@ -119,9 +91,9 @@ fn deploy_syslinux_artifacts() -> Result<()> {
return Ok(()); return Ok(());
} }
println!("Copying contents of /boot/syslinux to /run/images/syslinux"); println!("Copying contents of /boot/syslinux to /run/citadel/images/syslinux");
let run_images_syslinux = Path::new("/run/images/syslinux"); let run_images_syslinux = Path::new("/run/citadel/images/syslinux");
fs::create_dir_all(run_images_syslinux)?; fs::create_dir_all(run_images_syslinux)?;
for entry in fs::read_dir(boot_syslinux)? { for entry in fs::read_dir(boot_syslinux)? {
let entry = entry?; let entry = entry?;
@ -134,14 +106,31 @@ fn deploy_syslinux_artifacts() -> Result<()> {
Ok(()) Ok(())
} }
fn decompress_images() -> Result<()> { fn find_rootfs_image() -> Result<ResourceImage> {
println!("decompressing images"); for entry in fs::read_dir(IMAGE_DIRECTORY)? {
let mut threads = Vec::new();
for entry in fs::read_dir("/run/images")? {
let entry = entry?; let entry = entry?;
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()) {
threads.push(decompress_one_image(image)); if image.metainfo().image_type() == "rootfs" {
return Ok(image)
}
}
}
}
Err(format_err!("Unable to find rootfs resource image in {}", IMAGE_DIRECTORY))
}
fn decompress_images() -> Result<()> {
println!("decompressing images");
let mut threads = Vec::new();
for entry in fs::read_dir("/run/citadel/images")? {
let entry = entry?;
if entry.path().extension() == Some(OsStr::new("img")) {
if let Ok(image) = ResourceImage::from_path(&entry.path()) {
if image.is_compressed() {
threads.push(decompress_one_image(image));
}
} }
} }
} }
@ -157,19 +146,3 @@ fn decompress_one_image(image: ResourceImage) -> JoinHandle<Result<()>> {
image.decompress() image.decompress()
}) })
} }
fn cli_install() -> Result<()> {
let ok = cli::run_cli_install()?;
if !ok {
println!("Install cancelled...");
}
Ok(())
}
fn cli_install_to(target: &str) -> Result<()> {
let ok = cli::run_cli_install_with(target)?;
if !ok {
println!("Install cancelled...");
}
Ok(())
}

View File

@ -0,0 +1,74 @@
use std::fs;
use std::process::exit;
use libcitadel::{util,Result,ResourceImage,CommandLine,set_verbose,format_error};
mod live;
mod disks;
mod rootfs;
pub fn main(args: Vec<String>) {
if CommandLine::verbose() {
set_verbose(true);
}
let command = args.iter().skip(1).next();
let result = match command {
Some(s) if s == "rootfs" => do_rootfs(),
Some(s) if s == "setup" => do_setup(),
_ => Err(format_err!("Bad or missing argument")),
};
if let Err(ref e) = result {
warn!("Failed: {}", format_error(e));
exit(1);
}
}
fn do_rootfs() -> Result<()> {
if CommandLine::live_mode() || CommandLine::install_mode() {
live::live_rootfs()
} else {
rootfs::setup_rootfs()
}
}
fn do_setup() -> Result<()> {
if CommandLine::live_mode() || CommandLine::install_mode() {
live::live_setup()?;
}
ResourceImage::mount_image_type("kernel")?;
ResourceImage::mount_image_type("extra")?;
if CommandLine::overlay() {
mount_overlay()?;
}
Ok(())
}
fn mount_overlay() -> Result<()> {
info!("Creating rootfs overlay");
info!("Moving /sysroot mount to /rootfs.ro");
fs::create_dir_all("/rootfs.ro")?;
util::exec_cmdline("/usr/bin/mount", "--make-private /")?;
util::exec_cmdline("/usr/bin/mount", "--move /sysroot /rootfs.ro")?;
info!("Mounting tmpfs on /rootfs.rw");
fs::create_dir_all("/rootfs.rw")?;
util::exec_cmdline("/usr/bin/mount", "-t tmpfs -orw,noatime,mode=755 rootfs.rw /rootfs.rw")?;
info!("Creating /rootfs.rw/work /rootfs.rw/upperdir");
fs::create_dir_all("/rootfs.rw/upperdir")?;
fs::create_dir_all("/rootfs.rw/work")?;
info!("Mounting overlay on /sysroot");
util::exec_cmdline("/usr/bin/mount", "-t overlay overlay -olowerdir=/rootfs.ro,upperdir=/rootfs.rw/upperdir,workdir=/rootfs.rw/work /sysroot")?;
info!("Moving /rootfs.ro and /rootfs.rw to new root");
fs::create_dir_all("/sysroot/rootfs.ro")?;
fs::create_dir_all("/sysroot/rootfs.rw")?;
util::exec_cmdline("/usr/bin/mount", "--move /rootfs.ro /sysroot/rootfs.ro")?;
util::exec_cmdline("/usr/bin/mount", "--move /rootfs.rw /sysroot/rootfs.rw")?;
Ok(())
}

View File

@ -1,20 +1,11 @@
use std::process::Command; use std::process::Command;
use libcitadel::{BlockDev,CommandLine,ImageHeader,Partition,Result,verity}; use libcitadel::{BlockDev,ResourceImage,CommandLine,ImageHeader,Partition,Result,verity};
use std::path::Path; use std::path::Path;
use std::process::Stdio; use std::process::Stdio;
use ResourceImage;
pub fn setup_rootfs() -> Result<()> { pub fn setup_rootfs() -> Result<()> {
if CommandLine::install_mode() || CommandLine::live_mode() { let mut p = choose_boot_partiton(true)?;
setup_rootfs_resource()
} else {
let p = choose_boot_partiton(true)?;
setup_partition(p)
}
}
fn setup_partition(mut p: Partition) -> Result<()> {
if CommandLine::noverity() { if CommandLine::noverity() {
setup_partition_unverified(&p) setup_partition_unverified(&p)
} else { } else {
@ -22,15 +13,11 @@ fn setup_partition(mut p: Partition) -> Result<()> {
} }
} }
fn setup_rootfs_resource() -> Result<()> { pub fn setup_rootfs_resource(rootfs: &ResourceImage) -> Result<()> {
info!("Searching for rootfs resource image");
let img = ResourceImage::find_rootfs()?;
if CommandLine::noverity() { if CommandLine::noverity() {
setup_resource_unverified(&img) setup_resource_unverified(&rootfs)
} else { } else {
setup_resource_verified(&img) setup_resource_verified(&rootfs)
} }
} }

View File

@ -1,41 +1,29 @@
#[macro_use] extern crate libcitadel;
#[macro_use] extern crate failure;
#[macro_use] extern crate serde_derive;
use std::process::exit;
use std::path::Path; use std::path::Path;
use std::fs; use std::process::exit;
use clap::{App,Arg,SubCommand,ArgMatches}; use clap::{App,Arg,SubCommand,ArgMatches};
use clap::AppSettings::*; use clap::AppSettings::*;
use crate::build::UpdateBuilder;
use crate::config::BuildConfig;
use libcitadel::{Result,ResourceImage,set_verbose,format_error,Partition,KeyPair,ImageHeader}; use libcitadel::{Result,ResourceImage,set_verbose,format_error,Partition,KeyPair,ImageHeader};
use std::fs;
mod build; pub fn main(args: Vec<String>) {
mod config;
fn main() {
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])
.subcommand(SubCommand::with_name("build")
.about("Build an update image specified by a configuration file")
.arg(Arg::with_name("build-file")
.required(true)
.help("Path to image build config file")))
.subcommand(SubCommand::with_name("metainfo") .subcommand(SubCommand::with_name("metainfo")
.about("Display metainfo variables for an image file") .about("Display metainfo variables for an image file")
.arg(Arg::with_name("path") .arg(Arg::with_name("path")
.required(true) .required(true)
.help("Path to image file"))) .help("Path to image file")))
.subcommand(SubCommand::with_name("generate-verity") .subcommand(SubCommand::with_name("generate-verity")
.about("Generate dm-verity hash tree for an image file") .about("Generate dm-verity hash tree for an image file")
.arg(Arg::with_name("path") .arg(Arg::with_name("path")
.required(true) .required(true)
.help("Path to image file"))) .help("Path to image file")))
.subcommand(SubCommand::with_name("verify") .subcommand(SubCommand::with_name("verify")
.about("Verify dm-verity hash tree for an image file") .about("Verify dm-verity hash tree for an image file")
.arg(Arg::with_name("path") .arg(Arg::with_name("path")
@ -72,14 +60,13 @@ fn main() {
.subcommand(SubCommand::with_name("verify-shasum") .subcommand(SubCommand::with_name("verify-shasum")
.about("Verify the sha256 sum of the image") .about("Verify the sha256 sum of the image")
.arg(Arg::with_name("path") .arg(Arg::with_name("path")
.required(true) .required(true)
.help("Path to image file"))); .help("Path to image file")));
set_verbose(true); set_verbose(true);
let matches = app.get_matches();
let matches = app.get_matches_from(args);
let result = match matches.subcommand() { let result = match matches.subcommand() {
("build", Some(m)) => build_image(m),
("metainfo", Some(m)) => metainfo(m), ("metainfo", Some(m)) => metainfo(m),
("generate-verity", Some(m)) => generate_verity(m), ("generate-verity", Some(m)) => generate_verity(m),
("verify", Some(m)) => verify(m), ("verify", Some(m)) => verify(m),
@ -99,14 +86,6 @@ fn main() {
} }
} }
fn build_image(arg_matches: &ArgMatches) -> Result<()> {
let build_file = arg_matches.value_of("build-file").unwrap();
let config = BuildConfig::load(build_file)?;
let mut builder = UpdateBuilder::new(config);
builder.build()?;
Ok(())
}
fn metainfo(arg_matches: &ArgMatches) -> Result<()> { fn metainfo(arg_matches: &ArgMatches) -> Result<()> {
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())?);
@ -238,7 +217,7 @@ fn install_image(arg_matches: &ArgMatches) -> Result<()> {
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)?;
} }
fs::rename(source,image_dest)?; fs::rename(source,image_dest)?;
Ok(()) Ok(())
@ -299,9 +278,9 @@ fn choose_install_partition(verbose: bool) -> Result<Partition> {
if verbose { if verbose {
for p in &partitions { for p in &partitions {
info!("Partition: {} (Mounted: {}) (Empty: {})", info!("Partition: {} (Mounted: {}) (Empty: {})",
p.path().display(), p.path().display(),
bool_to_yesno(p.is_mounted()), bool_to_yesno(p.is_mounted()),
bool_to_yesno(!p.is_initialized())); bool_to_yesno(!p.is_initialized()));
} }
} }

View File

@ -1,9 +1,9 @@
use std::io::{self,Write}; use std::io::{self,Write};
use std::path::Path; use std::path::Path;
use crate::Result; use libcitadel::Result;
use crate::util::Disk; use super::disk::Disk;
use rpassword; use rpassword;
use crate::installer::Installer; use crate::install::installer::Installer;
pub fn run_cli_install() -> Result<bool> { pub fn run_cli_install() -> Result<bool> {
let disk = match choose_disk()? { let disk = match choose_disk()? {

View File

@ -1,58 +1,8 @@
use std::mem;
use std::ffi::CStr;
use std::str::from_utf8_unchecked;
use std::path::{Path,PathBuf}; use std::path::{Path,PathBuf};
use std::process::{Command,ExitStatus,Stdio};
use std::fs; use std::fs;
use libc::{self, c_char}; use libcitadel::Result;
use failure::ResultExt;
use libcitadel::OsRelease;
use super::Result;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct UtsName(libc::utsname);
#[allow(dead_code)]
impl UtsName {
pub fn sysname(&self) -> &str {
to_str(&(&self.0.sysname as *const c_char ) as *const *const c_char)
}
pub fn nodename(&self) -> &str {
to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char)
}
pub fn release(&self) -> &str {
to_str(&(&self.0.release as *const c_char ) as *const *const c_char)
}
pub fn version(&self) -> &str {
to_str(&(&self.0.version as *const c_char ) as *const *const c_char)
}
pub fn machine(&self) -> &str {
to_str(&(&self.0.machine as *const c_char ) as *const *const c_char)
}
}
pub fn uname() -> UtsName {
unsafe {
let mut ret: UtsName = mem::uninitialized();
libc::uname(&mut ret.0);
ret
}
}
#[inline]
fn to_str<'a>(s: *const *const c_char) -> &'a str {
unsafe {
let res = CStr::from_ptr(*s).to_bytes();
from_utf8_unchecked(res)
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Disk { pub struct Disk {
@ -110,6 +60,7 @@ impl Disk {
} }
/*
pub fn rootfs_channel() -> &'static str { pub fn rootfs_channel() -> &'static str {
match OsRelease::citadel_channel() { match OsRelease::citadel_channel() {
Some(channel) => channel, Some(channel) => channel,
@ -154,3 +105,4 @@ fn check_cmd_status(cmd_path: &str, status: &ExitStatus) -> Result<()> {
} }
Ok(()) Ok(())
} }
*/

View File

@ -6,11 +6,10 @@ use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use std::time::Instant; use std::time::Instant;
use libcitadel::util::{mount,exec_cmdline_with_output}; use libcitadel::util::{self,mount,exec_cmdline_with_output};
use libcitadel::RealmFS; use libcitadel::RealmFS;
use libcitadel::Result;
use super::util; use libcitadel::OsRelease;
use super::Result;
const BLKDEACTIVATE: &str = "/sbin/blkdeactivate"; const BLKDEACTIVATE: &str = "/sbin/blkdeactivate";
const CRYPTSETUP: &str = "/sbin/cryptsetup"; const CRYPTSETUP: &str = "/sbin/cryptsetup";
@ -40,7 +39,7 @@ const EXTRA_IMAGE_NAME: &str = "citadel-extra.img";
const INSTALL_MOUNT: &str = "/run/installer/mnt"; const INSTALL_MOUNT: &str = "/run/installer/mnt";
const LUKS_PASSPHRASE_FILE: &str = "/run/installer/luks-passphrase"; const LUKS_PASSPHRASE_FILE: &str = "/run/installer/luks-passphrase";
const DEFAULT_ARTIFACT_DIRECTORY: &str = "/run/images"; const DEFAULT_ARTIFACT_DIRECTORY: &str = "/run/citadel/images";
const KERNEL_CMDLINE: &str = "add_efi_memmap intel_iommu=off cryptomgr.notests rcupdate.rcu_expedited=1 rcu_nocbs=0-64 tsc=reliable no_timer_check noreplace-smp i915.fastboot=1 quiet splash"; const KERNEL_CMDLINE: &str = "add_efi_memmap intel_iommu=off cryptomgr.notests rcupdate.rcu_expedited=1 rcu_nocbs=0-64 tsc=reliable no_timer_check noreplace-smp i915.fastboot=1 quiet splash";
@ -183,15 +182,15 @@ impl Installer {
} }
fn setup_live_realm(&self) -> Result<()> { fn setup_live_realm(&self) -> Result<()> {
self.cmd(CITADEL_IMAGE, format!("decompress /run/images/base-realmfs.img"))?; self.cmd(CITADEL_IMAGE, format!("decompress /run/citadel/images/base-realmfs.img"))?;
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");
self.info(format!("creating directory {}", realmfs_dir.display()))?; self.info(format!("creating directory {}", realmfs_dir.display()))?;
fs::create_dir_all(&realmfs_dir)?; fs::create_dir_all(&realmfs_dir)?;
self.info(format!("creating symlink {} -> {}", base_realmfs.display(), "/run/images/base-realmfs.img"))?; self.info(format!("creating symlink {} -> {}", base_realmfs.display(), "/run/citadel/images/base-realmfs.img"))?;
unixfs::symlink("/run/images/base-realmfs.img", &base_realmfs)?; unixfs::symlink("/run/citadel/images/base-realmfs.img", &base_realmfs)?;
self.mount_realmfs()?; self.mount_realmfs()?;
self.setup_storage()?; self.setup_storage()?;
@ -201,7 +200,7 @@ impl Installer {
fs::write(self.storage().join("realms/realm-main/config"), "realmfs = \"base\"")?; fs::write(self.storage().join("realms/realm-main/config"), "realmfs = \"base\"")?;
let rootfs = self.storage().join("realms/realm-main/rootfs"); let rootfs = self.storage().join("realms/realm-main/rootfs");
fs::remove_file(&rootfs)?; fs::remove_file(&rootfs)?;
unixfs::symlink("/run/images/base-realmfs.mountpoint", &rootfs)?; unixfs::symlink("/run/citadel/images/base-realmfs.mountpoint", &rootfs)?;
self.info("Creating /Shared realms directory")?; self.info("Creating /Shared realms directory")?;
fs::create_dir_all(self.storage().join("realms/Shared"))?; fs::create_dir_all(self.storage().join("realms/Shared"))?;
@ -211,12 +210,12 @@ impl Installer {
} }
pub fn mount_realmfs(&self) -> Result<()> { pub fn mount_realmfs(&self) -> Result<()> {
self.info("Creating loop device for /run/images/base-realmfs.img")?; self.info("Creating loop device for /run/citadel/images/base-realmfs.img")?;
let args = format!("--offset 4096 -f --show /run/images/base-realmfs.img"); let args = format!("--offset 4096 -f --show /run/citadel/images/base-realmfs.img");
let loopdev = exec_cmdline_with_output("/sbin/losetup", args)?; let loopdev = exec_cmdline_with_output("/sbin/losetup", args)?;
self.info("Mounting image at /run/images/base-realmfs.mountpoint")?; self.info("Mounting image at /run/citadel/images/base-realmfs.mountpoint")?;
fs::create_dir_all("/run/images/base-realmfs.mountpoint")?; fs::create_dir_all("/run/citadel/images/base-realmfs.mountpoint")?;
mount(&loopdev, "/run/images/base-realmfs.mountpoint", Some("-oro"))?; mount(&loopdev, "/run/citadel/images/base-realmfs.mountpoint", Some("-oro"))?;
Ok(()) Ok(())
} }
@ -419,7 +418,7 @@ impl Installer {
/* /*
self.info("Creating rootfs symlink")?; self.info("Creating rootfs symlink")?;
unixfs::symlink( unixfs::symlink(
format!("/run/images/{}-realmfs.mountpoint", self.main_realmfs()), format!("/run/citadel/images/{}-realmfs.mountpoint", self.main_realmfs()),
format!("{}/rootfs", realm.display()))?; format!("{}/rootfs", realm.display()))?;
*/ */
@ -430,7 +429,10 @@ impl Installer {
} }
fn setup_storage_resources(&self) -> Result<()> { fn setup_storage_resources(&self) -> Result<()> {
let channel = util::rootfs_channel(); let channel = match OsRelease::citadel_channel() {
Some(channel) => channel,
None => "dev",
};
let resources = self.storage().join("resources").join(channel); let resources = self.storage().join("resources").join(channel);
fs::create_dir_all(&resources)?; fs::create_dir_all(&resources)?;

View File

@ -0,0 +1,28 @@
use std::process::exit;
pub(crate) mod installer;
mod cli;
mod disk;
use libcitadel::format_error;
pub fn main(args: Vec<String>) {
let mut args = args.iter().skip(1);
let result = if let Some(dev) = args.next() {
cli::run_cli_install_with(dev)
} else {
cli::run_cli_install()
};
let ok = match result {
Ok(ok) => ok,
Err(ref err) => {
println!("Install failed: {}", format_error(err));
exit(1);
},
};
if !ok {
println!("Install cancelled...");
}
}

58
citadel-tool/src/main.rs Normal file
View File

@ -0,0 +1,58 @@
#[macro_use] extern crate libcitadel;
#[macro_use] extern crate failure;
#[macro_use] extern crate serde_derive;
use std::env;
use std::path::Path;
use std::ffi::OsStr;
use std::iter;
mod boot;
mod image;
mod install;
mod mkimage;
fn main() {
let exe = match env::current_exe() {
Ok(path) => path,
Err(_e) => {
return;
},
};
let args = env::args().collect::<Vec<String>>();
if exe == Path::new("/usr/libexec/citadel-boot") {
boot::main(args);
} else if exe == Path::new("/usr/libexec/citadel-install") {
install::main(args);
} else if exe == Path::new("/usr/bin/citadel-image") {
image::main(args);
} else if exe.file_name() == Some(OsStr::new("citadel-mkimage")) {
mkimage::main(args);
} else if exe.file_name() == Some(OsStr::new("citadel-tool")) {
dispatch_command(args);
} else {
println!("Error: unknown executable {}", exe.display());
}
}
fn dispatch_command(args: Vec<String>) {
if let Some(command) = args.iter().skip(1).next() {
match command.as_str() {
"boot" => boot::main(rebuild_args("citadel-boot", args)),
"install" => install::main(rebuild_args("citadel-install", args)),
"image" => image::main(rebuild_args("citadel-image", args)),
"mkimage" => mkimage::main(rebuild_args("citadel-mkimage", args)),
_ => println!("Error: unknown command {}", command),
}
} else {
println!("Must provide an argument");
}
}
fn rebuild_args(command: &str, args: Vec<String>) -> Vec<String> {
iter::once(command.to_string())
.chain(args.into_iter().skip(2))
.collect()
}

View File

@ -6,7 +6,7 @@ use std::io::{self,Write};
use failure::ResultExt; use failure::ResultExt;
use libcitadel::{Result,ImageHeader,verity,util,devkeys}; use libcitadel::{Result,ImageHeader,verity,util,devkeys};
use crate::BuildConfig; use super::config::BuildConfig;
use std::path::Path; use std::path::Path;
pub struct UpdateBuilder { pub struct UpdateBuilder {

View File

@ -0,0 +1,31 @@
use std::process::exit;
use libcitadel::Result;
mod config;
mod build;
pub fn main(args: Vec<String>) {
let config_path = match args.iter().skip(1).next() {
Some(arg) => arg,
None => {
println!("Expected config file argument");
exit(1);
},
};
if let Err(err) = build_image(config_path) {
println!("Error: {}", err);
exit(1);
}
}
fn build_image(config_path: &str) -> Result<()> {
let conf = config::BuildConfig::load(config_path)?;
let mut builder = build::UpdateBuilder::new(conf);
builder.build()
}

View File

@ -6,7 +6,7 @@ use std::io::Write;
use crate::{ImageHeader,MetaInfo,Mount,Result,util,verity}; use crate::{ImageHeader,MetaInfo,Mount,Result,util,verity};
const BASE_PATH: &'static str = "/storage/realms/realmfs-images"; const BASE_PATH: &'static str = "/storage/realms/realmfs-images";
const RUN_DIRECTORY: &str = "/run/images"; const RUN_DIRECTORY: &str = "/run/citadel/images";
const MAX_REALMFS_NAME_LEN: usize = 40; const MAX_REALMFS_NAME_LEN: usize = 40;

View File

@ -8,7 +8,7 @@ use crate::{CommandLine,OsRelease,ImageHeader,MetaInfo,Result,Partition,Mount,ve
use failure::ResultExt; use failure::ResultExt;
const STORAGE_BASEDIR: &str = "/sysroot/storage/resources"; const STORAGE_BASEDIR: &str = "/sysroot/storage/resources";
const RUN_DIRECTORY: &str = "/run/images"; const RUN_DIRECTORY: &str = "/run/citadel/images";
/// Locates and mounts a resource image file. /// Locates and mounts a resource image file.
/// ///
@ -24,7 +24,7 @@ const RUN_DIRECTORY: &str = "/run/images";
/// citadel.noverity: Mount image without dm-verity. Also do not verify header signature. /// citadel.noverity: Mount image without dm-verity. Also do not verify header signature.
/// citadel.nosignatures: Do not verify header signature. /// citadel.nosignatures: Do not verify header signature.
/// ///
/// A requested image file will be searched for first in /run/images and if not found there the /// A requested image file will be searched for first in /run/citadel/images and if not found there the
/// usual location of /storage/resources is searched. /// usual location of /storage/resources is searched.
/// ///
pub struct ResourceImage { pub struct ResourceImage {
@ -35,7 +35,7 @@ pub struct ResourceImage {
impl ResourceImage { impl ResourceImage {
/// Locate and return a resource image of type `image_type`. /// Locate and return a resource image of type `image_type`.
/// First the /run/images directory is searched, and if not found there, /// First the /run/citadel/images directory is searched, and if not found there,
/// the image will be searched for in /storage/resources/$channel /// the image will be searched for in /storage/resources/$channel
pub fn find(image_type: &str) -> Result<ResourceImage> { pub fn find(image_type: &str) -> Result<ResourceImage> {
let channel = ResourceImage::rootfs_channel(); let channel = ResourceImage::rootfs_channel();
@ -59,7 +59,12 @@ impl ResourceImage {
Err(format_err!("Failed to find resource image of type: {}", image_type)) Err(format_err!("Failed to find resource image of type: {}", image_type))
} }
/// Locate a rootfs image in /run/images and return it pub fn mount_image_type(image_type: &str) -> Result<()> {
let mut image = ResourceImage::find(image_type)?;
image.mount()
}
/// Locate a rootfs image in /run/citadel/images and return it
pub fn find_rootfs() -> Result<ResourceImage> { pub fn find_rootfs() -> Result<ResourceImage> {
match search_directory(RUN_DIRECTORY, "rootfs", None)? { match search_directory(RUN_DIRECTORY, "rootfs", None)? {
Some(image) => Ok(image), Some(image) => Ok(image),

View File

@ -109,7 +109,7 @@ fn setup_device(srcdev: &str, devname: &str, nblocks: usize, roothash: &str) ->
} }
fn create_image_loop_device(file: &Path) -> Result<String> { fn create_image_loop_device(file: &Path) -> Result<String> {
let args = format!("--offset 4096 -f --show {}", file.display()); let args = format!("--offset 4096 --read-only -f --show {}", file.display());
let output = util::exec_cmdline_with_output(LOSETUP, args)?; let output = util::exec_cmdline_with_output(LOSETUP, args)?;
Ok(output) Ok(output)
} }