363 lines
12 KiB
Bash
Executable File
363 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
# POS: share usb-server — USB Redirector server control (--ls, --share; prompts when args omitted)
|
|
# POS_FLAGS: --ls --ls-shared --share --unshare --auto-share --callback --close-callback --auto-connect --disconnect --nickname --timeout --port --info --version menu
|
|
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
|
source "$(dirname "$0")/../lib/share-lib.sh" 2>/dev/null || source "$(dirname "$0")/share-lib.sh"
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: pos share usb server [flag] [args]
|
|
|
|
Control the USB Redirector server (usbsrv) — share local USB devices
|
|
over the network and manage connected clients.
|
|
|
|
Flags:
|
|
--ls List host USB devices and connected clients
|
|
--ls-shared List shared or in-use devices only
|
|
--share [dev-id] [client-id] Share a device and connect it to a client
|
|
(interactive picker when IDs are omitted)
|
|
--unshare [dev-id] Stop sharing a device
|
|
--auto-share on|off Toggle automatic sharing of new devices
|
|
--callback [addr:port] Create a callback connection to a client
|
|
--close-callback [target|all] Close a client callback
|
|
--auto-connect on|off [client] Toggle remote auto-connect for a client
|
|
--disconnect [dev-id|all] Disconnect a device from its clients
|
|
--nickname [dev-id] [nick] Set a device nickname (empty nick removes it)
|
|
--timeout [dev-id] [sec] Set device inactivity timeout (0 disables)
|
|
--port [num] Set the TCP port (restart server to apply)
|
|
--info Show server info
|
|
--version Show server version
|
|
-h, --help Show this help
|
|
|
|
Examples:
|
|
pos share usb server --ls
|
|
pos share usb server --share
|
|
pos share usb server --share 0-1 3
|
|
pos share usb server --auto-share on
|
|
pos share usb server --port 32032
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
cmd_ls() {
|
|
echo "==== HOST USB DEVICES ===="
|
|
usbsrv -list-devices
|
|
echo
|
|
echo "==== CONNECTED CLIENTS ===="
|
|
usbsrv -list-clients
|
|
}
|
|
|
|
cmd_ls_shared() {
|
|
echo "==== SHARED OR IN USE DEVICES ===="
|
|
usbsrv -list-devices | awk '
|
|
BEGIN {RS=""; FS="\n"}
|
|
{
|
|
for(i=1; i<=NF; i++) {
|
|
if ($i ~ /Status:.*(shared|in use)/) {
|
|
print $0 "\n"
|
|
break
|
|
}
|
|
}
|
|
}
|
|
' || true
|
|
}
|
|
|
|
cmd_share() {
|
|
local dev="${1:-}" client="${2:-}"
|
|
if [ -z "$dev" ]; then
|
|
echo "==== HOST USB DEVICES ===="
|
|
usbsrv -list-devices
|
|
echo
|
|
echo "==== CONNECTED CLIENTS ===="
|
|
usbsrv -list-clients
|
|
echo
|
|
read -rp "Enter device ID to share: " dev
|
|
read -rp "Enter client ID to connect to: " client
|
|
fi
|
|
[ -n "$dev" ] || err "no device ID given"
|
|
[ -n "$client" ] || err "no client ID given"
|
|
log "Sharing device ID $dev..."
|
|
usbsrv -share "$dev"
|
|
log "Connecting device $dev to client $client..."
|
|
usbsrv -connect-to "${client}-${dev}"
|
|
}
|
|
|
|
cmd_unshare() {
|
|
local dev="${1:-}"
|
|
if [ -z "$dev" ]; then
|
|
cmd_ls_shared
|
|
read -rp "Enter device ID to unshare: " dev
|
|
fi
|
|
[ -n "$dev" ] || err "no device ID given"
|
|
log "Unsharing device ID $dev..."
|
|
usbsrv -unshare "$dev"
|
|
}
|
|
|
|
cmd_auto_share() {
|
|
local mode="${1:-}"
|
|
if [ -z "$mode" ]; then
|
|
read -rp "Turn auto-share on or off? (on/off): " mode
|
|
fi
|
|
case "$mode" in
|
|
on|off) ;;
|
|
*) err "Invalid option '$mode' — must be 'on' or 'off'" ;;
|
|
esac
|
|
usbsrv -auto-share "$mode"
|
|
log "Auto-share set to $mode"
|
|
}
|
|
|
|
cmd_callback() {
|
|
local addr="${1:-}"
|
|
if [ -z "$addr" ]; then
|
|
read -rp "Enter client address:port for callback (e.g. 192.168.1.100:32032): " addr
|
|
fi
|
|
[ -n "$addr" ] || err "no address given"
|
|
usbsrv -create-callback "$addr"
|
|
}
|
|
|
|
cmd_close_callback() {
|
|
local target="${1:-}"
|
|
if [ -z "$target" ]; then
|
|
usbsrv -list-clients
|
|
read -rp "Enter client or clientid or 'all' to close callback: " target
|
|
fi
|
|
[ -n "$target" ] || err "no target given"
|
|
usbsrv -close-callback "$target"
|
|
}
|
|
|
|
cmd_auto_connect() {
|
|
local mode="${1:-}" client="${2:-}"
|
|
if [ -z "$mode" ]; then
|
|
read -rp "Enable or disable remote auto-connect? (on/off): " mode
|
|
fi
|
|
case "$mode" in
|
|
on|off) ;;
|
|
*) err "Invalid option '$mode' — must be 'on' or 'off'" ;;
|
|
esac
|
|
if [ -z "$client" ]; then
|
|
usbsrv -list-clients
|
|
read -rp "Enter client or clientid: " client
|
|
fi
|
|
[ -n "$client" ] || err "no client given"
|
|
usbsrv -remote-auto-connect "$mode" "$client"
|
|
}
|
|
|
|
cmd_disconnect() {
|
|
local dev="${1:-}"
|
|
if [ -z "$dev" ]; then
|
|
cmd_ls_shared
|
|
read -rp "Enter device ID or 'all' to disconnect from clients: " dev
|
|
fi
|
|
[ -n "$dev" ] || err "no device ID given"
|
|
usbsrv -disconnect-from "$dev"
|
|
}
|
|
|
|
cmd_nickname() {
|
|
local dev="${1:-}" nick="${2:-}"
|
|
if [ -z "$dev" ]; then
|
|
usbsrv -list-devices
|
|
read -rp "Enter device ID to set nickname (empty to remove): " dev
|
|
read -rp "Enter nickname (or leave empty to remove): " nick
|
|
fi
|
|
[ -n "$dev" ] || err "no device ID given"
|
|
usbsrv -set-nickname "$nick" "$dev"
|
|
}
|
|
|
|
cmd_timeout() {
|
|
local dev="${1:-}" sec="${2:-}"
|
|
if [ -z "$dev" ]; then
|
|
usbsrv -list-devices
|
|
read -rp "Enter device ID to set timeout for: " dev
|
|
read -rp "Enter timeout in seconds (0 to disable): " sec
|
|
fi
|
|
[ -n "$dev" ] || err "no device ID given"
|
|
[[ "$sec" =~ ^[0-9]+$ ]] || err "Invalid timeout '$sec' — must be a number"
|
|
usbsrv -set-timeout "$sec" "$dev"
|
|
}
|
|
|
|
cmd_port() {
|
|
local port="${1:-}"
|
|
if [ -z "$port" ]; then
|
|
read -rp "Enter new TCP port: " port
|
|
fi
|
|
[[ "$port" =~ ^[0-9]+$ ]] || err "Invalid port number '$port'"
|
|
usbsrv -set-tcp-port "$port"
|
|
warn "Restart your server for port changes to take effect."
|
|
}
|
|
|
|
command -v usbsrv &>/dev/null \
|
|
|| err "usbsrv not found — install the USB Redirector server (https://www.incentivespro.com/usb-server.html)"
|
|
|
|
# ── Interactive menu flows ─────────────────────────────────────
|
|
# Picker-first prompting: parsed listings feed the filtered picker; when the
|
|
# server is unreachable or nothing parses, fall back to the same raw-listing +
|
|
# manual-entry prompts the explicit commands have always used.
|
|
menu_pick_from_records() { # <prompt> <records-cmd...> — stdout: chosen ID · rc 1 cancel/fallback
|
|
local prompt="$1"; shift
|
|
local idx rec
|
|
local -a ids=()
|
|
if recs="$("$@")" && [ -n "$recs" ]; then
|
|
while IFS= read -r rec; do
|
|
[ -n "$rec" ] || continue
|
|
ids+=("${rec%%|*}")
|
|
done <<<"$recs"
|
|
if [ "${#ids[@]}" -gt 0 ]; then
|
|
if idx="$(share_pick "$prompt" "${ids[@]}")"; then
|
|
echo "${ids[$((idx - 1))]}"
|
|
return 0
|
|
fi
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
menu_share() {
|
|
local dev client
|
|
if ! dev="$(menu_pick_from_records "Share which USB device?" share_usb_devices)"; then
|
|
cmd_ls >/dev/null 2>&1 || true
|
|
read -rp "Enter device ID to share: " dev
|
|
[ -n "$dev" ] || { warn "Cancelled"; return 1; }
|
|
fi
|
|
if ! client="$(menu_pick_from_records "Connect to which client?" share_usb_clients)"; then
|
|
read -rp "Enter client ID to connect to: " client
|
|
[ -n "$client" ] || { warn "Cancelled"; return 1; }
|
|
fi
|
|
cmd_share "$dev" "$client"
|
|
}
|
|
|
|
menu_unshare() {
|
|
local dev
|
|
if ! dev="$(menu_pick_from_records "Stop sharing which device?" share_usb_devices)"; then
|
|
cmd_ls_shared >/dev/null 2>&1 || true
|
|
read -rp "Enter device ID to unshare: " dev
|
|
[ -n "$dev" ] || { warn "Cancelled"; return 1; }
|
|
fi
|
|
cmd_unshare "$dev"
|
|
}
|
|
|
|
menu_callback() {
|
|
local addr
|
|
addr="$(share_ask_value "Client address:port for callback (e.g. 192.168.1.100:32032)")" || return 1
|
|
[ -n "$addr" ] || { warn "Cancelled"; return 1; }
|
|
cmd_callback "$addr"
|
|
}
|
|
|
|
menu_close_callback() {
|
|
local target
|
|
if ! target="$(menu_pick_from_records "Close callback of which client?" share_usb_clients)"; then
|
|
target="$(share_ask_value "Client, client id or 'all' to close callback")" || return 1
|
|
[ -n "$target" ] || { warn "Cancelled"; return 1; }
|
|
fi
|
|
cmd_close_callback "$target"
|
|
}
|
|
|
|
menu_auto_connect() {
|
|
local mode client
|
|
mode="$(share_ask_value "Remote auto-connect on or off?" "on")" || return 1
|
|
case "$mode" in on|off) ;; *) warn "must be on/off"; return 1 ;; esac
|
|
if ! client="$(menu_pick_from_records "Toggle auto-connect for which client?" share_usb_clients)"; then
|
|
client="$(share_ask_value "Client or client id")" || return 1
|
|
[ -n "$client" ] || { warn "Cancelled"; return 1; }
|
|
fi
|
|
cmd_auto_connect "$mode" "$client"
|
|
}
|
|
|
|
menu_disconnect() {
|
|
local dev
|
|
if ! dev="$(menu_pick_from_records "Disconnect which device?" share_usb_devices)"; then
|
|
dev="$(share_ask_value "Device ID or 'all' to disconnect from clients")" || return 1
|
|
[ -n "$dev" ] || { warn "Cancelled"; return 1; }
|
|
fi
|
|
cmd_disconnect "$dev"
|
|
}
|
|
|
|
menu_nickname() {
|
|
local dev nick
|
|
if ! dev="$(menu_pick_from_records "Nickname which device?" share_usb_devices)"; then
|
|
dev="$(share_ask_value "Device ID")" || return 1
|
|
[ -n "$dev" ] || { warn "Cancelled"; return 1; }
|
|
fi
|
|
nick="$(share_ask_value "Nickname (empty removes it)")" || return 1
|
|
cmd_nickname "$dev" "$nick"
|
|
}
|
|
|
|
menu_timeout() {
|
|
local dev sec
|
|
if ! dev="$(menu_pick_from_records "Set inactivity timeout for which device?" share_usb_devices)"; then
|
|
dev="$(share_ask_value "Device ID")" || return 1
|
|
[ -n "$dev" ] || { warn "Cancelled"; return 1; }
|
|
fi
|
|
sec="$(share_ask_value "Timeout in seconds (0 disables)" "0")" || return 1
|
|
cmd_timeout "$dev" "$sec"
|
|
}
|
|
|
|
menu_port() {
|
|
local port
|
|
port="$(share_ask_value "New TCP port")" || return 1
|
|
[ -n "$port" ] || { warn "Cancelled"; return 1; }
|
|
cmd_port "$port"
|
|
}
|
|
|
|
run_menu() {
|
|
share_menu_guard || exit 1
|
|
while true; do
|
|
local choice
|
|
choice="$(share_menu_run "USB Redirector server" \
|
|
"List host devices and connected clients" \
|
|
"Share a device with a client" \
|
|
"Stop sharing a device" \
|
|
"Disconnect device(s) from clients" \
|
|
"Auto-share on/off" \
|
|
"Create a callback connection" \
|
|
"Close a client callback" \
|
|
"Client remote auto-connect on/off" \
|
|
"Set a device nickname" \
|
|
"Set a device inactivity timeout" \
|
|
"Set the TCP port")" || return 0
|
|
case "$choice" in
|
|
1) cmd_ls ;;
|
|
2) menu_share ;;
|
|
3) menu_unshare ;;
|
|
4) menu_disconnect ;;
|
|
5) cmd_auto_share ;;
|
|
6) menu_callback ;;
|
|
7) menu_close_callback ;;
|
|
8) menu_auto_connect ;;
|
|
9) menu_nickname ;;
|
|
10) menu_timeout ;;
|
|
11) menu_port ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
cmd="${1:-}"
|
|
|
|
case "$cmd" in
|
|
-h|--help) usage ;;
|
|
esac
|
|
|
|
case "$cmd" in
|
|
""|menu|--ls|--ls-shared|--share|--unshare|--auto-share|--callback|--close-callback|--auto-connect|--disconnect|--nickname|--timeout|--port|--info|--version) ;;
|
|
*) err "Unknown flag '$cmd'" ;;
|
|
esac
|
|
|
|
case "$cmd" in
|
|
""|menu) run_menu ;;
|
|
--ls) cmd_ls ;;
|
|
--ls-shared) cmd_ls_shared ;;
|
|
--share) shift; cmd_share "$@" ;;
|
|
--unshare) shift; cmd_unshare "$@" ;;
|
|
--auto-share) shift; cmd_auto_share "$@" ;;
|
|
--callback) shift; cmd_callback "$@" ;;
|
|
--close-callback) shift; cmd_close_callback "$@" ;;
|
|
--auto-connect) shift; cmd_auto_connect "$@" ;;
|
|
--disconnect) shift; cmd_disconnect "$@" ;;
|
|
--nickname) shift; cmd_nickname "$@" ;;
|
|
--timeout) shift; cmd_timeout "$@" ;;
|
|
--port) shift; cmd_port "$@" ;;
|
|
--info) usbsrv -info ;;
|
|
--version) usbsrv -version ;;
|
|
*) err "Unknown flag '$cmd'"; usage ;;
|
|
esac
|