Compare commits
29 Commits
daa806a8cb
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
a52f066c52
|
|||
|
9af9530fbf
|
|||
|
d210c4a104
|
|||
|
5d573f83db
|
|||
|
9fe29ea903
|
|||
|
e8f69eed45
|
|||
|
4e242233fb
|
|||
|
2e6ed2acbf
|
|||
|
8c2272bdc4
|
|||
|
dd9f8785c0
|
|||
|
0aeb3163a5
|
|||
|
1e7fe78d66
|
|||
|
75880897af
|
|||
|
f69d807756
|
|||
|
58736ce5da
|
|||
|
dbaf14718d
|
|||
|
543198e730
|
|||
|
14f7b610bb
|
|||
|
63833f6da3
|
|||
|
985ecd76a4
|
|||
|
f6fe732b4b
|
|||
|
6b70ce8a97
|
|||
|
f8f2d5a3ce
|
|||
|
a3237ff8a4
|
|||
|
e6d4769956
|
|||
|
a066063f6a
|
|||
|
cfd6b2455f
|
|||
|
8f15fbe003
|
|||
|
8413c4d7ac
|
65
README.md
Normal file
65
README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# LogalDeveloper's Arch Linux Installer
|
||||
|
||||
An automated Arch Linux installer which reflects my personal preferences.
|
||||
|
||||
## Features
|
||||
|
||||
### Options
|
||||
|
||||
- **Storage**: BTRFS, BTRFS with data duplication, BTRFS RAID1 (two disks), or ext4
|
||||
- **Networking**: Optional iwd for Wi-Fi
|
||||
- **Profiles**:
|
||||
- Minimal: Base system only
|
||||
- Basic Desktop: KDE Plasma with Chromium and multimedia
|
||||
- Office Workstation: KDE Plasma with full productivity suite
|
||||
|
||||
### Defaults
|
||||
|
||||
- **Full-disk encryption**: LUKS2 with modern key derivation
|
||||
- **Bootloader**: systemd-boot
|
||||
- **Networking**: systemd-networkd and systemd-resolved
|
||||
- **Firewall**: nftables with default inbound deny (except SSH) and default outbound allow
|
||||
- **SSH**: OpenSSH configured with modern algorithms only
|
||||
- **USB**: USBGuard for device whitelisting
|
||||
- **Users**: Root account disabled; sudo via wheel group
|
||||
- **Hardware**: Automatic CPU microcode installation (Intel/AMD), SSD TRIM, fwupd for firmware updates
|
||||
- **Performance**: TCP BBR congestion control
|
||||
- **Maintenance**: SMART monitoring, BTRFS scrub timer (when applicable)
|
||||
- **Mirror**: [My personal Arch Linux mirror](https://logal.dev/projects/arch-linux-mirror/)
|
||||
- **DNS**: No fallback DNS (must be provided via DHCP or static config), LLMNR and mDNS disabled
|
||||
- **CA**: LogalNet internal CA pre-installed
|
||||
- **Logging**: Installation log saved to `/var/log/arch-install.log`
|
||||
|
||||
### KDE Defaults
|
||||
|
||||
- **Audio**: PipeWire audio stack
|
||||
- **Browser**: Chromium with Vulkan and hardware video acceleration
|
||||
- **Wallet**: KWallet with PAM integration for auto-unlock on login
|
||||
|
||||
## Requirements
|
||||
|
||||
- Arch Linux live ISO
|
||||
- EFI-capable system
|
||||
- Internet connectivity
|
||||
- One disk for standard installation, two disks for RAID1
|
||||
|
||||
## Usage
|
||||
|
||||
Boot into the Arch Linux live ISO, then run:
|
||||
|
||||
```bash
|
||||
pacman -Sy git
|
||||
git clone https://git.logal.dev/LogalDeveloper/Arch-Linux-Installer.git
|
||||
cd Arch-Linux-Installer
|
||||
./install-arch-linux.sh
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
Profiles and package lists are defined in `config/profiles.conf`. Custom CA certificates can be added to `files/certs/`.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This installer is provided as-is. It is designed around my specific use case and probably makes assumptions that don't fit all hardware or configurations.
|
||||
|
||||
**Use at your own risk.** Always have backups before installing any operating system.
|
||||
58
config/defaults.conf
Normal file
58
config/defaults.conf
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# defaults.conf - Default configuration values for the Arch Linux Installer
|
||||
#
|
||||
# Central configuration file for paths, URLs, and base packages.
|
||||
|
||||
# Installer identification
|
||||
INSTALLER_NAME="LogalDeveloper's Arch Linux Installer"
|
||||
|
||||
# Network configuration
|
||||
INTERNET_CHECK_URL="https://logal.dev/"
|
||||
MIRROR_URL='https://mirrors.logal.dev/archlinux/$repo/os/$arch'
|
||||
|
||||
# Paths
|
||||
CA_CERTS_DIR="${SCRIPT_DIR}/files/certs"
|
||||
CONFIG_SRC_DIR="${SCRIPT_DIR}/files/etc"
|
||||
HOME_SKEL_DIR="${SCRIPT_DIR}/files/home-skel"
|
||||
HOME_SKEL_DESKTOP_DIR="${SCRIPT_DIR}/files/home-skel-desktop"
|
||||
MOUNT_POINT="/mnt"
|
||||
|
||||
# Base packages to install with pacstrap
|
||||
BASE_PACKAGES=(
|
||||
base
|
||||
linux
|
||||
linux-firmware
|
||||
bash-completion
|
||||
btrfs-progs
|
||||
arch-install-scripts
|
||||
smartmontools
|
||||
lm_sensors
|
||||
man-db
|
||||
btop
|
||||
htop
|
||||
nano
|
||||
less
|
||||
tmux
|
||||
rsync
|
||||
sudo
|
||||
iptables-nft
|
||||
openssh
|
||||
usbguard
|
||||
7zip
|
||||
fwupd
|
||||
)
|
||||
34
config/drivers.conf
Normal file
34
config/drivers.conf
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# drivers.conf - Graphics driver package definitions
|
||||
#
|
||||
# Defines package arrays for graphics driver installation.
|
||||
|
||||
# Intel graphics packages
|
||||
INTEL_PACKAGES=(
|
||||
mesa
|
||||
vulkan-intel
|
||||
intel-media-driver
|
||||
libva-intel-driver
|
||||
)
|
||||
|
||||
# NVIDIA graphics packages
|
||||
NVIDIA_PACKAGES=(
|
||||
mesa
|
||||
nvidia-open
|
||||
libva-nvidia-driver
|
||||
)
|
||||
28
config/luks.conf
Normal file
28
config/luks.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# luks.conf - LUKS encryption parameters
|
||||
#
|
||||
# Cryptographic settings for full-disk encryption using LUKS2.
|
||||
|
||||
LUKS_TYPE="luks2"
|
||||
LUKS_CIPHER="aes-xts-plain64"
|
||||
LUKS_HASH="sha512"
|
||||
LUKS_KEY_SIZE="512"
|
||||
LUKS_PBKDF="argon2id"
|
||||
LUKS_PBKDF_ITERATIONS="8"
|
||||
LUKS_PBKDF_MEMORY="4194304"
|
||||
LUKS_PBKDF_PARALLEL="4"
|
||||
169
config/profiles.conf
Normal file
169
config/profiles.conf
Normal file
@@ -0,0 +1,169 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# profiles.conf - Profile and desktop environment package definitions
|
||||
#
|
||||
# Defines KDE packages and installation profiles. Adding a new profile
|
||||
# requires only adding entries here - no code changes needed.
|
||||
|
||||
#===============================================================================
|
||||
# DESKTOP ENVIRONMENT PACKAGES
|
||||
#===============================================================================
|
||||
|
||||
# KDE Plasma base packages
|
||||
KDE_PACKAGES=(
|
||||
# Display manager
|
||||
sddm
|
||||
|
||||
# Core Plasma
|
||||
plasma-desktop
|
||||
plasma-pa
|
||||
kscreen
|
||||
|
||||
# Multimedia backend
|
||||
phonon-qt6-vlc
|
||||
vlc-plugin-ffmpeg
|
||||
ffmpeg
|
||||
|
||||
# System settings
|
||||
kinfocenter
|
||||
sddm-kcm
|
||||
kde-gtk-config
|
||||
breeze-gtk
|
||||
power-profiles-daemon
|
||||
|
||||
# Essential applications
|
||||
konsole
|
||||
dolphin
|
||||
kate
|
||||
ark
|
||||
spectacle
|
||||
gwenview
|
||||
kcalc
|
||||
|
||||
# Browser and media
|
||||
chromium
|
||||
haruna
|
||||
elisa
|
||||
okular
|
||||
|
||||
# File management
|
||||
filelight
|
||||
kfind
|
||||
|
||||
# Text and diff tools
|
||||
kompare
|
||||
|
||||
# Security
|
||||
plasma-vault
|
||||
kleopatra
|
||||
kwalletmanager
|
||||
kwallet-pam
|
||||
|
||||
# Accessibility
|
||||
kmousetool
|
||||
|
||||
# Utilities
|
||||
kcolorchooser
|
||||
kcharselect
|
||||
kalarm
|
||||
sweeper
|
||||
kdialog
|
||||
|
||||
# Thumbnails and previews
|
||||
kdegraphics-thumbnailers
|
||||
ffmpegthumbs
|
||||
markdownpart
|
||||
svgpart
|
||||
|
||||
# Audio
|
||||
pipewire
|
||||
pipewire-alsa
|
||||
pipewire-pulse
|
||||
pipewire-jack
|
||||
wireplumber
|
||||
|
||||
# Fonts
|
||||
noto-fonts
|
||||
noto-fonts-cjk
|
||||
noto-fonts-emoji
|
||||
noto-fonts-extra
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# PROFILE DEFINITIONS
|
||||
#===============================================================================
|
||||
|
||||
# Profile registry - order determines menu numbering
|
||||
PROFILES=(minimal basic office)
|
||||
|
||||
# Each profile defines:
|
||||
# PROFILE_<key>_NAME - Display name
|
||||
# PROFILE_<key>_DESC - Description shown in menu
|
||||
# PROFILE_<key>_KDE - Whether to install KDE (true/false)
|
||||
# PROFILE_<key>_PACKAGES - Package array
|
||||
# PROFILE_<key>_SERVICES - Services to enable array
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Profile: Minimal
|
||||
#---------------------------------------------------------------------------
|
||||
PROFILE_minimal_NAME="Minimal"
|
||||
PROFILE_minimal_DESC="Base Arch Linux system, no additional packages."
|
||||
PROFILE_minimal_KDE=false
|
||||
PROFILE_minimal_PACKAGES=()
|
||||
PROFILE_minimal_SERVICES=()
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Profile: Basic Desktop
|
||||
#---------------------------------------------------------------------------
|
||||
PROFILE_basic_NAME="Basic Desktop"
|
||||
PROFILE_basic_DESC="KDE Plasma with Chromium and multimedia applications."
|
||||
PROFILE_basic_KDE=true
|
||||
PROFILE_basic_PACKAGES=()
|
||||
PROFILE_basic_SERVICES=()
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Profile: Office Workstation
|
||||
#---------------------------------------------------------------------------
|
||||
PROFILE_office_NAME="Office Workstation"
|
||||
PROFILE_office_DESC="KDE Plasma with office and productivity applications."
|
||||
PROFILE_office_KDE=true
|
||||
PROFILE_office_PACKAGES=(
|
||||
# Office and documents
|
||||
libreoffice-fresh
|
||||
hunspell-en_us
|
||||
|
||||
# Graphics and multimedia
|
||||
gimp
|
||||
kdenlive
|
||||
kid3
|
||||
tenacity
|
||||
|
||||
# Communication
|
||||
neochat
|
||||
tokodon
|
||||
akregator
|
||||
|
||||
# Utilities
|
||||
keepassxc
|
||||
syncthing
|
||||
gnucash
|
||||
git
|
||||
|
||||
# Network analysis
|
||||
wireshark-qt
|
||||
)
|
||||
PROFILE_office_SERVICES=()
|
||||
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.1" encoding="UTF-8"?>
|
||||
|
||||
<channel name="thunar" version="1.0">
|
||||
<property name="last-view" type="string" value="ThunarDetailsView"/>
|
||||
<property name="misc-single-click" type="bool" value="false"/>
|
||||
<property name="last-icon-view-zoom-level" type="string" value="THUNAR_ZOOM_LEVEL_100_PERCENT"/>
|
||||
<property name="misc-change-window-icon" type="bool" value="false"/>
|
||||
<property name="default-view" type="string" value="ThunarDetailsView"/>
|
||||
<property name="last-details-view-zoom-level" type="string" value="THUNAR_ZOOM_LEVEL_38_PERCENT"/>
|
||||
<property name="last-separator-position" type="int" value="170"/>
|
||||
<property name="last-details-view-column-widths" type="string" value="50,50,141,360,141,219,50,50,710,50,111,69,50,78"/>
|
||||
<property name="misc-file-size-binary" type="bool" value="true"/>
|
||||
<property name="misc-thumbnail-mode" type="string" value="THUNAR_THUMBNAIL_MODE_NEVER"/>
|
||||
<property name="misc-thumbnail-max-file-size" type="uint64" value="524288"/>
|
||||
<property name="misc-date-style" type="string" value="THUNAR_DATE_STYLE_SHORT"/>
|
||||
<property name="shortcuts-icon-emblems" type="bool" value="true"/>
|
||||
<property name="tree-icon-emblems" type="bool" value="true"/>
|
||||
<property name="misc-image-preview-mode" type="string" value="THUNAR_IMAGE_PREVIEW_MODE_STANDALONE"/>
|
||||
<property name="misc-full-path-in-tab-title" type="bool" value="false"/>
|
||||
<property name="misc-exec-shell-scripts-by-default" type="string" value="THUNAR_EXECUTE_SHELL_SCRIPT_NEVER"/>
|
||||
<property name="misc-volume-management" type="bool" value="false"/>
|
||||
<property name="last-window-maximized" type="bool" value="true"/>
|
||||
<property name="hidden-bookmarks" type="array">
|
||||
<value type="string" value="network:///"/>
|
||||
<value type="string" value="computer:///"/>
|
||||
</property>
|
||||
<property name="last-details-view-visible-columns" type="string" value="THUNAR_COLUMN_DATE_MODIFIED,THUNAR_COLUMN_NAME,THUNAR_COLUMN_PERMISSIONS,THUNAR_COLUMN_SIZE,THUNAR_COLUMN_TYPE"/>
|
||||
<property name="last-window-width" type="int" value="640"/>
|
||||
<property name="last-window-height" type="int" value="480"/>
|
||||
<property name="last-sort-column" type="string" value="THUNAR_COLUMN_DATE_MODIFIED"/>
|
||||
<property name="last-sort-order" type="string" value="GTK_SORT_DESCENDING"/>
|
||||
</channel>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.1" encoding="UTF-8"?>
|
||||
|
||||
<channel name="xfce4-desktop" version="1.0">
|
||||
<property name="last-settings-migration-version" type="uint" value="1"/>
|
||||
<property name="windowlist-menu" type="empty">
|
||||
<property name="show" type="bool" value="false"/>
|
||||
</property>
|
||||
<property name="desktop-menu" type="empty">
|
||||
<property name="show" type="bool" value="false"/>
|
||||
<property name="show-delete" type="bool" value="false"/>
|
||||
</property>
|
||||
<property name="desktop-icons" type="empty">
|
||||
<property name="show-thumbnails" type="bool" value="false"/>
|
||||
<property name="file-icons" type="empty">
|
||||
<property name="show-filesystem" type="bool" value="false"/>
|
||||
</property>
|
||||
</property>
|
||||
<property name="last" type="empty">
|
||||
<property name="window-width" type="int" value="640"/>
|
||||
<property name="window-height" type="int" value="559"/>
|
||||
</property>
|
||||
</channel>
|
||||
@@ -1,196 +0,0 @@
|
||||
<?xml version="1.1" encoding="UTF-8"?>
|
||||
|
||||
<channel name="xfce4-keyboard-shortcuts" version="1.0">
|
||||
<property name="commands" type="empty">
|
||||
<property name="default" type="empty">
|
||||
<property name="<Alt>F1" type="empty"/>
|
||||
<property name="<Alt>F2" type="empty">
|
||||
<property name="startup-notify" type="empty"/>
|
||||
</property>
|
||||
<property name="<Alt>F3" type="empty">
|
||||
<property name="startup-notify" type="empty"/>
|
||||
</property>
|
||||
<property name="<Primary><Alt>Delete" type="empty"/>
|
||||
<property name="<Primary><Alt>l" type="empty"/>
|
||||
<property name="<Primary><Alt>t" type="empty"/>
|
||||
<property name="XF86Display" type="empty"/>
|
||||
<property name="<Super>p" type="empty"/>
|
||||
<property name="<Primary>Escape" type="empty"/>
|
||||
<property name="XF86WWW" type="empty"/>
|
||||
<property name="HomePage" type="empty"/>
|
||||
<property name="XF86Mail" type="empty"/>
|
||||
<property name="Print" type="empty"/>
|
||||
<property name="<Alt>Print" type="empty"/>
|
||||
<property name="<Shift>Print" type="empty"/>
|
||||
<property name="<Super>e" type="empty"/>
|
||||
<property name="<Primary><Alt>f" type="empty"/>
|
||||
<property name="<Primary><Alt>Escape" type="empty"/>
|
||||
<property name="<Primary><Shift>Escape" type="empty"/>
|
||||
<property name="<Super>r" type="empty">
|
||||
<property name="startup-notify" type="empty"/>
|
||||
</property>
|
||||
<property name="<Alt><Super>s" type="empty"/>
|
||||
</property>
|
||||
<property name="custom" type="empty">
|
||||
<property name="<Alt>F2" type="string" value="xfce4-appfinder --collapsed">
|
||||
<property name="startup-notify" type="bool" value="true"/>
|
||||
</property>
|
||||
<property name="<Alt>Print" type="string" value="xfce4-screenshooter -w"/>
|
||||
<property name="<Super>r" type="string" value="xfce4-appfinder -c">
|
||||
<property name="startup-notify" type="bool" value="true"/>
|
||||
</property>
|
||||
<property name="XF86WWW" type="string" value="exo-open --launch WebBrowser"/>
|
||||
<property name="XF86Mail" type="string" value="exo-open --launch MailReader"/>
|
||||
<property name="<Alt>F3" type="string" value="xfce4-appfinder">
|
||||
<property name="startup-notify" type="bool" value="true"/>
|
||||
</property>
|
||||
<property name="Print" type="string" value="xfce4-screenshooter"/>
|
||||
<property name="<Primary>Escape" type="string" value="xfdesktop --menu"/>
|
||||
<property name="<Shift>Print" type="string" value="xfce4-screenshooter -r"/>
|
||||
<property name="<Primary><Alt>Delete" type="string" value="xfce4-session-logout"/>
|
||||
<property name="<Alt><Super>s" type="string" value="orca"/>
|
||||
<property name="<Primary><Alt>t" type="string" value="exo-open --launch TerminalEmulator"/>
|
||||
<property name="<Primary><Alt>f" type="string" value="thunar"/>
|
||||
<property name="<Primary><Alt>l" type="string" value="xflock4"/>
|
||||
<property name="<Alt>F1" type="string" value="xfce4-popup-applicationsmenu"/>
|
||||
<property name="<Super>p" type="string" value="xfce4-display-settings --minimal"/>
|
||||
<property name="<Primary><Shift>Escape" type="string" value="xfce4-taskmanager"/>
|
||||
<property name="<Super>e" type="string" value="thunar"/>
|
||||
<property name="<Primary><Alt>Escape" type="string" value="xkill"/>
|
||||
<property name="HomePage" type="string" value="exo-open --launch WebBrowser"/>
|
||||
<property name="XF86Display" type="string" value="xfce4-display-settings --minimal"/>
|
||||
<property name="override" type="bool" value="true"/>
|
||||
</property>
|
||||
</property>
|
||||
<property name="xfwm4" type="empty">
|
||||
<property name="default" type="empty">
|
||||
<property name="<Alt>Insert" type="empty"/>
|
||||
<property name="Escape" type="empty"/>
|
||||
<property name="Left" type="empty"/>
|
||||
<property name="Right" type="empty"/>
|
||||
<property name="Up" type="empty"/>
|
||||
<property name="Down" type="empty"/>
|
||||
<property name="<Alt>Tab" type="empty"/>
|
||||
<property name="<Alt><Shift>Tab" type="empty"/>
|
||||
<property name="<Alt>Delete" type="empty"/>
|
||||
<property name="<Primary><Alt>Down" type="empty"/>
|
||||
<property name="<Primary><Alt>Left" type="empty"/>
|
||||
<property name="<Shift><Alt>Page_Down" type="empty"/>
|
||||
<property name="<Alt>F4" type="empty"/>
|
||||
<property name="<Alt>F6" type="empty"/>
|
||||
<property name="<Alt>F7" type="empty"/>
|
||||
<property name="<Alt>F8" type="empty"/>
|
||||
<property name="<Alt>F9" type="empty"/>
|
||||
<property name="<Alt>F10" type="empty"/>
|
||||
<property name="<Alt>F11" type="empty"/>
|
||||
<property name="<Alt>F12" type="empty"/>
|
||||
<property name="<Primary><Shift><Alt>Left" type="empty"/>
|
||||
<property name="<Primary><Alt>End" type="empty"/>
|
||||
<property name="<Primary><Alt>Home" type="empty"/>
|
||||
<property name="<Primary><Shift><Alt>Right" type="empty"/>
|
||||
<property name="<Primary><Shift><Alt>Up" type="empty"/>
|
||||
<property name="<Primary><Alt>KP_1" type="empty"/>
|
||||
<property name="<Primary><Alt>KP_2" type="empty"/>
|
||||
<property name="<Primary><Alt>KP_3" type="empty"/>
|
||||
<property name="<Primary><Alt>KP_4" type="empty"/>
|
||||
<property name="<Primary><Alt>KP_5" type="empty"/>
|
||||
<property name="<Primary><Alt>KP_6" type="empty"/>
|
||||
<property name="<Primary><Alt>KP_7" type="empty"/>
|
||||
<property name="<Primary><Alt>KP_8" type="empty"/>
|
||||
<property name="<Primary><Alt>KP_9" type="empty"/>
|
||||
<property name="<Alt>space" type="empty"/>
|
||||
<property name="<Shift><Alt>Page_Up" type="empty"/>
|
||||
<property name="<Primary><Alt>Right" type="empty"/>
|
||||
<property name="<Primary><Alt>d" type="empty"/>
|
||||
<property name="<Primary><Alt>Up" type="empty"/>
|
||||
<property name="<Super>Tab" type="empty"/>
|
||||
<property name="<Primary>F1" type="empty"/>
|
||||
<property name="<Primary>F2" type="empty"/>
|
||||
<property name="<Primary>F3" type="empty"/>
|
||||
<property name="<Primary>F4" type="empty"/>
|
||||
<property name="<Primary>F5" type="empty"/>
|
||||
<property name="<Primary>F6" type="empty"/>
|
||||
<property name="<Primary>F7" type="empty"/>
|
||||
<property name="<Primary>F8" type="empty"/>
|
||||
<property name="<Primary>F9" type="empty"/>
|
||||
<property name="<Primary>F10" type="empty"/>
|
||||
<property name="<Primary>F11" type="empty"/>
|
||||
<property name="<Primary>F12" type="empty"/>
|
||||
<property name="<Super>KP_Left" type="empty"/>
|
||||
<property name="<Super>KP_Right" type="empty"/>
|
||||
<property name="<Super>KP_Down" type="empty"/>
|
||||
<property name="<Super>KP_Up" type="empty"/>
|
||||
<property name="<Super>KP_Page_Up" type="empty"/>
|
||||
<property name="<Super>KP_Home" type="empty"/>
|
||||
<property name="<Super>KP_End" type="empty"/>
|
||||
<property name="<Super>KP_Next" type="empty"/>
|
||||
</property>
|
||||
<property name="custom" type="empty">
|
||||
<property name="<Primary>F12" type="string" value="workspace_12_key"/>
|
||||
<property name="<Super>KP_Down" type="string" value="tile_down_key"/>
|
||||
<property name="<Alt>F4" type="string" value="close_window_key"/>
|
||||
<property name="<Primary><Alt>KP_3" type="string" value="move_window_workspace_3_key"/>
|
||||
<property name="<Primary>F2" type="string" value="workspace_2_key"/>
|
||||
<property name="<Primary>F6" type="string" value="workspace_6_key"/>
|
||||
<property name="<Primary><Alt>Down" type="string" value="down_workspace_key"/>
|
||||
<property name="<Primary><Alt>KP_9" type="string" value="move_window_workspace_9_key"/>
|
||||
<property name="<Super>KP_Up" type="string" value="tile_up_key"/>
|
||||
<property name="<Primary><Alt>End" type="string" value="move_window_next_workspace_key"/>
|
||||
<property name="<Primary>F8" type="string" value="workspace_8_key"/>
|
||||
<property name="<Primary><Shift><Alt>Left" type="string" value="move_window_left_key"/>
|
||||
<property name="<Super>KP_Right" type="string" value="tile_right_key"/>
|
||||
<property name="<Primary><Alt>KP_4" type="string" value="move_window_workspace_4_key"/>
|
||||
<property name="Right" type="string" value="right_key"/>
|
||||
<property name="Down" type="string" value="down_key"/>
|
||||
<property name="<Primary>F3" type="string" value="workspace_3_key"/>
|
||||
<property name="<Shift><Alt>Page_Down" type="string" value="lower_window_key"/>
|
||||
<property name="<Primary>F9" type="string" value="workspace_9_key"/>
|
||||
<property name="<Alt>Tab" type="string" value="cycle_windows_key"/>
|
||||
<property name="<Primary><Shift><Alt>Right" type="string" value="move_window_right_key"/>
|
||||
<property name="<Primary><Alt>Right" type="string" value="right_workspace_key"/>
|
||||
<property name="<Alt>F6" type="string" value="stick_window_key"/>
|
||||
<property name="<Primary><Alt>KP_5" type="string" value="move_window_workspace_5_key"/>
|
||||
<property name="<Primary>F11" type="string" value="workspace_11_key"/>
|
||||
<property name="<Alt>F10" type="string" value="maximize_window_key"/>
|
||||
<property name="<Alt>Delete" type="string" value="del_workspace_key"/>
|
||||
<property name="<Super>Tab" type="string" value="switch_window_key"/>
|
||||
<property name="<Primary><Alt>d" type="string" value="show_desktop_key"/>
|
||||
<property name="<Primary>F4" type="string" value="workspace_4_key"/>
|
||||
<property name="<Super>KP_Page_Up" type="string" value="tile_up_right_key"/>
|
||||
<property name="<Alt>F7" type="string" value="move_window_key"/>
|
||||
<property name="Up" type="string" value="up_key"/>
|
||||
<property name="<Primary><Alt>KP_6" type="string" value="move_window_workspace_6_key"/>
|
||||
<property name="<Alt>F11" type="string" value="fullscreen_key"/>
|
||||
<property name="<Alt>space" type="string" value="popup_menu_key"/>
|
||||
<property name="<Super>KP_Home" type="string" value="tile_up_left_key"/>
|
||||
<property name="Escape" type="string" value="cancel_key"/>
|
||||
<property name="<Primary><Alt>KP_1" type="string" value="move_window_workspace_1_key"/>
|
||||
<property name="<Super>KP_Next" type="string" value="tile_down_right_key"/>
|
||||
<property name="<Super>KP_Left" type="string" value="tile_left_key"/>
|
||||
<property name="<Shift><Alt>Page_Up" type="string" value="raise_window_key"/>
|
||||
<property name="<Primary><Alt>Home" type="string" value="move_window_prev_workspace_key"/>
|
||||
<property name="<Alt><Shift>Tab" type="string" value="cycle_reverse_windows_key"/>
|
||||
<property name="<Primary><Alt>Left" type="string" value="left_workspace_key"/>
|
||||
<property name="<Alt>F12" type="string" value="above_key"/>
|
||||
<property name="<Primary><Shift><Alt>Up" type="string" value="move_window_up_key"/>
|
||||
<property name="<Primary>F5" type="string" value="workspace_5_key"/>
|
||||
<property name="<Alt>F8" type="string" value="resize_window_key"/>
|
||||
<property name="<Primary><Alt>KP_7" type="string" value="move_window_workspace_7_key"/>
|
||||
<property name="<Primary><Alt>KP_2" type="string" value="move_window_workspace_2_key"/>
|
||||
<property name="<Super>KP_End" type="string" value="tile_down_left_key"/>
|
||||
<property name="<Primary><Alt>Up" type="string" value="up_workspace_key"/>
|
||||
<property name="<Alt>F9" type="string" value="hide_window_key"/>
|
||||
<property name="<Primary>F7" type="string" value="workspace_7_key"/>
|
||||
<property name="<Primary>F10" type="string" value="workspace_10_key"/>
|
||||
<property name="Left" type="string" value="left_key"/>
|
||||
<property name="<Primary><Alt>KP_8" type="string" value="move_window_workspace_8_key"/>
|
||||
<property name="<Alt>Insert" type="string" value="add_workspace_key"/>
|
||||
<property name="<Primary>F1" type="string" value="workspace_1_key"/>
|
||||
<property name="override" type="bool" value="true"/>
|
||||
</property>
|
||||
</property>
|
||||
<property name="providers" type="array">
|
||||
<value type="string" value="xfwm4"/>
|
||||
<value type="string" value="commands"/>
|
||||
</property>
|
||||
</channel>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.1" encoding="UTF-8"?>
|
||||
|
||||
<channel name="xfce4-notifyd" version="1.0">
|
||||
<property name="log-max-size-enabled" type="bool" value="true"/>
|
||||
<property name="date-time-custom-format" type="string" value="%a %H:%M:%S"/>
|
||||
<property name="show-text-with-gauge" type="bool" value="true"/>
|
||||
</channel>
|
||||
@@ -1,82 +0,0 @@
|
||||
<?xml version="1.1" encoding="UTF-8"?>
|
||||
|
||||
<channel name="xfce4-panel" version="1.0">
|
||||
<property name="configver" type="int" value="2"/>
|
||||
<property name="panels" type="array">
|
||||
<value type="int" value="1"/>
|
||||
<property name="dark-mode" type="bool" value="true"/>
|
||||
<property name="panel-1" type="empty">
|
||||
<property name="position" type="string" value="p=8;x=640;y=786"/>
|
||||
<property name="length" type="uint" value="100"/>
|
||||
<property name="position-locked" type="bool" value="true"/>
|
||||
<property name="icon-size" type="uint" value="16"/>
|
||||
<property name="size" type="uint" value="26"/>
|
||||
<property name="plugin-ids" type="array">
|
||||
<value type="int" value="1"/>
|
||||
<value type="int" value="2"/>
|
||||
<value type="int" value="3"/>
|
||||
<value type="int" value="4"/>
|
||||
<value type="int" value="5"/>
|
||||
<value type="int" value="6"/>
|
||||
<value type="int" value="10"/>
|
||||
<value type="int" value="7"/>
|
||||
<value type="int" value="11"/>
|
||||
</property>
|
||||
</property>
|
||||
</property>
|
||||
<property name="plugins" type="empty">
|
||||
<property name="plugin-1" type="string" value="whiskermenu">
|
||||
<property name="show-button-title" type="bool" value="true"/>
|
||||
<property name="profile-shape" type="int" value="0"/>
|
||||
<property name="show-command-profile" type="bool" value="false"/>
|
||||
<property name="show-command-menueditor" type="bool" value="false"/>
|
||||
<property name="show-command-settings" type="bool" value="true"/>
|
||||
<property name="search-actions" type="int" value="0"/>
|
||||
<property name="favorites" type="array">
|
||||
</property>
|
||||
<property name="recent" type="array">
|
||||
<value type="string" value="xfce4-terminal.desktop"/>
|
||||
<value type="string" value="xfce4-terminal-emulator.desktop"/>
|
||||
</property>
|
||||
</property>
|
||||
<property name="plugin-2" type="string" value="tasklist">
|
||||
<property name="flat-buttons" type="bool" value="true"/>
|
||||
<property name="show-handle" type="bool" value="true"/>
|
||||
<property name="grouping" type="bool" value="true"/>
|
||||
<property name="include-all-workspaces" type="bool" value="false"/>
|
||||
</property>
|
||||
<property name="plugin-3" type="string" value="separator">
|
||||
<property name="expand" type="bool" value="true"/>
|
||||
<property name="style" type="uint" value="0"/>
|
||||
</property>
|
||||
<property name="plugin-4" type="string" value="pager">
|
||||
<property name="rows" type="uint" value="1"/>
|
||||
<property name="workspace-scrolling" type="bool" value="false"/>
|
||||
<property name="miniature-view" type="bool" value="true"/>
|
||||
</property>
|
||||
<property name="plugin-6" type="string" value="systray">
|
||||
<property name="single-row" type="bool" value="true"/>
|
||||
<property name="square-icons" type="bool" value="true"/>
|
||||
<property name="known-legacy-items" type="array">
|
||||
<value type="string" value="xfce4-power-manager"/>
|
||||
</property>
|
||||
</property>
|
||||
<property name="plugin-10" type="string" value="pulseaudio">
|
||||
<property name="enable-keyboard-shortcuts" type="bool" value="true"/>
|
||||
<property name="play-sound" type="bool" value="true"/>
|
||||
<property name="rec-indicator-persistent" type="bool" value="false"/>
|
||||
</property>
|
||||
<property name="plugin-11" type="string" value="clock">
|
||||
<property name="show-week-numbers" type="bool" value="false"/>
|
||||
<property name="digital-date-font" type="string" value="Noto Sans 8"/>
|
||||
<property name="digital-time-font" type="string" value="Noto Sans 8"/>
|
||||
<property name="digital-time-format" type="string" value="%l:%M:%S %P"/>
|
||||
<property name="digital-date-format" type="string" value="%a, %b %d, %Y"/>
|
||||
</property>
|
||||
<property name="plugin-5" type="string" value="separator">
|
||||
<property name="style" type="uint" value="0"/>
|
||||
<property name="expand" type="bool" value="false"/>
|
||||
</property>
|
||||
<property name="plugin-7" type="string" value="power-manager-plugin"/>
|
||||
</property>
|
||||
</channel>
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.1" encoding="UTF-8"?>
|
||||
|
||||
<channel name="xfce4-power-manager" version="1.0">
|
||||
<property name="xfce4-power-manager" type="empty">
|
||||
<property name="lock-screen-suspend-hibernate" type="bool" value="true"/>
|
||||
<property name="power-button-action" type="uint" value="4"/>
|
||||
<property name="hibernate-button-action" type="uint" value="3"/>
|
||||
<property name="sleep-button-action" type="uint" value="3"/>
|
||||
<property name="battery-button-action" type="uint" value="3"/>
|
||||
<property name="show-tray-icon" type="bool" value="false"/>
|
||||
<property name="inactivity-on-ac" type="uint" value="0"/>
|
||||
<property name="dpms-on-ac-off" type="uint" value="60"/>
|
||||
<property name="dpms-on-ac-sleep" type="uint" value="15"/>
|
||||
</property>
|
||||
</channel>
|
||||
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.1" encoding="UTF-8"?>
|
||||
|
||||
<channel name="xfce4-screensaver" version="1.0">
|
||||
<property name="saver" type="empty">
|
||||
<property name="mode" type="int" value="2"/>
|
||||
<property name="themes" type="empty">
|
||||
<property name="list" type="array">
|
||||
<value type="string" value="screensavers-xfce-floaters"/>
|
||||
</property>
|
||||
</property>
|
||||
<property name="enabled" type="bool" value="true"/>
|
||||
<property name="fullscreen-inhibit" type="bool" value="true"/>
|
||||
<property name="idle-activation" type="empty">
|
||||
<property name="delay" type="int" value="1"/>
|
||||
</property>
|
||||
</property>
|
||||
<property name="lock" type="empty">
|
||||
<property name="enabled" type="bool" value="true"/>
|
||||
<property name="saver-activation" type="empty">
|
||||
<property name="delay" type="int" value="2"/>
|
||||
<property name="enabled" type="bool" value="true"/>
|
||||
</property>
|
||||
<property name="sleep-activation" type="bool" value="true"/>
|
||||
<property name="status-messages" type="empty">
|
||||
<property name="enabled" type="bool" value="false"/>
|
||||
</property>
|
||||
<property name="user-switching" type="empty">
|
||||
<property name="enabled" type="bool" value="false"/>
|
||||
</property>
|
||||
</property>
|
||||
</channel>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.1" encoding="UTF-8"?>
|
||||
|
||||
<channel name="xfce4-terminal" version="1.0">
|
||||
<property name="scrolling-lines" type="uint" value="10000"/>
|
||||
<property name="misc-cursor-blinks" type="bool" value="true"/>
|
||||
<property name="color-use-theme" type="bool" value="false"/>
|
||||
<property name="color-background-vary" type="bool" value="false"/>
|
||||
<property name="misc-tab-close-middle-click" type="bool" value="false"/>
|
||||
<property name="misc-middle-click-opens-uri" type="bool" value="false"/>
|
||||
<property name="misc-bell" type="bool" value="false"/>
|
||||
<property name="misc-bell-urgent" type="bool" value="false"/>
|
||||
</channel>
|
||||
@@ -1,91 +0,0 @@
|
||||
<?xml version="1.1" encoding="UTF-8"?>
|
||||
|
||||
<channel name="xfwm4" version="1.0">
|
||||
<property name="general" type="empty">
|
||||
<property name="activate_action" type="string" value="bring"/>
|
||||
<property name="borderless_maximize" type="bool" value="true"/>
|
||||
<property name="box_move" type="bool" value="false"/>
|
||||
<property name="box_resize" type="bool" value="false"/>
|
||||
<property name="button_layout" type="string" value="O|SHMC"/>
|
||||
<property name="button_offset" type="int" value="0"/>
|
||||
<property name="button_spacing" type="int" value="0"/>
|
||||
<property name="click_to_focus" type="bool" value="true"/>
|
||||
<property name="cycle_apps_only" type="bool" value="false"/>
|
||||
<property name="cycle_draw_frame" type="bool" value="true"/>
|
||||
<property name="cycle_raise" type="bool" value="false"/>
|
||||
<property name="cycle_hidden" type="bool" value="true"/>
|
||||
<property name="cycle_minimum" type="bool" value="true"/>
|
||||
<property name="cycle_minimized" type="bool" value="false"/>
|
||||
<property name="cycle_preview" type="bool" value="true"/>
|
||||
<property name="cycle_tabwin_mode" type="int" value="0"/>
|
||||
<property name="cycle_workspaces" type="bool" value="false"/>
|
||||
<property name="double_click_action" type="string" value="maximize"/>
|
||||
<property name="double_click_distance" type="int" value="5"/>
|
||||
<property name="double_click_time" type="int" value="250"/>
|
||||
<property name="easy_click" type="string" value="Alt"/>
|
||||
<property name="focus_delay" type="int" value="250"/>
|
||||
<property name="focus_hint" type="bool" value="true"/>
|
||||
<property name="focus_new" type="bool" value="true"/>
|
||||
<property name="frame_opacity" type="int" value="100"/>
|
||||
<property name="frame_border_top" type="int" value="0"/>
|
||||
<property name="full_width_title" type="bool" value="true"/>
|
||||
<property name="horiz_scroll_opacity" type="bool" value="false"/>
|
||||
<property name="inactive_opacity" type="int" value="100"/>
|
||||
<property name="maximized_offset" type="int" value="0"/>
|
||||
<property name="mousewheel_rollup" type="bool" value="false"/>
|
||||
<property name="move_opacity" type="int" value="100"/>
|
||||
<property name="placement_mode" type="string" value="center"/>
|
||||
<property name="placement_ratio" type="int" value="20"/>
|
||||
<property name="popup_opacity" type="int" value="100"/>
|
||||
<property name="prevent_focus_stealing" type="bool" value="false"/>
|
||||
<property name="raise_delay" type="int" value="250"/>
|
||||
<property name="raise_on_click" type="bool" value="true"/>
|
||||
<property name="raise_on_focus" type="bool" value="false"/>
|
||||
<property name="raise_with_any_button" type="bool" value="true"/>
|
||||
<property name="repeat_urgent_blink" type="bool" value="false"/>
|
||||
<property name="resize_opacity" type="int" value="100"/>
|
||||
<property name="scroll_workspaces" type="bool" value="false"/>
|
||||
<property name="shadow_delta_height" type="int" value="0"/>
|
||||
<property name="shadow_delta_width" type="int" value="0"/>
|
||||
<property name="shadow_delta_x" type="int" value="0"/>
|
||||
<property name="shadow_delta_y" type="int" value="-3"/>
|
||||
<property name="shadow_opacity" type="int" value="50"/>
|
||||
<property name="show_app_icon" type="bool" value="false"/>
|
||||
<property name="show_dock_shadow" type="bool" value="true"/>
|
||||
<property name="show_frame_shadow" type="bool" value="true"/>
|
||||
<property name="show_popup_shadow" type="bool" value="false"/>
|
||||
<property name="snap_resist" type="bool" value="false"/>
|
||||
<property name="snap_to_border" type="bool" value="true"/>
|
||||
<property name="snap_to_windows" type="bool" value="false"/>
|
||||
<property name="snap_width" type="int" value="10"/>
|
||||
<property name="vblank_mode" type="string" value="auto"/>
|
||||
<property name="theme" type="string" value="Default"/>
|
||||
<property name="tile_on_move" type="bool" value="true"/>
|
||||
<property name="title_alignment" type="string" value="center"/>
|
||||
<property name="title_font" type="string" value="Noto Sans Bold 9"/>
|
||||
<property name="title_horizontal_offset" type="int" value="0"/>
|
||||
<property name="titleless_maximize" type="bool" value="false"/>
|
||||
<property name="title_shadow_active" type="string" value="false"/>
|
||||
<property name="title_shadow_inactive" type="string" value="false"/>
|
||||
<property name="title_vertical_offset_active" type="int" value="0"/>
|
||||
<property name="title_vertical_offset_inactive" type="int" value="0"/>
|
||||
<property name="toggle_workspaces" type="bool" value="false"/>
|
||||
<property name="unredirect_overlays" type="bool" value="true"/>
|
||||
<property name="urgent_blink" type="bool" value="false"/>
|
||||
<property name="use_compositing" type="bool" value="true"/>
|
||||
<property name="workspace_count" type="int" value="4"/>
|
||||
<property name="wrap_cycle" type="bool" value="true"/>
|
||||
<property name="wrap_layout" type="bool" value="true"/>
|
||||
<property name="wrap_resistance" type="int" value="10"/>
|
||||
<property name="wrap_windows" type="bool" value="false"/>
|
||||
<property name="wrap_workspaces" type="bool" value="false"/>
|
||||
<property name="zoom_desktop" type="bool" value="true"/>
|
||||
<property name="zoom_pointer" type="bool" value="true"/>
|
||||
<property name="workspace_names" type="array">
|
||||
<value type="string" value="Workspace 1"/>
|
||||
<value type="string" value="Workspace 2"/>
|
||||
<value type="string" value="Workspace 3"/>
|
||||
<value type="string" value="Workspace 4"/>
|
||||
</property>
|
||||
</property>
|
||||
</channel>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.1" encoding="UTF-8"?>
|
||||
|
||||
<channel name="xsettings" version="1.0">
|
||||
<property name="Net" type="empty">
|
||||
<property name="ThemeName" type="string" value="Adwaita-dark"/>
|
||||
<property name="IconThemeName" type="string" value="Papirus-Dark"/>
|
||||
<property name="DoubleClickTime" type="empty"/>
|
||||
<property name="DoubleClickDistance" type="empty"/>
|
||||
<property name="DndDragThreshold" type="empty"/>
|
||||
<property name="CursorBlink" type="empty"/>
|
||||
<property name="CursorBlinkTime" type="empty"/>
|
||||
<property name="SoundThemeName" type="empty"/>
|
||||
<property name="EnableEventSounds" type="empty"/>
|
||||
<property name="EnableInputFeedbackSounds" type="empty"/>
|
||||
</property>
|
||||
<property name="Xft" type="empty">
|
||||
<property name="DPI" type="empty"/>
|
||||
<property name="Antialias" type="empty"/>
|
||||
<property name="Hinting" type="empty"/>
|
||||
<property name="HintStyle" type="empty"/>
|
||||
<property name="RGBA" type="empty"/>
|
||||
</property>
|
||||
<property name="Gtk" type="empty">
|
||||
<property name="CanChangeAccels" type="empty"/>
|
||||
<property name="ColorPalette" type="empty"/>
|
||||
<property name="FontName" type="string" value="Noto Sans 10"/>
|
||||
<property name="MonospaceFontName" type="string" value="Noto Sans Mono 10"/>
|
||||
<property name="IconSizes" type="empty"/>
|
||||
<property name="KeyThemeName" type="empty"/>
|
||||
<property name="MenuImages" type="empty"/>
|
||||
<property name="ButtonImages" type="empty"/>
|
||||
<property name="MenuBarAccel" type="empty"/>
|
||||
<property name="CursorThemeName" type="string" value="Adwaita"/>
|
||||
<property name="CursorThemeSize" type="empty"/>
|
||||
<property name="DecorationLayout" type="string" value="icon,menu:minimize,maximize,close"/>
|
||||
<property name="DialogsUseHeader" type="empty"/>
|
||||
<property name="TitlebarMiddleClick" type="empty"/>
|
||||
</property>
|
||||
<property name="Gdk" type="empty">
|
||||
<property name="WindowScalingFactor" type="empty"/>
|
||||
</property>
|
||||
</channel>
|
||||
@@ -12,7 +12,7 @@ table inet filter {
|
||||
ct state invalid counter drop comment "drop invalid"
|
||||
meta l4proto { icmp, ipv6-icmp } counter accept comment "accept ICMP"
|
||||
|
||||
tcp dport ssh ct state { new } counter accept comment "accept new SSH connections"
|
||||
tcp dport ssh ct state new counter accept comment "accept new SSH connections"
|
||||
|
||||
counter comment "count any other dropped traffic"
|
||||
}
|
||||
@@ -20,11 +20,12 @@ table inet filter {
|
||||
chain output {
|
||||
type filter hook output priority filter; policy drop;
|
||||
|
||||
iif lo counter accept comment "accept any localhost traffic"
|
||||
oif lo counter accept comment "accept any localhost traffic"
|
||||
ct state { established, related } counter accept comment "accept established,related"
|
||||
ct state invalid counter drop comment "drop invalid"
|
||||
meta l4proto { icmp, ipv6-icmp } counter accept comment "accept ICMP"
|
||||
|
||||
udp dport https ct state new counter reject comment "reject new HTTP/3 connections"
|
||||
ct state new counter accept comment "accept new outbound connections"
|
||||
|
||||
counter comment "count any other dropped traffic"
|
||||
2
files/home-skel-desktop/.config/dolphinrc
Normal file
2
files/home-skel-desktop/.config/dolphinrc
Normal file
@@ -0,0 +1,2 @@
|
||||
[ContextMenu]
|
||||
ShowShareActions=false
|
||||
0
files/home-skel-desktop/.gitkeep
Normal file
0
files/home-skel-desktop/.gitkeep
Normal file
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
NoDisplay=true
|
||||
0
files/home-skel/.gitkeep
Normal file
0
files/home-skel/.gitkeep
Normal file
@@ -6,441 +6,239 @@
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
# Stop the script if any command exits non-zero.
|
||||
set -e
|
||||
#===============================================================================
|
||||
# INITIALIZATION
|
||||
#===============================================================================
|
||||
|
||||
# A wrapper for "echo" which prepends a prefix so output from the script can be easily differentiated from command output.
|
||||
print() {
|
||||
echo "[LogalDeveloper's Arch Linux Installer] $1"
|
||||
}
|
||||
# Determine script directory for sourcing modules
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
## Arch Linux Installation Guide Step 1.7 - Connect to the internet
|
||||
# Checks DNS and internet connectivity by making an HTTPS request. If it fails, assume no internet connection.
|
||||
print "Checking internet connectivity..."
|
||||
internet_check_url="https://logal.dev/"
|
||||
if curl -s --head $internet_check_url | grep "200" >/dev/null; then
|
||||
print "Internet connection is available!"
|
||||
else
|
||||
print "Internet connection appears not available (HTTP request to "$internet_check_url" failed). Please check network settings and re-run this script."
|
||||
exit 1
|
||||
fi
|
||||
# Source configuration
|
||||
source "${SCRIPT_DIR}/config/defaults.conf"
|
||||
source "${SCRIPT_DIR}/config/luks.conf"
|
||||
source "${SCRIPT_DIR}/config/drivers.conf"
|
||||
source "${SCRIPT_DIR}/config/profiles.conf"
|
||||
|
||||
## Arch Linux Installation Guide Step 1.8 - Update the system clock
|
||||
# Checks systemd-timesyncd to verify the system time is synchronized.
|
||||
print "Checking system time synchronization state..."
|
||||
if timedatectl status | grep -q "System clock synchronized: yes"; then
|
||||
print "System time is synchronized!"
|
||||
else
|
||||
print "The system time is not synchronized. Please check systemd-timesyncd and re-run this script."
|
||||
exit 1
|
||||
fi
|
||||
# Source core libraries
|
||||
source "${SCRIPT_DIR}/lib/core/common.sh"
|
||||
source "${SCRIPT_DIR}/lib/core/validation.sh"
|
||||
source "${SCRIPT_DIR}/lib/core/error.sh"
|
||||
source "${SCRIPT_DIR}/lib/core/logging.sh"
|
||||
|
||||
print "Setting mirrorlist to use private mirror..."
|
||||
echo "Server = https://mirrors.logal.dev/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist
|
||||
# Source disk operation modules
|
||||
source "${SCRIPT_DIR}/lib/disk/partition.sh"
|
||||
source "${SCRIPT_DIR}/lib/disk/luks.sh"
|
||||
source "${SCRIPT_DIR}/lib/disk/filesystem.sh"
|
||||
|
||||
# Provide the tip from the Arch Linux Installation Guide regarding optimal logical sector sizes.
|
||||
print "Please check the following items before proceding:"
|
||||
print " - If you intend to use an Advanced Format (e.g. NVMe) drive, verify the optimal sector size is selected. (https://wiki.archlinux.org/title/Advanced_Format)"
|
||||
print "If you need to go back, press Ctrl+C. Otherwise, press enter to continue."
|
||||
read
|
||||
# Source system configuration modules
|
||||
source "${SCRIPT_DIR}/lib/system/base.sh"
|
||||
source "${SCRIPT_DIR}/lib/system/bootloader.sh"
|
||||
source "${SCRIPT_DIR}/lib/system/locale.sh"
|
||||
source "${SCRIPT_DIR}/lib/system/network.sh"
|
||||
source "${SCRIPT_DIR}/lib/system/security.sh"
|
||||
source "${SCRIPT_DIR}/lib/system/user.sh"
|
||||
|
||||
## Arch Linux Installation Guide Step 1.9 - Partition the disks
|
||||
# Provide the user a listing of the disks and ask them which they'd like to install to.
|
||||
fdisk -l
|
||||
print "Disk information from 'fdisk -l' is provided above. Please enter the path to the disk you would like to install Arch Linux to (e.g. /dev/sda)."
|
||||
read install_disk
|
||||
print "Please confirm your selection by entering the same path again."
|
||||
read disk_confirm
|
||||
# Source desktop modules
|
||||
source "${SCRIPT_DIR}/lib/desktop/kde.sh"
|
||||
source "${SCRIPT_DIR}/lib/desktop/drivers.sh"
|
||||
|
||||
if [ "$install_disk" != "$disk_confirm" ]; then
|
||||
print "The same disk was not entered both times. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
# Source profile system
|
||||
source "${SCRIPT_DIR}/lib/system/profiles.sh"
|
||||
|
||||
# Triple check the user wants to continue installing to install disk.
|
||||
print "Last warning: Are you sure you want to install Arch Linux to '$install_disk'? All data on this disk will be wiped. Enter 'I am sure' exactly to confirm, or anything else to cancel."
|
||||
read final_confirmation
|
||||
# Enable error handling
|
||||
trap_errors
|
||||
|
||||
if [ "$final_confirmation" != "I am sure" ]; then
|
||||
print "Confirmation failed. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
#===============================================================================
|
||||
# STORAGE CONFIGURATION
|
||||
#===============================================================================
|
||||
|
||||
# Wipe all previous file systems from the install disk.
|
||||
print "Wiping existing partition table from $install_disk..."
|
||||
wipefs -a $install_disk
|
||||
# Prompt user for storage mode selection
|
||||
# Sets:
|
||||
# STORAGE_MODE - "single" or "raid1"
|
||||
# FILESYSTEM - "ext4", "btrfs", or "btrfs-dup"
|
||||
select_storage_mode() {
|
||||
print "Select storage and filesystem configuration:"
|
||||
print " 1 - BTRFS (Single disk)"
|
||||
print " 2 - BTRFS DUP (Single disk with duplicate data and metadata)"
|
||||
print " 3 - BTRFS RAID1 (Two disks with full redundancy)"
|
||||
print " 4 - BTRFS RAID1 (Three disks with enhanced metadata redundancy)"
|
||||
print " 5 - ext4 (Single disk)"
|
||||
read -r storage_choice
|
||||
|
||||
# Partition install disk.
|
||||
print "Partitioning $install_disk..."
|
||||
sgdisk --new 1:0:1G $install_disk # New GPT table, make a new partition 1G in size at the start
|
||||
sgdisk --typecode 1:ef00 $install_disk # Mark it as EFI System Partition
|
||||
sgdisk --new 2:0:0 $install_disk # Add a second partition taking up the rest of the remaining space.
|
||||
sgdisk --type-code 2:8309 $install_disk # Mark it as Linux LUKS
|
||||
|
||||
# /dev/nvme has an extra charater to identify partition number.
|
||||
if [[ $install_disk == /dev/nvme* ]]; then
|
||||
# Use "p" in the partition paths for NVMe drives.
|
||||
partition_prefix="${install_disk}p"
|
||||
else
|
||||
# Use just numbers for other drives.
|
||||
partition_prefix="${install_disk}"
|
||||
fi
|
||||
efi_partition=${partition_prefix}1
|
||||
root_partition=${partition_prefix}2
|
||||
|
||||
## Arch Linux Installation Guide Step 1.10 - Format the partitions
|
||||
print "Formatting ${efi_partition} as FAT32..."
|
||||
mkfs.fat -F 32 ${efi_partition}
|
||||
|
||||
print "Setting up encryption on ${root_partition}..."
|
||||
print "Please follow the cryptsetup prompts to enter your desired encryption passphrase."
|
||||
cryptsetup luksFormat --type luks2 --cipher aes-xts-plain64 --hash sha512 --key-size 512 --pbkdf argon2id --pbkdf-force-iterations 8 --pbkdf-memory 4194304 --pbkdf-parallel 4 --use-urandom ${root_partition}
|
||||
|
||||
print "Unlocking ${root_partition}..."
|
||||
print "Please enter the passphrase you just used to unlock the newly encrypted partition."
|
||||
cryptsetup open --allow-discards ${root_partition} cryptroot
|
||||
luks_uuid=$(cryptsetup luksDump ${root_partition} | grep 'UUID:' | awk '{print $2}')
|
||||
|
||||
print "What file system would you like for the root partition?"
|
||||
print " btrfs (Recommended)"
|
||||
print " ext4"
|
||||
read filesystem
|
||||
|
||||
case $filesystem in
|
||||
"ext4")
|
||||
print "Formatting /dev/mapper/cryptroot as ext4..."
|
||||
mkfs.ext4 /dev/mapper/cryptroot
|
||||
;;
|
||||
|
||||
*)
|
||||
print "Formatting /dev/mapper/cryptroot as btrfs..."
|
||||
mkfs.btrfs --csum xxhash /dev/mapper/cryptroot
|
||||
;;
|
||||
esac
|
||||
|
||||
## Arch Linux Installation Guide Step 1.11 - Mount the file systems
|
||||
print "Mounting partitions..."
|
||||
|
||||
case $filesystem in
|
||||
"ext4")
|
||||
mount -o "noatime,discard" /dev/mapper/cryptroot /mnt
|
||||
;;
|
||||
|
||||
*)
|
||||
mount -o "noatime,discard=async" /dev/mapper/cryptroot /mnt
|
||||
;;
|
||||
esac
|
||||
|
||||
mount --mkdir -o "fmask=0077,dmask=0077" ${efi_partition} /mnt/boot
|
||||
|
||||
## Arch Linux Installation Guide Step 2.2 - Install essential packages
|
||||
print "Installing Arch Linux base..."
|
||||
pacstrap -K /mnt base \
|
||||
linux \
|
||||
linux-firmware \
|
||||
bash-completion \
|
||||
btrfs-progs \
|
||||
smartmontools \
|
||||
ethtool \
|
||||
man-db \
|
||||
btop \
|
||||
htop \
|
||||
nano \
|
||||
less \
|
||||
tmux \
|
||||
rsync \
|
||||
sudo \
|
||||
iptables-nft \
|
||||
openssh \
|
||||
usbguard
|
||||
|
||||
print "Installing CPU microcode..."
|
||||
cpu_vendor=$(grep -m 1 'vendor_id' /proc/cpuinfo | awk '{print $3}')
|
||||
if [[ "${cpu_vendor}" == "GenuineIntel" ]]; then
|
||||
arch-chroot /mnt pacman --noconfirm -S intel-ucode
|
||||
elif [[ "${cpu_vendor}" == "AuthenticAMD" ]]; then
|
||||
arch-chroot /mnt pacman --noconfirm -S amd-ucode
|
||||
else
|
||||
echo "Unknown CPU vendor: ${cpu_vendor}. Please install microcode manually after installation, if available."
|
||||
fi
|
||||
|
||||
## Arch Linux Installation Guide Step 3.1 - Fstab
|
||||
print "Generating /etc/fstab..."
|
||||
genfstab -U /mnt >> /mnt/etc/fstab
|
||||
|
||||
## Arch Linux Installation Guide Step 3.4 - Localization
|
||||
print "Setting up locale..."
|
||||
arch-chroot /mnt sed -i '/^#.*en_US.UTF-8 UTF-8/s/^#//' /etc/locale.gen
|
||||
arch-chroot /mnt locale-gen
|
||||
arch-chroot /mnt systemd-firstboot --locale=en_US.UTF-8
|
||||
|
||||
## Arch Linux Installation Guide Step 3.3 - Time
|
||||
## Arch Linux Installation Guide Step 3.4 - Localization
|
||||
## Arch Linux Installation Guide Step 3.5 - Network configuration
|
||||
print "Entering first time setup..."
|
||||
print "Your keymap is probably 'us' and the time zone is probably 'America/New_York'."
|
||||
arch-chroot /mnt systemd-firstboot --prompt
|
||||
|
||||
## Arch Linux Installation Guide Step 3.6 - Initramfs
|
||||
default_mkinitcpio_line="HOOKS=(base systemd autodetect microcode modconf kms keyboard keymap sd-vconsole block filesystems fsck)"
|
||||
new_mkinitcpio_line="HOOKS=(systemd autodetect microcode modconf kms keyboard sd-vconsole block sd-encrypt filesystems fsck)"
|
||||
arch-chroot /mnt sed -i "s|^${default_mkinitcpio_line}|${new_mkinitcpio_line}|" /etc/mkinitcpio.conf
|
||||
arch-chroot /mnt mkinitcpio -P
|
||||
|
||||
## Arch Linux Installation Guide Step 3.8 - Boot loader
|
||||
print "Installing bootloader..."
|
||||
arch-chroot /mnt bootctl install
|
||||
arch-chroot /mnt sh -c "cat > /boot/loader/entries/arch.conf" <<EOF
|
||||
title Arch Linux
|
||||
linux /vmlinuz-linux
|
||||
initrd /initramfs-linux.img
|
||||
options rd.luks.name=${luks_uuid}=cryptroot rd.luks.options=discard root=/dev/mapper/cryptroot
|
||||
EOF
|
||||
|
||||
arch-chroot /mnt sed -i '/^#timeout 3/s/^#//' /boot/loader/loader.conf
|
||||
|
||||
print "Enabling fstrim timer..."
|
||||
arch-chroot /mnt systemctl enable fstrim.timer
|
||||
|
||||
if [ "$filesystem" = "btrfs" ]; then
|
||||
print "Enabling scrub timer..."
|
||||
arch-chroot /mnt systemctl enable btrfs-scrub@-.timer
|
||||
fi
|
||||
|
||||
print "Enabling sudo access for wheel group..."
|
||||
arch-chroot /mnt sed -i "s|^# %wheel ALL=(ALL:ALL) ALL|%wheel ALL=(ALL:ALL) ALL|" /etc/sudoers
|
||||
|
||||
print "Disabling root account..."
|
||||
arch-chroot /mnt passwd -l root
|
||||
|
||||
print "Please enter the username you'd like to use for your account"
|
||||
read username
|
||||
arch-chroot /mnt useradd -m -G wheel $username
|
||||
print "Please set the password for your new account."
|
||||
arch-chroot /mnt passwd $username
|
||||
|
||||
print "Installing default configuration files..."
|
||||
cp -r ./etc /mnt
|
||||
|
||||
print "Enabling systemd-resolved..."
|
||||
arch-chroot /mnt systemctl enable systemd-resolved.service
|
||||
ln -sf ../run/systemd/resolve/stub-resolv.conf /mnt/etc/resolv.conf
|
||||
|
||||
print "Enabling systemd-networkd..."
|
||||
arch-chroot /mnt systemctl enable systemd-networkd.service
|
||||
|
||||
print "Enabling systemd-timesyncd..."
|
||||
arch-chroot /mnt systemctl enable systemd-timesyncd.service
|
||||
|
||||
print "Enabling nftables firewall..."
|
||||
arch-chroot /mnt systemctl enable nftables.service
|
||||
|
||||
print "Enabling smartd..."
|
||||
arch-chroot /mnt systemctl enable smartd.service
|
||||
|
||||
print "Would you like to install iwd for Wi-Fi support? Enter 'y' exactly for yes, otherwise anything else to skip."
|
||||
read install_iwd
|
||||
|
||||
if [ "$install_iwd" == "y" ]; then
|
||||
print "Installing iwd..."
|
||||
arch-chroot /mnt pacman --noconfirm -S iwd
|
||||
arch-chroot /mnt systemctl enable iwd.service
|
||||
fi
|
||||
|
||||
print "Setting up and enabling OpenSSH server..."
|
||||
arch-chroot /mnt sed -i "s|PLACEHOLDER|${username}|" /etc/ssh/sshd_config
|
||||
arch-chroot /mnt ssh-keygen -t ed25519 -C "" -N "" -f /etc/ssh/ssh_host_ed25519_key
|
||||
arch-chroot /mnt systemctl enable sshd.service
|
||||
|
||||
print "Adding LogalNet Internal Certification Authority to system CA store..."
|
||||
cp ./logalnet-internal-ca.crt /mnt
|
||||
arch-chroot /mnt trust anchor --store /logalnet-internal-ca.crt
|
||||
arch-chroot /mnt rm /logalnet-internal-ca.crt
|
||||
|
||||
install_base_xfce() {
|
||||
arch-chroot /mnt pacman --noconfirm -S lightdm \
|
||||
lightdm-gtk-greeter \
|
||||
lightdm-gtk-greeter-settings \
|
||||
thunar \
|
||||
thunar-archive-plugin \
|
||||
gvfs \
|
||||
xfce4-panel \
|
||||
xfce4-power-manager \
|
||||
xfce4-session \
|
||||
xfce4-settings \
|
||||
xfce4-terminal \
|
||||
xfdesktop \
|
||||
xfwm4 \
|
||||
papirus-icon-theme \
|
||||
xfce4-battery-plugin \
|
||||
xfce4-notifyd \
|
||||
xfce4-whiskermenu-plugin \
|
||||
xfce4-screensaver \
|
||||
xfce4-screenshooter \
|
||||
mousepad \
|
||||
noto-fonts \
|
||||
noto-fonts-cjk \
|
||||
noto-fonts-emoji \
|
||||
noto-fonts-extra \
|
||||
pipewire \
|
||||
pipewire-alsa \
|
||||
pipewire-pulse \
|
||||
pipewire-jack \
|
||||
wireplumber \
|
||||
pavucontrol \
|
||||
xfce4-pulseaudio-plugin \
|
||||
ristretto \
|
||||
webp-pixbuf-loader \
|
||||
libopenraw \
|
||||
xarchiver \
|
||||
7zip \
|
||||
xreader
|
||||
arch-chroot /mnt systemctl enable lightdm.service
|
||||
|
||||
cp -r ./default-home-directory-config /mnt/home/$username/.config
|
||||
|
||||
arch-chroot /mnt sh -c "cat > /etc/lightdm/lightdm-gtk-greeter.conf" <<EOF
|
||||
[greeter]
|
||||
hide-user-image = true
|
||||
font-name = Noto Sans 10
|
||||
clock-format = %A, %B %d, %Y,%l:%M:%S %p
|
||||
theme-name = Adwaita-dark
|
||||
icon-theme-name = Papirus-Dark
|
||||
screensaver-timeout = 10
|
||||
user-background = false
|
||||
background = #77767b
|
||||
indicators = ~host;~spacer;~clock;~spacer;~power
|
||||
EOF
|
||||
|
||||
mkdir -p /mnt/home/$username/.config/systemd/user
|
||||
ln -s /dev/null /mnt/home/$username/.config/systemd/user/tumblerd.service
|
||||
|
||||
chown -R 1000:1000 /mnt/home/$username/.config
|
||||
|
||||
print "Would you like to install graphics drivers? Type 'intel' exactly for Intel graphics drivers, 'nvidia' for NVIDIA graphics drivers, or anything else to skip"
|
||||
read driver
|
||||
|
||||
case $driver in
|
||||
"intel")
|
||||
arch-chroot /mnt pacman --noconfirm -S mesa \
|
||||
vulkan-intel \
|
||||
intel-media-driver \
|
||||
libva-intel-driver
|
||||
case "$storage_choice" in
|
||||
"2")
|
||||
STORAGE_MODE="single"
|
||||
FILESYSTEM="btrfs-dup"
|
||||
print "BTRFS dup mode selected. Data and metadata will be duplicated on the same disk."
|
||||
;;
|
||||
"nvidia")
|
||||
arch-chroot /mnt pacman --noconfirm -S mesa \
|
||||
nvidia-open \
|
||||
libva-nvidia-driver
|
||||
"3")
|
||||
STORAGE_MODE="raid1"
|
||||
FILESYSTEM="btrfs"
|
||||
print "BTRFS RAID1 mode selected. You will need two disks of similar size."
|
||||
;;
|
||||
"4")
|
||||
STORAGE_MODE="raid1-3disk"
|
||||
FILESYSTEM="btrfs"
|
||||
print "BTRFS RAID1 3-disk mode selected. You will need three disks of similar size."
|
||||
;;
|
||||
"5")
|
||||
STORAGE_MODE="single"
|
||||
FILESYSTEM="ext4"
|
||||
print "ext4 on single disk selected."
|
||||
;;
|
||||
*)
|
||||
print "Skipping graphics driver installation."
|
||||
STORAGE_MODE="single"
|
||||
FILESYSTEM="btrfs"
|
||||
print "BTRFS on single disk selected."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
print "Base install complete. Select profile to install for this system:"
|
||||
print " 1 - Minimal"
|
||||
print " Base Arch Linux system, no additional packages."
|
||||
print " 2 - Server"
|
||||
print " Adds Restic, Docker, and Docker Compose."
|
||||
print " 3 - Minimal Desktop"
|
||||
print " XFCE 4 with no additional applications."
|
||||
print " 4 - Home Theater PC"
|
||||
print " XFCE 4 with Chromium and VLC media player."
|
||||
print " 5 - Home Theater PC with Gaming"
|
||||
print " XFCE 4 with Chromium, VLC media player, and Dolphin."
|
||||
print " 6 - Office Workstation"
|
||||
print " XFCE 4 with a full suite of desktop applications aimed at general office work."
|
||||
print " 7 - Software Development Workstation"
|
||||
print " XFCE 4 with a suite of software development applications."
|
||||
read profile
|
||||
#===============================================================================
|
||||
# PRE-INSTALLATION NOTES
|
||||
#===============================================================================
|
||||
|
||||
case $profile in
|
||||
"1")
|
||||
# Do nothing...
|
||||
;;
|
||||
show_pre_install_notes() {
|
||||
print "Please check the following items before proceeding:"
|
||||
print " - If you intend to use an Advanced Format (e.g. NVMe) drive, verify the optimal sector size is selected. (https://wiki.archlinux.org/title/Advanced_Format)"
|
||||
print "If you need to go back, press Ctrl+C. Otherwise, press enter to continue."
|
||||
read -r
|
||||
}
|
||||
|
||||
"2")
|
||||
arch-chroot /mnt pacman --noconfirm -S restic \
|
||||
docker \
|
||||
docker-compose
|
||||
arch-chroot /mnt systemctl enable docker.service
|
||||
;;
|
||||
#===============================================================================
|
||||
# MAIN INSTALLATION FLOW
|
||||
#===============================================================================
|
||||
|
||||
"3")
|
||||
install_base_xfce
|
||||
;;
|
||||
main() {
|
||||
# Initialize logging
|
||||
init_logging
|
||||
|
||||
"4")
|
||||
install_base_xfce
|
||||
arch-chroot /mnt pacman --noconfirm -S chromium \
|
||||
vlc \
|
||||
vlc-plugin-ffmpeg
|
||||
;;
|
||||
# Show banner
|
||||
print_banner
|
||||
|
||||
"5")
|
||||
install_base_xfce
|
||||
arch-chroot /mnt pacman --noconfirm -S dolphin-emu \
|
||||
chromium \
|
||||
vlc \
|
||||
vlc-plugin-ffmpeg
|
||||
;;
|
||||
#---------------------------------------------------------------------------
|
||||
# Phase 1: Pre-flight Checks
|
||||
#---------------------------------------------------------------------------
|
||||
set_phase "Pre-flight Checks"
|
||||
|
||||
"6")
|
||||
install_base_xfce
|
||||
arch-chroot /mnt pacman --noconfirm -S ffmpeg \
|
||||
chromium \
|
||||
gimp \
|
||||
git \
|
||||
gnucash \
|
||||
hunspell-en_us \
|
||||
keepassxc \
|
||||
libreoffice-fresh \
|
||||
qalculate-gtk \
|
||||
syncthing \
|
||||
tenacity \
|
||||
vlc \
|
||||
vlc-plugin-ffmpeg
|
||||
;;
|
||||
if ! check_internet; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"7")
|
||||
install_base_xfce
|
||||
arch-chroot /mnt pacman --noconfirm -S code \
|
||||
docker \
|
||||
docker-compose \
|
||||
ffmpeg \
|
||||
chromium \
|
||||
gimp \
|
||||
git \
|
||||
go \
|
||||
hunspell-en_us \
|
||||
intellij-idea-community-edition \
|
||||
jdk-openjdk \
|
||||
keepassxc \
|
||||
libreoffice-fresh \
|
||||
pycharm-community-edition \
|
||||
python \
|
||||
python-virtualenv \
|
||||
qalculate-gtk \
|
||||
syncthing \
|
||||
tenacity \
|
||||
vlc \
|
||||
vlc-plugin-ffmpeg \
|
||||
wireshark-qt
|
||||
;;
|
||||
if ! check_time_sync; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
*)
|
||||
echo -n "Unknown profile, defaulting to minimal install."
|
||||
;;
|
||||
esac
|
||||
configure_mirrorlist
|
||||
show_pre_install_notes
|
||||
|
||||
print "Please add or remove any USB devices, including the installer drive, to form the standard configuration for this system. USBGuard will be configured to only allow the USB devices connected at the time you press enter to be used; everything else will be blocked."
|
||||
print "When ready to proceed, press enter."
|
||||
read
|
||||
arch-chroot /mnt sh -c "usbguard generate-policy > /etc/usbguard/rules.conf"
|
||||
arch-chroot /mnt systemctl enable usbguard.service
|
||||
#---------------------------------------------------------------------------
|
||||
# Phase 2: Storage Configuration
|
||||
#---------------------------------------------------------------------------
|
||||
set_phase "Storage Configuration"
|
||||
|
||||
echo "\n\n\n\n\n"
|
||||
print "Installation complete!"
|
||||
select_storage_mode
|
||||
|
||||
print "Public SSH key fingerprint of this host:"
|
||||
arch-chroot /mnt ssh-keygen -lvf /etc/ssh/ssh_host_ed25519_key.pub
|
||||
if [ "$STORAGE_MODE" = "raid1" ]; then
|
||||
if ! select_raid1_disks; then
|
||||
exit 1
|
||||
fi
|
||||
elif [ "$STORAGE_MODE" = "raid1-3disk" ]; then
|
||||
if ! select_raid1_3disk_disks; then
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if ! select_single_disk; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Phase 3: Disk Preparation
|
||||
#---------------------------------------------------------------------------
|
||||
set_phase "Disk Preparation"
|
||||
|
||||
partition_disks "$STORAGE_MODE"
|
||||
|
||||
# Setup encryption
|
||||
if [ "$STORAGE_MODE" = "raid1" ]; then
|
||||
setup_encryption_raid1 "$ROOT_PARTITION" "$ROOT_PARTITION_2"
|
||||
elif [ "$STORAGE_MODE" = "raid1-3disk" ]; then
|
||||
setup_encryption_raid1_3disk "$ROOT_PARTITION" "$ROOT_PARTITION_2" "$ROOT_PARTITION_3"
|
||||
else
|
||||
setup_encryption_single "$ROOT_PARTITION"
|
||||
fi
|
||||
|
||||
# Format and mount filesystems
|
||||
format_and_mount_filesystems "$FILESYSTEM" "$STORAGE_MODE"
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Phase 4: Base System Installation
|
||||
#---------------------------------------------------------------------------
|
||||
set_phase "Base System Installation"
|
||||
|
||||
install_base_packages
|
||||
install_microcode
|
||||
generate_fstab
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Phase 5: System Configuration
|
||||
#---------------------------------------------------------------------------
|
||||
set_phase "System Configuration"
|
||||
|
||||
setup_locale
|
||||
configure_initramfs
|
||||
setup_bootloader "$STORAGE_MODE"
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Phase 6: User Account Setup
|
||||
#---------------------------------------------------------------------------
|
||||
set_phase "User Account Setup"
|
||||
|
||||
setup_user
|
||||
copy_config_files
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Phase 7: Network Configuration
|
||||
#---------------------------------------------------------------------------
|
||||
set_phase "Network Configuration"
|
||||
|
||||
setup_network
|
||||
prompt_install_wifi
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Phase 8: Security Configuration
|
||||
#---------------------------------------------------------------------------
|
||||
set_phase "Security Configuration"
|
||||
|
||||
setup_security "$FILESYSTEM"
|
||||
configure_ssh "$USERNAME"
|
||||
install_ca_certificates
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Phase 9: Profile Installation
|
||||
#---------------------------------------------------------------------------
|
||||
set_phase "Profile Installation"
|
||||
|
||||
select_and_install_profile "$USERNAME"
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Phase 10: Finalization
|
||||
#---------------------------------------------------------------------------
|
||||
set_phase "Finalization"
|
||||
|
||||
configure_usbguard
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Finish
|
||||
#---------------------------------------------------------------------------
|
||||
finalize_logging
|
||||
|
||||
echo -e "\n\n\n\n\n"
|
||||
print_success "Installation complete!"
|
||||
|
||||
show_ssh_fingerprint
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
|
||||
273
lib/core/common.sh
Normal file
273
lib/core/common.sh
Normal file
@@ -0,0 +1,273 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# common.sh - Core utility functions for messaging and user interaction
|
||||
#
|
||||
# Provides colored output, user prompts, and progress tracking used throughout
|
||||
# the installer.
|
||||
|
||||
# Color codes for terminal output
|
||||
readonly COLOR_RED='\033[0;31m'
|
||||
readonly COLOR_GREEN='\033[0;32m'
|
||||
readonly COLOR_YELLOW='\033[0;33m'
|
||||
readonly COLOR_BLUE='\033[0;34m'
|
||||
readonly COLOR_CYAN='\033[0;36m'
|
||||
readonly COLOR_BG_GRAY='\033[48;5;236m'
|
||||
readonly COLOR_RESET='\033[0m'
|
||||
|
||||
# Current installation phase (set by set_phase)
|
||||
CURRENT_PHASE=""
|
||||
|
||||
# Step counter for progress indicator
|
||||
CURRENT_STEP=0
|
||||
TOTAL_STEPS=10
|
||||
|
||||
# Print the installer banner (call once at start)
|
||||
print_banner() {
|
||||
echo ""
|
||||
echo -e "${COLOR_BLUE}:: ${INSTALLER_NAME} ::${COLOR_RESET}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Print a standard message with arrow prefix
|
||||
print() {
|
||||
echo -e "${COLOR_BLUE}→${COLOR_RESET} $1"
|
||||
}
|
||||
|
||||
# Print an informational message
|
||||
print_info() {
|
||||
echo -e "${COLOR_CYAN}ℹ${COLOR_RESET} $1"
|
||||
}
|
||||
|
||||
# Run a command with gray background for its output
|
||||
# Use this for commands that produce visible output (fdisk, pacstrap, pacman, etc.)
|
||||
# Logs the command before execution for auditing
|
||||
run_visible_cmd() {
|
||||
log_cmd "$@"
|
||||
echo -ne "${COLOR_BG_GRAY}"
|
||||
"$@"
|
||||
local exit_code=$?
|
||||
echo -e "${COLOR_RESET}"
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
# Run a command with piped input and gray background for its output
|
||||
# Logs the command (without the piped input) before execution
|
||||
# Arguments:
|
||||
# $1 - input to pipe to the command
|
||||
# $@ - command and arguments
|
||||
run_piped_cmd() {
|
||||
local input="$1"
|
||||
shift
|
||||
log_cmd "$@"
|
||||
echo -ne "${COLOR_BG_GRAY}"
|
||||
echo -n "$input" | "$@"
|
||||
local exit_code=$?
|
||||
echo -e "${COLOR_RESET}"
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
# Run a command with logging only (no visual wrapper)
|
||||
# Use for commands that need stdout preserved (pipes, redirections)
|
||||
run_cmd() {
|
||||
log_cmd "$@"
|
||||
"$@"
|
||||
}
|
||||
|
||||
# Run a command that is allowed to fail
|
||||
# Logs the command, suppresses stderr, and always returns success
|
||||
# Use for cleanup commands where failure is acceptable
|
||||
run_cmd_allow_fail() {
|
||||
log_cmd "$@"
|
||||
"$@" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Run a command in the chroot environment with logging
|
||||
# Use for commands that don't produce visible output
|
||||
run_cmd_in_chroot() {
|
||||
log_cmd arch-chroot "${MOUNT_POINT}" "$@"
|
||||
arch-chroot "${MOUNT_POINT}" "$@"
|
||||
}
|
||||
|
||||
# Run a command in the chroot environment with gray background
|
||||
# Use for commands that produce visible output (pacman, mkinitcpio, etc.)
|
||||
run_visible_cmd_in_chroot() {
|
||||
log_cmd arch-chroot "${MOUNT_POINT}" "$@"
|
||||
echo -ne "${COLOR_BG_GRAY}"
|
||||
arch-chroot "${MOUNT_POINT}" "$@"
|
||||
local exit_code=$?
|
||||
echo -e "${COLOR_RESET}"
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
# Print an installation step/phase header with progress indicator
|
||||
print_step() {
|
||||
local step="$1"
|
||||
CURRENT_STEP=$((CURRENT_STEP + 1))
|
||||
CURRENT_PHASE="$step"
|
||||
echo ""
|
||||
echo -e "${COLOR_BLUE}=== [${CURRENT_STEP}/${TOTAL_STEPS}] ${step} ===${COLOR_RESET}"
|
||||
}
|
||||
|
||||
# Print a success message
|
||||
print_success() {
|
||||
echo -e "${COLOR_GREEN}[OK]${COLOR_RESET} $1"
|
||||
}
|
||||
|
||||
# Print a warning message
|
||||
print_warning() {
|
||||
echo -e "${COLOR_YELLOW}[WARNING]${COLOR_RESET} $1"
|
||||
}
|
||||
|
||||
# Print an error message
|
||||
print_error() {
|
||||
echo -e "${COLOR_RED}[ERROR]${COLOR_RESET} $1" >&2
|
||||
}
|
||||
|
||||
# Ask for yes/no confirmation
|
||||
# Arguments:
|
||||
# $1 - prompt message
|
||||
# Returns:
|
||||
# 0 if user confirms, 1 otherwise
|
||||
confirm() {
|
||||
local prompt="$1"
|
||||
local response
|
||||
|
||||
print "${prompt} [y/N]: "
|
||||
read -r response
|
||||
|
||||
case "$response" in
|
||||
[yY][eE][sS]|[yY])
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Require "I am sure" confirmation for destructive operations
|
||||
# Arguments:
|
||||
# $1 - warning message
|
||||
# Returns:
|
||||
# 0 if user confirms, 1 otherwise
|
||||
require_confirmation() {
|
||||
local warning="$1"
|
||||
local response
|
||||
|
||||
print "${warning}"
|
||||
print "Enter 'I am sure' exactly to confirm, or anything else to cancel."
|
||||
read -r response
|
||||
|
||||
if [ "$response" = "I am sure" ]; then
|
||||
return 0
|
||||
else
|
||||
print "Confirmation failed. Exiting..."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Prompt for user input
|
||||
# Arguments:
|
||||
# $1 - prompt message
|
||||
# $2 - variable name to store result
|
||||
prompt() {
|
||||
local prompt_msg="$1"
|
||||
local var_name="$2"
|
||||
local response
|
||||
|
||||
print "$prompt_msg"
|
||||
read -r response
|
||||
eval "$var_name='$response'"
|
||||
}
|
||||
|
||||
# Prompt for secret input (no echo)
|
||||
# Arguments:
|
||||
# $1 - prompt message
|
||||
# $2 - variable name to store result
|
||||
prompt_secret() {
|
||||
local prompt_msg="$1"
|
||||
local var_name="$2"
|
||||
local response
|
||||
|
||||
print "$prompt_msg"
|
||||
read -rs response
|
||||
echo
|
||||
eval "$var_name='$response'"
|
||||
}
|
||||
|
||||
# Prompt for password with confirmation
|
||||
# Arguments:
|
||||
# $1 - prompt message
|
||||
# $2 - variable name to store result
|
||||
# Returns:
|
||||
# 0 on success, 1 if passwords don't match
|
||||
prompt_password() {
|
||||
local prompt_msg="$1"
|
||||
local var_name="$2"
|
||||
local password
|
||||
local password_confirm
|
||||
|
||||
print "$prompt_msg"
|
||||
read -rs password
|
||||
echo
|
||||
|
||||
print "Please confirm your password."
|
||||
read -rs password_confirm
|
||||
echo
|
||||
|
||||
if [ "$password" != "$password_confirm" ]; then
|
||||
print_error "Passwords do not match."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval "$var_name='$password'"
|
||||
unset password password_confirm
|
||||
return 0
|
||||
}
|
||||
|
||||
# Display a menu and get user selection
|
||||
# Arguments:
|
||||
# $1 - menu title
|
||||
# $@ - menu options (remaining arguments)
|
||||
# Returns:
|
||||
# Selected option number in MENU_SELECTION variable
|
||||
prompt_menu() {
|
||||
local title="$1"
|
||||
shift
|
||||
local options=("$@")
|
||||
local i=1
|
||||
|
||||
print "$title"
|
||||
for option in "${options[@]}"; do
|
||||
print " $i - $option"
|
||||
((i++))
|
||||
done
|
||||
|
||||
read -r MENU_SELECTION
|
||||
}
|
||||
|
||||
# Wait for user to press enter
|
||||
wait_for_enter() {
|
||||
local message="${1:-Press enter to continue.}"
|
||||
print "$message"
|
||||
read -r
|
||||
}
|
||||
|
||||
# Get the directory containing the main script
|
||||
get_script_dir() {
|
||||
echo "$SCRIPT_DIR"
|
||||
}
|
||||
126
lib/core/error.sh
Normal file
126
lib/core/error.sh
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# error.sh - Error handling framework
|
||||
#
|
||||
# Implements robust error handling for the installation process:
|
||||
# - Sets up bash strict mode (set -e) and ERR trap
|
||||
# - Provides detailed error messages with phase, line number, and failed command
|
||||
# - Offers automatic cleanup on failure (unmount filesystems, close LUKS)
|
||||
# - Includes retry helper for transient failures
|
||||
|
||||
# Enable strict error handling
|
||||
set -e
|
||||
|
||||
# Set up error trap
|
||||
trap_errors() {
|
||||
trap 'handle_error $? $LINENO "$BASH_COMMAND"' ERR
|
||||
}
|
||||
|
||||
# Error handler function
|
||||
# Arguments:
|
||||
# $1 - exit code
|
||||
# $2 - line number
|
||||
# $3 - failed command
|
||||
handle_error() {
|
||||
local exit_code=$1
|
||||
local line_number=$2
|
||||
local command="$3"
|
||||
|
||||
echo ""
|
||||
print_error "Installation failed!"
|
||||
print_error "Phase: ${CURRENT_PHASE:-unknown}"
|
||||
print_error "Exit code: $exit_code at line $line_number"
|
||||
print_error "Command: $command"
|
||||
echo ""
|
||||
|
||||
# Offer cleanup
|
||||
print "Would you like to attempt cleanup? [y/N]: "
|
||||
read -r response
|
||||
|
||||
case "$response" in
|
||||
[yY][eE][sS]|[yY])
|
||||
cleanup_on_error
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Cleanup function for error recovery
|
||||
cleanup_on_error() {
|
||||
print_warning "Cleaning up after error..."
|
||||
|
||||
# Unmount filesystems (ignore errors)
|
||||
run_cmd_allow_fail umount -R "${MOUNT_POINT}"
|
||||
|
||||
# Close LUKS containers (ignore errors)
|
||||
run_cmd_allow_fail cryptsetup close cryptroot
|
||||
run_cmd_allow_fail cryptsetup close cryptroot-primary
|
||||
run_cmd_allow_fail cryptsetup close cryptroot-secondary
|
||||
|
||||
print "Cleanup complete. You may retry the installation."
|
||||
}
|
||||
|
||||
# Set the current installation phase for better error messages
|
||||
# Arguments:
|
||||
# $1 - phase name
|
||||
set_phase() {
|
||||
CURRENT_PHASE="$1"
|
||||
print_step "$1"
|
||||
}
|
||||
|
||||
# Run a command with error context
|
||||
# Arguments:
|
||||
# $1 - description
|
||||
# $@ - command and arguments
|
||||
safe_run() {
|
||||
local description="$1"
|
||||
shift
|
||||
|
||||
log_cmd "$@"
|
||||
print " $description..."
|
||||
if ! "$@"; then
|
||||
print_error "Failed: $description"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Retry a command with exponential backoff
|
||||
# Arguments:
|
||||
# $1 - max attempts
|
||||
# $2 - initial delay in seconds
|
||||
# $@ - command and arguments
|
||||
retry() {
|
||||
local max_attempts="$1"
|
||||
local delay="$2"
|
||||
shift 2
|
||||
|
||||
log_cmd "$@"
|
||||
local attempt=1
|
||||
while [ $attempt -le $max_attempts ]; do
|
||||
if "$@"; then
|
||||
return 0
|
||||
fi
|
||||
print_warning "Attempt $attempt/$max_attempts failed. Retrying in ${delay}s..."
|
||||
sleep "$delay"
|
||||
((attempt++))
|
||||
((delay *= 2))
|
||||
done
|
||||
|
||||
print_error "All $max_attempts attempts failed."
|
||||
return 1
|
||||
}
|
||||
78
lib/core/logging.sh
Normal file
78
lib/core/logging.sh
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# logging.sh - Installation logging
|
||||
#
|
||||
# Captures the complete installation session for troubleshooting:
|
||||
# - Redirects all stdout/stderr to both console and log file
|
||||
# - Records timestamps at start and end of installation
|
||||
# - Includes git commit hash for version tracking
|
||||
# - Copies final log to /var/log/arch-install.log on installed system
|
||||
|
||||
# Temp location during installation (installed system's /var/log doesn't exist yet)
|
||||
LOG_FILE_TEMP="/tmp/arch-install.log"
|
||||
# Final location on the installed system
|
||||
LOG_FILE="/var/log/arch-install.log"
|
||||
GITEA_URL="https://git.logal.dev/LogalDeveloper/Arch-Linux-Installer"
|
||||
|
||||
# Print installer URL with commit if available
|
||||
print_installer_url() {
|
||||
if command -v git &>/dev/null && git -C "$SCRIPT_DIR" rev-parse --git-dir &>/dev/null 2>&1; then
|
||||
local commit
|
||||
commit=$(git -C "$SCRIPT_DIR" rev-parse HEAD)
|
||||
echo "=== Installer: ${GITEA_URL}/commit/${commit} ==="
|
||||
else
|
||||
echo "=== Installer: ${GITEA_URL} ==="
|
||||
fi
|
||||
}
|
||||
|
||||
# Initialize logging - tee all output to log file
|
||||
init_logging() {
|
||||
# Create log file with secure permissions
|
||||
touch "$LOG_FILE_TEMP"
|
||||
chown root:root "$LOG_FILE_TEMP"
|
||||
chmod 640 "$LOG_FILE_TEMP"
|
||||
|
||||
# Redirect stdout and stderr to both console and log
|
||||
exec > >(tee -a "$LOG_FILE_TEMP") 2>&1
|
||||
|
||||
# Write log header
|
||||
echo "=== Installation started at $(date) ==="
|
||||
print_installer_url
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Log a command before execution
|
||||
# Arguments:
|
||||
# $@ - command and arguments to log
|
||||
log_cmd() {
|
||||
echo -e "\033[0;35m[CMD]\033[0m $*"
|
||||
}
|
||||
|
||||
# Copy log file to installed system
|
||||
finalize_logging() {
|
||||
local final_log="${MOUNT_POINT}${LOG_FILE}"
|
||||
|
||||
# Write log footer
|
||||
echo ""
|
||||
echo "=== Installation finished at $(date) ==="
|
||||
print_installer_url
|
||||
echo "=== Log saved to: ${LOG_FILE} ==="
|
||||
|
||||
cp "$LOG_FILE_TEMP" "$final_log"
|
||||
chown root:root "$final_log"
|
||||
chmod 640 "$final_log"
|
||||
}
|
||||
138
lib/core/validation.sh
Normal file
138
lib/core/validation.sh
Normal file
@@ -0,0 +1,138 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# validation.sh - Input validation functions
|
||||
#
|
||||
# Validates user input (disks, usernames, menu selections) to prevent errors
|
||||
# during installation.
|
||||
|
||||
# Validate that a disk exists
|
||||
# Arguments:
|
||||
# $1 - disk path (e.g., /dev/sda)
|
||||
# Returns:
|
||||
# 0 if disk exists, 1 otherwise
|
||||
validate_disk_exists() {
|
||||
local disk="$1"
|
||||
|
||||
if [ ! -b "$disk" ]; then
|
||||
print_error "Disk '$disk' does not exist or is not a block device."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Validate that a disk is not mounted
|
||||
# Arguments:
|
||||
# $1 - disk path
|
||||
# Returns:
|
||||
# 0 if not mounted, 1 if mounted
|
||||
validate_disk_not_mounted() {
|
||||
local disk="$1"
|
||||
|
||||
if mount | grep -q "^${disk}"; then
|
||||
print_error "Disk '$disk' appears to be mounted. Please unmount it first."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Validate that two disks are different (for RAID1)
|
||||
# Arguments:
|
||||
# $1 - first disk path
|
||||
# $2 - second disk path
|
||||
# Returns:
|
||||
# 0 if different, 1 if same
|
||||
validate_disks_different() {
|
||||
local disk1="$1"
|
||||
local disk2="$2"
|
||||
|
||||
if [ "$disk1" = "$disk2" ]; then
|
||||
print_error "Both disks must be different. You entered the same disk twice."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Validate username format
|
||||
# Arguments:
|
||||
# $1 - username
|
||||
# Returns:
|
||||
# 0 if valid, 1 otherwise
|
||||
validate_username() {
|
||||
local username="$1"
|
||||
|
||||
# Check if empty
|
||||
if [ -z "$username" ]; then
|
||||
print_error "Username cannot be empty."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check length (max 32 chars)
|
||||
if [ ${#username} -gt 32 ]; then
|
||||
print_error "Username must be 32 characters or less."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check format (lowercase letters, digits, underscore, hyphen; must start with letter)
|
||||
if ! [[ "$username" =~ ^[a-z][a-z0-9_-]*$ ]]; then
|
||||
print_error "Username must start with a lowercase letter and contain only lowercase letters, digits, underscores, and hyphens."
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Validate menu selection is in range
|
||||
# Arguments:
|
||||
# $1 - selection
|
||||
# $2 - minimum value
|
||||
# $3 - maximum value
|
||||
# Returns:
|
||||
# 0 if valid, 1 otherwise
|
||||
validate_menu_selection() {
|
||||
local selection="$1"
|
||||
local min="$2"
|
||||
local max="$3"
|
||||
|
||||
# Check if it's a number
|
||||
if ! [[ "$selection" =~ ^[0-9]+$ ]]; then
|
||||
print_error "Please enter a number between $min and $max."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check range
|
||||
if [ "$selection" -lt "$min" ] || [ "$selection" -gt "$max" ]; then
|
||||
print_error "Please enter a number between $min and $max."
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Validate password is not empty
|
||||
# Arguments:
|
||||
# $1 - password
|
||||
# Returns:
|
||||
# 0 if valid, 1 otherwise
|
||||
validate_password_not_empty() {
|
||||
local password="$1"
|
||||
|
||||
if [ -z "$password" ]; then
|
||||
print_error "Password cannot be empty."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
37
lib/desktop/drivers.sh
Normal file
37
lib/desktop/drivers.sh
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# drivers.sh - Graphics driver installation
|
||||
#
|
||||
# Prompts the user to select and install graphics drivers (Intel, NVIDIA, or skip).
|
||||
# Driver package arrays are defined in config/drivers.conf.
|
||||
|
||||
# Prompt user for graphics driver selection and install
|
||||
prompt_install_graphics() {
|
||||
prompt_menu "Would you like to install graphics drivers?" "Intel" "NVIDIA" "Skip"
|
||||
|
||||
case "$MENU_SELECTION" in
|
||||
1)
|
||||
print "Installing Intel graphics drivers..."
|
||||
chroot_pacman_install "${INTEL_PACKAGES[@]}"
|
||||
;;
|
||||
2)
|
||||
print "Installing NVIDIA graphics drivers..."
|
||||
chroot_pacman_install "${NVIDIA_PACKAGES[@]}"
|
||||
;;
|
||||
*) print "Skipping graphics driver installation." ;;
|
||||
esac
|
||||
}
|
||||
48
lib/desktop/kde.sh
Normal file
48
lib/desktop/kde.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# kde.sh - KDE Plasma desktop environment installation
|
||||
#
|
||||
# Installs KDE Plasma with SDDM display manager.
|
||||
# KDE_PACKAGES is defined in config/profiles.conf.
|
||||
|
||||
# Install KDE base packages
|
||||
install_kde_packages() {
|
||||
chroot_pacman_install "${KDE_PACKAGES[@]}"
|
||||
}
|
||||
|
||||
# Copy desktop skeleton files to user home
|
||||
# Arguments:
|
||||
# $1 - username
|
||||
copy_desktop_skel() {
|
||||
local username="$1"
|
||||
local home_dir="${MOUNT_POINT}/home/${username}"
|
||||
|
||||
run_visible_cmd cp -r "${HOME_SKEL_DESKTOP_DIR}/." "${home_dir}/"
|
||||
run_visible_cmd rm -f "${home_dir}/.gitkeep"
|
||||
run_visible_cmd chown -R 1000:1000 "${home_dir}"
|
||||
}
|
||||
|
||||
# Full KDE installation
|
||||
# Arguments:
|
||||
# $1 - username
|
||||
install_kde() {
|
||||
local username="$1"
|
||||
|
||||
install_kde_packages
|
||||
chroot_systemd_enable sddm.service
|
||||
copy_desktop_skel "$username"
|
||||
}
|
||||
184
lib/disk/filesystem.sh
Normal file
184
lib/disk/filesystem.sh
Normal file
@@ -0,0 +1,184 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# filesystem.sh - Filesystem creation and mounting functions
|
||||
#
|
||||
# Creates and mounts filesystems on encrypted volumes:
|
||||
# - Formats EFI partition as FAT32
|
||||
# - Supports ext4, BTRFS, BTRFS with DUP, and BTRFS RAID1
|
||||
# - Uses xxhash checksum for BTRFS filesystems
|
||||
# - Mounts root with noatime and appropriate discard options
|
||||
# - Mounts EFI partition at /boot with restrictive permissions
|
||||
|
||||
# Format a partition as FAT32 (for EFI)
|
||||
# Arguments:
|
||||
# $1 - partition path
|
||||
format_efi_partition() {
|
||||
local partition="$1"
|
||||
|
||||
print "Formatting ${partition} as FAT32..."
|
||||
run_visible_cmd mkfs.fat -F 32 "$partition"
|
||||
}
|
||||
|
||||
# Format a device as ext4
|
||||
# Arguments:
|
||||
# $1 - device path (e.g., /dev/mapper/cryptroot)
|
||||
format_ext4() {
|
||||
local device="$1"
|
||||
|
||||
print "Formatting ${device} as ext4..."
|
||||
run_visible_cmd mkfs.ext4 "$device"
|
||||
}
|
||||
|
||||
# Format a device as BTRFS (single disk)
|
||||
# Arguments:
|
||||
# $1 - device path
|
||||
format_btrfs() {
|
||||
local device="$1"
|
||||
|
||||
print "Formatting ${device} as btrfs..."
|
||||
run_visible_cmd mkfs.btrfs --csum xxhash "$device"
|
||||
}
|
||||
|
||||
# Format a device as BTRFS with dup profile
|
||||
# Arguments:
|
||||
# $1 - device path
|
||||
format_btrfs_dup() {
|
||||
local device="$1"
|
||||
|
||||
print "Formatting ${device} as btrfs with dup profile..."
|
||||
run_visible_cmd mkfs.btrfs --csum xxhash --data dup --metadata dup "$device"
|
||||
}
|
||||
|
||||
# Format two devices as BTRFS RAID1
|
||||
# Arguments:
|
||||
# $1 - first device path
|
||||
# $2 - second device path
|
||||
format_btrfs_raid1() {
|
||||
local device1="$1"
|
||||
local device2="$2"
|
||||
|
||||
print "Formatting ${device1} and ${device2} as btrfs RAID1..."
|
||||
run_visible_cmd mkfs.btrfs --csum xxhash --data raid1 --metadata raid1 "$device1" "$device2"
|
||||
}
|
||||
|
||||
# Format three devices as BTRFS RAID1 with enhanced metadata redundancy
|
||||
# Arguments:
|
||||
# $1 - first device path
|
||||
# $2 - second device path
|
||||
# $3 - third device path
|
||||
format_btrfs_raid1_3disk() {
|
||||
local device1="$1"
|
||||
local device2="$2"
|
||||
local device3="$3"
|
||||
|
||||
print "Formatting ${device1}, ${device2}, and ${device3} as btrfs RAID1..."
|
||||
run_visible_cmd mkfs.btrfs --csum xxhash --data raid1 --metadata raid1c3 "$device1" "$device2" "$device3"
|
||||
}
|
||||
|
||||
# Format the root filesystem based on configuration
|
||||
# Arguments:
|
||||
# $1 - filesystem type (ext4, btrfs, btrfs-dup)
|
||||
# $2 - storage mode (single, raid1, raid1-3disk)
|
||||
format_root_filesystem() {
|
||||
local filesystem="$1"
|
||||
local storage_mode="$2"
|
||||
|
||||
case "$filesystem" in
|
||||
"ext4")
|
||||
if [ "$storage_mode" = "raid1" ] || [ "$storage_mode" = "raid1-3disk" ]; then
|
||||
print_error "ext4 cannot be used with RAID1."
|
||||
exit 1
|
||||
fi
|
||||
format_ext4 /dev/mapper/cryptroot
|
||||
;;
|
||||
|
||||
"btrfs-dup")
|
||||
format_btrfs_dup /dev/mapper/cryptroot
|
||||
;;
|
||||
|
||||
"btrfs"|*)
|
||||
if [ "$storage_mode" = "raid1" ]; then
|
||||
format_btrfs_raid1 /dev/mapper/cryptroot-1 /dev/mapper/cryptroot-2
|
||||
elif [ "$storage_mode" = "raid1-3disk" ]; then
|
||||
format_btrfs_raid1_3disk /dev/mapper/cryptroot-1 /dev/mapper/cryptroot-2 /dev/mapper/cryptroot-3
|
||||
else
|
||||
format_btrfs /dev/mapper/cryptroot
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Mount the root filesystem with appropriate options
|
||||
# Arguments:
|
||||
# $1 - filesystem type (ext4, btrfs, btrfs-dup)
|
||||
# $2 - storage mode (single, raid1, raid1-3disk)
|
||||
mount_root_filesystem() {
|
||||
local filesystem="$1"
|
||||
local storage_mode="$2"
|
||||
|
||||
print "Mounting partitions..."
|
||||
|
||||
case "$filesystem" in
|
||||
"ext4")
|
||||
run_visible_cmd mount -o "noatime,discard" /dev/mapper/cryptroot "${MOUNT_POINT}"
|
||||
;;
|
||||
|
||||
*)
|
||||
if [ "$storage_mode" = "raid1" ]; then
|
||||
run_visible_cmd mount -o "noatime,discard=async" /dev/mapper/cryptroot-1 "${MOUNT_POINT}"
|
||||
elif [ "$storage_mode" = "raid1-3disk" ]; then
|
||||
run_visible_cmd mount -o "noatime,discard=async" /dev/mapper/cryptroot-1 "${MOUNT_POINT}"
|
||||
else
|
||||
run_visible_cmd mount -o "noatime,discard=async" /dev/mapper/cryptroot "${MOUNT_POINT}"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Mount the EFI partition
|
||||
# Arguments:
|
||||
# $1 - EFI partition path
|
||||
mount_efi_partition() {
|
||||
local efi_partition="$1"
|
||||
|
||||
run_visible_cmd mount --mkdir -o "fmask=0077,dmask=0077" "$efi_partition" "${MOUNT_POINT}/boot"
|
||||
}
|
||||
|
||||
# Format and mount all filesystems
|
||||
# Arguments:
|
||||
# $1 - filesystem type
|
||||
# $2 - storage mode
|
||||
format_and_mount_filesystems() {
|
||||
local filesystem="$1"
|
||||
local storage_mode="$2"
|
||||
|
||||
# Format EFI partition(s)
|
||||
format_efi_partition "$EFI_PARTITION"
|
||||
if [ "$storage_mode" = "raid1" ]; then
|
||||
format_efi_partition "$EFI_PARTITION_2"
|
||||
elif [ "$storage_mode" = "raid1-3disk" ]; then
|
||||
format_efi_partition "$EFI_PARTITION_2"
|
||||
format_efi_partition "$EFI_PARTITION_3"
|
||||
fi
|
||||
|
||||
# Format and mount root
|
||||
format_root_filesystem "$filesystem" "$storage_mode"
|
||||
mount_root_filesystem "$filesystem" "$storage_mode"
|
||||
|
||||
# Mount EFI
|
||||
mount_efi_partition "$EFI_PARTITION"
|
||||
}
|
||||
185
lib/disk/luks.sh
Normal file
185
lib/disk/luks.sh
Normal file
@@ -0,0 +1,185 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# luks.sh - LUKS encryption functions
|
||||
#
|
||||
# Manages full-disk encryption using LUKS2:
|
||||
# - Prompts for encryption passphrase with confirmation
|
||||
# - Formats partitions with LUKS2 using secure defaults from luks.conf
|
||||
# - Opens/unlocks encrypted containers for filesystem creation
|
||||
# - Supports both single-disk and RAID1 encryption setups
|
||||
# - Securely clears passwords from memory after use
|
||||
|
||||
# Format a partition with LUKS encryption
|
||||
# Arguments:
|
||||
# $1 - partition path
|
||||
# $2 - encryption password
|
||||
setup_luks_encryption() {
|
||||
local partition="$1"
|
||||
local password="$2"
|
||||
|
||||
print "Setting up encryption on ${partition}..."
|
||||
|
||||
run_piped_cmd "$password" cryptsetup luksFormat \
|
||||
--type "$LUKS_TYPE" \
|
||||
--cipher "$LUKS_CIPHER" \
|
||||
--hash "$LUKS_HASH" \
|
||||
--key-size "$LUKS_KEY_SIZE" \
|
||||
--pbkdf "$LUKS_PBKDF" \
|
||||
--pbkdf-force-iterations "$LUKS_PBKDF_ITERATIONS" \
|
||||
--pbkdf-memory "$LUKS_PBKDF_MEMORY" \
|
||||
--pbkdf-parallel "$LUKS_PBKDF_PARALLEL" \
|
||||
--use-urandom \
|
||||
--key-file - \
|
||||
"$partition"
|
||||
}
|
||||
|
||||
# Open (unlock) a LUKS container
|
||||
# Arguments:
|
||||
# $1 - partition path
|
||||
# $2 - encryption password
|
||||
# $3 - mapper name (e.g., cryptroot)
|
||||
open_luks_container() {
|
||||
local partition="$1"
|
||||
local password="$2"
|
||||
local mapper_name="$3"
|
||||
|
||||
print "Unlocking ${partition}..."
|
||||
|
||||
run_piped_cmd "$password" cryptsetup open \
|
||||
--allow-discards \
|
||||
--key-file - \
|
||||
"$partition" \
|
||||
"$mapper_name"
|
||||
}
|
||||
|
||||
# Get the UUID of a LUKS container
|
||||
# Arguments:
|
||||
# $1 - partition path
|
||||
# Outputs:
|
||||
# UUID to stdout
|
||||
get_luks_uuid() {
|
||||
local partition="$1"
|
||||
|
||||
run_cmd cryptsetup luksDump "$partition" | grep 'UUID:' | awk '{print $2}'
|
||||
}
|
||||
|
||||
# Close a LUKS container
|
||||
# Arguments:
|
||||
# $1 - mapper name
|
||||
close_luks_container() {
|
||||
local mapper_name="$1"
|
||||
|
||||
run_cmd_allow_fail cryptsetup close "$mapper_name"
|
||||
}
|
||||
|
||||
# Prompt for encryption password with confirmation
|
||||
# Sets:
|
||||
# ENCRYPTION_PASSWORD - the entered password
|
||||
# Returns:
|
||||
# 0 on success, 1 on mismatch
|
||||
prompt_encryption_password() {
|
||||
if ! prompt_password "Please enter your desired encryption passphrase." ENCRYPTION_PASSWORD; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Setup encryption for single-disk mode
|
||||
# Arguments:
|
||||
# $1 - root partition path
|
||||
# Sets:
|
||||
# LUKS_UUID - UUID of the LUKS container
|
||||
setup_encryption_single() {
|
||||
local root_partition="$1"
|
||||
|
||||
if ! prompt_encryption_password; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
setup_luks_encryption "$root_partition" "$ENCRYPTION_PASSWORD"
|
||||
open_luks_container "$root_partition" "$ENCRYPTION_PASSWORD" "cryptroot"
|
||||
LUKS_UUID=$(get_luks_uuid "$root_partition")
|
||||
|
||||
# Clear password from memory
|
||||
unset ENCRYPTION_PASSWORD
|
||||
}
|
||||
|
||||
# Setup encryption for RAID1 mode
|
||||
# Arguments:
|
||||
# $1 - first root partition path
|
||||
# $2 - second root partition path
|
||||
# Sets:
|
||||
# LUKS_UUID - UUID of the first LUKS container
|
||||
# LUKS_UUID_2 - UUID of the second LUKS container
|
||||
setup_encryption_raid1() {
|
||||
local root_partition_1="$1"
|
||||
local root_partition_2="$2"
|
||||
|
||||
if ! prompt_encryption_password; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup first disk
|
||||
setup_luks_encryption "$root_partition_1" "$ENCRYPTION_PASSWORD"
|
||||
open_luks_container "$root_partition_1" "$ENCRYPTION_PASSWORD" "cryptroot-1"
|
||||
LUKS_UUID=$(get_luks_uuid "$root_partition_1")
|
||||
|
||||
# Setup second disk
|
||||
setup_luks_encryption "$root_partition_2" "$ENCRYPTION_PASSWORD"
|
||||
open_luks_container "$root_partition_2" "$ENCRYPTION_PASSWORD" "cryptroot-2"
|
||||
LUKS_UUID_2=$(get_luks_uuid "$root_partition_2")
|
||||
|
||||
# Clear password from memory
|
||||
unset ENCRYPTION_PASSWORD
|
||||
}
|
||||
|
||||
# Setup encryption for RAID1 3-disk mode
|
||||
# Arguments:
|
||||
# $1 - first root partition path
|
||||
# $2 - second root partition path
|
||||
# $3 - third root partition path
|
||||
# Sets:
|
||||
# LUKS_UUID - UUID of the first LUKS container
|
||||
# LUKS_UUID_2 - UUID of the second LUKS container
|
||||
# LUKS_UUID_3 - UUID of the third LUKS container
|
||||
setup_encryption_raid1_3disk() {
|
||||
local root_partition_1="$1"
|
||||
local root_partition_2="$2"
|
||||
local root_partition_3="$3"
|
||||
|
||||
if ! prompt_encryption_password; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup first disk
|
||||
setup_luks_encryption "$root_partition_1" "$ENCRYPTION_PASSWORD"
|
||||
open_luks_container "$root_partition_1" "$ENCRYPTION_PASSWORD" "cryptroot-1"
|
||||
LUKS_UUID=$(get_luks_uuid "$root_partition_1")
|
||||
|
||||
# Setup second disk
|
||||
setup_luks_encryption "$root_partition_2" "$ENCRYPTION_PASSWORD"
|
||||
open_luks_container "$root_partition_2" "$ENCRYPTION_PASSWORD" "cryptroot-2"
|
||||
LUKS_UUID_2=$(get_luks_uuid "$root_partition_2")
|
||||
|
||||
# Setup third disk
|
||||
setup_luks_encryption "$root_partition_3" "$ENCRYPTION_PASSWORD"
|
||||
open_luks_container "$root_partition_3" "$ENCRYPTION_PASSWORD" "cryptroot-3"
|
||||
LUKS_UUID_3=$(get_luks_uuid "$root_partition_3")
|
||||
|
||||
# Clear password from memory
|
||||
unset ENCRYPTION_PASSWORD
|
||||
}
|
||||
241
lib/disk/partition.sh
Normal file
241
lib/disk/partition.sh
Normal file
@@ -0,0 +1,241 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# partition.sh - Disk partitioning functions
|
||||
#
|
||||
# Handles disk selection and GPT partitioning for both single and RAID1 modes:
|
||||
# - Interactive disk selection with confirmation prompts
|
||||
# - Wipes existing partition tables using wipefs
|
||||
# - Creates GPT layout: 1GB EFI System Partition + LUKS root partition
|
||||
# - Handles NVMe vs SATA partition naming conventions (nvme0n1p1 vs sda1)
|
||||
|
||||
# Detect the correct partition prefix for a disk
|
||||
# NVMe drives use "p" before partition number (e.g., /dev/nvme0n1p1)
|
||||
# SATA/SAS drives append number directly (e.g., /dev/sda1)
|
||||
# Arguments:
|
||||
# $1 - disk path
|
||||
# Outputs:
|
||||
# Partition prefix to stdout
|
||||
detect_partition_prefix() {
|
||||
local disk="$1"
|
||||
|
||||
if [[ "$disk" == /dev/nvme* ]]; then
|
||||
echo "${disk}p"
|
||||
else
|
||||
echo "$disk"
|
||||
fi
|
||||
}
|
||||
|
||||
# Display disk information using fdisk
|
||||
show_disk_info() {
|
||||
run_visible_cmd fdisk -l
|
||||
}
|
||||
|
||||
# Select a disk with confirmation
|
||||
# Arguments:
|
||||
# $1 - prompt message
|
||||
# $2 - variable name to store selected disk
|
||||
# Returns:
|
||||
# 0 on success, 1 on validation failure
|
||||
select_disk() {
|
||||
local prompt_msg="$1"
|
||||
local var_name="$2"
|
||||
local selected_disk
|
||||
local disk_confirm
|
||||
|
||||
print "$prompt_msg"
|
||||
read -r selected_disk
|
||||
|
||||
# Validate disk exists
|
||||
if ! validate_disk_exists "$selected_disk"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Confirm selection
|
||||
print "Please confirm your selection by entering the same path again."
|
||||
read -r disk_confirm
|
||||
|
||||
if [ "$selected_disk" != "$disk_confirm" ]; then
|
||||
print_error "The same disk was not entered both times."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval "$var_name='$selected_disk'"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Wipe all filesystem signatures from a disk
|
||||
# Arguments:
|
||||
# $1 - disk path
|
||||
wipe_disk() {
|
||||
local disk="$1"
|
||||
|
||||
print "Wiping existing partition table from $disk..."
|
||||
# Discard all sectors first (useful for SSDs, may fail on HDDs)
|
||||
run_cmd_allow_fail blkdiscard -f "$disk"
|
||||
run_visible_cmd wipefs -a "$disk"
|
||||
}
|
||||
|
||||
# Create GPT partitions for EFI + root
|
||||
# Arguments:
|
||||
# $1 - disk path
|
||||
# Sets:
|
||||
# EFI_PARTITION - path to EFI partition
|
||||
# ROOT_PARTITION - path to root partition
|
||||
create_gpt_partitions() {
|
||||
local disk="$1"
|
||||
local prefix
|
||||
|
||||
print "Partitioning $disk..."
|
||||
|
||||
# Create GPT table with 1GB EFI partition
|
||||
run_visible_cmd sgdisk --new 1:0:1G "$disk"
|
||||
run_visible_cmd sgdisk --typecode 1:ef00 "$disk"
|
||||
|
||||
# Create root partition using remaining space
|
||||
run_visible_cmd sgdisk --new 2:0:0 "$disk"
|
||||
run_visible_cmd sgdisk --typecode 2:8309 "$disk"
|
||||
|
||||
# Set partition paths
|
||||
prefix=$(detect_partition_prefix "$disk")
|
||||
EFI_PARTITION="${prefix}1"
|
||||
ROOT_PARTITION="${prefix}2"
|
||||
}
|
||||
|
||||
# Select and configure disks for single-disk installation
|
||||
# Sets:
|
||||
# INSTALL_DISK - primary disk path
|
||||
# EFI_PARTITION - EFI partition path
|
||||
# ROOT_PARTITION - root partition path
|
||||
select_single_disk() {
|
||||
show_disk_info
|
||||
|
||||
if ! select_disk "Please enter the path to the disk you would like to install Arch Linux to (e.g. /dev/sda)." INSTALL_DISK; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! require_confirmation "Last warning: Are you sure you want to install Arch Linux to '$INSTALL_DISK'? All data on this disk will be wiped."; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Select and configure disks for RAID1 installation
|
||||
# Sets:
|
||||
# INSTALL_DISK - first disk path
|
||||
# INSTALL_DISK_2 - second disk path
|
||||
# EFI_PARTITION, EFI_PARTITION_2 - EFI partition paths
|
||||
# ROOT_PARTITION, ROOT_PARTITION_2 - root partition paths
|
||||
select_raid1_disks() {
|
||||
show_disk_info
|
||||
|
||||
if ! select_disk "Please enter the path to the FIRST disk for RAID1 (e.g. /dev/sda)." INSTALL_DISK; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! select_disk "Enter the path to the SECOND disk for RAID1 (e.g. /dev/sdb). This must be a DIFFERENT disk." INSTALL_DISK_2; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! validate_disks_different "$INSTALL_DISK" "$INSTALL_DISK_2"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! require_confirmation "Last warning: Are you sure you want to install Arch Linux in RAID1 mode to '$INSTALL_DISK' and '$INSTALL_DISK_2'? All data on BOTH disks will be wiped."; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Select and configure disks for RAID1 3-disk installation
|
||||
# Sets:
|
||||
# INSTALL_DISK - first disk path
|
||||
# INSTALL_DISK_2 - second disk path
|
||||
# INSTALL_DISK_3 - third disk path
|
||||
# EFI_PARTITION, EFI_PARTITION_2, EFI_PARTITION_3 - EFI partition paths
|
||||
# ROOT_PARTITION, ROOT_PARTITION_2, ROOT_PARTITION_3 - root partition paths
|
||||
select_raid1_3disk_disks() {
|
||||
show_disk_info
|
||||
|
||||
if ! select_disk "Please enter the path to the FIRST disk for RAID1 (e.g. /dev/sda)." INSTALL_DISK; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! select_disk "Enter the path to the SECOND disk for RAID1 (e.g. /dev/sdb). This must be a DIFFERENT disk." INSTALL_DISK_2; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! select_disk "Enter the path to the THIRD disk for RAID1 (e.g. /dev/sdc). This must be a DIFFERENT disk." INSTALL_DISK_3; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! validate_disks_different "$INSTALL_DISK" "$INSTALL_DISK_2" "$INSTALL_DISK_3"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! require_confirmation "Last warning: Are you sure you want to install Arch Linux in RAID1 3-disk mode to '$INSTALL_DISK', '$INSTALL_DISK_2', and '$INSTALL_DISK_3'? All data on ALL THREE disks will be wiped."; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Partition disks based on storage mode
|
||||
# Arguments:
|
||||
# $1 - storage mode (single, raid1, or raid1-3disk)
|
||||
partition_disks() {
|
||||
local storage_mode="$1"
|
||||
|
||||
if [ "$storage_mode" = "raid1" ]; then
|
||||
wipe_disk "$INSTALL_DISK"
|
||||
wipe_disk "$INSTALL_DISK_2"
|
||||
|
||||
create_gpt_partitions "$INSTALL_DISK"
|
||||
local primary_efi="$EFI_PARTITION"
|
||||
local primary_root="$ROOT_PARTITION"
|
||||
|
||||
create_gpt_partitions "$INSTALL_DISK_2"
|
||||
EFI_PARTITION_2="$EFI_PARTITION"
|
||||
ROOT_PARTITION_2="$ROOT_PARTITION"
|
||||
|
||||
EFI_PARTITION="$primary_efi"
|
||||
ROOT_PARTITION="$primary_root"
|
||||
elif [ "$storage_mode" = "raid1-3disk" ]; then
|
||||
wipe_disk "$INSTALL_DISK"
|
||||
wipe_disk "$INSTALL_DISK_2"
|
||||
wipe_disk "$INSTALL_DISK_3"
|
||||
|
||||
create_gpt_partitions "$INSTALL_DISK"
|
||||
local primary_efi="$EFI_PARTITION"
|
||||
local primary_root="$ROOT_PARTITION"
|
||||
|
||||
create_gpt_partitions "$INSTALL_DISK_2"
|
||||
EFI_PARTITION_2="$EFI_PARTITION"
|
||||
ROOT_PARTITION_2="$ROOT_PARTITION"
|
||||
|
||||
create_gpt_partitions "$INSTALL_DISK_3"
|
||||
EFI_PARTITION_3="$EFI_PARTITION"
|
||||
ROOT_PARTITION_3="$ROOT_PARTITION"
|
||||
|
||||
EFI_PARTITION="$primary_efi"
|
||||
ROOT_PARTITION="$primary_root"
|
||||
else
|
||||
wipe_disk "$INSTALL_DISK"
|
||||
create_gpt_partitions "$INSTALL_DISK"
|
||||
fi
|
||||
}
|
||||
101
lib/system/base.sh
Normal file
101
lib/system/base.sh
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# base.sh - Base system installation functions
|
||||
#
|
||||
# Installs and configures the core Arch Linux system:
|
||||
# - Runs pacstrap with base packages defined in defaults.conf
|
||||
# - Detects CPU vendor and installs appropriate microcode (Intel/AMD)
|
||||
# - Generates /etc/fstab with UUIDs
|
||||
# - Provides high-level chroot helpers for common operations
|
||||
# - Copies configuration files from installer to target system
|
||||
|
||||
# Install packages in the chroot environment using pacman
|
||||
# Arguments:
|
||||
# $@ - package names
|
||||
chroot_pacman_install() {
|
||||
run_visible_cmd_in_chroot pacman --noconfirm -S "$@"
|
||||
}
|
||||
|
||||
# Enable systemd units in the chroot environment
|
||||
# Arguments:
|
||||
# $@ - unit names (services, timers, etc.)
|
||||
chroot_systemd_enable() {
|
||||
for unit in "$@"; do
|
||||
print "Enabling ${unit}..."
|
||||
done
|
||||
run_visible_cmd_in_chroot systemctl enable "$@"
|
||||
}
|
||||
|
||||
# Install base Arch Linux packages
|
||||
install_base_packages() {
|
||||
print "Installing Arch Linux base..."
|
||||
|
||||
run_visible_cmd pacstrap -K "${MOUNT_POINT}" "${BASE_PACKAGES[@]}"
|
||||
}
|
||||
|
||||
# Detect CPU vendor
|
||||
# Outputs:
|
||||
# "intel", "amd", or "unknown"
|
||||
detect_cpu_vendor() {
|
||||
local vendor
|
||||
vendor=$(grep -m 1 'vendor_id' /proc/cpuinfo | awk '{print $3}')
|
||||
|
||||
case "$vendor" in
|
||||
"GenuineIntel")
|
||||
echo "intel"
|
||||
;;
|
||||
"AuthenticAMD")
|
||||
echo "amd"
|
||||
;;
|
||||
*)
|
||||
echo "unknown"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Install CPU microcode based on detected vendor
|
||||
install_microcode() {
|
||||
local vendor
|
||||
vendor=$(detect_cpu_vendor)
|
||||
|
||||
print "Installing CPU microcode..."
|
||||
|
||||
case "$vendor" in
|
||||
"intel")
|
||||
chroot_pacman_install intel-ucode
|
||||
;;
|
||||
"amd")
|
||||
chroot_pacman_install amd-ucode
|
||||
;;
|
||||
*)
|
||||
print_warning "Unknown CPU vendor: ${vendor}. Please install microcode manually after installation, if available."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Generate /etc/fstab
|
||||
generate_fstab() {
|
||||
print "Generating /etc/fstab..."
|
||||
log_cmd genfstab -U "${MOUNT_POINT}"
|
||||
genfstab -U "${MOUNT_POINT}" >> "${MOUNT_POINT}/etc/fstab"
|
||||
}
|
||||
|
||||
# Copy configuration files from installer to target system
|
||||
copy_config_files() {
|
||||
print "Installing default configuration files..."
|
||||
run_visible_cmd cp -r "${CONFIG_SRC_DIR}" "${MOUNT_POINT}"
|
||||
}
|
||||
108
lib/system/bootloader.sh
Normal file
108
lib/system/bootloader.sh
Normal file
@@ -0,0 +1,108 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# bootloader.sh - systemd-boot configuration
|
||||
#
|
||||
# Installs and configures systemd-boot as the bootloader:
|
||||
# - Runs bootctl install to set up EFI boot manager
|
||||
# - Creates boot entry with LUKS unlock parameters
|
||||
# - Supports both single-disk and RAID1 configurations
|
||||
# - Configures loader.conf timeout setting
|
||||
|
||||
# Install systemd-boot bootloader
|
||||
install_bootloader() {
|
||||
print "Installing bootloader..."
|
||||
run_visible_cmd_in_chroot bootctl install
|
||||
}
|
||||
|
||||
# Create boot entry for single-disk installation
|
||||
# Arguments:
|
||||
# $1 - LUKS UUID
|
||||
create_boot_entry_single() {
|
||||
local luks_uuid="$1"
|
||||
|
||||
run_cmd_in_chroot sh -c "cat > /boot/loader/entries/arch.conf" <<EOF
|
||||
title Arch Linux
|
||||
linux /vmlinuz-linux
|
||||
initrd /initramfs-linux.img
|
||||
options rd.luks.name=${luks_uuid}=cryptroot rd.luks.options=discard root=/dev/mapper/cryptroot
|
||||
EOF
|
||||
}
|
||||
|
||||
# Create boot entry for RAID1 installation
|
||||
# Arguments:
|
||||
# $1 - first LUKS UUID
|
||||
# $2 - second LUKS UUID
|
||||
create_boot_entry_raid1() {
|
||||
local luks_uuid_1="$1"
|
||||
local luks_uuid_2="$2"
|
||||
|
||||
run_cmd_in_chroot sh -c "cat > /boot/loader/entries/arch.conf" <<EOF
|
||||
title Arch Linux
|
||||
linux /vmlinuz-linux
|
||||
initrd /initramfs-linux.img
|
||||
options rd.luks.name=${luks_uuid_1}=cryptroot-1 rd.luks.name=${luks_uuid_2}=cryptroot-2 rd.luks.options=${luks_uuid_1}=discard rd.luks.options=${luks_uuid_2}=discard root=/dev/mapper/cryptroot-1
|
||||
EOF
|
||||
}
|
||||
|
||||
# Create boot entry for RAID1 3-disk installation
|
||||
# Arguments:
|
||||
# $1 - first LUKS UUID
|
||||
# $2 - second LUKS UUID
|
||||
# $3 - third LUKS UUID
|
||||
create_boot_entry_raid1_3disk() {
|
||||
local luks_uuid_1="$1"
|
||||
local luks_uuid_2="$2"
|
||||
local luks_uuid_3="$3"
|
||||
|
||||
run_cmd_in_chroot sh -c "cat > /boot/loader/entries/arch.conf" <<EOF
|
||||
title Arch Linux
|
||||
linux /vmlinuz-linux
|
||||
initrd /initramfs-linux.img
|
||||
options rd.luks.name=${luks_uuid_1}=cryptroot-1 rd.luks.name=${luks_uuid_2}=cryptroot-2 rd.luks.name=${luks_uuid_3}=cryptroot-3 rd.luks.options=${luks_uuid_1}=discard rd.luks.options=${luks_uuid_2}=discard rd.luks.options=${luks_uuid_3}=discard root=/dev/mapper/cryptroot-1
|
||||
EOF
|
||||
}
|
||||
|
||||
# Create appropriate boot entry based on storage mode
|
||||
# Arguments:
|
||||
# $1 - storage mode (single, raid1, raid1-3disk)
|
||||
create_boot_entry() {
|
||||
local storage_mode="$1"
|
||||
|
||||
if [ "$storage_mode" = "raid1" ]; then
|
||||
create_boot_entry_raid1 "$LUKS_UUID" "$LUKS_UUID_2"
|
||||
elif [ "$storage_mode" = "raid1-3disk" ]; then
|
||||
create_boot_entry_raid1_3disk "$LUKS_UUID" "$LUKS_UUID_2" "$LUKS_UUID_3"
|
||||
else
|
||||
create_boot_entry_single "$LUKS_UUID"
|
||||
fi
|
||||
}
|
||||
|
||||
# Configure loader.conf timeout
|
||||
configure_loader() {
|
||||
run_cmd_in_chroot sed -i '/^#timeout 3/s/^#//' /boot/loader/loader.conf
|
||||
}
|
||||
|
||||
# Full bootloader setup
|
||||
# Arguments:
|
||||
# $1 - storage mode
|
||||
setup_bootloader() {
|
||||
local storage_mode="$1"
|
||||
|
||||
install_bootloader
|
||||
create_boot_entry "$storage_mode"
|
||||
configure_loader
|
||||
}
|
||||
43
lib/system/locale.sh
Normal file
43
lib/system/locale.sh
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# locale.sh - Locale and timezone configuration
|
||||
#
|
||||
# Configures locale (en_US.UTF-8) and runs systemd-firstboot for timezone,
|
||||
# keymap, and hostname setup.
|
||||
|
||||
# Configure system locale (en_US.UTF-8)
|
||||
configure_locale() {
|
||||
print "Setting up locale..."
|
||||
|
||||
run_cmd_in_chroot sed -i '/^#.*en_US.UTF-8 UTF-8/s/^#//' /etc/locale.gen
|
||||
run_visible_cmd_in_chroot locale-gen
|
||||
run_cmd_in_chroot systemd-firstboot --locale=en_US.UTF-8
|
||||
}
|
||||
|
||||
# Run interactive firstboot setup for timezone, keymap, hostname
|
||||
run_firstboot() {
|
||||
print "Entering first time setup..."
|
||||
print "Your keymap is probably 'us' and the time zone is probably 'America/New_York'."
|
||||
|
||||
run_visible_cmd_in_chroot systemd-firstboot --prompt
|
||||
}
|
||||
|
||||
# Full locale and timezone setup
|
||||
setup_locale() {
|
||||
configure_locale
|
||||
run_firstboot
|
||||
}
|
||||
78
lib/system/network.sh
Normal file
78
lib/system/network.sh
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# network.sh - Network configuration
|
||||
#
|
||||
# Handles network setup for both installation and target system:
|
||||
# - Verifies internet connectivity before installation begins
|
||||
# - Checks system time synchronization via systemd-timesyncd
|
||||
# - Configures pacman mirrorlist to use private mirror
|
||||
# - Enables systemd-resolved, systemd-networkd, and systemd-timesyncd
|
||||
# - Optionally installs iwd for Wi-Fi support
|
||||
|
||||
# Check internet connectivity
|
||||
check_internet() {
|
||||
print "Checking internet connectivity..."
|
||||
|
||||
if curl -s --head "$INTERNET_CHECK_URL" | grep "200" >/dev/null; then
|
||||
print_success "Internet connection is available!"
|
||||
return 0
|
||||
else
|
||||
print_error "Internet connection appears not available (HTTP request to \"$INTERNET_CHECK_URL\" failed). Please check network settings and re-run this script."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check system time synchronization
|
||||
check_time_sync() {
|
||||
print "Checking system time synchronization state..."
|
||||
|
||||
if timedatectl status | grep -q "System clock synchronized: yes"; then
|
||||
print_success "System time is synchronized!"
|
||||
return 0
|
||||
else
|
||||
print_error "The system time is not synchronized. Please check systemd-timesyncd and re-run this script."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Configure pacman mirrorlist
|
||||
configure_mirrorlist() {
|
||||
print "Setting mirrorlist to use private mirror..."
|
||||
echo "Server = ${MIRROR_URL}" > /etc/pacman.d/mirrorlist
|
||||
}
|
||||
|
||||
# Enable systemd-resolved and configure resolv.conf symlink
|
||||
enable_resolved() {
|
||||
chroot_systemd_enable systemd-resolved.service
|
||||
run_visible_cmd ln -sf ../run/systemd/resolve/stub-resolv.conf "${MOUNT_POINT}/etc/resolv.conf"
|
||||
}
|
||||
|
||||
# Prompt and install iwd for Wi-Fi support
|
||||
prompt_install_wifi() {
|
||||
if confirm "Would you like to install iwd for Wi-Fi support?"; then
|
||||
print "Installing iwd..."
|
||||
chroot_pacman_install iwd
|
||||
chroot_systemd_enable iwd.service
|
||||
fi
|
||||
}
|
||||
|
||||
# Full network setup
|
||||
setup_network() {
|
||||
enable_resolved
|
||||
chroot_systemd_enable systemd-networkd.service
|
||||
chroot_systemd_enable systemd-timesyncd.service
|
||||
}
|
||||
145
lib/system/profiles.sh
Normal file
145
lib/system/profiles.sh
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# profiles.sh - Profile registry and management
|
||||
#
|
||||
# Provides functions to list, select, and install profiles.
|
||||
# Profile definitions are loaded from config/profiles.conf.
|
||||
# Adding a new profile requires only adding entries to the config file.
|
||||
|
||||
# Get profile key by menu number (1-based)
|
||||
# Arguments:
|
||||
# $1 - menu selection number
|
||||
# Outputs:
|
||||
# Profile key to stdout
|
||||
get_profile_key() {
|
||||
local index=$(($1 - 1))
|
||||
echo "${PROFILES[$index]}"
|
||||
}
|
||||
|
||||
# Display available profiles (auto-generated from PROFILES array)
|
||||
list_profiles() {
|
||||
print "Base install complete. Select profile to install for this system:"
|
||||
|
||||
local i=1
|
||||
for profile in "${PROFILES[@]}"; do
|
||||
local name_var="PROFILE_${profile}_NAME"
|
||||
local desc_var="PROFILE_${profile}_DESC"
|
||||
print " $i - ${!name_var}"
|
||||
print " ${!desc_var}"
|
||||
((i++))
|
||||
done
|
||||
}
|
||||
|
||||
# Get packages for a profile
|
||||
# Arguments:
|
||||
# $1 - profile key
|
||||
# Outputs:
|
||||
# Package list to stdout
|
||||
get_profile_packages() {
|
||||
local profile_key="$1"
|
||||
local pkg_var="PROFILE_${profile_key}_PACKAGES[@]"
|
||||
echo "${!pkg_var}"
|
||||
}
|
||||
|
||||
# Get services for a profile
|
||||
# Arguments:
|
||||
# $1 - profile key
|
||||
# Outputs:
|
||||
# Service list to stdout
|
||||
get_profile_services() {
|
||||
local profile_key="$1"
|
||||
local svc_var="PROFILE_${profile_key}_SERVICES[@]"
|
||||
echo "${!svc_var}"
|
||||
}
|
||||
|
||||
# Check if profile requires KDE
|
||||
# Arguments:
|
||||
# $1 - profile key
|
||||
# Returns:
|
||||
# 0 if requires KDE, 1 otherwise
|
||||
profile_requires_kde() {
|
||||
local profile_key="$1"
|
||||
local kde_var="PROFILE_${profile_key}_KDE"
|
||||
[ "${!kde_var}" = "true" ]
|
||||
}
|
||||
|
||||
# Validate profile selection
|
||||
# Arguments:
|
||||
# $1 - user selection
|
||||
# Returns:
|
||||
# 0 if valid, 1 otherwise
|
||||
validate_profile_selection() {
|
||||
local selection="$1"
|
||||
local max="${#PROFILES[@]}"
|
||||
[[ "$selection" =~ ^[1-9][0-9]*$ ]] && [ "$selection" -le "$max" ]
|
||||
}
|
||||
|
||||
# Install a profile
|
||||
# Arguments:
|
||||
# $1 - profile key
|
||||
# $2 - username
|
||||
install_profile() {
|
||||
local profile_key="$1"
|
||||
local username="$2"
|
||||
local packages
|
||||
local services
|
||||
|
||||
# Install KDE if required
|
||||
if profile_requires_kde "$profile_key"; then
|
||||
install_kde "$username"
|
||||
prompt_install_graphics
|
||||
fi
|
||||
|
||||
# Get and install profile packages
|
||||
packages=$(get_profile_packages "$profile_key")
|
||||
if [ -n "$packages" ]; then
|
||||
# shellcheck disable=SC2086
|
||||
chroot_pacman_install $packages
|
||||
fi
|
||||
|
||||
# Add user to wireshark group if wireshark was installed
|
||||
if run_cmd_in_chroot getent group wireshark > /dev/null 2>&1; then
|
||||
run_cmd_in_chroot usermod -aG wireshark "$username"
|
||||
fi
|
||||
|
||||
# Enable profile services
|
||||
services=$(get_profile_services "$profile_key")
|
||||
for service in $services; do
|
||||
chroot_systemd_enable "$service"
|
||||
done
|
||||
}
|
||||
|
||||
# Prompt user for profile selection and install
|
||||
# Arguments:
|
||||
# $1 - username
|
||||
select_and_install_profile() {
|
||||
local username="$1"
|
||||
local selection
|
||||
local profile_key
|
||||
|
||||
list_profiles
|
||||
read -r selection
|
||||
|
||||
# Validate selection
|
||||
if ! validate_profile_selection "$selection"; then
|
||||
print_warning "Unknown profile, defaulting to minimal install."
|
||||
selection="1"
|
||||
fi
|
||||
|
||||
profile_key=$(get_profile_key "$selection")
|
||||
install_profile "$profile_key" "$username"
|
||||
}
|
||||
123
lib/system/security.sh
Normal file
123
lib/system/security.sh
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# security.sh - Security hardening functions
|
||||
#
|
||||
# Applies security hardening to the installed system:
|
||||
# - Configures mkinitcpio with sd-encrypt hook for LUKS
|
||||
# - Enables sudo access for wheel group
|
||||
# - Disables root account login
|
||||
# - Enables nftables firewall, smartd, and fstrim timer
|
||||
# - Configures OpenSSH with restricted settings
|
||||
# - Installs custom CA certificates from certs directory to system trust store
|
||||
# - Sets up USBGuard to whitelist connected devices
|
||||
|
||||
# Configure mkinitcpio hooks for encrypted root
|
||||
configure_initramfs() {
|
||||
print "Configuring initramfs..."
|
||||
|
||||
local default_line="HOOKS=(base systemd autodetect microcode modconf kms keyboard keymap sd-vconsole block filesystems fsck)"
|
||||
local new_line="HOOKS=(systemd autodetect microcode modconf kms keyboard sd-vconsole block sd-encrypt filesystems fsck)"
|
||||
|
||||
run_cmd_in_chroot sed -i "s|^${default_line}|${new_line}|" /etc/mkinitcpio.conf
|
||||
run_visible_cmd_in_chroot mkinitcpio -P
|
||||
}
|
||||
|
||||
# Enable BTRFS scrub timer if using BTRFS filesystem
|
||||
# Arguments:
|
||||
# $1 - filesystem type
|
||||
enable_btrfs_scrub() {
|
||||
local filesystem="$1"
|
||||
|
||||
if [ "$filesystem" = "btrfs" ] || [ "$filesystem" = "btrfs-dup" ]; then
|
||||
chroot_systemd_enable btrfs-scrub@-.timer
|
||||
fi
|
||||
}
|
||||
|
||||
# Configure sudo access for wheel group
|
||||
configure_sudo() {
|
||||
print "Enabling sudo access for wheel group..."
|
||||
run_cmd_in_chroot sed -i "s|^# %wheel ALL=(ALL:ALL) ALL|%wheel ALL=(ALL:ALL) ALL|" /etc/sudoers
|
||||
}
|
||||
|
||||
# Disable root account login
|
||||
disable_root() {
|
||||
print "Disabling root account..."
|
||||
run_cmd_in_chroot passwd -l root
|
||||
}
|
||||
|
||||
# Configure SSH server
|
||||
# Arguments:
|
||||
# $1 - username to allow SSH access
|
||||
configure_ssh() {
|
||||
local username="$1"
|
||||
|
||||
print "Setting up and enabling OpenSSH server..."
|
||||
|
||||
run_cmd_in_chroot sed -i "s|PLACEHOLDER|${username}|" /etc/ssh/sshd_config
|
||||
run_visible_cmd_in_chroot ssh-keygen -t ed25519 -C "" -N "" -f /etc/ssh/ssh_host_ed25519_key
|
||||
chroot_systemd_enable sshd.service
|
||||
}
|
||||
|
||||
# Display SSH host key fingerprint
|
||||
show_ssh_fingerprint() {
|
||||
print "Public SSH key fingerprint of this host:"
|
||||
run_visible_cmd_in_chroot ssh-keygen -lvf /etc/ssh/ssh_host_ed25519_key.pub
|
||||
}
|
||||
|
||||
# Install custom CA certificates from certs directory
|
||||
install_ca_certificates() {
|
||||
local certs=("${CA_CERTS_DIR}"/*.crt)
|
||||
|
||||
if [ ! -e "${certs[0]}" ]; then
|
||||
print "No CA certificates found to install."
|
||||
return
|
||||
fi
|
||||
|
||||
for cert in "${certs[@]}"; do
|
||||
local cert_name
|
||||
cert_name=$(basename "$cert")
|
||||
print "Adding ${cert_name} to system CA store..."
|
||||
|
||||
run_visible_cmd cp "$cert" "${MOUNT_POINT}/${cert_name}"
|
||||
run_cmd_in_chroot trust anchor --store "/${cert_name}"
|
||||
run_cmd_in_chroot rm "/${cert_name}"
|
||||
done
|
||||
}
|
||||
|
||||
# Configure USBGuard
|
||||
configure_usbguard() {
|
||||
print "Please add or remove any USB devices, including the installer drive, to form the standard configuration for this system. USBGuard will be configured to only allow the USB devices connected at the time you press enter to be used; everything else will be blocked."
|
||||
print "When ready to proceed, press enter."
|
||||
read -r
|
||||
|
||||
run_cmd_in_chroot sh -c "usbguard generate-policy > /etc/usbguard/rules.conf"
|
||||
chroot_systemd_enable usbguard.service
|
||||
}
|
||||
|
||||
# Full security setup
|
||||
# Arguments:
|
||||
# $1 - filesystem type
|
||||
setup_security() {
|
||||
local filesystem="$1"
|
||||
|
||||
configure_sudo
|
||||
disable_root
|
||||
chroot_systemd_enable nftables.service
|
||||
chroot_systemd_enable smartd.service
|
||||
chroot_systemd_enable fstrim.timer
|
||||
enable_btrfs_scrub "$filesystem"
|
||||
}
|
||||
90
lib/system/user.sh
Normal file
90
lib/system/user.sh
Normal file
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2026 Logan Fick
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# user.sh - User account management
|
||||
#
|
||||
# Creates the primary user account with wheel group membership.
|
||||
|
||||
# Prompt for username
|
||||
# Sets:
|
||||
# USERNAME - the entered username
|
||||
prompt_username() {
|
||||
while true; do
|
||||
prompt "Please enter the username you'd like to use for your account:" USERNAME
|
||||
|
||||
if validate_username "$USERNAME"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
print "Please try again."
|
||||
done
|
||||
}
|
||||
|
||||
# Prompt for display name
|
||||
# Sets:
|
||||
# DISPLAY_NAME - the entered display name (empty if skipped)
|
||||
prompt_display_name() {
|
||||
prompt "Please enter your display name, or press Enter to skip (e.g., John Smith):" DISPLAY_NAME
|
||||
}
|
||||
|
||||
# Copy base skeleton files to user home
|
||||
# Arguments:
|
||||
# $1 - username
|
||||
copy_home_skel() {
|
||||
local username="$1"
|
||||
local home_dir="${MOUNT_POINT}/home/${username}"
|
||||
|
||||
run_visible_cmd cp -r "${HOME_SKEL_DIR}/." "${home_dir}/"
|
||||
run_visible_cmd rm -f "${home_dir}/.gitkeep"
|
||||
run_visible_cmd chown -R 1000:1000 "${home_dir}"
|
||||
}
|
||||
|
||||
# Create a user account
|
||||
# Arguments:
|
||||
# $1 - username
|
||||
# $2 - display name (optional)
|
||||
create_user() {
|
||||
local username="$1"
|
||||
local display_name="${2:-}"
|
||||
|
||||
if [[ -n "$display_name" ]]; then
|
||||
run_cmd_in_chroot useradd -m -G wheel -c "$display_name" "$username"
|
||||
else
|
||||
run_cmd_in_chroot useradd -m -G wheel "$username"
|
||||
fi
|
||||
copy_home_skel "$username"
|
||||
}
|
||||
|
||||
# Set password for a user
|
||||
# Arguments:
|
||||
# $1 - username
|
||||
set_user_password() {
|
||||
local username="$1"
|
||||
|
||||
print "Please set the password for your new account."
|
||||
run_visible_cmd_in_chroot passwd "$username"
|
||||
}
|
||||
|
||||
# Full user setup
|
||||
# Sets:
|
||||
# USERNAME - the created username
|
||||
# DISPLAY_NAME - the user's display name
|
||||
setup_user() {
|
||||
prompt_username
|
||||
prompt_display_name
|
||||
create_user "$USERNAME" "$DISPLAY_NAME"
|
||||
set_user_password "$USERNAME"
|
||||
}
|
||||
Reference in New Issue
Block a user