diff --git a/Cargo.lock b/Cargo.lock index 165bd3f..03f5c3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,53 +3,35 @@ version = 4 [[package]] -name = "anstream" -version = "0.6.18" +name = "argh" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "34ff18325c8a36b82f992e533ece1ec9f9a9db446bd1c14d4f936bac88fcd240" dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", + "argh_derive", + "argh_shared", + "rust-fuzzy-search", ] [[package]] -name = "anstyle" -version = "1.0.10" +name = "argh_derive" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "adb7b2b83a50d329d5d8ccc620f5c7064028828538bdf5646acd60dc1f767803" dependencies = [ - "utf8parse", + "argh_shared", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "anstyle-query" -version = "1.1.2" +name = "argh_shared" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "a464143cc82dedcdc3928737445362466b7674b5db4e2eb8e869846d6d84f4f6" dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" -dependencies = [ - "anstyle", - "once_cell", - "windows-sys", + "serde", ] [[package]] @@ -70,39 +52,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "clap" -version = "4.5.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" -dependencies = [ - "clap_builder", -] - -[[package]] -name = "clap_builder" -version = "4.5.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_lex" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" - -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - [[package]] name = "equivalent" version = "1.0.2" @@ -115,6 +64,12 @@ version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "indexmap" version = "2.8.0" @@ -125,12 +80,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itoa" version = "1.0.15" @@ -153,10 +102,11 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" name = "navinstall" version = "0.1.0" dependencies = [ - "clap", + "argh", "nix", "serde", "serde_json", + "strum", "toml", "yansi", ] @@ -173,12 +123,6 @@ dependencies = [ "libc", ] -[[package]] -name = "once_cell" -version = "1.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" - [[package]] name = "proc-macro2" version = "1.0.94" @@ -197,6 +141,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rust-fuzzy-search" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2" + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + [[package]] name = "ryu" version = "1.0.20" @@ -245,10 +201,26 @@ dependencies = [ ] [[package]] -name = "strsim" -version = "0.11.1" +name = "strum" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] [[package]] name = "syn" @@ -301,85 +273,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "winnow" version = "0.7.4" diff --git a/Cargo.toml b/Cargo.toml index 007c693..e11d292 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,10 @@ name = "navinstall" path = "src/main.rs" [dependencies] -clap = { version = "4.5.23", features = ["cargo"] } +argh = "0.1.13" nix = { version = "0.29.0", features = ["user"] } serde = { version = "1.0.216", features = ["derive"] } serde_json = "1.0.135" +strum = { version = "0.27.1", features = ["derive", "strum_macros"] } toml = "0.8.19" yansi = "1.0.1" diff --git a/src/args.rs b/src/args.rs index 7711c77..8d6db93 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,41 +1,108 @@ -use clap::{ArgAction, arg, command}; +use argh::FromArgs; -pub fn get_args() -> clap::ArgMatches { - command!() - .about("navOS Installer") - .subcommand( - command!("create-iso") - .about("Create a new installation medium ISO") - .arg(arg!(--without_gui "Create ISO with just terminal")) - .arg(arg!(--no_tmp "Create ISO on disk")) - .arg(arg!(--kb_layout "Create ISO with this keyboard layout")) - .arg(arg!(--kb_variant "Create ISO with this keyboard layout variant")) - .arg(arg!(--user "Change the system user")) - .arg(arg!(--pass "Change the system users password")) - .arg(arg!(--uid "Change the system users UID")) - .arg(arg!(--unit "Include a systemd unit").action(ArgAction::Append)) - .arg(arg!(--install "Create ISO which automatically installs upon boot.")) - ) - .subcommand( - command!() - .name("install") - .about("Install a system according to configuration") - .arg(arg!(-f --force "Install without confirming config")) - .arg(arg!([config] "Config file").required(true)), - ) - .subcommand( - command!() - .name("create-tar") - .arg(arg!([dir] "root fs dir").required(true)) - .about("Create a container tar image"), - ) - .subcommand( - command!() - .name("create-img") - .about("Create an install on a disk image for VMs or embedded devices") - .arg(arg!(--gpt "Use modern disk format")) - .arg(arg!([config] "Config file").required(true)) - .arg(arg!([image] "Image file").required(true)) - ) - .get_matches() +use crate::fx::Features; + +#[derive(FromArgs)] +/// navOS Installer +pub struct NavinstallArgs { + #[argh(subcommand)] + pub cmd: NavinstallCommands, +} + +#[derive(FromArgs)] +#[argh(subcommand)] +pub enum NavinstallCommands { + CreateISO(CreateISOCommand), + Install(InstallCommand), + CreateTar(CreateTarCommand), + CreateImage(CreateImageCommand), + Feature(FeatureCommand), +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "create-iso")] +/// Create a new installation medium ISO +pub struct CreateISOCommand { + #[argh(switch)] + /// create ISO with just terminal + pub without_gui: bool, + + #[argh(switch)] + /// create ISO on disk + pub no_tmp: bool, + + #[argh(option)] + /// create ISO with this keyboard layout + pub kb_layout: Option, + + #[argh(option)] + /// create ISO with this keyboard layout variant + pub kb_variant: Option, + + #[argh(option)] + /// change the system user + pub user: Option, + + #[argh(option)] + /// change the system users password + pub pass: Option, + + #[argh(option)] + /// change the system user UID + pub uid: Option, + + #[argh(option)] + /// include a systemd unit + pub unit: Vec, + + #[argh(option)] + /// create ISO which automatically installs upon boot. + pub install: Option, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "install")] +/// Install a system according to configuration +pub struct InstallCommand { + #[argh(switch, short = 'f')] + /// install without confirming config + pub force: bool, + + #[argh(positional)] + /// config file + pub config: String, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "create-tar")] +/// Create a container rootfs +pub struct CreateTarCommand { + #[argh(positional)] + /// rootfs dir + pub dir: String, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "create-img")] +/// Create an install on a disk image for VMs or embedded devices +pub struct CreateImageCommand { + #[argh(switch)] + /// use modern disk format + pub gpt: bool, + + #[argh(positional)] + /// config file + pub config: String, + + #[argh(positional)] + /// image file + pub image: String, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "fx")] +/// Enable or disable features +pub struct FeatureCommand { + #[argh(subcommand)] + pub cmd: Features, } diff --git a/src/config.rs b/src/config.rs index 5e83aec..0b6ce93 100644 --- a/src/config.rs +++ b/src/config.rs @@ -123,8 +123,10 @@ pub struct GeneralConfig { pub secure_boot: Option, } -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize, Default)] pub enum GPUVendor { + #[default] + NONE, AMD, NVIDIA, INTEL, diff --git a/src/create_iso.rs b/src/create_iso.rs index 4256b8e..222f81a 100644 --- a/src/create_iso.rs +++ b/src/create_iso.rs @@ -5,7 +5,7 @@ use yansi::{Color, Paint}; use crate::{expect_yes, install::uncomment_tag, linux::is_root, linux::run_command, print_status}; /// Build the `kxkbrc` file for the given `layout` and `variant -pub fn build_kxkbrc(layout: &str, variant: Option<&str>) -> String { +pub fn build_kxkbrc(layout: &str, variant: Option) -> String { let mut res = String::from("[Layout]\nUse=true\n"); res.push_str(&format!("LayoutList={layout}\n")); if let Some(variant) = variant { @@ -19,10 +19,10 @@ pub fn create_iso( without_gui: bool, no_tmp: bool, kb_layout: &str, - kb_variant: Option<&str>, + kb_variant: Option, user: Option<(String, String, u16)>, systemd_units: Vec, - install: Option<&String>, + install: Option, ) { if !is_root() { eprintln!("Error: You need root to create an ISO"); diff --git a/src/install/bluetooth.rs b/src/fx/bluetooth.rs similarity index 65% rename from src/install/bluetooth.rs rename to src/fx/bluetooth.rs index 689688d..a4aa439 100644 --- a/src/install/bluetooth.rs +++ b/src/fx/bluetooth.rs @@ -1,5 +1,12 @@ +use argh::FromArgs; + use crate::{linux::systemd_service_enable, pkg::install_pkgs, print_status}; +#[derive(FromArgs)] +#[argh(subcommand, name = "bluetooth")] +/// Enable Bluetooth support +pub struct BluetoothFeature {} + /// Enable Bluetooth pub fn setup_bluetooth() { print_status("Setting up Bluetooth"); diff --git a/src/fx/docker.rs b/src/fx/docker.rs new file mode 100644 index 0000000..4068ce1 --- /dev/null +++ b/src/fx/docker.rs @@ -0,0 +1,32 @@ +use argh::FromArgs; + +use crate::{ + linux::{arch_chroot, systemd_service_enable}, + pkg::install_pkgs, + print_status, +}; + +#[derive(FromArgs)] +#[argh(subcommand, name = "docker")] +/// Enable docker +pub struct DockerFeature { + #[argh(positional)] + /// assign user to docker group + pub user: Vec, +} + +/// Setup docker on the system +pub fn setup_docker(conf: &[String]) { + print_status("Setting up Docker"); + install_pkgs(&["docker", "docker-compose"]); + + systemd_service_enable("docker.service"); + + for user in conf { + arch_chroot( + &vec!["usermod", "-a", "-G", "docker", user.as_str()], + None, + false, + ); + } +} diff --git a/src/install/firewall.rs b/src/fx/firewall.rs similarity index 73% rename from src/install/firewall.rs rename to src/fx/firewall.rs index 0a48a73..7524d29 100644 --- a/src/install/firewall.rs +++ b/src/fx/firewall.rs @@ -1,9 +1,20 @@ +use argh::FromArgs; + use crate::{ linux::{arch_chroot, systemd_service_enable}, pkg::install_pkgs, print_status, }; +#[derive(FromArgs)] +#[argh(subcommand, name = "firewall")] +/// Install firewall +pub struct FirewallFeature { + #[argh(switch)] + /// allow SSH + pub ssh: bool, +} + /// Setup UFW pub fn setup_firewall(ssh: bool) { print_status("Enabling firewall"); diff --git a/src/install/gpu.rs b/src/fx/gpu.rs similarity index 77% rename from src/install/gpu.rs rename to src/fx/gpu.rs index 7ed6460..6fa0abf 100644 --- a/src/install/gpu.rs +++ b/src/fx/gpu.rs @@ -1,5 +1,16 @@ +use argh::FromArgs; + use crate::{config::GPUVendor, pkg::install_pkgs}; +#[derive(FromArgs)] +#[argh(subcommand, name = "gpu")] +/// Install GPU drivers +pub struct GPUFeature { + #[argh(positional)] + /// GPU vendor + pub vendor: String, +} + /// Setup GPU video drivers pub fn setup_video_drivers(vendor: &GPUVendor) { match vendor { @@ -24,5 +35,6 @@ pub fn setup_video_drivers(vendor: &GPUVendor) { "lib32-vulkan-intel", ]); } + GPUVendor::NONE => {} } } diff --git a/src/fx/mod.rs b/src/fx/mod.rs new file mode 100644 index 0000000..ecfb76e --- /dev/null +++ b/src/fx/mod.rs @@ -0,0 +1,73 @@ +use argh::FromArgs; +use bluetooth::BluetoothFeature; +use docker::DockerFeature; +use firewall::FirewallFeature; +use gpu::GPUFeature; +use ollama::OllamaFeature; +use ssh::SSHFeature; +use virt::{VirtFeature, VirtGuestFeature}; +use zram::ZramFeature; + +pub mod bluetooth; +pub mod docker; +pub mod firewall; +pub mod gpu; +pub mod ollama; +pub mod ssh; +pub mod virt; +pub mod zram; + +pub struct OptionSet { + values: Vec<(String, String)>, +} + +impl OptionSet { + pub fn new(values: Vec) -> Self { + Self { + values: values + .into_iter() + .map(|x| { + let s = x.split_once('=').unwrap(); + (s.0.to_owned(), s.1.to_owned()) + }) + .collect(), + } + } + + pub fn get(&self, key: &str) -> Option<&String> { + self.values.iter().find(|x| x.0 == key).map(|x| &x.1) + } + + pub fn get_many(&self, key: &str) -> Vec { + self.values + .iter() + .filter_map(|x| if x.0 == key { Some(x.1.clone()) } else { None }) + .collect() + } + + pub fn get_flag(&self, key: &str) -> Option { + if let Some(val) = self.get(key) { + if val == "true" || val == "1" { + return Some(true); + } else { + return Some(false); + } + } + + None + } +} + +#[derive(FromArgs)] +#[argh(subcommand)] +pub enum Features { + Bluetooth(BluetoothFeature), + GPU(GPUFeature), + Docker(DockerFeature), + Firewall(FirewallFeature), + Ollama(OllamaFeature), + SSH(SSHFeature), + Virt(VirtFeature), + VirtGuest(VirtGuestFeature), + Zram(ZramFeature), +} diff --git a/src/install/ollama.rs b/src/fx/ollama.rs similarity index 69% rename from src/install/ollama.rs rename to src/fx/ollama.rs index 47ca27e..ab36b10 100644 --- a/src/install/ollama.rs +++ b/src/fx/ollama.rs @@ -1,5 +1,7 @@ use std::time::Duration; +use argh::FromArgs; + use crate::{ config::OllamaConfig, linux::{arch_chroot, systemd_service_enable}, @@ -7,6 +9,28 @@ use crate::{ print_status, }; +#[derive(FromArgs)] +#[argh(subcommand, name = "ollama")] +/// Enable ollama AI +pub struct OllamaFeature { + #[argh(option)] + /// pull model + pub model: Vec, + + #[argh(switch)] + /// enable CUDA + pub gpu: bool, +} + +impl OllamaFeature { + pub fn into_config(self) -> OllamaConfig { + OllamaConfig { + models: Some(self.model), + gpu: self.gpu, + } + } +} + /// Setup Ollama AI Service pub fn setup_ollama(conf: &OllamaConfig) { if conf.gpu { diff --git a/src/install/ssh.rs b/src/fx/ssh.rs similarity index 59% rename from src/install/ssh.rs rename to src/fx/ssh.rs index 0af5f65..f0f89cb 100644 --- a/src/install/ssh.rs +++ b/src/fx/ssh.rs @@ -1,11 +1,50 @@ +use argh::FromArgs; + use crate::{ - config::SSHConfig, + config::{SSHConfig, SSHKey}, linux::{install_file, systemd_service_enable}, pkg::install_pkgs, print_status, }; use std::io::Write; +#[derive(FromArgs)] +#[argh(subcommand, name = "ssh")] +/// Enable SSH +pub struct SSHFeature { + #[argh(option)] + /// copy sshd config + pub sshd: Option, + + #[argh(option)] + /// add SSH key in the format 'user1,user2:key' + pub key: Vec, +} + +impl SSHFeature { + pub fn into_config(self) -> SSHConfig { + SSHConfig { + sshd_config: self.sshd, + key: Some( + self.key + .into_iter() + .map(|x| { + let (users, key) = x.split_once(':').unwrap(); + let users: Vec<_> = users + .split(',') + .map(std::string::ToString::to_string) + .collect(); + SSHKey { + key: key.to_string(), + users: users, + } + }) + .collect(), + ), + } + } +} + /// Setup SSH on the system /// /// This should be done after `setup_users()` to ensure that the users directories exist. diff --git a/src/fx/virt.rs b/src/fx/virt.rs new file mode 100644 index 0000000..8c2da99 --- /dev/null +++ b/src/fx/virt.rs @@ -0,0 +1,63 @@ +use argh::FromArgs; + +use crate::{ + linux::{arch_chroot, run_command_noerr, systemd_service_enable}, + pkg::install_pkgs, + print_status, +}; + +#[derive(FromArgs)] +#[argh(subcommand, name = "virt-guest")] +/// Install VM guest utils +pub struct VirtGuestFeature {} + +#[derive(FromArgs)] +#[argh(subcommand, name = "virt")] +/// Enable virtualization +pub struct VirtFeature { + #[argh(switch)] + /// enable virt-manager GUI + pub gui: bool, + + #[argh(option)] + /// add user to libvirt group + pub user: Vec, +} + +pub fn setup_virtualization(conf: &VirtFeature) { + install_pkgs(&["libvirt", "swtpm", "qemu-base"]); + + if conf.gui { + install_pkgs(&["virt-manager"]); + } + + systemd_service_enable("libvirtd.service"); + + for user in &conf.user { + arch_chroot( + &vec!["usermod", "-a", "-G", "libvirt", user.as_str()], + None, + false, + ); + } +} + +pub fn setup_vm_guest() { + print_status("Detected KVM. Installing utils"); + install_pkgs(&["qemu-guest-agent", "spice-vdagent"]); + systemd_service_enable("qemu-guest-agent.service"); + systemd_service_enable("spice-vdagentd.service"); +} + +/// Setup guest utils if running inside a VM +pub fn try_setup_vm_guest() { + let res = run_command_noerr(&["systemd-detect-virt", "--vm"], None, false); + let is_vm = res.0.trim(); + + match is_vm { + "qemu" | "kvm" => { + setup_vm_guest(); + } + _ => {} + } +} diff --git a/src/install/zram.rs b/src/fx/zram.rs similarity index 79% rename from src/install/zram.rs rename to src/fx/zram.rs index cc8b762..d9cbf36 100644 --- a/src/install/zram.rs +++ b/src/fx/zram.rs @@ -1,9 +1,16 @@ +use argh::FromArgs; + use crate::{ linux::{install_file, systemd_service_enable}, pkg::install_pkgs, print_status, }; +#[derive(FromArgs)] +#[argh(subcommand, name = "zram")] +/// Enable ZRAM +pub struct ZramFeature {} + /// Setup the ZRAM feature pub fn setup_zram() { install_pkgs(&["zram-generator"]); diff --git a/src/install/docker.rs b/src/install/docker.rs deleted file mode 100644 index b89e34c..0000000 --- a/src/install/docker.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::{ - config::UserConfig, - linux::{arch_chroot, systemd_service_enable}, - pkg::install_pkgs, - print_status, -}; - -/// Setup docker on the system -pub fn setup_docker(conf: &[UserConfig]) { - print_status("Setting up Docker"); - install_pkgs(&["docker", "docker-compose"]); - - systemd_service_enable("docker.service"); - - for user in conf { - if user.docker.unwrap_or_default() { - arch_chroot( - &vec!["usermod", "-a", "-G", "docker", user.name.as_str()], - None, - false, - ); - } - } -} diff --git a/src/install/first_boot.rs b/src/install/first_boot.rs index 36f4d83..61f63df 100644 --- a/src/install/first_boot.rs +++ b/src/install/first_boot.rs @@ -2,7 +2,7 @@ use crate::{ config::GeneralConfig, - linux::{arch_chroot, install_file, run_command, systemd_service_enable}, + linux::{arch_chroot, install_file, run_command}, print_status, }; diff --git a/src/install/mod.rs b/src/install/mod.rs index 63d37ea..e2f97ca 100644 --- a/src/install/mod.rs +++ b/src/install/mod.rs @@ -6,44 +6,36 @@ // DRIVE SELECTION -use bluetooth::setup_bluetooth; +use crate::config::InstallMode; +use crate::fx::docker::setup_docker; +use crate::fx::firewall::setup_firewall; +use crate::fx::gpu::setup_video_drivers; +use crate::fx::ollama::setup_ollama; +use crate::fx::ssh::setup_ssh; +use crate::fx::virt::{VirtFeature, setup_virtualization, try_setup_vm_guest}; +use crate::fx::zram::setup_zram; use boot::setup_bootloader; use desktop::setup_desktop; -use docker::setup_docker; use drives::{format_drives, mount_drives, setup_fstrim}; -use firewall::setup_firewall; use firmware::{setup_fwupd, setup_microcode}; use first_boot::{first_boot_values, genfstab}; -use gpu::setup_video_drivers; use kernel::setup_mkinitcpio; use navos::setup_navos; -use ollama::setup_ollama; use security::{has_secure_boot, setup_secure_boot, setup_tpm_unlock}; use skel::setup_skel; -use ssh::setup_ssh; use user::setup_users; -use virt::{setup_virtualization, setup_vm}; use yansi::{Color, Paint}; -use zram::setup_zram; -pub mod bluetooth; pub mod boot; pub mod desktop; -pub mod docker; pub mod drives; -pub mod firewall; pub mod firmware; pub mod first_boot; -pub mod gpu; pub mod kernel; pub mod navos; -pub mod ollama; pub mod security; pub mod skel; -pub mod ssh; pub mod user; -pub mod virt; -pub mod zram; // TODO : error handling // TODO : Power profile daemon @@ -51,6 +43,7 @@ pub mod zram; use crate::{ config::{InstallConfig, WlanConfig}, + fx::bluetooth::setup_bluetooth, linux::run_command, pkg::{self, install_pkgs, pacstrap_at, setup_kernel}, print_status, @@ -209,7 +202,25 @@ pub fn install_mnt(conf: InstallConfig, bare: bool) { // Applications if conf.pkg.virtualization.unwrap_or_default() { - setup_virtualization(&conf); + setup_virtualization(&VirtFeature { + gui: matches!(conf.general.mode, InstallMode::Desktop), + user: conf + .user + .as_ref() + .map(|users| { + users + .into_iter() + .filter_map(|x| { + if x.virtualization.unwrap_or_default() { + Some(x.name.clone()) + } else { + None + } + }) + .collect::>() + }) + .unwrap_or_default(), + }); } if conf.pkg.docker.unwrap_or_default() { @@ -219,7 +230,17 @@ pub fn install_mnt(conf: InstallConfig, bare: bool) { Vec::new() }; - setup_docker(&user_conf); + let docker_users: Vec<_> = user_conf + .iter() + .filter_map(|x| { + if x.docker.unwrap_or_default() { + Some(x.name.clone()) + } else { + None + } + }) + .collect(); + setup_docker(&docker_users); } if let Some(ai) = conf.ai { @@ -237,7 +258,7 @@ pub fn install_mnt(conf: InstallConfig, bare: bool) { // System setup_zram(); - setup_vm(); + try_setup_vm_guest(); if let Some(gpu_vendor) = &conf.general.gpu_driver { setup_video_drivers(gpu_vendor); } diff --git a/src/install/skel.rs b/src/install/skel.rs index e4231da..cfbe67d 100644 --- a/src/install/skel.rs +++ b/src/install/skel.rs @@ -5,10 +5,7 @@ pub fn setup_skel(conf: &GeneralConfig) { std::fs::create_dir_all("/mnt/etc/skel/.config").unwrap(); install_file( "/mnt/etc/skel/.config/kxkbrc", - &build_kxkbrc( - conf.keyboard_layout.as_str(), - conf.keyboard_variant.as_ref().map(|x| x.as_str()), - ), + &build_kxkbrc(conf.keyboard_layout.as_str(), conf.keyboard_variant.clone()), 0o644, ); } diff --git a/src/install/virt.rs b/src/install/virt.rs deleted file mode 100644 index d410875..0000000 --- a/src/install/virt.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{ - config::{InstallConfig, InstallMode}, - linux::{arch_chroot, run_command_noerr, systemd_service_enable}, - pkg::install_pkgs, - print_status, -}; - -pub fn setup_virtualization(conf: &InstallConfig) { - let user_conf = if let Some(user_conf) = &conf.user { - user_conf.clone() - } else { - Vec::new() - }; - - install_pkgs(&["libvirt", "swtpm", "qemu-base"]); - - if matches!(conf.general.mode, InstallMode::Desktop) { - install_pkgs(&["virt-manager"]); - } - - systemd_service_enable("libvirtd.service"); - - for user in user_conf { - if user.virtualization.unwrap_or_default() { - arch_chroot( - &vec!["usermod", "-a", "-G", "libvirt", user.name.as_str()], - None, - false, - ); - } - } -} - -/// Setup guest utils if running inside a VM -pub fn setup_vm() { - let res = run_command_noerr(&["systemd-detect-virt", "--vm"], None, false); - let is_vm = res.0.trim(); - - match is_vm { - "qemu" | "kvm" => { - print_status("Detected KVM. Installing utils"); - install_pkgs(&["qemu-guest-agent", "spice-vdagent"]); - systemd_service_enable("qemu-guest-agent.service"); - systemd_service_enable("spice-vdagentd.service"); - } - _ => {} - } -} diff --git a/src/lib.rs b/src/lib.rs index c8b9024..b76e924 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ pub mod args; pub mod config; pub mod create_iso; +pub mod fx; pub mod install; pub mod linux; pub mod pkg; diff --git a/src/main.rs b/src/main.rs index d866c2e..0214c77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,12 @@ use navinstall::config::InstallConfig; +use navinstall::fx::bluetooth::setup_bluetooth; +use navinstall::fx::docker::setup_docker; +use navinstall::fx::firewall::setup_firewall; +use navinstall::fx::gpu::setup_video_drivers; +use navinstall::fx::ollama::setup_ollama; +use navinstall::fx::ssh::setup_ssh; +use navinstall::fx::virt::{setup_virtualization, setup_vm_guest}; +use navinstall::fx::zram::setup_zram; use navinstall::install::navos::setup_navos; use navinstall::install::{ drives::setup_disk_image, install, install_mnt, security::ensure_secure_boot, @@ -24,69 +32,40 @@ fn main() { .paint(Color::Yellow) ); - let args = navinstall::args::get_args(); + let args: navinstall::args::NavinstallArgs = argh::from_env(); - match args.subcommand() { - Some(("create-iso", iso_args)) => { - let without_gui = iso_args.get_flag("without_gui"); - let no_tmp = iso_args.get_flag("no_tmp"); + match args.cmd { + navinstall::args::NavinstallCommands::CreateISO(create_isocommand) => { let kb_layout_default = "us".to_string(); - let kb_layout: &String = iso_args.get_one("kb_layout").unwrap_or(&kb_layout_default); - let kb_variant: Option<&str> = - iso_args.get_one("kb_variant").map(|x: &String| x.as_str()); - let auto_install_config: Option<&String> = iso_args.get_one("install"); + let kb_layout = create_isocommand.kb_layout.unwrap_or(kb_layout_default); - let user: Option<&String> = iso_args.get_one("user"); - let pass: Option<&String> = iso_args.get_one("pass"); - let uid: Option<&String> = iso_args.get_one("uid"); - - let user = if user.is_some() && pass.is_some() { + let user = if create_isocommand.user.is_some() && create_isocommand.pass.is_some() { Some(( - user.unwrap().to_string(), - pass.unwrap().to_string(), - uid.map(|x| x.parse().unwrap()).unwrap_or(1000), + create_isocommand.user.unwrap().to_string(), + create_isocommand.pass.unwrap().to_string(), + create_isocommand.uid.unwrap_or(1000), )) } else { None }; - let units: Vec<_> = iso_args.get_many("unit").unwrap_or_default().collect(); - create_iso( - without_gui, - no_tmp, - kb_layout, - kb_variant, + create_isocommand.without_gui, + create_isocommand.no_tmp, + &kb_layout, + create_isocommand.kb_variant, user, - units.into_iter().map(|x: &String| x.to_string()).collect(), - auto_install_config, + create_isocommand.unit, + create_isocommand.install, ); std::process::exit(0); } - Some(("create-tar", tar_options)) => { - ensure_root(); - let dir: &String = tar_options.get_one("dir").unwrap(); - print_status("Pacstrapping root fs"); - pacstrap_at(&dir, &[]); - setup_navos(dir.as_str()); - } - Some(("create-img", install_args)) => { - let config_file: &String = install_args.get_one("config").unwrap(); - let conf = read_conf(config_file); - let img_file: &String = install_args.get_one("image").unwrap(); - let gpt = install_args.get_flag("gpt"); - - setup_disk_image(img_file.as_str(), gpt); - install_mnt(conf, false); - } - Some(("install", install_args)) => { + navinstall::args::NavinstallCommands::Install(install_command) => { ensure_root(); - let config_file: &String = install_args.get_one("config").unwrap(); - let force = install_args.get_flag("force"); - let conf = read_conf(config_file); + let conf = read_conf(&install_command.config); - if !force { + if !install_command.force { print_config(&conf); print!("Do you want to proceed with this configuration? (yes/no) "); expect_yes(); @@ -99,7 +78,53 @@ fn main() { // Run the install(conf, true); } - _ => {} + navinstall::args::NavinstallCommands::CreateTar(create_tar_command) => { + ensure_root(); + print_status("Pacstrapping root fs"); + pacstrap_at(&create_tar_command.dir, &[]); + setup_navos(&create_tar_command.dir.as_str()); + } + navinstall::args::NavinstallCommands::CreateImage(create_image_command) => { + let conf = read_conf(&create_image_command.config); + setup_disk_image( + &create_image_command.image.as_str(), + create_image_command.gpt, + ); + install_mnt(conf, false); + } + navinstall::args::NavinstallCommands::Feature(feature_command) => { + match feature_command.cmd { + navinstall::fx::Features::Bluetooth(_) => setup_bluetooth(), + navinstall::fx::Features::GPU(gpufeature) => { + let vendor = match gpufeature.vendor.to_lowercase().as_str() { + "amd" => navinstall::config::GPUVendor::AMD, + "intel" => navinstall::config::GPUVendor::INTEL, + "nvidia" => navinstall::config::GPUVendor::NVIDIA, + _ => { + println!("{} is no GPU vendor", gpufeature.vendor); + std::process::exit(1); + } + }; + + setup_video_drivers(&vendor); + } + navinstall::fx::Features::Docker(docker_feature) => { + setup_docker(&docker_feature.user); + } + navinstall::fx::Features::Firewall(firewall_feature) => { + setup_firewall(firewall_feature.ssh) + } + navinstall::fx::Features::Ollama(ollama_feature) => { + setup_ollama(&ollama_feature.into_config()) + } + navinstall::fx::Features::SSH(sshfeature) => { + setup_ssh(&Some(sshfeature.into_config())) + } + navinstall::fx::Features::Virt(virt_feature) => setup_virtualization(&virt_feature), + navinstall::fx::Features::VirtGuest(_) => setup_vm_guest(), + navinstall::fx::Features::Zram(_) => setup_zram(), + } + } } } diff --git a/src/pkg.rs b/src/pkg.rs index e490346..4a2f4ce 100644 --- a/src/pkg.rs +++ b/src/pkg.rs @@ -47,7 +47,6 @@ pub fn setup_kernel(kernel: Option) { /// Initial system pacstrap pub fn pacstrap_at(dir: &str, pkg: &[String]) { - // TODO : rearrange pkgs + minify let mut cmd: Vec<&str> = vec!["pacstrap", "-K", dir, "base"]; #[cfg(target_arch = "aarch64")] diff --git a/src/print.rs b/src/print.rs index 3b5af3d..7fb6229 100644 --- a/src/print.rs +++ b/src/print.rs @@ -79,6 +79,7 @@ pub fn print_config(conf: &InstallConfig) { crate::config::GPUVendor::NVIDIA => { general_info.add_str(format!("🟩 NVIDIA GPU {}", "✔️".paint(Color::Green))) } + crate::config::GPUVendor::NONE => {} } } if conf.general.firewall.unwrap_or(true) {