#!/usr/bin/env bash # SPDX-License-Identifier: GPL-3.0-or-later # Copyright (c) 2025 LUXIM d.o.o., Slovenia # Author: Matjaž Mozetič # # Name: backtunnel-open-term # Summary: Open a command in the user's available terminal emulator, with logging and simple rotation. # Description: # Detects an installed terminal emulator (preferring Konsole on KDE sessions), opens it, # and executes the provided command with arguments. Logs session metadata and output to # ${XDG_STATE_HOME:-$HOME/.local/state}/backtunnel/servicemenu..log, keeping the # last 10 logs with a portable rotation routine. # # Usage: # backtunnel-open-term [args...] # # Examples: # backtunnel-open-term backtunnel-access-tui "/path/to/dir" # backtunnel-open-term bash -lc 'echo Hello' # # Dependencies: # - bash # - A terminal emulator (one of: konsole, kitty, alacritty, gnome-terminal, kgx, tilix, xfce4-terminal, xterm) # - stat, sort, cut, date (coreutils-compatible) # # Exit codes: # 0 started successfully (or backgrounded if no terminal is available) # 1+ invalid usage or failures during setup/execution # # Notes: # - If no terminal emulator is found, the command is started in the background via nohup # and the log path is printed. # - Uses exec for supported terminals to replace the current process; otherwise prints info then exits. set -euo pipefail # ... existing code ... 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 (portable, avoids SC2207 and xargs -r) # Purpose: remove older log files while remaining compatible across GNU/BSD userlands. 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=( "$@" ) { echo "=== BackTunnel servicemenu ===" echo "Time: $(date -Is)" echo "PWD: $(pwd)" echo "User: $(id -un) (uid=$(id -u))" echo "Cmd: ${cmd[*]}" echo "Env: BACKTUNNEL_DEBUG=${BACKTUNNEL_DEBUG:-} SHELL=${SHELL:-} DISPLAY=${DISPLAY:-}" echo # detect_term: choose a terminal emulator to launch the command # Output: prints the chosen terminal name or empty string if none found. # Prefers Konsole when KDE session is detected. detect_term() { if [[ -n "${KDE_FULL_SESSION:-}" ]] && command -v konsole >/dev/null 2>&1; then echo "konsole"; return fi # Prefer widely used modern terminals first when not on KDE for t in wezterm kitty alacritty kgx gnome-terminal tilix xfce4-terminal konsole xterm; do if command -v "$t" >/dev/null 2>&1; then echo "$t"; return; fi done echo "" # none } # ... existing code ... 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[@]}" ;; wezterm) exec wezterm start -- "${cmd[@]}" ;; kitty) exec kitty -e "${cmd[@]}" ;; alacritty) exec alacritty -e "${cmd[@]}" ;; 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[@]}" ;; *) echo "No terminal emulator found. Running in background (nohup)." nohup "${cmd[@]}" >/dev/null 2>&1 & echo "Started in background (pid=$!)." echo "Log: $LOG_FILE" exit 0 ;; esac } | tee -a "$LOG_FILE"