Linux Dreams and Nightmares

Stories from a Linux user's life who can't keep his fingers out of things he knows nothing about

During the pandemic I quickly realized that screen sharing, a feature crucially important for remote work, was something that didn't work out of the box in Wayland. I was too busy to invest a lot of time into looking into the problem so I just switched to X11 and forgot about it. If you found yourself in a similar situation and haven't figured it out so far, this is for you. When you know what to do, and you're not intimidated by sentences like “you will have to replace pulse-audio with pipewire”, the setup is actually quite straight forward. So here are all the steps involved in getting (full) screen-sharing and even OBS Studio to work on Wayland.

First of all: the usually so infallible ArchWiki let me down this time. They have instructions on how to set up screen sharing on Wayland but it simply doesn't work. I will however point to the Wiki, because it's only missing one – slightly intimidating – step. Replacing pulse-audio with pipewire. But having done that I can assure you: It's smooth as butter! My Bower & Wilkins PX headphones were supported out of the box with the AptX-HD Bluetooth audio codec; something that would require third party plugins with pulse. You won't actually notice any change after switching. Gnome sound settings, pavucontrol and pactl all continue to work like before. So that being said, let's get a move on!

There is a section in the ArchWiki page on Pipewire about WebRTC screen sharing. Follow the instructions there and come back here after having done so. Next, install pipewire-pulse and confirm that you want to replace pulse-audio. You might end up with a file /etc/pipewire/media-session/media-session.conf.pacnew. Make sure to move it to /etc/pipewire/media-session/media-session.conf or you won't find any sound devices. Start and enable the Pipewire sockets: systemctl --user enable --now pipewire{,-pulse}.socket. Give it a shot! Open up https://meet.jit.si and start a screen sharing session (assuming you followed the wiki and set the chrome://flags/#enable-webrtc-pipewire-capturer setting if you're using chrom(e|ium)).

OBS Studio

OBS Studio 27.0 rc2 has Pipewire support! You can install it from source (or the AUR as obs-studio-git). You will need to pass it the command line option --platform wayland, or you won't see the new capture input. Copy /usr/share/applications/com.obsproject.Studio.desktop to ~/.local/share/applications, add the command line option to Exec= and you should be good to go!

Thanks

Thanks to Rasi and hashworks at #archlinux.de for helping me figure most of this stuff out!

#wayland #linux

I've been a fan of MPD for many years but abandoned my setup after moving my entire music library to a remote server. Recently I discovered that MPD can be run in a “Satellite Setup”, which allows maintaining a central database and connecting other MPD instances to it. In this post I will describe how I've set it up and what my experience has been like.

#linux #linuxaudio #mpd #music

As mentioned above, an MPD “Satellite Setup” consists of a server, the single source of truth, and one or more satellite MPD instances that access the server. In my setup the server isn't used as a playback device (because it's in a datacenter), but you could run the server instance on your NAS at home and hook it up to your stereo. In either case the setup is the same.

So how does it work? Your server runs a normal MPD instance: you setup the config as you would normally and point it to your music library. But what about the satellites? The satellites relay all database queries to the server using the “proxy” database plugin. So far, so good, but what about the actual music files? These have to be accessed over the network on the satellites. MPD provides a few options for this through the SMB, NFS and WebDav storage plugins. Everything is handled in user space by MPD, no need to mount any remote file systems.

Because both SMB and NFS shouldn't be exposed over the internet, I decided to use WebDav to access my music files. I had already set up WebDav with HTTP Basic Auth for simple file sharing using the excellent nginx-dav-ext-module so all I had to do is point the satellite to the WebDav URL at the root of my music collection.

Server Setup

The server instance runs as a socket activated systemd service. Socket activation is a neat systemd feature that sets up a socket and starts the corresponding service whenever a connection is established. So instead of starting mpd.service, you start mpd.socket. The MPD package on ArchLinux provides these unit files. Here's what they look like:

