mirror of
https://github.com/stornic56/debianito-post-install.git
synced 2026-10-31 21:18:08 +00:00
Integrity fixes: atomic writes, rc checks, curl
- Verified the repository backup rc in migrate.sh. `_persistent_backup_repos()` now aborts before any destructive step when the tar archive is missing or corrupt, removing the invalid backup itself. - Verified the restore rc in migrate.sh. `_restore_backup()` now reports failure instead of printing a green "Backup restored" message, and its caller distinguishes a restored backup from a failed restore. - Made the upgrade phases fail honestly. `apt upgrade` and `apt full-upgrade` now abort with `apt --fix-broken install` + `dpkg --configure -a` instructions instead of reporting "completed successfully" over a partially-upgraded system. - Made repository source writes atomic. Both writers tee into a `.tmp` file and move into place, and the `/etc/apt/sources.list` removal now happens after the write. - Corrected the SID warning text in migrate.sh to reflect that security fixes arrive via package uploads rather than a nonexistent sid-security suite. - Added exit-path cleanup traps for the temp `.deb` files: `trap 'rm -f "$deb_path"' RETURN` in `heroic.sh`and `tools.sh`. - Tightened Heroic download handling: the API fetch uses `curl -fsS --connect-timeout 10 --max-time 30`, the download uses `curl -fsSL --max-time 300`, and the `.deb` is validated with both `dpkg-deb --info` and `dpkg-deb --contents`; `sudo apt install` is guarded with `if !` before the temporary file is deleted. - Tightened the OpenRGB install path: the Codeberg API fetch uses `curl -fsS --connect-timeout 10 --max-time 30` and the download `curl -fsSL --max-time 300`, with the same dual `dpkg-deb` integrity check as the Heroic fallback. - Guarded the OpenRGB post-install side effects: the script now aborts after removing the failed `.deb` if `sudo apt install` returns non-zero, so `modprobe i2c-dev`, `/etc/modules`, the `i2c` group membership, `udevadm`, and the `setcap` step no longer run when the package is absent.
This commit is contained in:
@@ -131,6 +131,10 @@ install_nvidia_bullseye() {
|
||||
# 32-bit Mesa/NVIDIA + gamemode + mangohud + goverlay + lutris
|
||||
# ---------------------------------------------------------------------------
|
||||
install_gaming_bullseye() {
|
||||
# Hardware state is detected on demand; GPU_TYPE is
|
||||
# consulted below for the 32-bit driver choice.
|
||||
_ensure_state_detected
|
||||
|
||||
echo -e "${YELLOW}Gaming setup (Bullseye)...${NC}"
|
||||
|
||||
local choices
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
manage_desktop_display() {
|
||||
# Hardware state is detected on demand; HAS_NVIDIA
|
||||
# (GDM3 Wayland option) is populated by the battery.
|
||||
_ensure_state_detected
|
||||
|
||||
while true; do
|
||||
local choice
|
||||
choice=$(_menu "Desktop & Display" \
|
||||
|
||||
@@ -346,6 +346,10 @@ _ensure_nonfree_repo() {
|
||||
|
||||
# ── Main entry point ──
|
||||
install_firmware() {
|
||||
# Hardware state is detected on demand; the device
|
||||
# arrays and DESKTOP_ENV/AUDIO_SERVER are read below.
|
||||
_ensure_state_detected
|
||||
|
||||
echo -e "${YELLOW}Base firmware check...${NC}"
|
||||
|
||||
if ! _ensure_nonfree_repo; then
|
||||
|
||||
@@ -69,6 +69,10 @@ ensure_contrib_repo() {
|
||||
}
|
||||
|
||||
install_gaming() {
|
||||
# Hardware state is detected on demand; GPU_TYPE is
|
||||
# consulted below for the NVIDIA hints.
|
||||
_ensure_state_detected
|
||||
|
||||
echo -e "${YELLOW}Gaming setup...${NC}"
|
||||
|
||||
# 1. Single checklist with ALL options (including i386 toggle)
|
||||
|
||||
@@ -4,12 +4,18 @@
|
||||
install_heroic() {
|
||||
local heroic_deb
|
||||
heroic_deb=$(mktemp "${TMPDIR:-/tmp}/heroic-XXXXXX.deb") || return 1
|
||||
# Remove the temp file on every exit path (RETURN
|
||||
# traps are not inherited by called functions).
|
||||
trap 'rm -f "$heroic_deb"' RETURN
|
||||
local ua="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
|
||||
_run_cmd "Heroic" "sudo apt install -y curl jq" "Installing dependencies..."
|
||||
|
||||
local json
|
||||
json=$(curl -s --connect-timeout 10 -H "User-Agent: $ua" \
|
||||
# -f: HTTP >= 400 must fail; --max-time bounds the
|
||||
# whole transfer (connect-timeout only covers the
|
||||
# connection phase).
|
||||
json=$(curl -fsS --connect-timeout 10 --max-time 30 -H "User-Agent: $ua" \
|
||||
"https://api.github.com/repos/Heroic-Games-Launcher/HeroicGamesLauncher/releases/latest") || {
|
||||
_msg "Heroic Error" "Could not fetch release data from GitHub API." 8 60
|
||||
return 1
|
||||
@@ -28,16 +34,23 @@ install_heroic() {
|
||||
# Strict URL validation before interpolating into a shell command (_run_cmd uses bash -c)
|
||||
[[ "$deb_url" =~ ^https://[A-Za-z0-9./_-]+\.deb$ ]] || { _msg "Error" "Invalid download URL: $deb_url"; return 1; }
|
||||
|
||||
_run_cmd "Heroic" "curl -fsSL -H 'User-Agent: $ua' -o '$heroic_deb' '$deb_url'" "Downloading Heroic..."
|
||||
_run_cmd "Heroic" "curl -fsSL --max-time 300 -H 'User-Agent: $ua' -o '$heroic_deb' '$deb_url'" "Downloading Heroic..."
|
||||
|
||||
if ! dpkg-deb --info "$heroic_deb" >/dev/null 2>&1; then
|
||||
# --info reads only the control archive; --contents
|
||||
# also walks the data archive, catching deb files
|
||||
# truncated after the control section.
|
||||
if ! dpkg-deb --info "$heroic_deb" >/dev/null 2>&1 || \
|
||||
! dpkg-deb --contents "$heroic_deb" >/dev/null 2>&1; then
|
||||
_msg "Heroic Error" "Downloaded .deb is corrupted or truncated.\n\nRemoving file." 10 60
|
||||
rm -f "$heroic_deb"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Package integrity verified.${NC}"
|
||||
_run_cmd "Heroic" "sudo apt install -y '$heroic_deb'" "Installing Heroic..."
|
||||
if ! _run_cmd "Heroic" "sudo apt install -y '$heroic_deb'" "Installing Heroic..."; then
|
||||
rm -f "$heroic_deb"
|
||||
return 1
|
||||
fi
|
||||
rm -f "$heroic_deb"
|
||||
echo -e "${GREEN}Heroic Games Launcher installed.${NC}"
|
||||
}
|
||||
|
||||
+19
-4
@@ -38,12 +38,16 @@ install_openrgb() {
|
||||
|
||||
local deb_path
|
||||
deb_path=$(mktemp "${TMPDIR:-/tmp}/openrgb-XXXXXX.deb") || return 1
|
||||
# Remove the temp file on every exit path.
|
||||
trap 'rm -f "$deb_path"' RETURN
|
||||
local ua="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
|
||||
_run_cmd "OpenRGB" "sudo apt install -y curl jq" "Installing dependencies..."
|
||||
|
||||
local json=""
|
||||
json=$(curl -s --connect-timeout 10 \
|
||||
# -f: HTTP >= 400 must fail; --max-time bounds the
|
||||
# whole transfer.
|
||||
json=$(curl -fsS --connect-timeout 10 --max-time 30 \
|
||||
"https://codeberg.org/api/v1/repos/OpenRGB/OpenRGB/releases?limit=1") || {
|
||||
echo -e "${RED}Could not fetch OpenRGB releases from Codeberg API.${NC}"
|
||||
return 1
|
||||
@@ -66,7 +70,7 @@ install_openrgb() {
|
||||
# Strict URL validation before interpolating into a shell command (_run_cmd uses bash -c)
|
||||
[[ "$deb_url" =~ ^https://[A-Za-z0-9./_-]+\.deb$ ]] || { _msg "Error" "Invalid download URL: $deb_url"; return 1; }
|
||||
|
||||
_run_cmd "OpenRGB" "curl -fsSL -o '${deb_path}' -A '${ua}' '${deb_url}'" "Downloading OpenRGB..."
|
||||
_run_cmd "OpenRGB" "curl -fsSL --max-time 300 -o '${deb_path}' -A '${ua}' '${deb_url}'" "Downloading OpenRGB..."
|
||||
|
||||
if [ -n "$sha256" ]; then
|
||||
if ! echo "$sha256 $deb_path" | sha256sum -c --strict; then
|
||||
@@ -76,7 +80,11 @@ install_openrgb() {
|
||||
fi
|
||||
echo -e "${GREEN}SHA256 verified.${NC}"
|
||||
else
|
||||
if ! dpkg-deb --info "$deb_path" >/dev/null 2>&1; then
|
||||
# --contents also walks the data archive,
|
||||
# catching deb files truncated after the
|
||||
# control section.
|
||||
if ! dpkg-deb --info "$deb_path" >/dev/null 2>&1 || \
|
||||
! dpkg-deb --contents "$deb_path" >/dev/null 2>&1; then
|
||||
echo -e "${RED}Downloaded .deb is corrupted. Removing.${NC}"
|
||||
rm -f "$deb_path"
|
||||
return 1
|
||||
@@ -84,7 +92,14 @@ install_openrgb() {
|
||||
echo -e "${YELLOW}No SHA256 in API, validated via dpkg-deb.${NC}"
|
||||
fi
|
||||
|
||||
sudo apt install -y "$deb_path"
|
||||
# The post-install steps (i2c group, udev rules)
|
||||
# must only run if the package was actually
|
||||
# installed; apt can fail on missing dependencies.
|
||||
if ! sudo apt install -y "$deb_path"; then
|
||||
rm -f "$deb_path"
|
||||
echo -e "${RED}OpenRGB installation failed (dependency or dpkg error).${NC}"
|
||||
return 1
|
||||
fi
|
||||
rm -f "$deb_path"
|
||||
|
||||
sudo modprobe i2c-dev
|
||||
|
||||
@@ -28,6 +28,10 @@ _warn_nvidia_gnome_wayland() {
|
||||
}
|
||||
|
||||
_install_amd_intel_stack() {
|
||||
# Hardware state is detected on demand; GPU_TYPE and
|
||||
# LSPCI_OUTPUT must be populated before planning.
|
||||
_ensure_state_detected
|
||||
|
||||
if [ "$GPU_TYPE" = "unknown" ] || [ -z "$GPU_TYPE" ]; then
|
||||
local mesa_pkgs=(mesa-vulkan-drivers libgl1-mesa-dri libglx-mesa0 libegl-mesa0 mesa-va-drivers)
|
||||
local ref_ver
|
||||
@@ -174,6 +178,10 @@ _apply_amd_gcn_grub_fix() {
|
||||
}
|
||||
|
||||
_install_nvidia_stack() {
|
||||
# Hardware state is detected on demand; HAS_NVIDIA and
|
||||
# LSPCI_OUTPUT must be populated before planning.
|
||||
_ensure_state_detected
|
||||
|
||||
if ! $HAS_NVIDIA; then
|
||||
_msg "NVIDIA Not Found" "No NVIDIA GPU was detected.\n\nPlease check your hardware and try again." 10 60
|
||||
return
|
||||
|
||||
@@ -33,6 +33,10 @@ show_kernel_menu() {
|
||||
}
|
||||
|
||||
_install_kernel_package() {
|
||||
# Hardware state is detected on demand; GPU_TYPE drives
|
||||
# the NVIDIA DKMS/RT warnings below.
|
||||
_ensure_state_detected
|
||||
|
||||
local pkg_base="$1"
|
||||
local flavor="$2"
|
||||
local bpo_flag="$3"
|
||||
|
||||
+68
-16
@@ -13,7 +13,13 @@ _persistent_backup_repos() {
|
||||
[ -f /etc/apt/sources.list ] && files+=("/etc/apt/sources.list")
|
||||
[ -d /etc/apt/sources.list.d ] && files+=("/etc/apt/sources.list.d")
|
||||
[ ${#files[@]} -eq 0 ] && return 1
|
||||
sudo tar czf "$_MIGRATE_BACKUP" "${files[@]}" 2>/dev/null
|
||||
# The rollback promise depends on this archive:
|
||||
# a failed backup must abort before anything is
|
||||
# deleted or rewritten.
|
||||
if ! sudo tar czf "$_MIGRATE_BACKUP" "${files[@]}" 2>/dev/null; then
|
||||
rm -f "$_MIGRATE_BACKUP"
|
||||
return 1
|
||||
fi
|
||||
echo "Backup: $_MIGRATE_BACKUP"
|
||||
}
|
||||
|
||||
@@ -23,7 +29,13 @@ _restore_backup() {
|
||||
return 1
|
||||
fi
|
||||
echo -e "${YELLOW}Restoring repository backup...${NC}"
|
||||
sudo tar xzf "$_MIGRATE_BACKUP" -C / 2>/dev/null
|
||||
if ! sudo tar xzf "$_MIGRATE_BACKUP" -C / 2>/dev/null; then
|
||||
_msg_red "Restore Failed" \
|
||||
"Extraction of $_MIGRATE_BACKUP failed.\n\
|
||||
/etc/apt sources may be missing or inconsistent.\n\n\
|
||||
Restore manually:\n sudo tar xzf $_MIGRATE_BACKUP -C /" 12 70
|
||||
return 1
|
||||
fi
|
||||
echo -e "${GREEN}Backup restored from $_MIGRATE_BACKUP${NC}"
|
||||
}
|
||||
|
||||
@@ -64,7 +76,14 @@ _write_deb822_branch() {
|
||||
fi
|
||||
|
||||
sudo mkdir -p /etc/apt/sources.list.d
|
||||
echo -e "$main_content" | sudo tee "$main_file" >/dev/null
|
||||
# Stage to a temp file and move atomically: an
|
||||
# interrupted write never leaves a partial sources.
|
||||
if ! echo -e "$main_content" | sudo tee "${main_file}.tmp" >/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
if ! sudo mv "${main_file}.tmp" "$main_file"; then
|
||||
return 1
|
||||
fi
|
||||
echo "Wrote $main_file"
|
||||
}
|
||||
|
||||
@@ -80,7 +99,14 @@ _write_classic_branch() {
|
||||
main_content+="deb https://security.debian.org/debian-security ${target}-security main contrib non-free non-free-firmware\n"
|
||||
fi
|
||||
|
||||
echo -e "$main_content" | sudo tee "$main_file" >/dev/null
|
||||
# Stage to a temp file and move atomically: an
|
||||
# interrupted write never leaves a partial sources.
|
||||
if ! echo -e "$main_content" | sudo tee "${main_file}.tmp" >/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
if ! sudo mv "${main_file}.tmp" "$main_file"; then
|
||||
return 1
|
||||
fi
|
||||
echo "Wrote $main_file"
|
||||
}
|
||||
|
||||
@@ -93,7 +119,8 @@ Risks include:\n\
|
||||
• NVIDIA / DKMS drivers may break\n\
|
||||
• System may fail to boot after reboot\n\
|
||||
• Some packages may be removed or replaced\n\
|
||||
• SID (unstable) receives NO security updates\n\n\
|
||||
• SID (unstable) has no release cycle; security\n\
|
||||
fixes arrive via package uploads, not DSAs\n\n\
|
||||
A full persistent backup will be saved to /var/backups/\n\
|
||||
so you can restore if things go wrong." 16 70
|
||||
|
||||
@@ -154,16 +181,20 @@ so you can restore if things go wrong." 16 70
|
||||
[ -f /etc/apt/sources.list.d/debian-backports.sources ] && sudo rm -f /etc/apt/sources.list.d/debian-backports.sources
|
||||
[ -f /etc/apt/sources.list.d/debian-backports.list ] && sudo rm -f /etc/apt/sources.list.d/debian-backports.list
|
||||
|
||||
# 4c. Remove any old classic source files to avoid conflicts
|
||||
[ -f /etc/apt/sources.list ] && sudo rm -f /etc/apt/sources.list
|
||||
|
||||
# 4d. Write new sources
|
||||
# 4c. Write new sources
|
||||
if ! _write_branch_sources "$target"; then
|
||||
echo -e "${RED}[-]${NC} Failed to write new sources. Restoring backup..."
|
||||
_restore_backup || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 4d. Remove the old classic file AFTER the new
|
||||
# sources are live: an interrupt between steps
|
||||
# leaves both files (recoverable), not none.
|
||||
# Only needed on the deb822 path; the classic
|
||||
# writer replaced sources.list atomically.
|
||||
[ "$DEBIAN_VERSION" = "13" ] && [ -f /etc/apt/sources.list ] && sudo rm -f /etc/apt/sources.list
|
||||
|
||||
# 4e. SID guardrails: install bug alerts before upgrade
|
||||
if [ "$target" = "sid" ]; then
|
||||
echo -e "${YELLOW}Installing apt-listbugs and apt-listchanges (SID guardrails)...${NC}"
|
||||
@@ -175,19 +206,40 @@ so you can restore if things go wrong." 16 70
|
||||
echo -e "${YELLOW}Running apt update...${NC}"
|
||||
if ! sudo apt update; then
|
||||
echo -e "${RED}apt update failed. Restoring backup...${NC}"
|
||||
_restore_backup
|
||||
_msg_red "Migration Failed" \
|
||||
"apt update failed. Backup has been restored from:\n\
|
||||
if _restore_backup; then
|
||||
_msg_red "Migration Failed" \
|
||||
"apt update failed. Backup has been restored from:\n\
|
||||
$_MIGRATE_BACKUP\n\n\
|
||||
Your system should be back to its previous state.\n\
|
||||
Run 'sudo apt update' manually to verify." 12 70
|
||||
return
|
||||
else
|
||||
_msg_red "Migration Failed" \
|
||||
"apt update failed AND the backup could not be\n\
|
||||
restored from $_MIGRATE_BACKUP.\n\n\
|
||||
Restore it manually:\n sudo tar xzf $_MIGRATE_BACKUP -C /\n\
|
||||
then run: sudo apt update" 12 70
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 4g. Full upgrade
|
||||
_run_cmd "Upgrade" "sudo apt upgrade -y" "Upgrading packages..."
|
||||
_run_cmd "Full-Upgrade" "sudo apt full-upgrade -y" "Running full-upgrade..."
|
||||
_run_cmd "Autoremove" "sudo apt autoremove -y" "Removing obsolete packages..."
|
||||
# A failed upgrade leaves packages half-installed.
|
||||
# Restoring only the sources would be worse (stable
|
||||
# sources + testing packages), so stop with an
|
||||
# honest report and manual-recovery instructions.
|
||||
if ! _run_cmd "Upgrade" "sudo apt upgrade -y" "Upgrading packages..."; then
|
||||
_msg_red "Migration Incomplete" \
|
||||
"Package upgrade failed. The system is partially\n\
|
||||
upgraded to ${target}. Do NOT reboot yet.\n\n\
|
||||
Repair with:\n sudo apt --fix-broken install\n sudo dpkg --configure -a\n\n\
|
||||
To roll back the branch manually, restore:\n sudo tar xzf $_MIGRATE_BACKUP -C /" 14 70
|
||||
return 1
|
||||
fi
|
||||
if ! _run_cmd "Full-Upgrade" "sudo apt full-upgrade -y" "Running full-upgrade..."; then
|
||||
_msg_red "Migration Incomplete" "full-upgrade failed. Repair with:\n sudo apt --fix-broken install\nBackup: $_MIGRATE_BACKUP" 10 70
|
||||
return 1
|
||||
fi
|
||||
_run_cmd "Autoremove" "sudo apt autoremove -y" "Removing obsolete packages..." || true
|
||||
|
||||
# 4h. Re-run detection to reflect new branch
|
||||
echo -e "${YELLOW}Re-running system detection for new branch...${NC}"
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
# Any active (non-commented) source of any format, in any file?
|
||||
# Returns: 0 if at least one active source exists, 1 otherwise
|
||||
has_active_deb_sources() {
|
||||
local apt_dir="${APT_DIR:-/etc/apt}"
|
||||
local f
|
||||
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list; do
|
||||
for f in "$apt_dir/sources.list" "$apt_dir/sources.list.d/"*.list; do
|
||||
[ -f "$f" ] || continue
|
||||
grep -qE '^[^#]*\bdeb\b' "$f" 2>/dev/null && return 0
|
||||
done
|
||||
for f in /etc/apt/sources.list.d/*.sources; do
|
||||
for f in "$apt_dir/sources.list.d/"*.sources; do
|
||||
[ -f "$f" ] || continue
|
||||
grep -qE '^Types:.*\bdeb\b' "$f" 2>/dev/null && \
|
||||
grep -qE '^URIs:' "$f" 2>/dev/null && return 0
|
||||
@@ -22,10 +23,11 @@ has_active_deb_sources() {
|
||||
# DEB822 is only valid on Debian 13 (Trixie); Debian 11/12 are classic-only.
|
||||
# Returns: "deb822", "classic", or "none"
|
||||
detect_repo_format() {
|
||||
if [ "$DEBIAN_VERSION" = "13" ] && [ -f /etc/apt/sources.list.d/debian.sources ] && \
|
||||
grep -qE '^Types:.*\bdeb\b' /etc/apt/sources.list.d/debian.sources 2>/dev/null; then
|
||||
local apt_dir="${APT_DIR:-/etc/apt}"
|
||||
if [ "$DEBIAN_VERSION" = "13" ] && [ -f "$apt_dir/sources.list.d/debian.sources" ] && \
|
||||
grep -qE '^Types:.*\bdeb\b' "$apt_dir/sources.list.d/debian.sources" 2>/dev/null; then
|
||||
echo "deb822"
|
||||
elif [ -f /etc/apt/sources.list ] && grep -qE '^[^#]*\bdeb\b' /etc/apt/sources.list 2>/dev/null; then
|
||||
elif [ -f "$apt_dir/sources.list" ] && grep -qE '^[^#]*\bdeb\b' "$apt_dir/sources.list" 2>/dev/null; then
|
||||
echo "classic"
|
||||
else
|
||||
echo "none"
|
||||
@@ -35,17 +37,18 @@ detect_repo_format() {
|
||||
# Detect the components currently active in the main repo file
|
||||
# Returns: components list (e.g. "main contrib non-free non-free-firmware")
|
||||
detect_active_components() {
|
||||
if [ "$DEBIAN_VERSION" = "13" ] && [ -f /etc/apt/sources.list.d/debian.sources ]; then
|
||||
local apt_dir="${APT_DIR:-/etc/apt}"
|
||||
if [ "$DEBIAN_VERSION" = "13" ] && [ -f "$apt_dir/sources.list.d/debian.sources" ]; then
|
||||
local comps
|
||||
comps=$(grep "^Components:" /etc/apt/sources.list.d/debian.sources 2>/dev/null | head -1 | cut -d: -f2- | sed 's/^[[:space:]]*//')
|
||||
comps=$(grep "^Components:" "$apt_dir/sources.list.d/debian.sources" 2>/dev/null | head -1 | cut -d: -f2- | sed 's/^[[:space:]]*//')
|
||||
if [ -n "$comps" ]; then
|
||||
echo "$comps"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
if [ -f /etc/apt/sources.list ]; then
|
||||
if [ -f "$apt_dir/sources.list" ]; then
|
||||
local comps
|
||||
comps=$(grep "^[^#]*deb .* main" /etc/apt/sources.list 2>/dev/null | head -1 | sed 's/.*main\s*//')
|
||||
comps=$(grep "^[^#]*deb .* main" "$apt_dir/sources.list" 2>/dev/null | head -1 | sed 's/.*main\s*//')
|
||||
if [ -n "$comps" ]; then
|
||||
echo "main $comps"
|
||||
return
|
||||
@@ -61,17 +64,18 @@ detect_active_components() {
|
||||
# Check whether backports are currently enabled (any format, any file)
|
||||
# Returns: 0 if enabled, 1 otherwise
|
||||
detect_backports_status() {
|
||||
local apt_dir="${APT_DIR:-/etc/apt}"
|
||||
local codename="$1"
|
||||
|
||||
if [ "$DEBIAN_VERSION" = "13" ] && [ -f /etc/apt/sources.list.d/debian.sources ]; then
|
||||
grep -qE "^Suites:.*${codename}-backports" /etc/apt/sources.list.d/debian.sources 2>/dev/null && return 0
|
||||
if [ "$DEBIAN_VERSION" = "13" ] && [ -f "$apt_dir/sources.list.d/debian.sources" ]; then
|
||||
grep -qE "^Suites:.*${codename}-backports" "$apt_dir/sources.list.d/debian.sources" 2>/dev/null && return 0
|
||||
fi
|
||||
if [ -f /etc/apt/sources.list ]; then
|
||||
grep -qE "^[^#]*${codename}-backports" /etc/apt/sources.list 2>/dev/null && return 0
|
||||
if [ -f "$apt_dir/sources.list" ]; then
|
||||
grep -qE "^[^#]*${codename}-backports" "$apt_dir/sources.list" 2>/dev/null && return 0
|
||||
fi
|
||||
if [ -d /etc/apt/sources.list.d ]; then
|
||||
grep -qrE "^Suites:.*${codename}-backports" /etc/apt/sources.list.d/*.sources 2>/dev/null && return 0
|
||||
grep -qrE "^[^#]*${codename}-backports" /etc/apt/sources.list.d/*.list 2>/dev/null && return 0
|
||||
if [ -d "$apt_dir/sources.list.d" ]; then
|
||||
grep -qrE "^Suites:.*${codename}-backports" "$apt_dir/sources.list.d/"*.sources 2>/dev/null && return 0
|
||||
grep -qrE "^[^#]*${codename}-backports" "$apt_dir/sources.list.d/"*.list 2>/dev/null && return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
@@ -84,24 +88,25 @@ detect_backports_status() {
|
||||
# "embedded-classic" (inside sources.list),
|
||||
# "none"
|
||||
detect_backports_location() {
|
||||
local apt_dir="${APT_DIR:-/etc/apt}"
|
||||
local codename="$1"
|
||||
|
||||
if [ "$DEBIAN_VERSION" = "13" ] && [ -f /etc/apt/sources.list.d/debian-backports.sources ] && \
|
||||
grep -qE "^Suites:.*${codename}-backports" /etc/apt/sources.list.d/debian-backports.sources 2>/dev/null; then
|
||||
if [ "$DEBIAN_VERSION" = "13" ] && [ -f "$apt_dir/sources.list.d/debian-backports.sources" ] && \
|
||||
grep -qE "^Suites:.*${codename}-backports" "$apt_dir/sources.list.d/debian-backports.sources" 2>/dev/null; then
|
||||
echo "standalone-deb822"
|
||||
elif [ -f /etc/apt/sources.list.d/debian-backports.list ] && \
|
||||
grep -qE "^[^#]*${codename}-backports" /etc/apt/sources.list.d/debian-backports.list 2>/dev/null; then
|
||||
elif [ -f "$apt_dir/sources.list.d/debian-backports.list" ] && \
|
||||
grep -qE "^[^#]*${codename}-backports" "$apt_dir/sources.list.d/debian-backports.list" 2>/dev/null; then
|
||||
echo "standalone-classic"
|
||||
elif [ "$DEBIAN_VERSION" = "13" ] && [ -f /etc/apt/sources.list.d/debian.sources ] && \
|
||||
grep -qE "^Suites:.*${codename}-backports" /etc/apt/sources.list.d/debian.sources 2>/dev/null; then
|
||||
elif [ "$DEBIAN_VERSION" = "13" ] && [ -f "$apt_dir/sources.list.d/debian.sources" ] && \
|
||||
grep -qE "^Suites:.*${codename}-backports" "$apt_dir/sources.list.d/debian.sources" 2>/dev/null; then
|
||||
echo "embedded-deb822"
|
||||
elif [ -f /etc/apt/sources.list ] && \
|
||||
grep -qE "^[^#]*${codename}-backports" /etc/apt/sources.list 2>/dev/null; then
|
||||
elif [ -f "$apt_dir/sources.list" ] && \
|
||||
grep -qE "^[^#]*${codename}-backports" "$apt_dir/sources.list" 2>/dev/null; then
|
||||
echo "embedded-classic"
|
||||
elif [ -d /etc/apt/sources.list.d ]; then
|
||||
if grep -qrE "^Suites:.*${codename}-backports" /etc/apt/sources.list.d/*.sources 2>/dev/null; then
|
||||
elif [ -d "$apt_dir/sources.list.d" ]; then
|
||||
if grep -qrE "^Suites:.*${codename}-backports" "$apt_dir/sources.list.d/"*.sources 2>/dev/null; then
|
||||
echo "embedded-deb822"
|
||||
elif grep -qrE "^[^#]*${codename}-backports" /etc/apt/sources.list.d/*.list 2>/dev/null; then
|
||||
elif grep -qrE "^[^#]*${codename}-backports" "$apt_dir/sources.list.d/"*.list 2>/dev/null; then
|
||||
echo "embedded-classic"
|
||||
else
|
||||
echo "none"
|
||||
|
||||
@@ -154,6 +154,10 @@ _swap_set_swappiness() {
|
||||
# ── Entry point ──
|
||||
|
||||
manage_swap() {
|
||||
# Hardware state is detected on demand; RAM_GB
|
||||
# drives the size recommendation.
|
||||
_ensure_state_detected
|
||||
|
||||
exec 9>"$SWAP_LOCK"
|
||||
flock -n 9 || {
|
||||
_msg "Busy" "Another swap operation is already running." 8 55
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
_show_sysinfo() {
|
||||
# Hardware state is detected on demand; populate it
|
||||
# before reading any DETECT_* global.
|
||||
_ensure_state_detected
|
||||
|
||||
local msg=""
|
||||
|
||||
# ── OS Block ──
|
||||
|
||||
+127
-46
@@ -28,6 +28,75 @@ APT_UPDATED=0
|
||||
# Cached output of `lspci -nn` for the whole session (populated once via _init_lspci_cache)
|
||||
LSPCI_OUTPUT=""
|
||||
|
||||
# Deferred hardware state: the detection battery runs on
|
||||
# first need (_ensure_state_detected) instead of at boot.
|
||||
STATE_DETECTED=0
|
||||
|
||||
# Network check result cache: 0 = online, 1 = offline,
|
||||
# empty = not checked yet (checked on first use).
|
||||
_NET_STATUS=""
|
||||
|
||||
# Lazy module registry (presence of a key = loaded).
|
||||
# Populated by _load_module() so every module is
|
||||
# sourced at most once per session.
|
||||
declare -A _LOADED_MODULES=()
|
||||
|
||||
# ── Lazy module loading ──────────────────────────
|
||||
# Sources a module on first use and remembers it in
|
||||
# _LOADED_MODULES. Call at the top of a main_menu()
|
||||
# case branch, before calling into the module:
|
||||
# _load_module <name> || continue
|
||||
# Cross-module dependencies (functions called at
|
||||
# runtime that live in another module) are declared
|
||||
# in the deps case below, so a module never calls
|
||||
# an unloaded function.
|
||||
_load_module() {
|
||||
# Defensive: an empty name would produce a bad
|
||||
# array subscript under `set -u`.
|
||||
local name="${1:-}"
|
||||
[ -n "$name" ] || return 1
|
||||
|
||||
# Already loaded in this session?
|
||||
[ -n "${_LOADED_MODULES[$name]+x}" ] && return 0
|
||||
|
||||
# Module file path. Most modules live directly in
|
||||
# modules/; a few have a dedicated subdirectory.
|
||||
local path
|
||||
case "$name" in
|
||||
system_prefs) path="${MODULES_DIR}/system/system_prefs.sh" ;;
|
||||
audio) path="${MODULES_DIR}/system/audio.sh" ;;
|
||||
java) path="${MODULES_DIR}/extras/java.sh" ;;
|
||||
repo_detect) path="${MODULES_DIR}/repos/repo_detect.sh" ;;
|
||||
*) path="${MODULES_DIR}/${name}.sh" ;;
|
||||
esac
|
||||
|
||||
# Cross-module dependencies, loaded first.
|
||||
local -a deps=()
|
||||
case "$name" in
|
||||
system_prefs) deps=(audio) ;;
|
||||
repos) deps=(repo_detect) ;;
|
||||
firmware) deps=(repo_detect bluetooth repos) ;;
|
||||
gaming) deps=(repo_detect java repos) ;;
|
||||
extras) deps=(java) ;;
|
||||
desktop_display) deps=(repos) ;;
|
||||
esac
|
||||
|
||||
local dep
|
||||
for dep in "${deps[@]}"; do
|
||||
_load_module "$dep" || return 1
|
||||
done
|
||||
|
||||
if [ ! -f "$path" ]; then
|
||||
echo -e "${RED}[-] Module not found: ${path}${NC}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
source "$path"
|
||||
_LOADED_MODULES["$name"]=1
|
||||
return 0
|
||||
}
|
||||
|
||||
# --------------------------
|
||||
# Pre-flight checks
|
||||
# --------------------------
|
||||
@@ -487,21 +556,22 @@ is_backports_enabled() {
|
||||
|
||||
local c_pattern="^[^#]*${codename}-backports[[:space:]]+"
|
||||
local d_pattern="Suites:.*${codename}-backports"
|
||||
local apt_dir="${APT_DIR:-/etc/apt}"
|
||||
|
||||
# Classic embedded (sources.list)
|
||||
if [ -f /etc/apt/sources.list ] && grep -Eq "$c_pattern" /etc/apt/sources.list 2>/dev/null; then
|
||||
if [ -f "$apt_dir/sources.list" ] && grep -Eq "$c_pattern" "$apt_dir/sources.list" 2>/dev/null; then
|
||||
echo true
|
||||
return
|
||||
fi
|
||||
|
||||
# Classic standalone (any .list file in sources.list.d)
|
||||
if [ -d /etc/apt/sources.list.d ] && grep -qrE "$c_pattern" /etc/apt/sources.list.d/*.list 2>/dev/null; then
|
||||
if [ -d "$apt_dir/sources.list.d" ] && grep -qrE "$c_pattern" "$apt_dir/sources.list.d/"*.list 2>/dev/null; then
|
||||
echo true
|
||||
return
|
||||
fi
|
||||
|
||||
# Deb822 any .sources file
|
||||
if [ -d /etc/apt/sources.list.d ] && grep -qr "$d_pattern" /etc/apt/sources.list.d/*.sources 2>/dev/null; then
|
||||
if [ -d "$apt_dir/sources.list.d" ] && grep -qr "$d_pattern" "$apt_dir/sources.list.d/"*.sources 2>/dev/null; then
|
||||
echo true
|
||||
return
|
||||
fi
|
||||
@@ -738,19 +808,29 @@ _detect_lang_pkg() {
|
||||
_check_network() {
|
||||
local target="${1:-deb.debian.org}"
|
||||
|
||||
if command -v ping &>/dev/null; then
|
||||
ping -c 1 -W 3 "$target" &>/dev/null && return 0
|
||||
fi
|
||||
# Single bounded probe (worst case 3 s; the old
|
||||
# ping/wget/curl cascade could block up to 13 s).
|
||||
[[ "$target" =~ ^[a-zA-Z0-9.-]+$ ]] || return 1
|
||||
timeout 3 bash -c "</dev/tcp/${target}/80" &>/dev/null
|
||||
}
|
||||
|
||||
if command -v wget &>/dev/null; then
|
||||
wget -q --timeout=5 --spider "http://${target}" &>/dev/null && return 0
|
||||
# ── On-demand network check (non-blocking boot) ──
|
||||
# The connectivity banner moved from boot to the first
|
||||
# apt operation. _NET_STATUS caches the result so the
|
||||
# probe runs at most once per session.
|
||||
_require_network() {
|
||||
if [ -z "$_NET_STATUS" ]; then
|
||||
if _check_network; then
|
||||
_NET_STATUS=0
|
||||
else
|
||||
_NET_STATUS=1
|
||||
echo -e "${YELLOW}──────────────────────────────────────────${NC}"
|
||||
echo -e "${YELLOW} No internet connectivity detected.${NC}"
|
||||
echo -e "${YELLOW} Package installation will fail without network.${NC}"
|
||||
echo -e "${YELLOW}──────────────────────────────────────────${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v curl &>/dev/null; then
|
||||
curl -s --connect-timeout 5 -o /dev/null "http://${target}" &>/dev/null && return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
return "$_NET_STATUS"
|
||||
}
|
||||
|
||||
# ----------------------------------
|
||||
@@ -763,6 +843,15 @@ _ensure_apt_updated() {
|
||||
echo -e "${GREEN}[+]${NC} APT package lists already refreshed this session."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Advisory: probe the network once (cached in
|
||||
# _NET_STATUS) before attempting an apt operation.
|
||||
_require_network || true
|
||||
|
||||
# A correct clock is required for HTTPS certificate
|
||||
# validation; only runs when apt is actually used.
|
||||
_ensure_time_synced
|
||||
|
||||
echo -e "${GREEN}[+]${NC} Refreshing APT package lists..."
|
||||
if sudo apt-get update; then
|
||||
APT_UPDATED=1
|
||||
@@ -772,41 +861,33 @@ _ensure_apt_updated() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# ----------------------------------
|
||||
# LightDM configuration
|
||||
# ----------------------------------
|
||||
_configure_lightdm() {
|
||||
command -v lightdm &>/dev/null || return 0
|
||||
|
||||
if _confirm "LightDM" "Configure LightDM to show the user list on the login screen?\n\nThis disables greeter-hide-users."; then
|
||||
if ! is_installed lightdm-gtk-greeter-settings; then
|
||||
echo -e "${YELLOW}Installing lightdm-gtk-greeter-settings...${NC}"
|
||||
if ! sudo DEBIAN_FRONTEND=noninteractive apt install -y lightdm-gtk-greeter-settings; then
|
||||
echo -e "${YELLOW}lightdm-gtk-greeter-settings could not be installed — LightDM configuration skipped.${NC}"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
local conf_dir="/etc/lightdm/lightdm.conf.d"
|
||||
local conf_file="${conf_dir}/99-show-users.conf"
|
||||
|
||||
if [ -f "$conf_file" ] && grep -q '^greeter-hide-users=false' "$conf_file"; then
|
||||
return
|
||||
fi
|
||||
|
||||
sudo mkdir -p "$conf_dir"
|
||||
printf '[Seat:*]\ngreeter-hide-users=false\n' | sudo tee "$conf_file" >/dev/null
|
||||
echo -e "${GREEN}LightDM configured to show user list.${NC}"
|
||||
fi
|
||||
# ── Deferred hardware state (lazy pre-flight) ──
|
||||
# The detection battery runs on first need instead of at
|
||||
# boot. Call _ensure_state_detected() before reading any
|
||||
# DETECT_* global (LSPCI_OUTPUT, GPU_*, ETH_NAMES,
|
||||
# DESKTOP_ENV, AUDIO_SERVER, ...).
|
||||
_run_detection_battery() {
|
||||
_init_lspci_cache
|
||||
detect_gpu
|
||||
detect_network
|
||||
detect_displayserver
|
||||
detect_storage
|
||||
detect_desktop_environment
|
||||
detect_audio_server
|
||||
detect_cpu_ram
|
||||
detect_kernel
|
||||
}
|
||||
_ensure_state_detected() {
|
||||
[ "$STATE_DETECTED" -eq 1 ] && return 0
|
||||
_run_detection_battery
|
||||
STATE_DETECTED=1
|
||||
}
|
||||
|
||||
# ── Lazy system state refresh ──
|
||||
refresh_system_state() {
|
||||
detect_debian_version
|
||||
detect_gpu
|
||||
detect_cpu_ram
|
||||
detect_network
|
||||
detect_desktop_environment
|
||||
detect_displayserver
|
||||
detect_audio_server
|
||||
# Forced re-run: a menu action may have changed the
|
||||
# hardware or repo state since the first detection.
|
||||
_run_detection_battery
|
||||
STATE_DETECTED=1
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
# zram.sh — ZRAM submenu: view, create/reconfigure, remove
|
||||
|
||||
zram_menu() {
|
||||
# Hardware state is detected on demand; RAM_KB
|
||||
# and RAM_SUMMARY are populated by the battery.
|
||||
_ensure_state_detected
|
||||
|
||||
while true; do
|
||||
local choice
|
||||
choice=$(_menu "ZRAM Configuration" \
|
||||
|
||||
Reference in New Issue
Block a user