diff --git a/fresh-install/detect_gpu.sh b/fresh-install/detect_gpu.sh
index e23c4a4..cc2eb96 100755
--- a/fresh-install/detect_gpu.sh
+++ b/fresh-install/detect_gpu.sh
@@ -2,17 +2,20 @@
# Lightweight GPU detection script.
# Detects GPU vendor and invokes the corresponding vendor install/management script.
# Exports: GPU_VENDOR
+# shellcheck source=./install_nvidia_driver.sh
+# shellcheck source=./install_amd_driver.sh
+# shellcheck source=./install_intel_driver.sh
set -e
GPU_VENDOR="unknown"
PCI_GPU_INFO=$(lspci -nn | grep -Ei 'vga|3d|display' || true)
if echo "$PCI_GPU_INFO" | grep -qi nvidia; then
- GPU_VENDOR="nvidia"
+ GPU_VENDOR="nvidia"
elif echo "$PCI_GPU_INFO" | grep -Eqi '\b(amd|advanced micro devices|ati)\b'; then
- GPU_VENDOR="amd"
+ GPU_VENDOR="amd"
elif echo "$PCI_GPU_INFO" | grep -qi intel; then
- GPU_VENDOR="intel"
+ GPU_VENDOR="intel"
fi
export GPU_VENDOR
@@ -21,6 +24,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
case "$GPU_VENDOR" in
nvidia)
if [ -x "$SCRIPT_DIR/install_nvidia_driver.sh" ]; then
+ # shellcheck source=./install_nvidia_driver.sh disable=SC1091
. "$SCRIPT_DIR/install_nvidia_driver.sh"
else
echo "NVIDIA installer script missing: $SCRIPT_DIR/install_nvidia_driver.sh"
@@ -28,6 +32,7 @@ case "$GPU_VENDOR" in
;;
amd)
if [ -x "$SCRIPT_DIR/install_amd_driver.sh" ]; then
+ # shellcheck source=./install_amd_driver.sh disable=SC1091
. "$SCRIPT_DIR/install_amd_driver.sh"
else
echo "AMD installer script missing: $SCRIPT_DIR/install_amd_driver.sh (placeholder)"
@@ -35,6 +40,7 @@ case "$GPU_VENDOR" in
;;
intel)
if [ -x "$SCRIPT_DIR/install_intel_driver.sh" ]; then
+ # shellcheck source=./install_intel_driver.sh disable=SC1091
. "$SCRIPT_DIR/install_intel_driver.sh"
else
echo "Intel installer script missing: $SCRIPT_DIR/install_intel_driver.sh"
diff --git a/fresh-install/detect_gpu_and_install.sh b/fresh-install/detect_gpu_and_install.sh
index 03fab11..767dec9 100755
--- a/fresh-install/detect_gpu_and_install.sh
+++ b/fresh-install/detect_gpu_and_install.sh
@@ -1,4 +1,5 @@
#!/usr/bin/env bash
# Backwards compatibility wrapper; prefer using detect_gpu.sh directly.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+# shellcheck source=./detect_gpu.sh disable=SC1091
. "$SCRIPT_DIR/detect_gpu.sh"
diff --git a/fresh-install/install_amd_driver.sh b/fresh-install/install_amd_driver.sh
index 77c9bdc..6cd3711 100755
--- a/fresh-install/install_amd_driver.sh
+++ b/fresh-install/install_amd_driver.sh
@@ -12,7 +12,10 @@
# AMD_VERBOSE=1 # verbose output
set -e
-[ "${GPU_VENDOR}" = "amd" ] || { echo "AMD installer invoked but GPU_VENDOR=${GPU_VENDOR}"; exit 0; }
+[ "${GPU_VENDOR}" = "amd" ] || {
+ echo "AMD installer invoked but GPU_VENDOR=${GPU_VENDOR}"
+ exit 0
+}
AMD_INSTALL_XF86=${AMD_INSTALL_XF86:-0}
AMD_INSTALL_AMDVLK=${AMD_INSTALL_AMDVLK:-0}
@@ -55,19 +58,22 @@ LIB32_AMDVLK_PKG="lib32-amdvlk"
# Simple AUR builder (reused from NVIDIA script style)
_build_aur_pkg() {
- local pkg="$1" url="https://aur.archlinux.org/${pkg}.git"
- mkdir -p "$HOME/aur"; cd "$HOME/aur"
+ local pkg="$1"
+ local url="https://aur.archlinux.org/${pkg}.git"
+ mkdir -p "$HOME/aur"
+ cd "$HOME/aur"
if [ ! -d "$pkg" ]; then git clone "$url"; else (cd "$pkg" && git fetch -q --all && git reset -q --hard origin/HEAD || git pull --ff-only || true); fi
- cd "$pkg"; rm -f -- *.pkg.tar.* 2>/dev/null || true
+ cd "$pkg"
+ rm -f -- *.pkg.tar.* 2> /dev/null || true
yes | makepkg -s -c -C --noconfirm --needed
- local built=( *.pkg.tar.zst )
+ local built=(*.pkg.tar.zst)
yes | sudo pacman -U --noconfirm "${built[@]}"
}
_install_repo_or_aur() {
local pkg="$1"
- if pacman -Si "$pkg" >/dev/null 2>&1; then
- if pacman -Qi "$pkg" >/dev/null 2>&1; then
+ if pacman -Si "$pkg" > /dev/null 2>&1; then
+ if pacman -Qi "$pkg" > /dev/null 2>&1; then
vlog "$pkg already installed"
else
yes | sudo pacman -Sy --noconfirm "$pkg"
@@ -101,7 +107,8 @@ fi
GPU_LINES=$(lspci -nn | grep -Ei 'vga|3d|display' | grep -iE 'amd|ati' || true)
SI_NAMES=(Tahiti Pitcairn Cape Verde Oland Hainan Curacao)
CIK_NAMES=(Bonaire Hawaii Kabini Kaveri Mullins Temash Spectre Spooky)
-IS_SI=0; IS_CIK=0
+IS_SI=0
+IS_CIK=0
for n in "${SI_NAMES[@]}"; do echo "$GPU_LINES" | grep -q "$n" && IS_SI=1 && break; done
for n in "${CIK_NAMES[@]}"; do echo "$GPU_LINES" | grep -q "$n" && IS_CIK=1 && break; done
@@ -135,7 +142,7 @@ else
fi
# Check active kernel driver
-KDRV=$(lspci -k -d ::0300 2>/dev/null | awk '/Kernel driver in use:/ {print $5; exit}')
+KDRV=$(lspci -k -d ::0300 2> /dev/null | awk '/Kernel driver in use:/ {print $5; exit}')
[ -z "$KDRV" ] && KDRV=$(lsmod | grep -E 'amdgpu|radeon' | head -n1 | awk '{print $1}')
info "Kernel driver in use: ${KDRV:-unknown}"
diff --git a/fresh-install/install_intel_driver.sh b/fresh-install/install_intel_driver.sh
index 096a548..f37c0e8 100755
--- a/fresh-install/install_intel_driver.sh
+++ b/fresh-install/install_intel_driver.sh
@@ -12,7 +12,10 @@
# INTEL_VERBOSE=0/1 # verbose logging
set -e
-[ "$GPU_VENDOR" = "intel" ] || { echo "Intel installer invoked but GPU_VENDOR=$GPU_VENDOR"; exit 0; }
+[ "$GPU_VENDOR" = "intel" ] || {
+ echo "Intel installer invoked but GPU_VENDOR=$GPU_VENDOR"
+ exit 0
+}
INTEL_USE_AMBER=${INTEL_USE_AMBER:-0}
INTEL_INSTALL_LIB32=${INTEL_INSTALL_LIB32:-auto}
@@ -41,10 +44,10 @@ fi
install_pkg() {
local pkg="$1"
- if pacman -Qi "$pkg" >/dev/null 2>&1; then
+ if pacman -Qi "$pkg" > /dev/null 2>&1; then
vlog "$pkg already installed"
else
- if pacman -Si "$pkg" >/dev/null 2>&1; then
+ if pacman -Si "$pkg" > /dev/null 2>&1; then
yes | sudo pacman -Sy --noconfirm "$pkg"
else
warn "Package $pkg not found in repos (not handling AUR here)"
@@ -86,7 +89,7 @@ if [ -n "$INTEL_ENABLE_GUC" ]; then
else
info "Configuring enable_guc=$INTEL_ENABLE_GUC"
sudo mkdir -p /etc/modprobe.d
- echo "options i915 enable_guc=$INTEL_ENABLE_GUC" | sudo tee /etc/modprobe.d/i915-guc.conf >/dev/null
+ echo "options i915 enable_guc=$INTEL_ENABLE_GUC" | sudo tee /etc/modprobe.d/i915-guc.conf > /dev/null
if [ "$INTEL_SKIP_INITRAMFS" != 1 ] && [ -f /etc/mkinitcpio.conf ]; then
info "Regenerating initramfs (mkinitcpio -P) for GuC/HuC change"
sudo mkinitcpio -P || warn "mkinitcpio failed; continue manually"
@@ -97,7 +100,7 @@ if [ -n "$INTEL_ENABLE_GUC" ]; then
fi
# Report kernel driver
-KDRV=$(lspci -k -d ::0300 2>/dev/null | awk '/Kernel driver in use:/ {print $5; exit}')
+KDRV=$(lspci -k -d ::0300 2> /dev/null | awk '/Kernel driver in use:/ {print $5; exit}')
[ -z "$KDRV" ] && KDRV=$(lsmod | grep -E 'i915|xe' | head -n1 | awk '{print $1}')
info "Kernel driver in use: ${KDRV:-unknown}"
diff --git a/fresh-install/install_nvidia_driver.sh b/fresh-install/install_nvidia_driver.sh
index ff174b7..ee126dc 100755
--- a/fresh-install/install_nvidia_driver.sh
+++ b/fresh-install/install_nvidia_driver.sh
@@ -4,15 +4,22 @@
# Outputs: NVIDIA_DRIVER_PACKAGE
set -e
-[ "$GPU_VENDOR" = "nvidia" ] || { echo "NVIDIA installer invoked but GPU_VENDOR=$GPU_VENDOR"; exit 0; }
+[ "$GPU_VENDOR" = "nvidia" ] || {
+ echo "NVIDIA installer invoked but GPU_VENDOR=$GPU_VENDOR"
+ exit 0
+}
_build_aur_pkg() {
- local pkg="$1"; local repo_url="https://aur.archlinux.org/${pkg}.git";
- mkdir -p "$HOME/aur"; cd "$HOME/aur";
+ local pkg="$1"
+ local repo_url="https://aur.archlinux.org/${pkg}.git"
+ mkdir -p "$HOME/aur"
+ cd "$HOME/aur"
if [ ! -d "$pkg" ]; then git clone "$repo_url"; else (cd "$pkg" && git fetch -q --all && git reset -q --hard origin/HEAD || git pull --ff-only || true); fi
- cd "$pkg"; rm -f -- *.pkg.tar.* 2>/dev/null || true
+ cd "$pkg"
+ rm -f -- *.pkg.tar.* 2> /dev/null || true
yes | makepkg -s -c -C --noconfirm --needed || return 1
- local built=( *.pkg.tar.zst ); yes | sudo pacman -U --noconfirm "${built[@]}"
+ local built=(*.pkg.tar.zst)
+ yes | sudo pacman -U --noconfirm "${built[@]}"
}
_choose_nvidia_pkg() {
@@ -23,8 +30,8 @@ _choose_nvidia_pkg() {
if [ $((have_linux + have_linux_lts)) -gt 1 ]; then multiple_kernels=1; else multiple_kernels=0; fi
# Optionally skip attempting to install nvidia-detect (some minimal repo setups don't have it yet)
- if [ -z "${NVIDIA_SKIP_DETECT:-}" ] && ! command -v nvidia-detect >/dev/null 2>&1; then
- if pacman -Si nvidia-detect >/dev/null 2>&1; then
+ if [ -z "${NVIDIA_SKIP_DETECT:-}" ] && ! command -v nvidia-detect > /dev/null 2>&1; then
+ if pacman -Si nvidia-detect > /dev/null 2>&1; then
echo "Attempting to install helper utility: nvidia-detect" >&2
# Use --needed to avoid forcing refresh (& avoid partial upgrade semantics with -Sy)
yes | sudo pacman -S --needed --noconfirm nvidia-detect || echo "nvidia-detect install failed (continuing with heuristic)" >&2
@@ -33,25 +40,34 @@ _choose_nvidia_pkg() {
fi
fi
- if command -v nvidia-detect >/dev/null 2>&1; then
- detect_out="$(nvidia-detect 2>/dev/null || true)"
+ if command -v nvidia-detect > /dev/null 2>&1; then
+ detect_out="$(nvidia-detect 2> /dev/null || true)"
fi
if [ -n "$detect_out" ]; then
- if echo "$detect_out" | grep -q '470'; then driver_pkg='nvidia-470xx-dkms'; legacy_detected=1; fi
- if echo "$detect_out" | grep -q '390'; then driver_pkg='nvidia-390xx-dkms'; legacy_detected=1; fi
- if echo "$detect_out" | grep -q '340'; then driver_pkg='nvidia-340xx-dkms'; legacy_detected=1; fi
+ if echo "$detect_out" | grep -q '470'; then
+ driver_pkg='nvidia-470xx-dkms'
+ legacy_detected=1
+ fi
+ if echo "$detect_out" | grep -q '390'; then
+ driver_pkg='nvidia-390xx-dkms'
+ legacy_detected=1
+ fi
+ if echo "$detect_out" | grep -q '340'; then
+ driver_pkg='nvidia-340xx-dkms'
+ legacy_detected=1
+ fi
fi
if [ "$legacy_detected" = 0 ]; then
# Heuristic modern driver selection
if [ "$multiple_kernels" = 1 ]; then
- if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-dkms >/dev/null 2>&1; then driver_pkg='nvidia-open-dkms'; else driver_pkg='nvidia-dkms'; fi
+ if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-dkms > /dev/null 2>&1; then driver_pkg='nvidia-open-dkms'; else driver_pkg='nvidia-dkms'; fi
else
if [ "$have_linux_lts" = 1 ] && [ "$have_linux" = 0 ]; then
- if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-lts >/dev/null 2>&1; then driver_pkg='nvidia-open-lts'; else driver_pkg='nvidia-lts'; fi
+ if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-lts > /dev/null 2>&1; then driver_pkg='nvidia-open-lts'; else driver_pkg='nvidia-lts'; fi
else
- if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open >/dev/null 2>&1; then driver_pkg='nvidia-open'; else driver_pkg='nvidia'; fi
+ if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open > /dev/null 2>&1; then driver_pkg='nvidia-open'; else driver_pkg='nvidia'; fi
fi
fi
else
@@ -62,21 +78,22 @@ _choose_nvidia_pkg() {
}
_remove_conflicting_nvidia_pkgs() {
- local keep="$1"; local candidates=(nvidia nvidia-lts nvidia-dkms nvidia-open nvidia-open-lts nvidia-open-dkms nvidia-470xx-dkms nvidia-390xx-dkms nvidia-340xx-dkms)
+ local keep="$1"
+ local candidates=(nvidia nvidia-lts nvidia-dkms nvidia-open nvidia-open-lts nvidia-open-dkms nvidia-470xx-dkms nvidia-390xx-dkms nvidia-340xx-dkms)
local to_remove=()
for p in "${candidates[@]}"; do
- if pacman -Qi "$p" >/dev/null 2>&1 && [ "$p" != "$keep" ]; then to_remove+=("$p"); fi
+ if pacman -Qi "$p" > /dev/null 2>&1 && [ "$p" != "$keep" ]; then to_remove+=("$p"); fi
done
if [ ${#to_remove[@]} -gt 0 ]; then yes | sudo pacman -Rns --noconfirm "${to_remove[@]}" || true; fi
}
_install_nvidia_stack() {
local driver_pkg="$1"
- if [[ "$driver_pkg" == nvidia-*xx-dkms ]]; then _build_aur_pkg "$driver_pkg"; else yes | sudo pacman -Sy --noconfirm "$driver_pkg"; fi
+ if [[ $driver_pkg == nvidia-*xx-dkms ]]; then _build_aur_pkg "$driver_pkg"; else yes | sudo pacman -Sy --noconfirm "$driver_pkg"; fi
local utils_pkg="nvidia-utils" utils32_pkg="lib32-nvidia-utils"
- if ! pacman -Qi "$utils_pkg" >/dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils_pkg"; fi
+ if ! pacman -Qi "$utils_pkg" > /dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils_pkg"; fi
if grep -q '^\[multilib\]' /etc/pacman.conf; then
- if ! pacman -Qi "$utils32_pkg" >/dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils32_pkg" || true; fi
+ if ! pacman -Qi "$utils32_pkg" > /dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils32_pkg" || true; fi
fi
}
diff --git a/fresh-install/main.sh b/fresh-install/main.sh
index 9b0d6b7..9dec655 100755
--- a/fresh-install/main.sh
+++ b/fresh-install/main.sh
@@ -1,119 +1,124 @@
-#!/bin/sh
+#!/usr/bin/env bash
+# shellcheck source=./detect_gpu.sh
+# shellcheck source=./detect_gpu_and_install.sh
set -e
# Function to play a sound on error
play_error_sound() {
- #pactl set-sink-volume @DEFAULT_SINK@ +50%
- for i in 1 2 3; do
- paplay /usr/share/sounds/freedesktop/stereo/dialog-error.oga
- done
- #pactl set-sink-volume @DEFAULT_SINK@ -50%
+ #pactl set-sink-volume @DEFAULT_SINK@ +50%
+ for _ in 1 2 3; do
+ paplay /usr/share/sounds/freedesktop/stereo/dialog-error.oga
+ done
+ #pactl set-sink-volume @DEFAULT_SINK@ -50%
}
# Trap errors and call the play_error_sound function
trap 'play_error_sound' ERR
-
sudo -v
git config --global init.defaultBranch main
# GPU detection (now split vendor-specific logic)
if [ -f "./detect_gpu.sh" ]; then
- . ./detect_gpu.sh
+ # shellcheck source=./detect_gpu.sh disable=SC1091
+ . ./detect_gpu.sh
elif [ -f "./detect_gpu_and_install.sh" ]; then
- . ./detect_gpu_and_install.sh
+ # shellcheck source=./detect_gpu_and_install.sh disable=SC1091
+ . ./detect_gpu_and_install.sh
else
- echo "GPU detection scripts not found; continuing without GPU specific installation."
+ echo "GPU detection scripts not found; continuing without GPU specific installation."
fi
install_from_aur() {
- if [ ! -d "$HOME/aur" ]; then
- mkdir -p "$HOME/aur"
- fi
- cd "$HOME/aur"
- local repo_url=$1
- local pkg_name=$2
- local repo_dir="$(basename "$repo_url" .git)"
+ local repo_url pkg_name repo_dir
+ repo_url="$1"
+ pkg_name="$2"
- if [ ! -d "$repo_dir" ]; then
- git clone "$repo_url"
- else
- echo "Repository $repo_dir already cloned; updating"
- (cd "$repo_dir" && git fetch --all -q && git reset --hard origin/HEAD -q || git pull --ff-only || true)
- fi
- cd "$repo_dir"
+ mkdir -p "$HOME/aur"
+ cd "$HOME/aur" || return 1
+ repo_dir="$(basename "$repo_url" .git)"
- if pacman -Qi "$pkg_name" >/dev/null 2>&1; then
- echo "$pkg_name is already installed"
- return 0
- fi
+ if [ ! -d "$repo_dir" ]; then
+ git clone "$repo_url"
+ else
+ echo "Repository $repo_dir already cloned; updating"
+ (cd "$repo_dir" && git fetch --all -q && git reset --hard origin/HEAD -q || git pull --ff-only || true)
+ fi
+ cd "$repo_dir" || return 1
- echo "Cleaning old package artifacts to avoid duplicate -U targets"
- find . -maxdepth 1 -type f -name '*.pkg.tar.*' -delete 2>/dev/null || true
+ if pacman -Qi "$pkg_name" > /dev/null 2>&1; then
+ echo "$pkg_name is already installed"
+ return 0
+ fi
- echo "Building $pkg_name (clean build)"
- # -c (clean up work dirs after) -C (clean build - remove src/ and pkg/ first)
- if ! yes | makepkg -s -c -C --noconfirm --nocheck --skipchecksums --skipinteg --skippgpcheck --needed; then
- echo "Build failed for $pkg_name" >&2
- return 1
- fi
+ echo "Cleaning old package artifacts to avoid duplicate -U targets"
+ find . -maxdepth 1 -type f -name '*.pkg.tar.*' -delete 2> /dev/null || true
- # Collect only the freshly built packages (should now be only current version)
- mapfile -t built_pkgs < <(ls -1 *.pkg.tar.zst 2>/dev/null || true)
- if [ ${#built_pkgs[@]} -eq 0 ]; then
- echo "No package files produced for $pkg_name" >&2
- return 1
- fi
+ echo "Building $pkg_name (clean build)"
+ # -c (clean up work dirs after) -C (clean build - remove src/ and pkg/ first)
+ if ! yes | makepkg -s -c -C --noconfirm --nocheck --skipchecksums --skipinteg --skippgpcheck --needed; then
+ echo "Build failed for $pkg_name" >&2
+ return 1
+ fi
- echo "Installing built package(s): ${built_pkgs[*]}"
- if ! yes | sudo pacman -U --noconfirm "${built_pkgs[@]}"; then
- echo "Installation failed for $pkg_name" >&2
- return 1
- fi
+ # Collect only the freshly built packages (should now be only current version)
+ mapfile -t built_pkgs < <(find . -maxdepth 1 -type f -name '*.pkg.tar.zst' -printf './%f\n')
+ if [ ${#built_pkgs[@]} -eq 0 ]; then
+ echo "No package files produced for $pkg_name" >&2
+ return 1
+ fi
+
+ echo "Installing built package(s): ${built_pkgs[*]}"
+ if ! yes | sudo pacman -U --noconfirm "${built_pkgs[@]}"; then
+ echo "Installation failed for $pkg_name" >&2
+ return 1
+ fi
}
process_packages() {
- local file_path=$1
- > failed.txt
- > done.txt
+ local file_path
+ file_path="$1"
+ : > failed.txt
+ : > done.txt
- while IFS= read -r pkg_name; do
- if [ -z "$pkg_name" ]; then
- continue
- fi
+ while IFS= read -r pkg_name; do
+ if [ -z "$pkg_name" ]; then
+ continue
+ fi
- local repo_url="https://aur.archlinux.org/${pkg_name}-git.git"
- local repo_dir="${pkg_name}-git"
+ local repo_url repo_dir
+ repo_url="https://aur.archlinux.org/${pkg_name}-git.git"
+ repo_dir="${pkg_name}-git"
- git clone $repo_url
- if [ -d "$repo_dir" ] && [ -z "$(ls -A $repo_dir)" ]; then
- echo "Repository $repo_dir is empty, trying without -git suffix"
- repo_url="https://aur.archlinux.org/${pkg_name}.git"
- repo_dir="${pkg_name}"
+ git clone "$repo_url"
+ if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then
+ echo "Repository $repo_dir is empty, trying without -git suffix"
+ repo_url="https://aur.archlinux.org/${pkg_name}.git"
+ repo_dir="${pkg_name}"
- git clone $repo_url
- if [ -d "$repo_dir" ] && [ -z "$(ls -A $repo_dir)" ]; then
- echo "Repository $repo_dir is empty, trying to install with pacman"
- if sudo pacman -Sy --noconfirm $pkg_name; then
- echo "$pkg_name" >> done.txt
- else
- echo "$pkg_name" >> failed.txt
- fi
- else
- if install_from_aur $repo_url $pkg_name; then
- echo "$pkg_name" >> done.txt
- else
- echo "$pkg_name" >> failed.txt
- fi
- fi
+ git clone "$repo_url"
+ if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then
+ echo "Repository $repo_dir is empty, trying to install with pacman"
+ if sudo pacman -Sy --noconfirm "$pkg_name"; then
+ echo "$pkg_name" >> done.txt
else
- if install_from_aur $repo_url $pkg_name; then
- echo "$pkg_name" >> done.txt
- else
- echo "$pkg_name" >> failed.txt
- fi
+ echo "$pkg_name" >> failed.txt
fi
- done < "$file_path"
+ else
+ if install_from_aur "$repo_url" "$pkg_name"; then
+ echo "$pkg_name" >> done.txt
+ else
+ echo "$pkg_name" >> failed.txt
+ fi
+ fi
+ else
+ if install_from_aur "$repo_url" "$pkg_name"; then
+ echo "$pkg_name" >> done.txt
+ else
+ echo "$pkg_name" >> failed.txt
+ fi
+ fi
+ done < "$file_path"
}
sudo cp /etc/makepkg.conf /etc/makepkg.conf.bak
@@ -124,161 +129,175 @@ sudo cp ./pacman.conf /etc/pacman.conf
# sudo cp ./mkinitcpio.conf /etc/mkinitcpio.conf
# mkinitcpio -P
# Reflector install / service management (idempotent & resilient)
-if pacman -Qi reflector >/dev/null 2>&1; then
- echo "reflector already installed"
+if pacman -Qi reflector > /dev/null 2>&1; then
+ echo "reflector already installed"
else
- yes | sudo pacman -Sy --noconfirm reflector || echo "Warning: reflector install failed (continuing)"
+ yes | sudo pacman -Sy --noconfirm reflector || echo "Warning: reflector install failed (continuing)"
fi
# Prefer timer over service (Arch default)
if systemctl list-unit-files | grep -q '^reflector.timer'; then
- if systemctl is-enabled reflector.timer >/dev/null 2>&1; then
- echo "reflector.timer already enabled"
- else
- sudo systemctl enable reflector.timer || echo "Warning: could not enable reflector.timer"
- fi
- if systemctl is-active reflector.timer >/dev/null 2>&1; then
- echo "reflector.timer already active"
- else
- if ! sudo systemctl start reflector.timer; then
- echo "Warning: failed to start reflector.timer (check: systemctl status reflector.timer; journalctl -xeu reflector.timer)"
- fi
+ if systemctl is-enabled reflector.timer > /dev/null 2>&1; then
+ echo "reflector.timer already enabled"
+ else
+ sudo systemctl enable reflector.timer || echo "Warning: could not enable reflector.timer"
+ fi
+ if systemctl is-active reflector.timer > /dev/null 2>&1; then
+ echo "reflector.timer already active"
+ else
+ if ! sudo systemctl start reflector.timer; then
+ echo "Warning: failed to start reflector.timer (check: systemctl status reflector.timer; journalctl -xeu reflector.timer)"
fi
+ fi
elif systemctl list-unit-files | grep -q '^reflector.service'; then
- if systemctl is-enabled reflector.service >/dev/null 2>&1; then
- echo "reflector.service already enabled"
- else
- sudo systemctl enable reflector.service || echo "Warning: could not enable reflector.service"
- fi
- if systemctl is-active reflector.service >/dev/null 2>&1; then
- echo "reflector.service already running"
- else
- if ! sudo systemctl start reflector.service; then
- echo "Warning: failed to start reflector.service (check: systemctl status reflector.service; journalctl -xeu reflector.service)"
- fi
+ if systemctl is-enabled reflector.service > /dev/null 2>&1; then
+ echo "reflector.service already enabled"
+ else
+ sudo systemctl enable reflector.service || echo "Warning: could not enable reflector.service"
+ fi
+ if systemctl is-active reflector.service > /dev/null 2>&1; then
+ echo "reflector.service already running"
+ else
+ if ! sudo systemctl start reflector.service; then
+ echo "Warning: failed to start reflector.service (check: systemctl status reflector.service; journalctl -xeu reflector.service)"
fi
+ fi
else
- echo "reflector systemd unit not found (neither timer nor service)"
+ echo "reflector systemd unit not found (neither timer nor service)"
fi
+# Read AUR packages from file (needed before pacman processing)
+declare -a aur_packages=()
+declare -a aur_package_names=()
+while IFS= read -r line; do
+ if [[ -n $line && $line =~ ^[a-z0-9] ]]; then
+ aur_packages+=("$line")
+ aur_package_names+=("${line%% *}")
+ fi
+done < "aur_packages.txt"
+
# Read pacman packages from file
declare -a pacman_packages
while IFS= read -r line; do
- # Skip empty lines and comments (lines not starting with alphanumeric characters)
- if [[ -n "$line" && "$line" =~ ^[a-z0-9] ]]; then
- pacman_packages+=("$line")
- fi
+ # Skip empty lines and comments (lines not starting with alphanumeric characters)
+ if [[ -n $line && $line =~ ^[a-z0-9] ]]; then
+ pacman_packages+=("$line")
+ fi
done < "pacman_packages.txt"
for pkg in "${pacman_packages[@]}"; do
- # Skip NVIDIA packages if GPU is not NVIDIA
- if [ "$GPU_VENDOR" != "nvidia" ] && { [ "$pkg" = "nvidia" ] || [ "$pkg" = "nvidia-utils" ] || [ "$pkg" = "lib32-nvidia-utils" ]; }; then
- echo "Skipping $pkg (GPU vendor: $GPU_VENDOR)"
- continue
- fi
- # Check for texlive subpackages
- if [ "$pkg" == "texlive" ]; then
- sub_pkgs=(
- texlive-basic texlive-bibtexextra texlive-binextra texlive-context texlive-fontsextra
- texlive-fontsrecommended texlive-fontutils texlive-formatsextra texlive-games texlive-humanities
- texlive-latex texlive-latexextra texlive-latexrecommended texlive-luatex texlive-mathscience
- texlive-metapost texlive-music texlive-pictures texlive-plaingeneric texlive-pstricks
- texlive-publishers texlive-xetex
- )
- all_installed=true
- for subpkg in "${sub_pkgs[@]}"; do
- if ! pacman -Qi "$subpkg" &> /dev/null; then
- all_installed=false
- break
- fi
- done
- if [ "$all_installed" = true ]; then
- echo "All texlive subpackages are installed, skipping texlive"
- continue
- fi
+ # Skip NVIDIA packages if GPU is not NVIDIA
+ if [ "$GPU_VENDOR" != "nvidia" ] && { [ "$pkg" = "nvidia" ] || [ "$pkg" = "nvidia-utils" ] || [ "$pkg" = "lib32-nvidia-utils" ]; }; then
+ echo "Skipping $pkg (GPU vendor: $GPU_VENDOR)"
+ continue
+ fi
+ # Check for texlive subpackages
+ if [ "$pkg" == "texlive" ]; then
+ sub_pkgs=(
+ texlive-basic texlive-bibtexextra texlive-binextra texlive-context texlive-fontsextra
+ texlive-fontsrecommended texlive-fontutils texlive-formatsextra texlive-games texlive-humanities
+ texlive-latex texlive-latexextra texlive-latexrecommended texlive-luatex texlive-mathscience
+ texlive-metapost texlive-music texlive-pictures texlive-plaingeneric texlive-pstricks
+ texlive-publishers texlive-xetex
+ )
+ all_installed=true
+ for subpkg in "${sub_pkgs[@]}"; do
+ if ! pacman -Qi "$subpkg" &> /dev/null; then
+ all_installed=false
+ break
+ fi
+ done
+ if [ "$all_installed" = true ]; then
+ echo "All texlive subpackages are installed, skipping texlive"
+ continue
fi
+ fi
- # Check for texlive-lang subpackages
- if [ "$pkg" == "texlive-lang" ]; then
- sub_pkgs=(
- texlive-langarabic texlive-langchinese texlive-langcjk texlive-langcyrillic
- texlive-langczechslovak texlive-langenglish texlive-langeuropean texlive-langfrench
- texlive-langgerman texlive-langgreek texlive-langitalian texlive-langjapanese
- texlive-langkorean texlive-langother texlive-langpolish texlive-langportuguese
- texlive-langspanish
- )
- all_installed=true
- for subpkg in "${sub_pkgs[@]}"; do
- if ! pacman -Qi "$subpkg" &> /dev/null; then
- all_installed=false
- break
- fi
- done
- if [ "$all_installed" = true ]; then
- echo "All texlive-lang subpackages are installed, skipping texlive-lang"
- continue
- fi
+ # Check for texlive-lang subpackages
+ if [ "$pkg" == "texlive-lang" ]; then
+ sub_pkgs=(
+ texlive-langarabic texlive-langchinese texlive-langcjk texlive-langcyrillic
+ texlive-langczechslovak texlive-langenglish texlive-langeuropean texlive-langfrench
+ texlive-langgerman texlive-langgreek texlive-langitalian texlive-langjapanese
+ texlive-langkorean texlive-langother texlive-langpolish texlive-langportuguese
+ texlive-langspanish
+ )
+ all_installed=true
+ for subpkg in "${sub_pkgs[@]}"; do
+ if ! pacman -Qi "$subpkg" &> /dev/null; then
+ all_installed=false
+ break
+ fi
+ done
+ if [ "$all_installed" = true ]; then
+ echo "All texlive-lang subpackages are installed, skipping texlive-lang"
+ continue
fi
+ fi
- if ! pacman -Qi "$pkg" &> /dev/null; then
- if ! echo "${aur_packages[@]}" | grep -q "$pkg"; then
- yes | sudo pacman -Sy --noconfirm "$pkg"
- else
- echo "$pkg exists in AUR packages, skipping pacman installation"
- fi
+ if ! pacman -Qi "$pkg" &> /dev/null; then
+ if ! printf '%s
+' "${aur_package_names[@]}" | grep -Fxq "$pkg"; then
+ yes | sudo pacman -Sy --noconfirm "$pkg"
else
- echo "$pkg is already installed"
+ echo "$pkg exists in AUR packages, skipping pacman installation"
fi
+ else
+ echo "$pkg is already installed"
+ fi
done
if ! command -v nvm &> /dev/null; then
- curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
else
- echo "nvm is already installed"
+ echo "nvm is already installed"
+fi
+export NVM_DIR="$HOME/.nvm"
+if [ -s "$NVM_DIR/nvm.sh" ]; then
+ # shellcheck source=/dev/null
+ . "$NVM_DIR/nvm.sh"
+else
+ echo "nvm.sh not found at $NVM_DIR/nvm.sh" >&2
+fi
+if command -v nvm &> /dev/null; then
+ nvm i v18.20.5
+ nvm install --lts
+else
+ echo "nvm command unavailable; skipping Node installation" >&2
fi
-export NVM_DIR=$HOME/.nvm;
-source $NVM_DIR/nvm.sh;
-nvm i v18.20.5
-nvm install --lts
sudo systemctl enable bluetooth.service
sudo systemctl start bluetooth.service
-# Read AUR packages from file
-declare -a aur_packages
-while IFS= read -r line; do
- # Skip empty lines and comments (lines not starting with alphanumeric characters)
- if [[ -n "$line" && "$line" =~ ^[a-z0-9] ]]; then
- aur_packages+=("$line")
- fi
-done < "aur_packages.txt"
-
for entry in "${aur_packages[@]}"; do
- pkg_name=$(echo "$entry" | cut -d' ' -f1)
- repo_url=$(echo "$entry" | cut -d' ' -f2)
- install_from_aur "$repo_url" "$pkg_name"
+ pkg_name=${entry%% *}
+ repo_url=${entry#* }
+ if [ "$repo_url" = "$pkg_name" ] || [ -z "$repo_url" ]; then
+ repo_url="https://aur.archlinux.org/${pkg_name}.git"
+ fi
+ install_from_aur "$repo_url" "$pkg_name"
done
cd ~/linux-configuration/fresh-install
if [ ! -d "$HOME/.config/mpv" ]; then
- mkdir -p "$HOME/.config/mpv"
+ mkdir -p "$HOME/.config/mpv"
fi
cp mpv.conf "$HOME/.config/mpv/mpv.conf"
if [ ! -d "$HOME/.oh-my-zsh" ]; then
- yes | sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
+ yes | sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
else
- echo "Oh My Zsh is already installed"
+ echo "Oh My Zsh is already installed"
fi
cd ~/linux-configuration
sudo hosts/install.sh
i3-configuration/install.sh
scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh
-scripts/fixes/nvidia_troubleshoot.sh
-sudo scripts/features/setup_activitywatch.sh
+scripts/fixes/nvidia_troubleshoot.sh
+sudo scripts/features/setup_activitywatch.sh
sudo scripts/utils/setup_media_organizer.sh
sudo scripts/digital_wellbeing/setup_pc_startup_monitor.sh
-yes | sudo scripts/setup_periodic_system.sh
+yes | sudo scripts/setup_periodic_system.sh
sudo scripts/setup_thorium_startup.sh
yes | protonup
-yes | sudo pacman -Syuu
+yes | sudo pacman -Syuu
#cd unreal-engine
## gh auth login
diff --git a/fresh-install/makepkg.conf b/fresh-install/makepkg.conf
index ac4c95f..e16580c 100644
--- a/fresh-install/makepkg.conf
+++ b/fresh-install/makepkg.conf
@@ -12,11 +12,11 @@
#-- The download utilities that makepkg should use to acquire sources
# Format: 'protocol::agent'
DLAGENTS=('file::/usr/bin/curl -qgC - -o %o %u'
- 'ftp::/usr/bin/curl -qgfC - --ftp-pasv --retry 3 --retry-delay 3 -o %o %u'
- 'http::/usr/bin/curl -qgb "" -fLC - --retry 3 --retry-delay 3 -o %o %u'
- 'https::/usr/bin/curl -qgb "" -fLC - --retry 3 --retry-delay 3 -o %o %u'
- 'rsync::/usr/bin/rsync --no-motd -z %u %o'
- 'scp::/usr/bin/scp -C %u %o')
+ 'ftp::/usr/bin/curl -qgfC - --ftp-pasv --retry 3 --retry-delay 3 -o %o %u'
+ 'http::/usr/bin/curl -qgb "" -fLC - --retry 3 --retry-delay 3 -o %o %u'
+ 'https::/usr/bin/curl -qgb "" -fLC - --retry 3 --retry-delay 3 -o %o %u'
+ 'rsync::/usr/bin/rsync --no-motd -z %u %o'
+ 'scp::/usr/bin/scp -C %u %o')
# Other common tools:
# /usr/bin/snarf
@@ -26,10 +26,10 @@ DLAGENTS=('file::/usr/bin/curl -qgC - -o %o %u'
#-- The package required by makepkg to download VCS sources
# Format: 'protocol::package'
VCSCLIENTS=('bzr::breezy'
- 'fossil::fossil'
- 'git::git'
- 'hg::mercurial'
- 'svn::subversion')
+ 'fossil::fossil'
+ 'git::git'
+ 'hg::mercurial'
+ 'svn::subversion')
#########################################################################
# ARCHITECTURE, COMPILE FLAGS
diff --git a/hosts/guard/enforce-hosts.sh b/hosts/guard/enforce-hosts.sh
index ba4e4a0..2eba90a 100755
--- a/hosts/guard/enforce-hosts.sh
+++ b/hosts/guard/enforce-hosts.sh
@@ -9,24 +9,24 @@ TARGET="/etc/hosts"
LOG_FILE="/var/log/hosts-guard.log"
log() {
- printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE" >&2
+ printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE" >&2
}
-if [[ ! -f "$CANONICAL_SOURCE" ]]; then
- log "Canonical hosts not found at $CANONICAL_SOURCE; aborting enforcement"
- exit 0
+if [[ ! -f $CANONICAL_SOURCE ]]; then
+ log "Canonical hosts not found at $CANONICAL_SOURCE; aborting enforcement"
+ exit 0
fi
if ! cmp -s "$CANONICAL_SOURCE" "$TARGET"; then
- log "Difference detected – restoring $TARGET from canonical copy"
- cp "$CANONICAL_SOURCE" "$TARGET"
- chmod 644 "$TARGET"
+ log "Difference detected – restoring $TARGET from canonical copy"
+ cp "$CANONICAL_SOURCE" "$TARGET"
+ chmod 644 "$TARGET"
else
- log "No drift detected (contents identical)"
+ log "No drift detected (contents identical)"
fi
# Re-apply protective attributes: immutable first, then read-only bind mount handled by separate unit
-chattr -i -a "$TARGET" 2>/dev/null || true
+chattr -i -a "$TARGET" 2> /dev/null || true
chattr +i "$TARGET" || log "Failed to set immutable attribute"
-log "Enforcement complete"
\ No newline at end of file
+log "Enforcement complete"
diff --git a/hosts/guard/install_pacman_hooks.sh b/hosts/guard/install_pacman_hooks.sh
index c4b63c5..4a1169c 100755
--- a/hosts/guard/install_pacman_hooks.sh
+++ b/hosts/guard/install_pacman_hooks.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
-require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi }
+require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi; }
require_root "$@"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -10,7 +10,7 @@ HOOKS_DIR="/etc/pacman.d/hooks"
install -d -m 755 "$HOOKS_DIR"
# Pre-transaction hook
-cat >"$HOOKS_DIR/10-unlock-etc-hosts.hook" <<'HOOK'
+cat > "$HOOKS_DIR/10-unlock-etc-hosts.hook" << 'HOOK'
[Trigger]
Operation = Upgrade
Operation = Install
@@ -26,7 +26,7 @@ NeedsTargets
HOOK
# Post-transaction hook
-cat >"$HOOKS_DIR/90-relock-etc-hosts.hook" <<'HOOK'
+cat > "$HOOKS_DIR/90-relock-etc-hosts.hook" << 'HOOK'
[Trigger]
Operation = Upgrade
Operation = Install
diff --git a/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh b/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh
index 450f4e7..e2f2a8b 100644
--- a/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh
+++ b/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh
@@ -5,22 +5,22 @@ TARGET=/etc/hosts
ENFORCE=/usr/local/sbin/enforce-hosts.sh
LOGTAG=hosts-guard-hook
-mount_layers_count() { awk '$5=="/etc/hosts"{c++} END{print c+0}' /proc/self/mountinfo 2>/dev/null || echo 0; }
+mount_layers_count() { awk '$5=="/etc/hosts"{c++} END{print c+0}' /proc/self/mountinfo 2> /dev/null || echo 0; }
collapse_mounts() {
local i=0
- if command -v mountpoint >/devnull 2>&1; then
+ if command -v mountpoint > /devnull 2>&1; then
while mountpoint -q "$TARGET"; do
- umount -l "$TARGET" >/dev/null 2>&1 || break
- i=$((i+1))
- (( i > 20 )) && break
+ umount -l "$TARGET" > /dev/null 2>&1 || break
+ i=$((i + 1))
+ ((i > 20)) && break
done
else
local cnt
cnt=$(mount_layers_count)
- while (( cnt > 1 )); do
- umount -l "$TARGET" >/dev/null 2>&1 || break
- i=$((i+1))
- (( i > 20 )) && break
+ while ((cnt > 1)); do
+ umount -l "$TARGET" > /dev/null 2>&1 || break
+ i=$((i + 1))
+ ((i > 20)) && break
cnt=$(mount_layers_count)
done
fi
@@ -28,23 +28,23 @@ collapse_mounts() {
# Ensure we end with a single read-only bind mount layer
logger -t "$LOGTAG" "post: relocking /etc/hosts (starting)"
-echo "$(date -Is) post-relock(start)" >> /run/hosts-guard-hook.log 2>/dev/null || true
+echo "$(date -Is) post-relock(start)" >> /run/hosts-guard-hook.log 2> /dev/null || true
collapse_mounts
-if [[ -x "$ENFORCE" ]]; then
- "$ENFORCE" >/dev/null 2>&1 || true
+if [[ -x $ENFORCE ]]; then
+ "$ENFORCE" > /dev/null 2>&1 || true
fi
# Apply exactly one ro bind layer
-mount --bind "$TARGET" "$TARGET" >/dev/null 2>&1 || true
-mount -o remount,ro,bind "$TARGET" >/dev/null 2>&1 || true
+mount --bind "$TARGET" "$TARGET" > /dev/null 2>&1 || true
+mount -o remount,ro,bind "$TARGET" > /dev/null 2>&1 || true
# Start only the path watcher; avoid bind-mount service (we already bound once)
-if command -v systemctl >/dev/null 2>&1; then
- systemctl start hosts-guard.path >/dev/null 2>&1 || true
+if command -v systemctl > /dev/null 2>&1; then
+ systemctl start hosts-guard.path > /dev/null 2>&1 || true
fi
logger -t "$LOGTAG" "post: relocking /etc/hosts (done)"
-echo "$(date -Is) post-relock(done)" >> /run/hosts-guard-hook.log 2>/dev/null || true
+echo "$(date -Is) post-relock(done)" >> /run/hosts-guard-hook.log 2> /dev/null || true
exit 0
diff --git a/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh b/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh
index e44866a..8edd96b 100644
--- a/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh
+++ b/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh
@@ -7,58 +7,57 @@ LOGTAG=hosts-guard-hook
stop_units_if_present() {
local units=(hosts-bind-mount.service hosts-guard.path)
for u in "${units[@]}"; do
- if command -v systemctl >/dev/null 2>&1; then
- if systemctl list-unit-files 2>/dev/null | grep -q "^$u"; then
- systemctl stop "$u" >/dev/null 2>&1 || true
+ if command -v systemctl > /dev/null 2>&1; then
+ if systemctl list-unit-files 2> /dev/null | grep -q "^$u"; then
+ systemctl stop "$u" > /dev/null 2>&1 || true
fi
fi
done
}
-is_ro_mount() { findmnt -no OPTIONS -T "$TARGET" 2>/dev/null | grep -qw ro; }
-is_bind_mount() { findmnt -no OPTIONS -T "$TARGET" 2>/dev/null | grep -qw bind; }
+is_ro_mount() { findmnt -no OPTIONS -T "$TARGET" 2> /dev/null | grep -qw ro; }
-mount_layers_count() { awk '$5=="/etc/hosts"{c++} END{print c+0}' /proc/self/mountinfo 2>/dev/null || echo 0; }
+mount_layers_count() { awk '$5=="/etc/hosts"{c++} END{print c+0}' /proc/self/mountinfo 2> /dev/null || echo 0; }
cleanup_mount_stacks() {
local i=0
# Unmount bind layers until /etc/hosts is no longer a mountpoint
- if command -v mountpoint >/dev/null 2>&1; then
+ if command -v mountpoint > /dev/null 2>&1; then
while mountpoint -q "$TARGET"; do
- umount -l "$TARGET" >/dev/null 2>&1 || break
- i=$((i+1))
- (( i > 20 )) && break
+ umount -l "$TARGET" > /dev/null 2>&1 || break
+ i=$((i + 1))
+ ((i > 20)) && break
done
else
# Fallback to best-effort using mountinfo count
local cnt
cnt=$(mount_layers_count)
- while (( cnt > 1 )); do
- umount -l "$TARGET" >/dev/null 2>&1 || break
- i=$((i+1))
- (( i > 20 )) && break
+ while ((cnt > 1)); do
+ umount -l "$TARGET" > /dev/null 2>&1 || break
+ i=$((i + 1))
+ ((i > 20)) && break
cnt=$(mount_layers_count)
done
fi
}
# Drop protective attributes if present
-if command -v lsattr >/dev/null 2>&1; then
- attrs=$(lsattr -d "$TARGET" 2>/dev/null || true)
- echo "$attrs" | grep -q " i " && chattr -i "$TARGET" >/dev/null 2>&1 || true
- echo "$attrs" | grep -q " a " && chattr -a "$TARGET" >/dev/null 2>&1 || true
+if command -v lsattr > /dev/null 2>&1; then
+ attrs=$(lsattr -d "$TARGET" 2> /dev/null || true)
+ echo "$attrs" | grep -q " i " && chattr -i "$TARGET" > /dev/null 2>&1 || true
+ echo "$attrs" | grep -q " a " && chattr -a "$TARGET" > /dev/null 2>&1 || true
fi
stop_units_if_present
logger -t "$LOGTAG" "pre: unlocking /etc/hosts (starting)"
-echo "$(date -Is) pre-unlock" >> /run/hosts-guard-hook.log 2>/dev/null || true
+echo "$(date -Is) pre-unlock" >> /run/hosts-guard-hook.log 2> /dev/null || true
# Always collapse any existing layers; we'll operate on the plain file
cleanup_mount_stacks
# If someone managed a ro single-layer mount, ensure rw by remounting or collapsing again
if is_ro_mount; then
- mount -o remount,rw "$TARGET" >/dev/null 2>&1 || cleanup_mount_stacks
+ mount -o remount,rw "$TARGET" > /dev/null 2>&1 || cleanup_mount_stacks
fi
logger -t "$LOGTAG" "pre: unlocking /etc/hosts (done)"
diff --git a/hosts/guard/psychological/unlock-hosts.sh b/hosts/guard/psychological/unlock-hosts.sh
index 4b82faf..959ae0e 100644
--- a/hosts/guard/psychological/unlock-hosts.sh
+++ b/hosts/guard/psychological/unlock-hosts.sh
@@ -11,36 +11,36 @@ DELAY_SECONDS=45
log() { printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG" >&2; }
-require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi }
+require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi; }
require_root "$@"
echo "Reason for editing /etc/hosts (will be logged):" >&2
read -r -p "Enter reason: " REASON
-if [[ -z ${REASON// } ]]; then
- echo "Empty reason not allowed. Aborting." >&2
- exit 1
+if [[ -z ${REASON// /} ]]; then
+ echo "Empty reason not allowed. Aborting." >&2
+ exit 1
fi
log "Requested intentional /etc/hosts modification session. Reason: $REASON"
logger -t "$SYSLOG_TAG" "session_start user=${SUDO_USER:-$USER} reason='$REASON'"
echo "This action is logged. A cooling-off delay of $DELAY_SECONDS seconds applies." >&2
for s in hosts-bind-mount.service hosts-guard.path; do
- if systemctl is-active --quiet "$s"; then
- log "Stopping $s"
- systemctl stop "$s" || true
- fi
- if systemctl is-enabled --quiet "$s"; then
- log "(Will re-enable later)"
- fi
+ if systemctl is-active --quiet "$s"; then
+ log "Stopping $s"
+ systemctl stop "$s" || true
+ fi
+ if systemctl is-enabled --quiet "$s"; then
+ log "(Will re-enable later)"
+ fi
done
# Remove attributes to allow edit
-chattr -i -a "$TARGET" 2>/dev/null || true
+chattr -i -a "$TARGET" 2> /dev/null || true
echo "Countdown:" >&2
-for ((i=DELAY_SECONDS; i>0; i--)); do
- printf '\rEdit window opens in %2d seconds... Press Ctrl+C to abort.' "$i" >&2
- sleep 1
+for ((i = DELAY_SECONDS; i > 0; i--)); do
+ printf '\rEdit window opens in %2d seconds... Press Ctrl+C to abort.' "$i" >&2
+ sleep 1
done
echo >&2
@@ -49,13 +49,13 @@ sha_before=$(sha256sum "$TARGET" | awk '{print $1}')
"$EDITOR_CMD" "$TARGET"
sha_after=$(sha256sum "$TARGET" | awk '{print $1}')
-if [[ "$sha_before" == "$sha_after" ]]; then
- log "No changes made to $TARGET. Reason: $REASON"
- logger -t "$SYSLOG_TAG" "no_change user=${SUDO_USER:-$USER} reason='$REASON'"
+if [[ $sha_before == "$sha_after" ]]; then
+ log "No changes made to $TARGET. Reason: $REASON"
+ logger -t "$SYSLOG_TAG" "no_change user=${SUDO_USER:-$USER} reason='$REASON'"
else
- log "Changes detected. Updating canonical copy and re-enforcing. Reason: $REASON"
- logger -t "$SYSLOG_TAG" "modified user=${SUDO_USER:-$USER} reason='$REASON'"
- cp "$TARGET" "$CANON"
+ log "Changes detected. Updating canonical copy and re-enforcing. Reason: $REASON"
+ logger -t "$SYSLOG_TAG" "modified user=${SUDO_USER:-$USER} reason='$REASON'"
+ cp "$TARGET" "$CANON"
fi
# Re-run enforcement
diff --git a/hosts/guard/setup_hosts_guard.sh b/hosts/guard/setup_hosts_guard.sh
index 7da6cdc..19da91c 100755
--- a/hosts/guard/setup_hosts_guard.sh
+++ b/hosts/guard/setup_hosts_guard.sh
@@ -46,10 +46,20 @@ ADD_ALIAS_STUB=1
msg() { printf '\e[1;32m[+]\e[0m %s\n' "$*"; }
note() { printf '\e[1;34m[i]\e[0m %s\n' "$*"; }
warn() { printf '\e[1;33m[!]\e[0m %s\n' "$*"; }
-err() { printf '\e[1;31m[x]\e[0m %s\n' "$*" >&2; }
-run() { if [[ $DRY_RUN -eq 1 ]]; then printf 'DRY-RUN: %s\n' "$*"; else eval "$@"; fi }
+err() { printf '\e[1;31m[x]\e[0m %s\n' "$*" >&2; }
+run() {
+ if [[ $DRY_RUN -eq 1 ]]; then
+ printf 'DRY-RUN:'
+ if [ "$#" -gt 0 ]; then
+ printf ' %q' "$@"
+ fi
+ printf '\n'
+ else
+ "$@"
+ fi
+}
-require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi }
+require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi; }
usage() { sed -n '1,/^set -euo pipefail/p' "$0" | sed 's/^# \{0,1\}//'; }
@@ -58,21 +68,71 @@ usage() { sed -n '1,/^set -euo pipefail/p' "$0" | sed 's/^# \{0,1\}//'; }
######################################################################
while [[ $# -gt 0 ]]; do
case "$1" in
- --force-snapshot) FORCE_SNAPSHOT=1 ; shift ;;
- --no-snapshot) DO_SNAPSHOT=0 ; shift ;;
- --skip-bind) ENABLE_BIND=0 ; shift ;;
- --skip-path-watch) ENABLE_PATH=0 ; shift ;;
- --delay) DELAY=${2:-} ; [[ -z ${DELAY} ]] && { err '--delay requires value'; exit 2; } ; shift 2 ;;
- --dry-run) DRY_RUN=1 ; shift ;;
- --no-shell-hooks) INSTALL_SHELL_HOOKS=0 ; shift ;;
- --shell-hooks) INSTALL_SHELL_HOOKS=1 ; shift ;;
- --no-audit) INSTALL_AUDIT_RULE=0 ; shift ;;
- --audit) INSTALL_AUDIT_RULE=1 ; shift ;;
- --no-alias-stub) ADD_ALIAS_STUB=0 ; shift ;;
- --alias-stub) ADD_ALIAS_STUB=1 ; shift ;;
- --uninstall) UNINSTALL=1 ; shift ;;
- -h|--help) usage; exit 0 ;;
- *) err "Unknown argument: $1"; usage; exit 2 ;;
+ --force-snapshot)
+ FORCE_SNAPSHOT=1
+ shift
+ ;;
+ --no-snapshot)
+ DO_SNAPSHOT=0
+ shift
+ ;;
+ --skip-bind)
+ ENABLE_BIND=0
+ shift
+ ;;
+ --skip-path-watch)
+ ENABLE_PATH=0
+ shift
+ ;;
+ --delay)
+ DELAY=${2:-}
+ [[ -z ${DELAY} ]] && {
+ err '--delay requires value'
+ exit 2
+ }
+ shift 2
+ ;;
+ --dry-run)
+ DRY_RUN=1
+ shift
+ ;;
+ --no-shell-hooks)
+ INSTALL_SHELL_HOOKS=0
+ shift
+ ;;
+ --shell-hooks)
+ INSTALL_SHELL_HOOKS=1
+ shift
+ ;;
+ --no-audit)
+ INSTALL_AUDIT_RULE=0
+ shift
+ ;;
+ --audit)
+ INSTALL_AUDIT_RULE=1
+ shift
+ ;;
+ --no-alias-stub)
+ ADD_ALIAS_STUB=0
+ shift
+ ;;
+ --alias-stub)
+ ADD_ALIAS_STUB=1
+ shift
+ ;;
+ --uninstall)
+ UNINSTALL=1
+ shift
+ ;;
+ -h | --help)
+ usage
+ exit 0
+ ;;
+ *)
+ err "Unknown argument: $1"
+ usage
+ exit 2
+ ;;
esac
done
@@ -119,7 +179,7 @@ if [[ $UNINSTALL -eq 1 ]]; then
"$SYSTEMD_DIR/hosts-bind-mount.service" \
"$ZSH_FILTER_SNIPPET" \
"$BASH_FILTER_SNIPPET"; do
- if [[ -e $f ]]; then run rm -f "$f"; fi
+ if [[ -e $f ]]; then run rm -f "$f"; fi
done
note "Leaving canonical snapshot at $CANON (remove manually if undesired)."
if [[ $DRY_RUN -eq 0 ]]; then systemctl daemon-reload; fi
@@ -134,10 +194,13 @@ note "Script directory: $SCRIPT_DIR"
note "Repository root: $REPO_ROOT"
for req in "$TEMPLATE_ENFORCE" "$TEMPLATE_UNLOCK" "$UNIT_GUARD_SERVICE"; do
- [[ -f $req ]] || { err "Missing template: $req"; exit 1; }
+ [[ -f $req ]] || {
+ err "Missing template: $req"
+ exit 1
+ }
done
-if [[ ! -f "$HOSTS" ]]; then
+if [[ ! -f $HOSTS ]]; then
err "$HOSTS does not exist. Run your hosts/install.sh first."
exit 1
fi
@@ -146,7 +209,7 @@ fi
# Snapshot
######################################################################
if [[ $DO_SNAPSHOT -eq 1 ]]; then
- if [[ -f "$CANON" && $FORCE_SNAPSHOT -eq 0 ]]; then
+ if [[ -f $CANON && $FORCE_SNAPSHOT -eq 0 ]]; then
note "Canonical snapshot exists (use --force-snapshot to overwrite)"
else
msg "Creating canonical snapshot at $CANON"
@@ -182,14 +245,12 @@ fi
if [[ $INSTALL_SHELL_HOOKS -eq 1 ]]; then
msg "Installing shell history suppression hooks for unlock command"
# Pattern matches commands invoking unlock-hosts (with or without sudo) & setup script force snapshot
- FILTER_PATTERN='(^|;|&&|\|\|)\s*(sudo\s+)?(/usr/local/sbin/)?unlock-hosts(\s|;|$)'
-
# Zsh: use zshaddhistory function
- if command -v zsh >/dev/null 2>&1; then
+ if command -v zsh > /dev/null 2>&1; then
if [[ $DRY_RUN -eq 1 ]]; then
echo "DRY-RUN: would create $ZSH_FILTER_SNIPPET"
else
- cat > "$ZSH_FILTER_SNIPPET" <<'ZEOF'
+ cat > "$ZSH_FILTER_SNIPPET" << 'ZEOF'
# Added by hosts guard setup – suppress unlock-hosts commands from Zsh history
autoload -Uz add-zsh-hook 2>/dev/null || true
_hosts_guard_history_filter() {
@@ -213,11 +274,11 @@ ZEOF
fi
# Bash: rely on HISTCONTROL and PROMPT_COMMAND filter
- if command -v bash >/dev/null 2>&1; then
+ if command -v bash > /dev/null 2>&1; then
if [[ $DRY_RUN -eq 1 ]]; then
echo "DRY-RUN: would create $BASH_FILTER_SNIPPET"
else
- cat > "$BASH_FILTER_SNIPPET" <<'BEOF'
+ cat > "$BASH_FILTER_SNIPPET" << 'BEOF'
# Added by hosts guard setup – suppress unlock-hosts commands from Bash history
export HISTCONTROL=ignoredups:erasedups
_hosts_guard_hist_filter() {
@@ -253,7 +314,7 @@ if [[ $ADD_ALIAS_STUB -eq 1 ]]; then
if [[ $DRY_RUN -eq 1 ]]; then
echo "DRY-RUN: would create $PROFILE_STUB"
else
- cat > "$PROFILE_STUB" <<'ASTUB'
+ cat > "$PROFILE_STUB" << 'ASTUB'
# Added by hosts guard setup – discourages casual use of unlock-hosts name
if command -v unlock-hosts >/dev/null 2>&1; then
alias unlock-hosts='command_not_found_handle 2>/dev/null || echo "Use: sudo /usr/local/sbin/unlock-hosts (logged & delayed)"'
@@ -267,16 +328,17 @@ fi
# Audit rule to record executions (requires auditd)
######################################################################
if [[ $INSTALL_AUDIT_RULE -eq 1 ]]; then
- if command -v auditctl >/dev/null 2>&1; then
- AUDIT_RULE="-w /usr/local/sbin/unlock-hosts -p x -k hosts_unlock"
- if auditctl -l 2>/dev/null | grep -Fq "/usr/local/sbin/unlock-hosts"; then
+ if command -v auditctl > /dev/null 2>&1; then
+ audit_rule_str="-w /usr/local/sbin/unlock-hosts -p x -k hosts_unlock"
+ audit_rule_args=(-w /usr/local/sbin/unlock-hosts -p x -k hosts_unlock)
+ if auditctl -l 2> /dev/null | grep -Fq "/usr/local/sbin/unlock-hosts"; then
note "Audit rule already present"
else
- run auditctl $AUDIT_RULE || warn "Failed to add audit rule (runtime)"
+ run auditctl "${audit_rule_args[@]}" || warn "Failed to add audit rule (runtime)"
if [[ $DRY_RUN -eq 1 ]]; then
echo "DRY-RUN: would create /etc/audit/rules.d/hosts_unlock.rules"
else
- echo "$AUDIT_RULE" > /etc/audit/rules.d/hosts_unlock.rules
+ echo "$audit_rule_str" > /etc/audit/rules.d/hosts_unlock.rules
fi
fi
else
@@ -323,22 +385,22 @@ fi
######################################################################
echo
msg "Hosts guard setup complete"
-echo "Canonical copy: $CANON"
-echo "Enforce script: $INSTALL_ENFORCE"
-echo "Unlock command: sudo $INSTALL_UNLOCK"
-echo "Delay (seconds): $DELAY"
-echo "Auto-revert path watch: $([[ $ENABLE_PATH -eq 1 ]] && echo enabled || echo disabled)"
-echo "Read-only bind mount: $([[ $ENABLE_BIND -eq 1 ]] && echo enabled || echo disabled)"
+echo "Canonical copy: $CANON"
+echo "Enforce script: $INSTALL_ENFORCE"
+echo "Unlock command: sudo $INSTALL_UNLOCK"
+echo "Delay (seconds): $DELAY"
+echo "Auto-revert path watch: $([[ $ENABLE_PATH -eq 1 ]] && echo enabled || echo disabled)"
+echo "Read-only bind mount: $([[ $ENABLE_BIND -eq 1 ]] && echo enabled || echo disabled)"
echo "Shell history suppression: $([[ $INSTALL_SHELL_HOOKS -eq 1 ]] && echo enabled || echo disabled)"
echo "Audit rule: $([[ $INSTALL_AUDIT_RULE -eq 1 ]] && echo enabled || echo disabled)"
echo "Alias stub: $([[ $ADD_ALIAS_STUB -eq 1 ]] && echo enabled || echo disabled)"
echo
-echo "Test flow:"
-echo " sudo sed -i '1s/.*/# tamper test/' /etc/hosts # Should revert automatically"
-echo " sudo $INSTALL_UNLOCK # Intentional edit workflow"
+echo "Test flow:"
+echo " sudo sed -i '1s/.*/# tamper test/' /etc/hosts # Should revert automatically"
+echo " sudo $INSTALL_UNLOCK # Intentional edit workflow"
echo
-echo "Uninstall:"
-echo " sudo $0 --uninstall"
-echo "(Optional) Skip shell history hooks: --no-shell-hooks"
+echo "Uninstall:"
+echo " sudo $0 --uninstall"
+echo "(Optional) Skip shell history hooks: --no-shell-hooks"
echo
exit 0
diff --git a/hosts/install.sh b/hosts/install.sh
index 8624cf5..eaa0035 100755
--- a/hosts/install.sh
+++ b/hosts/install.sh
@@ -2,7 +2,7 @@
# Re-run with sudo if not root
if [[ $EUID -ne 0 ]]; then
- exec sudo -E bash "$0" "$@"
+ exec sudo -E bash "$0" "$@"
fi
# Options
@@ -11,25 +11,25 @@ FLUSH_DNS=0
# Parse CLI flags
for arg in "$@"; do
- case "$arg" in
- --flush-dns)
- FLUSH_DNS=1
- ;;
- --no-flush-dns)
- FLUSH_DNS=0
- ;;
- -h|--help)
- echo "Usage: $0 [--flush-dns|--no-flush-dns]"
- exit 0
- ;;
- esac
+ case "$arg" in
+ --flush-dns)
+ FLUSH_DNS=1
+ ;;
+ --no-flush-dns)
+ FLUSH_DNS=0
+ ;;
+ -h | --help)
+ echo "Usage: $0 [--flush-dns|--no-flush-dns]"
+ exit 0
+ ;;
+ esac
done
# Enable systemd-resolved
sudo systemctl enable systemd-resolved
# Remove all attributes from /etc/hosts to allow modifications
-sudo chattr -i -a /etc/hosts 2>/dev/null || true
+sudo chattr -i -a /etc/hosts 2> /dev/null || true
# Source and local cache configuration
URL="https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews-gambling-porn-social/hosts"
@@ -38,33 +38,33 @@ LOCAL_CACHE="/etc/hosts.stevenblack"
# Helpers
extract_date_epoch_from_file() {
- # Grep "# Date:" line and convert to epoch seconds (UTC)
- local f="$1"
- local line
- line=$(grep -m1 '^# Date:' "$f" 2>/dev/null | sed -E 's/^# Date:[[:space:]]*(.*)[[:space:]]*\(UTC\).*/\1 UTC/')
- if [[ -n "$line" ]]; then
- date -u -d "$line" +%s 2>/dev/null || echo ""
- else
- echo ""
- fi
+ # Grep "# Date:" line and convert to epoch seconds (UTC)
+ local f="$1"
+ local line
+ line=$(grep -m1 '^# Date:' "$f" 2> /dev/null | sed -E 's/^# Date:[[:space:]]*(.*)[[:space:]]*\(UTC\).*/\1 UTC/')
+ if [[ -n $line ]]; then
+ date -u -d "$line" +%s 2> /dev/null || echo ""
+ else
+ echo ""
+ fi
}
fetch_remote_header() {
- # Try to fetch only the first ~4KB using HTTP Range; fallback to piping to head
- local out="$1"
- if curl -LfsS --max-time 10 -H 'Range: bytes=0-4095' "$URL" -o "$out"; then
- return 0
- fi
- # Fallback – may download more, but we only keep first lines
- if curl -LfsS --max-time 10 "$URL" | head -n 20 > "$out"; then
- return 0
- fi
- return 1
+ # Try to fetch only the first ~4KB using HTTP Range; fallback to piping to head
+ local out="$1"
+ if curl -LfsS --max-time 10 -H 'Range: bytes=0-4095' "$URL" -o "$out"; then
+ return 0
+ fi
+ # Fallback – may download more, but we only keep first lines
+ if curl -LfsS --max-time 10 "$URL" | head -n 20 > "$out"; then
+ return 0
+ fi
+ return 1
}
download_remote_full_to() {
- local out="$1"
- curl -LfsS "$URL" -o "$out"
+ local out="$1"
+ curl -LfsS "$URL" -o "$out"
}
# Decide whether to use cache or update
@@ -73,50 +73,47 @@ trap 'rm -f "$TMP_REMOTE_HEAD"' EXIT
REMOTE_AVAILABLE=0
if fetch_remote_header "$TMP_REMOTE_HEAD"; then
- REMOTE_AVAILABLE=1
+ REMOTE_AVAILABLE=1
fi
-USE_CACHE=0
NEED_UPDATE=0
-if [[ -f "$LOCAL_CACHE" ]]; then
- local_epoch=$(extract_date_epoch_from_file "$LOCAL_CACHE")
+if [[ -f $LOCAL_CACHE ]]; then
+ local_epoch=$(extract_date_epoch_from_file "$LOCAL_CACHE")
else
- local_epoch=""
+ local_epoch=""
fi
if [[ $REMOTE_AVAILABLE -eq 1 ]]; then
- remote_epoch=$(extract_date_epoch_from_file "$TMP_REMOTE_HEAD")
- if [[ -n "$local_epoch" && -n "$remote_epoch" && "$local_epoch" -ge "$remote_epoch" ]]; then
- echo "Using cached StevenBlack hosts (up-to-date)."
- USE_CACHE=1
- else
- echo "Cached version is missing or outdated; downloading latest StevenBlack hosts..."
- NEED_UPDATE=1
- fi
+ remote_epoch=$(extract_date_epoch_from_file "$TMP_REMOTE_HEAD")
+ if [[ -n $local_epoch && -n $remote_epoch && $local_epoch -ge $remote_epoch ]]; then
+ echo "Using cached StevenBlack hosts (up-to-date)."
+ else
+ echo "Cached version is missing or outdated; downloading latest StevenBlack hosts..."
+ NEED_UPDATE=1
+ fi
else
- if [[ -f "$LOCAL_CACHE" ]]; then
- echo "No internet; using cached StevenBlack hosts."
- USE_CACHE=1
- else
- echo "Error: No internet and no cached StevenBlack hosts found." >&2
- exit 1
- fi
+ if [[ -f $LOCAL_CACHE ]]; then
+ echo "No internet; using cached StevenBlack hosts."
+ else
+ echo "Error: No internet and no cached StevenBlack hosts found." >&2
+ exit 1
+ fi
fi
# Ensure we have a fresh cache if needed
if [[ $NEED_UPDATE -eq 1 ]]; then
- TMP_DL=$(mktemp)
- if download_remote_full_to "$TMP_DL"; then
- # Save raw upstream to cache
- sudo mv "$TMP_DL" "$LOCAL_CACHE"
- sudo chmod 644 "$LOCAL_CACHE"
- echo "Saved latest StevenBlack hosts to cache: $LOCAL_CACHE"
- else
- rm -f "$TMP_DL"
- echo "Error: Failed to download latest StevenBlack hosts." >&2
- exit 1
- fi
+ TMP_DL=$(mktemp)
+ if download_remote_full_to "$TMP_DL"; then
+ # Save raw upstream to cache
+ sudo mv "$TMP_DL" "$LOCAL_CACHE"
+ sudo chmod 644 "$LOCAL_CACHE"
+ echo "Saved latest StevenBlack hosts to cache: $LOCAL_CACHE"
+ else
+ rm -f "$TMP_DL"
+ echo "Error: Failed to download latest StevenBlack hosts." >&2
+ exit 1
+ fi
fi
# Install the base hosts from cache into /etc/hosts
@@ -265,10 +262,10 @@ sudo chattr -i /etc/hosts
sudo chattr +a /etc/hosts
# Optionally flush DNS caches
-if [[ "$FLUSH_DNS" -eq 1 ]]; then
- echo "Flushing DNS caches..."
- sudo systemd-resolve --flush-caches
- sudo systemctl restart NetworkManager.service
+if [[ $FLUSH_DNS -eq 1 ]]; then
+ echo "Flushing DNS caches..."
+ sudo systemd-resolve --flush-caches
+ sudo systemctl restart NetworkManager.service
else
- echo "DNS cache flush skipped (use --flush-dns to enable)."
-fi
\ No newline at end of file
+ echo "DNS cache flush skipped (use --flush-dns to enable)."
+fi
diff --git a/i3-configuration/i3blocks/activitywatch_status.sh b/i3-configuration/i3blocks/activitywatch_status.sh
index ecb9cba..9fa9efe 100755
--- a/i3-configuration/i3blocks/activitywatch_status.sh
+++ b/i3-configuration/i3blocks/activitywatch_status.sh
@@ -4,45 +4,45 @@
# Check if ActivityWatch is installed
check_installed() {
- # Check if activitywatch-bin package is installed
- if pacman -Qi activitywatch-bin &>/dev/null; then
- return 0
- fi
-
- # Check if aw-qt binary exists
- if command -v aw-qt &>/dev/null; then
- return 0
- fi
-
- return 1
+ # Check if activitywatch-bin package is installed
+ if pacman -Qi activitywatch-bin &> /dev/null; then
+ return 0
+ fi
+
+ # Check if aw-qt binary exists
+ if command -v aw-qt &> /dev/null; then
+ return 0
+ fi
+
+ return 1
}
# Check if ActivityWatch is running
check_running() {
- # Check for aw-qt process
- if pgrep -f "aw-qt" >/dev/null 2>&1; then
- return 0
- fi
-
- # Check for aw-server process
- if pgrep -f "aw-server" >/dev/null 2>&1; then
- return 0
- fi
-
- return 1
+ # Check for aw-qt process
+ if pgrep -f "aw-qt" > /dev/null 2>&1; then
+ return 0
+ fi
+
+ # Check for aw-server process
+ if pgrep -f "aw-server" > /dev/null 2>&1; then
+ return 0
+ fi
+
+ return 1
}
# Main logic
if ! check_installed; then
- echo "AW uninstalled"
- echo
- echo "#FF0000" # Red
+ echo "AW uninstalled"
+ echo
+ echo "#FF0000" # Red
elif check_running; then
- echo "AW on"
- echo
- echo "#00FF00" # Green
+ echo "AW on"
+ echo
+ echo "#00FF00" # Green
else
- echo "AW off"
- echo
- echo "#FF0000" # Red
+ echo "AW off"
+ echo
+ echo "#FF0000" # Red
fi
diff --git a/i3-configuration/i3blocks/battery_status.sh b/i3-configuration/i3blocks/battery_status.sh
index bba1a05..8d58b91 100755
--- a/i3-configuration/i3blocks/battery_status.sh
+++ b/i3-configuration/i3blocks/battery_status.sh
@@ -8,4 +8,4 @@ acpi -b | awk -F', ' '
if (time[1] != "") printf ", %s", time[1]
if ($1 ~ /Charging/) printf ", "
printf "\n"
- }'
\ No newline at end of file
+ }'
diff --git a/i3-configuration/i3blocks/bluetooth.sh b/i3-configuration/i3blocks/bluetooth.sh
index 02af983..bdd9bb8 100755
--- a/i3-configuration/i3blocks/bluetooth.sh
+++ b/i3-configuration/i3blocks/bluetooth.sh
@@ -5,11 +5,10 @@ bluetooth_info=$(bluetoothctl info)
# Check if Bluetooth is connected
if echo "$bluetooth_info" | grep -q "Connected: yes"; then
- device=$(echo "$bluetooth_info" | grep "Alias" | cut -d ' ' -f2-)
- echo " $device" # is the Bluetooth icon
- echo
- echo "#50FA7B" # Green for connected
+ device=$(echo "$bluetooth_info" | grep "Alias" | cut -d ' ' -f2-)
+ echo " $device" # is the Bluetooth icon
+ echo
+ echo "#50FA7B" # Green for connected
else
- echo " Disconnected"
+ echo " Disconnected"
fi
-
diff --git a/i3-configuration/i3blocks/cpu_monitor.sh b/i3-configuration/i3blocks/cpu_monitor.sh
index c720dfa..f2b7edc 100755
--- a/i3-configuration/i3blocks/cpu_monitor.sh
+++ b/i3-configuration/i3blocks/cpu_monitor.sh
@@ -3,46 +3,46 @@
# CPU Temperature
cpu_temp=$(sensors | awk '/^Tctl:/ {print $2}' | tr -d '+°C')
if [ -z "$cpu_temp" ]; then
- cpu_temp=$(sensors | awk '/^Package id 0:/ {print $4}' | tr -d '+°C')
+ cpu_temp=$(sensors | awk '/^Package id 0:/ {print $4}' | tr -d '+°C')
fi
if [ -z "$cpu_temp" ]; then
- cpu_temp=$(sensors | awk '/^Core 0:/ {print $3}' | tr -d '+°C')
+ cpu_temp=$(sensors | awk '/^Core 0:/ {print $3}' | tr -d '+°C')
fi
if [ -z "$cpu_temp" ]; then
- cpu_temp="N/A"
+ cpu_temp="N/A"
fi
# CPU Load (1-minute average)
cpu_load=$(awk '{print $1}' /proc/loadavg)
if [ -z "$cpu_load" ]; then
- cpu_load="N/A"
+ cpu_load="N/A"
fi
# Colors for CPU Load and Temperature
-cpu_color="#FFFFFF" # Default color
+cpu_color="#FFFFFF" # Default color
# Change color based on CPU load
-if [[ "$cpu_load" != "N/A" ]]; then
- cpu_load_float=$(echo "$cpu_load" | awk '{print ($1 + 0)}')
- if (( $(echo "$cpu_load_float < 1.0" | bc -l) )); then
- cpu_color="#50FA7B" # Green for low load
- elif (( $(echo "$cpu_load_float < 2.0" | bc -l) )); then
- cpu_color="#F1FA8C" # Yellow for medium load
- else
- cpu_color="#FF5555" # Red for high load
- fi
+if [[ $cpu_load != "N/A" ]]; then
+ cpu_load_float=$(echo "$cpu_load" | awk '{print ($1 + 0)}')
+ if (($(echo "$cpu_load_float < 1.0" | bc -l))); then
+ cpu_color="#50FA7B" # Green for low load
+ elif (($(echo "$cpu_load_float < 2.0" | bc -l))); then
+ cpu_color="#F1FA8C" # Yellow for medium load
+ else
+ cpu_color="#FF5555" # Red for high load
+ fi
fi
# Change color based on CPU temperature
-if [[ "$cpu_temp" != "N/A" ]]; then
- cpu_temp_float=$(echo "$cpu_temp" | awk '{print ($1 + 0)}')
- if (( $(echo "$cpu_temp_float < 65.0" | bc -l) )); then
- cpu_color="#50FA7B" # Green for low temperature
- elif (( $(echo "$cpu_temp_float < 85.0" | bc -l) )); then
- cpu_color="#F1FA8C" # Yellow for medium temperature
- else
- cpu_color="#FF5555" # Red for high temperature
- fi
+if [[ $cpu_temp != "N/A" ]]; then
+ cpu_temp_float=$(echo "$cpu_temp" | awk '{print ($1 + 0)}')
+ if (($(echo "$cpu_temp_float < 65.0" | bc -l))); then
+ cpu_color="#50FA7B" # Green for low temperature
+ elif (($(echo "$cpu_temp_float < 85.0" | bc -l))); then
+ cpu_color="#F1FA8C" # Yellow for medium temperature
+ else
+ cpu_color="#FF5555" # Red for high temperature
+ fi
fi
-echo -e " ${cpu_temp}°C, ${cpu_load}"
\ No newline at end of file
+echo -e " ${cpu_temp}°C, ${cpu_load}"
diff --git a/i3-configuration/i3blocks/gpu_monitor.sh b/i3-configuration/i3blocks/gpu_monitor.sh
index 412e079..cfe1c8b 100755
--- a/i3-configuration/i3blocks/gpu_monitor.sh
+++ b/i3-configuration/i3blocks/gpu_monitor.sh
@@ -2,41 +2,41 @@
# Function to get NVIDIA GPU metrics
get_nvidia_metrics() {
- gpu_temp=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits 2>/dev/null)
- if [ -z "$gpu_temp" ]; then
- gpu_temp="N/A"
- fi
+ gpu_temp=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits 2> /dev/null)
+ if [ -z "$gpu_temp" ]; then
+ gpu_temp="N/A"
+ fi
- gpu_load=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null)
- if [ -z "$gpu_load" ]; then
- gpu_load="N/A"
- fi
+ gpu_load=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2> /dev/null)
+ if [ -z "$gpu_load" ]; then
+ gpu_load="N/A"
+ fi
- echo "GPU Temp: $gpu_temp°C, GPU Load: $gpu_load"
+ echo "GPU Temp: $gpu_temp°C, GPU Load: $gpu_load"
}
# Function to get Intel GPU metrics
get_intel_metrics() {
- gpu_load=$(cat /sys/class/drm/card0/device/gpu_busy_percent 2>/dev/null)
- if [ -z "$gpu_load" ]; then
- gpu_load="N/A"
- fi
+ gpu_load=$(cat /sys/class/drm/card0/device/gpu_busy_percent 2> /dev/null)
+ if [ -z "$gpu_load" ]; then
+ gpu_load="N/A"
+ fi
- gpu_temp=$(sensors | awk '/^temp1:/ {print $2; exit}' | tr -d '+°C')
- if [ -z "$gpu_temp" ]; then
- gpu_temp="N/A"
- fi
+ gpu_temp=$(sensors | awk '/^temp1:/ {print $2; exit}' | tr -d '+°C')
+ if [ -z "$gpu_temp" ]; then
+ gpu_temp="N/A"
+ fi
- echo "GPU Temp: $gpu_temp°C, GPU Load: $gpu_load"
+ echo "GPU Temp: $gpu_temp°C, GPU Load: $gpu_load"
}
# Detect GPU type and get metrics
if lspci | grep -i nvidia > /dev/null; then
- gpu_metrics=$(get_nvidia_metrics)
+ gpu_metrics=$(get_nvidia_metrics)
elif lspci | grep -i vga | grep -i intel > /dev/null; then
- gpu_metrics=$(get_intel_metrics)
+ gpu_metrics=$(get_intel_metrics)
else
- echo "No supported GPU found."
+ echo "No supported GPU found."
fi
#!/bin/bash
@@ -44,22 +44,21 @@ fi
gpu_temp=$(echo "$gpu_metrics" | awk -F', ' '{print $1}' | awk -F': ' '{print $2}')
gpu_load=$(echo "$gpu_metrics" | awk -F', ' '{print $2}' | awk -F': ' '{print $2}')
-gpu_color="#FFFFFF"
+gpu_color="#FFFFFF"
# Colors for GPU Load
-if [[ "$gpu_load" != "N/A" ]]; then
- if (( $(echo "$gpu_load < 50.0" | bc -l) )); then
- gpu_color="#50FA7B" # Green
- elif (( $(echo "$gpu_load < 75.0" | bc -l) )); then
- gpu_color="#F1FA8C" # Yellow
- else
- gpu_color="#FF5555" # Red
- fi
-else
- gpu_color="#FFFFFF" # Default color
+if [[ $gpu_load != "N/A" ]]; then
+ if (($(echo "$gpu_load < 50.0" | bc -l))); then
+ gpu_color="#50FA7B" # Green
+ elif (($(echo "$gpu_load < 75.0" | bc -l))); then
+ gpu_color="#F1FA8C" # Yellow
+ else
+ gpu_color="#FF5555" # Red
+ fi
+else
+ gpu_color="#FFFFFF" # Default color
fi
# Output<
echo -e " ${gpu_temp}, ${gpu_load}%"
echo
-echo "#FFFFFF" # Default color for fallback (ignored if markup is enabled)
-
+echo "#FFFFFF" # Default color for fallback (ignored if markup is enabled)
diff --git a/i3-configuration/i3blocks/motherboard_temp.sh b/i3-configuration/i3blocks/motherboard_temp.sh
index ca0531f..77e47c5 100755
--- a/i3-configuration/i3blocks/motherboard_temp.sh
+++ b/i3-configuration/i3blocks/motherboard_temp.sh
@@ -4,24 +4,23 @@
temp=$(sensors | awk '/^temp1:/ {print $2; exit}' | tr -d '+°C')
# Ensure the temperature is a valid number
-if [[ ! "$temp" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
- echo " MB: N/A"
- echo
- echo "#FF5555" # Red color for error
- exit 1
+if [[ ! $temp =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
+ echo " MB: N/A"
+ echo
+ echo "#FF5555" # Red color for error
+ exit 1
fi
# Define temperature thresholds
-if (( $(echo "$temp < 50.0" | bc -l) )); then
- color="#50FA7B" # Green for OK temperature
-elif (( $(echo "$temp < 70.0" | bc -l) )); then
- color="#F1FA8C" # Yellow for warning temperature
+if (($(echo "$temp < 50.0" | bc -l))); then
+ color="#50FA7B" # Green for OK temperature
+elif (($(echo "$temp < 70.0" | bc -l))); then
+ color="#F1FA8C" # Yellow for warning temperature
else
- color="#FF5555" # Red for high temperature
+ color="#FF5555" # Red for high temperature
fi
# Output the temperature with the color
-echo " ${temp}°C" # is a thermometer icon
+echo " ${temp}°C" # is a thermometer icon
echo
echo $color
-
diff --git a/i3-configuration/i3blocks/network_monitor.sh b/i3-configuration/i3blocks/network_monitor.sh
index 8987549..7c845c0 100755
--- a/i3-configuration/i3blocks/network_monitor.sh
+++ b/i3-configuration/i3blocks/network_monitor.sh
@@ -2,23 +2,28 @@
# Function to detect all active network interfaces
detect_interfaces() {
- interfaces=()
- for interface in /sys/class/net/*; do
- interface=$(basename "$interface")
- if [[ "$interface" != "lo" && -d "/sys/class/net/$interface" && "$(cat /sys/class/net/$interface/operstate)" == "up" ]]; then
- interfaces+=("$interface")
- fi
- done
- echo "${interfaces[@]}"
+ local iface_path iface state
+ for iface_path in /sys/class/net/*; do
+ iface=$(basename "$iface_path")
+ if [[ $iface == "lo" || ! -d "/sys/class/net/$iface" ]]; then
+ continue
+ fi
+ if [[ -r "/sys/class/net/$iface/operstate" ]]; then
+ state=$(< "/sys/class/net/$iface/operstate")
+ if [[ $state == "up" ]]; then
+ printf '%s\n' "$iface"
+ fi
+ fi
+ done
}
# Detect all active network interfaces
-interfaces=$(detect_interfaces)
+mapfile -t interfaces < <(detect_interfaces)
# If no active interfaces are found, exit
-if [ -z "$interfaces" ]; then
- echo "No active network interfaces found"
- exit 1
+if [ "${#interfaces[@]}" -eq 0 ]; then
+ echo "No active network interfaces found"
+ exit 1
fi
# Initialize total RX and TX bytes
@@ -34,46 +39,50 @@ current_time=$(date +%s)
last_time=$current_time
# Iterate over each interface and accumulate RX and TX bytes
-for interface in $interfaces; do
- rx_path="/sys/class/net/$interface/statistics/rx_bytes"
- tx_path="/sys/class/net/$interface/statistics/tx_bytes"
+for interface in "${interfaces[@]}"; do
+ rx_path="/sys/class/net/$interface/statistics/rx_bytes"
+ tx_path="/sys/class/net/$interface/statistics/tx_bytes"
- rx_now=$(cat $rx_path 2>/dev/null)
- tx_now=$(cat $tx_path 2>/dev/null)
+ if ! read -r rx_now < "$rx_path"; then
+ rx_now=0
+ fi
+ if ! read -r tx_now < "$tx_path"; then
+ tx_now=0
+ fi
- state_file="/tmp/network_monitor_$interface"
- if [ -f "$state_file" ]; then
- read last_rx last_tx last_time < "$state_file"
- else
- last_rx=$rx_now
- last_tx=$tx_now
- fi
+ state_file="/tmp/network_monitor_$interface"
+ if [ -f "$state_file" ]; then
+ read -r last_rx last_tx last_time < "$state_file"
+ else
+ last_rx=$rx_now
+ last_tx=$tx_now
+ fi
- total_rx_now=$((total_rx_now + rx_now))
- total_tx_now=$((total_tx_now + tx_now))
- total_last_rx=$((total_last_rx + last_rx))
- total_last_tx=$((total_last_tx + last_tx))
+ total_rx_now=$((total_rx_now + rx_now))
+ total_tx_now=$((total_tx_now + tx_now))
+ total_last_rx=$((total_last_rx + last_rx))
+ total_last_tx=$((total_last_tx + last_tx))
- # Save current RX and TX bytes for the next check
- echo "$rx_now $tx_now $current_time" > "$state_file"
+ # Save current RX and TX bytes for the next check
+ echo "$rx_now $tx_now $current_time" > "$state_file"
done
# Calculate time difference
time_diff=$((current_time - last_time))
# Calculate total download and upload speeds in bytes per second
-if (( time_diff > 0 )); then
- total_rx_rate=$(( (total_rx_now - total_last_rx) / time_diff ))
- total_tx_rate=$(( (total_tx_now - total_last_tx) / time_diff ))
+if ((time_diff > 0)); then
+ total_rx_rate=$(((total_rx_now - total_last_rx) / time_diff))
+ total_tx_rate=$(((total_tx_now - total_last_tx) / time_diff))
else
- total_rx_rate=0
- total_tx_rate=0
+ total_rx_rate=0
+ total_tx_rate=0
fi
# Convert speeds to human-readable format
-rx_rate_human=$(numfmt --to=iec --suffix=B/s --padding=8 $total_rx_rate)
-tx_rate_human=$(numfmt --to=iec --suffix=B/s --padding=8 $total_tx_rate)
+rx_rate_human=$(numfmt --to=iec --suffix=B/s --padding=8 "$total_rx_rate")
+tx_rate_human=$(numfmt --to=iec --suffix=B/s --padding=8 "$total_tx_rate")
# Store the result of printf into a string and echo it
printf " %s %s\n" "$rx_rate_human" "$tx_rate_human"
-echo "#50FA7B"
\ No newline at end of file
+echo "#50FA7B"
diff --git a/i3-configuration/i3blocks/pc_startup_status.sh b/i3-configuration/i3blocks/pc_startup_status.sh
index 3bbc5b5..058e683 100755
--- a/i3-configuration/i3blocks/pc_startup_status.sh
+++ b/i3-configuration/i3blocks/pc_startup_status.sh
@@ -4,65 +4,69 @@
# Function to check if today is a monitored day
is_monitored_day() {
- local day_of_week=$(date +%u)
- if [[ "$day_of_week" == "1" ]] || [[ "$day_of_week" == "5" ]] || [[ "$day_of_week" == "6" ]] || [[ "$day_of_week" == "7" ]]; then
- return 0
- else
- return 1
- fi
+ local day_of_week
+ day_of_week=$(date +%u)
+ if [[ $day_of_week == "1" ]] || [[ $day_of_week == "5" ]] || [[ $day_of_week == "6" ]] || [[ $day_of_week == "7" ]]; then
+ return 0
+ else
+ return 1
+ fi
}
# Function to check if current time is in window
is_current_time_in_window() {
- local current_hour=$(date +%H)
- local current_hour_num=$((10#$current_hour))
- if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
- return 0
- else
- return 1
- fi
+ local current_hour current_hour_num
+ current_hour=$(date +%H)
+ current_hour_num=$((10#$current_hour))
+ if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
+ return 0
+ else
+ return 1
+ fi
}
# Function to check if PC was booted in window today
was_booted_in_window_today() {
- local today=$(date +%Y-%m-%d)
- local uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
- local boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
- local boot_date=$(echo "$boot_time" | cut -d' ' -f1)
-
- if [[ "$boot_date" != "$today" ]]; then
- return 1
- fi
-
- local boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1)
- local boot_hour_num=$((10#$boot_hour))
-
- if [[ $boot_hour_num -ge 5 ]] && [[ $boot_hour_num -lt 8 ]]; then
- return 0
- else
- return 1
- fi
+ local today uptime_seconds boot_time boot_date
+ today=$(date +%Y-%m-%d)
+ uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0")
+ boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
+ boot_date=$(echo "$boot_time" | cut -d' ' -f1)
+
+ if [[ $boot_date != "$today" ]]; then
+ return 1
+ fi
+
+ local boot_hour boot_hour_num
+ boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1)
+ boot_hour_num=$((10#$boot_hour))
+
+ if [[ $boot_hour_num -ge 5 ]] && [[ $boot_hour_num -lt 8 ]]; then
+ return 0
+ else
+ return 1
+ fi
}
# Main logic
if ! is_monitored_day; then
- # Not a monitored day
- echo "PC:skip"
- echo
- echo "#888888" # Gray
+ # Not a monitored day
+ echo "PC:skip"
+ echo
+ echo "#888888" # Gray
elif is_current_time_in_window; then
- # Currently in the window - all good
- echo "PC:live"
- echo
- echo "#00FF00" # Green
+ # Currently in the window - all good
+ echo "PC:live"
+ echo
+ echo "#00FF00" # Green
elif was_booted_in_window_today; then
- # Was booted in window today - compliant
- echo "PC:ok"
- echo
- echo "#00FF00" # Green
+ # Was booted in window today - compliant
+ echo "PC:ok"
+ echo
+ echo "#00FF00" # Green
else
- # Was NOT booted in window today - non-compliant
- echo "PC:warn"
- echo
- echo "#FF0000" # Red
+ # Was NOT booted in window today - non-compliant
+ echo "PC:warn"
+ echo
+ echo "#FF0000" # Red
fi
diff --git a/i3-configuration/i3blocks/volume.sh b/i3-configuration/i3blocks/volume.sh
index eae11bb..c6e1112 100755
--- a/i3-configuration/i3blocks/volume.sh
+++ b/i3-configuration/i3blocks/volume.sh
@@ -1,20 +1,19 @@
-
#!/bin/bash
# Get the current volume level and mute status
volume=$(pactl get-sink-volume @DEFAULT_SINK@ | awk '{print $5}' | tr -d '%')
mute=$(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}')
+color="#50FA7B"
# Determine icon and color based on mute status
if [ "$mute" = "yes" ]; then
- icon="🔇" # Muted
+ icon="🔇" # Muted
+ color="#FF5555"
else
- icon="🔊" # Volume icon
- color="#50FA7B" # Green
+ icon="🔊" # Volume icon
fi
# Output the volume with icon and color
echo "$icon $volume%"
echo
-echo $color
-
+echo "$color"
diff --git a/i3-configuration/i3blocks/warp_status.sh b/i3-configuration/i3blocks/warp_status.sh
index fba0fb8..a4ffa9d 100755
--- a/i3-configuration/i3blocks/warp_status.sh
+++ b/i3-configuration/i3blocks/warp_status.sh
@@ -2,25 +2,25 @@
# Check if warp-cli is installed
if ! command -v warp-cli &> /dev/null; then
- echo " N/A"
- exit 0
+ echo " N/A"
+ exit 0
fi
# Get the status from warp-cli
-status=$(warp-cli status 2>/dev/null | grep "Status update:" | awk '{print $3}')
+status=$(warp-cli status 2> /dev/null | grep "Status update:" | awk '{print $3}')
# Display the status with an icon
if [ "$status" = "Connected" ]; then
- echo "🔒 !!! WARP CONNECTED !!!"
- echo
- echo "#FFFF00" # Yellow
+ echo "🔒 !!! WARP CONNECTED !!!"
+ echo
+ echo "#FFFF00" # Yellow
elif [ "$status" = "Disconnected" ]; then
- echo "WARP disconnected"
- echo
- echo "#00FF00" # Green
+ echo "WARP disconnected"
+ echo
+ echo "#00FF00" # Green
else
- echo "⚠️ ! WARP unknown !"
- echo
- echo "#FF0000" # Red
- exit 0
+ echo "⚠️ ! WARP unknown !"
+ echo
+ echo "#FF0000" # Red
+ exit 0
fi
diff --git a/i3-configuration/i3blocks/wifi_monitor.sh b/i3-configuration/i3blocks/wifi_monitor.sh
index a85bb88..e818c62 100755
--- a/i3-configuration/i3blocks/wifi_monitor.sh
+++ b/i3-configuration/i3blocks/wifi_monitor.sh
@@ -5,23 +5,23 @@ wifi_interface=$(iw dev | awk '$1=="Interface"{print $2}')
# If no WiFi interface is found, exit
if [ -z "$wifi_interface" ]; then
- echo " down"
- exit 1
+ echo " down"
+ exit 1
fi
# Get the WiFi details
-wifi_info=$(iwconfig $wifi_interface 2>/dev/null)
+wifi_info=$(iwconfig "$wifi_interface" 2> /dev/null)
# Extract the SSID and signal strength
ssid=$(echo "$wifi_info" | awk -F '"' '/ESSID/ {print $2}')
-signal=$(echo "$wifi_info" | awk '/Signal level/ {print $4}' | tr -d 'level=')
+signal=$(echo "$wifi_info" | awk '/Signal level/ {print $4}' | sed 's/level=//')
# Get the IP address
-ip_address=$(ip addr show $wifi_interface | awk '/inet / {print $2}' | cut -d/ -f1)
+ip_address=$(ip addr show "$wifi_interface" | awk '/inet / {print $2}' | cut -d/ -f1)
# Output the result
if [ -z "$ssid" ]; then
- echo " down"
+ echo " down"
else
- echo " $ssid ($signal dBm) $ip_address"
-fi
\ No newline at end of file
+ echo " $ssid ($signal dBm) $ip_address"
+fi
diff --git a/i3-configuration/install.sh b/i3-configuration/install.sh
index f56538c..939175b 100755
--- a/i3-configuration/install.sh
+++ b/i3-configuration/install.sh
@@ -2,40 +2,40 @@
# Function to detect if the system is Ubuntu
is_ubuntu() {
- [ -f /etc/os-release ] && grep -qi 'ubuntu' /etc/os-release
+ [ -f /etc/os-release ] && grep -qi 'ubuntu' /etc/os-release
}
# Function to detect screen resolution and set font size
set_font_size() {
- resolution=$(xdpyinfo | grep dimensions | awk '{print $2}')
- width=$(echo $resolution | cut -d 'x' -f 1)
- # Do not change this font size, it actually makes i3blocks unbearable to look at:
- # Icons (like for slack) are too small and i3blocks are too big
- # Network monitor jumping becomes annoying
- if [ "$width" -gt 1920 ]; then
- echo "8"
- else
- echo "8"
- fi
+ resolution=$(xdpyinfo | grep dimensions | awk '{print $2}')
+ width=$(echo "$resolution" | cut -d 'x' -f 1)
+ # Do not change this font size, it actually makes i3blocks unbearable to look at:
+ # Icons (like for slack) are too small and i3blocks are too big
+ # Network monitor jumping becomes annoying
+ if [ "$width" -gt 1920 ]; then
+ echo "8"
+ else
+ echo "8"
+ fi
}
# Check if Intel GPU is detected
if lspci | grep -i 'vga' | grep -i 'intel'; then
- if is_ubuntu; then
- sudo apt-get update
- sudo apt-get install -y intel-gpu-tools
- sudo setcap cap_perfmon+ep /usr/bin/intel_gpu_top
- else
- yes | sudo pacman -S --needed intel-gpu-tools
- sudo setcap cap_perfmon+ep /usr/bin/intel_gpu_top
- fi
+ if is_ubuntu; then
+ sudo apt-get update
+ sudo apt-get install -y intel-gpu-tools
+ sudo setcap cap_perfmon+ep /usr/bin/intel_gpu_top
+ else
+ yes | sudo pacman -S --needed intel-gpu-tools
+ sudo setcap cap_perfmon+ep /usr/bin/intel_gpu_top
+ fi
fi
if is_ubuntu; then
- sudo apt-get update
- sudo apt-get install -y fonts-dejavu-core fonts-noto fonts-font-awesome bc jq iw pulseaudio-utils
+ sudo apt-get update
+ sudo apt-get install -y fonts-dejavu-core fonts-noto fonts-font-awesome bc jq iw pulseaudio-utils
else
- yes | sudo pacman -S --needed ttf-dejavu noto-fonts ttf-font-awesome bc jq iw acpi
+ yes | sudo pacman -S --needed ttf-dejavu noto-fonts ttf-font-awesome bc jq iw acpi
fi
# Set font size based on screen resolution
diff --git a/scripts/digital_wellbeing/install_leechblock.sh b/scripts/digital_wellbeing/install_leechblock.sh
index d505697..554cad9 100755
--- a/scripts/digital_wellbeing/install_leechblock.sh
+++ b/scripts/digital_wellbeing/install_leechblock.sh
@@ -12,17 +12,17 @@ SCRIPT_NAME=${0##*/}
info() { printf "\033[1;34m[INFO]\033[0m %s\n" "$*"; }
warn() { printf "\033[1;33m[WARN]\033[0m %s\n" "$*"; }
-err() { printf "\033[1;31m[ERR ]\033[0m %s\n" "$*"; }
+err() { printf "\033[1;31m[ERR ]\033[0m %s\n" "$*"; }
require_cmd() {
- if ! command -v "$1" >/dev/null 2>&1; then
+ if ! command -v "$1" > /dev/null 2>&1; then
err "Missing dependency: $1"
MISSING=1
fi
}
usage() {
- cat </dev/null 2>&1; then
+if ! command -v jq > /dev/null 2>&1; then
warn "jq not found — will fall back to a simpler tag detection method."
fi
-[[ $MISSING -eq 1 ]] && { err "Please install missing tools and re-run."; exit 1; }
+[[ $MISSING -eq 1 ]] && {
+ err "Please install missing tools and re-run."
+ exit 1
+}
REPO_OWNER="proginosko"
REPO_NAME="LeechBlockNG"
get_latest_tag() {
local tag
- if command -v jq >/dev/null 2>&1; then
+ if command -v jq > /dev/null 2>&1; then
tag=$(curl -fsSL "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" | jq -r '.tag_name // empty' || true)
- if [[ -n "$tag" && "$tag" != "null" ]]; then
- echo "$tag"; return 0
+ if [[ -n $tag && $tag != "null" ]]; then
+ echo "$tag"
+ return 0
fi
fi
# Fallback: follow redirect for /releases/latest to extract tag
tag=$(curl -fsSLI "https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/latest" | awk -F'/tag/' '/^location:/I {print $2}' | tr -d '\r\n' || true)
- if [[ -n "$tag" ]]; then
- echo "$tag"; return 0
+ if [[ -n $tag ]]; then
+ echo "$tag"
+ return 0
fi
return 1
}
-if [[ -z "$VERSION" ]]; then
+if [[ -z $VERSION ]]; then
info "Resolving latest release tag from GitHub…"
if ! VERSION=$(get_latest_tag); then
- err "Failed to determine latest version tag"; exit 1
+ err "Failed to determine latest version tag"
+ exit 1
fi
fi
-if [[ ! "$VERSION" =~ ^v?[0-9]+(\.[0-9]+)*$ ]]; then
+if [[ ! $VERSION =~ ^v?[0-9]+(\.[0-9]+)*$ ]]; then
warn "Version tag '$VERSION' doesn't look like vX[.Y[.Z]] — continuing anyway."
fi
-VERSION=${VERSION#v} # strip leading v for folder names
+VERSION=${VERSION#v} # strip leading v for folder names
TAG="v${VERSION}"
XDG_DATA_HOME=${XDG_DATA_HOME:-"$HOME/.local/share"}
@@ -108,7 +125,7 @@ INSTALL_ROOT="$XDG_DATA_HOME/leechblockng"
VERSION_DIR="$INSTALL_ROOT/$VERSION"
CURRENT_LINK="$INSTALL_ROOT/current"
-if [[ -d "$VERSION_DIR" && $FORCE -ne 1 ]]; then
+if [[ -d $VERSION_DIR && $FORCE -ne 1 ]]; then
info "LeechBlockNG $VERSION already present at $VERSION_DIR (use --force to reinstall)."
else
info "Downloading LeechBlockNG $TAG source from GitHub…"
@@ -122,11 +139,14 @@ else
tar -xzf "$ARCHIVE_FILE" -C "$tmpdir/extract"
# The archive usually extracts to REPO_NAME-TAG/ …
src_root=$(find "$tmpdir/extract" -maxdepth 1 -type d -name "${REPO_NAME}-*" | head -n1 || true)
- [[ -z "$src_root" ]] && { err "Could not locate extracted source root"; exit 1; }
+ [[ -z $src_root ]] && {
+ err "Could not locate extracted source root"
+ exit 1
+ }
# Find the extension manifest (support a couple of common layouts)
manifest_path=$(find "$src_root" -maxdepth 5 -type f -name manifest.json | head -n1 || true)
- if [[ -z "$manifest_path" ]]; then
+ if [[ -z $manifest_path ]]; then
err "manifest.json not found in the extracted archive. The project layout may have changed."
exit 1
fi
@@ -137,30 +157,30 @@ else
info "Installing to $VERSION_DIR…"
mkdir -p "$VERSION_DIR"
# Copy the extension directory as-is (avoid bringing tests or build scripts)
- rsync -a --delete "$ext_dir/" "$VERSION_DIR/" 2>/dev/null || cp -a "$ext_dir/." "$VERSION_DIR/"
+ rsync -a --delete "$ext_dir/" "$VERSION_DIR/" 2> /dev/null || cp -a "$ext_dir/." "$VERSION_DIR/"
ln -sfn "$VERSION_DIR" "$CURRENT_LINK"
fi
-EXT_PATH="$CURRENT_LINK" # stable path used by wrappers
+EXT_PATH="$CURRENT_LINK" # stable path used by wrappers
# Detect browsers
declare -A BROWSERS
BROWSERS=(
[chromium]="Chromium"
- [google-chrome-stable]="Google Chrome"
- [google-chrome]="Google Chrome"
- [brave-browser]="Brave"
- [vivaldi-stable]="Vivaldi"
+ [google - chrome - stable]="Google Chrome"
+ [google - chrome]="Google Chrome"
+ [brave - browser]="Brave"
+ [vivaldi - stable]="Vivaldi"
[vivaldi]="Vivaldi"
[opera]="Opera"
- [thorium-browser]="Thorium"
+ [thorium - browser]="Thorium"
)
declare -A FIREFOXES
FIREFOXES=(
[firefox]="Firefox"
- [firefox-developer-edition]="Firefox Developer Edition"
+ [firefox - developer - edition]="Firefox Developer Edition"
[librewolf]="LibreWolf"
)
@@ -173,15 +193,17 @@ user_apps_dir="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
mkdir -p "$user_apps_dir"
create_wrapper_and_desktop() {
- local bin="$1"; shift
- local pretty="$1"; shift
+ local bin="$1"
+ shift
+ local pretty="$1"
+ shift
local wrapper="$wrap_bin_dir/${bin}-with-leechblock"
local real_bin
real_bin=$(command -v "$bin" || true)
- [[ -z "$real_bin" ]] && return
+ [[ -z $real_bin ]] && return
- cat >"$wrapper" < "$wrapper" << WRAP
#!/usr/bin/env bash
exec "$real_bin" --load-extension="$EXT_PATH" "$@"
WRAP
@@ -189,18 +211,18 @@ WRAP
# Try to reuse icon from an existing desktop file if available
local sys_desktop existing_icon existing_name categories
- sys_desktop=$(grep -RIl "^Exec=.*${bin}" /usr/share/applications 2>/dev/null | head -n1 || true)
- if [[ -n "$sys_desktop" ]]; then
+ sys_desktop=$(grep -RIl "^Exec=.*${bin}" /usr/share/applications 2> /dev/null | head -n1 || true)
+ if [[ -n $sys_desktop ]]; then
existing_icon=$(awk -F= '/^Icon=/{print $2; exit}' "$sys_desktop" || true)
existing_name=$(awk -F= '/^Name=/{print $2; exit}' "$sys_desktop" || true)
categories=$(awk -F= '/^Categories=/{print $2; exit}' "$sys_desktop" || true)
fi
- [[ -z "$existing_icon" ]] && existing_icon="$bin"
- [[ -z "$existing_name" ]] && existing_name="$pretty"
- [[ -z "$categories" ]] && categories="Network;WebBrowser;"
+ [[ -z $existing_icon ]] && existing_icon="$bin"
+ [[ -z $existing_name ]] && existing_name="$pretty"
+ [[ -z $categories ]] && categories="Network;WebBrowser;"
local desktop_file="$user_apps_dir/${bin}-with-leechblock.desktop"
- cat >"$desktop_file" < "$desktop_file" << DESK
[Desktop Entry]
Name=${existing_name} (LeechBlock)
Exec=${wrapper} %U
@@ -218,14 +240,14 @@ DESK
info "Detecting installed browsers…"
for bin in "${!BROWSERS[@]}"; do
- if command -v "$bin" >/dev/null 2>&1; then
+ if command -v "$bin" > /dev/null 2>&1; then
create_wrapper_and_desktop "$bin" "${BROWSERS[$bin]}"
fi
done
ff_found=0
for bin in "${!FIREFOXES[@]}"; do
- if command -v "$bin" >/dev/null 2>&1; then
+ if command -v "$bin" > /dev/null 2>&1; then
ff_found=1
fi
done
@@ -239,7 +261,7 @@ fi
if [[ $ff_found -eq 1 ]]; then
echo
warn "Detected Firefox-based browser(s). Permanent install from GitHub source isn't possible on stable builds due to required signing."
- cat </dev/null 2>&1; then
+ if command -v firefox > /dev/null 2>&1; then
POLICY_DIRS+=("/etc/firefox/policies" "/usr/lib/firefox/distribution")
fi
- if command -v firefox-developer-edition >/dev/null 2>&1; then
+ if command -v firefox-developer-edition > /dev/null 2>&1; then
POLICY_DIRS+=("/etc/firefox-developer-edition/policies" "/usr/lib/firefox-developer-edition/distribution")
fi
- if command -v librewolf >/dev/null 2>&1; then
+ if command -v librewolf > /dev/null 2>&1; then
POLICY_DIRS+=("/etc/librewolf/policies" "/usr/lib/librewolf/distribution")
fi
# Generic mozilla path as fallback
@@ -291,7 +313,7 @@ if [[ $AUTO_FIREFOX -eq 1 && $ff_found -eq 1 ]]; then
if sudo test -f "$existing"; then
info "Merging into existing policies.json at $existing"
sudo cp "$existing" "$tmp_pol"
- if command -v jq >/dev/null 2>&1; then
+ if command -v jq > /dev/null 2>&1; then
merged=$(jq --arg id "$ADDON_ID" --arg url "$ADDON_AMO_URL" '
.policies |= (. // {}) |
.policies.ExtensionSettings |= (. // {}) |
@@ -300,16 +322,17 @@ if [[ $AUTO_FIREFOX -eq 1 && $ff_found -eq 1 ]]; then
.policies.ExtensionSettings[$id].installation_mode = "force_installed" |
.policies.ExtensionSettings[$id].install_url = $url
' "$tmp_pol") || merged=""
- if [[ -n "$merged" ]]; then
+ if [[ -n $merged ]]; then
printf '%s\n' "$merged" > "$tmp_pol"
else
warn "jq merge failed; skipping $pol_target"
- rm -f "$tmp_pol"; continue
+ rm -f "$tmp_pol"
+ continue
fi
else
warn "jq not available; creating minimal policies.json (existing file will be backed up)."
sudo cp "$existing" "${existing}.bak.$(date +%s)"
- cat > "$tmp_pol" < "$tmp_pol" << JSON
{
"policies": {
"ExtensionSettings": {
@@ -325,7 +348,7 @@ JSON
fi
else
info "Creating new policies.json at $pol_target"
- cat > "$tmp_pol" < "$tmp_pol" << JSON
{
"policies": {
"ExtensionSettings": {
diff --git a/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh b/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh
index 1e07980..3eefad5 100755
--- a/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh
+++ b/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh
@@ -14,7 +14,6 @@ GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
-BOLD='\033[1m'
NC='\033[0m' # No Color
# Script locations
@@ -57,7 +56,7 @@ else
echo -e "${YELLOW}Warning:${NC} Missing whitelist source at ${WHITELIST_SOURCE}${NC}"
fi
chmod +x "$WRAPPER_DEST"
-chmod 644 "$WORDS_DEST" "$BLOCKED_DEST" "$WHITELIST_DEST" 2>/dev/null || true
+chmod 644 "$WORDS_DEST" "$BLOCKED_DEST" "$WHITELIST_DEST" 2> /dev/null || true
# Automatically use symbolic link installation method
echo -e "${YELLOW}Installing using symbolic link method...${NC}"
@@ -75,4 +74,4 @@ sed -i 's|PACMAN_BIN="\/usr\/bin\/pacman"|PACMAN_BIN="\/usr\/bin\/pacman.orig"|g
echo -e "${BLUE}Creating symbolic link...${NC}"
ln -sf "$WRAPPER_DEST" /usr/bin/pacman
echo -e "${GREEN}Installation complete!${NC}"
-echo -e "Pacman is now wrapped. The original pacman is available at ${CYAN}/usr/bin/pacman.orig${NC}"
\ No newline at end of file
+echo -e "Pacman is now wrapped. The original pacman is available at ${CYAN}/usr/bin/pacman.orig${NC}"
diff --git a/scripts/digital_wellbeing/pacman/pacman_wrapper.sh b/scripts/digital_wellbeing/pacman/pacman_wrapper.sh
index 70c4046..a1f339b 100755
--- a/scripts/digital_wellbeing/pacman/pacman_wrapper.sh
+++ b/scripts/digital_wellbeing/pacman/pacman_wrapper.sh
@@ -18,647 +18,648 @@ declare -a WHITELISTED_NAMES_LIST=()
POLICY_LISTS_LOADED=0
load_policy_lists() {
- if [[ $POLICY_LISTS_LOADED -eq 1 ]]; then
- return
- fi
+ if [[ $POLICY_LISTS_LOADED -eq 1 ]]; then
+ return
+ fi
- local script_dir
- script_dir="$(dirname "$(readlink -f "$0")")"
- local blocked_file="$script_dir/pacman_blocked_keywords.txt"
- local whitelist_file="$script_dir/pacman_whitelist.txt"
+ local script_dir
+ script_dir="$(dirname "$(readlink -f "$0")")"
+ local blocked_file="$script_dir/pacman_blocked_keywords.txt"
+ local whitelist_file="$script_dir/pacman_whitelist.txt"
- if [[ -f "$blocked_file" ]]; then
- mapfile -t BLOCKED_KEYWORDS_LIST < <(sed 's/\r$//' "$blocked_file" | grep -Ev '^[[:space:]]*(#|$)' || true)
- else
- BLOCKED_KEYWORDS_LIST=()
- echo -e "${YELLOW}Warning:${NC} Missing blocked keywords file at $blocked_file" >&2
- fi
+ if [[ -f $blocked_file ]]; then
+ mapfile -t BLOCKED_KEYWORDS_LIST < <(sed 's/\r$//' "$blocked_file" | grep -Ev '^[[:space:]]*(#|$)' || true)
+ else
+ BLOCKED_KEYWORDS_LIST=()
+ echo -e "${YELLOW}Warning:${NC} Missing blocked keywords file at $blocked_file" >&2
+ fi
- if [[ -f "$whitelist_file" ]]; then
- mapfile -t WHITELISTED_NAMES_LIST < <(sed 's/\r$//' "$whitelist_file" | grep -Ev '^[[:space:]]*(#|$)' || true)
- else
- WHITELISTED_NAMES_LIST=()
- fi
+ if [[ -f $whitelist_file ]]; then
+ mapfile -t WHITELISTED_NAMES_LIST < <(sed 's/\r$//' "$whitelist_file" | grep -Ev '^[[:space:]]*(#|$)' || true)
+ else
+ WHITELISTED_NAMES_LIST=()
+ fi
- for i in "${!BLOCKED_KEYWORDS_LIST[@]}"; do
- BLOCKED_KEYWORDS_LIST[$i]="${BLOCKED_KEYWORDS_LIST[$i],,}"
- done
+ for i in "${!BLOCKED_KEYWORDS_LIST[@]}"; do
+ BLOCKED_KEYWORDS_LIST[i]="${BLOCKED_KEYWORDS_LIST[i],,}"
+ done
- for i in "${!WHITELISTED_NAMES_LIST[@]}"; do
- WHITELISTED_NAMES_LIST[$i]="${WHITELISTED_NAMES_LIST[$i],,}"
- done
+ for i in "${!WHITELISTED_NAMES_LIST[@]}"; do
+ WHITELISTED_NAMES_LIST[i]="${WHITELISTED_NAMES_LIST[i],,}"
+ done
- POLICY_LISTS_LOADED=1
+ POLICY_LISTS_LOADED=1
}
# Determine if this invocation may perform a transaction (upgrade/install/remove)
needs_unlock() {
- # If args include -S (install/upgrade), -U (local install), or -R (remove), we unlock
- # Also include -Su/-Syu/-Syuu when -S is part of the combined flag
- for arg in "$@"; do
- case "$arg" in
- -S*|-U|-R|--sync|--upgrade|--remove)
- return 0 ;;
- esac
- done
- return 1
+ # If args include -S (install/upgrade), -U (local install), or -R (remove), we unlock
+ # Also include -Su/-Syu/-Syuu when -S is part of the combined flag
+ for arg in "$@"; do
+ case "$arg" in
+ -S* | -U | -R | --sync | --upgrade | --remove)
+ return 0
+ ;;
+ esac
+ done
+ return 1
}
# Run pre/post hooks for /etc/hosts guard if present
pre_unlock_hosts() {
- local pre="/usr/local/share/hosts-guard/pacman-pre-unlock-hosts.sh"
- if [[ -x "$pre" ]]; then
- echo -e "${CYAN}[hosts-guard] Preparing /etc/hosts for transaction...${NC}" >&2
- /bin/bash "$pre" || true
- fi
+ local pre="/usr/local/share/hosts-guard/pacman-pre-unlock-hosts.sh"
+ if [[ -x $pre ]]; then
+ echo -e "${CYAN}[hosts-guard] Preparing /etc/hosts for transaction...${NC}" >&2
+ /bin/bash "$pre" || true
+ fi
}
post_relock_hosts() {
- local post="/usr/local/share/hosts-guard/pacman-post-relock-hosts.sh"
- if [[ -x "$post" ]]; then
- /bin/bash "$post" || true
- echo -e "${CYAN}[hosts-guard] Protections re-applied to /etc/hosts.${NC}" >&2
- fi
+ local post="/usr/local/share/hosts-guard/pacman-post-relock-hosts.sh"
+ if [[ -x $post ]]; then
+ /bin/bash "$post" || true
+ echo -e "${CYAN}[hosts-guard] Protections re-applied to /etc/hosts.${NC}" >&2
+ fi
}
-
# Ensure periodic system services (timer/monitor) are set up; if not, trigger setup
ensure_periodic_maintenance() {
- # Only proceed if systemd/systemctl is available
- if ! command -v systemctl >/dev/null 2>&1; then
- return 0
- fi
+ # Only proceed if systemd/systemctl is available
+ if ! command -v systemctl > /dev/null 2>&1; then
+ return 0
+ fi
- local timer_unit="periodic-system-maintenance.timer"
- local startup_unit="periodic-system-startup.service"
- local monitor_unit="hosts-file-monitor.service"
- local needs_setup=0
+ local timer_unit="periodic-system-maintenance.timer"
+ local startup_unit="periodic-system-startup.service"
+ local monitor_unit="hosts-file-monitor.service"
+ local needs_setup=0
- # Timer should be enabled and active
- systemctl --quiet is-enabled "$timer_unit" || needs_setup=1
- systemctl --quiet is-active "$timer_unit" || needs_setup=1
+ # Timer should be enabled and active
+ systemctl --quiet is-enabled "$timer_unit" || needs_setup=1
+ systemctl --quiet is-active "$timer_unit" || needs_setup=1
- # Monitor should be enabled and active
- systemctl --quiet is-enabled "$monitor_unit" || needs_setup=1
- systemctl --quiet is-active "$monitor_unit" || needs_setup=1
+ # Monitor should be enabled and active
+ systemctl --quiet is-enabled "$monitor_unit" || needs_setup=1
+ systemctl --quiet is-active "$monitor_unit" || needs_setup=1
- # Startup service should be enabled (it’s oneshot and may not be active except at boot)
- systemctl --quiet is-enabled "$startup_unit" || needs_setup=1
+ # Startup service should be enabled (it’s oneshot and may not be active except at boot)
+ systemctl --quiet is-enabled "$startup_unit" || needs_setup=1
- if [[ $needs_setup -eq 0 ]]; then
- return 0
- fi
+ if [[ $needs_setup -eq 0 ]]; then
+ return 0
+ fi
- echo -e "${YELLOW}Periodic maintenance services missing or inactive. Running setup...${NC}" >&2
+ echo -e "${YELLOW}Periodic maintenance services missing or inactive. Running setup...${NC}" >&2
- # Try to locate setup_periodic_system.sh
- local setup_script=""
- local self_dir
- self_dir="$(dirname "$(readlink -f "$0")")"
- if [[ -f "$self_dir/setup_periodic_system.sh" ]]; then
- setup_script="$self_dir/setup_periodic_system.sh"
- elif [[ -f "$HOME/linux-configuration/scripts/setup_periodic_system.sh" ]]; then
- setup_script="$HOME/linux-configuration/scripts/setup_periodic_system.sh"
- fi
+ # Try to locate setup_periodic_system.sh
+ local setup_script=""
+ local self_dir
+ self_dir="$(dirname "$(readlink -f "$0")")"
+ if [[ -f "$self_dir/setup_periodic_system.sh" ]]; then
+ setup_script="$self_dir/setup_periodic_system.sh"
+ elif [[ -f "$HOME/linux-configuration/scripts/setup_periodic_system.sh" ]]; then
+ setup_script="$HOME/linux-configuration/scripts/setup_periodic_system.sh"
+ fi
- if [[ -n "$setup_script" ]]; then
- if [[ $EUID -ne 0 ]]; then
- sudo bash "$setup_script"
- else
- bash "$setup_script"
- fi
- echo -e "${CYAN}Tip:${NC} To disable these later:" >&2
- echo " sudo systemctl disable periodic-system-maintenance.timer" >&2
- echo " sudo systemctl disable periodic-system-startup.service" >&2
- echo " sudo systemctl disable hosts-file-monitor.service" >&2
+ if [[ -n $setup_script ]]; then
+ if [[ $EUID -ne 0 ]]; then
+ sudo bash "$setup_script"
else
- echo -e "${RED}Could not locate setup_periodic_system.sh to configure services automatically.${NC}" >&2
+ bash "$setup_script"
fi
+ echo -e "${CYAN}Tip:${NC} To disable these later:" >&2
+ echo " sudo systemctl disable periodic-system-maintenance.timer" >&2
+ echo " sudo systemctl disable periodic-system-startup.service" >&2
+ echo " sudo systemctl disable hosts-file-monitor.service" >&2
+ else
+ echo -e "${RED}Could not locate setup_periodic_system.sh to configure services automatically.${NC}" >&2
+ fi
}
# Function to display help
function show_help() {
- echo -e "${BOLD}Pacman Wrapper Help${NC}"
- echo "This wrapper adds helpful features while preserving all pacman functionality."
- echo ""
- echo "Additional commands:"
- echo " --help-wrapper Show this help message"
+ echo -e "${BOLD}Pacman Wrapper Help${NC}"
+ echo "This wrapper adds helpful features while preserving all pacman functionality."
+ echo ""
+ echo "Additional commands:"
+ echo " --help-wrapper Show this help message"
}
# Function to display a message before executing
function display_operation() {
- case "$1" in
- -S|-Sy|-S\ *)
- echo -e "${BLUE}Installing packages...${NC}" >&2
- ;;
- -Syu|-Syyu)
- echo -e "${BLUE}Updating system...${NC}" >&2
- ;;
- -R|-Rs|-Rns|-R\ *)
- echo -e "${YELLOW}Removing packages...${NC}" >&2
- ;;
- -Ss|-Ss\ *)
- echo -e "${CYAN}Searching for packages...${NC}" >&2
- ;;
- -Q|-Qs|-Qi|-Ql|-Q\ *)
- echo -e "${CYAN}Querying package database...${NC}" >&2
- ;;
- -U|-U\ *)
- echo -e "${BLUE}Installing local packages...${NC}" >&2
- ;;
- -Scc)
- echo -e "${YELLOW}Cleaning package cache...${NC}" >&2
- ;;
- *)
- echo -e "${CYAN}Executing pacman command...${NC}" >&2
- ;;
- esac
+ case "$1" in
+ -S | -Sy | -S\ *)
+ echo -e "${BLUE}Installing packages...${NC}" >&2
+ ;;
+ -Syu | -Syyu)
+ echo -e "${BLUE}Updating system...${NC}" >&2
+ ;;
+ -R | -Rs | -Rns | -R\ *)
+ echo -e "${YELLOW}Removing packages...${NC}" >&2
+ ;;
+ -Ss | -Ss\ *)
+ echo -e "${CYAN}Searching for packages...${NC}" >&2
+ ;;
+ -Q | -Qs | -Qi | -Ql | -Q\ *)
+ echo -e "${CYAN}Querying package database...${NC}" >&2
+ ;;
+ -U | -U\ *)
+ echo -e "${BLUE}Installing local packages...${NC}" >&2
+ ;;
+ -Scc)
+ echo -e "${YELLOW}Cleaning package cache...${NC}" >&2
+ ;;
+ *)
+ echo -e "${CYAN}Executing pacman command...${NC}" >&2
+ ;;
+ esac
}
# Helper: return 0 if the given package name is blocked by policy
function is_blocked_package_name() {
- load_policy_lists
- local normalized="${1,,}"
+ load_policy_lists
+ local normalized="${1,,}"
- for allowed in "${WHITELISTED_NAMES_LIST[@]}"; do
- if [[ "$normalized" == "$allowed" ]]; then
- return 1
- fi
- done
+ for allowed in "${WHITELISTED_NAMES_LIST[@]}"; do
+ if [[ $normalized == "$allowed" ]]; then
+ return 1
+ fi
+ done
- for keyword in "${BLOCKED_KEYWORDS_LIST[@]}"; do
- if [[ -n "$keyword" && "$normalized" == *"$keyword"* ]]; then
- return 0
- fi
- done
+ for keyword in "${BLOCKED_KEYWORDS_LIST[@]}"; do
+ if [[ -n $keyword && $normalized == *"$keyword"* ]]; then
+ return 0
+ fi
+ done
- return 1
+ return 1
}
# Helper: detect if current invocation includes --noconfirm
function has_noconfirm_flag() {
- for arg in "$@"; do
- if [[ "$arg" == "--noconfirm" ]]; then
- return 0
- fi
- done
- return 1
+ for arg in "$@"; do
+ if [[ $arg == "--noconfirm" ]]; then
+ return 0
+ fi
+ done
+ return 1
}
# Handle stale pacman database lock if present and no package managers are running
check_and_handle_db_lock() {
- local lock_file="/var/lib/pacman/db.lck"
- # Quick exit if no lock
- if [[ ! -e "$lock_file" ]]; then
- return 0
+ local lock_file="/var/lib/pacman/db.lck"
+ # Quick exit if no lock
+ if [[ ! -e $lock_file ]]; then
+ return 0
+ fi
+
+ # Determine which processes actually have the lock open
+ local -a holders=()
+ if command -v fuser > /dev/null 2>&1; then
+ mapfile -t holders < <(fuser "$lock_file" 2> /dev/null | tr ' ' '\n' | grep -E '^[0-9]+$' || true)
+ elif command -v lsof > /dev/null 2>&1; then
+ mapfile -t holders < <(lsof -t "$lock_file" 2> /dev/null | grep -E '^[0-9]+$' || true)
+ else
+ holders=()
+ fi
+
+ # Filter out our own PID if it somehow appears
+ if [[ ${#holders[@]} -gt 0 ]]; then
+ local -a filtered=()
+ for pid in "${holders[@]}"; do
+ [[ $pid -eq $$ ]] && continue
+ filtered+=("$pid")
+ done
+ holders=("${filtered[@]}")
+ fi
+
+ if [[ ${#holders[@]} -gt 0 ]]; then
+ local pac_holder=0
+ local gui_holder=0
+ for pid in "${holders[@]}"; do
+ local comm args lower
+ comm=$(ps -p "$pid" -o comm= 2> /dev/null || true)
+ args=$(ps -p "$pid" -o args= 2> /dev/null || true)
+ lower="${comm,,} ${args,,}"
+ if [[ $lower == *" pacman"* || $lower == pacman* || $lower == *"/pacman "* || $lower == *" pamac"* ]]; then
+ pac_holder=1
+ elif [[ $lower == *packagekit* || $lower == *gnome-software* || $lower == *discover* ]]; then
+ gui_holder=1
+ fi
+ done
+
+ if [[ $pac_holder -eq 1 ]]; then
+ echo -e "${RED}Another pacman/pamac transaction is holding the database lock. Try again later.${NC}" >&2
+ return 1
fi
- # Determine which processes actually have the lock open
- local -a holders=()
- if command -v fuser >/dev/null 2>&1; then
- mapfile -t holders < <(fuser "$lock_file" 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+$' || true)
- elif command -v lsof >/dev/null 2>&1; then
- mapfile -t holders < <(lsof -t "$lock_file" 2>/dev/null | grep -E '^[0-9]+$' || true)
+ if [[ $gui_holder -eq 1 ]]; then
+ echo -e "${YELLOW}A background software updater is holding the pacman lock. Attempting to stop it...${NC}" >&2
+ if command -v systemctl > /dev/null 2>&1; then
+ systemctl --quiet stop packagekit.service 2> /dev/null || true
+ systemctl --quiet stop packagekit 2> /dev/null || true
+ fi
+ pkill -x packagekitd 2> /dev/null || true
+ pkill -f gnome-software 2> /dev/null || true
+ pkill -f discover 2> /dev/null || true
+ sleep 1
+
+ # Re-check holders
+ holders=()
+ if command -v fuser > /dev/null 2>&1; then
+ mapfile -t holders < <(fuser "$lock_file" 2> /dev/null | tr ' ' '\n' | grep -E '^[0-9]+$' || true)
+ elif command -v lsof > /dev/null 2>&1; then
+ mapfile -t holders < <(lsof -t "$lock_file" 2> /dev/null | grep -E '^[0-9]+$' || true)
+ fi
+ if [[ ${#holders[@]} -gt 0 ]]; then
+ echo -e "${RED}Cannot free the pacman lock; another process still holds it. Try again later.${NC}" >&2
+ return 1
+ fi
+ fi
+ fi
+
+ # Decide whether to remove the lock
+ local now epoch age
+ if epoch=$(stat -c %Y "$lock_file" 2> /dev/null); then
+ now=$(date +%s)
+ age=$((now - epoch))
+ else
+ age=999999
+ fi
+
+ # Auto-remove in non-interactive mode (--noconfirm) or if the lock is older than 10 minutes
+ if has_noconfirm_flag "$@" || [[ $age -ge 600 ]]; then
+ echo -e "${YELLOW}Stale pacman lock detected (age: ${age}s). Removing it automatically...${NC}" >&2
+ if [[ $EUID -ne 0 ]]; then
+ sudo rm -f "$lock_file" || return 1
else
- holders=()
+ rm -f "$lock_file" || return 1
fi
+ return 0
+ fi
- # Filter out our own PID if it somehow appears
- if [[ ${#holders[@]} -gt 0 ]]; then
- local -a filtered=()
- for pid in "${holders[@]}"; do
- [[ "$pid" -eq "$$" ]] && continue
- filtered+=("$pid")
- done
- holders=("${filtered[@]}")
- fi
-
- if [[ ${#holders[@]} -gt 0 ]]; then
- local pac_holder=0
- local gui_holder=0
- for pid in "${holders[@]}"; do
- local comm args lower
- comm=$(ps -p "$pid" -o comm= 2>/dev/null || true)
- args=$(ps -p "$pid" -o args= 2>/dev/null || true)
- lower="${comm,,} ${args,,}"
- if [[ "$lower" == *" pacman"* || "$lower" == pacman* || "$lower" == *"/pacman "* || "$lower" == *" pamac"* ]]; then
- pac_holder=1
- elif [[ "$lower" == *packagekit* || "$lower" == *gnome-software* || "$lower" == *discover* ]]; then
- gui_holder=1
- fi
- done
-
- if [[ $pac_holder -eq 1 ]]; then
- echo -e "${RED}Another pacman/pamac transaction is holding the database lock. Try again later.${NC}" >&2
- return 1
- fi
-
- if [[ $gui_holder -eq 1 ]]; then
- echo -e "${YELLOW}A background software updater is holding the pacman lock. Attempting to stop it...${NC}" >&2
- if command -v systemctl >/dev/null 2>&1; then
- systemctl --quiet stop packagekit.service 2>/dev/null || true
- systemctl --quiet stop packagekit 2>/dev/null || true
- fi
- pkill -x packagekitd 2>/dev/null || true
- pkill -f gnome-software 2>/dev/null || true
- pkill -f discover 2>/dev/null || true
- sleep 1
-
- # Re-check holders
- holders=()
- if command -v fuser >/dev/null 2>&1; then
- mapfile -t holders < <(fuser "$lock_file" 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+$' || true)
- elif command -v lsof >/dev/null 2>&1; then
- mapfile -t holders < <(lsof -t "$lock_file" 2>/dev/null | grep -E '^[0-9]+$' || true)
- fi
- if [[ ${#holders[@]} -gt 0 ]]; then
- echo -e "${RED}Cannot free the pacman lock; another process still holds it. Try again later.${NC}" >&2
- return 1
- fi
- fi
- fi
-
- # Decide whether to remove the lock
- local now epoch age
- if epoch=$(stat -c %Y "$lock_file" 2>/dev/null); then
- now=$(date +%s)
- age=$(( now - epoch ))
+ # Interactive prompt (15s timeout)
+ echo -e "${YELLOW}A pacman lock exists but no active pacman is running.${NC}" >&2
+ echo -e "${CYAN}Lock path:${NC} $lock_file (age: ${age}s)" >&2
+ read -r -t 15 -p $'Remove stale lock and continue? [y/N]: ' reply || reply="n"
+ if [[ ${reply,,} == "y" || ${reply,,} == "yes" ]]; then
+ if [[ $EUID -ne 0 ]]; then
+ sudo rm -f "$lock_file" || return 1
else
- age=999999
+ rm -f "$lock_file" || return 1
fi
-
- # Auto-remove in non-interactive mode (--noconfirm) or if the lock is older than 10 minutes
- if has_noconfirm_flag "$@" || [[ $age -ge 600 ]]; then
- echo -e "${YELLOW}Stale pacman lock detected (age: ${age}s). Removing it automatically...${NC}" >&2
- if [[ $EUID -ne 0 ]]; then
- sudo rm -f "$lock_file" || return 1
- else
- rm -f "$lock_file" || return 1
- fi
- return 0
- fi
-
- # Interactive prompt (15s timeout)
- echo -e "${YELLOW}A pacman lock exists but no active pacman is running.${NC}" >&2
- echo -e "${CYAN}Lock path:${NC} $lock_file (age: ${age}s)" >&2
- read -r -t 15 -p $'Remove stale lock and continue? [y/N]: ' reply || reply="n"
- if [[ "${reply,,}" == "y" || "${reply,,}" == "yes" ]]; then
- if [[ $EUID -ne 0 ]]; then
- sudo rm -f "$lock_file" || return 1
- else
- rm -f "$lock_file" || return 1
- fi
- return 0
- fi
- echo -e "${RED}Aborting due to existing pacman lock. Close other updaters and retry, or run with --noconfirm to auto-clear stale locks.${NC}" >&2
- return 1
+ return 0
+ fi
+ echo -e "${RED}Aborting due to existing pacman lock. Close other updaters and retry, or run with --noconfirm to auto-clear stale locks.${NC}" >&2
+ return 1
}
# Cleanup: remove any installed blocked packages (in addition to the queued operation)
function remove_installed_blocked_packages() {
- # args not used; kept for future policy extension
- # List installed package names
- mapfile -t installed_names < <("$PACMAN_BIN" -Qq 2>/dev/null)
- local to_remove=()
- for name in "${installed_names[@]}"; do
- if is_blocked_package_name "$name"; then
- to_remove+=("$name")
- fi
- done
-
- if [[ ${#to_remove[@]} -eq 0 ]]; then
- return 0
+ # args not used; kept for future policy extension
+ # List installed package names
+ mapfile -t installed_names < <("$PACMAN_BIN" -Qq 2> /dev/null)
+ local to_remove=()
+ for name in "${installed_names[@]}"; do
+ if is_blocked_package_name "$name"; then
+ to_remove+=("$name")
fi
+ done
- echo -e "${YELLOW}Policy cleanup:${NC} Removing blocked installed packages: ${BOLD}${to_remove[*]}${NC}" >&2
- local remove_cmd=("$PACMAN_BIN" -Rns --noconfirm)
- "${remove_cmd[@]}" "${to_remove[@]}"
- local rc=$?
- if [[ $rc -ne 0 ]]; then
- echo -e "${RED}Cleanup removal failed with exit code ${rc}.${NC}" >&2
- else
- echo -e "${GREEN}Cleanup removal completed for: ${to_remove[*]}${NC}" >&2
- fi
- return $rc
+ if [[ ${#to_remove[@]} -eq 0 ]]; then
+ return 0
+ fi
+
+ echo -e "${YELLOW}Policy cleanup:${NC} Removing blocked installed packages: ${BOLD}${to_remove[*]}${NC}" >&2
+ local remove_cmd=("$PACMAN_BIN" -Rns --noconfirm)
+ "${remove_cmd[@]}" "${to_remove[@]}"
+ local rc=$?
+ if [[ $rc -ne 0 ]]; then
+ echo -e "${RED}Cleanup removal failed with exit code ${rc}.${NC}" >&2
+ else
+ echo -e "${GREEN}Cleanup removal completed for: ${to_remove[*]}${NC}" >&2
+ fi
+ return $rc
}
# Function to check if user is trying to install packages that are always blocked
function check_for_always_blocked() {
- # Check if the command is an installation command
- if [[ "$1" == "-S" || "$1" == "-Sy" || "$1" == "-Syu" || "$1" == "-Syyu" || "$1" == "-U" ]]; then
- # Check all arguments
- for arg in "$@"; do
- # Strip repository prefix if present (like extra/ or community/)
- local package_name="${arg##*/}"
- if is_blocked_package_name "$package_name"; then
- return 0 # Always blocked package found
- fi
- done
- fi
- return 1 # No always blocked package found
+ # Check if the command is an installation command
+ if [[ $1 == "-S" || $1 == "-Sy" || $1 == "-Syu" || $1 == "-Syyu" || $1 == "-U" ]]; then
+ # Check all arguments
+ for arg in "$@"; do
+ # Strip repository prefix if present (like extra/ or community/)
+ local package_name="${arg##*/}"
+ if is_blocked_package_name "$package_name"; then
+ return 0 # Always blocked package found
+ fi
+ done
+ fi
+ return 1 # No always blocked package found
}
# Function to check if user is trying to install steam (challenge-eligible package)
function check_for_steam() {
- # List of packages that require challenge (only steam in this case)
- local steam_packages=("steam")
-
- # Check if the command is an installation command
- if [[ "$1" == "-S" || "$1" == "-Sy" || "$1" == "-Syu" || "$1" == "-Syyu" || "$1" == "-U" ]]; then
- # Check all arguments
- for arg in "$@"; do
- # Strip repository prefix if present (like extra/ or community/)
- local package_name="${arg##*/}"
-
- # Check if argument matches steam
- for package in "${steam_packages[@]}"; do
- if [[ "$arg" == "$package" || "$arg" == *"/$package-"* || "$arg" == *"/$package/"* ||
- "$arg" == *"/$package" || "$package_name" == "$package" ]]; then
- return 0 # Steam package found
- fi
- done
- done
- fi
- return 1 # No steam package found
+ # List of packages that require challenge (only steam in this case)
+ local steam_packages=("steam")
+
+ # Check if the command is an installation command
+ if [[ $1 == "-S" || $1 == "-Sy" || $1 == "-Syu" || $1 == "-Syyu" || $1 == "-U" ]]; then
+ # Check all arguments
+ for arg in "$@"; do
+ # Strip repository prefix if present (like extra/ or community/)
+ local package_name="${arg##*/}"
+
+ # Check if argument matches steam
+ for package in "${steam_packages[@]}"; do
+ if [[ $arg == "$package" || $arg == *"/$package-"* || $arg == *"/$package/"* ||
+ $arg == *"/$package" || $package_name == "$package" ]]; then
+ return 0 # Steam package found
+ fi
+ done
+ done
+ fi
+ return 1 # No steam package found
}
# Function to check if current day is a weekday (after 4PM Friday until midnight Sunday)
function is_weekday() {
- local day_of_week
- day_of_week=$(date +%u) # %u gives 1-7 (Monday is 1, Sunday is 7)
- local hour
- hour=$(date +%H) # %H gives hour in 24-hour format (00-23)
-
- # Monday through Thursday are always weekdays
- if [[ $day_of_week -ge 1 && $day_of_week -le 4 ]]; then
- return 0 # Is weekday
- # Friday before 4PM is weekday, after 4PM is weekend
- elif [[ $day_of_week -eq 5 ]]; then
- if [[ $hour -lt 14 ]]; then
- return 0 # Is weekday (Friday before 4PM)
- else
- return 1 # Is weekend (Friday after 4PM)
- fi
- # Saturday and Sunday are weekend
+ local day_of_week
+ day_of_week=$(date +%u) # %u gives 1-7 (Monday is 1, Sunday is 7)
+ local hour
+ hour=$(date +%H) # %H gives hour in 24-hour format (00-23)
+
+ # Monday through Thursday are always weekdays
+ if [[ $day_of_week -ge 1 && $day_of_week -le 4 ]]; then
+ return 0 # Is weekday
+ # Friday before 4PM is weekday, after 4PM is weekend
+ elif [[ $day_of_week -eq 5 ]]; then
+ if [[ $hour -lt 14 ]]; then
+ return 0 # Is weekday (Friday before 4PM)
else
- return 1 # Is weekend
+ return 1 # Is weekend (Friday after 4PM)
fi
+ # Saturday and Sunday are weekend
+ else
+ return 1 # Is weekend
+ fi
}
# Function to prompt for solving a word unscrambling challenge (only for steam)
function prompt_for_steam_challenge() {
- echo -e "${YELLOW}WARNING: You are trying to install Steam.${NC}"
-
- # Check if it's a weekday and block completely
- if is_weekday; then
+ echo -e "${YELLOW}WARNING: You are trying to install Steam.${NC}"
+
+ # Check if it's a weekday and block completely
+ if is_weekday; then
local day_name
day_name=$(date +%A)
- echo -e "${RED}Steam installation BLOCKED: Steam cannot be installed on weekdays.${NC}"
- echo -e "${RED}Today is $day_name. Please try again on the weekend (Saturday or Sunday).${NC}"
- return 1
- fi
-
- echo -e "${YELLOW}Weekend Steam challenge will begin shortly...${NC}"
-
- # Sleep for random 20-40 seconds
- # sleep_duration=$((RANDOM % 20 + 20))
- sleep_duration=$((RANDOM % 20))
- sleep $sleep_duration
-
- # Define path to words.txt (in the same directory as the script)
- script_dir="$(dirname "$(readlink -f "$0")")"
- words_file="$script_dir/words.txt"
-
- # Check if words.txt exists
- if [[ ! -f "$words_file" ]]; then
- echo -e "${RED}Error: words.txt file not found at $words_file${NC}"
- return 1
- fi
-
- # Choose a specific word length (5, 6, 7, or 8 characters)
- #
- word_length=5
- echo -e "${CYAN}Today's challenge: Words with ${word_length} letters${NC}"
-
- # Filter words by the specific chosen length and load random words
- words_count=160
- mapfile -t selected_words < <(grep -E "^[a-zA-Z]{$word_length}$" "$words_file" | shuf -n $words_count)
-
- # If we couldn't get enough words of the right length
- if [[ ${#selected_words[@]} -lt $words_count ]]; then
- echo -e "${RED}Warning: Could only find ${#selected_words[@]} words of length $word_length.${NC}"
- words_count=${#selected_words[@]}
- if [[ $words_count -eq 0 ]]; then
- echo -e "${RED}Error: No words of length $word_length found in $words_file${NC}"
- return 1
- fi
- fi
-
- # Convert all words to uppercase
- for i in "${!selected_words[@]}"; do
- selected_words[$i]=$(echo "${selected_words[$i]}" | tr '[:lower:]' '[:upper:]')
- done
-
- echo -e "${CYAN}Here are ${words_count} random words. Remember them:${NC}"
-
- # Display the words in a grid (4 columns)
- for (( i=0; i/dev/null
- wait $display_pid 2>/dev/null
- echo # Add a newline after the timer
-
- # Check if read timed out
- if [[ $read_status -ne 0 ]]; then
- echo -e "${RED}Time's up! Challenge failed. The correct word was '$target_word'.${NC}"
- return 1
- fi
-
- # Convert user input to uppercase and trim whitespaces
- user_input=$(echo "$user_input" | tr '[:lower:]' '[:upper:]' | xargs)
-
- if [[ "$user_input" == "$target_word" ]]; then
- echo -e "${GREEN}Correct! Proceeding with installation...${NC}"
-
- # Add sleep after successful challenge completion (20-40 seconds)
- # post_challenge_sleep=$((RANDOM % 20 + 20))
- post_challenge_sleep=$((RANDOM % 20))
- sleep $post_challenge_sleep
-
- return 0
- else
- echo -e "${RED}Incorrect answer. Installation aborted. The correct word was '$target_word'.${NC}"
- return 1
+ fi
+
+ # Convert all words to uppercase
+ for i in "${!selected_words[@]}"; do
+ selected_words[i]=$(echo "${selected_words[i]}" | tr '[:lower:]' '[:upper:]')
+ done
+
+ echo -e "${CYAN}Here are ${words_count} random words. Remember them:${NC}"
+
+ # Display the words in a grid (4 columns)
+ for ((i = 0; i < words_count; i++)); do
+ printf "${BLUE}%-15s${NC}" "${selected_words[i]}"
+ if (((i + 1) % 4 == 0)); then
+ echo ""
fi
+ done
+
+ # Select a random word to scramble (already in uppercase)
+ target_index=$((RANDOM % words_count))
+ target_word="${selected_words[target_index]}"
+
+ # Scramble the word
+ scrambled_word=$(echo "$target_word" | fold -w1 | shuf | tr -d '\n')
+
+ # Ensure scrambled word is different from original
+ if [[ $scrambled_word == "$target_word" ]]; then
+ # Use simple reversal as fallback
+ scrambled_word=$(echo "$target_word" | rev)
+ fi
+
+ echo -e "\n${YELLOW}One of those words has been scrambled to:${NC} ${CYAN}$scrambled_word${NC}"
+ echo -e "${YELLOW}Unscramble the word to proceed with installation (you have 2 minutes):${NC}"
+
+ # Set up a background process to display the timer
+ (
+ start_time=$(date +%s)
+ while true; do
+ current_time=$(date +%s)
+ elapsed=$((current_time - start_time))
+ remaining=$((60 - elapsed))
+
+ if [[ $remaining -le 0 ]]; then
+ echo -ne "\r${YELLOW}Time remaining: 0 seconds${NC} "
+ break
+ fi
+
+ echo -ne "\r${YELLOW}Time remaining: ${remaining} seconds${NC} "
+ sleep 1
+ done
+ ) &
+ display_pid=$!
+
+ # Read user input with timeout
+ read -t 60 -r user_input
+ read_status=$?
+
+ # Kill the timer display
+ kill "$display_pid" 2> /dev/null
+ wait "$display_pid" 2> /dev/null
+ echo # Add a newline after the timer
+
+ # Check if read timed out
+ if [[ $read_status -ne 0 ]]; then
+ echo -e "${RED}Time's up! Challenge failed. The correct word was '$target_word'.${NC}"
+ return 1
+ fi
+
+ # Convert user input to uppercase and trim whitespaces
+ user_input=$(echo "$user_input" | tr '[:lower:]' '[:upper:]' | xargs)
+
+ if [[ $user_input == "$target_word" ]]; then
+ echo -e "${GREEN}Correct! Proceeding with installation...${NC}"
+
+ # Add sleep after successful challenge completion (20-40 seconds)
+ # post_challenge_sleep=$((RANDOM % 20 + 20))
+ post_challenge_sleep=$((RANDOM % 20))
+ sleep "$post_challenge_sleep"
+
+ return 0
+ else
+ echo -e "${RED}Incorrect answer. Installation aborted. The correct word was '$target_word'.${NC}"
+ return 1
+ fi
}
# Function to prompt for solving a word unscrambling challenge (for virtualbox - always active)
+# shellcheck disable=SC2329 # Invoked dynamically when matching VirtualBox packages
function prompt_for_virtualbox_challenge() {
- echo -e "${YELLOW}WARNING: You are trying to install VirtualBox.${NC}"
- echo -e "${YELLOW}VirtualBox challenge will begin shortly...${NC}"
-
- # Sleep for random 10-30 seconds
- sleep_duration=$((RANDOM % 20 + 10))
- sleep $sleep_duration
-
- # Define path to words.txt (in the same directory as the script)
- script_dir="$(dirname "$(readlink -f "$0")")"
- words_file="$script_dir/words.txt"
-
- # Check if words.txt exists
- if [[ ! -f "$words_file" ]]; then
- echo -e "${RED}Error: words.txt file not found at $words_file${NC}"
- return 1
- fi
-
- # Choose a specific word length (6, 7, or 8 characters for VirtualBox)
- word_length=6
- echo -e "${CYAN}VirtualBox challenge: Words with ${word_length} letters${NC}"
-
- # Filter words by the specific chosen length and load random words
- words_count=120
- mapfile -t selected_words < <(grep -E "^[a-zA-Z]{$word_length}$" "$words_file" | shuf -n $words_count)
-
- # If we couldn't get enough words of the right length
- if [[ ${#selected_words[@]} -lt $words_count ]]; then
- echo -e "${RED}Warning: Could only find ${#selected_words[@]} words of length $word_length.${NC}"
- words_count=${#selected_words[@]}
- if [[ $words_count -eq 0 ]]; then
- echo -e "${RED}Error: No words of length $word_length found in $words_file${NC}"
- return 1
- fi
- fi
-
- # Convert all words to uppercase
- for i in "${!selected_words[@]}"; do
- selected_words[$i]=$(echo "${selected_words[$i]}" | tr '[:lower:]' '[:upper:]')
- done
-
- echo -e "${CYAN}Here are ${words_count} random words. Remember them:${NC}"
-
- # Display the words in a grid (4 columns)
- for (( i=0; i/dev/null
- wait $display_pid 2>/dev/null
- echo # Add a newline after the timer
-
- # Check if read timed out
- if [[ $read_status -ne 0 ]]; then
- echo -e "${RED}Time's up! VirtualBox challenge failed. The correct word was '$target_word'.${NC}"
- return 1
- fi
-
- # Convert user input to uppercase and trim whitespaces
- user_input=$(echo "$user_input" | tr '[:lower:]' '[:upper:]' | xargs)
-
- if [[ "$user_input" == "$target_word" ]]; then
- echo -e "${GREEN}Correct! Proceeding with VirtualBox installation...${NC}"
-
- # Add sleep after successful challenge completion (15-35 seconds)
- post_challenge_sleep=$((RANDOM % 20 + 15))
- sleep $post_challenge_sleep
-
- return 0
- else
- echo -e "${RED}Incorrect answer. VirtualBox installation aborted. The correct word was '$target_word'.${NC}"
- return 1
+ fi
+
+ # Convert all words to uppercase
+ for i in "${!selected_words[@]}"; do
+ selected_words[i]=$(echo "${selected_words[i]}" | tr '[:lower:]' '[:upper:]')
+ done
+
+ echo -e "${CYAN}Here are ${words_count} random words. Remember them:${NC}"
+
+ # Display the words in a grid (4 columns)
+ for ((i = 0; i < words_count; i++)); do
+ printf "${BLUE}%-15s${NC}" "${selected_words[i]}"
+ if (((i + 1) % 4 == 0)); then
+ echo ""
fi
+ done
+
+ # Select a random word to scramble (already in uppercase)
+ target_index=$((RANDOM % words_count))
+ target_word="${selected_words[target_index]}"
+
+ # Scramble the word
+ scrambled_word=$(echo "$target_word" | fold -w1 | shuf | tr -d '\n')
+
+ # Ensure scrambled word is different from original
+ if [[ $scrambled_word == "$target_word" ]]; then
+ # Use simple reversal as fallback
+ scrambled_word=$(echo "$target_word" | rev)
+ fi
+
+ echo -e "\n${YELLOW}One of those words has been scrambled to:${NC} ${CYAN}$scrambled_word${NC}"
+ echo -e "${YELLOW}Unscramble the word to proceed with VirtualBox installation (you have 90 seconds):${NC}"
+
+ # Set up a background process to display the timer
+ (
+ start_time=$(date +%s)
+ while true; do
+ current_time=$(date +%s)
+ elapsed=$((current_time - start_time))
+ remaining=$((90 - elapsed))
+
+ if [[ $remaining -le 0 ]]; then
+ echo -ne "\r${YELLOW}Time remaining: 0 seconds${NC} "
+ break
+ fi
+
+ echo -ne "\r${YELLOW}Time remaining: ${remaining} seconds${NC} "
+ sleep 1
+ done
+ ) &
+ display_pid=$!
+
+ # Read user input with timeout (90 seconds for VirtualBox)
+ read -t 90 -r user_input
+ read_status=$?
+
+ # Kill the timer display
+ kill "$display_pid" 2> /dev/null
+ wait "$display_pid" 2> /dev/null
+ echo # Add a newline after the timer
+
+ # Check if read timed out
+ if [[ $read_status -ne 0 ]]; then
+ echo -e "${RED}Time's up! VirtualBox challenge failed. The correct word was '$target_word'.${NC}"
+ return 1
+ fi
+
+ # Convert user input to uppercase and trim whitespaces
+ user_input=$(echo "$user_input" | tr '[:lower:]' '[:upper:]' | xargs)
+
+ if [[ $user_input == "$target_word" ]]; then
+ echo -e "${GREEN}Correct! Proceeding with VirtualBox installation...${NC}"
+
+ # Add sleep after successful challenge completion (15-35 seconds)
+ post_challenge_sleep=$((RANDOM % 20 + 15))
+ sleep "$post_challenge_sleep"
+
+ return 0
+ else
+ echo -e "${RED}Incorrect answer. VirtualBox installation aborted. The correct word was '$target_word'.${NC}"
+ return 1
+ fi
}
# Check for wrapper-specific commands
-if [[ "$1" == "--help-wrapper" ]]; then
- show_help
- exit 0
+if [[ $1 == "--help-wrapper" ]]; then
+ show_help
+ exit 0
fi
# Before any pacman action, ensure maintenance services exist
@@ -666,19 +667,18 @@ ensure_periodic_maintenance
# Check for always blocked packages first (highest priority)
if check_for_always_blocked "$@"; then
- echo -e "${RED}Installation BLOCKED: This package is permanently restricted and cannot be installed.${NC}"
- echo -e "${RED}Package installation has been denied by system policy.${NC}"
- # Regardless of the attempted action, enforce cleanup of any installed blocked packages
- remove_installed_blocked_packages "$@"
- exit 1
+ echo -e "${RED}Installation BLOCKED: This package is permanently restricted and cannot be installed.${NC}"
+ echo -e "${RED}Package installation has been denied by system policy.${NC}"
+ # Regardless of the attempted action, enforce cleanup of any installed blocked packages
+ remove_installed_blocked_packages "$@"
+ exit 1
fi
# Check for steam (challenge-eligible package)
if check_for_steam "$@"; then
- prompt_for_steam_challenge
- if [[ $? -ne 0 ]]; then
- exit 1
- fi
+ if ! prompt_for_steam_challenge; then
+ exit 1
+ fi
fi
# Display operation
@@ -692,19 +692,19 @@ start_time=$(date +%s)
# Execute the real pacman command (with /etc/hosts guard handling)
if needs_unlock "$@"; then
- pre_unlock_hosts
+ pre_unlock_hosts
fi
# Handle a possible stale DB lock before executing
if ! check_and_handle_db_lock "$@"; then
- exit 1
+ exit 1
fi
"$PACMAN_BIN" "$@"
exit_code=$?
if needs_unlock "$@"; then
- post_relock_hosts
+ post_relock_hosts
fi
# Record end time for statistics
@@ -713,21 +713,21 @@ duration=$((end_time - start_time))
# Display results
if [ $exit_code -eq 0 ]; then
- echo -e "${GREEN}Command completed successfully in ${duration}s.${NC}" >&2
+ echo -e "${GREEN}Command completed successfully in ${duration}s.${NC}" >&2
else
- echo -e "${RED}Command failed with exit code ${exit_code}.${NC}" >&2
+ echo -e "${RED}Command failed with exit code ${exit_code}.${NC}" >&2
fi
# After any operation, remove installed blocked packages as part of policy enforcement
remove_installed_blocked_packages "$@"
# Display some helpful tips depending on the operation
-if [[ "$1" == "-S" || "$1" == "-S "* ]] && [ $exit_code -eq 0 ]; then
- echo -e "${CYAN}Tip:${NC} You may need to log out or restart to use some newly installed software."
+if [[ $1 == "-S" || $1 == "-S "* ]] && [ $exit_code -eq 0 ]; then
+ echo -e "${CYAN}Tip:${NC} You may need to log out or restart to use some newly installed software."
fi
-if [[ "$1" == "-Syu" || "$1" == "-Syyu" ]] && [ $exit_code -eq 0 ]; then
- echo -e "${CYAN}Tip:${NC} Consider restarting after major updates."
+if [[ $1 == "-Syu" || $1 == "-Syyu" ]] && [ $exit_code -eq 0 ]; then
+ echo -e "${CYAN}Tip:${NC} Consider restarting after major updates."
fi
-exit $exit_code
\ No newline at end of file
+exit $exit_code
diff --git a/scripts/digital_wellbeing/pc_startup_visual_status.sh b/scripts/digital_wellbeing/pc_startup_visual_status.sh
index cfd5f55..aead3a4 100755
--- a/scripts/digital_wellbeing/pc_startup_visual_status.sh
+++ b/scripts/digital_wellbeing/pc_startup_visual_status.sh
@@ -24,254 +24,262 @@ BELL="🔔"
# Function to draw a box around text
draw_box() {
- local text="$1"
- local width=${#text}
- local padding=2
- local total_width=$((width + padding * 2))
-
- # Top border
- printf "┌"
- printf "─%.0s" $(seq 1 $total_width)
- printf "┐\n"
-
- # Content with padding
- printf "│%*s%s%*s│\n" $padding "" "$text" $padding ""
-
- # Bottom border
- printf "└"
- printf "─%.0s" $(seq 1 $total_width)
- printf "┘\n"
+ local text="$1"
+ local width=${#text}
+ local padding=2
+ local total_width=$((width + padding * 2))
+
+ # Top border
+ printf "┌"
+ printf "─%.0s" $(seq 1 $total_width)
+ printf "┐\n"
+
+ # Content with padding
+ printf "│%*s%s%*s│\n" $padding "" "$text" $padding ""
+
+ # Bottom border
+ printf "└"
+ printf "─%.0s" $(seq 1 $total_width)
+ printf "┘\n"
}
# Function to show current day status
show_day_status() {
- local day_of_week=$(date +%u)
- local day_name=$(date +%A)
- local today=$(date +%Y-%m-%d)
-
- printf "${BLUE}${CALENDAR} Day Status${NC}\n"
- printf "═══════════════\n"
-
- # Show all days with status
- local days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")
- local monitored=(1 0 0 0 1 1 1) # 1=monitored, 0=not monitored
-
- for i in {0..6}; do
- local day_num=$((i + 1))
- if [[ $day_num -eq 7 ]]; then day_num=0; fi # Sunday is 0 in some contexts
-
- if [[ ${monitored[$i]} -eq 1 ]]; then
- if [[ $day_of_week -eq $((i + 1)) ]] || [[ $day_of_week -eq 7 && $i -eq 6 ]]; then
- printf "${GREEN}${CHECK} ${days[$i]} (TODAY - MONITORED)${NC}\n"
- else
- printf "${CYAN}${CHECK} ${days[$i]} (monitored)${NC}\n"
- fi
- else
- if [[ $day_of_week -eq $((i + 1)) ]]; then
- printf "${GRAY}○ ${days[$i]} (TODAY - not monitored)${NC}\n"
- else
- printf "${GRAY}○ ${days[$i]}${NC}\n"
- fi
- fi
- done
-
- printf "\n"
+ local day_of_week
+ day_of_week=$(date +%u)
+
+ printf '%s%s Day Status%s\n' "$BLUE" "$CALENDAR" "$NC"
+ printf '═══════════════\n'
+
+ # Show all days with status
+ local days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")
+ local monitored=(1 0 0 0 1 1 1) # 1=monitored, 0=not monitored
+
+ for i in {0..6}; do
+ local day_num=$((i + 1))
+ if [[ $day_num -eq 7 ]]; then day_num=0; fi # Sunday is 0 in some contexts
+
+ if [[ ${monitored[$i]} -eq 1 ]]; then
+ if [[ $day_of_week -eq $((i + 1)) ]] || [[ $day_of_week -eq 7 && $i -eq 6 ]]; then
+ printf '%s%s %s (TODAY - MONITORED)%s\n' "$GREEN" "$CHECK" "${days[$i]}" "$NC"
+ else
+ printf '%s%s %s (monitored)%s\n' "$CYAN" "$CHECK" "${days[$i]}" "$NC"
+ fi
+ else
+ if [[ $day_of_week -eq $((i + 1)) ]]; then
+ printf '%s○ %s (TODAY - not monitored)%s\n' "$GRAY" "${days[$i]}" "$NC"
+ else
+ printf '%s○ %s%s\n' "$GRAY" "${days[$i]}" "$NC"
+ fi
+ fi
+ done
+
+ printf "\n"
}
# Function to show time window status
show_time_status() {
- local current_hour=$(date +%H)
- local current_minute=$(date +%M)
- local current_hour_num=$((10#$current_hour))
-
- printf "${YELLOW}${CLOCK} Time Window Status${NC}\n"
- printf "═══════════════════════\n"
-
- # Show 24-hour timeline with window highlighted
- printf "Timeline (24-hour format):\n"
- printf "00 01 02 03 04 "
- printf "${GREEN}05 06 07${NC} "
- printf "08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23\n"
- printf " "
- printf "${GREEN}▲─────▲${NC}\n"
- printf " "
- printf "${GREEN}Expected Window${NC}\n"
-
- # Current time indicator
- printf "\nCurrent time: ${WHITE}%02d:%s${NC}\n" $current_hour_num "$current_minute"
-
- if [[ $current_hour_num -ge 5 && $current_hour_num -lt 8 ]]; then
- printf "Status: ${GREEN}${CHECK} Within expected window (5AM-8AM)${NC}\n"
- else
- printf "Status: ${YELLOW}○ Outside expected window${NC}\n"
- fi
-
- printf "\n"
+ local current_hour current_minute current_hour_num
+ current_hour=$(date +%H)
+ current_minute=$(date +%M)
+ current_hour_num=$((10#$current_hour))
+
+ printf '%s%s Time Window Status%s\n' "$YELLOW" "$CLOCK" "$NC"
+ printf '═══════════════════════\n'
+
+ # Show 24-hour timeline with window highlighted
+ printf 'Timeline (24-hour format):\n'
+ printf '00 01 02 03 04 '
+ printf '%s05 06 07%s ' "$GREEN" "$NC"
+ printf '08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23\n'
+ printf ' '
+ printf '%s▲─────▲%s\n' "$GREEN" "$NC"
+ printf ' '
+ printf '%sExpected Window%s\n' "$GREEN" "$NC"
+
+ # Current time indicator
+ printf '\nCurrent time: %s%02d:%s%s\n' "$WHITE" "$current_hour_num" "$current_minute" "$NC"
+
+ if [[ $current_hour_num -ge 5 && $current_hour_num -lt 8 ]]; then
+ printf 'Status: %s%s Within expected window (5AM-8AM)%s\n' "$GREEN" "$CHECK" "$NC"
+ else
+ printf 'Status: %s○ Outside expected window%s\n' "$YELLOW" "$NC"
+ fi
+
+ printf '\n'
}
# Function to show boot time analysis
show_boot_analysis() {
- printf "${PURPLE}${COMPUTER} Boot Time Analysis${NC}\n"
- printf "═══════════════════════\n"
-
- # Get boot time
- local uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
- local boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
- local boot_date=$(echo "$boot_time" | cut -d' ' -f1)
- local boot_time_only=$(echo "$boot_time" | cut -d' ' -f2)
- local boot_hour=$(echo "$boot_time_only" | cut -d':' -f1)
- local boot_hour_num=$((10#$boot_hour))
- local today=$(date +%Y-%m-%d)
-
- printf "System boot time: ${WHITE}$boot_time${NC}\n"
-
- if [[ "$boot_date" == "$today" ]]; then
- printf "Boot date: ${GREEN}${CHECK} Today${NC}\n"
-
- if [[ $boot_hour_num -ge 5 && $boot_hour_num -lt 8 ]]; then
- printf "Boot window: ${GREEN}${CHECK} Within expected window (5AM-8AM)${NC}\n"
- printf "Status: ${GREEN}${CHECK} COMPLIANT${NC}\n"
- else
- printf "Boot window: ${RED}${CROSS} Outside expected window${NC}\n"
- printf "Status: ${RED}${WARNING} NON-COMPLIANT${NC}\n"
- fi
+ printf '%s%s Boot Time Analysis%s\n' "$PURPLE" "$COMPUTER" "$NC"
+ printf '═══════════════════════\n'
+
+ # Get boot time
+ local uptime_seconds boot_time boot_date boot_time_only boot_hour boot_hour_num today
+ uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0")
+ boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
+ boot_date=$(echo "$boot_time" | cut -d' ' -f1)
+ boot_time_only=$(echo "$boot_time" | cut -d' ' -f2)
+ boot_hour=$(echo "$boot_time_only" | cut -d':' -f1)
+ boot_hour_num=$((10#$boot_hour))
+ today=$(date +%Y-%m-%d)
+
+ printf 'System boot time: %s%s%s\n' "$WHITE" "$boot_time" "$NC"
+
+ if [[ $boot_date == "$today" ]]; then
+ printf 'Boot date: %s%s Today%s\n' "$GREEN" "$CHECK" "$NC"
+
+ if [[ $boot_hour_num -ge 5 && $boot_hour_num -lt 8 ]]; then
+ printf 'Boot window: %s%s Within expected window (5AM-8AM)%s\n' "$GREEN" "$CHECK" "$NC"
+ printf 'Status: %s%s COMPLIANT%s\n' "$GREEN" "$CHECK" "$NC"
else
- printf "Boot date: ${YELLOW}○ Not today ($boot_date)${NC}\n"
- printf "Status: ${YELLOW}○ System was not booted today${NC}\n"
+ printf 'Boot window: %s%s Outside expected window%s\n' "$RED" "$CROSS" "$NC"
+ printf 'Status: %s%s NON-COMPLIANT%s\n' "$RED" "$WARNING" "$NC"
fi
-
- printf "\n"
+ else
+ printf 'Boot date: %s○ Not today (%s)%s\n' "$YELLOW" "$boot_date" "$NC"
+ printf 'Status: %s○ System was not booted today%s\n' "$YELLOW" "$NC"
+ fi
+
+ printf '\n'
}
# Function to show monitoring system status
show_system_status() {
- printf "${CYAN}${BELL} Monitoring System${NC}\n"
- printf "═══════════════════════\n"
-
- # Check if timer exists and is enabled
- if systemctl is-enabled pc-startup-monitor.timer &>/dev/null; then
- printf "Service: ${GREEN}${CHECK} ENABLED${NC}\n"
-
- if systemctl is-active pc-startup-monitor.timer &>/dev/null; then
- printf "Timer: ${GREEN}${CHECK} ACTIVE${NC}\n"
- else
- printf "Timer: ${RED}${CROSS} INACTIVE${NC}\n"
- fi
-
- # Show next check time
- local next_check=$(systemctl list-timers pc-startup-monitor.timer --no-pager 2>/dev/null | grep pc-startup-monitor | awk '{print $1, $2, $3}' || echo "Not scheduled")
- printf "Next check: ${WHITE}$next_check${NC}\n"
-
+ printf '%s%s Monitoring System%s\n' "$CYAN" "$BELL" "$NC"
+ printf '═══════════════════════\n'
+
+ # Check if timer exists and is enabled
+ if systemctl is-enabled pc-startup-monitor.timer &> /dev/null; then
+ printf 'Service: %s%s ENABLED%s\n' "$GREEN" "$CHECK" "$NC"
+
+ if systemctl is-active pc-startup-monitor.timer &> /dev/null; then
+ printf 'Timer: %s%s ACTIVE%s\n' "$GREEN" "$CHECK" "$NC"
else
- printf "Service: ${RED}${CROSS} NOT ENABLED${NC}\n"
- printf "Timer: ${RED}${CROSS} NOT ACTIVE${NC}\n"
+ printf 'Timer: %s%s INACTIVE%s\n' "$RED" "$CROSS" "$NC"
fi
-
- printf "\n"
+
+ # Show next check time
+ local next_check
+ next_check=$(systemctl list-timers pc-startup-monitor.timer --no-pager 2> /dev/null | grep pc-startup-monitor | awk '{print $1, $2, $3}' || echo "Not scheduled")
+ printf 'Next check: %s%s%s\n' "$WHITE" "$next_check" "$NC"
+
+ else
+ printf 'Service: %s%s NOT ENABLED%s\n' "$RED" "$CROSS" "$NC"
+ printf 'Timer: %s%s NOT ACTIVE%s\n' "$RED" "$CROSS" "$NC"
+ fi
+
+ printf '\n'
}
# Function to show overall compliance status
show_compliance_overview() {
- local day_of_week=$(date +%u)
- local current_hour=$(date +%H)
- local current_hour_num=$((10#$current_hour))
-
- # Check if today is monitored
- local is_monitored=false
- if [[ "$day_of_week" == "1" ]] || [[ "$day_of_week" == "5" ]] || [[ "$day_of_week" == "6" ]] || [[ "$day_of_week" == "7" ]]; then
- is_monitored=true
- fi
-
- printf "${WHITE}"
- draw_box "COMPLIANCE OVERVIEW"
- printf "${NC}\n"
-
- if [[ "$is_monitored" == true ]]; then
- printf "Today: ${GREEN}${CHECK} Monitored day${NC}\n"
-
- # Check current compliance
- if [[ $current_hour_num -ge 5 && $current_hour_num -lt 8 ]]; then
- printf "Current status: ${GREEN}${CHECK} PC is on during expected window${NC}\n"
- printf "Action needed: ${GREEN}None - currently compliant${NC}\n"
- else
- # Check if booted in window
- local uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
- local boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
- local boot_date=$(echo "$boot_time" | cut -d' ' -f1)
- local boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1)
- local boot_hour_num=$((10#$boot_hour))
- local today=$(date +%Y-%m-%d)
-
- if [[ "$boot_date" == "$today" ]] && [[ $boot_hour_num -ge 5 && $boot_hour_num -lt 8 ]]; then
- printf "Current status: ${GREEN}${CHECK} PC was booted in expected window${NC}\n"
- printf "Action needed: ${GREEN}None - compliant${NC}\n"
- else
- printf "Current status: ${RED}${WARNING} PC was NOT booted in expected window${NC}\n"
- printf "Action needed: ${YELLOW}Warning will be shown at 8:30 AM${NC}\n"
- fi
- fi
+ local day_of_week current_hour current_hour_num
+ day_of_week=$(date +%u)
+ current_hour=$(date +%H)
+ current_hour_num=$((10#$current_hour))
+
+ # Check if today is monitored
+ local is_monitored=false
+ if [[ $day_of_week == "1" ]] || [[ $day_of_week == "5" ]] || [[ $day_of_week == "6" ]] || [[ $day_of_week == "7" ]]; then
+ is_monitored=true
+ fi
+
+ printf '%s' "$WHITE"
+ draw_box "COMPLIANCE OVERVIEW"
+ printf '%s\n' "$NC"
+
+ if [[ $is_monitored == true ]]; then
+ printf 'Today: %s%s Monitored day%s\n' "$GREEN" "$CHECK" "$NC"
+
+ # Check current compliance
+ if [[ $current_hour_num -ge 5 && $current_hour_num -lt 8 ]]; then
+ printf 'Current status: %s%s PC is on during expected window%s\n' "$GREEN" "$CHECK" "$NC"
+ printf 'Action needed: %sNone - currently compliant%s\n' "$GREEN" "$NC"
else
- printf "Today: ${GRAY}○ Not a monitored day${NC}\n"
- printf "Current status: ${GRAY}No monitoring required${NC}\n"
- printf "Action needed: ${GRAY}None${NC}\n"
+ # Check if booted in window
+ local uptime_seconds boot_time boot_date boot_hour boot_hour_num today
+ uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0")
+ boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
+ boot_date=$(echo "$boot_time" | cut -d' ' -f1)
+ boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1)
+ boot_hour_num=$((10#$boot_hour))
+ today=$(date +%Y-%m-%d)
+
+ if [[ $boot_date == "$today" ]] && [[ $boot_hour_num -ge 5 && $boot_hour_num -lt 8 ]]; then
+ printf 'Current status: %s%s PC was booted in expected window%s\n' "$GREEN" "$CHECK" "$NC"
+ printf 'Action needed: %sNone - compliant%s\n' "$GREEN" "$NC"
+ else
+ printf 'Current status: %s%s PC was NOT booted in expected window%s\n' "$RED" "$WARNING" "$NC"
+ printf 'Action needed: %sWarning will be shown at 8:30 AM%s\n' "$YELLOW" "$NC"
+ fi
fi
-
- printf "\n"
+ else
+ printf 'Today: %s○ Not a monitored day%s\n' "$GRAY" "$NC"
+ printf 'Current status: %sNo monitoring required%s\n' "$GRAY" "$NC"
+ printf 'Action needed: %sNone%s\n' "$GRAY" "$NC"
+ fi
+
+ printf '\n'
}
# Function to show recent activity
show_recent_activity() {
- printf "${GRAY}📋 Recent Activity${NC}\n"
- printf "════════════════\n"
-
- # Show last 5 log entries
- local logs=$(journalctl -t pc-startup-monitor --no-pager -n 5 --output=short 2>/dev/null || echo "No logs found")
-
- if [[ "$logs" == "No logs found" ]]; then
- printf "${GRAY}No recent monitoring activity${NC}\n"
- else
- echo "$logs" | while IFS= read -r line; do
- if [[ $line == *"WARNING"* ]]; then
- printf "${RED}$line${NC}\n"
- elif [[ $line == *"compliance OK"* ]]; then
- printf "${GREEN}$line${NC}\n"
- else
- printf "${GRAY}$line${NC}\n"
- fi
- done
- fi
-
- printf "\n"
+ printf '%s📋 Recent Activity%s\n' "$GRAY" "$NC"
+ printf '════════════════\n'
+
+ # Show last 5 log entries
+ local logs
+ logs=$(journalctl -t pc-startup-monitor --no-pager -n 5 --output=short 2> /dev/null || echo "No logs found")
+
+ if [[ $logs == "No logs found" ]]; then
+ printf '%sNo recent monitoring activity%s\n' "$GRAY" "$NC"
+ else
+ echo "$logs" | while IFS= read -r line; do
+ if [[ $line == *"WARNING"* ]]; then
+ printf '%s%s%s\n' "$RED" "$line" "$NC"
+ elif [[ $line == *"compliance OK"* ]]; then
+ printf '%s%s%s\n' "$GREEN" "$line" "$NC"
+ else
+ printf '%s%s%s\n' "$GRAY" "$line" "$NC"
+ fi
+ done
+ fi
+
+ printf '\n'
}
# Main display function
main() {
- clear
-
- # Header
- printf "${BLUE}"
- draw_box "PC STARTUP MONITOR - VISUAL STATUS"
- printf "${NC}\n\n"
-
- printf "${WHITE}Current Date/Time: $(date)${NC}\n"
- printf "${WHITE}System Uptime: $(uptime -p)${NC}\n\n"
-
- # Show all status sections
- show_day_status
- show_time_status
- show_boot_analysis
- show_system_status
- show_compliance_overview
- show_recent_activity
-
- # Footer with commands
- printf "${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"
- printf "${WHITE}Commands:${NC}\n"
- printf " ${CYAN}sudo pc-startup-monitor-manager.sh status${NC} - Show system status\n"
- printf " ${CYAN}sudo pc-startup-monitor-manager.sh test${NC} - Test monitor now\n"
- printf " ${CYAN}sudo pc-startup-monitor-manager.sh logs${NC} - View detailed logs\n"
- printf " ${CYAN}$0${NC} - Show this visual status\n"
- printf "${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"
+ clear
+
+ # Header
+ printf '%s' "$BLUE"
+ draw_box "PC STARTUP MONITOR - VISUAL STATUS"
+ printf '%s\n\n' "$NC"
+
+ local current_datetime system_uptime
+ current_datetime=$(date)
+ system_uptime=$(uptime -p)
+ printf '%sCurrent Date/Time: %s%s\n' "$WHITE" "$current_datetime" "$NC"
+ printf '%sSystem Uptime: %s%s\n\n' "$WHITE" "$system_uptime" "$NC"
+
+ # Show all status sections
+ show_day_status
+ show_time_status
+ show_boot_analysis
+ show_system_status
+ show_compliance_overview
+ show_recent_activity
+
+ # Footer with commands
+ printf '%s═══════════════════════════════════════════════════════════════%s\n' "$BLUE" "$NC"
+ printf '%sCommands:%s\n' "$WHITE" "$NC"
+ printf ' %s%s%s - Show system status\n' "$CYAN" "sudo pc-startup-monitor-manager.sh status" "$NC"
+ printf ' %s%s%s - Test monitor now\n' "$CYAN" "sudo pc-startup-monitor-manager.sh test" "$NC"
+ printf ' %s%s%s - View detailed logs\n' "$CYAN" "sudo pc-startup-monitor-manager.sh logs" "$NC"
+ printf ' %s%s%s - Show this visual status\n' "$CYAN" "$0" "$NC"
+ printf '%s═══════════════════════════════════════════════════════════════%s\n' "$BLUE" "$NC"
}
# Run main function
diff --git a/scripts/digital_wellbeing/remove_guest_mode.sh b/scripts/digital_wellbeing/remove_guest_mode.sh
index e975f94..3e875f8 100755
--- a/scripts/digital_wellbeing/remove_guest_mode.sh
+++ b/scripts/digital_wellbeing/remove_guest_mode.sh
@@ -12,10 +12,10 @@ SCRIPT_NAME=$(basename "$0")
UNDO=false
for arg in "$@"; do
- case "$arg" in
- --undo) UNDO=true ;;
- -h|--help)
- cat <"$tmp"
- install -m 0644 "$tmp" "$file"
- rm -f "$tmp"
+ mkdir -p "$target_dir"
+ # Write atomically
+ local tmp
+ tmp=$(mktemp)
+ printf '%s
+' "$POLICY_JSON" > "$tmp"
+ install -m 0644 "$tmp" "$file"
+ rm -f "$tmp"
}
remove_policy() {
- local target_dir="$1"; shift
- local file="$target_dir/$POLICY_FILENAME"
+ local target_dir="$1"
+ shift
+ local file="$target_dir/$POLICY_FILENAME"
- if [[ -f "$file" ]]; then
- echo "[remove] $file"
- rm -f -- "$file"
- else
- echo "[skip] $file (not present)"
- fi
+ if [[ -f $file ]]; then
+ echo "[remove] $file"
+ rm -f -- "$file"
+ else
+ echo "[skip] $file (not present)"
+ fi
}
changed_any=false
for key in "${!INSTALLED_KEYS[@]}"; do
- # If we somehow lack candidate dirs for a key, skip gracefully
- if [[ -z "${CANDIDATE_DIRS[$key]:-}" ]]; then
- echo "[warn] No known policy directories for '$key'; skipping."
- continue
- fi
+ # If we somehow lack candidate dirs for a key, skip gracefully
+ if [[ -z ${CANDIDATE_DIRS[$key]:-} ]]; then
+ echo "[warn] No known policy directories for '$key'; skipping."
+ continue
+ fi
- target_dir=$(choose_target_dir "$key")
+ target_dir=$(choose_target_dir "$key")
- if [[ "$UNDO" == true ]]; then
- remove_policy "$target_dir"
- else
- apply_policy "$target_dir"
- fi
+ if [[ $UNDO == true ]]; then
+ remove_policy "$target_dir"
+ else
+ apply_policy "$target_dir"
+ fi
- changed_any=true
+ changed_any=true
done
-if [[ "$changed_any" == false ]]; then
- echo "[info] Nothing to do."
+if [[ $changed_any == false ]]; then
+ echo "[info] Nothing to do."
fi
-if [[ "$UNDO" == true ]]; then
- echo "[done] Guest mode policy files removed where present. You may need to restart the browsers."
+if [[ $UNDO == true ]]; then
+ echo "[done] Guest mode policy files removed where present. You may need to restart the browsers."
else
- echo "[done] Guest mode disabled via managed policies. Please fully restart affected browsers."
- echo " If the Guest option still appears, it should be disabled/greyed out."
+ echo "[done] Guest mode disabled via managed policies. Please fully restart affected browsers."
+ echo " If the Guest option still appears, it should be disabled/greyed out."
fi
-
diff --git a/scripts/digital_wellbeing/setup_midnight_shutdown.sh b/scripts/digital_wellbeing/setup_midnight_shutdown.sh
index 7fd887e..cea2678 100755
--- a/scripts/digital_wellbeing/setup_midnight_shutdown.sh
+++ b/scripts/digital_wellbeing/setup_midnight_shutdown.sh
@@ -4,194 +4,190 @@
# Thursday-Sunday: Shutdown between 22:00-05:00
# Handles sudo privileges automatically
-set -e # Exit on any error
+set -e # Exit on any error
# Function to show usage
show_usage() {
- echo "Day-Specific Auto-Shutdown Setup for Arch Linux"
- echo "==============================================="
- echo "Usage: $0 [enable|disable|status]"
- echo ""
- echo "Commands:"
- echo " enable - Set up automatic shutdown with day-specific windows (default)"
- echo " disable - Remove automatic shutdown"
- echo " status - Show current status"
- echo ""
- echo "Shutdown Schedule:"
- echo " Monday-Wednesday: 21:00-05:00"
- echo " Thursday-Sunday: 22:00-05:00"
- echo ""
+ echo "Day-Specific Auto-Shutdown Setup for Arch Linux"
+ echo "==============================================="
+ echo "Usage: $0 [enable|disable|status]"
+ echo ""
+ echo "Commands:"
+ echo " enable - Set up automatic shutdown with day-specific windows (default)"
+ echo " disable - Remove automatic shutdown"
+ echo " status - Show current status"
+ echo ""
+ echo "Shutdown Schedule:"
+ echo " Monday-Wednesday: 21:00-05:00"
+ echo " Thursday-Sunday: 22:00-05:00"
+ echo ""
}
# Function to check and request sudo privileges
check_sudo() {
- if [[ $EUID -ne 0 ]]; then
- echo "This script requires sudo privileges to manage systemd services."
- echo "Requesting sudo access..."
- exec sudo "$0" "$@"
- fi
+ if [[ $EUID -ne 0 ]]; then
+ echo "This script requires sudo privileges to manage systemd services."
+ echo "Requesting sudo access..."
+ exec sudo "$0" "$@"
+ fi
}
# Get the actual user (even when running with sudo)
-if [[ -n "$SUDO_USER" ]]; then
- ACTUAL_USER="$SUDO_USER"
- USER_HOME="/home/$SUDO_USER"
+if [[ -n $SUDO_USER ]]; then
+ ACTUAL_USER="$SUDO_USER"
+ USER_HOME="/home/$SUDO_USER"
else
- ACTUAL_USER="$USER"
- USER_HOME="$HOME"
+ ACTUAL_USER="$USER"
+ USER_HOME="$HOME"
fi
# Function to disable and remove midnight shutdown
disable_midnight_shutdown() {
- echo "Disabling Day-Specific Auto-Shutdown"
- echo "===================================="
- echo "Current Date: $(date)"
- echo "User: $ACTUAL_USER"
- echo ""
-
- local timer_file="/etc/systemd/system/day-specific-shutdown.timer"
- local service_file="/etc/systemd/system/day-specific-shutdown.service"
- local script_file="/usr/local/bin/day-specific-shutdown-manager.sh"
- local check_script="/usr/local/bin/day-specific-shutdown-check.sh"
- local removed_files=()
-
- # Stop and disable timer if it exists
- if systemctl is-active day-specific-shutdown.timer &>/dev/null; then
- echo "Stopping day-specific-shutdown timer..."
- systemctl stop day-specific-shutdown.timer
- echo "✓ Timer stopped"
- fi
-
- if systemctl is-enabled day-specific-shutdown.timer &>/dev/null; then
- echo "Disabling day-specific-shutdown timer..."
- systemctl disable day-specific-shutdown.timer
- echo "✓ Timer disabled"
- fi
-
- # Remove timer file
- if [[ -f "$timer_file" ]]; then
- rm -f "$timer_file"
- removed_files+=("$timer_file")
- echo "✓ Removed timer file: $timer_file"
- fi
-
- # Remove service file
- if [[ -f "$service_file" ]]; then
- rm -f "$service_file"
- removed_files+=("$service_file")
- echo "✓ Removed service file: $service_file"
- fi
-
- # Remove management script
- if [[ -f "$script_file" ]]; then
- rm -f "$script_file"
- removed_files+=("$script_file")
- echo "✓ Removed management script: $script_file"
- fi
-
- # Remove check script
- if [[ -f "$check_script" ]]; then
- rm -f "$check_script"
- removed_files+=("$check_script")
- echo "✓ Removed check script: $check_script"
- fi
-
- # Reload systemd daemon
- if [[ ${#removed_files[@]} -gt 0 ]]; then
- echo "Reloading systemd daemon..."
- systemctl daemon-reload
- echo "✓ Systemd daemon reloaded"
- fi
-
- echo ""
- echo "============================================="
- echo "Day-Specific Auto-Shutdown Removal Complete"
- echo "============================================="
-
- if [[ ${#removed_files[@]} -gt 0 ]]; then
- echo "Removed files:"
- printf " %s\n" "${removed_files[@]}"
- echo ""
- echo "✓ Automatic day-specific shutdown has been completely disabled"
- echo "✓ Your PC will no longer shutdown automatically"
- else
- echo "No day-specific shutdown configuration was found to remove."
- fi
-
+ echo "Disabling Day-Specific Auto-Shutdown"
+ echo "===================================="
+ echo "Current Date: $(date)"
+ echo "User: $ACTUAL_USER"
+ echo ""
+
+ local timer_file="/etc/systemd/system/day-specific-shutdown.timer"
+ local service_file="/etc/systemd/system/day-specific-shutdown.service"
+ local script_file="/usr/local/bin/day-specific-shutdown-manager.sh"
+ local check_script="/usr/local/bin/day-specific-shutdown-check.sh"
+ local removed_files=()
+
+ # Stop and disable timer if it exists
+ if systemctl is-active day-specific-shutdown.timer &> /dev/null; then
+ echo "Stopping day-specific-shutdown timer..."
+ systemctl stop day-specific-shutdown.timer
+ echo "✓ Timer stopped"
+ fi
+
+ if systemctl is-enabled day-specific-shutdown.timer &> /dev/null; then
+ echo "Disabling day-specific-shutdown timer..."
+ systemctl disable day-specific-shutdown.timer
+ echo "✓ Timer disabled"
+ fi
+
+ # Remove timer file
+ if [[ -f $timer_file ]]; then
+ rm -f "$timer_file"
+ removed_files+=("$timer_file")
+ echo "✓ Removed timer file: $timer_file"
+ fi
+
+ # Remove service file
+ if [[ -f $service_file ]]; then
+ rm -f "$service_file"
+ removed_files+=("$service_file")
+ echo "✓ Removed service file: $service_file"
+ fi
+
+ # Remove management script
+ if [[ -f $script_file ]]; then
+ rm -f "$script_file"
+ removed_files+=("$script_file")
+ echo "✓ Removed management script: $script_file"
+ fi
+
+ # Remove check script
+ if [[ -f $check_script ]]; then
+ rm -f "$check_script"
+ removed_files+=("$check_script")
+ echo "✓ Removed check script: $check_script"
+ fi
+
+ # Reload systemd daemon
+ if [[ ${#removed_files[@]} -gt 0 ]]; then
+ echo "Reloading systemd daemon..."
+ systemctl daemon-reload
+ echo "✓ Systemd daemon reloaded"
+ fi
+
+ echo ""
+ echo "============================================="
+ echo "Day-Specific Auto-Shutdown Removal Complete"
+ echo "============================================="
+
+ if [[ ${#removed_files[@]} -gt 0 ]]; then
+ echo "Removed files:"
+ printf " %s\n" "${removed_files[@]}"
echo ""
+ echo "✓ Automatic day-specific shutdown has been completely disabled"
+ echo "✓ Your PC will no longer shutdown automatically"
+ else
+ echo "No day-specific shutdown configuration was found to remove."
+ fi
+
+ echo ""
}
# Function to show current status
show_current_status() {
- echo "Day-Specific Auto-Shutdown Status"
- echo "================================="
- echo "Current Date: $(date)"
- echo "User: $ACTUAL_USER"
- echo ""
-
- local timer_exists=false
- local service_exists=false
- local script_exists=false
-
- # Check if files exist
- if [[ -f "/etc/systemd/system/day-specific-shutdown.timer" ]]; then
- timer_exists=true
- echo "✓ Timer file exists"
+ echo "Day-Specific Auto-Shutdown Status"
+ echo "================================="
+ echo "Current Date: $(date)"
+ echo "User: $ACTUAL_USER"
+ echo ""
+
+ local timer_exists=false
+
+ # Check if files exist
+ if [[ -f "/etc/systemd/system/day-specific-shutdown.timer" ]]; then
+ timer_exists=true
+ echo "✓ Timer file exists"
+ else
+ echo "✗ Timer file missing"
+ fi
+
+ if [[ -f "/etc/systemd/system/day-specific-shutdown.service" ]]; then
+ echo "✓ Service file exists"
+ else
+ echo "✗ Service file missing"
+ fi
+
+ if [[ -f "/usr/local/bin/day-specific-shutdown-manager.sh" ]]; then
+ echo "✓ Management script exists"
+ else
+ echo "✗ Management script missing"
+ fi
+
+ echo ""
+
+ # Check systemd status
+ if $timer_exists; then
+ if systemctl is-enabled day-specific-shutdown.timer &> /dev/null; then
+ echo "✓ Timer is enabled"
+ if systemctl is-active day-specific-shutdown.timer &> /dev/null; then
+ echo "✓ Timer is active"
+ echo ""
+ echo "Next scheduled shutdown check:"
+ systemctl list-timers day-specific-shutdown.timer --no-pager 2> /dev/null | grep day-specific-shutdown || echo "Timer information not available"
+ else
+ echo "✗ Timer is not active"
+ fi
else
- echo "✗ Timer file missing"
+ echo "✗ Timer is not enabled"
fi
-
- if [[ -f "/etc/systemd/system/day-specific-shutdown.service" ]]; then
- service_exists=true
- echo "✓ Service file exists"
- else
- echo "✗ Service file missing"
- fi
-
- if [[ -f "/usr/local/bin/day-specific-shutdown-manager.sh" ]]; then
- script_exists=true
- echo "✓ Management script exists"
- else
- echo "✗ Management script missing"
- fi
-
- echo ""
-
- # Check systemd status
- if $timer_exists; then
- if systemctl is-enabled day-specific-shutdown.timer &>/dev/null; then
- echo "✓ Timer is enabled"
- if systemctl is-active day-specific-shutdown.timer &>/dev/null; then
- echo "✓ Timer is active"
- echo ""
- echo "Next scheduled shutdown check:"
- systemctl list-timers day-specific-shutdown.timer --no-pager 2>/dev/null | grep day-specific-shutdown || echo "Timer information not available"
- else
- echo "✗ Timer is not active"
- fi
- else
- echo "✗ Timer is not enabled"
- fi
- else
- echo "Status: NOT CONFIGURED"
- fi
-
- echo ""
- echo "Shutdown Schedule:"
- echo " Monday-Wednesday: 21:00-05:00"
- echo " Thursday-Sunday: 22:00-05:00"
- echo ""
+ else
+ echo "Status: NOT CONFIGURED"
+ fi
+
+ echo ""
+ echo "Shutdown Schedule:"
+ echo " Monday-Wednesday: 21:00-05:00"
+ echo " Thursday-Sunday: 22:00-05:00"
+ echo ""
}
# Function to create the shutdown service
create_shutdown_service() {
- echo ""
- echo "1. Creating Systemd Shutdown Service..."
- echo "======================================"
-
- local service_file="/etc/systemd/system/day-specific-shutdown.service"
-
- cat > "$service_file" << 'EOF'
+ echo ""
+ echo "1. Creating Systemd Shutdown Service..."
+ echo "======================================"
+
+ local service_file="/etc/systemd/system/day-specific-shutdown.service"
+
+ cat > "$service_file" << 'EOF'
[Unit]
Description=Automatic PC shutdown with day-specific time windows
DefaultDependencies=false
@@ -205,18 +201,18 @@ StandardOutput=journal
StandardError=journal
EOF
- echo "✓ Created systemd service: $service_file"
+ echo "✓ Created systemd service: $service_file"
}
# Function to create the shutdown timer
create_shutdown_timer() {
- echo ""
- echo "2. Creating Systemd Shutdown Timer..."
- echo "==================================="
-
- local timer_file="/etc/systemd/system/day-specific-shutdown.timer"
-
- cat > "$timer_file" << 'EOF'
+ echo ""
+ echo "2. Creating Systemd Shutdown Timer..."
+ echo "==================================="
+
+ local timer_file="/etc/systemd/system/day-specific-shutdown.timer"
+
+ cat > "$timer_file" << 'EOF'
[Unit]
Description=Timer for automatic PC shutdown with day-specific windows
Requires=day-specific-shutdown.service
@@ -248,18 +244,18 @@ RandomizedDelaySec=0
WantedBy=timers.target
EOF
- echo "✓ Created systemd timer: $timer_file"
+ echo "✓ Created systemd timer: $timer_file"
}
# Function to create management script
create_management_script() {
- echo ""
- echo "3. Creating Management Script..."
- echo "=============================="
-
- local script_file="/usr/local/bin/day-specific-shutdown-manager.sh"
-
- cat > "$script_file" << 'EOF'
+ echo ""
+ echo "3. Creating Management Script..."
+ echo "=============================="
+
+ local script_file="/usr/local/bin/day-specific-shutdown-manager.sh"
+
+ cat > "$script_file" << 'EOF'
#!/bin/bash
# Day-Specific Auto-Shutdown Manager
# Provides easy management of the day-specific shutdown feature
@@ -322,19 +318,19 @@ case "$1" in
esac
EOF
- chmod +x "$script_file"
- echo "✓ Created management script: $script_file"
+ chmod +x "$script_file"
+ echo "✓ Created management script: $script_file"
}
# Function to create smart shutdown check script
create_shutdown_check_script() {
- echo ""
- echo "4. Creating Smart Shutdown Check Script..."
- echo "========================================"
-
- local check_script="/usr/local/bin/day-specific-shutdown-check.sh"
-
- cat > "$check_script" << 'EOF'
+ echo ""
+ echo "4. Creating Smart Shutdown Check Script..."
+ echo "========================================"
+
+ local check_script="/usr/local/bin/day-specific-shutdown-check.sh"
+
+ cat > "$check_script" << 'EOF'
#!/bin/bash
# Smart day-specific shutdown check script
# Different shutdown windows based on day of week:
@@ -401,206 +397,206 @@ else
fi
EOF
- chmod +x "$check_script"
- echo "✓ Created smart shutdown check script: $check_script"
+ chmod +x "$check_script"
+ echo "✓ Created smart shutdown check script: $check_script"
}
# Function to enable the timer
enable_timer() {
- echo ""
- echo "5. Enabling Shutdown Timer..."
- echo "============================"
-
- # Reload systemd daemon
- systemctl daemon-reload
- echo "✓ Reloaded systemd daemon"
-
- # Enable the timer
- systemctl enable day-specific-shutdown.timer
- echo "✓ Enabled day-specific-shutdown timer"
-
- # Start the timer
- systemctl start day-specific-shutdown.timer
- echo "✓ Started day-specific-shutdown timer"
+ echo ""
+ echo "5. Enabling Shutdown Timer..."
+ echo "============================"
+
+ # Reload systemd daemon
+ systemctl daemon-reload
+ echo "✓ Reloaded systemd daemon"
+
+ # Enable the timer
+ systemctl enable day-specific-shutdown.timer
+ echo "✓ Enabled day-specific-shutdown timer"
+
+ # Start the timer
+ systemctl start day-specific-shutdown.timer
+ echo "✓ Started day-specific-shutdown timer"
}
# Function to test the setup
test_setup() {
- echo ""
- echo "6. Testing Setup..."
- echo "=================="
-
- echo "Service files:"
- if [[ -f "/etc/systemd/system/day-specific-shutdown.service" ]]; then
- echo "✓ Service file exists"
- else
- echo "✗ Service file missing"
- fi
-
- if [[ -f "/etc/systemd/system/day-specific-shutdown.timer" ]]; then
- echo "✓ Timer file exists"
- else
- echo "✗ Timer file missing"
- fi
-
- echo ""
- echo "Timer status:"
- if systemctl is-enabled day-specific-shutdown.timer &>/dev/null; then
- echo "✓ Timer is enabled"
- else
- echo "✗ Timer is not enabled"
- fi
-
- if systemctl is-active day-specific-shutdown.timer &>/dev/null; then
- echo "✓ Timer is active"
- else
- echo "✗ Timer is not active"
- fi
-
- echo ""
- echo "Next scheduled checks:"
- systemctl list-timers day-specific-shutdown.timer --no-pager 2>/dev/null | head -5 | grep day-specific-shutdown || echo "Timer information not available"
+ echo ""
+ echo "6. Testing Setup..."
+ echo "=================="
+
+ echo "Service files:"
+ if [[ -f "/etc/systemd/system/day-specific-shutdown.service" ]]; then
+ echo "✓ Service file exists"
+ else
+ echo "✗ Service file missing"
+ fi
+
+ if [[ -f "/etc/systemd/system/day-specific-shutdown.timer" ]]; then
+ echo "✓ Timer file exists"
+ else
+ echo "✗ Timer file missing"
+ fi
+
+ echo ""
+ echo "Timer status:"
+ if systemctl is-enabled day-specific-shutdown.timer &> /dev/null; then
+ echo "✓ Timer is enabled"
+ else
+ echo "✗ Timer is not enabled"
+ fi
+
+ if systemctl is-active day-specific-shutdown.timer &> /dev/null; then
+ echo "✓ Timer is active"
+ else
+ echo "✗ Timer is not active"
+ fi
+
+ echo ""
+ echo "Next scheduled checks:"
+ systemctl list-timers day-specific-shutdown.timer --no-pager 2> /dev/null | head -5 | grep day-specific-shutdown || echo "Timer information not available"
}
# Function to show final instructions
show_instructions() {
- echo ""
- echo "================================================="
- echo "Day-Specific Auto-Shutdown Setup Complete"
- echo "================================================="
- echo "Summary:"
- echo "✓ Systemd service created (/etc/systemd/system/day-specific-shutdown.service)"
- echo "✓ Systemd timer created (/etc/systemd/system/day-specific-shutdown.timer)"
- echo "✓ Management script created (/usr/local/bin/day-specific-shutdown-manager.sh)"
- echo "✓ Smart check script created (/usr/local/bin/day-specific-shutdown-check.sh)"
- echo "✓ Timer enabled and started"
- echo ""
- echo "Shutdown Schedule:"
- echo " Monday-Wednesday: 21:00-05:00 (9:00 PM to 5:00 AM)"
- echo " Thursday-Sunday: 22:00-05:00 (10:00 PM to 5:00 AM)"
- echo ""
- echo "Management commands:"
- echo " sudo day-specific-shutdown-manager.sh status - Check status"
- echo " sudo day-specific-shutdown-manager.sh logs - View shutdown logs"
- echo ""
- echo "How it works:"
- echo "• Timer checks every 30 minutes during potential shutdown windows"
- echo "• Smart logic determines shutdown eligibility based on day and time"
- echo "• Prevents accidental shutdowns outside designated time windows"
- echo ""
- echo "WARNING: This will automatically shutdown your PC during designated hours."
- echo "Make sure to save your work before the shutdown windows!"
- echo ""
+ echo ""
+ echo "================================================="
+ echo "Day-Specific Auto-Shutdown Setup Complete"
+ echo "================================================="
+ echo "Summary:"
+ echo "✓ Systemd service created (/etc/systemd/system/day-specific-shutdown.service)"
+ echo "✓ Systemd timer created (/etc/systemd/system/day-specific-shutdown.timer)"
+ echo "✓ Management script created (/usr/local/bin/day-specific-shutdown-manager.sh)"
+ echo "✓ Smart check script created (/usr/local/bin/day-specific-shutdown-check.sh)"
+ echo "✓ Timer enabled and started"
+ echo ""
+ echo "Shutdown Schedule:"
+ echo " Monday-Wednesday: 21:00-05:00 (9:00 PM to 5:00 AM)"
+ echo " Thursday-Sunday: 22:00-05:00 (10:00 PM to 5:00 AM)"
+ echo ""
+ echo "Management commands:"
+ echo " sudo day-specific-shutdown-manager.sh status - Check status"
+ echo " sudo day-specific-shutdown-manager.sh logs - View shutdown logs"
+ echo ""
+ echo "How it works:"
+ echo "• Timer checks every 30 minutes during potential shutdown windows"
+ echo "• Smart logic determines shutdown eligibility based on day and time"
+ echo "• Prevents accidental shutdowns outside designated time windows"
+ echo ""
+ echo "WARNING: This will automatically shutdown your PC during designated hours."
+ echo "Make sure to save your work before the shutdown windows!"
+ echo ""
}
# Function to prompt for confirmation
confirm_setup() {
- echo ""
- echo "WARNING: Day-Specific Auto-Shutdown Confirmation"
- echo "==============================================="
- echo "This will set up your PC to automatically shutdown during specific time windows."
- echo ""
- echo "Shutdown Schedule:"
- echo " Monday-Wednesday: 21:00-05:00 (9:00 PM to 5:00 AM)"
- echo " Thursday-Sunday: 22:00-05:00 (10:00 PM to 5:00 AM)"
- echo ""
- echo "Important considerations:"
- echo "- Any unsaved work will be lost during shutdown windows"
- echo "- Running processes will be terminated"
- echo "- Downloads/uploads in progress will be interrupted"
- echo "- You'll need to manually power on your PC each day"
- echo "- Timer checks every 30 minutes during potential shutdown windows"
- echo ""
- read -p "Do you want to proceed? (y/N): " confirm
-
- case "$confirm" in
- [yY]|[yY][eE][sS])
- echo "Proceeding with setup..."
- return 0
- ;;
- *)
- echo "Setup cancelled."
- exit 0
- ;;
- esac
+ echo ""
+ echo "WARNING: Day-Specific Auto-Shutdown Confirmation"
+ echo "==============================================="
+ echo "This will set up your PC to automatically shutdown during specific time windows."
+ echo ""
+ echo "Shutdown Schedule:"
+ echo " Monday-Wednesday: 21:00-05:00 (9:00 PM to 5:00 AM)"
+ echo " Thursday-Sunday: 22:00-05:00 (10:00 PM to 5:00 AM)"
+ echo ""
+ echo "Important considerations:"
+ echo "- Any unsaved work will be lost during shutdown windows"
+ echo "- Running processes will be terminated"
+ echo "- Downloads/uploads in progress will be interrupted"
+ echo "- You'll need to manually power on your PC each day"
+ echo "- Timer checks every 30 minutes during potential shutdown windows"
+ echo ""
+ read -r -p "Do you want to proceed? (y/N): " confirm
+
+ case "$confirm" in
+ [yY] | [yY][eE][sS])
+ echo "Proceeding with setup..."
+ return 0
+ ;;
+ *)
+ echo "Setup cancelled."
+ exit 0
+ ;;
+ esac
}
# Function to confirm disable
confirm_disable() {
- echo ""
- echo "Disable Day-Specific Auto-Shutdown Confirmation"
- echo "==============================================="
- echo "This will completely remove the automatic day-specific shutdown configuration."
- echo ""
- echo "After disabling:"
- echo "- Your PC will no longer shutdown automatically during any time windows"
- echo "- All related systemd services and timers will be removed"
- echo "- The management and check scripts will be deleted"
- echo ""
- read -p "Do you want to proceed with disabling? (y/N): " confirm
-
- case "$confirm" in
- [yY]|[yY][eE][sS])
- echo "Proceeding with disable..."
- return 0
- ;;
- *)
- echo "Disable cancelled."
- exit 0
- ;;
- esac
+ echo ""
+ echo "Disable Day-Specific Auto-Shutdown Confirmation"
+ echo "==============================================="
+ echo "This will completely remove the automatic day-specific shutdown configuration."
+ echo ""
+ echo "After disabling:"
+ echo "- Your PC will no longer shutdown automatically during any time windows"
+ echo "- All related systemd services and timers will be removed"
+ echo "- The management and check scripts will be deleted"
+ echo ""
+ read -r -p "Do you want to proceed with disabling? (y/N): " confirm
+
+ case "$confirm" in
+ [yY] | [yY][eE][sS])
+ echo "Proceeding with disable..."
+ return 0
+ ;;
+ *)
+ echo "Disable cancelled."
+ exit 0
+ ;;
+ esac
}
# Main execution flow for enable
enable_midnight_shutdown() {
- echo "Day-Specific Auto-Shutdown Setup for Arch Linux"
- echo "==============================================="
- echo "Current Date: $(date)"
- echo "User: $ACTUAL_USER"
- echo "Target user: $ACTUAL_USER"
- echo "User home: $USER_HOME"
-
- # Confirm setup
- confirm_setup
-
- # Create systemd files
- create_shutdown_service
- create_shutdown_timer
- create_management_script
- create_shutdown_check_script
-
- # Enable and start timer
- enable_timer
-
- # Test setup
- test_setup
-
- # Show instructions
- show_instructions
+ echo "Day-Specific Auto-Shutdown Setup for Arch Linux"
+ echo "==============================================="
+ echo "Current Date: $(date)"
+ echo "User: $ACTUAL_USER"
+ echo "Target user: $ACTUAL_USER"
+ echo "User home: $USER_HOME"
+
+ # Confirm setup
+ confirm_setup
+
+ # Create systemd files
+ create_shutdown_service
+ create_shutdown_timer
+ create_management_script
+ create_shutdown_check_script
+
+ # Enable and start timer
+ enable_timer
+
+ # Test setup
+ test_setup
+
+ # Show instructions
+ show_instructions
}
# Parse command line arguments
case "${1:-enable}" in
- "enable")
- check_sudo "$@"
- enable_midnight_shutdown
- ;;
- "disable")
- check_sudo "$@"
- confirm_disable
- disable_midnight_shutdown
- ;;
- "status")
- check_sudo "$@"
- show_current_status
- ;;
- "help"|"-h"|"--help")
- show_usage
- ;;
- *)
- echo "Error: Unknown command '$1'"
- echo ""
- show_usage
- exit 1
- ;;
+ "enable")
+ check_sudo "$@"
+ enable_midnight_shutdown
+ ;;
+ "disable")
+ check_sudo "$@"
+ confirm_disable
+ disable_midnight_shutdown
+ ;;
+ "status")
+ check_sudo "$@"
+ show_current_status
+ ;;
+ "help" | "-h" | "--help")
+ show_usage
+ ;;
+ *)
+ echo "Error: Unknown command '$1'"
+ echo ""
+ show_usage
+ exit 1
+ ;;
esac
diff --git a/scripts/digital_wellbeing/setup_pc_startup_monitor.sh b/scripts/digital_wellbeing/setup_pc_startup_monitor.sh
index 98c56fe..b94e8ad 100755
--- a/scripts/digital_wellbeing/setup_pc_startup_monitor.sh
+++ b/scripts/digital_wellbeing/setup_pc_startup_monitor.sh
@@ -3,59 +3,59 @@
# Checks if PC was turned on between 5AM-8AM on Monday, Friday, Saturday, Sunday
# Handles sudo privileges automatically
-set -e # Exit on any error
+set -e # Exit on any error
# Default to non-interactive mode
INTERACTIVE_MODE=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
- case $1 in
- -i|--interactive)
- INTERACTIVE_MODE=true
- shift
- ;;
- -h|--help)
- echo "Usage: $0 [OPTIONS]"
- echo "Options:"
- echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
- echo " -h, --help Show this help message"
- exit 0
- ;;
- *)
- echo "Unknown option: $1"
- echo "Use -h or --help for usage information"
- exit 1
- ;;
- esac
+ case $1 in
+ -i | --interactive)
+ INTERACTIVE_MODE=true
+ shift
+ ;;
+ -h | --help)
+ echo "Usage: $0 [OPTIONS]"
+ echo "Options:"
+ echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
+ echo " -h, --help Show this help message"
+ exit 0
+ ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Use -h or --help for usage information"
+ exit 1
+ ;;
+ esac
done
echo "PC Startup Time Monitor for Arch Linux"
echo "======================================"
echo "Current Date: $(date)"
echo "User: ${SUDO_USER:-$USER}"
-if [[ "$INTERACTIVE_MODE" == "true" ]]; then
- echo "Mode: Interactive (prompts enabled)"
+if [[ $INTERACTIVE_MODE == "true" ]]; then
+ echo "Mode: Interactive (prompts enabled)"
else
- echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
+ echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
fi
# Function to check and request sudo privileges
check_sudo() {
- if [[ $EUID -ne 0 ]]; then
- echo "This script requires sudo privileges to access system logs and create services."
- echo "Requesting sudo access..."
- exec sudo "$0" "$@"
- fi
+ if [[ $EUID -ne 0 ]]; then
+ echo "This script requires sudo privileges to access system logs and create services."
+ echo "Requesting sudo access..."
+ exec sudo "$0" "$@"
+ fi
}
# Get the actual user (even when running with sudo)
-if [[ -n "$SUDO_USER" ]]; then
- ACTUAL_USER="$SUDO_USER"
- USER_HOME="/home/$SUDO_USER"
+if [[ -n $SUDO_USER ]]; then
+ ACTUAL_USER="$SUDO_USER"
+ USER_HOME="/home/$SUDO_USER"
else
- ACTUAL_USER="$USER"
- USER_HOME="$HOME"
+ ACTUAL_USER="$USER"
+ USER_HOME="$HOME"
fi
echo "Target user: $ACTUAL_USER"
@@ -63,138 +63,147 @@ echo "User home: $USER_HOME"
# Function to check if today is a monitored day
is_monitored_day() {
- local day_of_week=$(date +%u) # 1=Monday, 7=Sunday
-
- # Check if today is Monday (1), Friday (5), Saturday (6), or Sunday (7)
- if [[ "$day_of_week" == "1" ]] || [[ "$day_of_week" == "5" ]] || [[ "$day_of_week" == "6" ]] || [[ "$day_of_week" == "7" ]]; then
- return 0 # Yes, it's a monitored day
- else
- return 1 # No, it's not a monitored day
- fi
+ local day_of_week
+ day_of_week=$(date +%u) # 1=Monday, 7=Sunday
+
+ # Check if today is Monday (1), Friday (5), Saturday (6), or Sunday (7)
+ if [[ $day_of_week == "1" ]] || [[ $day_of_week == "5" ]] || [[ $day_of_week == "6" ]] || [[ $day_of_week == "7" ]]; then
+ return 0 # Yes, it's a monitored day
+ else
+ return 1 # No, it's not a monitored day
+ fi
}
# Function to check if current time is between 5AM and 8AM
is_current_time_in_window() {
- local current_hour=$(date +%H)
- local current_hour_num=$((10#$current_hour)) # Convert to decimal to avoid octal issues
-
- if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
- return 0 # Yes, current time is in the 5AM-8AM window
- else
- return 1 # No, current time is outside the window
- fi
+ local current_hour current_hour_num
+ current_hour=$(date +%H)
+ current_hour_num=$((10#$current_hour)) # Convert to decimal to avoid octal issues
+
+ if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
+ return 0 # Yes, current time is in the 5AM-8AM window
+ else
+ return 1 # No, current time is outside the window
+ fi
}
# Function to check if PC was booted between 5AM-8AM today
was_booted_in_window_today() {
- local today=$(date +%Y-%m-%d)
- local boot_time=""
-
- # Get the last boot time using multiple methods for reliability
- if command -v uptime &>/dev/null; then
- # Method 1: Calculate boot time from uptime
- local uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
- if [[ $uptime_seconds -gt 0 ]]; then
- boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
- fi
+ local today boot_time
+ today=$(date +%Y-%m-%d)
+ boot_time=""
+
+ # Get the last boot time using multiple methods for reliability
+ if command -v uptime &> /dev/null; then
+ # Method 1: Calculate boot time from uptime
+ local uptime_seconds
+ uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0")
+ if [[ $uptime_seconds -gt 0 ]]; then
+ boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
fi
-
- # Method 2: Use systemd if available (fallback)
- if [[ -z "$boot_time" ]] && command -v systemctl &>/dev/null; then
- boot_time=$(systemd-analyze | grep "Startup finished" | sed -n 's/.*finished in .* = \(.*\)$/\1/p' 2>/dev/null || echo "")
- if [[ -n "$boot_time" ]]; then
- # This gives us relative time, need to calculate absolute time
- local current_time=$(date +%s)
- local uptime_sec=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
- boot_time=$(date -d "@$((current_time - uptime_sec))" +"%Y-%m-%d %H:%M:%S")
- fi
+ fi
+
+ # Method 2: Use systemd if available (fallback)
+ if [[ -z $boot_time ]] && command -v systemctl &> /dev/null; then
+ boot_time=$(systemd-analyze | grep "Startup finished" | sed -n 's/.*finished in .* = \(.*\)$/\1/p' 2> /dev/null || echo "")
+ if [[ -n $boot_time ]]; then
+ # This gives us relative time, need to calculate absolute time
+ local current_time uptime_sec
+ current_time=$(date +%s)
+ uptime_sec=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0")
+ boot_time=$(date -d "@$((current_time - uptime_sec))" +"%Y-%m-%d %H:%M:%S")
fi
-
- # Method 3: Use who -b (fallback)
- if [[ -z "$boot_time" ]] && command -v who &>/dev/null; then
- boot_time=$(who -b | awk '{print $3, $4}' 2>/dev/null || echo "")
- if [[ -n "$boot_time" ]]; then
- boot_time="$today $boot_time"
- fi
- fi
-
- # Method 4: Use /proc/uptime as final fallback
- if [[ -z "$boot_time" ]]; then
- local uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
- boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
- fi
-
- echo "Boot time detected: $boot_time"
-
- # Check if boot time is from today
- local boot_date=$(echo "$boot_time" | cut -d' ' -f1)
- if [[ "$boot_date" != "$today" ]]; then
- echo "PC was not booted today (boot date: $boot_date, today: $today)"
- return 1 # Not booted today
- fi
-
- # Extract hour from boot time
- local boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1)
- local boot_hour_num=$((10#$boot_hour)) # Convert to decimal
-
- echo "Boot hour: $boot_hour_num"
-
- # Check if boot time was between 5AM (5) and 8AM (7, since we want before 8AM)
- if [[ $boot_hour_num -ge 5 ]] && [[ $boot_hour_num -lt 8 ]]; then
- echo "PC was booted in the expected window (5AM-8AM)"
- return 0 # Yes, booted in window
- else
- echo "PC was NOT booted in the expected window (5AM-8AM)"
- return 1 # No, not booted in window
+ fi
+
+ # Method 3: Use who -b (fallback)
+ if [[ -z $boot_time ]] && command -v who &> /dev/null; then
+ boot_time=$(who -b | awk '{print $3, $4}' 2> /dev/null || echo "")
+ if [[ -n $boot_time ]]; then
+ boot_time="$today $boot_time"
fi
+ fi
+
+ # Method 4: Use /proc/uptime as final fallback
+ if [[ -z $boot_time ]]; then
+ local uptime_seconds
+ uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0")
+ boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
+ fi
+
+ echo "Boot time detected: $boot_time"
+
+ # Check if boot time is from today
+ local boot_date
+ boot_date=$(echo "$boot_time" | cut -d' ' -f1)
+ if [[ $boot_date != "$today" ]]; then
+ echo "PC was not booted today (boot date: $boot_date, today: $today)"
+ return 1 # Not booted today
+ fi
+
+ # Extract hour from boot time
+ local boot_hour boot_hour_num
+ boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1)
+ boot_hour_num=$((10#$boot_hour)) # Convert to decimal
+
+ echo "Boot hour: $boot_hour_num"
+
+ # Check if boot time was between 5AM (5) and 8AM (7, since we want before 8AM)
+ if [[ $boot_hour_num -ge 5 ]] && [[ $boot_hour_num -lt 8 ]]; then
+ echo "PC was booted in the expected window (5AM-8AM)"
+ return 0 # Yes, booted in window
+ else
+ echo "PC was NOT booted in the expected window (5AM-8AM)"
+ return 1 # No, not booted in window
+ fi
}
# Function to show notification/warning
show_startup_warning() {
- local day_name=$(date +%A)
- local current_time=$(date +"%H:%M")
- local today=$(date +%Y-%m-%d)
-
- echo ""
- echo "⚠️ PC STARTUP TIME WARNING"
- echo "=========================="
- echo "Date: $today ($day_name)"
- echo "Current time: $current_time"
- echo ""
- echo "This PC was expected to be turned on between 5:00 AM and 8:00 AM today,"
- echo "but it was not turned on during that time window."
- echo ""
- echo "Expected: Monday, Friday, Saturday, Sunday between 5:00-8:00 AM"
- echo "Actual: PC was turned on outside the expected window"
- echo ""
-
- # Log the warning
- logger -t pc-startup-monitor "WARNING: PC was not turned on during expected window (5AM-8AM) on $day_name $today"
-
- # Try to show desktop notification if possible
- if command -v notify-send &>/dev/null && [[ -n "$DISPLAY" ]]; then
- if [[ $EUID -eq 0 ]]; then
- # Running as root, send notification as user
- sudo -u "$ACTUAL_USER" DISPLAY="$DISPLAY" notify-send "PC Startup Warning" "PC was not turned on between 5AM-8AM as expected on $day_name" --urgency=normal --expire-time=10000 2>/dev/null || true
- else
- notify-send "PC Startup Warning" "PC was not turned on between 5AM-8AM as expected on $day_name" --urgency=normal --expire-time=10000 2>/dev/null || true
- fi
+ local day_name current_time today
+ day_name=$(date +%A)
+ current_time=$(date +"%H:%M")
+ today=$(date +%Y-%m-%d)
+
+ echo ""
+ echo "⚠️ PC STARTUP TIME WARNING"
+ echo "=========================="
+ echo "Date: $today ($day_name)"
+ echo "Current time: $current_time"
+ echo ""
+ echo "This PC was expected to be turned on between 5:00 AM and 8:00 AM today,"
+ echo "but it was not turned on during that time window."
+ echo ""
+ echo "Expected: Monday, Friday, Saturday, Sunday between 5:00-8:00 AM"
+ echo "Actual: PC was turned on outside the expected window"
+ echo ""
+
+ # Log the warning
+ logger -t pc-startup-monitor "WARNING: PC was not turned on during expected window (5AM-8AM) on $day_name $today"
+
+ # Try to show desktop notification if possible
+ if command -v notify-send &> /dev/null && [[ -n $DISPLAY ]]; then
+ if [[ $EUID -eq 0 ]]; then
+ # Running as root, send notification as user
+ sudo -u "$ACTUAL_USER" DISPLAY="$DISPLAY" notify-send "PC Startup Warning" "PC was not turned on between 5AM-8AM as expected on $day_name" --urgency=normal --expire-time=10000 2> /dev/null || true
+ else
+ notify-send "PC Startup Warning" "PC was not turned on between 5AM-8AM as expected on $day_name" --urgency=normal --expire-time=10000 2> /dev/null || true
fi
-
- echo "This warning has been logged to the system journal."
- echo "You can view startup logs with: journalctl -t pc-startup-monitor"
- echo ""
+ fi
+
+ echo "This warning has been logged to the system journal."
+ echo "You can view startup logs with: journalctl -t pc-startup-monitor"
+ echo ""
}
# Function to create the monitoring service
create_monitoring_service() {
- echo ""
- echo "1. Creating PC Startup Monitor Service..."
- echo "======================================="
-
- local service_file="/etc/systemd/system/pc-startup-monitor.service"
-
- cat > "$service_file" << 'EOF'
+ echo ""
+ echo "1. Creating PC Startup Monitor Service..."
+ echo "======================================="
+
+ local service_file="/etc/systemd/system/pc-startup-monitor.service"
+
+ cat > "$service_file" << 'EOF'
[Unit]
Description=PC Startup Time Monitor
After=multi-user.target
@@ -211,18 +220,18 @@ RemainAfterExit=true
WantedBy=multi-user.target
EOF
- echo "✓ Created monitoring service: $service_file"
+ echo "✓ Created monitoring service: $service_file"
}
# Function to create the monitoring timer
create_monitoring_timer() {
- echo ""
- echo "2. Creating PC Startup Monitor Timer..."
- echo "====================================="
-
- local timer_file="/etc/systemd/system/pc-startup-monitor.timer"
-
- cat > "$timer_file" << 'EOF'
+ echo ""
+ echo "2. Creating PC Startup Monitor Timer..."
+ echo "====================================="
+
+ local timer_file="/etc/systemd/system/pc-startup-monitor.timer"
+
+ cat > "$timer_file" << 'EOF'
[Unit]
Description=Timer for PC startup monitoring
Requires=pc-startup-monitor.service
@@ -236,25 +245,26 @@ AccuracySec=1m
WantedBy=timers.target
EOF
- echo "✓ Created monitoring timer: $timer_file"
+ echo "✓ Created monitoring timer: $timer_file"
}
# Function to create the main monitoring script
create_monitoring_script() {
- echo ""
- echo "3. Creating PC Startup Monitor Script..."
- echo "======================================"
-
- local script_file="/usr/local/bin/pc-startup-check.sh"
-
- cat > "$script_file" << 'EOF'
+ echo ""
+ echo "3. Creating PC Startup Monitor Script..."
+ echo "======================================"
+
+ local script_file="/usr/local/bin/pc-startup-check.sh"
+
+ cat > "$script_file" << 'EOF'
#!/bin/bash
# PC Startup Time Monitor Check Script
# Monitors if PC was turned on during expected hours on specific days
# Function to check if today is a monitored day
is_monitored_day() {
- local day_of_week=$(date +%u) # 1=Monday, 7=Sunday
+ local day_of_week
+ day_of_week=$(date +%u) # 1=Monday, 7=Sunday
# Check if today is Monday (1), Friday (5), Saturday (6), or Sunday (7)
if [[ "$day_of_week" == "1" ]] || [[ "$day_of_week" == "5" ]] || [[ "$day_of_week" == "6" ]] || [[ "$day_of_week" == "7" ]]; then
@@ -266,8 +276,9 @@ is_monitored_day() {
# Function to check if current time is between 5AM and 8AM
is_current_time_in_window() {
- local current_hour=$(date +%H)
- local current_hour_num=$((10#$current_hour))
+ local current_hour current_hour_num
+ current_hour=$(date +%H)
+ current_hour_num=$((10#$current_hour))
if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
return 0 # Yes, current time is in the 5AM-8AM window
@@ -278,21 +289,25 @@ is_current_time_in_window() {
# Function to check if PC was booted between 5AM-8AM today
was_booted_in_window_today() {
- local today=$(date +%Y-%m-%d)
+ local today boot_time
+ today=$(date +%Y-%m-%d)
- # Calculate boot time from uptime
- local uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
- local boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
+ # Calculate boot time from uptime
+ local uptime_seconds
+ uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
+ boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
- # Check if boot time is from today
- local boot_date=$(echo "$boot_time" | cut -d' ' -f1)
+ # Check if boot time is from today
+ local boot_date
+ boot_date=$(echo "$boot_time" | cut -d' ' -f1)
if [[ "$boot_date" != "$today" ]]; then
return 1 # Not booted today
fi
# Extract hour from boot time
- local boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1)
- local boot_hour_num=$((10#$boot_hour))
+ local boot_hour boot_hour_num
+ boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1)
+ boot_hour_num=$((10#$boot_hour))
# Check if boot time was between 5AM and 8AM
if [[ $boot_hour_num -ge 5 ]] && [[ $boot_hour_num -lt 8 ]]; then
@@ -304,9 +319,10 @@ was_booted_in_window_today() {
# Function to show notification/warning
show_startup_warning() {
- local day_name=$(date +%A)
- local current_time=$(date +"%H:%M")
- local today=$(date +%Y-%m-%d)
+ local day_name current_time today
+ day_name=$(date +%A)
+ current_time=$(date +"%H:%M")
+ today=$(date +%Y-%m-%d)
echo "⚠️ PC STARTUP TIME WARNING"
echo "Date: $today ($day_name)"
@@ -346,19 +362,19 @@ else
fi
EOF
- chmod +x "$script_file"
- echo "✓ Created monitoring script: $script_file"
+ chmod +x "$script_file"
+ echo "✓ Created monitoring script: $script_file"
}
# Function to create management script
create_management_script() {
- echo ""
- echo "4. Creating Management Script..."
- echo "=============================="
-
- local script_file="/usr/local/bin/pc-startup-monitor-manager.sh"
-
- cat > "$script_file" << 'EOF'
+ echo ""
+ echo "4. Creating Management Script..."
+ echo "=============================="
+
+ local script_file="/usr/local/bin/pc-startup-monitor-manager.sh"
+
+ cat > "$script_file" << 'EOF'
#!/bin/bash
# PC Startup Monitor Manager
# Provides easy management of the PC startup monitoring feature
@@ -421,150 +437,150 @@ case "$1" in
esac
EOF
- chmod +x "$script_file"
- echo "✓ Created management script: $script_file"
+ chmod +x "$script_file"
+ echo "✓ Created management script: $script_file"
}
# Function to enable the services
enable_services() {
- echo ""
- echo "5. Enabling PC Startup Monitor..."
- echo "==============================="
-
- # Reload systemd daemon
- systemctl daemon-reload
- echo "✓ Reloaded systemd daemon"
-
- # Enable and start the timer
- systemctl enable pc-startup-monitor.timer
- echo "✓ Enabled pc-startup-monitor timer"
-
- systemctl start pc-startup-monitor.timer
- echo "✓ Started pc-startup-monitor timer"
+ echo ""
+ echo "5. Enabling PC Startup Monitor..."
+ echo "==============================="
+
+ # Reload systemd daemon
+ systemctl daemon-reload
+ echo "✓ Reloaded systemd daemon"
+
+ # Enable and start the timer
+ systemctl enable pc-startup-monitor.timer
+ echo "✓ Enabled pc-startup-monitor timer"
+
+ systemctl start pc-startup-monitor.timer
+ echo "✓ Started pc-startup-monitor timer"
}
# Function to test the setup
test_setup() {
- echo ""
- echo "6. Testing Setup..."
- echo "=================="
-
- echo "Service files:"
- if [[ -f "/etc/systemd/system/pc-startup-monitor.service" ]]; then
- echo "✓ Service file exists"
- else
- echo "✗ Service file missing"
- fi
-
- if [[ -f "/etc/systemd/system/pc-startup-monitor.timer" ]]; then
- echo "✓ Timer file exists"
- else
- echo "✗ Timer file missing"
- fi
-
- echo ""
- echo "Timer status:"
- if systemctl is-enabled pc-startup-monitor.timer &>/dev/null; then
- echo "✓ Timer is enabled"
- else
- echo "✗ Timer is not enabled"
- fi
-
- if systemctl is-active pc-startup-monitor.timer &>/dev/null; then
- echo "✓ Timer is active"
- else
- echo "✗ Timer is not active"
- fi
-
- echo ""
- echo "Testing current logic:"
- /usr/local/bin/pc-startup-check.sh
+ echo ""
+ echo "6. Testing Setup..."
+ echo "=================="
+
+ echo "Service files:"
+ if [[ -f "/etc/systemd/system/pc-startup-monitor.service" ]]; then
+ echo "✓ Service file exists"
+ else
+ echo "✗ Service file missing"
+ fi
+
+ if [[ -f "/etc/systemd/system/pc-startup-monitor.timer" ]]; then
+ echo "✓ Timer file exists"
+ else
+ echo "✗ Timer file missing"
+ fi
+
+ echo ""
+ echo "Timer status:"
+ if systemctl is-enabled pc-startup-monitor.timer &> /dev/null; then
+ echo "✓ Timer is enabled"
+ else
+ echo "✗ Timer is not enabled"
+ fi
+
+ if systemctl is-active pc-startup-monitor.timer &> /dev/null; then
+ echo "✓ Timer is active"
+ else
+ echo "✗ Timer is not active"
+ fi
+
+ echo ""
+ echo "Testing current logic:"
+ /usr/local/bin/pc-startup-check.sh
}
# Function to show final instructions
show_instructions() {
- echo ""
- echo "=========================================="
- echo "PC Startup Monitor Setup Complete"
- echo "=========================================="
- echo "Summary:"
- echo "✓ Monitoring service created (/etc/systemd/system/pc-startup-monitor.service)"
- echo "✓ Monitoring timer created (/etc/systemd/system/pc-startup-monitor.timer)"
- echo "✓ Monitor script created (/usr/local/bin/pc-startup-check.sh)"
- echo "✓ Management script created (/usr/local/bin/pc-startup-monitor-manager.sh)"
- echo "✓ Timer enabled and started"
- echo ""
- echo "How it works:"
- echo "• Monitors PC startup times on Monday, Friday, Saturday, Sunday"
- echo "• Expects PC to be turned on between 5:00 AM - 8:00 AM"
- echo "• Checks daily at 8:30 AM if PC was turned on in expected window"
- echo "• Shows warning if PC was not turned on during expected time"
- echo ""
- echo "Management commands:"
- echo " sudo pc-startup-monitor-manager.sh status - Check status"
- echo " sudo pc-startup-monitor-manager.sh logs - View monitor logs"
- echo " sudo pc-startup-monitor-manager.sh test - Test monitor now"
- echo ""
- echo "Next check: Tomorrow at 8:30 AM (if it's a monitored day)"
- echo ""
+ echo ""
+ echo "=========================================="
+ echo "PC Startup Monitor Setup Complete"
+ echo "=========================================="
+ echo "Summary:"
+ echo "✓ Monitoring service created (/etc/systemd/system/pc-startup-monitor.service)"
+ echo "✓ Monitoring timer created (/etc/systemd/system/pc-startup-monitor.timer)"
+ echo "✓ Monitor script created (/usr/local/bin/pc-startup-check.sh)"
+ echo "✓ Management script created (/usr/local/bin/pc-startup-monitor-manager.sh)"
+ echo "✓ Timer enabled and started"
+ echo ""
+ echo "How it works:"
+ echo "• Monitors PC startup times on Monday, Friday, Saturday, Sunday"
+ echo "• Expects PC to be turned on between 5:00 AM - 8:00 AM"
+ echo "• Checks daily at 8:30 AM if PC was turned on in expected window"
+ echo "• Shows warning if PC was not turned on during expected time"
+ echo ""
+ echo "Management commands:"
+ echo " sudo pc-startup-monitor-manager.sh status - Check status"
+ echo " sudo pc-startup-monitor-manager.sh logs - View monitor logs"
+ echo " sudo pc-startup-monitor-manager.sh test - Test monitor now"
+ echo ""
+ echo "Next check: Tomorrow at 8:30 AM (if it's a monitored day)"
+ echo ""
}
# Function to prompt for confirmation
confirm_setup() {
- echo ""
- echo "PC Startup Monitor Setup"
- echo "======================="
- echo "This will set up monitoring for PC startup times."
- echo ""
- echo "Monitoring schedule:"
- echo "- Days: Monday, Friday, Saturday, Sunday"
- echo "- Expected startup time: 5:00 AM - 8:00 AM"
- echo "- Check time: 8:30 AM daily"
- echo "- Action: Show warning if PC wasn't started in expected window"
- echo ""
-
- if [[ "$INTERACTIVE_MODE" == "true" ]]; then
- read -p "Do you want to proceed? (y/N): " confirm
-
- case "$confirm" in
- [yY]|[yY][eE][sS])
- echo "Proceeding with setup..."
- return 0
- ;;
- *)
- echo "Setup cancelled."
- exit 0
- ;;
- esac
- else
- echo "Auto-proceeding with setup (use --interactive to prompt)"
+ echo ""
+ echo "PC Startup Monitor Setup"
+ echo "======================="
+ echo "This will set up monitoring for PC startup times."
+ echo ""
+ echo "Monitoring schedule:"
+ echo "- Days: Monday, Friday, Saturday, Sunday"
+ echo "- Expected startup time: 5:00 AM - 8:00 AM"
+ echo "- Check time: 8:30 AM daily"
+ echo "- Action: Show warning if PC wasn't started in expected window"
+ echo ""
+
+ if [[ $INTERACTIVE_MODE == "true" ]]; then
+ read -r -p "Do you want to proceed? (y/N): " confirm
+
+ case "$confirm" in
+ [yY] | [yY][eE][sS])
echo "Proceeding with setup..."
return 0
- fi
+ ;;
+ *)
+ echo "Setup cancelled."
+ exit 0
+ ;;
+ esac
+ else
+ echo "Auto-proceeding with setup (use --interactive to prompt)"
+ echo "Proceeding with setup..."
+ return 0
+ fi
}
# Main execution flow
main() {
- # Check for sudo privileges
- check_sudo "$@"
-
- # Confirm setup
- confirm_setup
-
- # Create all components
- create_monitoring_service
- create_monitoring_timer
- create_monitoring_script
- create_management_script
-
- # Enable services
- enable_services
-
- # Test setup
- test_setup
-
- # Show instructions
- show_instructions
+ # Check for sudo privileges
+ check_sudo "$@"
+
+ # Confirm setup
+ confirm_setup
+
+ # Create all components
+ create_monitoring_service
+ create_monitoring_timer
+ create_monitoring_script
+ create_management_script
+
+ # Enable services
+ enable_services
+
+ # Test setup
+ test_setup
+
+ # Show instructions
+ show_instructions
}
# Run main function
diff --git a/scripts/features/control_from_mobile.sh b/scripts/features/control_from_mobile.sh
index b5c9129..5e981f9 100755
--- a/scripts/features/control_from_mobile.sh
+++ b/scripts/features/control_from_mobile.sh
@@ -18,7 +18,7 @@ DEFAULT_BIND_ADDR="0.0.0.0"
readonly SCRIPT_NAME CONFIG_DIR STATE_DIR PASSWORD_FILE ENV_FILE RUNNER_FILE SERVICE_NAME SYSTEMD_USER_DIR SERVICE_FILE DEFAULT_DISPLAY DEFAULT_PORT DEFAULT_BIND_ADDR
usage() {
- cat <<'EOF'
+ cat << 'EOF'
Usage: control_from_mobile.sh [options]
Commands:
@@ -46,118 +46,117 @@ EOF
}
log() {
- printf '[%s] %s\n' "$SCRIPT_NAME" "$*"
+ printf '[%s] %s\n' "$SCRIPT_NAME" "$*"
}
warn() {
- printf '[%s] %s\n' "$SCRIPT_NAME" "$*" >&2
+ printf '[%s] %s\n' "$SCRIPT_NAME" "$*" >&2
}
die() {
- warn "$*"
- exit 1
+ warn "$*"
+ exit 1
}
require_non_root() {
- if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then
- die "Run this script as a regular desktop user, not root."
- fi
+ if [[ ${EUID:-$(id -u)} -eq 0 ]]; then
+ die "Run this script as a regular desktop user, not root."
+ fi
}
prompt_yes_no() {
- local prompt="$1"
- local reply
- read -r -p "$prompt [y/N]: " reply
- case "$reply" in
- [Yy][Ee][Ss]|[Yy]) return 0 ;;
- *) return 1 ;;
- esac
+ local prompt="$1"
+ local reply
+ read -r -p "$prompt [y/N]: " reply
+ case "$reply" in
+ [Yy][Ee][Ss] | [Yy]) return 0 ;;
+ *) return 1 ;;
+ esac
}
ensure_directories() {
- mkdir -p "$CONFIG_DIR" "$STATE_DIR" "$SYSTEMD_USER_DIR"
- chmod 700 "$CONFIG_DIR"
+ mkdir -p "$CONFIG_DIR" "$STATE_DIR" "$SYSTEMD_USER_DIR"
+ chmod 700 "$CONFIG_DIR"
}
missing_commands() {
- local missing=()
- for cmd in "$@"; do
- if ! command -v "$cmd" >/dev/null 2>&1; then
- missing+=("$cmd")
- fi
- done
- printf '%s\n' "${missing[@]-}"
+ local missing=()
+ for cmd in "$@"; do
+ if ! command -v "$cmd" > /dev/null 2>&1; then
+ missing+=("$cmd")
+ fi
+ done
+ printf '%s\n' "${missing[@]-}"
}
install_dependencies() {
- if ! command -v systemctl >/dev/null 2>&1; then
- die "systemctl not found. Install systemd before running this script."
- fi
+ if ! command -v systemctl > /dev/null 2>&1; then
+ die "systemctl not found. Install systemd before running this script."
+ fi
- local required=(x11vnc qrencode ssh)
- local needed=()
- mapfile -t needed < <(missing_commands "${required[@]}")
- if (( ${#needed[@]} == 0 )); then
- log "All required packages (${required[*]}) are present."
- return
- fi
+ local required=(x11vnc qrencode ssh)
+ local needed=()
+ mapfile -t needed < <(missing_commands "${required[@]}")
+ if ((${#needed[@]} == 0)); then
+ log "All required packages (${required[*]}) are present."
+ return
+ fi
- if command -v pacman >/dev/null 2>&1; then
- log "Installing missing packages: ${needed[*]}"
- sudo pacman -S --needed --noconfirm "${needed[@]}"
- else
- die "Missing commands (${needed[*]}). Install them manually and rerun setup."
- fi
+ if command -v pacman > /dev/null 2>&1; then
+ log "Installing missing packages: ${needed[*]}"
+ sudo pacman -S --needed --noconfirm "${needed[@]}"
+ else
+ die "Missing commands (${needed[*]}). Install them manually and rerun setup."
+ fi
}
create_password_file() {
- local force=${1:-0}
- if [[ -f "$PASSWORD_FILE" && "$force" -ne 1 ]];
- then
- log "Using existing VNC password file at $PASSWORD_FILE"
- return
- fi
+ local force=${1:-0}
+ if [[ -f $PASSWORD_FILE && $force -ne 1 ]]; then
+ log "Using existing VNC password file at $PASSWORD_FILE"
+ return
+ fi
- if [[ -f "$PASSWORD_FILE" ]]; then
- if ! prompt_yes_no "Regenerate the stored VNC password?"; then
- log "Keeping existing password."
- return
- fi
- fi
+ if [[ -f $PASSWORD_FILE ]]; then
+ if ! prompt_yes_no "Regenerate the stored VNC password?"; then
+ log "Keeping existing password."
+ return
+ fi
+ fi
- local password confirm generated=0
- read -rsp "Enter VNC password (leave blank to auto-generate): " password
- printf '\n'
- if [[ -z "$password" ]]; then
- generated=1
- password=$(LC_ALL=C tr -dc 'A-Za-z0-9' /dev/null
- install -m 600 "$tmp" "$PASSWORD_FILE"
- rm -f "$tmp"
+ local tmp
+ tmp=$(mktemp)
+ x11vnc -storepasswd "$password" "$tmp" > /dev/null
+ install -m 600 "$tmp" "$PASSWORD_FILE"
+ rm -f "$tmp"
- if (( generated == 0 )); then
- log "Password stored securely at $PASSWORD_FILE (hashed)."
- else
- log "Please write down the generated password; it will be needed on your Android device."
- fi
+ if ((generated == 0)); then
+ log "Password stored securely at $PASSWORD_FILE (hashed)."
+ else
+ log "Please write down the generated password; it will be needed on your Android device."
+ fi
}
create_env_file() {
- if [[ -f "$ENV_FILE" ]]; then
- return
- fi
- cat >"$ENV_FILE" < "$ENV_FILE" << EOF
# control-from-mobile configuration
# Adjust these values if needed and rerun: systemctl --user restart $SERVICE_NAME
X11_DISPLAY="$DEFAULT_DISPLAY"
@@ -165,11 +164,11 @@ VNC_PORT="$DEFAULT_PORT"
# Use 127.0.0.1 to force SSH tunnel-only access, or 0.0.0.0 to expose on LAN.
VNC_BIND_ADDR="$DEFAULT_BIND_ADDR"
EOF
- chmod 600 "$ENV_FILE"
+ chmod 600 "$ENV_FILE"
}
create_runner_script() {
- cat >"$RUNNER_FILE" <<'EOF'
+ cat > "$RUNNER_FILE" << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
@@ -209,11 +208,11 @@ exec /usr/bin/x11vnc \
-ncache_cr \
-o "$LOG_FILE"
EOF
- chmod 700 "$RUNNER_FILE"
+ chmod 700 "$RUNNER_FILE"
}
create_service_file() {
- cat >"$SERVICE_FILE" < "$SERVICE_FILE" << EOF
[Unit]
Description=Expose X11 desktop over VNC for Android control
After=graphical-session.target
@@ -234,183 +233,183 @@ EOF
}
reload_user_daemon() {
- systemctl --user daemon-reload
+ systemctl --user daemon-reload
}
ensure_service_present() {
- if [[ ! -f "$SERVICE_FILE" || ! -x "$RUNNER_FILE" ]]; then
- die "Service files missing. Run: $SCRIPT_NAME setup"
- fi
+ if [[ ! -f $SERVICE_FILE || ! -x $RUNNER_FILE ]]; then
+ die "Service files missing. Run: $SCRIPT_NAME setup"
+ fi
}
start_service() {
- ensure_service_present
- systemctl --user start "$SERVICE_NAME"
+ ensure_service_present
+ systemctl --user start "$SERVICE_NAME"
}
stop_service() {
- systemctl --user stop "$SERVICE_NAME" || true
+ systemctl --user stop "$SERVICE_NAME" || true
}
status_service() {
- if systemctl --user is-active --quiet "$SERVICE_NAME"; then
- log "Service is active."
- else
- log "Service is inactive."
- fi
- systemctl --user status "$SERVICE_NAME" --no-pager || true
+ if systemctl --user is-active --quiet "$SERVICE_NAME"; then
+ log "Service is active."
+ else
+ log "Service is inactive."
+ fi
+ systemctl --user status "$SERVICE_NAME" --no-pager || true
}
enable_service() {
- ensure_service_present
- systemctl --user enable "$SERVICE_NAME"
+ ensure_service_present
+ systemctl --user enable "$SERVICE_NAME"
}
disable_service() {
- systemctl --user disable "$SERVICE_NAME" || true
+ systemctl --user disable "$SERVICE_NAME" || true
}
show_info() {
- ensure_service_present
- # shellcheck disable=SC1090
- [[ -f "$ENV_FILE" ]] && source "$ENV_FILE"
- local port="${VNC_PORT:-$DEFAULT_PORT}"
- local bind_addr="${VNC_BIND_ADDR:-$DEFAULT_BIND_ADDR}"
- local display="${X11_DISPLAY:-$DEFAULT_DISPLAY}"
+ ensure_service_present
+ # shellcheck disable=SC1090
+ [[ -f $ENV_FILE ]] && source "$ENV_FILE"
+ local port="${VNC_PORT:-$DEFAULT_PORT}"
+ local bind_addr="${VNC_BIND_ADDR:-$DEFAULT_BIND_ADDR}"
+ local display="${X11_DISPLAY:-$DEFAULT_DISPLAY}"
- local is_active="inactive"
- if systemctl --user is-active --quiet "$SERVICE_NAME"; then
- is_active="active"
- fi
+ local is_active="inactive"
+ if systemctl --user is-active --quiet "$SERVICE_NAME"; then
+ is_active="active"
+ fi
- log "Service status: $is_active"
- log "Display: $display"
- log "Listening address: $bind_addr"
- log "VNC port: $port"
- log "Password file: $PASSWORD_FILE"
+ log "Service status: $is_active"
+ log "Display: $display"
+ log "Listening address: $bind_addr"
+ log "VNC port: $port"
+ log "Password file: $PASSWORD_FILE"
- local -a ip_list=()
- if command -v hostname >/dev/null 2>&1; then
- while IFS= read -r line; do
- [[ -z "$line" ]] && continue
- ip_list+=("$line")
- done < <(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]' || true)
- fi
+ local -a ip_list=()
+ if command -v hostname > /dev/null 2>&1; then
+ while IFS= read -r line; do
+ [[ -z $line ]] && continue
+ ip_list+=("$line")
+ done < <(hostname -I 2> /dev/null | tr ' ' '\n' | grep -E '^[0-9]' || true)
+ fi
- if (( ${#ip_list[@]} > 0 )); then
- log "Detected LAN IPs:"
- for ip in "${ip_list[@]}"; do
- printf ' - %s\n' "$ip"
- done
- else
- warn "Could not detect LAN IPs."
- fi
+ if ((${#ip_list[@]} > 0)); then
+ log "Detected LAN IPs:"
+ for ip in "${ip_list[@]}"; do
+ printf ' - %s\n' "$ip"
+ done
+ else
+ warn "Could not detect LAN IPs."
+ fi
- printf '\nRecommended Android clients (FOSS):\n'
- printf ' • bVNC (available on F-Droid) — supports full control.\n'
- printf ' • Termux + OpenSSH for establishing an SSH tunnel when exposing only on 127.0.0.1.\n'
- printf '\nConnect via VNC:\n'
- printf ' Host: \n Port: %s\n Password: \n' "$port"
+ printf '\nRecommended Android clients (FOSS):\n'
+ printf ' • bVNC (available on F-Droid) — supports full control.\n'
+ printf ' • Termux + OpenSSH for establishing an SSH tunnel when exposing only on 127.0.0.1.\n'
+ printf '\nConnect via VNC:\n'
+ printf ' Host: \n Port: %s\n Password: \n' "$port"
- local qr_host
- if (( ${#ip_list[@]} > 0 )); then
- qr_host="${ip_list[0]}"
- else
- qr_host="$bind_addr"
- if [[ "$qr_host" == "0.0.0.0" || "$qr_host" == "::" ]]; then
- qr_host="127.0.0.1"
- fi
- warn "Using fallback host $qr_host for QR code; replace with an accessible IP if needed."
- fi
+ local qr_host
+ if ((${#ip_list[@]} > 0)); then
+ qr_host="${ip_list[0]}"
+ else
+ qr_host="$bind_addr"
+ if [[ $qr_host == "0.0.0.0" || $qr_host == "::" ]]; then
+ qr_host="127.0.0.1"
+ fi
+ warn "Using fallback host $qr_host for QR code; replace with an accessible IP if needed."
+ fi
- if command -v qrencode >/dev/null 2>&1; then
- printf '\nConnection QR (vnc://%s:%s):\n' "$qr_host" "$port"
- qrencode -o - "vnc://$qr_host:$port" -t ASCII || true
- else
- warn "qrencode not found; reinstall qrencode to get QR codes."
- fi
+ if command -v qrencode > /dev/null 2>&1; then
+ printf '\nConnection QR (vnc://%s:%s):\n' "$qr_host" "$port"
+ qrencode -o - "vnc://$qr_host:$port" -t ASCII || true
+ else
+ warn "qrencode not found; reinstall qrencode to get QR codes."
+ fi
- printf '\nFor encrypted access outside your LAN, use Termux on Android:\n'
- printf ' ssh -L %s:localhost:%s @\n' "$port" "$port"
- printf 'Then point bVNC to 127.0.0.1:%s.\n' "$port"
+ printf '\nFor encrypted access outside your LAN, use Termux on Android:\n'
+ printf ' ssh -L %s:localhost:%s @\n' "$port" "$port"
+ printf 'Then point bVNC to 127.0.0.1:%s.\n' "$port"
}
uninstall_files() {
- local purge_password=${1:-0}
- stop_service
- disable_service
- rm -f "$SERVICE_FILE"
- rm -f "$RUNNER_FILE"
- rm -f "$ENV_FILE"
- if (( purge_password )); then
- rm -f "$PASSWORD_FILE"
- log "Removed password file."
- fi
- reload_user_daemon
- log "Removed generated files."
+ local purge_password=${1:-0}
+ stop_service
+ disable_service
+ rm -f "$SERVICE_FILE"
+ rm -f "$RUNNER_FILE"
+ rm -f "$ENV_FILE"
+ if ((purge_password)); then
+ rm -f "$PASSWORD_FILE"
+ log "Removed password file."
+ fi
+ reload_user_daemon
+ log "Removed generated files."
}
main() {
- require_non_root
+ require_non_root
- local cmd="${1:-}"
- shift || true
+ local cmd="${1:-}"
+ shift || true
- case "$cmd" in
- setup)
- local force=0
- if [[ "${1:-}" == "--force-password" ]]; then
- force=1
- shift || true
- fi
- ensure_directories
- install_dependencies
- create_password_file "$force"
- create_env_file
- create_runner_script
- create_service_file
- reload_user_daemon
- log "Setup complete. Start the service with: $SCRIPT_NAME start"
- ;;
- start)
- start_service
- show_info
- ;;
- stop)
- stop_service
- ;;
- restart)
- stop_service
- start_service
- ;;
- status)
- status_service
- ;;
- enable)
- enable_service
- ;;
- disable)
- disable_service
- ;;
- info)
- show_info
- ;;
- uninstall)
- local purge=0
- if [[ "${1:-}" == "--purge" ]]; then
- purge=1
- shift || true
- fi
- uninstall_files "$purge"
- ;;
- help|--help|-h|"" )
- usage
- ;;
- *)
- usage
- die "Unknown command: $cmd"
- ;;
- esac
+ case "$cmd" in
+ setup)
+ local force=0
+ if [[ ${1:-} == "--force-password" ]]; then
+ force=1
+ shift || true
+ fi
+ ensure_directories
+ install_dependencies
+ create_password_file "$force"
+ create_env_file
+ create_runner_script
+ create_service_file
+ reload_user_daemon
+ log "Setup complete. Start the service with: $SCRIPT_NAME start"
+ ;;
+ start)
+ start_service
+ show_info
+ ;;
+ stop)
+ stop_service
+ ;;
+ restart)
+ stop_service
+ start_service
+ ;;
+ status)
+ status_service
+ ;;
+ enable)
+ enable_service
+ ;;
+ disable)
+ disable_service
+ ;;
+ info)
+ show_info
+ ;;
+ uninstall)
+ local purge=0
+ if [[ ${1:-} == "--purge" ]]; then
+ purge=1
+ shift || true
+ fi
+ uninstall_files "$purge"
+ ;;
+ help | --help | -h | "")
+ usage
+ ;;
+ *)
+ usage
+ die "Unknown command: $cmd"
+ ;;
+ esac
}
main "$@"
diff --git a/scripts/features/setup_activitywatch.sh b/scripts/features/setup_activitywatch.sh
index 0fb9506..b205075 100755
--- a/scripts/features/setup_activitywatch.sh
+++ b/scripts/features/setup_activitywatch.sh
@@ -3,15 +3,15 @@
# Handles installation, startup, autostart, and i3blocks status
# Handles sudo privileges automatically
-set -e # Exit on any error
+set -e # Exit on any error
# Function to check and request sudo privileges for package installation
check_sudo() {
- if [[ $EUID -ne 0 ]] && [[ "$1" == "install" ]]; then
- echo "Package installation requires sudo privileges."
- echo "Requesting sudo access..."
- exec sudo "$0" "$@"
- fi
+ if [[ $EUID -ne 0 ]] && [[ $1 == "install" ]]; then
+ echo "Package installation requires sudo privileges."
+ echo "Requesting sudo access..."
+ exec sudo "$0" "$@"
+ fi
}
echo "ActivityWatch Setup for Arch Linux + i3"
@@ -20,12 +20,12 @@ echo "Current Date: $(date)"
echo "User: ${SUDO_USER:-$USER}"
# Get the actual user (even when running with sudo)
-if [[ -n "$SUDO_USER" ]]; then
- ACTUAL_USER="$SUDO_USER"
- USER_HOME="/home/$SUDO_USER"
+if [[ -n $SUDO_USER ]]; then
+ ACTUAL_USER="$SUDO_USER"
+ USER_HOME="/home/$SUDO_USER"
else
- ACTUAL_USER="$USER"
- USER_HOME="$HOME"
+ ACTUAL_USER="$USER"
+ USER_HOME="$HOME"
fi
echo "Target user: $ACTUAL_USER"
@@ -33,180 +33,180 @@ echo "User home: $USER_HOME"
# Function to check if ActivityWatch is installed
check_activitywatch_installed() {
- echo ""
- echo "1. Checking ActivityWatch Installation..."
- echo "========================================"
-
- # Check if activitywatch-bin is installed via pacman
- if pacman -Qi activitywatch-bin &>/dev/null; then
- echo "✓ activitywatch-bin package is installed"
- return 0
+ echo ""
+ echo "1. Checking ActivityWatch Installation..."
+ echo "========================================"
+
+ # Check if activitywatch-bin is installed via pacman
+ if pacman -Qi activitywatch-bin &> /dev/null; then
+ echo "✓ activitywatch-bin package is installed"
+ return 0
+ fi
+
+ # Check if aw-qt binary exists in common locations
+ local common_paths=(
+ "/usr/bin/aw-qt"
+ "/usr/local/bin/aw-qt"
+ "$USER_HOME/.local/bin/aw-qt"
+ "$USER_HOME/activitywatch/aw-qt"
+ )
+
+ for path in "${common_paths[@]}"; do
+ if [[ -x $path ]]; then
+ echo "✓ ActivityWatch found at: $path"
+ return 0
fi
-
- # Check if aw-qt binary exists in common locations
- local common_paths=(
- "/usr/bin/aw-qt"
- "/usr/local/bin/aw-qt"
- "$USER_HOME/.local/bin/aw-qt"
- "$USER_HOME/activitywatch/aw-qt"
- )
-
- for path in "${common_paths[@]}"; do
- if [[ -x "$path" ]]; then
- echo "✓ ActivityWatch found at: $path"
- return 0
- fi
- done
-
- echo "✗ ActivityWatch not found"
- return 1
+ done
+
+ echo "✗ ActivityWatch not found"
+ return 1
}
# Function to install ActivityWatch
install_activitywatch() {
- echo ""
- echo "2. Installing ActivityWatch..."
- echo "============================="
-
- # Check if we need sudo for installation
- check_sudo "install"
-
- echo "Installing activitywatch-bin from AUR..."
-
- # Check if an AUR helper is available
- local aur_helpers=("yay" "paru" "makepkg")
- local helper_found=""
-
- for helper in "${aur_helpers[@]}"; do
- if command -v "$helper" &>/dev/null; then
- helper_found="$helper"
- break
- fi
- done
-
- if [[ -n "$helper_found" && "$helper_found" != "makepkg" ]]; then
- echo "Using AUR helper: $helper_found"
- if [[ $EUID -eq 0 ]]; then
- # Running as root, need to install as user
- sudo -u "$ACTUAL_USER" "$helper_found" -S --noconfirm activitywatch-bin
- else
- "$helper_found" -S --noconfirm activitywatch-bin
- fi
- else
- echo "No AUR helper found. Installing manually with makepkg..."
- install_activitywatch_manual
+ echo ""
+ echo "2. Installing ActivityWatch..."
+ echo "============================="
+
+ # Check if we need sudo for installation
+ check_sudo "install"
+
+ echo "Installing activitywatch-bin from AUR..."
+
+ # Check if an AUR helper is available
+ local aur_helpers=("yay" "paru" "makepkg")
+ local helper_found=""
+
+ for helper in "${aur_helpers[@]}"; do
+ if command -v "$helper" &> /dev/null; then
+ helper_found="$helper"
+ break
fi
-
- echo "✓ ActivityWatch installation completed"
+ done
+
+ if [[ -n $helper_found && $helper_found != "makepkg" ]]; then
+ echo "Using AUR helper: $helper_found"
+ if [[ $EUID -eq 0 ]]; then
+ # Running as root, need to install as user
+ sudo -u "$ACTUAL_USER" "$helper_found" -S --noconfirm activitywatch-bin
+ else
+ "$helper_found" -S --noconfirm activitywatch-bin
+ fi
+ else
+ echo "No AUR helper found. Installing manually with makepkg..."
+ install_activitywatch_manual
+ fi
+
+ echo "✓ ActivityWatch installation completed"
}
# Function to manually install ActivityWatch via makepkg
install_activitywatch_manual() {
- local temp_dir="/tmp/activitywatch-install"
- local original_user="$ACTUAL_USER"
-
- # Create temp directory
- mkdir -p "$temp_dir"
- cd "$temp_dir"
-
- # Download PKGBUILD
- if command -v git &>/dev/null; then
- sudo -u "$original_user" git clone https://aur.archlinux.org/activitywatch-bin.git .
- else
- echo "Installing git..."
- pacman -S --noconfirm git
- sudo -u "$original_user" git clone https://aur.archlinux.org/activitywatch-bin.git .
- fi
-
- # Build and install package
- sudo -u "$original_user" makepkg -si --noconfirm
-
- # Cleanup
- cd /
- rm -rf "$temp_dir"
+ local temp_dir="/tmp/activitywatch-install"
+ local original_user="$ACTUAL_USER"
+
+ # Create temp directory
+ mkdir -p "$temp_dir"
+ cd "$temp_dir"
+
+ # Download PKGBUILD
+ if command -v git &> /dev/null; then
+ sudo -u "$original_user" git clone https://aur.archlinux.org/activitywatch-bin.git .
+ else
+ echo "Installing git..."
+ pacman -S --noconfirm git
+ sudo -u "$original_user" git clone https://aur.archlinux.org/activitywatch-bin.git .
+ fi
+
+ # Build and install package
+ sudo -u "$original_user" makepkg -si --noconfirm
+
+ # Cleanup
+ cd /
+ rm -rf "$temp_dir"
}
# Function to check if ActivityWatch is running
check_activitywatch_running() {
- echo ""
- echo "3. Checking ActivityWatch Status..."
- echo "=================================="
-
- # Check for aw-qt process
- if pgrep -f "aw-qt" >/dev/null; then
- echo "✓ ActivityWatch (aw-qt) is running"
- return 0
- fi
-
- # Check for aw-server process
- if pgrep -f "aw-server" >/dev/null; then
- echo "✓ ActivityWatch server is running"
- return 0
- fi
-
- echo "✗ ActivityWatch is not running"
- return 1
+ echo ""
+ echo "3. Checking ActivityWatch Status..."
+ echo "=================================="
+
+ # Check for aw-qt process
+ if pgrep -f "aw-qt" > /dev/null; then
+ echo "✓ ActivityWatch (aw-qt) is running"
+ return 0
+ fi
+
+ # Check for aw-server process
+ if pgrep -f "aw-server" > /dev/null; then
+ echo "✓ ActivityWatch server is running"
+ return 0
+ fi
+
+ echo "✗ ActivityWatch is not running"
+ return 1
}
# Function to start ActivityWatch
start_activitywatch() {
- echo ""
- echo "4. Starting ActivityWatch..."
- echo "==========================="
-
- # Find aw-qt executable
- local aw_qt_path=""
-
- if command -v aw-qt &>/dev/null; then
- aw_qt_path="$(which aw-qt)"
- elif [[ -x "/usr/bin/aw-qt" ]]; then
- aw_qt_path="/usr/bin/aw-qt"
- else
- echo "✗ Could not find aw-qt executable"
- return 1
- fi
-
- echo "Starting ActivityWatch as user: $ACTUAL_USER"
- echo "Using aw-qt from: $aw_qt_path"
-
- # Start as the actual user in the background
- if [[ $EUID -eq 0 ]]; then
- # Running as root, start as user
- sudo -u "$ACTUAL_USER" env DISPLAY=:0 "$aw_qt_path" &
- else
- # Running as user
- "$aw_qt_path" &
- fi
-
- # Give it time to start
- sleep 3
-
- if check_activitywatch_running >/dev/null 2>&1; then
- echo "✓ ActivityWatch started successfully"
- else
- echo "! ActivityWatch may be starting (check system tray)"
- fi
+ echo ""
+ echo "4. Starting ActivityWatch..."
+ echo "==========================="
+
+ # Find aw-qt executable
+ local aw_qt_path=""
+
+ if command -v aw-qt &> /dev/null; then
+ aw_qt_path="$(which aw-qt)"
+ elif [[ -x "/usr/bin/aw-qt" ]]; then
+ aw_qt_path="/usr/bin/aw-qt"
+ else
+ echo "✗ Could not find aw-qt executable"
+ return 1
+ fi
+
+ echo "Starting ActivityWatch as user: $ACTUAL_USER"
+ echo "Using aw-qt from: $aw_qt_path"
+
+ # Start as the actual user in the background
+ if [[ $EUID -eq 0 ]]; then
+ # Running as root, start as user
+ sudo -u "$ACTUAL_USER" env DISPLAY=:0 "$aw_qt_path" &
+ else
+ # Running as user
+ "$aw_qt_path" &
+ fi
+
+ # Give it time to start
+ sleep 3
+
+ if check_activitywatch_running > /dev/null 2>&1; then
+ echo "✓ ActivityWatch started successfully"
+ else
+ echo "! ActivityWatch may be starting (check system tray)"
+ fi
}
# Function to setup autostart
setup_autostart() {
- echo ""
- echo "5. Setting Up Autostart..."
- echo "========================="
-
- local autostart_dir="$USER_HOME/.config/autostart"
- local desktop_file="$autostart_dir/activitywatch.desktop"
- local i3_config="$USER_HOME/.config/i3/config"
-
- # Method 1: XDG Autostart (works with most desktop environments)
- if [[ $EUID -eq 0 ]]; then
- sudo -u "$ACTUAL_USER" mkdir -p "$autostart_dir"
- else
- mkdir -p "$autostart_dir"
- fi
-
- # Create desktop file for autostart
- cat > "$desktop_file" << EOF
+ echo ""
+ echo "5. Setting Up Autostart..."
+ echo "========================="
+
+ local autostart_dir="$USER_HOME/.config/autostart"
+ local desktop_file="$autostart_dir/activitywatch.desktop"
+ local i3_config="$USER_HOME/.config/i3/config"
+
+ # Method 1: XDG Autostart (works with most desktop environments)
+ if [[ $EUID -eq 0 ]]; then
+ sudo -u "$ACTUAL_USER" mkdir -p "$autostart_dir"
+ else
+ mkdir -p "$autostart_dir"
+ fi
+
+ # Create desktop file for autostart
+ cat > "$desktop_file" << EOF
[Desktop Entry]
Type=Application
Name=ActivityWatch
@@ -220,59 +220,61 @@ StartupNotify=false
Terminal=false
Categories=Utility;
EOF
-
- # Set proper ownership if running as root
- if [[ $EUID -eq 0 ]]; then
- chown "$ACTUAL_USER:$ACTUAL_USER" "$desktop_file"
- fi
-
- echo "✓ Created XDG autostart entry: $desktop_file"
-
- # Method 2: i3 config autostart (specific to i3)
- if [[ -f "$i3_config" ]]; then
- # Check if autostart entry already exists
- if ! grep -q "aw-qt" "$i3_config"; then
- # Add autostart entry to i3 config
- local temp_config="/tmp/i3_config_temp"
-
- if [[ $EUID -eq 0 ]]; then
- # Running as root
- sudo -u "$ACTUAL_USER" bash -c "echo '' >> '$i3_config'"
- sudo -u "$ACTUAL_USER" bash -c "echo '# Auto-start ActivityWatch' >> '$i3_config'"
- sudo -u "$ACTUAL_USER" bash -c "echo 'exec --no-startup-id aw-qt' >> '$i3_config'"
- else
- echo "" >> "$i3_config"
- echo "# Auto-start ActivityWatch" >> "$i3_config"
- echo "exec --no-startup-id aw-qt" >> "$i3_config"
- fi
-
- echo "✓ Added ActivityWatch to i3 config autostart"
- else
- echo "✓ ActivityWatch autostart already exists in i3 config"
- fi
+
+ # Set proper ownership if running as root
+ if [[ $EUID -eq 0 ]]; then
+ chown "$ACTUAL_USER:$ACTUAL_USER" "$desktop_file"
+ fi
+
+ echo "✓ Created XDG autostart entry: $desktop_file"
+
+ # Method 2: i3 config autostart (specific to i3)
+ if [[ -f $i3_config ]]; then
+ # Check if autostart entry already exists
+ if ! grep -q "aw-qt" "$i3_config"; then
+ # Add autostart entry to i3 config
+ if [[ $EUID -eq 0 ]]; then
+ # Running as root
+ sudo -u "$ACTUAL_USER" bash -c "cat <<'EOF' >> '$i3_config'
+
+# Auto-start ActivityWatch
+exec --no-startup-id aw-qt
+EOF"
+ else
+ {
+ printf '\n'
+ printf '# Auto-start ActivityWatch\n'
+ printf 'exec --no-startup-id aw-qt\n'
+ } >> "$i3_config"
+ fi
+
+ echo "✓ Added ActivityWatch to i3 config autostart"
else
- echo "! i3 config not found at $i3_config"
+ echo "✓ ActivityWatch autostart already exists in i3 config"
fi
+ else
+ echo "! i3 config not found at $i3_config"
+ fi
}
# Function to create i3blocks status script
create_i3blocks_status() {
- echo ""
- echo "6. Creating i3blocks Status Script..."
- echo "===================================="
-
- local i3blocks_dir="$USER_HOME/.config/i3blocks"
- local status_script="$i3blocks_dir/activitywatch_status.sh"
-
- # Create i3blocks directory if it doesn't exist
- if [[ $EUID -eq 0 ]]; then
- sudo -u "$ACTUAL_USER" mkdir -p "$i3blocks_dir"
- else
- mkdir -p "$i3blocks_dir"
- fi
-
- # Create the status script
- cat > "$status_script" << 'EOF'
+ echo ""
+ echo "6. Creating i3blocks Status Script..."
+ echo "===================================="
+
+ local i3blocks_dir="$USER_HOME/.config/i3blocks"
+ local status_script="$i3blocks_dir/activitywatch_status.sh"
+
+ # Create i3blocks directory if it doesn't exist
+ if [[ $EUID -eq 0 ]]; then
+ sudo -u "$ACTUAL_USER" mkdir -p "$i3blocks_dir"
+ else
+ mkdir -p "$i3blocks_dir"
+ fi
+
+ # Create the status script
+ cat > "$status_script" << 'EOF'
#!/bin/bash
# ActivityWatch status script for i3blocks
# Shows ActivityWatch installation and running status
@@ -323,134 +325,134 @@ else
fi
EOF
- chmod +x "$status_script"
-
- # Set proper ownership if running as root
- if [[ $EUID -eq 0 ]]; then
- chown "$ACTUAL_USER:$ACTUAL_USER" "$status_script"
- fi
-
- echo "✓ Created i3blocks status script: $status_script"
-
- # Show configuration instructions
- echo ""
- echo "To add to your i3blocks config, add this block:"
- echo ""
- echo "[activitywatch]"
- echo "command=~/.config/i3blocks/activitywatch_status.sh"
- echo "interval=10"
- echo "color=#FFFFFF"
- echo ""
+ chmod +x "$status_script"
+
+ # Set proper ownership if running as root
+ if [[ $EUID -eq 0 ]]; then
+ chown "$ACTUAL_USER:$ACTUAL_USER" "$status_script"
+ fi
+
+ echo "✓ Created i3blocks status script: $status_script"
+
+ # Show configuration instructions
+ echo ""
+ echo "To add to your i3blocks config, add this block:"
+ echo ""
+ echo "[activitywatch]"
+ echo "command=~/.config/i3blocks/activitywatch_status.sh"
+ echo "interval=10"
+ echo "color=#FFFFFF"
+ echo ""
}
# Function to test the setup
test_setup() {
- echo ""
- echo "7. Testing Setup..."
- echo "=================="
-
- echo "Installation status:"
- if check_activitywatch_installed >/dev/null 2>&1; then
- echo "✓ ActivityWatch is installed"
+ echo ""
+ echo "7. Testing Setup..."
+ echo "=================="
+
+ echo "Installation status:"
+ if check_activitywatch_installed > /dev/null 2>&1; then
+ echo "✓ ActivityWatch is installed"
+ else
+ echo "✗ ActivityWatch is not installed"
+ fi
+
+ echo "Running status:"
+ if check_activitywatch_running > /dev/null 2>&1; then
+ echo "✓ ActivityWatch is running"
+ else
+ echo "✗ ActivityWatch is not running"
+ fi
+
+ echo "Autostart files:"
+ if [[ -f "$USER_HOME/.config/autostart/activitywatch.desktop" ]]; then
+ echo "✓ XDG autostart file exists"
+ else
+ echo "✗ XDG autostart file missing"
+ fi
+
+ if [[ -f "$USER_HOME/.config/i3/config" ]] && grep -q "aw-qt" "$USER_HOME/.config/i3/config"; then
+ echo "✓ i3 autostart configured"
+ else
+ echo "! i3 autostart may not be configured"
+ fi
+
+ echo "i3blocks status script:"
+ if [[ -x "$USER_HOME/.config/i3blocks/activitywatch_status.sh" ]]; then
+ echo "✓ i3blocks status script created"
+ echo "Testing status script:"
+ if [[ $EUID -eq 0 ]]; then
+ sudo -u "$ACTUAL_USER" "$USER_HOME/.config/i3blocks/activitywatch_status.sh"
else
- echo "✗ ActivityWatch is not installed"
- fi
-
- echo "Running status:"
- if check_activitywatch_running >/dev/null 2>&1; then
- echo "✓ ActivityWatch is running"
- else
- echo "✗ ActivityWatch is not running"
- fi
-
- echo "Autostart files:"
- if [[ -f "$USER_HOME/.config/autostart/activitywatch.desktop" ]]; then
- echo "✓ XDG autostart file exists"
- else
- echo "✗ XDG autostart file missing"
- fi
-
- if [[ -f "$USER_HOME/.config/i3/config" ]] && grep -q "aw-qt" "$USER_HOME/.config/i3/config"; then
- echo "✓ i3 autostart configured"
- else
- echo "! i3 autostart may not be configured"
- fi
-
- echo "i3blocks status script:"
- if [[ -x "$USER_HOME/.config/i3blocks/activitywatch_status.sh" ]]; then
- echo "✓ i3blocks status script created"
- echo "Testing status script:"
- if [[ $EUID -eq 0 ]]; then
- sudo -u "$ACTUAL_USER" "$USER_HOME/.config/i3blocks/activitywatch_status.sh"
- else
- "$USER_HOME/.config/i3blocks/activitywatch_status.sh"
- fi
- else
- echo "✗ i3blocks status script missing"
+ "$USER_HOME/.config/i3blocks/activitywatch_status.sh"
fi
+ else
+ echo "✗ i3blocks status script missing"
+ fi
}
# Function to show final instructions
show_instructions() {
- echo ""
- echo "=========================================="
- echo "ActivityWatch Setup Complete"
- echo "=========================================="
- echo "Summary:"
- echo "✓ ActivityWatch installation checked/completed"
- echo "✓ ActivityWatch startup configured"
- echo "✓ Autostart configured (XDG + i3)"
- echo "✓ i3blocks status script created"
- echo ""
- echo "Next steps:"
- echo "1. Add the i3blocks configuration to your config file:"
- echo " ~/.config/i3blocks/config"
- echo ""
- echo "2. Reload i3 configuration:"
- echo " Super+Shift+R"
- echo ""
- echo "3. ActivityWatch web interface should be available at:"
- echo " http://localhost:5600"
- echo ""
- echo "4. Check system tray for ActivityWatch icon"
- echo ""
- echo "Files created:"
- echo " ~/.config/autostart/activitywatch.desktop"
- echo " ~/.config/i3blocks/activitywatch_status.sh"
- echo " ~/.config/i3/config (modified)"
- echo ""
+ echo ""
+ echo "=========================================="
+ echo "ActivityWatch Setup Complete"
+ echo "=========================================="
+ echo "Summary:"
+ echo "✓ ActivityWatch installation checked/completed"
+ echo "✓ ActivityWatch startup configured"
+ echo "✓ Autostart configured (XDG + i3)"
+ echo "✓ i3blocks status script created"
+ echo ""
+ echo "Next steps:"
+ echo "1. Add the i3blocks configuration to your config file:"
+ echo " ~/.config/i3blocks/config"
+ echo ""
+ echo "2. Reload i3 configuration:"
+ echo " Super+Shift+R"
+ echo ""
+ echo "3. ActivityWatch web interface should be available at:"
+ echo " http://localhost:5600"
+ echo ""
+ echo "4. Check system tray for ActivityWatch icon"
+ echo ""
+ echo "Files created:"
+ echo " ~/.config/autostart/activitywatch.desktop"
+ echo " ~/.config/i3blocks/activitywatch_status.sh"
+ echo " ~/.config/i3/config (modified)"
+ echo ""
}
# Main execution flow
main() {
- local need_install=false
- local need_start=false
-
- # Check installation
- if ! check_activitywatch_installed; then
- need_install=true
- fi
-
- # Install if needed
- if [[ "$need_install" == true ]]; then
- install_activitywatch
- fi
-
- # Check if running
- if ! check_activitywatch_running; then
- need_start=true
- fi
-
- # Start if needed
- if [[ "$need_start" == true ]]; then
- start_activitywatch
- fi
-
- # Always set up autostart and i3blocks (in case they're missing)
- setup_autostart
- create_i3blocks_status
- test_setup
- show_instructions
+ local need_install=false
+ local need_start=false
+
+ # Check installation
+ if ! check_activitywatch_installed; then
+ need_install=true
+ fi
+
+ # Install if needed
+ if [[ $need_install == true ]]; then
+ install_activitywatch
+ fi
+
+ # Check if running
+ if ! check_activitywatch_running; then
+ need_start=true
+ fi
+
+ # Start if needed
+ if [[ $need_start == true ]]; then
+ start_activitywatch
+ fi
+
+ # Always set up autostart and i3blocks (in case they're missing)
+ setup_autostart
+ create_i3blocks_status
+ test_setup
+ show_instructions
}
# Run main function
diff --git a/scripts/fixes/fix_virtualbox.sh b/scripts/fixes/fix_virtualbox.sh
index 5500ce9..f60efec 100644
--- a/scripts/fixes/fix_virtualbox.sh
+++ b/scripts/fixes/fix_virtualbox.sh
@@ -3,194 +3,194 @@
set -euo pipefail
on_error() {
- local exit_code=$?
- local line_number=$1
- printf '\033[1;31m[ERROR]\033[0m Unexpected failure at line %s (exit code %s).\n' "${line_number}" "${exit_code}" >&2
+ local exit_code=$?
+ local line_number=$1
+ printf '\033[1;31m[ERROR]\033[0m Unexpected failure at line %s (exit code %s).\n' "${line_number}" "${exit_code}" >&2
}
trap 'on_error ${LINENO}' ERR
log_info() {
- printf '\033[1;34m[INFO]\033[0m %s\n' "$*"
+ printf '\033[1;34m[INFO]\033[0m %s\n' "$*"
}
log_warn() {
- printf '\033[1;33m[WARN]\033[0m %s\n' "$*"
+ printf '\033[1;33m[WARN]\033[0m %s\n' "$*"
}
log_error() {
- printf '\033[1;31m[ERROR]\033[0m %s\n' "$*" >&2
- exit 1
+ printf '\033[1;31m[ERROR]\033[0m %s\n' "$*" >&2
+ exit 1
}
require_root() {
- if [[ "${EUID}" -ne 0 ]]; then
- log_error "This script must be run as root (try again with sudo)."
- fi
+ if [[ ${EUID} -ne 0 ]]; then
+ log_error "This script must be run as root (try again with sudo)."
+ fi
}
require_pacman() {
- if ! command -v pacman >/dev/null 2>&1; then
- log_error "pacman not found. This script is intended for Arch Linux systems."
- fi
+ if ! command -v pacman > /dev/null 2>&1; then
+ log_error "pacman not found. This script is intended for Arch Linux systems."
+ fi
}
detect_kernel_release() {
- uname -r
+ uname -r
}
select_host_package() {
- local kernel_release=$1
- case "${kernel_release}" in
- *-lts)
- echo "virtualbox-host-modules-lts"
- ;;
- *-arch*)
- echo "virtualbox-host-modules-arch"
- ;;
- *)
- echo "virtualbox-host-dkms"
- ;;
- esac
+ local kernel_release=$1
+ case "${kernel_release}" in
+ *-lts)
+ echo "virtualbox-host-modules-lts"
+ ;;
+ *-arch*)
+ echo "virtualbox-host-modules-arch"
+ ;;
+ *)
+ echo "virtualbox-host-dkms"
+ ;;
+ esac
}
collect_kernel_headers() {
- local -a headers=()
- local kernel_pkg header_pkg
- for kernel_pkg in linux linux-lts linux-zen linux-hardened; do
- if pacman -Q "${kernel_pkg}" >/dev/null 2>&1; then
- header_pkg="${kernel_pkg}-headers"
- headers+=("${header_pkg}")
- fi
- done
- if [[ ${#headers[@]} -gt 0 ]]; then
- printf '%s\n' "${headers[@]}"
- fi
+ local -a headers=()
+ local kernel_pkg header_pkg
+ for kernel_pkg in linux linux-lts linux-zen linux-hardened; do
+ if pacman -Q "${kernel_pkg}" > /dev/null 2>&1; then
+ header_pkg="${kernel_pkg}-headers"
+ headers+=("${header_pkg}")
+ fi
+ done
+ if [[ ${#headers[@]} -gt 0 ]]; then
+ printf '%s\n' "${headers[@]}"
+ fi
}
maybe_remove_conflicting_host_packages() {
- local selected_package=$1
- local -a candidates=("virtualbox-host-dkms" "virtualbox-host-modules-arch" "virtualbox-host-modules-lts")
- local pkg
- for pkg in "${candidates[@]}"; do
- if [[ "${pkg}" != "${selected_package}" ]] && pacman -Q "${pkg}" >/dev/null 2>&1; then
- log_warn "Removing conflicting package ${pkg} before installing ${selected_package}."
- pacman -Rsn "${PACMAN_REMOVE_FLAGS[@]}" "${pkg}"
- fi
- done
+ local selected_package=$1
+ local -a candidates=("virtualbox-host-dkms" "virtualbox-host-modules-arch" "virtualbox-host-modules-lts")
+ local pkg
+ for pkg in "${candidates[@]}"; do
+ if [[ ${pkg} != "${selected_package}" ]] && pacman -Q "${pkg}" > /dev/null 2>&1; then
+ log_warn "Removing conflicting package ${pkg} before installing ${selected_package}."
+ pacman -Rsn "${PACMAN_REMOVE_FLAGS[@]}" "${pkg}"
+ fi
+ done
}
install_packages() {
- local -a packages=()
- local -a headers=()
- local host_package=$1
- shift
- if [[ $# -gt 0 ]]; then
- mapfile -t headers < <(printf '%s\n' "$@" | sort -u)
- fi
- packages+=("virtualbox" "virtualbox-guest-iso" "${host_package}")
- if [[ "${host_package}" == "virtualbox-host-dkms" ]]; then
- packages+=("dkms")
- fi
- if [[ ${#headers[@]} -gt 0 ]]; then
- packages+=("${headers[@]}")
- fi
- log_info "Installing packages: ${packages[*]}"
- pacman -S "${PACMAN_INSTALL_FLAGS[@]}" "${packages[@]}"
+ local -a packages=()
+ local -a headers=()
+ local host_package=$1
+ shift
+ if [[ $# -gt 0 ]]; then
+ mapfile -t headers < <(printf '%s\n' "$@" | sort -u)
+ fi
+ packages+=("virtualbox" "virtualbox-guest-iso" "${host_package}")
+ if [[ ${host_package} == "virtualbox-host-dkms" ]]; then
+ packages+=("dkms")
+ fi
+ if [[ ${#headers[@]} -gt 0 ]]; then
+ packages+=("${headers[@]}")
+ fi
+ log_info "Installing packages: ${packages[*]}"
+ pacman -S "${PACMAN_INSTALL_FLAGS[@]}" "${packages[@]}"
}
rebuild_virtualbox_modules() {
- local host_package=$1
- if [[ "${host_package}" == "virtualbox-host-dkms" ]]; then
- if command -v dkms >/dev/null 2>&1; then
- log_info "Rebuilding VirtualBox DKMS modules for all installed kernels."
- dkms autoinstall
- else
- log_warn "dkms command not found; skipping DKMS rebuild."
- fi
- fi
+ local host_package=$1
+ if [[ ${host_package} == "virtualbox-host-dkms" ]]; then
+ if command -v dkms > /dev/null 2>&1; then
+ log_info "Rebuilding VirtualBox DKMS modules for all installed kernels."
+ dkms autoinstall
+ else
+ log_warn "dkms command not found; skipping DKMS rebuild."
+ fi
+ fi
}
reload_virtualbox_modules() {
- log_info "Loading VirtualBox kernel modules."
- if [[ -x /sbin/rcvboxdrv ]]; then
- /sbin/rcvboxdrv setup || log_warn "rcvboxdrv reported an issue while setting up modules."
- elif [[ -x /usr/lib/virtualbox/vboxdrv.sh ]]; then
- /usr/lib/virtualbox/vboxdrv.sh setup || log_warn "vboxdrv.sh reported an issue while setting up modules."
- fi
+ log_info "Loading VirtualBox kernel modules."
+ if [[ -x /sbin/rcvboxdrv ]]; then
+ /sbin/rcvboxdrv setup || log_warn "rcvboxdrv reported an issue while setting up modules."
+ elif [[ -x /usr/lib/virtualbox/vboxdrv.sh ]]; then
+ /usr/lib/virtualbox/vboxdrv.sh setup || log_warn "vboxdrv.sh reported an issue while setting up modules."
+ fi
- local -a modules=(vboxdrv vboxnetflt vboxnetadp vboxpci)
- local mod
- for mod in "${modules[@]}"; do
- if ! lsmod | awk '{print $1}' | grep -Fxq "${mod}"; then
- if ! modprobe "${mod}" >/dev/null 2>&1; then
- log_warn "Module ${mod} failed to load; check dmesg for details."
- fi
- fi
- done
+ local -a modules=(vboxdrv vboxnetflt vboxnetadp vboxpci)
+ local mod
+ for mod in "${modules[@]}"; do
+ if ! lsmod | awk '{print $1}' | grep -Fxq "${mod}"; then
+ if ! modprobe "${mod}" > /dev/null 2>&1; then
+ log_warn "Module ${mod} failed to load; check dmesg for details."
+ fi
+ fi
+ done
- if ! lsmod | awk '{print $1}' | grep -Fxq "vboxdrv"; then
- log_error "VirtualBox kernel driver (vboxdrv) failed to load. Review /var/log and dmesg output for clues."
- fi
- log_info "VirtualBox kernel driver loaded successfully."
+ if ! lsmod | awk '{print $1}' | grep -Fxq "vboxdrv"; then
+ log_error "VirtualBox kernel driver (vboxdrv) failed to load. Review /var/log and dmesg output for clues."
+ fi
+ log_info "VirtualBox kernel driver loaded successfully."
}
warn_if_secure_boot_enabled() {
- local secure_boot_file
- if [[ -d /sys/firmware/efi/efivars ]]; then
- secure_boot_file=$(find /sys/firmware/efi/efivars -maxdepth 1 -name 'SecureBoot-*' -print -quit 2>/dev/null || true)
- if [[ -n "${secure_boot_file}" && -r "${secure_boot_file}" ]]; then
- local state
- state=$(hexdump -n 1 -s 4 -e '1 "%d"' "${secure_boot_file}" 2>/dev/null || echo "0")
- if [[ "${state}" == "1" ]]; then
- log_warn "EFI Secure Boot appears to be enabled. You may need to sign VirtualBox modules manually."
- fi
- fi
- fi
+ local secure_boot_file
+ if [[ -d /sys/firmware/efi/efivars ]]; then
+ secure_boot_file=$(find /sys/firmware/efi/efivars -maxdepth 1 -name 'SecureBoot-*' -print -quit 2> /dev/null || true)
+ if [[ -n ${secure_boot_file} && -r ${secure_boot_file} ]]; then
+ local state
+ state=$(hexdump -n 1 -s 4 -e '1 "%d"' "${secure_boot_file}" 2> /dev/null || echo "0")
+ if [[ ${state} == "1" ]]; then
+ log_warn "EFI Secure Boot appears to be enabled. You may need to sign VirtualBox modules manually."
+ fi
+ fi
+ fi
}
remind_group_membership() {
- local invoking_user=${SUDO_USER:-}
- if [[ -n "${invoking_user}" && "${invoking_user}" != "root" ]]; then
- if ! id -nG "${invoking_user}" | grep -qw "vboxusers"; then
- log_warn "User ${invoking_user} is not in the vboxusers group. Add them with: sudo gpasswd -a ${invoking_user} vboxusers"
- else
- log_info "User ${invoking_user} is already in the vboxusers group."
- fi
- fi
+ local invoking_user=${SUDO_USER:-}
+ if [[ -n ${invoking_user} && ${invoking_user} != "root" ]]; then
+ if ! id -nG "${invoking_user}" | grep -qw "vboxusers"; then
+ log_warn "User ${invoking_user} is not in the vboxusers group. Add them with: sudo gpasswd -a ${invoking_user} vboxusers"
+ else
+ log_info "User ${invoking_user} is already in the vboxusers group."
+ fi
+ fi
}
main() {
- require_root
- require_pacman
+ require_root
+ require_pacman
- PACMAN_INSTALL_FLAGS=(--needed)
- PACMAN_REMOVE_FLAGS=()
- if [[ "${PACMAN_CONFIRM:-0}" == "1" ]]; then
- log_info "PACMAN_CONFIRM=1 detected; pacman will prompt for confirmation."
- else
- PACMAN_INSTALL_FLAGS+=(--noconfirm)
- PACMAN_REMOVE_FLAGS+=(--noconfirm)
- fi
+ PACMAN_INSTALL_FLAGS=(--needed)
+ PACMAN_REMOVE_FLAGS=()
+ if [[ ${PACMAN_CONFIRM:-0} == "1" ]]; then
+ log_info "PACMAN_CONFIRM=1 detected; pacman will prompt for confirmation."
+ else
+ PACMAN_INSTALL_FLAGS+=(--noconfirm)
+ PACMAN_REMOVE_FLAGS+=(--noconfirm)
+ fi
- local kernel_release host_package
- kernel_release=$(detect_kernel_release)
- log_info "Detected running kernel: ${kernel_release}"
- host_package=$(select_host_package "${kernel_release}")
- log_info "Selected VirtualBox host package: ${host_package}"
+ local kernel_release host_package
+ kernel_release=$(detect_kernel_release)
+ log_info "Detected running kernel: ${kernel_release}"
+ host_package=$(select_host_package "${kernel_release}")
+ log_info "Selected VirtualBox host package: ${host_package}"
- mapfile -t kernel_headers < <(collect_kernel_headers)
- if [[ "${host_package}" == "virtualbox-host-dkms" && ${#kernel_headers[@]} -eq 0 ]]; then
- log_warn "No matching kernel headers detected. Ensure you've installed headers for your kernel so DKMS can build modules."
- fi
+ mapfile -t kernel_headers < <(collect_kernel_headers)
+ if [[ ${host_package} == "virtualbox-host-dkms" && ${#kernel_headers[@]} -eq 0 ]]; then
+ log_warn "No matching kernel headers detected. Ensure you've installed headers for your kernel so DKMS can build modules."
+ fi
- maybe_remove_conflicting_host_packages "${host_package}"
- install_packages "${host_package}" "${kernel_headers[@]}"
- rebuild_virtualbox_modules "${host_package}"
- reload_virtualbox_modules
- warn_if_secure_boot_enabled
- remind_group_membership
+ maybe_remove_conflicting_host_packages "${host_package}"
+ install_packages "${host_package}" "${kernel_headers[@]}"
+ rebuild_virtualbox_modules "${host_package}"
+ reload_virtualbox_modules
+ warn_if_secure_boot_enabled
+ remind_group_membership
- log_info "VirtualBox installation and driver setup complete."
+ log_info "VirtualBox installation and driver setup complete."
}
main "$@"
diff --git a/scripts/fixes/nvidia_troubleshoot.sh b/scripts/fixes/nvidia_troubleshoot.sh
index f11e3f1..666cc41 100755
--- a/scripts/fixes/nvidia_troubleshoot.sh
+++ b/scripts/fixes/nvidia_troubleshoot.sh
@@ -3,40 +3,40 @@
# Script to disable NVIDIA GSP firmware and apply comprehensive NVIDIA fixes
# This addresses GSP issues, mesh shaders, OpenGL problems, and other NVIDIA issues
-set -e # Exit on any error
+set -e # Exit on any error
# Default to non-interactive mode
INTERACTIVE_MODE=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
- case $1 in
- -i|--interactive)
- INTERACTIVE_MODE=true
- shift
- ;;
- -h|--help)
- echo "Usage: $0 [OPTIONS]"
- echo "Options:"
- echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
- echo " -h, --help Show this help message"
- exit 0
- ;;
- *)
- echo "Unknown option: $1"
- echo "Use -h or --help for usage information"
- exit 1
- ;;
- esac
+ case $1 in
+ -i | --interactive)
+ INTERACTIVE_MODE=true
+ shift
+ ;;
+ -h | --help)
+ echo "Usage: $0 [OPTIONS]"
+ echo "Options:"
+ echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
+ echo " -h, --help Show this help message"
+ exit 0
+ ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Use -h or --help for usage information"
+ exit 1
+ ;;
+ esac
done
# Function to check and request sudo privileges
check_sudo() {
- if [[ $EUID -ne 0 ]]; then
- echo "This script requires sudo privileges to modify system files."
- echo "Requesting sudo access..."
- exec sudo "$0" "$@"
- fi
+ if [[ $EUID -ne 0 ]]; then
+ echo "This script requires sudo privileges to modify system files."
+ echo "Requesting sudo access..."
+ exec sudo "$0" "$@"
+ fi
}
# Check for sudo privileges after argument parsing
@@ -47,15 +47,15 @@ echo "=================================================="
echo "Current Date: $(date)"
echo "User: $USER"
echo "Original user: ${SUDO_USER:-$USER}"
-if [[ "$INTERACTIVE_MODE" == "true" ]]; then
- echo "Mode: Interactive (prompts enabled)"
+if [[ $INTERACTIVE_MODE == "true" ]]; then
+ echo "Mode: Interactive (prompts enabled)"
else
- echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
+ echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
fi
# Check if nvidia module is loaded
if ! lsmod | grep -q nvidia; then
- echo "Warning: NVIDIA module not currently loaded"
+ echo "Warning: NVIDIA module not currently loaded"
fi
# Create modprobe configuration directory if it doesn't exist
@@ -78,32 +78,32 @@ echo "✓ Configuration written to: $CONFIG_FILE"
# Function to backup file if it exists
backup_file() {
- local file="$1"
- if [[ -f "$file" ]]; then
- cp "$file" "$file.backup.$(date +%Y%m%d_%H%M%S)"
- echo "✓ Backed up $file"
- fi
+ local file="$1"
+ if [[ -f $file ]]; then
+ cp "$file" "$file.backup.$(date +%Y%m%d_%H%M%S)"
+ echo "✓ Backed up $file"
+ fi
}
# Function to add or update xorg.conf for RenderAccel
configure_xorg() {
- echo ""
- echo "2. Configuring Xorg Settings..."
- echo "==============================="
-
- XORG_CONF="/etc/X11/xorg.conf"
- XORG_CONF_D="/etc/X11/xorg.conf.d"
- NVIDIA_CONF="$XORG_CONF_D/20-nvidia.conf"
-
- # Create xorg.conf.d directory if it doesn't exist
- mkdir -p "$XORG_CONF_D"
-
- # Backup existing xorg.conf if it exists
- backup_file "$XORG_CONF"
- backup_file "$NVIDIA_CONF"
-
- # Create NVIDIA-specific configuration
- cat > "$NVIDIA_CONF" << EOF
+ echo ""
+ echo "2. Configuring Xorg Settings..."
+ echo "==============================="
+
+ XORG_CONF="/etc/X11/xorg.conf"
+ XORG_CONF_D="/etc/X11/xorg.conf.d"
+ NVIDIA_CONF="$XORG_CONF_D/20-nvidia.conf"
+
+ # Create xorg.conf.d directory if it doesn't exist
+ mkdir -p "$XORG_CONF_D"
+
+ # Backup existing xorg.conf if it exists
+ backup_file "$XORG_CONF"
+ backup_file "$NVIDIA_CONF"
+
+ # Create NVIDIA-specific configuration
+ cat > "$NVIDIA_CONF" << EOF
# NVIDIA configuration with RenderAccel disabled
# Created by nvidia_troubleshoot.sh on $(date)
Section "Device"
@@ -112,103 +112,107 @@ Section "Device"
Option "RenderAccel" "false"
EndSection
EOF
-
- echo "✓ Created $NVIDIA_CONF with RenderAccel disabled"
+
+ echo "✓ Created $NVIDIA_CONF with RenderAccel disabled"
}
# Function to add GCC mismatch workaround
configure_gcc_workaround() {
- echo ""
- echo "3. Configuring GCC Mismatch Workaround..."
- echo "=========================================="
-
- PROFILE_FILE="/etc/profile"
- backup_file "$PROFILE_FILE"
-
- # Check if IGNORE_CC_MISMATCH is already set
- if ! grep -q "IGNORE_CC_MISMATCH" "$PROFILE_FILE"; then
- echo "" >> "$PROFILE_FILE"
- echo "# NVIDIA GCC version mismatch workaround" >> "$PROFILE_FILE"
- echo "# Added by nvidia_troubleshoot.sh on $(date)" >> "$PROFILE_FILE"
- echo "export IGNORE_CC_MISMATCH=1" >> "$PROFILE_FILE"
- echo "✓ Added IGNORE_CC_MISMATCH=1 to $PROFILE_FILE"
- else
- echo "✓ IGNORE_CC_MISMATCH already configured in $PROFILE_FILE"
- fi
+ echo ""
+ echo "3. Configuring GCC Mismatch Workaround..."
+ echo "=========================================="
+
+ local PROFILE_FILE="/etc/profile"
+ local timestamp
+ timestamp=$(date)
+ backup_file "$PROFILE_FILE"
+
+ # Check if IGNORE_CC_MISMATCH is already set
+ if ! grep -q "IGNORE_CC_MISMATCH" "$PROFILE_FILE"; then
+ {
+ printf '\n'
+ printf '# NVIDIA GCC version mismatch workaround\n'
+ printf '# Added by nvidia_troubleshoot.sh on %s\n' "$timestamp"
+ printf 'export IGNORE_CC_MISMATCH=1\n'
+ } >> "$PROFILE_FILE"
+ echo "✓ Added IGNORE_CC_MISMATCH=1 to $PROFILE_FILE"
+ else
+ echo "✓ IGNORE_CC_MISMATCH already configured in $PROFILE_FILE"
+ fi
}
# Function to install pyroveil for mesh shader issues
install_pyroveil() {
- echo ""
- echo "4. Pyroveil Setup for Mesh Shader Issues..."
- echo "==========================================="
-
- local user_home="/home/$SUDO_USER"
- local pyroveil_dir="$user_home/pyroveil"
-
- echo "Mesh shaders have poor support on NVIDIA drivers, causing issues in games"
- echo "like Final Fantasy VII Rebirth. Pyroveil can work around these problems."
- echo ""
-
- local install_pyroveil=true
-
- if [[ "$INTERACTIVE_MODE" == "true" ]]; then
- read -p "Would you like to install Pyroveil? (y/N): " -n 1 -r
- echo
- if [[ ! $REPLY =~ ^[Yy]$ ]]; then
- install_pyroveil=false
- fi
- else
- echo "Auto-installing Pyroveil (use --interactive to prompt)"
+ echo ""
+ echo "4. Pyroveil Setup for Mesh Shader Issues..."
+ echo "==========================================="
+
+ local user_home="/home/$SUDO_USER"
+ local pyroveil_dir="$user_home/pyroveil"
+
+ echo "Mesh shaders have poor support on NVIDIA drivers, causing issues in games"
+ echo "like Final Fantasy VII Rebirth. Pyroveil can work around these problems."
+ echo ""
+
+ local install_pyroveil=true
+
+ if [[ $INTERACTIVE_MODE == "true" ]]; then
+ read -p "Would you like to install Pyroveil? (y/N): " -n 1 -r
+ echo
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+ install_pyroveil=false
fi
-
- if [[ "$install_pyroveil" == "true" ]]; then
- # Check for required dependencies
- local missing_deps=()
-
- for dep in git cmake ninja gcc; do
- if ! command -v $dep &> /dev/null; then
- missing_deps+=($dep)
- fi
- done
-
- if [[ ${#missing_deps[@]} -gt 0 ]]; then
- echo "Missing dependencies: ${missing_deps[*]}"
- echo "Please install them first. On Arch Linux:"
- echo "pacman -S base-devel git cmake ninja"
- return 1
- fi
-
- # Clone and build pyroveil as the original user
- echo "Installing Pyroveil to $pyroveil_dir..."
-
- if [[ -d "$pyroveil_dir" ]]; then
- echo "Pyroveil directory already exists. Updating..."
- sudo -u "$SUDO_USER" bash -c "cd '$pyroveil_dir' && git pull"
- else
- sudo -u "$SUDO_USER" git clone https://github.com/HansKristian-Work/pyroveil.git "$pyroveil_dir"
- fi
-
- sudo -u "$SUDO_USER" bash -c "
+ else
+ echo "Auto-installing Pyroveil (use --interactive to prompt)"
+ fi
+
+ if [[ $install_pyroveil == "true" ]]; then
+ # Check for required dependencies
+ local missing_deps=()
+
+ for dep in git cmake ninja gcc; do
+ if ! command -v "$dep" &> /dev/null; then
+ missing_deps+=("$dep")
+ fi
+ done
+
+ if [[ ${#missing_deps[@]} -gt 0 ]]; then
+ echo "Missing dependencies: ${missing_deps[*]}"
+ echo "Please install them first. On Arch Linux:"
+ echo "pacman -S base-devel git cmake ninja"
+ return 1
+ fi
+
+ # Clone and build pyroveil as the original user
+ echo "Installing Pyroveil to $pyroveil_dir..."
+
+ if [[ -d $pyroveil_dir ]]; then
+ echo "Pyroveil directory already exists. Updating..."
+ sudo -u "$SUDO_USER" bash -c "cd '$pyroveil_dir' && git pull"
+ else
+ sudo -u "$SUDO_USER" git clone https://github.com/HansKristian-Work/pyroveil.git "$pyroveil_dir"
+ fi
+
+ sudo -u "$SUDO_USER" bash -c "
cd '$pyroveil_dir'
git submodule update --init
cmake . -Bbuild -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$user_home/.local
ninja -C build install
"
-
- echo "✓ Pyroveil installed successfully"
- echo ""
- echo "To use Pyroveil with games that have mesh shader issues:"
- echo "1. For Final Fantasy VII Rebirth:"
- echo " PYROVEIL=1 PYROVEIL_CONFIG=$pyroveil_dir/hacks/ffvii-rebirth-nvidia/pyroveil.json %command%"
- echo ""
- echo "2. For Steam games, add to launch options:"
- echo " PYROVEIL=1 PYROVEIL_CONFIG=/path/to/config/pyroveil.json %command%"
- echo ""
- echo "Available configs in: $pyroveil_dir/hacks/"
-
- # Create a helper script
- cat > "$user_home/run-with-pyroveil.sh" << EOF
+
+ echo "✓ Pyroveil installed successfully"
+ echo ""
+ echo "To use Pyroveil with games that have mesh shader issues:"
+ echo "1. For Final Fantasy VII Rebirth:"
+ echo " PYROVEIL=1 PYROVEIL_CONFIG=$pyroveil_dir/hacks/ffvii-rebirth-nvidia/pyroveil.json %command%"
+ echo ""
+ echo "2. For Steam games, add to launch options:"
+ echo " PYROVEIL=1 PYROVEIL_CONFIG=/path/to/config/pyroveil.json %command%"
+ echo ""
+ echo "Available configs in: $pyroveil_dir/hacks/"
+
+ # Create a helper script
+ cat > "$user_home/run-with-pyroveil.sh" << EOF
#!/bin/bash
# Helper script to run games with Pyroveil
# Usage: ./run-with-pyroveil.sh
@@ -233,88 +237,89 @@ echo "Config file: \$PYROVEIL_CONFIG"
exec "\$@"
EOF
-
- chown "$SUDO_USER:$SUDO_USER" "$user_home/run-with-pyroveil.sh"
- chmod +x "$user_home/run-with-pyroveil.sh"
- echo "✓ Created helper script: $user_home/run-with-pyroveil.sh"
-
- else
- echo "Skipping Pyroveil installation"
- echo "Note: You can manually install it later for mesh shader issues"
- fi
+
+ chown "$SUDO_USER:$SUDO_USER" "$user_home/run-with-pyroveil.sh"
+ chmod +x "$user_home/run-with-pyroveil.sh"
+ echo "✓ Created helper script: $user_home/run-with-pyroveil.sh"
+
+ else
+ echo "Skipping Pyroveil installation"
+ echo "Note: You can manually install it later for mesh shader issues"
+ fi
}
# Function to check for kernel parameter modifications
suggest_kernel_params() {
- echo ""
- echo "5. Kernel Parameter Recommendations..."
- echo "====================================="
-
- echo "NVIDIA Driver Issues and Recommended Kernel Parameters:"
- echo ""
- echo "A) For 'conflicting memory type' or 'failed to allocate primary buffer' errors"
- echo " (especially with nvidia-96xx drivers):"
- echo " → Add 'nopat' to kernel parameters"
- echo ""
- echo "B) For OpenGL visual glitches, hangs, and errors with modern CPUs:"
- echo " → Consider disabling micro-op cache in BIOS settings"
- echo " → This affects Intel Sandy Bridge (2011+) and AMD Zen (2017+) CPUs"
- echo " → Helps with severe graphical glitches in Xwayland applications"
- echo " → Note: Disabling micro-op cache reduces CPU performance"
- echo ""
- echo "To add kernel parameters:"
- echo "1. Edit /etc/default/grub"
- echo "2. Add parameters to GRUB_CMDLINE_LINUX_DEFAULT"
- echo "3. Run: grub-mkconfig -o /boot/grub/grub.cfg"
- echo "4. Reboot"
- echo ""
- echo "Example GRUB_CMDLINE_LINUX_DEFAULT line:"
- echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nopat"'
-
- # Check current CPU for micro-op cache relevance
- echo ""
- echo "CPU Information (for micro-op cache consideration):"
- if command -v lscpu &> /dev/null; then
- local cpu_info=$(lscpu | grep "Model name" | cut -d: -f2 | xargs)
- echo "Current CPU: $cpu_info"
-
- if echo "$cpu_info" | grep -qi "intel"; then
- echo "→ Intel CPU detected. Sandy Bridge (2011) and later have micro-op cache"
- elif echo "$cpu_info" | grep -qi "amd"; then
- echo "→ AMD CPU detected. Zen (2017) and later have micro-op cache"
- fi
+ echo ""
+ echo "5. Kernel Parameter Recommendations..."
+ echo "====================================="
+
+ echo "NVIDIA Driver Issues and Recommended Kernel Parameters:"
+ echo ""
+ echo "A) For 'conflicting memory type' or 'failed to allocate primary buffer' errors"
+ echo " (especially with nvidia-96xx drivers):"
+ echo " → Add 'nopat' to kernel parameters"
+ echo ""
+ echo "B) For OpenGL visual glitches, hangs, and errors with modern CPUs:"
+ echo " → Consider disabling micro-op cache in BIOS settings"
+ echo " → This affects Intel Sandy Bridge (2011+) and AMD Zen (2017+) CPUs"
+ echo " → Helps with severe graphical glitches in Xwayland applications"
+ echo " → Note: Disabling micro-op cache reduces CPU performance"
+ echo ""
+ echo "To add kernel parameters:"
+ echo "1. Edit /etc/default/grub"
+ echo "2. Add parameters to GRUB_CMDLINE_LINUX_DEFAULT"
+ echo "3. Run: grub-mkconfig -o /boot/grub/grub.cfg"
+ echo "4. Reboot"
+ echo ""
+ echo "Example GRUB_CMDLINE_LINUX_DEFAULT line:"
+ echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nopat"'
+
+ # Check current CPU for micro-op cache relevance
+ echo ""
+ echo "CPU Information (for micro-op cache consideration):"
+ if command -v lscpu &> /dev/null; then
+ local cpu_info
+ cpu_info=$(lscpu | grep "Model name" | cut -d: -f2 | xargs)
+ echo "Current CPU: $cpu_info"
+
+ if echo "$cpu_info" | grep -qi "intel"; then
+ echo "→ Intel CPU detected. Sandy Bridge (2011) and later have micro-op cache"
+ elif echo "$cpu_info" | grep -qi "amd"; then
+ echo "→ AMD CPU detected. Zen (2017) and later have micro-op cache"
fi
+ fi
}
# Function to suggest desktop environment settings
suggest_desktop_settings() {
+ echo ""
+ echo "6. Desktop Environment Recommendations..."
+ echo "========================================"
+
+ echo "For fullscreen application freezing/crashing issues:"
+ echo ""
+ echo "Enable Display Compositing and Direct fullscreen rendering:"
+ echo ""
+ echo "• KDE Plasma:"
+ echo " System Settings → Display and Monitor → Compositor"
+ echo " → Enable compositor + Enable direct rendering for fullscreen windows"
+ echo ""
+ echo "• GNOME:"
+ echo " Use Extensions or dconf-editor to enable compositing features"
+ echo ""
+ echo "• XFCE:"
+ echo " Settings → Window Manager Tweaks → Compositor"
+ echo " → Enable display compositing"
+ echo ""
+ echo "• Cinnamon:"
+ echo " System Settings → Effects → Enable desktop effects"
+
+ # Detect current desktop environment
+ if [[ -n $XDG_CURRENT_DESKTOP ]]; then
echo ""
- echo "6. Desktop Environment Recommendations..."
- echo "========================================"
-
- echo "For fullscreen application freezing/crashing issues:"
- echo ""
- echo "Enable Display Compositing and Direct fullscreen rendering:"
- echo ""
- echo "• KDE Plasma:"
- echo " System Settings → Display and Monitor → Compositor"
- echo " → Enable compositor + Enable direct rendering for fullscreen windows"
- echo ""
- echo "• GNOME:"
- echo " Use Extensions or dconf-editor to enable compositing features"
- echo ""
- echo "• XFCE:"
- echo " Settings → Window Manager Tweaks → Compositor"
- echo " → Enable display compositing"
- echo ""
- echo "• Cinnamon:"
- echo " System Settings → Effects → Enable desktop effects"
-
- # Detect current desktop environment
- if [[ -n "$XDG_CURRENT_DESKTOP" ]]; then
- echo ""
- echo "Detected desktop environment: $XDG_CURRENT_DESKTOP"
- fi
+ echo "Detected desktop environment: $XDG_CURRENT_DESKTOP"
+ fi
}
# Apply all configurations
@@ -327,13 +332,13 @@ echo ""
echo "7. Regenerating Initramfs..."
echo "============================"
if command -v mkinitcpio &> /dev/null; then
- mkinitcpio -P
- echo "✓ Initramfs regenerated with mkinitcpio"
+ mkinitcpio -P
+ echo "✓ Initramfs regenerated with mkinitcpio"
elif command -v dracut &> /dev/null; then
- dracut --force
- echo "✓ Initramfs regenerated with dracut"
+ dracut --force
+ echo "✓ Initramfs regenerated with dracut"
else
- echo "Warning: Could not find mkinitcpio or dracut. You may need to manually regenerate initramfs."
+ echo "Warning: Could not find mkinitcpio or dracut. You may need to manually regenerate initramfs."
fi
# Display all recommendations
@@ -349,7 +354,7 @@ echo "✓ GSP firmware disabled"
echo "✓ RenderAccel disabled in Xorg configuration"
echo "✓ GCC version mismatch workaround added"
if [[ -d "/home/$SUDO_USER/pyroveil" ]]; then
- echo "✓ Pyroveil installed for mesh shader issues"
+ echo "✓ Pyroveil installed for mesh shader issues"
fi
echo "✓ Initramfs regenerated"
echo ""
@@ -359,4 +364,4 @@ echo "• Configure desktop environment compositing settings"
echo "• Add kernel parameters if needed (nopat for memory issues)"
echo ""
echo "IMPORTANT: You must reboot for changes to take effect!"
-echo "After reboot, verify GSP with: cat /proc/driver/nvidia/params | grep EnableGpuFirmware"
\ No newline at end of file
+echo "After reboot, verify GSP with: cat /proc/driver/nvidia/params | grep EnableGpuFirmware"
diff --git a/scripts/meta/shell_check.sh b/scripts/meta/shell_check.sh
index 8b7d327..6fd4be7 100755
--- a/scripts/meta/shell_check.sh
+++ b/scripts/meta/shell_check.sh
@@ -25,24 +25,20 @@ INSTALL_ONLY="false"
LIST_ONLY="false"
VERBOSE="false"
-log() {
- printf '%s\n' "$*"
-}
-
log_info() {
- printf '\033[1;34m[INFO]\033[0m %s\n' "$*"
+ printf '\033[1;34m[INFO]\033[0m %s\n' "$*"
}
log_warn() {
- printf '\033[1;33m[WARN]\033[0m %s\n' "$*"
+ printf '\033[1;33m[WARN]\033[0m %s\n' "$*"
}
log_error() {
- printf '\033[1;31m[ERROR]\033[0m %s\n' "$*" >&2
+ printf '\033[1;31m[ERROR]\033[0m %s\n' "$*" >&2
}
usage() {
- cat </dev/null 2>&1; }
+is_cmd() { command -v "$1" > /dev/null 2>&1; }
is_arch() { is_cmd pacman; }
have_aur_helper() { is_cmd yay || is_cmd paru; }
install_if_missing() {
- local pkg cmd
- pkg="$1"; cmd="$2"
- if is_cmd "$cmd"; then
- [[ "$VERBOSE" == "true" ]] && log_info "Found $cmd"
- return 0
- fi
+ local pkg cmd
+ pkg="$1"
+ cmd="$2"
+ if is_cmd "$cmd"; then
+ [[ $VERBOSE == "true" ]] && log_info "Found $cmd"
+ return 0
+ fi
- if [[ "$SKIP_INSTALL" == "true" ]]; then
- log_warn "Skipping install of $pkg ($cmd not found)"
- return 1
- fi
+ if [[ $SKIP_INSTALL == "true" ]]; then
+ log_warn "Skipping install of $pkg ($cmd not found)"
+ return 1
+ fi
- if is_arch; then
- log_info "Installing $pkg via pacman..."
- if ! sudo pacman -S --needed --noconfirm "$pkg"; then
- log_warn "Failed to install $pkg via pacman."
- return 1
- fi
- return 0
- else
- log_warn "Non-Arch system detected. Please install '$pkg' manually."
- return 1
- fi
+ if is_arch; then
+ log_info "Installing $pkg via pacman..."
+ if ! sudo pacman -S --needed --noconfirm "$pkg"; then
+ log_warn "Failed to install $pkg via pacman."
+ return 1
+ fi
+ return 0
+ else
+ log_warn "Non-Arch system detected. Please install '$pkg' manually."
+ return 1
+ fi
}
install_linters() {
- local ok=0
+ local ok=0
- # Core linters
- install_if_missing shellcheck shellcheck || ok=1
- install_if_missing shfmt shfmt || ok=1
+ # Core linters
+ install_if_missing shellcheck shellcheck || ok=1
+ install_if_missing shfmt shfmt || ok=1
- # Optional linters (best-effort)
- # checkbashisms may be in repos or AUR; try pacman first, then AUR helper
- if ! is_cmd checkbashisms; then
- if is_arch; then
- if ! sudo pacman -S --needed --noconfirm checkbashisms 2>/dev/null; then
- if have_aur_helper; then
- log_info "Installing checkbashisms from AUR (requires yay/paru)..."
- if is_cmd yay; then yay -S --noconfirm checkbashisms || true; fi
- if is_cmd paru; then paru -S --noconfirm checkbashisms || true; fi
- else
- log_warn "checkbashisms not installed (no AUR helper)."
- fi
- fi
- fi
- fi
+ # Optional linters (best-effort)
+ # checkbashisms may be in repos or AUR; try pacman first, then AUR helper
+ if ! is_cmd checkbashisms; then
+ if is_arch; then
+ if ! sudo pacman -S --needed --noconfirm checkbashisms 2> /dev/null; then
+ if have_aur_helper; then
+ log_info "Installing checkbashisms from AUR (requires yay/paru)..."
+ if is_cmd yay; then yay -S --noconfirm checkbashisms || true; fi
+ if is_cmd paru; then paru -S --noconfirm checkbashisms || true; fi
+ else
+ log_warn "checkbashisms not installed (no AUR helper)."
+ fi
+ fi
+ fi
+ fi
- # bashate (python-based), typically available as python-bashate in AUR
- if ! is_cmd bashate; then
- if is_arch && have_aur_helper; then
- log_info "Installing bashate from AUR (requires yay/paru)..."
- if is_cmd yay; then yay -S --noconfirm python-bashate || true; fi
- if is_cmd paru; then paru -S --noconfirm python-bashate || true; fi
- else
- # Try pip if user has it and wants to
- if is_cmd pipx; then
- log_info "Installing bashate via pipx..."
- pipx install bashate || true
- elif is_cmd pip3; then
- log_info "Installing bashate via pip (user)..."
- pip3 install --user bashate || true
- else
- log_warn "bashate not installed (no AUR helper or pip available)."
- fi
- fi
- fi
+ # bashate (python-based), typically available as python-bashate in AUR
+ if ! is_cmd bashate; then
+ if is_arch && have_aur_helper; then
+ log_info "Installing bashate from AUR (requires yay/paru)..."
+ if is_cmd yay; then yay -S --noconfirm python-bashate || true; fi
+ if is_cmd paru; then paru -S --noconfirm python-bashate || true; fi
+ else
+ # Try pip if user has it and wants to
+ if is_cmd pipx; then
+ log_info "Installing bashate via pipx..."
+ pipx install bashate || true
+ elif is_cmd pip3; then
+ log_info "Installing bashate via pip (user)..."
+ pip3 install --user bashate || true
+ else
+ log_warn "bashate not installed (no AUR helper or pip available)."
+ fi
+ fi
+ fi
- return "$ok"
+ return "$ok"
}
TMPDIR=$(mktemp -d)
-cleanup() { rm -rf "$TMPDIR"; }
-trap cleanup EXIT
+trap 'rm -rf "${TMPDIR:-}"' EXIT
ABS_FILES_Z="$TMPDIR/files_abs.zlist"
REL_FILES_Z="$TMPDIR/files_rel.zlist"
discover_shell_files() {
- local base="$1"
- local -a all
- all=()
+ local base="$1"
+ local -a all
+ all=()
- if git -C "$base" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
- while IFS= read -r -d '' f; do all+=("$f"); done < <(git -C "$base" ls-files -z)
- while IFS= read -r -d '' f; do all+=("$f"); done < <(git -C "$base" ls-files --others --exclude-standard -z)
- else
- while IFS= read -r -d '' f; do
- # trim leading ./ to keep consistent style with git paths
- f="${f#./}"
- f="${f#${base}/}"
- all+=("$f")
- done < <(find "$base" -type f -print0)
- fi
+ if git -C "$base" rev-parse --is-inside-work-tree > /dev/null 2>&1; then
+ while IFS= read -r -d '' f; do all+=("$f"); done < <(git -C "$base" ls-files -z)
+ while IFS= read -r -d '' f; do all+=("$f"); done < <(git -C "$base" ls-files --others --exclude-standard -z)
+ else
+ while IFS= read -r -d '' f; do
+ # trim leading ./ to keep consistent style with git paths
+ f="${f#./}"
+ f="${f#"${base}"/}"
+ all+=("$f")
+ done < <(find "$base" -type f -print0)
+ fi
- local -a shells
- shells=()
+ local -a shells
+ shells=()
- for rel in "${all[@]}"; do
- # skip binary-ish or huge files quickly by extension heuristic
- case "$rel" in
- *.png|*.jpg|*.jpeg|*.gif|*.ico|*.pdf|*.svg|*.zip|*.tar|*.gz|*.xz|*.7z|*.so|*.o|*.bin)
- continue ;;
- esac
+ for rel in "${all[@]}"; do
+ # skip binary-ish or huge files quickly by extension heuristic
+ case "$rel" in
+ *.png | *.jpg | *.jpeg | *.gif | *.ico | *.pdf | *.svg | *.zip | *.tar | *.gz | *.xz | *.7z | *.so | *.o | *.bin)
+ continue
+ ;;
+ esac
- local abs="$base/$rel"
- [[ -f "$abs" && -r "$abs" ]] || continue
+ local abs="$base/$rel"
+ [[ -f $abs && -r $abs ]] || continue
- if [[ "$rel" == *.sh || "$rel" == *.bash || "$rel" == *.zsh ]]; then
- shells+=("$rel")
- continue
- fi
+ if [[ $rel == *.sh || $rel == *.bash || $rel == *.zsh ]]; then
+ shells+=("$rel")
+ continue
+ fi
- # Check shebang
- local first
- first=$(head -n 1 -- "$abs" 2>/dev/null || true)
- if [[ "$first" =~ ^#! && "$first" =~ (ba|z|d|k)?sh ]]; then
- shells+=("$rel")
- continue
- fi
+ # Check shebang
+ local first
+ first=$(head -n 1 -- "$abs" 2> /dev/null || true)
+ if [[ $first =~ ^#! && $first =~ (ba|z|d|k)?sh ]]; then
+ shells+=("$rel")
+ continue
+ fi
- # Also catch executable files with shell shebang even without extension
- if [[ -x "$abs" ]]; then
- if [[ "$first" =~ ^#! && "$first" =~ (ba|z|d|k)?sh ]]; then
- shells+=("$rel")
- fi
- fi
- done
+ # Also catch executable files with shell shebang even without extension
+ if [[ -x $abs ]]; then
+ if [[ $first =~ ^#! && $first =~ (ba|z|d|k)?sh ]]; then
+ shells+=("$rel")
+ fi
+ fi
+ done
- # write lists
- : >"$REL_FILES_Z"
- : >"$ABS_FILES_Z"
- for rel in "${shells[@]}"; do
- printf '%s\0' "$rel" >> "$REL_FILES_Z"
- printf '%s\0' "$base/$rel" >> "$ABS_FILES_Z"
- done
+ # write lists
+ : > "$REL_FILES_Z"
+ : > "$ABS_FILES_Z"
+ for rel in "${shells[@]}"; do
+ printf '%s\0' "$rel" >> "$REL_FILES_Z"
+ printf '%s\0' "$base/$rel" >> "$ABS_FILES_Z"
+ done
}
print_file_list() {
- local count
- count=$(tr -cd '\0' < "$REL_FILES_Z" | wc -c)
- log_info "Discovered $count shell file(s) under $ROOT_DIR"
- if [[ "$VERBOSE" == "true" ]]; then
- tr '\0' '\n' < "$REL_FILES_Z" | sed 's/^/ - /'
- fi
+ local count
+ count=$(tr -cd '\0' < "$REL_FILES_Z" | wc -c)
+ log_info "Discovered $count shell file(s) under $ROOT_DIR"
+ if [[ $VERBOSE == "true" ]]; then
+ tr '\0' '\n' < "$REL_FILES_Z" | sed 's/^/ - /'
+ fi
}
run_linters() {
- local issues=0
- local count
- count=$(tr -cd '\0' < "$ABS_FILES_Z" | wc -c)
- if [[ "$count" -eq 0 ]]; then
- log_warn "No shell files found to lint."
- return 0
- fi
+ local issues=0
+ local count
+ count=$(tr -cd '\0' < "$ABS_FILES_Z" | wc -c)
+ if [[ $count -eq 0 ]]; then
+ log_warn "No shell files found to lint."
+ return 0
+ fi
- mapfile -d '' -t FILES < "$ABS_FILES_Z"
+ mapfile -d '' -t FILES < "$ABS_FILES_Z"
- log_info "Running shellcheck..."
- local sc_out="$TMPDIR/shellcheck.txt"
- if is_cmd shellcheck; then
- if ! shellcheck -x -S style "${FILES[@]}" >"$sc_out" 2>&1; then
- issues=$((issues+1))
- fi
- else
- log_warn "shellcheck not found; skipping"
- fi
+ log_info "Running shellcheck..."
+ local sc_out="$TMPDIR/shellcheck.txt"
+ if is_cmd shellcheck; then
+ if ! shellcheck -x -S style "${FILES[@]}" > "$sc_out" 2>&1; then
+ issues=$((issues + 1))
+ fi
+ else
+ log_warn "shellcheck not found; skipping"
+ fi
- log_info "Running shfmt (diff mode)..."
- local shfmt_out="$TMPDIR/shfmt.diff"
- if is_cmd shfmt; then
- if ! shfmt -d -i 2 -ci -sr -s "${FILES[@]}" >"$shfmt_out" 2>&1; then
- # shfmt returns non-zero when diff exists
- issues=$((issues+1))
- fi
- else
- log_warn "shfmt not found; skipping"
- fi
+ log_info "Running shfmt (diff mode)..."
+ local shfmt_out="$TMPDIR/shfmt.diff"
+ if is_cmd shfmt; then
+ if ! shfmt -d -i 2 -ci -sr -s "${FILES[@]}" > "$shfmt_out" 2>&1; then
+ # shfmt returns non-zero when diff exists
+ issues=$((issues + 1))
+ fi
+ else
+ log_warn "shfmt not found; skipping"
+ fi
- log_info "Running checkbashisms (optional)..."
- local cbi_out="$TMPDIR/checkbashisms.txt"
- if is_cmd checkbashisms; then
- # checkbashisms exits 0 if OK, 1 if issues
- if ! checkbashisms "${FILES[@]}" >"$cbi_out" 2>&1; then
- issues=$((issues+1))
- fi
- else
- log_warn "checkbashisms not found; skipping"
- fi
+ log_info "Running checkbashisms (optional)..."
+ local cbi_out="$TMPDIR/checkbashisms.txt"
+ local cbi_status=0
+ if is_cmd checkbashisms; then
+ # checkbashisms exits 0 if OK, 1 if issues, other codes for tool warnings
+ checkbashisms "${FILES[@]}" > "$cbi_out" 2>&1
+ cbi_status=$?
+ if [[ $cbi_status -eq 1 ]]; then
+ issues=$((issues + 1))
+ elif [[ $cbi_status -ne 0 ]]; then
+ log_warn "checkbashisms exited with status $cbi_status (treated as warning)"
+ fi
+ else
+ log_warn "checkbashisms not found; skipping"
+ fi
- log_info "Running bash/zsh/sh syntax checks (-n)..."
- local bash_out="$TMPDIR/bash_syntax.txt"
- local zsh_out="$TMPDIR/zsh_syntax.txt"
- local sh_out="$TMPDIR/sh_syntax.txt"
+ log_info "Running bash/zsh/sh syntax checks (-n)..."
+ local bash_out="$TMPDIR/bash_syntax.txt"
+ local zsh_out="$TMPDIR/zsh_syntax.txt"
+ local sh_out="$TMPDIR/sh_syntax.txt"
- # Partition files by shebang for better accuracy
- local -a BASH_FILES ZSH_FILES SH_FILES
- BASH_FILES=(); ZSH_FILES=(); SH_FILES=()
- for f in "${FILES[@]}"; do
- local first
- first=$(head -n 1 -- "$f" 2>/dev/null || true)
- if [[ "$first" =~ bash ]]; then
- BASH_FILES+=("$f")
- elif [[ "$first" =~ zsh ]]; then
- ZSH_FILES+=("$f")
- else
- SH_FILES+=("$f")
- fi
- done
+ # Partition files by shebang for better accuracy
+ local -a BASH_FILES ZSH_FILES SH_FILES
+ BASH_FILES=()
+ ZSH_FILES=()
+ SH_FILES=()
+ for f in "${FILES[@]}"; do
+ local first
+ first=$(head -n 1 -- "$f" 2> /dev/null || true)
+ if [[ $first =~ bash ]]; then
+ BASH_FILES+=("$f")
+ elif [[ $first =~ zsh ]]; then
+ ZSH_FILES+=("$f")
+ else
+ SH_FILES+=("$f")
+ fi
+ done
- if [[ ${#BASH_FILES[@]} -gt 0 ]] && is_cmd bash; then
- if ! bash -n "${BASH_FILES[@]}" 2>"$bash_out"; then
- issues=$((issues+1))
- fi
- fi
- if [[ ${#ZSH_FILES[@]} -gt 0 ]] && is_cmd zsh; then
- if ! zsh -n "${ZSH_FILES[@]}" 2>"$zsh_out"; then
- issues=$((issues+1))
- fi
- fi
- # prefer dash if present for /bin/sh style
- if [[ ${#SH_FILES[@]} -gt 0 ]]; then
- if is_cmd dash; then
- if ! dash -n "${SH_FILES[@]}" 2>"$sh_out"; then
- issues=$((issues+1))
- fi
- elif is_cmd sh; then
- if ! sh -n "${SH_FILES[@]}" 2>"$sh_out"; then
- issues=$((issues+1))
- fi
- fi
- fi
+ if [[ ${#BASH_FILES[@]} -gt 0 ]] && is_cmd bash; then
+ if ! bash -n "${BASH_FILES[@]}" 2> "$bash_out"; then
+ issues=$((issues + 1))
+ fi
+ fi
+ if [[ ${#ZSH_FILES[@]} -gt 0 ]] && is_cmd zsh; then
+ if ! zsh -n "${ZSH_FILES[@]}" 2> "$zsh_out"; then
+ issues=$((issues + 1))
+ fi
+ fi
+ # prefer dash if present for /bin/sh style
+ if [[ ${#SH_FILES[@]} -gt 0 ]]; then
+ if is_cmd dash; then
+ if ! dash -n "${SH_FILES[@]}" 2> "$sh_out"; then
+ issues=$((issues + 1))
+ fi
+ elif is_cmd sh; then
+ if ! sh -n "${SH_FILES[@]}" 2> "$sh_out"; then
+ issues=$((issues + 1))
+ fi
+ fi
+ fi
- echo
- log_info "========== Shell Lint Report =========="
+ echo
+ log_info "========== Shell Lint Report =========="
- if [[ -s "$sc_out" ]]; then
- printf '\n\033[1m-- shellcheck --\033[0m\n'
- cat "$sc_out"
- else
- printf '\n\033[1;32m-- shellcheck: PASS (no issues) --\033[0m\n'
- fi
+ if [[ -s $sc_out ]]; then
+ printf '\n\033[1m-- shellcheck --\033[0m\n'
+ cat "$sc_out"
+ else
+ printf '\n\033[1;32m-- shellcheck: PASS (no issues) --\033[0m\n'
+ fi
- if [[ -s "$shfmt_out" ]]; then
- printf '\n\033[1m-- shfmt (diffs found) --\033[0m\n'
- cat "$shfmt_out"
- else
- printf '\n\033[1;32m-- shfmt: PASS (formatted) --\033[0m\n'
- fi
+ if [[ -s $shfmt_out ]]; then
+ printf '\n\033[1m-- shfmt (diffs found) --\033[0m\n'
+ cat "$shfmt_out"
+ else
+ printf '\n\033[1;32m-- shfmt: PASS (formatted) --\033[0m\n'
+ fi
- if [[ -s "$cbi_out" ]]; then
- printf '\n\033[1m-- checkbashisms --\033[0m\n'
- cat "$cbi_out"
- else
- printf '\n\033[1;32m-- checkbashisms: PASS (or skipped) --\033[0m\n'
- fi
+ if [[ -s $cbi_out ]]; then
+ printf '\n\033[1m-- checkbashisms --\033[0m\n'
+ cat "$cbi_out"
+ else
+ printf '\n\033[1;32m-- checkbashisms: PASS (or skipped) --\033[0m\n'
+ fi
- if [[ -s "$bash_out" ]]; then
- printf '\n\033[1m-- bash -n (syntax) --\033[0m\n'
- cat "$bash_out"
- else
- printf '\n\033[1;32m-- bash -n: PASS (or none) --\033[0m\n'
- fi
+ if [[ -s $bash_out ]]; then
+ printf '\n\033[1m-- bash -n (syntax) --\033[0m\n'
+ cat "$bash_out"
+ else
+ printf '\n\033[1;32m-- bash -n: PASS (or none) --\033[0m\n'
+ fi
- if [[ -s "$zsh_out" ]]; then
- printf '\n\033[1m-- zsh -n (syntax) --\033[0m\n'
- cat "$zsh_out"
- else
- printf '\n\033[1;32m-- zsh -n: PASS (or none) --\033[0m\n'
- fi
+ if [[ -s $zsh_out ]]; then
+ printf '\n\033[1m-- zsh -n (syntax) --\033[0m\n'
+ cat "$zsh_out"
+ else
+ printf '\n\033[1;32m-- zsh -n: PASS (or none) --\033[0m\n'
+ fi
- if [[ -s "$sh_out" ]]; then
- printf '\n\033[1m-- sh/dash -n (syntax) --\033[0m\n'
- cat "$sh_out"
- else
- printf '\n\033[1;32m-- sh/dash -n: PASS (or none) --\033[0m\n'
- fi
+ if [[ -s $sh_out ]]; then
+ printf '\n\033[1m-- sh/dash -n (syntax) --\033[0m\n'
+ cat "$sh_out"
+ else
+ printf '\n\033[1;32m-- sh/dash -n: PASS (or none) --\033[0m\n'
+ fi
- echo
- if [[ $issues -gt 0 ]]; then
- log_error "Linting completed with $issues tool(s) reporting issues."
- return 1
- else
- log_info "All checks passed."
- return 0
- fi
+ echo
+ if [[ $issues -gt 0 ]]; then
+ log_error "Linting completed with $issues tool(s) reporting issues."
+ return 1
+ else
+ log_info "All checks passed."
+ return 0
+ fi
}
# Main
-if [[ "$INSTALL_ONLY" == "true" ]]; then
- install_linters
- exit $?
+if [[ $INSTALL_ONLY == "true" ]]; then
+ install_linters
+ exit $?
fi
# Only attempt installs if not list-only
-if [[ "$LIST_ONLY" != "true" ]]; then
- install_linters || true
+if [[ $LIST_ONLY != "true" ]]; then
+ install_linters || true
fi
discover_shell_files "$ROOT_DIR"
print_file_list
-if [[ "$LIST_ONLY" == "true" ]]; then
- exit 0
+if [[ $LIST_ONLY == "true" ]]; then
+ exit 0
fi
run_linters
exit $?
-
diff --git a/scripts/setup_periodic_system.sh b/scripts/setup_periodic_system.sh
index c24bc4b..abc4900 100755
--- a/scripts/setup_periodic_system.sh
+++ b/scripts/setup_periodic_system.sh
@@ -3,40 +3,40 @@
# Executes every hour and on system startup
# Handles sudo privileges automatically
-set -e # Exit on any error
+set -e # Exit on any error
# Default to non-interactive mode
INTERACTIVE_MODE=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
- case $1 in
- -i|--interactive)
- INTERACTIVE_MODE=true
- shift
- ;;
- -h|--help)
- echo "Usage: $0 [OPTIONS]"
- echo "Options:"
- echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
- echo " -h, --help Show this help message"
- exit 0
- ;;
- *)
- echo "Unknown option: $1"
- echo "Use -h or --help for usage information"
- exit 1
- ;;
- esac
+ case $1 in
+ -i | --interactive)
+ INTERACTIVE_MODE=true
+ shift
+ ;;
+ -h | --help)
+ echo "Usage: $0 [OPTIONS]"
+ echo "Options:"
+ echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
+ echo " -h, --help Show this help message"
+ exit 0
+ ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Use -h or --help for usage information"
+ exit 1
+ ;;
+ esac
done
# Function to check and request sudo privileges
check_sudo() {
- if [[ $EUID -ne 0 ]]; then
- echo "This script requires sudo privileges to create systemd services and timers."
- echo "Requesting sudo access..."
- exec sudo "$0" "$@"
- fi
+ if [[ $EUID -ne 0 ]]; then
+ echo "This script requires sudo privileges to create systemd services and timers."
+ echo "Requesting sudo access..."
+ exec sudo "$0" "$@"
+ fi
}
# Check for sudo privileges after argument parsing
@@ -47,10 +47,10 @@ echo "==================================================="
echo "Current Date: $(date)"
echo "User: $USER"
echo "Original user: ${SUDO_USER:-$USER}"
-if [[ "$INTERACTIVE_MODE" == "true" ]]; then
- echo "Mode: Interactive (prompts enabled)"
+if [[ $INTERACTIVE_MODE == "true" ]]; then
+ echo "Mode: Interactive (prompts enabled)"
else
- echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
+ echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
fi
# Get the directory where this script is located
@@ -86,231 +86,231 @@ TEMPLATE_LOGROTATE="$LOGROTATE_TEMPLATES/periodic-system-maintenance"
# Function to verify required files exist
verify_files() {
- echo ""
- echo "1. Verifying Required Files..."
- echo "=============================="
-
- local missing_files=()
-
- if [[ ! -f "$PACMAN_WRAPPER_SCRIPT" ]]; then
- missing_files+=("$PACMAN_WRAPPER_SCRIPT")
- fi
-
- if [[ ! -f "$PACMAN_WRAPPER_INSTALL" ]]; then
- missing_files+=("$PACMAN_WRAPPER_INSTALL")
- fi
-
- if [[ ! -f "$HOSTS_INSTALL_SCRIPT" ]]; then
- missing_files+=("$HOSTS_INSTALL_SCRIPT")
- fi
-
- # Check template files as well
- for tmpl in \
- "$TEMPLATE_MAINT_SCRIPT" \
- "$TEMPLATE_HOSTS_MONITOR" \
- "$TEMPLATE_BROWSER_WRAPPER" \
- "$TEMPLATE_SVC_MAINT" \
- "$TEMPLATE_TIMER" \
- "$TEMPLATE_STARTUP" \
- "$TEMPLATE_HOSTS_SVC" \
- "$TEMPLATE_LOGROTATE"; do
- if [[ ! -f "$tmpl" ]]; then
- missing_files+=("$tmpl")
- fi
- done
+ echo ""
+ echo "1. Verifying Required Files..."
+ echo "=============================="
- if [[ ${#missing_files[@]} -gt 0 ]]; then
- echo "Error: The following required files are missing:"
- for file in "${missing_files[@]}"; do
- echo " - $file"
- done
- exit 1
+ local missing_files=()
+
+ if [[ ! -f $PACMAN_WRAPPER_SCRIPT ]]; then
+ missing_files+=("$PACMAN_WRAPPER_SCRIPT")
+ fi
+
+ if [[ ! -f $PACMAN_WRAPPER_INSTALL ]]; then
+ missing_files+=("$PACMAN_WRAPPER_INSTALL")
+ fi
+
+ if [[ ! -f $HOSTS_INSTALL_SCRIPT ]]; then
+ missing_files+=("$HOSTS_INSTALL_SCRIPT")
+ fi
+
+ # Check template files as well
+ for tmpl in \
+ "$TEMPLATE_MAINT_SCRIPT" \
+ "$TEMPLATE_HOSTS_MONITOR" \
+ "$TEMPLATE_BROWSER_WRAPPER" \
+ "$TEMPLATE_SVC_MAINT" \
+ "$TEMPLATE_TIMER" \
+ "$TEMPLATE_STARTUP" \
+ "$TEMPLATE_HOSTS_SVC" \
+ "$TEMPLATE_LOGROTATE"; do
+ if [[ ! -f $tmpl ]]; then
+ missing_files+=("$tmpl")
fi
-
- echo "✓ All required files found"
+ done
+
+ if [[ ${#missing_files[@]} -gt 0 ]]; then
+ echo "Error: The following required files are missing:"
+ for file in "${missing_files[@]}"; do
+ echo " - $file"
+ done
+ exit 1
+ fi
+
+ echo "✓ All required files found"
}
# Function to create the combined execution script
create_execution_script() {
- echo ""
- echo "2. Creating Combined Execution Script..."
- echo "======================================="
-
- local exec_script="/usr/local/bin/periodic-system-maintenance.sh"
+ echo ""
+ echo "2. Creating Combined Execution Script..."
+ echo "======================================="
- # Install from template with path substitutions
- sed \
- -e "s|__PACMAN_WRAPPER_INSTALL__|$PACMAN_WRAPPER_INSTALL|g" \
- -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
- "$TEMPLATE_MAINT_SCRIPT" > "$exec_script"
+ local exec_script="/usr/local/bin/periodic-system-maintenance.sh"
- chmod +x "$exec_script"
- echo "✓ Installed execution script from template: $exec_script"
+ # Install from template with path substitutions
+ sed \
+ -e "s|__PACMAN_WRAPPER_INSTALL__|$PACMAN_WRAPPER_INSTALL|g" \
+ -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
+ "$TEMPLATE_MAINT_SCRIPT" > "$exec_script"
+
+ chmod +x "$exec_script"
+ echo "✓ Installed execution script from template: $exec_script"
}
# Function to create systemd service
create_systemd_service() {
- echo ""
- echo "3. Creating Systemd Service..."
- echo "============================="
-
- local service_file="/etc/systemd/system/periodic-system-maintenance.service"
- install -m 0644 "$TEMPLATE_SVC_MAINT" "$service_file"
- echo "✓ Installed systemd service from template: $service_file"
+ echo ""
+ echo "3. Creating Systemd Service..."
+ echo "============================="
+
+ local service_file="/etc/systemd/system/periodic-system-maintenance.service"
+ install -m 0644 "$TEMPLATE_SVC_MAINT" "$service_file"
+ echo "✓ Installed systemd service from template: $service_file"
}
# Function to create systemd timer for hourly execution
create_systemd_timer() {
- echo ""
- echo "4. Creating Systemd Timer..."
- echo "============================"
-
- local timer_file="/etc/systemd/system/periodic-system-maintenance.timer"
- install -m 0644 "$TEMPLATE_TIMER" "$timer_file"
- echo "✓ Installed systemd timer from template: $timer_file"
+ echo ""
+ echo "4. Creating Systemd Timer..."
+ echo "============================"
+
+ local timer_file="/etc/systemd/system/periodic-system-maintenance.timer"
+ install -m 0644 "$TEMPLATE_TIMER" "$timer_file"
+ echo "✓ Installed systemd timer from template: $timer_file"
}
# Function to create startup service (additional to timer)
create_startup_service() {
- echo ""
- echo "5. Creating Startup Service..."
- echo "=============================="
-
- local startup_service="/etc/systemd/system/periodic-system-startup.service"
- install -m 0644 "$TEMPLATE_STARTUP" "$startup_service"
- echo "✓ Installed startup service from template: $startup_service"
+ echo ""
+ echo "5. Creating Startup Service..."
+ echo "=============================="
+
+ local startup_service="/etc/systemd/system/periodic-system-startup.service"
+ install -m 0644 "$TEMPLATE_STARTUP" "$startup_service"
+ echo "✓ Installed startup service from template: $startup_service"
}
# Function to create hosts file monitor service
create_hosts_monitor_service() {
- echo ""
- echo "6. Creating Hosts File Monitor Service..."
- echo "========================================"
-
- local monitor_script="/usr/local/bin/hosts-file-monitor.sh"
- local monitor_service="/etc/systemd/system/hosts-file-monitor.service"
+ echo ""
+ echo "6. Creating Hosts File Monitor Service..."
+ echo "========================================"
- # Install the monitor script from template with substitution
- sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
- "$TEMPLATE_HOSTS_MONITOR" > "$monitor_script"
- chmod +x "$monitor_script"
- echo "✓ Installed hosts monitor script from template: $monitor_script"
+ local monitor_script="/usr/local/bin/hosts-file-monitor.sh"
+ local monitor_service="/etc/systemd/system/hosts-file-monitor.service"
- # Install the systemd service from template
- install -m 0644 "$TEMPLATE_HOSTS_SVC" "$monitor_service"
- echo "✓ Installed hosts monitor service from template: $monitor_service"
+ # Install the monitor script from template with substitution
+ sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
+ "$TEMPLATE_HOSTS_MONITOR" > "$monitor_script"
+ chmod +x "$monitor_script"
+ echo "✓ Installed hosts monitor script from template: $monitor_script"
+
+ # Install the systemd service from template
+ install -m 0644 "$TEMPLATE_HOSTS_SVC" "$monitor_service"
+ echo "✓ Installed hosts monitor service from template: $monitor_service"
}
# Function to install browser pre-exec wrapper and wire common browser names
install_browser_preexec_wrapper() {
- echo ""
- echo "6.1 Installing Browser Pre-Exec Wrapper..."
- echo "========================================="
+ echo ""
+ echo "6.1 Installing Browser Pre-Exec Wrapper..."
+ echo "========================================="
- local wrapper="/usr/local/bin/browser-preexec-wrapper"
- sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
- "$TEMPLATE_BROWSER_WRAPPER" > "$wrapper"
- chmod +x "$wrapper"
- echo "✓ Installed wrapper: $wrapper"
+ local wrapper="/usr/local/bin/browser-preexec-wrapper"
+ sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
+ "$TEMPLATE_BROWSER_WRAPPER" > "$wrapper"
+ chmod +x "$wrapper"
+ echo "✓ Installed wrapper: $wrapper"
- # Allow passwordless execution of hosts installer for root-only actions
- local sudoers_file="/etc/sudoers.d/hosts-install-no-passwd"
- if command -v visudo >/dev/null 2>&1; then
- echo "${SUDO_USER:-$USER} ALL=(ALL) NOPASSWD: $HOSTS_INSTALL_SCRIPT" > "$sudoers_file"
- chmod 440 "$sudoers_file"
- # Validate syntax
- visudo -c >/dev/null || echo "Warning: sudoers validation returned non-zero"
- echo "✓ Sudoers drop-in created: $sudoers_file"
- else
- echo "visudo not found; skipping sudoers drop-in"
- fi
+ # Allow passwordless execution of hosts installer for root-only actions
+ local sudoers_file="/etc/sudoers.d/hosts-install-no-passwd"
+ if command -v visudo > /dev/null 2>&1; then
+ echo "${SUDO_USER:-$USER} ALL=(ALL) NOPASSWD: $HOSTS_INSTALL_SCRIPT" > "$sudoers_file"
+ chmod 440 "$sudoers_file"
+ # Validate syntax
+ visudo -c > /dev/null || echo "Warning: sudoers validation returned non-zero"
+ echo "✓ Sudoers drop-in created: $sudoers_file"
+ else
+ echo "visudo not found; skipping sudoers drop-in"
+ fi
- # Create symlinks for common browser commands to the wrapper in /usr/local/bin
- # This takes precedence over /usr/bin in PATH on most systems.
- local browsers=( "thorium-browser" "google-chrome" "google-chrome-stable" "chromium" "brave" "brave-browser" "vivaldi-stable" "firefox" )
- for b in "${browsers[@]}"; do
- local link="/usr/local/bin/$b"
- ln -sf "$wrapper" "$link"
- done
- echo "✓ Symlinked wrapper for common browsers in /usr/local/bin"
+ # Create symlinks for common browser commands to the wrapper in /usr/local/bin
+ # This takes precedence over /usr/bin in PATH on most systems.
+ local browsers=("thorium-browser" "google-chrome" "google-chrome-stable" "chromium" "brave" "brave-browser" "vivaldi-stable" "firefox")
+ for b in "${browsers[@]}"; do
+ local link="/usr/local/bin/$b"
+ ln -sf "$wrapper" "$link"
+ done
+ echo "✓ Symlinked wrapper for common browsers in /usr/local/bin"
}
# Function to enable and start services
enable_services() {
- echo ""
- echo "7. Enabling Services and Timer..."
- echo "================================="
-
- # Reload systemd daemon
- systemctl daemon-reload
- echo "✓ Systemd daemon reloaded"
-
- # Enable and start the timer
- systemctl enable periodic-system-maintenance.timer
- systemctl start periodic-system-maintenance.timer
- echo "✓ Timer enabled and started"
-
- # Enable startup service (but don't start it now)
- systemctl enable periodic-system-startup.service
- echo "✓ Startup service enabled"
-
- # Enable hosts file monitor service
- systemctl enable hosts-file-monitor.service
- systemctl start hosts-file-monitor.service
- echo "✓ Hosts file monitor service enabled and started"
-
- # Show timer status
- echo ""
- echo "Timer Status:"
- systemctl status periodic-system-maintenance.timer --no-pager -l
-
- echo ""
- echo "Hosts Monitor Status:"
- systemctl status hosts-file-monitor.service --no-pager -l
-
- echo ""
- echo "Next scheduled runs:"
- systemctl list-timers periodic-system-maintenance.timer --no-pager
+ echo ""
+ echo "7. Enabling Services and Timer..."
+ echo "================================="
+
+ # Reload systemd daemon
+ systemctl daemon-reload
+ echo "✓ Systemd daemon reloaded"
+
+ # Enable and start the timer
+ systemctl enable periodic-system-maintenance.timer
+ systemctl start periodic-system-maintenance.timer
+ echo "✓ Timer enabled and started"
+
+ # Enable startup service (but don't start it now)
+ systemctl enable periodic-system-startup.service
+ echo "✓ Startup service enabled"
+
+ # Enable hosts file monitor service
+ systemctl enable hosts-file-monitor.service
+ systemctl start hosts-file-monitor.service
+ echo "✓ Hosts file monitor service enabled and started"
+
+ # Show timer status
+ echo ""
+ echo "Timer Status:"
+ systemctl status periodic-system-maintenance.timer --no-pager -l
+
+ echo ""
+ echo "Hosts Monitor Status:"
+ systemctl status hosts-file-monitor.service --no-pager -l
+
+ echo ""
+ echo "Next scheduled runs:"
+ systemctl list-timers periodic-system-maintenance.timer --no-pager
}
# Function to create log rotation configuration
create_log_rotation() {
- echo ""
- echo "8. Setting up Log Rotation..."
- echo "============================="
-
- local logrotate_conf="/etc/logrotate.d/periodic-system-maintenance"
- install -m 0644 "$TEMPLATE_LOGROTATE" "$logrotate_conf"
- echo "✓ Installed log rotation configuration from template: $logrotate_conf"
+ echo ""
+ echo "8. Setting up Log Rotation..."
+ echo "============================="
+
+ local logrotate_conf="/etc/logrotate.d/periodic-system-maintenance"
+ install -m 0644 "$TEMPLATE_LOGROTATE" "$logrotate_conf"
+ echo "✓ Installed log rotation configuration from template: $logrotate_conf"
}
# Function to run initial execution
run_initial_execution() {
- echo ""
- echo "9. Running Initial Execution..."
- echo "==============================="
-
- local run_initial=true
-
- if [[ "$INTERACTIVE_MODE" == "true" ]]; then
- echo "Would you like to run the system maintenance now to test the setup?"
- read -p "Run initial execution? (y/N): " -n 1 -r
- echo
-
- if [[ ! $REPLY =~ ^[Yy]$ ]]; then
- run_initial=false
- fi
- else
- echo "Auto-running initial execution to test the setup (use --interactive to prompt)"
- fi
-
- if [[ "$run_initial" == "true" ]]; then
- echo "Running initial system maintenance..."
- /usr/local/bin/periodic-system-maintenance.sh
- echo "✓ Initial execution completed"
- else
- echo "Skipping initial execution"
+ echo ""
+ echo "9. Running Initial Execution..."
+ echo "==============================="
+
+ local run_initial=true
+
+ if [[ $INTERACTIVE_MODE == "true" ]]; then
+ echo "Would you like to run the system maintenance now to test the setup?"
+ read -p "Run initial execution? (y/N): " -n 1 -r
+ echo
+
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+ run_initial=false
fi
+ else
+ echo "Auto-running initial execution to test the setup (use --interactive to prompt)"
+ fi
+
+ if [[ $run_initial == "true" ]]; then
+ echo "Running initial system maintenance..."
+ /usr/local/bin/periodic-system-maintenance.sh
+ echo "✓ Initial execution completed"
+ else
+ echo "Skipping initial execution"
+ fi
}
# Main execution
diff --git a/scripts/setup_thorium_startup.sh b/scripts/setup_thorium_startup.sh
index 86e5f79..042a1f8 100755
--- a/scripts/setup_thorium_startup.sh
+++ b/scripts/setup_thorium_startup.sh
@@ -2,40 +2,40 @@
# Script to set up automatic Thorium browser launch with Fitatu website on startup
# Opens https://www.fitatu.com/ in Thorium browser every time the system boots
-set -e # Exit on any error
+set -e # Exit on any error
# Default to non-interactive mode
INTERACTIVE_MODE=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
- case $1 in
- -i|--interactive)
- INTERACTIVE_MODE=true
- shift
- ;;
- -h|--help)
- echo "Usage: $0 [OPTIONS]"
- echo "Options:"
- echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
- echo " -h, --help Show this help message"
- exit 0
- ;;
- *)
- echo "Unknown option: $1"
- echo "Use -h or --help for usage information"
- exit 1
- ;;
- esac
+ case $1 in
+ -i | --interactive)
+ INTERACTIVE_MODE=true
+ shift
+ ;;
+ -h | --help)
+ echo "Usage: $0 [OPTIONS]"
+ echo "Options:"
+ echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
+ echo " -h, --help Show this help message"
+ exit 0
+ ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Use -h or --help for usage information"
+ exit 1
+ ;;
+ esac
done
# Function to check and request sudo privileges
check_sudo() {
- if [[ $EUID -ne 0 ]]; then
- echo "This script requires sudo privileges to create systemd services."
- echo "Requesting sudo access..."
- exec sudo "$0" "$@"
- fi
+ if [[ $EUID -ne 0 ]]; then
+ echo "This script requires sudo privileges to create systemd services."
+ echo "Requesting sudo access..."
+ exec sudo "$0" "$@"
+ fi
}
# Check for sudo privileges after argument parsing
@@ -46,10 +46,10 @@ echo "=================================="
echo "Current Date: $(date)"
echo "User: $USER"
echo "Original user: ${SUDO_USER:-$USER}"
-if [[ "$INTERACTIVE_MODE" == "true" ]]; then
- echo "Mode: Interactive (prompts enabled)"
+if [[ $INTERACTIVE_MODE == "true" ]]; then
+ echo "Mode: Interactive (prompts enabled)"
else
- echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
+ echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
fi
# Target URL
@@ -64,72 +64,72 @@ echo "User home: $USER_HOME"
# Function to check if Thorium browser is installed
check_thorium_browser() {
- echo ""
- echo "1. Checking Thorium Browser Installation..."
- echo "=========================================="
-
- if ! command -v "$BROWSER_COMMAND" &> /dev/null; then
- echo "Warning: Thorium browser not found in PATH"
- echo "Checking alternative locations..."
-
- # Check common installation paths
- local alt_paths=(
- "/opt/thorium/thorium"
- "/usr/bin/thorium"
- "/usr/local/bin/thorium"
- "/opt/thorium-browser/thorium-browser"
- "${USER_HOME}/.local/bin/thorium-browser"
- )
-
- local found=false
- for path in "${alt_paths[@]}"; do
- if [[ -x "$path" ]]; then
- BROWSER_COMMAND="$path"
- echo "✓ Found Thorium browser at: $path"
- found=true
- break
- fi
- done
-
- if [[ "$found" != true ]]; then
- echo "Error: Thorium browser not found!"
- echo "Please install Thorium browser first or ensure it's in your PATH."
- echo ""
- echo "You can install Thorium browser from:"
- echo "https://thorium.rocks/"
- echo ""
-
- local continue_anyway=false
-
- if [[ "$INTERACTIVE_MODE" == "true" ]]; then
- read -p "Continue anyway? The service will be created but may fail to start (y/N): " -n 1 -r
- echo
- if [[ $REPLY =~ ^[Yy]$ ]]; then
- continue_anyway=true
- fi
- else
- echo "Auto-continuing anyway - service will be created but may fail to start (use --interactive to prompt)"
- continue_anyway=true
- fi
-
- if [[ "$continue_anyway" != true ]]; then
- exit 1
- fi
+ echo ""
+ echo "1. Checking Thorium Browser Installation..."
+ echo "=========================================="
+
+ if ! command -v "$BROWSER_COMMAND" &> /dev/null; then
+ echo "Warning: Thorium browser not found in PATH"
+ echo "Checking alternative locations..."
+
+ # Check common installation paths
+ local alt_paths=(
+ "/opt/thorium/thorium"
+ "/usr/bin/thorium"
+ "/usr/local/bin/thorium"
+ "/opt/thorium-browser/thorium-browser"
+ "${USER_HOME}/.local/bin/thorium-browser"
+ )
+
+ local found=false
+ for path in "${alt_paths[@]}"; do
+ if [[ -x $path ]]; then
+ BROWSER_COMMAND="$path"
+ echo "✓ Found Thorium browser at: $path"
+ found=true
+ break
+ fi
+ done
+
+ if [[ $found != true ]]; then
+ echo "Error: Thorium browser not found!"
+ echo "Please install Thorium browser first or ensure it's in your PATH."
+ echo ""
+ echo "You can install Thorium browser from:"
+ echo "https://thorium.rocks/"
+ echo ""
+
+ local continue_anyway=false
+
+ if [[ $INTERACTIVE_MODE == "true" ]]; then
+ read -p "Continue anyway? The service will be created but may fail to start (y/N): " -n 1 -r
+ echo
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ continue_anyway=true
fi
- else
- echo "✓ Thorium browser found: $(which $BROWSER_COMMAND)"
+ else
+ echo "Auto-continuing anyway - service will be created but may fail to start (use --interactive to prompt)"
+ continue_anyway=true
+ fi
+
+ if [[ $continue_anyway != true ]]; then
+ exit 1
+ fi
fi
+ else
+ echo "✓ Thorium browser found: $(which $BROWSER_COMMAND)"
+ fi
}
# Function to create the browser launcher script
create_launcher_script() {
- echo ""
- echo "2. Creating Browser Launcher Script..."
- echo "====================================="
-
- local launcher_script="/usr/local/bin/thorium-fitatu-launcher.sh"
-
- cat > "$launcher_script" << EOF
+ echo ""
+ echo "2. Creating Browser Launcher Script..."
+ echo "====================================="
+
+ local launcher_script="/usr/local/bin/thorium-fitatu-launcher.sh"
+
+ cat > "$launcher_script" << EOF
#!/bin/bash
# Thorium browser launcher for Fitatu website
# Created by setup_thorium_startup.sh on $(date)
@@ -203,24 +203,24 @@ else
fi
EOF
- chmod +x "$launcher_script"
- echo "✓ Created launcher script: $launcher_script"
+ chmod +x "$launcher_script"
+ echo "✓ Created launcher script: $launcher_script"
}
# Function to create systemd service for user session
create_user_systemd_service() {
- echo ""
- echo "3. Creating User Systemd Service..."
- echo "=================================="
-
- local user_systemd_dir="$USER_HOME/.config/systemd/user"
- local service_file="$user_systemd_dir/thorium-fitatu-startup.service"
-
- # Create user systemd directory
- sudo -u "${SUDO_USER}" mkdir -p "$user_systemd_dir"
-
- # Create the service file
- sudo -u "${SUDO_USER}" tee "$service_file" > /dev/null << EOF
+ echo ""
+ echo "3. Creating User Systemd Service..."
+ echo "=================================="
+
+ local user_systemd_dir="$USER_HOME/.config/systemd/user"
+ local service_file="$user_systemd_dir/thorium-fitatu-startup.service"
+
+ # Create user systemd directory
+ sudo -u "${SUDO_USER}" mkdir -p "$user_systemd_dir"
+
+ # Create the service file
+ sudo -u "${SUDO_USER}" tee "$service_file" > /dev/null << EOF
[Unit]
Description=Launch Thorium Browser with Fitatu on Startup
After=graphical-session.target
@@ -245,18 +245,18 @@ TimeoutStartSec=120
WantedBy=default.target
EOF
- echo "✓ Created user systemd service: $service_file"
+ echo "✓ Created user systemd service: $service_file"
}
# Function to create system-wide systemd service (alternative approach)
create_system_systemd_service() {
- echo ""
- echo "4. Creating System Systemd Service..."
- echo "===================================="
-
- local service_file="/etc/systemd/system/thorium-fitatu-startup.service"
-
- cat > "$service_file" << EOF
+ echo ""
+ echo "4. Creating System Systemd Service..."
+ echo "===================================="
+
+ local service_file="/etc/systemd/system/thorium-fitatu-startup.service"
+
+ cat > "$service_file" << EOF
[Unit]
Description=Launch Thorium Browser with Fitatu on Startup
After=multi-user.target network-online.target
@@ -283,23 +283,23 @@ TimeoutStartSec=180
WantedBy=multi-user.target
EOF
- echo "✓ Created system systemd service: $service_file"
+ echo "✓ Created system systemd service: $service_file"
}
# Function to create autostart desktop entry (additional method)
create_autostart_entry() {
- echo ""
- echo "5. Creating Autostart Desktop Entry..."
- echo "====================================="
-
- local autostart_dir="$USER_HOME/.config/autostart"
- local desktop_file="$autostart_dir/thorium-fitatu.desktop"
-
- # Create autostart directory
- sudo -u "${SUDO_USER}" mkdir -p "$autostart_dir"
-
- # Create desktop entry
- sudo -u "${SUDO_USER}" tee "$desktop_file" > /dev/null << EOF
+ echo ""
+ echo "5. Creating Autostart Desktop Entry..."
+ echo "====================================="
+
+ local autostart_dir="$USER_HOME/.config/autostart"
+ local desktop_file="$autostart_dir/thorium-fitatu.desktop"
+
+ # Create autostart directory
+ sudo -u "${SUDO_USER}" mkdir -p "$autostart_dir"
+
+ # Create desktop entry
+ sudo -u "${SUDO_USER}" tee "$desktop_file" > /dev/null << EOF
[Desktop Entry]
Type=Application
Name=Thorium Fitatu Startup
@@ -314,45 +314,45 @@ Terminal=false
Categories=Network;WebBrowser;
EOF
- echo "✓ Created autostart desktop entry: $desktop_file"
+ echo "✓ Created autostart desktop entry: $desktop_file"
}
# Function to create i3 config autostart entry
create_i3_autostart() {
- echo ""
- echo "6. Creating i3 Config Autostart Entry..."
- echo "======================================="
-
- local i3_config="$USER_HOME/.config/i3/config"
- local i3_config_dir="$USER_HOME/.config/i3"
-
- # Create i3 config directory if it doesn't exist
- sudo -u "${SUDO_USER}" mkdir -p "$i3_config_dir"
-
- # Check if i3 config exists
- if [[ -f "$i3_config" ]]; then
- # Check if autostart entry already exists
- if ! sudo -u "${SUDO_USER}" grep -q "thorium-fitatu-launcher" "$i3_config"; then
- # Add autostart entry to i3 config
- sudo -u "${SUDO_USER}" bash -c "echo '' >> '$i3_config'"
- sudo -u "${SUDO_USER}" bash -c "echo '# Auto-start Thorium browser with Fitatu' >> '$i3_config'"
- sudo -u "${SUDO_USER}" bash -c "echo 'exec --no-startup-id /usr/local/bin/thorium-fitatu-launcher.sh' >> '$i3_config'"
- echo "✓ Added autostart entry to i3 config: $i3_config"
- else
- echo "✓ Autostart entry already exists in i3 config"
- fi
+ echo ""
+ echo "6. Creating i3 Config Autostart Entry..."
+ echo "======================================="
+
+ local i3_config="$USER_HOME/.config/i3/config"
+ local i3_config_dir="$USER_HOME/.config/i3"
+
+ # Create i3 config directory if it doesn't exist
+ sudo -u "${SUDO_USER}" mkdir -p "$i3_config_dir"
+
+ # Check if i3 config exists
+ if [[ -f $i3_config ]]; then
+ # Check if autostart entry already exists
+ if ! sudo -u "${SUDO_USER}" grep -q "thorium-fitatu-launcher" "$i3_config"; then
+ # Add autostart entry to i3 config
+ sudo -u "${SUDO_USER}" bash -c "echo '' >> '$i3_config'"
+ sudo -u "${SUDO_USER}" bash -c "echo '# Auto-start Thorium browser with Fitatu' >> '$i3_config'"
+ sudo -u "${SUDO_USER}" bash -c "echo 'exec --no-startup-id /usr/local/bin/thorium-fitatu-launcher.sh' >> '$i3_config'"
+ echo "✓ Added autostart entry to i3 config: $i3_config"
else
- echo "Warning: i3 config file not found at $i3_config"
- echo "You may need to manually add the following line to your i3 config:"
- echo "exec --no-startup-id /usr/local/bin/thorium-fitatu-launcher.sh"
+ echo "✓ Autostart entry already exists in i3 config"
fi
+ else
+ echo "Warning: i3 config file not found at $i3_config"
+ echo "You may need to manually add the following line to your i3 config:"
+ echo "exec --no-startup-id /usr/local/bin/thorium-fitatu-launcher.sh"
+ fi
}
# Function to create a script to enable user service after login
create_user_enable_script() {
- local enable_script="$USER_HOME/.config/thorium-enable-service.sh"
-
- sudo -u "${SUDO_USER}" tee "$enable_script" > /dev/null << 'EOF'
+ local enable_script="$USER_HOME/.config/thorium-enable-service.sh"
+
+ sudo -u "${SUDO_USER}" tee "$enable_script" > /dev/null << 'EOF'
#!/bin/bash
# Script to enable thorium-fitatu-startup user service
# This runs once to enable the service, then removes itself
@@ -365,110 +365,110 @@ systemctl --user enable thorium-fitatu-startup.service
rm "$0"
EOF
- sudo -u "${SUDO_USER}" chmod +x "$enable_script"
-
- # Add to user's .bashrc to run on next login
- local bashrc="$USER_HOME/.bashrc"
- if [[ -f "$bashrc" ]]; then
- sudo -u "${SUDO_USER}" bash -c "echo '' >> '$bashrc'"
- sudo -u "${SUDO_USER}" bash -c "echo '# Auto-enable thorium service (temporary)' >> '$bashrc'"
- sudo -u "${SUDO_USER}" bash -c "echo '[[ -x ~/.config/thorium-enable-service.sh ]] && ~/.config/thorium-enable-service.sh' >> '$bashrc'"
- fi
+ sudo -u "${SUDO_USER}" chmod +x "$enable_script"
+
+ # Add to user's .bashrc to run on next login
+ local bashrc="$USER_HOME/.bashrc"
+ if [[ -f $bashrc ]]; then
+ sudo -u "${SUDO_USER}" bash -c "echo '' >> '$bashrc'"
+ sudo -u "${SUDO_USER}" bash -c "echo '# Auto-enable thorium service (temporary)' >> '$bashrc'"
+ sudo -u "${SUDO_USER}" bash -c "echo '[[ -x ~/.config/thorium-enable-service.sh ]] && ~/.config/thorium-enable-service.sh' >> '$bashrc'"
+ fi
}
# Function to enable services
enable_services() {
- echo ""
- echo "7. Enabling Services..."
- echo "======================"
-
- # Reload systemd daemon
- systemctl daemon-reload
- echo "✓ System daemon reloaded"
-
- # Enable system service
- systemctl enable thorium-fitatu-startup.service
- echo "✓ System service enabled"
-
- # Enable lingering for the user (allows user services to run without login)
- loginctl enable-linger "${SUDO_USER}"
- echo "✓ User lingering enabled"
-
- # Create a script to enable user service after login
- create_user_enable_script
- echo "✓ User service will be enabled on next login"
+ echo ""
+ echo "7. Enabling Services..."
+ echo "======================"
+
+ # Reload systemd daemon
+ systemctl daemon-reload
+ echo "✓ System daemon reloaded"
+
+ # Enable system service
+ systemctl enable thorium-fitatu-startup.service
+ echo "✓ System service enabled"
+
+ # Enable lingering for the user (allows user services to run without login)
+ loginctl enable-linger "${SUDO_USER}"
+ echo "✓ User lingering enabled"
+
+ # Create a script to enable user service after login
+ create_user_enable_script
+ echo "✓ User service will be enabled on next login"
}
# Function to test the setup
test_setup() {
- echo ""
- echo "8. Testing Setup..."
- echo "=================="
-
- local run_test=true
-
- if [[ "$INTERACTIVE_MODE" == "true" ]]; then
- echo "Would you like to test the browser launcher now?"
- read -p "Test launch Thorium browser with Fitatu? (y/N): " -n 1 -r
- echo
-
- if [[ ! $REPLY =~ ^[Yy]$ ]]; then
- run_test=false
- fi
- else
- echo "Auto-testing the browser launcher (use --interactive to prompt)"
+ echo ""
+ echo "8. Testing Setup..."
+ echo "=================="
+
+ local run_test=true
+
+ if [[ $INTERACTIVE_MODE == "true" ]]; then
+ echo "Would you like to test the browser launcher now?"
+ read -p "Test launch Thorium browser with Fitatu? (y/N): " -n 1 -r
+ echo
+
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+ run_test=false
fi
-
- if [[ "$run_test" == "true" ]]; then
- echo "Testing browser launch..."
- echo "Note: This will open Thorium browser with Fitatu website"
-
- # Test the launcher immediately
- if /usr/local/bin/thorium-fitatu-launcher.sh; then
- echo "✓ Test launch completed successfully"
- else
- echo "✗ Test launch failed"
- echo "Check that Thorium browser is properly installed and accessible"
- fi
+ else
+ echo "Auto-testing the browser launcher (use --interactive to prompt)"
+ fi
+
+ if [[ $run_test == "true" ]]; then
+ echo "Testing browser launch..."
+ echo "Note: This will open Thorium browser with Fitatu website"
+
+ # Test the launcher immediately
+ if /usr/local/bin/thorium-fitatu-launcher.sh; then
+ echo "✓ Test launch completed successfully"
else
- echo "Skipping test launch"
+ echo "✗ Test launch failed"
+ echo "Check that Thorium browser is properly installed and accessible"
fi
+ else
+ echo "Skipping test launch"
+ fi
}
# Function to show usage instructions
show_instructions() {
- echo ""
- echo "=========================================="
- echo "Thorium Browser Auto-Startup Setup Complete"
- echo "=========================================="
- echo "Summary:"
- echo "✓ Launcher script created: /usr/local/bin/thorium-fitatu-launcher.sh"
- echo "✓ System service created: thorium-fitatu-startup.service"
- echo "✓ User service created: ~/.config/systemd/user/thorium-fitatu-startup.service"
- echo "✓ Autostart entry created: ~/.config/autostart/thorium-fitatu.desktop"
- echo "✓ i3 autostart entry added to: ~/.config/i3/config"
- echo "✓ Services enabled for automatic startup"
- echo ""
- echo "The system will now:"
- echo "• Launch Thorium browser with $TARGET_URL on every startup"
- echo "• Use multiple methods to ensure reliable startup"
- echo "• Wait for desktop environment to be ready before launching"
- echo "• User service will be enabled automatically on next login"
- echo ""
- echo "To check status:"
- echo " systemctl status thorium-fitatu-startup.service"
- echo " systemctl --user status thorium-fitatu-startup.service (after login)"
- echo ""
- echo "To view logs:"
- echo " journalctl -u thorium-fitatu-startup.service"
- echo " journalctl --user -u thorium-fitatu-startup.service"
- echo ""
- echo "To disable (if needed):"
- echo " sudo systemctl disable thorium-fitatu-startup.service"
- echo " systemctl --user disable thorium-fitatu-startup.service"
- echo " rm ~/.config/autostart/thorium-fitatu.desktop"
- echo ""
- echo "IMPORTANT: Browser will launch automatically on next reboot!"
+ echo ""
+ echo "=========================================="
+ echo "Thorium Browser Auto-Startup Setup Complete"
+ echo "=========================================="
+ echo "Summary:"
+ echo "✓ Launcher script created: /usr/local/bin/thorium-fitatu-launcher.sh"
+ echo "✓ System service created: thorium-fitatu-startup.service"
+ echo "✓ User service created: ~/.config/systemd/user/thorium-fitatu-startup.service"
+ echo "✓ Autostart entry created: ~/.config/autostart/thorium-fitatu.desktop"
+ echo "✓ i3 autostart entry added to: ~/.config/i3/config"
+ echo "✓ Services enabled for automatic startup"
+ echo ""
+ echo "The system will now:"
+ echo "• Launch Thorium browser with $TARGET_URL on every startup"
+ echo "• Use multiple methods to ensure reliable startup"
+ echo "• Wait for desktop environment to be ready before launching"
+ echo "• User service will be enabled automatically on next login"
+ echo ""
+ echo "To check status:"
+ echo " systemctl status thorium-fitatu-startup.service"
+ echo " systemctl --user status thorium-fitatu-startup.service (after login)"
+ echo ""
+ echo "To view logs:"
+ echo " journalctl -u thorium-fitatu-startup.service"
+ echo " journalctl --user -u thorium-fitatu-startup.service"
+ echo ""
+ echo "To disable (if needed):"
+ echo " sudo systemctl disable thorium-fitatu-startup.service"
+ echo " systemctl --user disable thorium-fitatu-startup.service"
+ echo " rm ~/.config/autostart/thorium-fitatu.desktop"
+ echo ""
+ echo "IMPORTANT: Browser will launch automatically on next reboot!"
}
# Main execution
diff --git a/scripts/system-maintenance/bin/browser-preexec-wrapper.sh b/scripts/system-maintenance/bin/browser-preexec-wrapper.sh
index 3e4fec8..bc17de7 100755
--- a/scripts/system-maintenance/bin/browser-preexec-wrapper.sh
+++ b/scripts/system-maintenance/bin/browser-preexec-wrapper.sh
@@ -11,7 +11,7 @@ real_bin="/usr/bin/${prog_name}"
# If run directly (not via a browser symlink) or if the target binary doesn't exist,
# allow passing the real browser command as the first argument for testing:
-if [[ ! -x "$real_bin" || "$prog_name" == "browser-preexec-wrapper.sh" ]]; then
+if [[ ! -x $real_bin || $prog_name == "browser-preexec-wrapper.sh" ]]; then
if [[ $# -ge 1 ]]; then
real_bin="$1"
shift
@@ -24,10 +24,10 @@ if [[ ! -x "$real_bin" || "$prog_name" == "browser-preexec-wrapper.sh" ]]; then
fi
# Best-effort: install hosts file quietly; don't block browser startup
-if command -v sudo >/dev/null 2>&1; then
- sudo -n "$HOSTS_INSTALL_SCRIPT" >/dev/null 2>&1 || true
+if command -v sudo > /dev/null 2>&1; then
+ sudo -n "$HOSTS_INSTALL_SCRIPT" > /dev/null 2>&1 || true
else
- "$HOSTS_INSTALL_SCRIPT" >/dev/null 2>&1 || true
+ "$HOSTS_INSTALL_SCRIPT" > /dev/null 2>&1 || true
fi
exec "$real_bin" "$@"
diff --git a/scripts/system-maintenance/bin/hosts-file-monitor.sh b/scripts/system-maintenance/bin/hosts-file-monitor.sh
index 4cf3255..b4d9e0d 100755
--- a/scripts/system-maintenance/bin/hosts-file-monitor.sh
+++ b/scripts/system-maintenance/bin/hosts-file-monitor.sh
@@ -11,100 +11,100 @@ HOSTS_INSTALL_SCRIPT="__HOSTS_INSTALL_SCRIPT__"
# Function to log with timestamp
log_message() {
- echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" >&2
+ echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" >&2
}
# Function to check if hosts file needs restoration
needs_restoration() {
- # Check if file exists
- if [[ ! -f "$HOSTS_FILE" ]]; then
- return 0 # File missing, needs restoration
- fi
-
- # Check if file is empty or too small (less than 1000 lines indicates tampering)
- local line_count
- line_count=$(wc -l < "$HOSTS_FILE" 2>/dev/null || echo "0")
- if [[ "$line_count" -lt 1000 ]]; then
- return 0 # File too small, likely tampered with
- fi
-
- # Check if our custom entries are missing
- if ! grep -q "Custom blocking entries" "$HOSTS_FILE" 2>/dev/null; then
- return 0 # Our custom entries missing, needs restoration
- fi
-
- # Check if StevenBlack entries are missing
- if ! grep -q "StevenBlack" "$HOSTS_FILE" 2>/dev/null; then
- return 0 # StevenBlack entries missing, needs restoration
- fi
-
- return 1 # File seems intact
+ # Check if file exists
+ if [[ ! -f $HOSTS_FILE ]]; then
+ return 0 # File missing, needs restoration
+ fi
+
+ # Check if file is empty or too small (less than 1000 lines indicates tampering)
+ local line_count
+ line_count=$(wc -l < "$HOSTS_FILE" 2> /dev/null || echo "0")
+ if [[ $line_count -lt 1000 ]]; then
+ return 0 # File too small, likely tampered with
+ fi
+
+ # Check if our custom entries are missing
+ if ! grep -q "Custom blocking entries" "$HOSTS_FILE" 2> /dev/null; then
+ return 0 # Our custom entries missing, needs restoration
+ fi
+
+ # Check if StevenBlack entries are missing
+ if ! grep -q "StevenBlack" "$HOSTS_FILE" 2> /dev/null; then
+ return 0 # StevenBlack entries missing, needs restoration
+ fi
+
+ return 1 # File seems intact
}
# Function to restore hosts file
restore_hosts_file() {
- log_message "Hosts file modification detected - initiating restoration"
-
- if [[ -f "$HOSTS_INSTALL_SCRIPT" ]]; then
- log_message "Running hosts installation script: $HOSTS_INSTALL_SCRIPT"
-
- if bash "$HOSTS_INSTALL_SCRIPT" >> "$LOG_FILE" 2>&1; then
- log_message "Hosts file restoration completed successfully"
- else
- log_message "Hosts file restoration failed with exit code $?"
- fi
+ log_message "Hosts file modification detected - initiating restoration"
+
+ if [[ -f $HOSTS_INSTALL_SCRIPT ]]; then
+ log_message "Running hosts installation script: $HOSTS_INSTALL_SCRIPT"
+
+ if bash "$HOSTS_INSTALL_SCRIPT" >> "$LOG_FILE" 2>&1; then
+ log_message "Hosts file restoration completed successfully"
else
- log_message "ERROR: Hosts install script not found at $HOSTS_INSTALL_SCRIPT"
+ log_message "Hosts file restoration failed with exit code $?"
fi
+ else
+ log_message "ERROR: Hosts install script not found at $HOSTS_INSTALL_SCRIPT"
+ fi
}
# Function to monitor with inotifywait
monitor_with_inotify() {
- log_message "Starting hosts file monitoring with inotify"
-
- # Monitor the hosts file and its directory for various events
- inotifywait -m -e delete,move,modify,attrib,create --format '%w%f %e %T' --timefmt '%Y-%m-%d %H:%M:%S' "$HOSTS_FILE" /etc/ 2>/dev/null | \
+ log_message "Starting hosts file monitoring with inotify"
+
+ # Monitor the hosts file and its directory for various events
+ inotifywait -m -e delete,move,modify,attrib,create --format '%w%f %e %T' --timefmt '%Y-%m-%d %H:%M:%S' "$HOSTS_FILE" /etc/ 2> /dev/null |
while read -r file event time; do
- # Check if the event is related to our hosts file
- if [[ "$file" == "$HOSTS_FILE" ]] || [[ "$file" == "/etc/hosts" ]]; then
- log_message "Event detected: $event on $file at $time"
-
- # Small delay to avoid rapid-fire events
- sleep 2
-
- # Check if restoration is needed
- if needs_restoration; then
- restore_hosts_file
- else
- log_message "Hosts file check passed - no restoration needed"
- fi
+ # Check if the event is related to our hosts file
+ if [[ $file == "$HOSTS_FILE" ]] || [[ $file == "/etc/hosts" ]]; then
+ log_message "Event detected: $event on $file at $time"
+
+ # Small delay to avoid rapid-fire events
+ sleep 2
+
+ # Check if restoration is needed
+ if needs_restoration; then
+ restore_hosts_file
+ else
+ log_message "Hosts file check passed - no restoration needed"
fi
+ fi
done
}
# Function to monitor with polling (fallback)
monitor_with_polling() {
- log_message "Starting hosts file monitoring with polling (fallback method)"
-
- while true; do
- if needs_restoration; then
- restore_hosts_file
- fi
-
- # Check every 30 seconds
- sleep 30
- done
+ log_message "Starting hosts file monitoring with polling (fallback method)"
+
+ while true; do
+ if needs_restoration; then
+ restore_hosts_file
+ fi
+
+ # Check every 30 seconds
+ sleep 30
+ done
}
# Main execution
log_message "=== Hosts File Monitor Started ==="
# Check if inotify-tools is available
-if command -v inotifywait >/dev/null 2>&1; then
- log_message "Using inotify for file monitoring"
- monitor_with_inotify
+if command -v inotifywait > /dev/null 2>&1; then
+ log_message "Using inotify for file monitoring"
+ monitor_with_inotify
else
- log_message "inotify-tools not available, using polling method"
- log_message "Consider installing inotify-tools for better performance: pacman -S inotify-tools"
- monitor_with_polling
+ log_message "inotify-tools not available, using polling method"
+ log_message "Consider installing inotify-tools for better performance: pacman -S inotify-tools"
+ monitor_with_polling
fi
diff --git a/scripts/system-maintenance/bin/periodic-system-maintenance.sh b/scripts/system-maintenance/bin/periodic-system-maintenance.sh
index cd80264..2127daf 100755
--- a/scripts/system-maintenance/bin/periodic-system-maintenance.sh
+++ b/scripts/system-maintenance/bin/periodic-system-maintenance.sh
@@ -13,30 +13,30 @@ HOSTS_INSTALL_SCRIPT="__HOSTS_INSTALL_SCRIPT__"
# Function to log with timestamp
log_message() {
- echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
+ echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# Function to execute with logging
execute_with_log() {
- local script_path="$1"
- local script_name="$2"
-
- log_message "Starting $script_name"
- echo "Executing $script_name..." >&2
-
- if [[ -f "$script_path" ]]; then
- if bash "$script_path" >> "$LOG_FILE" 2>&1; then
- log_message "$script_name completed successfully"
- echo "✓ $script_name completed successfully" >&2
- else
- local ec=$?
- log_message "$script_name failed with exit code $ec"
- echo "✗ $script_name failed (exit $ec)" >&2
- fi
+ local script_path="$1"
+ local script_name="$2"
+
+ log_message "Starting $script_name"
+ echo "Executing $script_name..." >&2
+
+ if [[ -f $script_path ]]; then
+ if bash "$script_path" >> "$LOG_FILE" 2>&1; then
+ log_message "$script_name completed successfully"
+ echo "✓ $script_name completed successfully" >&2
else
- log_message "$script_name not found at $script_path"
- echo "✗ $script_name not found at $script_path" >&2
+ local ec=$?
+ log_message "$script_name failed with exit code $ec"
+ echo "✗ $script_name failed (exit $ec)" >&2
fi
+ else
+ log_message "$script_name not found at $script_path"
+ echo "✗ $script_name not found at $script_path" >&2
+ fi
}
# Start maintenance
diff --git a/scripts/test_removal.sh b/scripts/test_removal.sh
index 31a3903..58e7adc 100644
--- a/scripts/test_removal.sh
+++ b/scripts/test_removal.sh
@@ -5,38 +5,37 @@
set -euo pipefail
DOWNLOADS_DIR="$HOME/Downloads"
-HOME_DIR="$HOME"
# Test function
test_file_removal() {
- local files=()
-
- # Find a few test files
- while IFS= read -r -d '' file; do
- files+=("$file")
- done < <(find "$DOWNLOADS_DIR" -name "*.jpg" -print0 2>/dev/null | head -z -n 2)
-
- echo "Found ${#files[@]} test files:"
- for file in "${files[@]}"; do
- echo " - $file"
- done
-
- echo "Attempting to remove files..."
- local removed=0
- local failed=0
-
- for file in "${files[@]}"; do
- echo "Removing: $file"
- if rm "$file" 2>/dev/null; then
- echo " SUCCESS"
- ((removed++))
- else
- echo " FAILED (exit code: $?)"
- ((failed++))
- fi
- done
-
- echo "Results: $removed removed, $failed failed"
+ local files=()
+
+ # Find a few test files
+ while IFS= read -r -d '' file; do
+ files+=("$file")
+ done < <(find "$DOWNLOADS_DIR" -name "*.jpg" -print0 2> /dev/null | head -z -n 2)
+
+ echo "Found ${#files[@]} test files:"
+ for file in "${files[@]}"; do
+ echo " - $file"
+ done
+
+ echo "Attempting to remove files..."
+ local removed=0
+ local failed=0
+
+ for file in "${files[@]}"; do
+ echo "Removing: $file"
+ if rm "$file" 2> /dev/null; then
+ echo " SUCCESS"
+ ((removed++))
+ else
+ echo " FAILED (exit code: $?)"
+ ((failed++))
+ fi
+ done
+
+ echo "Results: $removed removed, $failed failed"
}
test_file_removal
diff --git a/scripts/utils/convert_words.sh b/scripts/utils/convert_words.sh
index b2849d5..0115a2c 100755
--- a/scripts/utils/convert_words.sh
+++ b/scripts/utils/convert_words.sh
@@ -2,25 +2,25 @@
# Check if input and output files are provided
if [ $# -ne 2 ]; then
- echo "Usage: $0 input_file.txt output_file.txt"
- exit 1
+ echo "Usage: $0 input_file.txt output_file.txt"
+ exit 1
fi
# Check if the input file exists
if [ ! -f "$1" ]; then
- echo "Error: File '$1' not found"
- exit 1
+ echo "Error: File '$1' not found"
+ exit 1
fi
# Store output file name
output_file="$2"
# Clear output file at the beginning
-> "$output_file"
+: > "$output_file"
# Process file using a pipeline of specialized tools
# 1. tr - remove non-alphabetic chars except newlines
-# 2. tr - convert to uppercase
+# 2. tr - convert to uppercase
# 3. grep - filter by length (5-8 characters)
# 4. sort - sort the words alphabetically
# 5. uniq - remove duplicates
diff --git a/scripts/utils/organize_downloads.sh b/scripts/utils/organize_downloads.sh
index f0dce47..c3e2c92 100755
--- a/scripts/utils/organize_downloads.sh
+++ b/scripts/utils/organize_downloads.sh
@@ -13,7 +13,7 @@ SAMPLE_LIMIT=20
# Simple usage helper
usage() {
- cat <&2
- usage
- exit 1
- ;;
- esac
+ case "${1}" in
+ -n | --dry-run)
+ DRY_RUN=true
+ shift
+ ;;
+ --sample=*)
+ SAMPLE_LIMIT="${1#*=}"
+ shift
+ ;;
+ -h | --help)
+ usage
+ exit 0
+ ;;
+ *)
+ echo "Unknown option: $1" >&2
+ usage
+ exit 1
+ ;;
+ esac
done
# Function to check if file has media extension
is_media_file() {
- local file="$1"
- local extension="${file##*.}"
- extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]')
-
- # Check if it's an image
- for ext in "${IMAGE_EXTENSIONS[@]}"; do
- if [[ "$extension" == "$ext" ]]; then
- return 0
- fi
- done
-
- # Check if it's a video
- for ext in "${VIDEO_EXTENSIONS[@]}"; do
- if [[ "$extension" == "$ext" ]]; then
- return 0
- fi
- done
-
- return 1
+ local file="$1"
+ local extension="${file##*.}"
+ extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]')
+
+ # Check if it's an image
+ for ext in "${IMAGE_EXTENSIONS[@]}"; do
+ if [[ $extension == "$ext" ]]; then
+ return 0
+ fi
+ done
+
+ # Check if it's a video
+ for ext in "${VIDEO_EXTENSIONS[@]}"; do
+ if [[ $extension == "$ext" ]]; then
+ return 0
+ fi
+ done
+
+ return 1
}
# Function to find media files in a directory (non-recursive for home, avoid common system dirs)
find_media_files() {
- local search_dir="$1"
- local files=()
- # Directories to exclude under Downloads
- local -a EXCLUDES=(
- ".git" ".hg" ".svn" ".cache" "node_modules" "dist" "build" "out" "target" "coverage" "__pycache__" "venv" ".venv"
- # previous staging dirs created by this script
- ".media_organize_" "media_organize_"
- )
-
- if [[ "$search_dir" == "$HOME_DIR" ]]; then
- # For home directory, only check files directly in ~ (not subdirectories)
- # Exclude common system/config directories
- while IFS= read -r -d '' file; do
- local basename=$(basename "$file")
- # Skip hidden files and common system directories
- if [[ ! "$basename" =~ ^\. ]] && [[ -f "$file" ]]; then
- if is_media_file "$file"; then
- files+=("$file")
- fi
- fi
- done < <(find "$search_dir" -maxdepth 1 -type f -print0 2>/dev/null)
- else
- # For Downloads, search recursively, pruning excluded directories
- # Build prune expression
- local prune_expr=()
- for ex in "${EXCLUDES[@]}"; do
- prune_expr+=( -name "$ex*" -o )
- done
- # Remove trailing -o
- unset 'prune_expr[${#prune_expr[@]}-1]'
+ local search_dir="$1"
+ local files=()
+ # Directories to exclude under Downloads
+ local -a EXCLUDES=(
+ ".git" ".hg" ".svn" ".cache" "node_modules" "dist" "build" "out" "target" "coverage" "__pycache__" "venv" ".venv"
+ # previous staging dirs created by this script
+ ".media_organize_" "media_organize_"
+ )
- while IFS= read -r -d '' file; do
- if is_media_file "$file"; then
- files+=("$file")
- fi
- done < <(find "$search_dir" \( -type d \( ${prune_expr[@]} \) -prune \) -o -type f -print0 2>/dev/null)
- fi
-
- printf '%s\n' "${files[@]}"
+ if [[ $search_dir == "$HOME_DIR" ]]; then
+ # For home directory, only check files directly in ~ (not subdirectories)
+ # Exclude common system/config directories
+ while IFS= read -r -d '' file; do
+ local basename
+ basename=$(basename "$file")
+ # Skip hidden files and common system directories
+ if [[ ! $basename =~ ^\. ]] && [[ -f $file ]]; then
+ if is_media_file "$file"; then
+ files+=("$file")
+ fi
+ fi
+ done < <(find "$search_dir" -maxdepth 1 -type f -print0 2> /dev/null)
+ else
+ # For Downloads, search recursively, pruning excluded directories
+ # Build prune expression
+ local prune_expr=()
+ for ex in "${EXCLUDES[@]}"; do
+ prune_expr+=(-name "$ex*" -o)
+ done
+ # Remove trailing -o
+ unset 'prune_expr[${#prune_expr[@]}-1]'
+
+ while IFS= read -r -d '' file; do
+ if is_media_file "$file"; then
+ files+=("$file")
+ fi
+ done < <(find "$search_dir" \( -type d \( "${prune_expr[@]}" \) -prune \) -o -type f -print0 2> /dev/null)
+ fi
+
+ printf '%s\n' "${files[@]}"
}
# Function to create timestamped zip archive
create_media_archive() {
- local files=("$@")
-
- if [[ ${#files[@]} -eq 0 ]]; then
- log "No media files found to archive."
- return 0
- fi
-
- # Create timestamp for archive name
- local timestamp=$(date '+%Y%m%d_%H%M%S')
- local archive_name="media_archive_${timestamp}.zip"
- local archive_path="$DOWNLOADS_DIR/$archive_name"
-
- # Create temporary directory (fallback to /tmp if needed)
- if ! mkdir -p "$TEMP_DIR" 2>/dev/null; then
- TEMP_DIR="/tmp/media_organize_$$"
- mkdir -p "$TEMP_DIR"
- fi
+ local files=("$@")
- # Ensure temp dir is cleaned up on function return; trap unsets itself after running
- trap 'rm -rf "$TEMP_DIR" 2>/dev/null || true; trap - RETURN' RETURN
-
- log "Found ${#files[@]} media files to archive."
- log "Creating archive: $archive_path"
-
- # Copy files to temp directory maintaining relative structure
- local successfully_copied=()
- local copy_errors=0
-
- for file in "${files[@]}"; do
- if [[ -f "$file" ]]; then
- local relative_path=""
- if [[ "$file" == "$DOWNLOADS_DIR"* ]]; then
- relative_path="downloads/${file#$DOWNLOADS_DIR/}"
- else
- relative_path="home/${file#$HOME_DIR/}"
- fi
-
- local temp_file="$TEMP_DIR/$relative_path"
- local temp_dir=$(dirname "$temp_file")
-
- mkdir -p "$temp_dir"
- # Check readability first to provide a clearer error
- if [[ ! -r "$file" ]]; then
- log "WARNING: Cannot read $file (permission denied?)"
- ((copy_errors++))
- continue
- fi
+ if [[ ${#files[@]} -eq 0 ]]; then
+ log "No media files found to archive."
+ return 0
+ fi
- # Attempt copy and capture any error for logging
- local cp_err
- if cp_err=$(cp -p "$file" "$temp_file" 2>&1); then
- successfully_copied+=("$file")
- else
- # Surface the cp error so the user can see the reason
- log "WARNING: Failed to copy $file -> $cp_err"
- # Special hint for space issues
- if echo "$cp_err" | grep -qi "No space left on device"; then
- log "HINT: Not enough free space to stage files. Using $TEMP_DIR. Free up space or change TEMP_DIR."
- fi
- ((copy_errors++))
- fi
+ # Create timestamp for archive name
+ local timestamp
+ timestamp=$(date '+%Y%m%d_%H%M%S')
+ local archive_name="media_archive_${timestamp}.zip"
+ local archive_path="$DOWNLOADS_DIR/$archive_name"
+
+ # Create temporary directory (fallback to /tmp if needed)
+ if ! mkdir -p "$TEMP_DIR" 2> /dev/null; then
+ TEMP_DIR="/tmp/media_organize_$$"
+ mkdir -p "$TEMP_DIR"
+ fi
+
+ # Ensure temp dir is cleaned up on function return; trap unsets itself after running
+ trap 'rm -rf "$TEMP_DIR" 2>/dev/null || true; trap - RETURN' RETURN
+
+ log "Found ${#files[@]} media files to archive."
+ log "Creating archive: $archive_path"
+
+ # Copy files to temp directory maintaining relative structure
+ local successfully_copied=()
+ local copy_errors=0
+
+ for file in "${files[@]}"; do
+ if [[ -f $file ]]; then
+ local relative_path=""
+ if [[ $file == "$DOWNLOADS_DIR"* ]]; then
+ relative_path="downloads/${file#"$DOWNLOADS_DIR"/}"
+ else
+ relative_path="home/${file#"$HOME_DIR"/}"
+ fi
+
+ local temp_file="$TEMP_DIR/$relative_path"
+ local temp_dir
+ temp_dir=$(dirname "$temp_file")
+
+ mkdir -p "$temp_dir"
+ # Check readability first to provide a clearer error
+ if [[ ! -r $file ]]; then
+ log "WARNING: Cannot read $file (permission denied?)"
+ ((copy_errors++))
+ continue
+ fi
+
+ # Attempt copy and capture any error for logging
+ local cp_err
+ if cp_err=$(cp -p "$file" "$temp_file" 2>&1); then
+ successfully_copied+=("$file")
+ else
+ # Surface the cp error so the user can see the reason
+ log "WARNING: Failed to copy $file -> $cp_err"
+ # Special hint for space issues
+ if echo "$cp_err" | grep -qi "No space left on device"; then
+ log "HINT: Not enough free space to stage files. Using $TEMP_DIR. Free up space or change TEMP_DIR."
fi
+ ((copy_errors++))
+ fi
+ fi
+ done
+
+ if [[ ${#successfully_copied[@]} -eq 0 ]]; then
+ log "ERROR: No files were successfully copied to temp directory."
+ rm -rf "$TEMP_DIR"
+ return 1
+ fi
+
+ if [[ $copy_errors -gt 0 ]]; then
+ log "WARNING: $copy_errors files failed to copy."
+ fi
+
+ # Create zip archive with maximum compression
+ log "Creating zip archive with ${#successfully_copied[@]} files..."
+ cd "$TEMP_DIR"
+ if zip -9 -r "$archive_path" . 2>&1; then
+ log "Successfully created archive with ${#successfully_copied[@]} files."
+
+ # Verify the zip file was actually created and is not empty
+ if [[ ! -f $archive_path ]]; then
+ log "ERROR: Archive file was not created at $archive_path"
+ rm -rf "$TEMP_DIR"
+ return 1
+ fi
+
+ local archive_size
+ archive_size=$(stat -c%s "$archive_path" 2> /dev/null || echo "0")
+ if [[ $archive_size -eq 0 ]]; then
+ log "ERROR: Archive file is empty"
+ rm -rf "$TEMP_DIR"
+ return 1
+ fi
+
+ # Remove original files only if zip was successful
+ local removed_count=0
+ local remove_errors=0
+
+ log "Starting to remove ${#successfully_copied[@]} original files..."
+
+ # Temporarily disable strict error handling for file removal
+ set +e
+
+ for file in "${successfully_copied[@]}"; do
+ if [[ -f $file ]]; then
+ if rm "$file" 2> /dev/null; then
+ removed_count=$((removed_count + 1))
+ log "Removed: $(basename "$file")"
+ else
+ remove_errors=$((remove_errors + 1))
+ log "ERROR: Failed to remove $(basename "$file")"
+ fi
+ else
+ log "WARNING: File no longer exists: $(basename "$file")"
+ fi
done
-
- if [[ ${#successfully_copied[@]} -eq 0 ]]; then
- log "ERROR: No files were successfully copied to temp directory."
- rm -rf "$TEMP_DIR"
- return 1
+
+ # Re-enable strict error handling
+ set -e
+
+ log "Successfully removed $removed_count original files."
+ if [[ $remove_errors -gt 0 ]]; then
+ log "WARNING: Failed to remove $remove_errors files."
fi
-
- if [[ $copy_errors -gt 0 ]]; then
- log "WARNING: $copy_errors files failed to copy."
- fi
-
- # Create zip archive with maximum compression
- log "Creating zip archive with ${#successfully_copied[@]} files..."
- cd "$TEMP_DIR"
- if zip -9 -r "$archive_path" . 2>&1; then
- log "Successfully created archive with ${#successfully_copied[@]} files."
-
- # Verify the zip file was actually created and is not empty
- if [[ ! -f "$archive_path" ]]; then
- log "ERROR: Archive file was not created at $archive_path"
- rm -rf "$TEMP_DIR"
- return 1
- fi
-
- local archive_size=$(stat -c%s "$archive_path" 2>/dev/null || echo "0")
- if [[ "$archive_size" -eq 0 ]]; then
- log "ERROR: Archive file is empty"
- rm -rf "$TEMP_DIR"
- return 1
- fi
-
- # Remove original files only if zip was successful
- local removed_count=0
- local remove_errors=0
-
- log "Starting to remove ${#successfully_copied[@]} original files..."
-
- # Temporarily disable strict error handling for file removal
- set +e
-
- for file in "${successfully_copied[@]}"; do
- if [[ -f "$file" ]]; then
- if rm "$file" 2>/dev/null; then
- removed_count=$((removed_count + 1))
- log "Removed: $(basename "$file")"
- else
- remove_errors=$((remove_errors + 1))
- log "ERROR: Failed to remove $(basename "$file")"
- fi
- else
- log "WARNING: File no longer exists: $(basename "$file")"
- fi
- done
-
- # Re-enable strict error handling
- set -e
-
- log "Successfully removed $removed_count original files."
- if [[ $remove_errors -gt 0 ]]; then
- log "WARNING: Failed to remove $remove_errors files."
- fi
- log "Archive size: $(du -h "$archive_path" | cut -f1)"
-
+ log "Archive size: $(du -h "$archive_path" | cut -f1)"
+
# Cleanup temp directory (trap will also attempt, which is safe)
rm -rf "$TEMP_DIR"
-
- # Return success only if we removed files or there were no files to remove
- if [[ $removed_count -gt 0 ]] || [[ ${#successfully_copied[@]} -eq 0 ]]; then
- return 0
- else
- log "ERROR: Failed to remove any files after successful archive creation."
- return 1
- fi
+
+ # Return success only if we removed files or there were no files to remove
+ if [[ $removed_count -gt 0 ]] || [[ ${#successfully_copied[@]} -eq 0 ]]; then
+ return 0
else
- log "ERROR: Failed to create archive. Original files preserved."
- log "Zip command failed."
- rm -rf "$TEMP_DIR"
- return 1
+ log "ERROR: Failed to remove any files after successful archive creation."
+ return 1
fi
+ else
+ log "ERROR: Failed to create archive. Original files preserved."
+ log "Zip command failed."
+ rm -rf "$TEMP_DIR"
+ return 1
+ fi
}
# Main execution
main() {
- log "Starting media file organization..."
-
- # Check if required directories exist
- if [[ ! -d "$DOWNLOADS_DIR" ]]; then
- log "ERROR: Downloads directory not found: $DOWNLOADS_DIR"
- exit 1
- fi
-
- if [[ ! -d "$HOME_DIR" ]]; then
- log "ERROR: Home directory not found: $HOME_DIR"
- exit 1
- fi
-
- # Check if zip command is available
- if ! command -v zip >/dev/null 2>&1; then
- log "ERROR: zip command not found. Please install zip package."
- exit 1
- fi
-
- # Find all media files
- log "Scanning for media files..."
- local all_files=()
-
- # Find files in Downloads directory
- log "Scanning Downloads directory..."
- while IFS= read -r file; do
- [[ -n "$file" ]] && all_files+=("$file")
- done < <(find_media_files "$DOWNLOADS_DIR")
-
- # Find files in home directory (only direct files, not subdirectories)
- log "Scanning home directory (root level only)..."
- while IFS= read -r file; do
- [[ -n "$file" ]] && all_files+=("$file")
- done < <(find_media_files "$HOME_DIR")
-
- if $DRY_RUN; then
- log "Dry-run mode: summarizing what would be archived."
- if [[ ${#all_files[@]} -eq 0 ]]; then
- log "No media files found to organize."
- exit 0
- fi
+ log "Starting media file organization..."
- # Count by extension
- declare -A ext_counts=()
- # Count by top-level directory under Downloads
- declare -A dir_counts=()
- # Sample paths for suspect extensions
- declare -A samples_ts=()
- declare -A samples_svg=()
- declare -A samples_ico=()
+ # Check if required directories exist
+ if [[ ! -d $DOWNLOADS_DIR ]]; then
+ log "ERROR: Downloads directory not found: $DOWNLOADS_DIR"
+ exit 1
+ fi
- for f in "${all_files[@]}"; do
- # Extension
- ext="${f##*.}"
- ext="${ext,,}"
- ((ext_counts["$ext"]++)) || true
+ if [[ ! -d $HOME_DIR ]]; then
+ log "ERROR: Home directory not found: $HOME_DIR"
+ exit 1
+ fi
- # Top directory under Downloads
- if [[ "$f" == "$DOWNLOADS_DIR"/* ]]; then
- rel="${f#"$DOWNLOADS_DIR"/}"
- topdir="${rel%%/*}"
- [[ "$topdir" == "$rel" ]] && topdir="."
- ((dir_counts["$topdir"]++)) || true
- else
- ((dir_counts["~"]++)) || true
- fi
+ # Check if zip command is available
+ if ! command -v zip > /dev/null 2>&1; then
+ log "ERROR: zip command not found. Please install zip package."
+ exit 1
+ fi
- # Samples for suspect extensions
- case "$ext" in
- ts)
- if [[ ${#samples_ts[@]} -lt $SAMPLE_LIMIT ]]; then samples_ts["$f"]=1; fi
- ;;
- svg)
- if [[ ${#samples_svg[@]} -lt $SAMPLE_LIMIT ]]; then samples_svg["$f"]=1; fi
- ;;
- ico)
- if [[ ${#samples_ico[@]} -lt $SAMPLE_LIMIT ]]; then samples_ico["$f"]=1; fi
- ;;
- esac
- done
+ # Find all media files
+ log "Scanning for media files..."
+ local all_files=()
- echo ""
- echo "Summary by extension (top 20):"
- for k in "${!ext_counts[@]}"; do
- printf "%8d %s\n" "${ext_counts[$k]}" "$k"
- done | sort -nr | head -n 20
+ # Find files in Downloads directory
+ log "Scanning Downloads directory..."
+ while IFS= read -r file; do
+ [[ -n $file ]] && all_files+=("$file")
+ done < <(find_media_files "$DOWNLOADS_DIR")
- echo ""
- echo "Top contributing directories under Downloads (top 20):"
- for k in "${!dir_counts[@]}"; do
- printf "%8d %s\n" "${dir_counts[$k]}" "$k"
- done | sort -nr | head -n 20
+ # Find files in home directory (only direct files, not subdirectories)
+ log "Scanning home directory (root level only)..."
+ while IFS= read -r file; do
+ [[ -n $file ]] && all_files+=("$file")
+ done < <(find_media_files "$HOME_DIR")
- echo ""
- if [[ ${#samples_ts[@]} -gt 0 ]]; then
- echo "Sample .ts files (TypeScript vs Transport Stream) up to $SAMPLE_LIMIT:"
- for p in "${!samples_ts[@]}"; do echo " $p"; done | sort
- echo ""
- fi
- if [[ ${#samples_svg[@]} -gt 0 ]]; then
- echo "Sample .svg files up to $SAMPLE_LIMIT:"
- for p in "${!samples_svg[@]}"; do echo " $p"; done | sort
- echo ""
- fi
- if [[ ${#samples_ico[@]} -gt 0 ]]; then
- echo "Sample .ico files up to $SAMPLE_LIMIT:"
- for p in "${!samples_ico[@]}"; do echo " $p"; done | sort
- echo ""
- fi
-
- echo "Total files that would be archived: ${#all_files[@]}"
- echo "(Use: $(basename "$0") --dry-run --sample=50 to see more examples.)"
- exit 0
+ if $DRY_RUN; then
+ log "Dry-run mode: summarizing what would be archived."
+ if [[ ${#all_files[@]} -eq 0 ]]; then
+ log "No media files found to organize."
+ exit 0
fi
- # Create archive if files found
- if [[ ${#all_files[@]} -gt 0 ]]; then
- create_media_archive "${all_files[@]}"
- log "Media organization completed successfully."
- else
- log "No media files found to organize."
+ # Count by extension
+ declare -A ext_counts=()
+ # Count by top-level directory under Downloads
+ declare -A dir_counts=()
+ # Sample paths for suspect extensions
+ declare -A samples_ts=()
+ declare -A samples_svg=()
+ declare -A samples_ico=()
+
+ for f in "${all_files[@]}"; do
+ # Extension
+ ext="${f##*.}"
+ ext="${ext,,}"
+ ((ext_counts["$ext"]++)) || true
+
+ # Top directory under Downloads
+ if [[ $f == "$DOWNLOADS_DIR"/* ]]; then
+ rel="${f#"$DOWNLOADS_DIR"/}"
+ topdir="${rel%%/*}"
+ [[ $topdir == "$rel" ]] && topdir="."
+ ((dir_counts["$topdir"]++)) || true
+ else
+ ((dir_counts["~"]++)) || true
+ fi
+
+ # Samples for suspect extensions
+ case "$ext" in
+ ts)
+ if [[ ${#samples_ts[@]} -lt $SAMPLE_LIMIT ]]; then samples_ts["$f"]=1; fi
+ ;;
+ svg)
+ if [[ ${#samples_svg[@]} -lt $SAMPLE_LIMIT ]]; then samples_svg["$f"]=1; fi
+ ;;
+ ico)
+ if [[ ${#samples_ico[@]} -lt $SAMPLE_LIMIT ]]; then samples_ico["$f"]=1; fi
+ ;;
+ esac
+ done
+
+ echo ""
+ echo "Summary by extension (top 20):"
+ for k in "${!ext_counts[@]}"; do
+ printf "%8d %s\n" "${ext_counts[$k]}" "$k"
+ done | sort -nr | head -n 20
+
+ echo ""
+ echo "Top contributing directories under Downloads (top 20):"
+ for k in "${!dir_counts[@]}"; do
+ printf "%8d %s\n" "${dir_counts[$k]}" "$k"
+ done | sort -nr | head -n 20
+
+ echo ""
+ if [[ ${#samples_ts[@]} -gt 0 ]]; then
+ echo "Sample .ts files (TypeScript vs Transport Stream) up to $SAMPLE_LIMIT:"
+ for p in "${!samples_ts[@]}"; do echo " $p"; done | sort
+ echo ""
fi
+ if [[ ${#samples_svg[@]} -gt 0 ]]; then
+ echo "Sample .svg files up to $SAMPLE_LIMIT:"
+ for p in "${!samples_svg[@]}"; do echo " $p"; done | sort
+ echo ""
+ fi
+ if [[ ${#samples_ico[@]} -gt 0 ]]; then
+ echo "Sample .ico files up to $SAMPLE_LIMIT:"
+ for p in "${!samples_ico[@]}"; do echo " $p"; done | sort
+ echo ""
+ fi
+
+ echo "Total files that would be archived: ${#all_files[@]}"
+ echo "(Use: $(basename "$0") --dry-run --sample=50 to see more examples.)"
+ exit 0
+ fi
+
+ # Create archive if files found
+ if [[ ${#all_files[@]} -gt 0 ]]; then
+ create_media_archive "${all_files[@]}"
+ log "Media organization completed successfully."
+ else
+ log "No media files found to organize."
+ fi
}
# Run main function
-main "$@"
\ No newline at end of file
+main "$@"
diff --git a/scripts/utils/setup_media_organizer.sh b/scripts/utils/setup_media_organizer.sh
index 9256ace..6b8c149 100755
--- a/scripts/utils/setup_media_organizer.sh
+++ b/scripts/utils/setup_media_organizer.sh
@@ -13,20 +13,20 @@ USER_NAME="$(whoami)"
# Function to log messages
log() {
- echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Check if organize script exists
-if [[ ! -f "$ORGANIZE_SCRIPT" ]]; then
- log "ERROR: organize_downloads.sh not found at $ORGANIZE_SCRIPT"
- exit 1
+if [[ ! -f $ORGANIZE_SCRIPT ]]; then
+ log "ERROR: organize_downloads.sh not found at $ORGANIZE_SCRIPT"
+ exit 1
fi
# Check if running as root for systemd service creation
if [[ $EUID -ne 0 ]]; then
- log "This script needs to be run as root to create systemd service."
- log "Please run: sudo $0"
- exit 1
+ log "This script needs to be run as root to create systemd service."
+ log "Please run: sudo $0"
+ exit 1
fi
log "Setting up media organizer startup service..."
diff --git a/scripts/utils/setup_passwordless_system.sh b/scripts/utils/setup_passwordless_system.sh
index 91dec8a..089c1ed 100755
--- a/scripts/utils/setup_passwordless_system.sh
+++ b/scripts/utils/setup_passwordless_system.sh
@@ -6,34 +6,34 @@
# --reboot: Offer to reboot after setup completion
# --logout: Allow restart of LightDM (which will logout the user)
-set -e # Exit on any error
+set -e # Exit on any error
# Check for flags
OFFER_REBOOT=false
ALLOW_LOGOUT=false
for arg in "$@"; do
- case $arg in
- --reboot)
- OFFER_REBOOT=true
- shift
- ;;
- --logout)
- ALLOW_LOGOUT=true
- shift
- ;;
- *)
- # Unknown option, keep it for sudo check
- ;;
- esac
+ case $arg in
+ --reboot)
+ OFFER_REBOOT=true
+ shift
+ ;;
+ --logout)
+ ALLOW_LOGOUT=true
+ shift
+ ;;
+ *)
+ # Unknown option, keep it for sudo check
+ ;;
+ esac
done
# Function to check and request sudo privileges
check_sudo() {
- if [[ $EUID -ne 0 ]]; then
- echo "This script requires sudo privileges to modify system configurations."
- echo "Requesting sudo access..."
- exec sudo "$0" "$@"
- fi
+ if [[ $EUID -ne 0 ]]; then
+ echo "This script requires sudo privileges to modify system configurations."
+ echo "Requesting sudo access..."
+ exec sudo "$0" "$@"
+ fi
}
# Check for sudo privileges first
@@ -46,9 +46,9 @@ echo "User: $USER"
echo "Original user: ${SUDO_USER:-$USER}"
# Verify we have a valid user
-if [[ -z "${SUDO_USER}" ]]; then
- echo "Error: Could not determine the original user. Please run this script with sudo."
- exit 1
+if [[ -z ${SUDO_USER} ]]; then
+ echo "Error: Could not determine the original user. Please run this script with sudo."
+ exit 1
fi
TARGET_USER="${SUDO_USER}"
@@ -56,24 +56,26 @@ echo "Target user for configuration: $TARGET_USER"
# Function to backup files
backup_file() {
- local file="$1"
- if [[ -f "$file" ]]; then
- local backup="${file}.backup.$(date +%Y%m%d_%H%M%S)"
- cp "$file" "$backup"
- echo "✓ Backed up $file to $backup"
- fi
+ local file="$1"
+ if [[ -f $file ]]; then
+ local backup timestamp
+ timestamp=$(date +%Y%m%d_%H%M%S)
+ backup="${file}.backup.$timestamp"
+ cp "$file" "$backup"
+ echo "✓ Backed up $file to $backup"
+ fi
}
# Function to configure passwordless sudo
configure_passwordless_sudo() {
- echo ""
- echo "1. Configuring Passwordless Sudo..."
- echo "=================================="
-
- local sudoers_file="/etc/sudoers.d/99-passwordless-${TARGET_USER}"
-
- # Create sudoers configuration for passwordless access
- cat > "$sudoers_file" << EOF
+ echo ""
+ echo "1. Configuring Passwordless Sudo..."
+ echo "=================================="
+
+ local sudoers_file="/etc/sudoers.d/99-passwordless-${TARGET_USER}"
+
+ # Create sudoers configuration for passwordless access
+ cat > "$sudoers_file" << EOF
# Passwordless sudo configuration for user: ${TARGET_USER}
# Created by setup_passwordless_system.sh on $(date)
# WARNING: This allows the user to run any command without password
@@ -88,55 +90,55 @@ Defaults:${TARGET_USER} !requiretty
Defaults:${TARGET_USER} env_keep += "HOME PATH DISPLAY XAUTHORITY"
EOF
- # Set proper permissions for sudoers file
- chmod 440 "$sudoers_file"
-
- # Verify the sudoers file syntax
- if visudo -c -f "$sudoers_file"; then
- echo "✓ Passwordless sudo configured for user: $TARGET_USER"
- echo "✓ Sudoers file created: $sudoers_file"
- else
- echo "✗ Error: Invalid sudoers syntax. Removing file for safety."
- rm -f "$sudoers_file"
- exit 1
- fi
+ # Set proper permissions for sudoers file
+ chmod 440 "$sudoers_file"
+
+ # Verify the sudoers file syntax
+ if visudo -c -f "$sudoers_file"; then
+ echo "✓ Passwordless sudo configured for user: $TARGET_USER"
+ echo "✓ Sudoers file created: $sudoers_file"
+ else
+ echo "✗ Error: Invalid sudoers syntax. Removing file for safety."
+ rm -f "$sudoers_file"
+ exit 1
+ fi
}
# Function to configure lightdm auto-login
configure_lightdm_autologin() {
- echo ""
- echo "2. Configuring LightDM Auto-Login..."
- echo "==================================="
-
- local lightdm_conf="/etc/lightdm/lightdm.conf"
- local lightdm_conf_dir="/etc/lightdm/lightdm.conf.d"
- local custom_conf="$lightdm_conf_dir/50-autologin.conf"
-
- # Create lightdm config directory if it doesn't exist
- mkdir -p "$lightdm_conf_dir"
-
- # Backup existing lightdm configuration
- backup_file "$lightdm_conf"
-
- # Check if lightdm is installed
- if ! command -v lightdm &> /dev/null; then
- echo "Warning: LightDM not found. Installing lightdm..."
- pacman -S --noconfirm lightdm lightdm-gtk-greeter
- fi
-
- # Method 1: Update the main lightdm.conf file directly
- sed -i "/^#autologin-user=/c\autologin-user=${TARGET_USER}" "$lightdm_conf"
- sed -i "/^#autologin-user-timeout=/c\autologin-user-timeout=0" "$lightdm_conf"
- sed -i "/^#autologin-session=/c\autologin-session=i3" "$lightdm_conf"
- sed -i "/^#autologin-in-background=/c\autologin-in-background=false" "$lightdm_conf"
-
- # Also set user-session to i3 as fallback
- sed -i "/^#user-session=/c\user-session=i3" "$lightdm_conf"
-
- echo "✓ LightDM auto-login configured in main config file"
-
- # Method 2: Also create the separate config file for redundancy
- cat > "$custom_conf" << EOF
+ echo ""
+ echo "2. Configuring LightDM Auto-Login..."
+ echo "==================================="
+
+ local lightdm_conf="/etc/lightdm/lightdm.conf"
+ local lightdm_conf_dir="/etc/lightdm/lightdm.conf.d"
+ local custom_conf="$lightdm_conf_dir/50-autologin.conf"
+
+ # Create lightdm config directory if it doesn't exist
+ mkdir -p "$lightdm_conf_dir"
+
+ # Backup existing lightdm configuration
+ backup_file "$lightdm_conf"
+
+ # Check if lightdm is installed
+ if ! command -v lightdm &> /dev/null; then
+ echo "Warning: LightDM not found. Installing lightdm..."
+ pacman -S --noconfirm lightdm lightdm-gtk-greeter
+ fi
+
+ # Method 1: Update the main lightdm.conf file directly
+ sed -i "/^#autologin-user=/c\autologin-user=${TARGET_USER}" "$lightdm_conf"
+ sed -i "/^#autologin-user-timeout=/c\autologin-user-timeout=0" "$lightdm_conf"
+ sed -i "/^#autologin-session=/c\autologin-session=i3" "$lightdm_conf"
+ sed -i "/^#autologin-in-background=/c\autologin-in-background=false" "$lightdm_conf"
+
+ # Also set user-session to i3 as fallback
+ sed -i "/^#user-session=/c\user-session=i3" "$lightdm_conf"
+
+ echo "✓ LightDM auto-login configured in main config file"
+
+ # Method 2: Also create the separate config file for redundancy
+ cat > "$custom_conf" << EOF
# LightDM Auto-Login Configuration
# Created by setup_passwordless_system.sh on $(date)
@@ -158,37 +160,37 @@ greeter-session=lightdm-gtk-greeter
autologin-in-background=false
EOF
- echo "✓ LightDM auto-login also configured in separate config file: $custom_conf"
-
- # Enable lightdm service
- systemctl enable lightdm.service
- echo "✓ LightDM service enabled"
-
- # Restart lightdm to apply changes only if --logout flag is provided
- if [[ "$ALLOW_LOGOUT" == true ]]; then
- echo "Restarting LightDM to apply auto-login settings..."
- systemctl restart lightdm.service
- echo "✓ LightDM restarted"
- else
- echo "✓ LightDM configuration complete (restart lightdm or reboot to activate auto-login)"
- fi
+ echo "✓ LightDM auto-login also configured in separate config file: $custom_conf"
+
+ # Enable lightdm service
+ systemctl enable lightdm.service
+ echo "✓ LightDM service enabled"
+
+ # Restart lightdm to apply changes only if --logout flag is provided
+ if [[ $ALLOW_LOGOUT == true ]]; then
+ echo "Restarting LightDM to apply auto-login settings..."
+ systemctl restart lightdm.service
+ echo "✓ LightDM restarted"
+ else
+ echo "✓ LightDM configuration complete (restart lightdm or reboot to activate auto-login)"
+ fi
}
# Function to configure i3 session
configure_i3_session() {
- echo ""
- echo "3. Configuring i3 Session..."
- echo "==========================="
-
- local xsessions_dir="/usr/share/xsessions"
- local i3_desktop="$xsessions_dir/i3.desktop"
-
- # Create xsessions directory if it doesn't exist
- mkdir -p "$xsessions_dir"
-
- # Check if i3.desktop exists, create if not
- if [[ ! -f "$i3_desktop" ]]; then
- cat > "$i3_desktop" << EOF
+ echo ""
+ echo "3. Configuring i3 Session..."
+ echo "==========================="
+
+ local xsessions_dir="/usr/share/xsessions"
+ local i3_desktop="$xsessions_dir/i3.desktop"
+
+ # Create xsessions directory if it doesn't exist
+ mkdir -p "$xsessions_dir"
+
+ # Check if i3.desktop exists, create if not
+ if [[ ! -f $i3_desktop ]]; then
+ cat > "$i3_desktop" << EOF
[Desktop Entry]
Name=i3
Comment=improved dynamic tiling window manager
@@ -199,42 +201,42 @@ X-LightDM-DesktopName=i3
DesktopNames=i3
Keywords=tiling;wm;windowmanager;window;manager;
EOF
- echo "✓ Created i3 desktop session file: $i3_desktop"
- else
- echo "✓ i3 desktop session file already exists"
- fi
-
- # Ensure user has i3 config directory
- local user_home="/home/${TARGET_USER}"
- local i3_config_dir="$user_home/.config/i3"
-
- if [[ ! -d "$i3_config_dir" ]]; then
- sudo -u "$TARGET_USER" mkdir -p "$i3_config_dir"
- echo "✓ Created i3 config directory for user: $TARGET_USER"
- fi
+ echo "✓ Created i3 desktop session file: $i3_desktop"
+ else
+ echo "✓ i3 desktop session file already exists"
+ fi
+
+ # Ensure user has i3 config directory
+ local user_home="/home/${TARGET_USER}"
+ local i3_config_dir="$user_home/.config/i3"
+
+ if [[ ! -d $i3_config_dir ]]; then
+ sudo -u "$TARGET_USER" mkdir -p "$i3_config_dir"
+ echo "✓ Created i3 config directory for user: $TARGET_USER"
+ fi
}
# Function to configure additional auto-login settings
configure_additional_settings() {
- echo ""
- echo "4. Configuring Additional Settings..."
- echo "===================================="
-
- # Add user to autologin group if it exists
- if getent group autologin &> /dev/null; then
- usermod -a -G autologin "$TARGET_USER"
- echo "✓ Added $TARGET_USER to autologin group"
- else
- # Create autologin group
- groupadd -r autologin
- usermod -a -G autologin "$TARGET_USER"
- echo "✓ Created autologin group and added $TARGET_USER"
- fi
-
- # Configure pam for auto-login (if needed)
- local pam_lightdm="/etc/pam.d/lightdm-autologin"
- if [[ ! -f "$pam_lightdm" ]]; then
- cat > "$pam_lightdm" << EOF
+ echo ""
+ echo "4. Configuring Additional Settings..."
+ echo "===================================="
+
+ # Add user to autologin group if it exists
+ if getent group autologin &> /dev/null; then
+ usermod -a -G autologin "$TARGET_USER"
+ echo "✓ Added $TARGET_USER to autologin group"
+ else
+ # Create autologin group
+ groupadd -r autologin
+ usermod -a -G autologin "$TARGET_USER"
+ echo "✓ Created autologin group and added $TARGET_USER"
+ fi
+
+ # Configure pam for auto-login (if needed)
+ local pam_lightdm="/etc/pam.d/lightdm-autologin"
+ if [[ ! -f $pam_lightdm ]]; then
+ cat > "$pam_lightdm" << EOF
#%PAM-1.0
# LightDM auto-login PAM configuration
# Created by setup_passwordless_system.sh on $(date)
@@ -247,98 +249,98 @@ password include system-local-login
session include system-local-login
session optional pam_gnome_keyring.so auto_start
EOF
- echo "✓ Created PAM configuration for auto-login"
- fi
+ echo "✓ Created PAM configuration for auto-login"
+ fi
}
# Function to test configurations
test_configurations() {
- echo ""
- echo "5. Testing Configurations..."
- echo "==========================="
-
- # Test sudo configuration
- echo "Testing passwordless sudo..."
- if sudo -u "$TARGET_USER" sudo -n true 2>/dev/null; then
- echo "✓ Passwordless sudo test passed"
- else
- echo "! Passwordless sudo test failed (may require logout/login)"
- fi
-
- # Test lightdm configuration
- echo "Testing LightDM configuration..."
- if lightdm --test-mode --debug 2>/dev/null | grep -q "seat"; then
- echo "✓ LightDM configuration test passed"
- else
- echo "! LightDM configuration test completed (check logs if issues occur)"
- fi
-
- # Verify user is in autologin group
- if groups "$TARGET_USER" | grep -q autologin; then
- echo "✓ User is in autologin group"
- else
- echo "! User may not be in autologin group"
- fi
+ echo ""
+ echo "5. Testing Configurations..."
+ echo "==========================="
+
+ # Test sudo configuration
+ echo "Testing passwordless sudo..."
+ if sudo -u "$TARGET_USER" sudo -n true 2> /dev/null; then
+ echo "✓ Passwordless sudo test passed"
+ else
+ echo "! Passwordless sudo test failed (may require logout/login)"
+ fi
+
+ # Test lightdm configuration
+ echo "Testing LightDM configuration..."
+ if lightdm --test-mode --debug 2> /dev/null | grep -q "seat"; then
+ echo "✓ LightDM configuration test passed"
+ else
+ echo "! LightDM configuration test completed (check logs if issues occur)"
+ fi
+
+ # Verify user is in autologin group
+ if groups "$TARGET_USER" | grep -q autologin; then
+ echo "✓ User is in autologin group"
+ else
+ echo "! User may not be in autologin group"
+ fi
}
# Function to show security warnings
show_security_warnings() {
- echo ""
- echo "⚠️ SECURITY WARNINGS ⚠️"
- echo "========================"
- echo ""
- echo "The following security changes have been made:"
- echo ""
- echo "1. PASSWORDLESS SUDO:"
- echo " • User '$TARGET_USER' can now run ANY command as root without password"
- echo " • This includes system-critical operations and file modifications"
- echo " • Malicious software running as this user can gain full system access"
- echo ""
- echo "2. AUTO-LOGIN:"
- echo " • System automatically logs in user '$TARGET_USER' on boot"
- echo " • No password required to access the desktop environment"
- echo " • Physical access to the machine = full user access"
- echo ""
- echo "3. RECOMMENDATIONS:"
- echo " • Use full disk encryption to protect against physical access"
- echo " • Ensure the system is in a physically secure location"
- echo " • Consider using this only on personal/development machines"
- echo " • Regularly monitor system logs for unauthorized access"
- echo " • Keep the system updated and use a firewall"
- echo ""
- echo "4. TO DISABLE THESE SETTINGS:"
- echo " • Remove passwordless sudo: sudo rm /etc/sudoers.d/99-passwordless-${TARGET_USER}"
- echo " • Disable auto-login: sudo rm /etc/lightdm/lightdm.conf.d/50-autologin.conf"
- echo " • Restart LightDM: sudo systemctl restart lightdm"
- echo ""
+ echo ""
+ echo "⚠️ SECURITY WARNINGS ⚠️"
+ echo "========================"
+ echo ""
+ echo "The following security changes have been made:"
+ echo ""
+ echo "1. PASSWORDLESS SUDO:"
+ echo " • User '$TARGET_USER' can now run ANY command as root without password"
+ echo " • This includes system-critical operations and file modifications"
+ echo " • Malicious software running as this user can gain full system access"
+ echo ""
+ echo "2. AUTO-LOGIN:"
+ echo " • System automatically logs in user '$TARGET_USER' on boot"
+ echo " • No password required to access the desktop environment"
+ echo " • Physical access to the machine = full user access"
+ echo ""
+ echo "3. RECOMMENDATIONS:"
+ echo " • Use full disk encryption to protect against physical access"
+ echo " • Ensure the system is in a physically secure location"
+ echo " • Consider using this only on personal/development machines"
+ echo " • Regularly monitor system logs for unauthorized access"
+ echo " • Keep the system updated and use a firewall"
+ echo ""
+ echo "4. TO DISABLE THESE SETTINGS:"
+ echo " • Remove passwordless sudo: sudo rm /etc/sudoers.d/99-passwordless-${TARGET_USER}"
+ echo " • Disable auto-login: sudo rm /etc/lightdm/lightdm.conf.d/50-autologin.conf"
+ echo " • Restart LightDM: sudo systemctl restart lightdm"
+ echo ""
}
# Function to show final instructions
show_final_instructions() {
- echo ""
- echo "=========================================="
- echo "Passwordless System Setup Complete"
- echo "=========================================="
- echo "Summary:"
- echo "✓ Passwordless sudo configured for user: $TARGET_USER"
- echo "✓ LightDM auto-login configured"
- echo "✓ i3 session configured"
- echo "✓ Additional auto-login settings applied"
- echo ""
- echo "Changes will take effect after:"
- echo "• Logout/login for sudo changes"
- echo "• System reboot for auto-login"
- echo ""
- echo "To verify after reboot:"
- echo " sudo whoami # Should not ask for password"
- echo " systemctl status lightdm # Should show auto-login active"
- echo ""
- echo "Configuration files created:"
- echo " /etc/sudoers.d/99-passwordless-${TARGET_USER}"
- echo " /etc/lightdm/lightdm.conf.d/50-autologin.conf"
- echo " /etc/pam.d/lightdm-autologin"
- echo ""
- echo "IMPORTANT: Reboot recommended to activate all changes!"
+ echo ""
+ echo "=========================================="
+ echo "Passwordless System Setup Complete"
+ echo "=========================================="
+ echo "Summary:"
+ echo "✓ Passwordless sudo configured for user: $TARGET_USER"
+ echo "✓ LightDM auto-login configured"
+ echo "✓ i3 session configured"
+ echo "✓ Additional auto-login settings applied"
+ echo ""
+ echo "Changes will take effect after:"
+ echo "• Logout/login for sudo changes"
+ echo "• System reboot for auto-login"
+ echo ""
+ echo "To verify after reboot:"
+ echo " sudo whoami # Should not ask for password"
+ echo " systemctl status lightdm # Should show auto-login active"
+ echo ""
+ echo "Configuration files created:"
+ echo " /etc/sudoers.d/99-passwordless-${TARGET_USER}"
+ echo " /etc/lightdm/lightdm.conf.d/50-autologin.conf"
+ echo " /etc/pam.d/lightdm-autologin"
+ echo ""
+ echo "IMPORTANT: Reboot recommended to activate all changes!"
}
# Main execution
@@ -351,22 +353,22 @@ show_security_warnings
show_final_instructions
# Only offer reboot if --reboot flag was provided
-if [[ "$OFFER_REBOOT" == true ]]; then
- echo ""
- echo "Would you like to reboot now to activate all changes?"
- read -p "Reboot system now? (y/N): " -n 1 -r
- echo
+if [[ $OFFER_REBOOT == true ]]; then
+ echo ""
+ echo "Would you like to reboot now to activate all changes?"
+ read -p "Reboot system now? (y/N): " -n 1 -r
+ echo
- if [[ $REPLY =~ ^[Yy]$ ]]; then
- echo "Rebooting system in 5 seconds..."
- sleep 5
- reboot
- else
- echo "Remember to reboot when convenient to activate all changes."
- fi
-else
- echo ""
- echo "Setup completed successfully."
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ echo "Rebooting system in 5 seconds..."
+ sleep 5
+ reboot
+ else
echo "Remember to reboot when convenient to activate all changes."
- echo "To automatically prompt for reboot in the future, use: $0 --reboot"
+ fi
+else
+ echo ""
+ echo "Setup completed successfully."
+ echo "Remember to reboot when convenient to activate all changes."
+ echo "To automatically prompt for reboot in the future, use: $0 --reboot"
fi
diff --git a/scripts/utils/sort_downloads.sh b/scripts/utils/sort_downloads.sh
index 71ae20e..5f89d73 100755
--- a/scripts/utils/sort_downloads.sh
+++ b/scripts/utils/sort_downloads.sh
@@ -3,23 +3,33 @@
# Function to sort files in a given directory
sort_files() {
local dir=$1
- cd "$dir"
+ cd "$dir" || return 1
+
+ local old_nullglob
+ old_nullglob=$(shopt -p nullglob || true)
+ shopt -s nullglob
# Create directories if they do not exist
mkdir -p images videos documents
# Move video files to the videos folder
- mv *.webm *.mp4 *.mkv *.avi *.mov *.flv videos/ 2>/dev/null
+ mv -- ./*.webm ./*.mp4 ./*.mkv ./*.avi ./*.mov ./*.flv videos/ 2> /dev/null
# Move image files to the images folder
- mv *.png *.jpg *.jpeg *.gif *.webp *.bmp images/ 2>/dev/null
+ mv -- ./*.png ./*.jpg ./*.jpeg ./*.gif ./*.webp ./*.bmp images/ 2> /dev/null
# Move document files to the documents folder
- mv *.pdf *.doc *.docx *.txt *.odt documents/ 2>/dev/null
+ mv -- ./*.pdf ./*.doc ./*.docx ./*.txt ./*.odt documents/ 2> /dev/null
+
+ if [[ -n $old_nullglob ]]; then
+ eval "$old_nullglob"
+ else
+ shopt -u nullglob
+ fi
}
# Sort files in the Downloads folder
sort_files ~/Downloads
# Sort files in the home folder
-sort_files ~
\ No newline at end of file
+sort_files ~
diff --git a/scripts/utils/steam_compatibility.sh b/scripts/utils/steam_compatibility.sh
index d904f07..e847815 100755
--- a/scripts/utils/steam_compatibility.sh
+++ b/scripts/utils/steam_compatibility.sh
@@ -20,16 +20,16 @@ set -euo pipefail
SCRIPT_NAME=${0##*/}
ABORT=0
on_abort() {
- ABORT=1
- log "Aborted by user"
- exit 130
+ ABORT=1
+ log "Aborted by user"
+ exit 130
}
trap on_abort INT TERM
# --------------------------- CLI args ---------------------------
usage() {
- cat <&2; }
-die() { printf "[%s] ERROR: %s\n" "$SCRIPT_NAME" "$*" >&2; exit 1; }
-vlog() { if [[ $VERBOSE -eq 1 ]]; then printf "[%s][verbose] %s\n" "$SCRIPT_NAME" "$*" >&2; fi }
+die() {
+ printf "[%s] ERROR: %s\n" "$SCRIPT_NAME" "$*" >&2
+ exit 1
+}
+vlog() { if [[ $VERBOSE -eq 1 ]]; then printf "[%s][verbose] %s\n" "$SCRIPT_NAME" "$*" >&2; fi; }
require_cmd() {
- command -v "$1" >/dev/null 2>&1 || die "Missing dependency: $1"
+ command -v "$1" > /dev/null 2>&1 || die "Missing dependency: $1"
}
for cmd in curl jq awk sed grep sort lspci free uname; do
- require_cmd "$cmd"
+ require_cmd "$cmd"
done
HAS_TIMEOUT=0
-if command -v timeout >/dev/null 2>&1; then
- HAS_TIMEOUT=1
+if command -v timeout > /dev/null 2>&1; then
+ HAS_TIMEOUT=1
fi
# Safe HTTP GET with optional timeout if available
http_get() {
- local url="$1"
- shift || true
- local ua="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124 Safari/537.36"
- if [[ $HAS_TIMEOUT -eq 1 ]]; then
- timeout 12s curl -sSL --compressed --retry 2 --retry-delay 0.5 --retry-connrefused \
- -H "User-Agent: $ua" -H 'Accept: application/json, text/plain, */*' "$url" "$@" 2>/dev/null
- else
- curl -sSL --compressed --retry 2 --retry-delay 0.5 --retry-connrefused \
- -H "User-Agent: $ua" -H 'Accept: application/json, text/plain, */*' "$url" "$@" 2>/dev/null
- fi
+ local url="$1"
+ shift || true
+ local ua="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124 Safari/537.36"
+ if [[ $HAS_TIMEOUT -eq 1 ]]; then
+ timeout 12s curl -sSL --compressed --retry 2 --retry-delay 0.5 --retry-connrefused \
+ -H "User-Agent: $ua" -H 'Accept: application/json, text/plain, */*' "$url" "$@" 2> /dev/null
+ else
+ curl -sSL --compressed --retry 2 --retry-delay 0.5 --retry-connrefused \
+ -H "User-Agent: $ua" -H 'Accept: application/json, text/plain, */*' "$url" "$@" 2> /dev/null
+ fi
}
# --------------------------- System detection ---------------------------
@@ -100,94 +112,127 @@ SYSTEM_CPU_CLASS="unknown"
SYSTEM_GPU_VENDOR="unknown"
SYSTEM_RAM_GB=0
SYSTEM_ARCH="$(uname -m || echo unknown)"
-SYSTEM_OS="linux"
-to_int() { awk '{gsub(/[^0-9]/,""); if($0=="") print 0; else print $0}' <<<"$1"; }
+to_int() { awk '{gsub(/[^0-9]/,""); if($0=="") print 0; else print $0}' <<< "$1"; }
detect_system() {
- # CPU model
- if command -v lscpu >/dev/null 2>&1; then
- SYSTEM_CPU_MODEL=$(lscpu | awk -F': *' '/Model name/ {print $2; exit}')
- fi
- if [[ -z "$SYSTEM_CPU_MODEL" && -r /proc/cpuinfo ]]; then
- SYSTEM_CPU_MODEL=$(awk -F': *' '/model name/ {print $2; exit}' /proc/cpuinfo)
- fi
- SYSTEM_CPU_MODEL=${SYSTEM_CPU_MODEL:-unknown}
+ # CPU model
+ if command -v lscpu > /dev/null 2>&1; then
+ SYSTEM_CPU_MODEL=$(lscpu | awk -F': *' '/Model name/ {print $2; exit}')
+ fi
+ if [[ -z $SYSTEM_CPU_MODEL && -r /proc/cpuinfo ]]; then
+ SYSTEM_CPU_MODEL=$(awk -F': *' '/model name/ {print $2; exit}' /proc/cpuinfo)
+ fi
+ SYSTEM_CPU_MODEL=${SYSTEM_CPU_MODEL:-unknown}
- # CPU class (very rough)
- lc_model=$(tr '[:upper:]' '[:lower:]' <<<"$SYSTEM_CPU_MODEL")
- if grep -qiE 'i9-|core\(tm\) i9| ryzen 9' <<<"$lc_model"; then SYSTEM_CPU_CLASS="tier4";
- elif grep -qiE 'i7-|core\(tm\) i7| ryzen 7' <<<"$lc_model"; then SYSTEM_CPU_CLASS="tier3";
- elif grep -qiE 'i5-|core\(tm\) i5| ryzen 5' <<<"$lc_model"; then SYSTEM_CPU_CLASS="tier2";
- elif grep -qiE 'i3-|core\(tm\) i3| ryzen 3| pentium|celeron|atom' <<<"$lc_model"; then SYSTEM_CPU_CLASS="tier1";
- else SYSTEM_CPU_CLASS="tier2"; fi
+ # CPU class (very rough)
+ lc_model=$(tr '[:upper:]' '[:lower:]' <<< "$SYSTEM_CPU_MODEL")
+ if grep -qiE 'i9-|core\(tm\) i9| ryzen 9' <<< "$lc_model"; then
+ SYSTEM_CPU_CLASS="tier4"
+ elif grep -qiE 'i7-|core\(tm\) i7| ryzen 7' <<< "$lc_model"; then
+ SYSTEM_CPU_CLASS="tier3"
+ elif grep -qiE 'i5-|core\(tm\) i5| ryzen 5' <<< "$lc_model"; then
+ SYSTEM_CPU_CLASS="tier2"
+ elif grep -qiE 'i3-|core\(tm\) i3| ryzen 3| pentium|celeron|atom' <<< "$lc_model"; then
+ SYSTEM_CPU_CLASS="tier1"
+ else SYSTEM_CPU_CLASS="tier2"; fi
- # GPU vendor
- local vga
- vga=$(lspci 2>/dev/null | grep -iE 'vga|3d|display' | head -n1 || true)
- lc_vga=$(tr '[:upper:]' '[:lower:]' <<<"$vga")
- if grep -q 'nvidia' <<<"$lc_vga"; then SYSTEM_GPU_VENDOR="nvidia";
- elif grep -q -E 'amd|ati|radeon' <<<"$lc_vga"; then SYSTEM_GPU_VENDOR="amd";
- elif grep -q 'intel' <<<"$lc_vga"; then SYSTEM_GPU_VENDOR="intel";
- else SYSTEM_GPU_VENDOR="unknown"; fi
+ # GPU vendor
+ local vga
+ vga=$(lspci 2> /dev/null | grep -iE 'vga|3d|display' | head -n1 || true)
+ lc_vga=$(tr '[:upper:]' '[:lower:]' <<< "$vga")
+ if grep -q 'nvidia' <<< "$lc_vga"; then
+ SYSTEM_GPU_VENDOR="nvidia"
+ elif grep -q -E 'amd|ati|radeon' <<< "$lc_vga"; then
+ SYSTEM_GPU_VENDOR="amd"
+ elif grep -q 'intel' <<< "$lc_vga"; then
+ SYSTEM_GPU_VENDOR="intel"
+ else SYSTEM_GPU_VENDOR="unknown"; fi
- # RAM GB
- local mem_kb
- mem_kb=$(awk '/MemTotal/ {print $2; exit}' /proc/meminfo 2>/dev/null || echo 0)
- if [[ "$mem_kb" -gt 0 ]]; then
- SYSTEM_RAM_GB=$(( (mem_kb + 1023*1024) / (1024*1024) ))
- else
- local mem_mb
- mem_mb=$(free -m | awk '/Mem:/ {print $2; exit}')
- SYSTEM_RAM_GB=$(( (mem_mb + 1023) / 1024 ))
- fi
+ # RAM GB
+ local mem_kb
+ mem_kb=$(awk '/MemTotal/ {print $2; exit}' /proc/meminfo 2> /dev/null || echo 0)
+ if [[ $mem_kb -gt 0 ]]; then
+ SYSTEM_RAM_GB=$(((mem_kb + 1023 * 1024) / (1024 * 1024)))
+ else
+ local mem_mb
+ mem_mb=$(free -m | awk '/Mem:/ {print $2; exit}')
+ SYSTEM_RAM_GB=$(((mem_mb + 1023) / 1024))
+ fi
}
cpu_class_rank() {
- case "$1" in
- tier1) echo 1 ;;
- tier2) echo 2 ;;
- tier3) echo 3 ;;
- tier4) echo 4 ;;
- *) echo 2 ;;
- esac
+ case "$1" in
+ tier1) echo 1 ;;
+ tier2) echo 2 ;;
+ tier3) echo 3 ;;
+ tier4) echo 4 ;;
+ *) echo 2 ;;
+ esac
}
required_cpu_rank_from_text() {
- local t=$(tr '[:upper:]' '[:lower:]' <<<"$1")
- if grep -qE 'i9|ryzen 9' <<<"$t"; then echo 4; return; fi
- if grep -qE 'i7|ryzen 7' <<<"$t"; then echo 3; return; fi
- if grep -qE 'i5|ryzen 5' <<<"$t"; then echo 2; return; fi
- if grep -qE 'i3|ryzen 3|pentium|celeron|atom' <<<"$t"; then echo 1; return; fi
- echo 2
+ local t
+ t=$(tr '[:upper:]' '[:lower:]' <<< "$1")
+ if grep -qE 'i9|ryzen 9' <<< "$t"; then
+ echo 4
+ return
+ fi
+ if grep -qE 'i7|ryzen 7' <<< "$t"; then
+ echo 3
+ return
+ fi
+ if grep -qE 'i5|ryzen 5' <<< "$t"; then
+ echo 2
+ return
+ fi
+ if grep -qE 'i3|ryzen 3|pentium|celeron|atom' <<< "$t"; then
+ echo 1
+ return
+ fi
+ echo 2
}
gpu_vendor_required_from_text() {
- local t=$(tr '[:upper:]' '[:lower:]' <<<"$1")
- if grep -qE 'nvidia|geforce|gtx|rtx' <<<"$t"; then echo nvidia; return; fi
- if grep -qE 'amd|radeon|rx[ -]?[0-9]' <<<"$t"; then echo amd; return; fi
- if grep -qE 'intel( graphics| arc| iris| hd)' <<<"$t"; then echo intel; return; fi
- echo unknown
+ local t
+ t=$(tr '[:upper:]' '[:lower:]' <<< "$1")
+ if grep -qE 'nvidia|geforce|gtx|rtx' <<< "$t"; then
+ echo nvidia
+ return
+ fi
+ if grep -qE 'amd|radeon|rx[ -]?[0-9]' <<< "$t"; then
+ echo amd
+ return
+ fi
+ if grep -qE 'intel( graphics| arc| iris| hd)' <<< "$t"; then
+ echo intel
+ return
+ fi
+ echo unknown
}
strip_html() {
- sed -E 's/<[^>]+>//g; s/ / /g; s/&/\&/g; s/\r//g' <<<"$1"
+ sed -E 's/<[^>]+>//g; s/ / /g; s/&/\&/g; s/\r//g' <<< "$1"
}
parse_ram_gb() {
- # Extract first RAM mention and convert to GB integer
- local text="$1"
- local num unit val
- # Prefer GB
- num=$(grep -oiE '([0-9]+)\s*(gb|gib)' <<<"$text" | head -n1 | grep -oiE '^[0-9]+' || true)
- if [[ -n "$num" ]]; then echo "$num"; return; fi
- # Try MB
- num=$(grep -oiE '([0-9]+)\s*(mb|mib)' <<<"$text" | head -n1 | grep -oiE '^[0-9]+' || true)
- if [[ -n "$num" ]]; then
- val=$(( (num + 1023) / 1024 ))
- echo "$val"; return
- fi
- echo 0
+ # Extract first RAM mention and convert to GB integer
+ local text="$1"
+ local num val
+ # Prefer GB
+ num=$(grep -oiE '([0-9]+)\s*(gb|gib)' <<< "$text" | head -n1 | grep -oiE '^[0-9]+' || true)
+ if [[ -n $num ]]; then
+ echo "$num"
+ return
+ fi
+ # Try MB
+ num=$(grep -oiE '([0-9]+)\s*(mb|mib)' <<< "$text" | head -n1 | grep -oiE '^[0-9]+' || true)
+ if [[ -n $num ]]; then
+ val=$(((num + 1023) / 1024))
+ echo "$val"
+ return
+ fi
+ echo 0
}
# --------------------------- Steam data ---------------------------
@@ -200,26 +245,26 @@ CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/steam-compat-check"
RESULTS_CACHE="$CACHE_DIR/results.tsv"
load_credentials() {
- # Prefer environment, else config file
- if [[ -n "${STEAM_API_KEY:-}" && -n "${STEAM_ID64:-}" ]]; then
- return 0
- fi
- if [[ -r "$CONFIG_FILE" ]]; then
- # shellcheck disable=SC1090
- . "$CONFIG_FILE" || true
- fi
- if [[ -z "${STEAM_API_KEY:-}" || -z "${STEAM_ID64:-}" ]]; then
- return 1
- fi
- return 0
+ # Prefer environment, else config file
+ if [[ -n ${STEAM_API_KEY:-} && -n ${STEAM_ID64:-} ]]; then
+ return 0
+ fi
+ if [[ -r $CONFIG_FILE ]]; then
+ # shellcheck disable=SC1090
+ . "$CONFIG_FILE" || true
+ fi
+ if [[ -z ${STEAM_API_KEY:-} || -z ${STEAM_ID64:-} ]]; then
+ return 1
+ fi
+ return 0
}
save_credentials() {
- local key="$1" id="$2"
- mkdir -p "$CONFIG_DIR"
- chmod 700 "$CONFIG_DIR" 2>/dev/null || true
- umask 177
- cat > "$CONFIG_FILE" < /dev/null || true
+ umask 177
+ cat > "$CONFIG_FILE" << EOF
# Saved by $SCRIPT_NAME
STEAM_API_KEY="$key"
STEAM_ID64="$id"
@@ -227,95 +272,95 @@ EOF
}
prompt_for_credentials() {
- if [[ ! -t 0 ]]; then
- die "STEAM_API_KEY/STEAM_ID64 not set and input is non-interactive. Export them or create $CONFIG_FILE."
- fi
- echo "Steam Web API credentials are required to scan your full library."
- echo
- echo "Where to get them:"
- echo "- Steam Web API Key: https://steamcommunity.com/dev/apikey"
- echo " Log in, set any domain (e.g., 127.0.0.1), then copy the key."
- echo "- SteamID64 (17-digit ID starting with 765):"
- echo " * Easiest: https://steamid.io/ (paste your profile URL to get the 64-bit ID)"
- echo " * Or enable URL bar in Steam (Settings > Interface), open your profile; the URL contains the ID."
- echo
- local key id
- read -r -p "Enter Steam Web API Key: " key
- read -r -p "Enter Steam 64-bit ID (begins with 765…): " id
- if [[ -z "$key" || -z "$id" ]]; then
- die "Credentials not provided. Exiting."
- fi
- # Light validation for ID64
- if ! grep -qE '^765[0-9]{14}$' <<<"$id"; then
- log "Warning: Steam ID64 format unexpected; continuing anyway."
- fi
- STEAM_API_KEY="$key"
- STEAM_ID64="$id"
- export STEAM_API_KEY STEAM_ID64
- save_credentials "$STEAM_API_KEY" "$STEAM_ID64"
- log "Saved credentials to $CONFIG_FILE"
+ if [[ ! -t 0 ]]; then
+ die "STEAM_API_KEY/STEAM_ID64 not set and input is non-interactive. Export them or create $CONFIG_FILE."
+ fi
+ echo "Steam Web API credentials are required to scan your full library."
+ echo
+ echo "Where to get them:"
+ echo "- Steam Web API Key: https://steamcommunity.com/dev/apikey"
+ echo " Log in, set any domain (e.g., 127.0.0.1), then copy the key."
+ echo "- SteamID64 (17-digit ID starting with 765):"
+ echo " * Easiest: https://steamid.io/ (paste your profile URL to get the 64-bit ID)"
+ echo " * Or enable URL bar in Steam (Settings > Interface), open your profile; the URL contains the ID."
+ echo
+ local key id
+ read -r -p "Enter Steam Web API Key: " key
+ read -r -p "Enter Steam 64-bit ID (begins with 765…): " id
+ if [[ -z $key || -z $id ]]; then
+ die "Credentials not provided. Exiting."
+ fi
+ # Light validation for ID64
+ if ! grep -qE '^765[0-9]{14}$' <<< "$id"; then
+ log "Warning: Steam ID64 format unexpected; continuing anyway."
+ fi
+ STEAM_API_KEY="$key"
+ STEAM_ID64="$id"
+ export STEAM_API_KEY STEAM_ID64
+ save_credentials "$STEAM_API_KEY" "$STEAM_ID64"
+ log "Saved credentials to $CONFIG_FILE"
}
-STEAM_DIRS=( "$HOME/.steam/steam" "$HOME/.local/share/Steam" )
+STEAM_DIRS=("$HOME/.steam/steam" "$HOME/.local/share/Steam")
find_steamapps_dirs() {
- local dirs=()
- for base in "${STEAM_DIRS[@]}"; do
- [[ -d "$base" ]] || continue
- if [[ -f "$base/steamapps/libraryfolders.vdf" ]]; then
- # Newer format includes nested objects with paths
- local paths
- paths=$(grep -oE '"path"\s+"[^"]+"' "$base/steamapps/libraryfolders.vdf" | sed -E 's/.*"([^"]+)"/\1/' || true)
- if [[ -n "$paths" ]]; then
- while IFS= read -r p; do
- [[ -d "$p/steamapps" ]] && dirs+=("$p/steamapps")
- done <<<"$paths"
- fi
- fi
- [[ -d "$base/steamapps" ]] && dirs+=("$base/steamapps")
- done
- # de-dupe
- printf "%s\n" "${dirs[@]}" 2>/dev/null | awk '!seen[$0]++'
+ local dirs=()
+ for base in "${STEAM_DIRS[@]}"; do
+ [[ -d $base ]] || continue
+ if [[ -f "$base/steamapps/libraryfolders.vdf" ]]; then
+ # Newer format includes nested objects with paths
+ local paths
+ paths=$(grep -oE '"path"\s+"[^"]+"' "$base/steamapps/libraryfolders.vdf" | sed -E 's/.*"([^"]+)"/\1/' || true)
+ if [[ -n $paths ]]; then
+ while IFS= read -r p; do
+ [[ -d "$p/steamapps" ]] && dirs+=("$p/steamapps")
+ done <<< "$paths"
+ fi
+ fi
+ [[ -d "$base/steamapps" ]] && dirs+=("$base/steamapps")
+ done
+ # de-dupe
+ printf "%s\n" "${dirs[@]}" 2> /dev/null | awk '!seen[$0]++'
}
list_installed_games() {
- local d appid name
- while IFS= read -r d; do
- [[ -d "$d" ]] || continue
- for mf in "$d"/appmanifest_*.acf; do
- [[ -f "$mf" ]] || continue
- appid=$(grep -oE '"appid"\s+"[0-9]+"' "$mf" | sed -E 's/.*"([0-9]+)"/\1/' | head -n1)
- name=$(grep -oE '"name"\s+"[^"]+"' "$mf" | sed -E 's/.*"([^"]+)"/\1/' | head -n1)
- if [[ -n "$appid" ]]; then
- printf "%s\t%s\n" "$appid" "${name:-Unknown}"
- fi
- done
- done < <(find_steamapps_dirs)
+ local d appid name
+ while IFS= read -r d; do
+ [[ -d $d ]] || continue
+ for mf in "$d"/appmanifest_*.acf; do
+ [[ -f $mf ]] || continue
+ appid=$(grep -oE '"appid"\s+"[0-9]+"' "$mf" | sed -E 's/.*"([0-9]+)"/\1/' | head -n1)
+ name=$(grep -oE '"name"\s+"[^"]+"' "$mf" | sed -E 's/.*"([^"]+)"/\1/' | head -n1)
+ if [[ -n $appid ]]; then
+ printf "%s\t%s\n" "$appid" "${name:-Unknown}"
+ fi
+ done
+ done < <(find_steamapps_dirs)
}
list_owned_games_via_api() {
- local key="${STEAM_API_KEY:-}" sid="${STEAM_ID64:-}"
- if [[ -z "$key" || -z "$sid" ]]; then
- return 1
- fi
- local url="https://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=${key}&steamid=${sid}&include_appinfo=1&include_played_free_games=1&format=json"
- http_get "$url" | jq -r '.response.games[]? | "\(.appid)\t\(.name)"' || return 1
+ local key="${STEAM_API_KEY:-}" sid="${STEAM_ID64:-}"
+ if [[ -z $key || -z $sid ]]; then
+ return 1
+ fi
+ local url="https://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=${key}&steamid=${sid}&include_appinfo=1&include_played_free_games=1&format=json"
+ http_get "$url" | jq -r '.response.games[]? | "\(.appid)\t\(.name)"' || return 1
}
fetch_appdetails_json() {
- local appid="$1"
- # Using store API (no key)
- local url="https://store.steampowered.com/api/appdetails?appids=${appid}&l=en&cc=us"
- http_get "$url" || true
+ local appid="$1"
+ # Using store API (no key)
+ local url="https://store.steampowered.com/api/appdetails?appids=${appid}&l=en&cc=us"
+ http_get "$url" || true
}
extract_requirements_and_platforms() {
- # Input: JSON from appdetails; Output: TSV of fields
- # Fields: success, linux, windows, mac, min_text, rec_text, type
- local appid="$1" json="$2"
- # Some apps return {"APPID": {"success":true, "data":{...}}}
- local out
- out=$(jq -r --arg APP "$appid" '
+ # Input: JSON from appdetails; Output: TSV of fields
+ # Fields: success, linux, windows, mac, min_text, rec_text, type
+ local appid="$1" json="$2"
+ # Some apps return {"APPID": {"success":true, "data":{...}}}
+ local out
+ out=$(jq -r --arg APP "$appid" '
.[$APP] as $root | if ($root.success==true and ($root.data|type)=="object") then
($root.data.platforms.linux // false) as $linux |
($root.data.platforms.windows // false) as $windows |
@@ -327,18 +372,18 @@ extract_requirements_and_platforms() {
["ok", ($linux|tostring), ($windows|tostring), ($mac|tostring), ($min|tostring), ($rec|tostring), ($type|tostring)] | @tsv
else
["fail", "false", "false", "false", "", "", ""] | @tsv
- end' 2>/dev/null <<<"$json") || true
- if [[ -z "$out" ]]; then
- out=$'fail false false false '
- fi
- printf '%s\n' "$out"
+ end' 2> /dev/null <<< "$json") || true
+ if [[ -z $out ]]; then
+ out=$'fail false false false '
+ fi
+ printf '%s\n' "$out"
}
# Read JSON from stdin (avoids storing large/binary data in variables)
extract_requirements_and_platforms_stdin() {
- local appid="$1"
- local out
- out=$(jq -r --arg APP "$appid" '
+ local appid="$1"
+ local out
+ out=$(jq -r --arg APP "$appid" '
.[$APP] as $root | if ($root.success==true and ($root.data|type)=="object") then
($root.data.platforms.linux // false) as $linux |
($root.data.platforms.windows // false) as $windows |
@@ -349,270 +394,270 @@ extract_requirements_and_platforms_stdin() {
["ok", ($linux|tostring), ($windows|tostring), ($mac|tostring), ($min|tostring), ($rec|tostring), ($type|tostring)] | @tsv
else
["fail", "false", "false", "false", "", "", ""] | @tsv
- end' 2>/dev/null) || true
- if [[ -z "$out" ]]; then
- out=$'fail\tfalse\tfalse\tfalse\t\t\t'
- fi
- printf '%s\n' "$out"
+ end' 2> /dev/null) || true
+ if [[ -z $out ]]; then
+ out=$'fail\tfalse\tfalse\tfalse\t\t\t'
+ fi
+ printf '%s\n' "$out"
}
score_game() {
- local linux_support="$1" min_txt="$2" rec_txt="$3"
- local score=0
+ local linux_support="$1" min_txt="$2" rec_txt="$3"
+ local score=0
- # Linux platform support
- if [[ "$linux_support" == "true" ]]; then
- score=$((score + 50))
- else
- score=$((score + 20)) # Assume Proton potential
- fi
+ # Linux platform support
+ if [[ $linux_support == "true" ]]; then
+ score=$((score + 50))
+ else
+ score=$((score + 20)) # Assume Proton potential
+ fi
- local min_plain rec_plain
- min_plain=$(strip_html "$min_txt")
- rec_plain=$(strip_html "$rec_txt")
+ local min_plain rec_plain
+ min_plain=$(strip_html "$min_txt")
+ rec_plain=$(strip_html "$rec_txt")
- local min_ram rec_ram
- min_ram=$(parse_ram_gb "$min_plain")
- rec_ram=$(parse_ram_gb "$rec_plain")
+ local min_ram rec_ram
+ min_ram=$(parse_ram_gb "$min_plain")
+ rec_ram=$(parse_ram_gb "$rec_plain")
- # RAM checks
- if [[ "$min_ram" -gt 0 ]]; then
- if [[ "$SYSTEM_RAM_GB" -ge "$min_ram" ]]; then score=$((score + 15)); else score=$((score - 30)); fi
- fi
- if [[ "$rec_ram" -gt 0 ]]; then
- if [[ "$SYSTEM_RAM_GB" -ge "$rec_ram" ]]; then score=$((score + 10)); else score=$((score - 10)); fi
- fi
+ # RAM checks
+ if [[ $min_ram -gt 0 ]]; then
+ if [[ $SYSTEM_RAM_GB -ge $min_ram ]]; then score=$((score + 15)); else score=$((score - 30)); fi
+ fi
+ if [[ $rec_ram -gt 0 ]]; then
+ if [[ $SYSTEM_RAM_GB -ge $rec_ram ]]; then score=$((score + 10)); else score=$((score - 10)); fi
+ fi
- # CPU checks (very rough tiers)
- local req_rank sys_rank
- req_rank=$(required_cpu_rank_from_text "$min_plain $rec_plain")
- sys_rank=$(cpu_class_rank "$SYSTEM_CPU_CLASS")
- if [[ "$sys_rank" -ge "$req_rank" ]]; then score=$((score + 10)); else score=$((score - 10)); fi
+ # CPU checks (very rough tiers)
+ local req_rank sys_rank
+ req_rank=$(required_cpu_rank_from_text "$min_plain $rec_plain")
+ sys_rank=$(cpu_class_rank "$SYSTEM_CPU_CLASS")
+ if [[ $sys_rank -ge $req_rank ]]; then score=$((score + 10)); else score=$((score - 10)); fi
- # GPU vendor hints
- local req_gpu vendor
- req_gpu=$(gpu_vendor_required_from_text "$min_plain $rec_plain")
- vendor="$SYSTEM_GPU_VENDOR"
- if [[ "$req_gpu" == "unknown" ]]; then
- score=$((score + 5))
- elif [[ "$req_gpu" == "$vendor" ]]; then
- score=$((score + 10))
- else
- score=$((score - 10))
- fi
+ # GPU vendor hints
+ local req_gpu vendor
+ req_gpu=$(gpu_vendor_required_from_text "$min_plain $rec_plain")
+ vendor="$SYSTEM_GPU_VENDOR"
+ if [[ $req_gpu == "unknown" ]]; then
+ score=$((score + 5))
+ elif [[ $req_gpu == "$vendor" ]]; then
+ score=$((score + 10))
+ else
+ score=$((score - 10))
+ fi
- # 64-bit OS requirement
- if grep -qi '64-?bit' <<<"$min_plain $rec_plain"; then
- if [[ "$SYSTEM_ARCH" == "x86_64" || "$SYSTEM_ARCH" == "aarch64" ]]; then
- score=$((score + 5))
- else
- score=$((score - 20))
- fi
- fi
+ # 64-bit OS requirement
+ if grep -qi '64-?bit' <<< "$min_plain $rec_plain"; then
+ if [[ $SYSTEM_ARCH == "x86_64" || $SYSTEM_ARCH == "aarch64" ]]; then
+ score=$((score + 5))
+ else
+ score=$((score - 20))
+ fi
+ fi
- printf "%s\t%s\t%s\n" "$score" "$min_ram" "$rec_ram"
+ printf "%s\t%s\t%s\n" "$score" "$min_ram" "$rec_ram"
}
print_header() {
- printf "%-5s %-8s %-6s %-6s %-8s %-9s %s\n" "Rank" "Score" "MinRAM" "RecRAM" "Linux" "ProtonDB" "Title"
+ printf "%-5s %-8s %-6s %-6s %-8s %-9s %s\n" "Rank" "Score" "MinRAM" "RecRAM" "Linux" "ProtonDB" "Title"
}
check_network_or_exit() {
- # Quick probe to Steam Store API; exit early if not reachable
- local probe_url="https://store.steampowered.com/api/appdetails?appids=10&l=en&cc=us"
- if ! http_get "$probe_url" | jq -e '."10".success == true' >/dev/null 2>&1; then
- log "Warning: store.steampowered.com probe failed (network or rate-limit). Continuing and handling per-app."
- fi
+ # Quick probe to Steam Store API; exit early if not reachable
+ local probe_url="https://store.steampowered.com/api/appdetails?appids=10&l=en&cc=us"
+ if ! http_get "$probe_url" | jq -e '."10".success == true' > /dev/null 2>&1; then
+ log "Warning: store.steampowered.com probe failed (network or rate-limit). Continuing and handling per-app."
+ fi
}
is_known_tool_name() {
- local name_lc
- name_lc=$(tr '[:upper:]' '[:lower:]' <<<"$1")
- if grep -qE 'steam linux runtime|proton|compatibility tool' <<<"$name_lc"; then
- return 0
- fi
- return 1
+ local name_lc
+ name_lc=$(tr '[:upper:]' '[:lower:]' <<< "$1")
+ if grep -qE 'steam linux runtime|proton|compatibility tool' <<< "$name_lc"; then
+ return 0
+ fi
+ return 1
}
ensure_cache_dir() {
- mkdir -p "$CACHE_DIR" 2>/dev/null || true
+ mkdir -p "$CACHE_DIR" 2> /dev/null || true
}
declare -A CACHE_MAP
load_cache_map() {
- CACHE_MAP=()
- if [[ -r "$RESULTS_CACHE" ]]; then
- while IFS= read -r raw_line; do
- # Normalize historical caches that contain literal "\t" instead of real tabs
- local norm_line
- norm_line=$(printf "%s" "$raw_line" | sed -E $'s/\\t/\t/g; s/\r$//')
- IFS=$'\t' read -r c_score c_appid c_linux c_min c_rec c_name c_pdb <<<"$norm_line"
- [[ -z "${c_appid:-}" ]] && continue
- c_pdb=${c_pdb:-unknown}
- CACHE_MAP["$c_appid"]="$c_score\t$c_appid\t$c_linux\t$c_min\t$c_rec\t$c_name\t$c_pdb"
- done < "$RESULTS_CACHE"
- fi
+ CACHE_MAP=()
+ if [[ -r $RESULTS_CACHE ]]; then
+ while IFS= read -r raw_line; do
+ # Normalize historical caches that contain literal "\t" instead of real tabs
+ local norm_line
+ norm_line=$(printf "%s" "$raw_line" | sed -E $'s/\\t/\t/g; s/\r$//')
+ IFS=$'\t' read -r c_score c_appid c_linux c_min c_rec c_name c_pdb <<< "$norm_line"
+ [[ -z ${c_appid:-} ]] && continue
+ c_pdb=${c_pdb:-unknown}
+ CACHE_MAP["$c_appid"]="$c_score\t$c_appid\t$c_linux\t$c_min\t$c_rec\t$c_name\t$c_pdb"
+ done < "$RESULTS_CACHE"
+ fi
}
# --------------------------- ProtonDB integration ---------------------------
fetch_protondb_tier() {
- local appid="$1"
- local url="https://www.protondb.com/api/v1/reports/summaries/${appid}.json"
- # Returns minimal JSON including .tier, .confidence; we only need .tier
- local tier
- tier=$(http_get "$url" | jq -r 'try .tier // "unknown"' 2>/dev/null || true)
- if [[ -z "$tier" || "$tier" == "null" ]]; then
- echo "unknown"
- else
- tr '[:upper:]' '[:lower:]' <<<"$tier" | tr -d '\r\n'
- fi
+ local appid="$1"
+ local url="https://www.protondb.com/api/v1/reports/summaries/${appid}.json"
+ # Returns minimal JSON including .tier, .confidence; we only need .tier
+ local tier
+ tier=$(http_get "$url" | jq -r 'try .tier // "unknown"' 2> /dev/null || true)
+ if [[ -z $tier || $tier == "null" ]]; then
+ echo "unknown"
+ else
+ tr '[:upper:]' '[:lower:]' <<< "$tier" | tr -d '\r\n'
+ fi
}
protondb_allowed() {
- local tier="$(tr '[:upper:]' '[:lower:]' <<<"${1:-}")"
- case "$tier" in
- platinum|native|gold|silver|unknown|"") return 0 ;;
- bronze|pending|borked|unsupported|broken) return 1 ;;
- *) return 0 ;;
- esac
+ local tier
+ tier=$(tr '[:upper:]' '[:lower:]' <<< "${1:-}")
+ case "$tier" in
+ platinum | native | gold | silver | unknown | "") return 0 ;;
+ bronze | pending | borked | unsupported | broken) return 1 ;;
+ *) return 0 ;;
+ esac
}
main() {
- parse_args "$@"
- detect_system
- log "System: CPU=[$SYSTEM_CPU_MODEL] class=$SYSTEM_CPU_CLASS | GPU=$SYSTEM_GPU_VENDOR | RAM=${SYSTEM_RAM_GB}GB | Arch=$SYSTEM_ARCH"
+ parse_args "$@"
+ detect_system
+ log "System: CPU=[$SYSTEM_CPU_MODEL] class=$SYSTEM_CPU_CLASS | GPU=$SYSTEM_GPU_VENDOR | RAM=${SYSTEM_RAM_GB}GB | Arch=$SYSTEM_ARCH"
- local tmpdir
- tmpdir=$(mktemp -d)
- trap '[[ -n "${tmpdir:-}" ]] && rm -rf "$tmpdir"' EXIT
+ local tmpdir
+ tmpdir=$(mktemp -d)
+ trap '[[ -n "${tmpdir:-}" ]] && rm -rf "$tmpdir"' EXIT
- local games_tsv="$tmpdir/games.tsv"
- : > "$games_tsv"
+ local games_tsv="$tmpdir/games.tsv"
+ : > "$games_tsv"
- # Ensure credentials exist: load from env/config or prompt, else exit
- if ! load_credentials; then
- prompt_for_credentials
- fi
+ # Ensure credentials exist: load from env/config or prompt, else exit
+ if ! load_credentials; then
+ prompt_for_credentials
+ fi
- # Fail fast if we cannot reach the store API to avoid noisy per-app errors
- check_network_or_exit
+ # Fail fast if we cannot reach the store API to avoid noisy per-app errors
+ check_network_or_exit
- if list_owned_games_via_api > "$games_tsv" 2>/dev/null; then
- log "Fetched owned games via Steam Web API"
- fi
+ if list_owned_games_via_api > "$games_tsv" 2> /dev/null; then
+ log "Fetched owned games via Steam Web API"
+ fi
- if [[ ! -s "$games_tsv" ]]; then
- die "No games found from Steam Web API. Check STEAM_API_KEY/STEAM_ID64 and network connectivity."
- fi
+ if [[ ! -s $games_tsv ]]; then
+ die "No games found from Steam Web API. Check STEAM_API_KEY/STEAM_ID64 and network connectivity."
+ fi
- # Fail fast if we cannot reach the store API to avoid noisy per-app errors
- check_network_or_exit
+ # Fail fast if we cannot reach the store API to avoid noisy per-app errors
+ check_network_or_exit
- ensure_cache_dir
- if [[ $CLEAR_CACHE -eq 1 ]] && [[ -f "$RESULTS_CACHE" ]]; then
- rm -f "$RESULTS_CACHE" || true
- log "Cleared cache: $RESULTS_CACHE"
- fi
- if [[ $FORCE_REFRESH -eq 0 ]]; then
- load_cache_map
- else
- CACHE_MAP=()
- fi
+ ensure_cache_dir
+ if [[ $CLEAR_CACHE -eq 1 ]] && [[ -f $RESULTS_CACHE ]]; then
+ rm -f "$RESULTS_CACHE" || true
+ log "Cleared cache: $RESULTS_CACHE"
+ fi
+ if [[ $FORCE_REFRESH -eq 0 ]]; then
+ load_cache_map
+ else
+ CACHE_MAP=()
+ fi
- local results_combined="$tmpdir/results.tsv"
- : > "$results_combined"
+ local results_combined="$tmpdir/results.tsv"
+ : > "$results_combined"
- local count=0
- local total
- total=$(wc -l < "$games_tsv" | tr -d ' ')
- [[ -z "$total" ]] && total=0
- while IFS=$'\t' read -r appid name; do
- [[ $ABORT -eq 1 ]] && break
- [[ -n "$appid" ]] || continue
- if is_known_tool_name "$name"; then
- vlog "[$((count+1))/$total] Skipping compatibility tool: $name ($appid)"
- continue
- fi
- # If cached, reuse; else analyze and cache
- if [[ $FORCE_REFRESH -eq 0 && -n "${CACHE_MAP[$appid]+isset}" ]]; then
- # Normalize to include ProtonDB column if older cache lacked it
- IFS=$'\t' read -r c_score c_a c_linux c_min c_rec c_name c_pdb <<<"${CACHE_MAP[$appid]}"
- c_pdb=${c_pdb:-unknown}
- vlog "[$((count+1))/$total] Cache hit: $name ($appid) | ProtonDB=$c_pdb"
- printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$c_score" "$c_a" "$c_linux" "$c_min" "$c_rec" "$c_name" "$c_pdb" >> "$results_combined"
- continue
- fi
- count=$((count + 1))
- log "Analyzing: $name ($appid) [$count/$total]"
- local row url
- url="https://store.steampowered.com/api/appdetails?appids=${appid}&l=en&cc=us"
- vlog "[$count/$total] Fetching store appdetails: $url"
- # Be gentle with the store API
- sleep 0.1
- row=$(http_get "$url" | extract_requirements_and_platforms_stdin "$appid" || true)
- if [[ -z "$row" ]]; then continue; fi
- local status linux windows mac min_txt rec_txt type
- IFS=$'\t' read -r status linux windows mac min_txt rec_txt type <<<"$row"
- vlog "[$count/$total] Parsed store data: status=$status linux=$linux type=$type"
- # Occasionally Steam returns success=false spuriously; retry once
- if [[ "$status" != "ok" ]]; then
- vlog "[$count/$total] Store status=fail; retrying once..."
- sleep 0.3
- row=$(http_get "$url" | extract_requirements_and_platforms_stdin "$appid" || true)
- IFS=$'\t' read -r status linux windows mac min_txt rec_txt type <<<"$row"
- vlog "[$count/$total] After retry: status=$status"
- fi
- # Try filtered endpoint that often bypasses age/region gates
- if [[ "$status" != "ok" ]]; then
- local url2="https://store.steampowered.com/api/appdetails?appids=${appid}&filters=platforms,linux_requirements,pc_requirements,type&l=en&cc=us"
- vlog "[$count/$total] Retrying with filters: $url2"
- sleep 0.1
- row=$(http_get "$url2" | extract_requirements_and_platforms_stdin "$appid" || true)
- IFS=$'\t' read -r status linux windows mac min_txt rec_txt type <<<"$row"
- vlog "[$count/$total] Filtered fetch status=$status"
- fi
- if [[ "$status" != "ok" ]]; then continue; fi
- if [[ "$type" != "game" && "$type" != "dlc" && "$type" != "" ]]; then continue; fi
- # ProtonDB tier
- [[ $ABORT -eq 1 ]] && break
- local pdb_tier
- vlog "[$count/$total] Fetching ProtonDB tier for appid=$appid"
- pdb_tier=$(fetch_protondb_tier "$appid")
- vlog "[$count/$total] ProtonDB tier=$pdb_tier"
+ local count=0
+ local total
+ total=$(wc -l < "$games_tsv" | tr -d ' ')
+ [[ -z $total ]] && total=0
+ while IFS=$'\t' read -r appid name; do
+ [[ $ABORT -eq 1 ]] && break
+ [[ -n $appid ]] || continue
+ if is_known_tool_name "$name"; then
+ vlog "[$((count + 1))/$total] Skipping compatibility tool: $name ($appid)"
+ continue
+ fi
+ # If cached, reuse; else analyze and cache
+ if [[ $FORCE_REFRESH -eq 0 && -n ${CACHE_MAP[$appid]+isset} ]]; then
+ # Normalize to include ProtonDB column if older cache lacked it
+ IFS=$'\t' read -r c_score c_a c_linux c_min c_rec c_name c_pdb <<< "${CACHE_MAP[$appid]}"
+ c_pdb=${c_pdb:-unknown}
+ vlog "[$((count + 1))/$total] Cache hit: $name ($appid) | ProtonDB=$c_pdb"
+ printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$c_score" "$c_a" "$c_linux" "$c_min" "$c_rec" "$c_name" "$c_pdb" >> "$results_combined"
+ continue
+ fi
+ count=$((count + 1))
+ log "Analyzing: $name ($appid) [$count/$total]"
+ local row url
+ url="https://store.steampowered.com/api/appdetails?appids=${appid}&l=en&cc=us"
+ vlog "[$count/$total] Fetching store appdetails: $url"
+ # Be gentle with the store API
+ sleep 0.1
+ row=$(http_get "$url" | extract_requirements_and_platforms_stdin "$appid" || true)
+ if [[ -z $row ]]; then continue; fi
+ local status linux _windows _mac min_txt rec_txt type
+ IFS=$'\t' read -r status linux _windows _mac min_txt rec_txt type <<< "$row"
+ vlog "[$count/$total] Parsed store data: status=$status linux=$linux type=$type"
+ # Occasionally Steam returns success=false spuriously; retry once
+ if [[ $status != "ok" ]]; then
+ vlog "[$count/$total] Store status=fail; retrying once..."
+ sleep 0.3
+ row=$(http_get "$url" | extract_requirements_and_platforms_stdin "$appid" || true)
+ IFS=$'\t' read -r status linux _windows _mac min_txt rec_txt type <<< "$row"
+ vlog "[$count/$total] After retry: status=$status"
+ fi
+ # Try filtered endpoint that often bypasses age/region gates
+ if [[ $status != "ok" ]]; then
+ local url2="https://store.steampowered.com/api/appdetails?appids=${appid}&filters=platforms,linux_requirements,pc_requirements,type&l=en&cc=us"
+ vlog "[$count/$total] Retrying with filters: $url2"
+ sleep 0.1
+ row=$(http_get "$url2" | extract_requirements_and_platforms_stdin "$appid" || true)
+ IFS=$'\t' read -r status linux _windows _mac min_txt rec_txt type <<< "$row"
+ vlog "[$count/$total] Filtered fetch status=$status"
+ fi
+ if [[ $status != "ok" ]]; then continue; fi
+ if [[ $type != "game" && $type != "dlc" && $type != "" ]]; then continue; fi
+ # ProtonDB tier
+ [[ $ABORT -eq 1 ]] && break
+ local pdb_tier
+ vlog "[$count/$total] Fetching ProtonDB tier for appid=$appid"
+ pdb_tier=$(fetch_protondb_tier "$appid")
+ vlog "[$count/$total] ProtonDB tier=$pdb_tier"
- # Compute hardware-based score
- local score_line s_score s_min_ram s_rec_ram
- score_line=$(score_game "$linux" "$min_txt" "$rec_txt")
- IFS=$'\t' read -r s_score s_min_ram s_rec_ram <<<"$score_line"
+ # Compute hardware-based score
+ local score_line s_score s_min_ram s_rec_ram
+ score_line=$(score_game "$linux" "$min_txt" "$rec_txt")
+ IFS=$'\t' read -r s_score s_min_ram s_rec_ram <<< "$score_line"
- # Gate by ProtonDB: if bronze or below -> mark unplayable and force low score
- if ! protondb_allowed "$pdb_tier"; then
- s_score=-999
- fi
+ # Gate by ProtonDB: if bronze or below -> mark unplayable and force low score
+ if ! protondb_allowed "$pdb_tier"; then
+ s_score=-999
+ fi
- printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$s_score" "$appid" "$linux" "$s_min_ram" "$s_rec_ram" "$name" "$pdb_tier" >> "$results_combined"
- vlog "[$count/$total] Scored and recorded: score=$s_score min=${s_min_ram}G rec=${s_rec_ram}G"
- done < "$games_tsv"
+ printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$s_score" "$appid" "$linux" "$s_min_ram" "$s_rec_ram" "$name" "$pdb_tier" >> "$results_combined"
+ vlog "[$count/$total] Scored and recorded: score=$s_score min=${s_min_ram}G rec=${s_rec_ram}G"
+ done < "$games_tsv"
- if [[ ! -s "$results_combined" ]]; then
- die "No compatible entries parsed from store API."
- fi
+ if [[ ! -s $results_combined ]]; then
+ die "No compatible entries parsed from store API."
+ fi
- print_header
- local rank=0
- sort -t $'\t' -k1,1nr -k6,6 "$results_combined" | while IFS=$'\t' read -r score appid linux min_ram rec_ram name pdb_tier; do
- rank=$((rank + 1))
- local display_name="$name"
- if ! protondb_allowed "$pdb_tier"; then
- display_name="$name [UNPLAYABLE]"
- fi
- printf "%-5s %-8s %-6s %-6s %-8s %-9s %s\n" "$rank" "$score" "${min_ram}G" "${rec_ram}G" "$linux" "${pdb_tier:-unknown}" "$display_name"
- done
+ print_header
+ local rank=0
+ sort -t $'\t' -k1,1nr -k6,6 "$results_combined" | while IFS=$'\t' read -r score appid linux min_ram rec_ram name pdb_tier; do
+ rank=$((rank + 1))
+ local display_name="$name"
+ if ! protondb_allowed "$pdb_tier"; then
+ display_name="$name [UNPLAYABLE]"
+ fi
+ printf "%-5s %-8s %-6s %-6s %-8s %-9s %s\n" "$rank" "$score" "${min_ram}G" "${rec_ram}G" "$linux" "${pdb_tier:-unknown}" "$display_name"
+ done
- # Persist updated results for future runs (only current library entries)
- cp -f "$results_combined" "$RESULTS_CACHE" 2>/dev/null || cat "$results_combined" > "$RESULTS_CACHE"
+ # Persist updated results for future runs (only current library entries)
+ cp -f "$results_combined" "$RESULTS_CACHE" 2> /dev/null || cat "$results_combined" > "$RESULTS_CACHE"
}
main "$@"
-
diff --git a/scripts/utils/toggle_mic.sh b/scripts/utils/toggle_mic.sh
index b824dfb..0c16b91 100755
--- a/scripts/utils/toggle_mic.sh
+++ b/scripts/utils/toggle_mic.sh
@@ -2,47 +2,47 @@
# Check if amixer is installed, if not, install it
if ! command -v amixer &> /dev/null; then
- echo "amixer could not be found, installing..."
- sudo pacman -S --noconfirm alsa-utils
+ echo "amixer could not be found, installing..."
+ sudo pacman -S --noconfirm alsa-utils
fi
# Ensure dbus is running
if ! pgrep -x "dbus-daemon" > /dev/null; then
- echo "Starting dbus..."
- sudo systemctl start dbus
+ echo "Starting dbus..."
+ sudo systemctl start dbus
fi
# Ensure dbus is properly initialized for the user session
-export $(dbus-launch)
+eval "$(dbus-launch)"
# Ensure notification-daemon is installed
if ! pacman -Qs notification-daemon > /dev/null; then
- echo "Installing notification-daemon..."
- sudo pacman -S --noconfirm notification-daemon
+ echo "Installing notification-daemon..."
+ sudo pacman -S --noconfirm notification-daemon
fi
# Ensure dunst is installed and running
if ! pacman -Qs dunst > /dev/null; then
- echo "Installing dunst..."
- sudo pacman -S --noconfirm dunst
+ echo "Installing dunst..."
+ sudo pacman -S --noconfirm dunst
fi
if ! pgrep -x "dunst" > /dev/null; then
- echo "Starting dunst..."
- dunst &
+ echo "Starting dunst..."
+ dunst &
fi
# Get the current state of the microphone
MIC_STATE=$(amixer get Capture | grep '\[on\]')
if [ -z "$MIC_STATE" ]; then
- # If the microphone is off, turn it on
- amixer set Capture cap
- sleep 1 # Add a delay to ensure notify-send works correctly
- notify-send "Microphone" "Microphone is now ON"
+ # If the microphone is off, turn it on
+ amixer set Capture cap
+ sleep 1 # Add a delay to ensure notify-send works correctly
+ notify-send "Microphone" "Microphone is now ON"
else
- # If the microphone is on, turn it off
- amixer set Capture nocap
- sleep 1 # Add a delay to ensure notify-send works correctly
- notify-send "Microphone" "Microphone is now OFF"
+ # If the microphone is on, turn it off
+ amixer set Capture nocap
+ sleep 1 # Add a delay to ensure notify-send works correctly
+ notify-send "Microphone" "Microphone is now OFF"
fi
diff --git a/scripts/utils/toggle_wheel.sh b/scripts/utils/toggle_wheel.sh
index 96e586b..cc77e27 100755
--- a/scripts/utils/toggle_wheel.sh
+++ b/scripts/utils/toggle_wheel.sh
@@ -10,48 +10,48 @@ ACTION=$1
# Check if script is run as root
if [[ $EUID -ne 0 ]]; then
- echo "This script must be run as root. Please run with sudo."
- exit 1
+ echo "This script must be run as root. Please run with sudo."
+ exit 1
fi
# Check if action parameter is provided
-if [[ "$ACTION" != "on" && "$ACTION" != "off" ]]; then
- echo "Usage: $0 [on|off]"
- exit 1
+if [[ $ACTION != "on" && $ACTION != "off" ]]; then
+ echo "Usage: $0 [on|off]"
+ exit 1
fi
DEVICE_PATH=""
# Find the device path in sysfs (robust scan)
for d in /sys/bus/usb/devices/*; do
- if [[ -f "$d/idVendor" && -f "$d/idProduct" ]]; then
- v=$(cat "$d/idVendor")
- p=$(cat "$d/idProduct")
- if [[ "$v" == "$VENDOR_ID" && "$p" == "$PRODUCT_ID" ]]; then
- DEVICE_PATH="$d"
- break
- fi
+ if [[ -f "$d/idVendor" && -f "$d/idProduct" ]]; then
+ v=$(cat "$d/idVendor")
+ p=$(cat "$d/idProduct")
+ if [[ $v == "$VENDOR_ID" && $p == "$PRODUCT_ID" ]]; then
+ DEVICE_PATH="$d"
+ break
fi
+ fi
done
# Check if device was found
if [ -z "$DEVICE_PATH" ]; then
- echo "Device with Vendor ID $VENDOR_ID and Product ID $PRODUCT_ID not found in /sys/bus/usb/devices."
- echo "Tip: Run 'lsusb | grep ${VENDOR_ID}:${PRODUCT_ID}' to verify it's connected."
- exit 1
+ echo "Device with Vendor ID $VENDOR_ID and Product ID $PRODUCT_ID not found in /sys/bus/usb/devices."
+ echo "Tip: Run 'lsusb | grep ${VENDOR_ID}:${PRODUCT_ID}' to verify it's connected."
+ exit 1
fi
# Enable or disable the device
if [ ! -e "$DEVICE_PATH/authorized" ]; then
- echo "The 'authorized' attribute is not present at $DEVICE_PATH."
- echo "This device may not support toggling via 'authorized'."
- exit 1
+ echo "The 'authorized' attribute is not present at $DEVICE_PATH."
+ echo "This device may not support toggling via 'authorized'."
+ exit 1
fi
if [ "$ACTION" == "off" ]; then
- echo '0' > "$DEVICE_PATH/authorized"
- echo "Device at $(basename "$DEVICE_PATH") turned off."
+ echo '0' > "$DEVICE_PATH/authorized"
+ echo "Device at $(basename "$DEVICE_PATH") turned off."
elif [ "$ACTION" == "on" ]; then
- echo '1' > "$DEVICE_PATH/authorized"
- echo "Device at $(basename "$DEVICE_PATH") turned on."
-fi
\ No newline at end of file
+ echo '1' > "$DEVICE_PATH/authorized"
+ echo "Device at $(basename "$DEVICE_PATH") turned on."
+fi
diff --git a/scripts/utils/toggle_window_manager.sh b/scripts/utils/toggle_window_manager.sh
index 748b7fb..5b8c35a 100755
--- a/scripts/utils/toggle_window_manager.sh
+++ b/scripts/utils/toggle_window_manager.sh
@@ -5,58 +5,61 @@ set -euo pipefail
# Configuration -----------------------------------------------------------------
TARGET_SESSION_NAME="Xfce Session"
TARGET_PACKAGES=(
- xfwm4 # Compositing window manager with XFCE integration
- xfce4-session # Provides the Xfce session entry for display managers
- xfce4-panel # Panel with system tray support
- xfce4-settings # Settings daemon (enables compositing toggle, theming, etc.)
- xfce4-terminal # Handy default terminal for the new environment
+ xfwm4 # Compositing window manager with XFCE integration
+ xfce4-session # Provides the Xfce session entry for display managers
+ xfce4-panel # Panel with system tray support
+ xfce4-settings # Settings daemon (enables compositing toggle, theming, etc.)
+ xfce4-terminal # Handy default terminal for the new environment
)
# Utility functions --------------------------------------------------------------
info() { echo "[INFO] $*"; }
warn() { echo "[WARN] $*" >&2; }
-error() { echo "[ERROR] $*" >&2; exit 1; }
+error() {
+ echo "[ERROR] $*" >&2
+ exit 1
+}
require_command() {
- local cmd="$1" pkg_hint="${2:-}"
- if ! command -v "$cmd" >/dev/null 2>&1; then
- if [[ -n "$pkg_hint" ]]; then
- warn "Install '$pkg_hint' to obtain the '$cmd' command."
- fi
- error "Required command '$cmd' not found."
- fi
+ local cmd="$1" pkg_hint="${2:-}"
+ if ! command -v "$cmd" > /dev/null 2>&1; then
+ if [[ -n $pkg_hint ]]; then
+ warn "Install '$pkg_hint' to obtain the '$cmd' command."
+ fi
+ error "Required command '$cmd' not found."
+ fi
}
ensure_pacman() {
- require_command pacman "pacman"
- if ! grep -qi "arch" /etc/os-release 2>/dev/null; then
- warn "This script was designed for Arch Linux; continuing anyway."
- fi
+ require_command pacman "pacman"
+ if ! grep -qi "arch" /etc/os-release 2> /dev/null; then
+ warn "This script was designed for Arch Linux; continuing anyway."
+ fi
}
install_packages() {
- local missing=()
- for pkg in "${TARGET_PACKAGES[@]}"; do
- if ! pacman -Qi "$pkg" >/dev/null 2>&1; then
- missing+=("$pkg")
- fi
- done
+ local missing=()
+ for pkg in "${TARGET_PACKAGES[@]}"; do
+ if ! pacman -Qi "$pkg" > /dev/null 2>&1; then
+ missing+=("$pkg")
+ fi
+ done
- if [[ ${#missing[@]} -eq 0 ]]; then
- info "All target packages are already installed."
- return
- fi
+ if [[ ${#missing[@]} -eq 0 ]]; then
+ info "All target packages are already installed."
+ return
+ fi
- if ! command -v sudo >/dev/null 2>&1; then
- error "sudo is required to install packages. Install sudo or run this script as root."
- fi
+ if ! command -v sudo > /dev/null 2>&1; then
+ error "sudo is required to install packages. Install sudo or run this script as root."
+ fi
- info "Installing missing packages: ${missing[*]}"
- sudo pacman -S --needed --noconfirm "${missing[@]}"
+ info "Installing missing packages: ${missing[*]}"
+ sudo pacman -S --needed --noconfirm "${missing[@]}"
}
print_post_install_tips() {
- cat </dev/null 2>&1; then
- info "Terminating current session (ID: $session_id) via loginctl."
- loginctl terminate-session "$session_id"
- return
- fi
+ if [[ -n $session_id ]] && loginctl show-session "$session_id" > /dev/null 2>&1; then
+ info "Terminating current session (ID: $session_id) via loginctl."
+ loginctl terminate-session "$session_id"
+ return
+ fi
- if loginctl list-sessions 2>/dev/null | awk '{print $1" "$3}' | grep -q " $USER$"; then
- info "Terminating all sessions for user '$USER' via loginctl."
- loginctl terminate-user "$USER"
- return
- fi
+ if loginctl list-sessions 2> /dev/null | awk '{print $1" "$3}' | grep -q " $USER$"; then
+ info "Terminating all sessions for user '$USER' via loginctl."
+ loginctl terminate-user "$USER"
+ return
+ fi
- warn "loginctl could not terminate the session; attempting fallback logout."
- pkill -KILL -u "$USER" || error "Failed to terminate user sessions. Please log out manually."
+ warn "loginctl could not terminate the session; attempting fallback logout."
+ pkill -KILL -u "$USER" || error "Failed to terminate user sessions. Please log out manually."
}
main() {
- ensure_pacman
- install_packages
- print_post_install_tips
+ ensure_pacman
+ install_packages
+ print_post_install_tips
- # Give the user a moment to read the instructions before logging out.
- logout_user
+ # Give the user a moment to read the instructions before logging out.
+ logout_user
}
main "$@"
diff --git a/scripts/utils/turn_off_auto_idle_screen_shutdown.sh b/scripts/utils/turn_off_auto_idle_screen_shutdown.sh
index 309f507..719d69e 100755
--- a/scripts/utils/turn_off_auto_idle_screen_shutdown.sh
+++ b/scripts/utils/turn_off_auto_idle_screen_shutdown.sh
@@ -22,20 +22,20 @@ set -euo pipefail
log() { printf "[idle-off] %s\n" "$*"; }
warn() { printf "[idle-off][WARN] %s\n" "$*" >&2; }
-has_cmd() { command -v "$1" >/dev/null 2>&1; }
+has_cmd() { command -v "$1" > /dev/null 2>&1; }
persist_systemd=false
watch_controller=false
for arg in "${@:-}"; do
- case "$arg" in
- --persist-systemd)
- persist_systemd=true
- ;;
- --watch-controller)
- watch_controller=true
- ;;
- -h|--help)
- cat </dev/null | grep -q '^org\.gnome\.desktop\.session$'; then
- log "Applying GNOME settings to disable idle and lock"
- # No lock on idle
- gsettings set org.gnome.desktop.screensaver lock-enabled false 2>/dev/null || warn "Failed to set GNOME lock-enabled"
- # No idle delay (0 = never)
- gsettings set org.gnome.desktop.session idle-delay 0 2>/dev/null || warn "Failed to set GNOME idle-delay"
- # No automatic suspend on AC or battery
- gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing' 2>/dev/null || true
- gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type 'nothing' 2>/dev/null || true
- # Optional: ensure screensaver idle-activation-enabled is false (for older setups)
- gsettings set org.gnome.desktop.screensaver idle-activation-enabled false 2>/dev/null || true
- fi
- fi
+ if has_cmd gsettings; then
+ # Detect GNOME by presence of GNOME schemas
+ if gsettings list-schemas 2> /dev/null | grep -q '^org\.gnome\.desktop\.session$'; then
+ log "Applying GNOME settings to disable idle and lock"
+ # No lock on idle
+ gsettings set org.gnome.desktop.screensaver lock-enabled false 2> /dev/null || warn "Failed to set GNOME lock-enabled"
+ # No idle delay (0 = never)
+ gsettings set org.gnome.desktop.session idle-delay 0 2> /dev/null || warn "Failed to set GNOME idle-delay"
+ # No automatic suspend on AC or battery
+ gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing' 2> /dev/null || true
+ gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type 'nothing' 2> /dev/null || true
+ # Optional: ensure screensaver idle-activation-enabled is false (for older setups)
+ gsettings set org.gnome.desktop.screensaver idle-activation-enabled false 2> /dev/null || true
+ fi
+ fi
}
disable_kde_idle() {
- # Best-effort: turn off auto-locker; note: Plasma on Wayland still may rely on compositor-level settings
- if has_cmd kwriteconfig5; then
- log "Disabling KDE Plasma screen auto-lock (kscreenlockerrc)"
- kwriteconfig5 --file kscreenlockerrc --group Daemon --key Autolock false 2>/dev/null || true
- kwriteconfig5 --file kscreenlockerrc --group Daemon --key LockOnResume false 2>/dev/null || true
- kwriteconfig5 --file kscreenlockerrc --group Daemon --key Timeout 0 2>/dev/null || true
- fi
+ # Best-effort: turn off auto-locker; note: Plasma on Wayland still may rely on compositor-level settings
+ if has_cmd kwriteconfig5; then
+ log "Disabling KDE Plasma screen auto-lock (kscreenlockerrc)"
+ kwriteconfig5 --file kscreenlockerrc --group Daemon --key Autolock false 2> /dev/null || true
+ kwriteconfig5 --file kscreenlockerrc --group Daemon --key LockOnResume false 2> /dev/null || true
+ kwriteconfig5 --file kscreenlockerrc --group Daemon --key Timeout 0 2> /dev/null || true
+ fi
}
disable_sway_idle() {
- # Sway commonly uses swayidle for idle actions; killing it prevents screen blanking/locking
- if pgrep -x sway >/dev/null 2>&1; then
- if pgrep -x swayidle >/dev/null 2>&1; then
- log "Killing swayidle to prevent Wayland idle actions"
- pkill -x swayidle || true
- fi
- fi
+ # Sway commonly uses swayidle for idle actions; killing it prevents screen blanking/locking
+ if pgrep -x sway > /dev/null 2>&1; then
+ if pgrep -x swayidle > /dev/null 2>&1; then
+ log "Killing swayidle to prevent Wayland idle actions"
+ pkill -x swayidle || true
+ fi
+ fi
}
disable_lock_daemons() {
- # Stop common screen lockers/idle helpers if running
- local daemons=(xss-lock light-locker xscreensaver gnome-screensaver)
- local found=false
- for d in "${daemons[@]}"; do
- if pgrep -x "$d" >/dev/null 2>&1; then
- found=true
- log "Stopping ${d}"
- pkill -x "$d" || true
- fi
- done
- if [[ "$found" == false ]]; then
- log "No known lock daemons running"
- fi
+ # Stop common screen lockers/idle helpers if running
+ local daemons=(xss-lock light-locker xscreensaver gnome-screensaver)
+ local found=false
+ for d in "${daemons[@]}"; do
+ if pgrep -x "$d" > /dev/null 2>&1; then
+ found=true
+ log "Stopping ${d}"
+ pkill -x "$d" || true
+ fi
+ done
+ if [[ $found == false ]]; then
+ log "No known lock daemons running"
+ fi
}
disable_tty_idle() {
- if has_cmd setterm; then
- log "Disabling TTY blanking and powersave"
- # Apply to the current TTY; also attempt to broadcast to common TTYs
- setterm -blank 0 -powersave off -powerdown 0 || true
- for tty in /dev/tty{1..12}; do
- [[ -e "$tty" ]] || continue
- setterm -blank 0 -powersave off -powerdown 0 <"$tty" >/dev/null 2>&1 || true
- done
- fi
+ if has_cmd setterm; then
+ log "Disabling TTY blanking and powersave"
+ # Apply to the current TTY; also attempt to broadcast to common TTYs
+ setterm -blank 0 -powersave off -powerdown 0 || true
+ for tty in /dev/tty{1..12}; do
+ [[ -e $tty ]] || continue
+ setterm -blank 0 -powersave off -powerdown 0 < "$tty" > /dev/null 2>&1 || true
+ done
+ fi
}
reset_idle_activity() {
- # Trigger activity hints depending on environment
- if [[ -n "${DISPLAY:-}" ]]; then
- if has_cmd xset; then
- xset s reset || true
- xset -dpms || true
- xset s off || true
- xset s noblank || true
- fi
- if has_cmd xdotool; then
- # No-op mousemove to generate X11 activity without visible movement
- xdotool mousemove_relative -- 0 0 2>/dev/null || true
- fi
- fi
+ # Trigger activity hints depending on environment
+ if [[ -n ${DISPLAY:-} ]]; then
+ if has_cmd xset; then
+ xset s reset || true
+ xset -dpms || true
+ xset s off || true
+ xset s noblank || true
+ fi
+ if has_cmd xdotool; then
+ # No-op mousemove to generate X11 activity without visible movement
+ xdotool mousemove_relative -- 0 0 2> /dev/null || true
+ fi
+ fi
}
watch_js_device() {
- local dev="$1"
- log "Watching controller device: $dev"
- while :; do
- if [[ ! -e "$dev" ]]; then
- warn "Device disappeared: $dev"
- break
- fi
- # Joystick API event size is 8 bytes; block until an event arrives
- if dd if="$dev" bs=8 count=1 status=none of=/dev/null; then
- reset_idle_activity
- # Debounce bursts of events
- sleep 0.3
- else
- # On read error (e.g., permission), backoff
- sleep 1
- fi
- done
+ local dev="$1"
+ log "Watching controller device: $dev"
+ while :; do
+ if [[ ! -e $dev ]]; then
+ warn "Device disappeared: $dev"
+ break
+ fi
+ # Joystick API event size is 8 bytes; block until an event arrives
+ if dd if="$dev" bs=8 count=1 status=none of=/dev/null; then
+ reset_idle_activity
+ # Debounce bursts of events
+ sleep 0.3
+ else
+ # On read error (e.g., permission), backoff
+ sleep 1
+ fi
+ done
}
start_controller_watchers() {
- # Attempt to watch all /dev/input/js* devices; rescan periodically for new ones
- local seen=""
- declare -A pids
+ # Attempt to watch all /dev/input/js* devices; rescan periodically for new ones
+ declare -A pids
- # Initial permission check
- local any_js=false any_readable=false
- for dev in /dev/input/js*; do
- [[ -e "$dev" ]] || continue
- any_js=true
- if [[ -r "$dev" ]]; then any_readable=true; fi
- done
- if [[ "$any_js" == true && "$any_readable" == false ]]; then
- warn "No read permission to /dev/input/js*; add your user to the 'input' group or create udev rules."
- fi
+ # Initial permission check
+ local any_js=false any_readable=false
+ for dev in /dev/input/js*; do
+ [[ -e $dev ]] || continue
+ any_js=true
+ if [[ -r $dev ]]; then any_readable=true; fi
+ done
+ if [[ $any_js == true && $any_readable == false ]]; then
+ warn "No read permission to /dev/input/js*; add your user to the 'input' group or create udev rules."
+ fi
- while :; do
- local found_any=false
- for dev in /dev/input/js*; do
- [[ -e "$dev" ]] || continue
- found_any=true
- if [[ -z "${pids[$dev]:-}" ]] || ! kill -0 "${pids[$dev]}" 2>/dev/null; then
- # Start a watcher for this device in background
- watch_js_device "$dev" &
- pids[$dev]=$!
- fi
- done
- if [[ "$found_any" == false ]]; then
- # No joystick devices; quiet rescan
- sleep 5
- else
- # Rescan less frequently when active
- sleep 2
- fi
- done
+ while :; do
+ local found_any=false
+ for dev in /dev/input/js*; do
+ [[ -e $dev ]] || continue
+ found_any=true
+ if [[ -z ${pids[$dev]:-} ]] || ! kill -0 "${pids[$dev]}" 2> /dev/null; then
+ # Start a watcher for this device in background
+ watch_js_device "$dev" &
+ pids[$dev]=$!
+ fi
+ done
+ if [[ $found_any == false ]]; then
+ # No joystick devices; quiet rescan
+ sleep 5
+ else
+ # Rescan less frequently when active
+ sleep 2
+ fi
+ done
}
persist_with_systemd_logind() {
- # Set IdleAction=ignore in /etc/systemd/logind.conf and restart logind
- # Warning: restarting logind can affect user sessions (e.g., inhibit handling). Use with care.
- if [[ "$persist_systemd" != true ]]; then
- return 0
- fi
- if ! has_cmd sudo; then
- warn "sudo not found; cannot persist systemd-logind setting"
- return 0
- fi
- log "Persisting: setting systemd-logind IdleAction=ignore (requires sudo)"
- sudo sh -c '
+ # Set IdleAction=ignore in /etc/systemd/logind.conf and restart logind
+ # Warning: restarting logind can affect user sessions (e.g., inhibit handling). Use with care.
+ if [[ $persist_systemd != true ]]; then
+ return 0
+ fi
+ if ! has_cmd sudo; then
+ warn "sudo not found; cannot persist systemd-logind setting"
+ return 0
+ fi
+ log "Persisting: setting systemd-logind IdleAction=ignore (requires sudo)"
+ sudo sh -c '
set -e
conf=/etc/systemd/logind.conf
if [ ! -f "$conf" ]; then
@@ -235,39 +234,38 @@ persist_with_systemd_logind() {
printf "\nIdleAction=ignore\n" >> "$conf"
fi
'
- log "Restarting systemd-logind to apply changes (may briefly affect session inhibitors)"
- sudo systemctl restart systemd-logind || warn "Failed to restart systemd-logind"
+ log "Restarting systemd-logind to apply changes (may briefly affect session inhibitors)"
+ sudo systemctl restart systemd-logind || warn "Failed to restart systemd-logind"
}
main() {
- log "Starting idle/lock disablement"
+ log "Starting idle/lock disablement"
- # Environment-aware steps
- disable_x11_idle
- disable_gnome_idle
- disable_kde_idle
- disable_sway_idle
+ # Environment-aware steps
+ disable_x11_idle
+ disable_gnome_idle
+ disable_kde_idle
+ disable_sway_idle
- # Generic steps
- disable_lock_daemons
- disable_tty_idle
+ # Generic steps
+ disable_lock_daemons
+ disable_tty_idle
- # Optional persistence
- persist_with_systemd_logind
+ # Optional persistence
+ persist_with_systemd_logind
- if [[ "$watch_controller" == true ]]; then
- log "Controller activity watcher enabled"
- # Keep the script alive to watch controllers
- start_controller_watchers &
- watcher_pid=$!
- log "Watcher PID: $watcher_pid"
- # Wait indefinitely and forward termination
- trap 'log "Stopping controller watcher"; kill "$watcher_pid" 2>/dev/null || true; exit 0' INT TERM
- wait "$watcher_pid"
- else
- log "Done. The screen should no longer blank, lock, or power down automatically."
- fi
+ if [[ $watch_controller == true ]]; then
+ log "Controller activity watcher enabled"
+ # Keep the script alive to watch controllers
+ start_controller_watchers &
+ watcher_pid=$!
+ log "Watcher PID: $watcher_pid"
+ # Wait indefinitely and forward termination
+ trap 'log "Stopping controller watcher"; kill "$watcher_pid" 2>/dev/null || true; exit 0' INT TERM
+ wait "$watcher_pid"
+ else
+ log "Done. The screen should no longer blank, lock, or power down automatically."
+ fi
}
main "$@"
-