diff --git a/README.md b/README.md
index 8f8da78..9e3b7e2 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,6 @@
### Dependencies
-* zenity (unless called with option `--no-gui`)
* jq
* Audio subsystem:
- PulseAudio or
diff --git a/pulseaudio-tcp b/pulseaudio-tcp
index 20e531e..8f8e4c6 100644
--- a/pulseaudio-tcp
+++ b/pulseaudio-tcp
@@ -17,144 +17,81 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+##
+# Arguments
+
+operation=$1
+
+##
+# Configuration
+
+config_dir="$HOME"/.config/pulseaudio-tcp
+config="$config_dir"/config.inc.sh
+
##
# Functions
-usage() {
- cat >&2 <&2
-
- if [[ -t 1 ]] && $gui && [[ -n $(type -p zenity) ]] ; then
- case "$level" in
- ERROR)
- zenity --error --text="$msg"
- ;;
- *)
- zenity --info --text="$msg"
- ;;
- esac
- fi
-}
-
-error() {
- log ERROR "$@"
-}
-
-info() {
- log INFO "$@"
-}
-
-debug() {
- if "$debug" ; then
- log DEBUG "$@"
- else
- gui=false log DEBUG "$@"
- fi
-}
-
_ssh() {
if ssh -S "$USER"-pulseaudio "$remote_user"@"$remote_ip" "$@" ; then
return 0
else
- error "SSH remote_ip=$remote_ip failed."
+ echo "ERROR: SSH remote_ip=$remote_ip failed." >&2
return 1
fi
}
-question_yesno() {
- question=$*
-
- if "$gui" ; then
- if zenity --question --text="$question" ; then
- return 0
- else
- return 1
- fi
- else
- while read -r -p "$question [y|n] " answer ; do
- case $answer in
- [yY])
- return 0
- ;;
- [nN])
- return 1
- ;;
- esac
- done
- fi
-}
-
-question_input() {
- title=$1
- question=$2
- value=$3
-
- if $gui ; then
- zenity --entry --title="$title" --text="$question" --entry-text="$value"
- else
- echo "$title" >&2
- read -r -p "$question ($value)" answer
- [[ -z $answer ]] && answer=$value
- echo "$answer"
- fi
-}
-
# Perform setup operation
do_setup() {
if test -e "$config" ; then
- if question_yesno "$config already exists, overwrite it?"; then
- if ! test -w "$config" ; then
- error "$config is not writable."
- return 1 ;
- elif ! test -r "$config" ; then
- error "$config is not readable."
- return 1 ;
- else
- . "$config"
- fi
- else
- error "Setup aborted."
+ read -r -p "config=$config already exists, overwrite it? (Y|n) " answer
+
+ case "$answer" in
+ y|Y|"")
+ if ! test -w "$config" ; then
+ echo "ERROR: config=$config is not writable." >&2 ;
+ return 1 ;
+ elif ! test -r "$config" ; then
+ echo "ERROR: config=$config is not readable." >&2 ;
+ return 1 ;
+ else
+ . "$config"
+ fi
+ ;;
+ *)
+ echo "ERROR: Setup aborted." >&2
return 1
- fi
+ esac
elif ! test -e "$config_dir" ; then
mkdir -p "$config_dir" || {
- error "Could not create $config_dir"
+ echo "ERROR: Could not create config_dir=$config_dir." >&2 ;
return 1 ;
}
elif ! test -d "$config_dir" ; then
- error "$config_dir is not a directory"
+ echo "ERROR: config_dir=$config_dir is not a directory." >&2
return 1
elif ! test -w "$config_dir" ; then
- error "$config_dir is not writable"
+ echo "ERROR: config_dir=$config_dir is not writable." >&2
return 1
fi
while true ; do
- if remote_ip_in=$(question_input "Set remote host" "Enter name or IP address of remote host:" "$remote_ip") ; then
+ read -r -p "Enter IP address of remote host ($remote_ip): " remote_ip_in
+
+ if test -n "$remote_ip_in" ; then
+ break
+ elif test -n "$remote_ip" ; then
+ remote_ip_in=$remote_ip
break
fi
done
while true ; do
- if remote_user_in=$(question_input "Set remote user" "Enter username on remote host:" "$remote_user") ; then
+ read -r -p "Enter username on remote host $remote_ip ($remote_user): " remote_user_in
+
+ if test -n "$remote_user_in" ; then
+ break
+ elif test -n "$remote_user" ; then
+ remote_user_in=$remote_user
break
fi
done
@@ -162,25 +99,65 @@ do_setup() {
inbound=${inbound:-true}
while true ; do
- if question_yesno "Enable inbound audio?"; then
- inbound_in=true
- break
+ if "$inbound" ; then
+ prompt="Y/n"
else
- inbound_in=false
- break
+ prompt="y/N"
fi
+
+ read -r -p "Enable inbound audio ($prompt): " inbound_in
+
+ case "$inbound_in" in
+ "")
+ if test -n "$inbound" ; then
+ inbound_in=$inbound
+ break 2
+ fi
+ ;;
+ y|Y)
+ inbound_in=true
+ break 2
+ ;;
+ n|N)
+ inbound_in=false
+ break 2
+ ;;
+ *)
+ echo "ERROR: Please type \"y\" or \"n\"." >&2
+ ;;
+ esac
done
outbound=${outbound:-true}
while true ; do
- if question_yesno "Enable outbound audio?"; then
- outbound_in=true
- break
+ if "$outbound" ; then
+ prompt="Y/n"
else
- outbound_in=false
- break
+ prompt="y/N"
fi
+
+ read -r -p "Enable outbound audio ($prompt): " outbound_in
+
+ case "$outbound_in" in
+ "")
+ if test -n "$outbound" ; then
+ outbound_in=$outbound
+ break 2
+ fi
+ ;;
+ y|Y)
+ outbound_in=true
+ break 2
+ ;;
+ n|N)
+ outbound_in=false
+ break 2
+ ;;
+ *)
+ echo "ERROR: Please type \"y\" or \"n\"." >&2
+ ;;
+ esac
done
cat > "$config" << EOF
@@ -215,40 +192,37 @@ check_pa_ssh() {
# Perform status operation
do_status() {
rv=0
- errors=()
if ! check_pa_ssh ; then
rv=1
fi
if ! _ssh pactl list modules | grep -Fq "Name: module-native-protocol-tcp" ; then
- errors+=("PulseAudio module \"module-native-protocol-tcp\" is not loaded on remote host $remote_ip.")
+ echo "ERROR: PulseAudio module \"module-native-protocol-tcp\" is not loaded on remote_ip=$remote_ip." >&2
rv=1
fi
if "$outbound" ; then
if ! pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then
- errors+=("PulseAudio module \"module-tunnel-sink\" is not loaded.")
+ echo "ERROR: PulseAudio module \"module-tunnel-sink\" is not loaded." >&2
rv=1
fi
if ! pactl get-default-sink | grep -Fq "tunnel-sink.tcp:127.0.0.1" ; then
- errors+=("\"tunnel-sink.tcp:127.0.0.1\" is not the default PulseAudio sink.")
+ echo "ERROR: \"tunnel-sink.tcp:127.0.0.1\" is not the default PulseAudio sink." >&2
rv=1
fi
fi
if "$inbound" ; then
if ! pactl list modules | grep -Fq "Name: module-tunnel-source" ; then
- errors+=("PulseAudio module \"module-tunnel-source\" is not loaded.")
+ echo "ERROR: PulseAudio module \"module-tunnel-source\" is not loaded." >&2
rv=1
fi
fi
- if [[ $rv -eq 0 ]] ; then
- info "All checks passed; pulseaudio-tcp status is okay."
- else
- error "pulseaudio-tcp status is not okay: ${errors[*]}"
+ if test "$rv" -eq 0 ; then
+ echo "INFO: All checks passed; pulseaudio-tcp status is okay." >&2
fi
return "$rv"
@@ -257,10 +231,10 @@ do_status() {
# Acquire PulseAudio cookie from remote host
sync_pa_cookie() {
if scp -q "$remote_user"@"$remote_ip":.config/pulse/cookie ~/.config/pulse/cookie ; then
- debug "Synced PulseAudio cookie from remote host $remote_ip."
+ echo "INFO: Synced PulseAudio cookie from remote_ip=$remote_ip." >&2
return 0
else
- error "Unable to sync PulseAudio cookie from remote host $remote_ip."
+ echo "ERROR: Unable to sync PulseAudio cookie from remote_ip=$remote_ip." >&2
return 1
fi
}
@@ -273,13 +247,13 @@ establish_ssh_portforward() {
# Enable PulseAudio TCP tunnel server on remote host
enable_remote_pa_tunnel_server() {
if _ssh pactl list modules | grep -Fq "Name: module-native-protocol-tcp" ; then
- debug "PulseAudio module \"module-native-protocol-tcp\" already loaded on remote host $remote_ip."
+ echo "INFO: PulseAudio module \"module-native-protocol-tcp\" already loaded on remote_ip=$remote_ip." >&2
return 0
elif _ssh pactl load-module module-native-protocol-tcp listen=127.0.0.1 auth-ip-acl=127.0.0.1 ; then
- debug "Loaded PulseAudio module \"module-native-protocol-tcp\" on remote host $remote_ip."
+ echo "INFO: Loaded PulseAudio module \"module-native-protocol-tcp\" on remote_ip=$remote_ip." >&2
return 0
else
- error "Unable to load PulseAudio module \"module-native-protocol-tcp\" on remote host $remote_ip."
+ echo "ERROR: Unable to load PulseAudio module \"module-native-protocol-tcp\" on remote_ip=$remote_ip." >&2
return 1
fi
}
@@ -287,13 +261,13 @@ enable_remote_pa_tunnel_server() {
# Enable tunnel sink on local host
enable_local_pa_tunnel_sink() {
if pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then
- debug "PulseAudio module \"module-tunnel-sink\" already loaded."
+ echo "INFO: PulseAudio module \"module-tunnel-sink\" already loaded." >&2
return 0
elif pactl load-module module-tunnel-sink server=tcp:127.0.0.1 ; then
- debug "Loaded PulseAudio module \"module-tunnel-sink\"."
+ echo "INFO: Loaded PulseAudio module \"module-tunnel-sink\"." >&2
return 0
else
- error "Unable to load PulseAudio module \"module-tunnel-sink\"."
+ echo "ERROR: Unable to load PulseAudio module \"module-tunnel-sink\"." >&2
return 1
fi
}
@@ -301,13 +275,13 @@ enable_local_pa_tunnel_sink() {
# Enable tunnel source on local host
enable_local_pa_tunnel_source() {
if pactl list modules | grep -Fq "Name: module-tunnel-source" ; then
- debug "PulseAudio module \"module-tunnel-source\" already loaded."
+ echo "INFO: PulseAudio module \"module-tunnel-source\" already loaded." >&2
return 0
elif pactl load-module module-tunnel-source server=tcp:127.0.0.1 ; then
- debug "Loaded PulseAudio module \"module-tunnel-source\"."
+ echo "INFO: Loaded PulseAudio module \"module-tunnel-source\"." >&2
return 0
else
- error "Unable to load PulseAudio module \"module-tunnel-source\"."
+ echo "ERROR: Unable to load PulseAudio module \"module-tunnel-source\"." >&2
return 1
fi
}
@@ -315,10 +289,10 @@ enable_local_pa_tunnel_source() {
# Set tunnel sink as default sink on local host
set_local_pa_tunnel_sink_as_default() {
if pactl set-default-sink tunnel-sink.tcp:127.0.0.1 ; then
- debug "Set \"tunnel-sink.tcp:127.0.0.1\" as default PulseAudio sink."
+ echo "INFO: Set \"tunnel-sink.tcp:127.0.0.1\" as default PulseAudio sink." >&2
return 0
else
- error "Failed to set \"tunnel-sink.tcp:127.0.0.1\" as default PulseAudio sink."
+ echo "ERROR: Failed to set \"tunnel-sink.tcp:127.0.0.1\" as default PulseAudio sink." >&2
return 1
fi
}
@@ -331,7 +305,6 @@ do_start() {
if "$outbound" ; then
enable_local_pa_tunnel_sink || return 1
- # workaround, local tunnel is not available immediately
sleep 1
set_local_pa_tunnel_sink_as_default || return 1
fi
@@ -346,10 +319,10 @@ do_start() {
# Remove PulseAudio TCP tunnel sink on local host
remove_local_pa_tunnel_sink() {
if ! pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then
- debug " PulseAudio module \"module-tunnel-sink\" is not loaded."
+ echo "INFO: PulseAudio module \"module-tunnel-sink\" is not loaded." >&2
return 0
elif ! pactl list sinks | grep -Fq "tunnel-sink.tcp:127.0.0.1" ; then
- debug "No PulseAudio tunnel sink to 127.0.0.1 exists."
+ echo "INFO: No PulseAudio tunnel sink to 127.0.0.1 exists." >&2
return 0
else
owner_module=$(
@@ -358,10 +331,10 @@ remove_local_pa_tunnel_sink() {
)
if ! pactl unload-module "$owner_module" ; then
- error "Could not unload owner module $owner_module of PulseAudio sink \"tunnel-sink.tcp:127.0.0.1\"."
+ echo "ERROR: Could not unload owner module $owner_module of PulseAudio sink \"tunnel-sink.tcp:127.0.0.1\"." >&2
return 1
else
- debug "Unloaded owner module $owner_module of PulseAudio sink \"tunnel-sink.tcp:127.0.0.1\"."
+ echo "INFO: Unloaded owner module $owner_module of PulseAudio sink \"tunnel-sink.tcp:127.0.0.1\"." >&2
return 0
fi
fi
@@ -370,10 +343,10 @@ remove_local_pa_tunnel_sink() {
# Remove PulseAudio TCP tunnel source on local host
remove_local_pa_tunnel_source() {
if ! pactl list modules | grep -Fq "Name: module-tunnel-source" ; then
- debug "PulseAudio module \"module-tunnel-source\" is not loaded."
+ echo "INFO: PulseAudio module \"module-tunnel-source\" is not loaded." >&2
return 0
elif ! pactl list sources | grep -Fq "tunnel-source.tcp:127.0.0.1" ; then
- debug "No PulseAudio tunnel source from 127.0.0.1 exists."
+ echo "INFO: No PulseAudio tunnel source from 127.0.0.1 exists." >&2
return 0
else
owner_module=$(
@@ -382,10 +355,10 @@ remove_local_pa_tunnel_source() {
)
if ! pactl unload-module "$owner_module" ; then
- error "Could not unload owner module $owner_module of PulseAudio source \"tunnel-source.tcp:127.0.0.1\"."
+ echo "ERROR: Could not unload owner module $owner_module of PulseAudio source \"tunnel-source.tcp:127.0.0.1\"." >&2
return 1
else
- debug "Unloaded owner module $owner_module of PulseAudio source \"tunnel-source.tcp:127.0.0.1\"."
+ echo "INFO: Unloaded owner module $owner_module of PulseAudio source \"tunnel-source.tcp:127.0.0.1\"." >&2
return 0
fi
fi
@@ -394,13 +367,13 @@ remove_local_pa_tunnel_source() {
# Stop PulseAudio TCP tunnel server on remote host.
disable_remote_pa_tunnel_server() {
if ! _ssh pactl list modules | grep -Fq "Name: module-native-protocol-tcp" ; then
- debug "PulseAudio module \"module-native-protocol-tcp\" not loaded on remote_ip=$remote_ip."
+ echo "INFO: PulseAudio module \"module-native-protocol-tcp\" not loaded on remote_ip=$remote_ip." >&2
return 0
elif ! _ssh pactl unload-module module-native-protocol-tcp ; then
- error "Could not unload PulseAudio module \"module-native-protocol-tcp\" on remote host $remote_ip."
+ echo "ERROR: Could not unload PulseAudio module \"module-native-protocol-tcp\" on remote_ip=$remote_ip." >&2
return 1
else
- debug "Unloaded PulseAudio module \"module-native-protocol-tcp\" on remote host $remote_ip."
+ echo "INFO: Unloaded PulseAudio module \"module-native-protocol-tcp\" on remote_ip=$remote_ip." >&2
return 0
fi
}
@@ -430,93 +403,58 @@ do_stop() {
return 0
}
-##
-# Arguments
-
-for arg in "$@" ; do
- case "$arg" in
- --debug)
- debug_cmdline=true
- ;;
- --no-gui)
- gui_cmdline=false
- ;;
- --help)
- help_cmdline=true
- ;;
- *)
- operation=$arg
- ;;
- esac
-done
-
-##
-# Configuration
-
-config_dir="$HOME"/.config/pulseaudio-tcp
-config=$config_dir/config.inc.sh
-
-if [[ $help_cmdline = true ]] ; then
- usage
- exit 0
-fi
-
-if [[ $debug_cmdline = true ]] ; then
- debug=true
-else
- debug=false
-fi
-
-if [[ $gui_cmdline = false ]] ; then
- gui=false
-else
- gui=true
-fi
-
##
# Main Program
rv=0
if test "$operation" = setup ; then
- info "Entering setup mode ..."
+ echo "Entering setup mode ..."
else
if ! test -e "$config" ; then
- error "Configfile $config does not exist (use \"$0 setup\" first)."
+ echo "ERROR: Configfile $config does not exist (use \"$0 setup\" first)." >&2
rv=1
- elif ! test -r "$config" ; then
- error "Configfile $config is not readable."
+ elif ! test -r "$config" ; then
+ echo "ERROR: Configfile $config is not readable." >&2
rv=1
elif ! test -f "$config" ; then
- error "Configfile $config is not a regular file."
+ echo "ERROR: Configfile $config is not a regular file." >&2
rv=1
else
. "$config"
- if [[ -z $remote_ip ]] ; then
- error "\"remote_ip=\" not set in configfile $config."
+ if test -z "$remote_ip" ; then
+ echo "ERROR: \"remote_ip=\" not set in configfile $config." >&2
rv=1
- elif [[ -z $remote_user ]] ; then
- error "\"remote_user=\" not set in configfile $config."
+ elif test -z "$remote_user" ; then
+ echo "ERROR: \"remote_user=\" not set in configfile $config." >&2
rv=1
fi
fi
- required_cmds=( jq pactl ssh )
- "$gui" && required_cmds+=( zenity )
-
- for exe in "${required_cmds[@]}" ; do
- if [[ -z $(type -p "$exe") ]] ; then
- error "Required executable \"$exe\" not found."
- rv=1
- fi
- done
+ if test -z "$(which jq)" ; then
+ echo "ERROR: Required executable \"jq\" not found." >&2
+ rv=1
+ elif test -z "$(which pactl)" ; then
+ echo "ERROR: Required executable \"pactl\" not found." >&2
+ rv=1
+ elif test -z "$(which ssh)" ; then
+ echo "ERROR: Required executable \"ssh\" not found." >&2
+ rv=1
+ fi
fi
-if [[ $rv -ne 0 ]] ; then
- error "Preliminary checks failed, skipping operation."
+if test "$rv" -ne 0 ; then
+ echo "ERROR: Preliminary checks failed, skipping operation." >&2
else
case "$operation" in
+ ""|-h|--help)
+ cat << EOF
+Setup and run encrypted connection to remote PulseAudio/Pipewire server
+Usage: $0 restart|setup|start|status|stop
+EOF
+ rv=0
+ ;;
setup)
do_setup
rv=$?
@@ -539,10 +477,11 @@ else
rv=$?
;;
*)
- usage
+ echo "ERROR: Usage: $0 restart|setup|start|status|stop" >&2
rv=1
;;
esac
fi
exit "$rv"
+