fix: shellcheck issues

This commit is contained in:
Krzysztof Rudnicki 2025-11-01 15:36:22 +01:00
parent 2c46984c61
commit 795f56023e
54 changed files with 5479 additions and 5231 deletions

View File

@ -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"

View File

@ -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"

View File

@ -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}"

View File

@ -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}"

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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"
log "Enforcement complete"

View File

@ -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

View File

@ -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

View File

@ -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)"

View File

@ -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

View File

@ -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

View File

@ -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
echo "DNS cache flush skipped (use --flush-dns to enable)."
fi

View File

@ -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

View File

@ -8,4 +8,4 @@ acpi -b | awk -F', ' '
if (time[1] != "") printf ", %s", time[1]
if ($1 ~ /Charging/) printf ", "
printf "\n"
}'
}'

View File

@ -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

View File

@ -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 "<span color=\"$cpu_color\"> ${cpu_temp}°C, ${cpu_load}</span>"
echo -e "<span color=\"$cpu_color\"> ${cpu_temp}°C, ${cpu_load}</span>"

View File

@ -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 "<span color=\"$gpu_color\"> ${gpu_temp}, ${gpu_load}%</span>"
echo
echo "#FFFFFF" # Default color for fallback (ignored if markup is enabled)
echo "#FFFFFF" # Default color for fallback (ignored if markup is enabled)

View File

@ -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

View File

@ -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"
echo "#50FA7B"

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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
echo "$ssid ($signal dBm) $ip_address"
fi

View File

@ -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

View File

