Add accessor key authorization and enhance completion logic
Introduce `backtunnel-authorize` for managing restricted SFTP-only keys, and update `backtunnel-share` to support temporary accessor key authorization via `--allow-key` and `--allow-known`. Extend bash completion with profile, accessor, and SSH host suggestions. Revamp README sections to include updated workflows, quick starts, and key management details.
This commit is contained in:
9
scripts/backtunnel-authorize
Normal file
9
scripts/backtunnel-authorize
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
name="${1:-}"
|
||||
file="${2:-}"
|
||||
[[ -n "$name" && -n "$file" && -f "$file" ]] || { echo "Usage: backtunnel-authorize <name> <pubkey-file>"; exit 1; }
|
||||
dir="${XDG_CONFIG_HOME:-$HOME/.config}/backtunnel/authorized"
|
||||
mkdir -p "$dir"
|
||||
install -m 644 "$file" "$dir/$name.pub"
|
||||
echo "Saved: $dir/$name.pub"
|
||||
32
scripts/backtunnel-keys
Normal file
32
scripts/backtunnel-keys
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# backtunnel-keys: manage accessor-side keys
|
||||
# Usage:
|
||||
# backtunnel-keys print # print (and generate if missing) the public key
|
||||
# backtunnel-keys path # print the private/public key paths
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
KEY="$HOME/.ssh/id_ed25519_backtunnel"
|
||||
PUB="$KEY.pub"
|
||||
|
||||
cmd="${1:-print}"
|
||||
|
||||
case "$cmd" in
|
||||
print)
|
||||
if [[ ! -f "$KEY" ]]; then
|
||||
ssh-keygen -t ed25519 -f "$KEY" -N "" -C "backtunnel" >/dev/null
|
||||
fi
|
||||
if [[ ! -f "$PUB" ]]; then
|
||||
echo "Missing public key $PUB" >&2; exit 1
|
||||
fi
|
||||
cat "$PUB"
|
||||
;;
|
||||
path)
|
||||
echo "private: $KEY"
|
||||
echo "public : $PUB"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {print|path}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -1,21 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2025. LUXIM d.o.o., Slovenia - Matjaž Mozetič.
|
||||
# Licensed under the GNU GPL v3.0
|
||||
|
||||
#
|
||||
# backtunnel-share: Share a folder using reverse SSH for a limited duration
|
||||
# Syntax: backtunnel-share /path/to/folder with remoteuser:remotehost for 2h
|
||||
# Options: -p|--tunnel-port <PORT> -l|--local-ssh-port <PORT>
|
||||
# -i|--invite [--invite-mount <PATH>] [--invite-file <FILE>] [--qr] -h|--help
|
||||
# Syntax:
|
||||
# backtunnel-share /path/to/folder with remoteuser:remotehost for 2h [options]
|
||||
#
|
||||
# Options:
|
||||
# -p|--tunnel-port <PORT> Remote port to expose with -R (default: 2222)
|
||||
# -l|--local-ssh-port <PORT> Local sshd port to forward to (default: 22)
|
||||
# -i|--invite Print an invite command (and/or QR) for chat
|
||||
# --invite-mount <PATH> Suggested mount point in invite (default: $HOME/remote-rssh)
|
||||
# --invite-file <FILE> Also write invite text (with unmount hint) to FILE
|
||||
# --qr Also render the invite command as a QR code (qrencode)
|
||||
# --allow-key <FILE> Temporarily authorize accessor's public key for this session
|
||||
# --allow-known <NAME> Temporarily authorize ~/.config/backtunnel/authorized/NAME.pub
|
||||
# -h|--help Show help
|
||||
#
|
||||
# Profiles: ~/.config/backtunnel/profiles.ini (overrides /etc/backtunnel/profiles.ini then /usr/share/backtunnel/profiles.ini)
|
||||
# [default] tunnel_port=2222 local_ssh_port=22 invite_mount=$HOME/remote-rssh invite=true qr=false
|
||||
# [alice] user=alice host=vps.example.com tunnel_port=4422
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Config search order: user → system → packaged example
|
||||
# ----------------------------
|
||||
# Config discovery
|
||||
# ----------------------------
|
||||
CONFIG_USER="${XDG_CONFIG_HOME:-$HOME/.config}/backtunnel/profiles.ini"
|
||||
CONFIG_SYS="/etc/backtunnel/profiles.ini"
|
||||
CONFIG_PKG="/usr/share/backtunnel/profiles.ini"
|
||||
|
||||
# resolve CONFIG_FILE to first existing
|
||||
if [[ -f "$CONFIG_USER" ]]; then
|
||||
CONFIG_FILE="$CONFIG_USER"
|
||||
elif [[ -f "$CONFIG_SYS" ]]; then
|
||||
@@ -24,7 +37,10 @@ else
|
||||
CONFIG_FILE="$CONFIG_PKG"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2317 # invoked later at runtime; ShellCheck can't see call path
|
||||
# ----------------------------
|
||||
# INI helpers
|
||||
# ----------------------------
|
||||
# shellcheck disable=SC2317
|
||||
ini_get() { # ini_get SECTION KEY -> value
|
||||
local sec="$1" key="$2"
|
||||
awk -v s="[""$sec""]" -v k="$key" '
|
||||
@@ -78,15 +94,23 @@ profile_apply_defaults() { # set globals if unset; named overrides default
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------------
|
||||
# Defaults
|
||||
# ----------------------------
|
||||
TUNNEL_PORT=2222 # remote-side port exposed via -R
|
||||
LOCAL_SSH_PORT=22 # local sshd port to forward to
|
||||
DURATION="" # required: e.g. 30m, 2h, 1d
|
||||
|
||||
TUNNEL_PORT=2222 # remote-side port exposed via -R
|
||||
LOCAL_SSH_PORT=22 # local sshd port to forward to
|
||||
DURATION="" # required: e.g. 30m, 2h, 1d
|
||||
|
||||
INVITE=false # print a ready-to-copy access command
|
||||
INVITE=false # print a ready-to-copy access command
|
||||
INVITE_MOUNT="$HOME/remote-rssh"
|
||||
INVITE_FILE=""
|
||||
QR=false # render invite as terminal QR (requires qrencode)
|
||||
QR=false # render invite as terminal QR (requires qrencode)
|
||||
|
||||
# Accessor authorization (session-scoped)
|
||||
ALLOW_KEY_FILE=""
|
||||
ALLOW_KNOWN_NAME=""
|
||||
AUTHORIZED_STORE="${XDG_CONFIG_HOME:-$HOME/.config}/backtunnel/authorized"
|
||||
ADDED_MARKER_ID="" # unique ID for cleanup removal
|
||||
|
||||
usage() {
|
||||
cat >&2 <<EOF
|
||||
@@ -96,7 +120,7 @@ Usage:
|
||||
Positional (required, in order):
|
||||
/path/to/folder Informational only (the actual folder is mounted by backtunnel-access)
|
||||
with Literal keyword
|
||||
remoteuser:remotehost Or remoteuser@remotehost
|
||||
remoteuser:remotehost Or remoteuser@remotehost (or @profile)
|
||||
for Literal keyword
|
||||
<duration> e.g. 30m, 2h, 1d (passed to 'timeout')
|
||||
|
||||
@@ -107,17 +131,23 @@ Options:
|
||||
--invite-mount PATH Mount point suggested in invite (default: ${INVITE_MOUNT})
|
||||
--invite-file FILE Also write the invite text (with unmount hint) to FILE
|
||||
--qr Also print a QR code (requires 'qrencode')
|
||||
|
||||
--allow-key FILE Authorize this public key for this session (restricted SFTP-only, tunnel-only)
|
||||
--allow-known NAME Authorize ~/.config/backtunnel/authorized/NAME.pub
|
||||
|
||||
-h, --help Show this help
|
||||
|
||||
Examples:
|
||||
$(basename "$0") ~/projects with alice:vps.example.com for 2h
|
||||
$(basename "$0") ~/projects with alice@vps.example.com for 1d -p 4422 -l 2222
|
||||
$(basename "$0") ~/projects with alice@vps.example.com for 2h -i --qr
|
||||
$(basename "$0") ~/projects with @work for 2h -i --qr --allow-known alice
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- basic positional parsing ---
|
||||
# ----------------------------
|
||||
# Positional parsing
|
||||
# ----------------------------
|
||||
[[ $# -lt 5 ]] && usage
|
||||
|
||||
FOLDER=$1
|
||||
@@ -139,7 +169,9 @@ profile_apply_defaults "$profile_name"
|
||||
# Expand @name -> user@host for actual ssh use
|
||||
REMOTE="$(profile_expand_remote "$REMOTE")"
|
||||
|
||||
# --- optional flags ---
|
||||
# ----------------------------
|
||||
# Optional flags
|
||||
# ----------------------------
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-p|--tunnel-port)
|
||||
@@ -170,6 +202,16 @@ while [[ $# -gt 0 ]]; do
|
||||
QR=true
|
||||
shift
|
||||
;;
|
||||
--allow-key)
|
||||
[[ $# -lt 2 ]] && usage
|
||||
ALLOW_KEY_FILE=$2
|
||||
shift 2
|
||||
;;
|
||||
--allow-known)
|
||||
[[ $# -lt 2 ]] && usage
|
||||
ALLOW_KNOWN_NAME=$2
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
@@ -180,13 +222,17 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
# --- validate duration (timeout supports s,m,h,d) ---
|
||||
# ----------------------------
|
||||
# Duration validation
|
||||
# ----------------------------
|
||||
if [[ ! "$DURATION" =~ ^[0-9]+[smhd]$ ]]; then
|
||||
echo "Invalid duration '$DURATION' (use forms like 30m, 2h, 1d)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- split remote user/host ---
|
||||
# ----------------------------
|
||||
# Split remote user/host
|
||||
# ----------------------------
|
||||
REMOTE_USER="" REMOTE_HOST=""
|
||||
if [[ "$REMOTE" == *:* ]]; then
|
||||
REMOTE_USER=${REMOTE%%:*}
|
||||
@@ -195,30 +241,120 @@ elif [[ "$REMOTE" == *"@"* ]]; then
|
||||
REMOTE_USER=${REMOTE%%@*}
|
||||
REMOTE_HOST=${REMOTE#*@}
|
||||
else
|
||||
echo "Invalid remote format. Use remoteuser:remotehost or remoteuser@remotehost" >&2
|
||||
echo "Invalid remote format. Use remoteuser:remotehost or remoteuser@remotehost or @profile" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- deps check ---
|
||||
# ----------------------------
|
||||
# Deps check
|
||||
# ----------------------------
|
||||
command -v ssh >/dev/null 2>&1 || { echo "ssh not found."; exit 1; }
|
||||
command -v timeout >/dev/null 2>&1 || { echo "timeout not found."; exit 1; }
|
||||
command -v tail >/dev/null 2>&1 || { echo "tail not found."; exit 1; } # used for --pid wait
|
||||
command -v tail >/dev/null 2>&1 || { echo "tail not found."; exit 1; }
|
||||
|
||||
# ----------------------------
|
||||
# Accessor temporary authorization
|
||||
# ----------------------------
|
||||
restrict_key_line() { # usage: restrict_key_line <pubkey_text>
|
||||
local pk="$1"
|
||||
# Normalize
|
||||
pk="${pk//$'\r'/}"
|
||||
pk="${pk//$'\n'/}"
|
||||
if [[ "$pk" != ssh-* ]]; then
|
||||
echo "Provided key does not look like an OpenSSH public key." >&2
|
||||
return 1
|
||||
fi
|
||||
printf 'from="127.0.0.1",command="internal-sftp",restrict %s' "$pk"
|
||||
}
|
||||
|
||||
add_temp_authorized_key() {
|
||||
local pubkey_text="$1"
|
||||
local ak="$HOME/.ssh/authorized_keys"
|
||||
# SC2155: Declare and assign separately to avoid masking return values.
|
||||
# shellcheck disable=SC2155
|
||||
local marker
|
||||
marker="BACKTUNNEL-TEMP-$(date +%s)-$$-$RANDOM"
|
||||
local start="# ${marker} START"
|
||||
local end="# ${marker} END"
|
||||
|
||||
mkdir -p "$HOME/.ssh"
|
||||
touch "$ak"
|
||||
chmod 700 "$HOME/.ssh"
|
||||
chmod 600 "$ak"
|
||||
|
||||
local restricted
|
||||
restricted="$(restrict_key_line "$pubkey_text")" || return 1
|
||||
|
||||
{
|
||||
echo "$start"
|
||||
echo "$restricted"
|
||||
echo "$end"
|
||||
} >> "$ak"
|
||||
|
||||
ADDED_MARKER_ID="$marker"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2317
|
||||
remove_temp_authorized_key() {
|
||||
local ak="$HOME/.ssh/authorized_keys"
|
||||
[[ -z "${ADDED_MARKER_ID:-}" ]] && return 0
|
||||
[[ -f "$ak" ]] || return 0
|
||||
awk -v m="$ADDED_MARKER_ID" '
|
||||
$0 ~ "# " m " START" {skip=1; next}
|
||||
$0 ~ "# " m " END" {skip=0; next}
|
||||
!skip {print}
|
||||
' "$ak" > "${ak}.btnew" && mv "${ak}.btnew" "$ak"
|
||||
}
|
||||
|
||||
ACCESSOR_PUBKEY_TEXT=""
|
||||
|
||||
if [[ -n "$ALLOW_KNOWN_NAME" ]]; then
|
||||
f="${AUTHORIZED_STORE}/${ALLOW_KNOWN_NAME}.pub"
|
||||
if [[ -f "$f" ]]; then
|
||||
ACCESSOR_PUBKEY_TEXT="$(cat "$f")"
|
||||
else
|
||||
echo "Known accessor key not found: $f" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$ALLOW_KEY_FILE" ]]; then
|
||||
if [[ -f "$ALLOW_KEY_FILE" ]]; then
|
||||
ACCESSOR_PUBKEY_TEXT="$(cat "$ALLOW_KEY_FILE")"
|
||||
else
|
||||
echo "Given key file not found: $ALLOW_KEY_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$ACCESSOR_PUBKEY_TEXT" ]]; then
|
||||
add_temp_authorized_key "$ACCESSOR_PUBKEY_TEXT"
|
||||
fi
|
||||
|
||||
# ----------------------------
|
||||
# Banner
|
||||
# ----------------------------
|
||||
echo "⏳ Sharing '${FOLDER}' via reverse SSH:"
|
||||
echo " local sshd port : ${LOCAL_SSH_PORT}"
|
||||
echo " remote bind port : ${TUNNEL_PORT} (on ${REMOTE_HOST})"
|
||||
echo " remote user : ${REMOTE_USER}"
|
||||
echo " duration : ${DURATION}"
|
||||
if [[ -n "$ACCESSOR_PUBKEY_TEXT" ]]; then
|
||||
echo " accessor key : temporarily authorized (restricted)"
|
||||
fi
|
||||
echo
|
||||
|
||||
# --- print invite (optional) ---
|
||||
# ----------------------------
|
||||
# Invite (optional)
|
||||
# If accessor key was pre-authorized, we can omit the auth-setup line.
|
||||
# ----------------------------
|
||||
if $INVITE; then
|
||||
INVITE_CMD="backtunnel-access '${FOLDER}' from ${REMOTE_USER}@${REMOTE_HOST} -p ${TUNNEL_PORT} -m '${INVITE_MOUNT}'"
|
||||
# Also provide a one-time auth bootstrap that installs a restricted, tunnel-only SFTP key
|
||||
AUTH_CMD="backtunnel-auth-setup -p ${TUNNEL_PORT} ${REMOTE_USER}@localhost"
|
||||
|
||||
INVITE_TEXT=$(
|
||||
cat <<EOT
|
||||
if [[ -z "$ACCESSOR_PUBKEY_TEXT" ]]; then
|
||||
AUTH_CMD="backtunnel-auth-setup -p ${TUNNEL_PORT} ${REMOTE_USER}@localhost"
|
||||
INVITE_TEXT=$(
|
||||
cat <<EOT
|
||||
|
||||
# 1) (one-time) install a tunnel-only, SFTP-only key via the reverse tunnel:
|
||||
${AUTH_CMD}
|
||||
@@ -228,13 +364,25 @@ ${INVITE_CMD}
|
||||
|
||||
# Unmount when done:
|
||||
fusermount -u '${INVITE_MOUNT}' || fusermount3 -u '${INVITE_MOUNT}'
|
||||
|
||||
EOT
|
||||
)
|
||||
)
|
||||
else
|
||||
AUTH_CMD=""
|
||||
INVITE_TEXT=$(
|
||||
cat <<EOT
|
||||
|
||||
# Mount the share:
|
||||
${INVITE_CMD}
|
||||
|
||||
# Unmount when done:
|
||||
fusermount -u '${INVITE_MOUNT}' || fusermount3 -u '${INVITE_MOUNT}'
|
||||
EOT
|
||||
)
|
||||
fi
|
||||
|
||||
echo "🔗 Invite (copy to chat):"
|
||||
echo "------------------------------------------------------------"
|
||||
echo "${AUTH_CMD}"
|
||||
[[ -n "$AUTH_CMD" ]] && echo "${AUTH_CMD}"
|
||||
echo "${INVITE_CMD}"
|
||||
echo "------------------------------------------------------------"
|
||||
|
||||
@@ -259,25 +407,30 @@ echo " backtunnel-access '${FOLDER}' from ${REMOTE_USER}@${REMOTE_HOST} -p $
|
||||
echo "Listener will appear on: ${REMOTE_HOST}:${TUNNEL_PORT} (run access there)."
|
||||
echo "To stop sharing early: press Ctrl+C in this window."
|
||||
|
||||
# --- optional pre-flight: warn if remote loopback port already in use (best-effort) ---
|
||||
# ----------------------------
|
||||
# Pre-flight: warn if remote loopback port already in use (best-effort)
|
||||
# ----------------------------
|
||||
if ssh -o BatchMode=yes -o ConnectTimeout=5 "${REMOTE_USER}@${REMOTE_HOST}" \
|
||||
"command -v nc >/dev/null 2>&1 && nc -z 127.0.0.1 ${TUNNEL_PORT}"; then
|
||||
echo "⚠️ Port ${TUNNEL_PORT} on remote 127.0.0.1 appears in use; choose another with -p." >&2
|
||||
# Uncomment the next line to make it a hard failure:
|
||||
# exit 1
|
||||
# You may 'exit 1' here if you prefer a hard failure
|
||||
fi
|
||||
|
||||
# --- run ssh in background; trap Ctrl-C/TERM/EXIT to stop it cleanly ---
|
||||
# ----------------------------
|
||||
# Cleanup & run SSH
|
||||
# ----------------------------
|
||||
SSH_PID=""
|
||||
|
||||
# shellcheck disable=SC2317 # cleanup is invoked indirectly via trap
|
||||
# shellcheck disable=SC2317
|
||||
cleanup() {
|
||||
# stop ssh child if running
|
||||
if [[ -n "${SSH_PID:-}" ]] && kill -0 "$SSH_PID" 2>/dev/null; then
|
||||
echo "⏹️ Stopping share..."
|
||||
kill -TERM "$SSH_PID" 2>/dev/null || true
|
||||
# give ssh a moment to exit gracefully
|
||||
wait "$SSH_PID" 2>/dev/null || true
|
||||
fi
|
||||
# remove temporary authorized key if we added one
|
||||
remove_temp_authorized_key
|
||||
}
|
||||
trap cleanup INT TERM EXIT
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# BackTunnel installer (terminal/TUI service menus)
|
||||
# BackTunnel installer (scripts, man page, completions, Dolphin service menus)
|
||||
set -euo pipefail
|
||||
|
||||
PREFIX=${PREFIX:-/usr}
|
||||
@@ -12,60 +12,89 @@ KSVC5="$DESTDIR$PREFIX/share/kservices5/ServiceMenus"
|
||||
APPDIR="$DESTDIR$PREFIX/share/applications"
|
||||
BCOMP="$DESTDIR$PREFIX/share/bash-completion/completions"
|
||||
SHARE_DIR="$DESTDIR$PREFIX/share/backtunnel"
|
||||
ETC_DIR="$DESTDIR/etc/backtunnel"
|
||||
|
||||
say() { printf '[BackTunnel] %s\n' "$*"; }
|
||||
say() { printf '[BackTunnel] %s\n' "$@"; }
|
||||
|
||||
say "Installing to PREFIX=${PREFIX} DESTDIR=${DESTDIR}"
|
||||
# --- Create dirs ---
|
||||
install -d -m 755 "$BINDIR" "$MANDIR" "$KIO_SM" "$KSVC5" "$BCOMP" "$SHARE_DIR" "$APPDIR"
|
||||
install -d -m 755 "$ETC_DIR"
|
||||
|
||||
# --- Binaries (CLI) ---
|
||||
install -Dm755 "scripts/backtunnel-share" "$BINDIR/backtunnel-share"
|
||||
install -Dm755 "scripts/backtunnel-access" "$BINDIR/backtunnel-access"
|
||||
install -Dm755 "scripts/backtunnel-auth-setup" "$BINDIR/backtunnel-auth-setup"
|
||||
|
||||
# --- GUI wrappers (optional) ---
|
||||
install -Dm755 "scripts/backtunnel-share-gui" "$BINDIR/backtunnel-share-gui"
|
||||
install -Dm755 "scripts/backtunnel-access-gui" "$BINDIR/backtunnel-access-gui"
|
||||
|
||||
# --- Terminal opener + TUIs (used by service menus) ---
|
||||
install -Dm755 "scripts/backtunnel-open-term" "$BINDIR/backtunnel-open-term"
|
||||
install -Dm755 "scripts/backtunnel-share-tui" "$BINDIR/backtunnel-share-tui"
|
||||
install -Dm755 "scripts/backtunnel-access-tui" "$BINDIR/backtunnel-access-tui"
|
||||
# --- Install binaries ---
|
||||
for f in \
|
||||
scripts/backtunnel-share \
|
||||
scripts/backtunnel-access \
|
||||
scripts/backtunnel-share-gui \
|
||||
scripts/backtunnel-access-gui \
|
||||
scripts/backtunnel-open-term \
|
||||
scripts/backtunnel-share-tui \
|
||||
scripts/backtunnel-access-tui \
|
||||
scripts/backtunnel-keys \
|
||||
scripts/backtunnel-authorize \
|
||||
scripts/backtunnel-auth-setup
|
||||
do
|
||||
if [[ -f "$f" ]]; then
|
||||
install -m 755 "$f" "$BINDIR/$(basename "$f")"
|
||||
say "Installed $(basename "$f")"
|
||||
fi
|
||||
done
|
||||
|
||||
# --- Man page ---
|
||||
install -Dm644 "man/backtunnel.1" "$MANDIR/backtunnel.1"
|
||||
if [[ -f man/backtunnel.1 ]]; then
|
||||
install -m 644 man/backtunnel.1 "$MANDIR/backtunnel.1"
|
||||
say "Installed man page"
|
||||
fi
|
||||
|
||||
# --- Bash completions (install for both command names) ---
|
||||
install -Dm644 "completions/backtunnel.bash" "$BCOMP/backtunnel-share"
|
||||
install -Dm644 "completions/backtunnel.bash" "$BCOMP/backtunnel-access"
|
||||
# --- Bash completion (same file serves both commands) ---
|
||||
if [[ -f completions/backtunnel.bash ]]; then
|
||||
install -m 644 completions/backtunnel.bash "$BCOMP/backtunnel-share"
|
||||
install -m 644 completions/backtunnel.bash "$BCOMP/backtunnel-access"
|
||||
say "Installed bash completions"
|
||||
fi
|
||||
|
||||
# --- Dolphin service menus (Plasma 6) ---
|
||||
install -Dm644 "servicemenus/backtunnel_share.desktop" "$KIO_SM/backtunnel_share.desktop"
|
||||
install -Dm644 "servicemenus/backtunnel_access.desktop" "$KIO_SM/backtunnel_access.desktop"
|
||||
|
||||
# --- Plasma 5 legacy path (harmless if unused) ---
|
||||
install -Dm644 "servicemenus/backtunnel_share.desktop" "$KSVC5/backtunnel_share.desktop"
|
||||
install -Dm644 "servicemenus/backtunnel_access.desktop" "$KSVC5/backtunnel_access.desktop"
|
||||
# --- Dolphin service menus (Plasma 6 primary, Plasma 5 fallback) ---
|
||||
if [[ -f servicemenus/backtunnel_share.desktop ]]; then
|
||||
install -m 644 servicemenus/backtunnel_share.desktop "$KIO_SM/backtunnel_share.desktop"
|
||||
install -m 644 servicemenus/backtunnel_share.desktop "$KSVC5/backtunnel_share.desktop"
|
||||
say "Installed Dolphin: Share"
|
||||
fi
|
||||
if [[ -f servicemenus/backtunnel_access.desktop ]]; then
|
||||
install -m 644 servicemenus/backtunnel_access.desktop "$KIO_SM/backtunnel_access.desktop"
|
||||
install -m 644 servicemenus/backtunnel_access.desktop "$KSVC5/backtunnel_access.desktop"
|
||||
say "Installed Dolphin: Access"
|
||||
fi
|
||||
|
||||
# --- Optional desktop launcher ---
|
||||
if [[ -f "desktop/backtunnel.desktop" ]]; then
|
||||
install -Dm644 "desktop/backtunnel.desktop" "$APPDIR/backtunnel.desktop"
|
||||
if [[ -f desktop/backtunnel.desktop ]]; then
|
||||
install -m 644 desktop/backtunnel.desktop "$APPDIR/backtunnel.desktop"
|
||||
say "Installed desktop launcher"
|
||||
fi
|
||||
|
||||
# --- Example profiles (system default + packaged fallback) ---
|
||||
install -Dm644 "docs/profiles.ini.example" "$DESTDIR/etc/backtunnel/profiles.ini"
|
||||
install -Dm644 "docs/profiles.ini.example" "$SHARE_DIR/profiles.ini"
|
||||
|
||||
# --- Refresh desktop/KDE cache (best-effort) ---
|
||||
if command -v update-desktop-database >/dev/null 2>&1; then
|
||||
say "Refreshing desktop database..."
|
||||
update-desktop-database -q || true
|
||||
# --- Profiles defaults ---
|
||||
if [[ -f docs/profiles.ini.example ]]; then
|
||||
install -m 644 docs/profiles.ini.example "$SHARE_DIR/profiles.ini"
|
||||
# install system default if not present (don’t overwrite local admin config)
|
||||
if [[ ! -f "$ETC_DIR/profiles.ini" ]]; then
|
||||
install -m 644 docs/profiles.ini.example "$ETC_DIR/profiles.ini"
|
||||
say "Installed default /etc/backtunnel/profiles.ini"
|
||||
else
|
||||
say "Preserved existing /etc/backtunnel/profiles.ini"
|
||||
fi
|
||||
fi
|
||||
if command -v kbuildsycoca6 >/dev/null 2>&1; then
|
||||
say "Rebuilding KDE sycoca (Plasma 6)..."
|
||||
kbuildsycoca6 --noincremental >/dev/null 2>&1 || true
|
||||
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
|
||||
say "Rebuilding KDE sycoca (Plasma 5)..."
|
||||
kbuildsycoca5 --noincremental >/dev/null 2>&1 || true
|
||||
|
||||
# --- Refresh desktop/KDE cache (best-effort; skip during DESTDIR packaging) ---
|
||||
if [[ -z "${DESTDIR}" ]]; then
|
||||
if command -v update-desktop-database >/dev/null 2>&1; then
|
||||
say "Refreshing desktop database..."
|
||||
update-desktop-database -q || true
|
||||
fi
|
||||
if command -v kbuildsycoca6 >/dev/null 2>&1; then
|
||||
say "Rebuilding KDE sycoca (Plasma 6)..."
|
||||
kbuildsycoca6 --noincremental >/dev/null 2>&1 || true
|
||||
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
|
||||
say "Rebuilding KDE sycoca (Plasma 5)..."
|
||||
kbuildsycoca5 --noincremental >/dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
|
||||
say "Install complete."
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
# BackTunnel uninstaller (with optional PURGE=1 to remove shared defaults)
|
||||
# BackTunnel uninstaller (use PURGE=1 to remove /usr/share/backtunnel and /etc/backtunnel)
|
||||
set -euo pipefail
|
||||
|
||||
PREFIX=${PREFIX:-/usr}
|
||||
DESTDIR=${DESTDIR:-}
|
||||
PURGE=${PURGE:-0} # set PURGE=1 to remove /usr/share/backtunnel and /etc/backtunnel
|
||||
PURGE=${PURGE:-0}
|
||||
|
||||
BINDIR="$DESTDIR$PREFIX/bin"
|
||||
MANDIR="$DESTDIR$PREFIX/share/man/man1"
|
||||
@@ -15,73 +15,45 @@ BCOMP="$DESTDIR$PREFIX/share/bash-completion/completions"
|
||||
SHARE_DIR="$DESTDIR$PREFIX/share/backtunnel"
|
||||
ETC_DIR="$DESTDIR/etc/backtunnel"
|
||||
|
||||
say() { printf '[BackTunnel] %s\n' "$*"; }
|
||||
say_warn() { printf '[BackTunnel] WARN: %s\n' "$*" >&2; }
|
||||
say() { printf '[BackTunnel] %s\n' "$@"; }
|
||||
# ... existing code ...
|
||||
rm -f \
|
||||
"$BINDIR/backtunnel-share" \
|
||||
"$BINDIR/backtunnel-access" \
|
||||
"$BINDIR/backtunnel-share-gui" \
|
||||
"$BINDIR/backtunnel-access-gui" \
|
||||
"$BINDIR/backtunnel-open-term" \
|
||||
"$BINDIR/backtunnel-share-tui" \
|
||||
"$BINDIR/backtunnel-access-tui" \
|
||||
"$BINDIR/backtunnel-keys" \
|
||||
"$BINDIR/backtunnel-authorize" \
|
||||
"$BINDIR/backtunnel-auth-setup" \
|
||||
"$BINDIR/backtunnel-init"
|
||||
|
||||
# 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})"
|
||||
|
||||
# --- Remove binaries ---
|
||||
rm -f "$BINDIR/backtunnel-share" \
|
||||
"$BINDIR/backtunnel-access" \
|
||||
"$BINDIR/backtunnel-auth-setup" \
|
||||
"$BINDIR/backtunnel-share-gui" \
|
||||
"$BINDIR/backtunnel-access-gui" \
|
||||
"$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"
|
||||
|
||||
# --- Bash completions ---
|
||||
rm -f "$BCOMP/backtunnel-share" \
|
||||
"$BCOMP/backtunnel-access"
|
||||
rm -f \
|
||||
"$BCOMP/backtunnel-share" \
|
||||
"$BCOMP/backtunnel-access"
|
||||
|
||||
# --- Dolphin service menus (Plasma 6 + legacy) ---
|
||||
rm -f "$KIO_SM/backtunnel_share.desktop" \
|
||||
"$KIO_SM/backtunnel_access.desktop" \
|
||||
"$KSVC5/backtunnel_share.desktop" \
|
||||
"$KSVC5/backtunnel_access.desktop"
|
||||
rm -f \
|
||||
"$KIO_SM/backtunnel_share.desktop" \
|
||||
"$KIO_SM/backtunnel_access.desktop" \
|
||||
"$KSVC5/backtunnel_share.desktop" \
|
||||
"$KSVC5/backtunnel_access.desktop"
|
||||
|
||||
# --- Optional desktop launcher ---
|
||||
rm -f "$APPDIR/backtunnel.desktop"
|
||||
|
||||
# --- Shared defaults (only if PURGE=1) ---
|
||||
# ... existing code ...
|
||||
# Optional purge of packaged defaults
|
||||
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 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
|
||||
prune_dir "$SHARE_DIR" "$DESTDIR$PREFIX/share"
|
||||
say "Keeping shared defaults: $SHARE_DIR/ and $ETC_DIR/ (set PURGE=1 to remove)"
|
||||
rm -rf "$SHARE_DIR" || true
|
||||
rm -rf "$ETC_DIR" || true
|
||||
say "Purged /usr/share/backtunnel and /etc/backtunnel"
|
||||
fi
|
||||
|
||||
# --- 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
|
||||
# Refresh caches only if not in DESTDIR (packaging)
|
||||
if [[ -z "${DESTDIR}" ]]; then
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user