#1036912 pipewire-pulse: "address already in use" when enabling tcp server due to race condition on user switching

Package:
pipewire-pulse
Source:
pipewire-pulse
Description:
PipeWire PulseAudio daemon
Submitter:
Micha Moskovic
Date:
2023-07-01 19:33:05 UTC
Severity:
normal
#1036912#5
Date:
2023-05-29 10:59:34 UTC
From:
To:
Dear Maintainer,

I want to have pipewire-pulse listen on a TCP socket in order to be able
to play music from an mpd deamon running as a different user, which is
configured to use pulseaudio output on localhost (there might be a
better way to play sound from a different user now with pipewire but
couldn't find anything conclusive online).

I enabled it by modifying the example configuration and setting the
following in /etc/pipewire/pipewire-pulse.conf:

pulse.properties = {
    # the addresses this server listens on
    server.address = [
        "unix:native"
        "tcp:127.0.0.1:4713"               # IPv4 on a single address
    ]
# [more default config]
}

The problem is that when the "pipewire-pulse" service starts as part of
the session of the interactive user, it can't bind to this port. The
following is displayed in the logs for the service:

mai 29 11:43:55 baraddur systemd[2281]: Started pipewire-pulse.service - PipeWire PulseAudio.
mai 29 11:43:55 baraddur pipewire-pulse[2320]: mod.rt: Can't find org.freedesktop.portal.Desktop. Is xdg-desktop-portal running?
mai 29 11:43:55 baraddur pipewire-pulse[2320]: mod.rt: found session bus but no portal
mai 29 11:43:55 baraddur pipewire-pulse[2320]: mod.protocol-pulse: server 0x5580e6aa9160: bind() failed: Adresse déjà utilisée
mai 29 11:43:55 baraddur pipewire-pulse[2320]: mod.protocol-pulse: pulse-server 0x5580e6aa8ab0: failed to start server on 'tcp:127.0.0.1:4713': Adresse déjà util>

("Adresse déjà utilisée" is French for "address already in use").

Manually restarting the service with "systemctl --user restart
pipewire-pulse.service" fixes the issue and the service starts without
any warnings or errors.

I strongly suspect that this is due to a race condition with the
pipewire-pulse server of the gdm display manager. Indeed, if right after
starting the computer computer I open a shell on a different tty instead
of my graphical GNOME session and run "lsof -n -i :4713", I see that
"pipewire-pulse" is already listening on that port for user
"Debian-gdm". Presumably, it is still running while the user service
starts, preventing it from binding to the port.

Would there be any way to fix that?

Best regards,
Micha

#1036912#10
Date:
2023-06-15 19:08:33 UTC
From:
To:
Dear Maintainer,

I can confirm this bug too.
The thing is.. I'm using KDE Plasma and sddm to log in not gdm.
I can confirm pipewire-pulse works as expected if you restart it with:

systemctl --user restart pipewire-pulse.service

As a workaround I had been using:

pactl load-module module-native-protocol-tcp

Which also solves the problem until next reboot.
mpd work fines using any of the workarounds.

In my case using lsof -n -i :4713 in another TTY before login produces:

COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
pipewire- 1523 sddm   15u  IPv4  20934      0t0  TCP *:4713 (LISTEN)
pipewire- 1523 sddm   16u  IPv6  20935      0t0  TCP *:4713 (LISTEN)


systemctl --user status pipewire-pulse.service

Produces:

pipewire-pulse.service - PipeWire PulseAudio
     Loaded: loaded (/usr/lib/systemd/user/pipewire-pulse.service; enabled; preset: enabled)
     Active: active (running) since Wed 2023-06-14 11:28:23 -05; 18min ago
TriggeredBy: ● pipewire-pulse.socket
   Main PID: 2080 (pipewire-pulse)
      Tasks: 2 (limit: 76966)
     Memory: 17.9M
        CPU: 156ms
     CGroup: /user.slice/user-1000.slice/user@1000.service/session.slice/pipewire-pulse.service
             └─2080 /usr/bin/pipewire-pulse

Jun 14 11:28:23 harpia systemd[2061]: Started pipewire-pulse.service - PipeWire PulseAudio.
Jun 14 11:28:23 harpia pipewire-pulse[2080]: mod.rt: Can't find org.freedesktop.portal.Desktop. Is xdg-desktop-portal running?
Jun 14 11:28:23 harpia pipewire-pulse[2080]: mod.rt: found session bus but no portal
Jun 14 11:28:23 harpia pipewire-pulse[2080]: mod.protocol-pulse: server 0x55f739398460: bind() failed: Address already in use
Jun 14 11:28:23 harpia pipewire-pulse[2080]: mod.protocol-pulse: pulse-server 0x55f739397bb0: failed to start server on 'tcp:0.0.0.0:4713': Address already in use
Jun 14 11:28:23 harpia pipewire-pulse[2080]: mod.protocol-pulse: server 0x55f7393988b0: bind() failed: Address already in use
Jun 14 11:28:23 harpia pipewire-pulse[2080]: mod.protocol-pulse: pulse-server 0x55f739397bb0: failed to start server on 'tcp:[::]:4713': Address already in use
~



Here is my /usr/share/pipewire/pipewire-pulse.conf just in case (although the file seems not to be involved in the bug).


# PulseAudio config file for PipeWire version "0.3.65" #
#
# Copy and edit this file in /etc/pipewire for system-wide changes
# or in ~/.config/pipewire for local changes.
#
# It is also possible to place a file with an updated section in
# /etc/pipewire/pipewire-pulse.conf.d/ for system-wide changes or in
# ~/.config/pipewire/pipewire-pulse.conf.d/ for local changes.
#

context.properties = {
    ## Configure properties in the system.
    #mem.warn-mlock  = false
    #mem.allow-mlock = true
    #mem.mlock-all   = false
    #log.level       = 2

    #default.clock.quantum-limit = 8192
}

context.spa-libs = {
    audio.convert.* = audioconvert/libspa-audioconvert
    support.*       = support/libspa-support
}

context.modules = [
    { name = libpipewire-module-rt
        args = {
            nice.level   = -11
            #rt.prio      = 88
            #rt.time.soft = -1
            #rt.time.hard = -1
        }
        flags = [ ifexists nofail ]
    }
    { name = libpipewire-module-protocol-native }
    { name = libpipewire-module-client-node }
    { name = libpipewire-module-adapter }
    { name = libpipewire-module-metadata }

    { name = libpipewire-module-protocol-pulse
        args = {
	    # contents of pulse.properties can also be placed here
	    # to have config per server.
        }
    }
]

# Extra scripts can be started here. Setup in default.pa can be moved in
# a script or in pulse.cmd below
context.exec = [
    #{ path = "pactl"        args = "load-module module-always-sink" }
    #{ path = "pactl"        args = "upload-sample my-sample.wav my-sample" }
    #{ path = "/usr/bin/sh"  args = "~/.config/pipewire/default.pw" }
]

# Extra commands can be executed here.
#   load-module : loads a module with args and flags
#      args = "<module-name> <module-args>"
#      flags = [ "no-fail" ]
pulse.cmd = [
    { cmd = "load-module" args = "module-always-sink" flags = [ ] }
    #{ cmd = "load-module" args = "module-switch-on-connect" }
    #{ cmd = "load-module" args = "module-gsettings" flags = [ "nofail" ] }
]

stream.properties = {
    #node.latency          = 1024/48000
    #node.autoconnect      = true
    #resample.quality      = 4
    #channelmix.normalize  = false
    #channelmix.mix-lfe    = true
    #channelmix.upmix      = true
    #channelmix.upmix-method = psd  # none, simple
    #channelmix.lfe-cutoff = 150
    #channelmix.fc-cutoff  = 12000
    #channelmix.rear-delay = 12.0
    #channelmix.stereo-widen = 0.0
    #channelmix.hilbert-taps = 0
    #dither.noise = 0
}

pulse.properties = {
    # the addresses this server listens on
    server.address = [
        "unix:native"
        #"unix:/tmp/something"              # absolute paths may be used
        #"tcp:4713"                   # IPv4 and IPv6 on all addresses
	#"tcp:[::]:9999"                    # IPv6 on all addresses
        # IPv4 on a single address
        #
        { address = "tcp:4713"             # address
        #  max-clients = 64                 # maximum number of clients
        #  listen-backlog = 32              # backlog in the server listen queue
          client.access = "allowed"     # permissions for clients
        }
    ]
    #pulse.min.req          = 256/48000     # 5ms
    #pulse.default.req      = 960/48000     # 20 milliseconds
    #pulse.min.frag         = 256/48000     # 5ms
    #pulse.default.frag     = 96000/48000   # 2 seconds
    #pulse.default.tlength  = 96000/48000   # 2 seconds
    #pulse.min.quantum      = 256/48000     # 5ms
    #pulse.idle.timeout     = 0             # don't pause after underruns
    #pulse.default.format   = F32
    #pulse.default.position = [ FL FR ]
    # These overrides are only applied when running in a vm.
    vm.overrides = {
        pulse.min.quantum = 1024/48000      # 22ms
    }
}

# client/stream specific properties
pulse.rules = [
    {
        matches = [
            {
                # all keys must match the value. ~ starts regex.
                #client.name                = "Firefox"
                #application.process.binary = "teams"
                #application.name           = "~speech-dispatcher.*"
            }
        ]
        actions = {
            update-props = {
                #node.latency = 512/48000
            }
            # Possible quirks:"
            #    force-s16-info                 forces sink and source info as S16 format
            #    remove-capture-dont-move       removes the capture DONT_MOVE flag
            #quirks = [ ]
        }
    }
    {
        # skype does not want to use devices that don't have an S16 sample format.
        matches = [
             { application.process.binary = "teams" }
             { application.process.binary = "teams-insiders" }
             { application.process.binary = "skypeforlinux" }
        ]
        actions = { quirks = [ force-s16-info ] }
    }
    {
        # firefox marks the capture streams as don't move and then they
        # can't be moved with pavucontrol or other tools.
        matches = [ { application.process.binary = "firefox" } ]
        actions = { quirks = [ remove-capture-dont-move ] }
    }
    {
        # speech dispatcher asks for too small latency and then underruns.
        matches = [ { application.name = "~speech-dispatcher.*" } ]
        actions = {
            update-props = {
                pulse.min.req          = 512/48000      # 10.6ms
                pulse.min.quantum      = 512/48000      # 10.6ms
                pulse.idle.timeout     = 5              # pause after 5 seconds of underrun
            }
        }
    }
]

#1036912#15
Date:
2023-07-01 19:23:33 UTC
From:
To:
Dear Maintainer,

I can confirm this bug as well, with lightdm instead of gdm or
sddm.

"lsof -n -i :4713" produces similar output to what
Messrs. Moskovic and Chaparro report, with lightdm (or
Debian-lightdm, not sure) as user.

Restarting pipewire-pulse manually with systemctl works, but only
when done manually after login is completed.

Trying to do it automatically as part of the login process (in
.xsessionrc, for instance) does not work : lightdm probably keeps
listening long enough for it not to work.

Best,

Aymeric