From 80add18dc937291a5235ecb5ca62cdc542decd44 Mon Sep 17 00:00:00 2001 From: MggMuggins <mggmugginsmc@gmail.com> Date: Thu, 26 Jul 2018 12:43:57 -0500 Subject: [PATCH] Functional Partial rewrite --- Cargo.lock | 110 ++++++++++++++++++++---- Cargo.toml | 3 +- src/install.rs | 205 -------------------------------------------- src/lib.rs | 226 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 319 insertions(+), 225 deletions(-) delete mode 100644 src/install.rs diff --git a/Cargo.lock b/Cargo.lock index 85bf9c5..8eb6809 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,27 @@ dependencies = [ "scoped_threadpool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "backtrace" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "base64" version = "0.6.0" @@ -64,6 +85,16 @@ name = "byteorder" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cc" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "clap" version = "2.27.1" @@ -108,9 +139,23 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "extra" -version = "0.1.0" -source = "git+https://github.com/redox-os/libextra.git#402932084acd5fef4812945887ceaaa2ddd5f264" +name = "failure" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.9 (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 = "fixedbitset" @@ -391,10 +436,11 @@ name = "redox_installer" version = "0.2.0" dependencies = [ "arg_parser 0.1.0 (git+https://github.com/redox-os/arg-parser.git)", + "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "liner 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "pkgutils 0.1.1 (git+https://github.com/redox-os/pkgutils.git)", "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_users 0.1.0 (git+https://github.com/redox-os/users.git)", "redoxfs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -415,16 +461,6 @@ dependencies = [ "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "redox_users" -version = "0.1.0" -source = "git+https://github.com/redox-os/users.git#50f4022e6c713a131bbbfde360cb0f369e96c672" -dependencies = [ - "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "extra 0.1.0 (git+https://github.com/redox-os/libextra.git)", - "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "redoxfs" version = "0.3.1" @@ -450,6 +486,11 @@ dependencies = [ "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustc-demangle" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rustls" version = "0.9.0" @@ -574,6 +615,15 @@ 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 = "tar" version = "0.4.13" @@ -736,11 +786,30 @@ name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi" +version = "0.3.5" +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-build" version = "0.1.1" source = "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 = "xattr" version = "0.1.11" @@ -753,6 +822,8 @@ dependencies = [ "checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45" "checksum arg_parser 0.1.0 (git+https://github.com/redox-os/arg-parser.git)" = "<none>" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" +"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" +"checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e" "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" "checksum bidir-map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6c3d05037e57974413eef55a8505df19de3cb4dc7a8f8389e1588ec492ae9c73" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" @@ -761,13 +832,16 @@ dependencies = [ "checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9" "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" +"checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275" +"checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e" "checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180" "checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum crc 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fba69ea0e15e720f7e1cfe1cf3bc55007fbd41e32b8ae11cfa343e7e5961e79a" "checksum crc-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "003d1170779d405378223470f5864b41b79a91969be1260e4de7b4ec069af69c" "checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3" -"checksum extra 0.1.0 (git+https://github.com/redox-os/libextra.git)" = "<none>" +"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 fixedbitset 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "85cb8fec437468d86dc7c83ca7cfc933341d561873275f22dd5eedefa63a6478" "checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159" "checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82" @@ -803,9 +877,9 @@ dependencies = [ "checksum rayon-core 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e64b609139d83da75902f88fd6c01820046840a18471e4dfcd5ac7c0f46bea53" "checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum redox_users 0.1.0 (git+https://github.com/redox-os/users.git)" = "<none>" "checksum redoxfs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f800e00e74d5e26b5066798195a0bffab834ecf0ae7f460d82d884c9569fa36" "checksum ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2a6dc7fc06a05e6de183c5b97058582e9da2de0c136eafe49609769c507724" +"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum rustls 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17727f4b991294da2c84d75a43c003151ff58072212768800f66c56ee46dca43" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum scoped_threadpool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4ea459fe3ceff01e09534847c49860891d3ff1c12b4eb7731b67f2778fb60190" @@ -822,6 +896,7 @@ dependencies = [ "checksum syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)" = "58fd09df59565db3399efbba34ba8a2fec1307511ebd245d0061ff9d42691673" "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 tar 0.4.13 (git+https://github.com/redox-os/tar-rs)" = "<none>" "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" @@ -845,5 +920,8 @@ dependencies = [ "checksum webpki 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e499345fc4c6b7c79a5b8756d4592c4305510a13512e79efafe00dfbd67bbac6" "checksum webpki-roots 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bfb3f50499f21ad2317f442845e3b5805b007f1e728f59885c99e61b8c181a7" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"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 xattr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "5f04de8a1346489a2f9e9bd8526b73d135ec554227b17568456e86aa35b6f3fc" diff --git a/Cargo.toml b/Cargo.toml index 5faa37f..b873ce4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,9 @@ path = "src/lib.rs" [dependencies] arg_parser = { git = "https://github.com/redox-os/arg-parser.git" } +argon2rs = { version = "0.2", default-features = false } liner = "0.1" +failure = "0.1" pkgutils = { git = "https://github.com/redox-os/pkgutils.git" } rand = "0.3" redoxfs = "0.3" @@ -20,4 +22,3 @@ serde = "0.8" serde_derive = "0.8" termion = "1.5.1" toml = { version = "0.2", default-features = false, features = ["serde"] } -redox_users = { git = "https://github.com/redox-os/users.git" } diff --git a/src/install.rs b/src/install.rs deleted file mode 100644 index ad76f80..0000000 --- a/src/install.rs +++ /dev/null @@ -1,205 +0,0 @@ -extern crate liner; -extern crate pkgutils; -extern crate rand; -extern crate termion; -extern crate redox_users; - -use self::rand::Rng; -use self::termion::input::TermRead; -use self::pkgutils::{Repo, Package}; - -use std::{env, fs}; -use std::ffi::OsStr; -use std::io::{self, stderr, Write}; -use std::os::unix::ffi::OsStrExt; -use std::os::unix::fs::symlink; -use std::path::Path; -use std::process::{self, Command}; -use std::str::FromStr; - -use config::Config; - -const REMOTE: &'static str = "https://static.redox-os.org/pkg"; -const TARGET: &'static str = "x86_64-unknown-redox"; - -fn unwrap_or_prompt<T: FromStr>(option: Option<T>, context: &mut liner::Context, prompt: &str) -> Result<T, String> { - match option { - None => { - let line = context.read_line(prompt, &mut |_| {}).map_err(|err| format!("failed to read line: {}", err))?; - T::from_str(&line).map_err(|_| format!("failed to parse '{}'", line)) - }, - Some(t) => Ok(t) - } -} - -fn prompt_password(prompt: &str, confirm_prompt: &str) -> Result<String, String> { - let stdin = io::stdin(); - let mut stdin = stdin.lock(); - let stdout = io::stdout(); - let mut stdout = stdout.lock(); - - stdout.write(prompt.as_bytes()).map_err(|err| format!("failed to write to stdout: {}", err))?; - stdout.flush().map_err(|err| format!("failed to flush stdout: {}", err))?; - if let Some(password) = stdin.read_passwd(&mut stdout).map_err(|err| format!("failed to read password: {}", err))? { - stdout.write(b"\n").map_err(|err| format!("failed to write to stdout: {}", err))?; - stdout.flush().map_err(|err| format!("failed to flush stdout: {}", err))?; - - if password.is_empty() { - Ok(password) - } else { - stdout.write(confirm_prompt.as_bytes()).map_err(|err| format!("failed to write to stdout: {}", err))?; - stdout.flush().map_err(|err| format!("failed to flush stdout: {}", err))?; - if let Some(confirm_password) = stdin.read_passwd(&mut stdout).map_err(|err| format!("failed to read password: {}", err))? { - stdout.write(b"\n").map_err(|err| format!("failed to write to stdout: {}", err))?; - stdout.flush().map_err(|err| format!("failed to flush stdout: {}", err))?; - - if confirm_password == password { - let salt = format!("{:X}", rand::OsRng::new().unwrap().next_u64()); - Ok(redox_users::User::encode_passwd(&password, &salt)) - } else { - Err("passwords do not match".to_string()) - } - } else { - Err("passwords do not match".to_string()) - } - } - } else { - Ok(String::new()) - } -} - -fn install_packages<S: AsRef<str>>(config: &Config, dest: &str, cookbook: Option<S>) { - let mut repo = Repo::new(TARGET); - repo.add_remote(REMOTE); - - if let Some(cookbook) = cookbook { - let status = Command::new("./repo.sh") - .current_dir(cookbook.as_ref()) - .args(config.packages.keys()) - .spawn() - .unwrap() - .wait() - .unwrap(); - - if !status.success() { - write!(stderr(), "./repo.sh failed.").unwrap(); - process::exit(1); - } - - for (packagename, _package) in &config.packages { - println!("Installing package {}", packagename); - let path = format!("{}/{}/repo/{}/{}.tar.gz", - env::current_dir().unwrap().to_string_lossy(), - cookbook.as_ref(), TARGET, packagename); - Package::from_path(&path).unwrap().install(dest).unwrap(); - } - } else { - for (packagename, _package) in &config.packages { - println!("Installing package {}", packagename); - repo.fetch(&packagename).unwrap().install(dest).unwrap(); - } - } -} - -pub fn install<P: AsRef<Path>, S: AsRef<str>>(config: Config, output: P, cookbook: Option<S>) -> Result<(), String> { - let output = output.as_ref(); - - println!("Install {:#?} to {}", config, output.display()); - - let mut context = liner::Context::new(); - - macro_rules! prompt { - ($dst:expr, $def:expr, $($arg:tt)*) => (if config.general.prompt { - match unwrap_or_prompt($dst, &mut context, &format!($($arg)*)) { - Ok(res) => if res.is_empty() { - Ok($def) - } else { - Ok(res) - }, - Err(err) => Err(err) - } - } else { - Ok($dst.unwrap_or($def)) - }) - } - - // TODO: Mount disk if output is a file - let sysroot = output.to_owned(); - - macro_rules! dir { - ($path:expr) => {{ - let mut path = sysroot.clone(); - path.push($path); - println!("Create directory {}", path.display()); - fs::create_dir_all(&path).map_err(|err| format!("failed to create {}: {}", path.display(), err))?; - }}; - } - - macro_rules! file { - ($path:expr, $data:expr, $symlink:expr) => {{ - let mut path = sysroot.clone(); - path.push($path); - if let Some(parent) = path.parent() { - println!("Create file parent {}", parent.display()); - fs::create_dir_all(parent).map_err(|err| format!("failed to create file parent {}: {}", parent.display(), err))?; - } - if $symlink { - println!("Create symlink {}", path.display()); - symlink(&OsStr::from_bytes($data), &path).map_err(|err| format!("failed to symlink {}: {}", path.display(), err))?; - } else { - println!("Create file {}", path.display()); - let mut file = fs::File::create(&path).map_err(|err| format!("failed to create {}: {}", path.display(), err))?; - file.write_all($data).map_err(|err| format!("failed to write {}: {}", path.display(), err))?; - } - }}; - } - - dir!(""); - - install_packages(&config, sysroot.to_str().unwrap(), cookbook); - - for file in config.files { - file!(file.path.trim_matches('/'), file.data.as_bytes(), file.symlink); - } - - let mut passwd = String::new(); - let mut next_uid = 1000; - for (username, user) in config.users { - let password = if let Some(password) = user.password { - password - } else if config.general.prompt { - prompt_password(&format!("{}: enter password: ", username), &format!("{}: confirm password: ", username))? - } else { - String::new() - }; - - let uid = user.uid.unwrap_or(next_uid); - - if uid >= next_uid { - next_uid = uid + 1; - } - - let gid = user.gid.unwrap_or(uid); - - let name = prompt!(user.name, username.clone(), "{}: name [{}]: ", username, username)?; - let home = prompt!(user.home, format!("/home/{}", username), "{}: home [/home/{}]: ", username, username)?; - let shell = prompt!(user.shell, "/bin/ion".to_string(), "{}: shell [/bin/ion]: ", username)?; - - println!("Adding user {}:", username); - println!("\tPassword: {}", password); - println!("\tUID: {}", uid); - println!("\tGID: {}", gid); - println!("\tName: {}", name); - println!("\tHome: {}", home); - println!("\tShell: {}", shell); - - dir!(home.trim_matches('/')); - - passwd.push_str(&format!("{};{};{};{};{};file:{};file:{}\n", username, password, uid, gid, name, home, shell)); - } - if ! passwd.is_empty() { - file!("etc/passwd", passwd.as_bytes(), false); - } - - Ok(()) -} diff --git a/src/lib.rs b/src/lib.rs index 3fe6a21..6a2c50d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,229 @@ #[macro_use] extern crate serde_derive; +extern crate argon2rs; +extern crate liner; +extern crate failure; +extern crate pkgutils; +extern crate rand; +extern crate termion; + +mod config; pub use config::Config; -pub use install::install; -mod config; -mod install; +use argon2rs::verifier::Encoded; +use argon2rs::{Argon2, Variant}; +use failure::{Error, err_msg}; +use rand::{OsRng, Rng}; +use termion::input::TermRead; +use pkgutils::{Repo, Package}; + +use std::{env, fs}; +use std::ffi::OsStr; +use std::io::{self, stderr, Write}; +use std::os::unix::ffi::OsStrExt; +use std::os::unix::fs::symlink; +use std::path::Path; +use std::process::{self, Command}; +use std::str::FromStr; + +type Result<T> = std::result::Result<T, Error>; + +const REMOTE: &'static str = "https://static.redox-os.org/pkg"; +const TARGET: &'static str = "x86_64-unknown-redox"; + +/// Converts a password to a serialized argon2rs hash, understandable +/// by redox_users. If the password is blank, the hash is blank. +fn hash_password(password: &str) -> Result<String> { + if password != "" { + let a2 = Argon2::new(10, 1, 4096, Variant::Argon2i)?; + let salt = format!("{:X}", OsRng::new()?.next_u64()); + let enc = Encoded::new( + a2, + password.as_bytes(), + salt.as_bytes(), + &[], + &[] + ); + + Ok(String::from_utf8(enc.to_u8())?) + } else { + Ok("".to_string()) + } +} + +fn unwrap_or_prompt<T: FromStr>(option: Option<T>, context: &mut liner::Context, prompt: &str) -> Result<T> { + match option { + Some(t) => Ok(t), + None => { + let line = context.read_line(prompt, &mut |_| {})?; + T::from_str(&line).map_err(|_err| err_msg("failed to parse input")) + } + } +} + +/// Returns a password collected from the user (plaintext) +fn prompt_password(prompt: &str, confirm_prompt: &str) -> Result<String> { + let stdin = io::stdin(); + let mut stdin = stdin.lock(); + let stdout = io::stdout(); + let mut stdout = stdout.lock(); + + print!("{}", prompt); + let password = stdin.read_passwd(&mut stdout)?; + + print!("\n{}", confirm_prompt); + let confirm_password = stdin.read_passwd(&mut stdout)?; + // TODO: Remove this debug msg + println!("\nPass: {:?}; ConfPass: {:?};", password, confirm_password); + + // Note: Actually comparing two Option<String> values + if confirm_password == password { + Ok(password.unwrap_or("".to_string())) + } else { + Err(err_msg("passwords do not match")) + } +} + +fn install_packages<S: AsRef<str>>(config: &Config, dest: &str, cookbook: Option<S>) { + let mut repo = Repo::new(TARGET); + repo.add_remote(REMOTE); + + if let Some(cookbook) = cookbook { + let status = Command::new("./repo.sh") + .current_dir(cookbook.as_ref()) + .args(config.packages.keys()) + .spawn() + .unwrap() + .wait() + .unwrap(); + + if !status.success() { + write!(stderr(), "./repo.sh failed.").unwrap(); + process::exit(1); + } + + for (packagename, _package) in &config.packages { + println!("Installing package {}", packagename); + let path = format!("{}/{}/repo/{}/{}.tar.gz", + env::current_dir().unwrap().to_string_lossy(), + cookbook.as_ref(), TARGET, packagename); + Package::from_path(&path).unwrap().install(dest).unwrap(); + } + } else { + for (packagename, _package) in &config.packages { + println!("Installing package {}", packagename); + repo.fetch(&packagename).unwrap().install(dest).unwrap(); + } + } +} + +pub fn install<P: AsRef<Path>, S: AsRef<str>>(config: Config, output: P, cookbook: Option<S>) -> Result<()> { + let output = output.as_ref(); + println!("Install {:#?} to {}", config, output.display()); + + let mut context = liner::Context::new(); + + macro_rules! prompt { + ($dst:expr, $def:expr, $($arg:tt)*) => (if config.general.prompt { + match unwrap_or_prompt($dst, &mut context, &format!($($arg)*)) { + Ok(res) => if res.is_empty() { + Ok($def) + } else { + Ok(res) + }, + Err(err) => Err(err) + } + } else { + Ok($dst.unwrap_or($def)) + }) + } + + // TODO: Mount disk if output is a file + let sysroot = output.to_owned(); + + macro_rules! dir { + ($path:expr) => {{ + let mut path = sysroot.clone(); + path.push($path); + println!("Create directory {}", path.display()); + fs::create_dir_all(&path)?; + }}; + } + + macro_rules! file { + ($path:expr, $data:expr, $symlink:expr) => {{ + let mut path = sysroot.clone(); + path.push($path); + if let Some(parent) = path.parent() { + println!("Create file parent {}", parent.display()); + fs::create_dir_all(parent)?; + } + if $symlink { + println!("Create symlink {}", path.display()); + symlink(&OsStr::from_bytes($data), &path)?; + } else { + println!("Create file {}", path.display()); + let mut file = fs::File::create(&path)?; + file.write_all($data)?; + } + }}; + } + + dir!(""); + + install_packages(&config, sysroot.to_str().unwrap(), cookbook); + + for file in config.files { + file!(file.path.trim_matches('/'), file.data.as_bytes(), file.symlink); + } + + let mut passwd = String::new(); + let mut next_uid = 1000; + + for (username, user) in config.users { + // plaintext + let password = if let Some(password) = user.password { + password + } else if config.general.prompt { + prompt_password( + &format!("{}: enter password: ", username), + &format!("{}: confirm password: ", username))? + } else { + String::new() + }; + + let uid = user.uid.unwrap_or(next_uid); + + if uid >= next_uid { + next_uid = uid + 1; + } + + let gid = user.gid.unwrap_or(uid); + + let name = prompt!(user.name, username.clone(), "{}: name [{}]: ", username, username)?; + let home = prompt!(user.home, format!("/home/{}", username), "{}: home [/home/{}]: ", username, username)?; + let shell = prompt!(user.shell, "/bin/ion".to_string(), "{}: shell [/bin/ion]: ", username)?; + + println!("Adding user {}:", username); + println!("\tPassword: {}", password); + println!("\tUID: {}", uid); + println!("\tGID: {}", gid); + println!("\tName: {}", name); + println!("\tHome: {}", home); + println!("\tShell: {}", shell); + + dir!(home.trim_matches('/')); + + let password = hash_password(&password)?; + + passwd.push_str(&format!("{};{};{};{};{};file:{};file:{}\n", username, password, uid, gid, name, home, shell)); + } + + if ! passwd.is_empty() { + file!("etc/passwd", passwd.as_bytes(), false); + } + + Ok(()) +} -- GitLab