Multiseat mit Debian 12

PCs sind seit geraumer Zeit so schnell, dass sie sich die meiste Zeit mit Nichtstun beschäftigen können. Es ist daher naheliegend, einen einzigen Computer für mehrere Benutzer an mehreren Arbeitsplätzen zu verwenden, mit je einer Tastatur, Bildschirm und Maus. So spart man außerdem Geld, Zeit, Platz und Nerven. Und man lernt was.

Was braucht’s?

Der PC braucht pro Arbeitsplatz eine eigene Grafikkarte, und natürlich jeweils Monitor, Tastatur und Maus. Ich gehe unten mal von zwei Arbeitsplätzen aus. Im Rechner stecken also zwei Grafikkarten, und es sind zwei Tastaturen und zwei Mäuse angesteckt.

Wie funktioniert’s?

Jede Grafikkarte wird fest einem Arbeitsplatz („seat“) zugeordnet, genauso die Tastaturen und Mäuse. Die Soundkarte wird gemeinsam genutzt: Die Benutzer können sich so gegenseitig die Lautstärke verstellen, was je nach Charakter Vor- und Nachteil sein kann. USB-Geräte werden ebenfalls gemeinsam verwendet: USB-Sticks können von allen benutzt werden. Ebenso DVD-Brenner, Drucker usw.

Grundsätzlich kann man auch mehrere Soundkarten einsetzen und alle USB-Ports ganz streng Benutzern zuordnen. Ich gehe hier jedoch von einem Heimbüro aus, in dem Bequemlichkeit wichtig ist und sich die Nutzer nicht absichtlich auf die Nerven gehen wollen.

Los geht’s!

Soundkarte gemeinsam nutzen

Normalerweise startet systemd für jeden Benutzer, der sich am Rechner anmeldet, eine eigene Pulseaudio-Instanz. Der zuletzt angemeldete Nutzer bekommt die Soundkarte zugeteilt. Da bei Multiseat aber mehrerer Nutzer gleichzeitig angemeldet sind kann das so nicht funktionieren.

Man könnte nun für jeden Benutzer eine eigene Soundkarte kaufen, aber das ist unnötig.

Die Lösung: Nur eine einzige, systemweite Pulseaudio-Instanz für alle Benutzer verwenden.

  1. Zuerst das Starten der User-Instanzen abschalten:
    systemctl --global --now mask pulseaudio.service
    systemctl --global --now mask pulseaudio.socket
  2. Einen neuen systemd-Service für Pulseaudio erstellen. Dazu folgende Datei „/etc/systemd/system/pulseaudio.service“ anlegen:
    Description=PulseAudio Sound System
    After=dbus.service avahi-daemon.service network-online.target
    [Service]
    Type=notify
    ExecStart=/usr/bin/pulseaudio --system --daemonize=no --disallow-module-loading=0 --disallow-exit --log-target=journal
    Restart=always
    [Install]
    WantedBy=multi-user.target sound.target
  3. Den neuen Pulseaudio-Service aktivieren: systemctl daemon-reload; systemctl --now enable pulseaudio.service
  4. Damit die Benutzern den neuen systemweiten Pulseaudio-Service nutzen dürfen, müssen sie Mitglied in der Gruppe „pulse-access“ sein. Hinzufügen eines einzelnen Benutzers  geht so: adduser <username> pulse-access

Wichtig zu wissen: Im systemweiten Modus – also ab jetzt – verwendet Pulseaudio nicht mehr die Konfigurationsdatei /etc/pulse/default.pa sondern /etc/pulse/system.pa.

Grafikkarte, Tastatur und Maus den Arbeitsplätzen zuordnen

Ein normales Linux-System kennt bereits das Konzept mehrerer Arbeitsplätze pro Rechner – standardmäßig werden schlicht sämtliche Geräte dem Arbeitsplatz „seat0“ zugewiesen: Stecken zwei Mäuse am Rechner, steuern beide den gleichen Mauszeiger.

# loginctl list-seats

SEAT 
seat0

Die dem seat0 zugeteilten Geräte – und das sind zur Zeit noch alle – kann man mit loginctl seat-status seat0 anzeigen lassen:

seat0
         Devices:
                  ├─/sys/devices/LNXSYSTM:00/LNXPWRBN:00/input/input6
                  │ input:input6 "Power Button"
                  ├─/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0C:00/input/input5
                  │ input:input5 "Power Button"
