mirror of
https://github.com/kuhyx/scripts.git
synced 2026-07-04 13:03:05 +02:00
fix: shellcheck issues
This commit is contained in:
parent
2c46984c61
commit
795f56023e
@ -2,17 +2,20 @@
|
|||||||
# Lightweight GPU detection script.
|
# Lightweight GPU detection script.
|
||||||
# Detects GPU vendor and invokes the corresponding vendor install/management script.
|
# Detects GPU vendor and invokes the corresponding vendor install/management script.
|
||||||
# Exports: GPU_VENDOR
|
# Exports: GPU_VENDOR
|
||||||
|
# shellcheck source=./install_nvidia_driver.sh
|
||||||
|
# shellcheck source=./install_amd_driver.sh
|
||||||
|
# shellcheck source=./install_intel_driver.sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
GPU_VENDOR="unknown"
|
GPU_VENDOR="unknown"
|
||||||
PCI_GPU_INFO=$(lspci -nn | grep -Ei 'vga|3d|display' || true)
|
PCI_GPU_INFO=$(lspci -nn | grep -Ei 'vga|3d|display' || true)
|
||||||
|
|
||||||
if echo "$PCI_GPU_INFO" | grep -qi nvidia; then
|
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
|
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
|
elif echo "$PCI_GPU_INFO" | grep -qi intel; then
|
||||||
GPU_VENDOR="intel"
|
GPU_VENDOR="intel"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export GPU_VENDOR
|
export GPU_VENDOR
|
||||||
@ -21,6 +24,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
case "$GPU_VENDOR" in
|
case "$GPU_VENDOR" in
|
||||||
nvidia)
|
nvidia)
|
||||||
if [ -x "$SCRIPT_DIR/install_nvidia_driver.sh" ]; then
|
if [ -x "$SCRIPT_DIR/install_nvidia_driver.sh" ]; then
|
||||||
|
# shellcheck source=./install_nvidia_driver.sh disable=SC1091
|
||||||
. "$SCRIPT_DIR/install_nvidia_driver.sh"
|
. "$SCRIPT_DIR/install_nvidia_driver.sh"
|
||||||
else
|
else
|
||||||
echo "NVIDIA installer script missing: $SCRIPT_DIR/install_nvidia_driver.sh"
|
echo "NVIDIA installer script missing: $SCRIPT_DIR/install_nvidia_driver.sh"
|
||||||
@ -28,6 +32,7 @@ case "$GPU_VENDOR" in
|
|||||||
;;
|
;;
|
||||||
amd)
|
amd)
|
||||||
if [ -x "$SCRIPT_DIR/install_amd_driver.sh" ]; then
|
if [ -x "$SCRIPT_DIR/install_amd_driver.sh" ]; then
|
||||||
|
# shellcheck source=./install_amd_driver.sh disable=SC1091
|
||||||
. "$SCRIPT_DIR/install_amd_driver.sh"
|
. "$SCRIPT_DIR/install_amd_driver.sh"
|
||||||
else
|
else
|
||||||
echo "AMD installer script missing: $SCRIPT_DIR/install_amd_driver.sh (placeholder)"
|
echo "AMD installer script missing: $SCRIPT_DIR/install_amd_driver.sh (placeholder)"
|
||||||
@ -35,6 +40,7 @@ case "$GPU_VENDOR" in
|
|||||||
;;
|
;;
|
||||||
intel)
|
intel)
|
||||||
if [ -x "$SCRIPT_DIR/install_intel_driver.sh" ]; then
|
if [ -x "$SCRIPT_DIR/install_intel_driver.sh" ]; then
|
||||||
|
# shellcheck source=./install_intel_driver.sh disable=SC1091
|
||||||
. "$SCRIPT_DIR/install_intel_driver.sh"
|
. "$SCRIPT_DIR/install_intel_driver.sh"
|
||||||
else
|
else
|
||||||
echo "Intel installer script missing: $SCRIPT_DIR/install_intel_driver.sh"
|
echo "Intel installer script missing: $SCRIPT_DIR/install_intel_driver.sh"
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Backwards compatibility wrapper; prefer using detect_gpu.sh directly.
|
# Backwards compatibility wrapper; prefer using detect_gpu.sh directly.
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=./detect_gpu.sh disable=SC1091
|
||||||
. "$SCRIPT_DIR/detect_gpu.sh"
|
. "$SCRIPT_DIR/detect_gpu.sh"
|
||||||
|
|||||||
@ -12,7 +12,10 @@
|
|||||||
# AMD_VERBOSE=1 # verbose output
|
# AMD_VERBOSE=1 # verbose output
|
||||||
set -e
|
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_XF86=${AMD_INSTALL_XF86:-0}
|
||||||
AMD_INSTALL_AMDVLK=${AMD_INSTALL_AMDVLK:-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)
|
# Simple AUR builder (reused from NVIDIA script style)
|
||||||
_build_aur_pkg() {
|
_build_aur_pkg() {
|
||||||
local pkg="$1" url="https://aur.archlinux.org/${pkg}.git"
|
local pkg="$1"
|
||||||
mkdir -p "$HOME/aur"; cd "$HOME/aur"
|
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
|
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
|
yes | makepkg -s -c -C --noconfirm --needed
|
||||||
local built=( *.pkg.tar.zst )
|
local built=(*.pkg.tar.zst)
|
||||||
yes | sudo pacman -U --noconfirm "${built[@]}"
|
yes | sudo pacman -U --noconfirm "${built[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
_install_repo_or_aur() {
|
_install_repo_or_aur() {
|
||||||
local pkg="$1"
|
local pkg="$1"
|
||||||
if pacman -Si "$pkg" >/dev/null 2>&1; then
|
if pacman -Si "$pkg" > /dev/null 2>&1; then
|
||||||
if pacman -Qi "$pkg" >/dev/null 2>&1; then
|
if pacman -Qi "$pkg" > /dev/null 2>&1; then
|
||||||
vlog "$pkg already installed"
|
vlog "$pkg already installed"
|
||||||
else
|
else
|
||||||
yes | sudo pacman -Sy --noconfirm "$pkg"
|
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)
|
GPU_LINES=$(lspci -nn | grep -Ei 'vga|3d|display' | grep -iE 'amd|ati' || true)
|
||||||
SI_NAMES=(Tahiti Pitcairn Cape Verde Oland Hainan Curacao)
|
SI_NAMES=(Tahiti Pitcairn Cape Verde Oland Hainan Curacao)
|
||||||
CIK_NAMES=(Bonaire Hawaii Kabini Kaveri Mullins Temash Spectre Spooky)
|
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 "${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
|
for n in "${CIK_NAMES[@]}"; do echo "$GPU_LINES" | grep -q "$n" && IS_CIK=1 && break; done
|
||||||
|
|
||||||
@ -135,7 +142,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check active kernel driver
|
# 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}')
|
[ -z "$KDRV" ] && KDRV=$(lsmod | grep -E 'amdgpu|radeon' | head -n1 | awk '{print $1}')
|
||||||
info "Kernel driver in use: ${KDRV:-unknown}"
|
info "Kernel driver in use: ${KDRV:-unknown}"
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,10 @@
|
|||||||
# INTEL_VERBOSE=0/1 # verbose logging
|
# INTEL_VERBOSE=0/1 # verbose logging
|
||||||
set -e
|
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_USE_AMBER=${INTEL_USE_AMBER:-0}
|
||||||
INTEL_INSTALL_LIB32=${INTEL_INSTALL_LIB32:-auto}
|
INTEL_INSTALL_LIB32=${INTEL_INSTALL_LIB32:-auto}
|
||||||
@ -41,10 +44,10 @@ fi
|
|||||||
|
|
||||||
install_pkg() {
|
install_pkg() {
|
||||||
local pkg="$1"
|
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"
|
vlog "$pkg already installed"
|
||||||
else
|
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"
|
yes | sudo pacman -Sy --noconfirm "$pkg"
|
||||||
else
|
else
|
||||||
warn "Package $pkg not found in repos (not handling AUR here)"
|
warn "Package $pkg not found in repos (not handling AUR here)"
|
||||||
@ -86,7 +89,7 @@ if [ -n "$INTEL_ENABLE_GUC" ]; then
|
|||||||
else
|
else
|
||||||
info "Configuring enable_guc=$INTEL_ENABLE_GUC"
|
info "Configuring enable_guc=$INTEL_ENABLE_GUC"
|
||||||
sudo mkdir -p /etc/modprobe.d
|
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
|
if [ "$INTEL_SKIP_INITRAMFS" != 1 ] && [ -f /etc/mkinitcpio.conf ]; then
|
||||||
info "Regenerating initramfs (mkinitcpio -P) for GuC/HuC change"
|
info "Regenerating initramfs (mkinitcpio -P) for GuC/HuC change"
|
||||||
sudo mkinitcpio -P || warn "mkinitcpio failed; continue manually"
|
sudo mkinitcpio -P || warn "mkinitcpio failed; continue manually"
|
||||||
@ -97,7 +100,7 @@ if [ -n "$INTEL_ENABLE_GUC" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Report kernel driver
|
# 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}')
|
[ -z "$KDRV" ] && KDRV=$(lsmod | grep -E 'i915|xe' | head -n1 | awk '{print $1}')
|
||||||
info "Kernel driver in use: ${KDRV:-unknown}"
|
info "Kernel driver in use: ${KDRV:-unknown}"
|
||||||
|
|
||||||
|
|||||||
@ -4,15 +4,22 @@
|
|||||||
# Outputs: NVIDIA_DRIVER_PACKAGE
|
# Outputs: NVIDIA_DRIVER_PACKAGE
|
||||||
set -e
|
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() {
|
_build_aur_pkg() {
|
||||||
local pkg="$1"; local repo_url="https://aur.archlinux.org/${pkg}.git";
|
local pkg="$1"
|
||||||
mkdir -p "$HOME/aur"; cd "$HOME/aur";
|
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
|
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
|
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() {
|
_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
|
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)
|
# 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 [ -z "${NVIDIA_SKIP_DETECT:-}" ] && ! command -v nvidia-detect > /dev/null 2>&1; then
|
||||||
if pacman -Si 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
|
echo "Attempting to install helper utility: nvidia-detect" >&2
|
||||||
# Use --needed to avoid forcing refresh (& avoid partial upgrade semantics with -Sy)
|
# 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
|
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
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v nvidia-detect >/dev/null 2>&1; then
|
if command -v nvidia-detect > /dev/null 2>&1; then
|
||||||
detect_out="$(nvidia-detect 2>/dev/null || true)"
|
detect_out="$(nvidia-detect 2> /dev/null || true)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$detect_out" ]; then
|
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 '470'; then
|
||||||
if echo "$detect_out" | grep -q '390'; then driver_pkg='nvidia-390xx-dkms'; legacy_detected=1; fi
|
driver_pkg='nvidia-470xx-dkms'
|
||||||
if echo "$detect_out" | grep -q '340'; then driver_pkg='nvidia-340xx-dkms'; legacy_detected=1; fi
|
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
|
fi
|
||||||
|
|
||||||
if [ "$legacy_detected" = 0 ]; then
|
if [ "$legacy_detected" = 0 ]; then
|
||||||
# Heuristic modern driver selection
|
# Heuristic modern driver selection
|
||||||
if [ "$multiple_kernels" = 1 ]; then
|
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
|
else
|
||||||
if [ "$have_linux_lts" = 1 ] && [ "$have_linux" = 0 ]; then
|
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
|
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
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
@ -62,21 +78,22 @@ _choose_nvidia_pkg() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_remove_conflicting_nvidia_pkgs() {
|
_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=()
|
local to_remove=()
|
||||||
for p in "${candidates[@]}"; do
|
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
|
done
|
||||||
if [ ${#to_remove[@]} -gt 0 ]; then yes | sudo pacman -Rns --noconfirm "${to_remove[@]}" || true; fi
|
if [ ${#to_remove[@]} -gt 0 ]; then yes | sudo pacman -Rns --noconfirm "${to_remove[@]}" || true; fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_install_nvidia_stack() {
|
_install_nvidia_stack() {
|
||||||
local driver_pkg="$1"
|
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"
|
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 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
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,119 +1,124 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck source=./detect_gpu.sh
|
||||||
|
# shellcheck source=./detect_gpu_and_install.sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Function to play a sound on error
|
# Function to play a sound on error
|
||||||
play_error_sound() {
|
play_error_sound() {
|
||||||
#pactl set-sink-volume @DEFAULT_SINK@ +50%
|
#pactl set-sink-volume @DEFAULT_SINK@ +50%
|
||||||
for i in 1 2 3; do
|
for _ in 1 2 3; do
|
||||||
paplay /usr/share/sounds/freedesktop/stereo/dialog-error.oga
|
paplay /usr/share/sounds/freedesktop/stereo/dialog-error.oga
|
||||||
done
|
done
|
||||||
#pactl set-sink-volume @DEFAULT_SINK@ -50%
|
#pactl set-sink-volume @DEFAULT_SINK@ -50%
|
||||||
}
|
}
|
||||||
|
|
||||||
# Trap errors and call the play_error_sound function
|
# Trap errors and call the play_error_sound function
|
||||||
trap 'play_error_sound' ERR
|
trap 'play_error_sound' ERR
|
||||||
|
|
||||||
|
|
||||||
sudo -v
|
sudo -v
|
||||||
git config --global init.defaultBranch main
|
git config --global init.defaultBranch main
|
||||||
|
|
||||||
# GPU detection (now split vendor-specific logic)
|
# GPU detection (now split vendor-specific logic)
|
||||||
if [ -f "./detect_gpu.sh" ]; then
|
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
|
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
|
else
|
||||||
echo "GPU detection scripts not found; continuing without GPU specific installation."
|
echo "GPU detection scripts not found; continuing without GPU specific installation."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
install_from_aur() {
|
install_from_aur() {
|
||||||
if [ ! -d "$HOME/aur" ]; then
|
local repo_url pkg_name repo_dir
|
||||||
mkdir -p "$HOME/aur"
|
repo_url="$1"
|
||||||
fi
|
pkg_name="$2"
|
||||||
cd "$HOME/aur"
|
|
||||||
local repo_url=$1
|
|
||||||
local pkg_name=$2
|
|
||||||
local repo_dir="$(basename "$repo_url" .git)"
|
|
||||||
|
|
||||||
if [ ! -d "$repo_dir" ]; then
|
mkdir -p "$HOME/aur"
|
||||||
git clone "$repo_url"
|
cd "$HOME/aur" || return 1
|
||||||
else
|
repo_dir="$(basename "$repo_url" .git)"
|
||||||
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"
|
|
||||||
|
|
||||||
if pacman -Qi "$pkg_name" >/dev/null 2>&1; then
|
if [ ! -d "$repo_dir" ]; then
|
||||||
echo "$pkg_name is already installed"
|
git clone "$repo_url"
|
||||||
return 0
|
else
|
||||||
fi
|
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"
|
if pacman -Qi "$pkg_name" > /dev/null 2>&1; then
|
||||||
find . -maxdepth 1 -type f -name '*.pkg.tar.*' -delete 2>/dev/null || true
|
echo "$pkg_name is already installed"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Building $pkg_name (clean build)"
|
echo "Cleaning old package artifacts to avoid duplicate -U targets"
|
||||||
# -c (clean up work dirs after) -C (clean build - remove src/ and pkg/ first)
|
find . -maxdepth 1 -type f -name '*.pkg.tar.*' -delete 2> /dev/null || true
|
||||||
if ! yes | makepkg -s -c -C --noconfirm --nocheck --skipchecksums --skipinteg --skippgpcheck --needed; then
|
|
||||||
echo "Build failed for $pkg_name" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Collect only the freshly built packages (should now be only current version)
|
echo "Building $pkg_name (clean build)"
|
||||||
mapfile -t built_pkgs < <(ls -1 *.pkg.tar.zst 2>/dev/null || true)
|
# -c (clean up work dirs after) -C (clean build - remove src/ and pkg/ first)
|
||||||
if [ ${#built_pkgs[@]} -eq 0 ]; then
|
if ! yes | makepkg -s -c -C --noconfirm --nocheck --skipchecksums --skipinteg --skippgpcheck --needed; then
|
||||||
echo "No package files produced for $pkg_name" >&2
|
echo "Build failed for $pkg_name" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Installing built package(s): ${built_pkgs[*]}"
|
# Collect only the freshly built packages (should now be only current version)
|
||||||
if ! yes | sudo pacman -U --noconfirm "${built_pkgs[@]}"; then
|
mapfile -t built_pkgs < <(find . -maxdepth 1 -type f -name '*.pkg.tar.zst' -printf './%f\n')
|
||||||
echo "Installation failed for $pkg_name" >&2
|
if [ ${#built_pkgs[@]} -eq 0 ]; then
|
||||||
return 1
|
echo "No package files produced for $pkg_name" >&2
|
||||||
fi
|
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() {
|
process_packages() {
|
||||||
local file_path=$1
|
local file_path
|
||||||
> failed.txt
|
file_path="$1"
|
||||||
> done.txt
|
: > failed.txt
|
||||||
|
: > done.txt
|
||||||
|
|
||||||
while IFS= read -r pkg_name; do
|
while IFS= read -r pkg_name; do
|
||||||
if [ -z "$pkg_name" ]; then
|
if [ -z "$pkg_name" ]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local repo_url="https://aur.archlinux.org/${pkg_name}-git.git"
|
local repo_url repo_dir
|
||||||
local repo_dir="${pkg_name}-git"
|
repo_url="https://aur.archlinux.org/${pkg_name}-git.git"
|
||||||
|
repo_dir="${pkg_name}-git"
|
||||||
|
|
||||||
git clone $repo_url
|
git clone "$repo_url"
|
||||||
if [ -d "$repo_dir" ] && [ -z "$(ls -A $repo_dir)" ]; then
|
if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then
|
||||||
echo "Repository $repo_dir is empty, trying without -git suffix"
|
echo "Repository $repo_dir is empty, trying without -git suffix"
|
||||||
repo_url="https://aur.archlinux.org/${pkg_name}.git"
|
repo_url="https://aur.archlinux.org/${pkg_name}.git"
|
||||||
repo_dir="${pkg_name}"
|
repo_dir="${pkg_name}"
|
||||||
|
|
||||||
git clone $repo_url
|
git clone "$repo_url"
|
||||||
if [ -d "$repo_dir" ] && [ -z "$(ls -A $repo_dir)" ]; then
|
if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then
|
||||||
echo "Repository $repo_dir is empty, trying to install with pacman"
|
echo "Repository $repo_dir is empty, trying to install with pacman"
|
||||||
if sudo pacman -Sy --noconfirm $pkg_name; then
|
if sudo pacman -Sy --noconfirm "$pkg_name"; then
|
||||||
echo "$pkg_name" >> done.txt
|
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
|
|
||||||
else
|
else
|
||||||
if install_from_aur $repo_url $pkg_name; then
|
echo "$pkg_name" >> failed.txt
|
||||||
echo "$pkg_name" >> done.txt
|
|
||||||
else
|
|
||||||
echo "$pkg_name" >> failed.txt
|
|
||||||
fi
|
|
||||||
fi
|
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
|
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
|
# sudo cp ./mkinitcpio.conf /etc/mkinitcpio.conf
|
||||||
# mkinitcpio -P
|
# mkinitcpio -P
|
||||||
# Reflector install / service management (idempotent & resilient)
|
# Reflector install / service management (idempotent & resilient)
|
||||||
if pacman -Qi reflector >/dev/null 2>&1; then
|
if pacman -Qi reflector > /dev/null 2>&1; then
|
||||||
echo "reflector already installed"
|
echo "reflector already installed"
|
||||||
else
|
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
|
fi
|
||||||
# Prefer timer over service (Arch default)
|
# Prefer timer over service (Arch default)
|
||||||
if systemctl list-unit-files | grep -q '^reflector.timer'; then
|
if systemctl list-unit-files | grep -q '^reflector.timer'; then
|
||||||
if systemctl is-enabled reflector.timer >/dev/null 2>&1; then
|
if systemctl is-enabled reflector.timer > /dev/null 2>&1; then
|
||||||
echo "reflector.timer already enabled"
|
echo "reflector.timer already enabled"
|
||||||
else
|
else
|
||||||
sudo systemctl enable reflector.timer || echo "Warning: could not enable reflector.timer"
|
sudo systemctl enable reflector.timer || echo "Warning: could not enable reflector.timer"
|
||||||
fi
|
fi
|
||||||
if systemctl is-active reflector.timer >/dev/null 2>&1; then
|
if systemctl is-active reflector.timer > /dev/null 2>&1; then
|
||||||
echo "reflector.timer already active"
|
echo "reflector.timer already active"
|
||||||
else
|
else
|
||||||
if ! sudo systemctl start reflector.timer; then
|
if ! sudo systemctl start reflector.timer; then
|
||||||
echo "Warning: failed to start reflector.timer (check: systemctl status reflector.timer; journalctl -xeu reflector.timer)"
|
echo "Warning: failed to start reflector.timer (check: systemctl status reflector.timer; journalctl -xeu reflector.timer)"
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
elif systemctl list-unit-files | grep -q '^reflector.service'; then
|
elif systemctl list-unit-files | grep -q '^reflector.service'; then
|
||||||
if systemctl is-enabled reflector.service >/dev/null 2>&1; then
|
if systemctl is-enabled reflector.service > /dev/null 2>&1; then
|
||||||
echo "reflector.service already enabled"
|
echo "reflector.service already enabled"
|
||||||
else
|
else
|
||||||
sudo systemctl enable reflector.service || echo "Warning: could not enable reflector.service"
|
sudo systemctl enable reflector.service || echo "Warning: could not enable reflector.service"
|
||||||
fi
|
fi
|
||||||
if systemctl is-active reflector.service >/dev/null 2>&1; then
|
if systemctl is-active reflector.service > /dev/null 2>&1; then
|
||||||
echo "reflector.service already running"
|
echo "reflector.service already running"
|
||||||
else
|
else
|
||||||
if ! sudo systemctl start reflector.service; then
|
if ! sudo systemctl start reflector.service; then
|
||||||
echo "Warning: failed to start reflector.service (check: systemctl status reflector.service; journalctl -xeu reflector.service)"
|
echo "Warning: failed to start reflector.service (check: systemctl status reflector.service; journalctl -xeu reflector.service)"
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "reflector systemd unit not found (neither timer nor service)"
|
echo "reflector systemd unit not found (neither timer nor service)"
|
||||||
fi
|
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
|
# Read pacman packages from file
|
||||||
declare -a pacman_packages
|
declare -a pacman_packages
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
# Skip empty lines and comments (lines not starting with alphanumeric characters)
|
# Skip empty lines and comments (lines not starting with alphanumeric characters)
|
||||||
if [[ -n "$line" && "$line" =~ ^[a-z0-9] ]]; then
|
if [[ -n $line && $line =~ ^[a-z0-9] ]]; then
|
||||||
pacman_packages+=("$line")
|
pacman_packages+=("$line")
|
||||||
fi
|
fi
|
||||||
done < "pacman_packages.txt"
|
done < "pacman_packages.txt"
|
||||||
|
|
||||||
for pkg in "${pacman_packages[@]}"; do
|
for pkg in "${pacman_packages[@]}"; do
|
||||||
# Skip NVIDIA packages if GPU is not NVIDIA
|
# Skip NVIDIA packages if GPU is not NVIDIA
|
||||||
if [ "$GPU_VENDOR" != "nvidia" ] && { [ "$pkg" = "nvidia" ] || [ "$pkg" = "nvidia-utils" ] || [ "$pkg" = "lib32-nvidia-utils" ]; }; then
|
if [ "$GPU_VENDOR" != "nvidia" ] && { [ "$pkg" = "nvidia" ] || [ "$pkg" = "nvidia-utils" ] || [ "$pkg" = "lib32-nvidia-utils" ]; }; then
|
||||||
echo "Skipping $pkg (GPU vendor: $GPU_VENDOR)"
|
echo "Skipping $pkg (GPU vendor: $GPU_VENDOR)"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
# Check for texlive subpackages
|
# Check for texlive subpackages
|
||||||
if [ "$pkg" == "texlive" ]; then
|
if [ "$pkg" == "texlive" ]; then
|
||||||
sub_pkgs=(
|
sub_pkgs=(
|
||||||
texlive-basic texlive-bibtexextra texlive-binextra texlive-context texlive-fontsextra
|
texlive-basic texlive-bibtexextra texlive-binextra texlive-context texlive-fontsextra
|
||||||
texlive-fontsrecommended texlive-fontutils texlive-formatsextra texlive-games texlive-humanities
|
texlive-fontsrecommended texlive-fontutils texlive-formatsextra texlive-games texlive-humanities
|
||||||
texlive-latex texlive-latexextra texlive-latexrecommended texlive-luatex texlive-mathscience
|
texlive-latex texlive-latexextra texlive-latexrecommended texlive-luatex texlive-mathscience
|
||||||
texlive-metapost texlive-music texlive-pictures texlive-plaingeneric texlive-pstricks
|
texlive-metapost texlive-music texlive-pictures texlive-plaingeneric texlive-pstricks
|
||||||
texlive-publishers texlive-xetex
|
texlive-publishers texlive-xetex
|
||||||
)
|
)
|
||||||
all_installed=true
|
all_installed=true
|
||||||
for subpkg in "${sub_pkgs[@]}"; do
|
for subpkg in "${sub_pkgs[@]}"; do
|
||||||
if ! pacman -Qi "$subpkg" &> /dev/null; then
|
if ! pacman -Qi "$subpkg" &> /dev/null; then
|
||||||
all_installed=false
|
all_installed=false
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ "$all_installed" = true ]; then
|
if [ "$all_installed" = true ]; then
|
||||||
echo "All texlive subpackages are installed, skipping texlive"
|
echo "All texlive subpackages are installed, skipping texlive"
|
||||||
continue
|
continue
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Check for texlive-lang subpackages
|
# Check for texlive-lang subpackages
|
||||||
if [ "$pkg" == "texlive-lang" ]; then
|
if [ "$pkg" == "texlive-lang" ]; then
|
||||||
sub_pkgs=(
|
sub_pkgs=(
|
||||||
texlive-langarabic texlive-langchinese texlive-langcjk texlive-langcyrillic
|
texlive-langarabic texlive-langchinese texlive-langcjk texlive-langcyrillic
|
||||||
texlive-langczechslovak texlive-langenglish texlive-langeuropean texlive-langfrench
|
texlive-langczechslovak texlive-langenglish texlive-langeuropean texlive-langfrench
|
||||||
texlive-langgerman texlive-langgreek texlive-langitalian texlive-langjapanese
|
texlive-langgerman texlive-langgreek texlive-langitalian texlive-langjapanese
|
||||||
texlive-langkorean texlive-langother texlive-langpolish texlive-langportuguese
|
texlive-langkorean texlive-langother texlive-langpolish texlive-langportuguese
|
||||||
texlive-langspanish
|
texlive-langspanish
|
||||||
)
|
)
|
||||||
all_installed=true
|
all_installed=true
|
||||||
for subpkg in "${sub_pkgs[@]}"; do
|
for subpkg in "${sub_pkgs[@]}"; do
|
||||||
if ! pacman -Qi "$subpkg" &> /dev/null; then
|
if ! pacman -Qi "$subpkg" &> /dev/null; then
|
||||||
all_installed=false
|
all_installed=false
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ "$all_installed" = true ]; then
|
if [ "$all_installed" = true ]; then
|
||||||
echo "All texlive-lang subpackages are installed, skipping texlive-lang"
|
echo "All texlive-lang subpackages are installed, skipping texlive-lang"
|
||||||
continue
|
continue
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if ! pacman -Qi "$pkg" &> /dev/null; then
|
if ! pacman -Qi "$pkg" &> /dev/null; then
|
||||||
if ! echo "${aur_packages[@]}" | grep -q "$pkg"; then
|
if ! printf '%s
|
||||||
yes | sudo pacman -Sy --noconfirm "$pkg"
|
' "${aur_package_names[@]}" | grep -Fxq "$pkg"; then
|
||||||
else
|
yes | sudo pacman -Sy --noconfirm "$pkg"
|
||||||
echo "$pkg exists in AUR packages, skipping pacman installation"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo "$pkg is already installed"
|
echo "$pkg exists in AUR packages, skipping pacman installation"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
echo "$pkg is already installed"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
if ! command -v nvm &> /dev/null; then
|
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
|
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
|
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 enable bluetooth.service
|
||||||
sudo systemctl start 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
|
for entry in "${aur_packages[@]}"; do
|
||||||
pkg_name=$(echo "$entry" | cut -d' ' -f1)
|
pkg_name=${entry%% *}
|
||||||
repo_url=$(echo "$entry" | cut -d' ' -f2)
|
repo_url=${entry#* }
|
||||||
install_from_aur "$repo_url" "$pkg_name"
|
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
|
done
|
||||||
|
|
||||||
cd ~/linux-configuration/fresh-install
|
cd ~/linux-configuration/fresh-install
|
||||||
if [ ! -d "$HOME/.config/mpv" ]; then
|
if [ ! -d "$HOME/.config/mpv" ]; then
|
||||||
mkdir -p "$HOME/.config/mpv"
|
mkdir -p "$HOME/.config/mpv"
|
||||||
fi
|
fi
|
||||||
cp mpv.conf "$HOME/.config/mpv/mpv.conf"
|
cp mpv.conf "$HOME/.config/mpv/mpv.conf"
|
||||||
|
|
||||||
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
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
|
else
|
||||||
echo "Oh My Zsh is already installed"
|
echo "Oh My Zsh is already installed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd ~/linux-configuration
|
cd ~/linux-configuration
|
||||||
sudo hosts/install.sh
|
sudo hosts/install.sh
|
||||||
i3-configuration/install.sh
|
i3-configuration/install.sh
|
||||||
scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh
|
scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh
|
||||||
scripts/fixes/nvidia_troubleshoot.sh
|
scripts/fixes/nvidia_troubleshoot.sh
|
||||||
sudo scripts/features/setup_activitywatch.sh
|
sudo scripts/features/setup_activitywatch.sh
|
||||||
sudo scripts/utils/setup_media_organizer.sh
|
sudo scripts/utils/setup_media_organizer.sh
|
||||||
sudo scripts/digital_wellbeing/setup_pc_startup_monitor.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
|
sudo scripts/setup_thorium_startup.sh
|
||||||
yes | protonup
|
yes | protonup
|
||||||
yes | sudo pacman -Syuu
|
yes | sudo pacman -Syuu
|
||||||
|
|
||||||
#cd unreal-engine
|
#cd unreal-engine
|
||||||
## gh auth login
|
## gh auth login
|
||||||
|
|||||||
@ -12,11 +12,11 @@
|
|||||||
#-- The download utilities that makepkg should use to acquire sources
|
#-- The download utilities that makepkg should use to acquire sources
|
||||||
# Format: 'protocol::agent'
|
# Format: 'protocol::agent'
|
||||||
DLAGENTS=('file::/usr/bin/curl -qgC - -o %o %u'
|
DLAGENTS=('file::/usr/bin/curl -qgC - -o %o %u'
|
||||||
'ftp::/usr/bin/curl -qgfC - --ftp-pasv --retry 3 --retry-delay 3 -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'
|
'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'
|
'https::/usr/bin/curl -qgb "" -fLC - --retry 3 --retry-delay 3 -o %o %u'
|
||||||
'rsync::/usr/bin/rsync --no-motd -z %u %o'
|
'rsync::/usr/bin/rsync --no-motd -z %u %o'
|
||||||
'scp::/usr/bin/scp -C %u %o')
|
'scp::/usr/bin/scp -C %u %o')
|
||||||
|
|
||||||
# Other common tools:
|
# Other common tools:
|
||||||
# /usr/bin/snarf
|
# /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
|
#-- The package required by makepkg to download VCS sources
|
||||||
# Format: 'protocol::package'
|
# Format: 'protocol::package'
|
||||||
VCSCLIENTS=('bzr::breezy'
|
VCSCLIENTS=('bzr::breezy'
|
||||||
'fossil::fossil'
|
'fossil::fossil'
|
||||||
'git::git'
|
'git::git'
|
||||||
'hg::mercurial'
|
'hg::mercurial'
|
||||||
'svn::subversion')
|
'svn::subversion')
|
||||||
|
|
||||||
#########################################################################
|
#########################################################################
|
||||||
# ARCHITECTURE, COMPILE FLAGS
|
# ARCHITECTURE, COMPILE FLAGS
|
||||||
|
|||||||
@ -9,24 +9,24 @@ TARGET="/etc/hosts"
|
|||||||
LOG_FILE="/var/log/hosts-guard.log"
|
LOG_FILE="/var/log/hosts-guard.log"
|
||||||
|
|
||||||
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
|
if [[ ! -f $CANONICAL_SOURCE ]]; then
|
||||||
log "Canonical hosts not found at $CANONICAL_SOURCE; aborting enforcement"
|
log "Canonical hosts not found at $CANONICAL_SOURCE; aborting enforcement"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! cmp -s "$CANONICAL_SOURCE" "$TARGET"; then
|
if ! cmp -s "$CANONICAL_SOURCE" "$TARGET"; then
|
||||||
log "Difference detected – restoring $TARGET from canonical copy"
|
log "Difference detected – restoring $TARGET from canonical copy"
|
||||||
cp "$CANONICAL_SOURCE" "$TARGET"
|
cp "$CANONICAL_SOURCE" "$TARGET"
|
||||||
chmod 644 "$TARGET"
|
chmod 644 "$TARGET"
|
||||||
else
|
else
|
||||||
log "No drift detected (contents identical)"
|
log "No drift detected (contents identical)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Re-apply protective attributes: immutable first, then read-only bind mount handled by separate unit
|
# 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"
|
chattr +i "$TARGET" || log "Failed to set immutable attribute"
|
||||||
|
|
||||||
log "Enforcement complete"
|
log "Enforcement complete"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
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 "$@"
|
require_root "$@"
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
@ -10,7 +10,7 @@ HOOKS_DIR="/etc/pacman.d/hooks"
|
|||||||
install -d -m 755 "$HOOKS_DIR"
|
install -d -m 755 "$HOOKS_DIR"
|
||||||
|
|
||||||
# Pre-transaction hook
|
# Pre-transaction hook
|
||||||
cat >"$HOOKS_DIR/10-unlock-etc-hosts.hook" <<'HOOK'
|
cat > "$HOOKS_DIR/10-unlock-etc-hosts.hook" << 'HOOK'
|
||||||
[Trigger]
|
[Trigger]
|
||||||
Operation = Upgrade
|
Operation = Upgrade
|
||||||
Operation = Install
|
Operation = Install
|
||||||
@ -26,7 +26,7 @@ NeedsTargets
|
|||||||
HOOK
|
HOOK
|
||||||
|
|
||||||
# Post-transaction hook
|
# Post-transaction hook
|
||||||
cat >"$HOOKS_DIR/90-relock-etc-hosts.hook" <<'HOOK'
|
cat > "$HOOKS_DIR/90-relock-etc-hosts.hook" << 'HOOK'
|
||||||
[Trigger]
|
[Trigger]
|
||||||
Operation = Upgrade
|
Operation = Upgrade
|
||||||
Operation = Install
|
Operation = Install
|
||||||
|
|||||||
@ -5,22 +5,22 @@ TARGET=/etc/hosts
|
|||||||
ENFORCE=/usr/local/sbin/enforce-hosts.sh
|
ENFORCE=/usr/local/sbin/enforce-hosts.sh
|
||||||
LOGTAG=hosts-guard-hook
|
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() {
|
collapse_mounts() {
|
||||||
local i=0
|
local i=0
|
||||||
if command -v mountpoint >/devnull 2>&1; then
|
if command -v mountpoint > /devnull 2>&1; then
|
||||||
while mountpoint -q "$TARGET"; do
|
while mountpoint -q "$TARGET"; do
|
||||||
umount -l "$TARGET" >/dev/null 2>&1 || break
|
umount -l "$TARGET" > /dev/null 2>&1 || break
|
||||||
i=$((i+1))
|
i=$((i + 1))
|
||||||
(( i > 20 )) && break
|
((i > 20)) && break
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
local cnt
|
local cnt
|
||||||
cnt=$(mount_layers_count)
|
cnt=$(mount_layers_count)
|
||||||
while (( cnt > 1 )); do
|
while ((cnt > 1)); do
|
||||||
umount -l "$TARGET" >/dev/null 2>&1 || break
|
umount -l "$TARGET" > /dev/null 2>&1 || break
|
||||||
i=$((i+1))
|
i=$((i + 1))
|
||||||
(( i > 20 )) && break
|
((i > 20)) && break
|
||||||
cnt=$(mount_layers_count)
|
cnt=$(mount_layers_count)
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
@ -28,23 +28,23 @@ collapse_mounts() {
|
|||||||
|
|
||||||
# Ensure we end with a single read-only bind mount layer
|
# Ensure we end with a single read-only bind mount layer
|
||||||
logger -t "$LOGTAG" "post: relocking /etc/hosts (starting)"
|
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
|
collapse_mounts
|
||||||
|
|
||||||
if [[ -x "$ENFORCE" ]]; then
|
if [[ -x $ENFORCE ]]; then
|
||||||
"$ENFORCE" >/dev/null 2>&1 || true
|
"$ENFORCE" > /dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Apply exactly one ro bind layer
|
# Apply exactly one ro bind layer
|
||||||
mount --bind "$TARGET" "$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
|
mount -o remount,ro,bind "$TARGET" > /dev/null 2>&1 || true
|
||||||
|
|
||||||
# Start only the path watcher; avoid bind-mount service (we already bound once)
|
# Start only the path watcher; avoid bind-mount service (we already bound once)
|
||||||
if command -v systemctl >/dev/null 2>&1; then
|
if command -v systemctl > /dev/null 2>&1; then
|
||||||
systemctl start hosts-guard.path >/dev/null 2>&1 || true
|
systemctl start hosts-guard.path > /dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
logger -t "$LOGTAG" "post: relocking /etc/hosts (done)"
|
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
|
exit 0
|
||||||
|
|||||||
@ -7,58 +7,57 @@ LOGTAG=hosts-guard-hook
|
|||||||
stop_units_if_present() {
|
stop_units_if_present() {
|
||||||
local units=(hosts-bind-mount.service hosts-guard.path)
|
local units=(hosts-bind-mount.service hosts-guard.path)
|
||||||
for u in "${units[@]}"; do
|
for u in "${units[@]}"; do
|
||||||
if command -v systemctl >/dev/null 2>&1; then
|
if command -v systemctl > /dev/null 2>&1; then
|
||||||
if systemctl list-unit-files 2>/dev/null | grep -q "^$u"; then
|
if systemctl list-unit-files 2> /dev/null | grep -q "^$u"; then
|
||||||
systemctl stop "$u" >/dev/null 2>&1 || true
|
systemctl stop "$u" > /dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
is_ro_mount() { findmnt -no OPTIONS -T "$TARGET" 2>/dev/null | grep -qw ro; }
|
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; }
|
|
||||||
|
|
||||||
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() {
|
cleanup_mount_stacks() {
|
||||||
local i=0
|
local i=0
|
||||||
# Unmount bind layers until /etc/hosts is no longer a mountpoint
|
# 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
|
while mountpoint -q "$TARGET"; do
|
||||||
umount -l "$TARGET" >/dev/null 2>&1 || break
|
umount -l "$TARGET" > /dev/null 2>&1 || break
|
||||||
i=$((i+1))
|
i=$((i + 1))
|
||||||
(( i > 20 )) && break
|
((i > 20)) && break
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
# Fallback to best-effort using mountinfo count
|
# Fallback to best-effort using mountinfo count
|
||||||
local cnt
|
local cnt
|
||||||
cnt=$(mount_layers_count)
|
cnt=$(mount_layers_count)
|
||||||
while (( cnt > 1 )); do
|
while ((cnt > 1)); do
|
||||||
umount -l "$TARGET" >/dev/null 2>&1 || break
|
umount -l "$TARGET" > /dev/null 2>&1 || break
|
||||||
i=$((i+1))
|
i=$((i + 1))
|
||||||
(( i > 20 )) && break
|
((i > 20)) && break
|
||||||
cnt=$(mount_layers_count)
|
cnt=$(mount_layers_count)
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Drop protective attributes if present
|
# Drop protective attributes if present
|
||||||
if command -v lsattr >/dev/null 2>&1; then
|
if command -v lsattr > /dev/null 2>&1; then
|
||||||
attrs=$(lsattr -d "$TARGET" 2>/dev/null || true)
|
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 " i " && chattr -i "$TARGET" > /dev/null 2>&1 || true
|
||||||
echo "$attrs" | grep -q " a " && chattr -a "$TARGET" >/dev/null 2>&1 || true
|
echo "$attrs" | grep -q " a " && chattr -a "$TARGET" > /dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
stop_units_if_present
|
stop_units_if_present
|
||||||
|
|
||||||
logger -t "$LOGTAG" "pre: unlocking /etc/hosts (starting)"
|
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
|
# Always collapse any existing layers; we'll operate on the plain file
|
||||||
cleanup_mount_stacks
|
cleanup_mount_stacks
|
||||||
|
|
||||||
# If someone managed a ro single-layer mount, ensure rw by remounting or collapsing again
|
# If someone managed a ro single-layer mount, ensure rw by remounting or collapsing again
|
||||||
if is_ro_mount; then
|
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
|
fi
|
||||||
|
|
||||||
logger -t "$LOGTAG" "pre: unlocking /etc/hosts (done)"
|
logger -t "$LOGTAG" "pre: unlocking /etc/hosts (done)"
|
||||||
|
|||||||
@ -11,36 +11,36 @@ DELAY_SECONDS=45
|
|||||||
|
|
||||||
log() { printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG" >&2; }
|
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 "$@"
|
require_root "$@"
|
||||||
|
|
||||||
echo "Reason for editing /etc/hosts (will be logged):" >&2
|
echo "Reason for editing /etc/hosts (will be logged):" >&2
|
||||||
read -r -p "Enter reason: " REASON
|
read -r -p "Enter reason: " REASON
|
||||||
if [[ -z ${REASON// } ]]; then
|
if [[ -z ${REASON// /} ]]; then
|
||||||
echo "Empty reason not allowed. Aborting." >&2
|
echo "Empty reason not allowed. Aborting." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
log "Requested intentional /etc/hosts modification session. Reason: $REASON"
|
log "Requested intentional /etc/hosts modification session. Reason: $REASON"
|
||||||
logger -t "$SYSLOG_TAG" "session_start user=${SUDO_USER:-$USER} 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
|
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
|
for s in hosts-bind-mount.service hosts-guard.path; do
|
||||||
if systemctl is-active --quiet "$s"; then
|
if systemctl is-active --quiet "$s"; then
|
||||||
log "Stopping $s"
|
log "Stopping $s"
|
||||||
systemctl stop "$s" || true
|
systemctl stop "$s" || true
|
||||||
fi
|
fi
|
||||||
if systemctl is-enabled --quiet "$s"; then
|
if systemctl is-enabled --quiet "$s"; then
|
||||||
log "(Will re-enable later)"
|
log "(Will re-enable later)"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Remove attributes to allow edit
|
# Remove attributes to allow edit
|
||||||
chattr -i -a "$TARGET" 2>/dev/null || true
|
chattr -i -a "$TARGET" 2> /dev/null || true
|
||||||
|
|
||||||
echo "Countdown:" >&2
|
echo "Countdown:" >&2
|
||||||
for ((i=DELAY_SECONDS; i>0; i--)); do
|
for ((i = DELAY_SECONDS; i > 0; i--)); do
|
||||||
printf '\rEdit window opens in %2d seconds... Press Ctrl+C to abort.' "$i" >&2
|
printf '\rEdit window opens in %2d seconds... Press Ctrl+C to abort.' "$i" >&2
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
echo >&2
|
echo >&2
|
||||||
|
|
||||||
@ -49,13 +49,13 @@ sha_before=$(sha256sum "$TARGET" | awk '{print $1}')
|
|||||||
"$EDITOR_CMD" "$TARGET"
|
"$EDITOR_CMD" "$TARGET"
|
||||||
sha_after=$(sha256sum "$TARGET" | awk '{print $1}')
|
sha_after=$(sha256sum "$TARGET" | awk '{print $1}')
|
||||||
|
|
||||||
if [[ "$sha_before" == "$sha_after" ]]; then
|
if [[ $sha_before == "$sha_after" ]]; then
|
||||||
log "No changes made to $TARGET. Reason: $REASON"
|
log "No changes made to $TARGET. Reason: $REASON"
|
||||||
logger -t "$SYSLOG_TAG" "no_change user=${SUDO_USER:-$USER} reason='$REASON'"
|
logger -t "$SYSLOG_TAG" "no_change user=${SUDO_USER:-$USER} reason='$REASON'"
|
||||||
else
|
else
|
||||||
log "Changes detected. Updating canonical copy and re-enforcing. Reason: $REASON"
|
log "Changes detected. Updating canonical copy and re-enforcing. Reason: $REASON"
|
||||||
logger -t "$SYSLOG_TAG" "modified user=${SUDO_USER:-$USER} reason='$REASON'"
|
logger -t "$SYSLOG_TAG" "modified user=${SUDO_USER:-$USER} reason='$REASON'"
|
||||||
cp "$TARGET" "$CANON"
|
cp "$TARGET" "$CANON"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Re-run enforcement
|
# Re-run enforcement
|
||||||
|
|||||||
@ -46,10 +46,20 @@ ADD_ALIAS_STUB=1
|
|||||||
msg() { printf '\e[1;32m[+]\e[0m %s\n' "$*"; }
|
msg() { printf '\e[1;32m[+]\e[0m %s\n' "$*"; }
|
||||||
note() { printf '\e[1;34m[i]\e[0m %s\n' "$*"; }
|
note() { printf '\e[1;34m[i]\e[0m %s\n' "$*"; }
|
||||||
warn() { printf '\e[1;33m[!]\e[0m %s\n' "$*"; }
|
warn() { printf '\e[1;33m[!]\e[0m %s\n' "$*"; }
|
||||||
err() { printf '\e[1;31m[x]\e[0m %s\n' "$*" >&2; }
|
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 }
|
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\}//'; }
|
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
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--force-snapshot) FORCE_SNAPSHOT=1 ; shift ;;
|
--force-snapshot)
|
||||||
--no-snapshot) DO_SNAPSHOT=0 ; shift ;;
|
FORCE_SNAPSHOT=1
|
||||||
--skip-bind) ENABLE_BIND=0 ; shift ;;
|
shift
|
||||||
--skip-path-watch) ENABLE_PATH=0 ; shift ;;
|
;;
|
||||||
--delay) DELAY=${2:-} ; [[ -z ${DELAY} ]] && { err '--delay requires value'; exit 2; } ; shift 2 ;;
|
--no-snapshot)
|
||||||
--dry-run) DRY_RUN=1 ; shift ;;
|
DO_SNAPSHOT=0
|
||||||
--no-shell-hooks) INSTALL_SHELL_HOOKS=0 ; shift ;;
|
shift
|
||||||
--shell-hooks) INSTALL_SHELL_HOOKS=1 ; shift ;;
|
;;
|
||||||
--no-audit) INSTALL_AUDIT_RULE=0 ; shift ;;
|
--skip-bind)
|
||||||
--audit) INSTALL_AUDIT_RULE=1 ; shift ;;
|
ENABLE_BIND=0
|
||||||
--no-alias-stub) ADD_ALIAS_STUB=0 ; shift ;;
|
shift
|
||||||
--alias-stub) ADD_ALIAS_STUB=1 ; shift ;;
|
;;
|
||||||
--uninstall) UNINSTALL=1 ; shift ;;
|
--skip-path-watch)
|
||||||
-h|--help) usage; exit 0 ;;
|
ENABLE_PATH=0
|
||||||
*) err "Unknown argument: $1"; usage; exit 2 ;;
|
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
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
@ -119,7 +179,7 @@ if [[ $UNINSTALL -eq 1 ]]; then
|
|||||||
"$SYSTEMD_DIR/hosts-bind-mount.service" \
|
"$SYSTEMD_DIR/hosts-bind-mount.service" \
|
||||||
"$ZSH_FILTER_SNIPPET" \
|
"$ZSH_FILTER_SNIPPET" \
|
||||||
"$BASH_FILTER_SNIPPET"; do
|
"$BASH_FILTER_SNIPPET"; do
|
||||||
if [[ -e $f ]]; then run rm -f "$f"; fi
|
if [[ -e $f ]]; then run rm -f "$f"; fi
|
||||||
done
|
done
|
||||||
note "Leaving canonical snapshot at $CANON (remove manually if undesired)."
|
note "Leaving canonical snapshot at $CANON (remove manually if undesired)."
|
||||||
if [[ $DRY_RUN -eq 0 ]]; then systemctl daemon-reload; fi
|
if [[ $DRY_RUN -eq 0 ]]; then systemctl daemon-reload; fi
|
||||||
@ -134,10 +194,13 @@ note "Script directory: $SCRIPT_DIR"
|
|||||||
note "Repository root: $REPO_ROOT"
|
note "Repository root: $REPO_ROOT"
|
||||||
|
|
||||||
for req in "$TEMPLATE_ENFORCE" "$TEMPLATE_UNLOCK" "$UNIT_GUARD_SERVICE"; do
|
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
|
done
|
||||||
|
|
||||||
if [[ ! -f "$HOSTS" ]]; then
|
if [[ ! -f $HOSTS ]]; then
|
||||||
err "$HOSTS does not exist. Run your hosts/install.sh first."
|
err "$HOSTS does not exist. Run your hosts/install.sh first."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@ -146,7 +209,7 @@ fi
|
|||||||
# Snapshot
|
# Snapshot
|
||||||
######################################################################
|
######################################################################
|
||||||
if [[ $DO_SNAPSHOT -eq 1 ]]; then
|
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)"
|
note "Canonical snapshot exists (use --force-snapshot to overwrite)"
|
||||||
else
|
else
|
||||||
msg "Creating canonical snapshot at $CANON"
|
msg "Creating canonical snapshot at $CANON"
|
||||||
@ -182,14 +245,12 @@ fi
|
|||||||
if [[ $INSTALL_SHELL_HOOKS -eq 1 ]]; then
|
if [[ $INSTALL_SHELL_HOOKS -eq 1 ]]; then
|
||||||
msg "Installing shell history suppression hooks for unlock command"
|
msg "Installing shell history suppression hooks for unlock command"
|
||||||
# Pattern matches commands invoking unlock-hosts (with or without sudo) & setup script force snapshot
|
# 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
|
# 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
|
if [[ $DRY_RUN -eq 1 ]]; then
|
||||||
echo "DRY-RUN: would create $ZSH_FILTER_SNIPPET"
|
echo "DRY-RUN: would create $ZSH_FILTER_SNIPPET"
|
||||||
else
|
else
|
||||||
cat > "$ZSH_FILTER_SNIPPET" <<'ZEOF'
|
cat > "$ZSH_FILTER_SNIPPET" << 'ZEOF'
|
||||||
# Added by hosts guard setup – suppress unlock-hosts commands from Zsh history
|
# Added by hosts guard setup – suppress unlock-hosts commands from Zsh history
|
||||||
autoload -Uz add-zsh-hook 2>/dev/null || true
|
autoload -Uz add-zsh-hook 2>/dev/null || true
|
||||||
_hosts_guard_history_filter() {
|
_hosts_guard_history_filter() {
|
||||||
@ -213,11 +274,11 @@ ZEOF
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Bash: rely on HISTCONTROL and PROMPT_COMMAND filter
|
# 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
|
if [[ $DRY_RUN -eq 1 ]]; then
|
||||||
echo "DRY-RUN: would create $BASH_FILTER_SNIPPET"
|
echo "DRY-RUN: would create $BASH_FILTER_SNIPPET"
|
||||||
else
|
else
|
||||||
cat > "$BASH_FILTER_SNIPPET" <<'BEOF'
|
cat > "$BASH_FILTER_SNIPPET" << 'BEOF'
|
||||||
# Added by hosts guard setup – suppress unlock-hosts commands from Bash history
|
# Added by hosts guard setup – suppress unlock-hosts commands from Bash history
|
||||||
export HISTCONTROL=ignoredups:erasedups
|
export HISTCONTROL=ignoredups:erasedups
|
||||||
_hosts_guard_hist_filter() {
|
_hosts_guard_hist_filter() {
|
||||||
@ -253,7 +314,7 @@ if [[ $ADD_ALIAS_STUB -eq 1 ]]; then
|
|||||||
if [[ $DRY_RUN -eq 1 ]]; then
|
if [[ $DRY_RUN -eq 1 ]]; then
|
||||||
echo "DRY-RUN: would create $PROFILE_STUB"
|
echo "DRY-RUN: would create $PROFILE_STUB"
|
||||||
else
|
else
|
||||||
cat > "$PROFILE_STUB" <<'ASTUB'
|
cat > "$PROFILE_STUB" << 'ASTUB'
|
||||||
# Added by hosts guard setup – discourages casual use of unlock-hosts name
|
# Added by hosts guard setup – discourages casual use of unlock-hosts name
|
||||||
if command -v unlock-hosts >/dev/null 2>&1; then
|
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)"'
|
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)
|
# Audit rule to record executions (requires auditd)
|
||||||
######################################################################
|
######################################################################
|
||||||
if [[ $INSTALL_AUDIT_RULE -eq 1 ]]; then
|
if [[ $INSTALL_AUDIT_RULE -eq 1 ]]; then
|
||||||
if command -v auditctl >/dev/null 2>&1; then
|
if command -v auditctl > /dev/null 2>&1; then
|
||||||
AUDIT_RULE="-w /usr/local/sbin/unlock-hosts -p x -k hosts_unlock"
|
audit_rule_str="-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
|
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"
|
note "Audit rule already present"
|
||||||
else
|
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
|
if [[ $DRY_RUN -eq 1 ]]; then
|
||||||
echo "DRY-RUN: would create /etc/audit/rules.d/hosts_unlock.rules"
|
echo "DRY-RUN: would create /etc/audit/rules.d/hosts_unlock.rules"
|
||||||
else
|
else
|
||||||
echo "$AUDIT_RULE" > /etc/audit/rules.d/hosts_unlock.rules
|
echo "$audit_rule_str" > /etc/audit/rules.d/hosts_unlock.rules
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
@ -323,22 +385,22 @@ fi
|
|||||||
######################################################################
|
######################################################################
|
||||||
echo
|
echo
|
||||||
msg "Hosts guard setup complete"
|
msg "Hosts guard setup complete"
|
||||||
echo "Canonical copy: $CANON"
|
echo "Canonical copy: $CANON"
|
||||||
echo "Enforce script: $INSTALL_ENFORCE"
|
echo "Enforce script: $INSTALL_ENFORCE"
|
||||||
echo "Unlock command: sudo $INSTALL_UNLOCK"
|
echo "Unlock command: sudo $INSTALL_UNLOCK"
|
||||||
echo "Delay (seconds): $DELAY"
|
echo "Delay (seconds): $DELAY"
|
||||||
echo "Auto-revert path watch: $([[ $ENABLE_PATH -eq 1 ]] && echo enabled || echo disabled)"
|
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 "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 "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 "Audit rule: $([[ $INSTALL_AUDIT_RULE -eq 1 ]] && echo enabled || echo disabled)"
|
||||||
echo "Alias stub: $([[ $ADD_ALIAS_STUB -eq 1 ]] && echo enabled || echo disabled)"
|
echo "Alias stub: $([[ $ADD_ALIAS_STUB -eq 1 ]] && echo enabled || echo disabled)"
|
||||||
echo
|
echo
|
||||||
echo "Test flow:"
|
echo "Test flow:"
|
||||||
echo " sudo sed -i '1s/.*/# tamper test/' /etc/hosts # Should revert automatically"
|
echo " sudo sed -i '1s/.*/# tamper test/' /etc/hosts # Should revert automatically"
|
||||||
echo " sudo $INSTALL_UNLOCK # Intentional edit workflow"
|
echo " sudo $INSTALL_UNLOCK # Intentional edit workflow"
|
||||||
echo
|
echo
|
||||||
echo "Uninstall:"
|
echo "Uninstall:"
|
||||||
echo " sudo $0 --uninstall"
|
echo " sudo $0 --uninstall"
|
||||||
echo "(Optional) Skip shell history hooks: --no-shell-hooks"
|
echo "(Optional) Skip shell history hooks: --no-shell-hooks"
|
||||||
echo
|
echo
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
141
hosts/install.sh
141
hosts/install.sh
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Re-run with sudo if not root
|
# Re-run with sudo if not root
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
exec sudo -E bash "$0" "$@"
|
exec sudo -E bash "$0" "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
@ -11,25 +11,25 @@ FLUSH_DNS=0
|
|||||||
|
|
||||||
# Parse CLI flags
|
# Parse CLI flags
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--flush-dns)
|
--flush-dns)
|
||||||
FLUSH_DNS=1
|
FLUSH_DNS=1
|
||||||
;;
|
;;
|
||||||
--no-flush-dns)
|
--no-flush-dns)
|
||||||
FLUSH_DNS=0
|
FLUSH_DNS=0
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h | --help)
|
||||||
echo "Usage: $0 [--flush-dns|--no-flush-dns]"
|
echo "Usage: $0 [--flush-dns|--no-flush-dns]"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Enable systemd-resolved
|
# Enable systemd-resolved
|
||||||
sudo systemctl enable systemd-resolved
|
sudo systemctl enable systemd-resolved
|
||||||
|
|
||||||
# Remove all attributes from /etc/hosts to allow modifications
|
# 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
|
# Source and local cache configuration
|
||||||
URL="https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews-gambling-porn-social/hosts"
|
URL="https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews-gambling-porn-social/hosts"
|
||||||
@ -38,33 +38,33 @@ LOCAL_CACHE="/etc/hosts.stevenblack"
|
|||||||
|
|
||||||
# Helpers
|
# Helpers
|
||||||
extract_date_epoch_from_file() {
|
extract_date_epoch_from_file() {
|
||||||
# Grep "# Date:" line and convert to epoch seconds (UTC)
|
# Grep "# Date:" line and convert to epoch seconds (UTC)
|
||||||
local f="$1"
|
local f="$1"
|
||||||
local line
|
local line
|
||||||
line=$(grep -m1 '^# Date:' "$f" 2>/dev/null | sed -E 's/^# Date:[[:space:]]*(.*)[[:space:]]*\(UTC\).*/\1 UTC/')
|
line=$(grep -m1 '^# Date:' "$f" 2> /dev/null | sed -E 's/^# Date:[[:space:]]*(.*)[[:space:]]*\(UTC\).*/\1 UTC/')
|
||||||
if [[ -n "$line" ]]; then
|
if [[ -n $line ]]; then
|
||||||
date -u -d "$line" +%s 2>/dev/null || echo ""
|
date -u -d "$line" +%s 2> /dev/null || echo ""
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch_remote_header() {
|
fetch_remote_header() {
|
||||||
# Try to fetch only the first ~4KB using HTTP Range; fallback to piping to head
|
# Try to fetch only the first ~4KB using HTTP Range; fallback to piping to head
|
||||||
local out="$1"
|
local out="$1"
|
||||||
if curl -LfsS --max-time 10 -H 'Range: bytes=0-4095' "$URL" -o "$out"; then
|
if curl -LfsS --max-time 10 -H 'Range: bytes=0-4095' "$URL" -o "$out"; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
# Fallback – may download more, but we only keep first lines
|
# Fallback – may download more, but we only keep first lines
|
||||||
if curl -LfsS --max-time 10 "$URL" | head -n 20 > "$out"; then
|
if curl -LfsS --max-time 10 "$URL" | head -n 20 > "$out"; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
download_remote_full_to() {
|
download_remote_full_to() {
|
||||||
local out="$1"
|
local out="$1"
|
||||||
curl -LfsS "$URL" -o "$out"
|
curl -LfsS "$URL" -o "$out"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Decide whether to use cache or update
|
# Decide whether to use cache or update
|
||||||
@ -73,50 +73,47 @@ trap 'rm -f "$TMP_REMOTE_HEAD"' EXIT
|
|||||||
|
|
||||||
REMOTE_AVAILABLE=0
|
REMOTE_AVAILABLE=0
|
||||||
if fetch_remote_header "$TMP_REMOTE_HEAD"; then
|
if fetch_remote_header "$TMP_REMOTE_HEAD"; then
|
||||||
REMOTE_AVAILABLE=1
|
REMOTE_AVAILABLE=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
USE_CACHE=0
|
|
||||||
NEED_UPDATE=0
|
NEED_UPDATE=0
|
||||||
|
|
||||||
if [[ -f "$LOCAL_CACHE" ]]; then
|
if [[ -f $LOCAL_CACHE ]]; then
|
||||||
local_epoch=$(extract_date_epoch_from_file "$LOCAL_CACHE")
|
local_epoch=$(extract_date_epoch_from_file "$LOCAL_CACHE")
|
||||||
else
|
else
|
||||||
local_epoch=""
|
local_epoch=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $REMOTE_AVAILABLE -eq 1 ]]; then
|
if [[ $REMOTE_AVAILABLE -eq 1 ]]; then
|
||||||
remote_epoch=$(extract_date_epoch_from_file "$TMP_REMOTE_HEAD")
|
remote_epoch=$(extract_date_epoch_from_file "$TMP_REMOTE_HEAD")
|
||||||
if [[ -n "$local_epoch" && -n "$remote_epoch" && "$local_epoch" -ge "$remote_epoch" ]]; then
|
if [[ -n $local_epoch && -n $remote_epoch && $local_epoch -ge $remote_epoch ]]; then
|
||||||
echo "Using cached StevenBlack hosts (up-to-date)."
|
echo "Using cached StevenBlack hosts (up-to-date)."
|
||||||
USE_CACHE=1
|
else
|
||||||
else
|
echo "Cached version is missing or outdated; downloading latest StevenBlack hosts..."
|
||||||
echo "Cached version is missing or outdated; downloading latest StevenBlack hosts..."
|
NEED_UPDATE=1
|
||||||
NEED_UPDATE=1
|
fi
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
if [[ -f "$LOCAL_CACHE" ]]; then
|
if [[ -f $LOCAL_CACHE ]]; then
|
||||||
echo "No internet; using cached StevenBlack hosts."
|
echo "No internet; using cached StevenBlack hosts."
|
||||||
USE_CACHE=1
|
else
|
||||||
else
|
echo "Error: No internet and no cached StevenBlack hosts found." >&2
|
||||||
echo "Error: No internet and no cached StevenBlack hosts found." >&2
|
exit 1
|
||||||
exit 1
|
fi
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure we have a fresh cache if needed
|
# Ensure we have a fresh cache if needed
|
||||||
if [[ $NEED_UPDATE -eq 1 ]]; then
|
if [[ $NEED_UPDATE -eq 1 ]]; then
|
||||||
TMP_DL=$(mktemp)
|
TMP_DL=$(mktemp)
|
||||||
if download_remote_full_to "$TMP_DL"; then
|
if download_remote_full_to "$TMP_DL"; then
|
||||||
# Save raw upstream to cache
|
# Save raw upstream to cache
|
||||||
sudo mv "$TMP_DL" "$LOCAL_CACHE"
|
sudo mv "$TMP_DL" "$LOCAL_CACHE"
|
||||||
sudo chmod 644 "$LOCAL_CACHE"
|
sudo chmod 644 "$LOCAL_CACHE"
|
||||||
echo "Saved latest StevenBlack hosts to cache: $LOCAL_CACHE"
|
echo "Saved latest StevenBlack hosts to cache: $LOCAL_CACHE"
|
||||||
else
|
else
|
||||||
rm -f "$TMP_DL"
|
rm -f "$TMP_DL"
|
||||||
echo "Error: Failed to download latest StevenBlack hosts." >&2
|
echo "Error: Failed to download latest StevenBlack hosts." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install the base hosts from cache into /etc/hosts
|
# Install the base hosts from cache into /etc/hosts
|
||||||
@ -265,10 +262,10 @@ sudo chattr -i /etc/hosts
|
|||||||
sudo chattr +a /etc/hosts
|
sudo chattr +a /etc/hosts
|
||||||
|
|
||||||
# Optionally flush DNS caches
|
# Optionally flush DNS caches
|
||||||
if [[ "$FLUSH_DNS" -eq 1 ]]; then
|
if [[ $FLUSH_DNS -eq 1 ]]; then
|
||||||
echo "Flushing DNS caches..."
|
echo "Flushing DNS caches..."
|
||||||
sudo systemd-resolve --flush-caches
|
sudo systemd-resolve --flush-caches
|
||||||
sudo systemctl restart NetworkManager.service
|
sudo systemctl restart NetworkManager.service
|
||||||
else
|
else
|
||||||
echo "DNS cache flush skipped (use --flush-dns to enable)."
|
echo "DNS cache flush skipped (use --flush-dns to enable)."
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -4,45 +4,45 @@
|
|||||||
|
|
||||||
# Check if ActivityWatch is installed
|
# Check if ActivityWatch is installed
|
||||||
check_installed() {
|
check_installed() {
|
||||||
# Check if activitywatch-bin package is installed
|
# Check if activitywatch-bin package is installed
|
||||||
if pacman -Qi activitywatch-bin &>/dev/null; then
|
if pacman -Qi activitywatch-bin &> /dev/null; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if aw-qt binary exists
|
# Check if aw-qt binary exists
|
||||||
if command -v aw-qt &>/dev/null; then
|
if command -v aw-qt &> /dev/null; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if ActivityWatch is running
|
# Check if ActivityWatch is running
|
||||||
check_running() {
|
check_running() {
|
||||||
# Check for aw-qt process
|
# Check for aw-qt process
|
||||||
if pgrep -f "aw-qt" >/dev/null 2>&1; then
|
if pgrep -f "aw-qt" > /dev/null 2>&1; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for aw-server process
|
# Check for aw-server process
|
||||||
if pgrep -f "aw-server" >/dev/null 2>&1; then
|
if pgrep -f "aw-server" > /dev/null 2>&1; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main logic
|
# Main logic
|
||||||
if ! check_installed; then
|
if ! check_installed; then
|
||||||
echo "AW uninstalled"
|
echo "AW uninstalled"
|
||||||
echo
|
echo
|
||||||
echo "#FF0000" # Red
|
echo "#FF0000" # Red
|
||||||
elif check_running; then
|
elif check_running; then
|
||||||
echo "AW on"
|
echo "AW on"
|
||||||
echo
|
echo
|
||||||
echo "#00FF00" # Green
|
echo "#00FF00" # Green
|
||||||
else
|
else
|
||||||
echo "AW off"
|
echo "AW off"
|
||||||
echo
|
echo
|
||||||
echo "#FF0000" # Red
|
echo "#FF0000" # Red
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -8,4 +8,4 @@ acpi -b | awk -F', ' '
|
|||||||
if (time[1] != "") printf ", %s", time[1]
|
if (time[1] != "") printf ", %s", time[1]
|
||||||
if ($1 ~ /Charging/) printf ", "
|
if ($1 ~ /Charging/) printf ", "
|
||||||
printf "\n"
|
printf "\n"
|
||||||
}'
|
}'
|
||||||
|
|||||||
@ -5,11 +5,10 @@ bluetooth_info=$(bluetoothctl info)
|
|||||||
|
|
||||||
# Check if Bluetooth is connected
|
# Check if Bluetooth is connected
|
||||||
if echo "$bluetooth_info" | grep -q "Connected: yes"; then
|
if echo "$bluetooth_info" | grep -q "Connected: yes"; then
|
||||||
device=$(echo "$bluetooth_info" | grep "Alias" | cut -d ' ' -f2-)
|
device=$(echo "$bluetooth_info" | grep "Alias" | cut -d ' ' -f2-)
|
||||||
echo " $device" # is the Bluetooth icon
|
echo " $device" # is the Bluetooth icon
|
||||||
echo
|
echo
|
||||||
echo "#50FA7B" # Green for connected
|
echo "#50FA7B" # Green for connected
|
||||||
else
|
else
|
||||||
echo " Disconnected"
|
echo " Disconnected"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@ -3,46 +3,46 @@
|
|||||||
# CPU Temperature
|
# CPU Temperature
|
||||||
cpu_temp=$(sensors | awk '/^Tctl:/ {print $2}' | tr -d '+°C')
|
cpu_temp=$(sensors | awk '/^Tctl:/ {print $2}' | tr -d '+°C')
|
||||||
if [ -z "$cpu_temp" ]; then
|
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
|
fi
|
||||||
if [ -z "$cpu_temp" ]; then
|
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
|
fi
|
||||||
if [ -z "$cpu_temp" ]; then
|
if [ -z "$cpu_temp" ]; then
|
||||||
cpu_temp="N/A"
|
cpu_temp="N/A"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# CPU Load (1-minute average)
|
# CPU Load (1-minute average)
|
||||||
cpu_load=$(awk '{print $1}' /proc/loadavg)
|
cpu_load=$(awk '{print $1}' /proc/loadavg)
|
||||||
if [ -z "$cpu_load" ]; then
|
if [ -z "$cpu_load" ]; then
|
||||||
cpu_load="N/A"
|
cpu_load="N/A"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Colors for CPU Load and Temperature
|
# Colors for CPU Load and Temperature
|
||||||
cpu_color="#FFFFFF" # Default color
|
cpu_color="#FFFFFF" # Default color
|
||||||
|
|
||||||
# Change color based on CPU load
|
# Change color based on CPU load
|
||||||
if [[ "$cpu_load" != "N/A" ]]; then
|
if [[ $cpu_load != "N/A" ]]; then
|
||||||
cpu_load_float=$(echo "$cpu_load" | awk '{print ($1 + 0)}')
|
cpu_load_float=$(echo "$cpu_load" | awk '{print ($1 + 0)}')
|
||||||
if (( $(echo "$cpu_load_float < 1.0" | bc -l) )); then
|
if (($(echo "$cpu_load_float < 1.0" | bc -l))); then
|
||||||
cpu_color="#50FA7B" # Green for low load
|
cpu_color="#50FA7B" # Green for low load
|
||||||
elif (( $(echo "$cpu_load_float < 2.0" | bc -l) )); then
|
elif (($(echo "$cpu_load_float < 2.0" | bc -l))); then
|
||||||
cpu_color="#F1FA8C" # Yellow for medium load
|
cpu_color="#F1FA8C" # Yellow for medium load
|
||||||
else
|
else
|
||||||
cpu_color="#FF5555" # Red for high load
|
cpu_color="#FF5555" # Red for high load
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Change color based on CPU temperature
|
# Change color based on CPU temperature
|
||||||
if [[ "$cpu_temp" != "N/A" ]]; then
|
if [[ $cpu_temp != "N/A" ]]; then
|
||||||
cpu_temp_float=$(echo "$cpu_temp" | awk '{print ($1 + 0)}')
|
cpu_temp_float=$(echo "$cpu_temp" | awk '{print ($1 + 0)}')
|
||||||
if (( $(echo "$cpu_temp_float < 65.0" | bc -l) )); then
|
if (($(echo "$cpu_temp_float < 65.0" | bc -l))); then
|
||||||
cpu_color="#50FA7B" # Green for low temperature
|
cpu_color="#50FA7B" # Green for low temperature
|
||||||
elif (( $(echo "$cpu_temp_float < 85.0" | bc -l) )); then
|
elif (($(echo "$cpu_temp_float < 85.0" | bc -l))); then
|
||||||
cpu_color="#F1FA8C" # Yellow for medium temperature
|
cpu_color="#F1FA8C" # Yellow for medium temperature
|
||||||
else
|
else
|
||||||
cpu_color="#FF5555" # Red for high temperature
|
cpu_color="#FF5555" # Red for high temperature
|
||||||
fi
|
fi
|
||||||
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>"
|
||||||
|
|||||||
@ -2,41 +2,41 @@
|
|||||||
|
|
||||||
# Function to get NVIDIA GPU metrics
|
# Function to get NVIDIA GPU metrics
|
||||||
get_nvidia_metrics() {
|
get_nvidia_metrics() {
|
||||||
gpu_temp=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits 2>/dev/null)
|
gpu_temp=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits 2> /dev/null)
|
||||||
if [ -z "$gpu_temp" ]; then
|
if [ -z "$gpu_temp" ]; then
|
||||||
gpu_temp="N/A"
|
gpu_temp="N/A"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
gpu_load=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null)
|
gpu_load=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2> /dev/null)
|
||||||
if [ -z "$gpu_load" ]; then
|
if [ -z "$gpu_load" ]; then
|
||||||
gpu_load="N/A"
|
gpu_load="N/A"
|
||||||
fi
|
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
|
# Function to get Intel GPU metrics
|
||||||
get_intel_metrics() {
|
get_intel_metrics() {
|
||||||
gpu_load=$(cat /sys/class/drm/card0/device/gpu_busy_percent 2>/dev/null)
|
gpu_load=$(cat /sys/class/drm/card0/device/gpu_busy_percent 2> /dev/null)
|
||||||
if [ -z "$gpu_load" ]; then
|
if [ -z "$gpu_load" ]; then
|
||||||
gpu_load="N/A"
|
gpu_load="N/A"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
gpu_temp=$(sensors | awk '/^temp1:/ {print $2; exit}' | tr -d '+°C')
|
gpu_temp=$(sensors | awk '/^temp1:/ {print $2; exit}' | tr -d '+°C')
|
||||||
if [ -z "$gpu_temp" ]; then
|
if [ -z "$gpu_temp" ]; then
|
||||||
gpu_temp="N/A"
|
gpu_temp="N/A"
|
||||||
fi
|
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
|
# Detect GPU type and get metrics
|
||||||
if lspci | grep -i nvidia > /dev/null; then
|
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
|
elif lspci | grep -i vga | grep -i intel > /dev/null; then
|
||||||
gpu_metrics=$(get_intel_metrics)
|
gpu_metrics=$(get_intel_metrics)
|
||||||
else
|
else
|
||||||
echo "No supported GPU found."
|
echo "No supported GPU found."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
@ -44,22 +44,21 @@ fi
|
|||||||
gpu_temp=$(echo "$gpu_metrics" | awk -F', ' '{print $1}' | awk -F': ' '{print $2}')
|
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_load=$(echo "$gpu_metrics" | awk -F', ' '{print $2}' | awk -F': ' '{print $2}')
|
||||||
|
|
||||||
gpu_color="#FFFFFF"
|
gpu_color="#FFFFFF"
|
||||||
# Colors for GPU Load
|
# Colors for GPU Load
|
||||||
if [[ "$gpu_load" != "N/A" ]]; then
|
if [[ $gpu_load != "N/A" ]]; then
|
||||||
if (( $(echo "$gpu_load < 50.0" | bc -l) )); then
|
if (($(echo "$gpu_load < 50.0" | bc -l))); then
|
||||||
gpu_color="#50FA7B" # Green
|
gpu_color="#50FA7B" # Green
|
||||||
elif (( $(echo "$gpu_load < 75.0" | bc -l) )); then
|
elif (($(echo "$gpu_load < 75.0" | bc -l))); then
|
||||||
gpu_color="#F1FA8C" # Yellow
|
gpu_color="#F1FA8C" # Yellow
|
||||||
else
|
else
|
||||||
gpu_color="#FF5555" # Red
|
gpu_color="#FF5555" # Red
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
gpu_color="#FFFFFF" # Default color
|
gpu_color="#FFFFFF" # Default color
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Output<
|
# Output<
|
||||||
echo -e "<span color=\"$gpu_color\"> ${gpu_temp}, ${gpu_load}%</span>"
|
echo -e "<span color=\"$gpu_color\"> ${gpu_temp}, ${gpu_load}%</span>"
|
||||||
echo
|
echo
|
||||||
echo "#FFFFFF" # Default color for fallback (ignored if markup is enabled)
|
echo "#FFFFFF" # Default color for fallback (ignored if markup is enabled)
|
||||||
|
|
||||||
|
|||||||
@ -4,24 +4,23 @@
|
|||||||
temp=$(sensors | awk '/^temp1:/ {print $2; exit}' | tr -d '+°C')
|
temp=$(sensors | awk '/^temp1:/ {print $2; exit}' | tr -d '+°C')
|
||||||
|
|
||||||
# Ensure the temperature is a valid number
|
# Ensure the temperature is a valid number
|
||||||
if [[ ! "$temp" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
|
if [[ ! $temp =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
|
||||||
echo " MB: N/A"
|
echo " MB: N/A"
|
||||||
echo
|
echo
|
||||||
echo "#FF5555" # Red color for error
|
echo "#FF5555" # Red color for error
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Define temperature thresholds
|
# Define temperature thresholds
|
||||||
if (( $(echo "$temp < 50.0" | bc -l) )); then
|
if (($(echo "$temp < 50.0" | bc -l))); then
|
||||||
color="#50FA7B" # Green for OK temperature
|
color="#50FA7B" # Green for OK temperature
|
||||||
elif (( $(echo "$temp < 70.0" | bc -l) )); then
|
elif (($(echo "$temp < 70.0" | bc -l))); then
|
||||||
color="#F1FA8C" # Yellow for warning temperature
|
color="#F1FA8C" # Yellow for warning temperature
|
||||||
else
|
else
|
||||||
color="#FF5555" # Red for high temperature
|
color="#FF5555" # Red for high temperature
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Output the temperature with the color
|
# Output the temperature with the color
|
||||||
echo " ${temp}°C" # is a thermometer icon
|
echo " ${temp}°C" # is a thermometer icon
|
||||||
echo
|
echo
|
||||||
echo $color
|
echo $color
|
||||||
|
|
||||||
|
|||||||
@ -2,23 +2,28 @@
|
|||||||
|
|
||||||
# Function to detect all active network interfaces
|
# Function to detect all active network interfaces
|
||||||
detect_interfaces() {
|
detect_interfaces() {
|
||||||
interfaces=()
|
local iface_path iface state
|
||||||
for interface in /sys/class/net/*; do
|
for iface_path in /sys/class/net/*; do
|
||||||
interface=$(basename "$interface")
|
iface=$(basename "$iface_path")
|
||||||
if [[ "$interface" != "lo" && -d "/sys/class/net/$interface" && "$(cat /sys/class/net/$interface/operstate)" == "up" ]]; then
|
if [[ $iface == "lo" || ! -d "/sys/class/net/$iface" ]]; then
|
||||||
interfaces+=("$interface")
|
continue
|
||||||
fi
|
fi
|
||||||
done
|
if [[ -r "/sys/class/net/$iface/operstate" ]]; then
|
||||||
echo "${interfaces[@]}"
|
state=$(< "/sys/class/net/$iface/operstate")
|
||||||
|
if [[ $state == "up" ]]; then
|
||||||
|
printf '%s\n' "$iface"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
# Detect all active network interfaces
|
# Detect all active network interfaces
|
||||||
interfaces=$(detect_interfaces)
|
mapfile -t interfaces < <(detect_interfaces)
|
||||||
|
|
||||||
# If no active interfaces are found, exit
|
# If no active interfaces are found, exit
|
||||||
if [ -z "$interfaces" ]; then
|
if [ "${#interfaces[@]}" -eq 0 ]; then
|
||||||
echo "No active network interfaces found"
|
echo "No active network interfaces found"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Initialize total RX and TX bytes
|
# Initialize total RX and TX bytes
|
||||||
@ -34,46 +39,50 @@ current_time=$(date +%s)
|
|||||||
last_time=$current_time
|
last_time=$current_time
|
||||||
|
|
||||||
# Iterate over each interface and accumulate RX and TX bytes
|
# Iterate over each interface and accumulate RX and TX bytes
|
||||||
for interface in $interfaces; do
|
for interface in "${interfaces[@]}"; do
|
||||||
rx_path="/sys/class/net/$interface/statistics/rx_bytes"
|
rx_path="/sys/class/net/$interface/statistics/rx_bytes"
|
||||||
tx_path="/sys/class/net/$interface/statistics/tx_bytes"
|
tx_path="/sys/class/net/$interface/statistics/tx_bytes"
|
||||||
|
|
||||||
rx_now=$(cat $rx_path 2>/dev/null)
|
if ! read -r rx_now < "$rx_path"; then
|
||||||
tx_now=$(cat $tx_path 2>/dev/null)
|
rx_now=0
|
||||||
|
fi
|
||||||
|
if ! read -r tx_now < "$tx_path"; then
|
||||||
|
tx_now=0
|
||||||
|
fi
|
||||||
|
|
||||||
state_file="/tmp/network_monitor_$interface"
|
state_file="/tmp/network_monitor_$interface"
|
||||||
if [ -f "$state_file" ]; then
|
if [ -f "$state_file" ]; then
|
||||||
read last_rx last_tx last_time < "$state_file"
|
read -r last_rx last_tx last_time < "$state_file"
|
||||||
else
|
else
|
||||||
last_rx=$rx_now
|
last_rx=$rx_now
|
||||||
last_tx=$tx_now
|
last_tx=$tx_now
|
||||||
fi
|
fi
|
||||||
|
|
||||||
total_rx_now=$((total_rx_now + rx_now))
|
total_rx_now=$((total_rx_now + rx_now))
|
||||||
total_tx_now=$((total_tx_now + tx_now))
|
total_tx_now=$((total_tx_now + tx_now))
|
||||||
total_last_rx=$((total_last_rx + last_rx))
|
total_last_rx=$((total_last_rx + last_rx))
|
||||||
total_last_tx=$((total_last_tx + last_tx))
|
total_last_tx=$((total_last_tx + last_tx))
|
||||||
|
|
||||||
# Save current RX and TX bytes for the next check
|
# Save current RX and TX bytes for the next check
|
||||||
echo "$rx_now $tx_now $current_time" > "$state_file"
|
echo "$rx_now $tx_now $current_time" > "$state_file"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Calculate time difference
|
# Calculate time difference
|
||||||
time_diff=$((current_time - last_time))
|
time_diff=$((current_time - last_time))
|
||||||
|
|
||||||
# Calculate total download and upload speeds in bytes per second
|
# Calculate total download and upload speeds in bytes per second
|
||||||
if (( time_diff > 0 )); then
|
if ((time_diff > 0)); then
|
||||||
total_rx_rate=$(( (total_rx_now - total_last_rx) / time_diff ))
|
total_rx_rate=$(((total_rx_now - total_last_rx) / time_diff))
|
||||||
total_tx_rate=$(( (total_tx_now - total_last_tx) / time_diff ))
|
total_tx_rate=$(((total_tx_now - total_last_tx) / time_diff))
|
||||||
else
|
else
|
||||||
total_rx_rate=0
|
total_rx_rate=0
|
||||||
total_tx_rate=0
|
total_tx_rate=0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Convert speeds to human-readable format
|
# Convert speeds to human-readable format
|
||||||
rx_rate_human=$(numfmt --to=iec --suffix=B/s --padding=8 $total_rx_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)
|
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
|
# Store the result of printf into a string and echo it
|
||||||
printf " %s %s\n" "$rx_rate_human" "$tx_rate_human"
|
printf " %s %s\n" "$rx_rate_human" "$tx_rate_human"
|
||||||
echo "#50FA7B"
|
echo "#50FA7B"
|
||||||
|
|||||||
@ -4,65 +4,69 @@
|
|||||||
|
|
||||||
# Function to check if today is a monitored day
|
# Function to check if today is a monitored day
|
||||||
is_monitored_day() {
|
is_monitored_day() {
|
||||||
local day_of_week=$(date +%u)
|
local day_of_week
|
||||||
if [[ "$day_of_week" == "1" ]] || [[ "$day_of_week" == "5" ]] || [[ "$day_of_week" == "6" ]] || [[ "$day_of_week" == "7" ]]; then
|
day_of_week=$(date +%u)
|
||||||
return 0
|
if [[ $day_of_week == "1" ]] || [[ $day_of_week == "5" ]] || [[ $day_of_week == "6" ]] || [[ $day_of_week == "7" ]]; then
|
||||||
else
|
return 0
|
||||||
return 1
|
else
|
||||||
fi
|
return 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to check if current time is in window
|
# Function to check if current time is in window
|
||||||
is_current_time_in_window() {
|
is_current_time_in_window() {
|
||||||
local current_hour=$(date +%H)
|
local current_hour current_hour_num
|
||||||
local current_hour_num=$((10#$current_hour))
|
current_hour=$(date +%H)
|
||||||
if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
|
current_hour_num=$((10#$current_hour))
|
||||||
return 0
|
if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
|
||||||
else
|
return 0
|
||||||
return 1
|
else
|
||||||
fi
|
return 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to check if PC was booted in window today
|
# Function to check if PC was booted in window today
|
||||||
was_booted_in_window_today() {
|
was_booted_in_window_today() {
|
||||||
local today=$(date +%Y-%m-%d)
|
local today uptime_seconds boot_time boot_date
|
||||||
local uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
|
today=$(date +%Y-%m-%d)
|
||||||
local boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
|
uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0")
|
||||||
local boot_date=$(echo "$boot_time" | cut -d' ' -f1)
|
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
|
if [[ $boot_date != "$today" ]]; then
|
||||||
fi
|
return 1
|
||||||
|
fi
|
||||||
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)
|
||||||
if [[ $boot_hour_num -ge 5 ]] && [[ $boot_hour_num -lt 8 ]]; then
|
boot_hour_num=$((10#$boot_hour))
|
||||||
return 0
|
|
||||||
else
|
if [[ $boot_hour_num -ge 5 ]] && [[ $boot_hour_num -lt 8 ]]; then
|
||||||
return 1
|
return 0
|
||||||
fi
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main logic
|
# Main logic
|
||||||
if ! is_monitored_day; then
|
if ! is_monitored_day; then
|
||||||
# Not a monitored day
|
# Not a monitored day
|
||||||
echo "PC:skip"
|
echo "PC:skip"
|
||||||
echo
|
echo
|
||||||
echo "#888888" # Gray
|
echo "#888888" # Gray
|
||||||
elif is_current_time_in_window; then
|
elif is_current_time_in_window; then
|
||||||
# Currently in the window - all good
|
# Currently in the window - all good
|
||||||
echo "PC:live"
|
echo "PC:live"
|
||||||
echo
|
echo
|
||||||
echo "#00FF00" # Green
|
echo "#00FF00" # Green
|
||||||
elif was_booted_in_window_today; then
|
elif was_booted_in_window_today; then
|
||||||
# Was booted in window today - compliant
|
# Was booted in window today - compliant
|
||||||
echo "PC:ok"
|
echo "PC:ok"
|
||||||
echo
|
echo
|
||||||
echo "#00FF00" # Green
|
echo "#00FF00" # Green
|
||||||
else
|
else
|
||||||
# Was NOT booted in window today - non-compliant
|
# Was NOT booted in window today - non-compliant
|
||||||
echo "PC:warn"
|
echo "PC:warn"
|
||||||
echo
|
echo
|
||||||
echo "#FF0000" # Red
|
echo "#FF0000" # Red
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -1,20 +1,19 @@
|
|||||||
|
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Get the current volume level and mute status
|
# Get the current volume level and mute status
|
||||||
volume=$(pactl get-sink-volume @DEFAULT_SINK@ | awk '{print $5}' | tr -d '%')
|
volume=$(pactl get-sink-volume @DEFAULT_SINK@ | awk '{print $5}' | tr -d '%')
|
||||||
mute=$(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}')
|
mute=$(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}')
|
||||||
|
color="#50FA7B"
|
||||||
|
|
||||||
# Determine icon and color based on mute status
|
# Determine icon and color based on mute status
|
||||||
if [ "$mute" = "yes" ]; then
|
if [ "$mute" = "yes" ]; then
|
||||||
icon="🔇" # Muted
|
icon="🔇" # Muted
|
||||||
|
color="#FF5555"
|
||||||
else
|
else
|
||||||
icon="🔊" # Volume icon
|
icon="🔊" # Volume icon
|
||||||
color="#50FA7B" # Green
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Output the volume with icon and color
|
# Output the volume with icon and color
|
||||||
echo "$icon $volume%"
|
echo "$icon $volume%"
|
||||||
echo
|
echo
|
||||||
echo $color
|
echo "$color"
|
||||||
|
|
||||||
|
|||||||
@ -2,25 +2,25 @@
|
|||||||
|
|
||||||
# Check if warp-cli is installed
|
# Check if warp-cli is installed
|
||||||
if ! command -v warp-cli &> /dev/null; then
|
if ! command -v warp-cli &> /dev/null; then
|
||||||
echo " N/A"
|
echo " N/A"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the status from warp-cli
|
# 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
|
# Display the status with an icon
|
||||||
if [ "$status" = "Connected" ]; then
|
if [ "$status" = "Connected" ]; then
|
||||||
echo "🔒 !!! WARP CONNECTED !!!"
|
echo "🔒 !!! WARP CONNECTED !!!"
|
||||||
echo
|
echo
|
||||||
echo "#FFFF00" # Yellow
|
echo "#FFFF00" # Yellow
|
||||||
elif [ "$status" = "Disconnected" ]; then
|
elif [ "$status" = "Disconnected" ]; then
|
||||||
echo "WARP disconnected"
|
echo "WARP disconnected"
|
||||||
echo
|
echo
|
||||||
echo "#00FF00" # Green
|
echo "#00FF00" # Green
|
||||||
else
|
else
|
||||||
echo "⚠️ ! WARP unknown !"
|
echo "⚠️ ! WARP unknown !"
|
||||||
echo
|
echo
|
||||||
echo "#FF0000" # Red
|
echo "#FF0000" # Red
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -5,23 +5,23 @@ wifi_interface=$(iw dev | awk '$1=="Interface"{print $2}')
|
|||||||
|
|
||||||
# If no WiFi interface is found, exit
|
# If no WiFi interface is found, exit
|
||||||
if [ -z "$wifi_interface" ]; then
|
if [ -z "$wifi_interface" ]; then
|
||||||
echo " down"
|
echo " down"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the WiFi details
|
# 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
|
# Extract the SSID and signal strength
|
||||||
ssid=$(echo "$wifi_info" | awk -F '"' '/ESSID/ {print $2}')
|
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
|
# 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
|
# Output the result
|
||||||
if [ -z "$ssid" ]; then
|
if [ -z "$ssid" ]; then
|
||||||
echo " down"
|
echo " down"
|
||||||
else
|
else
|
||||||
echo " $ssid ($signal dBm) $ip_address"
|
echo " $ssid ($signal dBm) $ip_address"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -2,40 +2,40 @@
|
|||||||
|
|
||||||
# Function to detect if the system is Ubuntu
|
# Function to detect if the system is Ubuntu
|
||||||
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
|
# Function to detect screen resolution and set font size
|
||||||
set_font_size() {
|
set_font_size() {
|
||||||
resolution=$(xdpyinfo | grep dimensions | awk '{print $2}')
|
resolution=$(xdpyinfo | grep dimensions | awk '{print $2}')
|
||||||
width=$(echo $resolution | cut -d 'x' -f 1)
|
width=$(echo "$resolution" | cut -d 'x' -f 1)
|
||||||
# Do not change this font size, it actually makes i3blocks unbearable to look at:
|
# 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
|
# Icons (like for slack) are too small and i3blocks are too big
|
||||||
# Network monitor jumping becomes annoying
|
# Network monitor jumping becomes annoying
|
||||||
if [ "$width" -gt 1920 ]; then
|
if [ "$width" -gt 1920 ]; then
|
||||||
echo "8"
|
echo "8"
|
||||||
else
|
else
|
||||||
echo "8"
|
echo "8"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if Intel GPU is detected
|
# Check if Intel GPU is detected
|
||||||
if lspci | grep -i 'vga' | grep -i 'intel'; then
|
if lspci | grep -i 'vga' | grep -i 'intel'; then
|
||||||
if is_ubuntu; then
|
if is_ubuntu; then
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y intel-gpu-tools
|
sudo apt-get install -y intel-gpu-tools
|
||||||
sudo setcap cap_perfmon+ep /usr/bin/intel_gpu_top
|
sudo setcap cap_perfmon+ep /usr/bin/intel_gpu_top
|
||||||
else
|
else
|
||||||
yes | sudo pacman -S --needed intel-gpu-tools
|
yes | sudo pacman -S --needed intel-gpu-tools
|
||||||
sudo setcap cap_perfmon+ep /usr/bin/intel_gpu_top
|
sudo setcap cap_perfmon+ep /usr/bin/intel_gpu_top
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if is_ubuntu; then
|
if is_ubuntu; then
|
||||||
sudo apt-get update
|
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 install -y fonts-dejavu-core fonts-noto fonts-font-awesome bc jq iw pulseaudio-utils
|
||||||
else
|
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
|
fi
|
||||||
|
|
||||||
# Set font size based on screen resolution
|
# Set font size based on screen resolution
|
||||||
|
|||||||
@ -12,17 +12,17 @@ SCRIPT_NAME=${0##*/}
|
|||||||
|
|
||||||
info() { printf "\033[1;34m[INFO]\033[0m %s\n" "$*"; }
|
info() { printf "\033[1;34m[INFO]\033[0m %s\n" "$*"; }
|
||||||
warn() { printf "\033[1;33m[WARN]\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() {
|
require_cmd() {
|
||||||
if ! command -v "$1" >/dev/null 2>&1; then
|
if ! command -v "$1" > /dev/null 2>&1; then
|
||||||
err "Missing dependency: $1"
|
err "Missing dependency: $1"
|
||||||
MISSING=1
|
MISSING=1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat << EOF
|
||||||
${SCRIPT_NAME} — Download and wire up LeechBlockNG from GitHub
|
${SCRIPT_NAME} — Download and wire up LeechBlockNG from GitHub
|
||||||
|
|
||||||
Usage: ${SCRIPT_NAME} [--version vX.Y[.Z]] [--force] [--install-firefox]
|
Usage: ${SCRIPT_NAME} [--version vX.Y[.Z]] [--force] [--install-firefox]
|
||||||
@ -46,15 +46,26 @@ AUTO_FIREFOX=0
|
|||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--version)
|
--version)
|
||||||
VERSION="$2"; shift 2;;
|
VERSION="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
--force)
|
--force)
|
||||||
FORCE=1; shift;;
|
FORCE=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--install-firefox)
|
--install-firefox)
|
||||||
AUTO_FIREFOX=1; shift;;
|
AUTO_FIREFOX=1
|
||||||
-h|--help)
|
shift
|
||||||
usage; exit 0;;
|
;;
|
||||||
|
-h | --help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
err "Unknown argument: $1"; usage; exit 2;;
|
err "Unknown argument: $1"
|
||||||
|
usage
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
@ -65,42 +76,48 @@ require_cmd tar
|
|||||||
require_cmd find
|
require_cmd find
|
||||||
require_cmd sed
|
require_cmd sed
|
||||||
require_cmd awk
|
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."
|
warn "jq not found — will fall back to a simpler tag detection method."
|
||||||
fi
|
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_OWNER="proginosko"
|
||||||
REPO_NAME="LeechBlockNG"
|
REPO_NAME="LeechBlockNG"
|
||||||
|
|
||||||
get_latest_tag() {
|
get_latest_tag() {
|
||||||
local 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)
|
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
|
if [[ -n $tag && $tag != "null" ]]; then
|
||||||
echo "$tag"; return 0
|
echo "$tag"
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
# Fallback: follow redirect for /releases/latest to extract tag
|
# 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)
|
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
|
if [[ -n $tag ]]; then
|
||||||
echo "$tag"; return 0
|
echo "$tag"
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if [[ -z "$VERSION" ]]; then
|
if [[ -z $VERSION ]]; then
|
||||||
info "Resolving latest release tag from GitHub…"
|
info "Resolving latest release tag from GitHub…"
|
||||||
if ! VERSION=$(get_latest_tag); then
|
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
|
||||||
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."
|
warn "Version tag '$VERSION' doesn't look like vX[.Y[.Z]] — continuing anyway."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VERSION=${VERSION#v} # strip leading v for folder names
|
VERSION=${VERSION#v} # strip leading v for folder names
|
||||||
TAG="v${VERSION}"
|
TAG="v${VERSION}"
|
||||||
|
|
||||||
XDG_DATA_HOME=${XDG_DATA_HOME:-"$HOME/.local/share"}
|
XDG_DATA_HOME=${XDG_DATA_HOME:-"$HOME/.local/share"}
|
||||||
@ -108,7 +125,7 @@ INSTALL_ROOT="$XDG_DATA_HOME/leechblockng"
|
|||||||
VERSION_DIR="$INSTALL_ROOT/$VERSION"
|
VERSION_DIR="$INSTALL_ROOT/$VERSION"
|
||||||
CURRENT_LINK="$INSTALL_ROOT/current"
|
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)."
|
info "LeechBlockNG $VERSION already present at $VERSION_DIR (use --force to reinstall)."
|
||||||
else
|
else
|
||||||
info "Downloading LeechBlockNG $TAG source from GitHub…"
|
info "Downloading LeechBlockNG $TAG source from GitHub…"
|
||||||
@ -122,11 +139,14 @@ else
|
|||||||
tar -xzf "$ARCHIVE_FILE" -C "$tmpdir/extract"
|
tar -xzf "$ARCHIVE_FILE" -C "$tmpdir/extract"
|
||||||
# The archive usually extracts to REPO_NAME-TAG/ …
|
# The archive usually extracts to REPO_NAME-TAG/ …
|
||||||
src_root=$(find "$tmpdir/extract" -maxdepth 1 -type d -name "${REPO_NAME}-*" | head -n1 || true)
|
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)
|
# 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)
|
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."
|
err "manifest.json not found in the extracted archive. The project layout may have changed."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@ -137,30 +157,30 @@ else
|
|||||||
info "Installing to $VERSION_DIR…"
|
info "Installing to $VERSION_DIR…"
|
||||||
mkdir -p "$VERSION_DIR"
|
mkdir -p "$VERSION_DIR"
|
||||||
# Copy the extension directory as-is (avoid bringing tests or build scripts)
|
# 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"
|
ln -sfn "$VERSION_DIR" "$CURRENT_LINK"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
EXT_PATH="$CURRENT_LINK" # stable path used by wrappers
|
EXT_PATH="$CURRENT_LINK" # stable path used by wrappers
|
||||||
|
|
||||||
# Detect browsers
|
# Detect browsers
|
||||||
declare -A BROWSERS
|
declare -A BROWSERS
|
||||||
BROWSERS=(
|
BROWSERS=(
|
||||||
[chromium]="Chromium"
|
[chromium]="Chromium"
|
||||||
[google-chrome-stable]="Google Chrome"
|
[google - chrome - stable]="Google Chrome"
|
||||||
[google-chrome]="Google Chrome"
|
[google - chrome]="Google Chrome"
|
||||||
[brave-browser]="Brave"
|
[brave - browser]="Brave"
|
||||||
[vivaldi-stable]="Vivaldi"
|
[vivaldi - stable]="Vivaldi"
|
||||||
[vivaldi]="Vivaldi"
|
[vivaldi]="Vivaldi"
|
||||||
[opera]="Opera"
|
[opera]="Opera"
|
||||||
[thorium-browser]="Thorium"
|
[thorium - browser]="Thorium"
|
||||||
)
|
)
|
||||||
|
|
||||||
declare -A FIREFOXES
|
declare -A FIREFOXES
|
||||||
FIREFOXES=(
|
FIREFOXES=(
|
||||||
[firefox]="Firefox"
|
[firefox]="Firefox"
|
||||||
[firefox-developer-edition]="Firefox Developer Edition"
|
[firefox - developer - edition]="Firefox Developer Edition"
|
||||||
[librewolf]="LibreWolf"
|
[librewolf]="LibreWolf"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -173,15 +193,17 @@ user_apps_dir="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
|
|||||||
mkdir -p "$user_apps_dir"
|
mkdir -p "$user_apps_dir"
|
||||||
|
|
||||||
create_wrapper_and_desktop() {
|
create_wrapper_and_desktop() {
|
||||||
local bin="$1"; shift
|
local bin="$1"
|
||||||
local pretty="$1"; shift
|
shift
|
||||||
|
local pretty="$1"
|
||||||
|
shift
|
||||||
local wrapper="$wrap_bin_dir/${bin}-with-leechblock"
|
local wrapper="$wrap_bin_dir/${bin}-with-leechblock"
|
||||||
|
|
||||||
local real_bin
|
local real_bin
|
||||||
real_bin=$(command -v "$bin" || true)
|
real_bin=$(command -v "$bin" || true)
|
||||||
[[ -z "$real_bin" ]] && return
|
[[ -z $real_bin ]] && return
|
||||||
|
|
||||||
cat >"$wrapper" <<WRAP
|
cat > "$wrapper" << WRAP
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
exec "$real_bin" --load-extension="$EXT_PATH" "$@"
|
exec "$real_bin" --load-extension="$EXT_PATH" "$@"
|
||||||
WRAP
|
WRAP
|
||||||
@ -189,18 +211,18 @@ WRAP
|
|||||||
|
|
||||||
# Try to reuse icon from an existing desktop file if available
|
# Try to reuse icon from an existing desktop file if available
|
||||||
local sys_desktop existing_icon existing_name categories
|
local sys_desktop existing_icon existing_name categories
|
||||||
sys_desktop=$(grep -RIl "^Exec=.*${bin}" /usr/share/applications 2>/dev/null | head -n1 || true)
|
sys_desktop=$(grep -RIl "^Exec=.*${bin}" /usr/share/applications 2> /dev/null | head -n1 || true)
|
||||||
if [[ -n "$sys_desktop" ]]; then
|
if [[ -n $sys_desktop ]]; then
|
||||||
existing_icon=$(awk -F= '/^Icon=/{print $2; exit}' "$sys_desktop" || true)
|
existing_icon=$(awk -F= '/^Icon=/{print $2; exit}' "$sys_desktop" || true)
|
||||||
existing_name=$(awk -F= '/^Name=/{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)
|
categories=$(awk -F= '/^Categories=/{print $2; exit}' "$sys_desktop" || true)
|
||||||
fi
|
fi
|
||||||
[[ -z "$existing_icon" ]] && existing_icon="$bin"
|
[[ -z $existing_icon ]] && existing_icon="$bin"
|
||||||
[[ -z "$existing_name" ]] && existing_name="$pretty"
|
[[ -z $existing_name ]] && existing_name="$pretty"
|
||||||
[[ -z "$categories" ]] && categories="Network;WebBrowser;"
|
[[ -z $categories ]] && categories="Network;WebBrowser;"
|
||||||
|
|
||||||
local desktop_file="$user_apps_dir/${bin}-with-leechblock.desktop"
|
local desktop_file="$user_apps_dir/${bin}-with-leechblock.desktop"
|
||||||
cat >"$desktop_file" <<DESK
|
cat > "$desktop_file" << DESK
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=${existing_name} (LeechBlock)
|
Name=${existing_name} (LeechBlock)
|
||||||
Exec=${wrapper} %U
|
Exec=${wrapper} %U
|
||||||
@ -218,14 +240,14 @@ DESK
|
|||||||
|
|
||||||
info "Detecting installed browsers…"
|
info "Detecting installed browsers…"
|
||||||
for bin in "${!BROWSERS[@]}"; do
|
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]}"
|
create_wrapper_and_desktop "$bin" "${BROWSERS[$bin]}"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
ff_found=0
|
ff_found=0
|
||||||
for bin in "${!FIREFOXES[@]}"; do
|
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
|
ff_found=1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@ -239,7 +261,7 @@ fi
|
|||||||
if [[ $ff_found -eq 1 ]]; then
|
if [[ $ff_found -eq 1 ]]; then
|
||||||
echo
|
echo
|
||||||
warn "Detected Firefox-based browser(s). Permanent install from GitHub source isn't possible on stable builds due to required signing."
|
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:
|
Options:
|
||||||
1) Install from Mozilla Add-ons (recommended):
|
1) Install from Mozilla Add-ons (recommended):
|
||||||
https://addons.mozilla.org/firefox/addon/leechblock-ng/
|
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
|
# Determine policy directories for detected Firefox-like browsers
|
||||||
declare -a POLICY_DIRS
|
declare -a POLICY_DIRS
|
||||||
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")
|
POLICY_DIRS+=("/etc/firefox/policies" "/usr/lib/firefox/distribution")
|
||||||
fi
|
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")
|
POLICY_DIRS+=("/etc/firefox-developer-edition/policies" "/usr/lib/firefox-developer-edition/distribution")
|
||||||
fi
|
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")
|
POLICY_DIRS+=("/etc/librewolf/policies" "/usr/lib/librewolf/distribution")
|
||||||
fi
|
fi
|
||||||
# Generic mozilla path as fallback
|
# 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
|
if sudo test -f "$existing"; then
|
||||||
info "Merging into existing policies.json at $existing"
|
info "Merging into existing policies.json at $existing"
|
||||||
sudo cp "$existing" "$tmp_pol"
|
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" '
|
merged=$(jq --arg id "$ADDON_ID" --arg url "$ADDON_AMO_URL" '
|
||||||
.policies |= (. // {}) |
|
.policies |= (. // {}) |
|
||||||
.policies.ExtensionSettings |= (. // {}) |
|
.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].installation_mode = "force_installed" |
|
||||||
.policies.ExtensionSettings[$id].install_url = $url
|
.policies.ExtensionSettings[$id].install_url = $url
|
||||||
' "$tmp_pol") || merged=""
|
' "$tmp_pol") || merged=""
|
||||||
if [[ -n "$merged" ]]; then
|
if [[ -n $merged ]]; then
|
||||||
printf '%s\n' "$merged" > "$tmp_pol"
|
printf '%s\n' "$merged" > "$tmp_pol"
|
||||||
else
|
else
|
||||||
warn "jq merge failed; skipping $pol_target"
|
warn "jq merge failed; skipping $pol_target"
|
||||||
rm -f "$tmp_pol"; continue
|
rm -f "$tmp_pol"
|
||||||
|
continue
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
warn "jq not available; creating minimal policies.json (existing file will be backed up)."
|
warn "jq not available; creating minimal policies.json (existing file will be backed up)."
|
||||||
sudo cp "$existing" "${existing}.bak.$(date +%s)"
|
sudo cp "$existing" "${existing}.bak.$(date +%s)"
|
||||||
cat > "$tmp_pol" <<JSON
|
cat > "$tmp_pol" << JSON
|
||||||
{
|
{
|
||||||
"policies": {
|
"policies": {
|
||||||
"ExtensionSettings": {
|
"ExtensionSettings": {
|
||||||
@ -325,7 +348,7 @@ JSON
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
info "Creating new policies.json at $pol_target"
|
info "Creating new policies.json at $pol_target"
|
||||||
cat > "$tmp_pol" <<JSON
|
cat > "$tmp_pol" << JSON
|
||||||
{
|
{
|
||||||
"policies": {
|
"policies": {
|
||||||
"ExtensionSettings": {
|
"ExtensionSettings": {
|
||||||
|
|||||||
@ -14,7 +14,6 @@ GREEN='\033[0;32m'
|
|||||||
YELLOW='\033[0;33m'
|
YELLOW='\033[0;33m'
|
||||||
BLUE='\033[0;34m'
|
BLUE='\033[0;34m'
|
||||||
CYAN='\033[0;36m'
|
CYAN='\033[0;36m'
|
||||||
BOLD='\033[1m'
|
|
||||||
NC='\033[0m' # No Color
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
# Script locations
|
# Script locations
|
||||||
@ -57,7 +56,7 @@ else
|
|||||||
echo -e "${YELLOW}Warning:${NC} Missing whitelist source at ${WHITELIST_SOURCE}${NC}"
|
echo -e "${YELLOW}Warning:${NC} Missing whitelist source at ${WHITELIST_SOURCE}${NC}"
|
||||||
fi
|
fi
|
||||||
chmod +x "$WRAPPER_DEST"
|
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
|
# Automatically use symbolic link installation method
|
||||||
echo -e "${YELLOW}Installing using symbolic link method...${NC}"
|
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}"
|
echo -e "${BLUE}Creating symbolic link...${NC}"
|
||||||
ln -sf "$WRAPPER_DEST" /usr/bin/pacman
|
ln -sf "$WRAPPER_DEST" /usr/bin/pacman
|
||||||
echo -e "${GREEN}Installation complete!${NC}"
|
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
@ -24,254 +24,262 @@ BELL="🔔"
|
|||||||
|
|
||||||
# Function to draw a box around text
|
# Function to draw a box around text
|
||||||
draw_box() {
|
draw_box() {
|
||||||
local text="$1"
|
local text="$1"
|
||||||
local width=${#text}
|
local width=${#text}
|
||||||
local padding=2
|
local padding=2
|
||||||
local total_width=$((width + padding * 2))
|
local total_width=$((width + padding * 2))
|
||||||
|
|
||||||
# Top border
|
# Top border
|
||||||
printf "┌"
|
printf "┌"
|
||||||
printf "─%.0s" $(seq 1 $total_width)
|
printf "─%.0s" $(seq 1 $total_width)
|
||||||
printf "┐\n"
|
printf "┐\n"
|
||||||
|
|
||||||
# Content with padding
|
# Content with padding
|
||||||
printf "│%*s%s%*s│\n" $padding "" "$text" $padding ""
|
printf "│%*s%s%*s│\n" $padding "" "$text" $padding ""
|
||||||
|
|
||||||
# Bottom border
|
# Bottom border
|
||||||
printf "└"
|
printf "└"
|
||||||
printf "─%.0s" $(seq 1 $total_width)
|
printf "─%.0s" $(seq 1 $total_width)
|
||||||
printf "┘\n"
|
printf "┘\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to show current day status
|
# Function to show current day status
|
||||||
show_day_status() {
|
show_day_status() {
|
||||||
local day_of_week=$(date +%u)
|
local day_of_week
|
||||||
local day_name=$(date +%A)
|
day_of_week=$(date +%u)
|
||||||
local today=$(date +%Y-%m-%d)
|
|
||||||
|
printf '%s%s Day Status%s\n' "$BLUE" "$CALENDAR" "$NC"
|
||||||
printf "${BLUE}${CALENDAR} Day Status${NC}\n"
|
printf '═══════════════\n'
|
||||||
printf "═══════════════\n"
|
|
||||||
|
# Show all days with status
|
||||||
# Show all days with status
|
local days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")
|
||||||
local days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")
|
local monitored=(1 0 0 0 1 1 1) # 1=monitored, 0=not monitored
|
||||||
local monitored=(1 0 0 0 1 1 1) # 1=monitored, 0=not monitored
|
|
||||||
|
for i in {0..6}; do
|
||||||
for i in {0..6}; do
|
local day_num=$((i + 1))
|
||||||
local day_num=$((i + 1))
|
if [[ $day_num -eq 7 ]]; then day_num=0; fi # Sunday is 0 in some contexts
|
||||||
if [[ $day_num -eq 7 ]]; then day_num=0; fi # Sunday is 0 in some contexts
|
|
||||||
|
if [[ ${monitored[$i]} -eq 1 ]]; then
|
||||||
if [[ ${monitored[$i]} -eq 1 ]]; then
|
if [[ $day_of_week -eq $((i + 1)) ]] || [[ $day_of_week -eq 7 && $i -eq 6 ]]; 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"
|
||||||
printf "${GREEN}${CHECK} ${days[$i]} (TODAY - MONITORED)${NC}\n"
|
else
|
||||||
else
|
printf '%s%s %s (monitored)%s\n' "$CYAN" "$CHECK" "${days[$i]}" "$NC"
|
||||||
printf "${CYAN}${CHECK} ${days[$i]} (monitored)${NC}\n"
|
fi
|
||||||
fi
|
else
|
||||||
else
|
if [[ $day_of_week -eq $((i + 1)) ]]; then
|
||||||
if [[ $day_of_week -eq $((i + 1)) ]]; then
|
printf '%s○ %s (TODAY - not monitored)%s\n' "$GRAY" "${days[$i]}" "$NC"
|
||||||
printf "${GRAY}○ ${days[$i]} (TODAY - not monitored)${NC}\n"
|
else
|
||||||
else
|
printf '%s○ %s%s\n' "$GRAY" "${days[$i]}" "$NC"
|
||||||
printf "${GRAY}○ ${days[$i]}${NC}\n"
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
done
|
||||||
done
|
|
||||||
|
printf "\n"
|
||||||
printf "\n"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to show time window status
|
# Function to show time window status
|
||||||
show_time_status() {
|
show_time_status() {
|
||||||
local current_hour=$(date +%H)
|
local current_hour current_minute current_hour_num
|
||||||
local current_minute=$(date +%M)
|
current_hour=$(date +%H)
|
||||||
local current_hour_num=$((10#$current_hour))
|
current_minute=$(date +%M)
|
||||||
|
current_hour_num=$((10#$current_hour))
|
||||||
printf "${YELLOW}${CLOCK} Time Window Status${NC}\n"
|
|
||||||
printf "═══════════════════════\n"
|
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"
|
# Show 24-hour timeline with window highlighted
|
||||||
printf "00 01 02 03 04 "
|
printf 'Timeline (24-hour format):\n'
|
||||||
printf "${GREEN}05 06 07${NC} "
|
printf '00 01 02 03 04 '
|
||||||
printf "08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23\n"
|
printf '%s05 06 07%s ' "$GREEN" "$NC"
|
||||||
printf " "
|
printf '08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23\n'
|
||||||
printf "${GREEN}▲─────▲${NC}\n"
|
printf ' '
|
||||||
printf " "
|
printf '%s▲─────▲%s\n' "$GREEN" "$NC"
|
||||||
printf "${GREEN}Expected Window${NC}\n"
|
printf ' '
|
||||||
|
printf '%sExpected Window%s\n' "$GREEN" "$NC"
|
||||||
# Current time indicator
|
|
||||||
printf "\nCurrent time: ${WHITE}%02d:%s${NC}\n" $current_hour_num "$current_minute"
|
# 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: ${GREEN}${CHECK} Within expected window (5AM-8AM)${NC}\n"
|
if [[ $current_hour_num -ge 5 && $current_hour_num -lt 8 ]]; then
|
||||||
else
|
printf 'Status: %s%s Within expected window (5AM-8AM)%s\n' "$GREEN" "$CHECK" "$NC"
|
||||||
printf "Status: ${YELLOW}○ Outside expected window${NC}\n"
|
else
|
||||||
fi
|
printf 'Status: %s○ Outside expected window%s\n' "$YELLOW" "$NC"
|
||||||
|
fi
|
||||||
printf "\n"
|
|
||||||
|
printf '\n'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to show boot time analysis
|
# Function to show boot time analysis
|
||||||
show_boot_analysis() {
|
show_boot_analysis() {
|
||||||
printf "${PURPLE}${COMPUTER} Boot Time Analysis${NC}\n"
|
printf '%s%s Boot Time Analysis%s\n' "$PURPLE" "$COMPUTER" "$NC"
|
||||||
printf "═══════════════════════\n"
|
printf '═══════════════════════\n'
|
||||||
|
|
||||||
# Get boot time
|
# Get boot time
|
||||||
local uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
|
local uptime_seconds boot_time boot_date boot_time_only boot_hour boot_hour_num today
|
||||||
local boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
|
uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0")
|
||||||
local boot_date=$(echo "$boot_time" | cut -d' ' -f1)
|
boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
|
||||||
local boot_time_only=$(echo "$boot_time" | cut -d' ' -f2)
|
boot_date=$(echo "$boot_time" | cut -d' ' -f1)
|
||||||
local boot_hour=$(echo "$boot_time_only" | cut -d':' -f1)
|
boot_time_only=$(echo "$boot_time" | cut -d' ' -f2)
|
||||||
local boot_hour_num=$((10#$boot_hour))
|
boot_hour=$(echo "$boot_time_only" | cut -d':' -f1)
|
||||||
local today=$(date +%Y-%m-%d)
|
boot_hour_num=$((10#$boot_hour))
|
||||||
|
today=$(date +%Y-%m-%d)
|
||||||
printf "System boot time: ${WHITE}$boot_time${NC}\n"
|
|
||||||
|
printf 'System boot time: %s%s%s\n' "$WHITE" "$boot_time" "$NC"
|
||||||
if [[ "$boot_date" == "$today" ]]; then
|
|
||||||
printf "Boot date: ${GREEN}${CHECK} Today${NC}\n"
|
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: ${GREEN}${CHECK} Within expected window (5AM-8AM)${NC}\n"
|
if [[ $boot_hour_num -ge 5 && $boot_hour_num -lt 8 ]]; then
|
||||||
printf "Status: ${GREEN}${CHECK} COMPLIANT${NC}\n"
|
printf 'Boot window: %s%s Within expected window (5AM-8AM)%s\n' "$GREEN" "$CHECK" "$NC"
|
||||||
else
|
printf 'Status: %s%s COMPLIANT%s\n' "$GREEN" "$CHECK" "$NC"
|
||||||
printf "Boot window: ${RED}${CROSS} Outside expected window${NC}\n"
|
|
||||||
printf "Status: ${RED}${WARNING} NON-COMPLIANT${NC}\n"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
printf "Boot date: ${YELLOW}○ Not today ($boot_date)${NC}\n"
|
printf 'Boot window: %s%s Outside expected window%s\n' "$RED" "$CROSS" "$NC"
|
||||||
printf "Status: ${YELLOW}○ System was not booted today${NC}\n"
|
printf 'Status: %s%s NON-COMPLIANT%s\n' "$RED" "$WARNING" "$NC"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
printf "\n"
|
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
|
# Function to show monitoring system status
|
||||||
show_system_status() {
|
show_system_status() {
|
||||||
printf "${CYAN}${BELL} Monitoring System${NC}\n"
|
printf '%s%s Monitoring System%s\n' "$CYAN" "$BELL" "$NC"
|
||||||
printf "═══════════════════════\n"
|
printf '═══════════════════════\n'
|
||||||
|
|
||||||
# Check if timer exists and is enabled
|
# Check if timer exists and is enabled
|
||||||
if systemctl is-enabled pc-startup-monitor.timer &>/dev/null; then
|
if systemctl is-enabled pc-startup-monitor.timer &> /dev/null; then
|
||||||
printf "Service: ${GREEN}${CHECK} ENABLED${NC}\n"
|
printf 'Service: %s%s ENABLED%s\n' "$GREEN" "$CHECK" "$NC"
|
||||||
|
|
||||||
if systemctl is-active pc-startup-monitor.timer &>/dev/null; then
|
if systemctl is-active pc-startup-monitor.timer &> /dev/null; then
|
||||||
printf "Timer: ${GREEN}${CHECK} ACTIVE${NC}\n"
|
printf 'Timer: %s%s ACTIVE%s\n' "$GREEN" "$CHECK" "$NC"
|
||||||
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"
|
|
||||||
|
|
||||||
else
|
else
|
||||||
printf "Service: ${RED}${CROSS} NOT ENABLED${NC}\n"
|
printf 'Timer: %s%s INACTIVE%s\n' "$RED" "$CROSS" "$NC"
|
||||||
printf "Timer: ${RED}${CROSS} NOT ACTIVE${NC}\n"
|
|
||||||
fi
|
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
|
# Function to show overall compliance status
|
||||||
show_compliance_overview() {
|
show_compliance_overview() {
|
||||||
local day_of_week=$(date +%u)
|
local day_of_week current_hour current_hour_num
|
||||||
local current_hour=$(date +%H)
|
day_of_week=$(date +%u)
|
||||||
local current_hour_num=$((10#$current_hour))
|
current_hour=$(date +%H)
|
||||||
|
current_hour_num=$((10#$current_hour))
|
||||||
# Check if today is monitored
|
|
||||||
local is_monitored=false
|
# Check if today is monitored
|
||||||
if [[ "$day_of_week" == "1" ]] || [[ "$day_of_week" == "5" ]] || [[ "$day_of_week" == "6" ]] || [[ "$day_of_week" == "7" ]]; then
|
local is_monitored=false
|
||||||
is_monitored=true
|
if [[ $day_of_week == "1" ]] || [[ $day_of_week == "5" ]] || [[ $day_of_week == "6" ]] || [[ $day_of_week == "7" ]]; then
|
||||||
fi
|
is_monitored=true
|
||||||
|
fi
|
||||||
printf "${WHITE}"
|
|
||||||
draw_box "COMPLIANCE OVERVIEW"
|
printf '%s' "$WHITE"
|
||||||
printf "${NC}\n"
|
draw_box "COMPLIANCE OVERVIEW"
|
||||||
|
printf '%s\n' "$NC"
|
||||||
if [[ "$is_monitored" == true ]]; then
|
|
||||||
printf "Today: ${GREEN}${CHECK} Monitored day${NC}\n"
|
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
|
# Check current compliance
|
||||||
printf "Current status: ${GREEN}${CHECK} PC is on during expected window${NC}\n"
|
if [[ $current_hour_num -ge 5 && $current_hour_num -lt 8 ]]; then
|
||||||
printf "Action needed: ${GREEN}None - currently compliant${NC}\n"
|
printf 'Current status: %s%s PC is on during expected window%s\n' "$GREEN" "$CHECK" "$NC"
|
||||||
else
|
printf 'Action needed: %sNone - currently compliant%s\n' "$GREEN" "$NC"
|
||||||
# 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
|
|
||||||
else
|
else
|
||||||
printf "Today: ${GRAY}○ Not a monitored day${NC}\n"
|
# Check if booted in window
|
||||||
printf "Current status: ${GRAY}No monitoring required${NC}\n"
|
local uptime_seconds boot_time boot_date boot_hour boot_hour_num today
|
||||||
printf "Action needed: ${GRAY}None${NC}\n"
|
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
|
fi
|
||||||
|
else
|
||||||
printf "\n"
|
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
|
# Function to show recent activity
|
||||||
show_recent_activity() {
|
show_recent_activity() {
|
||||||
printf "${GRAY}📋 Recent Activity${NC}\n"
|
printf '%s📋 Recent Activity%s\n' "$GRAY" "$NC"
|
||||||
printf "════════════════\n"
|
printf '════════════════\n'
|
||||||
|
|
||||||
# Show last 5 log entries
|
# 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")
|
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 "${GRAY}No recent monitoring activity${NC}\n"
|
if [[ $logs == "No logs found" ]]; then
|
||||||
else
|
printf '%sNo recent monitoring activity%s\n' "$GRAY" "$NC"
|
||||||
echo "$logs" | while IFS= read -r line; do
|
else
|
||||||
if [[ $line == *"WARNING"* ]]; then
|
echo "$logs" | while IFS= read -r line; do
|
||||||
printf "${RED}$line${NC}\n"
|
if [[ $line == *"WARNING"* ]]; then
|
||||||
elif [[ $line == *"compliance OK"* ]]; then
|
printf '%s%s%s\n' "$RED" "$line" "$NC"
|
||||||
printf "${GREEN}$line${NC}\n"
|
elif [[ $line == *"compliance OK"* ]]; then
|
||||||
else
|
printf '%s%s%s\n' "$GREEN" "$line" "$NC"
|
||||||
printf "${GRAY}$line${NC}\n"
|
else
|
||||||
fi
|
printf '%s%s%s\n' "$GRAY" "$line" "$NC"
|
||||||
done
|
fi
|
||||||
fi
|
done
|
||||||
|
fi
|
||||||
printf "\n"
|
|
||||||
|
printf '\n'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main display function
|
# Main display function
|
||||||
main() {
|
main() {
|
||||||
clear
|
clear
|
||||||
|
|
||||||
# Header
|
# Header
|
||||||
printf "${BLUE}"
|
printf '%s' "$BLUE"
|
||||||
draw_box "PC STARTUP MONITOR - VISUAL STATUS"
|
draw_box "PC STARTUP MONITOR - VISUAL STATUS"
|
||||||
printf "${NC}\n\n"
|
printf '%s\n\n' "$NC"
|
||||||
|
|
||||||
printf "${WHITE}Current Date/Time: $(date)${NC}\n"
|
local current_datetime system_uptime
|
||||||
printf "${WHITE}System Uptime: $(uptime -p)${NC}\n\n"
|
current_datetime=$(date)
|
||||||
|
system_uptime=$(uptime -p)
|
||||||
# Show all status sections
|
printf '%sCurrent Date/Time: %s%s\n' "$WHITE" "$current_datetime" "$NC"
|
||||||
show_day_status
|
printf '%sSystem Uptime: %s%s\n\n' "$WHITE" "$system_uptime" "$NC"
|
||||||
show_time_status
|
|
||||||
show_boot_analysis
|
# Show all status sections
|
||||||
show_system_status
|
show_day_status
|
||||||
show_compliance_overview
|
show_time_status
|
||||||
show_recent_activity
|
show_boot_analysis
|
||||||
|
show_system_status
|
||||||
# Footer with commands
|
show_compliance_overview
|
||||||
printf "${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"
|
show_recent_activity
|
||||||
printf "${WHITE}Commands:${NC}\n"
|
|
||||||
printf " ${CYAN}sudo pc-startup-monitor-manager.sh status${NC} - Show system status\n"
|
# Footer with commands
|
||||||
printf " ${CYAN}sudo pc-startup-monitor-manager.sh test${NC} - Test monitor now\n"
|
printf '%s═══════════════════════════════════════════════════════════════%s\n' "$BLUE" "$NC"
|
||||||
printf " ${CYAN}sudo pc-startup-monitor-manager.sh logs${NC} - View detailed logs\n"
|
printf '%sCommands:%s\n' "$WHITE" "$NC"
|
||||||
printf " ${CYAN}$0${NC} - Show this visual status\n"
|
printf ' %s%s%s - Show system status\n' "$CYAN" "sudo pc-startup-monitor-manager.sh status" "$NC"
|
||||||
printf "${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"
|
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
|
# Run main function
|
||||||
|
|||||||
@ -12,10 +12,10 @@ SCRIPT_NAME=$(basename "$0")
|
|||||||
UNDO=false
|
UNDO=false
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--undo) UNDO=true ;;
|
--undo) UNDO=true ;;
|
||||||
-h|--help)
|
-h | --help)
|
||||||
cat <<EOF
|
cat << EOF
|
||||||
Usage: $SCRIPT_NAME [--undo]
|
Usage: $SCRIPT_NAME [--undo]
|
||||||
|
|
||||||
Actions:
|
Actions:
|
||||||
@ -29,40 +29,40 @@ Notes:
|
|||||||
- Requires root privileges to write to /etc/* policy paths. Will self-elevate via sudo.
|
- Requires root privileges to write to /etc/* policy paths. Will self-elevate via sudo.
|
||||||
- Restart affected browsers to apply changes.
|
- Restart affected browsers to apply changes.
|
||||||
EOF
|
EOF
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Re-exec as root if needed
|
# Re-exec as root if needed
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
echo "[info] Elevating privileges with sudo..."
|
echo "[info] Elevating privileges with sudo..."
|
||||||
exec sudo -E bash "$0" "$@"
|
exec sudo -E bash "$0" "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Map binaries to a logical product key
|
# Map binaries to a logical product key
|
||||||
declare -A BIN_TO_KEY=(
|
declare -A BIN_TO_KEY=(
|
||||||
[thorium-browser]=thorium-browser
|
[thorium - browser]=thorium-browser
|
||||||
[thorium]=thorium-browser
|
[thorium]=thorium-browser
|
||||||
[chromium]=chromium
|
[chromium]=chromium
|
||||||
[google-chrome]=google-chrome
|
[google - chrome]=google-chrome
|
||||||
[google-chrome-stable]=google-chrome
|
[google - chrome - stable]=google-chrome
|
||||||
[brave-browser]=brave-browser
|
[brave - browser]=brave-browser
|
||||||
[vivaldi]=vivaldi
|
[vivaldi]=vivaldi
|
||||||
[vivaldi-stable]=vivaldi
|
[vivaldi - stable]=vivaldi
|
||||||
[microsoft-edge-stable]=microsoft-edge-stable
|
[microsoft - edge - stable]=microsoft-edge-stable
|
||||||
[opera]=opera
|
[opera]=opera
|
||||||
)
|
)
|
||||||
|
|
||||||
# Candidate policy directories per product key (first existing or first creatable is used)
|
# Candidate policy directories per product key (first existing or first creatable is used)
|
||||||
declare -A CANDIDATE_DIRS=(
|
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"
|
[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"
|
[chromium]="/etc/chromium/policies/managed"
|
||||||
[google-chrome]="/etc/opt/chrome/policies/managed"
|
[google - chrome]="/etc/opt/chrome/policies/managed"
|
||||||
[brave-browser]="/etc/opt/brave/policies/managed"
|
[brave - browser]="/etc/opt/brave/policies/managed"
|
||||||
[vivaldi]="/etc/opt/vivaldi/policies/managed"
|
[vivaldi]="/etc/opt/vivaldi/policies/managed"
|
||||||
[microsoft-edge-stable]="/etc/opt/edge/policies/managed"
|
[microsoft - edge - stable]="/etc/opt/edge/policies/managed"
|
||||||
[opera]="/etc/opt/opera/policies/managed"
|
[opera]="/etc/opt/opera/policies/managed"
|
||||||
)
|
)
|
||||||
|
|
||||||
POLICY_FILENAME="99-disable-guest-mode.json"
|
POLICY_FILENAME="99-disable-guest-mode.json"
|
||||||
@ -75,88 +75,89 @@ POLICY_JSON='{
|
|||||||
# Discover installed browsers
|
# Discover installed browsers
|
||||||
declare -A INSTALLED_KEYS=()
|
declare -A INSTALLED_KEYS=()
|
||||||
for bin in "${!BIN_TO_KEY[@]}"; do
|
for bin in "${!BIN_TO_KEY[@]}"; do
|
||||||
if command -v "$bin" >/dev/null 2>&1; then
|
if command -v "$bin" > /dev/null 2>&1; then
|
||||||
key=${BIN_TO_KEY[$bin]}
|
key=${BIN_TO_KEY[$bin]}
|
||||||
INSTALLED_KEYS[$key]=1
|
INSTALLED_KEYS[$key]=1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ ${#INSTALLED_KEYS[@]} -eq 0 ]]; then
|
if [[ ${#INSTALLED_KEYS[@]} -eq 0 ]]; then
|
||||||
echo "[warn] No supported Chromium-based browsers detected in PATH. Proceeding to configure Thorium paths anyway."
|
echo "[warn] No supported Chromium-based browsers detected in PATH. Proceeding to configure Thorium paths anyway."
|
||||||
INSTALLED_KEYS[thorium-browser]=1
|
INSTALLED_KEYS[thorium - browser]=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
choose_target_dir() {
|
choose_target_dir() {
|
||||||
local key="$1"
|
local key="$1"
|
||||||
local IFS=":"
|
local IFS=":"
|
||||||
local dirs
|
local dirs
|
||||||
read -r -a dirs <<< "${CANDIDATE_DIRS[$key]:-}"
|
read -r -a dirs <<< "${CANDIDATE_DIRS[$key]:-}"
|
||||||
# Prefer an existing directory; else pick the first candidate
|
# Prefer an existing directory; else pick the first candidate
|
||||||
for d in "${dirs[@]}"; do
|
for d in "${dirs[@]}"; do
|
||||||
if [[ -d "$d" ]]; then
|
if [[ -d $d ]]; then
|
||||||
echo "$d"
|
echo "$d"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
echo "${dirs[0]}"
|
echo "${dirs[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply_policy() {
|
apply_policy() {
|
||||||
local target_dir="$1"; shift
|
local target_dir="$1"
|
||||||
local file="$target_dir/$POLICY_FILENAME"
|
shift
|
||||||
|
local file="$target_dir/$POLICY_FILENAME"
|
||||||
|
|
||||||
echo "[apply] $file"
|
echo "[apply] $file"
|
||||||
|
|
||||||
mkdir -p "$target_dir"
|
mkdir -p "$target_dir"
|
||||||
# Write atomically
|
# Write atomically
|
||||||
local tmp
|
local tmp
|
||||||
tmp=$(mktemp)
|
tmp=$(mktemp)
|
||||||
printf '%s
|
printf '%s
|
||||||
' "$POLICY_JSON" >"$tmp"
|
' "$POLICY_JSON" > "$tmp"
|
||||||
install -m 0644 "$tmp" "$file"
|
install -m 0644 "$tmp" "$file"
|
||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_policy() {
|
remove_policy() {
|
||||||
local target_dir="$1"; shift
|
local target_dir="$1"
|
||||||
local file="$target_dir/$POLICY_FILENAME"
|
shift
|
||||||
|
local file="$target_dir/$POLICY_FILENAME"
|
||||||
|
|
||||||
if [[ -f "$file" ]]; then
|
if [[ -f $file ]]; then
|
||||||
echo "[remove] $file"
|
echo "[remove] $file"
|
||||||
rm -f -- "$file"
|
rm -f -- "$file"
|
||||||
else
|
else
|
||||||
echo "[skip] $file (not present)"
|
echo "[skip] $file (not present)"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
changed_any=false
|
changed_any=false
|
||||||
|
|
||||||
for key in "${!INSTALLED_KEYS[@]}"; do
|
for key in "${!INSTALLED_KEYS[@]}"; do
|
||||||
# If we somehow lack candidate dirs for a key, skip gracefully
|
# If we somehow lack candidate dirs for a key, skip gracefully
|
||||||
if [[ -z "${CANDIDATE_DIRS[$key]:-}" ]]; then
|
if [[ -z ${CANDIDATE_DIRS[$key]:-} ]]; then
|
||||||
echo "[warn] No known policy directories for '$key'; skipping."
|
echo "[warn] No known policy directories for '$key'; skipping."
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
target_dir=$(choose_target_dir "$key")
|
target_dir=$(choose_target_dir "$key")
|
||||||
|
|
||||||
if [[ "$UNDO" == true ]]; then
|
if [[ $UNDO == true ]]; then
|
||||||
remove_policy "$target_dir"
|
remove_policy "$target_dir"
|
||||||
else
|
else
|
||||||
apply_policy "$target_dir"
|
apply_policy "$target_dir"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
changed_any=true
|
changed_any=true
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ "$changed_any" == false ]]; then
|
if [[ $changed_any == false ]]; then
|
||||||
echo "[info] Nothing to do."
|
echo "[info] Nothing to do."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$UNDO" == true ]]; then
|
if [[ $UNDO == true ]]; then
|
||||||
echo "[done] Guest mode policy files removed where present. You may need to restart the browsers."
|
echo "[done] Guest mode policy files removed where present. You may need to restart the browsers."
|
||||||
else
|
else
|
||||||
echo "[done] Guest mode disabled via managed policies. Please fully restart affected browsers."
|
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 " If the Guest option still appears, it should be disabled/greyed out."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@ -4,194 +4,190 @@
|
|||||||
# Thursday-Sunday: Shutdown between 22:00-05:00
|
# Thursday-Sunday: Shutdown between 22:00-05:00
|
||||||
# Handles sudo privileges automatically
|
# Handles sudo privileges automatically
|
||||||
|
|
||||||
set -e # Exit on any error
|
set -e # Exit on any error
|
||||||
|
|
||||||
# Function to show usage
|
# Function to show usage
|
||||||
show_usage() {
|
show_usage() {
|
||||||
echo "Day-Specific Auto-Shutdown Setup for Arch Linux"
|
echo "Day-Specific Auto-Shutdown Setup for Arch Linux"
|
||||||
echo "==============================================="
|
echo "==============================================="
|
||||||
echo "Usage: $0 [enable|disable|status]"
|
echo "Usage: $0 [enable|disable|status]"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Commands:"
|
echo "Commands:"
|
||||||
echo " enable - Set up automatic shutdown with day-specific windows (default)"
|
echo " enable - Set up automatic shutdown with day-specific windows (default)"
|
||||||
echo " disable - Remove automatic shutdown"
|
echo " disable - Remove automatic shutdown"
|
||||||
echo " status - Show current status"
|
echo " status - Show current status"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Shutdown Schedule:"
|
echo "Shutdown Schedule:"
|
||||||
echo " Monday-Wednesday: 21:00-05:00"
|
echo " Monday-Wednesday: 21:00-05:00"
|
||||||
echo " Thursday-Sunday: 22:00-05:00"
|
echo " Thursday-Sunday: 22:00-05:00"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to check and request sudo privileges
|
# Function to check and request sudo privileges
|
||||||
check_sudo() {
|
check_sudo() {
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
echo "This script requires sudo privileges to manage systemd services."
|
echo "This script requires sudo privileges to manage systemd services."
|
||||||
echo "Requesting sudo access..."
|
echo "Requesting sudo access..."
|
||||||
exec sudo "$0" "$@"
|
exec sudo "$0" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get the actual user (even when running with sudo)
|
# Get the actual user (even when running with sudo)
|
||||||
if [[ -n "$SUDO_USER" ]]; then
|
if [[ -n $SUDO_USER ]]; then
|
||||||
ACTUAL_USER="$SUDO_USER"
|
ACTUAL_USER="$SUDO_USER"
|
||||||
USER_HOME="/home/$SUDO_USER"
|
USER_HOME="/home/$SUDO_USER"
|
||||||
else
|
else
|
||||||
ACTUAL_USER="$USER"
|
ACTUAL_USER="$USER"
|
||||||
USER_HOME="$HOME"
|
USER_HOME="$HOME"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Function to disable and remove midnight shutdown
|
# Function to disable and remove midnight shutdown
|
||||||
disable_midnight_shutdown() {
|
disable_midnight_shutdown() {
|
||||||
echo "Disabling Day-Specific Auto-Shutdown"
|
echo "Disabling Day-Specific Auto-Shutdown"
|
||||||
echo "===================================="
|
echo "===================================="
|
||||||
echo "Current Date: $(date)"
|
echo "Current Date: $(date)"
|
||||||
echo "User: $ACTUAL_USER"
|
echo "User: $ACTUAL_USER"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
local timer_file="/etc/systemd/system/day-specific-shutdown.timer"
|
local timer_file="/etc/systemd/system/day-specific-shutdown.timer"
|
||||||
local service_file="/etc/systemd/system/day-specific-shutdown.service"
|
local service_file="/etc/systemd/system/day-specific-shutdown.service"
|
||||||
local script_file="/usr/local/bin/day-specific-shutdown-manager.sh"
|
local script_file="/usr/local/bin/day-specific-shutdown-manager.sh"
|
||||||
local check_script="/usr/local/bin/day-specific-shutdown-check.sh"
|
local check_script="/usr/local/bin/day-specific-shutdown-check.sh"
|
||||||
local removed_files=()
|
local removed_files=()
|
||||||
|
|
||||||
# Stop and disable timer if it exists
|
# Stop and disable timer if it exists
|
||||||
if systemctl is-active day-specific-shutdown.timer &>/dev/null; then
|
if systemctl is-active day-specific-shutdown.timer &> /dev/null; then
|
||||||
echo "Stopping day-specific-shutdown timer..."
|
echo "Stopping day-specific-shutdown timer..."
|
||||||
systemctl stop day-specific-shutdown.timer
|
systemctl stop day-specific-shutdown.timer
|
||||||
echo "✓ Timer stopped"
|
echo "✓ Timer stopped"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if systemctl is-enabled day-specific-shutdown.timer &>/dev/null; then
|
if systemctl is-enabled day-specific-shutdown.timer &> /dev/null; then
|
||||||
echo "Disabling day-specific-shutdown timer..."
|
echo "Disabling day-specific-shutdown timer..."
|
||||||
systemctl disable day-specific-shutdown.timer
|
systemctl disable day-specific-shutdown.timer
|
||||||
echo "✓ Timer disabled"
|
echo "✓ Timer disabled"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove timer file
|
# Remove timer file
|
||||||
if [[ -f "$timer_file" ]]; then
|
if [[ -f $timer_file ]]; then
|
||||||
rm -f "$timer_file"
|
rm -f "$timer_file"
|
||||||
removed_files+=("$timer_file")
|
removed_files+=("$timer_file")
|
||||||
echo "✓ Removed timer file: $timer_file"
|
echo "✓ Removed timer file: $timer_file"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove service file
|
# Remove service file
|
||||||
if [[ -f "$service_file" ]]; then
|
if [[ -f $service_file ]]; then
|
||||||
rm -f "$service_file"
|
rm -f "$service_file"
|
||||||
removed_files+=("$service_file")
|
removed_files+=("$service_file")
|
||||||
echo "✓ Removed service file: $service_file"
|
echo "✓ Removed service file: $service_file"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove management script
|
# Remove management script
|
||||||
if [[ -f "$script_file" ]]; then
|
if [[ -f $script_file ]]; then
|
||||||
rm -f "$script_file"
|
rm -f "$script_file"
|
||||||
removed_files+=("$script_file")
|
removed_files+=("$script_file")
|
||||||
echo "✓ Removed management script: $script_file"
|
echo "✓ Removed management script: $script_file"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove check script
|
# Remove check script
|
||||||
if [[ -f "$check_script" ]]; then
|
if [[ -f $check_script ]]; then
|
||||||
rm -f "$check_script"
|
rm -f "$check_script"
|
||||||
removed_files+=("$check_script")
|
removed_files+=("$check_script")
|
||||||
echo "✓ Removed check script: $check_script"
|
echo "✓ Removed check script: $check_script"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Reload systemd daemon
|
# Reload systemd daemon
|
||||||
if [[ ${#removed_files[@]} -gt 0 ]]; then
|
if [[ ${#removed_files[@]} -gt 0 ]]; then
|
||||||
echo "Reloading systemd daemon..."
|
echo "Reloading systemd daemon..."
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
echo "✓ Systemd daemon reloaded"
|
echo "✓ Systemd daemon reloaded"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "============================================="
|
echo "============================================="
|
||||||
echo "Day-Specific Auto-Shutdown Removal Complete"
|
echo "Day-Specific Auto-Shutdown Removal Complete"
|
||||||
echo "============================================="
|
echo "============================================="
|
||||||
|
|
||||||
if [[ ${#removed_files[@]} -gt 0 ]]; then
|
if [[ ${#removed_files[@]} -gt 0 ]]; then
|
||||||
echo "Removed files:"
|
echo "Removed files:"
|
||||||
printf " %s\n" "${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 ""
|
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
|
# Function to show current status
|
||||||
show_current_status() {
|
show_current_status() {
|
||||||
echo "Day-Specific Auto-Shutdown Status"
|
echo "Day-Specific Auto-Shutdown Status"
|
||||||
echo "================================="
|
echo "================================="
|
||||||
echo "Current Date: $(date)"
|
echo "Current Date: $(date)"
|
||||||
echo "User: $ACTUAL_USER"
|
echo "User: $ACTUAL_USER"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
local timer_exists=false
|
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
|
||||||
# Check if files exist
|
timer_exists=true
|
||||||
if [[ -f "/etc/systemd/system/day-specific-shutdown.timer" ]]; then
|
echo "✓ Timer file exists"
|
||||||
timer_exists=true
|
else
|
||||||
echo "✓ Timer file exists"
|
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
|
else
|
||||||
echo "✗ Timer file missing"
|
echo "✗ Timer is not enabled"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
if [[ -f "/etc/systemd/system/day-specific-shutdown.service" ]]; then
|
echo "Status: NOT CONFIGURED"
|
||||||
service_exists=true
|
fi
|
||||||
echo "✓ Service file exists"
|
|
||||||
else
|
echo ""
|
||||||
echo "✗ Service file missing"
|
echo "Shutdown Schedule:"
|
||||||
fi
|
echo " Monday-Wednesday: 21:00-05:00"
|
||||||
|
echo " Thursday-Sunday: 22:00-05:00"
|
||||||
if [[ -f "/usr/local/bin/day-specific-shutdown-manager.sh" ]]; then
|
echo ""
|
||||||
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 ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create the shutdown service
|
# Function to create the shutdown service
|
||||||
create_shutdown_service() {
|
create_shutdown_service() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "1. Creating Systemd Shutdown Service..."
|
echo "1. Creating Systemd Shutdown Service..."
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
|
|
||||||
local service_file="/etc/systemd/system/day-specific-shutdown.service"
|
local service_file="/etc/systemd/system/day-specific-shutdown.service"
|
||||||
|
|
||||||
cat > "$service_file" << 'EOF'
|
cat > "$service_file" << 'EOF'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Automatic PC shutdown with day-specific time windows
|
Description=Automatic PC shutdown with day-specific time windows
|
||||||
DefaultDependencies=false
|
DefaultDependencies=false
|
||||||
@ -205,18 +201,18 @@ StandardOutput=journal
|
|||||||
StandardError=journal
|
StandardError=journal
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✓ Created systemd service: $service_file"
|
echo "✓ Created systemd service: $service_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create the shutdown timer
|
# Function to create the shutdown timer
|
||||||
create_shutdown_timer() {
|
create_shutdown_timer() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. Creating Systemd Shutdown Timer..."
|
echo "2. Creating Systemd Shutdown Timer..."
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
|
|
||||||
local timer_file="/etc/systemd/system/day-specific-shutdown.timer"
|
local timer_file="/etc/systemd/system/day-specific-shutdown.timer"
|
||||||
|
|
||||||
cat > "$timer_file" << 'EOF'
|
cat > "$timer_file" << 'EOF'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Timer for automatic PC shutdown with day-specific windows
|
Description=Timer for automatic PC shutdown with day-specific windows
|
||||||
Requires=day-specific-shutdown.service
|
Requires=day-specific-shutdown.service
|
||||||
@ -248,18 +244,18 @@ RandomizedDelaySec=0
|
|||||||
WantedBy=timers.target
|
WantedBy=timers.target
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✓ Created systemd timer: $timer_file"
|
echo "✓ Created systemd timer: $timer_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create management script
|
# Function to create management script
|
||||||
create_management_script() {
|
create_management_script() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. Creating Management Script..."
|
echo "3. Creating Management Script..."
|
||||||
echo "=============================="
|
echo "=============================="
|
||||||
|
|
||||||
local script_file="/usr/local/bin/day-specific-shutdown-manager.sh"
|
local script_file="/usr/local/bin/day-specific-shutdown-manager.sh"
|
||||||
|
|
||||||
cat > "$script_file" << 'EOF'
|
cat > "$script_file" << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Day-Specific Auto-Shutdown Manager
|
# Day-Specific Auto-Shutdown Manager
|
||||||
# Provides easy management of the day-specific shutdown feature
|
# Provides easy management of the day-specific shutdown feature
|
||||||
@ -322,19 +318,19 @@ case "$1" in
|
|||||||
esac
|
esac
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
chmod +x "$script_file"
|
chmod +x "$script_file"
|
||||||
echo "✓ Created management script: $script_file"
|
echo "✓ Created management script: $script_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create smart shutdown check script
|
# Function to create smart shutdown check script
|
||||||
create_shutdown_check_script() {
|
create_shutdown_check_script() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. Creating Smart Shutdown Check Script..."
|
echo "4. Creating Smart Shutdown Check Script..."
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
|
|
||||||
local check_script="/usr/local/bin/day-specific-shutdown-check.sh"
|
local check_script="/usr/local/bin/day-specific-shutdown-check.sh"
|
||||||
|
|
||||||
cat > "$check_script" << 'EOF'
|
cat > "$check_script" << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Smart day-specific shutdown check script
|
# Smart day-specific shutdown check script
|
||||||
# Different shutdown windows based on day of week:
|
# Different shutdown windows based on day of week:
|
||||||
@ -401,206 +397,206 @@ else
|
|||||||
fi
|
fi
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
chmod +x "$check_script"
|
chmod +x "$check_script"
|
||||||
echo "✓ Created smart shutdown check script: $check_script"
|
echo "✓ Created smart shutdown check script: $check_script"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to enable the timer
|
# Function to enable the timer
|
||||||
enable_timer() {
|
enable_timer() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "5. Enabling Shutdown Timer..."
|
echo "5. Enabling Shutdown Timer..."
|
||||||
echo "============================"
|
echo "============================"
|
||||||
|
|
||||||
# Reload systemd daemon
|
# Reload systemd daemon
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
echo "✓ Reloaded systemd daemon"
|
echo "✓ Reloaded systemd daemon"
|
||||||
|
|
||||||
# Enable the timer
|
# Enable the timer
|
||||||
systemctl enable day-specific-shutdown.timer
|
systemctl enable day-specific-shutdown.timer
|
||||||
echo "✓ Enabled day-specific-shutdown timer"
|
echo "✓ Enabled day-specific-shutdown timer"
|
||||||
|
|
||||||
# Start the timer
|
# Start the timer
|
||||||
systemctl start day-specific-shutdown.timer
|
systemctl start day-specific-shutdown.timer
|
||||||
echo "✓ Started day-specific-shutdown timer"
|
echo "✓ Started day-specific-shutdown timer"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to test the setup
|
# Function to test the setup
|
||||||
test_setup() {
|
test_setup() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "6. Testing Setup..."
|
echo "6. Testing Setup..."
|
||||||
echo "=================="
|
echo "=================="
|
||||||
|
|
||||||
echo "Service files:"
|
echo "Service files:"
|
||||||
if [[ -f "/etc/systemd/system/day-specific-shutdown.service" ]]; then
|
if [[ -f "/etc/systemd/system/day-specific-shutdown.service" ]]; then
|
||||||
echo "✓ Service file exists"
|
echo "✓ Service file exists"
|
||||||
else
|
else
|
||||||
echo "✗ Service file missing"
|
echo "✗ Service file missing"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "/etc/systemd/system/day-specific-shutdown.timer" ]]; then
|
if [[ -f "/etc/systemd/system/day-specific-shutdown.timer" ]]; then
|
||||||
echo "✓ Timer file exists"
|
echo "✓ Timer file exists"
|
||||||
else
|
else
|
||||||
echo "✗ Timer file missing"
|
echo "✗ Timer file missing"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Timer status:"
|
echo "Timer status:"
|
||||||
if systemctl is-enabled day-specific-shutdown.timer &>/dev/null; then
|
if systemctl is-enabled day-specific-shutdown.timer &> /dev/null; then
|
||||||
echo "✓ Timer is enabled"
|
echo "✓ Timer is enabled"
|
||||||
else
|
else
|
||||||
echo "✗ Timer is not enabled"
|
echo "✗ Timer is not enabled"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if systemctl is-active day-specific-shutdown.timer &>/dev/null; then
|
if systemctl is-active day-specific-shutdown.timer &> /dev/null; then
|
||||||
echo "✓ Timer is active"
|
echo "✓ Timer is active"
|
||||||
else
|
else
|
||||||
echo "✗ Timer is not active"
|
echo "✗ Timer is not active"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Next scheduled checks:"
|
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"
|
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
|
# Function to show final instructions
|
||||||
show_instructions() {
|
show_instructions() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "================================================="
|
echo "================================================="
|
||||||
echo "Day-Specific Auto-Shutdown Setup Complete"
|
echo "Day-Specific Auto-Shutdown Setup Complete"
|
||||||
echo "================================================="
|
echo "================================================="
|
||||||
echo "Summary:"
|
echo "Summary:"
|
||||||
echo "✓ Systemd service created (/etc/systemd/system/day-specific-shutdown.service)"
|
echo "✓ Systemd service created (/etc/systemd/system/day-specific-shutdown.service)"
|
||||||
echo "✓ Systemd timer created (/etc/systemd/system/day-specific-shutdown.timer)"
|
echo "✓ Systemd timer created (/etc/systemd/system/day-specific-shutdown.timer)"
|
||||||
echo "✓ Management script created (/usr/local/bin/day-specific-shutdown-manager.sh)"
|
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 "✓ Smart check script created (/usr/local/bin/day-specific-shutdown-check.sh)"
|
||||||
echo "✓ Timer enabled and started"
|
echo "✓ Timer enabled and started"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Shutdown Schedule:"
|
echo "Shutdown Schedule:"
|
||||||
echo " Monday-Wednesday: 21:00-05:00 (9:00 PM to 5:00 AM)"
|
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 " Thursday-Sunday: 22:00-05:00 (10:00 PM to 5:00 AM)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Management commands:"
|
echo "Management commands:"
|
||||||
echo " sudo day-specific-shutdown-manager.sh status - Check status"
|
echo " sudo day-specific-shutdown-manager.sh status - Check status"
|
||||||
echo " sudo day-specific-shutdown-manager.sh logs - View shutdown logs"
|
echo " sudo day-specific-shutdown-manager.sh logs - View shutdown logs"
|
||||||
echo ""
|
echo ""
|
||||||
echo "How it works:"
|
echo "How it works:"
|
||||||
echo "• Timer checks every 30 minutes during potential shutdown windows"
|
echo "• Timer checks every 30 minutes during potential shutdown windows"
|
||||||
echo "• Smart logic determines shutdown eligibility based on day and time"
|
echo "• Smart logic determines shutdown eligibility based on day and time"
|
||||||
echo "• Prevents accidental shutdowns outside designated time windows"
|
echo "• Prevents accidental shutdowns outside designated time windows"
|
||||||
echo ""
|
echo ""
|
||||||
echo "WARNING: This will automatically shutdown your PC during designated hours."
|
echo "WARNING: This will automatically shutdown your PC during designated hours."
|
||||||
echo "Make sure to save your work before the shutdown windows!"
|
echo "Make sure to save your work before the shutdown windows!"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to prompt for confirmation
|
# Function to prompt for confirmation
|
||||||
confirm_setup() {
|
confirm_setup() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "WARNING: Day-Specific Auto-Shutdown Confirmation"
|
echo "WARNING: Day-Specific Auto-Shutdown Confirmation"
|
||||||
echo "==============================================="
|
echo "==============================================="
|
||||||
echo "This will set up your PC to automatically shutdown during specific time windows."
|
echo "This will set up your PC to automatically shutdown during specific time windows."
|
||||||
echo ""
|
echo ""
|
||||||
echo "Shutdown Schedule:"
|
echo "Shutdown Schedule:"
|
||||||
echo " Monday-Wednesday: 21:00-05:00 (9:00 PM to 5:00 AM)"
|
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 " Thursday-Sunday: 22:00-05:00 (10:00 PM to 5:00 AM)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Important considerations:"
|
echo "Important considerations:"
|
||||||
echo "- Any unsaved work will be lost during shutdown windows"
|
echo "- Any unsaved work will be lost during shutdown windows"
|
||||||
echo "- Running processes will be terminated"
|
echo "- Running processes will be terminated"
|
||||||
echo "- Downloads/uploads in progress will be interrupted"
|
echo "- Downloads/uploads in progress will be interrupted"
|
||||||
echo "- You'll need to manually power on your PC each day"
|
echo "- You'll need to manually power on your PC each day"
|
||||||
echo "- Timer checks every 30 minutes during potential shutdown windows"
|
echo "- Timer checks every 30 minutes during potential shutdown windows"
|
||||||
echo ""
|
echo ""
|
||||||
read -p "Do you want to proceed? (y/N): " confirm
|
read -r -p "Do you want to proceed? (y/N): " confirm
|
||||||
|
|
||||||
case "$confirm" in
|
case "$confirm" in
|
||||||
[yY]|[yY][eE][sS])
|
[yY] | [yY][eE][sS])
|
||||||
echo "Proceeding with setup..."
|
echo "Proceeding with setup..."
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Setup cancelled."
|
echo "Setup cancelled."
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to confirm disable
|
# Function to confirm disable
|
||||||
confirm_disable() {
|
confirm_disable() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "Disable Day-Specific Auto-Shutdown Confirmation"
|
echo "Disable Day-Specific Auto-Shutdown Confirmation"
|
||||||
echo "==============================================="
|
echo "==============================================="
|
||||||
echo "This will completely remove the automatic day-specific shutdown configuration."
|
echo "This will completely remove the automatic day-specific shutdown configuration."
|
||||||
echo ""
|
echo ""
|
||||||
echo "After disabling:"
|
echo "After disabling:"
|
||||||
echo "- Your PC will no longer shutdown automatically during any time windows"
|
echo "- Your PC will no longer shutdown automatically during any time windows"
|
||||||
echo "- All related systemd services and timers will be removed"
|
echo "- All related systemd services and timers will be removed"
|
||||||
echo "- The management and check scripts will be deleted"
|
echo "- The management and check scripts will be deleted"
|
||||||
echo ""
|
echo ""
|
||||||
read -p "Do you want to proceed with disabling? (y/N): " confirm
|
read -r -p "Do you want to proceed with disabling? (y/N): " confirm
|
||||||
|
|
||||||
case "$confirm" in
|
case "$confirm" in
|
||||||
[yY]|[yY][eE][sS])
|
[yY] | [yY][eE][sS])
|
||||||
echo "Proceeding with disable..."
|
echo "Proceeding with disable..."
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Disable cancelled."
|
echo "Disable cancelled."
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main execution flow for enable
|
# Main execution flow for enable
|
||||||
enable_midnight_shutdown() {
|
enable_midnight_shutdown() {
|
||||||
echo "Day-Specific Auto-Shutdown Setup for Arch Linux"
|
echo "Day-Specific Auto-Shutdown Setup for Arch Linux"
|
||||||
echo "==============================================="
|
echo "==============================================="
|
||||||
echo "Current Date: $(date)"
|
echo "Current Date: $(date)"
|
||||||
echo "User: $ACTUAL_USER"
|
echo "User: $ACTUAL_USER"
|
||||||
echo "Target user: $ACTUAL_USER"
|
echo "Target user: $ACTUAL_USER"
|
||||||
echo "User home: $USER_HOME"
|
echo "User home: $USER_HOME"
|
||||||
|
|
||||||
# Confirm setup
|
# Confirm setup
|
||||||
confirm_setup
|
confirm_setup
|
||||||
|
|
||||||
# Create systemd files
|
# Create systemd files
|
||||||
create_shutdown_service
|
create_shutdown_service
|
||||||
create_shutdown_timer
|
create_shutdown_timer
|
||||||
create_management_script
|
create_management_script
|
||||||
create_shutdown_check_script
|
create_shutdown_check_script
|
||||||
|
|
||||||
# Enable and start timer
|
# Enable and start timer
|
||||||
enable_timer
|
enable_timer
|
||||||
|
|
||||||
# Test setup
|
# Test setup
|
||||||
test_setup
|
test_setup
|
||||||
|
|
||||||
# Show instructions
|
# Show instructions
|
||||||
show_instructions
|
show_instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
case "${1:-enable}" in
|
case "${1:-enable}" in
|
||||||
"enable")
|
"enable")
|
||||||
check_sudo "$@"
|
check_sudo "$@"
|
||||||
enable_midnight_shutdown
|
enable_midnight_shutdown
|
||||||
;;
|
;;
|
||||||
"disable")
|
"disable")
|
||||||
check_sudo "$@"
|
check_sudo "$@"
|
||||||
confirm_disable
|
confirm_disable
|
||||||
disable_midnight_shutdown
|
disable_midnight_shutdown
|
||||||
;;
|
;;
|
||||||
"status")
|
"status")
|
||||||
check_sudo "$@"
|
check_sudo "$@"
|
||||||
show_current_status
|
show_current_status
|
||||||
;;
|
;;
|
||||||
"help"|"-h"|"--help")
|
"help" | "-h" | "--help")
|
||||||
show_usage
|
show_usage
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Error: Unknown command '$1'"
|
echo "Error: Unknown command '$1'"
|
||||||
echo ""
|
echo ""
|
||||||
show_usage
|
show_usage
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@ -3,59 +3,59 @@
|
|||||||
# Checks if PC was turned on between 5AM-8AM on Monday, Friday, Saturday, Sunday
|
# Checks if PC was turned on between 5AM-8AM on Monday, Friday, Saturday, Sunday
|
||||||
# Handles sudo privileges automatically
|
# Handles sudo privileges automatically
|
||||||
|
|
||||||
set -e # Exit on any error
|
set -e # Exit on any error
|
||||||
|
|
||||||
# Default to non-interactive mode
|
# Default to non-interactive mode
|
||||||
INTERACTIVE_MODE=false
|
INTERACTIVE_MODE=false
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
-i|--interactive)
|
-i | --interactive)
|
||||||
INTERACTIVE_MODE=true
|
INTERACTIVE_MODE=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h | --help)
|
||||||
echo "Usage: $0 [OPTIONS]"
|
echo "Usage: $0 [OPTIONS]"
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
|
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
|
||||||
echo " -h, --help Show this help message"
|
echo " -h, --help Show this help message"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown option: $1"
|
echo "Unknown option: $1"
|
||||||
echo "Use -h or --help for usage information"
|
echo "Use -h or --help for usage information"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "PC Startup Time Monitor for Arch Linux"
|
echo "PC Startup Time Monitor for Arch Linux"
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
echo "Current Date: $(date)"
|
echo "Current Date: $(date)"
|
||||||
echo "User: ${SUDO_USER:-$USER}"
|
echo "User: ${SUDO_USER:-$USER}"
|
||||||
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
|
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||||
echo "Mode: Interactive (prompts enabled)"
|
echo "Mode: Interactive (prompts enabled)"
|
||||||
else
|
else
|
||||||
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
|
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Function to check and request sudo privileges
|
# Function to check and request sudo privileges
|
||||||
check_sudo() {
|
check_sudo() {
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
echo "This script requires sudo privileges to access system logs and create services."
|
echo "This script requires sudo privileges to access system logs and create services."
|
||||||
echo "Requesting sudo access..."
|
echo "Requesting sudo access..."
|
||||||
exec sudo "$0" "$@"
|
exec sudo "$0" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get the actual user (even when running with sudo)
|
# Get the actual user (even when running with sudo)
|
||||||
if [[ -n "$SUDO_USER" ]]; then
|
if [[ -n $SUDO_USER ]]; then
|
||||||
ACTUAL_USER="$SUDO_USER"
|
ACTUAL_USER="$SUDO_USER"
|
||||||
USER_HOME="/home/$SUDO_USER"
|
USER_HOME="/home/$SUDO_USER"
|
||||||
else
|
else
|
||||||
ACTUAL_USER="$USER"
|
ACTUAL_USER="$USER"
|
||||||
USER_HOME="$HOME"
|
USER_HOME="$HOME"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Target user: $ACTUAL_USER"
|
echo "Target user: $ACTUAL_USER"
|
||||||
@ -63,138 +63,147 @@ echo "User home: $USER_HOME"
|
|||||||
|
|
||||||
# Function to check if today is a monitored day
|
# Function to check if today is a monitored day
|
||||||
is_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
|
# Check if today is Monday (1), Friday (5), Saturday (6), or Sunday (7)
|
||||||
return 0 # Yes, it's a monitored day
|
if [[ $day_of_week == "1" ]] || [[ $day_of_week == "5" ]] || [[ $day_of_week == "6" ]] || [[ $day_of_week == "7" ]]; then
|
||||||
else
|
return 0 # Yes, it's a monitored day
|
||||||
return 1 # No, it's not a monitored day
|
else
|
||||||
fi
|
return 1 # No, it's not a monitored day
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to check if current time is between 5AM and 8AM
|
# Function to check if current time is between 5AM and 8AM
|
||||||
is_current_time_in_window() {
|
is_current_time_in_window() {
|
||||||
local current_hour=$(date +%H)
|
local current_hour current_hour_num
|
||||||
local current_hour_num=$((10#$current_hour)) # Convert to decimal to avoid octal issues
|
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
|
if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
|
||||||
else
|
return 0 # Yes, current time is in the 5AM-8AM window
|
||||||
return 1 # No, current time is outside the window
|
else
|
||||||
fi
|
return 1 # No, current time is outside the window
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to check if PC was booted between 5AM-8AM today
|
# Function to check if PC was booted between 5AM-8AM today
|
||||||
was_booted_in_window_today() {
|
was_booted_in_window_today() {
|
||||||
local today=$(date +%Y-%m-%d)
|
local today boot_time
|
||||||
local 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
|
# Get the last boot time using multiple methods for reliability
|
||||||
# Method 1: Calculate boot time from uptime
|
if command -v uptime &> /dev/null; then
|
||||||
local uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
|
# Method 1: Calculate boot time from uptime
|
||||||
if [[ $uptime_seconds -gt 0 ]]; then
|
local uptime_seconds
|
||||||
boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
|
uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0")
|
||||||
fi
|
if [[ $uptime_seconds -gt 0 ]]; then
|
||||||
|
boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
# Method 2: Use systemd if available (fallback)
|
|
||||||
if [[ -z "$boot_time" ]] && command -v systemctl &>/dev/null; then
|
# Method 2: Use systemd if available (fallback)
|
||||||
boot_time=$(systemd-analyze | grep "Startup finished" | sed -n 's/.*finished in .* = \(.*\)$/\1/p' 2>/dev/null || echo "")
|
if [[ -z $boot_time ]] && command -v systemctl &> /dev/null; then
|
||||||
if [[ -n "$boot_time" ]]; then
|
boot_time=$(systemd-analyze | grep "Startup finished" | sed -n 's/.*finished in .* = \(.*\)$/\1/p' 2> /dev/null || echo "")
|
||||||
# This gives us relative time, need to calculate absolute time
|
if [[ -n $boot_time ]]; then
|
||||||
local current_time=$(date +%s)
|
# This gives us relative time, need to calculate absolute time
|
||||||
local uptime_sec=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
|
local current_time uptime_sec
|
||||||
boot_time=$(date -d "@$((current_time - uptime_sec))" +"%Y-%m-%d %H:%M:%S")
|
current_time=$(date +%s)
|
||||||
fi
|
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
|
||||||
|
fi
|
||||||
# Method 3: Use who -b (fallback)
|
|
||||||
if [[ -z "$boot_time" ]] && command -v who &>/dev/null; then
|
# Method 3: Use who -b (fallback)
|
||||||
boot_time=$(who -b | awk '{print $3, $4}' 2>/dev/null || echo "")
|
if [[ -z $boot_time ]] && command -v who &> /dev/null; then
|
||||||
if [[ -n "$boot_time" ]]; then
|
boot_time=$(who -b | awk '{print $3, $4}' 2> /dev/null || echo "")
|
||||||
boot_time="$today $boot_time"
|
if [[ -n $boot_time ]]; then
|
||||||
fi
|
boot_time="$today $boot_time"
|
||||||
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
|
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
|
# Function to show notification/warning
|
||||||
show_startup_warning() {
|
show_startup_warning() {
|
||||||
local day_name=$(date +%A)
|
local day_name current_time today
|
||||||
local current_time=$(date +"%H:%M")
|
day_name=$(date +%A)
|
||||||
local today=$(date +%Y-%m-%d)
|
current_time=$(date +"%H:%M")
|
||||||
|
today=$(date +%Y-%m-%d)
|
||||||
echo ""
|
|
||||||
echo "⚠️ PC STARTUP TIME WARNING"
|
echo ""
|
||||||
echo "=========================="
|
echo "⚠️ PC STARTUP TIME WARNING"
|
||||||
echo "Date: $today ($day_name)"
|
echo "=========================="
|
||||||
echo "Current time: $current_time"
|
echo "Date: $today ($day_name)"
|
||||||
echo ""
|
echo "Current time: $current_time"
|
||||||
echo "This PC was expected to be turned on between 5:00 AM and 8:00 AM today,"
|
echo ""
|
||||||
echo "but it was not turned on during that time window."
|
echo "This PC was expected to be turned on between 5:00 AM and 8:00 AM today,"
|
||||||
echo ""
|
echo "but it was not turned on during that time window."
|
||||||
echo "Expected: Monday, Friday, Saturday, Sunday between 5:00-8:00 AM"
|
echo ""
|
||||||
echo "Actual: PC was turned on outside the expected window"
|
echo "Expected: Monday, Friday, Saturday, Sunday between 5:00-8:00 AM"
|
||||||
echo ""
|
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"
|
# 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
|
# Try to show desktop notification if possible
|
||||||
if [[ $EUID -eq 0 ]]; then
|
if command -v notify-send &> /dev/null && [[ -n $DISPLAY ]]; then
|
||||||
# Running as root, send notification as user
|
if [[ $EUID -eq 0 ]]; then
|
||||||
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
|
# Running as root, send notification as user
|
||||||
else
|
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
|
||||||
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
|
||||||
fi
|
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
|
fi
|
||||||
|
fi
|
||||||
echo "This warning has been logged to the system journal."
|
|
||||||
echo "You can view startup logs with: journalctl -t pc-startup-monitor"
|
echo "This warning has been logged to the system journal."
|
||||||
echo ""
|
echo "You can view startup logs with: journalctl -t pc-startup-monitor"
|
||||||
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create the monitoring service
|
# Function to create the monitoring service
|
||||||
create_monitoring_service() {
|
create_monitoring_service() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "1. Creating PC Startup Monitor Service..."
|
echo "1. Creating PC Startup Monitor Service..."
|
||||||
echo "======================================="
|
echo "======================================="
|
||||||
|
|
||||||
local service_file="/etc/systemd/system/pc-startup-monitor.service"
|
local service_file="/etc/systemd/system/pc-startup-monitor.service"
|
||||||
|
|
||||||
cat > "$service_file" << 'EOF'
|
cat > "$service_file" << 'EOF'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=PC Startup Time Monitor
|
Description=PC Startup Time Monitor
|
||||||
After=multi-user.target
|
After=multi-user.target
|
||||||
@ -211,18 +220,18 @@ RemainAfterExit=true
|
|||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✓ Created monitoring service: $service_file"
|
echo "✓ Created monitoring service: $service_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create the monitoring timer
|
# Function to create the monitoring timer
|
||||||
create_monitoring_timer() {
|
create_monitoring_timer() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. Creating PC Startup Monitor Timer..."
|
echo "2. Creating PC Startup Monitor Timer..."
|
||||||
echo "====================================="
|
echo "====================================="
|
||||||
|
|
||||||
local timer_file="/etc/systemd/system/pc-startup-monitor.timer"
|
local timer_file="/etc/systemd/system/pc-startup-monitor.timer"
|
||||||
|
|
||||||
cat > "$timer_file" << 'EOF'
|
cat > "$timer_file" << 'EOF'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Timer for PC startup monitoring
|
Description=Timer for PC startup monitoring
|
||||||
Requires=pc-startup-monitor.service
|
Requires=pc-startup-monitor.service
|
||||||
@ -236,25 +245,26 @@ AccuracySec=1m
|
|||||||
WantedBy=timers.target
|
WantedBy=timers.target
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✓ Created monitoring timer: $timer_file"
|
echo "✓ Created monitoring timer: $timer_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create the main monitoring script
|
# Function to create the main monitoring script
|
||||||
create_monitoring_script() {
|
create_monitoring_script() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. Creating PC Startup Monitor Script..."
|
echo "3. Creating PC Startup Monitor Script..."
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
|
|
||||||
local script_file="/usr/local/bin/pc-startup-check.sh"
|
local script_file="/usr/local/bin/pc-startup-check.sh"
|
||||||
|
|
||||||
cat > "$script_file" << 'EOF'
|
cat > "$script_file" << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# PC Startup Time Monitor Check Script
|
# PC Startup Time Monitor Check Script
|
||||||
# Monitors if PC was turned on during expected hours on specific days
|
# Monitors if PC was turned on during expected hours on specific days
|
||||||
|
|
||||||
# Function to check if today is a monitored day
|
# Function to check if today is a monitored day
|
||||||
is_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)
|
# 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
|
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
|
# Function to check if current time is between 5AM and 8AM
|
||||||
is_current_time_in_window() {
|
is_current_time_in_window() {
|
||||||
local current_hour=$(date +%H)
|
local current_hour current_hour_num
|
||||||
local current_hour_num=$((10#$current_hour))
|
current_hour=$(date +%H)
|
||||||
|
current_hour_num=$((10#$current_hour))
|
||||||
|
|
||||||
if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
|
if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
|
||||||
return 0 # Yes, current time is in the 5AM-8AM window
|
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
|
# Function to check if PC was booted between 5AM-8AM today
|
||||||
was_booted_in_window_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
|
# Calculate boot time from uptime
|
||||||
local uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
|
local uptime_seconds
|
||||||
local boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
|
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
|
# Check if boot time is from today
|
||||||
local boot_date=$(echo "$boot_time" | cut -d' ' -f1)
|
local boot_date
|
||||||
|
boot_date=$(echo "$boot_time" | cut -d' ' -f1)
|
||||||
if [[ "$boot_date" != "$today" ]]; then
|
if [[ "$boot_date" != "$today" ]]; then
|
||||||
return 1 # Not booted today
|
return 1 # Not booted today
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extract hour from boot time
|
# Extract hour from boot time
|
||||||
local boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1)
|
local boot_hour boot_hour_num
|
||||||
local boot_hour_num=$((10#$boot_hour))
|
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
|
# Check if boot time was between 5AM and 8AM
|
||||||
if [[ $boot_hour_num -ge 5 ]] && [[ $boot_hour_num -lt 8 ]]; then
|
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
|
# Function to show notification/warning
|
||||||
show_startup_warning() {
|
show_startup_warning() {
|
||||||
local day_name=$(date +%A)
|
local day_name current_time today
|
||||||
local current_time=$(date +"%H:%M")
|
day_name=$(date +%A)
|
||||||
local today=$(date +%Y-%m-%d)
|
current_time=$(date +"%H:%M")
|
||||||
|
today=$(date +%Y-%m-%d)
|
||||||
|
|
||||||
echo "⚠️ PC STARTUP TIME WARNING"
|
echo "⚠️ PC STARTUP TIME WARNING"
|
||||||
echo "Date: $today ($day_name)"
|
echo "Date: $today ($day_name)"
|
||||||
@ -346,19 +362,19 @@ else
|
|||||||
fi
|
fi
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
chmod +x "$script_file"
|
chmod +x "$script_file"
|
||||||
echo "✓ Created monitoring script: $script_file"
|
echo "✓ Created monitoring script: $script_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create management script
|
# Function to create management script
|
||||||
create_management_script() {
|
create_management_script() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. Creating Management Script..."
|
echo "4. Creating Management Script..."
|
||||||
echo "=============================="
|
echo "=============================="
|
||||||
|
|
||||||
local script_file="/usr/local/bin/pc-startup-monitor-manager.sh"
|
local script_file="/usr/local/bin/pc-startup-monitor-manager.sh"
|
||||||
|
|
||||||
cat > "$script_file" << 'EOF'
|
cat > "$script_file" << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# PC Startup Monitor Manager
|
# PC Startup Monitor Manager
|
||||||
# Provides easy management of the PC startup monitoring feature
|
# Provides easy management of the PC startup monitoring feature
|
||||||
@ -421,150 +437,150 @@ case "$1" in
|
|||||||
esac
|
esac
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
chmod +x "$script_file"
|
chmod +x "$script_file"
|
||||||
echo "✓ Created management script: $script_file"
|
echo "✓ Created management script: $script_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to enable the services
|
# Function to enable the services
|
||||||
enable_services() {
|
enable_services() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "5. Enabling PC Startup Monitor..."
|
echo "5. Enabling PC Startup Monitor..."
|
||||||
echo "==============================="
|
echo "==============================="
|
||||||
|
|
||||||
# Reload systemd daemon
|
# Reload systemd daemon
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
echo "✓ Reloaded systemd daemon"
|
echo "✓ Reloaded systemd daemon"
|
||||||
|
|
||||||
# Enable and start the timer
|
# Enable and start the timer
|
||||||
systemctl enable pc-startup-monitor.timer
|
systemctl enable pc-startup-monitor.timer
|
||||||
echo "✓ Enabled pc-startup-monitor timer"
|
echo "✓ Enabled pc-startup-monitor timer"
|
||||||
|
|
||||||
systemctl start pc-startup-monitor.timer
|
systemctl start pc-startup-monitor.timer
|
||||||
echo "✓ Started pc-startup-monitor timer"
|
echo "✓ Started pc-startup-monitor timer"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to test the setup
|
# Function to test the setup
|
||||||
test_setup() {
|
test_setup() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "6. Testing Setup..."
|
echo "6. Testing Setup..."
|
||||||
echo "=================="
|
echo "=================="
|
||||||
|
|
||||||
echo "Service files:"
|
echo "Service files:"
|
||||||
if [[ -f "/etc/systemd/system/pc-startup-monitor.service" ]]; then
|
if [[ -f "/etc/systemd/system/pc-startup-monitor.service" ]]; then
|
||||||
echo "✓ Service file exists"
|
echo "✓ Service file exists"
|
||||||
else
|
else
|
||||||
echo "✗ Service file missing"
|
echo "✗ Service file missing"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "/etc/systemd/system/pc-startup-monitor.timer" ]]; then
|
if [[ -f "/etc/systemd/system/pc-startup-monitor.timer" ]]; then
|
||||||
echo "✓ Timer file exists"
|
echo "✓ Timer file exists"
|
||||||
else
|
else
|
||||||
echo "✗ Timer file missing"
|
echo "✗ Timer file missing"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Timer status:"
|
echo "Timer status:"
|
||||||
if systemctl is-enabled pc-startup-monitor.timer &>/dev/null; then
|
if systemctl is-enabled pc-startup-monitor.timer &> /dev/null; then
|
||||||
echo "✓ Timer is enabled"
|
echo "✓ Timer is enabled"
|
||||||
else
|
else
|
||||||
echo "✗ Timer is not enabled"
|
echo "✗ Timer is not enabled"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if systemctl is-active pc-startup-monitor.timer &>/dev/null; then
|
if systemctl is-active pc-startup-monitor.timer &> /dev/null; then
|
||||||
echo "✓ Timer is active"
|
echo "✓ Timer is active"
|
||||||
else
|
else
|
||||||
echo "✗ Timer is not active"
|
echo "✗ Timer is not active"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Testing current logic:"
|
echo "Testing current logic:"
|
||||||
/usr/local/bin/pc-startup-check.sh
|
/usr/local/bin/pc-startup-check.sh
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to show final instructions
|
# Function to show final instructions
|
||||||
show_instructions() {
|
show_instructions() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "PC Startup Monitor Setup Complete"
|
echo "PC Startup Monitor Setup Complete"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "Summary:"
|
echo "Summary:"
|
||||||
echo "✓ Monitoring service created (/etc/systemd/system/pc-startup-monitor.service)"
|
echo "✓ Monitoring service created (/etc/systemd/system/pc-startup-monitor.service)"
|
||||||
echo "✓ Monitoring timer created (/etc/systemd/system/pc-startup-monitor.timer)"
|
echo "✓ Monitoring timer created (/etc/systemd/system/pc-startup-monitor.timer)"
|
||||||
echo "✓ Monitor script created (/usr/local/bin/pc-startup-check.sh)"
|
echo "✓ Monitor script created (/usr/local/bin/pc-startup-check.sh)"
|
||||||
echo "✓ Management script created (/usr/local/bin/pc-startup-monitor-manager.sh)"
|
echo "✓ Management script created (/usr/local/bin/pc-startup-monitor-manager.sh)"
|
||||||
echo "✓ Timer enabled and started"
|
echo "✓ Timer enabled and started"
|
||||||
echo ""
|
echo ""
|
||||||
echo "How it works:"
|
echo "How it works:"
|
||||||
echo "• Monitors PC startup times on Monday, Friday, Saturday, Sunday"
|
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 "• 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 "• 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 "• Shows warning if PC was not turned on during expected time"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Management commands:"
|
echo "Management commands:"
|
||||||
echo " sudo pc-startup-monitor-manager.sh status - Check status"
|
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 logs - View monitor logs"
|
||||||
echo " sudo pc-startup-monitor-manager.sh test - Test monitor now"
|
echo " sudo pc-startup-monitor-manager.sh test - Test monitor now"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Next check: Tomorrow at 8:30 AM (if it's a monitored day)"
|
echo "Next check: Tomorrow at 8:30 AM (if it's a monitored day)"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to prompt for confirmation
|
# Function to prompt for confirmation
|
||||||
confirm_setup() {
|
confirm_setup() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "PC Startup Monitor Setup"
|
echo "PC Startup Monitor Setup"
|
||||||
echo "======================="
|
echo "======================="
|
||||||
echo "This will set up monitoring for PC startup times."
|
echo "This will set up monitoring for PC startup times."
|
||||||
echo ""
|
echo ""
|
||||||
echo "Monitoring schedule:"
|
echo "Monitoring schedule:"
|
||||||
echo "- Days: Monday, Friday, Saturday, Sunday"
|
echo "- Days: Monday, Friday, Saturday, Sunday"
|
||||||
echo "- Expected startup time: 5:00 AM - 8:00 AM"
|
echo "- Expected startup time: 5:00 AM - 8:00 AM"
|
||||||
echo "- Check time: 8:30 AM daily"
|
echo "- Check time: 8:30 AM daily"
|
||||||
echo "- Action: Show warning if PC wasn't started in expected window"
|
echo "- Action: Show warning if PC wasn't started in expected window"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
|
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||||
read -p "Do you want to proceed? (y/N): " confirm
|
read -r -p "Do you want to proceed? (y/N): " confirm
|
||||||
|
|
||||||
case "$confirm" in
|
case "$confirm" in
|
||||||
[yY]|[yY][eE][sS])
|
[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 "Proceeding with setup..."
|
echo "Proceeding with setup..."
|
||||||
return 0
|
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 execution flow
|
||||||
main() {
|
main() {
|
||||||
# Check for sudo privileges
|
# Check for sudo privileges
|
||||||
check_sudo "$@"
|
check_sudo "$@"
|
||||||
|
|
||||||
# Confirm setup
|
# Confirm setup
|
||||||
confirm_setup
|
confirm_setup
|
||||||
|
|
||||||
# Create all components
|
# Create all components
|
||||||
create_monitoring_service
|
create_monitoring_service
|
||||||
create_monitoring_timer
|
create_monitoring_timer
|
||||||
create_monitoring_script
|
create_monitoring_script
|
||||||
create_management_script
|
create_management_script
|
||||||
|
|
||||||
# Enable services
|
# Enable services
|
||||||
enable_services
|
enable_services
|
||||||
|
|
||||||
# Test setup
|
# Test setup
|
||||||
test_setup
|
test_setup
|
||||||
|
|
||||||
# Show instructions
|
# Show instructions
|
||||||
show_instructions
|
show_instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run main function
|
# Run main function
|
||||||
|
|||||||
@ -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
|
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() {
|
usage() {
|
||||||
cat <<'EOF'
|
cat << 'EOF'
|
||||||
Usage: control_from_mobile.sh <command> [options]
|
Usage: control_from_mobile.sh <command> [options]
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
@ -46,118 +46,117 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
log() {
|
log() {
|
||||||
printf '[%s] %s\n' "$SCRIPT_NAME" "$*"
|
printf '[%s] %s\n' "$SCRIPT_NAME" "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
warn() {
|
warn() {
|
||||||
printf '[%s] %s\n' "$SCRIPT_NAME" "$*" >&2
|
printf '[%s] %s\n' "$SCRIPT_NAME" "$*" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
warn "$*"
|
warn "$*"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
require_non_root() {
|
require_non_root() {
|
||||||
if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then
|
if [[ ${EUID:-$(id -u)} -eq 0 ]]; then
|
||||||
die "Run this script as a regular desktop user, not root."
|
die "Run this script as a regular desktop user, not root."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_yes_no() {
|
prompt_yes_no() {
|
||||||
local prompt="$1"
|
local prompt="$1"
|
||||||
local reply
|
local reply
|
||||||
read -r -p "$prompt [y/N]: " reply
|
read -r -p "$prompt [y/N]: " reply
|
||||||
case "$reply" in
|
case "$reply" in
|
||||||
[Yy][Ee][Ss]|[Yy]) return 0 ;;
|
[Yy][Ee][Ss] | [Yy]) return 0 ;;
|
||||||
*) return 1 ;;
|
*) return 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_directories() {
|
ensure_directories() {
|
||||||
mkdir -p "$CONFIG_DIR" "$STATE_DIR" "$SYSTEMD_USER_DIR"
|
mkdir -p "$CONFIG_DIR" "$STATE_DIR" "$SYSTEMD_USER_DIR"
|
||||||
chmod 700 "$CONFIG_DIR"
|
chmod 700 "$CONFIG_DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
missing_commands() {
|
missing_commands() {
|
||||||
local missing=()
|
local missing=()
|
||||||
for cmd in "$@"; do
|
for cmd in "$@"; do
|
||||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
if ! command -v "$cmd" > /dev/null 2>&1; then
|
||||||
missing+=("$cmd")
|
missing+=("$cmd")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
printf '%s\n' "${missing[@]-}"
|
printf '%s\n' "${missing[@]-}"
|
||||||
}
|
}
|
||||||
|
|
||||||
install_dependencies() {
|
install_dependencies() {
|
||||||
if ! command -v systemctl >/dev/null 2>&1; then
|
if ! command -v systemctl > /dev/null 2>&1; then
|
||||||
die "systemctl not found. Install systemd before running this script."
|
die "systemctl not found. Install systemd before running this script."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local required=(x11vnc qrencode ssh)
|
local required=(x11vnc qrencode ssh)
|
||||||
local needed=()
|
local needed=()
|
||||||
mapfile -t needed < <(missing_commands "${required[@]}")
|
mapfile -t needed < <(missing_commands "${required[@]}")
|
||||||
if (( ${#needed[@]} == 0 )); then
|
if ((${#needed[@]} == 0)); then
|
||||||
log "All required packages (${required[*]}) are present."
|
log "All required packages (${required[*]}) are present."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v pacman >/dev/null 2>&1; then
|
if command -v pacman > /dev/null 2>&1; then
|
||||||
log "Installing missing packages: ${needed[*]}"
|
log "Installing missing packages: ${needed[*]}"
|
||||||
sudo pacman -S --needed --noconfirm "${needed[@]}"
|
sudo pacman -S --needed --noconfirm "${needed[@]}"
|
||||||
else
|
else
|
||||||
die "Missing commands (${needed[*]}). Install them manually and rerun setup."
|
die "Missing commands (${needed[*]}). Install them manually and rerun setup."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
create_password_file() {
|
create_password_file() {
|
||||||
local force=${1:-0}
|
local force=${1:-0}
|
||||||
if [[ -f "$PASSWORD_FILE" && "$force" -ne 1 ]];
|
if [[ -f $PASSWORD_FILE && $force -ne 1 ]]; then
|
||||||
then
|
log "Using existing VNC password file at $PASSWORD_FILE"
|
||||||
log "Using existing VNC password file at $PASSWORD_FILE"
|
return
|
||||||
return
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$PASSWORD_FILE" ]]; then
|
if [[ -f $PASSWORD_FILE ]]; then
|
||||||
if ! prompt_yes_no "Regenerate the stored VNC password?"; then
|
if ! prompt_yes_no "Regenerate the stored VNC password?"; then
|
||||||
log "Keeping existing password."
|
log "Keeping existing password."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local password confirm generated=0
|
local password confirm generated=0
|
||||||
read -rsp "Enter VNC password (leave blank to auto-generate): " password
|
read -rsp "Enter VNC password (leave blank to auto-generate): " password
|
||||||
printf '\n'
|
printf '\n'
|
||||||
if [[ -z "$password" ]]; then
|
if [[ -z $password ]]; then
|
||||||
generated=1
|
generated=1
|
||||||
password=$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 8)
|
password=$(LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 8)
|
||||||
log "Generated VNC password: $password"
|
log "Generated VNC password: $password"
|
||||||
else
|
else
|
||||||
read -rsp "Confirm password: " confirm
|
read -rsp "Confirm password: " confirm
|
||||||
printf '\n'
|
printf '\n'
|
||||||
if [[ "$password" != "$confirm" ]]; then
|
if [[ $password != "$confirm" ]]; then
|
||||||
die "Passwords do not match."
|
die "Passwords do not match."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local tmp
|
local tmp
|
||||||
tmp=$(mktemp)
|
tmp=$(mktemp)
|
||||||
x11vnc -storepasswd "$password" "$tmp" >/dev/null
|
x11vnc -storepasswd "$password" "$tmp" > /dev/null
|
||||||
install -m 600 "$tmp" "$PASSWORD_FILE"
|
install -m 600 "$tmp" "$PASSWORD_FILE"
|
||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
|
|
||||||
if (( generated == 0 )); then
|
if ((generated == 0)); then
|
||||||
log "Password stored securely at $PASSWORD_FILE (hashed)."
|
log "Password stored securely at $PASSWORD_FILE (hashed)."
|
||||||
else
|
else
|
||||||
log "Please write down the generated password; it will be needed on your Android device."
|
log "Please write down the generated password; it will be needed on your Android device."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
create_env_file() {
|
create_env_file() {
|
||||||
if [[ -f "$ENV_FILE" ]]; then
|
if [[ -f $ENV_FILE ]]; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
cat >"$ENV_FILE" <<EOF
|
cat > "$ENV_FILE" << EOF
|
||||||
# control-from-mobile configuration
|
# control-from-mobile configuration
|
||||||
# Adjust these values if needed and rerun: systemctl --user restart $SERVICE_NAME
|
# Adjust these values if needed and rerun: systemctl --user restart $SERVICE_NAME
|
||||||
X11_DISPLAY="$DEFAULT_DISPLAY"
|
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.
|
# 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"
|
VNC_BIND_ADDR="$DEFAULT_BIND_ADDR"
|
||||||
EOF
|
EOF
|
||||||
chmod 600 "$ENV_FILE"
|
chmod 600 "$ENV_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_runner_script() {
|
create_runner_script() {
|
||||||
cat >"$RUNNER_FILE" <<'EOF'
|
cat > "$RUNNER_FILE" << 'EOF'
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
IFS=$'\n\t'
|
IFS=$'\n\t'
|
||||||
@ -209,11 +208,11 @@ exec /usr/bin/x11vnc \
|
|||||||
-ncache_cr \
|
-ncache_cr \
|
||||||
-o "$LOG_FILE"
|
-o "$LOG_FILE"
|
||||||
EOF
|
EOF
|
||||||
chmod 700 "$RUNNER_FILE"
|
chmod 700 "$RUNNER_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_service_file() {
|
create_service_file() {
|
||||||
cat >"$SERVICE_FILE" <<EOF
|
cat > "$SERVICE_FILE" << EOF
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Expose X11 desktop over VNC for Android control
|
Description=Expose X11 desktop over VNC for Android control
|
||||||
After=graphical-session.target
|
After=graphical-session.target
|
||||||
@ -234,183 +233,183 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
reload_user_daemon() {
|
reload_user_daemon() {
|
||||||
systemctl --user daemon-reload
|
systemctl --user daemon-reload
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_service_present() {
|
ensure_service_present() {
|
||||||
if [[ ! -f "$SERVICE_FILE" || ! -x "$RUNNER_FILE" ]]; then
|
if [[ ! -f $SERVICE_FILE || ! -x $RUNNER_FILE ]]; then
|
||||||
die "Service files missing. Run: $SCRIPT_NAME setup"
|
die "Service files missing. Run: $SCRIPT_NAME setup"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
start_service() {
|
start_service() {
|
||||||
ensure_service_present
|
ensure_service_present
|
||||||
systemctl --user start "$SERVICE_NAME"
|
systemctl --user start "$SERVICE_NAME"
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_service() {
|
stop_service() {
|
||||||
systemctl --user stop "$SERVICE_NAME" || true
|
systemctl --user stop "$SERVICE_NAME" || true
|
||||||
}
|
}
|
||||||
|
|
||||||
status_service() {
|
status_service() {
|
||||||
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
||||||
log "Service is active."
|
log "Service is active."
|
||||||
else
|
else
|
||||||
log "Service is inactive."
|
log "Service is inactive."
|
||||||
fi
|
fi
|
||||||
systemctl --user status "$SERVICE_NAME" --no-pager || true
|
systemctl --user status "$SERVICE_NAME" --no-pager || true
|
||||||
}
|
}
|
||||||
|
|
||||||
enable_service() {
|
enable_service() {
|
||||||
ensure_service_present
|
ensure_service_present
|
||||||
systemctl --user enable "$SERVICE_NAME"
|
systemctl --user enable "$SERVICE_NAME"
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_service() {
|
disable_service() {
|
||||||
systemctl --user disable "$SERVICE_NAME" || true
|
systemctl --user disable "$SERVICE_NAME" || true
|
||||||
}
|
}
|
||||||
|
|
||||||
show_info() {
|
show_info() {
|
||||||
ensure_service_present
|
ensure_service_present
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
[[ -f "$ENV_FILE" ]] && source "$ENV_FILE"
|
[[ -f $ENV_FILE ]] && source "$ENV_FILE"
|
||||||
local port="${VNC_PORT:-$DEFAULT_PORT}"
|
local port="${VNC_PORT:-$DEFAULT_PORT}"
|
||||||
local bind_addr="${VNC_BIND_ADDR:-$DEFAULT_BIND_ADDR}"
|
local bind_addr="${VNC_BIND_ADDR:-$DEFAULT_BIND_ADDR}"
|
||||||
local display="${X11_DISPLAY:-$DEFAULT_DISPLAY}"
|
local display="${X11_DISPLAY:-$DEFAULT_DISPLAY}"
|
||||||
|
|
||||||
local is_active="inactive"
|
local is_active="inactive"
|
||||||
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
||||||
is_active="active"
|
is_active="active"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Service status: $is_active"
|
log "Service status: $is_active"
|
||||||
log "Display: $display"
|
log "Display: $display"
|
||||||
log "Listening address: $bind_addr"
|
log "Listening address: $bind_addr"
|
||||||
log "VNC port: $port"
|
log "VNC port: $port"
|
||||||
log "Password file: $PASSWORD_FILE"
|
log "Password file: $PASSWORD_FILE"
|
||||||
|
|
||||||
local -a ip_list=()
|
local -a ip_list=()
|
||||||
if command -v hostname >/dev/null 2>&1; then
|
if command -v hostname > /dev/null 2>&1; then
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
[[ -z "$line" ]] && continue
|
[[ -z $line ]] && continue
|
||||||
ip_list+=("$line")
|
ip_list+=("$line")
|
||||||
done < <(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]' || true)
|
done < <(hostname -I 2> /dev/null | tr ' ' '\n' | grep -E '^[0-9]' || true)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if (( ${#ip_list[@]} > 0 )); then
|
if ((${#ip_list[@]} > 0)); then
|
||||||
log "Detected LAN IPs:"
|
log "Detected LAN IPs:"
|
||||||
for ip in "${ip_list[@]}"; do
|
for ip in "${ip_list[@]}"; do
|
||||||
printf ' - %s\n' "$ip"
|
printf ' - %s\n' "$ip"
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
warn "Could not detect LAN IPs."
|
warn "Could not detect LAN IPs."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '\nRecommended Android clients (FOSS):\n'
|
printf '\nRecommended Android clients (FOSS):\n'
|
||||||
printf ' • bVNC (available on F-Droid) — supports full control.\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 ' • Termux + OpenSSH for establishing an SSH tunnel when exposing only on 127.0.0.1.\n'
|
||||||
printf '\nConnect via VNC:\n'
|
printf '\nConnect via VNC:\n'
|
||||||
printf ' Host: <your-ip>\n Port: %s\n Password: <stored during setup>\n' "$port"
|
printf ' Host: <your-ip>\n Port: %s\n Password: <stored during setup>\n' "$port"
|
||||||
|
|
||||||
local qr_host
|
local qr_host
|
||||||
if (( ${#ip_list[@]} > 0 )); then
|
if ((${#ip_list[@]} > 0)); then
|
||||||
qr_host="${ip_list[0]}"
|
qr_host="${ip_list[0]}"
|
||||||
else
|
else
|
||||||
qr_host="$bind_addr"
|
qr_host="$bind_addr"
|
||||||
if [[ "$qr_host" == "0.0.0.0" || "$qr_host" == "::" ]]; then
|
if [[ $qr_host == "0.0.0.0" || $qr_host == "::" ]]; then
|
||||||
qr_host="127.0.0.1"
|
qr_host="127.0.0.1"
|
||||||
fi
|
fi
|
||||||
warn "Using fallback host $qr_host for QR code; replace with an accessible IP if needed."
|
warn "Using fallback host $qr_host for QR code; replace with an accessible IP if needed."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v qrencode >/dev/null 2>&1; then
|
if command -v qrencode > /dev/null 2>&1; then
|
||||||
printf '\nConnection QR (vnc://%s:%s):\n' "$qr_host" "$port"
|
printf '\nConnection QR (vnc://%s:%s):\n' "$qr_host" "$port"
|
||||||
qrencode -o - "vnc://$qr_host:$port" -t ASCII || true
|
qrencode -o - "vnc://$qr_host:$port" -t ASCII || true
|
||||||
else
|
else
|
||||||
warn "qrencode not found; reinstall qrencode to get QR codes."
|
warn "qrencode not found; reinstall qrencode to get QR codes."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '\nFor encrypted access outside your LAN, use Termux on Android:\n'
|
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 ' ssh -L %s:localhost:%s <user>@<public-ip>\n' "$port" "$port"
|
||||||
printf 'Then point bVNC to 127.0.0.1:%s.\n' "$port"
|
printf 'Then point bVNC to 127.0.0.1:%s.\n' "$port"
|
||||||
}
|
}
|
||||||
|
|
||||||
uninstall_files() {
|
uninstall_files() {
|
||||||
local purge_password=${1:-0}
|
local purge_password=${1:-0}
|
||||||
stop_service
|
stop_service
|
||||||
disable_service
|
disable_service
|
||||||
rm -f "$SERVICE_FILE"
|
rm -f "$SERVICE_FILE"
|
||||||
rm -f "$RUNNER_FILE"
|
rm -f "$RUNNER_FILE"
|
||||||
rm -f "$ENV_FILE"
|
rm -f "$ENV_FILE"
|
||||||
if (( purge_password )); then
|
if ((purge_password)); then
|
||||||
rm -f "$PASSWORD_FILE"
|
rm -f "$PASSWORD_FILE"
|
||||||
log "Removed password file."
|
log "Removed password file."
|
||||||
fi
|
fi
|
||||||
reload_user_daemon
|
reload_user_daemon
|
||||||
log "Removed generated files."
|
log "Removed generated files."
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
require_non_root
|
require_non_root
|
||||||
|
|
||||||
local cmd="${1:-}"
|
local cmd="${1:-}"
|
||||||
shift || true
|
shift || true
|
||||||
|
|
||||||
case "$cmd" in
|
case "$cmd" in
|
||||||
setup)
|
setup)
|
||||||
local force=0
|
local force=0
|
||||||
if [[ "${1:-}" == "--force-password" ]]; then
|
if [[ ${1:-} == "--force-password" ]]; then
|
||||||
force=1
|
force=1
|
||||||
shift || true
|
shift || true
|
||||||
fi
|
fi
|
||||||
ensure_directories
|
ensure_directories
|
||||||
install_dependencies
|
install_dependencies
|
||||||
create_password_file "$force"
|
create_password_file "$force"
|
||||||
create_env_file
|
create_env_file
|
||||||
create_runner_script
|
create_runner_script
|
||||||
create_service_file
|
create_service_file
|
||||||
reload_user_daemon
|
reload_user_daemon
|
||||||
log "Setup complete. Start the service with: $SCRIPT_NAME start"
|
log "Setup complete. Start the service with: $SCRIPT_NAME start"
|
||||||
;;
|
;;
|
||||||
start)
|
start)
|
||||||
start_service
|
start_service
|
||||||
show_info
|
show_info
|
||||||
;;
|
;;
|
||||||
stop)
|
stop)
|
||||||
stop_service
|
stop_service
|
||||||
;;
|
;;
|
||||||
restart)
|
restart)
|
||||||
stop_service
|
stop_service
|
||||||
start_service
|
start_service
|
||||||
;;
|
;;
|
||||||
status)
|
status)
|
||||||
status_service
|
status_service
|
||||||
;;
|
;;
|
||||||
enable)
|
enable)
|
||||||
enable_service
|
enable_service
|
||||||
;;
|
;;
|
||||||
disable)
|
disable)
|
||||||
disable_service
|
disable_service
|
||||||
;;
|
;;
|
||||||
info)
|
info)
|
||||||
show_info
|
show_info
|
||||||
;;
|
;;
|
||||||
uninstall)
|
uninstall)
|
||||||
local purge=0
|
local purge=0
|
||||||
if [[ "${1:-}" == "--purge" ]]; then
|
if [[ ${1:-} == "--purge" ]]; then
|
||||||
purge=1
|
purge=1
|
||||||
shift || true
|
shift || true
|
||||||
fi
|
fi
|
||||||
uninstall_files "$purge"
|
uninstall_files "$purge"
|
||||||
;;
|
;;
|
||||||
help|--help|-h|"" )
|
help | --help | -h | "")
|
||||||
usage
|
usage
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
die "Unknown command: $cmd"
|
die "Unknown command: $cmd"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
@ -3,15 +3,15 @@
|
|||||||
# Handles installation, startup, autostart, and i3blocks status
|
# Handles installation, startup, autostart, and i3blocks status
|
||||||
# Handles sudo privileges automatically
|
# 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
|
# Function to check and request sudo privileges for package installation
|
||||||
check_sudo() {
|
check_sudo() {
|
||||||
if [[ $EUID -ne 0 ]] && [[ "$1" == "install" ]]; then
|
if [[ $EUID -ne 0 ]] && [[ $1 == "install" ]]; then
|
||||||
echo "Package installation requires sudo privileges."
|
echo "Package installation requires sudo privileges."
|
||||||
echo "Requesting sudo access..."
|
echo "Requesting sudo access..."
|
||||||
exec sudo "$0" "$@"
|
exec sudo "$0" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "ActivityWatch Setup for Arch Linux + i3"
|
echo "ActivityWatch Setup for Arch Linux + i3"
|
||||||
@ -20,12 +20,12 @@ echo "Current Date: $(date)"
|
|||||||
echo "User: ${SUDO_USER:-$USER}"
|
echo "User: ${SUDO_USER:-$USER}"
|
||||||
|
|
||||||
# Get the actual user (even when running with sudo)
|
# Get the actual user (even when running with sudo)
|
||||||
if [[ -n "$SUDO_USER" ]]; then
|
if [[ -n $SUDO_USER ]]; then
|
||||||
ACTUAL_USER="$SUDO_USER"
|
ACTUAL_USER="$SUDO_USER"
|
||||||
USER_HOME="/home/$SUDO_USER"
|
USER_HOME="/home/$SUDO_USER"
|
||||||
else
|
else
|
||||||
ACTUAL_USER="$USER"
|
ACTUAL_USER="$USER"
|
||||||
USER_HOME="$HOME"
|
USER_HOME="$HOME"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Target user: $ACTUAL_USER"
|
echo "Target user: $ACTUAL_USER"
|
||||||
@ -33,180 +33,180 @@ echo "User home: $USER_HOME"
|
|||||||
|
|
||||||
# Function to check if ActivityWatch is installed
|
# Function to check if ActivityWatch is installed
|
||||||
check_activitywatch_installed() {
|
check_activitywatch_installed() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "1. Checking ActivityWatch Installation..."
|
echo "1. Checking ActivityWatch Installation..."
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
|
|
||||||
# Check if activitywatch-bin is installed via pacman
|
# Check if activitywatch-bin is installed via pacman
|
||||||
if pacman -Qi activitywatch-bin &>/dev/null; then
|
if pacman -Qi activitywatch-bin &> /dev/null; then
|
||||||
echo "✓ activitywatch-bin package is installed"
|
echo "✓ activitywatch-bin package is installed"
|
||||||
return 0
|
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
|
fi
|
||||||
|
done
|
||||||
# Check if aw-qt binary exists in common locations
|
|
||||||
local common_paths=(
|
echo "✗ ActivityWatch not found"
|
||||||
"/usr/bin/aw-qt"
|
return 1
|
||||||
"/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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to install ActivityWatch
|
# Function to install ActivityWatch
|
||||||
install_activitywatch() {
|
install_activitywatch() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. Installing ActivityWatch..."
|
echo "2. Installing ActivityWatch..."
|
||||||
echo "============================="
|
echo "============================="
|
||||||
|
|
||||||
# Check if we need sudo for installation
|
# Check if we need sudo for installation
|
||||||
check_sudo "install"
|
check_sudo "install"
|
||||||
|
|
||||||
echo "Installing activitywatch-bin from AUR..."
|
echo "Installing activitywatch-bin from AUR..."
|
||||||
|
|
||||||
# Check if an AUR helper is available
|
# Check if an AUR helper is available
|
||||||
local aur_helpers=("yay" "paru" "makepkg")
|
local aur_helpers=("yay" "paru" "makepkg")
|
||||||
local helper_found=""
|
local helper_found=""
|
||||||
|
|
||||||
for helper in "${aur_helpers[@]}"; do
|
for helper in "${aur_helpers[@]}"; do
|
||||||
if command -v "$helper" &>/dev/null; then
|
if command -v "$helper" &> /dev/null; then
|
||||||
helper_found="$helper"
|
helper_found="$helper"
|
||||||
break
|
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
|
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
echo "✓ ActivityWatch installation completed"
|
|
||||||
|
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
|
# Function to manually install ActivityWatch via makepkg
|
||||||
install_activitywatch_manual() {
|
install_activitywatch_manual() {
|
||||||
local temp_dir="/tmp/activitywatch-install"
|
local temp_dir="/tmp/activitywatch-install"
|
||||||
local original_user="$ACTUAL_USER"
|
local original_user="$ACTUAL_USER"
|
||||||
|
|
||||||
# Create temp directory
|
# Create temp directory
|
||||||
mkdir -p "$temp_dir"
|
mkdir -p "$temp_dir"
|
||||||
cd "$temp_dir"
|
cd "$temp_dir"
|
||||||
|
|
||||||
# Download PKGBUILD
|
# Download PKGBUILD
|
||||||
if command -v git &>/dev/null; then
|
if command -v git &> /dev/null; then
|
||||||
sudo -u "$original_user" git clone https://aur.archlinux.org/activitywatch-bin.git .
|
sudo -u "$original_user" git clone https://aur.archlinux.org/activitywatch-bin.git .
|
||||||
else
|
else
|
||||||
echo "Installing git..."
|
echo "Installing git..."
|
||||||
pacman -S --noconfirm git
|
pacman -S --noconfirm git
|
||||||
sudo -u "$original_user" git clone https://aur.archlinux.org/activitywatch-bin.git .
|
sudo -u "$original_user" git clone https://aur.archlinux.org/activitywatch-bin.git .
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Build and install package
|
# Build and install package
|
||||||
sudo -u "$original_user" makepkg -si --noconfirm
|
sudo -u "$original_user" makepkg -si --noconfirm
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
cd /
|
cd /
|
||||||
rm -rf "$temp_dir"
|
rm -rf "$temp_dir"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to check if ActivityWatch is running
|
# Function to check if ActivityWatch is running
|
||||||
check_activitywatch_running() {
|
check_activitywatch_running() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. Checking ActivityWatch Status..."
|
echo "3. Checking ActivityWatch Status..."
|
||||||
echo "=================================="
|
echo "=================================="
|
||||||
|
|
||||||
# Check for aw-qt process
|
# Check for aw-qt process
|
||||||
if pgrep -f "aw-qt" >/dev/null; then
|
if pgrep -f "aw-qt" > /dev/null; then
|
||||||
echo "✓ ActivityWatch (aw-qt) is running"
|
echo "✓ ActivityWatch (aw-qt) is running"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for aw-server process
|
# Check for aw-server process
|
||||||
if pgrep -f "aw-server" >/dev/null; then
|
if pgrep -f "aw-server" > /dev/null; then
|
||||||
echo "✓ ActivityWatch server is running"
|
echo "✓ ActivityWatch server is running"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✗ ActivityWatch is not running"
|
echo "✗ ActivityWatch is not running"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to start ActivityWatch
|
# Function to start ActivityWatch
|
||||||
start_activitywatch() {
|
start_activitywatch() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. Starting ActivityWatch..."
|
echo "4. Starting ActivityWatch..."
|
||||||
echo "==========================="
|
echo "==========================="
|
||||||
|
|
||||||
# Find aw-qt executable
|
# Find aw-qt executable
|
||||||
local aw_qt_path=""
|
local aw_qt_path=""
|
||||||
|
|
||||||
if command -v aw-qt &>/dev/null; then
|
if command -v aw-qt &> /dev/null; then
|
||||||
aw_qt_path="$(which aw-qt)"
|
aw_qt_path="$(which aw-qt)"
|
||||||
elif [[ -x "/usr/bin/aw-qt" ]]; then
|
elif [[ -x "/usr/bin/aw-qt" ]]; then
|
||||||
aw_qt_path="/usr/bin/aw-qt"
|
aw_qt_path="/usr/bin/aw-qt"
|
||||||
else
|
else
|
||||||
echo "✗ Could not find aw-qt executable"
|
echo "✗ Could not find aw-qt executable"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Starting ActivityWatch as user: $ACTUAL_USER"
|
echo "Starting ActivityWatch as user: $ACTUAL_USER"
|
||||||
echo "Using aw-qt from: $aw_qt_path"
|
echo "Using aw-qt from: $aw_qt_path"
|
||||||
|
|
||||||
# Start as the actual user in the background
|
# Start as the actual user in the background
|
||||||
if [[ $EUID -eq 0 ]]; then
|
if [[ $EUID -eq 0 ]]; then
|
||||||
# Running as root, start as user
|
# Running as root, start as user
|
||||||
sudo -u "$ACTUAL_USER" env DISPLAY=:0 "$aw_qt_path" &
|
sudo -u "$ACTUAL_USER" env DISPLAY=:0 "$aw_qt_path" &
|
||||||
else
|
else
|
||||||
# Running as user
|
# Running as user
|
||||||
"$aw_qt_path" &
|
"$aw_qt_path" &
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Give it time to start
|
# Give it time to start
|
||||||
sleep 3
|
sleep 3
|
||||||
|
|
||||||
if check_activitywatch_running >/dev/null 2>&1; then
|
if check_activitywatch_running > /dev/null 2>&1; then
|
||||||
echo "✓ ActivityWatch started successfully"
|
echo "✓ ActivityWatch started successfully"
|
||||||
else
|
else
|
||||||
echo "! ActivityWatch may be starting (check system tray)"
|
echo "! ActivityWatch may be starting (check system tray)"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to setup autostart
|
# Function to setup autostart
|
||||||
setup_autostart() {
|
setup_autostart() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "5. Setting Up Autostart..."
|
echo "5. Setting Up Autostart..."
|
||||||
echo "========================="
|
echo "========================="
|
||||||
|
|
||||||
local autostart_dir="$USER_HOME/.config/autostart"
|
local autostart_dir="$USER_HOME/.config/autostart"
|
||||||
local desktop_file="$autostart_dir/activitywatch.desktop"
|
local desktop_file="$autostart_dir/activitywatch.desktop"
|
||||||
local i3_config="$USER_HOME/.config/i3/config"
|
local i3_config="$USER_HOME/.config/i3/config"
|
||||||
|
|
||||||
# Method 1: XDG Autostart (works with most desktop environments)
|
# Method 1: XDG Autostart (works with most desktop environments)
|
||||||
if [[ $EUID -eq 0 ]]; then
|
if [[ $EUID -eq 0 ]]; then
|
||||||
sudo -u "$ACTUAL_USER" mkdir -p "$autostart_dir"
|
sudo -u "$ACTUAL_USER" mkdir -p "$autostart_dir"
|
||||||
else
|
else
|
||||||
mkdir -p "$autostart_dir"
|
mkdir -p "$autostart_dir"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create desktop file for autostart
|
# Create desktop file for autostart
|
||||||
cat > "$desktop_file" << EOF
|
cat > "$desktop_file" << EOF
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Name=ActivityWatch
|
Name=ActivityWatch
|
||||||
@ -220,59 +220,61 @@ StartupNotify=false
|
|||||||
Terminal=false
|
Terminal=false
|
||||||
Categories=Utility;
|
Categories=Utility;
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Set proper ownership if running as root
|
# Set proper ownership if running as root
|
||||||
if [[ $EUID -eq 0 ]]; then
|
if [[ $EUID -eq 0 ]]; then
|
||||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$desktop_file"
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$desktop_file"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✓ Created XDG autostart entry: $desktop_file"
|
echo "✓ Created XDG autostart entry: $desktop_file"
|
||||||
|
|
||||||
# Method 2: i3 config autostart (specific to i3)
|
# Method 2: i3 config autostart (specific to i3)
|
||||||
if [[ -f "$i3_config" ]]; then
|
if [[ -f $i3_config ]]; then
|
||||||
# Check if autostart entry already exists
|
# Check if autostart entry already exists
|
||||||
if ! grep -q "aw-qt" "$i3_config"; then
|
if ! grep -q "aw-qt" "$i3_config"; then
|
||||||
# Add autostart entry to i3 config
|
# Add autostart entry to i3 config
|
||||||
local temp_config="/tmp/i3_config_temp"
|
if [[ $EUID -eq 0 ]]; then
|
||||||
|
# Running as root
|
||||||
if [[ $EUID -eq 0 ]]; then
|
sudo -u "$ACTUAL_USER" bash -c "cat <<'EOF' >> '$i3_config'
|
||||||
# Running as root
|
|
||||||
sudo -u "$ACTUAL_USER" bash -c "echo '' >> '$i3_config'"
|
# Auto-start ActivityWatch
|
||||||
sudo -u "$ACTUAL_USER" bash -c "echo '# Auto-start ActivityWatch' >> '$i3_config'"
|
exec --no-startup-id aw-qt
|
||||||
sudo -u "$ACTUAL_USER" bash -c "echo 'exec --no-startup-id aw-qt' >> '$i3_config'"
|
EOF"
|
||||||
else
|
else
|
||||||
echo "" >> "$i3_config"
|
{
|
||||||
echo "# Auto-start ActivityWatch" >> "$i3_config"
|
printf '\n'
|
||||||
echo "exec --no-startup-id aw-qt" >> "$i3_config"
|
printf '# Auto-start ActivityWatch\n'
|
||||||
fi
|
printf 'exec --no-startup-id aw-qt\n'
|
||||||
|
} >> "$i3_config"
|
||||||
echo "✓ Added ActivityWatch to i3 config autostart"
|
fi
|
||||||
else
|
|
||||||
echo "✓ ActivityWatch autostart already exists in i3 config"
|
echo "✓ Added ActivityWatch to i3 config autostart"
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo "! i3 config not found at $i3_config"
|
echo "✓ ActivityWatch autostart already exists in i3 config"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
echo "! i3 config not found at $i3_config"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create i3blocks status script
|
# Function to create i3blocks status script
|
||||||
create_i3blocks_status() {
|
create_i3blocks_status() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "6. Creating i3blocks Status Script..."
|
echo "6. Creating i3blocks Status Script..."
|
||||||
echo "===================================="
|
echo "===================================="
|
||||||
|
|
||||||
local i3blocks_dir="$USER_HOME/.config/i3blocks"
|
local i3blocks_dir="$USER_HOME/.config/i3blocks"
|
||||||
local status_script="$i3blocks_dir/activitywatch_status.sh"
|
local status_script="$i3blocks_dir/activitywatch_status.sh"
|
||||||
|
|
||||||
# Create i3blocks directory if it doesn't exist
|
# Create i3blocks directory if it doesn't exist
|
||||||
if [[ $EUID -eq 0 ]]; then
|
if [[ $EUID -eq 0 ]]; then
|
||||||
sudo -u "$ACTUAL_USER" mkdir -p "$i3blocks_dir"
|
sudo -u "$ACTUAL_USER" mkdir -p "$i3blocks_dir"
|
||||||
else
|
else
|
||||||
mkdir -p "$i3blocks_dir"
|
mkdir -p "$i3blocks_dir"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create the status script
|
# Create the status script
|
||||||
cat > "$status_script" << 'EOF'
|
cat > "$status_script" << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# ActivityWatch status script for i3blocks
|
# ActivityWatch status script for i3blocks
|
||||||
# Shows ActivityWatch installation and running status
|
# Shows ActivityWatch installation and running status
|
||||||
@ -323,134 +325,134 @@ else
|
|||||||
fi
|
fi
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
chmod +x "$status_script"
|
chmod +x "$status_script"
|
||||||
|
|
||||||
# Set proper ownership if running as root
|
# Set proper ownership if running as root
|
||||||
if [[ $EUID -eq 0 ]]; then
|
if [[ $EUID -eq 0 ]]; then
|
||||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$status_script"
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$status_script"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✓ Created i3blocks status script: $status_script"
|
echo "✓ Created i3blocks status script: $status_script"
|
||||||
|
|
||||||
# Show configuration instructions
|
# Show configuration instructions
|
||||||
echo ""
|
echo ""
|
||||||
echo "To add to your i3blocks config, add this block:"
|
echo "To add to your i3blocks config, add this block:"
|
||||||
echo ""
|
echo ""
|
||||||
echo "[activitywatch]"
|
echo "[activitywatch]"
|
||||||
echo "command=~/.config/i3blocks/activitywatch_status.sh"
|
echo "command=~/.config/i3blocks/activitywatch_status.sh"
|
||||||
echo "interval=10"
|
echo "interval=10"
|
||||||
echo "color=#FFFFFF"
|
echo "color=#FFFFFF"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to test the setup
|
# Function to test the setup
|
||||||
test_setup() {
|
test_setup() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "7. Testing Setup..."
|
echo "7. Testing Setup..."
|
||||||
echo "=================="
|
echo "=================="
|
||||||
|
|
||||||
echo "Installation status:"
|
echo "Installation status:"
|
||||||
if check_activitywatch_installed >/dev/null 2>&1; then
|
if check_activitywatch_installed > /dev/null 2>&1; then
|
||||||
echo "✓ ActivityWatch is installed"
|
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
|
else
|
||||||
echo "✗ ActivityWatch is not installed"
|
"$USER_HOME/.config/i3blocks/activitywatch_status.sh"
|
||||||
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"
|
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
echo "✗ i3blocks status script missing"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to show final instructions
|
# Function to show final instructions
|
||||||
show_instructions() {
|
show_instructions() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "ActivityWatch Setup Complete"
|
echo "ActivityWatch Setup Complete"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "Summary:"
|
echo "Summary:"
|
||||||
echo "✓ ActivityWatch installation checked/completed"
|
echo "✓ ActivityWatch installation checked/completed"
|
||||||
echo "✓ ActivityWatch startup configured"
|
echo "✓ ActivityWatch startup configured"
|
||||||
echo "✓ Autostart configured (XDG + i3)"
|
echo "✓ Autostart configured (XDG + i3)"
|
||||||
echo "✓ i3blocks status script created"
|
echo "✓ i3blocks status script created"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Next steps:"
|
echo "Next steps:"
|
||||||
echo "1. Add the i3blocks configuration to your config file:"
|
echo "1. Add the i3blocks configuration to your config file:"
|
||||||
echo " ~/.config/i3blocks/config"
|
echo " ~/.config/i3blocks/config"
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. Reload i3 configuration:"
|
echo "2. Reload i3 configuration:"
|
||||||
echo " Super+Shift+R"
|
echo " Super+Shift+R"
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. ActivityWatch web interface should be available at:"
|
echo "3. ActivityWatch web interface should be available at:"
|
||||||
echo " http://localhost:5600"
|
echo " http://localhost:5600"
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. Check system tray for ActivityWatch icon"
|
echo "4. Check system tray for ActivityWatch icon"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Files created:"
|
echo "Files created:"
|
||||||
echo " ~/.config/autostart/activitywatch.desktop"
|
echo " ~/.config/autostart/activitywatch.desktop"
|
||||||
echo " ~/.config/i3blocks/activitywatch_status.sh"
|
echo " ~/.config/i3blocks/activitywatch_status.sh"
|
||||||
echo " ~/.config/i3/config (modified)"
|
echo " ~/.config/i3/config (modified)"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main execution flow
|
# Main execution flow
|
||||||
main() {
|
main() {
|
||||||
local need_install=false
|
local need_install=false
|
||||||
local need_start=false
|
local need_start=false
|
||||||
|
|
||||||
# Check installation
|
# Check installation
|
||||||
if ! check_activitywatch_installed; then
|
if ! check_activitywatch_installed; then
|
||||||
need_install=true
|
need_install=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install if needed
|
# Install if needed
|
||||||
if [[ "$need_install" == true ]]; then
|
if [[ $need_install == true ]]; then
|
||||||
install_activitywatch
|
install_activitywatch
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if running
|
# Check if running
|
||||||
if ! check_activitywatch_running; then
|
if ! check_activitywatch_running; then
|
||||||
need_start=true
|
need_start=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Start if needed
|
# Start if needed
|
||||||
if [[ "$need_start" == true ]]; then
|
if [[ $need_start == true ]]; then
|
||||||
start_activitywatch
|
start_activitywatch
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Always set up autostart and i3blocks (in case they're missing)
|
# Always set up autostart and i3blocks (in case they're missing)
|
||||||
setup_autostart
|
setup_autostart
|
||||||
create_i3blocks_status
|
create_i3blocks_status
|
||||||
test_setup
|
test_setup
|
||||||
show_instructions
|
show_instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run main function
|
# Run main function
|
||||||
|
|||||||
@ -3,194 +3,194 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
on_error() {
|
on_error() {
|
||||||
local exit_code=$?
|
local exit_code=$?
|
||||||
local line_number=$1
|
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
|
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
|
trap 'on_error ${LINENO}' ERR
|
||||||
|
|
||||||
log_info() {
|
log_info() {
|
||||||
printf '\033[1;34m[INFO]\033[0m %s\n' "$*"
|
printf '\033[1;34m[INFO]\033[0m %s\n' "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
log_warn() {
|
log_warn() {
|
||||||
printf '\033[1;33m[WARN]\033[0m %s\n' "$*"
|
printf '\033[1;33m[WARN]\033[0m %s\n' "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
log_error() {
|
log_error() {
|
||||||
printf '\033[1;31m[ERROR]\033[0m %s\n' "$*" >&2
|
printf '\033[1;31m[ERROR]\033[0m %s\n' "$*" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
require_root() {
|
require_root() {
|
||||||
if [[ "${EUID}" -ne 0 ]]; then
|
if [[ ${EUID} -ne 0 ]]; then
|
||||||
log_error "This script must be run as root (try again with sudo)."
|
log_error "This script must be run as root (try again with sudo)."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
require_pacman() {
|
require_pacman() {
|
||||||
if ! command -v pacman >/dev/null 2>&1; then
|
if ! command -v pacman > /dev/null 2>&1; then
|
||||||
log_error "pacman not found. This script is intended for Arch Linux systems."
|
log_error "pacman not found. This script is intended for Arch Linux systems."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
detect_kernel_release() {
|
detect_kernel_release() {
|
||||||
uname -r
|
uname -r
|
||||||
}
|
}
|
||||||
|
|
||||||
select_host_package() {
|
select_host_package() {
|
||||||
local kernel_release=$1
|
local kernel_release=$1
|
||||||
case "${kernel_release}" in
|
case "${kernel_release}" in
|
||||||
*-lts)
|
*-lts)
|
||||||
echo "virtualbox-host-modules-lts"
|
echo "virtualbox-host-modules-lts"
|
||||||
;;
|
;;
|
||||||
*-arch*)
|
*-arch*)
|
||||||
echo "virtualbox-host-modules-arch"
|
echo "virtualbox-host-modules-arch"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "virtualbox-host-dkms"
|
echo "virtualbox-host-dkms"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_kernel_headers() {
|
collect_kernel_headers() {
|
||||||
local -a headers=()
|
local -a headers=()
|
||||||
local kernel_pkg header_pkg
|
local kernel_pkg header_pkg
|
||||||
for kernel_pkg in linux linux-lts linux-zen linux-hardened; do
|
for kernel_pkg in linux linux-lts linux-zen linux-hardened; do
|
||||||
if pacman -Q "${kernel_pkg}" >/dev/null 2>&1; then
|
if pacman -Q "${kernel_pkg}" > /dev/null 2>&1; then
|
||||||
header_pkg="${kernel_pkg}-headers"
|
header_pkg="${kernel_pkg}-headers"
|
||||||
headers+=("${header_pkg}")
|
headers+=("${header_pkg}")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [[ ${#headers[@]} -gt 0 ]]; then
|
if [[ ${#headers[@]} -gt 0 ]]; then
|
||||||
printf '%s\n' "${headers[@]}"
|
printf '%s\n' "${headers[@]}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
maybe_remove_conflicting_host_packages() {
|
maybe_remove_conflicting_host_packages() {
|
||||||
local selected_package=$1
|
local selected_package=$1
|
||||||
local -a candidates=("virtualbox-host-dkms" "virtualbox-host-modules-arch" "virtualbox-host-modules-lts")
|
local -a candidates=("virtualbox-host-dkms" "virtualbox-host-modules-arch" "virtualbox-host-modules-lts")
|
||||||
local pkg
|
local pkg
|
||||||
for pkg in "${candidates[@]}"; do
|
for pkg in "${candidates[@]}"; do
|
||||||
if [[ "${pkg}" != "${selected_package}" ]] && pacman -Q "${pkg}" >/dev/null 2>&1; then
|
if [[ ${pkg} != "${selected_package}" ]] && pacman -Q "${pkg}" > /dev/null 2>&1; then
|
||||||
log_warn "Removing conflicting package ${pkg} before installing ${selected_package}."
|
log_warn "Removing conflicting package ${pkg} before installing ${selected_package}."
|
||||||
pacman -Rsn "${PACMAN_REMOVE_FLAGS[@]}" "${pkg}"
|
pacman -Rsn "${PACMAN_REMOVE_FLAGS[@]}" "${pkg}"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
install_packages() {
|
install_packages() {
|
||||||
local -a packages=()
|
local -a packages=()
|
||||||
local -a headers=()
|
local -a headers=()
|
||||||
local host_package=$1
|
local host_package=$1
|
||||||
shift
|
shift
|
||||||
if [[ $# -gt 0 ]]; then
|
if [[ $# -gt 0 ]]; then
|
||||||
mapfile -t headers < <(printf '%s\n' "$@" | sort -u)
|
mapfile -t headers < <(printf '%s\n' "$@" | sort -u)
|
||||||
fi
|
fi
|
||||||
packages+=("virtualbox" "virtualbox-guest-iso" "${host_package}")
|
packages+=("virtualbox" "virtualbox-guest-iso" "${host_package}")
|
||||||
if [[ "${host_package}" == "virtualbox-host-dkms" ]]; then
|
if [[ ${host_package} == "virtualbox-host-dkms" ]]; then
|
||||||
packages+=("dkms")
|
packages+=("dkms")
|
||||||
fi
|
fi
|
||||||
if [[ ${#headers[@]} -gt 0 ]]; then
|
if [[ ${#headers[@]} -gt 0 ]]; then
|
||||||
packages+=("${headers[@]}")
|
packages+=("${headers[@]}")
|
||||||
fi
|
fi
|
||||||
log_info "Installing packages: ${packages[*]}"
|
log_info "Installing packages: ${packages[*]}"
|
||||||
pacman -S "${PACMAN_INSTALL_FLAGS[@]}" "${packages[@]}"
|
pacman -S "${PACMAN_INSTALL_FLAGS[@]}" "${packages[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
rebuild_virtualbox_modules() {
|
rebuild_virtualbox_modules() {
|
||||||
local host_package=$1
|
local host_package=$1
|
||||||
if [[ "${host_package}" == "virtualbox-host-dkms" ]]; then
|
if [[ ${host_package} == "virtualbox-host-dkms" ]]; then
|
||||||
if command -v dkms >/dev/null 2>&1; then
|
if command -v dkms > /dev/null 2>&1; then
|
||||||
log_info "Rebuilding VirtualBox DKMS modules for all installed kernels."
|
log_info "Rebuilding VirtualBox DKMS modules for all installed kernels."
|
||||||
dkms autoinstall
|
dkms autoinstall
|
||||||
else
|
else
|
||||||
log_warn "dkms command not found; skipping DKMS rebuild."
|
log_warn "dkms command not found; skipping DKMS rebuild."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
reload_virtualbox_modules() {
|
reload_virtualbox_modules() {
|
||||||
log_info "Loading VirtualBox kernel modules."
|
log_info "Loading VirtualBox kernel modules."
|
||||||
if [[ -x /sbin/rcvboxdrv ]]; then
|
if [[ -x /sbin/rcvboxdrv ]]; then
|
||||||
/sbin/rcvboxdrv setup || log_warn "rcvboxdrv reported an issue while setting up modules."
|
/sbin/rcvboxdrv setup || log_warn "rcvboxdrv reported an issue while setting up modules."
|
||||||
elif [[ -x /usr/lib/virtualbox/vboxdrv.sh ]]; then
|
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."
|
/usr/lib/virtualbox/vboxdrv.sh setup || log_warn "vboxdrv.sh reported an issue while setting up modules."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local -a modules=(vboxdrv vboxnetflt vboxnetadp vboxpci)
|
local -a modules=(vboxdrv vboxnetflt vboxnetadp vboxpci)
|
||||||
local mod
|
local mod
|
||||||
for mod in "${modules[@]}"; do
|
for mod in "${modules[@]}"; do
|
||||||
if ! lsmod | awk '{print $1}' | grep -Fxq "${mod}"; then
|
if ! lsmod | awk '{print $1}' | grep -Fxq "${mod}"; then
|
||||||
if ! modprobe "${mod}" >/dev/null 2>&1; then
|
if ! modprobe "${mod}" > /dev/null 2>&1; then
|
||||||
log_warn "Module ${mod} failed to load; check dmesg for details."
|
log_warn "Module ${mod} failed to load; check dmesg for details."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if ! lsmod | awk '{print $1}' | grep -Fxq "vboxdrv"; then
|
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."
|
log_error "VirtualBox kernel driver (vboxdrv) failed to load. Review /var/log and dmesg output for clues."
|
||||||
fi
|
fi
|
||||||
log_info "VirtualBox kernel driver loaded successfully."
|
log_info "VirtualBox kernel driver loaded successfully."
|
||||||
}
|
}
|
||||||
|
|
||||||
warn_if_secure_boot_enabled() {
|
warn_if_secure_boot_enabled() {
|
||||||
local secure_boot_file
|
local secure_boot_file
|
||||||
if [[ -d /sys/firmware/efi/efivars ]]; then
|
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)
|
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
|
if [[ -n ${secure_boot_file} && -r ${secure_boot_file} ]]; then
|
||||||
local state
|
local state
|
||||||
state=$(hexdump -n 1 -s 4 -e '1 "%d"' "${secure_boot_file}" 2>/dev/null || echo "0")
|
state=$(hexdump -n 1 -s 4 -e '1 "%d"' "${secure_boot_file}" 2> /dev/null || echo "0")
|
||||||
if [[ "${state}" == "1" ]]; then
|
if [[ ${state} == "1" ]]; then
|
||||||
log_warn "EFI Secure Boot appears to be enabled. You may need to sign VirtualBox modules manually."
|
log_warn "EFI Secure Boot appears to be enabled. You may need to sign VirtualBox modules manually."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
remind_group_membership() {
|
remind_group_membership() {
|
||||||
local invoking_user=${SUDO_USER:-}
|
local invoking_user=${SUDO_USER:-}
|
||||||
if [[ -n "${invoking_user}" && "${invoking_user}" != "root" ]]; then
|
if [[ -n ${invoking_user} && ${invoking_user} != "root" ]]; then
|
||||||
if ! id -nG "${invoking_user}" | grep -qw "vboxusers"; 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"
|
log_warn "User ${invoking_user} is not in the vboxusers group. Add them with: sudo gpasswd -a ${invoking_user} vboxusers"
|
||||||
else
|
else
|
||||||
log_info "User ${invoking_user} is already in the vboxusers group."
|
log_info "User ${invoking_user} is already in the vboxusers group."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
require_root
|
require_root
|
||||||
require_pacman
|
require_pacman
|
||||||
|
|
||||||
PACMAN_INSTALL_FLAGS=(--needed)
|
PACMAN_INSTALL_FLAGS=(--needed)
|
||||||
PACMAN_REMOVE_FLAGS=()
|
PACMAN_REMOVE_FLAGS=()
|
||||||
if [[ "${PACMAN_CONFIRM:-0}" == "1" ]]; then
|
if [[ ${PACMAN_CONFIRM:-0} == "1" ]]; then
|
||||||
log_info "PACMAN_CONFIRM=1 detected; pacman will prompt for confirmation."
|
log_info "PACMAN_CONFIRM=1 detected; pacman will prompt for confirmation."
|
||||||
else
|
else
|
||||||
PACMAN_INSTALL_FLAGS+=(--noconfirm)
|
PACMAN_INSTALL_FLAGS+=(--noconfirm)
|
||||||
PACMAN_REMOVE_FLAGS+=(--noconfirm)
|
PACMAN_REMOVE_FLAGS+=(--noconfirm)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local kernel_release host_package
|
local kernel_release host_package
|
||||||
kernel_release=$(detect_kernel_release)
|
kernel_release=$(detect_kernel_release)
|
||||||
log_info "Detected running kernel: ${kernel_release}"
|
log_info "Detected running kernel: ${kernel_release}"
|
||||||
host_package=$(select_host_package "${kernel_release}")
|
host_package=$(select_host_package "${kernel_release}")
|
||||||
log_info "Selected VirtualBox host package: ${host_package}"
|
log_info "Selected VirtualBox host package: ${host_package}"
|
||||||
|
|
||||||
mapfile -t kernel_headers < <(collect_kernel_headers)
|
mapfile -t kernel_headers < <(collect_kernel_headers)
|
||||||
if [[ "${host_package}" == "virtualbox-host-dkms" && ${#kernel_headers[@]} -eq 0 ]]; then
|
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."
|
log_warn "No matching kernel headers detected. Ensure you've installed headers for your kernel so DKMS can build modules."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
maybe_remove_conflicting_host_packages "${host_package}"
|
maybe_remove_conflicting_host_packages "${host_package}"
|
||||||
install_packages "${host_package}" "${kernel_headers[@]}"
|
install_packages "${host_package}" "${kernel_headers[@]}"
|
||||||
rebuild_virtualbox_modules "${host_package}"
|
rebuild_virtualbox_modules "${host_package}"
|
||||||
reload_virtualbox_modules
|
reload_virtualbox_modules
|
||||||
warn_if_secure_boot_enabled
|
warn_if_secure_boot_enabled
|
||||||
remind_group_membership
|
remind_group_membership
|
||||||
|
|
||||||
log_info "VirtualBox installation and driver setup complete."
|
log_info "VirtualBox installation and driver setup complete."
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
@ -3,40 +3,40 @@
|
|||||||
# Script to disable NVIDIA GSP firmware and apply comprehensive NVIDIA fixes
|
# Script to disable NVIDIA GSP firmware and apply comprehensive NVIDIA fixes
|
||||||
# This addresses GSP issues, mesh shaders, OpenGL problems, and other NVIDIA issues
|
# 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
|
# Default to non-interactive mode
|
||||||
INTERACTIVE_MODE=false
|
INTERACTIVE_MODE=false
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
-i|--interactive)
|
-i | --interactive)
|
||||||
INTERACTIVE_MODE=true
|
INTERACTIVE_MODE=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h | --help)
|
||||||
echo "Usage: $0 [OPTIONS]"
|
echo "Usage: $0 [OPTIONS]"
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
|
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
|
||||||
echo " -h, --help Show this help message"
|
echo " -h, --help Show this help message"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown option: $1"
|
echo "Unknown option: $1"
|
||||||
echo "Use -h or --help for usage information"
|
echo "Use -h or --help for usage information"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Function to check and request sudo privileges
|
# Function to check and request sudo privileges
|
||||||
check_sudo() {
|
check_sudo() {
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
echo "This script requires sudo privileges to modify system files."
|
echo "This script requires sudo privileges to modify system files."
|
||||||
echo "Requesting sudo access..."
|
echo "Requesting sudo access..."
|
||||||
exec sudo "$0" "$@"
|
exec sudo "$0" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check for sudo privileges after argument parsing
|
# Check for sudo privileges after argument parsing
|
||||||
@ -47,15 +47,15 @@ echo "=================================================="
|
|||||||
echo "Current Date: $(date)"
|
echo "Current Date: $(date)"
|
||||||
echo "User: $USER"
|
echo "User: $USER"
|
||||||
echo "Original user: ${SUDO_USER:-$USER}"
|
echo "Original user: ${SUDO_USER:-$USER}"
|
||||||
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
|
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||||
echo "Mode: Interactive (prompts enabled)"
|
echo "Mode: Interactive (prompts enabled)"
|
||||||
else
|
else
|
||||||
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
|
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if nvidia module is loaded
|
# Check if nvidia module is loaded
|
||||||
if ! lsmod | grep -q nvidia; then
|
if ! lsmod | grep -q nvidia; then
|
||||||
echo "Warning: NVIDIA module not currently loaded"
|
echo "Warning: NVIDIA module not currently loaded"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create modprobe configuration directory if it doesn't exist
|
# 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
|
# Function to backup file if it exists
|
||||||
backup_file() {
|
backup_file() {
|
||||||
local file="$1"
|
local file="$1"
|
||||||
if [[ -f "$file" ]]; then
|
if [[ -f $file ]]; then
|
||||||
cp "$file" "$file.backup.$(date +%Y%m%d_%H%M%S)"
|
cp "$file" "$file.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
echo "✓ Backed up $file"
|
echo "✓ Backed up $file"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to add or update xorg.conf for RenderAccel
|
# Function to add or update xorg.conf for RenderAccel
|
||||||
configure_xorg() {
|
configure_xorg() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. Configuring Xorg Settings..."
|
echo "2. Configuring Xorg Settings..."
|
||||||
echo "==============================="
|
echo "==============================="
|
||||||
|
|
||||||
XORG_CONF="/etc/X11/xorg.conf"
|
XORG_CONF="/etc/X11/xorg.conf"
|
||||||
XORG_CONF_D="/etc/X11/xorg.conf.d"
|
XORG_CONF_D="/etc/X11/xorg.conf.d"
|
||||||
NVIDIA_CONF="$XORG_CONF_D/20-nvidia.conf"
|
NVIDIA_CONF="$XORG_CONF_D/20-nvidia.conf"
|
||||||
|
|
||||||
# Create xorg.conf.d directory if it doesn't exist
|
# Create xorg.conf.d directory if it doesn't exist
|
||||||
mkdir -p "$XORG_CONF_D"
|
mkdir -p "$XORG_CONF_D"
|
||||||
|
|
||||||
# Backup existing xorg.conf if it exists
|
# Backup existing xorg.conf if it exists
|
||||||
backup_file "$XORG_CONF"
|
backup_file "$XORG_CONF"
|
||||||
backup_file "$NVIDIA_CONF"
|
backup_file "$NVIDIA_CONF"
|
||||||
|
|
||||||
# Create NVIDIA-specific configuration
|
# Create NVIDIA-specific configuration
|
||||||
cat > "$NVIDIA_CONF" << EOF
|
cat > "$NVIDIA_CONF" << EOF
|
||||||
# NVIDIA configuration with RenderAccel disabled
|
# NVIDIA configuration with RenderAccel disabled
|
||||||
# Created by nvidia_troubleshoot.sh on $(date)
|
# Created by nvidia_troubleshoot.sh on $(date)
|
||||||
Section "Device"
|
Section "Device"
|
||||||
@ -112,103 +112,107 @@ Section "Device"
|
|||||||
Option "RenderAccel" "false"
|
Option "RenderAccel" "false"
|
||||||
EndSection
|
EndSection
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✓ Created $NVIDIA_CONF with RenderAccel disabled"
|
echo "✓ Created $NVIDIA_CONF with RenderAccel disabled"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to add GCC mismatch workaround
|
# Function to add GCC mismatch workaround
|
||||||
configure_gcc_workaround() {
|
configure_gcc_workaround() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. Configuring GCC Mismatch Workaround..."
|
echo "3. Configuring GCC Mismatch Workaround..."
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
|
|
||||||
PROFILE_FILE="/etc/profile"
|
local PROFILE_FILE="/etc/profile"
|
||||||
backup_file "$PROFILE_FILE"
|
local timestamp
|
||||||
|
timestamp=$(date)
|
||||||
# Check if IGNORE_CC_MISMATCH is already set
|
backup_file "$PROFILE_FILE"
|
||||||
if ! grep -q "IGNORE_CC_MISMATCH" "$PROFILE_FILE"; then
|
|
||||||
echo "" >> "$PROFILE_FILE"
|
# Check if IGNORE_CC_MISMATCH is already set
|
||||||
echo "# NVIDIA GCC version mismatch workaround" >> "$PROFILE_FILE"
|
if ! grep -q "IGNORE_CC_MISMATCH" "$PROFILE_FILE"; then
|
||||||
echo "# Added by nvidia_troubleshoot.sh on $(date)" >> "$PROFILE_FILE"
|
{
|
||||||
echo "export IGNORE_CC_MISMATCH=1" >> "$PROFILE_FILE"
|
printf '\n'
|
||||||
echo "✓ Added IGNORE_CC_MISMATCH=1 to $PROFILE_FILE"
|
printf '# NVIDIA GCC version mismatch workaround\n'
|
||||||
else
|
printf '# Added by nvidia_troubleshoot.sh on %s\n' "$timestamp"
|
||||||
echo "✓ IGNORE_CC_MISMATCH already configured in $PROFILE_FILE"
|
printf 'export IGNORE_CC_MISMATCH=1\n'
|
||||||
fi
|
} >> "$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
|
# Function to install pyroveil for mesh shader issues
|
||||||
install_pyroveil() {
|
install_pyroveil() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. Pyroveil Setup for Mesh Shader Issues..."
|
echo "4. Pyroveil Setup for Mesh Shader Issues..."
|
||||||
echo "==========================================="
|
echo "==========================================="
|
||||||
|
|
||||||
local user_home="/home/$SUDO_USER"
|
local user_home="/home/$SUDO_USER"
|
||||||
local pyroveil_dir="$user_home/pyroveil"
|
local pyroveil_dir="$user_home/pyroveil"
|
||||||
|
|
||||||
echo "Mesh shaders have poor support on NVIDIA drivers, causing issues in games"
|
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 "like Final Fantasy VII Rebirth. Pyroveil can work around these problems."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
local install_pyroveil=true
|
local install_pyroveil=true
|
||||||
|
|
||||||
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
|
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||||
read -p "Would you like to install Pyroveil? (y/N): " -n 1 -r
|
read -p "Would you like to install Pyroveil? (y/N): " -n 1 -r
|
||||||
echo
|
echo
|
||||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
install_pyroveil=false
|
install_pyroveil=false
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Auto-installing Pyroveil (use --interactive to prompt)"
|
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
if [[ "$install_pyroveil" == "true" ]]; then
|
echo "Auto-installing Pyroveil (use --interactive to prompt)"
|
||||||
# Check for required dependencies
|
fi
|
||||||
local missing_deps=()
|
|
||||||
|
if [[ $install_pyroveil == "true" ]]; then
|
||||||
for dep in git cmake ninja gcc; do
|
# Check for required dependencies
|
||||||
if ! command -v $dep &> /dev/null; then
|
local missing_deps=()
|
||||||
missing_deps+=($dep)
|
|
||||||
fi
|
for dep in git cmake ninja gcc; do
|
||||||
done
|
if ! command -v "$dep" &> /dev/null; then
|
||||||
|
missing_deps+=("$dep")
|
||||||
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
fi
|
||||||
echo "Missing dependencies: ${missing_deps[*]}"
|
done
|
||||||
echo "Please install them first. On Arch Linux:"
|
|
||||||
echo "pacman -S base-devel git cmake ninja"
|
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
||||||
return 1
|
echo "Missing dependencies: ${missing_deps[*]}"
|
||||||
fi
|
echo "Please install them first. On Arch Linux:"
|
||||||
|
echo "pacman -S base-devel git cmake ninja"
|
||||||
# Clone and build pyroveil as the original user
|
return 1
|
||||||
echo "Installing Pyroveil to $pyroveil_dir..."
|
fi
|
||||||
|
|
||||||
if [[ -d "$pyroveil_dir" ]]; then
|
# Clone and build pyroveil as the original user
|
||||||
echo "Pyroveil directory already exists. Updating..."
|
echo "Installing Pyroveil to $pyroveil_dir..."
|
||||||
sudo -u "$SUDO_USER" bash -c "cd '$pyroveil_dir' && git pull"
|
|
||||||
else
|
if [[ -d $pyroveil_dir ]]; then
|
||||||
sudo -u "$SUDO_USER" git clone https://github.com/HansKristian-Work/pyroveil.git "$pyroveil_dir"
|
echo "Pyroveil directory already exists. Updating..."
|
||||||
fi
|
sudo -u "$SUDO_USER" bash -c "cd '$pyroveil_dir' && git pull"
|
||||||
|
else
|
||||||
sudo -u "$SUDO_USER" bash -c "
|
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'
|
cd '$pyroveil_dir'
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
cmake . -Bbuild -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$user_home/.local
|
cmake . -Bbuild -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$user_home/.local
|
||||||
ninja -C build install
|
ninja -C build install
|
||||||
"
|
"
|
||||||
|
|
||||||
echo "✓ Pyroveil installed successfully"
|
echo "✓ Pyroveil installed successfully"
|
||||||
echo ""
|
echo ""
|
||||||
echo "To use Pyroveil with games that have mesh shader issues:"
|
echo "To use Pyroveil with games that have mesh shader issues:"
|
||||||
echo "1. For Final Fantasy VII Rebirth:"
|
echo "1. For Final Fantasy VII Rebirth:"
|
||||||
echo " PYROVEIL=1 PYROVEIL_CONFIG=$pyroveil_dir/hacks/ffvii-rebirth-nvidia/pyroveil.json %command%"
|
echo " PYROVEIL=1 PYROVEIL_CONFIG=$pyroveil_dir/hacks/ffvii-rebirth-nvidia/pyroveil.json %command%"
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. For Steam games, add to launch options:"
|
echo "2. For Steam games, add to launch options:"
|
||||||
echo " PYROVEIL=1 PYROVEIL_CONFIG=/path/to/config/pyroveil.json %command%"
|
echo " PYROVEIL=1 PYROVEIL_CONFIG=/path/to/config/pyroveil.json %command%"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Available configs in: $pyroveil_dir/hacks/"
|
echo "Available configs in: $pyroveil_dir/hacks/"
|
||||||
|
|
||||||
# Create a helper script
|
# Create a helper script
|
||||||
cat > "$user_home/run-with-pyroveil.sh" << EOF
|
cat > "$user_home/run-with-pyroveil.sh" << EOF
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Helper script to run games with Pyroveil
|
# Helper script to run games with Pyroveil
|
||||||
# Usage: ./run-with-pyroveil.sh <config-name> <command>
|
# Usage: ./run-with-pyroveil.sh <config-name> <command>
|
||||||
@ -233,88 +237,89 @@ echo "Config file: \$PYROVEIL_CONFIG"
|
|||||||
|
|
||||||
exec "\$@"
|
exec "\$@"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
chown "$SUDO_USER:$SUDO_USER" "$user_home/run-with-pyroveil.sh"
|
chown "$SUDO_USER:$SUDO_USER" "$user_home/run-with-pyroveil.sh"
|
||||||
chmod +x "$user_home/run-with-pyroveil.sh"
|
chmod +x "$user_home/run-with-pyroveil.sh"
|
||||||
echo "✓ Created helper script: $user_home/run-with-pyroveil.sh"
|
echo "✓ Created helper script: $user_home/run-with-pyroveil.sh"
|
||||||
|
|
||||||
else
|
else
|
||||||
echo "Skipping Pyroveil installation"
|
echo "Skipping Pyroveil installation"
|
||||||
echo "Note: You can manually install it later for mesh shader issues"
|
echo "Note: You can manually install it later for mesh shader issues"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to check for kernel parameter modifications
|
# Function to check for kernel parameter modifications
|
||||||
suggest_kernel_params() {
|
suggest_kernel_params() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "5. Kernel Parameter Recommendations..."
|
echo "5. Kernel Parameter Recommendations..."
|
||||||
echo "====================================="
|
echo "====================================="
|
||||||
|
|
||||||
echo "NVIDIA Driver Issues and Recommended Kernel Parameters:"
|
echo "NVIDIA Driver Issues and Recommended Kernel Parameters:"
|
||||||
echo ""
|
echo ""
|
||||||
echo "A) For 'conflicting memory type' or 'failed to allocate primary buffer' errors"
|
echo "A) For 'conflicting memory type' or 'failed to allocate primary buffer' errors"
|
||||||
echo " (especially with nvidia-96xx drivers):"
|
echo " (especially with nvidia-96xx drivers):"
|
||||||
echo " → Add 'nopat' to kernel parameters"
|
echo " → Add 'nopat' to kernel parameters"
|
||||||
echo ""
|
echo ""
|
||||||
echo "B) For OpenGL visual glitches, hangs, and errors with modern CPUs:"
|
echo "B) For OpenGL visual glitches, hangs, and errors with modern CPUs:"
|
||||||
echo " → Consider disabling micro-op cache in BIOS settings"
|
echo " → Consider disabling micro-op cache in BIOS settings"
|
||||||
echo " → This affects Intel Sandy Bridge (2011+) and AMD Zen (2017+) CPUs"
|
echo " → This affects Intel Sandy Bridge (2011+) and AMD Zen (2017+) CPUs"
|
||||||
echo " → Helps with severe graphical glitches in Xwayland applications"
|
echo " → Helps with severe graphical glitches in Xwayland applications"
|
||||||
echo " → Note: Disabling micro-op cache reduces CPU performance"
|
echo " → Note: Disabling micro-op cache reduces CPU performance"
|
||||||
echo ""
|
echo ""
|
||||||
echo "To add kernel parameters:"
|
echo "To add kernel parameters:"
|
||||||
echo "1. Edit /etc/default/grub"
|
echo "1. Edit /etc/default/grub"
|
||||||
echo "2. Add parameters to GRUB_CMDLINE_LINUX_DEFAULT"
|
echo "2. Add parameters to GRUB_CMDLINE_LINUX_DEFAULT"
|
||||||
echo "3. Run: grub-mkconfig -o /boot/grub/grub.cfg"
|
echo "3. Run: grub-mkconfig -o /boot/grub/grub.cfg"
|
||||||
echo "4. Reboot"
|
echo "4. Reboot"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Example GRUB_CMDLINE_LINUX_DEFAULT line:"
|
echo "Example GRUB_CMDLINE_LINUX_DEFAULT line:"
|
||||||
echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nopat"'
|
echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nopat"'
|
||||||
|
|
||||||
# Check current CPU for micro-op cache relevance
|
# Check current CPU for micro-op cache relevance
|
||||||
echo ""
|
echo ""
|
||||||
echo "CPU Information (for micro-op cache consideration):"
|
echo "CPU Information (for micro-op cache consideration):"
|
||||||
if command -v lscpu &> /dev/null; then
|
if command -v lscpu &> /dev/null; then
|
||||||
local cpu_info=$(lscpu | grep "Model name" | cut -d: -f2 | xargs)
|
local cpu_info
|
||||||
echo "Current CPU: $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"
|
if echo "$cpu_info" | grep -qi "intel"; then
|
||||||
elif echo "$cpu_info" | grep -qi "amd"; then
|
echo "→ Intel CPU detected. Sandy Bridge (2011) and later have micro-op cache"
|
||||||
echo "→ AMD CPU detected. Zen (2017) and later have micro-op cache"
|
elif echo "$cpu_info" | grep -qi "amd"; then
|
||||||
fi
|
echo "→ AMD CPU detected. Zen (2017) and later have micro-op cache"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to suggest desktop environment settings
|
# Function to suggest desktop environment settings
|
||||||
suggest_desktop_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 ""
|
||||||
echo "6. Desktop Environment Recommendations..."
|
echo "Detected desktop environment: $XDG_CURRENT_DESKTOP"
|
||||||
echo "========================================"
|
fi
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Apply all configurations
|
# Apply all configurations
|
||||||
@ -327,13 +332,13 @@ echo ""
|
|||||||
echo "7. Regenerating Initramfs..."
|
echo "7. Regenerating Initramfs..."
|
||||||
echo "============================"
|
echo "============================"
|
||||||
if command -v mkinitcpio &> /dev/null; then
|
if command -v mkinitcpio &> /dev/null; then
|
||||||
mkinitcpio -P
|
mkinitcpio -P
|
||||||
echo "✓ Initramfs regenerated with mkinitcpio"
|
echo "✓ Initramfs regenerated with mkinitcpio"
|
||||||
elif command -v dracut &> /dev/null; then
|
elif command -v dracut &> /dev/null; then
|
||||||
dracut --force
|
dracut --force
|
||||||
echo "✓ Initramfs regenerated with dracut"
|
echo "✓ Initramfs regenerated with dracut"
|
||||||
else
|
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
|
fi
|
||||||
|
|
||||||
# Display all recommendations
|
# Display all recommendations
|
||||||
@ -349,7 +354,7 @@ echo "✓ GSP firmware disabled"
|
|||||||
echo "✓ RenderAccel disabled in Xorg configuration"
|
echo "✓ RenderAccel disabled in Xorg configuration"
|
||||||
echo "✓ GCC version mismatch workaround added"
|
echo "✓ GCC version mismatch workaround added"
|
||||||
if [[ -d "/home/$SUDO_USER/pyroveil" ]]; then
|
if [[ -d "/home/$SUDO_USER/pyroveil" ]]; then
|
||||||
echo "✓ Pyroveil installed for mesh shader issues"
|
echo "✓ Pyroveil installed for mesh shader issues"
|
||||||
fi
|
fi
|
||||||
echo "✓ Initramfs regenerated"
|
echo "✓ Initramfs regenerated"
|
||||||
echo ""
|
echo ""
|
||||||
@ -359,4 +364,4 @@ echo "• Configure desktop environment compositing settings"
|
|||||||
echo "• Add kernel parameters if needed (nopat for memory issues)"
|
echo "• Add kernel parameters if needed (nopat for memory issues)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "IMPORTANT: You must reboot for changes to take effect!"
|
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"
|
||||||
|
|||||||
@ -25,24 +25,20 @@ INSTALL_ONLY="false"
|
|||||||
LIST_ONLY="false"
|
LIST_ONLY="false"
|
||||||
VERBOSE="false"
|
VERBOSE="false"
|
||||||
|
|
||||||
log() {
|
|
||||||
printf '%s\n' "$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
log_info() {
|
log_info() {
|
||||||
printf '\033[1;34m[INFO]\033[0m %s\n' "$*"
|
printf '\033[1;34m[INFO]\033[0m %s\n' "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
log_warn() {
|
log_warn() {
|
||||||
printf '\033[1;33m[WARN]\033[0m %s\n' "$*"
|
printf '\033[1;33m[WARN]\033[0m %s\n' "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
log_error() {
|
log_error() {
|
||||||
printf '\033[1;31m[ERROR]\033[0m %s\n' "$*" >&2
|
printf '\033[1;31m[ERROR]\033[0m %s\n' "$*" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat << EOF
|
||||||
Usage: $(basename "$0") [options]
|
Usage: $(basename "$0") [options]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
@ -61,341 +57,363 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--path)
|
--path)
|
||||||
ROOT_DIR="$2"; shift 2 ;;
|
ROOT_DIR="$2"
|
||||||
--skip-install)
|
shift 2
|
||||||
SKIP_INSTALL="true"; shift ;;
|
;;
|
||||||
--install-only)
|
--skip-install)
|
||||||
INSTALL_ONLY="true"; shift ;;
|
SKIP_INSTALL="true"
|
||||||
--list-only)
|
shift
|
||||||
LIST_ONLY="true"; shift ;;
|
;;
|
||||||
--verbose)
|
--install-only)
|
||||||
VERBOSE="true"; shift ;;
|
INSTALL_ONLY="true"
|
||||||
-h|--help)
|
shift
|
||||||
usage; exit 0 ;;
|
;;
|
||||||
*)
|
--list-only)
|
||||||
log_error "Unknown argument: $1"; usage; exit 2 ;;
|
LIST_ONLY="true"
|
||||||
esac
|
shift
|
||||||
|
;;
|
||||||
|
--verbose)
|
||||||
|
VERBOSE="true"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h | --help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_error "Unknown argument: $1"
|
||||||
|
usage
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ ! -d "$ROOT_DIR" ]]; then
|
if [[ ! -d $ROOT_DIR ]]; then
|
||||||
log_error "Path not found: $ROOT_DIR"
|
log_error "Path not found: $ROOT_DIR"
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
is_cmd() { command -v "$1" >/dev/null 2>&1; }
|
is_cmd() { command -v "$1" > /dev/null 2>&1; }
|
||||||
|
|
||||||
is_arch() { is_cmd pacman; }
|
is_arch() { is_cmd pacman; }
|
||||||
have_aur_helper() { is_cmd yay || is_cmd paru; }
|
have_aur_helper() { is_cmd yay || is_cmd paru; }
|
||||||
|
|
||||||
install_if_missing() {
|
install_if_missing() {
|
||||||
local pkg cmd
|
local pkg cmd
|
||||||
pkg="$1"; cmd="$2"
|
pkg="$1"
|
||||||
if is_cmd "$cmd"; then
|
cmd="$2"
|
||||||
[[ "$VERBOSE" == "true" ]] && log_info "Found $cmd"
|
if is_cmd "$cmd"; then
|
||||||
return 0
|
[[ $VERBOSE == "true" ]] && log_info "Found $cmd"
|
||||||
fi
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$SKIP_INSTALL" == "true" ]]; then
|
if [[ $SKIP_INSTALL == "true" ]]; then
|
||||||
log_warn "Skipping install of $pkg ($cmd not found)"
|
log_warn "Skipping install of $pkg ($cmd not found)"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if is_arch; then
|
if is_arch; then
|
||||||
log_info "Installing $pkg via pacman..."
|
log_info "Installing $pkg via pacman..."
|
||||||
if ! sudo pacman -S --needed --noconfirm "$pkg"; then
|
if ! sudo pacman -S --needed --noconfirm "$pkg"; then
|
||||||
log_warn "Failed to install $pkg via pacman."
|
log_warn "Failed to install $pkg via pacman."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
log_warn "Non-Arch system detected. Please install '$pkg' manually."
|
log_warn "Non-Arch system detected. Please install '$pkg' manually."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
install_linters() {
|
install_linters() {
|
||||||
local ok=0
|
local ok=0
|
||||||
|
|
||||||
# Core linters
|
# Core linters
|
||||||
install_if_missing shellcheck shellcheck || ok=1
|
install_if_missing shellcheck shellcheck || ok=1
|
||||||
install_if_missing shfmt shfmt || ok=1
|
install_if_missing shfmt shfmt || ok=1
|
||||||
|
|
||||||
# Optional linters (best-effort)
|
# Optional linters (best-effort)
|
||||||
# checkbashisms may be in repos or AUR; try pacman first, then AUR helper
|
# checkbashisms may be in repos or AUR; try pacman first, then AUR helper
|
||||||
if ! is_cmd checkbashisms; then
|
if ! is_cmd checkbashisms; then
|
||||||
if is_arch; then
|
if is_arch; then
|
||||||
if ! sudo pacman -S --needed --noconfirm checkbashisms 2>/dev/null; then
|
if ! sudo pacman -S --needed --noconfirm checkbashisms 2> /dev/null; then
|
||||||
if have_aur_helper; then
|
if have_aur_helper; then
|
||||||
log_info "Installing checkbashisms from AUR (requires yay/paru)..."
|
log_info "Installing checkbashisms from AUR (requires yay/paru)..."
|
||||||
if is_cmd yay; then yay -S --noconfirm checkbashisms || true; fi
|
if is_cmd yay; then yay -S --noconfirm checkbashisms || true; fi
|
||||||
if is_cmd paru; then paru -S --noconfirm checkbashisms || true; fi
|
if is_cmd paru; then paru -S --noconfirm checkbashisms || true; fi
|
||||||
else
|
else
|
||||||
log_warn "checkbashisms not installed (no AUR helper)."
|
log_warn "checkbashisms not installed (no AUR helper)."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# bashate (python-based), typically available as python-bashate in AUR
|
# bashate (python-based), typically available as python-bashate in AUR
|
||||||
if ! is_cmd bashate; then
|
if ! is_cmd bashate; then
|
||||||
if is_arch && have_aur_helper; then
|
if is_arch && have_aur_helper; then
|
||||||
log_info "Installing bashate from AUR (requires yay/paru)..."
|
log_info "Installing bashate from AUR (requires yay/paru)..."
|
||||||
if is_cmd yay; then yay -S --noconfirm python-bashate || true; fi
|
if is_cmd yay; then yay -S --noconfirm python-bashate || true; fi
|
||||||
if is_cmd paru; then paru -S --noconfirm python-bashate || true; fi
|
if is_cmd paru; then paru -S --noconfirm python-bashate || true; fi
|
||||||
else
|
else
|
||||||
# Try pip if user has it and wants to
|
# Try pip if user has it and wants to
|
||||||
if is_cmd pipx; then
|
if is_cmd pipx; then
|
||||||
log_info "Installing bashate via pipx..."
|
log_info "Installing bashate via pipx..."
|
||||||
pipx install bashate || true
|
pipx install bashate || true
|
||||||
elif is_cmd pip3; then
|
elif is_cmd pip3; then
|
||||||
log_info "Installing bashate via pip (user)..."
|
log_info "Installing bashate via pip (user)..."
|
||||||
pip3 install --user bashate || true
|
pip3 install --user bashate || true
|
||||||
else
|
else
|
||||||
log_warn "bashate not installed (no AUR helper or pip available)."
|
log_warn "bashate not installed (no AUR helper or pip available)."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return "$ok"
|
return "$ok"
|
||||||
}
|
}
|
||||||
|
|
||||||
TMPDIR=$(mktemp -d)
|
TMPDIR=$(mktemp -d)
|
||||||
cleanup() { rm -rf "$TMPDIR"; }
|
trap 'rm -rf "${TMPDIR:-}"' EXIT
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
ABS_FILES_Z="$TMPDIR/files_abs.zlist"
|
ABS_FILES_Z="$TMPDIR/files_abs.zlist"
|
||||||
REL_FILES_Z="$TMPDIR/files_rel.zlist"
|
REL_FILES_Z="$TMPDIR/files_rel.zlist"
|
||||||
|
|
||||||
discover_shell_files() {
|
discover_shell_files() {
|
||||||
local base="$1"
|
local base="$1"
|
||||||
local -a all
|
local -a all
|
||||||
all=()
|
all=()
|
||||||
|
|
||||||
if git -C "$base" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
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 -z)
|
||||||
while IFS= read -r -d '' f; do all+=("$f"); done < <(git -C "$base" ls-files --others --exclude-standard -z)
|
while IFS= read -r -d '' f; do all+=("$f"); done < <(git -C "$base" ls-files --others --exclude-standard -z)
|
||||||
else
|
else
|
||||||
while IFS= read -r -d '' f; do
|
while IFS= read -r -d '' f; do
|
||||||
# trim leading ./ to keep consistent style with git paths
|
# trim leading ./ to keep consistent style with git paths
|
||||||
f="${f#./}"
|
f="${f#./}"
|
||||||
f="${f#${base}/}"
|
f="${f#"${base}"/}"
|
||||||
all+=("$f")
|
all+=("$f")
|
||||||
done < <(find "$base" -type f -print0)
|
done < <(find "$base" -type f -print0)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local -a shells
|
local -a shells
|
||||||
shells=()
|
shells=()
|
||||||
|
|
||||||
for rel in "${all[@]}"; do
|
for rel in "${all[@]}"; do
|
||||||
# skip binary-ish or huge files quickly by extension heuristic
|
# skip binary-ish or huge files quickly by extension heuristic
|
||||||
case "$rel" in
|
case "$rel" in
|
||||||
*.png|*.jpg|*.jpeg|*.gif|*.ico|*.pdf|*.svg|*.zip|*.tar|*.gz|*.xz|*.7z|*.so|*.o|*.bin)
|
*.png | *.jpg | *.jpeg | *.gif | *.ico | *.pdf | *.svg | *.zip | *.tar | *.gz | *.xz | *.7z | *.so | *.o | *.bin)
|
||||||
continue ;;
|
continue
|
||||||
esac
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
local abs="$base/$rel"
|
local abs="$base/$rel"
|
||||||
[[ -f "$abs" && -r "$abs" ]] || continue
|
[[ -f $abs && -r $abs ]] || continue
|
||||||
|
|
||||||
if [[ "$rel" == *.sh || "$rel" == *.bash || "$rel" == *.zsh ]]; then
|
if [[ $rel == *.sh || $rel == *.bash || $rel == *.zsh ]]; then
|
||||||
shells+=("$rel")
|
shells+=("$rel")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check shebang
|
# Check shebang
|
||||||
local first
|
local first
|
||||||
first=$(head -n 1 -- "$abs" 2>/dev/null || true)
|
first=$(head -n 1 -- "$abs" 2> /dev/null || true)
|
||||||
if [[ "$first" =~ ^#! && "$first" =~ (ba|z|d|k)?sh ]]; then
|
if [[ $first =~ ^#! && $first =~ (ba|z|d|k)?sh ]]; then
|
||||||
shells+=("$rel")
|
shells+=("$rel")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Also catch executable files with shell shebang even without extension
|
# Also catch executable files with shell shebang even without extension
|
||||||
if [[ -x "$abs" ]]; then
|
if [[ -x $abs ]]; then
|
||||||
if [[ "$first" =~ ^#! && "$first" =~ (ba|z|d|k)?sh ]]; then
|
if [[ $first =~ ^#! && $first =~ (ba|z|d|k)?sh ]]; then
|
||||||
shells+=("$rel")
|
shells+=("$rel")
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# write lists
|
# write lists
|
||||||
: >"$REL_FILES_Z"
|
: > "$REL_FILES_Z"
|
||||||
: >"$ABS_FILES_Z"
|
: > "$ABS_FILES_Z"
|
||||||
for rel in "${shells[@]}"; do
|
for rel in "${shells[@]}"; do
|
||||||
printf '%s\0' "$rel" >> "$REL_FILES_Z"
|
printf '%s\0' "$rel" >> "$REL_FILES_Z"
|
||||||
printf '%s\0' "$base/$rel" >> "$ABS_FILES_Z"
|
printf '%s\0' "$base/$rel" >> "$ABS_FILES_Z"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
print_file_list() {
|
print_file_list() {
|
||||||
local count
|
local count
|
||||||
count=$(tr -cd '\0' < "$REL_FILES_Z" | wc -c)
|
count=$(tr -cd '\0' < "$REL_FILES_Z" | wc -c)
|
||||||
log_info "Discovered $count shell file(s) under $ROOT_DIR"
|
log_info "Discovered $count shell file(s) under $ROOT_DIR"
|
||||||
if [[ "$VERBOSE" == "true" ]]; then
|
if [[ $VERBOSE == "true" ]]; then
|
||||||
tr '\0' '\n' < "$REL_FILES_Z" | sed 's/^/ - /'
|
tr '\0' '\n' < "$REL_FILES_Z" | sed 's/^/ - /'
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
run_linters() {
|
run_linters() {
|
||||||
local issues=0
|
local issues=0
|
||||||
local count
|
local count
|
||||||
count=$(tr -cd '\0' < "$ABS_FILES_Z" | wc -c)
|
count=$(tr -cd '\0' < "$ABS_FILES_Z" | wc -c)
|
||||||
if [[ "$count" -eq 0 ]]; then
|
if [[ $count -eq 0 ]]; then
|
||||||
log_warn "No shell files found to lint."
|
log_warn "No shell files found to lint."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mapfile -d '' -t FILES < "$ABS_FILES_Z"
|
mapfile -d '' -t FILES < "$ABS_FILES_Z"
|
||||||
|
|
||||||
log_info "Running shellcheck..."
|
log_info "Running shellcheck..."
|
||||||
local sc_out="$TMPDIR/shellcheck.txt"
|
local sc_out="$TMPDIR/shellcheck.txt"
|
||||||
if is_cmd shellcheck; then
|
if is_cmd shellcheck; then
|
||||||
if ! shellcheck -x -S style "${FILES[@]}" >"$sc_out" 2>&1; then
|
if ! shellcheck -x -S style "${FILES[@]}" > "$sc_out" 2>&1; then
|
||||||
issues=$((issues+1))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
log_warn "shellcheck not found; skipping"
|
log_warn "shellcheck not found; skipping"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_info "Running shfmt (diff mode)..."
|
log_info "Running shfmt (diff mode)..."
|
||||||
local shfmt_out="$TMPDIR/shfmt.diff"
|
local shfmt_out="$TMPDIR/shfmt.diff"
|
||||||
if is_cmd shfmt; then
|
if is_cmd shfmt; then
|
||||||
if ! shfmt -d -i 2 -ci -sr -s "${FILES[@]}" >"$shfmt_out" 2>&1; then
|
if ! shfmt -d -i 2 -ci -sr -s "${FILES[@]}" > "$shfmt_out" 2>&1; then
|
||||||
# shfmt returns non-zero when diff exists
|
# shfmt returns non-zero when diff exists
|
||||||
issues=$((issues+1))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
log_warn "shfmt not found; skipping"
|
log_warn "shfmt not found; skipping"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_info "Running checkbashisms (optional)..."
|
log_info "Running checkbashisms (optional)..."
|
||||||
local cbi_out="$TMPDIR/checkbashisms.txt"
|
local cbi_out="$TMPDIR/checkbashisms.txt"
|
||||||
if is_cmd checkbashisms; then
|
local cbi_status=0
|
||||||
# checkbashisms exits 0 if OK, 1 if issues
|
if is_cmd checkbashisms; then
|
||||||
if ! checkbashisms "${FILES[@]}" >"$cbi_out" 2>&1; then
|
# checkbashisms exits 0 if OK, 1 if issues, other codes for tool warnings
|
||||||
issues=$((issues+1))
|
checkbashisms "${FILES[@]}" > "$cbi_out" 2>&1
|
||||||
fi
|
cbi_status=$?
|
||||||
else
|
if [[ $cbi_status -eq 1 ]]; then
|
||||||
log_warn "checkbashisms not found; skipping"
|
issues=$((issues + 1))
|
||||||
fi
|
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)..."
|
log_info "Running bash/zsh/sh syntax checks (-n)..."
|
||||||
local bash_out="$TMPDIR/bash_syntax.txt"
|
local bash_out="$TMPDIR/bash_syntax.txt"
|
||||||
local zsh_out="$TMPDIR/zsh_syntax.txt"
|
local zsh_out="$TMPDIR/zsh_syntax.txt"
|
||||||
local sh_out="$TMPDIR/sh_syntax.txt"
|
local sh_out="$TMPDIR/sh_syntax.txt"
|
||||||
|
|
||||||
# Partition files by shebang for better accuracy
|
# Partition files by shebang for better accuracy
|
||||||
local -a BASH_FILES ZSH_FILES SH_FILES
|
local -a BASH_FILES ZSH_FILES SH_FILES
|
||||||
BASH_FILES=(); ZSH_FILES=(); SH_FILES=()
|
BASH_FILES=()
|
||||||
for f in "${FILES[@]}"; do
|
ZSH_FILES=()
|
||||||
local first
|
SH_FILES=()
|
||||||
first=$(head -n 1 -- "$f" 2>/dev/null || true)
|
for f in "${FILES[@]}"; do
|
||||||
if [[ "$first" =~ bash ]]; then
|
local first
|
||||||
BASH_FILES+=("$f")
|
first=$(head -n 1 -- "$f" 2> /dev/null || true)
|
||||||
elif [[ "$first" =~ zsh ]]; then
|
if [[ $first =~ bash ]]; then
|
||||||
ZSH_FILES+=("$f")
|
BASH_FILES+=("$f")
|
||||||
else
|
elif [[ $first =~ zsh ]]; then
|
||||||
SH_FILES+=("$f")
|
ZSH_FILES+=("$f")
|
||||||
fi
|
else
|
||||||
done
|
SH_FILES+=("$f")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
if [[ ${#BASH_FILES[@]} -gt 0 ]] && is_cmd bash; then
|
if [[ ${#BASH_FILES[@]} -gt 0 ]] && is_cmd bash; then
|
||||||
if ! bash -n "${BASH_FILES[@]}" 2>"$bash_out"; then
|
if ! bash -n "${BASH_FILES[@]}" 2> "$bash_out"; then
|
||||||
issues=$((issues+1))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if [[ ${#ZSH_FILES[@]} -gt 0 ]] && is_cmd zsh; then
|
if [[ ${#ZSH_FILES[@]} -gt 0 ]] && is_cmd zsh; then
|
||||||
if ! zsh -n "${ZSH_FILES[@]}" 2>"$zsh_out"; then
|
if ! zsh -n "${ZSH_FILES[@]}" 2> "$zsh_out"; then
|
||||||
issues=$((issues+1))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
# prefer dash if present for /bin/sh style
|
# prefer dash if present for /bin/sh style
|
||||||
if [[ ${#SH_FILES[@]} -gt 0 ]]; then
|
if [[ ${#SH_FILES[@]} -gt 0 ]]; then
|
||||||
if is_cmd dash; then
|
if is_cmd dash; then
|
||||||
if ! dash -n "${SH_FILES[@]}" 2>"$sh_out"; then
|
if ! dash -n "${SH_FILES[@]}" 2> "$sh_out"; then
|
||||||
issues=$((issues+1))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
elif is_cmd sh; then
|
elif is_cmd sh; then
|
||||||
if ! sh -n "${SH_FILES[@]}" 2>"$sh_out"; then
|
if ! sh -n "${SH_FILES[@]}" 2> "$sh_out"; then
|
||||||
issues=$((issues+1))
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
log_info "========== Shell Lint Report =========="
|
log_info "========== Shell Lint Report =========="
|
||||||
|
|
||||||
if [[ -s "$sc_out" ]]; then
|
if [[ -s $sc_out ]]; then
|
||||||
printf '\n\033[1m-- shellcheck --\033[0m\n'
|
printf '\n\033[1m-- shellcheck --\033[0m\n'
|
||||||
cat "$sc_out"
|
cat "$sc_out"
|
||||||
else
|
else
|
||||||
printf '\n\033[1;32m-- shellcheck: PASS (no issues) --\033[0m\n'
|
printf '\n\033[1;32m-- shellcheck: PASS (no issues) --\033[0m\n'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -s "$shfmt_out" ]]; then
|
if [[ -s $shfmt_out ]]; then
|
||||||
printf '\n\033[1m-- shfmt (diffs found) --\033[0m\n'
|
printf '\n\033[1m-- shfmt (diffs found) --\033[0m\n'
|
||||||
cat "$shfmt_out"
|
cat "$shfmt_out"
|
||||||
else
|
else
|
||||||
printf '\n\033[1;32m-- shfmt: PASS (formatted) --\033[0m\n'
|
printf '\n\033[1;32m-- shfmt: PASS (formatted) --\033[0m\n'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -s "$cbi_out" ]]; then
|
if [[ -s $cbi_out ]]; then
|
||||||
printf '\n\033[1m-- checkbashisms --\033[0m\n'
|
printf '\n\033[1m-- checkbashisms --\033[0m\n'
|
||||||
cat "$cbi_out"
|
cat "$cbi_out"
|
||||||
else
|
else
|
||||||
printf '\n\033[1;32m-- checkbashisms: PASS (or skipped) --\033[0m\n'
|
printf '\n\033[1;32m-- checkbashisms: PASS (or skipped) --\033[0m\n'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -s "$bash_out" ]]; then
|
if [[ -s $bash_out ]]; then
|
||||||
printf '\n\033[1m-- bash -n (syntax) --\033[0m\n'
|
printf '\n\033[1m-- bash -n (syntax) --\033[0m\n'
|
||||||
cat "$bash_out"
|
cat "$bash_out"
|
||||||
else
|
else
|
||||||
printf '\n\033[1;32m-- bash -n: PASS (or none) --\033[0m\n'
|
printf '\n\033[1;32m-- bash -n: PASS (or none) --\033[0m\n'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -s "$zsh_out" ]]; then
|
if [[ -s $zsh_out ]]; then
|
||||||
printf '\n\033[1m-- zsh -n (syntax) --\033[0m\n'
|
printf '\n\033[1m-- zsh -n (syntax) --\033[0m\n'
|
||||||
cat "$zsh_out"
|
cat "$zsh_out"
|
||||||
else
|
else
|
||||||
printf '\n\033[1;32m-- zsh -n: PASS (or none) --\033[0m\n'
|
printf '\n\033[1;32m-- zsh -n: PASS (or none) --\033[0m\n'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -s "$sh_out" ]]; then
|
if [[ -s $sh_out ]]; then
|
||||||
printf '\n\033[1m-- sh/dash -n (syntax) --\033[0m\n'
|
printf '\n\033[1m-- sh/dash -n (syntax) --\033[0m\n'
|
||||||
cat "$sh_out"
|
cat "$sh_out"
|
||||||
else
|
else
|
||||||
printf '\n\033[1;32m-- sh/dash -n: PASS (or none) --\033[0m\n'
|
printf '\n\033[1;32m-- sh/dash -n: PASS (or none) --\033[0m\n'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
if [[ $issues -gt 0 ]]; then
|
if [[ $issues -gt 0 ]]; then
|
||||||
log_error "Linting completed with $issues tool(s) reporting issues."
|
log_error "Linting completed with $issues tool(s) reporting issues."
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
log_info "All checks passed."
|
log_info "All checks passed."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main
|
# Main
|
||||||
if [[ "$INSTALL_ONLY" == "true" ]]; then
|
if [[ $INSTALL_ONLY == "true" ]]; then
|
||||||
install_linters
|
install_linters
|
||||||
exit $?
|
exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Only attempt installs if not list-only
|
# Only attempt installs if not list-only
|
||||||
if [[ "$LIST_ONLY" != "true" ]]; then
|
if [[ $LIST_ONLY != "true" ]]; then
|
||||||
install_linters || true
|
install_linters || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
discover_shell_files "$ROOT_DIR"
|
discover_shell_files "$ROOT_DIR"
|
||||||
print_file_list
|
print_file_list
|
||||||
|
|
||||||
if [[ "$LIST_ONLY" == "true" ]]; then
|
if [[ $LIST_ONLY == "true" ]]; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
run_linters
|
run_linters
|
||||||
exit $?
|
exit $?
|
||||||
|
|
||||||
|
|||||||
@ -3,40 +3,40 @@
|
|||||||
# Executes every hour and on system startup
|
# Executes every hour and on system startup
|
||||||
# Handles sudo privileges automatically
|
# Handles sudo privileges automatically
|
||||||
|
|
||||||
set -e # Exit on any error
|
set -e # Exit on any error
|
||||||
|
|
||||||
# Default to non-interactive mode
|
# Default to non-interactive mode
|
||||||
INTERACTIVE_MODE=false
|
INTERACTIVE_MODE=false
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
-i|--interactive)
|
-i | --interactive)
|
||||||
INTERACTIVE_MODE=true
|
INTERACTIVE_MODE=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h | --help)
|
||||||
echo "Usage: $0 [OPTIONS]"
|
echo "Usage: $0 [OPTIONS]"
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
|
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
|
||||||
echo " -h, --help Show this help message"
|
echo " -h, --help Show this help message"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown option: $1"
|
echo "Unknown option: $1"
|
||||||
echo "Use -h or --help for usage information"
|
echo "Use -h or --help for usage information"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Function to check and request sudo privileges
|
# Function to check and request sudo privileges
|
||||||
check_sudo() {
|
check_sudo() {
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
echo "This script requires sudo privileges to create systemd services and timers."
|
echo "This script requires sudo privileges to create systemd services and timers."
|
||||||
echo "Requesting sudo access..."
|
echo "Requesting sudo access..."
|
||||||
exec sudo "$0" "$@"
|
exec sudo "$0" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check for sudo privileges after argument parsing
|
# Check for sudo privileges after argument parsing
|
||||||
@ -47,10 +47,10 @@ echo "==================================================="
|
|||||||
echo "Current Date: $(date)"
|
echo "Current Date: $(date)"
|
||||||
echo "User: $USER"
|
echo "User: $USER"
|
||||||
echo "Original user: ${SUDO_USER:-$USER}"
|
echo "Original user: ${SUDO_USER:-$USER}"
|
||||||
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
|
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||||
echo "Mode: Interactive (prompts enabled)"
|
echo "Mode: Interactive (prompts enabled)"
|
||||||
else
|
else
|
||||||
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
|
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the directory where this script is located
|
# 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
|
# Function to verify required files exist
|
||||||
verify_files() {
|
verify_files() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "1. Verifying Required Files..."
|
echo "1. Verifying Required Files..."
|
||||||
echo "=============================="
|
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
|
|
||||||
|
|
||||||
if [[ ${#missing_files[@]} -gt 0 ]]; then
|
local missing_files=()
|
||||||
echo "Error: The following required files are missing:"
|
|
||||||
for file in "${missing_files[@]}"; do
|
if [[ ! -f $PACMAN_WRAPPER_SCRIPT ]]; then
|
||||||
echo " - $file"
|
missing_files+=("$PACMAN_WRAPPER_SCRIPT")
|
||||||
done
|
fi
|
||||||
exit 1
|
|
||||||
|
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
|
fi
|
||||||
|
done
|
||||||
echo "✓ All required files found"
|
|
||||||
|
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
|
# Function to create the combined execution script
|
||||||
create_execution_script() {
|
create_execution_script() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. Creating Combined Execution Script..."
|
echo "2. Creating Combined Execution Script..."
|
||||||
echo "======================================="
|
echo "======================================="
|
||||||
|
|
||||||
local exec_script="/usr/local/bin/periodic-system-maintenance.sh"
|
|
||||||
|
|
||||||
# Install from template with path substitutions
|
local exec_script="/usr/local/bin/periodic-system-maintenance.sh"
|
||||||
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"
|
# Install from template with path substitutions
|
||||||
echo "✓ Installed execution script from template: $exec_script"
|
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
|
# Function to create systemd service
|
||||||
create_systemd_service() {
|
create_systemd_service() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. Creating Systemd Service..."
|
echo "3. Creating Systemd Service..."
|
||||||
echo "============================="
|
echo "============================="
|
||||||
|
|
||||||
local service_file="/etc/systemd/system/periodic-system-maintenance.service"
|
local service_file="/etc/systemd/system/periodic-system-maintenance.service"
|
||||||
install -m 0644 "$TEMPLATE_SVC_MAINT" "$service_file"
|
install -m 0644 "$TEMPLATE_SVC_MAINT" "$service_file"
|
||||||
echo "✓ Installed systemd service from template: $service_file"
|
echo "✓ Installed systemd service from template: $service_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create systemd timer for hourly execution
|
# Function to create systemd timer for hourly execution
|
||||||
create_systemd_timer() {
|
create_systemd_timer() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. Creating Systemd Timer..."
|
echo "4. Creating Systemd Timer..."
|
||||||
echo "============================"
|
echo "============================"
|
||||||
|
|
||||||
local timer_file="/etc/systemd/system/periodic-system-maintenance.timer"
|
local timer_file="/etc/systemd/system/periodic-system-maintenance.timer"
|
||||||
install -m 0644 "$TEMPLATE_TIMER" "$timer_file"
|
install -m 0644 "$TEMPLATE_TIMER" "$timer_file"
|
||||||
echo "✓ Installed systemd timer from template: $timer_file"
|
echo "✓ Installed systemd timer from template: $timer_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create startup service (additional to timer)
|
# Function to create startup service (additional to timer)
|
||||||
create_startup_service() {
|
create_startup_service() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "5. Creating Startup Service..."
|
echo "5. Creating Startup Service..."
|
||||||
echo "=============================="
|
echo "=============================="
|
||||||
|
|
||||||
local startup_service="/etc/systemd/system/periodic-system-startup.service"
|
local startup_service="/etc/systemd/system/periodic-system-startup.service"
|
||||||
install -m 0644 "$TEMPLATE_STARTUP" "$startup_service"
|
install -m 0644 "$TEMPLATE_STARTUP" "$startup_service"
|
||||||
echo "✓ Installed startup service from template: $startup_service"
|
echo "✓ Installed startup service from template: $startup_service"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create hosts file monitor service
|
# Function to create hosts file monitor service
|
||||||
create_hosts_monitor_service() {
|
create_hosts_monitor_service() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "6. Creating Hosts File Monitor Service..."
|
echo "6. Creating Hosts File Monitor Service..."
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
|
|
||||||
local monitor_script="/usr/local/bin/hosts-file-monitor.sh"
|
|
||||||
local monitor_service="/etc/systemd/system/hosts-file-monitor.service"
|
|
||||||
|
|
||||||
# Install the monitor script from template with substitution
|
local monitor_script="/usr/local/bin/hosts-file-monitor.sh"
|
||||||
sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
|
local monitor_service="/etc/systemd/system/hosts-file-monitor.service"
|
||||||
"$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 the monitor script from template with substitution
|
||||||
install -m 0644 "$TEMPLATE_HOSTS_SVC" "$monitor_service"
|
sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
|
||||||
echo "✓ Installed hosts monitor service from template: $monitor_service"
|
"$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
|
# Function to install browser pre-exec wrapper and wire common browser names
|
||||||
install_browser_preexec_wrapper() {
|
install_browser_preexec_wrapper() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "6.1 Installing Browser Pre-Exec Wrapper..."
|
echo "6.1 Installing Browser Pre-Exec Wrapper..."
|
||||||
echo "========================================="
|
echo "========================================="
|
||||||
|
|
||||||
local wrapper="/usr/local/bin/browser-preexec-wrapper"
|
local wrapper="/usr/local/bin/browser-preexec-wrapper"
|
||||||
sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
|
sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
|
||||||
"$TEMPLATE_BROWSER_WRAPPER" > "$wrapper"
|
"$TEMPLATE_BROWSER_WRAPPER" > "$wrapper"
|
||||||
chmod +x "$wrapper"
|
chmod +x "$wrapper"
|
||||||
echo "✓ Installed wrapper: $wrapper"
|
echo "✓ Installed wrapper: $wrapper"
|
||||||
|
|
||||||
# Allow passwordless execution of hosts installer for root-only actions
|
# Allow passwordless execution of hosts installer for root-only actions
|
||||||
local sudoers_file="/etc/sudoers.d/hosts-install-no-passwd"
|
local sudoers_file="/etc/sudoers.d/hosts-install-no-passwd"
|
||||||
if command -v visudo >/dev/null 2>&1; then
|
if command -v visudo > /dev/null 2>&1; then
|
||||||
echo "${SUDO_USER:-$USER} ALL=(ALL) NOPASSWD: $HOSTS_INSTALL_SCRIPT" > "$sudoers_file"
|
echo "${SUDO_USER:-$USER} ALL=(ALL) NOPASSWD: $HOSTS_INSTALL_SCRIPT" > "$sudoers_file"
|
||||||
chmod 440 "$sudoers_file"
|
chmod 440 "$sudoers_file"
|
||||||
# Validate syntax
|
# Validate syntax
|
||||||
visudo -c >/dev/null || echo "Warning: sudoers validation returned non-zero"
|
visudo -c > /dev/null || echo "Warning: sudoers validation returned non-zero"
|
||||||
echo "✓ Sudoers drop-in created: $sudoers_file"
|
echo "✓ Sudoers drop-in created: $sudoers_file"
|
||||||
else
|
else
|
||||||
echo "visudo not found; skipping sudoers drop-in"
|
echo "visudo not found; skipping sudoers drop-in"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create symlinks for common browser commands to the wrapper 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.
|
# 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" )
|
local browsers=("thorium-browser" "google-chrome" "google-chrome-stable" "chromium" "brave" "brave-browser" "vivaldi-stable" "firefox")
|
||||||
for b in "${browsers[@]}"; do
|
for b in "${browsers[@]}"; do
|
||||||
local link="/usr/local/bin/$b"
|
local link="/usr/local/bin/$b"
|
||||||
ln -sf "$wrapper" "$link"
|
ln -sf "$wrapper" "$link"
|
||||||
done
|
done
|
||||||
echo "✓ Symlinked wrapper for common browsers in /usr/local/bin"
|
echo "✓ Symlinked wrapper for common browsers in /usr/local/bin"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to enable and start services
|
# Function to enable and start services
|
||||||
enable_services() {
|
enable_services() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "7. Enabling Services and Timer..."
|
echo "7. Enabling Services and Timer..."
|
||||||
echo "================================="
|
echo "================================="
|
||||||
|
|
||||||
# Reload systemd daemon
|
# Reload systemd daemon
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
echo "✓ Systemd daemon reloaded"
|
echo "✓ Systemd daemon reloaded"
|
||||||
|
|
||||||
# Enable and start the timer
|
# Enable and start the timer
|
||||||
systemctl enable periodic-system-maintenance.timer
|
systemctl enable periodic-system-maintenance.timer
|
||||||
systemctl start periodic-system-maintenance.timer
|
systemctl start periodic-system-maintenance.timer
|
||||||
echo "✓ Timer enabled and started"
|
echo "✓ Timer enabled and started"
|
||||||
|
|
||||||
# Enable startup service (but don't start it now)
|
# Enable startup service (but don't start it now)
|
||||||
systemctl enable periodic-system-startup.service
|
systemctl enable periodic-system-startup.service
|
||||||
echo "✓ Startup service enabled"
|
echo "✓ Startup service enabled"
|
||||||
|
|
||||||
# Enable hosts file monitor service
|
# Enable hosts file monitor service
|
||||||
systemctl enable hosts-file-monitor.service
|
systemctl enable hosts-file-monitor.service
|
||||||
systemctl start hosts-file-monitor.service
|
systemctl start hosts-file-monitor.service
|
||||||
echo "✓ Hosts file monitor service enabled and started"
|
echo "✓ Hosts file monitor service enabled and started"
|
||||||
|
|
||||||
# Show timer status
|
# Show timer status
|
||||||
echo ""
|
echo ""
|
||||||
echo "Timer Status:"
|
echo "Timer Status:"
|
||||||
systemctl status periodic-system-maintenance.timer --no-pager -l
|
systemctl status periodic-system-maintenance.timer --no-pager -l
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Hosts Monitor Status:"
|
echo "Hosts Monitor Status:"
|
||||||
systemctl status hosts-file-monitor.service --no-pager -l
|
systemctl status hosts-file-monitor.service --no-pager -l
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Next scheduled runs:"
|
echo "Next scheduled runs:"
|
||||||
systemctl list-timers periodic-system-maintenance.timer --no-pager
|
systemctl list-timers periodic-system-maintenance.timer --no-pager
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create log rotation configuration
|
# Function to create log rotation configuration
|
||||||
create_log_rotation() {
|
create_log_rotation() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "8. Setting up Log Rotation..."
|
echo "8. Setting up Log Rotation..."
|
||||||
echo "============================="
|
echo "============================="
|
||||||
|
|
||||||
local logrotate_conf="/etc/logrotate.d/periodic-system-maintenance"
|
local logrotate_conf="/etc/logrotate.d/periodic-system-maintenance"
|
||||||
install -m 0644 "$TEMPLATE_LOGROTATE" "$logrotate_conf"
|
install -m 0644 "$TEMPLATE_LOGROTATE" "$logrotate_conf"
|
||||||
echo "✓ Installed log rotation configuration from template: $logrotate_conf"
|
echo "✓ Installed log rotation configuration from template: $logrotate_conf"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to run initial execution
|
# Function to run initial execution
|
||||||
run_initial_execution() {
|
run_initial_execution() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "9. Running Initial Execution..."
|
echo "9. Running Initial Execution..."
|
||||||
echo "==============================="
|
echo "==============================="
|
||||||
|
|
||||||
local run_initial=true
|
local run_initial=true
|
||||||
|
|
||||||
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
|
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||||
echo "Would you like to run the system maintenance now to test the setup?"
|
echo "Would you like to run the system maintenance now to test the setup?"
|
||||||
read -p "Run initial execution? (y/N): " -n 1 -r
|
read -p "Run initial execution? (y/N): " -n 1 -r
|
||||||
echo
|
echo
|
||||||
|
|
||||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
run_initial=false
|
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
|
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
|
# Main execution
|
||||||
|
|||||||
@ -2,40 +2,40 @@
|
|||||||
# Script to set up automatic Thorium browser launch with Fitatu website on startup
|
# 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
|
# 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
|
# Default to non-interactive mode
|
||||||
INTERACTIVE_MODE=false
|
INTERACTIVE_MODE=false
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
-i|--interactive)
|
-i | --interactive)
|
||||||
INTERACTIVE_MODE=true
|
INTERACTIVE_MODE=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h | --help)
|
||||||
echo "Usage: $0 [OPTIONS]"
|
echo "Usage: $0 [OPTIONS]"
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
|
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
|
||||||
echo " -h, --help Show this help message"
|
echo " -h, --help Show this help message"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown option: $1"
|
echo "Unknown option: $1"
|
||||||
echo "Use -h or --help for usage information"
|
echo "Use -h or --help for usage information"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Function to check and request sudo privileges
|
# Function to check and request sudo privileges
|
||||||
check_sudo() {
|
check_sudo() {
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
echo "This script requires sudo privileges to create systemd services."
|
echo "This script requires sudo privileges to create systemd services."
|
||||||
echo "Requesting sudo access..."
|
echo "Requesting sudo access..."
|
||||||
exec sudo "$0" "$@"
|
exec sudo "$0" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check for sudo privileges after argument parsing
|
# Check for sudo privileges after argument parsing
|
||||||
@ -46,10 +46,10 @@ echo "=================================="
|
|||||||
echo "Current Date: $(date)"
|
echo "Current Date: $(date)"
|
||||||
echo "User: $USER"
|
echo "User: $USER"
|
||||||
echo "Original user: ${SUDO_USER:-$USER}"
|
echo "Original user: ${SUDO_USER:-$USER}"
|
||||||
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
|
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||||
echo "Mode: Interactive (prompts enabled)"
|
echo "Mode: Interactive (prompts enabled)"
|
||||||
else
|
else
|
||||||
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
|
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Target URL
|
# Target URL
|
||||||
@ -64,72 +64,72 @@ echo "User home: $USER_HOME"
|
|||||||
|
|
||||||
# Function to check if Thorium browser is installed
|
# Function to check if Thorium browser is installed
|
||||||
check_thorium_browser() {
|
check_thorium_browser() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "1. Checking Thorium Browser Installation..."
|
echo "1. Checking Thorium Browser Installation..."
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
|
|
||||||
if ! command -v "$BROWSER_COMMAND" &> /dev/null; then
|
if ! command -v "$BROWSER_COMMAND" &> /dev/null; then
|
||||||
echo "Warning: Thorium browser not found in PATH"
|
echo "Warning: Thorium browser not found in PATH"
|
||||||
echo "Checking alternative locations..."
|
echo "Checking alternative locations..."
|
||||||
|
|
||||||
# Check common installation paths
|
# Check common installation paths
|
||||||
local alt_paths=(
|
local alt_paths=(
|
||||||
"/opt/thorium/thorium"
|
"/opt/thorium/thorium"
|
||||||
"/usr/bin/thorium"
|
"/usr/bin/thorium"
|
||||||
"/usr/local/bin/thorium"
|
"/usr/local/bin/thorium"
|
||||||
"/opt/thorium-browser/thorium-browser"
|
"/opt/thorium-browser/thorium-browser"
|
||||||
"${USER_HOME}/.local/bin/thorium-browser"
|
"${USER_HOME}/.local/bin/thorium-browser"
|
||||||
)
|
)
|
||||||
|
|
||||||
local found=false
|
local found=false
|
||||||
for path in "${alt_paths[@]}"; do
|
for path in "${alt_paths[@]}"; do
|
||||||
if [[ -x "$path" ]]; then
|
if [[ -x $path ]]; then
|
||||||
BROWSER_COMMAND="$path"
|
BROWSER_COMMAND="$path"
|
||||||
echo "✓ Found Thorium browser at: $path"
|
echo "✓ Found Thorium browser at: $path"
|
||||||
found=true
|
found=true
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ "$found" != true ]]; then
|
if [[ $found != true ]]; then
|
||||||
echo "Error: Thorium browser not found!"
|
echo "Error: Thorium browser not found!"
|
||||||
echo "Please install Thorium browser first or ensure it's in your PATH."
|
echo "Please install Thorium browser first or ensure it's in your PATH."
|
||||||
echo ""
|
echo ""
|
||||||
echo "You can install Thorium browser from:"
|
echo "You can install Thorium browser from:"
|
||||||
echo "https://thorium.rocks/"
|
echo "https://thorium.rocks/"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
local continue_anyway=false
|
local continue_anyway=false
|
||||||
|
|
||||||
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
|
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||||
read -p "Continue anyway? The service will be created but may fail to start (y/N): " -n 1 -r
|
read -p "Continue anyway? The service will be created but may fail to start (y/N): " -n 1 -r
|
||||||
echo
|
echo
|
||||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
continue_anyway=true
|
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
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "✓ Thorium browser found: $(which $BROWSER_COMMAND)"
|
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
|
fi
|
||||||
|
else
|
||||||
|
echo "✓ Thorium browser found: $(which $BROWSER_COMMAND)"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create the browser launcher script
|
# Function to create the browser launcher script
|
||||||
create_launcher_script() {
|
create_launcher_script() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. Creating Browser Launcher Script..."
|
echo "2. Creating Browser Launcher Script..."
|
||||||
echo "====================================="
|
echo "====================================="
|
||||||
|
|
||||||
local launcher_script="/usr/local/bin/thorium-fitatu-launcher.sh"
|
local launcher_script="/usr/local/bin/thorium-fitatu-launcher.sh"
|
||||||
|
|
||||||
cat > "$launcher_script" << EOF
|
cat > "$launcher_script" << EOF
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Thorium browser launcher for Fitatu website
|
# Thorium browser launcher for Fitatu website
|
||||||
# Created by setup_thorium_startup.sh on $(date)
|
# Created by setup_thorium_startup.sh on $(date)
|
||||||
@ -203,24 +203,24 @@ else
|
|||||||
fi
|
fi
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
chmod +x "$launcher_script"
|
chmod +x "$launcher_script"
|
||||||
echo "✓ Created launcher script: $launcher_script"
|
echo "✓ Created launcher script: $launcher_script"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create systemd service for user session
|
# Function to create systemd service for user session
|
||||||
create_user_systemd_service() {
|
create_user_systemd_service() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. Creating User Systemd Service..."
|
echo "3. Creating User Systemd Service..."
|
||||||
echo "=================================="
|
echo "=================================="
|
||||||
|
|
||||||
local user_systemd_dir="$USER_HOME/.config/systemd/user"
|
local user_systemd_dir="$USER_HOME/.config/systemd/user"
|
||||||
local service_file="$user_systemd_dir/thorium-fitatu-startup.service"
|
local service_file="$user_systemd_dir/thorium-fitatu-startup.service"
|
||||||
|
|
||||||
# Create user systemd directory
|
# Create user systemd directory
|
||||||
sudo -u "${SUDO_USER}" mkdir -p "$user_systemd_dir"
|
sudo -u "${SUDO_USER}" mkdir -p "$user_systemd_dir"
|
||||||
|
|
||||||
# Create the service file
|
# Create the service file
|
||||||
sudo -u "${SUDO_USER}" tee "$service_file" > /dev/null << EOF
|
sudo -u "${SUDO_USER}" tee "$service_file" > /dev/null << EOF
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Launch Thorium Browser with Fitatu on Startup
|
Description=Launch Thorium Browser with Fitatu on Startup
|
||||||
After=graphical-session.target
|
After=graphical-session.target
|
||||||
@ -245,18 +245,18 @@ TimeoutStartSec=120
|
|||||||
WantedBy=default.target
|
WantedBy=default.target
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✓ Created user systemd service: $service_file"
|
echo "✓ Created user systemd service: $service_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create system-wide systemd service (alternative approach)
|
# Function to create system-wide systemd service (alternative approach)
|
||||||
create_system_systemd_service() {
|
create_system_systemd_service() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. Creating System Systemd Service..."
|
echo "4. Creating System Systemd Service..."
|
||||||
echo "===================================="
|
echo "===================================="
|
||||||
|
|
||||||
local service_file="/etc/systemd/system/thorium-fitatu-startup.service"
|
local service_file="/etc/systemd/system/thorium-fitatu-startup.service"
|
||||||
|
|
||||||
cat > "$service_file" << EOF
|
cat > "$service_file" << EOF
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Launch Thorium Browser with Fitatu on Startup
|
Description=Launch Thorium Browser with Fitatu on Startup
|
||||||
After=multi-user.target network-online.target
|
After=multi-user.target network-online.target
|
||||||
@ -283,23 +283,23 @@ TimeoutStartSec=180
|
|||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✓ Created system systemd service: $service_file"
|
echo "✓ Created system systemd service: $service_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create autostart desktop entry (additional method)
|
# Function to create autostart desktop entry (additional method)
|
||||||
create_autostart_entry() {
|
create_autostart_entry() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "5. Creating Autostart Desktop Entry..."
|
echo "5. Creating Autostart Desktop Entry..."
|
||||||
echo "====================================="
|
echo "====================================="
|
||||||
|
|
||||||
local autostart_dir="$USER_HOME/.config/autostart"
|
local autostart_dir="$USER_HOME/.config/autostart"
|
||||||
local desktop_file="$autostart_dir/thorium-fitatu.desktop"
|
local desktop_file="$autostart_dir/thorium-fitatu.desktop"
|
||||||
|
|
||||||
# Create autostart directory
|
# Create autostart directory
|
||||||
sudo -u "${SUDO_USER}" mkdir -p "$autostart_dir"
|
sudo -u "${SUDO_USER}" mkdir -p "$autostart_dir"
|
||||||
|
|
||||||
# Create desktop entry
|
# Create desktop entry
|
||||||
sudo -u "${SUDO_USER}" tee "$desktop_file" > /dev/null << EOF
|
sudo -u "${SUDO_USER}" tee "$desktop_file" > /dev/null << EOF
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Name=Thorium Fitatu Startup
|
Name=Thorium Fitatu Startup
|
||||||
@ -314,45 +314,45 @@ Terminal=false
|
|||||||
Categories=Network;WebBrowser;
|
Categories=Network;WebBrowser;
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✓ Created autostart desktop entry: $desktop_file"
|
echo "✓ Created autostart desktop entry: $desktop_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to create i3 config autostart entry
|
# Function to create i3 config autostart entry
|
||||||
create_i3_autostart() {
|
create_i3_autostart() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "6. Creating i3 Config Autostart Entry..."
|
echo "6. Creating i3 Config Autostart Entry..."
|
||||||
echo "======================================="
|
echo "======================================="
|
||||||
|
|
||||||
local i3_config="$USER_HOME/.config/i3/config"
|
local i3_config="$USER_HOME/.config/i3/config"
|
||||||
local i3_config_dir="$USER_HOME/.config/i3"
|
local i3_config_dir="$USER_HOME/.config/i3"
|
||||||
|
|
||||||
# Create i3 config directory if it doesn't exist
|
# Create i3 config directory if it doesn't exist
|
||||||
sudo -u "${SUDO_USER}" mkdir -p "$i3_config_dir"
|
sudo -u "${SUDO_USER}" mkdir -p "$i3_config_dir"
|
||||||
|
|
||||||
# Check if i3 config exists
|
# Check if i3 config exists
|
||||||
if [[ -f "$i3_config" ]]; then
|
if [[ -f $i3_config ]]; then
|
||||||
# Check if autostart entry already exists
|
# Check if autostart entry already exists
|
||||||
if ! sudo -u "${SUDO_USER}" grep -q "thorium-fitatu-launcher" "$i3_config"; then
|
if ! sudo -u "${SUDO_USER}" grep -q "thorium-fitatu-launcher" "$i3_config"; then
|
||||||
# Add autostart entry to i3 config
|
# Add autostart entry to i3 config
|
||||||
sudo -u "${SUDO_USER}" bash -c "echo '' >> '$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 '# 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'"
|
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"
|
echo "✓ Added autostart entry to i3 config: $i3_config"
|
||||||
else
|
|
||||||
echo "✓ Autostart entry already exists in i3 config"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo "Warning: i3 config file not found at $i3_config"
|
echo "✓ Autostart entry already exists in 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
|
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
|
# Function to create a script to enable user service after login
|
||||||
create_user_enable_script() {
|
create_user_enable_script() {
|
||||||
local enable_script="$USER_HOME/.config/thorium-enable-service.sh"
|
local enable_script="$USER_HOME/.config/thorium-enable-service.sh"
|
||||||
|
|
||||||
sudo -u "${SUDO_USER}" tee "$enable_script" > /dev/null << 'EOF'
|
sudo -u "${SUDO_USER}" tee "$enable_script" > /dev/null << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Script to enable thorium-fitatu-startup user service
|
# Script to enable thorium-fitatu-startup user service
|
||||||
# This runs once to enable the service, then removes itself
|
# This runs once to enable the service, then removes itself
|
||||||
@ -365,110 +365,110 @@ systemctl --user enable thorium-fitatu-startup.service
|
|||||||
rm "$0"
|
rm "$0"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
sudo -u "${SUDO_USER}" chmod +x "$enable_script"
|
sudo -u "${SUDO_USER}" chmod +x "$enable_script"
|
||||||
|
|
||||||
# Add to user's .bashrc to run on next login
|
# Add to user's .bashrc to run on next login
|
||||||
local bashrc="$USER_HOME/.bashrc"
|
local bashrc="$USER_HOME/.bashrc"
|
||||||
if [[ -f "$bashrc" ]]; then
|
if [[ -f $bashrc ]]; then
|
||||||
sudo -u "${SUDO_USER}" bash -c "echo '' >> '$bashrc'"
|
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 '# 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'"
|
sudo -u "${SUDO_USER}" bash -c "echo '[[ -x ~/.config/thorium-enable-service.sh ]] && ~/.config/thorium-enable-service.sh' >> '$bashrc'"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to enable services
|
# Function to enable services
|
||||||
enable_services() {
|
enable_services() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "7. Enabling Services..."
|
echo "7. Enabling Services..."
|
||||||
echo "======================"
|
echo "======================"
|
||||||
|
|
||||||
# Reload systemd daemon
|
# Reload systemd daemon
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
echo "✓ System daemon reloaded"
|
echo "✓ System daemon reloaded"
|
||||||
|
|
||||||
# Enable system service
|
# Enable system service
|
||||||
systemctl enable thorium-fitatu-startup.service
|
systemctl enable thorium-fitatu-startup.service
|
||||||
echo "✓ System service enabled"
|
echo "✓ System service enabled"
|
||||||
|
|
||||||
# Enable lingering for the user (allows user services to run without login)
|
# Enable lingering for the user (allows user services to run without login)
|
||||||
loginctl enable-linger "${SUDO_USER}"
|
loginctl enable-linger "${SUDO_USER}"
|
||||||
echo "✓ User lingering enabled"
|
echo "✓ User lingering enabled"
|
||||||
|
|
||||||
# Create a script to enable user service after login
|
# Create a script to enable user service after login
|
||||||
create_user_enable_script
|
create_user_enable_script
|
||||||
echo "✓ User service will be enabled on next login"
|
echo "✓ User service will be enabled on next login"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to test the setup
|
# Function to test the setup
|
||||||
test_setup() {
|
test_setup() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "8. Testing Setup..."
|
echo "8. Testing Setup..."
|
||||||
echo "=================="
|
echo "=================="
|
||||||
|
|
||||||
local run_test=true
|
local run_test=true
|
||||||
|
|
||||||
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
|
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||||
echo "Would you like to test the browser launcher now?"
|
echo "Would you like to test the browser launcher now?"
|
||||||
read -p "Test launch Thorium browser with Fitatu? (y/N): " -n 1 -r
|
read -p "Test launch Thorium browser with Fitatu? (y/N): " -n 1 -r
|
||||||
echo
|
echo
|
||||||
|
|
||||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
run_test=false
|
run_test=false
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Auto-testing the browser launcher (use --interactive to prompt)"
|
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
if [[ "$run_test" == "true" ]]; then
|
echo "Auto-testing the browser launcher (use --interactive to prompt)"
|
||||||
echo "Testing browser launch..."
|
fi
|
||||||
echo "Note: This will open Thorium browser with Fitatu website"
|
|
||||||
|
if [[ $run_test == "true" ]]; then
|
||||||
# Test the launcher immediately
|
echo "Testing browser launch..."
|
||||||
if /usr/local/bin/thorium-fitatu-launcher.sh; then
|
echo "Note: This will open Thorium browser with Fitatu website"
|
||||||
echo "✓ Test launch completed successfully"
|
|
||||||
else
|
# Test the launcher immediately
|
||||||
echo "✗ Test launch failed"
|
if /usr/local/bin/thorium-fitatu-launcher.sh; then
|
||||||
echo "Check that Thorium browser is properly installed and accessible"
|
echo "✓ Test launch completed successfully"
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo "Skipping test launch"
|
echo "✗ Test launch failed"
|
||||||
|
echo "Check that Thorium browser is properly installed and accessible"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
echo "Skipping test launch"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to show usage instructions
|
# Function to show usage instructions
|
||||||
show_instructions() {
|
show_instructions() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "Thorium Browser Auto-Startup Setup Complete"
|
echo "Thorium Browser Auto-Startup Setup Complete"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "Summary:"
|
echo "Summary:"
|
||||||
echo "✓ Launcher script created: /usr/local/bin/thorium-fitatu-launcher.sh"
|
echo "✓ Launcher script created: /usr/local/bin/thorium-fitatu-launcher.sh"
|
||||||
echo "✓ System service created: thorium-fitatu-startup.service"
|
echo "✓ System service created: thorium-fitatu-startup.service"
|
||||||
echo "✓ User service created: ~/.config/systemd/user/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 "✓ Autostart entry created: ~/.config/autostart/thorium-fitatu.desktop"
|
||||||
echo "✓ i3 autostart entry added to: ~/.config/i3/config"
|
echo "✓ i3 autostart entry added to: ~/.config/i3/config"
|
||||||
echo "✓ Services enabled for automatic startup"
|
echo "✓ Services enabled for automatic startup"
|
||||||
echo ""
|
echo ""
|
||||||
echo "The system will now:"
|
echo "The system will now:"
|
||||||
echo "• Launch Thorium browser with $TARGET_URL on every startup"
|
echo "• Launch Thorium browser with $TARGET_URL on every startup"
|
||||||
echo "• Use multiple methods to ensure reliable startup"
|
echo "• Use multiple methods to ensure reliable startup"
|
||||||
echo "• Wait for desktop environment to be ready before launching"
|
echo "• Wait for desktop environment to be ready before launching"
|
||||||
echo "• User service will be enabled automatically on next login"
|
echo "• User service will be enabled automatically on next login"
|
||||||
echo ""
|
echo ""
|
||||||
echo "To check status:"
|
echo "To check status:"
|
||||||
echo " systemctl status thorium-fitatu-startup.service"
|
echo " systemctl status thorium-fitatu-startup.service"
|
||||||
echo " systemctl --user status thorium-fitatu-startup.service (after login)"
|
echo " systemctl --user status thorium-fitatu-startup.service (after login)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "To view logs:"
|
echo "To view logs:"
|
||||||
echo " journalctl -u thorium-fitatu-startup.service"
|
echo " journalctl -u thorium-fitatu-startup.service"
|
||||||
echo " journalctl --user -u thorium-fitatu-startup.service"
|
echo " journalctl --user -u thorium-fitatu-startup.service"
|
||||||
echo ""
|
echo ""
|
||||||
echo "To disable (if needed):"
|
echo "To disable (if needed):"
|
||||||
echo " sudo systemctl disable thorium-fitatu-startup.service"
|
echo " sudo systemctl disable thorium-fitatu-startup.service"
|
||||||
echo " systemctl --user disable thorium-fitatu-startup.service"
|
echo " systemctl --user disable thorium-fitatu-startup.service"
|
||||||
echo " rm ~/.config/autostart/thorium-fitatu.desktop"
|
echo " rm ~/.config/autostart/thorium-fitatu.desktop"
|
||||||
echo ""
|
echo ""
|
||||||
echo "IMPORTANT: Browser will launch automatically on next reboot!"
|
echo "IMPORTANT: Browser will launch automatically on next reboot!"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main execution
|
# Main execution
|
||||||
|
|||||||
@ -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,
|
# 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:
|
# 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
|
if [[ $# -ge 1 ]]; then
|
||||||
real_bin="$1"
|
real_bin="$1"
|
||||||
shift
|
shift
|
||||||
@ -24,10 +24,10 @@ if [[ ! -x "$real_bin" || "$prog_name" == "browser-preexec-wrapper.sh" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Best-effort: install hosts file quietly; don't block browser startup
|
# Best-effort: install hosts file quietly; don't block browser startup
|
||||||
if command -v sudo >/dev/null 2>&1; then
|
if command -v sudo > /dev/null 2>&1; then
|
||||||
sudo -n "$HOSTS_INSTALL_SCRIPT" >/dev/null 2>&1 || true
|
sudo -n "$HOSTS_INSTALL_SCRIPT" > /dev/null 2>&1 || true
|
||||||
else
|
else
|
||||||
"$HOSTS_INSTALL_SCRIPT" >/dev/null 2>&1 || true
|
"$HOSTS_INSTALL_SCRIPT" > /dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec "$real_bin" "$@"
|
exec "$real_bin" "$@"
|
||||||
|
|||||||
@ -11,100 +11,100 @@ HOSTS_INSTALL_SCRIPT="__HOSTS_INSTALL_SCRIPT__"
|
|||||||
|
|
||||||
# Function to log with timestamp
|
# Function to log with timestamp
|
||||||
log_message() {
|
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
|
# Function to check if hosts file needs restoration
|
||||||
needs_restoration() {
|
needs_restoration() {
|
||||||
# Check if file exists
|
# Check if file exists
|
||||||
if [[ ! -f "$HOSTS_FILE" ]]; then
|
if [[ ! -f $HOSTS_FILE ]]; then
|
||||||
return 0 # File missing, needs restoration
|
return 0 # File missing, needs restoration
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if file is empty or too small (less than 1000 lines indicates tampering)
|
# Check if file is empty or too small (less than 1000 lines indicates tampering)
|
||||||
local line_count
|
local line_count
|
||||||
line_count=$(wc -l < "$HOSTS_FILE" 2>/dev/null || echo "0")
|
line_count=$(wc -l < "$HOSTS_FILE" 2> /dev/null || echo "0")
|
||||||
if [[ "$line_count" -lt 1000 ]]; then
|
if [[ $line_count -lt 1000 ]]; then
|
||||||
return 0 # File too small, likely tampered with
|
return 0 # File too small, likely tampered with
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if our custom entries are missing
|
# Check if our custom entries are missing
|
||||||
if ! grep -q "Custom blocking entries" "$HOSTS_FILE" 2>/dev/null; then
|
if ! grep -q "Custom blocking entries" "$HOSTS_FILE" 2> /dev/null; then
|
||||||
return 0 # Our custom entries missing, needs restoration
|
return 0 # Our custom entries missing, needs restoration
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if StevenBlack entries are missing
|
# Check if StevenBlack entries are missing
|
||||||
if ! grep -q "StevenBlack" "$HOSTS_FILE" 2>/dev/null; then
|
if ! grep -q "StevenBlack" "$HOSTS_FILE" 2> /dev/null; then
|
||||||
return 0 # StevenBlack entries missing, needs restoration
|
return 0 # StevenBlack entries missing, needs restoration
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 1 # File seems intact
|
return 1 # File seems intact
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to restore hosts file
|
# Function to restore hosts file
|
||||||
restore_hosts_file() {
|
restore_hosts_file() {
|
||||||
log_message "Hosts file modification detected - initiating restoration"
|
log_message "Hosts file modification detected - initiating restoration"
|
||||||
|
|
||||||
if [[ -f "$HOSTS_INSTALL_SCRIPT" ]]; then
|
if [[ -f $HOSTS_INSTALL_SCRIPT ]]; then
|
||||||
log_message "Running hosts installation script: $HOSTS_INSTALL_SCRIPT"
|
log_message "Running hosts installation script: $HOSTS_INSTALL_SCRIPT"
|
||||||
|
|
||||||
if bash "$HOSTS_INSTALL_SCRIPT" >> "$LOG_FILE" 2>&1; then
|
if bash "$HOSTS_INSTALL_SCRIPT" >> "$LOG_FILE" 2>&1; then
|
||||||
log_message "Hosts file restoration completed successfully"
|
log_message "Hosts file restoration completed successfully"
|
||||||
else
|
|
||||||
log_message "Hosts file restoration failed with exit code $?"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
log_message "ERROR: Hosts install script not found at $HOSTS_INSTALL_SCRIPT"
|
log_message "Hosts file restoration failed with exit code $?"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
log_message "ERROR: Hosts install script not found at $HOSTS_INSTALL_SCRIPT"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to monitor with inotifywait
|
# Function to monitor with inotifywait
|
||||||
monitor_with_inotify() {
|
monitor_with_inotify() {
|
||||||
log_message "Starting hosts file monitoring with inotify"
|
log_message "Starting hosts file monitoring with inotify"
|
||||||
|
|
||||||
# Monitor the hosts file and its directory for various events
|
# 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 | \
|
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
|
while read -r file event time; do
|
||||||
# Check if the event is related to our hosts file
|
# Check if the event is related to our hosts file
|
||||||
if [[ "$file" == "$HOSTS_FILE" ]] || [[ "$file" == "/etc/hosts" ]]; then
|
if [[ $file == "$HOSTS_FILE" ]] || [[ $file == "/etc/hosts" ]]; then
|
||||||
log_message "Event detected: $event on $file at $time"
|
log_message "Event detected: $event on $file at $time"
|
||||||
|
|
||||||
# Small delay to avoid rapid-fire events
|
# Small delay to avoid rapid-fire events
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
# Check if restoration is needed
|
# Check if restoration is needed
|
||||||
if needs_restoration; then
|
if needs_restoration; then
|
||||||
restore_hosts_file
|
restore_hosts_file
|
||||||
else
|
else
|
||||||
log_message "Hosts file check passed - no restoration needed"
|
log_message "Hosts file check passed - no restoration needed"
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to monitor with polling (fallback)
|
# Function to monitor with polling (fallback)
|
||||||
monitor_with_polling() {
|
monitor_with_polling() {
|
||||||
log_message "Starting hosts file monitoring with polling (fallback method)"
|
log_message "Starting hosts file monitoring with polling (fallback method)"
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
if needs_restoration; then
|
if needs_restoration; then
|
||||||
restore_hosts_file
|
restore_hosts_file
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check every 30 seconds
|
# Check every 30 seconds
|
||||||
sleep 30
|
sleep 30
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main execution
|
# Main execution
|
||||||
log_message "=== Hosts File Monitor Started ==="
|
log_message "=== Hosts File Monitor Started ==="
|
||||||
|
|
||||||
# Check if inotify-tools is available
|
# Check if inotify-tools is available
|
||||||
if command -v inotifywait >/dev/null 2>&1; then
|
if command -v inotifywait > /dev/null 2>&1; then
|
||||||
log_message "Using inotify for file monitoring"
|
log_message "Using inotify for file monitoring"
|
||||||
monitor_with_inotify
|
monitor_with_inotify
|
||||||
else
|
else
|
||||||
log_message "inotify-tools not available, using polling method"
|
log_message "inotify-tools not available, using polling method"
|
||||||
log_message "Consider installing inotify-tools for better performance: pacman -S inotify-tools"
|
log_message "Consider installing inotify-tools for better performance: pacman -S inotify-tools"
|
||||||
monitor_with_polling
|
monitor_with_polling
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -13,30 +13,30 @@ HOSTS_INSTALL_SCRIPT="__HOSTS_INSTALL_SCRIPT__"
|
|||||||
|
|
||||||
# Function to log with timestamp
|
# Function to log with timestamp
|
||||||
log_message() {
|
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
|
# Function to execute with logging
|
||||||
execute_with_log() {
|
execute_with_log() {
|
||||||
local script_path="$1"
|
local script_path="$1"
|
||||||
local script_name="$2"
|
local script_name="$2"
|
||||||
|
|
||||||
log_message "Starting $script_name"
|
log_message "Starting $script_name"
|
||||||
echo "Executing $script_name..." >&2
|
echo "Executing $script_name..." >&2
|
||||||
|
|
||||||
if [[ -f "$script_path" ]]; then
|
if [[ -f $script_path ]]; then
|
||||||
if bash "$script_path" >> "$LOG_FILE" 2>&1; then
|
if bash "$script_path" >> "$LOG_FILE" 2>&1; then
|
||||||
log_message "$script_name completed successfully"
|
log_message "$script_name completed successfully"
|
||||||
echo "✓ $script_name completed successfully" >&2
|
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
|
|
||||||
else
|
else
|
||||||
log_message "$script_name not found at $script_path"
|
local ec=$?
|
||||||
echo "✗ $script_name not found at $script_path" >&2
|
log_message "$script_name failed with exit code $ec"
|
||||||
|
echo "✗ $script_name failed (exit $ec)" >&2
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
log_message "$script_name not found at $script_path"
|
||||||
|
echo "✗ $script_name not found at $script_path" >&2
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Start maintenance
|
# Start maintenance
|
||||||
|
|||||||
@ -5,38 +5,37 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
DOWNLOADS_DIR="$HOME/Downloads"
|
DOWNLOADS_DIR="$HOME/Downloads"
|
||||||
HOME_DIR="$HOME"
|
|
||||||
|
|
||||||
# Test function
|
# Test function
|
||||||
test_file_removal() {
|
test_file_removal() {
|
||||||
local files=()
|
local files=()
|
||||||
|
|
||||||
# Find a few test files
|
# Find a few test files
|
||||||
while IFS= read -r -d '' file; do
|
while IFS= read -r -d '' file; do
|
||||||
files+=("$file")
|
files+=("$file")
|
||||||
done < <(find "$DOWNLOADS_DIR" -name "*.jpg" -print0 2>/dev/null | head -z -n 2)
|
done < <(find "$DOWNLOADS_DIR" -name "*.jpg" -print0 2> /dev/null | head -z -n 2)
|
||||||
|
|
||||||
echo "Found ${#files[@]} test files:"
|
echo "Found ${#files[@]} test files:"
|
||||||
for file in "${files[@]}"; do
|
for file in "${files[@]}"; do
|
||||||
echo " - $file"
|
echo " - $file"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Attempting to remove files..."
|
echo "Attempting to remove files..."
|
||||||
local removed=0
|
local removed=0
|
||||||
local failed=0
|
local failed=0
|
||||||
|
|
||||||
for file in "${files[@]}"; do
|
for file in "${files[@]}"; do
|
||||||
echo "Removing: $file"
|
echo "Removing: $file"
|
||||||
if rm "$file" 2>/dev/null; then
|
if rm "$file" 2> /dev/null; then
|
||||||
echo " SUCCESS"
|
echo " SUCCESS"
|
||||||
((removed++))
|
((removed++))
|
||||||
else
|
else
|
||||||
echo " FAILED (exit code: $?)"
|
echo " FAILED (exit code: $?)"
|
||||||
((failed++))
|
((failed++))
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Results: $removed removed, $failed failed"
|
echo "Results: $removed removed, $failed failed"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_file_removal
|
test_file_removal
|
||||||
|
|||||||
@ -2,25 +2,25 @@
|
|||||||
|
|
||||||
# Check if input and output files are provided
|
# Check if input and output files are provided
|
||||||
if [ $# -ne 2 ]; then
|
if [ $# -ne 2 ]; then
|
||||||
echo "Usage: $0 input_file.txt output_file.txt"
|
echo "Usage: $0 input_file.txt output_file.txt"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if the input file exists
|
# Check if the input file exists
|
||||||
if [ ! -f "$1" ]; then
|
if [ ! -f "$1" ]; then
|
||||||
echo "Error: File '$1' not found"
|
echo "Error: File '$1' not found"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Store output file name
|
# Store output file name
|
||||||
output_file="$2"
|
output_file="$2"
|
||||||
|
|
||||||
# Clear output file at the beginning
|
# Clear output file at the beginning
|
||||||
> "$output_file"
|
: > "$output_file"
|
||||||
|
|
||||||
# Process file using a pipeline of specialized tools
|
# Process file using a pipeline of specialized tools
|
||||||
# 1. tr - remove non-alphabetic chars except newlines
|
# 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)
|
# 3. grep - filter by length (5-8 characters)
|
||||||
# 4. sort - sort the words alphabetically
|
# 4. sort - sort the words alphabetically
|
||||||
# 5. uniq - remove duplicates
|
# 5. uniq - remove duplicates
|
||||||
|
|||||||
@ -13,7 +13,7 @@ SAMPLE_LIMIT=20
|
|||||||
|
|
||||||
# Simple usage helper
|
# Simple usage helper
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat << EOF
|
||||||
Usage: $(basename "$0") [--dry-run|-n] [--sample=N]
|
Usage: $(basename "$0") [--dry-run|-n] [--sample=N]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
@ -36,369 +36,373 @@ VIDEO_EXTENSIONS=("mp4" "avi" "mkv" "mov" "wmv" "flv" "webm" "m4v" "3gp" "ogv" "
|
|||||||
|
|
||||||
# Function to log messages with timestamp
|
# Function to log messages with timestamp
|
||||||
log() {
|
log() {
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Parse CLI flags early
|
# Parse CLI flags early
|
||||||
while [[ ${1:-} =~ ^- ]]; do
|
while [[ ${1:-} =~ ^- ]]; do
|
||||||
case "${1}" in
|
case "${1}" in
|
||||||
-n|--dry-run)
|
-n | --dry-run)
|
||||||
DRY_RUN=true
|
DRY_RUN=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--sample=*)
|
--sample=*)
|
||||||
SAMPLE_LIMIT="${1#*=}"
|
SAMPLE_LIMIT="${1#*=}"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h | --help)
|
||||||
usage
|
usage
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown option: $1" >&2
|
echo "Unknown option: $1" >&2
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Function to check if file has media extension
|
# Function to check if file has media extension
|
||||||
is_media_file() {
|
is_media_file() {
|
||||||
local file="$1"
|
local file="$1"
|
||||||
local extension="${file##*.}"
|
local extension="${file##*.}"
|
||||||
extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]')
|
extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
# Check if it's an image
|
# Check if it's an image
|
||||||
for ext in "${IMAGE_EXTENSIONS[@]}"; do
|
for ext in "${IMAGE_EXTENSIONS[@]}"; do
|
||||||
if [[ "$extension" == "$ext" ]]; then
|
if [[ $extension == "$ext" ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Check if it's a video
|
# Check if it's a video
|
||||||
for ext in "${VIDEO_EXTENSIONS[@]}"; do
|
for ext in "${VIDEO_EXTENSIONS[@]}"; do
|
||||||
if [[ "$extension" == "$ext" ]]; then
|
if [[ $extension == "$ext" ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to find media files in a directory (non-recursive for home, avoid common system dirs)
|
# Function to find media files in a directory (non-recursive for home, avoid common system dirs)
|
||||||
find_media_files() {
|
find_media_files() {
|
||||||
local search_dir="$1"
|
local search_dir="$1"
|
||||||
local files=()
|
local files=()
|
||||||
# Directories to exclude under Downloads
|
# Directories to exclude under Downloads
|
||||||
local -a EXCLUDES=(
|
local -a EXCLUDES=(
|
||||||
".git" ".hg" ".svn" ".cache" "node_modules" "dist" "build" "out" "target" "coverage" "__pycache__" "venv" ".venv"
|
".git" ".hg" ".svn" ".cache" "node_modules" "dist" "build" "out" "target" "coverage" "__pycache__" "venv" ".venv"
|
||||||
# previous staging dirs created by this script
|
# previous staging dirs created by this script
|
||||||
".media_organize_" "media_organize_"
|
".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]'
|
|
||||||
|
|
||||||
while IFS= read -r -d '' file; do
|
if [[ $search_dir == "$HOME_DIR" ]]; then
|
||||||
if is_media_file "$file"; then
|
# For home directory, only check files directly in ~ (not subdirectories)
|
||||||
files+=("$file")
|
# Exclude common system/config directories
|
||||||
fi
|
while IFS= read -r -d '' file; do
|
||||||
done < <(find "$search_dir" \( -type d \( ${prune_expr[@]} \) -prune \) -o -type f -print0 2>/dev/null)
|
local basename
|
||||||
fi
|
basename=$(basename "$file")
|
||||||
|
# Skip hidden files and common system directories
|
||||||
printf '%s\n' "${files[@]}"
|
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
|
# Function to create timestamped zip archive
|
||||||
create_media_archive() {
|
create_media_archive() {
|
||||||
local files=("$@")
|
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
|
|
||||||
|
|
||||||
# Ensure temp dir is cleaned up on function return; trap unsets itself after running
|
if [[ ${#files[@]} -eq 0 ]]; then
|
||||||
trap 'rm -rf "$TEMP_DIR" 2>/dev/null || true; trap - RETURN' RETURN
|
log "No media files found to archive."
|
||||||
|
return 0
|
||||||
log "Found ${#files[@]} media files to archive."
|
fi
|
||||||
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
|
|
||||||
|
|
||||||
# Attempt copy and capture any error for logging
|
# Create timestamp for archive name
|
||||||
local cp_err
|
local timestamp
|
||||||
if cp_err=$(cp -p "$file" "$temp_file" 2>&1); then
|
timestamp=$(date '+%Y%m%d_%H%M%S')
|
||||||
successfully_copied+=("$file")
|
local archive_name="media_archive_${timestamp}.zip"
|
||||||
else
|
local archive_path="$DOWNLOADS_DIR/$archive_name"
|
||||||
# Surface the cp error so the user can see the reason
|
|
||||||
log "WARNING: Failed to copy $file -> $cp_err"
|
# Create temporary directory (fallback to /tmp if needed)
|
||||||
# Special hint for space issues
|
if ! mkdir -p "$TEMP_DIR" 2> /dev/null; then
|
||||||
if echo "$cp_err" | grep -qi "No space left on device"; then
|
TEMP_DIR="/tmp/media_organize_$$"
|
||||||
log "HINT: Not enough free space to stage files. Using $TEMP_DIR. Free up space or change TEMP_DIR."
|
mkdir -p "$TEMP_DIR"
|
||||||
fi
|
fi
|
||||||
((copy_errors++))
|
|
||||||
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
|
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
|
done
|
||||||
|
|
||||||
if [[ ${#successfully_copied[@]} -eq 0 ]]; then
|
# Re-enable strict error handling
|
||||||
log "ERROR: No files were successfully copied to temp directory."
|
set -e
|
||||||
rm -rf "$TEMP_DIR"
|
|
||||||
return 1
|
log "Successfully removed $removed_count original files."
|
||||||
|
if [[ $remove_errors -gt 0 ]]; then
|
||||||
|
log "WARNING: Failed to remove $remove_errors files."
|
||||||
fi
|
fi
|
||||||
|
log "Archive size: $(du -h "$archive_path" | cut -f1)"
|
||||||
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)"
|
|
||||||
|
|
||||||
# Cleanup temp directory (trap will also attempt, which is safe)
|
# Cleanup temp directory (trap will also attempt, which is safe)
|
||||||
rm -rf "$TEMP_DIR"
|
rm -rf "$TEMP_DIR"
|
||||||
|
|
||||||
# Return success only if we removed files or there were no files to remove
|
# Return success only if we removed files or there were no files to remove
|
||||||
if [[ $removed_count -gt 0 ]] || [[ ${#successfully_copied[@]} -eq 0 ]]; then
|
if [[ $removed_count -gt 0 ]] || [[ ${#successfully_copied[@]} -eq 0 ]]; then
|
||||||
return 0
|
return 0
|
||||||
else
|
|
||||||
log "ERROR: Failed to remove any files after successful archive creation."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
log "ERROR: Failed to create archive. Original files preserved."
|
log "ERROR: Failed to remove any files after successful archive creation."
|
||||||
log "Zip command failed."
|
return 1
|
||||||
rm -rf "$TEMP_DIR"
|
|
||||||
return 1
|
|
||||||
fi
|
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 execution
|
||||||
main() {
|
main() {
|
||||||
log "Starting media file organization..."
|
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
|
|
||||||
|
|
||||||
# Count by extension
|
# Check if required directories exist
|
||||||
declare -A ext_counts=()
|
if [[ ! -d $DOWNLOADS_DIR ]]; then
|
||||||
# Count by top-level directory under Downloads
|
log "ERROR: Downloads directory not found: $DOWNLOADS_DIR"
|
||||||
declare -A dir_counts=()
|
exit 1
|
||||||
# Sample paths for suspect extensions
|
fi
|
||||||
declare -A samples_ts=()
|
|
||||||
declare -A samples_svg=()
|
|
||||||
declare -A samples_ico=()
|
|
||||||
|
|
||||||
for f in "${all_files[@]}"; do
|
if [[ ! -d $HOME_DIR ]]; then
|
||||||
# Extension
|
log "ERROR: Home directory not found: $HOME_DIR"
|
||||||
ext="${f##*.}"
|
exit 1
|
||||||
ext="${ext,,}"
|
fi
|
||||||
((ext_counts["$ext"]++)) || true
|
|
||||||
|
|
||||||
# Top directory under Downloads
|
# Check if zip command is available
|
||||||
if [[ "$f" == "$DOWNLOADS_DIR"/* ]]; then
|
if ! command -v zip > /dev/null 2>&1; then
|
||||||
rel="${f#"$DOWNLOADS_DIR"/}"
|
log "ERROR: zip command not found. Please install zip package."
|
||||||
topdir="${rel%%/*}"
|
exit 1
|
||||||
[[ "$topdir" == "$rel" ]] && topdir="."
|
fi
|
||||||
((dir_counts["$topdir"]++)) || true
|
|
||||||
else
|
|
||||||
((dir_counts["~"]++)) || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Samples for suspect extensions
|
# Find all media files
|
||||||
case "$ext" in
|
log "Scanning for media files..."
|
||||||
ts)
|
local all_files=()
|
||||||
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 ""
|
# Find files in Downloads directory
|
||||||
echo "Summary by extension (top 20):"
|
log "Scanning Downloads directory..."
|
||||||
for k in "${!ext_counts[@]}"; do
|
while IFS= read -r file; do
|
||||||
printf "%8d %s\n" "${ext_counts[$k]}" "$k"
|
[[ -n $file ]] && all_files+=("$file")
|
||||||
done | sort -nr | head -n 20
|
done < <(find_media_files "$DOWNLOADS_DIR")
|
||||||
|
|
||||||
echo ""
|
# Find files in home directory (only direct files, not subdirectories)
|
||||||
echo "Top contributing directories under Downloads (top 20):"
|
log "Scanning home directory (root level only)..."
|
||||||
for k in "${!dir_counts[@]}"; do
|
while IFS= read -r file; do
|
||||||
printf "%8d %s\n" "${dir_counts[$k]}" "$k"
|
[[ -n $file ]] && all_files+=("$file")
|
||||||
done | sort -nr | head -n 20
|
done < <(find_media_files "$HOME_DIR")
|
||||||
|
|
||||||
echo ""
|
if $DRY_RUN; then
|
||||||
if [[ ${#samples_ts[@]} -gt 0 ]]; then
|
log "Dry-run mode: summarizing what would be archived."
|
||||||
echo "Sample .ts files (TypeScript vs Transport Stream) up to $SAMPLE_LIMIT:"
|
if [[ ${#all_files[@]} -eq 0 ]]; then
|
||||||
for p in "${!samples_ts[@]}"; do echo " $p"; done | sort
|
log "No media files found to organize."
|
||||||
echo ""
|
exit 0
|
||||||
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
|
fi
|
||||||
|
|
||||||
# Create archive if files found
|
# Count by extension
|
||||||
if [[ ${#all_files[@]} -gt 0 ]]; then
|
declare -A ext_counts=()
|
||||||
create_media_archive "${all_files[@]}"
|
# Count by top-level directory under Downloads
|
||||||
log "Media organization completed successfully."
|
declare -A dir_counts=()
|
||||||
else
|
# Sample paths for suspect extensions
|
||||||
log "No media files found to organize."
|
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
|
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
|
# Run main function
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
@ -13,20 +13,20 @@ USER_NAME="$(whoami)"
|
|||||||
|
|
||||||
# Function to log messages
|
# Function to log messages
|
||||||
log() {
|
log() {
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if organize script exists
|
# Check if organize script exists
|
||||||
if [[ ! -f "$ORGANIZE_SCRIPT" ]]; then
|
if [[ ! -f $ORGANIZE_SCRIPT ]]; then
|
||||||
log "ERROR: organize_downloads.sh not found at $ORGANIZE_SCRIPT"
|
log "ERROR: organize_downloads.sh not found at $ORGANIZE_SCRIPT"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if running as root for systemd service creation
|
# Check if running as root for systemd service creation
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
log "This script needs to be run as root to create systemd service."
|
log "This script needs to be run as root to create systemd service."
|
||||||
log "Please run: sudo $0"
|
log "Please run: sudo $0"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Setting up media organizer startup service..."
|
log "Setting up media organizer startup service..."
|
||||||
|
|||||||
@ -6,34 +6,34 @@
|
|||||||
# --reboot: Offer to reboot after setup completion
|
# --reboot: Offer to reboot after setup completion
|
||||||
# --logout: Allow restart of LightDM (which will logout the user)
|
# --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
|
# Check for flags
|
||||||
OFFER_REBOOT=false
|
OFFER_REBOOT=false
|
||||||
ALLOW_LOGOUT=false
|
ALLOW_LOGOUT=false
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case $arg in
|
case $arg in
|
||||||
--reboot)
|
--reboot)
|
||||||
OFFER_REBOOT=true
|
OFFER_REBOOT=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--logout)
|
--logout)
|
||||||
ALLOW_LOGOUT=true
|
ALLOW_LOGOUT=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
# Unknown option, keep it for sudo check
|
# Unknown option, keep it for sudo check
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Function to check and request sudo privileges
|
# Function to check and request sudo privileges
|
||||||
check_sudo() {
|
check_sudo() {
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
echo "This script requires sudo privileges to modify system configurations."
|
echo "This script requires sudo privileges to modify system configurations."
|
||||||
echo "Requesting sudo access..."
|
echo "Requesting sudo access..."
|
||||||
exec sudo "$0" "$@"
|
exec sudo "$0" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check for sudo privileges first
|
# Check for sudo privileges first
|
||||||
@ -46,9 +46,9 @@ echo "User: $USER"
|
|||||||
echo "Original user: ${SUDO_USER:-$USER}"
|
echo "Original user: ${SUDO_USER:-$USER}"
|
||||||
|
|
||||||
# Verify we have a valid user
|
# Verify we have a valid user
|
||||||
if [[ -z "${SUDO_USER}" ]]; then
|
if [[ -z ${SUDO_USER} ]]; then
|
||||||
echo "Error: Could not determine the original user. Please run this script with sudo."
|
echo "Error: Could not determine the original user. Please run this script with sudo."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TARGET_USER="${SUDO_USER}"
|
TARGET_USER="${SUDO_USER}"
|
||||||
@ -56,24 +56,26 @@ echo "Target user for configuration: $TARGET_USER"
|
|||||||
|
|
||||||
# Function to backup files
|
# Function to backup files
|
||||||
backup_file() {
|
backup_file() {
|
||||||
local file="$1"
|
local file="$1"
|
||||||
if [[ -f "$file" ]]; then
|
if [[ -f $file ]]; then
|
||||||
local backup="${file}.backup.$(date +%Y%m%d_%H%M%S)"
|
local backup timestamp
|
||||||
cp "$file" "$backup"
|
timestamp=$(date +%Y%m%d_%H%M%S)
|
||||||
echo "✓ Backed up $file to $backup"
|
backup="${file}.backup.$timestamp"
|
||||||
fi
|
cp "$file" "$backup"
|
||||||
|
echo "✓ Backed up $file to $backup"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to configure passwordless sudo
|
# Function to configure passwordless sudo
|
||||||
configure_passwordless_sudo() {
|
configure_passwordless_sudo() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "1. Configuring Passwordless Sudo..."
|
echo "1. Configuring Passwordless Sudo..."
|
||||||
echo "=================================="
|
echo "=================================="
|
||||||
|
|
||||||
local sudoers_file="/etc/sudoers.d/99-passwordless-${TARGET_USER}"
|
local sudoers_file="/etc/sudoers.d/99-passwordless-${TARGET_USER}"
|
||||||
|
|
||||||
# Create sudoers configuration for passwordless access
|
# Create sudoers configuration for passwordless access
|
||||||
cat > "$sudoers_file" << EOF
|
cat > "$sudoers_file" << EOF
|
||||||
# Passwordless sudo configuration for user: ${TARGET_USER}
|
# Passwordless sudo configuration for user: ${TARGET_USER}
|
||||||
# Created by setup_passwordless_system.sh on $(date)
|
# Created by setup_passwordless_system.sh on $(date)
|
||||||
# WARNING: This allows the user to run any command without password
|
# 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"
|
Defaults:${TARGET_USER} env_keep += "HOME PATH DISPLAY XAUTHORITY"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Set proper permissions for sudoers file
|
# Set proper permissions for sudoers file
|
||||||
chmod 440 "$sudoers_file"
|
chmod 440 "$sudoers_file"
|
||||||
|
|
||||||
# Verify the sudoers file syntax
|
# Verify the sudoers file syntax
|
||||||
if visudo -c -f "$sudoers_file"; then
|
if visudo -c -f "$sudoers_file"; then
|
||||||
echo "✓ Passwordless sudo configured for user: $TARGET_USER"
|
echo "✓ Passwordless sudo configured for user: $TARGET_USER"
|
||||||
echo "✓ Sudoers file created: $sudoers_file"
|
echo "✓ Sudoers file created: $sudoers_file"
|
||||||
else
|
else
|
||||||
echo "✗ Error: Invalid sudoers syntax. Removing file for safety."
|
echo "✗ Error: Invalid sudoers syntax. Removing file for safety."
|
||||||
rm -f "$sudoers_file"
|
rm -f "$sudoers_file"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to configure lightdm auto-login
|
# Function to configure lightdm auto-login
|
||||||
configure_lightdm_autologin() {
|
configure_lightdm_autologin() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. Configuring LightDM Auto-Login..."
|
echo "2. Configuring LightDM Auto-Login..."
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
|
|
||||||
local lightdm_conf="/etc/lightdm/lightdm.conf"
|
local lightdm_conf="/etc/lightdm/lightdm.conf"
|
||||||
local lightdm_conf_dir="/etc/lightdm/lightdm.conf.d"
|
local lightdm_conf_dir="/etc/lightdm/lightdm.conf.d"
|
||||||
local custom_conf="$lightdm_conf_dir/50-autologin.conf"
|
local custom_conf="$lightdm_conf_dir/50-autologin.conf"
|
||||||
|
|
||||||
# Create lightdm config directory if it doesn't exist
|
# Create lightdm config directory if it doesn't exist
|
||||||
mkdir -p "$lightdm_conf_dir"
|
mkdir -p "$lightdm_conf_dir"
|
||||||
|
|
||||||
# Backup existing lightdm configuration
|
# Backup existing lightdm configuration
|
||||||
backup_file "$lightdm_conf"
|
backup_file "$lightdm_conf"
|
||||||
|
|
||||||
# Check if lightdm is installed
|
# Check if lightdm is installed
|
||||||
if ! command -v lightdm &> /dev/null; then
|
if ! command -v lightdm &> /dev/null; then
|
||||||
echo "Warning: LightDM not found. Installing lightdm..."
|
echo "Warning: LightDM not found. Installing lightdm..."
|
||||||
pacman -S --noconfirm lightdm lightdm-gtk-greeter
|
pacman -S --noconfirm lightdm lightdm-gtk-greeter
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Method 1: Update the main lightdm.conf file directly
|
# Method 1: Update the main lightdm.conf file directly
|
||||||
sed -i "/^#autologin-user=/c\autologin-user=${TARGET_USER}" "$lightdm_conf"
|
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-user-timeout=/c\autologin-user-timeout=0" "$lightdm_conf"
|
||||||
sed -i "/^#autologin-session=/c\autologin-session=i3" "$lightdm_conf"
|
sed -i "/^#autologin-session=/c\autologin-session=i3" "$lightdm_conf"
|
||||||
sed -i "/^#autologin-in-background=/c\autologin-in-background=false" "$lightdm_conf"
|
sed -i "/^#autologin-in-background=/c\autologin-in-background=false" "$lightdm_conf"
|
||||||
|
|
||||||
# Also set user-session to i3 as fallback
|
# Also set user-session to i3 as fallback
|
||||||
sed -i "/^#user-session=/c\user-session=i3" "$lightdm_conf"
|
sed -i "/^#user-session=/c\user-session=i3" "$lightdm_conf"
|
||||||
|
|
||||||
echo "✓ LightDM auto-login configured in main config file"
|
echo "✓ LightDM auto-login configured in main config file"
|
||||||
|
|
||||||
# Method 2: Also create the separate config file for redundancy
|
# Method 2: Also create the separate config file for redundancy
|
||||||
cat > "$custom_conf" << EOF
|
cat > "$custom_conf" << EOF
|
||||||
# LightDM Auto-Login Configuration
|
# LightDM Auto-Login Configuration
|
||||||
# Created by setup_passwordless_system.sh on $(date)
|
# Created by setup_passwordless_system.sh on $(date)
|
||||||
|
|
||||||
@ -158,37 +160,37 @@ greeter-session=lightdm-gtk-greeter
|
|||||||
autologin-in-background=false
|
autologin-in-background=false
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✓ LightDM auto-login also configured in separate config file: $custom_conf"
|
echo "✓ LightDM auto-login also configured in separate config file: $custom_conf"
|
||||||
|
|
||||||
# Enable lightdm service
|
# Enable lightdm service
|
||||||
systemctl enable lightdm.service
|
systemctl enable lightdm.service
|
||||||
echo "✓ LightDM service enabled"
|
echo "✓ LightDM service enabled"
|
||||||
|
|
||||||
# Restart lightdm to apply changes only if --logout flag is provided
|
# Restart lightdm to apply changes only if --logout flag is provided
|
||||||
if [[ "$ALLOW_LOGOUT" == true ]]; then
|
if [[ $ALLOW_LOGOUT == true ]]; then
|
||||||
echo "Restarting LightDM to apply auto-login settings..."
|
echo "Restarting LightDM to apply auto-login settings..."
|
||||||
systemctl restart lightdm.service
|
systemctl restart lightdm.service
|
||||||
echo "✓ LightDM restarted"
|
echo "✓ LightDM restarted"
|
||||||
else
|
else
|
||||||
echo "✓ LightDM configuration complete (restart lightdm or reboot to activate auto-login)"
|
echo "✓ LightDM configuration complete (restart lightdm or reboot to activate auto-login)"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to configure i3 session
|
# Function to configure i3 session
|
||||||
configure_i3_session() {
|
configure_i3_session() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. Configuring i3 Session..."
|
echo "3. Configuring i3 Session..."
|
||||||
echo "==========================="
|
echo "==========================="
|
||||||
|
|
||||||
local xsessions_dir="/usr/share/xsessions"
|
local xsessions_dir="/usr/share/xsessions"
|
||||||
local i3_desktop="$xsessions_dir/i3.desktop"
|
local i3_desktop="$xsessions_dir/i3.desktop"
|
||||||
|
|
||||||
# Create xsessions directory if it doesn't exist
|
# Create xsessions directory if it doesn't exist
|
||||||
mkdir -p "$xsessions_dir"
|
mkdir -p "$xsessions_dir"
|
||||||
|
|
||||||
# Check if i3.desktop exists, create if not
|
# Check if i3.desktop exists, create if not
|
||||||
if [[ ! -f "$i3_desktop" ]]; then
|
if [[ ! -f $i3_desktop ]]; then
|
||||||
cat > "$i3_desktop" << EOF
|
cat > "$i3_desktop" << EOF
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=i3
|
Name=i3
|
||||||
Comment=improved dynamic tiling window manager
|
Comment=improved dynamic tiling window manager
|
||||||
@ -199,42 +201,42 @@ X-LightDM-DesktopName=i3
|
|||||||
DesktopNames=i3
|
DesktopNames=i3
|
||||||
Keywords=tiling;wm;windowmanager;window;manager;
|
Keywords=tiling;wm;windowmanager;window;manager;
|
||||||
EOF
|
EOF
|
||||||
echo "✓ Created i3 desktop session file: $i3_desktop"
|
echo "✓ Created i3 desktop session file: $i3_desktop"
|
||||||
else
|
else
|
||||||
echo "✓ i3 desktop session file already exists"
|
echo "✓ i3 desktop session file already exists"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure user has i3 config directory
|
# Ensure user has i3 config directory
|
||||||
local user_home="/home/${TARGET_USER}"
|
local user_home="/home/${TARGET_USER}"
|
||||||
local i3_config_dir="$user_home/.config/i3"
|
local i3_config_dir="$user_home/.config/i3"
|
||||||
|
|
||||||
if [[ ! -d "$i3_config_dir" ]]; then
|
if [[ ! -d $i3_config_dir ]]; then
|
||||||
sudo -u "$TARGET_USER" mkdir -p "$i3_config_dir"
|
sudo -u "$TARGET_USER" mkdir -p "$i3_config_dir"
|
||||||
echo "✓ Created i3 config directory for user: $TARGET_USER"
|
echo "✓ Created i3 config directory for user: $TARGET_USER"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to configure additional auto-login settings
|
# Function to configure additional auto-login settings
|
||||||
configure_additional_settings() {
|
configure_additional_settings() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. Configuring Additional Settings..."
|
echo "4. Configuring Additional Settings..."
|
||||||
echo "===================================="
|
echo "===================================="
|
||||||
|
|
||||||
# Add user to autologin group if it exists
|
# Add user to autologin group if it exists
|
||||||
if getent group autologin &> /dev/null; then
|
if getent group autologin &> /dev/null; then
|
||||||
usermod -a -G autologin "$TARGET_USER"
|
usermod -a -G autologin "$TARGET_USER"
|
||||||
echo "✓ Added $TARGET_USER to autologin group"
|
echo "✓ Added $TARGET_USER to autologin group"
|
||||||
else
|
else
|
||||||
# Create autologin group
|
# Create autologin group
|
||||||
groupadd -r autologin
|
groupadd -r autologin
|
||||||
usermod -a -G autologin "$TARGET_USER"
|
usermod -a -G autologin "$TARGET_USER"
|
||||||
echo "✓ Created autologin group and added $TARGET_USER"
|
echo "✓ Created autologin group and added $TARGET_USER"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Configure pam for auto-login (if needed)
|
# Configure pam for auto-login (if needed)
|
||||||
local pam_lightdm="/etc/pam.d/lightdm-autologin"
|
local pam_lightdm="/etc/pam.d/lightdm-autologin"
|
||||||
if [[ ! -f "$pam_lightdm" ]]; then
|
if [[ ! -f $pam_lightdm ]]; then
|
||||||
cat > "$pam_lightdm" << EOF
|
cat > "$pam_lightdm" << EOF
|
||||||
#%PAM-1.0
|
#%PAM-1.0
|
||||||
# LightDM auto-login PAM configuration
|
# LightDM auto-login PAM configuration
|
||||||
# Created by setup_passwordless_system.sh on $(date)
|
# Created by setup_passwordless_system.sh on $(date)
|
||||||
@ -247,98 +249,98 @@ password include system-local-login
|
|||||||
session include system-local-login
|
session include system-local-login
|
||||||
session optional pam_gnome_keyring.so auto_start
|
session optional pam_gnome_keyring.so auto_start
|
||||||
EOF
|
EOF
|
||||||
echo "✓ Created PAM configuration for auto-login"
|
echo "✓ Created PAM configuration for auto-login"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to test configurations
|
# Function to test configurations
|
||||||
test_configurations() {
|
test_configurations() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "5. Testing Configurations..."
|
echo "5. Testing Configurations..."
|
||||||
echo "==========================="
|
echo "==========================="
|
||||||
|
|
||||||
# Test sudo configuration
|
# Test sudo configuration
|
||||||
echo "Testing passwordless sudo..."
|
echo "Testing passwordless sudo..."
|
||||||
if sudo -u "$TARGET_USER" sudo -n true 2>/dev/null; then
|
if sudo -u "$TARGET_USER" sudo -n true 2> /dev/null; then
|
||||||
echo "✓ Passwordless sudo test passed"
|
echo "✓ Passwordless sudo test passed"
|
||||||
else
|
else
|
||||||
echo "! Passwordless sudo test failed (may require logout/login)"
|
echo "! Passwordless sudo test failed (may require logout/login)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test lightdm configuration
|
# Test lightdm configuration
|
||||||
echo "Testing LightDM configuration..."
|
echo "Testing LightDM configuration..."
|
||||||
if lightdm --test-mode --debug 2>/dev/null | grep -q "seat"; then
|
if lightdm --test-mode --debug 2> /dev/null | grep -q "seat"; then
|
||||||
echo "✓ LightDM configuration test passed"
|
echo "✓ LightDM configuration test passed"
|
||||||
else
|
else
|
||||||
echo "! LightDM configuration test completed (check logs if issues occur)"
|
echo "! LightDM configuration test completed (check logs if issues occur)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify user is in autologin group
|
# Verify user is in autologin group
|
||||||
if groups "$TARGET_USER" | grep -q autologin; then
|
if groups "$TARGET_USER" | grep -q autologin; then
|
||||||
echo "✓ User is in autologin group"
|
echo "✓ User is in autologin group"
|
||||||
else
|
else
|
||||||
echo "! User may not be in autologin group"
|
echo "! User may not be in autologin group"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to show security warnings
|
# Function to show security warnings
|
||||||
show_security_warnings() {
|
show_security_warnings() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "⚠️ SECURITY WARNINGS ⚠️"
|
echo "⚠️ SECURITY WARNINGS ⚠️"
|
||||||
echo "========================"
|
echo "========================"
|
||||||
echo ""
|
echo ""
|
||||||
echo "The following security changes have been made:"
|
echo "The following security changes have been made:"
|
||||||
echo ""
|
echo ""
|
||||||
echo "1. PASSWORDLESS SUDO:"
|
echo "1. PASSWORDLESS SUDO:"
|
||||||
echo " • User '$TARGET_USER' can now run ANY command as root without password"
|
echo " • User '$TARGET_USER' can now run ANY command as root without password"
|
||||||
echo " • This includes system-critical operations and file modifications"
|
echo " • This includes system-critical operations and file modifications"
|
||||||
echo " • Malicious software running as this user can gain full system access"
|
echo " • Malicious software running as this user can gain full system access"
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. AUTO-LOGIN:"
|
echo "2. AUTO-LOGIN:"
|
||||||
echo " • System automatically logs in user '$TARGET_USER' on boot"
|
echo " • System automatically logs in user '$TARGET_USER' on boot"
|
||||||
echo " • No password required to access the desktop environment"
|
echo " • No password required to access the desktop environment"
|
||||||
echo " • Physical access to the machine = full user access"
|
echo " • Physical access to the machine = full user access"
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. RECOMMENDATIONS:"
|
echo "3. RECOMMENDATIONS:"
|
||||||
echo " • Use full disk encryption to protect against physical access"
|
echo " • Use full disk encryption to protect against physical access"
|
||||||
echo " • Ensure the system is in a physically secure location"
|
echo " • Ensure the system is in a physically secure location"
|
||||||
echo " • Consider using this only on personal/development machines"
|
echo " • Consider using this only on personal/development machines"
|
||||||
echo " • Regularly monitor system logs for unauthorized access"
|
echo " • Regularly monitor system logs for unauthorized access"
|
||||||
echo " • Keep the system updated and use a firewall"
|
echo " • Keep the system updated and use a firewall"
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. TO DISABLE THESE SETTINGS:"
|
echo "4. TO DISABLE THESE SETTINGS:"
|
||||||
echo " • Remove passwordless sudo: sudo rm /etc/sudoers.d/99-passwordless-${TARGET_USER}"
|
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 " • Disable auto-login: sudo rm /etc/lightdm/lightdm.conf.d/50-autologin.conf"
|
||||||
echo " • Restart LightDM: sudo systemctl restart lightdm"
|
echo " • Restart LightDM: sudo systemctl restart lightdm"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to show final instructions
|
# Function to show final instructions
|
||||||
show_final_instructions() {
|
show_final_instructions() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "Passwordless System Setup Complete"
|
echo "Passwordless System Setup Complete"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "Summary:"
|
echo "Summary:"
|
||||||
echo "✓ Passwordless sudo configured for user: $TARGET_USER"
|
echo "✓ Passwordless sudo configured for user: $TARGET_USER"
|
||||||
echo "✓ LightDM auto-login configured"
|
echo "✓ LightDM auto-login configured"
|
||||||
echo "✓ i3 session configured"
|
echo "✓ i3 session configured"
|
||||||
echo "✓ Additional auto-login settings applied"
|
echo "✓ Additional auto-login settings applied"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Changes will take effect after:"
|
echo "Changes will take effect after:"
|
||||||
echo "• Logout/login for sudo changes"
|
echo "• Logout/login for sudo changes"
|
||||||
echo "• System reboot for auto-login"
|
echo "• System reboot for auto-login"
|
||||||
echo ""
|
echo ""
|
||||||
echo "To verify after reboot:"
|
echo "To verify after reboot:"
|
||||||
echo " sudo whoami # Should not ask for password"
|
echo " sudo whoami # Should not ask for password"
|
||||||
echo " systemctl status lightdm # Should show auto-login active"
|
echo " systemctl status lightdm # Should show auto-login active"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Configuration files created:"
|
echo "Configuration files created:"
|
||||||
echo " /etc/sudoers.d/99-passwordless-${TARGET_USER}"
|
echo " /etc/sudoers.d/99-passwordless-${TARGET_USER}"
|
||||||
echo " /etc/lightdm/lightdm.conf.d/50-autologin.conf"
|
echo " /etc/lightdm/lightdm.conf.d/50-autologin.conf"
|
||||||
echo " /etc/pam.d/lightdm-autologin"
|
echo " /etc/pam.d/lightdm-autologin"
|
||||||
echo ""
|
echo ""
|
||||||
echo "IMPORTANT: Reboot recommended to activate all changes!"
|
echo "IMPORTANT: Reboot recommended to activate all changes!"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main execution
|
# Main execution
|
||||||
@ -351,22 +353,22 @@ show_security_warnings
|
|||||||
show_final_instructions
|
show_final_instructions
|
||||||
|
|
||||||
# Only offer reboot if --reboot flag was provided
|
# Only offer reboot if --reboot flag was provided
|
||||||
if [[ "$OFFER_REBOOT" == true ]]; then
|
if [[ $OFFER_REBOOT == true ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "Would you like to reboot now to activate all changes?"
|
echo "Would you like to reboot now to activate all changes?"
|
||||||
read -p "Reboot system now? (y/N): " -n 1 -r
|
read -p "Reboot system now? (y/N): " -n 1 -r
|
||||||
echo
|
echo
|
||||||
|
|
||||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
echo "Rebooting system in 5 seconds..."
|
echo "Rebooting system in 5 seconds..."
|
||||||
sleep 5
|
sleep 5
|
||||||
reboot
|
reboot
|
||||||
else
|
else
|
||||||
echo "Remember to reboot when convenient to activate all changes."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
echo "Setup completed successfully."
|
|
||||||
echo "Remember to reboot when convenient to activate all changes."
|
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
|
fi
|
||||||
|
|||||||
@ -3,23 +3,33 @@
|
|||||||
# Function to sort files in a given directory
|
# Function to sort files in a given directory
|
||||||
sort_files() {
|
sort_files() {
|
||||||
local dir=$1
|
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
|
# Create directories if they do not exist
|
||||||
mkdir -p images videos documents
|
mkdir -p images videos documents
|
||||||
|
|
||||||
# Move video files to the videos folder
|
# 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
|
# 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
|
# 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 in the Downloads folder
|
||||||
sort_files ~/Downloads
|
sort_files ~/Downloads
|
||||||
|
|
||||||
# Sort files in the home folder
|
# Sort files in the home folder
|
||||||
sort_files ~
|
sort_files ~
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -2,47 +2,47 @@
|
|||||||
|
|
||||||
# Check if amixer is installed, if not, install it
|
# Check if amixer is installed, if not, install it
|
||||||
if ! command -v amixer &> /dev/null; then
|
if ! command -v amixer &> /dev/null; then
|
||||||
echo "amixer could not be found, installing..."
|
echo "amixer could not be found, installing..."
|
||||||
sudo pacman -S --noconfirm alsa-utils
|
sudo pacman -S --noconfirm alsa-utils
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure dbus is running
|
# Ensure dbus is running
|
||||||
if ! pgrep -x "dbus-daemon" > /dev/null; then
|
if ! pgrep -x "dbus-daemon" > /dev/null; then
|
||||||
echo "Starting dbus..."
|
echo "Starting dbus..."
|
||||||
sudo systemctl start dbus
|
sudo systemctl start dbus
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure dbus is properly initialized for the user session
|
# Ensure dbus is properly initialized for the user session
|
||||||
export $(dbus-launch)
|
eval "$(dbus-launch)"
|
||||||
|
|
||||||
# Ensure notification-daemon is installed
|
# Ensure notification-daemon is installed
|
||||||
if ! pacman -Qs notification-daemon > /dev/null; then
|
if ! pacman -Qs notification-daemon > /dev/null; then
|
||||||
echo "Installing notification-daemon..."
|
echo "Installing notification-daemon..."
|
||||||
sudo pacman -S --noconfirm notification-daemon
|
sudo pacman -S --noconfirm notification-daemon
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure dunst is installed and running
|
# Ensure dunst is installed and running
|
||||||
if ! pacman -Qs dunst > /dev/null; then
|
if ! pacman -Qs dunst > /dev/null; then
|
||||||
echo "Installing dunst..."
|
echo "Installing dunst..."
|
||||||
sudo pacman -S --noconfirm dunst
|
sudo pacman -S --noconfirm dunst
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! pgrep -x "dunst" > /dev/null; then
|
if ! pgrep -x "dunst" > /dev/null; then
|
||||||
echo "Starting dunst..."
|
echo "Starting dunst..."
|
||||||
dunst &
|
dunst &
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the current state of the microphone
|
# Get the current state of the microphone
|
||||||
MIC_STATE=$(amixer get Capture | grep '\[on\]')
|
MIC_STATE=$(amixer get Capture | grep '\[on\]')
|
||||||
|
|
||||||
if [ -z "$MIC_STATE" ]; then
|
if [ -z "$MIC_STATE" ]; then
|
||||||
# If the microphone is off, turn it on
|
# If the microphone is off, turn it on
|
||||||
amixer set Capture cap
|
amixer set Capture cap
|
||||||
sleep 1 # Add a delay to ensure notify-send works correctly
|
sleep 1 # Add a delay to ensure notify-send works correctly
|
||||||
notify-send "Microphone" "Microphone is now ON"
|
notify-send "Microphone" "Microphone is now ON"
|
||||||
else
|
else
|
||||||
# If the microphone is on, turn it off
|
# If the microphone is on, turn it off
|
||||||
amixer set Capture nocap
|
amixer set Capture nocap
|
||||||
sleep 1 # Add a delay to ensure notify-send works correctly
|
sleep 1 # Add a delay to ensure notify-send works correctly
|
||||||
notify-send "Microphone" "Microphone is now OFF"
|
notify-send "Microphone" "Microphone is now OFF"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -10,48 +10,48 @@ ACTION=$1
|
|||||||
|
|
||||||
# Check if script is run as root
|
# Check if script is run as root
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
echo "This script must be run as root. Please run with sudo."
|
echo "This script must be run as root. Please run with sudo."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if action parameter is provided
|
# Check if action parameter is provided
|
||||||
if [[ "$ACTION" != "on" && "$ACTION" != "off" ]]; then
|
if [[ $ACTION != "on" && $ACTION != "off" ]]; then
|
||||||
echo "Usage: $0 [on|off]"
|
echo "Usage: $0 [on|off]"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DEVICE_PATH=""
|
DEVICE_PATH=""
|
||||||
|
|
||||||
# Find the device path in sysfs (robust scan)
|
# Find the device path in sysfs (robust scan)
|
||||||
for d in /sys/bus/usb/devices/*; do
|
for d in /sys/bus/usb/devices/*; do
|
||||||
if [[ -f "$d/idVendor" && -f "$d/idProduct" ]]; then
|
if [[ -f "$d/idVendor" && -f "$d/idProduct" ]]; then
|
||||||
v=$(cat "$d/idVendor")
|
v=$(cat "$d/idVendor")
|
||||||
p=$(cat "$d/idProduct")
|
p=$(cat "$d/idProduct")
|
||||||
if [[ "$v" == "$VENDOR_ID" && "$p" == "$PRODUCT_ID" ]]; then
|
if [[ $v == "$VENDOR_ID" && $p == "$PRODUCT_ID" ]]; then
|
||||||
DEVICE_PATH="$d"
|
DEVICE_PATH="$d"
|
||||||
break
|
break
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Check if device was found
|
# Check if device was found
|
||||||
if [ -z "$DEVICE_PATH" ]; then
|
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 "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."
|
echo "Tip: Run 'lsusb | grep ${VENDOR_ID}:${PRODUCT_ID}' to verify it's connected."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Enable or disable the device
|
# Enable or disable the device
|
||||||
if [ ! -e "$DEVICE_PATH/authorized" ]; then
|
if [ ! -e "$DEVICE_PATH/authorized" ]; then
|
||||||
echo "The 'authorized' attribute is not present at $DEVICE_PATH."
|
echo "The 'authorized' attribute is not present at $DEVICE_PATH."
|
||||||
echo "This device may not support toggling via 'authorized'."
|
echo "This device may not support toggling via 'authorized'."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$ACTION" == "off" ]; then
|
if [ "$ACTION" == "off" ]; then
|
||||||
echo '0' > "$DEVICE_PATH/authorized"
|
echo '0' > "$DEVICE_PATH/authorized"
|
||||||
echo "Device at $(basename "$DEVICE_PATH") turned off."
|
echo "Device at $(basename "$DEVICE_PATH") turned off."
|
||||||
elif [ "$ACTION" == "on" ]; then
|
elif [ "$ACTION" == "on" ]; then
|
||||||
echo '1' > "$DEVICE_PATH/authorized"
|
echo '1' > "$DEVICE_PATH/authorized"
|
||||||
echo "Device at $(basename "$DEVICE_PATH") turned on."
|
echo "Device at $(basename "$DEVICE_PATH") turned on."
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -5,58 +5,61 @@ set -euo pipefail
|
|||||||
# Configuration -----------------------------------------------------------------
|
# Configuration -----------------------------------------------------------------
|
||||||
TARGET_SESSION_NAME="Xfce Session"
|
TARGET_SESSION_NAME="Xfce Session"
|
||||||
TARGET_PACKAGES=(
|
TARGET_PACKAGES=(
|
||||||
xfwm4 # Compositing window manager with XFCE integration
|
xfwm4 # Compositing window manager with XFCE integration
|
||||||
xfce4-session # Provides the Xfce session entry for display managers
|
xfce4-session # Provides the Xfce session entry for display managers
|
||||||
xfce4-panel # Panel with system tray support
|
xfce4-panel # Panel with system tray support
|
||||||
xfce4-settings # Settings daemon (enables compositing toggle, theming, etc.)
|
xfce4-settings # Settings daemon (enables compositing toggle, theming, etc.)
|
||||||
xfce4-terminal # Handy default terminal for the new environment
|
xfce4-terminal # Handy default terminal for the new environment
|
||||||
)
|
)
|
||||||
|
|
||||||
# Utility functions --------------------------------------------------------------
|
# Utility functions --------------------------------------------------------------
|
||||||
info() { echo "[INFO] $*"; }
|
info() { echo "[INFO] $*"; }
|
||||||
warn() { echo "[WARN] $*" >&2; }
|
warn() { echo "[WARN] $*" >&2; }
|
||||||
error() { echo "[ERROR] $*" >&2; exit 1; }
|
error() {
|
||||||
|
echo "[ERROR] $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
require_command() {
|
require_command() {
|
||||||
local cmd="$1" pkg_hint="${2:-}"
|
local cmd="$1" pkg_hint="${2:-}"
|
||||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
if ! command -v "$cmd" > /dev/null 2>&1; then
|
||||||
if [[ -n "$pkg_hint" ]]; then
|
if [[ -n $pkg_hint ]]; then
|
||||||
warn "Install '$pkg_hint' to obtain the '$cmd' command."
|
warn "Install '$pkg_hint' to obtain the '$cmd' command."
|
||||||
fi
|
fi
|
||||||
error "Required command '$cmd' not found."
|
error "Required command '$cmd' not found."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_pacman() {
|
ensure_pacman() {
|
||||||
require_command pacman "pacman"
|
require_command pacman "pacman"
|
||||||
if ! grep -qi "arch" /etc/os-release 2>/dev/null; then
|
if ! grep -qi "arch" /etc/os-release 2> /dev/null; then
|
||||||
warn "This script was designed for Arch Linux; continuing anyway."
|
warn "This script was designed for Arch Linux; continuing anyway."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
install_packages() {
|
install_packages() {
|
||||||
local missing=()
|
local missing=()
|
||||||
for pkg in "${TARGET_PACKAGES[@]}"; do
|
for pkg in "${TARGET_PACKAGES[@]}"; do
|
||||||
if ! pacman -Qi "$pkg" >/dev/null 2>&1; then
|
if ! pacman -Qi "$pkg" > /dev/null 2>&1; then
|
||||||
missing+=("$pkg")
|
missing+=("$pkg")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ ${#missing[@]} -eq 0 ]]; then
|
if [[ ${#missing[@]} -eq 0 ]]; then
|
||||||
info "All target packages are already installed."
|
info "All target packages are already installed."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v sudo >/dev/null 2>&1; then
|
if ! command -v sudo > /dev/null 2>&1; then
|
||||||
error "sudo is required to install packages. Install sudo or run this script as root."
|
error "sudo is required to install packages. Install sudo or run this script as root."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "Installing missing packages: ${missing[*]}"
|
info "Installing missing packages: ${missing[*]}"
|
||||||
sudo pacman -S --needed --noconfirm "${missing[@]}"
|
sudo pacman -S --needed --noconfirm "${missing[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
print_post_install_tips() {
|
print_post_install_tips() {
|
||||||
cat <<EOF
|
cat << EOF
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
XFCE session installed.
|
XFCE session installed.
|
||||||
@ -73,31 +76,31 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
logout_user() {
|
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
|
if [[ -n $session_id ]] && loginctl show-session "$session_id" > /dev/null 2>&1; then
|
||||||
info "Terminating current session (ID: $session_id) via loginctl."
|
info "Terminating current session (ID: $session_id) via loginctl."
|
||||||
loginctl terminate-session "$session_id"
|
loginctl terminate-session "$session_id"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if loginctl list-sessions 2>/dev/null | awk '{print $1" "$3}' | grep -q " $USER$"; then
|
if loginctl list-sessions 2> /dev/null | awk '{print $1" "$3}' | grep -q " $USER$"; then
|
||||||
info "Terminating all sessions for user '$USER' via loginctl."
|
info "Terminating all sessions for user '$USER' via loginctl."
|
||||||
loginctl terminate-user "$USER"
|
loginctl terminate-user "$USER"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
warn "loginctl could not terminate the session; attempting fallback logout."
|
warn "loginctl could not terminate the session; attempting fallback logout."
|
||||||
pkill -KILL -u "$USER" || error "Failed to terminate user sessions. Please log out manually."
|
pkill -KILL -u "$USER" || error "Failed to terminate user sessions. Please log out manually."
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
ensure_pacman
|
ensure_pacman
|
||||||
install_packages
|
install_packages
|
||||||
print_post_install_tips
|
print_post_install_tips
|
||||||
|
|
||||||
# Give the user a moment to read the instructions before logging out.
|
# Give the user a moment to read the instructions before logging out.
|
||||||
logout_user
|
logout_user
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
@ -22,20 +22,20 @@ set -euo pipefail
|
|||||||
|
|
||||||
log() { printf "[idle-off] %s\n" "$*"; }
|
log() { printf "[idle-off] %s\n" "$*"; }
|
||||||
warn() { printf "[idle-off][WARN] %s\n" "$*" >&2; }
|
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
|
persist_systemd=false
|
||||||
watch_controller=false
|
watch_controller=false
|
||||||
for arg in "${@:-}"; do
|
for arg in "${@:-}"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--persist-systemd)
|
--persist-systemd)
|
||||||
persist_systemd=true
|
persist_systemd=true
|
||||||
;;
|
;;
|
||||||
--watch-controller)
|
--watch-controller)
|
||||||
watch_controller=true
|
watch_controller=true
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h | --help)
|
||||||
cat <<EOF
|
cat << EOF
|
||||||
Usage: $(basename "$0") [--persist-systemd] [--watch-controller]
|
Usage: $(basename "$0") [--persist-systemd] [--watch-controller]
|
||||||
|
|
||||||
Disables idle detection, screen blanking, and auto-lock for the current session.
|
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: systemd-logind IdleAction=ignore
|
||||||
- Optional: watch controller input and reset idle timers
|
- Optional: watch controller input and reset idle timers
|
||||||
EOF
|
EOF
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
disable_x11_idle() {
|
disable_x11_idle() {
|
||||||
if [[ -n "${DISPLAY:-}" ]] && has_cmd xset; then
|
if [[ -n ${DISPLAY:-} ]] && has_cmd xset; then
|
||||||
log "Disabling X11 DPMS/screensaver/blanking via xset"
|
log "Disabling X11 DPMS/screensaver/blanking via xset"
|
||||||
xset -dpms || true
|
xset -dpms || true
|
||||||
xset s off || true
|
xset s off || true
|
||||||
xset s noblank || true
|
xset s noblank || true
|
||||||
else
|
else
|
||||||
log "X11/xset not detected or DISPLAY not set; skipping xset"
|
log "X11/xset not detected or DISPLAY not set; skipping xset"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_gnome_idle() {
|
disable_gnome_idle() {
|
||||||
if has_cmd gsettings; then
|
if has_cmd gsettings; then
|
||||||
# Detect GNOME by presence of GNOME schemas
|
# Detect GNOME by presence of GNOME schemas
|
||||||
if gsettings list-schemas 2>/dev/null | grep -q '^org\.gnome\.desktop\.session$'; then
|
if gsettings list-schemas 2> /dev/null | grep -q '^org\.gnome\.desktop\.session$'; then
|
||||||
log "Applying GNOME settings to disable idle and lock"
|
log "Applying GNOME settings to disable idle and lock"
|
||||||
# No lock on idle
|
# No lock on idle
|
||||||
gsettings set org.gnome.desktop.screensaver lock-enabled false 2>/dev/null || warn "Failed to set GNOME lock-enabled"
|
gsettings set org.gnome.desktop.screensaver lock-enabled false 2> /dev/null || warn "Failed to set GNOME lock-enabled"
|
||||||
# No idle delay (0 = never)
|
# No idle delay (0 = never)
|
||||||
gsettings set org.gnome.desktop.session idle-delay 0 2>/dev/null || warn "Failed to set GNOME idle-delay"
|
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
|
# 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-ac-type 'nothing' 2> /dev/null || true
|
||||||
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-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)
|
# 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
|
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false 2> /dev/null || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_kde_idle() {
|
disable_kde_idle() {
|
||||||
# Best-effort: turn off auto-locker; note: Plasma on Wayland still may rely on compositor-level settings
|
# Best-effort: turn off auto-locker; note: Plasma on Wayland still may rely on compositor-level settings
|
||||||
if has_cmd kwriteconfig5; then
|
if has_cmd kwriteconfig5; then
|
||||||
log "Disabling KDE Plasma screen auto-lock (kscreenlockerrc)"
|
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 Autolock false 2> /dev/null || true
|
||||||
kwriteconfig5 --file kscreenlockerrc --group Daemon --key LockOnResume 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
|
kwriteconfig5 --file kscreenlockerrc --group Daemon --key Timeout 0 2> /dev/null || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_sway_idle() {
|
disable_sway_idle() {
|
||||||
# Sway commonly uses swayidle for idle actions; killing it prevents screen blanking/locking
|
# 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 sway > /dev/null 2>&1; then
|
||||||
if pgrep -x swayidle >/dev/null 2>&1; then
|
if pgrep -x swayidle > /dev/null 2>&1; then
|
||||||
log "Killing swayidle to prevent Wayland idle actions"
|
log "Killing swayidle to prevent Wayland idle actions"
|
||||||
pkill -x swayidle || true
|
pkill -x swayidle || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_lock_daemons() {
|
disable_lock_daemons() {
|
||||||
# Stop common screen lockers/idle helpers if running
|
# Stop common screen lockers/idle helpers if running
|
||||||
local daemons=(xss-lock light-locker xscreensaver gnome-screensaver)
|
local daemons=(xss-lock light-locker xscreensaver gnome-screensaver)
|
||||||
local found=false
|
local found=false
|
||||||
for d in "${daemons[@]}"; do
|
for d in "${daemons[@]}"; do
|
||||||
if pgrep -x "$d" >/dev/null 2>&1; then
|
if pgrep -x "$d" > /dev/null 2>&1; then
|
||||||
found=true
|
found=true
|
||||||
log "Stopping ${d}"
|
log "Stopping ${d}"
|
||||||
pkill -x "$d" || true
|
pkill -x "$d" || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [[ "$found" == false ]]; then
|
if [[ $found == false ]]; then
|
||||||
log "No known lock daemons running"
|
log "No known lock daemons running"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_tty_idle() {
|
disable_tty_idle() {
|
||||||
if has_cmd setterm; then
|
if has_cmd setterm; then
|
||||||
log "Disabling TTY blanking and powersave"
|
log "Disabling TTY blanking and powersave"
|
||||||
# Apply to the current TTY; also attempt to broadcast to common TTYs
|
# Apply to the current TTY; also attempt to broadcast to common TTYs
|
||||||
setterm -blank 0 -powersave off -powerdown 0 || true
|
setterm -blank 0 -powersave off -powerdown 0 || true
|
||||||
for tty in /dev/tty{1..12}; do
|
for tty in /dev/tty{1..12}; do
|
||||||
[[ -e "$tty" ]] || continue
|
[[ -e $tty ]] || continue
|
||||||
setterm -blank 0 -powersave off -powerdown 0 <"$tty" >/dev/null 2>&1 || true
|
setterm -blank 0 -powersave off -powerdown 0 < "$tty" > /dev/null 2>&1 || true
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
reset_idle_activity() {
|
reset_idle_activity() {
|
||||||
# Trigger activity hints depending on environment
|
# Trigger activity hints depending on environment
|
||||||
if [[ -n "${DISPLAY:-}" ]]; then
|
if [[ -n ${DISPLAY:-} ]]; then
|
||||||
if has_cmd xset; then
|
if has_cmd xset; then
|
||||||
xset s reset || true
|
xset s reset || true
|
||||||
xset -dpms || true
|
xset -dpms || true
|
||||||
xset s off || true
|
xset s off || true
|
||||||
xset s noblank || true
|
xset s noblank || true
|
||||||
fi
|
fi
|
||||||
if has_cmd xdotool; then
|
if has_cmd xdotool; then
|
||||||
# No-op mousemove to generate X11 activity without visible movement
|
# No-op mousemove to generate X11 activity without visible movement
|
||||||
xdotool mousemove_relative -- 0 0 2>/dev/null || true
|
xdotool mousemove_relative -- 0 0 2> /dev/null || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
watch_js_device() {
|
watch_js_device() {
|
||||||
local dev="$1"
|
local dev="$1"
|
||||||
log "Watching controller device: $dev"
|
log "Watching controller device: $dev"
|
||||||
while :; do
|
while :; do
|
||||||
if [[ ! -e "$dev" ]]; then
|
if [[ ! -e $dev ]]; then
|
||||||
warn "Device disappeared: $dev"
|
warn "Device disappeared: $dev"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
# Joystick API event size is 8 bytes; block until an event arrives
|
# 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
|
if dd if="$dev" bs=8 count=1 status=none of=/dev/null; then
|
||||||
reset_idle_activity
|
reset_idle_activity
|
||||||
# Debounce bursts of events
|
# Debounce bursts of events
|
||||||
sleep 0.3
|
sleep 0.3
|
||||||
else
|
else
|
||||||
# On read error (e.g., permission), backoff
|
# On read error (e.g., permission), backoff
|
||||||
sleep 1
|
sleep 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
start_controller_watchers() {
|
start_controller_watchers() {
|
||||||
# Attempt to watch all /dev/input/js* devices; rescan periodically for new ones
|
# Attempt to watch all /dev/input/js* devices; rescan periodically for new ones
|
||||||
local seen=""
|
declare -A pids
|
||||||
declare -A pids
|
|
||||||
|
|
||||||
# Initial permission check
|
# Initial permission check
|
||||||
local any_js=false any_readable=false
|
local any_js=false any_readable=false
|
||||||
for dev in /dev/input/js*; do
|
for dev in /dev/input/js*; do
|
||||||
[[ -e "$dev" ]] || continue
|
[[ -e $dev ]] || continue
|
||||||
any_js=true
|
any_js=true
|
||||||
if [[ -r "$dev" ]]; then any_readable=true; fi
|
if [[ -r $dev ]]; then any_readable=true; fi
|
||||||
done
|
done
|
||||||
if [[ "$any_js" == true && "$any_readable" == false ]]; then
|
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."
|
warn "No read permission to /dev/input/js*; add your user to the 'input' group or create udev rules."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while :; do
|
while :; do
|
||||||
local found_any=false
|
local found_any=false
|
||||||
for dev in /dev/input/js*; do
|
for dev in /dev/input/js*; do
|
||||||
[[ -e "$dev" ]] || continue
|
[[ -e $dev ]] || continue
|
||||||
found_any=true
|
found_any=true
|
||||||
if [[ -z "${pids[$dev]:-}" ]] || ! kill -0 "${pids[$dev]}" 2>/dev/null; then
|
if [[ -z ${pids[$dev]:-} ]] || ! kill -0 "${pids[$dev]}" 2> /dev/null; then
|
||||||
# Start a watcher for this device in background
|
# Start a watcher for this device in background
|
||||||
watch_js_device "$dev" &
|
watch_js_device "$dev" &
|
||||||
pids[$dev]=$!
|
pids[$dev]=$!
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [[ "$found_any" == false ]]; then
|
if [[ $found_any == false ]]; then
|
||||||
# No joystick devices; quiet rescan
|
# No joystick devices; quiet rescan
|
||||||
sleep 5
|
sleep 5
|
||||||
else
|
else
|
||||||
# Rescan less frequently when active
|
# Rescan less frequently when active
|
||||||
sleep 2
|
sleep 2
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
persist_with_systemd_logind() {
|
persist_with_systemd_logind() {
|
||||||
# Set IdleAction=ignore in /etc/systemd/logind.conf and restart 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.
|
# Warning: restarting logind can affect user sessions (e.g., inhibit handling). Use with care.
|
||||||
if [[ "$persist_systemd" != true ]]; then
|
if [[ $persist_systemd != true ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if ! has_cmd sudo; then
|
if ! has_cmd sudo; then
|
||||||
warn "sudo not found; cannot persist systemd-logind setting"
|
warn "sudo not found; cannot persist systemd-logind setting"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
log "Persisting: setting systemd-logind IdleAction=ignore (requires sudo)"
|
log "Persisting: setting systemd-logind IdleAction=ignore (requires sudo)"
|
||||||
sudo sh -c '
|
sudo sh -c '
|
||||||
set -e
|
set -e
|
||||||
conf=/etc/systemd/logind.conf
|
conf=/etc/systemd/logind.conf
|
||||||
if [ ! -f "$conf" ]; then
|
if [ ! -f "$conf" ]; then
|
||||||
@ -235,39 +234,38 @@ persist_with_systemd_logind() {
|
|||||||
printf "\nIdleAction=ignore\n" >> "$conf"
|
printf "\nIdleAction=ignore\n" >> "$conf"
|
||||||
fi
|
fi
|
||||||
'
|
'
|
||||||
log "Restarting systemd-logind to apply changes (may briefly affect session inhibitors)"
|
log "Restarting systemd-logind to apply changes (may briefly affect session inhibitors)"
|
||||||
sudo systemctl restart systemd-logind || warn "Failed to restart systemd-logind"
|
sudo systemctl restart systemd-logind || warn "Failed to restart systemd-logind"
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
log "Starting idle/lock disablement"
|
log "Starting idle/lock disablement"
|
||||||
|
|
||||||
# Environment-aware steps
|
# Environment-aware steps
|
||||||
disable_x11_idle
|
disable_x11_idle
|
||||||
disable_gnome_idle
|
disable_gnome_idle
|
||||||
disable_kde_idle
|
disable_kde_idle
|
||||||
disable_sway_idle
|
disable_sway_idle
|
||||||
|
|
||||||
# Generic steps
|
# Generic steps
|
||||||
disable_lock_daemons
|
disable_lock_daemons
|
||||||
disable_tty_idle
|
disable_tty_idle
|
||||||
|
|
||||||
# Optional persistence
|
# Optional persistence
|
||||||
persist_with_systemd_logind
|
persist_with_systemd_logind
|
||||||
|
|
||||||
if [[ "$watch_controller" == true ]]; then
|
if [[ $watch_controller == true ]]; then
|
||||||
log "Controller activity watcher enabled"
|
log "Controller activity watcher enabled"
|
||||||
# Keep the script alive to watch controllers
|
# Keep the script alive to watch controllers
|
||||||
start_controller_watchers &
|
start_controller_watchers &
|
||||||
watcher_pid=$!
|
watcher_pid=$!
|
||||||
log "Watcher PID: $watcher_pid"
|
log "Watcher PID: $watcher_pid"
|
||||||
# Wait indefinitely and forward termination
|
# Wait indefinitely and forward termination
|
||||||
trap 'log "Stopping controller watcher"; kill "$watcher_pid" 2>/dev/null || true; exit 0' INT TERM
|
trap 'log "Stopping controller watcher"; kill "$watcher_pid" 2>/dev/null || true; exit 0' INT TERM
|
||||||
wait "$watcher_pid"
|
wait "$watcher_pid"
|
||||||
else
|
else
|
||||||
log "Done. The screen should no longer blank, lock, or power down automatically."
|
log "Done. The screen should no longer blank, lock, or power down automatically."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user