diff --git a/scripts/backtunnel-share b/scripts/backtunnel-share index 263ebe6..f271287 100644 --- a/scripts/backtunnel-share +++ b/scripts/backtunnel-share @@ -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 -l|--local-ssh-port -i|--invite [--invite-mount ] [--invite-file ] [--qr] -h|--help +# Options: -p|--tunnel-port -l|--local-ssh-port +# -i|--invite [--invite-mount ] [--invite-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" diff --git a/scripts/backtunnel-share-gui b/scripts/backtunnel-share-gui new file mode 100644 index 0000000..d8cafb1 --- /dev/null +++ b/scripts/backtunnel-share-gui @@ -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" diff --git a/servicemenus/backtunnel_share.desktop b/servicemenus/backtunnel_share.desktop index 0b2bf17..0d88bea 100644 --- a/servicemenus/backtunnel_share.desktop +++ b/servicemenus/backtunnel_share.desktop @@ -10,4 +10,4 @@ X-KDE-Priority=TopLevel [Desktop Action BackTunnelShare] Name=Share via BackTunnel… Icon=network-server -Exec=/bin/bash -lc 'FOLDER="$1"; REMOTE="$(kdialog --inputbox "Remote (user@host or user:host):" "user@vps.example.com")" || exit 1; DUR="$(kdialog --combobox "Share duration" 30m 2h 6h 1d 2d --editable "2h")" || exit 1; TPORT="$(kdialog --inputbox "Tunnel port on remote:" "2222")" || exit 1; LPORT="$(kdialog --inputbox "Local SSH port to expose:" "22")" || exit 1; if kdialog --yesno "Print invite line for chat?"; then INV="-i"; else INV=""; fi; if kdialog --yesno "Show QR code for the invite?"; then QR="--qr"; else QR=""; fi; MP="$(kdialog --inputbox "Suggested mount point in invite:" "/mnt/remote-rssh")" || exit 1; exec konsole --noclose -e backtunnel-share "$FOLDER" with "$REMOTE" for "$DUR" -p "$TPORT" -l "$LPORT" $INV $QR --invite-mount "$MP"' _ %f +Exec=backtunnel-share-gui %f