From c46a1da405d413a7b4a0437060deadae997d9c61 Mon Sep 17 00:00:00 2001 From: sysadminmatmoz Date: Sat, 20 Sep 2025 08:53:18 +0200 Subject: [PATCH] Improve script robustness and portability for logging, terminal execution, and uninstallation workflows. --- scripts/backtunnel-open-term | 39 ++++++++++++++++++++++++++++---- scripts/uninstall.sh | 44 +++++++++++++++++++++++++++--------- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/scripts/backtunnel-open-term b/scripts/backtunnel-open-term index d7a2ab4..4feddd0 100644 --- a/scripts/backtunnel-open-term +++ b/scripts/backtunnel-open-term @@ -7,8 +7,28 @@ LOG_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/backtunnel" [[ -d "$LOG_DIR" ]] || mkdir -p "$LOG_DIR" LOG_FILE="${LOG_DIR}/servicemenu.$(date +%Y%m%d-%H%M%S).log" -# Simple rotation: keep the last 10 files -ls -1t "$LOG_DIR"/servicemenu.*.log 2>/dev/null | awk 'NR>10{print}' | xargs -r rm -f +# Simple rotation: keep the last 10 files (portable, avoids SC2207 and xargs -r) +shopt -s nullglob +logs=( "$LOG_DIR"/servicemenu.*.log ) +if (( ${#logs[@]} > 10 )); then + pairs=() + for f in "${logs[@]}"; do + # Try GNU stat, then BSD stat; fall back to 0 if neither works + if mtime=$(stat -c %Y -- "$f" 2>/dev/null); then + : + elif mtime=$(stat -f %m -- "$f" 2>/dev/null); then + : + else + mtime=0 + fi + pairs+=( "$mtime"$'\t'"$f" ) + done + IFS=$'\n' read -r -d '' -a sorted < <(printf '%s\n' "${pairs[@]}" | sort -rn -k1,1 | cut -f2-; printf '\0') + for (( i=10; i<${#sorted[@]}; i++ )); do + rm -f -- "${sorted[i]}" + done +fi +shopt -u nullglob cmd=( "$@" ) { @@ -34,13 +54,24 @@ cmd=( "$@" ) term="$(detect_term)" echo "Chosen terminal: ${term:-}"; echo + # Prebuild a shell-escaped command for bash -lc cases (preserves args) + shell_cmd="" + if (( ${#cmd[@]} )); then + printf -v shell_cmd '%q' "${cmd[0]}" + if (( ${#cmd[@]} > 1 )); then + for a in "${cmd[@]:1}"; do + printf -v shell_cmd '%s %q' "$shell_cmd" "$a" + done + fi + fi + # Run command in terminal (use hold/noclose if supported) case "$term" in konsole) exec konsole --noclose -e "${cmd[@]}" ;; kitty) exec kitty -e "${cmd[@]}" ;; alacritty) exec alacritty -e "${cmd[@]}" ;; - gnome-terminal) exec gnome-terminal -- bash -lc "exec \"${cmd[0]}\" ${cmd[@]:1}" ;; - kgx) exec kgx -- bash -lc "exec \"${cmd[0]}\" ${cmd[@]:1}" ;; # GNOME Console + gnome-terminal) exec gnome-terminal -- bash -lc "exec $shell_cmd" ;; + kgx) exec kgx -- bash -lc "exec $shell_cmd" ;; # GNOME Console tilix) exec tilix -e "${cmd[@]}" ;; xfce4-terminal) exec xfce4-terminal -e "${cmd[@]}" ;; xterm) exec xterm -hold -e "${cmd[@]}" ;; diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh index f6323fb..30c99e3 100644 --- a/scripts/uninstall.sh +++ b/scripts/uninstall.sh @@ -16,6 +16,22 @@ SHARE_DIR="$DESTDIR$PREFIX/share/backtunnel" ETC_DIR="$DESTDIR/etc/backtunnel" say() { printf '[BackTunnel] %s\n' "$*"; } +say_warn() { printf '[BackTunnel] WARN: %s\n' "$*" >&2; } + +# Portable directory prune: remove dir if empty, then move up until stop boundary +prune_dir() { + local dir="${1%/}" + local stop="${2%/}" + while [[ -n "$dir" && "$dir" != "/" && "$dir" != "$stop" ]]; do + rmdir "$dir" 2>/dev/null || break + dir="$(dirname "$dir")" + done +} + +# Friendly notice if uninstalling the live system without root +if [[ -z "$DESTDIR" && ${EUID:-$(id -u)} -ne 0 ]]; then + say_warn "Running without root; some files may not be removed due to permissions." +fi say "Uninstalling from PREFIX=${PREFIX} DESTDIR=${DESTDIR} (PURGE=${PURGE})" @@ -27,6 +43,8 @@ rm -f "$BINDIR/backtunnel-share" \ "$BINDIR/backtunnel-open-term" \ "$BINDIR/backtunnel-share-tui" \ "$BINDIR/backtunnel-access-tui" +# Optionally remove helper that may be present on some installs +rm -f "$BINDIR/backtunnel-init" # --- Man page --- rm -f "$MANDIR/backtunnel.1" @@ -42,28 +60,32 @@ rm -f "$KIO_SM/backtunnel_share.desktop" \ "$KSVC5/backtunnel_access.desktop" # --- Optional desktop launcher --- -rm -f "$APPDIR/backtunnel.desktop" || true +rm -f "$APPDIR/backtunnel.desktop" # --- Shared defaults (only if PURGE=1) --- if [[ "$PURGE" = "1" ]]; then say "Purging shared defaults under $SHARE_DIR and $ETC_DIR" rm -f "$SHARE_DIR/profiles.ini" 2>/dev/null || true rm -f "$ETC_DIR/profiles.ini" 2>/dev/null || true - # Remove directories if empty (and prune empty parents) - rmdir -p --ignore-fail-on-non-empty "$SHARE_DIR" 2>/dev/null || true - rmdir -p --ignore-fail-on-non-empty "$ETC_DIR" 2>/dev/null || true + # Remove directories if empty (and prune empty parents up to safe boundaries) + prune_dir "$SHARE_DIR" "$DESTDIR$PREFIX/share" + prune_dir "$ETC_DIR" "$DESTDIR/etc" else # Optionally clean up empty share dir if package manager removed files already - rmdir -p --ignore-fail-on-non-empty "$SHARE_DIR" 2>/dev/null || true + prune_dir "$SHARE_DIR" "$DESTDIR$PREFIX/share" say "Keeping shared defaults: $SHARE_DIR/ and $ETC_DIR/ (set PURGE=1 to remove)" fi -# --- Refresh desktop/KDE cache (best-effort) --- -command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database -q || true -if command -v kbuildsycoca6 >/dev/null 2>&1; then - kbuildsycoca6 --noincremental >/dev/null 2>&1 || true -elif command -v kbuildsycoca5 >/dev/null 2>&1; then - kbuildsycoca5 --noincremental >/dev/null 2>&1 || true +# --- Refresh desktop/KDE cache (best-effort, skip during packaging) --- +if [[ -z "$DESTDIR" ]]; then + if command -v update-desktop-database >/dev/null 2>&1; then + update-desktop-database -q || true + fi + if command -v kbuildsycoca6 >/dev/null 2>&1; then + kbuildsycoca6 --noincremental >/dev/null 2>&1 || true + elif command -v kbuildsycoca5 >/dev/null 2>&1; then + kbuildsycoca5 --noincremental >/dev/null 2>&1 || true + fi fi say "Uninstall complete."