Multiseat mit Debian

Veröffentlicht am Autor geierb

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.

Soundkarte gemeinsam nutzen

Normalerweise startet systemd für jeden Benutzer, der sich am Rechner anmeldet, eine eigene Pulseaudio-Instanz. Der aktive 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 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.target
    [Service]
    Type=notify
    ExecStart=/usr/bin/pulseaudio --system --daemonize=no --realtime --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

Grafikkarte, Tastatur und Maus den Arbeitsplätzen zuordnen

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

# loginctl list-seats

SEAT 
seat0

Die dem seat0 zugeteilten Geräte – aktuell sind das 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/77-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.

Xorg-Minimalkonfiguration

Eigentlich könnte man hier jetzt schon fertig sein: Die Geräte sind zugeordnet, Sound gibt’s auch, was will man mehr? Nur: Bei mir hat es so noch nicht funktioniert. Ganz gleich ob ich sddm, lightdm oder xdm als Display Manager verwendet hab und mit welchen Optionen ich dort Xorg starten lies: Entweder waren Geräte waren falsch zugeordnet, sämtliche Bildschirme blieb schwarz oder der Anmeldedialog wurde nur auf einem Bildschirm angezeigt. Letztendlich musste ich für jeden Arbeitsplatz eine eigene kleine Konfigurationsdatei anlegen, in der ich jeder Xorg-Instanz nochmal die richtige Grafikkarte  anhand der PCI-ID und den Seatnamen mitgeteilt habe.

Hier, für seat-anna, “/etc/X11/xorg_seat-anna.conf”:

Section "ServerLayout"
    Identifier  "seat-anna"
    Screen    0 "Screen1"             0 0
EndSection
Section "ServerFlags"
    Option      "AutoAddGPU"          "off"
    Option      "AllowMouseOpenFail"  "true"
EndSection
Section "Device"
    Identifier  "Videocard1"
    BusID       "PCI:08:00:0"
    Option      "ProbeAllGpus"        "false"
EndSection
Section "Screen"
    Identifier  "Screen1"
    Device      "Videocard1"
EndSection

Und für seat0 die Datei “/etc/X11/xorg_seat0.conf” – die Unterschiede zwischen den beiden Dateien sind rot markiert:

Section "ServerLayout"
    Identifier  "seat0"
    Screen    0 "Screen1"             0 0
EndSection
Section "ServerFlags"
    Option      "AutoAddGPU"          "off"
    Option      "AllowMouseOpenFail"  "true"
EndSection
Section "Device"
    Identifier  "Videocard1"
    BusID       "PCI:09:00:0"
    Option      "ProbeAllGpus"        "false"
EndSection
Section "Screen"
    Identifier  "Screen1"
    Device      "Videocard1"
EndSection

Die BusID kann man z.B. aus der loginctl-Ausgabe rauslesen:

loginctl seat-status seat-anna

         Devices:
                  ├─/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
[...]

Die PCI-ID ist das, was direkt vor “drm” steht, hier also “0000:08:00.0”. Xorg erwartet das in einer etwas anderen Schreibweise: “0000” durch “PCI” ersetzen, den Punkt durch einen Doppelpunkt – so wird aus “0000:08:00.0” ein “PCI:08:00:0”

Die beiden Xorg-Konfigurationsdateien müssen jetzt noch in den Display Manager eingetragen werden: Der Display Manager zeigt den grafischen Anmeldedialog an, und das soll natürlich auf beiden Bildschirmen passieren, mit korrekt zugeordneter Maus und Tastatur.

Nach einigem Ausprobieren hat sich lightdm als der am besten geeignete Display Manager herausgestellt. Mit sddm dagegen ging es gar nicht.

Bei lightdm muss lediglich für die einzelnen Seats ein Xorg-Startbefehl am Ende der “/etc/lightdm/lightdm.conf” eingetragen werden:

[Seat:seat0]
xserver-command=/usr/lib/xorg/Xorg :0 -config xorg_seat0.conf -sharevts -keeptty
[Seat:seat-anna]
xserver-command=/usr/lib/xorg/Xorg :1 -config xorg_seat-anna.conf -sharevts -keeptty

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

Feinschliff

USB-Geräte

Damit angestöpselte USB-Geräte von allen Arbeitsplätzen aus verwendet werden können (sofern der angemeldete Benutzer Mitglied der Gruppe “plugdev” ist) folgende Datei anlegen:

“/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

Und damit von allen Arbeitsplätzen aus der Rechner heruntergefahren werden kann (sofern der angemeldete Benutzer Mitglied der Gruppe “powerdev” ist):

“/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

Bluetooth und Audio

Da abweichend vom Standard nur eine einzige Pulseaudio als systemweite Instanz läuft und nicht 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:

useradd -g bluetooth pulse

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:

.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.

Außerdem darf Pulseaudio erst dann gestartet werden, wenn Bluetooth funktioniert. 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!

 

Update

Alles funktioniert monatelang ganz wunderbar hervorragend – bis ich beschloss, eine zusätzliche PCIe-Karte in den Rechner einzubauen: Dadurch wurden die PCI-IDs zum Teil durcheinandergewirbelt, die oben mit “loginctl attach” den Seats zugewiesenen wurden. Auch wenn es nervt: Einfach die Geräte wie oben beschrieben nochmal den Seats zuteilen, nach 10 Minuten läuft alles wieder wie gehabt.