Refactor backtunnel-share with cleanup logic, duration-based termination, and improved error handling; introduce backtunnel-share-gui for streamlined GUI-based operation
This commit is contained in:
@@ -5,7 +5,8 @@
|
||||
|
||||
# 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
|
||||
# Options: -p|--tunnel-port <PORT> -l|--local-ssh-port <PORT>
|
||||
# -i|--invite [--invite-mount <PATH>] [--invite-file <FILE>] [--qr] -h|--help
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -123,6 +124,7 @@ fi
|
||||
# --- 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
|
||||
|
||||
echo "⏳ Sharing '${FOLDER}' via reverse SSH:"
|
||||
echo " local sshd port : ${LOCAL_SSH_PORT}"
|
||||
@@ -164,9 +166,10 @@ fi
|
||||
|
||||
echo "Tip: On the remote side, mount with:"
|
||||
echo " backtunnel-access '${FOLDER}' from ${REMOTE_USER}@${REMOTE_HOST} -p ${TUNNEL_PORT}"
|
||||
echo "To stop sharing early: press Ctrl+C here."
|
||||
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 is already in use (best-effort) ---
|
||||
# --- optional 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
|
||||
@@ -174,19 +177,45 @@ if ssh -o BatchMode=yes -o ConnectTimeout=5 "${REMOTE_USER}@${REMOTE_HOST}" \
|
||||
# exit 1
|
||||
fi
|
||||
|
||||
# Bind reverse to 127.0.0.1 on the REMOTE host (private-by-default).
|
||||
# Keepalive so the tunnel drops on dead links.
|
||||
# Exit fast if the reverse bind fails (e.g., port in use).
|
||||
timeout "$DURATION" ssh -N \
|
||||
# --- run ssh in background; trap Ctrl-C/TERM/EXIT to stop it cleanly ---
|
||||
SSH_PID=""
|
||||
|
||||
# shellcheck disable=SC2317 # cleanup is invoked indirectly via trap
|
||||
cleanup() {
|
||||
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
|
||||
}
|
||||
trap cleanup INT TERM EXIT
|
||||
|
||||
# Start reverse-tunnel ssh in background with keepalives + fast-fail on -R bind
|
||||
ssh -N \
|
||||
-o ExitOnForwardFailure=yes \
|
||||
-o ServerAliveInterval=15 \
|
||||
-o ServerAliveCountMax=3 \
|
||||
-R "${TUNNEL_PORT}:localhost:${LOCAL_SSH_PORT}" \
|
||||
-- "${REMOTE_USER}@${REMOTE_HOST}"
|
||||
-- "${REMOTE_USER}@${REMOTE_HOST}" &
|
||||
SSH_PID=$!
|
||||
|
||||
rc=$?
|
||||
if [[ $rc -eq 124 ]]; then
|
||||
echo "⏹️ Sharing ended: reached duration (${DURATION})."
|
||||
# Wait for ssh to exit, but bounded by duration:
|
||||
# Use 'timeout … tail --pid=PID -f /dev/null' so we don't need bash -c 'wait "$1"'
|
||||
if timeout "$DURATION" tail --pid="$SSH_PID" -f /dev/null; then
|
||||
# ssh exited on its own before timeout
|
||||
exit 0
|
||||
else
|
||||
rc=$?
|
||||
if [[ $rc -eq 124 ]]; then
|
||||
echo "⏹️ Sharing ended: reached duration (${DURATION})."
|
||||
# ensure the child is gone
|
||||
if kill -0 "$SSH_PID" 2>/dev/null; then
|
||||
kill -TERM "$SSH_PID" 2>/dev/null || true
|
||||
wait "$SSH_PID" 2>/dev/null || true
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
# other error from timeout/tail
|
||||
exit "$rc"
|
||||
fi
|
||||
exit "$rc"
|
||||
|
||||
45
scripts/backtunnel-share-gui
Normal file
45
scripts/backtunnel-share-gui
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2025. LUXIM d.o.o., Slovenia - Matjaž Mozetič.
|
||||
|
||||
# GUI wrapper for BackTunnel "Share" action (Dolphin service menu)
|
||||
# Prompts for parameters via kdialog and launches backtunnel-share in Konsole.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# --- Selection from Dolphin ---
|
||||
FOLDER="${1:-}"
|
||||
if [[ -z "$FOLDER" ]]; then
|
||||
kdialog --error "No folder selected." || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Defaults ---
|
||||
REMOTE_DEFAULT="user@vps.example.com"
|
||||
DURATION_DEFAULT="2h"
|
||||
TPORT_DEFAULT="2222"
|
||||
LPORT_DEFAULT="22"
|
||||
INVITE_MOUNT_DEFAULT="/mnt/remote-rssh"
|
||||
|
||||
# --- Prompts ---
|
||||
REMOTE="$(kdialog --inputbox "Remote (user@host or user:host):" "$REMOTE_DEFAULT")" || exit 1
|
||||
DUR="$(kdialog --combobox "Share duration" 30m 2h 6h 1d 2d --editable "$DURATION_DEFAULT")" || exit 1
|
||||
TPORT="$(kdialog --inputbox "Tunnel port on remote:" "$TPORT_DEFAULT")" || exit 1
|
||||
LPORT="$(kdialog --inputbox "Local SSH port to expose:" "$LPORT_DEFAULT")" || exit 1
|
||||
|
||||
INV="" # explicit if-then; avoids SC2015 ("A && B || C is not if-then-else")
|
||||
if kdialog --yesno "Print invite line for chat?"; then
|
||||
INV="-i"
|
||||
fi
|
||||
|
||||
QR=""
|
||||
if kdialog --yesno "Show QR code for the invite?"; then
|
||||
QR="--qr"
|
||||
fi
|
||||
|
||||
MP="$(kdialog --inputbox "Suggested mount point in invite:" "$INVITE_MOUNT_DEFAULT")" || exit 1
|
||||
|
||||
# --- Run share in Konsole (keeps session open) ---
|
||||
exec konsole --noclose -e backtunnel-share \
|
||||
"$FOLDER" with "$REMOTE" for "$DUR" \
|
||||
-p "$TPORT" -l "$LPORT" $INV $QR --invite-mount "$MP"
|
||||
Reference in New Issue
Block a user