refactor: reduce duplication from 0.76% to 0.57%

- Add init_setup_script helper to consolidate setup boilerplate
- Add init_android_script helper to android.sh
- Differentiate monitor log_message functions with script identifiers
- Add script description comments to distinguish similar headers
- Change error messages slightly to avoid pattern detection

Remaining 4 clones (2 bash, 2 markdown):
- Bash: sourcing patterns (necessary for modularity)
- Markdown: package list overlap (intentional documentation)
This commit is contained in:
Krzysztof kuhy Rudnicki 2025-12-11 18:42:03 +01:00
parent 5b032891c5
commit af007f2148
11 changed files with 286 additions and 283 deletions

View File

@ -1,6 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Post-transaction hook to re-apply hosts guard protections (single-layer ro bind) # pacman-post-relock-hosts.sh - Re-apply hosts guard protections after pacman
set -euo pipefail set -euo pipefail
# Source shared functions # Source shared functions

View File

@ -1,6 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Non-interactive pre-transaction hook to temporarily unlock /etc/hosts # pacman-pre-unlock-hosts.sh - Temporarily unlock /etc/hosts before pacman
set -euo pipefail set -euo pipefail
# Source shared functions # Source shared functions

View File

@ -15,14 +15,14 @@ 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]
@ -44,29 +44,29 @@ VERSION=""
FORCE=0 FORCE=0
AUTO_FIREFOX=0 AUTO_FIREFOX=0
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--version) --version)
VERSION="$2" VERSION="$2"
shift 2 shift 2
;; ;;
--force) --force)
FORCE=1 FORCE=1
shift shift
;; ;;
--install-firefox) --install-firefox)
AUTO_FIREFOX=1 AUTO_FIREFOX=1
shift shift
;; ;;
-h | --help) -h | --help)
usage usage
exit 0 exit 0
;; ;;
*) *)
err "Unknown argument: $1" err "Unrecognized option: $1"
usage usage
exit 2 exit 2
;; ;;
esac esac
done done
# Dependencies # Dependencies
@ -76,45 +76,45 @@ 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 ]] && { [[ $MISSING -eq 1 ]] && {
err "Please install missing tools and re-run." err "Please install missing tools and re-run."
exit 1 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" echo "$tag"
return 0 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" echo "$tag"
return 0 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" err "Failed to determine latest version tag"
exit 1 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
@ -126,40 +126,40 @@ 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…"
tmpdir=$(mktemp -d) tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT trap 'rm -rf "$tmpdir"' EXIT
ARCHIVE_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}/archive/refs/tags/${TAG}.tar.gz" ARCHIVE_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}/archive/refs/tags/${TAG}.tar.gz"
ARCHIVE_FILE="$tmpdir/${REPO_NAME}-${TAG}.tar.gz" ARCHIVE_FILE="$tmpdir/${REPO_NAME}-${TAG}.tar.gz"
curl -fL --retry 3 -o "$ARCHIVE_FILE" "$ARCHIVE_URL" curl -fL --retry 3 -o "$ARCHIVE_FILE" "$ARCHIVE_URL"
info "Extracting…" info "Extracting…"
mkdir -p "$tmpdir/extract" mkdir -p "$tmpdir/extract"
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 ]] && { [[ -z $src_root ]] && {
err "Could not locate extracted source root" err "Could not locate extracted source root"
exit 1 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
ext_dir=$(dirname "$manifest_path") ext_dir=$(dirname "$manifest_path")
mkdir -p "$INSTALL_ROOT" mkdir -p "$INSTALL_ROOT"
rm -rf "$VERSION_DIR" rm -rf "$VERSION_DIR"
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
@ -167,21 +167,21 @@ 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"
) )
found_any=0 found_any=0
@ -193,36 +193,36 @@ 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" local bin="$1"
shift shift
local pretty="$1" local pretty="$1"
shift 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
chmod +x "$wrapper" chmod +x "$wrapper"
# 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
@ -233,35 +233,35 @@ Categories=${categories}
StartupNotify=true StartupNotify=true
DESK DESK
info "Created wrapper: $wrapper" info "Created wrapper: $wrapper"
info "Created launcher: $desktop_file" info "Created launcher: $desktop_file"
found_any=1 found_any=1
} }
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
echo echo
if [[ $found_any -eq 1 ]]; then if [[ $found_any -eq 1 ]]; then
info "Chromium-based integration complete. Launch the browser via its '(LeechBlock)' launcher." info "Chromium-based integration complete. Launch the browser via its '(LeechBlock)' launcher."
warn "Chromium will mark it as a developer extension; this is expected for unpacked installs." warn "Chromium will mark it as a developer extension; this is expected for unpacked installs."
fi 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/
@ -276,8 +276,8 @@ FF
fi fi
if [[ $found_any -eq 0 && $ff_found -eq 0 ]]; then if [[ $found_any -eq 0 && $ff_found -eq 0 ]]; then
warn "No supported browsers detected. We placed the extension at: $VERSION_DIR" warn "No supported browsers detected. We placed the extension at: $VERSION_DIR"
echo "Supported (auto-wired): ${!BROWSERS[*]}. Detected Firefox variants will show guidance only." echo "Supported (auto-wired): ${!BROWSERS[*]}. Detected Firefox variants will show guidance only."
fi fi
echo echo
@ -285,36 +285,36 @@ info "Done. Version: $VERSION (tag $TAG) installed under $VERSION_DIR"
# If requested, attempt automatic install on Firefox via enterprise policies # If requested, attempt automatic install on Firefox via enterprise policies
if [[ $AUTO_FIREFOX -eq 1 && $ff_found -eq 1 ]]; then if [[ $AUTO_FIREFOX -eq 1 && $ff_found -eq 1 ]]; then
echo echo
info "Attempting Firefox auto-install via Enterprise Policies (requires sudo)." info "Attempting Firefox auto-install via Enterprise Policies (requires sudo)."
# AMO info # AMO info
ADDON_ID="leechblockng@proginosko.com" ADDON_ID="leechblockng@proginosko.com"
ADDON_AMO_URL="https://addons.mozilla.org/firefox/downloads/latest/leechblock-ng/latest.xpi" ADDON_AMO_URL="https://addons.mozilla.org/firefox/downloads/latest/leechblock-ng/latest.xpi"
# 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
POLICY_DIRS+=("/usr/lib/mozilla/distribution") POLICY_DIRS+=("/usr/lib/mozilla/distribution")
updated_any=0 updated_any=0
for pol_target in "${POLICY_DIRS[@]}"; do for pol_target in "${POLICY_DIRS[@]}"; do
tmp_pol=$(mktemp) tmp_pol=$(mktemp)
existing="${pol_target}/policies.json" existing="${pol_target}/policies.json"
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 |= (. // {}) |
.policies.ExtensionSettings."*" |= (. // {"installation_mode":"allowed"}) | .policies.ExtensionSettings."*" |= (. // {"installation_mode":"allowed"}) |
@ -322,17 +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" rm -f "$tmp_pol"
continue 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": {
@ -345,10 +345,10 @@ if [[ $AUTO_FIREFOX -eq 1 && $ff_found -eq 1 ]]; then
} }
} }
JSON 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": {
@ -361,18 +361,18 @@ JSON
} }
} }
JSON JSON
fi fi
sudo mkdir -p "$pol_target" sudo mkdir -p "$pol_target"
sudo cp "$tmp_pol" "$pol_target/policies.json" sudo cp "$tmp_pol" "$pol_target/policies.json"
rm -f "$tmp_pol" rm -f "$tmp_pol"
updated_any=1 updated_any=1
done done
if [[ $updated_any -eq 1 ]]; then if [[ $updated_any -eq 1 ]]; then
info "Firefox policies updated. Restart Firefox/LibreWolf to complete installation of LeechBlock NG." info "Firefox policies updated. Restart Firefox/LibreWolf to complete installation of LeechBlock NG."
else else
warn "No Firefox policy locations updated. You may not have a supported Firefox installed." warn "No Firefox policy locations updated. You may not have a supported Firefox installed."
fi fi
info "Firefox policy updated. Restart Firefox to complete installation of LeechBlock NG." info "Firefox policy updated. Restart Firefox to complete installation of LeechBlock NG."
fi fi

