From f2b1a43f6257510bf0a110dd512bd192dfc2abe5 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Sat, 28 Dec 2024 01:19:44 +0100 Subject: [PATCH] refactor --- Cargo.lock | 7 + Cargo.toml | 1 + src/install.rs | 382 -------------------------------------- src/install/boot.rs | 11 ++ src/install/drives.rs | 60 ++++++ src/install/first_boot.rs | 53 ++++++ src/install/kernel.rs | 24 +++ src/install/mod.rs | 97 ++++++++++ src/install/security.rs | 103 ++++++++++ src/install/user.rs | 27 +++ src/install/zram.rs | 23 +++ src/main.rs | 31 +++- src/pkg.rs | 24 ++- 13 files changed, 453 insertions(+), 390 deletions(-) delete mode 100644 src/install.rs create mode 100644 src/install/boot.rs create mode 100644 src/install/drives.rs create mode 100644 src/install/first_boot.rs create mode 100644 src/install/kernel.rs create mode 100644 src/install/mod.rs create mode 100644 src/install/security.rs create mode 100644 src/install/user.rs create mode 100644 src/install/zram.rs diff --git a/Cargo.lock b/Cargo.lock index c6da70e..d4efc4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,6 +150,7 @@ dependencies = [ "nix", "serde", "toml", + "yansi", ] [[package]] @@ -355,3 +356,9 @@ checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/Cargo.toml b/Cargo.toml index ea12e78..8cff2a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,4 @@ clap = { version = "4.5.23", features = ["cargo"] } nix = { version = "0.29.0", features = ["user"] } serde = { version = "1.0.216", features = ["derive"] } toml = "0.8.19" +yansi = "1.0.1" diff --git a/src/install.rs b/src/install.rs deleted file mode 100644 index e439ab3..0000000 --- a/src/install.rs +++ /dev/null @@ -1,382 +0,0 @@ -// TODO : Setup ssh (config + authorized_keys) -// TODO : Setup virtualization -// TODO : Setup docker -// TODO : Autojoin docker swarm -// TODO : Autojoin teleport - -// DRIVE SELECTION - -use crate::{ - config::{DriveConfig, GeneralConfig, InstallConfig, PackageConfig, UserConfig}, - pkg::{self, install_pkgs}, - run_command, -}; - -pub fn str_vec(v: Vec<&str>) -> Vec { - v.into_iter().map(|x| x.to_string()).collect() -} - -pub fn format_drives(conf: &DriveConfig, encrypted: bool) { - // EFI (BOOT) - run_command( - &str_vec(vec!["mkfs.vfat", "-F", "32", conf.boot.as_str()]), - None, - false, - ); - - // ROOT - if encrypted { - run_command( - &str_vec(vec!["cryptsetup", "luksFormat", conf.root.as_str()]), - None, - true, - ); - } else { - run_command(&str_vec(vec!["mkfs.ext4", conf.root.as_str()]), None, false); - } -} - -// MOUNT - -pub fn mount_drives(conf: &DriveConfig, encrypted: bool) { - if encrypted { - run_command( - &str_vec(vec!["cryptsetup", "open", conf.root.as_str(), "root"]), - None, - true, - ); - - run_command( - &str_vec(vec!["mount", "/dev/mapper/root", "/mnt"]), - None, - false, - ); - } else { - run_command( - &str_vec(vec!["mount", conf.root.as_str(), "/mnt"]), - None, - false, - ); - } - - // TODO : Secure mount options - run_command( - &str_vec(vec!["mount", "--mkdir", conf.boot.as_str(), "/mnt/boot"]), - None, - false, - ); -} - -// PACSTRAP - -pub fn pacstrap(conf: &PackageConfig) { - let mut cmd: Vec = vec![ - "pacstrap".into(), - "-K".into(), - "/mnt".into(), - "base".into(), - "linux".into(), - "linux-firmware".into(), - "linux-headers".into(), - "git".into(), - "networkmanager".into(), - "nano".into(), - "doas".into(), - ]; - - cmd.extend(conf.pkg.clone()); - - run_command(&cmd, None, true); -} - -// GENFSTAB - -pub fn genfstab() { - let (stdout, _) = run_command(&str_vec(vec!["genfstab", "-U", "/mnt"]), None, false); - std::fs::write("/mnt/etc/fstab", stdout).unwrap(); -} - -pub fn first_boot_values(conf: &GeneralConfig) { - // CHROOT - run_command( - &vec![ - "arch-chroot".into(), - "/mnt".into(), - "systemd-firstboot".into(), - format!("--locale={}", conf.locale), - format!("--keymap={}", conf.keymap), - format!("--timezone={}", conf.timezone), - format!("--hostname={}", conf.hostname), - ], - None, - false, - ); - - // LOCALE - uncomment_first_value_of(&conf.locale, "/mnt/etc/locale.gen"); - run_command( - &str_vec(vec!["arch-chroot", "/mnt", "locale-gen"]), - None, - false, - ); - - run_command( - &str_vec(vec!["arch-chroot", "/mnt", "hwclock", "--systohc"]), - None, - false, - ); - - run_command( - &str_vec(vec![ - "arch-chroot", - "/mnt", - "systemctl", - "enable", - "NetworkManager.service", - ]), - None, - false, - ); -} - -pub fn uncomment_first_value_of(value: &str, file: &str) { - // read in the file - let content = std::fs::read_to_string(file).unwrap(); - let mut new = String::new(); - let mut found = false; - - // search for the first instance of `value` in the file - // uncomment the '#' symbol if there is one - for line in content.lines() { - if line.contains(value) && !found { - new.push_str(&format!("{}\n", line.replace("#", ""))); - found = true; - } else { - new.push_str(&format!("{line}\n")); - } - } - // write back - std::fs::write(file, new).unwrap(); -} - -pub fn setup_zram() { - install_pkgs(&["zram-generator"]); - std::fs::write( - "/mnt/etc/systemd/zram-generator.conf", - include_str!("root/zram-generator.conf"), - ) - .unwrap(); - run_command( - &str_vec(vec![ - "arch-chroot", - "/mnt", - "systemctl", - "enable", - "systemd-zram-setup@zram0.service", - ]), - None, - false, - ); -} - -// MKINITCPIO + UKI - -pub fn setup_mkinitcpio() { - std::fs::write( - "/mnt/etc/mkinitcpio.d/linux.preset", - include_str!("root/mkinitcpio/linux.preset"), - ) - .unwrap(); - // TODO : more configs - std::fs::write( - "/mnt/etc/mkinitcpio.conf", - include_str!("root/mkinitcpio.conf"), - ) - .unwrap(); - run_command( - &str_vec(vec!["arch-chroot", "/mnt", "mkinitcpio", "--allpresets"]), - None, - true, - ); -} - -// SECURE BOOT - -pub fn setup_secure_boot() { - // TODO : Assert sb setup mode - let (stdout, _) = run_command(&str_vec(vec!["sbctl", "status"]), None, false); - let binding = stdout.lines().collect::>(); - let status = binding.get(2).unwrap(); - - if !status.contains("Setup Mode") { - println!("[!] Secure Boot is not in Setup Mode"); - std::process::exit(1); - } else { - if !status.contains("Enabled") { - println!("[!] Secure Boot is not in Setup Mode"); - std::process::exit(1); - } - } - - run_command(&vec!["sbctl".into(), "create-keys".into()], None, false); - - run_command( - &str_vec(vec!["sbctl", "enroll-keys", "--microsoft"]), - None, - false, - ); - run_command( - &str_vec(vec![ - "sbctl", - "sign", - "-s", - "/boot/EFI/Linux/arch-linux.efi", - ]), - None, - false, - ); - - run_command( - &str_vec(vec![ - "sbctl", - "sign", - "-s", - "/boot/EFI/Linux/arch-linux-fallback.efi", - ]), - None, - false, - ); - - run_command( - &str_vec(vec![ - "sbctl", - "sign", - "-s", - "/boot/EFI/systemd/systemd-bootx64.efi", - ]), - None, - false, - ); - - run_command( - &str_vec(vec!["sbctl", "sign", "-s", "/boot/EFI/Boot/bootx64.efi"]), - None, - false, - ); - - run_command(&str_vec(vec!["sbctl", "verify"]), None, false); -} - -// MODS - -/// Post Installer - -// TPM Unlock - -pub fn setup_tpm_unlock(conf: &DriveConfig) { - install_pkgs(&["tpm2-tools"]); - - // systemd-cryptenroll --tpm2-device=list - - // Recovery Key - run_command( - &str_vec(vec![ - "arch-chroot", - "/mnt", - "systemd-cryptenroll", - "--recovery-key", - &conf.root, - ]), - None, - false, - ); - - run_command( - &str_vec(vec![ - "arch-chroot", - "/mnt", - "systemd-cryptenroll", - "--tpm2-device=auto", - &conf.root, - "--tpm2-pcrs=7", - ]), - None, - false, - ); -} - -pub fn setup_bootloader() { - run_command( - &str_vec(vec!["arch-chroot", "/mnt", "bootctl", "install"]), - None, - false, - ); -} - -pub fn setup_users(conf: &[UserConfig]) { - let mut doas_conf = String::new(); - - for user in conf { - run_command( - &str_vec(vec!["arch-chroot", "/mnt", "useradd", "-m", &user.name]), - None, - false, - ); - - run_command( - &str_vec(vec!["arch-chroot", "/mnt", "passwd", &user.name]), - Some(&format!("{}\n{}\n", user.password, user.password)), - false, - ); - - if user.doas_root { - doas_conf.push_str(&format!("permit {} as root\n", user.name)); - } - } - - std::fs::write("/mnt/etc/doas.conf", doas_conf).unwrap(); -} - -pub fn install(conf: InstallConfig) { - // Drive Setup - format_drives(&conf.drive, conf.general.encryption); - mount_drives(&conf.drive, conf.general.encryption); - - // Base Install - pacstrap(&conf.pkg); - genfstab(); - - // System Setup - first_boot_values(&conf.general); - setup_users(&conf.user); - - setup_bootloader(); - - match conf.general.mode { - crate::config::InstallMode::Base => {} - crate::config::InstallMode::Desktop => { - install_pkgs(&pkg::DESKTOP_PKG); - } - crate::config::InstallMode::Server => { - install_pkgs(&pkg::SERVER_PKG); - } - crate::config::InstallMode::Kiosk => { - // TODO - } - } - - if conf.pkg.virtualization { - // TODO : Enable virtualization - } - - if conf.pkg.docker { - // TODO : Enable docker - } - - setup_zram(); - setup_secure_boot(); - setup_mkinitcpio(); - setup_tpm_unlock(&conf.drive); - - println!("System install complete"); -} diff --git a/src/install/boot.rs b/src/install/boot.rs new file mode 100644 index 0000000..b726b66 --- /dev/null +++ b/src/install/boot.rs @@ -0,0 +1,11 @@ +use crate::run_command; + +use super::str_vec; + +pub fn setup_bootloader() { + run_command( + &str_vec(vec!["arch-chroot", "/mnt", "bootctl", "install"]), + None, + false, + ); +} diff --git a/src/install/drives.rs b/src/install/drives.rs new file mode 100644 index 0000000..fb3d021 --- /dev/null +++ b/src/install/drives.rs @@ -0,0 +1,60 @@ +use crate::{config::DriveConfig, run_command}; + +use super::str_vec; + +pub fn format_drives(conf: &DriveConfig, encrypted: bool) { + // EFI (BOOT) + run_command( + &str_vec(vec!["mkfs.vfat", "-F", "32", conf.boot.as_str()]), + None, + false, + ); + + // ROOT + if encrypted { + run_command( + &str_vec(vec!["cryptsetup", "luksFormat", conf.root.as_str()]), + None, + true, + ); + } else { + run_command(&str_vec(vec!["mkfs.ext4", conf.root.as_str()]), None, false); + } +} + +// MOUNT + +pub fn mount_drives(conf: &DriveConfig, encrypted: bool) { + if encrypted { + run_command( + &str_vec(vec!["cryptsetup", "open", conf.root.as_str(), "root"]), + None, + true, + ); + + run_command( + &str_vec(vec!["mount", "/dev/mapper/root", "/mnt"]), + None, + false, + ); + } else { + run_command( + &str_vec(vec!["mount", conf.root.as_str(), "/mnt"]), + None, + false, + ); + } + + run_command( + &str_vec(vec![ + "mount", + "--mkdir", + conf.boot.as_str(), + "/mnt/boot", + "-o", + "rw,nosuid,nodev,noatime,fmask=0137,dmask=0027", + ]), + None, + false, + ); +} diff --git a/src/install/first_boot.rs b/src/install/first_boot.rs new file mode 100644 index 0000000..36ae91e --- /dev/null +++ b/src/install/first_boot.rs @@ -0,0 +1,53 @@ +// GENFSTAB + +use crate::{config::GeneralConfig, run_command}; + +use super::{str_vec, uncomment_first_value_of}; + +pub fn genfstab() { + let (stdout, _) = run_command(&str_vec(vec!["genfstab", "-U", "/mnt"]), None, false); + std::fs::write("/mnt/etc/fstab", stdout).unwrap(); +} + +pub fn first_boot_values(conf: &GeneralConfig) { + // CHROOT + run_command( + &vec![ + "arch-chroot".into(), + "/mnt".into(), + "systemd-firstboot".into(), + format!("--locale={}", conf.locale), + format!("--keymap={}", conf.keymap), + format!("--timezone={}", conf.timezone), + format!("--hostname={}", conf.hostname), + ], + None, + false, + ); + + // LOCALE + uncomment_first_value_of(&conf.locale, "/mnt/etc/locale.gen"); + run_command( + &str_vec(vec!["arch-chroot", "/mnt", "locale-gen"]), + None, + false, + ); + + run_command( + &str_vec(vec!["arch-chroot", "/mnt", "hwclock", "--systohc"]), + None, + false, + ); + + run_command( + &str_vec(vec![ + "arch-chroot", + "/mnt", + "systemctl", + "enable", + "NetworkManager.service", + ]), + None, + false, + ); +} diff --git a/src/install/kernel.rs b/src/install/kernel.rs new file mode 100644 index 0000000..f2ebc44 --- /dev/null +++ b/src/install/kernel.rs @@ -0,0 +1,24 @@ +// MKINITCPIO + UKI + +use crate::run_command; + +use super::str_vec; + +pub fn setup_mkinitcpio() { + std::fs::write( + "/mnt/etc/mkinitcpio.d/linux.preset", + include_str!("../root/mkinitcpio/linux.preset"), + ) + .unwrap(); + // TODO : more configs + std::fs::write( + "/mnt/etc/mkinitcpio.conf", + include_str!("../root/mkinitcpio.conf"), + ) + .unwrap(); + run_command( + &str_vec(vec!["arch-chroot", "/mnt", "mkinitcpio", "--allpresets"]), + None, + true, + ); +} diff --git a/src/install/mod.rs b/src/install/mod.rs new file mode 100644 index 0000000..c64e71c --- /dev/null +++ b/src/install/mod.rs @@ -0,0 +1,97 @@ +// TODO : Setup ssh (config + authorized_keys) +// TODO : Setup virtualization +// TODO : Setup docker +// TODO : Autojoin docker swarm +// TODO : Autojoin teleport + +// DRIVE SELECTION + +use boot::setup_bootloader; +use drives::{format_drives, mount_drives}; +use first_boot::{first_boot_values, genfstab}; +use kernel::setup_mkinitcpio; +use security::{setup_secure_boot, setup_tpm_unlock}; +use user::setup_users; +use yansi::{Color, Paint}; +use zram::setup_zram; + +pub mod boot; +pub mod drives; +pub mod first_boot; +pub mod kernel; +pub mod security; +pub mod user; +pub mod zram; + +use crate::{ + config::InstallConfig, + pkg::{self, install_pkgs, pacstrap}, +}; + +pub fn str_vec(v: Vec<&str>) -> Vec { + v.into_iter().map(|x| x.to_string()).collect() +} + +pub fn uncomment_first_value_of(value: &str, file: &str) { + // read in the file + let content = std::fs::read_to_string(file).unwrap(); + let mut new = String::new(); + let mut found = false; + + // search for the first instance of `value` in the file + // uncomment the '#' symbol if there is one + for line in content.lines() { + if line.contains(value) && !found { + new.push_str(&format!("{}\n", line.replace("#", ""))); + found = true; + } else { + new.push_str(&format!("{line}\n")); + } + } + // write back + std::fs::write(file, new).unwrap(); +} + +pub fn install(conf: InstallConfig) { + // Drive Setup + format_drives(&conf.drive, conf.general.encryption); + mount_drives(&conf.drive, conf.general.encryption); + + // Base Install + pacstrap(&conf.pkg); + genfstab(); + + // System Setup + first_boot_values(&conf.general); + setup_users(&conf.user); + + setup_bootloader(); + + match conf.general.mode { + crate::config::InstallMode::Base => {} + crate::config::InstallMode::Desktop => { + install_pkgs(&pkg::DESKTOP_PKG); + } + crate::config::InstallMode::Server => { + install_pkgs(&pkg::SERVER_PKG); + } + crate::config::InstallMode::Kiosk => { + // TODO + } + } + + if conf.pkg.virtualization { + // TODO : Enable virtualization + } + + if conf.pkg.docker { + // TODO : Enable docker + } + + setup_zram(); + setup_secure_boot(); + setup_mkinitcpio(); + setup_tpm_unlock(&conf.drive); + + println!("{}", "System install complete".paint(Color::Green)); +} diff --git a/src/install/security.rs b/src/install/security.rs new file mode 100644 index 0000000..383646a --- /dev/null +++ b/src/install/security.rs @@ -0,0 +1,103 @@ +// TPM Unlock + +use crate::{config::DriveConfig, pkg::install_pkgs, run_command}; + +use super::str_vec; + +pub fn setup_tpm_unlock(conf: &DriveConfig) { + install_pkgs(&["tpm2-tools"]); + + // systemd-cryptenroll --tpm2-device=list + + // Recovery Key + run_command( + &str_vec(vec![ + "arch-chroot", + "/mnt", + "systemd-cryptenroll", + "--recovery-key", + &conf.root, + ]), + None, + false, + ); + + run_command( + &str_vec(vec![ + "arch-chroot", + "/mnt", + "systemd-cryptenroll", + "--tpm2-device=auto", + &conf.root, + "--tpm2-pcrs=7", + ]), + None, + false, + ); +} + +// SECURE BOOT + +pub fn setup_secure_boot() { + let (stdout, _) = run_command(&str_vec(vec!["sbctl", "status"]), None, false); + let binding = stdout.lines().collect::>(); + let status = binding.get(1).unwrap(); + + if !status.contains("Setup Mode") { + println!("[!] Secure Boot is not in Setup Mode"); + std::process::exit(1); + } else { + if !status.contains("Enabled") { + println!("[!] Secure Boot is not in Setup Mode"); + std::process::exit(1); + } + } + + run_command(&vec!["sbctl".into(), "create-keys".into()], None, false); + + run_command( + &str_vec(vec!["sbctl", "enroll-keys", "--microsoft"]), + None, + false, + ); + run_command( + &str_vec(vec![ + "sbctl", + "sign", + "-s", + "/boot/EFI/Linux/arch-linux.efi", + ]), + None, + false, + ); + + run_command( + &str_vec(vec![ + "sbctl", + "sign", + "-s", + "/boot/EFI/Linux/arch-linux-fallback.efi", + ]), + None, + false, + ); + + run_command( + &str_vec(vec![ + "sbctl", + "sign", + "-s", + "/boot/EFI/systemd/systemd-bootx64.efi", + ]), + None, + false, + ); + + run_command( + &str_vec(vec!["sbctl", "sign", "-s", "/boot/EFI/Boot/bootx64.efi"]), + None, + false, + ); + + run_command(&str_vec(vec!["sbctl", "verify"]), None, false); +} diff --git a/src/install/user.rs b/src/install/user.rs new file mode 100644 index 0000000..b47d5ba --- /dev/null +++ b/src/install/user.rs @@ -0,0 +1,27 @@ +use crate::{config::UserConfig, run_command}; + +use super::str_vec; + +pub fn setup_users(conf: &[UserConfig]) { + let mut doas_conf = String::new(); + + for user in conf { + run_command( + &str_vec(vec!["arch-chroot", "/mnt", "useradd", "-m", &user.name]), + None, + false, + ); + + run_command( + &str_vec(vec!["arch-chroot", "/mnt", "passwd", &user.name]), + Some(&format!("{}\n{}\n", user.password, user.password)), + false, + ); + + if user.doas_root { + doas_conf.push_str(&format!("permit {} as root\n", user.name)); + } + } + + std::fs::write("/mnt/etc/doas.conf", doas_conf).unwrap(); +} diff --git a/src/install/zram.rs b/src/install/zram.rs new file mode 100644 index 0000000..03724b4 --- /dev/null +++ b/src/install/zram.rs @@ -0,0 +1,23 @@ +use crate::{pkg::install_pkgs, run_command}; + +use super::str_vec; + +pub fn setup_zram() { + install_pkgs(&["zram-generator"]); + std::fs::write( + "/mnt/etc/systemd/zram-generator.conf", + include_str!("../root/zram-generator.conf"), + ) + .unwrap(); + run_command( + &str_vec(vec![ + "arch-chroot", + "/mnt", + "systemctl", + "enable", + "systemd-zram-setup@zram0.service", + ]), + None, + false, + ); +} diff --git a/src/main.rs b/src/main.rs index 73bf02e..fe3d06c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,13 +10,18 @@ mod install; mod pkg; use create_iso::create_iso; use install::install; +use yansi::{Color, Paint}; fn is_root() -> bool { getuid() == Uid::from_raw(0) } fn run_command(cmd: &[String], input: Option<&str>, inherit: bool) -> (String, String) { - println!("--> {}", cmd.join(" ")); + println!( + "{} {}", + "-->".paint(Color::Red), + cmd.join(" ").paint(Color::Blue.bold()) + ); let mut cmd_setup = std::process::Command::new(cmd[0].clone()); let mut cmd_setup = cmd_setup.args(cmd.into_iter().skip(1).collect::>()); @@ -62,7 +67,11 @@ fn run_command(cmd: &[String], input: Option<&str>, inherit: bool) -> (String, S } fn main() { - println!("⚠️ Warning: This is an alpha version of the installer. DO NOT USE in PROD"); + println!( + "{}", + "⚠️ Warning: This is an alpha version of the installer. DO NOT USE in PROD" + .paint(Color::Yellow) + ); let args = args::get_args(); @@ -83,12 +92,16 @@ fn main() { Ok(content) => match toml::from_str(&content) { Ok(config) => config, Err(e) => { - eprintln!("Error: Could not deserialize TOML file. {e}"); + eprintln!( + "{} {}", + "Error: Could not deserialize TOML file.".paint(Color::Red), + e.paint(Color::Red) + ); std::process::exit(1); } }, Err(_) => { - eprintln!("Error: Could not read config file."); + eprintln!("{}", "Error: Could not read config file.".paint(Color::Red)); std::process::exit(1); } }; @@ -104,19 +117,23 @@ fn main() { Ok(content) => match toml::from_str(&content) { Ok(config) => config, Err(e) => { - eprintln!("Error: Could not deserialize TOML file. {e}"); + eprintln!( + "{} {}", + "Error: Could not deserialize TOML file.".paint(Color::Red), + e.paint(Color::Red) + ); std::process::exit(1); } }, Err(_) => { - eprintln!("Error: Could not read config file."); + eprintln!("{}", "Error: Could not read config file.".paint(Color::Red)); std::process::exit(1); } }; // TODO : Show config println!("Config: {conf:?}"); - println!("\nDo you want to proceed with this configuration? (yes/no)"); + print!("\nDo you want to proceed with this configuration? (yes/no) "); let mut input = String::new(); std::io::stdout().flush().expect("Error flushing stdout."); diff --git a/src/pkg.rs b/src/pkg.rs index 73f2e84..c0579ff 100644 --- a/src/pkg.rs +++ b/src/pkg.rs @@ -1,4 +1,4 @@ -use crate::{install::str_vec, run_command}; +use crate::{config::PackageConfig, install::str_vec, run_command}; pub const DESKTOP_PKG: [&str; 2] = ["plasma", "sddm"]; @@ -12,3 +12,25 @@ pub fn install_pkgs(pkg: &[&str]) { run_command(&str_vec(cmd), None, true); } + +// PACSTRAP + +pub fn pacstrap(conf: &PackageConfig) { + let mut cmd: Vec = vec![ + "pacstrap".into(), + "-K".into(), + "/mnt".into(), + "base".into(), + "linux".into(), + "linux-firmware".into(), + "linux-headers".into(), + "git".into(), + "networkmanager".into(), + "nano".into(), + "doas".into(), + ]; + + cmd.extend(conf.pkg.clone()); + + run_command(&cmd, None, true); +}