# /usr/lib/systemd/system/mpd.socket
[Socket]
ListenStream=%t/mpd/socket
ListenStream=6600
Backlog=5
KeepAlive=true
PassCredentials=true

[Install]
WantedBy=sockets.target

You can see that the mpd.socket unit binds to unix://$RUNTIME_DIRECTORY_ROOT/mpd/socket as well as tcp://localhost:6600.

# /usr/lib/systemd/system/mpd.service
[Unit]
Description=Music Player Daemon
Documentation=man:mpd(1) man:mpd.conf(5)
After=network.target sound.target

[Service]
User=mpd
Type=notify
ExecStart=/usr/bin/mpd --no-daemon

# Enable this setting to ask systemd to watch over MPD, see
# systemd.service(5).  This is disabled by default because it causes
# periodic wakeups which are unnecessary if MPD is not playing.
#WatchdogSec=120

# allow MPD to use real-time priority 40
LimitRTPRIO=40
LimitRTTIME=infinity

# for io_uring
LimitMEMLOCK=64M

# disallow writing to /usr, /bin, /sbin, ...
ProtectSystem=yes

# more paranoid security settings
NoNewPrivileges=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
# AF_NETLINK is required by libsmbclient, or it will exit() .. *sigh*
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
RestrictNamespaces=yes

[Install]
WantedBy=multi-user.target
Also=mpd.socket

The server's mpd config is pretty straight forward:

pid_file            "/run/mpd/mpd.pid"
playlist_directory  "/var/lib/mpd/playlists"
music_directory     "/media/shared/music"

database {
    plugin           "simple"
    path             "/var/lib/mpd/mpd.db"
    cache_directory  "/var/lib/mpd/cache"
}

audio_output {
    type  "null"
    name  "This server does not need to play music, but it can"
}

That was the easy part. Setting up the satellite is a bit trickier, but only if you need to access your MPD instance over the internet. MPD doesn't currently have support for TLS sockets, so even if you set up access restrictions, authentication would be unencrypted and vulnerable to man-in-the-middle attacks. I decided to set up SSH port forwarding of the server's MPD port to my satellite. To harden things a bit more, I added the following to my server's sshd config:

# /etc/ssh/sshd_config
Match User mpd
        ForceCommand "echo 'Port forwarding only'"
        AllowTcpForwarding local
        X11Forwarding no
        AllowAgentForwarding no
        PermitTTY no
        PermitOpen 127.0.0.1:6600 [::1]:6600 localhost:6600
        AuthorizedKeysFile /etc/ssh/authorized_keys.d/mpd/authorized_keys

In addition I overrode the mpd.socket unit to only listen on loopback devices:

# /etc/systemd/system/mpd.socket.d/override.conf
[Socket]
ListenStream=
ListenStream=127.0.0.1:6600
ListenStream=[::1]:6600

When creating the authorized_keys file and its parent directories, file permissions are crucial:

drwxr-xr-x 3 root root 4096 Jan 15 15:50 /etc/ssh/authorized_keys.d/
dr-x------ 2 mpd  mpd  4096 Jan 15 15:50 /etc/ssh/authorized_keys.d/mpd
-r--r----- 1 root mpd   276 Jan 15 15:55 /etc/ssh/authorized_keys.d/mpd/authorized_keys

Satellite Setup

On the satellite, I created a new ssh keypair without a passphrase and added the public key to the authorized_keys file above.

Port forwarding being set up is a precondition for the satellite MPD to work, so I wrote a simple systemd user service handling the port-forwarding:

# /home/jokke/.config/systemd/user/mpd-port-forward.service
[Unit]
Description = port forwarding of mpd port from server

[Service]
ExecStart = /usr/bin/ssh -N -L 6601:localhost:6600 -i %h/.ssh/mpd my.server.tld
Restart = always

The ArchLinux MPD package also ships with a systemd user service and socket. This is what they look like:

 # /usr/lib/systemd/user/mpd.service
