From ffbd34e14ff0ad115d4a62066d9694e6e8622d76 Mon Sep 17 00:00:00 2001 From: Roey Darwish Dror Date: Wed, 5 Jun 2019 10:05:59 +0300 Subject: [PATCH] Add presets support --- Cargo.lock | 31 ++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/args.rs | 4 ++++ src/error.rs | 8 +++++++- src/main.rs | 56 ++++++++++++++++++++++++++++++++++++++++++-------- src/presets.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++ src/process.rs | 18 ++++++++-------- 7 files changed, 150 insertions(+), 19 deletions(-) create mode 100644 src/presets.rs diff --git a/Cargo.lock b/Cargo.lock index 9c20bb6..7df7f37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,9 +7,11 @@ dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -397,6 +399,24 @@ name = "scoped_threadpool" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "serde" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "simplelog" version = "0.5.3" @@ -505,6 +525,14 @@ dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "toml" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-segmentation" version = "1.3.0" @@ -606,6 +634,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ccc78bfd5acd7bf3e89cffcf899e5cb1a52d6fafa8dec2739ad70c9577a57288" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" +"checksum serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "32746bf0f26eab52f06af0d0aa1984f641341d06d8d673c693871da2d188c9be" +"checksum serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "46a3223d0c9ba936b61c0d2e3e559e3217dbfb8d65d06d26e8b3c25de38bae3e" "checksum simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e95345f185d5adeb8ec93459d2dc99654e294cc6ccf5b75414d8ea262de9a13" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3d0760c312538987d363c36c42339b55f5ee176ea8808bbe4543d484a291c8d1" @@ -617,6 +647,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" "checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/Cargo.toml b/Cargo.toml index 6e72ec2..9f43a95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,5 @@ structopt = "0.2.14" simplelog = "0.5.3" tempfile = "3.0.5" nix = "0.12.0" +serde = { version = "1.0.92", features = ["derive"] } +toml = "0.5.1" diff --git a/src/args.rs b/src/args.rs index d9e4a55..f129cab 100644 --- a/src/args.rs +++ b/src/args.rs @@ -41,6 +41,10 @@ pub struct CreateCommand { /// Encrypt the root partition #[structopt(short = "e", long = "encrypted-root")] pub encrypted_root: bool, + + /// Path to preset files + #[structopt(long = "presets")] + pub presets: Vec, } #[derive(StructOpt)] diff --git a/src/error.rs b/src/error.rs index fafe884..ab6c575 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,7 +6,7 @@ pub struct Error { inner: Context, } -#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)] +#[derive(Clone, Eq, PartialEq, Debug, Fail)] pub enum ErrorKind { #[fail(display = "Error quering information about the block device")] DeviceQuery, @@ -73,6 +73,12 @@ pub enum ErrorKind { #[fail(display = "Failed launching Qemu")] Qemu, + + #[fail(display = "Error loading preset \"{}\"", _0)] + Preset(String), + + #[fail(display = "Error executing preset script")] + PresetScript, } impl Fail for Error { diff --git a/src/main.rs b/src/main.rs index e20ace4..1d4dda8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod args; mod error; +mod presets; mod process; mod storage; mod tool; @@ -13,9 +14,10 @@ use failure::{Fail, ResultExt}; use log::{debug, error, info, warn}; use nix::sys::signal; use simplelog::*; +use std::collections::HashSet; use std::fs; use std::io::Write; -use std::os::unix::process::CommandExt as UnixCommandExt; +use std::os::unix::{fs::PermissionsExt, process::CommandExt as UnixCommandExt}; use std::path::Path; use std::process::exit; use std::thread; @@ -74,6 +76,8 @@ fn fix_fstab(fstab: &str) -> String { } fn create(command: CreateCommand) -> Result<(), Error> { + let presets = presets::Presets::load(&command.presets)?; + let sgdisk = Tool::find("sgdisk")?; let pacstrap = Tool::find("pacstrap")?; let arch_chroot = Tool::find("arch-chroot")?; @@ -141,19 +145,26 @@ fn create(command: CreateCommand) -> Result<(), Error> { let mount_stack = mount(mount_point.path(), &boot_filesystem, &root_filesystem)?; + let mut packages: HashSet = [ + "base", + "grub", + "efibootmgr", + "intel-ucode", + "networkmanager", + "broadcom-wl", + ] + .iter() + .map(|s| String::from(*s)) + .collect(); + + packages.extend(presets.packages); + info!("Bootstrapping system"); pacstrap .execute() .arg("-c") .arg(mount_point.path()) - .args(&[ - "base", - "grub", - "efibootmgr", - "intel-ucode", - "networkmanager", - "broadcom-wl", - ]) + .args(packages) .args(&command.extra_packages) .run(ErrorKind::Pacstrap)?; @@ -167,6 +178,33 @@ fn create(command: CreateCommand) -> Result<(), Error> { debug!("fstab:\n{}", fstab); fs::write(mount_point.path().join("etc/fstab"), fstab).context(ErrorKind::Fstab)?; + if !presets.scripts.is_empty() { + info!("Running custom scripts"); + } + + for script in presets.scripts { + let mut script_file = + tempfile::NamedTempFile::new_in(mount_point.path()).context(ErrorKind::PresetScript)?; + script_file + .write_all(script.as_bytes()) + .and_then(|_| script_file.as_file_mut().metadata()) + .and_then(|metadata| { + let mut permissions = metadata.permissions(); + permissions.set_mode(0o755); + fs::set_permissions(script_file.path(), permissions) + }) + .context(ErrorKind::PresetScript)?; + + let script_path = script_file.into_temp_path(); + arch_chroot + .execute() + .arg(mount_point.path()) + .arg(Path::new("/").join(script_path.file_name().unwrap())) + .run(ErrorKind::PostInstallation)?; + } + + info!("Performing post installation tasks"); + arch_chroot .execute() .arg(mount_point.path()) diff --git a/src/presets.rs b/src/presets.rs new file mode 100644 index 0000000..ed32593 --- /dev/null +++ b/src/presets.rs @@ -0,0 +1,50 @@ +use crate::error::{Error, ErrorKind}; +use failure::ResultExt; +use serde::Deserialize; +use std::collections::HashSet; +use std::fs; +use std::path::{Path, PathBuf}; +use toml; + +#[derive(Deserialize)] + +struct Preset { + packages: Option>, + script: Option, +} + +impl Preset { + fn load(path: &Path) -> Result { + let data = fs::read_to_string(path) + .with_context(|_| ErrorKind::Preset(format!("{}", path.display())))?; + Ok(toml::from_str(&data) + .with_context(|_| ErrorKind::Preset(format!("{}", path.display())))?) + } +} + +pub struct Presets { + pub packages: HashSet, + pub scripts: Vec, +} + +impl Presets { + pub fn load(list: &[PathBuf]) -> Result { + let mut packages = HashSet::new(); + let mut scripts = Vec::new(); + + for preset in list { + let Preset { + script, + packages: preset_packages, + } = Preset::load(&preset)?; + + if let Some(preset_packages) = preset_packages { + packages.extend(preset_packages); + } + + scripts.extend(script); + } + + Ok(Self { packages, scripts }) + } +} diff --git a/src/process.rs b/src/process.rs index e6f3e9f..51eafd6 100644 --- a/src/process.rs +++ b/src/process.rs @@ -20,32 +20,32 @@ pub trait CommandExt { impl CommandExt for Command { fn run(&mut self, context: ErrorKind) -> Result<(), Error> { - let exit_status = self.spawn().context(context)?.wait().context(context)?; + let exit_status = self + .spawn() + .with_context(|_| context.clone())? + .wait() + .with_context(|_| context.clone())?; if !exit_status.success() { - return Err(ProcessError::BadExitCode(exit_status) - .context(context) - .into()); + Err(ProcessError::BadExitCode(exit_status)).with_context(|_| context.clone())?; } Ok(()) } fn run_text_output(&mut self, context: ErrorKind) -> Result { - let output = self.output().context(context)?; + let output = self.output().with_context(|_| context.clone())?; if !output.status.success() { let error = str::from_utf8(&output.stderr).unwrap_or("[INVALID UTF8]"); error!("{}", error); - return Err(ProcessError::BadExitCode(output.status) - .context(context) - .into()); + Err(ProcessError::BadExitCode(output.status)).with_context(|_| context.clone())?; } Ok(String::from( str::from_utf8(&output.stdout) .map_err(|_| ProcessError::InvalidUtf8) - .context(context)?, + .with_context(|_| context.clone())?, )) } }