diff --git a/installs/desktop.toml b/installs/desktop.toml index 8b09546..75c08ce 100644 --- a/installs/desktop.toml +++ b/installs/desktop.toml @@ -33,6 +33,12 @@ hostname = "navos" # Root password root_password = "root" +# Enable Bluetooth +bluetooth = true + +# GPU Video Drivers +gpu_driver = "NVIDIA" + [pkg] # Additional packages pkg = [ @@ -49,5 +55,5 @@ name = "u" # User password password = "pass" -# Allow user to use `doas` as root -doas_root= true +# Add user to wheel group +wheel = true diff --git a/installs/full.toml b/installs/full.toml index 02e9c43..85e39b8 100644 --- a/installs/full.toml +++ b/installs/full.toml @@ -33,6 +33,12 @@ hostname = "navos" # Root password root_password = "root" +# Enable Bluetooth +bluetooth = true + +# GPU Video Drivers +gpu_driver = "NVIDIA" + [pkg] # Additional packages pkg = [ @@ -66,8 +72,8 @@ home_dir = "/home/u" # Set the shell of the user shell = "/bin/bash" -# Allow user to use `doas` as root -doas_root= true +# Add user to wheel group +wheel = true # Add user to Docker group docker = true diff --git a/installs/server.toml b/installs/server.toml index b7c763c..67a33b4 100644 --- a/installs/server.toml +++ b/installs/server.toml @@ -52,8 +52,8 @@ name = "u" # User password password = "pass" -# Allow user to use `doas` as root -doas_root= true +# Add user to wheel group +wheel = true # Add user to Docker group docker = true diff --git a/src/config.rs b/src/config.rs index 4330d54..2b4b301 100644 --- a/src/config.rs +++ b/src/config.rs @@ -44,7 +44,7 @@ pub struct UserConfig { pub uid: Option, pub home_dir: Option, pub shell: Option, - pub doas_root: Option, + pub wheel: Option, pub docker: Option, pub virtualization: Option, } @@ -85,6 +85,17 @@ pub struct GeneralConfig { pub hostname: String, // Root password pub root_password: Option, + // Enable Bluetooth + pub bluetooth: Option, + /// Install Video Driver + pub gpu_driver: Option +} + +#[derive(Debug, Clone, Deserialize)] +pub enum GPUVendor { + AMD, + NVIDIA, + INTEL } #[derive(Debug, Clone, Deserialize)] diff --git a/src/install/bluetooth.rs b/src/install/bluetooth.rs new file mode 100644 index 0000000..689688d --- /dev/null +++ b/src/install/bluetooth.rs @@ -0,0 +1,8 @@ +use crate::{linux::systemd_service_enable, pkg::install_pkgs, print_status}; + +/// Enable Bluetooth +pub fn setup_bluetooth() { + print_status("Setting up Bluetooth"); + install_pkgs(&["bluez", "bluez-utils"]); + systemd_service_enable("bluetooth.service"); +} diff --git a/src/install/desktop.rs b/src/install/desktop.rs new file mode 100644 index 0000000..3b3b2d3 --- /dev/null +++ b/src/install/desktop.rs @@ -0,0 +1,29 @@ +use crate::{ + config::InstallConfig, + linux::install_file, + pkg::{self, install_pkgs}, + print_status, +}; + +use super::navos::setup_navos; + +pub fn setup_desktop(conf: &InstallConfig) { + setup_navos(); + install_pkgs(&pkg::DESKTOP_PKG); + print_status("Enable SDDM"); + print_status("Set keyboard layout for SDDM"); + install_file( + "/mnt/usr/share/sddm/scripts/Xsetup", + &format!( + "#!/bin/sh\n# Xsetup\nsetxkbmap {},us\n", + conf.general.keyboard_layout + ), + 0o644, + ); + + std::os::unix::fs::symlink( + "/usr/lib/systemd/system/sddm.service", + "/mnt/etc/systemd/system/display-manager.service", + ) + .unwrap(); +} diff --git a/src/install/firmware.rs b/src/install/firmware.rs new file mode 100644 index 0000000..7bbd09e --- /dev/null +++ b/src/install/firmware.rs @@ -0,0 +1,24 @@ +use crate::{linux::systemd_service_enable, pkg::install_pkgs, print_status}; + +pub fn setup_fwupd() { + print_status("Enabling firmware updates"); + install_pkgs(&["fwupd"]); + systemd_service_enable("fwupd-refresh.timer"); +} + +pub fn setup_microcode() { + print_status("Installing CPU Microcode"); + let cpuinfo = std::fs::read_to_string("/proc/cpuinfo").unwrap(); + + for line in cpuinfo.lines() { + if line.starts_with("vendor_id") { + if line.contains("GenuineIntel") { + install_pkgs(&["intel-ucode"]); + } + + if line.contains("AuthenticAMD") { + install_pkgs(&["amd-ucode"]); + } + } + } +} diff --git a/src/install/first_boot.rs b/src/install/first_boot.rs index 6085b23..d59bcf6 100644 --- a/src/install/first_boot.rs +++ b/src/install/first_boot.rs @@ -2,7 +2,7 @@ use crate::{ config::GeneralConfig, - linux::{arch_chroot, run_command, systemd_service_enable}, + linux::{arch_chroot, install_file, run_command, systemd_service_enable}, print_status, }; @@ -45,6 +45,8 @@ pub fn first_boot_values(conf: &GeneralConfig) { print_status("Writing /etc/hostname"); std::fs::write("/mnt/etc/hostname", format!("{}\n", conf.hostname)).unwrap(); + install_file("/mnt/etc/hosts", &format!("127.0.0.1 localhost\n::1 localhost\n127.0.1.1 {}\n", conf.hostname), 0o644); + // LOCALE print_status("Setting locale"); uncomment_first_value_of(&conf.locale, "/mnt/etc/locale.gen"); diff --git a/src/install/gpu.rs b/src/install/gpu.rs new file mode 100644 index 0000000..73e5cca --- /dev/null +++ b/src/install/gpu.rs @@ -0,0 +1,15 @@ +use crate::{config::GPUVendor, pkg::install_pkgs}; + +pub fn setup_video_drivers(vendor: &GPUVendor) { + match vendor { + GPUVendor::AMD => { + install_pkgs(&["xf86-video-amdgpu", "mesa", "lib32-mesa", "vulkan-radeon", "lib32-vulkan-radeon"]); + }, + GPUVendor::NVIDIA => { + install_pkgs(&["nvidia", "nvidia-utils", "lib32-nvidia-utils"]); + }, + GPUVendor::INTEL => { + install_pkgs(&["xf86-video-intel2", "mesa", "lib32-mesa", "vulkan-intel", "lib32-vulkan-intel"]); + }, + } +} diff --git a/src/install/mod.rs b/src/install/mod.rs index 228483e..3ac2c15 100644 --- a/src/install/mod.rs +++ b/src/install/mod.rs @@ -1,12 +1,17 @@ // TODO : Autojoin docker swarm // TODO : Autojoin teleport +// TODO : Firewall // DRIVE SELECTION +use bluetooth::setup_bluetooth; use boot::setup_bootloader; +use desktop::setup_desktop; use docker::setup_docker; use drives::{format_drives, mount_drives}; +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; @@ -14,10 +19,13 @@ use security::{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 first_boot; @@ -28,13 +36,14 @@ pub mod security; pub mod skel; pub mod ssh; pub mod user; +pub mod virt; pub mod zram; +pub mod firmware; +pub mod gpu; use crate::{ - config::{InstallConfig, InstallMode}, - linux::{arch_chroot, install_file, systemd_service_enable}, + config::InstallConfig, pkg::{self, install_pkgs, pacstrap}, - print_status, }; pub fn uncomment_first_value_of(value: &str, file: &str) { @@ -91,31 +100,14 @@ pub fn install(conf: InstallConfig) { setup_skel(&conf.general); setup_users(&conf.user.as_ref().unwrap_or(&Vec::new())); - setup_ssh(conf.ssh); + setup_ssh(&conf.ssh); setup_bootloader(); match conf.general.mode { crate::config::InstallMode::Base => {} crate::config::InstallMode::Desktop => { - setup_navos(); - install_pkgs(&pkg::DESKTOP_PKG); - print_status("Enable SDDM"); - print_status("Set keyboard layout for SDDM"); - install_file( - "/mnt/usr/share/sddm/scripts/Xsetup", - &format!( - "#!/bin/sh\n# Xsetup\nsetxkbmap {},us\n", - conf.general.keyboard_layout - ), - 0o644, - ); - - std::os::unix::fs::symlink( - "/usr/lib/systemd/system/sddm.service", - "/mnt/etc/systemd/system/display-manager.service", - ) - .unwrap(); + setup_desktop(&conf); } crate::config::InstallMode::Server => { setup_navos(); @@ -127,29 +119,7 @@ pub fn install(conf: InstallConfig) { } if conf.pkg.virtualization.unwrap_or_default() { - let user_conf = if let Some(user_conf) = &conf.user { - user_conf.clone() - } else { - Vec::new() - }; - - install_pkgs(&["libvirt"]); - - 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_virtualization(&conf); } if conf.pkg.docker.unwrap_or_default() { @@ -167,6 +137,17 @@ pub fn install(conf: InstallConfig) { } setup_zram(); + + if conf.general.bluetooth.unwrap_or_default() { + setup_bluetooth(); + } + + setup_vm(); + if let Some(gpu_vendor) = &conf.general.gpu_driver { + setup_video_drivers(gpu_vendor); + } + setup_fwupd(); + setup_microcode(); setup_mkinitcpio(&conf.drive); setup_secure_boot(); diff --git a/src/install/ssh.rs b/src/install/ssh.rs index f1685de..1157df9 100644 --- a/src/install/ssh.rs +++ b/src/install/ssh.rs @@ -4,7 +4,7 @@ use std::io::Write; /// Setup SSH on the system /// /// This should be done after `setup_users()` to ensure that the users directories exist. -pub fn setup_ssh(conf: Option) { +pub fn setup_ssh(conf: &Option) { if let Some(conf) = conf { install_pkgs(&["openssh"]); @@ -14,7 +14,7 @@ pub fn setup_ssh(conf: Option) { install_file("/mnt/etc/ssh/sshd_config", &content, 0o644); } - for key in &conf.key.unwrap_or_default() { + for key in conf.key.as_ref().unwrap_or(&Vec::new()) { for user in &key.users { let path = if user == "root" { std::fs::create_dir_all("/root/.ssh").unwrap(); diff --git a/src/install/user.rs b/src/install/user.rs index 25ed68f..b463a74 100644 --- a/src/install/user.rs +++ b/src/install/user.rs @@ -1,7 +1,5 @@ use crate::{ - config::UserConfig, - linux::{arch_chroot, install_file}, - print_status, + config::UserConfig, linux::{arch_chroot, install_file}, pkg::install_pkgs, print_status }; pub fn change_passwd(user: &str, pw: &str) { @@ -10,7 +8,10 @@ pub fn change_passwd(user: &str, pw: &str) { /// Setup the users of the system pub fn setup_users(conf: &[UserConfig]) { - let mut doas_conf = String::new(); + if !conf.is_empty() { + install_pkgs(&["doas"]); + install_file("/mnt/etc/doas.conf", "permit persist :wheel as root", 0o644); + } for user in conf { let mut cmd = vec!["useradd"]; @@ -45,11 +46,13 @@ pub fn setup_users(conf: &[UserConfig]) { change_passwd(&user.name, &user.password); - if user.doas_root.unwrap_or_default() { - print_status(&format!("Allowing root doas for {}", user.name)); - doas_conf.push_str(&format!("permit {} as root\n", user.name)); + if user.wheel.unwrap_or_default() { + print_status(&format!("Adding {} to wheel", user.name)); + arch_chroot( + &vec!["usermod", "-a", "-G", "wheel", user.name.as_str()], + None, + false, + ); } } - - install_file("/mnt/etc/doas.conf", &doas_conf, 0o644); } diff --git a/src/install/virt.rs b/src/install/virt.rs new file mode 100644 index 0000000..78757fe --- /dev/null +++ b/src/install/virt.rs @@ -0,0 +1,35 @@ +use crate::{ + config::{InstallConfig, InstallMode}, + linux::{arch_chroot, systemd_service_enable}, + pkg::install_pkgs, +}; + +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"]); + + 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, + ); + } + } +} + +pub fn setup_vm() { + // TODO : Install guest tools if in virt with systemd-detect-virt +} diff --git a/src/linux.rs b/src/linux.rs index d6fd2fc..3711f80 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -75,6 +75,7 @@ pub fn install_file(path: &str, content: &str, permissions: u32) { file.write_all(content.as_bytes()).unwrap(); let permissions = std::fs::Permissions::from_mode(permissions); + // TODO : Fix permission format print_status(&format!("Wrote file {path} [{permissions:#?}]")); std::fs::set_permissions(path, permissions).unwrap(); } diff --git a/src/main.rs b/src/main.rs index bdf8902..986bd0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,20 +97,20 @@ pub fn read_conf(config_file: &str) -> InstallConfig { let config_content = std::fs::read_to_string(config_file); match config_content { - Ok(content) => match toml::from_str(&content) { - Ok(config) => config, - Err(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.".paint(Color::Red)); - std::process::exit(1); - } + Ok(content) => match toml::from_str(&content) { + Ok(config) => config, + Err(e) => { + eprintln!( + "{} {}", + "Error: Could not deserialize TOML file.".paint(Color::Red), + e.paint(Color::Red) + ); + std::process::exit(1); } -} \ No newline at end of file + }, + Err(_) => { + eprintln!("{}", "Error: Could not read config file.".paint(Color::Red)); + std::process::exit(1); + } + } +} diff --git a/src/pkg.rs b/src/pkg.rs index 1fb60a3..479412e 100644 --- a/src/pkg.rs +++ b/src/pkg.rs @@ -3,7 +3,31 @@ use crate::{ linux::{arch_chroot, run_command}, }; -pub const DESKTOP_PKG: [&str; 5] = ["plasma", "sddm", "konsole", "dolphin", "navos/navos"]; +pub const DESKTOP_PKG: [&str; 16] = [ + // Desktop + "plasma", + "sddm", + + // Sound + "pipewire", + "pipewire-alsa", + "pipewire-pulse", + "pipewire-jack", + "wireplumber", + + // Applications + "konsole", + "dolphin", + "ffmpegthumbs", + "kate", + "okular", + "gwenview", + "ark", + "flatpak", + + // Misc + "navos/navos" +]; pub const SERVER_PKG: [&str; 2] = ["tmux", "navos/navos"]; @@ -32,7 +56,11 @@ pub fn pacstrap(conf: &PackageConfig) { "git", "networkmanager", "nano", - "doas", + "openssh", + "zsh", + "zsh-completions", + "zsh-autosuggestions", + "man" ]; cmd.extend( diff --git a/src/print.rs b/src/print.rs index 24cf4e1..efa6877 100644 --- a/src/print.rs +++ b/src/print.rs @@ -58,11 +58,17 @@ pub fn print_config(conf: &InstallConfig) { conf.general.timezone )); if conf.general.root_password.is_some() { - general_info.add_str(format!( - "🔑 {} {}", - "Root Password".paint(Color::Yellow), - "✔️".paint(Color::Green) - )); + general_info.add_str(format!("🔑 Root Password {}", "✔️".paint(Color::Green))); + } + if conf.general.bluetooth.unwrap_or_default() { + general_info.add_str(format!("🌀 Bluetooth {}", "✔️".paint(Color::Green))); + } + if let Some(vendor) = &conf.general.gpu_driver { + match vendor { + crate::config::GPUVendor::AMD => general_info.add_str(format!("🟥 AMD GPU {}", "✔️".paint(Color::Green))), + crate::config::GPUVendor::INTEL => general_info.add_str(format!("🟦 Intel GPU {}", "✔️".paint(Color::Green))), + crate::config::GPUVendor::NVIDIA => general_info.add_str(format!("🟩 NVIDIA GPU {}", "✔️".paint(Color::Green))), + } } root_info.add_tree("🔨 General", general_info); @@ -79,7 +85,8 @@ pub fn print_config(conf: &InstallConfig) { if !conf.pkg.pkg.is_empty() { pkg_info.add_str(format!( - "📦 Additional packages: {}", + "📦 {} {}", + "Additional packages:".paint(Color::Yellow), conf.pkg.pkg.join(" ") )); } @@ -94,7 +101,7 @@ pub fn print_config(conf: &InstallConfig) { for user in user_conf { let mut groups = Vec::new(); - if user.doas_root.unwrap_or_default() { + if user.wheel.unwrap_or_default() { groups.push("🔑"); } @@ -145,7 +152,11 @@ pub fn print_config(conf: &InstallConfig) { } if let Some(models) = &ai_conf.models { - ai_info.add_str(format!("⬇️ Pull Models: {}", models.join(", "))); + ai_info.add_str(format!( + "⬇️ {} {}", + "Pull Models:".paint(Color::Yellow), + models.join(", ") + )); } root_info.add_tree("🦙 Ollama", ai_info);