[Unit]
Description=Music Player Daemon
Documentation=man:mpd(1) man:mpd.conf(5)
After=network.target sound.target

[Service]
Type=notify
ExecStart=/usr/bin/mpd --no-daemon

# Enable this setting to ask systemd to watch over MPD, see
# systemd.service(5).  This is disabled by default because it causes
# periodic wakeups which are unnecessary if MPD is not playing.
#WatchdogSec=120

# allow MPD to use real-time priority 40
LimitRTPRIO=40
LimitRTTIME=infinity

# for io_uring
LimitMEMLOCK=64M

# disallow writing to /usr, /bin, /sbin, ...
ProtectSystem=yes

# more paranoid security settings
NoNewPrivileges=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
# AF_NETLINK is required by libsmbclient, or it will exit() .. *sigh*
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
RestrictNamespaces=yes

# Note that "ProtectKernelModules=yes" is missing in the user unit
# because systemd 232 is unable to reduce its own capabilities
# ("Failed at step CAPABILITIES spawning /usr/bin/mpd: Operation not
# permitted")

[Install]
WantedBy=default.target
# /usr/lib/systemd/user/mpd.socket
[Socket]
ListenStream=%t/mpd/socket
ListenStream=6600
Backlog=5
KeepAlive=true
PassCredentials=true

[Install]
WantedBy=sockets.target

I want the SSH port forwarding to be started before mpd starts, so I added an override to the mpd user service:

# /home/jokke/.config/systemd/user/mpd.service.d/override.conf
[Unit]
Requires=mpd-port-forward.service
Wants=mpdscribble.service
After=mpd-port-forward.service
Before=mpdscribble.service

I defined mpd-port-forward.service as a hard requirement (Requires and not Wants) and defined the order using the After setting.

Whats left is configuring the satellite MPD instance to use the proxy database and WebDav storage plugin:

# ...
music_directory   "https://user:pass@my.server.tld/path/to/music"
# ...
database {
       plugin "proxy"
       host "localhost"
       port "6601"
}

Note: I point MPD to my forwarded port on localhost.

It's time to start and enable the socket units. On the server: systemctl enable --now mpd.socket. On the satellite: systemctl enable --now --user mpd.socket. That's it! As soon as an MPD client tries to connect to the socket on the satellite several things will happen in the following order:

  • Due to the Requires and After config of the mpd user service override, the mpd-port-forward.service is started and forwards the MPD port from the server to the satellite.
  • The MPD service itself is started

If you payed close attention, you might've noticed the Wants= and Before= setting in the mpd user service override. MPDScribble is an MPD client which tracks your listening habits either to a local file or to cloud services like ListenBrainz, Libre.fm or Last.fm. I've set up mpdscribble to report listened tracks to ListenBrainz. This is done with yet another systemd user service:

# /home/jokke/.config/systemd/user/mpdscribble.service
[Unit]
Description=AudioScrobbler for MPD
PartOf=mpd.service

[Service]
ExecStart=/usr/bin/mpdscribble --no-daemon --conf %E/mpdscribble/mpdscribble.conf --log - --host %t/mpd/so>
Restart=always
RestartSec=10s

Notice the PartOf setting here. It tells systemd, that whenever the given service is (re)started or stopped, this service should receive the same treatment. The Before setting in the mpd user service takes care of the ordering. Just make sure to notify systemd of the changes by running systemctl --user daemon-reload. So to continue with the list above:

  • MPDScribble is started

Conclusion

I think this demonstrates the power and versatility of systemd (user) services by using dependency management and ordering as well as socket activation! Although socket activation isn't necessary for a working satellite setup, it's nice to have and reduces your boot times.

I'm very happy to count on MPD for music player controls yet again and being able to share my library between several devices. I have noticed that some commands take a bit longer than on a completely local setup, but I guess that's to be expected. Playback is buttersmooth and library browsing works exactly like you'd expect.

If you need to share playlists between satellites, consider mounting a network storage and pointing your satellite mpd instances there for the playlist_directory.