[...]
                  ├─/sys/devices/pci0000:00/0000:00:03.1/0000:08:00.0/drm/card0
                  │ [MASTER] drm:card0
                  │ ├─/sys/devices/pci0000:00/0000:00:03.1/0000:08:00.0/drm/card0/card0-DP-1
                  │ │ [MASTER] drm:card0-DP-1
                  │ ├─/sys/devices/pci0000:00/0000:00:03.1/0000:08:00.0/drm/card0/card0-DVI-D-1
                  │ │ [MASTER] drm:card0-DVI-D-1
                  │ └─/sys/devices/pci0000:00/0000:00:03.1/0000:08:00.0/drm/card0/card0-HDMI-A-1
                  │   [MASTER] drm:card0-HDMI-A-1
                  ├─/sys/devices/pci0000:00/0000:00:03.1/0000:08:00.0/drm/renderD128
                  │ drm:renderD128
                  ├─/sys/devices/pci0000:00/0000:00:03.1/0000:08:00.0/graphics/fb0
                  │ [MASTER] graphics:fb0 "amdgpudrmfb"
                  ├─/sys/devices/pci0000:00/0000:00:03.1/0000:08:00.1/sound/card0
[...]
                  ├─/sys/devices/pci0000:00/0000:00:03.2/0000:09:00.0/drm/card1
                  │ [MASTER] drm:card1
                  │ ├─/sys/devices/pci0000:00/0000:00:03.2/0000:09:00.0/drm/card1/card1-DP-2
                  │ │ [MASTER] drm:card1-DP-2
                  │ ├─/sys/devices/pci0000:00/0000:00:03.2/0000:09:00.0/drm/card1/card1-DP-3
                  │ │ [MASTER] drm:card1-DP-3
                  │ ├─/sys/devices/pci0000:00/0000:00:03.2/0000:09:00.0/drm/card1/card1-DVI-D-2
                  │ │ [MASTER] drm:card1-DVI-D-2
                  │ ├─/sys/devices/pci0000:00/0000:00:03.2/0000:09:00.0/drm/card1/card1-HDMI-A-2
                  │ │ [MASTER] drm:card1-HDMI-A-2
                  │ └─/sys/devices/pci0000:00/0000:00:03.2/0000:09:00.0/drm/card1/card1-HDMI-A-3
                  │   [MASTER] drm:card1-HDMI-A-3
                  ├─/sys/devices/pci0000:00/0000:00:03.2/0000:09:00.0/drm/renderD129
                  │ drm:renderD129
                  ├─/sys/devices/pci0000:00/0000:00:03.2/0000:09:00.0/graphics/fb1
                  │ [MASTER] graphics:fb1 "amdgpudrmfb"
[...]
                  ├─/sys/devices/pci0000:00/0000:00:07.1/0000:0a:00.3/usb5
                  │ usb:usb5
                  │ └─/sys/devices/pci0000:00/0000:00:07.1/0000:0a:00.3/usb5/5-2
                  │   usb:5-2
                  │   ├─/sys/devices/pci0000:00/0000:00:07.1/0000:0a:00.3/usb5/5-2/5-2.1/5-2.1:1.0/0003:0557:2213.0002/input/input1
                  │   │ input:input1 "ATEN Advance Tech Inc. KVM V1.2.116"
                  │   └─/sys/devices/pci0000:00/0000:00:07.1/0000:0a:00.3/usb5/5-2/5-2.1/5-2.1:1.1/0003:0557:2213.0004/input/input3
                  │     input:input3 "ATEN Advance Tech Inc. KVM V1.2.116"
                  └─/sys/devices/pci0000:00/0000:00:07.1/0000:0a:00.3/usb6
                    usb:usb6
[...]

Man muss also nur die Geräte, die dem zweiten Arbeitsplatz gehören sollen, einem anderen Seat, z.B. „seat-anna“, zuteilen. Das sollte eine Grafikkarte (endet mit …/drm/cardN) sein:

loginctl attach seat-anna /sys/devices/pci0000:00/0000:00:03.1/0000:08:00.0/drm/card0

Und Annas Tastatur und Maus:

loginctl attach seat-anna /sys/devices/pci0000:00/0000:00:07.1/0000:0a:00.3/usb5/5-2/5-2.1/5-2.1:1.0/0003:0557:2213.0002/input/input1
loginctl attach seat-anna /sys/devices/pci0000:00/0000:00:07.1/0000:0a:00.3/usb5/5-2/5-2.1/5-2.1:1.1/0003:0557:2213.0004/input/input3