@ -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 <<EOF
cat << EOF
${SCRIPT_NAME} — Download and wire up LeechBlockNG from GitHub
Usage: ${SCRIPT_NAME} [--version vX.Y[.Z]] [--force] [--install-firefox]
@ -46,15 +46,26 @@ AUTO_FIREFOX=0
while [[ $# -gt 0 ]]; do
case "$1" in
--version)
VERSION="$2"; shift 2;;
VERSION="$2"
shift 2
;;
--force)
FORCE=1; shift;;
FORCE=1
shift
;;
--install-firefox)
AUTO_FIREFOX=1; shift;;
-h|--help)
usage; exit 0;;
AUTO_FIREFOX=1
shift
;;
-h | --help)
usage
exit 0
;;
*)
err "Unknown argument: $1"; usage; exit 2;;
err "Unknown argument: $1"
usage
exit 2
;;
esac
done
@ -65,42 +76,48 @@ require_cmd tar
require_cmd find
require_cmd sed
require_cmd awk
if ! command -v jq >/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" <<WRAP
cat > "$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" <<DESK
cat > "$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 <<FF
cat << FF
Options:
1) Install from Mozilla Add-ons (recommended):
https://addons.mozilla.org/firefox/addon/leechblock-ng/
@ -272,13 +294,13 @@ if [[ $AUTO_FIREFOX -eq 1 && $ff_found -eq 1 ]]; then
# Determine policy directories for detected Firefox-like browsers
declare -a POLICY_DIRS
POLICY_DIRS=()
if command -v firefox >/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" <<JSON
cat > "$tmp_pol" << JSON
{
"policies": {
"ExtensionSettings": {
@ -325,7 +348,7 @@ JSON
fi
else
info "Creating new policies.json at $pol_target"
cat > "$tmp_pol" <<JSON
cat > "$tmp_pol" << JSON
{
"policies": {
"ExtensionSettings": {

View File

@ -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}"
echo -e "Pacman is now wrapped. The original pacman is available at ${CYAN}/usr/bin/pacman.orig${NC}"

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -12,10 +12,10 @@ SCRIPT_NAME=$(basename "$0")
UNDO=false
for arg in "$@"; do
case "$arg" in
--undo) UNDO=true ;;
-h|--help)
cat <<EOF
case "$arg" in
--undo) UNDO=true ;;
-h | --help)
cat << EOF
Usage: $SCRIPT_NAME [--undo]
Actions:
@ -29,40 +29,40 @@ Notes:
- Requires root privileges to write to /etc/* policy paths. Will self-elevate via sudo.
- Restart affected browsers to apply changes.
EOF
exit 0
;;
esac
exit 0
;;
esac
done
# Re-exec as root if needed
if [[ $EUID -ne 0 ]]; then
echo "[info] Elevating privileges with sudo..."
exec sudo -E bash "$0" "$@"
echo "[info] Elevating privileges with sudo..."
exec sudo -E bash "$0" "$@"
fi
# Map binaries to a logical product key
declare -A BIN_TO_KEY=(
[thorium-browser]=thorium-browser
[thorium]=thorium-browser
[chromium]=chromium
[google-chrome]=google-chrome
[google-chrome-stable]=google-chrome
[brave-browser]=brave-browser
[vivaldi]=vivaldi
[vivaldi-stable]=vivaldi
[microsoft-edge-stable]=microsoft-edge-stable
[opera]=opera
[thorium - browser]=thorium-browser
[thorium]=thorium-browser
[chromium]=chromium
[google - chrome]=google-chrome
[google - chrome - stable]=google-chrome
[brave - browser]=brave-browser
[vivaldi]=vivaldi
[vivaldi - stable]=vivaldi
[microsoft - edge - stable]=microsoft-edge-stable
[opera]=opera
)
# Candidate policy directories per product key (first existing or first creatable is used)
declare -A CANDIDATE_DIRS=(
[thorium-browser]="/etc/thorium/policies/managed:/etc/opt/thorium/policies/managed:/etc/opt/thorium-browser/policies/managed:/etc/thorium-browser/policies/managed"
[chromium]="/etc/chromium/policies/managed"
[google-chrome]="/etc/opt/chrome/policies/managed"
[brave-browser]="/etc/opt/brave/policies/managed"
[vivaldi]="/etc/opt/vivaldi/policies/managed"
[microsoft-edge-stable]="/etc/opt/edge/policies/managed"
[opera]="/etc/opt/opera/policies/managed"
[thorium - browser]="/etc/thorium/policies/managed:/etc/opt/thorium/policies/managed:/etc/opt/thorium-browser/policies/managed:/etc/thorium-browser/policies/managed"
[chromium]="/etc/chromium/policies/managed"
[google - chrome]="/etc/opt/chrome/policies/managed"
[brave - browser]="/etc/opt/brave/policies/managed"
[vivaldi]="/etc/opt/vivaldi/policies/managed"
[microsoft - edge - stable]="/etc/opt/edge/policies/managed"
[opera]="/etc/opt/opera/policies/managed"
)
POLICY_FILENAME="99-disable-guest-mode.json"
@ -75,88 +75,89 @@ POLICY_JSON='{
# Discover installed browsers
declare -A INSTALLED_KEYS=()
for bin in "${!BIN_TO_KEY[@]}"; do
if command -v "$bin" >/dev/null 2>&1; then
key=${BIN_TO_KEY[$bin]}
INSTALLED_KEYS[$key]=1
fi
if command -v "$bin" > /dev/null 2>&1; then
key=${BIN_TO_KEY[$bin]}
INSTALLED_KEYS[$key]=1
fi
done
if [[ ${#INSTALLED_KEYS[@]} -eq 0 ]]; then
echo "[warn] No supported Chromium-based browsers detected in PATH. Proceeding to configure Thorium paths anyway."
INSTALLED_KEYS[thorium-browser]=1
echo "[warn] No supported Chromium-based browsers detected in PATH. Proceeding to configure Thorium paths anyway."
INSTALLED_KEYS[thorium - browser]=1
fi
choose_target_dir() {
local key="$1"
local IFS=":"
local dirs
read -r -a dirs <<< "${CANDIDATE_DIRS[$key]:-}"
# Prefer an existing directory; else pick the first candidate
for d in "${dirs[@]}"; do
if [[ -d "$d" ]]; then
echo "$d"
return 0
fi
done
echo "${dirs[0]}"
local key="$1"
local IFS=":"
local dirs
read -r -a dirs <<< "${CANDIDATE_DIRS[$key]:-}"
# Prefer an existing directory; else pick the first candidate
for d in "${dirs[@]}"; do
if [[ -d $d ]]; then
echo "$d"
return 0
fi
done
echo "${dirs[0]}"
}
apply_policy() {
local target_dir="$1"; shift
local file="$target_dir/$POLICY_FILENAME"
local target_dir="$1"
shift
local file="$target_dir/$POLICY_FILENAME"
echo "[apply] $file"
echo "[apply] $file"
mkdir -p "$target_dir"
# Write atomically
local tmp
tmp=$(mktemp)
printf '%s
' "$POLICY_JSON" >"$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

View File

@ -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

View File

@ -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

View File

@ -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 <command> [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/urandom | head -c 8)
log "Generated VNC password: $password"
else
read -rsp "Confirm password: " confirm
printf '\n'
if [[ "$password" != "$confirm" ]]; then
die "Passwords do not match."
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/urandom | head -c 8)
log "Generated VNC password: $password"
else
read -rsp "Confirm password: " confirm
printf '\n'
if [[ $password != "$confirm" ]]; then
die "Passwords do not match."
fi
fi
local tmp
tmp=$(mktemp)
x11vnc -storepasswd "$password" "$tmp" >/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" <<EOF
if [[ -f $ENV_FILE ]]; then
return
fi
cat > "$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" <<EOF
cat > "$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: <your-ip>\n Port: %s\n Password: <stored during setup>\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: <your-ip>\n Port: %s\n Password: <stored during setup>\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 <user>@<public-ip>\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 <user>@<public-ip>\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 "$@"

View File

@ -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

View File

@ -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 "$@"

View File

@ -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 <config-name> <command>
@ -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"
echo "After reboot, verify GSP with: cat /proc/driver/nvidia/params | grep EnableGpuFirmware"

View File

@ -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 <<EOF
cat << EOF
Usage: $(basename "$0") [options]
Options:
@ -61,341 +57,363 @@ EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--path)
ROOT_DIR="$2"; shift 2 ;;
--skip-install)
SKIP_INSTALL="true"; shift ;;
--install-only)
INSTALL_ONLY="true"; shift ;;
--list-only)
LIST_ONLY="true"; shift ;;
--verbose)
VERBOSE="true"; shift ;;
-h|--help)
usage; exit 0 ;;
*)
log_error "Unknown argument: $1"; usage; exit 2 ;;
esac
case "$1" in
--path)
ROOT_DIR="$2"
shift 2
;;
--skip-install)
SKIP_INSTALL="true"
shift
;;
--install-only)
INSTALL_ONLY="true"
shift
;;
--list-only)
LIST_ONLY="true"
shift
;;
--verbose)
VERBOSE="true"
shift
;;
-h | --help)
usage
exit 0
;;
*)
log_error "Unknown argument: $1"
usage
exit 2
;;
esac
done
if [[ ! -d "$ROOT_DIR" ]]; then
log_error "Path not found: $ROOT_DIR"
exit 2
if [[ ! -d $ROOT_DIR ]]; then
log_error "Path not found: $ROOT_DIR"
exit 2
fi
is_cmd() { command -v "$1" >/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 $?

View File

@ -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

View File

@ -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

View File

@ -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" "$@"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -13,7 +13,7 @@ SAMPLE_LIMIT=20
# Simple usage helper
usage() {
cat <<EOF
cat << EOF
Usage: $(basename "$0") [--dry-run|-n] [--sample=N]
Options:
@ -36,369 +36,373 @@ VIDEO_EXTENSIONS=("mp4" "avi" "mkv" "mov" "wmv" "flv" "webm" "m4v" "3gp" "ogv" "
# Function to log messages with timestamp
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Parse CLI flags early
while [[ ${1:-} =~ ^- ]]; do
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
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 "$@"
main "$@"

View File

@ -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..."

View File

@ -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

View File

@ -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 ~
sort_files ~

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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
echo '1' > "$DEVICE_PATH/authorized"
echo "Device at $(basename "$DEVICE_PATH") turned on."
fi

View File

@ -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 <<EOF
cat << EOF
------------------------------------------------------------------------
XFCE session installed.
@ -73,31 +76,31 @@ EOF
}
logout_user() {
local session_id="${XDG_SESSION_ID:-}"
local session_id="${XDG_SESSION_ID:-}"
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 [[ -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 "$@"

View File

@ -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 <<EOF
case "$arg" in
--persist-systemd)
persist_systemd=true
;;
--watch-controller)
watch_controller=true
;;
-h | --help)
cat << EOF
Usage: $(basename "$0") [--persist-systemd] [--watch-controller]
Disables idle detection, screen blanking, and auto-lock for the current session.
@ -54,173 +54,172 @@ What this does:
- Optional: systemd-logind IdleAction=ignore
- Optional: watch controller input and reset idle timers
EOF
exit 0
;;
esac
exit 0
;;
esac
done
disable_x11_idle() {
if [[ -n "${DISPLAY:-}" ]] && has_cmd xset; then
log "Disabling X11 DPMS/screensaver/blanking via xset"
xset -dpms || true
xset s off || true
xset s noblank || true
else
log "X11/xset not detected or DISPLAY not set; skipping xset"
fi
if [[ -n ${DISPLAY:-} ]] && has_cmd xset; then
log "Disabling X11 DPMS/screensaver/blanking via xset"
xset -dpms || true
xset s off || true
xset s noblank || true
else
log "X11/xset not detected or DISPLAY not set; skipping xset"
fi
}
disable_gnome_idle() {
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
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 "$@"