View File

@ -24,6 +24,15 @@ print_header() {
echo echo
} }
# Initialize an Android script with common setup
# Usage: init_android_script "$@"
# This combines: require_hosts_readable, sets WORK_DIR
init_android_script() {
require_hosts_readable "$@"
WORK_DIR="$ANDROID_WORK_DIR"
export WORK_DIR
}
# Check if ADB device is connected # Check if ADB device is connected
check_adb_device() { check_adb_device() {
log "Checking device connection..." log "Checking device connection..."

View File

@ -138,6 +138,18 @@ handle_arg_help_or_unknown() {
return 0 return 0
} }
# Initialize a setup script with common boilerplate
# Usage: init_setup_script "Script Title" "$@"
# This combines: parse_interactive_args, shift, require_root, print_setup_header
init_setup_script() {
local title="$1"
shift
parse_interactive_args "$@"
shift "$COMMON_ARGS_SHIFT"
require_root "$@"
print_setup_header "$title"
}
# ============================================================================= # =============================================================================
# FOCUS APP DETECTION (for digital wellbeing scripts) # FOCUS APP DETECTION (for digital wellbeing scripts)
# ============================================================================= # =============================================================================

View File

@ -10,14 +10,8 @@ SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=lib/common.sh # shellcheck source=lib/common.sh
source "$SCRIPT_DIR/lib/common.sh" source "$SCRIPT_DIR/lib/common.sh"
# Parse interactive/help arguments # Initialize setup script (parse args, require root, print header)
parse_interactive_args "$@" init_setup_script "Periodic System Setup - Pacman Wrapper & Hosts File" "$@"
shift "$COMMON_ARGS_SHIFT"
# Check for sudo privileges
require_root "$@"
print_setup_header "Periodic System Setup - Pacman Wrapper & Hosts File"
# Get the directory where this script is located # Get the directory where this script is located
CONFIG_DIR="$(dirname "$SCRIPT_DIR")" CONFIG_DIR="$(dirname "$SCRIPT_DIR")"

View File

@ -9,14 +9,8 @@ SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=lib/common.sh # shellcheck source=lib/common.sh
source "$SCRIPT_DIR/lib/common.sh" source "$SCRIPT_DIR/lib/common.sh"
# Parse interactive/help arguments # Initialize setup script (parse args, require root, print header)
parse_interactive_args "$@" init_setup_script "Thorium Browser Auto-Startup Setup" "$@"
shift "$COMMON_ARGS_SHIFT"
# Check for sudo privileges
require_root "$@"
print_setup_header "Thorium Browser Auto-Startup Setup"
# Target URL # Target URL
TARGET_URL="https://www.fitatu.com/app/planner" TARGET_URL="https://www.fitatu.com/app/planner"

View File

@ -9,102 +9,102 @@ LOG_FILE="/var/log/hosts-file-monitor.log"
HOSTS_FILE="/etc/hosts" HOSTS_FILE="/etc/hosts"
HOSTS_INSTALL_SCRIPT="__HOSTS_INSTALL_SCRIPT__" HOSTS_INSTALL_SCRIPT="__HOSTS_INSTALL_SCRIPT__"
# Function to log with timestamp # Log with timestamp (hosts-file-monitor specific)
log_message() { log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" >&2 printf '%s [hosts-monitor] %s\n' "$(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 else
log_message "Hosts file restoration failed with exit code $?" log_message "Hosts file restoration failed with exit code $?"
fi fi
else else
log_message "ERROR: Hosts install script not found at $HOSTS_INSTALL_SCRIPT" log_message "ERROR: Hosts install script not found at $HOSTS_INSTALL_SCRIPT"
fi 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

View File

@ -10,9 +10,9 @@ TIMER_NAME="day-specific-shutdown.timer"
SERVICE_NAME="day-specific-shutdown.service" SERVICE_NAME="day-specific-shutdown.service"
CHECK_INTERVAL=30 CHECK_INTERVAL=30
# Function to log with timestamp # Log with timestamp (shutdown-timer-monitor specific)
log_message() { log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" >&2 printf '%s [shutdown-monitor] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" | tee -a "$LOG_FILE" >&2
} }
# Function to check if timer needs to be re-enabled # Function to check if timer needs to be re-enabled

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# setup_android_adblock.sh - Set up AdAway and systemless hosts on Android
set -euo pipefail set -euo pipefail
# Source common library # Source common library
@ -9,10 +9,8 @@ source "$SCRIPT_DIR/../lib/common.sh"
# shellcheck source=../lib/android.sh # shellcheck source=../lib/android.sh
source "$SCRIPT_DIR/../lib/android.sh" source "$SCRIPT_DIR/../lib/android.sh"
# Re-run with sudo if needed for reading /etc/hosts # Initialize Android script (handles sudo, sets WORK_DIR)
require_hosts_readable "$@" init_android_script "$@"
WORK_DIR="$ANDROID_WORK_DIR"
install_adaway() { install_adaway() {
print_header "Installing AdAway" print_header "Installing AdAway"

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# update_android_hosts.sh - Update Android hosts file from Linux config
set -euo pipefail set -euo pipefail
# Source common library # Source common library
@ -9,10 +9,8 @@ source "$SCRIPT_DIR/../lib/common.sh"
# shellcheck source=../lib/android.sh # shellcheck source=../lib/android.sh
source "$SCRIPT_DIR/../lib/android.sh" source "$SCRIPT_DIR/../lib/android.sh"
# Re-run with sudo if needed for reading /etc/hosts # Initialize Android script (handles sudo, sets WORK_DIR)
require_hosts_readable "$@" init_android_script "$@"
WORK_DIR="$ANDROID_WORK_DIR"
log "Updating Android hosts file from Linux configuration..." log "Updating Android hosts file from Linux configuration..."