Wenn man will kann man auch ganze USB-Hubs einem Benutzer zuteilen. Alles, was daran angeschlossen wird, gehört dann ausschließlich diesem Benutzer. Da Annas Eingabegeräte beide an „usb5“ hängen würde statt der obigen zwei Befehle auch das hier funktionieren:

loginctl attach seat-anna /sys/devices/pci0000:00/0000:00:07.1/0000:0a:00.3/usb5

loginctl baut udev-Regeln und speichert diese unter „/etc/udev/rules.d/<irgendeineNummer>-seat-<device>.rules“. Der Name des Seats wird als Tag zu den Geräten hinzugefügt. Mit loginctl flush-devices könnte man sämtliche Zuordnungen wieder aufheben.

Display-Manager konfigurieren

Nach einigem Ausprobieren hat sich lightdm als der am besten geeignete Display Manager herausgestellt. Mit sddm, dem Standard-Display Manager von KDE/Plasma, ging Multiseat dagegen gar nicht.

Bei lightdm müssen für jeden Seat lediglich ein paar Zeilen am Ende der „/etc/lightdm/lightdm.conf“ hinzugefügt werden:

[Seat:seat0]
xserver-command=/usr/lib/xorg/Xorg

[Seat:seat-anna]
xserver-command=/usr/lib/xorg/Xorg
xserver-share=true

Laut „Quelle: Internet“ sollte bei allen Seats außer „seat0“ „xserver-share=true“ stehen.

Damit ist das Gröbste erledigt, das Anmelden sollte jetzt an beiden Arbeitsplätzen gelingen.

Feinschliff

USB-Geräte für alle

Damit angestöpselte USB-Geräte, wie z.B. USB-Sticks, Headsets oder Bluetooth-Dongles, von allen Arbeitsplätzen aus verwendet werden können (sofern der angemeldete Benutzer Mitglied der Gruppe „plugdev“ ist) folgende Datei anlegen:

Bis inklusive Debian 11: „/etc/polkit-1/localauthority/50-local.d/20-org.freedesktop.udisks2.pkla“:

[Storage Permissions]
Identity=unix-group:plugdev
Action=org.freedesktop.udisks2.*;org.blueman.*;org.freedesktop.pulseaudio;org.gtk.vfs.file-operations
ResultAny=yes
ResultInactive=yes
ResultActive=yes

Ab Debian 12: „/etc/polkit-1/rules.d/20-org.freedesktop.udisks2.conf“:

polkit.addRule(function(action, subject) { 
   if ((action.startsWith("org.freedesktop.udisks2.") ||
        action.startsWith("org.blueman.") ||
        action.id == "org.freedesktop.pulseaudio" ||
        action.id == "org.gtk.vfs.file-operations") &&
       subject.isInGroup("plugdev"))
   {
       return polkit.Result.YES;
   }
});

USB-Geräte einzelnen Seats zuweisen

Andere USB-Geräte (z.B. Gamepads) sollen dagegen nur an einem bestimmten Arbeitsplatz funktionieren. Dazu mit lsusb die ID die Geräts herausfinden:

[...]
Bus 006 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub 
Bus 005 Device 004: ID 054c:09cc Sony Corp. DualShock 4 [CUH-ZCT2x] 
Bus 005 Device 003: ID 046a:0011 Cherry GmbH G83 (RS 6000) Keyboard
[...]

Der DualShock4-Controller hat die Vendor-ID 054c und die Product-ID 09cc. Soll dieser dem „seat-anna“ zugeordnet werden, eine Datei „/etc/udev/rules.d/99-dualshock4-für-seat-anna.rules“ anlegen mit diesem Inhalt:

TAG=="seat", SUBSYSTEM=="usb", ATTR{idVendor}=="054c", ATTR{idProduct}=="09cc", ENV{ID_SEAT}="seat-anna"

Anschließend die Regel aktivieren mit:

udevadm control --reload-rules
udevadm trigger

Achtung: Da hier nur nach Hersteller („idVendor“) und Produkt („idProduct“) separiert wird, werden so ALLE DualShock4-Controller stets dem „seat-anna“ zugewiesen! Sollen DualShock4-Controller an mehreren Seats verwendeten werden, dann muss man, wie oben mit Tastatur und Maus, USB-Ports den Seats zuweisen und die Controller immer in diesen Port anstecken.

Rechner herunterfahren

Soll der Rechner von allen Arbeitsplätzen aus (und nicht nur von seat0 aus) heruntergefahren werden können (sofern der angemeldete Benutzer Mitglied der Gruppe „powerdev“ ist):

