diff --git a/citadel-tools/.gitignore b/citadel-tools/.gitignore deleted file mode 100644 index fce0f33..0000000 --- a/citadel-tools/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -**/target/ -**/*.rs.bk diff --git a/citadel-tools/citadel-appimg/Cargo.lock b/citadel-tools/citadel-appimg/Cargo.lock deleted file mode 100644 index 32c8d57..0000000 --- a/citadel-tools/citadel-appimg/Cargo.lock +++ /dev/null @@ -1,242 +0,0 @@ -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atty" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cc" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "citadel-appimg" -version = "0.1.0" -dependencies = [ - "clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clap" -version = "2.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.39" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_syscall" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "strsim" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synstructure" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termion" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "textwrap" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-width" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vec_map" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4" -"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2" -"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" -"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" -"checksum cc 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9be26b24e988625409b19736d130f0c7d224f01d06454b5f81d8d23d6c1a618f" -"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dc18f6f4005132120d9711636b32c46a233fad94df6217fa1d81c5e97a9f200" -"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" -"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" -"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" -"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" -"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" -"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" -"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" -"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" -"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/citadel-tools/citadel-appimg/Cargo.toml b/citadel-tools/citadel-appimg/Cargo.toml deleted file mode 100644 index 7ff7ccc..0000000 --- a/citadel-tools/citadel-appimg/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "citadel-appimg" -version = "0.1.0" -authors = ["Bruce Leidl "] -homepage = "http://github.com/subgraph/citadel" - -[dependencies] -clap = "2.30.0" -failure = "0.1.1" -lazy_static = "1.0.0" diff --git a/citadel-tools/citadel-appimg/src/appimg.rs b/citadel-tools/citadel-appimg/src/appimg.rs deleted file mode 100644 index 7569de9..0000000 --- a/citadel-tools/citadel-appimg/src/appimg.rs +++ /dev/null @@ -1,216 +0,0 @@ - -use std::path::{Path,PathBuf}; -use std::fs::{self,File}; -use std::io::{BufRead,BufReader}; - -use Result; -use systemd; - -use util::*; - -lazy_static!{ - static ref APPIMG_BASE_PATH: PathBuf = PathBuf::from("/storage/appimg"); - static ref USERDATA_BASE_PATH: PathBuf = PathBuf::from("/storage/user-data"); - static ref SYSTEMD_UNIT_PATH: PathBuf = PathBuf::from("/run/systemd/system"); - static ref SYSTEMD_NSPAWN_PATH: PathBuf = PathBuf::from("/run/systemd/nspawn"); -} - - - -#[derive(Debug)] -pub struct AppImg { - name: String, - config: AppImgConfig, -} - -impl AppImg { - - pub fn new(name: &str) -> Result { - let mut img = AppImg { - name: name.to_string(), - config: AppImgConfig::new(name), - }; - img.load_config()?; - img.write_systemd_files()?; - Ok(img) - } - - fn load_config(&mut self) -> Result<()> { - let mut path = APPIMG_BASE_PATH.join(&self.name); - path.push("config"); - if !path.exists() { - return Ok(()); - } - self.config.load(&path)?; - Ok(()) - } - - fn write_systemd_files(&self) -> Result<()> { - if !self.nspawn_config_path().exists() { - self.write_nspawn_config()?; - } - if !self.service_unit_path().exists() { - self.write_service_unit()?; - } - Ok(()) - } - - pub fn is_running(&self) -> bool { - systemd::sysctl_is_active(&self.service_unit_name()) - } - - pub fn start(&self) -> Result<()> { - if self.is_running() { - warn!("image {} is already running", self.name); - return Ok(()); - } - self.write_nspawn_config()?; - self.write_service_unit()?; - systemd::systemctl_start(&self.service_unit_name()); - Ok(()) - - } - - pub fn stop(&self) { - if !self.is_running() { - warn!("image {} is not running", self.name); - return; - } - systemd::systemctl_stop(&self.service_unit_name()); - } - - pub fn name(&self) -> &str { - &self.name - } - - fn nspawn_config_path(&self) -> PathBuf { - SYSTEMD_NSPAWN_PATH.join(&format!("{}.nspawn", self.name)) - } - - fn service_unit_name(&self) -> String { - format!("appimg-{}.service", self.name) - } - - fn service_unit_path(&self) -> PathBuf { - SYSTEMD_UNIT_PATH.join(&format!("appimg-{}.service", self.name)) - } - - fn write_nspawn_config(&self) -> Result<()> { - let mut extra = String::new(); - extra += &format!("Bind={}:/home/user\n", self.config.home()); - if self.config.use_kvm() { - extra += "Bind=/dev/kvm\n"; - } - if self.config.use_gpu() { - extra += "Bind=/dev/dri/renderD128\n"; - } - - let content = systemd::generate_nspawn_file(&extra); - fs::create_dir_all(SYSTEMD_NSPAWN_PATH.as_path())?; - write_string_to_file(&self.nspawn_config_path(), &content)?; - - Ok(()) - } - - fn write_service_unit(&self) -> Result<()> { - let content = systemd::generate_service_file(&self.name); - fs::create_dir_all(&SYSTEMD_UNIT_PATH.as_path())?; - - write_string_to_file(&self.service_unit_path(), &content)?; - Ok(()) - } -} - -#[derive(Debug)] -pub struct AppImgConfig { - img_name: String, - home: Option, - kvm: bool, - gpu: bool, -} - -const DEFAULT_HOME_PATH: &str = "/storage/user-data/primary-home"; - -impl AppImgConfig { - fn new(img_name: &str) -> AppImgConfig { - AppImgConfig{ - img_name: img_name.to_string(), - home: None, - kvm: false, - gpu: false, - } - } - - pub fn home(&self) -> String { - if let Some(ref name) = self.home { - if is_valid_name(name) { - let home_path = USERDATA_BASE_PATH.join(name); - return home_path.to_str().unwrap().to_string(); - } - } - DEFAULT_HOME_PATH.to_string() - } - - pub fn use_kvm(&self) -> bool { - self.kvm - - } - - pub fn use_gpu(&self) -> bool { - self.gpu - } - - fn reset(&mut self) { - self.home = None; - self.kvm = false; - self.gpu = false; - } - - fn load(&mut self, path: &Path) -> Result<()> { - self.reset(); - let f = File::open(path)?; - let reader = BufReader::new(f); - for line in reader.lines() { - let line = line?; - let v = line.split('=').collect::>(); - if v.len() == 2 { - self.process_keyval(v[0].trim(), v[1].trim()); - - } - } - Ok(()) - } - - fn process_keyval(&mut self, k: &str, v: &str) { - match k { - - "home" => { - if is_valid_name(v) { - let home_path = USERDATA_BASE_PATH.join(v); - if !home_path.is_dir() { - warn!("'home' value '{}' in config file of image {} refers to directory that doesn't exist", v, self.img_name); - - } - self.home = Some(v.to_string()); - } else { - warn!("Invalid home value '{}' in config file of image {}", v, self.img_name); - } - }, - "use-kvm" => { - self.kvm = v == "yes"; - - }, - - "use-gpu" => { - self.gpu = v == "yes"; - }, - - _ => { - warn!("unknown keyword '{}' in config file for image {}", k, self.img_name); - }, - } - } - - -} - diff --git a/citadel-tools/citadel-appimg/src/main.rs b/citadel-tools/citadel-appimg/src/main.rs deleted file mode 100644 index 13221f2..0000000 --- a/citadel-tools/citadel-appimg/src/main.rs +++ /dev/null @@ -1,110 +0,0 @@ -#[macro_use] extern crate failure; -#[macro_use] extern crate lazy_static; - -extern crate clap; - -macro_rules! warn { - ($e:expr) => { println!("[!]: {}", $e); }; - ($fmt:expr, $($arg:tt)+) => { println!("[!]: {}", format!($fmt, $($arg)+)); }; -} - -mod appimg; -mod util; -mod systemd; -mod manager; - -use failure::Error; -use clap::{App,Arg,ArgMatches,SubCommand}; -use clap::AppSettings::*; -use std::process::exit; -use std::result; - -pub type Result = result::Result; - -pub use appimg::AppImg; -use manager::ImageManager; - -fn main() { - let matches = App::new("citadel-appimg") - .about("Subgraph Citadel application image management") - .settings(&[ArgRequiredElseHelp, ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder]) - - .subcommand(SubCommand::with_name("list") - .about("Display list of application images")) - - .subcommand(SubCommand::with_name("start") - .about("Launch an application image") - .arg(Arg::with_name("name"))) - - .subcommand(SubCommand::with_name("stop") - .about("Stop a running application image") - .arg(Arg::with_name("name").required(true))) - - .subcommand(SubCommand::with_name("default") - .about("Set an application image as the default image to boot") - .arg(Arg::with_name("name").required(true))) - - .subcommand(SubCommand::with_name("current") - .about("Set an application image as 'current'") - .arg(Arg::with_name("name").required(true))) - - .get_matches(); - - let result = match matches.subcommand() { - ("list", _) => list_cmd(), - ("start", Some(m)) => start_cmd(m), - ("stop", Some(m)) => stop_cmd(m), - ("default", Some(m)) => default_cmd(m), - ("current", Some(m)) => current_cmd(m), - _ => Ok(()), - }; - if let Err(e) = result { - println!("{}", e); - exit(1); - } -} - - - -fn list_cmd() -> Result<()> { - let manager = ImageManager::load()?; - manager.list()?; - Ok(()) -} - -fn start_cmd(matches: &ArgMatches) -> Result<()> { - let mut manager = ImageManager::load()?; - match matches.value_of("name") { - Some(name) => manager.start_image(name), - None => manager.start_default(), - } -} - -fn stop_cmd(matches: &ArgMatches) -> Result<()> { - let name = matches.value_of("name").unwrap(); - let mut manager = ImageManager::load()?; - manager.stop_image(name)?; - Ok(()) -} - -fn default_cmd(matches: &ArgMatches) -> Result<()> { - let name = matches.value_of("name").unwrap(); - let mut manager = ImageManager::load()?; - if manager.image_exists(name) { - manager.set_default(name)?; - } else { - warn!("No image '{}' exists", name); - } - Ok(()) -} - -fn current_cmd(matches: &ArgMatches) -> Result<()> { - let name = matches.value_of("name").unwrap(); - let mut manager = ImageManager::load()?; - if manager.image_exists(name) { - manager.set_current(name)?; - } else { - warn!("No image '{}' exists", name); - } - Ok(()) -} diff --git a/citadel-tools/citadel-appimg/src/manager.rs b/citadel-tools/citadel-appimg/src/manager.rs deleted file mode 100644 index ba798a2..0000000 --- a/citadel-tools/citadel-appimg/src/manager.rs +++ /dev/null @@ -1,297 +0,0 @@ -use std::path::{Path,PathBuf}; -use std::io::Write; -use std::fs; -use std::os::unix::fs::{OpenOptionsExt,symlink}; -use std::collections::HashMap; - -use AppImg; -use Result; -use util::*; -use systemd; - -lazy_static!{ - static ref APPIMG_BASE_PATH: PathBuf = PathBuf::from("/storage/appimg"); - static ref APPIMG_RUN_PATH: PathBuf = PathBuf::from("/run/appimg"); -} - -const DESKTOPD_SERVICE: &str = "citadel-desktopd.service"; - - -pub struct ImageManager { - images: HashMap, - default: Option, - current: Option, -} - - -impl ImageManager { - fn new() -> Result { - let default = default_appimg()?; - let current = current_appimg()?; - Ok(ImageManager { - images: HashMap::new(), - default, current, - }) - } - - pub fn load() -> Result { - let mut manager = ImageManager::new()?; - for dent in fs::read_dir(APPIMG_BASE_PATH.as_path())? { - let path = dent?.path(); - manager.process_path(&path)?; - } - Ok(manager) - } - - fn process_path(&mut self, path: &Path) -> Result<()> { - let meta = path.symlink_metadata()?; - if !meta.is_dir() { - return Ok(()) - } - let name = path_filename(path); - if !is_valid_name(name) { - warn!("ignoring directory in appimg storage which has invalid appimg name: {}", name); - return Ok(()) - } - - let appimg = AppImg::new(name)?; - self.images.insert(appimg.name().to_string(), appimg); - Ok(()) - } - - pub fn list(&self) -> Result<()> { - for img in self.images.values() { - let cur = if self.is_current(img) { "(current)" } else { "" }; - let def = if self.is_default(img) { "(default)" } else { "" }; - let run = if img.is_running() { "[running]"} else {""}; - println!(" {} {} {} {}", img.name(), run, def, cur); - } - - Ok(()) - } - - pub fn start_default(&mut self) -> Result<()> { - let name = match self.default { - Some(ref s) => s.clone(), - None => bail!("No default image to start"), - }; - self.start_image(&name) - - } - - pub fn start_image(&mut self, name: &str) -> Result<()> { - // if current is not set, set it to this instance - let set_as_current = self.current.is_none(); - if set_as_current { - self.set_current_target(name)?; - } - - match self.images.get(name) { - Some(img) => { - img.start()?; - }, - None => { - // XXX if set_as_current do something here to undo what set_current has done - warn!("Cannot start '{}'. Image does not exist"); - return Ok(()) - }, - } - - if set_as_current { - systemd::systemctl_restart(DESKTOPD_SERVICE); - } - Ok(()) - } - - pub fn stop_image(&mut self, name: &str) -> Result<()> { - match self.images.get(name) { - Some(img) => img.stop(), - None => { - warn!("Cannot stop '{}'. Image does not exist"); - return Ok(()) - }, - } - let current = match self.current { - Some(ref s) => s.clone(), - None => return Ok(()), - }; - if current == name { - systemd::systemctl_stop(DESKTOPD_SERVICE); - let path = APPIMG_RUN_PATH.join("current.appimg"); - if path.exists() { - fs::remove_file(&path)?; - } - if let Some(img_name) = self.find_running_image_name() { - self.set_current_target(&img_name)?; - systemd::systemctl_start(DESKTOPD_SERVICE); - } - } - Ok(()) - } - - fn find_running_image_name(&self) -> Option { - for img in self.images.values() { - if img.is_running() { - return Some(img.name().to_string()); - } - } - None - } - - fn is_current(&self, img: &AppImg) -> bool { - self.same_name(img, &self.current) - } - - fn is_default(&self, img: &AppImg) -> bool { - self.same_name(img, &self.default) - } - - fn same_name(&self, img: &AppImg, name: &Option) -> bool { - if let Some(ref name) = *name { - name == img.name() - } else { - false - } - } - pub fn image_exists(&self, name: &str) -> bool { - self.images.contains_key(name) - } - - - pub fn set_default(&mut self, name: &str) -> Result<()> { - if !is_valid_name(name) { - warn!("{} is not a valid image name", name); - return Ok(()) - } - - if let Some(ref default) = self.default { - if default == name { - warn!("{} is already default appimg", name); - return Ok(()) - } - } - - let path = APPIMG_BASE_PATH.join("default.appimg"); - if path.exists() { - fs::remove_file(&path)?; - } - symlink(name, &path)?; - self.default = Some(name.to_string()); - Ok(()) - } - - pub fn set_current(&mut self, name: &str) -> Result<()> { - { - let img = match self.images.get(name) { - Some(img) => img, - None => { - warn!("Cannot set {} as current, no image with that name exists", name); - return Ok(()); - }, - }; - - if self.is_current(img) { - warn!("Image {} is already current image", name); - return Ok(()); - } - - if !img.is_running() { - img.start()?; - } - } - self.set_current_target(name)?; - systemd::systemctl_restart(DESKTOPD_SERVICE); - Ok(()) - } - - - - fn set_current_target(&mut self, name: &str) -> Result<()> { - if !is_valid_name(name) { - warn!("{} is not a valid image name", name); - return Ok(()) - } - if let Some(ref current) = self.current { - if current == name { - warn!("{} is already current appimg", name); - return Ok(()) - } - } - - fs::create_dir_all(APPIMG_RUN_PATH.as_path())?; - let path = APPIMG_RUN_PATH.join("current.appimg"); - let target = APPIMG_BASE_PATH.join(name); - if path.exists() { - fs::remove_file(&path)?; - } - symlink(&target, &path)?; - - let script = format!("#!/bin/bash\nmachinectl -E DESKTOP_STARTUP_ID=${{DESKTOP_STARTUP_ID}} shell user@{} /usr/libexec/launch $@\n", name); - let script_path = APPIMG_RUN_PATH.join("run-in-image"); - let mut f = fs::OpenOptions::new() - .create(true) - .write(true) - .mode(0o755) - .open(&script_path)?; - - f.write_all(script.as_bytes())?; - self.current = Some(name.to_string()); - Ok(()) - } -} - -fn appimg_symlink(symlink: &Path) -> Result> { - if !symlink.exists() { - return Ok(None); - } - - if !symlink.symlink_metadata()?.file_type().is_symlink() { - bail!("{} exists but it is not a symlink", symlink.display()); - } - - let link = fs::read_link(&symlink)?; - - let appimg_name = appimg_name_for_symlink_target(&link)?; - if !is_valid_name(&appimg_name) { - bail!("symlink {} points to a directory with a name ({}) that is not a valid appimg name", symlink.display(), appimg_name); - } - - Ok(Some(appimg_name)) -} - -fn default_appimg() -> Result> { - appimg_symlink(&APPIMG_BASE_PATH.join("default.appimg")) -} - -fn current_appimg() -> Result> { - appimg_symlink(&APPIMG_RUN_PATH.join("current.appimg")) -} - -/// -/// Returns a name only if target points to some subdirectory of APPIMG_BASE_PATH -/// -fn appimg_name_for_symlink_target(target: &Path) -> Result { - let path = if target.is_absolute() { - target.to_path_buf() - } else if target.components().count() == 1 { - APPIMG_BASE_PATH.join(target) - } else { - bail!("symlink target has invalid value: {}", target.display()) - }; - - match path.parent() { - Some(parent) => { - if parent != APPIMG_BASE_PATH.as_path() { - bail!("symlink target points outside of /storage/appimg directory"); - } - }, - None => { - bail!("symlink target has invalid value (no parent): {}", target.display()) - }, - }; - - if !path.is_dir() { - bail!("symlink target {} is not a directory", path.display()); - } - Ok(path_filename(&path).to_string()) -} diff --git a/citadel-tools/citadel-appimg/src/systemd.rs b/citadel-tools/citadel-appimg/src/systemd.rs deleted file mode 100644 index aeda46f..0000000 --- a/citadel-tools/citadel-appimg/src/systemd.rs +++ /dev/null @@ -1,113 +0,0 @@ - -use std::process::Command; - -const SYSTEMCTL_PATH: &str = "/usr/bin/systemctl"; - -pub fn sysctl_is_active(name: &str) -> bool { - let mut cmd = Command::new(SYSTEMCTL_PATH); - cmd.arg("-q"); - cmd.arg("is-active"); - cmd.arg(name); - match cmd.status() { - Ok(status) => status.success(), - Err(e) => { - warn!("failed to execute /usr/bin/systemctl: {}", e); - false - } - } -} - -pub fn systemctl_restart(name: &str) -> bool { - run_systemctl("restart", name) -} - -pub fn systemctl_start(name: &str) -> bool { - run_systemctl("start", name) -} - -pub fn systemctl_stop(name: &str) -> bool { - run_systemctl("stop", name) -} - -fn run_systemctl(op: &str, name: &str) -> bool { - let mut cmd = Command::new(SYSTEMCTL_PATH); - cmd.arg(op); - cmd.arg(name); - match cmd.output() { - Err(e) => { - warn!("failed to execute /usr/bin/systemctl: {}", e); - false - } - Ok(output) => { - if !output.status.success() { - warn!("error running systemctl {}: {}", op, String::from_utf8(output.stderr).unwrap()); - return false - } - true - } - } -} - -pub fn generate_nspawn_file(extra_bind_mounts: &str) -> String { - NSPAWN_FILE_TEMPLATE.replace("$EXTRA_BIND_MOUNTS", extra_bind_mounts) -} - -pub fn generate_service_file(appimg_name: &str) -> String { - APPIMG_SERVICE_TEMPLATE.replace("$APPIMG_NAME", appimg_name) -} - -pub const NSPAWN_FILE_TEMPLATE: &str = r###" -[Exec] -Boot=true -Environment=IFCONFIG_IP=172.17.0.2/24 -Environment=IFCONFIG_GW=172.17.0.1 - -[Files] -BindReadOnly=/usr/share/themes/Adapta -BindReadOnly=/usr/share/themes/Adapta-Eta -BindReadOnly=/usr/share/themes/Adapta-Nokto -BindReadOnly=/usr/share/themes/Adapta-Nokto-Eta -BindReadOnly=/usr/share/icons/Paper - -BindReadOnly=/storage/citadel-state/resolv.conf:/etc/resolv.conf - -# -# Bind mounts for sound and pulse audio -# -Bind=/dev/snd -Bind=/dev/shm -BindReadOnly=/run/user/1000/pulse:/run/user/host/pulse - -BindReadOnly=/tmp/.X11-unix -BindReadOnly=/run/user/1000/wayland-0:/run/user/host/wayland-0 - -$EXTRA_BIND_MOUNTS - -# -# Uncomment to enable kvm access in container -# -#Bind=/dev/kvm - -# -# Uncomment to enable GPU access in container -# -#Bind=/dev/dri/renderD128 - -[Network] -Zone=clear -"###; - -pub const APPIMG_SERVICE_TEMPLATE: &str = r###" -[Unit] -Description=Application Image $APPIMG_NAME instance -Wants=citadel-desktopd.service - -[Service] -Environment=SYSTEMD_NSPAWN_SHARE_NS_IPC=1 -ExecStart=/usr/bin/systemd-nspawn --quiet --keep-unit --machine=$APPIMG_NAME --link-journal=try-guest --directory=/storage/appimg/$APPIMG_NAME/rootfs - -KillMode=mixed -Type=notify -RestartForceExitStatus=133 -SuccessExitStatus=133 -"###; diff --git a/citadel-tools/citadel-appimg/src/util.rs b/citadel-tools/citadel-appimg/src/util.rs deleted file mode 100644 index 5a005e8..0000000 --- a/citadel-tools/citadel-appimg/src/util.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::io::Write; -use std::fs::File; -use std::path::Path; - -use Result; - -pub fn path_filename(path: &Path) -> &str { - if let Some(osstr) = path.file_name() { - if let Some(name) = osstr.to_str() { - return name; - } - } - "" -} - -pub fn write_string_to_file(path: &Path, s: &str) -> Result<()> { - let mut f = File::create(path)?; - f.write_all(s.as_bytes())?; - Ok(()) -} - -fn is_alphanum_or_dash(c: char) -> bool { - is_ascii(c) && (c.is_alphanumeric() || c == '-') -} - -fn is_ascii(c: char) -> bool { - c as u32 <= 0x7F -} - -fn is_first_char_alphabetic(s: &str) -> bool { - if let Some(c) = s.chars().next() { - return is_ascii(c) && c.is_alphabetic() - } - false -} - -pub fn is_valid_name(name: &str) -> bool { - is_first_char_alphabetic(name) && name.chars().all(is_alphanum_or_dash) -} diff --git a/citadel-tools/citadel-desktopd/Cargo.lock b/citadel-tools/citadel-desktopd/Cargo.lock deleted file mode 100644 index 05575ae..0000000 --- a/citadel-tools/citadel-desktopd/Cargo.lock +++ /dev/null @@ -1,479 +0,0 @@ -[[package]] -name = "aho-corasick" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atty" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bytes" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cc" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "chrono" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "desktopd" -version = "0.1.0" -dependencies = [ - "env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "inotify 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "env_logger" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "gcc" -version = "0.3.54" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "inotify" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "inotify-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "inotify-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "iovec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.36" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "log" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memchr" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nix" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-integer" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-iter" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_syscall" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex-syntax" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc-demangle" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde_derive" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive_internals" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synstructure" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termcolor" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wincolor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termion" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread_local" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "time" -version = "0.1.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "toml" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "utf8-ranges" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wincolor" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" -"checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859" -"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2" -"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" -"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" -"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" -"checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9" -"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0" -"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" -"checksum env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f15f0b172cb4f52ed5dbf47f774a387cd2315d1bf7894ab5af9b083ae27efa5a" -"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" -"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" -"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" -"checksum inotify 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41aaf46578a4628ff6c17c30993aed7e5188fae0817c78c558d3b7baaba1ffe5" -"checksum inotify-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7dceb94c43f70baf4c4cd6afbc1e9037d4161dbe68df8a2cd4351a23319ee4fb" -"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" -"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" -"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" -"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" -"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" -"checksum nix 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fd5681d13fda646462cfbd4e5f2051279a89a544d50eb98c365b507246839f" -"checksum num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cc4083e14b542ea3eb9b5f33ff48bd373a92d78687e74f4cc0a30caeb754f0ca" -"checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" -"checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -"checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa" -"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" -"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e" -"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526" -"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0" -"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5" -"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" -"checksum termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9065bced9c3e43453aa3d56f1e98590b8455b341d2fa191a1090c0dd0b242c75" -"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" -"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" -"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0878187fa88838d2006c0a76f30d64797098426245b375383f60acb6aed8a203" diff --git a/citadel-tools/citadel-desktopd/Cargo.toml b/citadel-tools/citadel-desktopd/Cargo.toml deleted file mode 100644 index d128b66..0000000 --- a/citadel-tools/citadel-desktopd/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "citadel-desktopd" -version = "0.1.0" -authors = ["brl@subgraph.com"] - -[dependencies] -failure = "0.1.1" -lazy_static = "1.0" -log = "0.4.0" -env_logger = "0.5.3" -inotify = "0.5" -toml = "0.4.5" -serde_derive = "1.0.27" -serde = "1.0.27" -nix = "0.10.0" diff --git a/citadel-tools/citadel-desktopd/conf/citadel-desktopd.conf b/citadel-tools/citadel-desktopd/conf/citadel-desktopd.conf deleted file mode 100644 index 1e7cc02..0000000 --- a/citadel-tools/citadel-desktopd/conf/citadel-desktopd.conf +++ /dev/null @@ -1,6 +0,0 @@ -[options] -exec_prefix="/usr/bin/realms run --" -target_directory="/home/citadel/.local/share/applications" - -[[sources]] -path="/run/realms/current.realm/rootfs/usr/share/applications" diff --git a/citadel-tools/citadel-desktopd/conf/citadel-desktopd.service b/citadel-tools/citadel-desktopd/conf/citadel-desktopd.service deleted file mode 100644 index a367e4e..0000000 --- a/citadel-tools/citadel-desktopd/conf/citadel-desktopd.service +++ /dev/null @@ -1,5 +0,0 @@ -[Unit] -Description=Desktop Integration Manager - -[Service] -ExecStart=/usr/libexec/citadel-desktopd /usr/share/citadel/citadel-desktopd.conf diff --git a/citadel-tools/citadel-desktopd/src/config.rs b/citadel-tools/citadel-desktopd/src/config.rs deleted file mode 100644 index 5760e9d..0000000 --- a/citadel-tools/citadel-desktopd/src/config.rs +++ /dev/null @@ -1,120 +0,0 @@ -use std::path::{Path,PathBuf}; -use std::fs::File; -use std::io::Read; -use Result; -use toml; - -#[derive(Clone)] -pub struct Config { - exec_prefix: String, - target_directory: PathBuf, - source_paths: Vec, -} - - -impl Config { - pub fn from_path>(path: P) -> Result { - let toml = ConfigToml::from_path(path)?; - let config = toml.to_config()?; - Ok(config) - } - - pub fn exec_prefix(&self) -> &str { - self.exec_prefix.as_ref() - } - - pub fn target_directory(&self) -> &Path { - self.target_directory.as_ref() - } - - pub fn source_paths(&self) -> &Vec { - self.source_paths.as_ref() - } -} - -#[derive(Deserialize)] -struct ConfigToml { - options: Option, - sources: Option>, -} - -#[derive(Deserialize)] -struct Options { - exec_prefix: Option, - target_directory: Option, -} - -impl Options { - fn exec_prefix(&self) -> Result<&str> { - match self.exec_prefix { - Some(ref s) => Ok(s.as_str()), - None => Err(format_err!("missing 'exec_prefix=' field")), - } - } - - fn target_directory(&self) -> Result<&str> { - match self.target_directory { - Some(ref s) => Ok(s.as_str()), - None => Err(format_err!("missing 'target_directory=' field")), - } - } -} - -#[derive(Deserialize,Clone)] -struct Source { - path: Option, -} - -impl Source { - fn path(&self) -> Result<&str> { - match self.path { - Some(ref s) => Ok(s.as_str()), - None => Err(format_err!("missing 'path=' field")), - } - } -} - -fn load_as_string(path: &Path) -> Result { - let mut f = File::open(path)?; - let mut buffer = String::new(); - f.read_to_string(&mut buffer)?; - Ok(buffer) -} - -impl ConfigToml { - fn from_path>(path: P) -> Result { - let s = load_as_string(path.as_ref())?; - let config = toml::from_str::(&s)?; - Ok(config) - } - - fn options(&self) -> Result<&Options> { - self.options.as_ref() - .ok_or(format_err!("missing '[options]' section")) - } - - fn sources(&self) -> Result> { - match self.sources { - Some(ref srcs) => Ok(srcs.clone()), - None => Err(format_err!("missing '[[sources]]' section(s)")), - } - } - - fn to_config(&self) -> Result { - let options = self.options()?; - let exec_prefix = options.exec_prefix()?.to_string() + " "; - let target_path = options.target_directory()?.to_string(); - let target_directory = PathBuf::from(target_path); - - let mut source_paths = Vec::new(); - for src in self.sources()? { - let path = src.path()?; - source_paths.push(PathBuf::from(path)); - } - Ok(Config{ - exec_prefix, - target_directory, - source_paths, - }) - } -} diff --git a/citadel-tools/citadel-desktopd/src/desktop.rs b/citadel-tools/citadel-desktopd/src/desktop.rs deleted file mode 100644 index 542c6a5..0000000 --- a/citadel-tools/citadel-desktopd/src/desktop.rs +++ /dev/null @@ -1,188 +0,0 @@ -use std::io::Write; -use std::fs::File; -use std::path::Path; -use std::collections::HashMap; -use Result; - - -pub struct DesktopFile { - filename: String, - lines: Vec, - // map from key of key/value pair to index of line in lines vector - main_map: HashMap, - // map from group name to map of key/value -> index - groups: HashMap>, -} - - -impl DesktopFile { - - pub fn write_to_dir>(&self, directory: P) -> Result<()> { - let mut path = directory.as_ref().to_path_buf(); - path.push(self.filename.as_str()); - let f = File::create(&path)?; - self.write_to(f)?; - Ok(()) - } - - pub fn write_to(&self, mut w: W) -> Result<()> { - for line in &self.lines { - line.write_to(&mut w)?; - } - Ok(()) - } - - pub fn filename(&self) -> &str { - self.filename.as_ref() - } - - // - // Conditions for translating a .desktop entry - // - // Type=Application // Mandatory 'Type' key must be Application - // NotShowIn= // If 'NotShowIn' key is present, must not contain GNOME - // OnlyShowIn= // If 'OnlyShowIn' key is present, must contain 'GNOME' - // Terminal=false // If 'Terminal' key is present, must be false - // Hidden=false // If 'Hidden' key is present, must be false - // - pub fn is_showable(&self) -> bool { - self.is_application_type() && self.show_in_gnome() && - !(self.needs_terminal() || self.is_hidden()) - } - - fn needs_terminal(&self) -> bool { - self.key_exists_and_not_false("Terminal") - } - - fn is_application_type(&self) -> bool { - if let Some(t) = self.get_key_val("Type") { - return t == "Application" - } - false - } - - fn show_in_gnome(&self) -> bool { - if self.key_exists("NotShowIn") && self.key_value_contains("NotShowIn", "GNOME") { - return false; - } - if self.key_exists("OnlyShowIn") && !self.key_value_contains("OnlyShowIn", "GNOME") { - return false; - } - true - } - - fn key_value_contains(&self, key: &str, s: &str) -> bool { - match self.get_key_val(key) { - Some(val) => val.contains(s), - None => false, - } - } - - fn key_exists(&self, key: &str) -> bool { - self.main_map.contains_key(key) - } - - fn is_hidden(&self) -> bool { - self.key_exists_and_not_false("Hidden") - } - - fn key_exists_and_not_false(&self, key: &str) -> bool { - if let Some(s) = self.get_key_val(key) { - if s != "false" { - return true; - } - } - false - } - - fn get_key_val(&self, key: &str) -> Option<&str> { - if let Some(idx) = self.main_map.get(key) { - match self.lines[*idx] { - Line::KeyValue(_, ref v) => return Some(v), - ref line => panic!("Key lookup on '{}' returned wrong line type: {:?}", key, line), - } - } - None - } - - - pub fn new(filename: &str) -> DesktopFile { - DesktopFile { - filename: filename.to_string(), - lines: Vec::new(), - main_map: HashMap::new(), - groups: HashMap::new(), - } - } - - pub fn add_line(&mut self, line: Line) { - if line.is_key_value_type() { - let idx = self.lines.len(); - self.main_map.insert(line.get_key_string(), idx); - } - self.lines.push(line); - } - - pub fn add_action_line(&mut self, action: &str, line: Line) { - if line.is_key_value_type() { - let idx = self.lines.len(); - let map = self.groups.entry(action.to_string()).or_insert(HashMap::new()); - map.insert(line.get_key_string(), idx); - } - self.lines.push(line); - } - - -} - - -#[derive(Debug)] -pub enum Line { - Empty, - Comment(String), - ExecLine(String), - KeyValue(String,String), - KeyLocaleValue(String,String,String), - DesktopHeader, - ActionHeader(String), - GroupHeader(String) -} - - -impl Line { - pub fn is_action_header(&self) -> bool { - match *self { - Line::ActionHeader(..) => true, - _ => false, - } - } - - fn is_key_value_type(&self) -> bool { - match *self { - Line::KeyValue(..) | Line::KeyLocaleValue(..) => true, - _ => false, - } - } - - fn get_key_string(&self) -> String { - match *self { - Line::KeyValue(ref k, ..) => k.to_string(), - Line::KeyLocaleValue(ref k, ref loc, ..) => format!("{}[{}]", k, loc), - _ => panic!("get_key_string() called on Line item which is not a key/value type"), - } - } - - fn write_to(&self, mut w: W) -> Result<()> { - match *self { - Line::Empty => writeln!(w)?, - Line::Comment(ref s) => writeln!(w, "#{}", s)?, - Line::ExecLine(ref s) => writeln!(w, "Exec={}", s)?, - Line::KeyValue(ref k, ref v) => writeln!(w, "{}={}", k, v)?, - Line::KeyLocaleValue(ref k, ref loc, ref v) => writeln!(w, "{}[{}]={}", k, loc, v)?, - Line::DesktopHeader => writeln!(w, "[Desktop Entry]")?, - Line::ActionHeader(ref action) => writeln!(w, "[Desktop Action {}]", action)?, - Line::GroupHeader(..) => {}, - } - Ok(()) - } -} diff --git a/citadel-tools/citadel-desktopd/src/desktop_file_sync.rs b/citadel-tools/citadel-desktopd/src/desktop_file_sync.rs deleted file mode 100644 index 573f623..0000000 --- a/citadel-tools/citadel-desktopd/src/desktop_file_sync.rs +++ /dev/null @@ -1,120 +0,0 @@ -use std::sync::{Arc,Mutex}; -use std::path::Path; -use std::fs; - -use failure::ResultExt; - -use monitor::{DirectoryMonitor,MonitorEventHandler}; -use parser::DesktopFileParser; -use config::Config; -use Result; - - -pub struct DesktopFileSync { - config: Config, - monitor: DirectoryMonitor, -} - -impl DesktopFileSync { - pub fn new(config: Config) -> DesktopFileSync { - let handler = Arc::new(Mutex::new(SyncHandler::new(config.clone()))); - let monitor = DirectoryMonitor::new(handler.clone()); - DesktopFileSync { - config, monitor - } - } - - pub fn clear_target_directory(&self) -> Result<()> { - let entries = fs::read_dir(self.config.target_directory())?; - - for entry in entries { - let path = entry?.path(); - if is_desktop_file(&path) { - fs::remove_file(&path).context(format!("remove_file({:?})", path))?; - } - } - Ok(()) - } - - pub fn sync_source(&mut self, src: &Path) -> Result<()> { - self.clear_target_directory()?; - self.sync_source_directory(src)?; - self.monitor.set_monitor_sources(&[src.to_path_buf()]); - Ok(()) - } - - fn sync_source_directory(&self, src: &Path) -> Result<()> { - let entries = fs::read_dir(src)?; - for entry in entries { - let path = entry?.path(); - if is_desktop_file(path.as_path()) { - if let Err(e) = sync_desktop_file(path.as_path(), &self.config) { - info!("error syncing desktop file {:?}: {}", path, e); - } - } - } - Ok(()) - } -} - -struct SyncHandler { - config: Config, -} - -impl SyncHandler { - fn new(config: Config) -> SyncHandler { - SyncHandler { config } - } -} - -impl MonitorEventHandler for SyncHandler { - fn file_added(&self, path: &Path) -> Result<()> { - info!("file_added: {:?}", path); - if is_desktop_file(path) { - sync_desktop_file(path, &self.config)?; - } - Ok(()) - } - - fn file_removed(&self, path: &Path) -> Result<()> { - info!("file_removed: {:?}", path); - let filename = filename_from_path(path)?; - let target_path = self.config.target_directory().join(filename); - if target_path.exists() { - fs::remove_file(target_path.as_path())?; - } - Ok(()) - } -} - -fn sync_desktop_file(source: &Path, config: &Config) -> Result<()> { - if !is_desktop_file(source) { - return Err(format_err!("source path [{:?}] is not desktop file", source)); - } - let df = DesktopFileParser::parse_from_path(source, config.exec_prefix())?; - if df.is_showable() { - df.write_to_dir(config.target_directory())?; - } else { - info!("ignoring {} as not showable", df.filename()); - } - Ok(()) -} - -fn is_desktop_file(path: &Path) -> bool { - if let Some(ext) = path.extension() { - return ext == "desktop" - } - false -} - -fn filename_from_path(path: &Path) -> Result<&str> { - let filename = match path.file_name() { - Some(name) => name, - None => return Err(format_err!("Path {:?} has no filename component", path)), - }; - match filename.to_str() { - Some(s) => Ok(s), - None => Err(format_err!("Filename has invalid utf8 encoding")), - } -} - diff --git a/citadel-tools/citadel-desktopd/src/main.rs b/citadel-tools/citadel-desktopd/src/main.rs deleted file mode 100644 index 8f4c252..0000000 --- a/citadel-tools/citadel-desktopd/src/main.rs +++ /dev/null @@ -1,57 +0,0 @@ -#[macro_use] extern crate failure; -#[macro_use] extern crate lazy_static; -#[macro_use] extern crate log; -#[macro_use] extern crate serde_derive; -extern crate env_logger; -extern crate serde; -extern crate toml; -extern crate inotify; -extern crate nix; - -mod desktop; -mod parser; -mod config; -mod monitor; -mod desktop_file_sync; - -use std::result; -use std::process; -use failure::Error; -use desktop_file_sync::DesktopFileSync; - -use config::Config; - -pub type Result = result::Result; - - -fn main() { - std::env::set_var("RUST_LOG", "info"); - env_logger::init(); - - let mut args = std::env::args(); - args.next(); - if args.len() != 1 { - println!("expected config file argument"); - process::exit(1); - } - - let config_path = args.next().unwrap(); - let config = match Config::from_path(&config_path) { - Err(e) => { - warn!("Failed to load configuration file: {}", e); - process::exit(1); - }, - Ok(config) => config, - }; - - let src = config.source_paths().first().unwrap().clone(); - - let mut dfs = DesktopFileSync::new(config.clone()); - if let Err(e) = dfs.sync_source(src.as_path()) { - warn!("error calling sync_source: {}", e); - } - loop { - std::thread::sleep(std::time::Duration::new(120, 0)); - } - -} diff --git a/citadel-tools/citadel-desktopd/src/monitor.rs b/citadel-tools/citadel-desktopd/src/monitor.rs deleted file mode 100644 index d71641f..0000000 --- a/citadel-tools/citadel-desktopd/src/monitor.rs +++ /dev/null @@ -1,210 +0,0 @@ -use std::collections::HashMap; -use std::path::{Path,PathBuf}; -use std::sync::{Arc,Mutex,Once,ONCE_INIT}; -use std::sync::atomic::{AtomicBool,Ordering}; -use std::thread::{self,JoinHandle}; -use std::os::unix::thread::JoinHandleExt; -use std::io::ErrorKind; - -use nix::libc; -use nix::sys::signal; - -use inotify::{Events,Inotify,EventMask,WatchMask,Event,WatchDescriptor}; - -use Result; - -pub trait MonitorEventHandler: Send+Sync { - fn file_added(&self, path: &Path) -> Result<()> { let _ = path; Ok(()) } - fn file_removed(&self, path: &Path) -> Result<()> { let _ = path; Ok(()) } - fn directory_added(&self, path: &Path) -> Result<()> { let _ = path; Ok(()) } - fn directory_removed(&self, path: &Path) -> Result<()> { let _ = path; Ok(()) } -} - -pub struct DirectoryMonitor { - event_handler: Arc>, - worker_handle: Option, -} - -impl DirectoryMonitor { - pub fn new(handler: Arc>) -> DirectoryMonitor { - initialize(); - DirectoryMonitor { - event_handler: handler, - worker_handle: None, - } - } - - pub fn set_monitor_sources(&mut self, sources: &[PathBuf]) { - if let Some(handle) = self.worker_handle.take() { - handle.stop(); - handle.wait(); - } - let sources = Vec::from(sources); - let h = MonitorWorker::start_worker(sources, self.event_handler.clone()); - self.worker_handle = Some(h); - } -} - -struct MonitorWorker { - descriptors: HashMap, - inotify: Inotify, - exit_flag: Arc, - watch_paths: Vec, - handler: Arc>, -} - - -impl MonitorWorker { - fn start_worker(watch_paths: Vec, handler: Arc>) -> WorkerHandle { - let exit_flag = Arc::new(AtomicBool::new(false)); - let flag_clone = exit_flag.clone(); - let jhandle = thread::spawn(move || { - - let mut worker = match MonitorWorker::new(watch_paths, flag_clone, handler) { - Ok(worker) => worker, - Err(e) => { - info!("failed to initialize inotify handle: {}", e); - return; - } - }; - if let Err(e) = worker.run() { - info!("error returned from worker thread: {}", e); - } - }); - WorkerHandle::new(jhandle, exit_flag) - } - - fn new(watch_paths: Vec, exit_flag: Arc, handler: Arc>) -> Result { - Ok(MonitorWorker { - descriptors: HashMap::new(), - inotify: Inotify::init()?, - exit_flag, - watch_paths, - handler, - }) - } - - fn add_watches(&mut self) -> Result<()> { - let watch_flags = WatchMask::CREATE | WatchMask::DELETE | WatchMask::MOVED_TO | - WatchMask::DONT_FOLLOW | WatchMask::ONLYDIR; - for p in &self.watch_paths { - let wd = self.inotify.add_watch(p, watch_flags)?; - self.descriptors.insert(wd, p.clone()); - } - Ok(()) - } - - fn read_events<'a>(&mut self, buffer: &'a mut [u8]) -> Result>> { - if self.exit_flag.load(Ordering::Relaxed) { - return Ok(None); - } - - match self.inotify.read_events_blocking(buffer) { - Ok(events) => Ok(Some(events)), - Err(e) => { - if e.kind() == ErrorKind::Interrupted { - Ok(None) - } else { - Err(e.into()) - } - } - } - } - - fn process_events(&self, events: Events) { - for ev in events { - if let Err(e) = self.handle_event(&ev) { - info!("error handling inotify event: {}", e); - } - } - } - - fn run(&mut self) -> Result<()> { - info!("running monitor event loop"); - self.add_watches()?; - let mut buffer = [0u8; 4096]; - loop { - match self.read_events(&mut buffer)? { - Some(events) => self.process_events(events), - None => break, - } - } - Ok(()) - } - - fn full_event_path(&self, ev: &Event) -> Result { - let filename = ev.name - .ok_or(format_err!("inotify event received without a filename"))?; - let path = self.descriptors.get(&ev.wd) - .ok_or(format_err!("Failed to find descriptor for received inotify event"))?; - Ok(path.join(filename)) - } - - fn handle_event(&self, ev: &Event) -> Result<()> { - let handler = self.handler.lock().unwrap(); - let pb = self.full_event_path(ev)?; - let path = pb.as_path(); - let is_create = ev.mask.intersects(EventMask::CREATE|EventMask::MOVED_TO); - if !is_create && !ev.mask.contains(EventMask::DELETE) { - return Err(format_err!("Unexpected mask value for inotify event: {:?}", ev.mask)); - } - - if ev.mask.contains(EventMask::ISDIR) { - if is_create { - handler.directory_added(path)?; - } else { - handler.directory_removed(path)?; - } - - } else { - if is_create { - handler.file_added(path)?; - } else { - handler.file_removed(path)?; - } - } - Ok(()) - } -} - -pub struct WorkerHandle { - join_handle: JoinHandle<()>, - exit_flag: Arc, -} - -impl WorkerHandle { - fn new(join_handle: JoinHandle<()>, exit_flag: Arc) -> WorkerHandle { - WorkerHandle { join_handle, exit_flag } - } - - pub fn stop(&self) { - info!("calling stop on monitor"); - let tid = self.join_handle.as_pthread_t(); - self.exit_flag.store(true, Ordering::Relaxed); - unsafe { - libc::pthread_kill(tid, signal::SIGUSR1 as libc::c_int); - } - } - - pub fn wait(self) { - if let Err(e) = self.join_handle.join() { - warn!("monitor thread panic with '{:?}'", e); - } - } -} - -static INITIALIZE_ONCE: Once = ONCE_INIT; - -fn initialize() { - INITIALIZE_ONCE.call_once(|| { - let h = signal::SigHandler::Handler(sighandler); - let sa = signal::SigAction::new(h, signal::SaFlags::empty(), signal::SigSet::empty()); - if let Err(e) = unsafe { signal::sigaction(signal::SIGUSR1, &sa) } { - warn!("Error setting signal handler: {}", e); - } - }); -} - -extern fn sighandler(_: libc::c_int) { - // do nothing, signal is only used to EINTR blocking inotify call -} \ No newline at end of file diff --git a/citadel-tools/citadel-desktopd/src/parser.rs b/citadel-tools/citadel-desktopd/src/parser.rs deleted file mode 100644 index d8f8633..0000000 --- a/citadel-tools/citadel-desktopd/src/parser.rs +++ /dev/null @@ -1,307 +0,0 @@ - -use std::io::Read; -use std::fs::File; -use std::path::Path; -use std::collections::HashSet; -use desktop::{DesktopFile,Line}; -use Result; - -lazy_static! { - // These are the keys which are copied into the translated .desktop files - static ref KEY_WHITELIST: HashSet<&'static str> = [ - "Type", "Version", "Name", "GenericName", "NoDisplay", "Comment", "Icon", "Hidden", - "OnlyShowIn", "NotShowIn", "Path", "Terminal", "Actions", "MimeType", - "Categories", "Keywords", "StartupNotify", "StartupWMClass", "URL", "DocPath", - "X-GNOME-FullName", "X-GNOME-Provides", "X-Desktop-File-Install-Version", "X-GNOME-UsesNotifications", - "X-GNOME-DocPath", "X-Geoclue-Reason", "X-GNOME-SingleWindow", "X-GNOME-Gettext-Domain", - "X-MultipleArgs", - ].iter().cloned().collect(); - - // These are keys which are recognized but deliberately ignored. - static ref KEY_IGNORELIST: HashSet<&'static str> = [ - "DBusActivatable", "Implements", "TryExec", "InitialPreference", "Encoding", "X-KDE-Protocols", "X-GIO-NoFuse", "X-Gnome-Vfs-System", - "X-GNOME-Autostart-Phase", "X-GNOME-Autostart-Notify", "X-GNOME-AutoRestart", - "X-GNOME-Bugzilla-Bugzilla", "X-GNOME-Bugzilla-Product", "X-GNOME-Bugzilla-Component", "X-GNOME-Bugzilla-Version", - "X-GNOME-Bugzilla-ExtraInfoScript", "X-GNOME-Bugzilla-OtherBinaries", "X-GNOME-Autostart-enabled", - "X-AppInstall-Package", "X-KDE-SubstituteUID", "X-Ubuntu-Gettext-Domain", "X-AppInstall-Keywords", - "X-Ayatana-Desktop-Shortcuts", "X-GNOME-Settings-Panel", "X-GNOME-WMSettingsModule", "X-GNOME-WMName", - "X-GnomeWMSettingsLibrary", - ].iter().cloned().collect(); -} - -fn is_whitelisted_key(key: &str) -> bool { - KEY_WHITELIST.contains(key) -} - -fn filename_from_path(path: &Path) -> Result<&str> { - let filename = match path.file_name() { - Some(name) => name, - None => return Err(format_err!("Path {:?} has no filename component", path)), - }; - match filename.to_str() { - Some(s) => Ok(s), - None => Err(format_err!("Filename has invalid utf8 encoding")), - } -} -pub struct DesktopFileParser { - desktop_file: DesktopFile, - exec_prefix: String, - seen_header: bool, - current_action: Option, - in_ignored_group: bool, - known_actions: HashSet, -} - - -impl DesktopFileParser { - fn new(filename: &str, exec_prefix: &str) -> DesktopFileParser { - DesktopFileParser { - desktop_file: DesktopFile::new(filename), - exec_prefix: exec_prefix.to_string(), - seen_header: false, - current_action: None, - in_ignored_group: false, - known_actions: HashSet::new(), - } - } - - pub fn parse_from_path>(path: P, exec_prefix: &str) -> Result { - let filename = filename_from_path(path.as_ref())?; - let f = File::open(path.as_ref())?; - DesktopFileParser::parse_from_reader(f, filename, exec_prefix) - } - - fn parse_from_reader(mut r: T, filename: &str, exec_prefix: &str) -> Result { - let mut buffer = String::new(); - r.read_to_string(&mut buffer)?; - DesktopFileParser::parse_from_string(&buffer, filename, exec_prefix) - } - - fn parse_from_string(body: &str, filename: &str, exec_prefix: &str) -> Result { - let mut parser = DesktopFileParser::new(filename, exec_prefix); - for s in body.lines() { - match LineParser::parse(s) { - Some(line) => parser.process_line(line)?, - None => return Err(format_err!("Failed to parse line: '{}'", s)) - } - } - Ok(parser.desktop_file) - } - - fn process_initial(&mut self, line: Line) -> Result<()> { - match line { - Line::Comment(_) | Line::Empty => {}, - Line::DesktopHeader => self.seen_header = true, - _ => return Err(format_err!("Missing Desktop Entry header")) - } - self.desktop_file.add_line(line); - Ok(()) - } - - fn process_line(&mut self, mut line: Line) -> Result<()> { - if self.in_ignored_group && !line.is_action_header() { - return Ok(()) - } - if !self.seen_header { - return self.process_initial(line) - } - - if let Line::KeyValue(ref k, ref value) = line { - if k == "Actions" { - for s in value.split_terminator(";") { - self.known_actions.insert(s.trim().to_string()); - } - } - - } - - match line { - Line::ExecLine(ref mut s) => { - s.insert_str(0,self.exec_prefix.as_str()) - }, - Line::DesktopHeader => return Err(format_err!("Duplicate Desktop Entry header")), - Line::ActionHeader(ref action) => { - if self.known_actions.contains(action) { - self.current_action = Some(action.to_string()); - self.in_ignored_group = false; - } else { - return Err(format_err!("Desktop Action header with undecleared action: {}", action)) - } - }, - Line::GroupHeader(_) => { - self.in_ignored_group = true; - return Ok(()) - }, - Line::KeyLocaleValue(ref k,_,_) | Line::KeyValue(ref k,_) => { - - if !is_whitelisted_key(k) { - if !KEY_IGNORELIST.contains(k.as_str()) { - info!("Unknown key in {}: {}", self.desktop_file.filename(), k); - } - return Ok(()) - } - } - _ => {}, - } - if let Some(ref action) = self.current_action { - self.desktop_file.add_action_line(action, line) - } else { - self.desktop_file.add_line(line); - } - Ok(()) - } -} - -const DESKTOP_ACTION: &'static str = "Desktop Action "; - -struct LineParser<'a> { - s: &'a str, -} - -impl <'a> LineParser<'a> { - fn new(s: &'a str) -> LineParser<'a> { - LineParser { - s, - } - } - - fn parse(s: &'a str) -> Option { - if let Some(line) = LineParser::new(s)._parse() { - if validate_line(&line) { - return Some(line) - } - } - None - } - - fn first(&self) -> Option { - self.s.chars().next() - } - - fn last(&self) -> Option { - self.s.chars().next_back() - } - - fn _parse(&mut self) -> Option { - match self.first() { - None => Some(Line::Empty), - Some('#') => Some(Line::Comment(self.s[1..].to_string())), - Some('[') => self.parse_header(), - Some(_) => self.parse_keyval(), - } - } - - fn parse_header(&mut self) -> Option { - if self.last().unwrap() != ']' { - return None - } - let content = &self.s[1..self.s.len() - 1]; - if content.starts_with(DESKTOP_ACTION) { - let action = &content[DESKTOP_ACTION.len()..]; - return Some(Line::ActionHeader(action.to_string())) - } else if content == "Desktop Entry" { - return Some(Line::DesktopHeader) - } - return Some(Line::GroupHeader(content.to_string())) - } - - fn parse_keyval(&self) -> Option { - let parts: Vec<&str> = self.s.splitn(2, "=").collect(); - if parts.len() != 2 { - return None - } - let key = parts[0].trim(); - let val = parts[1].trim(); - if !key.contains("[") { - if key == "Exec" { - return Some(Line::ExecLine(val.to_string())) - } - return Some(Line::KeyValue(key.to_string(), val.to_string())) - } - self.parse_locale(key).map(|(key,locale)| Line::KeyLocaleValue(key, locale, val.to_string())) - } - - fn parse_locale(&self, key: &str) -> Option<(String,String)> { - let idx = key.find("[").unwrap(); - let (k,loc) = key.split_at(idx); - let mut chars = loc.chars(); - if let Some(']') = chars.next_back() { - chars.next(); - if k.trim() == "Exec" { - // Exec key with locale not allowed - return None; - } - return Some((k.trim().to_string(), chars.as_str().to_string())) - } - None - } -} - -fn is_alphanum_or_dash(c: char) -> bool { - is_ascii(c) && (c.is_alphanumeric() || c == '-') -} - -fn is_ascii(c: char) -> bool { - c as u32 <= 0x7F -} - -fn is_first_char_alphabetic(s: &str) -> bool { - if let Some(c) = s.chars().next() { - return is_ascii(c) && c.is_alphabetic() - } - false -} - -fn is_valid_key(key: &str) -> bool { - if !is_first_char_alphabetic(key) { - return false - } - key.chars().all(is_alphanum_or_dash) -} - -fn is_valid_locale(locale: &str) -> bool { - !locale.is_empty() && locale.chars().all(|c| { - is_alphanum_or_dash(c) || c == '_' || c == '.' || c == '@' - }) -} - -fn is_valid_value(value: &str) -> bool { - value.chars().all(|c| { - !(c.is_control() || c as u32 == 0 ) - }) -} - -fn is_valid_action(action: &str) -> bool { - is_first_char_alphabetic(action) && action.chars().all(is_alphanum_or_dash) -} - -fn is_valid_group(group: &str) -> bool { - is_first_char_alphabetic(group) && group.chars().all(|c| { - is_ascii(c) && !c.is_control() - }) -} - -fn is_valid_exec(val: &str) -> bool { - val.chars().all(|c| { - is_ascii(c) && !(c.is_control() || c as u32 == 0) - }) -} - -pub fn validate_line(line: &Line) -> bool { - match *line { - Line::ExecLine(ref s) => is_valid_exec(s), - Line::KeyValue(ref k, ref v) => is_valid_key(k) && is_valid_value(v), - Line::KeyLocaleValue(ref k, ref l, ref v) => is_valid_key(k) && is_valid_locale(l) && is_valid_value(v), - Line::ActionHeader(ref action) => is_valid_action(action), - Line::GroupHeader(ref group) => is_valid_group(group), - _ => true, - } -} - -#[test] -fn test_parser() { - let tests = vec!["###", "", "# hello", "[Desktop Entry]", "[Desktop Action foo]", "Foo=Bar", "Foo[hehe]=Lol"]; - for t in tests { - println!("{:?}", LineParser::parse(t)); - } -} diff --git a/citadel-tools/citadel-realms/Cargo.lock b/citadel-tools/citadel-realms/Cargo.lock deleted file mode 100644 index 2ee093f..0000000 --- a/citadel-tools/citadel-realms/Cargo.lock +++ /dev/null @@ -1,350 +0,0 @@ -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atty" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cc" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "citadel-realms" -version = "0.1.0" -dependencies = [ - "clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clap" -version = "2.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libc" -version = "0.2.39" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_syscall" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "same-file" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde_derive" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive_internals" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "strsim" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "0.12.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synstructure" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termcolor" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termion" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "textwrap" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "toml" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-width" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vec_map" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "walkdir" -version = "2.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wincolor" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4" -"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2" -"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" -"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" -"checksum cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fedf677519ac9e865c4ff43ef8f930773b37ed6e6ea61b6b83b400a7b5787f49" -"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dc18f6f4005132120d9711636b32c46a233fad94df6217fa1d81c5e97a9f200" -"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" -"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" -"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff" -"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" -"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" -"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637" -"checksum serde 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4d6340aa5fcdac490a1aa3511ff079b1cdaa45ffb766b2fd83395dae085cd5" -"checksum serde_derive 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "a5c7e2a9833bb397a3284b55e208b895e2486b8e6c6682a428e309204cd9d75a" -"checksum serde_derive_internals 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc848d073be32cd982380c06587ea1d433bc1a4c4a111de07ec2286a3ddade8" -"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5bc2d6ff27891209efa5f63e9de78648d7801f085e4653701a692ce938d6fd" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" -"checksum termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "56c456352e44f9f91f774ddeeed27c1ec60a2455ed66d692059acfb1d731bda1" -"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" -"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" -"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" -"checksum walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63636bd0eb3d00ccb8b9036381b526efac53caf112b7783b730ab3f8e44da369" -"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" diff --git a/citadel-tools/citadel-realms/Cargo.toml b/citadel-tools/citadel-realms/Cargo.toml deleted file mode 100644 index 2dd374c..0000000 --- a/citadel-tools/citadel-realms/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "citadel-realms" -version = "0.1.0" -authors = ["Bruce Leidl "] -homepage = "http://github.com/subgraph/citadel" - -[dependencies] -libc = "0.2" -clap = "2.30.0" -failure = "0.1.1" -toml = "0.4.5" -serde_derive = "1.0.27" -serde = "1.0.27" -termcolor = "0.3" -walkdir = "2" diff --git a/citadel-tools/citadel-realms/README.md b/citadel-tools/citadel-realms/README.md deleted file mode 120000 index 7c0b648..0000000 --- a/citadel-tools/citadel-realms/README.md +++ /dev/null @@ -1 +0,0 @@ -../../docs/realms.md \ No newline at end of file diff --git a/citadel-tools/citadel-realms/src/appimg.rs b/citadel-tools/citadel-realms/src/appimg.rs deleted file mode 100644 index 0a93e1a..0000000 --- a/citadel-tools/citadel-realms/src/appimg.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::path::Path; -use std::process::Command; - -use Realm; -use Result; - -const BASE_APPIMG_PATH: &str = "/storage/appimg/base.appimg"; -const BTRFS_COMMAND: &str = "/usr/bin/btrfs"; - -pub fn clone_base_appimg(target_realm: &Realm) -> Result<()> { - if !Path::new(BASE_APPIMG_PATH).exists() { - bail!("base appimg does not exist at {}", BASE_APPIMG_PATH); - } - let target = format!("/realms/realm-{}/rootfs", target_realm.name()); - let target_path = Path::new(&target); - - if target_path.exists() { - bail!("cannot create clone of base appimg for realm '{}' because rootfs directory already exists at {}", - target_realm.name(), target); - } - - if !target_path.parent().unwrap().exists() { - bail!("cannot create clone of base appimg for realm '{}' because realm directory /realms/realm-{} does not exist.", - target_realm.name(), target_realm.name()); - } - - Command::new(BTRFS_COMMAND) - .args(&["subvolume", "snapshot", BASE_APPIMG_PATH, &target ]) - .status() - .map_err(|e| format_err!("failed to execute {}: {}", BTRFS_COMMAND, e))?; - Ok(()) - -} - -pub fn delete_rootfs_subvolume(realm: &Realm) -> Result<()> { - let path = realm.base_path().join("rootfs"); - Command::new(BTRFS_COMMAND) - .args(&["subvolume", "delete", path.to_str().unwrap() ]) - .status() - .map_err(|e| format_err!("failed to execute {}: {}", BTRFS_COMMAND, e))?; - Ok(()) -} - diff --git a/citadel-tools/citadel-realms/src/config.rs b/citadel-tools/citadel-realms/src/config.rs deleted file mode 100644 index 6bcffff..0000000 --- a/citadel-tools/citadel-realms/src/config.rs +++ /dev/null @@ -1,112 +0,0 @@ -use std::path::Path; -use std::fs::File; -use std::io::Read; -use toml; -use Result; - -fn default_true() -> bool { - true -} - -fn default_zone() -> String { - "clear".to_owned() -} - -#[derive (Deserialize,Clone)] -pub struct RealmConfig { - #[serde(default = "default_true", rename="use-shared-dir")] - use_shared_dir: bool, - - #[serde(default, rename="use-ephemeral-home")] - use_ephemeral_home: bool, - - #[serde(default = "default_true", rename="use-sound")] - use_sound: bool, - - #[serde(default = "default_true", rename="use-x11")] - use_x11: bool, - - #[serde(default = "default_true", rename="use-wayland")] - use_wayland: bool, - - #[serde(default, rename="use-kvm")] - use_kvm: bool, - - #[serde(default,rename="use-gpu")] - use_gpu: bool, - - #[serde(default = "default_true", rename="use-network")] - use_network: bool, - - #[serde(default = "default_zone", rename="network-zone")] - network_zone: String, -} - -impl RealmConfig { - pub fn load_or_default(path: &Path) -> Result { - if path.exists() { - let s = load_as_string(&path)?; - let config = toml::from_str::(&s)?; - Ok(config) - } else { - Ok(RealmConfig::default()) - } - } - - pub fn default() -> RealmConfig { - RealmConfig { - use_shared_dir: true, - use_ephemeral_home: false, - use_sound: true, - use_x11: true, - use_wayland: true, - use_kvm: false, - use_gpu: false, - use_network: true, - network_zone: default_zone(), - } - } - - pub fn kvm(&self) -> bool { - self.use_kvm - } - - pub fn gpu(&self) -> bool { - self.use_gpu - } - - pub fn shared_dir(&self) -> bool { - self.use_shared_dir - } - - pub fn emphemeral_home(&self) -> bool { - self.use_ephemeral_home - } - - pub fn sound(&self) -> bool { - self.use_sound - } - - pub fn x11(&self) -> bool { - self.use_x11 - } - - pub fn wayland(&self) -> bool { - self.use_wayland - } - - pub fn network(&self) -> bool { - self.use_network - } - - pub fn network_zone(&self) -> &str { - &self.network_zone - } -} - -fn load_as_string(path: &Path) -> Result { - let mut f = File::open(path)?; - let mut buffer = String::new(); - f.read_to_string(&mut buffer)?; - Ok(buffer) -} diff --git a/citadel-tools/citadel-realms/src/main.rs b/citadel-tools/citadel-realms/src/main.rs deleted file mode 100644 index a72bdb5..0000000 --- a/citadel-tools/citadel-realms/src/main.rs +++ /dev/null @@ -1,307 +0,0 @@ -#[macro_use] extern crate failure; -#[macro_use] extern crate serde_derive; - -extern crate libc; -extern crate clap; -extern crate toml; -extern crate termcolor; -extern crate walkdir; - -use failure::Error; -use clap::{App,Arg,ArgMatches,SubCommand}; -use clap::AppSettings::*; -use std::process::exit; -use std::cell::RefCell; -use std::result; - -pub type Result = result::Result; - -thread_local! { - pub static VERBOSE: RefCell = RefCell::new(true); -} -pub fn verbose() -> bool { - VERBOSE.with(|f| *f.borrow()) -} - -macro_rules! warn { - ($e:expr) => { println!("[!]: {}", $e); }; - ($fmt:expr, $($arg:tt)+) => { println!("[!]: {}", format!($fmt, $($arg)+)); }; -} - -macro_rules! info { - ($e:expr) => { if ::verbose() { println!("[+]: {}", $e); } }; - ($fmt:expr, $($arg:tt)+) => { if ::verbose() { println!("[+]: {}", format!($fmt, $($arg)+)); } }; -} - -mod manager; -mod realm; -mod util; -mod systemd; -mod config; -mod network; -mod appimg; - -use realm::{Realm,RealmSymlinks}; -use manager::RealmManager; -use config::RealmConfig; -use systemd::Systemd; -use network::NetworkConfig; - -fn main() { - let app = App::new("citadel-realms") - .about("Subgraph Citadel realm management") - .after_help("'realms help ' to display help for an individual subcommand\n") - .global_settings(&[ColoredHelp, DisableVersion, DeriveDisplayOrder, VersionlessSubcommands ]) - .arg(Arg::with_name("help").long("help").hidden(true)) - .arg(Arg::with_name("quiet") - .long("quiet") - .help("Don't display extra output")) - - .subcommand(SubCommand::with_name("list") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Display list of all realms")) - - .subcommand(SubCommand::with_name("shell") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Open shell in current or named realm") - - .arg(Arg::with_name("realm-name") - .help("Name of a realm to open shell in. Use current realm if omitted.")) - - .arg(Arg::with_name("root-shell") - .long("root") - .help("Open shell as root instead of user account."))) - - .subcommand(SubCommand::with_name("terminal") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Launch terminal in current or named realm") - - .arg(Arg::with_name("realm-name") - .help("Name of realm to open terminal in. Use current realm if omitted."))) - - .subcommand(SubCommand::with_name("start") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Start named realm or default realm") - .arg(Arg::with_name("realm-name") - .help("Name of realm to start. Use default realm if omitted."))) - - .subcommand(SubCommand::with_name("stop") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Stop a running realm by name") - .arg(Arg::with_name("realm-name") - .required(true) - .help("Name of realm to stop."))) - - .subcommand(SubCommand::with_name("default") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Choose a realm to start automatically on boot") - .arg(Arg::with_name("realm-name") - .help("Name of a realm to set as default. Display current default realm if omitted."))) - - .subcommand(SubCommand::with_name("current") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Choose a realm to set as 'current' realm") - .arg(Arg::with_name("realm-name") - .help("Name of a realm to set as current, will start if necessary. Display current realm name if omitted."))) - - .subcommand(SubCommand::with_name("run") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Execute a command in named realm or current realm") - - .arg(Arg::with_name("realm-name") - .help("Name of realm to run command in, start if necessary. Use current realm if omitted.")) - .arg(Arg::with_name("args") - .required(true) - .last(true) - .allow_hyphen_values(true) - .multiple(true))) - - .subcommand(SubCommand::with_name("update-appimg") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Launch shell to update application image") - - .arg(Arg::with_name("appimg-name") - .long("appimg") - .help("Name of application image in /storage/appimg directory. Default is to use base.appimg") - .takes_value(true))) - - - .subcommand(SubCommand::with_name("new") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Create a new realm with the name provided") - .arg(Arg::with_name("realm-name") - .required(true) - .help("Name to assign to newly created realm"))) - - .subcommand(SubCommand::with_name("remove") - .arg(Arg::with_name("help").long("help").hidden(true)) - .about("Remove realm by name") - - .arg(Arg::with_name("no-confirm") - .long("no-confirm") - .help("Do not prompt for confirmation.")) - .arg(Arg::with_name("remove-home") - .long("remove-home") - .help("Also remove home directory with --no-confirm rather than moving it to /realms/removed-homes")) - - .arg(Arg::with_name("realm-name") - .help("Name of realm to remove") - .required(true))); - - - - let matches = app.get_matches(); - - if matches.is_present("quiet") { - VERBOSE.with(|f| *f.borrow_mut() = false); - } - - let result = match matches.subcommand() { - ("list", _) => do_list(), - ("start", Some(m)) => do_start(m), - ("stop", Some(m)) => do_stop(m), - ("default", Some(m)) => do_default(m), - ("current", Some(m)) => do_current(m), - ("run", Some(m)) => do_run(m), - ("shell", Some(m)) => do_shell(m), - ("terminal", Some(m)) => do_terminal(m), - ("new", Some(m)) => do_new(m), - ("remove", Some(m)) => do_remove(m), - ("update-appimg", _) => do_update_appimg(), - _ => { - let _ = do_list(); - exit(0); - }, - }; - - if let Err(e) = result { - warn!("{}", e); - exit(1); - } -} - -fn is_root() -> bool { - unsafe { - libc::geteuid() == 0 - } -} - -fn require_root() -> Result<()> { - if !is_root() { - bail!("You need to do that as root") - } - Ok(()) -} - -fn do_list() -> Result<()> { - let manager = RealmManager::load()?; - println!(); - manager.list()?; - println!("\n 'realms help' for list of commands\n"); - Ok(()) -} - -fn do_start(matches: &ArgMatches) -> Result<()> { - require_root()?; - let mut manager = RealmManager::load()?; - match matches.value_of("realm-name") { - Some(name) => manager.start_named_realm(name)?, - None => manager.start_default()?, - }; - Ok(()) -} - -fn do_stop(matches: &ArgMatches) -> Result<()> { - require_root()?; - let name = matches.value_of("realm-name").unwrap(); - let mut manager = RealmManager::load()?; - manager.stop_realm(name)?; - Ok(()) -} - -fn do_default(matches: &ArgMatches) -> Result<()> { - let manager = RealmManager::load()?; - - match matches.value_of("realm-name") { - Some(name) => { - require_root()?; - manager.set_default_by_name(name)?; - }, - None => { - if let Some(name) = manager.default_realm_name() { - println!("Default Realm: {}", name); - } else { - println!("No default realm."); - } - }, - } - Ok(()) -} - -fn do_current(matches: &ArgMatches) -> Result<()> { - let manager = RealmManager::load()?; - - match matches.value_of("realm-name") { - Some(name) => { - require_root()?; - manager.set_current_by_name(name)?; - }, - None => { - if let Some(name) = manager.current_realm_name() { - println!("Current Realm: {}", name); - } else { - println!("No current realm."); - } - }, - } - Ok(()) -} - - -fn do_run(matches: &ArgMatches) -> Result<()> { - let args: Vec<&str> = matches.values_of("args").unwrap().collect(); - let mut v = Vec::new(); - for arg in args { - v.push(arg.to_string()); - } - let manager = RealmManager::load()?; - manager.run_in_realm(matches.value_of("realm-name"), &v, true)?; - Ok(()) -} - -fn do_shell(matches: &ArgMatches) -> Result<()> { - let manager = RealmManager::load()?; - let root = matches.is_present("root-shell"); - manager.launch_shell(matches.value_of("realm-name"), root)?; - Ok(()) -} - -fn do_terminal(matches: &ArgMatches) -> Result<()> { - let manager = RealmManager::load()?; - manager.launch_terminal(matches.value_of("realm-name"))?; - Ok(()) -} - -fn do_new(matches: &ArgMatches) -> Result<()> { - require_root()?; - let name = matches.value_of("realm-name").unwrap(); - let mut manager = RealmManager::load()?; - manager.new_realm(name)?; - Ok(()) -} - -fn do_remove(matches: &ArgMatches) -> Result<()> { - require_root()?; - let confirm = !matches.is_present("no-confirm"); - let save_home = !matches.is_present("remove-home"); - let name = matches.value_of("realm-name").unwrap(); - let mut manager = RealmManager::load()?; - manager.remove_realm(name, confirm, save_home)?; - Ok(()) -} - -fn do_update_appimg() -> Result<()> { - require_root()?; - let manager = RealmManager::load()?; - manager.base_appimg_update() -} diff --git a/citadel-tools/citadel-realms/src/manager.rs b/citadel-tools/citadel-realms/src/manager.rs deleted file mode 100644 index 867f074..0000000 --- a/citadel-tools/citadel-realms/src/manager.rs +++ /dev/null @@ -1,399 +0,0 @@ -use std::rc::Rc; -use std::cell::RefCell; -use std::path::{Path,PathBuf}; -use std::fs; -use std::collections::HashMap; -use std::io::Write; - - -use Realm; -use Result; -use Systemd; -use RealmSymlinks; -use NetworkConfig; -use util::*; - -const REALMS_BASE_PATH: &str = "/realms"; - -pub struct RealmManager { - /// Map from realm name -> realm - realm_map: HashMap, - - /// Sorted for 'list' - realm_list: Vec, - - /// track status of 'current' and 'default' symlinks - symlinks: Rc>, - - /// finds free ip addresses to use - network: Rc>, - - /// interface to systemd - systemd: Systemd, -} - - -impl RealmManager { - fn new() -> Result { - let network = RealmManager::create_network_config()?; - - Ok(RealmManager { - realm_map: HashMap::new(), - realm_list: Vec::new(), - symlinks: Rc::new(RefCell::new(RealmSymlinks::new())), - network: network.clone(), - systemd: Systemd::new(network), - }) - } - - fn create_network_config() -> Result>> { - let mut network = NetworkConfig::new(); - network.add_bridge("clear", "172.17.0.0/24")?; - Ok(Rc::new(RefCell::new(network))) - } - - pub fn load() -> Result { - let mut manager = RealmManager::new()?; - manager.symlinks.borrow_mut().load_symlinks()?; - if ! PathBuf::from(REALMS_BASE_PATH).exists() { - bail!("realms base directory {} does not exist", REALMS_BASE_PATH); - } - for dent in fs::read_dir(REALMS_BASE_PATH)? { - let path = dent?.path(); - manager.process_realm_path(&path) - .map_err(|e| format_err!("error processing entry {} in realm base dir: {}", path.display(), e))?; - } - manager.realm_list.sort_unstable(); - Ok(manager) - } - - /// - /// Process `path` as an entry from the base realms directory and - /// if `path` is a directory, and directory name has prefix "realm-" - /// extract chars after prefix as realm name and add a new `Realm` - /// instance - /// - fn process_realm_path(&mut self, path: &Path) -> Result<()> { - let meta = path.symlink_metadata()?; - if !meta.is_dir() { - return Ok(()) - } - - let fname = path_filename(path); - if !fname.starts_with("realm-") { - return Ok(()) - } - - let (_, realm_name) = fname.split_at(6); - if !is_valid_realm_name(realm_name) { - warn!("ignoring directory in realm storage which has invalid realm name: {}", realm_name); - return Ok(()) - } - let rootfs = path.join("rootfs"); - if !rootfs.exists() { - warn!("realm directory {} does not have a rootfs, ignoring", path.display()); - return Ok(()) - } - - match Realm::new(realm_name, self.symlinks.clone(), self.network.clone()) { - Ok(realm) => { self.add_realm_entry(realm);} , - Err(e) => warn!("Ignoring '{}': {}", realm_name, e), - }; - Ok(()) - - } - - fn add_realm_entry(&mut self, realm: Realm) -> &Realm { - self.realm_map.insert(realm.name().to_owned(), realm.clone()); - self.realm_list.push(realm.clone()); - self.realm_map.get(realm.name()).expect("cannot find realm we just added to map") - } - - fn remove_realm_entry(&mut self, name: &str) -> Result<()> { - self.realm_map.remove(name); - let list = self.realm_list.clone(); - let mut have_default = false; - self.realm_list.clear(); - for realm in list { - if realm.name() != name { - if realm.is_default() { - have_default = true; - } - self.realm_list.push(realm); - } - } - if !have_default && !self.realm_list.is_empty() { - self.symlinks.borrow_mut().set_default_symlink(self.realm_list[0].name())?; - } - Ok(()) - } - - pub fn current_realm_name(&self) -> Option { - self.symlinks.borrow().current() - } - - pub fn default_realm_name(&self) -> Option { - self.symlinks.borrow().default() - } - - /// - /// Execute shell in a realm. If `realm_name` is `None` then exec - /// shell in current realm, otherwise look up realm by name. - /// - /// If `root_shell` is true, open a root shell, otherwise open - /// a user (uid = 1000) shell. - /// - pub fn launch_shell(&self, realm_name: Option<&str>, root_shell: bool) -> Result<()> { - let run_shell = |realm: &Realm| { - info!("opening shell in realm '{}'", realm.name()); - realm.exec_shell(root_shell)?; - info!("exiting shell in realm '{}'", realm.name()); - Ok(()) - }; - - if let Some(name) = realm_name { - self.with_named_realm(name, true, run_shell) - } else { - self.with_current_realm(run_shell) - } - } - - pub fn launch_terminal(&self, name: Option<&str>) -> Result<()> { - let run_terminal = |realm: &Realm| { - info!("opening terminal in realm '{}'", realm.name()); - let title_arg = format!("Realm: {}", realm.name()); - realm.run(&["/usr/bin/gnome-terminal".to_owned(), "--title".to_owned(), title_arg], true) - }; - - if let Some(name) = name { - self.with_named_realm(name, true, run_terminal) - } else { - self.with_current_realm(run_terminal) - } - - } - - pub fn run_in_realm(&self, realm_name: Option<&str>, args: &[String], use_launcher: bool) -> Result<()> { - - if let Some(name) = realm_name { - self.with_named_realm(name, true, |realm| realm.run(args, use_launcher)) - } else { - self.with_current_realm(|realm| realm.run(args, use_launcher)) - } - } - - fn with_current_realmResult<()>>(&self, f: F) -> Result<()> { - match self.symlinks.borrow().current() { - Some(ref name) => { - self.with_named_realm(name, false, f)?; - }, - None => { - warn!("No current realm instance to run command in"); - } - } - Ok(()) - } - - fn with_named_realmResult<()>>(&self, name: &str, want_start: bool, f: F) -> Result<()> { - match self.realm(name) { - Some(realm) => { - if want_start && !realm.is_running()? { - info!("realm '{}' is not running, starting it.", realm.name()); - self.start_realm(realm)?; - } - f(realm) - }, - None => bail!("no realm with name '{}' exists", name), - } - } - - pub fn list(&self) -> Result<()> { - let mut out = ColoredOutput::new(); - self.print_realm_header(&mut out); - for realm in &self.realm_list { - self.print_realm(realm, &mut out)?; - } - Ok(()) - } - - fn print_realm_header(&self, out: &mut ColoredOutput) { - out.write(" REALMS ").bold("bold").write(": current, ").bright("colored") - .write(": running, (default) starts on boot\n").write(" ------\n\n"); - } - - fn print_realm(&self, realm: &Realm, out: &mut ColoredOutput) -> Result<()> { - let name = format!("{:12}", realm.name()); - if realm.is_current() { - out.write(" > ").bold(&name); - } else if realm.is_running()? { - out.write(" ").bright(&name); - } else { - out.write(" ").dim(&name); - } - - if realm.is_default() { - out.write(" (default)"); - } - out.write("\n"); - Ok(()) - } - - pub fn start_default(&mut self) -> Result<()> { - let default = self.symlinks.borrow().default(); - if let Some(ref realm_name) = default { - self.start_named_realm(realm_name)?; - return Ok(()); - } - bail!("No default realm to start"); - } - - pub fn start_named_realm(&mut self, realm_name: &str) -> Result<()> { - info!("starting realm '{}'", realm_name); - self.with_named_realm(realm_name, false, |realm| self.start_realm(realm)) - } - - fn start_realm(&self, realm: &Realm) -> Result<()> { - let mut symlinks = self.symlinks.borrow_mut(); - let no_current_realm = symlinks.current().is_none(); - // no realm is current, so make this realm the current one - // service file for realm will also start desktopd, so this symlink - // must be created before launching realm. - if no_current_realm { - symlinks.set_current_symlink(Some(realm.name()))?; - } - if let Err(e) = realm.start() { - if no_current_realm { - // oops realm failed to start, need to reset symlink we changed - symlinks.set_current_symlink(None)?; - } - return Err(e); - } - Ok(()) - } - - - pub fn stop_realm(&mut self, name: &str) -> Result<()> { - match self.realm_map.get(name) { - Some(realm) => { - realm.stop()?; - self.set_current_if_none()?; - }, - None => { - warn!("Cannot stop '{}'. Realm does not exist", name); - return Ok(()) - }, - }; - Ok(()) - } - - fn set_current_if_none(&self) -> Result<()> { - let mut symlinks = self.symlinks.borrow_mut(); - if symlinks.current().is_some() { - return Ok(()); - } - - if let Some(ref name) = self.find_running_realm_name()? { - symlinks.set_current_symlink(Some(name))?; - self.systemd.restart_desktopd()?; - } else { - self.systemd.stop_desktopd()?; - } - Ok(()) - } - - fn find_running_realm_name(&self) -> Result> { - for realm in self.realm_map.values() { - if realm.is_running()? { - return Ok(Some(realm.name().to_string())); - } - } - Ok(None) - } - - pub fn set_current_by_name(&self, realm_name: &str) -> Result<()> { - self.with_named_realm(realm_name, false, |realm| realm.set_current()) - } - - pub fn set_default_by_name(&self, realm_name: &str) -> Result<()> { - self.with_named_realm(realm_name, false, |realm| realm.set_default()) - } - pub fn realm_name_exists(&self, name: &str) -> bool { - self.realm_map.contains_key(name) - } - - pub fn realm(&self, name: &str) -> Option<&Realm> { - self.realm_map.get(name) - } - - pub fn new_realm(&mut self, name: &str) -> Result<&Realm> { - if !is_valid_realm_name(name) { - bail!("'{}' is not a valid realm name. Only letters, numbers and dash '-' symbol allowed in name. First character must be a letter", name); - } else if self.realm_name_exists(name) { - bail!("A realm with name '{}' already exists", name); - } - - let realm = Realm::new(name, self.symlinks.clone(), self.network.clone())?; - - match realm.create_realm_directory() { - Ok(()) => Ok(self.add_realm_entry(realm)), - Err(e) => { - fs::remove_dir_all(realm.base_path())?; - Err(e) - }, - } - - } - - pub fn remove_realm(&mut self, realm_name: &str, confirm: bool, save_home: bool) -> Result<()> { - self.with_named_realm(realm_name, false, |realm| { - if realm.base_path().join(".realmlock").exists() { - warn!("Realm '{}' has .realmlock file in base directory to protect it from deletion.", realm.name()); - warn!("Remove this file from {} before running 'realms remove {}' if you really want to delete it", realm.base_path().display(), realm.name()); - return Ok(()); - } - let mut save_home = save_home; - if confirm { - if !RealmManager::confirm_delete(realm.name(), &mut save_home)? { - return Ok(()); - } - } - realm.delete_realm(save_home)?; - self.set_current_if_none() - })?; - - self.remove_realm_entry(realm_name)?; - Ok(()) - } - - fn confirm_delete(realm_name: &str, save_home: &mut bool) -> Result { - let you_sure = RealmManager::prompt_user(&format!("Are you sure you want to remove realm '{}'?", realm_name), false)?; - if !you_sure { - info!("Ok, not removing"); - return Ok(false); - } - - println!("\nThe home directory for this realm can be saved in /realms/removed/home-{}\n", realm_name); - *save_home = RealmManager::prompt_user("Would you like to save the home directory?", true)?; - Ok(true) - } - - fn prompt_user(prompt: &str, default_y: bool) -> Result { - let yn = if default_y { "(Y/n)" } else { "(y/N)" }; - use std::io::{stdin,stdout}; - print!("{} {} : ", prompt, yn); - stdout().flush()?; - let mut line = String::new(); - stdin().read_line(&mut line)?; - - let yes = match line.trim().chars().next() { - Some(c) => c == 'Y' || c == 'y', - None => default_y, - }; - Ok(yes) - } - - pub fn base_appimg_update(&self) -> Result<()> { - info!("Entering root shell on base appimg"); - self.systemd.base_image_update_shell() - } -} diff --git a/citadel-tools/citadel-realms/src/network.rs b/citadel-tools/citadel-realms/src/network.rs deleted file mode 100644 index ab8a35c..0000000 --- a/citadel-tools/citadel-realms/src/network.rs +++ /dev/null @@ -1,210 +0,0 @@ -use std::path::PathBuf; -use std::net::Ipv4Addr; -use std::collections::{HashSet,HashMap}; -use std::io::{BufReader,BufRead,Write}; -use std::fs::{self,File}; - -use Result; - -const MIN_MASK: usize = 16; -const MAX_MASK: usize = 24; -const RESERVED_OCTET: u32 = 213; - -/// Manage ip address assignment for bridges -pub struct NetworkConfig { - allocators: HashMap, -} - -impl NetworkConfig { - pub fn new() -> NetworkConfig { - NetworkConfig { - allocators: HashMap::new(), - } - } - - pub fn add_bridge(&mut self, name: &str, network: &str) -> Result<()> { - let allocator = BridgeAllocator::for_bridge(name, network) - .map_err(|e| format_err!("Failed to create bridge allocator: {}", e))?; - self.allocators.insert(name.to_owned(), allocator); - Ok(()) - } - pub fn gateway(&self, bridge: &str) -> Result { - match self.allocators.get(bridge) { - Some(allocator) => Ok(allocator.gateway()), - None => bail!("Failed to return gateway address for bridge {} because it does not exist", bridge), - } - } - pub fn allocate_address_for(&mut self, bridge: &str, realm_name: &str) -> Result { - match self.allocators.get_mut(bridge) { - Some(allocator) => allocator.allocate_address_for(realm_name), - None => bail!("Failed to allocate address for bridge {} because it does not exist", bridge), - } - } - pub fn free_allocation_for(&mut self, bridge: &str, realm_name: &str) -> Result<()> { - match self.allocators.get_mut(bridge) { - Some(allocator) => allocator.free_allocation_for(realm_name), - None => bail!("Failed to free address on bridge {} because it does not exist", bridge), - } - } - - pub fn reserved(&self, bridge: &str) -> Result { - match self.allocators.get(bridge) { - Some(allocator) => Ok(allocator.reserved()), - None => bail!("Failed to return reserved address for bridge {} because it does not exist", bridge), - } - } -} - -/// -/// Allocates IP addresses for a bridge shared by multiple realms. -/// -/// State information is stored in /run/realms/network-$bridge as -/// colon ':' separated pairs of realm name and allocated ip address -/// -/// realm-a:172.17.0.2 -/// realm-b:172.17.0.3 -/// -struct BridgeAllocator { - bridge: String, - network: Ipv4Addr, - mask_size: usize, - allocated: HashSet, - allocations: HashMap, -} - -impl BridgeAllocator { - pub fn for_bridge(bridge: &str, network: &str) -> Result { - let (addr_str, mask_size) = match network.find('/') { - Some(idx) => { - let (net,bits) = network.split_at(idx); - (net.to_owned(), bits[1..].parse()?) - }, - None => (network.to_owned(), 24), - }; - if mask_size > MAX_MASK || mask_size < MIN_MASK { - bail!("Unsupported network mask size of {}", mask_size); - } - - let mask = (1u32 << (32 - mask_size)) - 1; - let ip = addr_str.parse::()?; - - if (u32::from(ip) & mask) != 0 { - bail!("network {} has masked bits with netmask /{}", addr_str, mask_size); - } - - let mut conf = BridgeAllocator::new(bridge, ip, mask_size); - conf.load_state()?; - Ok(conf) - } - - fn new(bridge: &str, network: Ipv4Addr, mask_size: usize) -> BridgeAllocator { - let mut allocator = BridgeAllocator { - bridge: bridge.to_owned(), - allocated: HashSet::new(), - allocations: HashMap::new(), - network, mask_size, - }; - let rsv = u32::from(network) | RESERVED_OCTET; - allocator.allocated.insert(Ipv4Addr::from(rsv)); - allocator - } - - fn allocate_address_for(&mut self, realm_name: &str) -> Result { - match self.find_free_address() { - Some(addr) => { - self.allocated.insert(addr.clone()); - if let Some(old) = self.allocations.insert(realm_name.to_owned(), addr.clone()) { - self.allocated.remove(&old); - } - self.write_state()?; - return Ok(format!("{}/{}", addr, self.mask_size)); - }, - None => bail!("No free IP address could be found to assign to {}", realm_name), - } - - } - - fn find_free_address(&self) -> Option { - let mask = (1u32 << (32 - self.mask_size)) - 1; - let net = u32::from(self.network); - for i in 2..mask { - let addr = Ipv4Addr::from(net + i); - if !self.allocated.contains(&addr) { - return Some(addr); - } - } - None - } - - fn gateway(&self) -> String { - let gw = u32::from(self.network) + 1; - let addr = Ipv4Addr::from(gw); - addr.to_string() - } - - fn reserved(&self) -> String { - let rsv = u32::from(self.network) | RESERVED_OCTET; - let addr = Ipv4Addr::from(rsv); - format!("{}/{}", addr, self.mask_size) - } - - fn free_allocation_for(&mut self, realm_name: &str) -> Result<()> { - match self.allocations.remove(realm_name) { - Some(ip) => { - self.allocated.remove(&ip); - self.write_state()?; - } - None => warn!("No address allocation found for realm {}", realm_name), - }; - Ok(()) - } - - fn state_file_path(&self) -> PathBuf { - PathBuf::from(format!("/run/realms/network-{}", self.bridge)) - } - - - fn load_state(&mut self) -> Result<()> { - let path = self.state_file_path(); - if !path.exists() { - return Ok(()) - } - let f = File::open(path)?; - let reader = BufReader::new(f); - for line in reader.lines() { - let line = &line?; - self.parse_state_line(line)?; - } - - Ok(()) - } - - fn parse_state_line(&mut self, line: &str) -> Result<()> { - match line.find(":") { - Some(idx) => { - let (name,addr) = line.split_at(idx); - let ip = addr[1..].parse::()?; - self.allocated.insert(ip.clone()); - self.allocations.insert(name.to_owned(), ip); - }, - None => bail!("Could not parse line from network state file: {}", line), - } - Ok(()) - } - - fn write_state(&mut self) -> Result<()> { - let path = self.state_file_path(); - let dir = path.parent().unwrap(); - if !dir.exists() { - fs::create_dir_all(dir) - .map_err(|e| format_err!("failed to create directory {} for network allocation state file: {}", dir.display(), e))?; - } - let mut f = File::create(&path) - .map_err(|e| format_err!("failed to open network state file {} for writing: {}", path.display(), e))?; - - for (realm,addr) in &self.allocations { - writeln!(f, "{}:{}", realm, addr)?; - } - Ok(()) - } -} diff --git a/citadel-tools/citadel-realms/src/realm.rs b/citadel-tools/citadel-realms/src/realm.rs deleted file mode 100644 index 28cea1e..0000000 --- a/citadel-tools/citadel-realms/src/realm.rs +++ /dev/null @@ -1,375 +0,0 @@ -use std::path::{PathBuf,Path}; -use std::rc::Rc; -use std::cmp::Ordering; -use std::cell::{RefCell,Cell}; -use std::fs::{self,File}; -use std::os::unix::fs::{symlink,MetadataExt}; - -use {RealmConfig,Result,Systemd,NetworkConfig}; -use util::*; -use appimg::*; - -const REALMS_BASE_PATH: &str = "/realms"; -const REALMS_RUN_PATH: &str = "/run/realms"; - -#[derive(Clone)] -pub struct Realm { - /// The realm name. Corresponds to a directory with path /realms/realm-$name/ - name: String, - - /// modify time of timestamp file which is updated when realm is set to current. - ts: Cell, - - /// Configuration options, either default values or values read from file /realms/realm-$name/config - config: RealmConfig, - - /// wrapper around various calls to systemd utilities - systemd: Systemd, - - /// reads and manages 'current' and 'default' symlinks, shared between all instances - symlinks: Rc>, -} - -impl Realm { - pub fn new(name: &str, symlinks: Rc>, network: Rc>) -> Result { - let mut realm = Realm { - name: name.to_string(), - ts: Cell::new(0), - systemd: Systemd::new(network), - config: RealmConfig::default(), symlinks, - }; - realm.load_config()?; - realm.load_timestamp()?; - Ok(realm) - } - - fn load_config(&mut self) -> Result<()> { - let path = self.base_path().join("config"); - self.config = RealmConfig::load_or_default(&path) - .map_err(|e| format_err!("failed to load realm config file {}: {}", path.display(), e))?; - Ok(()) - } - - pub fn config(&self) -> &RealmConfig { - &self.config - } - - pub fn base_path(&self) -> PathBuf { - PathBuf::from(REALMS_BASE_PATH).join(format!("realm-{}", self.name)) - } - - pub fn set_default(&self) -> Result<()> { - if self.is_default() { - info!("Realm '{}' is already default realm", self.name()); - return Ok(()) - } - self.symlinks.borrow_mut().set_default_symlink(&self.name)?; - info!("Realm '{}' set as default realm", self.name()); - Ok(()) - } - - pub fn set_current(&self) -> Result<()> { - if self.is_current() { - info!("Realm '{}' is already current realm", self.name()); - return Ok(()) - } - if !self.is_running()? { - self.start()?; - } - self.symlinks.borrow_mut().set_current_symlink(Some(&self.name))?; - self.systemd.restart_desktopd()?; - self.update_timestamp()?; - info!("Realm '{}' set as current realm", self.name()); - Ok(()) - } - - pub fn is_default(&self) -> bool { - self.symlinks.borrow().is_name_default(&self.name) - } - - pub fn is_current(&self) -> bool { - self.symlinks.borrow().is_name_current(&self.name) - } - - pub fn is_running(&self) -> Result { - self.systemd.realm_is_active(self) - } - - pub fn run(&self, args: &[String], use_launcher: bool) -> Result<()> { - self.systemd.machinectl_shell(self, args, use_launcher)?; - Ok(()) - } - - pub fn exec_shell(&self, as_root: bool) -> Result<()> { - self.systemd.machinectl_exec_shell(self, as_root) - } - - pub fn start(&self) -> Result<()> { - self.systemd.start_realm(self)?; - info!("Started realm '{}'", self.name()); - Ok(()) - } - - pub fn stop(&self) -> Result<()> { - self.systemd.stop_realm(self)?; - if self.is_current() { - self.symlinks.borrow_mut().set_current_symlink(None)?; - } - info!("Stopped realm '{}'", self.name()); - Ok(()) - } - - pub fn name(&self) -> &str { - &self.name - } - - fn load_timestamp(&self) -> Result<()> { - let tstamp = self.base_path().join(".tstamp"); - if tstamp.exists() { - let meta = tstamp.metadata()?; - self.ts.set(meta.mtime()); - } - Ok(()) - } - - /// create an empty file which is used to track the time at which - /// this realm was last made 'current'. These times are used - /// to order the output when listing realms. - fn update_timestamp(&self) -> Result<()> { - let tstamp = self.base_path().join(".tstamp"); - if tstamp.exists() { - fs::remove_file(&tstamp)?; - } - File::create(&tstamp) - .map_err(|e| format_err!("failed to create timestamp file {}: {}", tstamp.display(), e))?; - // also load the new value - self.load_timestamp()?; - Ok(()) - } - - pub fn create_realm_directory(&self) -> Result<()> { - if self.base_path().exists() { - bail!("realm base directory {} already exists, cannot create", self.base_path().display()); - } - - fs::create_dir(self.base_path()) - .map_err(|e| format_err!("failed to create realm base directory {}: {}", self.base_path().display(), e))?; - - self.create_home_directory() - .map_err(|e| format_err!("failed to create realm home directory {}: {}", self.base_path().join("home").display(), e))?; - - // This must be last step because if an error is returned caller assumes that subvolume was - // never created and does not need to be removed. - clone_base_appimg(self)?; - Ok(()) - } - - fn create_home_directory(&self) -> Result<()> { - let home = self.base_path().join("home"); - fs::create_dir(&home)?; - chown(&home, 1000, 1000)?; - let skel = PathBuf::from(REALMS_BASE_PATH).join("skel"); - if skel.exists() { - info!("Populating realm home directory with files from {}", skel.display()); - copy_tree(&skel, &home)?; - } - Ok(()) - } - - pub fn delete_realm(&self, save_home: bool) -> Result<()> { - if save_home { - self.save_home_for_delete()?; - } - if self.is_running()? { - self.stop()?; - } - info!("removing rootfs subvolume for '{}'", self.name()); - delete_rootfs_subvolume(self)?; - - info!("removing realm directory {}", self.base_path().display()); - fs::remove_dir_all(self.base_path())?; - - info!("realm '{}' has been removed", self.name()); - Ok(()) - } - - fn save_home_for_delete(&self) -> Result<()> { - let target = PathBuf::from(&format!("/realms/removed/home-{}", self.name())); - if !Path::new("/realms/removed").exists() { - fs::create_dir("/realms/removed")?; - } - - fs::rename(self.base_path().join("home"), &target) - .map_err(|e| format_err!("unable to move realm home directory to {}: {}", target.display(), e))?; - info!("home directory been moved to /realms/removed/home-{}, delete it at your leisure", self.name()); - Ok(()) - } - -} - -impl Ord for Realm { - fn cmp(&self, other: &Realm) -> Ordering { - let self_run = self.is_running().unwrap_or(false); - let other_run = other.is_running().unwrap_or(false); - - if self_run && !other_run { - Ordering::Less - } else if !self_run && other_run { - Ordering::Greater - } else { - let self_ts = self.ts.get(); - let other_ts = other.ts.get(); - other_ts.cmp(&self_ts) - } - } -} - -impl PartialOrd for Realm { - fn partial_cmp(&self, other: &Realm) -> Option { - Some(self.cmp(other)) - } -} - -impl PartialEq for Realm { - fn eq(&self, other: &Realm) -> bool { - self.cmp(other) == Ordering::Equal - } -} - -impl Eq for Realm {} - -pub struct RealmSymlinks { - current_name: Option, - default_name: Option, -} - -impl RealmSymlinks { - pub fn new() -> RealmSymlinks { - RealmSymlinks { - current_name: None, - default_name: None, - } - } - - pub fn load_symlinks(&mut self) -> Result<()> { - self.current_name = self.resolve_realm_name(&PathBuf::from(REALMS_RUN_PATH).join("current.realm"))?; - self.default_name = self.resolve_realm_name(&PathBuf::from(REALMS_BASE_PATH).join("default.realm"))?; - Ok(()) - } - - fn is_name_default(&self, name: &str) -> bool { - match self.default() { - Some(dname) => dname == name, - None => false, - } - } - - fn is_name_current(&self, name: &str) -> bool { - match self.current() { - Some(cname) => cname == name, - None => false, - } - } - - pub fn current(&self) -> Option { - self.current_name.clone() - } - - pub fn default(&self) -> Option { - self.default_name.clone() - } - - - pub fn set_current_symlink(&mut self, name: Option<&str>) -> Result<()> { - let runpath = Path::new(REALMS_RUN_PATH); - if !runpath.exists() { - fs::create_dir_all(runpath) - .map_err(|e| format_err!("failed to create realm runtime directory {}: {}", runpath.display(), e))?; - } - - let path = runpath.join("current.realm"); - if let Some(n) = name { - let tmp = Path::new("/run/current.realm.tmp"); - let target = PathBuf::from(REALMS_BASE_PATH).join(format!("realm-{}", n)); - symlink(&target, tmp) - .map_err(|e| format_err!("failed to create symlink from {} to {}: {}", tmp.display(), target.display(), e))?; - - fs::rename(tmp, &path) - .map_err(|e| format_err!("failed to rename symlink from {} to {}: {}", tmp.display(), path.display(), e))?; - - self.current_name = Some(n.to_owned()); - } else { - if path.exists() { - fs::remove_file(&path) - .map_err(|e| format_err!("failed to remove current symlink {}: {}", path.display(), e))?; - } - self.current_name = None; - } - Ok(()) - } - - pub fn set_default_symlink(&mut self, name: &str) -> Result<()> { - let path = PathBuf::from(REALMS_BASE_PATH).join("default.realm"); - let tmp = Path::new("/realms/default.realm.tmp"); - let target = format!("realm-{}", name); - symlink(&target, tmp) - .map_err(|e| format_err!("failed to create symlink from {} to {}: {}", tmp.display(), target, e))?; - fs::rename(tmp, &path) - .map_err(|e| format_err!("failed to rename symlink from {} to {}: {}", tmp.display(), path.display(), e))?; - - self.default_name = Some(name.to_owned()); - Ok(()) - } - - fn resolve_realm_name(&self, path: &Path) -> Result> { - if !path.exists() { - return Ok(None); - } - let meta = path.symlink_metadata()?; - if !meta.file_type().is_symlink() { - bail!("{} exists but it is not a symlink", path.display()); - } - let target = RealmSymlinks::absolute_target(path)?; - RealmSymlinks::ensure_subdir_of_base(path, &target)?; - if !target.is_dir() { - bail!("target of symlink {} is not a directory", path.display()); - } - let filename = path_filename(&target); - if !filename.starts_with("realm-") { - bail!("target of symlink {} is not a realm directory", path.display()); - } - Ok(Some(filename[6..].to_string())) - } - - /// Read target of symlink `path` and resolve it to an absolute - /// path - fn absolute_target(path: &Path) -> Result { - let target = fs::read_link(path)?; - if target.is_absolute() { - Ok(target) - } else if target.components().count() == 1 { - match path.parent() { - Some(parent) => return Ok(parent.join(target)), - None => bail!("Cannot resolve absolute path of symlink target because symlink path has no parent"), - } - } else { - bail!("symlink target has invalid value: {}", target.display()) - } - } - - fn ensure_subdir_of_base(path: &Path, target: &Path) -> Result<()> { - let realms_base = PathBuf::from(REALMS_BASE_PATH); - match target.parent() { - Some(parent) => { - if parent != realms_base.as_path() { - bail!("target of symlink {} points outside of {} directory: {}", path.display(), REALMS_BASE_PATH, target.display()); - } - }, - None => bail!("target of symlink {} has invalid value (no parent): {}", path.display(), target.display()), - } - Ok(()) - } - -} - - diff --git a/citadel-tools/citadel-realms/src/systemd.rs b/citadel-tools/citadel-realms/src/systemd.rs deleted file mode 100644 index 79ae428..0000000 --- a/citadel-tools/citadel-realms/src/systemd.rs +++ /dev/null @@ -1,383 +0,0 @@ -use std::rc::Rc; -use std::cell::RefCell; -use std::process::Command; -use std::path::{Path,PathBuf}; -use std::fs::{self,File}; -use std::fmt::Write; -use std::io::Write as IoWrite; -use std::env; - -const SYSTEMCTL_PATH: &str = "/usr/bin/systemctl"; -const MACHINECTL_PATH: &str = "/usr/bin/machinectl"; -const SYSTEMD_NSPAWN_PATH: &str = "/run/systemd/nspawn"; -const SYSTEMD_UNIT_PATH: &str = "/run/systemd/system"; - -const DESKTOPD_SERVICE: &str = "citadel-desktopd.service"; - -use Realm; -use NetworkConfig; -use Result; -use util::{path_filename,is_first_char_alphabetic}; - -#[derive(Clone)] -pub struct Systemd { - network: Rc>, -} - -impl Systemd { - - pub fn new(network: Rc>) -> Systemd { - Systemd { network } - } - - pub fn realm_is_active(&self, realm: &Realm) -> Result { - let active = self.is_active(&self.realm_service_name(realm))?; - let has_config = self.realm_config_exists(realm); - if active && !has_config { - bail!("Realm {} is running, but config files are missing", realm.name()); - } - if !active && has_config { - bail!("Realm {} is not running, but config files are present", realm.name()); - } - Ok(active) - } - - pub fn start_realm(&self, realm: &Realm) -> Result<()> { - if self.realm_is_active(realm)? { - warn!("Realm {} is already running", realm.name()); - return Ok(()) - } - self.write_realm_launch_config(realm)?; - self.systemctl_start(&self.realm_service_name(realm))?; - if realm.config().emphemeral_home() { - self.setup_ephemeral_home(realm)?; - } - - Ok(()) - - } - - pub fn base_image_update_shell(&self) -> Result<()> { - let netconf = self.network.borrow_mut(); - let gw = netconf.gateway("clear")?; - let addr = netconf.reserved("clear")?; - let gw_env = format!("--setenv=IFCONFIG_GW={}", gw); - let addr_env = format!("--setenv=IFCONFIG_IP={}", addr); - - Command::new("/usr/bin/systemd-nspawn") - .args(&[ - &addr_env, &gw_env, - "--quiet", - "--machine=base-appimg-update", - "--directory=/storage/appimg/base.appimg", - "--network-zone=clear", - "/bin/bash", "-c", "/usr/libexec/configure-host0.sh && exec /bin/bash" - ]).status()?; - Ok(()) - } - - fn setup_ephemeral_home(&self, realm: &Realm) -> Result<()> { - - // 1) if exists: machinectl copy-to /realms/skel /home/user - if Path::new("/realms/skel").exists() { - self.machinectl_copy_to(realm, "/realms/skel", "/home/user")?; - } - - // 2) if exists: machinectl copy-to /realms/realm-$name /home/user - let realm_skel = realm.base_path().join("skel"); - if realm_skel.exists() { - self.machinectl_copy_to(realm, realm_skel.to_str().unwrap(), "/home/user")?; - } - - let home = realm.base_path().join("home"); - if !home.exists() { - return Ok(()); - } - - // 3) for each : machinectl bind /realms/realm-$name/home/$dir /home/user/$dir - for dent in fs::read_dir(home)? { - let path = dent?.path(); - self.bind_mount_home_subdir(realm, &path)?; - } - - Ok(()) - } - - fn bind_mount_home_subdir(&self, realm: &Realm, path: &Path) -> Result<()> { - let path = path.canonicalize()?; - if !path.is_dir() { - return Ok(()); - } - let fname = path_filename(&path); - if !is_first_char_alphabetic(fname) { - return Ok(()); - } - let from = format!("/realms/realm-{}/home/{}", realm.name(), fname); - let to = format!("/home/user/{}", fname); - self.machinectl_bind(realm, &from, &to)?; - Ok(()) - } - - pub fn stop_realm(&self, realm: &Realm) -> Result<()> { - if !self.realm_is_active(realm)? { - warn!("Realm {} is not running", realm.name()); - return Ok(()); - } - self.systemctl_stop(&self.realm_service_name(realm))?; - self.remove_realm_launch_config(realm)?; - self.network.borrow_mut().free_allocation_for(realm.config().network_zone(), realm.name())?; - Ok(()) - } - - pub fn restart_desktopd(&self) -> Result { - self.systemctl_restart(DESKTOPD_SERVICE) - } - pub fn stop_desktopd(&self) -> Result { - self.systemctl_stop(DESKTOPD_SERVICE) - } - - fn realm_service_name(&self, realm: &Realm) -> String { - format!("realm-{}.service", realm.name()) - } - - fn is_active(&self, name: &str) -> Result { - Command::new(SYSTEMCTL_PATH) - .args(&["--quiet", "is-active", name]) - .status() - .map(|status| status.success()) - .map_err(|e| format_err!("failed to execute{}: {}", MACHINECTL_PATH, e)) - - } - - - fn systemctl_restart(&self, name: &str) -> Result { - self.run_systemctl("restart", name) - } - - fn systemctl_start(&self, name: &str) -> Result { - self.run_systemctl("start", name) - } - - fn systemctl_stop(&self, name: &str) -> Result { - self.run_systemctl("stop", name) - } - - fn run_systemctl(&self, op: &str, name: &str) -> Result { - Command::new(SYSTEMCTL_PATH) - .arg(op) - .arg(name) - .status() - .map(|status| status.success()) - .map_err(|e| format_err!("failed to execute {}: {}", MACHINECTL_PATH, e)) - } - - fn machinectl_copy_to(&self, realm: &Realm, from: &str, to: &str) -> Result<()> { - Command::new(MACHINECTL_PATH) - .args(&["copy-to", realm.name(), from, to ]) - .status() - .map_err(|e| format_err!("failed to machinectl copy-to {} {} {}: {}", realm.name(), from, to, e))?; - Ok(()) - } - - fn machinectl_bind(&self, realm: &Realm, from: &str, to: &str) -> Result<()> { - Command::new(MACHINECTL_PATH) - .args(&["--mkdir", "bind", realm.name(), from, to ]) - .status() - .map_err(|e| format_err!("failed to machinectl bind {} {} {}: {}", realm.name(), from, to, e))?; - Ok(()) - - } - - pub fn machinectl_exec_shell(&self, realm: &Realm, as_root: bool) -> Result<()> { - let namevar = format!("--setenv=REALM_NAME={}", realm.name()); - let user = if as_root { "root" } else { "user" }; - let user_at_host = format!("{}@{}", user, realm.name()); - Command::new(MACHINECTL_PATH) - .args(&[ &namevar, "--quiet", "shell", &user_at_host, "/bin/bash"]) - .status() - .map_err(|e| format_err!("failed to execute{}: {}", MACHINECTL_PATH, e))?; - - Ok(()) - } - - pub fn machinectl_shell(&self, realm: &Realm, args: &[String], launcher: bool) -> Result<()> { - let namevar = format!("--setenv=REALM_NAME={}", realm.name()); - let mut cmd = Command::new(MACHINECTL_PATH); - cmd.arg("--quiet"); - match env::var("DESKTOP_STARTUP_ID") { - Ok(val) => { - cmd.arg("-E"); - cmd.arg(&format!("DESKTOP_STARTUP_ID={}", val)); - }, - Err(_) => {}, - }; - cmd.arg(&namevar); - cmd.arg("shell"); - cmd.arg(format!("user@{}", realm.name())); - - if launcher { - cmd.arg("/usr/libexec/launch"); - } - - for arg in args { - cmd.arg(&arg); - } - cmd.status().map_err(|e| format_err!("failed to execute{}: {}", MACHINECTL_PATH, e))?; - Ok(()) - } - - - fn realm_service_path(&self, realm: &Realm) -> PathBuf { - PathBuf::from(SYSTEMD_UNIT_PATH).join(self.realm_service_name(realm)) - } - - fn realm_nspawn_path(&self, realm: &Realm) -> PathBuf { - PathBuf::from(SYSTEMD_NSPAWN_PATH).join(format!("{}.nspawn", realm.name())) - } - - fn realm_config_exists(&self, realm: &Realm) -> bool { - self.realm_service_path(realm).exists() || self.realm_nspawn_path(realm).exists() - } - - fn remove_realm_launch_config(&self, realm: &Realm) -> Result<()> { - let nspawn_path = self.realm_nspawn_path(realm); - if nspawn_path.exists() { - fs::remove_file(&nspawn_path)?; - } - let service_path = self.realm_service_path(realm); - if service_path.exists() { - fs::remove_file(&service_path)?; - } - Ok(()) - } - - fn write_realm_launch_config(&self, realm: &Realm) -> Result<()> { - let nspawn_path = self.realm_nspawn_path(realm); - let nspawn_content = self.generate_nspawn_file(realm)?; - self.write_launch_config_file(&nspawn_path, &nspawn_content) - .map_err(|e| format_err!("failed to write nspawn config file {}: {}", nspawn_path.display(), e))?; - - let service_path = self.realm_service_path(realm); - let service_content = self.generate_service_file(realm); - self.write_launch_config_file(&service_path, &service_content) - .map_err(|e| format_err!("failed to write service config file {}: {}", service_path.display(), e))?; - - Ok(()) - } - - /// Write the string `content` to file `path`. If the directory does - /// not already exist, create it. - fn write_launch_config_file(&self, path: &Path, content: &str) -> Result<()> { - match path.parent() { - Some(parent) => { - if !parent.exists() { - fs::create_dir_all(parent)?; - } - }, - None => bail!("config file path {} has no parent?", path.display()), - }; - let mut f = File::create(path)?; - f.write_all(content.as_bytes())?; - Ok(()) - } - - fn generate_nspawn_file(&self, realm: &Realm) -> Result { - Ok(NSPAWN_FILE_TEMPLATE - .replace("$EXTRA_BIND_MOUNTS", &self.generate_extra_bind_mounts(realm)?) - - .replace("$NETWORK_CONFIG", &self.generate_network_config(realm)?)) - } - - fn generate_extra_bind_mounts(&self, realm: &Realm) -> Result { - let config = realm.config(); - let mut s = String::new(); - - if config.emphemeral_home() { - writeln!(s, "TemporaryFileSystem=/home/user:mode=755,uid=1000,gid=1000")?; - } else { - writeln!(s, "Bind={}/home:/home/user", realm.base_path().display())?; - } - - if config.shared_dir() && Path::new("/realms/Shared").exists() { - writeln!(s, "Bind=/realms/Shared:/home/user/Shared")?; - } - - if config.kvm() { - writeln!(s, "Bind=/dev/kvm")?; - } - - if config.gpu() { - writeln!(s, "Bind=/dev/dri/renderD128")?; - } - - if config.sound() { - writeln!(s, "Bind=/dev/snd")?; - writeln!(s, "Bind=/dev/shm")?; - writeln!(s, "BindReadOnly=/run/user/1000/pulse:/run/user/host/pulse")?; - } - - if config.x11() { - writeln!(s, "BindReadOnly=/tmp/.X11-unix")?; - } - - if config.wayland() { - writeln!(s, "BindReadOnly=/run/user/1000/wayland-0:/run/user/host/wayland-0")?; - } - - Ok(s) - } - - fn generate_network_config(&self, realm: &Realm) -> Result { - let mut s = String::new(); - if realm.config().network() { - let mut netconf = self.network.borrow_mut(); - let zone = realm.config().network_zone(); - let addr = netconf.allocate_address_for(zone, realm.name())?; - let gw = netconf.gateway(zone)?; - writeln!(s, "Environment=IFCONFIG_IP={}", addr)?; - writeln!(s, "Environment=IFCONFIG_GW={}", gw)?; - writeln!(s, "[Network]")?; - writeln!(s, "Zone=clear")?; - } else { - writeln!(s, "[Network]")?; - writeln!(s, "Private=true")?; - } - Ok(s) - } - - fn generate_service_file(&self, realm: &Realm) -> String { - let rootfs = format!("/realms/realm-{}/rootfs", realm.name()); - REALM_SERVICE_TEMPLATE.replace("$REALM_NAME", realm.name()).replace("$ROOTFS", &rootfs) - } -} - - -pub const NSPAWN_FILE_TEMPLATE: &str = r###" -[Exec] -Boot=true -$NETWORK_CONFIG - -[Files] -BindReadOnly=/usr/share/themes -BindReadOnly=/usr/share/icons/Paper - -BindReadOnly=/storage/citadel-state/resolv.conf:/etc/resolv.conf - -$EXTRA_BIND_MOUNTS - -"###; - -pub const REALM_SERVICE_TEMPLATE: &str = r###" -[Unit] -Description=Application Image $REALM_NAME instance -Wants=citadel-desktopd.service - -[Service] -Environment=SYSTEMD_NSPAWN_SHARE_NS_IPC=1 -ExecStart=/usr/bin/systemd-nspawn --quiet --notify-ready=yes --keep-unit --machine=$REALM_NAME --link-journal=try-guest --directory=$ROOTFS - -KillMode=mixed -Type=notify -RestartForceExitStatus=133 -SuccessExitStatus=133 -"###; diff --git a/citadel-tools/citadel-realms/src/util.rs b/citadel-tools/citadel-realms/src/util.rs deleted file mode 100644 index 6b34c3a..0000000 --- a/citadel-tools/citadel-realms/src/util.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::path::Path; -use std::fs; -use std::os::unix::ffi::OsStrExt; -use std::os::unix::fs::MetadataExt; -use std::ffi::CString; -use std::io::{self,Write}; - -use libc; -use walkdir::WalkDir; - -use Result; - - -pub fn path_filename(path: &Path) -> &str { - if let Some(osstr) = path.file_name() { - if let Some(name) = osstr.to_str() { - return name; - } - } - "" -} - -fn is_alphanum_or_dash(c: char) -> bool { - is_ascii(c) && (c.is_alphanumeric() || c == '-') -} - -fn is_ascii(c: char) -> bool { - c as u32 <= 0x7F -} - -pub fn is_first_char_alphabetic(s: &str) -> bool { - if let Some(c) = s.chars().next() { - return is_ascii(c) && c.is_alphabetic() - } - false -} - -const MAX_REALM_NAME_LEN:usize = 128; - -/// Valid realm names: -/// * must start with an alphabetic ascii letter character -/// * may only contain ascii characters which are letters, numbers, or the dash '-' symbol -/// * must not be empty or have a length exceeding 128 characters -pub fn is_valid_realm_name(name: &str) -> bool { - name.len() <= MAX_REALM_NAME_LEN && - // Also false on empty string - is_first_char_alphabetic(name) && - name.chars().all(is_alphanum_or_dash) -} - -pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { - let cstr = CString::new(path.as_os_str().as_bytes())?; - unsafe { - if libc::chown(cstr.as_ptr(), uid, gid) == -1 { - return Err(io::Error::last_os_error()); - } - } - Ok(()) -} - -fn copy_path(from: &Path, to: &Path) -> Result<()> { - if to.exists() { - bail!("destination path {} already exists which is not expected", to.display()); - } - - let meta = from.metadata()?; - - if from.is_dir() { - fs::create_dir(to)?; - } else { - fs::copy(&from, &to)?; - } - - chown(to, meta.uid(), meta.gid())?; - Ok(()) - -} -pub fn copy_tree(from_base: &Path, to_base: &Path) -> Result<()> { - for entry in WalkDir::new(from_base) { - let path = entry?.path().to_owned(); - let to = to_base.join(path.strip_prefix(from_base)?); - if &to != to_base { - copy_path(&path, &to) - .map_err(|e| format_err!("failed to copy {} to {}: {}", path.display(), to.display(), e))?; - } - } - Ok(()) -} - -use termcolor::{ColorChoice,Color,ColorSpec,WriteColor,StandardStream}; - -pub struct ColoredOutput { - color_bright: ColorSpec, - color_bold: ColorSpec, - color_dim: ColorSpec, - stream: StandardStream, -} - - -impl ColoredOutput { - pub fn new() -> ColoredOutput { - ColoredOutput::new_with_colors(Color::Rgb(0, 110, 180), Color::Rgb(100, 100, 80)) - } - - pub fn new_with_colors(bright: Color, dim: Color) -> ColoredOutput { - let mut out = ColoredOutput { - color_bright: ColorSpec::new(), - color_bold: ColorSpec::new(), - color_dim: ColorSpec::new(), - stream: StandardStream::stdout(ColorChoice::AlwaysAnsi), - }; - out.color_bright.set_fg(Some(bright.clone())); - out.color_bold.set_fg(Some(bright)).set_bold(true); - out.color_dim.set_fg(Some(dim)); - - out - } - - pub fn write(&mut self, s: &str) -> &mut Self { - write!(&mut self.stream, "{}", s).unwrap(); - self.stream.reset().unwrap(); - self - } - pub fn bright(&mut self, s: &str) -> &mut Self { - self.stream.set_color(&self.color_bright).unwrap(); - self.write(s) - } - pub fn bold(&mut self, s: &str) -> &mut Self { - self.stream.set_color(&self.color_bold).unwrap(); - self.write(s) - } - pub fn dim(&mut self, s: &str) -> &mut Self { - self.stream.set_color(&self.color_dim).unwrap(); - self.write(s) - } - -} diff --git a/citadel-tools/citadel-update/.gitignore b/citadel-tools/citadel-update/.gitignore deleted file mode 100644 index 700059f..0000000 --- a/citadel-tools/citadel-update/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -**/target/ -**/*.rs.bk -.idea/ diff --git a/citadel-tools/citadel-update/Cargo.lock b/citadel-tools/citadel-update/Cargo.lock deleted file mode 100644 index 6e2528b..0000000 --- a/citadel-tools/citadel-update/Cargo.lock +++ /dev/null @@ -1,535 +0,0 @@ -[[package]] -name = "ansi_term" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "arrayref" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "atty" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "block-buffer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "build_const" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byte-tools" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bytes" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cc" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "citadel-rootfs" -version = "0.1.0" -dependencies = [ - "clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519-dalek 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "lzma-rs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clap" -version = "2.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clear_on_drop" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crc" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "curve25519-dalek" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ed25519-dalek" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "curve25519-dalek 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "gcc" -version = "0.3.54" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "generic-array" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "iovec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libc" -version = "0.2.36" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "log" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lzma-rs" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nix" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rand" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_syscall" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc-serialize" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde_derive" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive_internals" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "strsim" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "subtle" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "subtle" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synstructure" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termion" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "textwrap" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "toml" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "typenum" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-width" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vec_map" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" -"checksum arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd1479b7c29641adbd35ff3b5c293922d696a92f25c8c975da3e0acbc87258f" -"checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859" -"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2" -"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" -"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" -"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" -"checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9" -"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" -"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" -"checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9" -"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0" -"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c07b9257a00f3fc93b7f3c417fc15607ec7a56823bc2c37ec744e266387de5b" -"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" -"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7" -"checksum curve25519-dalek 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6734ff1a930d90b3ee54b7d6eba1b520f8724a1f353cf4f2b4b171a9ce63d814" -"checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603" -"checksum ed25519-dalek 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3692ef38cc617236a39120ef0b91794e5e4d5c96227607a6740bfaaab53ac3c" -"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" -"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" -"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" -"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" -"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" -"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" -"checksum lzma-rs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d49cc18d2e4235afb294250b5eae3c8ac150fc3662fd46fbb7f48c6cb6567b7" -"checksum nix 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fd5681d13fda646462cfbd4e5f2051279a89a544d50eb98c365b507246839f" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -"checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" -"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum rustc-demangle 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f312457f8a4fa31d3581a6f423a70d6c33a10b95291985df55f1ff670ec10ce8" -"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" -"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526" -"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0" -"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5" -"checksum sha2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7daca11f2fdb8559c4f6c588386bed5e2ad4b6605c1442935a7f08144a918688" -"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum subtle 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7a6bab57c3efd01ebd3d750f4244ae0af4cdd1fc505a7904a41603192b803c5" -"checksum subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc7f6353c2ee5407358d063a14cccc1630804527090a6fb5a9489ce4924280fb" -"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" -"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" -"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" -"checksum typenum 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a99dc6780ef33c78780b826cf9d2a78840b72cae9474de4bcaf9051e60ebbd" -"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" -"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/citadel-tools/citadel-update/Cargo.toml b/citadel-tools/citadel-update/Cargo.toml deleted file mode 100644 index 3ecf077..0000000 --- a/citadel-tools/citadel-update/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "citadel-update" -version = "0.1.0" -authors = ["Bruce Leidl "] -homepage = "https://github.com/subgraph/citadel" - -[dependencies] -clap = "2.30.0" -failure = "0.1.1" -libc = "0.2" -nix = "0.10.0" -ed25519-dalek = "^0.6" -rand = "0.4.2" -sha2 = "0.7.0" -toml = "0.4.5" -serde_derive = "1.0.27" -serde = "1.0.27" -rustc-serialize = "0.3.24" -lzma-rs = "0.1.0" diff --git a/citadel-tools/citadel-update/conf/citadel-update.conf b/citadel-tools/citadel-update/conf/citadel-update.conf deleted file mode 100644 index f44d12f..0000000 --- a/citadel-tools/citadel-update/conf/citadel-update.conf +++ /dev/null @@ -1,13 +0,0 @@ - - -# This is where images to be installed are stored -citadel_updates = "/storage/citadel-updates" - -kernel_updates = "/storage/kernel-updates" - -# This is where update images are built -image_builds = "/storage/image-builds" - -[channel.test] -update_server = "" -pubkey = "7a6743a61cff946083f2496c4df0e5afb958c84eec3185e63cbe7a695c20e732" diff --git a/citadel-tools/citadel-update/src/blockdev.rs b/citadel-tools/citadel-update/src/blockdev.rs deleted file mode 100644 index 862af77..0000000 --- a/citadel-tools/citadel-update/src/blockdev.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::path::Path; -use std::fs::File; -use std::io::{Read,Write,Seek,SeekFrom}; -use std::os::unix::io::AsRawFd; -use std::fs::OpenOptions; -use std::os::unix::fs::OpenOptionsExt; -use libc; - -use Result; -use util::path_str; - -const REQUIRED_ALIGNMENT: usize = 4096; -const DEFAULT_ALIGNMENT: usize = REQUIRED_ALIGNMENT; - -pub struct AlignedBuffer { - buffer: Vec, - alignment: usize, - size: usize, - align_offset: usize, -} - -impl AlignedBuffer { - - pub fn new(size: usize) -> AlignedBuffer { - AlignedBuffer::new_with_alignment(size, DEFAULT_ALIGNMENT) - } - - pub fn from_slice(bytes: &[u8]) -> AlignedBuffer { - AlignedBuffer::from_slice_with_alignment(bytes, DEFAULT_ALIGNMENT) - } - - pub fn new_with_alignment(size: usize, alignment: usize) -> AlignedBuffer { - AlignedBuffer { - alignment, size, - buffer: vec![0; size + alignment], - align_offset: 0, - } - } - - pub fn from_slice_with_alignment(bytes: &[u8], alignment: usize) -> AlignedBuffer { - let mut ab = AlignedBuffer::new_with_alignment(bytes.len(), alignment); - ab.as_mut().copy_from_slice(bytes); - ab - } - - /// - /// Calculates an offset into `self.buffer` array that is physically - /// located at a 4096 byte alignment boundary and returns slice at - /// this offset. `self.align_offset` is set so that access functions - /// will use the right offset. - /// - /// I/O on block devices must use 4k aligned memory: - /// - /// https://people.redhat.com/msnitzer/docs/io-limits.txt - /// - /// Or maybe just 512 byte aligned memory: - /// - /// https://www.quora.com/Why-does-O_DIRECT-require-I-O-to-be-512-byte-aligned - /// - fn align_buffer(&mut self) { - let addr = self.buffer.as_ptr() as usize; - let offset = self.alignment - (addr & (self.alignment - 1)); - self.align_offset = offset; - } -} - -impl AsRef<[u8]> for AlignedBuffer { - fn as_ref(&self) -> &[u8] { - let start = self.align_offset; - let end = start + self.size; - &(self.buffer.as_slice())[start..end] - } -} - -impl AsMut<[u8]> for AlignedBuffer { - fn as_mut(&mut self) -> &mut [u8] { - self.align_buffer(); - let start = self.align_offset; - let end = start + self.size; - &mut self.buffer.as_mut_slice()[start..end] - } -} - -pub const SECTOR_SIZE: usize = 512; -pub const ALIGNMENT_MASK: usize = 4095; - -ioctl!(read blk_getsize64 with 0x12, 114; u64); - -pub struct BlockDev { - file: File, -} - -impl BlockDev { - pub fn open_ro>(path: P) -> Result { - BlockDev::open(path.as_ref(), false) - } - pub fn open_rw>(path: P) -> Result { - BlockDev::open(path.as_ref(), true) - } - - fn open(path: &Path, write: bool) -> Result { - let mut oo = OpenOptions::new(); - oo.read(true); - oo.custom_flags(libc::O_DIRECT | libc::O_SYNC); - if write { - oo.write(true); - } - let file = oo.open(path) - .map_err(|e| format_err!("Failed to open block device {}: {}", path_str(path), e))?; - Ok(BlockDev{file}) - } - - pub fn size(&self) -> Result { - let mut sz = 0u64; - unsafe { - blk_getsize64(self.file.as_raw_fd(), &mut sz) - .map_err(|e| format_err!("Error calling getsize ioctl on block device: {}", e))?; - } - Ok(sz) - } - - pub fn nsectors(&self) -> Result { - Ok((self.size()? as usize) >> 9) - } - - fn setup_io(&mut self, offset: usize, buffer: &[u8]) -> Result<()> { - let addr = buffer.as_ptr() as usize; - if addr & ALIGNMENT_MASK != 0 { - bail!("block device i/o attempted with incorrectly aligned buffer: {:p}", buffer); - } - if buffer.len() % SECTOR_SIZE != 0 { - bail!("buffer length {} is not a multiple of sector size", buffer.len()); - } - let count = buffer.len() / SECTOR_SIZE; - if offset + count > self.nsectors()? { - bail!("sector_io({}, {}) is past end of device", offset, buffer.len()); - } - self.file.seek(SeekFrom::Start((offset * SECTOR_SIZE) as u64))?; - Ok(()) - } - - pub fn read_sectors(&mut self, offset: usize, buffer: &mut [u8]) -> Result<()> { - self.setup_io(offset, buffer)?; - self.file.read_exact(buffer)?; - Ok(()) - } - - pub fn write_sectors(&mut self, offset: usize, buffer: &[u8]) -> Result<()> { - self.setup_io(offset, buffer)?; - self.file.write_all(buffer)?; - Ok(()) - } - -} diff --git a/citadel-tools/citadel-update/src/boot.rs b/citadel-tools/citadel-update/src/boot.rs deleted file mode 100644 index cce4fac..0000000 --- a/citadel-tools/citadel-update/src/boot.rs +++ /dev/null @@ -1,100 +0,0 @@ - - -use {Result,Partition,Config}; - -pub struct BootSelection { - partitions: Vec, -} - -impl BootSelection { - pub fn load_partitions() -> Result { - let partitions = Partition::rootfs_partitions() - .map_err(|e| format_err!("Could not load rootfs partition info: {}", e))?; - - Ok(BootSelection { - partitions - }) - } - - pub fn choose_install_partition(&self) -> Option<&Partition> { - self.choose(|p| { - // first pass, if there is a partition which is not mounted and - // not initialized use that one - !p.is_mounted() && !p.is_initialized() - }).or_else(|| self.choose(|p| { - // second pass, just find one that's not mounted - !p.is_mounted() - })) - } - - fn choose(&self, pred: F) -> Option<&Partition> - where F: Sized + Fn(&&Partition) -> bool - { - self.partitions.iter().find(pred) - } - - /// Find the best rootfs partition to boot from - pub fn choose_boot_partition(&self) -> Option<&Partition> { - let mut best: Option<&Partition> = None; - - for p in &self.partitions { - if is_better(&best, p) { - best = Some(p); - } - } - best - } - - - /// Perform checks for error states at boot time. - pub fn scan_boot_partitions(&self, config: &Config) -> Result<()> { - for p in &self.partitions { - if let Err(e) = p.boot_scan(config) { - warn!("error in bootscan of partition {}: {}", p.path_str(), e); - } - } - Ok(()) - } -} - -fn is_better<'a>(current_best: &Option<&'a Partition>, other: &'a Partition) -> bool { - - // Only consider partitions in state NEW or state GOOD - if !other.is_good() && !other.is_new() { - return false; - } - // If metainfo is broken, then no, it's not better - if !other.metainfo().is_ok() { - return false; - } - - let best = match *current_best { - Some(p) => p, - // No current 'best', so 'other' is better, whatever it is. - None => return true, - }; - - // First parition with PREFER flag trumps everything else - if best.is_preferred() { - return false; - } - - // These are guaranteed to unwrap() - let best_version = best.metainfo().unwrap().version(); - let other_version = other.metainfo().unwrap().version(); - - if best_version > other_version { - return false; - } - - if other_version > best_version { - return true; - } - - // choose NEW over GOOD if versions are the same - if other.is_new() && best.is_good() { - return true; - } - // ... but if all things otherwise match, return first match - false -} diff --git a/citadel-tools/citadel-update/src/config.rs b/citadel-tools/citadel-update/src/config.rs deleted file mode 100644 index e5bd10e..0000000 --- a/citadel-tools/citadel-update/src/config.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::path::Path; -use std::collections::HashMap; - -use ed25519_dalek::{Signature,PublicKey,Keypair}; -use rustc_serialize::hex::FromHex; -use sha2::Sha512; -use toml; - -use util::{read_file_as_string,path_str}; -use Result; - - -const DEFAULT_CONFIG_PATH: &str = "/usr/share/citadel/citadel-rootfs.conf"; -fn default_citadel_updates() -> String { "/storage/citadel-updates".to_string() } -fn default_kernel_updates() -> String { "/storage/kernel-updates".to_string() } -fn default_image_builds() -> String { "/storage/image-builds".to_string() } - -#[derive(Deserialize)] -pub struct Config { - default_channel: Option, - - #[serde (default= "default_citadel_updates")] - citadel_updates: String, - - #[serde (default = "default_kernel_updates")] - kernel_updates: String, - - #[serde (default = "default_image_builds")] - image_builds: String, - - channel: HashMap, -} - -impl Config { - - pub fn load_default() -> Result { - Config::load(DEFAULT_CONFIG_PATH) - } - - pub fn load>(path: P) -> Result { - let config = match Config::from_path(path.as_ref()) { - Ok(config) => config, - Err(e) => bail!("Failed to load config file {}: {}", path_str(path.as_ref()), e), - }; - Ok(config) - } - - fn from_path(path: &Path) -> Result { - let s = read_file_as_string(path.as_ref())?; - let mut config = toml::from_str::(&s)?; - for (k,v) in config.channel.iter_mut() { - v.name = k.to_string(); - } - - Ok(config) - } - - pub fn get_default_channel(&self) -> Option { - - if let Some(ref name) = self.default_channel { - if let Some(c) = self.channel(name) { - return Some(c); - } - } - - if self.channel.len() == 1 { - return self.channel.values().next().map(|c| c.clone()); - } - None - } - - pub fn channel(&self, name: &str) -> Option { - self.channel.get(name).map(|c| c.clone() ) - } - - pub fn citadel_updates_base(&self) -> &str { - &self.citadel_updates - } - - pub fn kernel_updates_base(&self) -> &str { - &self.kernel_updates - } - - pub fn image_builds_base(&self) -> &str { - &self.image_builds - } - - pub fn get_private_key(&self, channel: &str) -> Option { - if let Some(channel_config) = self.channel.get(channel) { - if let Some(ref key) = channel_config.keypair { - return Some(key.clone()); - } - } - None - } - - pub fn get_public_key(&self, channel: &str) -> Option { - if let Some(channel_config) = self.channel.get(channel) { - return Some(channel_config.pubkey.clone()); - } - None - } -} - -#[derive(Deserialize,Clone)] -pub struct Channel { - update_server: Option, - pubkey: String, - keypair: Option, - - #[serde(skip)] - name: String, -} - -impl Channel { - pub fn name(&self) -> &str { - &self.name - } - - pub fn sign(&self, data: &[u8]) -> Result { - let keybytes = match self.keypair { - Some(ref hex) => hex.from_hex()?, - None => bail!("No private signing key available for channel {}", self.name), - }; - let privkey = Keypair::from_bytes(&keybytes)?; - let sig = privkey.sign::(data); - Ok(sig) - } - - pub fn verify(&self, data: &[u8], sigbytes: &[u8]) -> Result { - let keybytes = self.pubkey.from_hex()?; - let pubkey = PublicKey::from_bytes(&keybytes)?; - let sig = Signature::from_bytes(sigbytes)?; - Ok(pubkey.verify::(data, &sig)) - } - -} - diff --git a/citadel-tools/citadel-update/src/main.rs b/citadel-tools/citadel-update/src/main.rs deleted file mode 100644 index a30db23..0000000 --- a/citadel-tools/citadel-update/src/main.rs +++ /dev/null @@ -1,290 +0,0 @@ -#[macro_use] extern crate failure; -#[macro_use] extern crate nix; -#[macro_use] extern crate serde_derive; - - -extern crate libc; -extern crate clap; -extern crate serde; -extern crate toml; -extern crate ed25519_dalek; -extern crate sha2; -extern crate rand; -extern crate rustc_serialize; - - -thread_local! { - pub static VERBOSE: RefCell = RefCell::new(false); - pub static SYSOP: RefCell = RefCell::new(false); -} - -pub fn verbose() -> bool { - VERBOSE.with(|f| { - *f.borrow() - }) -} - -fn sysop() -> bool { - SYSOP.with(|f| { - *f.borrow() - }) -} - -macro_rules! info { - ($e:expr) => { if ::verbose() { println!("[+] {}", $e);} }; - ($fmt:expr, $($arg:tt)+) => { if ::verbose() { println!("[+] {}", format!($fmt, $($arg)+));} }; -} -macro_rules! warn { - ($e:expr) => { if ::verbose() { println!("WARNING: {}", $e);} }; - ($fmt:expr, $($arg:tt)+) => { if ::verbose() { println!("WARNING: {}", format!($fmt, $($arg)+));} }; -} - -macro_rules! notify { - ($e:expr) => { println!("[+] {}", $e); }; - ($fmt:expr, $($arg:tt)+) => { println!("[+] {}", format!($fmt, $($arg)+)); }; -} - -use std::result; -use std::process::exit; - -use failure::Error; -use clap::{App,Arg,ArgMatches, SubCommand}; -use clap::AppSettings::*; -use sha2::Sha512; -use rand::OsRng; -use ed25519_dalek::Keypair; -use rustc_serialize::hex::ToHex; -use std::cell::RefCell; -use std::env; - - -pub use config::Config; -pub use metainfo::Metainfo; -pub use blockdev::BlockDev; -pub use partition::{Partition,MAX_METAINFO_LEN}; -use unpacker::UpdateImageUnpacker; -use packer::UpdateImagePacker; -use boot::BootSelection; - -mod boot; -mod metainfo; -mod partition; -mod blockdev; -mod config; -mod packer; -mod unpacker; -mod util; - -pub type Result = result::Result; - -fn main() { - match env::var("CITADEL_SYSOP") { - Ok(_) => SYSOP.with(|f| *f.borrow_mut() = true), - _ => {}, - }; - - let mut app = App::new("citadel-update") - .about("Subgraph Citadel update and rootfs management") - .settings(&[ArgRequiredElseHelp, ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder]) - .arg(Arg::with_name("v") - .help("Verbose output") - .short("v") - .long("verbose")) - .arg(Arg::with_name("config") - .help("Optionally specify an alternate config file") - .takes_value(true) - .short("c") .long("config")) - - .subcommand(SubCommand::with_name("list") - .about("Show information about all rootfs partitions")) - - .subcommand(SubCommand::with_name("which-boot") - .about("Show which rootfs paritition would currently boot according to the boot selection algorithm")) - - .subcommand(SubCommand::with_name("verify-update") - .about("Verify the signature of an update image")) - - .subcommand(SubCommand::with_name("update") - .about("Download update if available and install") - .arg(Arg::with_name("download-only") - .help("Only download available update, don't install") - .long("download"))) - - .subcommand(SubCommand::with_name("install-update") - .about("Install an update image") - .arg(Arg::with_name("image") - .required(true))); - if sysop() { - - app = app.subcommand(SubCommand::with_name("build-update") - .about("Create an update image from a raw citadel-image.ext2 file") - .arg(Arg::with_name("image") - .required(true))) - - .subcommand(SubCommand::with_name("genkeys") - .about("Generate a new update keypair")); - - } - - let matches = app.get_matches(); - - - let config = load_config(&matches); - - if matches.is_present("v") { - VERBOSE.with(|f| *f.borrow_mut() = true); - } - - let result = match matches.subcommand() { - ("list", Some(_)) => list_cmd(&config), - ("which-boot", Some(_)) => { which_boot_cmd(&config)}, - ("update", Some(m)) => update_cmd(&config, m), - ("verify-update", Some(m)) => verify_update_cmd(&config, m), - ("install-update", Some(m)) => install_update_cmd(&config, m), - ("build-update", Some(m)) => build_update_cmd(&config, m), - ("genkeys", Some(_)) => genkeys_cmd(), - ("mount-rootfs", Some(m)) => mount_rootfs_cmd(&config, m), - (s, Some(_)) => {info!("subcommand: {}", s); Ok(())}, - _ => Ok(()), - }; - - if let Err(e) = result { - println!("{}", e); - exit(1); - } -} - -fn load_config(arg_matches: &ArgMatches) -> Config { - let config_load = match arg_matches.value_of("config") { - Some(path) => Config::load(path), - None => Config::load_default(), - }; - match config_load { - Ok(config) => config, - Err(e) => { - println!("{}", e); - exit(1); - } - } -} - -fn list_cmd(_config: &Config) -> Result<()> { - println!("{:^30} {:^14} {:^8} {:^8} {:^12}", "DEVICE PATH", "MOUNTED", "CHANNEL", "VERSION", "STATUS"); - for p in Partition::rootfs_partitions()? { - let info = partition_info(&p); - println!("{:^30} {:^14} {:^8} {:^8} {:^12}", info.0, info.1, info.2, info.3, info.4); - } - Ok(()) -} - - -fn partition_info(part: &Partition) -> (String,String,String,String,String) { - let mounted = if part.is_mounted() { "[Yes]".to_string() } else { String::new() }; - let status = if part.is_initialized() { part.status_label() } else { "Not Initialized".to_string() }; - let (channel, version) = match part.metainfo() { - Ok(meta) => (meta.channel().to_string(), meta.version().to_string()), - _ => (String::new(), String::new()), - }; - (part.path_str().to_string(), mounted, channel, version, status) -} - -fn mount_rootfs_cmd(_config: &Config, _matches: &ArgMatches) -> Result<()> { - // mounting installer rootfs should happen here too? - // perhaps based on cmd line flag - // maybe define guid like the gpt generator looks for - // for: rootfs inside luks, rootfs outside luks - let bs = BootSelection::load_partitions()?; - let _p = match bs.choose_boot_partition() { - Some(p) => p, - None => bail!("None of the rootfs partitions have a bootable image"), - }; - Ok(()) -} - -fn which_boot_cmd(_config: &Config) -> Result<()> { - let select = BootSelection::load_partitions()?; - match select.choose_boot_partition() { - Some(part) => { - notify!("Next boot will be from partition: {}", part.path_str()); - }, - None => { - warn!("None of the rootfs partitions are currently in bootable state."); - warn!("Unless a valid image is installed, computer will fail to boot"); - } - } - Ok(()) -} - -fn verify_update_cmd(_config: &Config, _matches: &ArgMatches) -> Result<()> { - println!("do verify_update"); - Ok(()) -} - -fn update_cmd(_config: &Config, _matches: &ArgMatches) -> Result<()> { - // note to self, remember to verify that downloaded update image version matches the version - // which is expected. - Ok(()) - -} - -fn install_update_cmd(config: &Config, matches: &ArgMatches) -> Result<()> { - let image_path = match matches.value_of("image") { - Some(val) => val, - None => bail!("install-update requires an image path"), - }; - let unpack = UpdateImageUnpacker::open(image_path, config)?; - info!("unpacking image channel: {} version: {}", unpack.metainfo().channel(), unpack.metainfo().version()); - unpack.unpack_disk_image()?; - info!("decompressing image"); - unpack.decompress_disk_image()?; - info!("verifying shasum"); - unpack.verify_shasum()?; - - let bs = BootSelection::load_partitions()?; - let p = match bs.choose_install_partition() { - Some(p) => p, - None => bail!("None of the rootfs partitions are available to install update to"), - }; - info!("installing to {}", p.path_str()); - unpack.write_partition(p)?; - notify!("Update image successfully installed to {}", p.path_str()); - - - //update::UpdateImage::new(path); - // 1) read header, extract metainfo - // 2) verify signature on metainfo - // 3) determine if this version/channel makes sense to be installed - // 4) xtrat image data to temporary file with https://crates.io/crates/lzma-rs - // 5) verify sha256 on image data - // - // 6) choose rootfs partition - // 7) write partition info block, setting status to INVALID - // 8) write update image to device - // 9) run verifyupdate using provided --salt - // 10) re-rewrite parition info block with status NEW - Ok(()) -} - -fn build_update_cmd(config: &Config, matches: &ArgMatches) -> Result<()> { - let image_path = match matches.value_of("image") { - Some(val) => val, - None => bail!("build-update requires an image path"), - }; - let channel = match config.get_default_channel() { - Some(ch) => ch, - None => bail!("Could not determine default channel from config file"), - }; - let mut builder = UpdateImagePacker::new(config, channel, image_path)?; - builder.build()?; - Ok(()) -} - -fn genkeys_cmd() -> Result<()> { - let mut rng = OsRng::new()?; - let keypair = Keypair::generate::(&mut rng); - - println!("pubkey = \"{}\"", keypair.public.to_bytes().to_hex()); - println!("privkey = \"{}\"", keypair.to_bytes().to_hex()); - - Ok(()) -} diff --git a/citadel-tools/citadel-update/src/metainfo.rs b/citadel-tools/citadel-update/src/metainfo.rs deleted file mode 100644 index 54f743a..0000000 --- a/citadel-tools/citadel-update/src/metainfo.rs +++ /dev/null @@ -1,48 +0,0 @@ -#[derive(Deserialize,Serialize,Clone)] -pub struct Metainfo { - channel: String, - version: u32, - base_version: u32, - date: String, - gitrev: String, - nsectors: u32, - shasum: String, - verity_salt: String, - verity_root: String, -} - - -impl Metainfo { - - pub fn channel(&self) -> &str { - &self.channel - } - - pub fn version(&self) -> u32 { - self.version - } - - pub fn date(&self) -> &str { - &self.date - } - - pub fn gitrev(&self) -> &str { - &self.gitrev - } - - pub fn nsectors(&self) -> usize { - self.nsectors as usize - } - - pub fn shasum(&self) -> &str { - &self.shasum - } - - pub fn verity_root(&self) -> &str { - &self.verity_root - } - - pub fn verity_salt(&self) -> &str { - &self.verity_salt - } -} diff --git a/citadel-tools/citadel-update/src/packer.rs b/citadel-tools/citadel-update/src/packer.rs deleted file mode 100644 index 0185f16..0000000 --- a/citadel-tools/citadel-update/src/packer.rs +++ /dev/null @@ -1,209 +0,0 @@ - -use std::path::Path; -use std::io::{self,Write}; -use std::fs::{File,OpenOptions}; -use std::collections::HashMap; - -use config::{Config,Channel}; -use util::*; - -use Result; - -const IMAGE_FILENAME: &str = "citadel-image.ext2"; - -/// -/// -pub struct UpdateImagePacker { - workdir: Workdir, - version: usize, - channel: Channel, - nsectors: usize, - verity_salt: String, - verity_root: String, - shasum: String, - header: Vec, -} - -impl UpdateImagePacker { - pub fn new(config: &Config, channel: Channel, image_path: &str) -> Result { - let mut workdir = Workdir::new(config.image_builds_base(), channel.name()); - let version = workdir.find_next_version()?; - - sanity_check_source(image_path)?; - let mut from = File::open(image_path)?; - let mut to = File::create(workdir.filepath(IMAGE_FILENAME))?; - io::copy(&mut from, &mut to)?; - - Ok(UpdateImagePacker { - workdir, version, - channel: channel.to_owned(), - nsectors: 0, - verity_salt: String::new(), - verity_root: String::new(), - shasum: String::new(), - header: Vec::new(), - }) - } - - pub fn build(&mut self) -> Result<()> { - self.pad_image(4096)?; - let meta = self.workdir.filepath(IMAGE_FILENAME).metadata()?; - self.nsectors = (meta.len() / 512) as usize; - self.build_verity()?; - self.calculate_image_shasum()?; - self.build_update_header()?; - self.compress_image()?; - self.write_update_image()?; - - Ok(()) - } - - fn pad_image(&self, size: usize) -> Result<()> { - let path = self.workdir.filepath(IMAGE_FILENAME); - let meta = path.metadata()?; - let rem = (meta.len() as usize) % size; - if rem == 0 { - return Ok(()); - } - let padlen = size - rem; - info!("padding image with {} bytes", padlen); - let zeros = vec![0u8; padlen]; - - let mut file = OpenOptions::new().append(true).open(&path)?; - file.write_all(&zeros)?; - Ok(()) - } - - fn build_verity(&mut self) -> Result<()> { - info!("Building dm-verity hash tree"); - let verity_output = run_verityformat_command(&self.workdir.filepath(IMAGE_FILENAME), &self.workdir.filepath("verifyhash.out"))?; - write_string_to_file(&self.workdir.filepath("verityinfo"), &verity_output)?; - - let map = UpdateImagePacker::parse_verity_output(&verity_output); - - self.verity_root = match map.get("Root hash") { - Some(v) => v.to_owned(), - None => bail!("No root hash found in veritysetup output"), - }; - - self.verity_salt = match map.get("Salt") { - Some(v) => v.to_owned(), - None => bail!("No Salt found in veritysetup output"), - }; - - info!("Verity root: {}", self.verity_root); - Ok(()) - } - - fn calculate_image_shasum(&mut self) -> Result<()> { - info!("Calculating sha256 digest over image file"); - self.shasum = run_sha256_command(&self.workdir.filepath(IMAGE_FILENAME))?; - Ok(()) - } - - fn parse_verity_output(output: &str) -> HashMap { - let mut map = HashMap::new(); - for line in output.lines() { - if let Some((k,v)) = UpdateImagePacker::parse_verity_line(line) { - map.insert(k, v); - } - } - map - } - - fn parse_verity_line(line: &str) -> Option<(String,String)> { - let v = line.split(':').map(|s| s.trim()) - .collect::>(); - - if v.len() == 2 { - Some((v[0].to_string(), v[1].to_string())) - } else { - None - } - } - - - fn build_update_header(&mut self) -> Result<()> { - info!("Creating update image header"); - let metainfo = self.generate_metainfo()?; - - let mut f = File::create(self.workdir.filepath("metainfo"))?; - f.write_all(&metainfo)?; - - let sig = self.channel.sign(&metainfo)?; - - let mut szbuf = [0u8; 2]; - szbuf[0] = (metainfo.len() >> 8) as u8; - szbuf[1] = metainfo.len() as u8; - - self.header.write_all(b"UPDT")?; - self.header.write_all(&szbuf)?; - self.header.write_all(&metainfo)?; - self.header.write_all(&sig.to_bytes())?; - Ok(()) - } - - fn generate_metainfo(&self) -> Result> { - let mut v = Vec::new(); - writeln!(v, "channel = \"{}\"", self.channel.name())?; - writeln!(v, "version = {}", self.version)?; - writeln!(v, "base_version = {}", self.version)?; - writeln!(v, "date = \"\"")?; - writeln!(v, "gitrev = \"\"")?; - writeln!(v, "nsectors = {}", self.nsectors)?; - - writeln!(v, "shasum = \"{}\"", self.shasum)?; - writeln!(v, "verity_salt = \"{}\"", self.verity_salt)?; - writeln!(v, "verity_root = \"{}\"", self.verity_root)?; - Ok(v) - } - - fn compress_image(&self) -> Result<()> { - info!("Compressing image file"); - run_xz_command(&self.workdir.filepath(IMAGE_FILENAME), false)?; - Ok(()) - } - - fn write_update_image(&self) -> Result<()> { - let img_path = self.workdir.filepath("citadel-update.img"); - info!("writing update image to {}", path_str(&img_path)); - let mut out = File::create(&img_path)?; - - out.write_all(self.header.as_slice())?; - - let mut image = File::open(self.workdir.filepath("citadel-image.ext2.xz"))?; - io::copy(&mut image, &mut out)?; - - Ok(()) - } -} - - -fn sanity_check_source>(src: P) -> Result<()> { - let src: &Path = src.as_ref(); - let meta = match src.metadata() { - Ok(md) => md, - Err(e) => bail!("Could not load image file {}: {}", path_str(src), e), - }; - - if !meta.file_type().is_file() { - bail!("Image file {} exists but is not a regular file"); - } - - let filetype = match run_file_command(&src) { - Ok(s) => s, - Err(e) => bail!("{}", e), - }; - - if filetype.starts_with("XZ") { - bail!("Image file is compressed, decompress first"); - } else if !filetype.starts_with("Linux rev 1.0 ext2 filesystem data") { - bail!("Image file is not an ext2 filesystem as expected:\n {}", - filetype.trim_right()); - } - - if meta.len() % 512 != 0 { - bail!("Image file size is not a multiple of sector size (512 bytes)"); - } - Ok(()) -} diff --git a/citadel-tools/citadel-update/src/partition.rs b/citadel-tools/citadel-update/src/partition.rs deleted file mode 100644 index c2808dc..0000000 --- a/citadel-tools/citadel-update/src/partition.rs +++ /dev/null @@ -1,481 +0,0 @@ -use std::path::{Path,PathBuf}; -use std::str; -use std::cell::{RefCell,Ref}; - -use toml; -use ed25519_dalek::SIGNATURE_LENGTH; - -use Result; -use Metainfo; -use BlockDev; -use Config; -use blockdev::AlignedBuffer; -use util::*; - -const MAGIC: &[u8] = b"CTDL"; - -/// Size in bytes of the Partition Info Block -const BLOCK_SIZE: usize = 4096; - -/// Size of the PIB header up to the metainfo data -const HEADER_SIZE: usize = 8; - -/// The maximum length of metainfo data in a Partition Info Block. -/// This size is the entire block minus the header and signature. -pub const MAX_METAINFO_LEN: usize = BLOCK_SIZE - (HEADER_SIZE + SIGNATURE_LENGTH); - -/// -/// Flag to override the algorithm which selects a partition to mount during boot -/// -const FLAG_PREFER: u8 = 1; - -/// -/// The last 4096 bytes of a rootfs block device stores a structure -/// called the Partition Info Block. -/// -/// The layout of this structure is the following: -/// -/// field size (bytes) offset -/// ----- ------------ ------ -/// -/// magic 4 0 -/// status 1 4 -/// flags 1 5 -/// length 2 6 -/// -/// metainfo 8 -/// -/// signature 64 8 + length -/// -/// magic : Must match ascii bytes 'CTDL' for the block to be considered valid -/// -/// status : See `PartitionStatus` for description of defined valid values -/// -/// flags : Only one flag is defined, `FLAG_PREFER` -/// -/// length : Big endian 16 bit size in bytes of metainfo field -/// -/// metainfo : A utf-8 TOML document with various fields describing the rootfs image -/// on this partition. -/// -/// signature : ed25519 signature of the content of metainfo field -/// -struct Infoblock(RefCell>); - -impl Infoblock { - fn new() -> Infoblock { - let v = vec![0; BLOCK_SIZE]; - Infoblock(RefCell::new(v)) - } - - fn reset(&self) { - for b in &mut self.0.borrow_mut()[..] { - *b = 0; - } - self.write_bytes(0, MAGIC); - } - - fn w8(&self, idx: usize, val: u8) { - self.0.borrow_mut()[idx] = val; - } - - fn r8(&self, idx: usize) -> u8 { - self.0.borrow()[idx] - } - - fn write_bytes(&self, offset: usize, data: &[u8]) { - self.0.borrow_mut()[offset..offset+data.len()].copy_from_slice(data) - } - - fn read_bytes(&self, offset: usize, len: usize) -> Vec { - Vec::from(&self.0.borrow()[offset..offset+len]) - } - - fn status(&self) -> u8 { - self.r8(4) - } - - fn set_status(&self, status: u8) { - self.w8(4, status); - } - - fn has_status(&self, status: u8) -> bool { - self.is_valid() && self.status() == status - } - - fn flags(&self) -> u8 { - self.r8(5) - } - - fn has_flag(&self, flag: u8) -> bool { - self.is_valid() && (self.flags() & flag) == flag - } - - /// Returns `true` if flag value changed - fn set_flag(&self, flag: u8, value: bool) -> bool { - let old = self.flags(); - - if value { - self.w8(5, old | flag); - } else { - self.w8(5, old & !flag); - } - self.flags() == old - } - - fn metainfo_len(&self) -> usize { - let high = self.r8(6) as usize; - let low = self.r8(7) as usize; - (high << 8) | low - } - - fn set_metainfo_len(&self, mlen: usize) { - assert!(mlen <= MAX_METAINFO_LEN); - let high = (mlen >> 8) as u8; - let low = mlen as u8; - self.w8(6, high); - self.w8(7, low); - } - - fn write_metainfo(&self, metainfo: &[u8]) { - self.set_metainfo_len(metainfo.len()); - self.write_bytes(8, metainfo); - } - - fn write_signature(&self, signature: &[u8]) { - assert_eq!(signature.len(), SIGNATURE_LENGTH); - self.write_bytes(8 + self.metainfo_len(), signature); - } - - fn read_metainfo(&self) -> Vec { - assert!(self.is_valid()); - self.read_bytes(8, self.metainfo_len()) - } - - fn read_signature(&self) -> Vec { - assert!(self.is_valid()); - self.read_bytes(8 + self.metainfo_len(), SIGNATURE_LENGTH) - } - - fn is_valid(&self) -> bool { - &self.0.borrow()[0..4] == MAGIC && - is_valid_status_code(self.status()) && - self.flags() & !FLAG_PREFER == 0 && - self.metainfo_len() > 0 && self.metainfo_len() <= MAX_METAINFO_LEN - } - - fn from_slice(&self, bytes: &[u8]) { - self.0.borrow_mut().copy_from_slice(bytes); - } - - fn as_ref(&self) -> Ref> { - self.0.borrow() - } -} - -pub struct Partition { - path: PathBuf, - is_mounted: bool, - infoblock: Infoblock, -} - -impl Partition { - /// - /// Return a `Vec` of all rootfs partitions on the system. Usually - /// there are two (rootfsA and rootfsB). - /// - pub fn rootfs_partitions() -> Result> { - let mut v = Vec::new(); - for path in rootfs_partition_paths()? { - v.push(Partition::load(&path)?); - } - Ok(v) - } - - /// Construct a new `Partition` object for the device `dev` and load - /// the Partition Info Block structure from the block device. - fn load(dev: &Path) -> Result { - let is_mounted = is_path_mounted(dev)?; - let part = Partition::new(dev, is_mounted); - part.read_infoblock()?; - Ok(part) - } - - fn new(path: &Path, is_mounted: bool) -> Partition { - Partition { - path: path.to_path_buf(), - is_mounted, - infoblock: Infoblock::new(), - } - } - - /// - /// For the passed in `BlockDev` instance calculate and return - /// the sector offset of the Partition Info Block, which is - /// located 8 sectors (4096 bytes) from the end of the partition. - /// - fn infoblock_offset(&self, bdev: &BlockDev) -> Result { - let nsectors = bdev.nsectors()?; - if nsectors < 8 { - bail!("{} is a block device but it's very short, {} sectors", - self.path_str(), nsectors); - } - Ok(nsectors - 8) - } - - /// - /// Open the block device for this partition and load the - /// Partition Info Block into the internal buffer `self.infoblock`. - /// - fn read_infoblock(&self) -> Result<()> { - let mut dev = BlockDev::open_ro(&self.path)?; - let off = self.infoblock_offset(&dev)?; - let mut buffer = AlignedBuffer::new(BLOCK_SIZE); - dev.read_sectors(off, buffer.as_mut())?; - self.infoblock.from_slice(buffer.as_ref()); - Ok(()) - } - - /// - /// Open the block device for this partition and write the - /// internal buffer `self.infoblock` into the Partition Info - /// Block. - /// - fn write_infoblock(&self) -> Result<()> { - let mut dev = BlockDev::open_rw(&self.path)?; - let off = self.infoblock_offset(&dev)?; - let buffer = AlignedBuffer::from_slice(self.infoblock.as_ref().as_slice()); - dev.write_sectors(off, buffer.as_ref())?; - Ok(()) - } - - pub fn path(&self) -> &Path { - &self.path - } - - pub fn path_str(&self) -> &str { - self.path.to_str().unwrap() - } - - /// - /// Returns true if this partition is currently mounted and - /// cannot be written to. - /// - pub fn is_mounted(&self) -> bool { - self.is_mounted - } - - /// - /// Update the Partition Info Block for this partition with a new - /// status field. - /// - pub fn write_status(&self, status: u8) -> Result<()> { - self.infoblock.set_status(status); - self.write_infoblock()?; - Ok(()) - } - - pub fn set_prefer_flag(&self, value: bool) -> Result<()> { - if self.infoblock.set_flag(FLAG_PREFER, value) { - self.write_infoblock()?; - } - Ok(()) - } - - /// - /// Write metainfo and signature to the Partition Info Block of this partition. - /// - /// This also sets the internal buffer `self.infoblock` to the block contents - /// written to disk. - /// - /// Writing new partition info also set status to `STATUS_INVALID` as this function - /// is meant to be called in preparation for writing a raw disk image to partition. - /// - /// Caller should write status as `STATUS_NEW` after raw image has been successfully - /// written to partition. - /// - pub fn write_partition_info(&self, metainfo: &[u8], signature: &[u8]) -> Result<()> { - let mlen = metainfo.len(); - - if mlen > MAX_METAINFO_LEN { - bail!("cannot write partition because metainfo field is too long ({} bytes)", metainfo.len()); - } - - if mlen == 0 { - bail!("cannot write partition because metainfo is empty"); - } - - if signature.len() != SIGNATURE_LENGTH { - bail!("cannot write partition info because signature has wrong length {} != {}", - signature.len(), SIGNATURE_LENGTH); - } - - self.infoblock.reset(); - self.infoblock.write_metainfo(metainfo); - self.infoblock.write_signature(signature); - - self.write_infoblock()?; - - Ok(()) - } - - /// - /// Returns true only if this partition has a valid Partition Information - /// Block and the signature on the metainfo field can be verified with - /// the provided `PublicKey`. - /// - pub fn verify_signature(&self, config: &Config) -> Result { - if !self.infoblock.is_valid() { - bail!("Cannot verify signature because partition is invalid"); - } - let metainfo = self.metainfo()?; - let channel = match config.channel(metainfo.channel()) { - Some(ch) => ch, - None => bail!("No public key configured for channel '{}'", metainfo.channel()), - }; - - let data = self.infoblock.read_metainfo(); - let signature = self.infoblock.read_signature(); - Ok(channel.verify(&data, &signature)?) - } - - /// - /// Parse the bytes from the metainfo section of the Partition Information - /// Block and return a `Metainfo` structure. - /// - pub fn metainfo(&self) -> Result { - if !self.infoblock.is_valid() { - bail!("partition is invalid"); - } - let bytes = self.infoblock.read_metainfo(); - let metainfo = toml::from_slice::(&bytes)?; - Ok(metainfo) - } - - pub fn is_new(&self) -> bool { - self.infoblock.has_status(STATUS_NEW) - } - - pub fn is_good(&self) -> bool { - self.infoblock.has_status(STATUS_GOOD) - } - - pub fn is_preferred(&self) -> bool { - self.infoblock.has_flag(FLAG_PREFER) - } - - pub fn status_label(&self) -> String { - status_code_label(self.infoblock.status()) - } - - /// `true` if the Partition Info Block fields - /// contain legal values. `false` indicates that - /// the data is corrupted or was never written to - /// this partition. - pub fn is_initialized(&self) -> bool { - self.infoblock.is_valid() - } - - pub fn write_image(&self, image_path: &Path, metainfo: &[u8], signature: &[u8]) -> Result<()> { - if self.is_mounted { - bail!("Cannot write to mounted device {}", self.path_str()); - } - info!("Writing raw rootfs disk image to {}", self.path_str()); - self.write_partition_info(metainfo, signature)?; - run_write_image_dd(image_path, &self.path)?; - - let meta = image_path.metadata()?; - let len = meta.len() as usize; - let nblocks = len / 4096; - - info!("Generating dm-verity hash tree"); - let mut verityinfo = image_path.to_path_buf(); - verityinfo.pop(); - verityinfo.push("verity-format.out"); - let out = run_verityinstall_command(self.path(), self.metainfo()?.verity_salt(), nblocks, len)?; - write_string_to_file(&verityinfo, &out)?; - - info!("Setting parition status field to STATUS_NEW"); - self.write_status(STATUS_NEW)?; - - Ok(()) - } - - /// Called at boot to perform various checks and possibly - /// update the status field to an error state. - /// - /// Mark `STATUS_TRY_BOOT` partition as `STATUS_FAILED`. - /// - /// If metainfo cannot be parsed, mark as `STATUS_BAD_META`. - /// - /// Verify metainfo signature and mark `STATUS_BAD_SIG` if - /// signature verification fails. - /// - pub fn boot_scan(&self, config: &Config) -> Result<()> { - if !self.is_initialized() { - return Ok(()); - } - if self.infoblock.status() == STATUS_TRY_BOOT { - warn!("Partition {} has STATUS_TRY_BOOT, assuming it failed boot attempt and marking STATUS_FAILED", self.path_str()); - self.write_status(STATUS_FAILED)?; - } - - if let Err(_) = self.metainfo() { - warn!("Partition {} has invalid metainfo, setting STATUS_BAD_META", self.path_str()); - self.write_status(STATUS_BAD_META)?; - return Ok(()); - } - - match self.verify_signature(config) { - Err(e) => { - warn!("Error verifying parition signature on {}: {}", self.path_str(), e); - warn!("Partition {} has bad signature, marking STATUS_BAD_SIG", self.path_str()); - self.write_status(STATUS_BAD_SIG)?; - }, - Ok(false) => { - warn!("Partition {} has bad signature, marking STATUS_BAD_SIG", self.path_str()); - self.write_status(STATUS_BAD_SIG)?; - }, - Ok(true) => { /* signature good */ }, - }; - Ok(()) - } -} - - - -/// Set on partition before writing a new rootfs disk image -const STATUS_INVALID : u8 = 0; - -/// Set on partition after write of new rootfs disk image completes successfully -const STATUS_NEW : u8 = 1; - -/// Set on boot selected partition if in `STATUS_NEW` state. -const STATUS_TRY_BOOT: u8 = 2; - -/// Set on boot when a `STATUS_TRY_BOOT` partition successfully launches desktop -const STATUS_GOOD : u8 = 3; - -/// Set on boot for any partition in state `STATUS_TRY_BOOT` -const STATUS_FAILED : u8 = 4; - -/// Set on boot selected partition when signature fails to verify -const STATUS_BAD_SIG : u8 = 5; - -/// Set on boot selected partition when Metainfo cannot be parsed from Partition Info Block -const STATUS_BAD_META: u8 = 6; - -const CODE_TO_LABEL: [&str; 7] = ["Invalid", "New", "Try Boot", "Good", "Failed Boot", "Bad Signature", "Bad Metainfo"]; - -fn is_valid_status_code(code: u8) -> bool { - code <= STATUS_BAD_META -} - -fn status_code_label(code: u8) -> String { - if is_valid_status_code(code) { - CODE_TO_LABEL[code as usize].to_string() - } else { - format!("Invalid status code: {}", code) - } -} - diff --git a/citadel-tools/citadel-update/src/unpacker.rs b/citadel-tools/citadel-update/src/unpacker.rs deleted file mode 100644 index 33aa56f..0000000 --- a/citadel-tools/citadel-update/src/unpacker.rs +++ /dev/null @@ -1,129 +0,0 @@ - -use std::path::{Path,PathBuf}; -use std::fs::{self,File}; -use std::io::{self,Read}; - -use toml; -use ed25519_dalek::SIGNATURE_LENGTH; - -use Result; -use Config; -use Metainfo; -use Partition; -use util::*; -use MAX_METAINFO_LEN; - -pub struct UpdateImageUnpacker { - path: PathBuf, - workdir: Workdir, - metainfo: Metainfo, - metainfo_bytes: Vec, - signature_bytes: Vec, - header_len: usize, -} - -impl UpdateImageUnpacker { - pub fn open>(path: P, config: &Config) -> Result { - let mut f = File::open(path.as_ref())?; - - UpdateImageUnpacker::read_magic(&mut f)?; - let metainfo_bytes = UpdateImageUnpacker::read_metainfo(&mut f)?; - let metainfo = toml::from_slice::(&metainfo_bytes)?; - - let mut signature_bytes = vec![0; SIGNATURE_LENGTH]; - f.read_exact(&mut signature_bytes)?; - - let channel = match config.channel(metainfo.channel()) { - Some(ch) => ch, - None => bail!("Channel '{}' not found in configuration", metainfo.channel()), - }; - - if !channel.verify(metainfo_bytes.as_slice(), &signature_bytes)? { - bail!("Signature verification failed"); - } - - - let mut workdir = Workdir::new(config.citadel_updates_base(), metainfo.channel()); - workdir.set_version(metainfo.version() as usize)?; - - let mlen = metainfo_bytes.len(); - - Ok(UpdateImageUnpacker { - path: PathBuf::from(path.as_ref()), - workdir, metainfo, metainfo_bytes, - signature_bytes, - - header_len: 6 + SIGNATURE_LENGTH + mlen, - }) - } - - fn read_magic(r: &mut File) -> Result<()> { - let mut buf = [0u8; 4]; - r.read_exact(&mut buf)?; - - if &buf != b"UPDT" { - bail!("not an update image, bad magic value"); - } - Ok(()) - } - fn read_metainfo(r: &mut File) -> Result> { - let mut lenbuf = [0u8; 2]; - r.read_exact(&mut lenbuf)?; - let len = (lenbuf[0] as usize) << 8 | (lenbuf[1] as usize); - if len == 0 || len > MAX_METAINFO_LEN { - bail!("metainfo length field has invalid value: {}", len); - } - let mut bytes = vec![0u8; len]; - r.read_exact(bytes.as_mut_slice())?; - Ok(bytes) - } - - pub fn metainfo(&self) -> &Metainfo { - &self.metainfo - } - - pub fn unpack_disk_image(&self) -> Result<()> { - let mut from = File::open(&self.path)?; - info!("{} -> {}", path_str(&self.path), path_str(&self.workdir.filepath("citadel-image.ext2.xz"))); - let mut to = File::create(&self.workdir.filepath("citadel-image.ext2.xz"))?; - let mut discard = vec![0u8; self.header_len]; - from.read_exact(&mut discard)?; - io::copy(&mut from, &mut to)?; - Ok(()) - } - - pub fn decompress_disk_image(&self) -> Result<()> { - let output = self.workdir.filepath("citadel-image.ext2"); - if output.exists() { - fs::remove_file(&output)?; - } - run_xz_command(&self.workdir.filepath("citadel-image.ext2.xz"), true)?; - - let file_sz = fs::metadata(&output)?.len() as usize; - let meta_sz = self.metainfo.nsectors() * 512; - if file_sz != meta_sz { - bail!("Uncompressed images size {} does not match size declared in metainfo {}", file_sz, meta_sz); - } - Ok(()) - } - - pub fn verify_shasum(&self) -> Result<()> { - let path = self.workdir.filepath("citadel-image.ext2"); - let shasum = run_sha256_command(&path)?; - if shasum != self.metainfo.shasum() { - let mut bad = path.clone(); - bad.pop(); bad.push("citadel-image.ext2.badsum"); - fs::rename(&path, &bad)?; - bail!("Failed sha256 sum of {}: {}", path_str(&path), shasum); - } - info!("GOOD: {}", shasum); - Ok(()) - } - - pub fn write_partition(&self, part: &Partition) -> Result<()> { - let path = self.workdir.filepath("citadel-image.ext2"); - part.write_image(&path, &self.metainfo_bytes, &self.signature_bytes)?; - Ok(()) - } - -} diff --git a/citadel-tools/citadel-update/src/util.rs b/citadel-tools/citadel-update/src/util.rs deleted file mode 100644 index 89a345f..0000000 --- a/citadel-tools/citadel-update/src/util.rs +++ /dev/null @@ -1,168 +0,0 @@ -use std::path::{Path,PathBuf}; -use std::fs::{self,File}; -use std::process::Command; -use std::io::{Read,Write,BufReader,BufRead}; - -use Result; - -pub struct Workdir(PathBuf); - -impl Workdir { - pub fn new(base: &str, channel: &str) -> Workdir { - let mut pb = PathBuf::from(base); - pb.push(channel); - Workdir(pb) - } - - pub fn find_next_version(&mut self) -> Result { - let mut version = 1; - loop { - let path = self.0.join(version.to_string()); - if !path.exists() { - self.set_version(version)?; - return Ok(version); - } - version += 1; - } - } - - pub fn set_version(&mut self, version: usize) -> Result<()> { - self.0.push(version.to_string()); - fs::create_dir_all(&self.0)?; - Ok(()) - } - - pub fn filepath(&self, name: &str) -> PathBuf { - self.0.join(name) - } - -} - - -/// -/// Returns `true` if `path` matches the source field (first field) -/// of any of the mount lines listed in /proc/mounts -/// -pub fn is_path_mounted(path: &Path) -> Result { - let path_str = path.to_str().unwrap(); - let f = File::open("/proc/mounts")?; - let reader = BufReader::new(f); - for line in reader.lines() { - if let Some(s) = line?.split_whitespace().next() { - if s == path_str { - return Ok(true); - } - } - } - Ok(false) -} - -/// -/// Converts a `Path` into `&str` representation, assuming -/// that it contains valid utf-8 -/// -pub fn path_str(path: &Path) -> &str { - path.to_str().unwrap() -} - -pub fn rootfs_partition_paths() -> Result> { - let mut rootfs_paths = Vec::new(); - for dent in fs::read_dir("/dev/mapper")? { - let path = dent?.path(); - if is_path_rootfs(&path) { - rootfs_paths.push(path); - } - } - Ok(rootfs_paths) -} -pub fn is_path_rootfs(path: &Path) -> bool { - path_filename(path).starts_with("citadel-rootfs") -} - -fn path_filename(path: &Path) -> &str { - if let Some(osstr) = path.file_name() { - if let Some(name) = osstr.to_str() { - return name; - } - } - "" -} - -pub fn write_string_to_file(path: &Path, s: &str) -> Result<()> { - let mut f = File::create(path)?; - f.write_all(s.as_bytes())?; - Ok(()) -} - -pub fn read_file_as_string(path: &Path) -> Result { - let mut f = File::open(path)?; - let mut buffer = String::new(); - f.read_to_string(&mut buffer)?; - Ok(buffer) -} - -pub fn run_file_command(path: &Path) -> Result { - let path = path_str(path); - let output = try_run_command("/usr/bin/file", &["-b", path])?; - Ok(output) -} - -pub fn run_xz_command(path: &Path, decompress: bool) -> Result<()> { - let path = path_str(path); - if decompress { - let _ = try_run_command("/usr/bin/xz", &["-d", path])?; - } else { - let _ = try_run_command("/usr/bin/xz", &["-T0", path])?; - } - Ok(()) -} - -pub fn run_sha256_command(path: &Path) -> Result { - let path = path_str(path); - let output = try_run_command("/usr/bin/sha256sum", &[path])?; - - let v: Vec<&str> = output.split_whitespace().collect(); - Ok(v[0].trim().to_owned()) -} - -pub fn run_verityformat_command(srcfile: &Path, hashfile: &Path) -> Result { - let srcfile = path_str(srcfile); - let hashfile = path_str(hashfile); - let output = try_run_command("/usr/sbin/veritysetup", - &["format", srcfile, hashfile])?; - Ok(output) -} - -pub fn run_verityinstall_command(block_device: &Path, salt: &str, data_blocks: usize, hash_offset: usize) -> Result { - let data_device = path_str(block_device).to_owned(); - let hash_device = path_str(block_device).to_owned(); - let arg1 = format!("--data-blocks={}", data_blocks); - let arg2 = format!("--hash-offset={}", hash_offset); - let arg3 = format!("--salt={}", salt); - let output = try_run_command("/usr/sbin/veritysetup", &[&arg1, &arg2, &arg3, "format", &data_device, &hash_device])?; - Ok(output) -} - -pub fn run_write_image_dd(image_src: &Path, block_device: &Path) -> Result<()> { - let src = format!("if={}", path_str(image_src)); - let dst = format!("of={}", path_str(block_device)); - let _ = try_run_command("/bin/dd", &[&src, &dst, "bs=4M"])?; - Ok(()) -} - -fn try_run_command(cmd_path: &str, args: &[&str]) -> Result { - let mut cmd = Command::new(cmd_path); - for arg in args { - cmd.arg(arg); - } - - let result = cmd.output()?; - - if !result.status.success() { - let err = String::from_utf8(result.stderr)?; - let argstr = args.join(" "); - bail!("{} {} command failed: {}", cmd_path, argstr, err); - } - let output = String::from_utf8(result.stdout)?; - Ok(output) -}