Bis inklusive Debian 11: „/etc/polkit-1/localauthority/50-local.d/10-org.freedesktop.upower.pkla“:

[Suspend/hibernate permissions]
Identity=unix-group:powerdev
Action=org.freedesktop.upower.*
ResultAny=yes
ResultInactive=yes
ResultActive=yes

Ab Debian 12: „/etc/polkit-1/rules.d/50-org.freedesktop.upower.conf“:

polkit.addRule(function(action, subject) { 
   if (action.startsWith("org.freedesktop.upower.") &&
       subject.isInGroup("powerdev"))
   {
       return polkit.Result.YES;
   }
});

Bluetooth und Audio

Da, abweichend vom Standard, Pulseaudio als systemweite Instanz läuft und nicht jeweils eine für jeden angemeldeten Benutzer, müssen einige Kleinigkeiten angepasst werden damit Audio über Bluetooth funktioniert:

Der User „pulse“ muss Mitglied der Gruppe „bluetooth“ werden:

adduser pulse bluetooth

Dann „/etc/dbus-1/system.d/pulseaudio-system.conf“ mit folgendem Inhalt anlegen:

<?xml version="1.0"?><!--*-nxml-*-->
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">

<policy user="pulse">
 <allow own="org.pulseaudio.Server"/>
 <allow send_destination="org.pulseaudio.Server"/>
 <allow receive_sender="org.pulseaudio.Server"/>
 <allow send_destination="org.ofono"/>
</policy>

Anschließend Dbus seine Konfigurationsdateien mit systemctl reload dbus neu einlesen lassen.

Jetzt Pulseaudio das Nachladen von Modulen – in diesem Fall speziell das bluetooth-device-Modul, das für jedes Bluetooth-Gerät nachgeladen wird – erlauben:

Dafür in „/etc/pulse/daemon.conf“ diese Zeile hinzufügen:

allow-module-loading = yes

Und in „/etc/pulse/system.pa“ das hier hinzufügen um die Bluetooth-Basisunterstützung einzuschalten:

.ifexists module-bluetooth-policy.so
load-module module-bluetooth-policy
.endif

.ifexists module-bluez5-discover.so
load-module module-bluez5-discover
.endif

Andere Einträge in dieser Datei, die „bluez“ oder „bluetooth“ enthalten müssen entfernt werden.

Pulseaudio darf beim Systemstart erst nach Bluetooth/bluez gestartet werden. Dazu in „/etc/systemd/system/pulseaudio.service“ die Zeile

After=dbus.service avahi-daemon.service network.target

erweitern zu:

After=dbus.service avahi-daemon.service network.target bluetooth.target

Anschließend Pulseaudio neu starten: systemctl daemon-reload; systemctl restart pulseaudio

Das war’s!

Updates:

Update 2019:

Alles funktioniert monatelang ganz wunderbar hervorragend – bis ich beschloss, eine zusätzliche PCIe-Karte in den Rechner einzubauen: Dadurch wurden die PCI-IDs durcheinandergewirbelt, die oben mit „loginctl attach“ den Seats zugewiesenen wurden. Damit’s wieder funktioniert einfach die Geräte, wie oben beschrieben, nochmal den Seats zuteilen, und schon läuft alles wieder wie gehabt.

Update 2021:

Update auf Debian 11 durchgeführt: Keine Probleme, keine Nacharbeit erforderlich. Daher Titel dieses Beitrags angepasst.

Update 2022:

DXVK, die D3D9/D3D10/D3D1-Implementierung in Vulkan, verwendet nicht die dem Seat zugeordenete Grafikkarte, sondern eine beliebige. Wenn’s bei einem Benutzer in Wine oder Proton ruckelt, mit vulkaninfo | grep "^GPU id" den Namen „seiner“ GPU rausfinden und in die Umgebungsvariable DXVK_FILTER_DEVICE_NAME eintragen.
Es reicht ein eindeutiger Teil der Ausgabe. z.B. bei „GPU id : 1 (AMD Radeon RX 550 Series (RADV POLARIS11))“ reicht export DXVK_FILTER_DEVICE_NAME=POLARIS11.

Update 2023:

Update auf Debian 12 durchgeführt: Polkit-Regeln müssen vom pkla-Format in’s conf-Format konvertiert werden, habe den Text entsprechend angepasst. Ansonsten: Keine Probleme, keine weitere Nacharbeit erforderlich. Daher Titel dieses Beitrags wieder einmal angepasst.