multiseat (original) (raw)

Back to systemd

This page is mostly of historical interest. It has been replaced by a man page: sd-login(3).

Multi-Seat on Linux

systemd, versions 30 and newer, includes support for keeping track of user sessions and seats. This adds full hotplug multi-seat support to Linux. Here's a quick overview on how it works.

If you are interested in the actual APIs for multi-seat support, please have a look at the logind Bus API and sd-login(7). If you are planning to write (or port) a multi-seat aware display manager, please have a look at Writing Display Managers.

Definition of Terms

All hardware devices that are eligible to being assigned to a seat, are assigned to one. A device can be assigned to only one seat at a time. If a device is not assigned to any particular other seat it is implicitly assigned to the special default seat called "seat0".

Note that hardware like printers, hard disks or network cards is generally not assigned to a specific seat. They are available to all seats equally. (Well, with one exception: USB sticks can be assigned to a seat.)

"seat0" always exists.

udev Rules

Assignment of hardware devices to seats is managed inside the udev database, via settings on the devices:

A seat exists only and exclusively because a properly tagged device with the right ID_SEAT property exists. Besides udev rules there is no persistent data about seats stored on disk.

Note that logind manages ACLs on a number of device classes, to allow user code to access the device nodes attached to a seat as long as the user has an active session on it. This is mostly transparent to applications. As mentioned above, for certain user software it might be a good idea to watch whether they can access device nodes instead of thinking about seats.

Uses

This is only a very basic introduction, for details see logind Bus API and sd-login(7). If you have questions ping Lennart.

Examples

You may use loginctl to list and introspect the seats on your system. Here's an example on a system with two seats:

$ loginctl list-seats
SEAT
seat0
seat-usb-pci-0000_00_1d_0-usb-0_1_2

$ loginctl seat-status seat0
seat0
        Sessions: *1
         Devices:
                  ├ /sys/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2
                  │ (input:input2) "Power Button"
                  ├ /sys/devices/LNXSYSTM:00/device:00/PNP0A08:00/LNXVIDEO:00/input/input8
                  │ (input:input8) "Video Bus"
                  ├ /sys/devices/LNXSYSTM:00/device:00/PNP0C0D:00/input/input0
                  │ (input:input0) "Lid Switch"
                  ├ /sys/devices/LNXSYSTM:00/device:00/PNP0C0E:00/input/input1
                  │ (input:input1) "Sleep Button"
                  ├ /sys/devices/pci0000:00/0000:00:02.0/drm/card0
                  │ (drm:card0)
                  ├ /sys/devices/pci0000:00/0000:00:02.0/graphics/fb0
                  │ (graphics:fb0) "inteldrmfb"
                  ├ /sys/devices/pci0000:00/0000:00:1a.0/usb1
                  │ (usb:usb1)
                  │ └ /sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1
                  │   (usb:1-1)
                  │   ├ /sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.6/1-1.6:1.0/input/input6
                  │   │ (input:input6) "Integrated Camera"
                  │   └ /sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.6/1-1.6:1.0/video4linux/video0
                  │     (video4linux:video0) "Integrated Camera"
                  ├ /sys/devices/pci0000:00/0000:00:1b.0/sound/card0
                  │ (sound:card0) "Intel"
                  ├ /sys/devices/pci0000:00/0000:00:1d.0/usb2
                  │ (usb:usb2)
                  │ └ /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1
                  │   (usb:2-1)
                  │   ├ /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8/2-1.8:1.0/input/input5
                  │   │ (input:input5) "N-Trig Touchscreen"
                  │   └ /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8/2-1.8:1.1/input/input9
                  │     (input:input9) "N-Trig Touchscreen"
                  ├ /sys/devices/platform/i8042/serio0/input/input3
                  │ (input:input3) "AT Translated Set 2 keyboard"
                  ├ /sys/devices/platform/i8042/serio1/input/input4
                  │ (input:input4) "SynPS/2 Synaptics TouchPad"
                  ├ /sys/devices/platform/i8042/serio1/serio2/input/input10
                  │ (input:input10) "TPPS/2 IBM TrackPoint"
                  ├ /sys/devices/platform/thinkpad_acpi/input/input7
                  │ (input:input7) "ThinkPad Extra Buttons"
                  └ /sys/devices/platform/thinkpad_acpi/sound/card29
                    (sound:card29) "ThinkPadEC"

$ loginctl seat-status seat-usb-pci-0000_00_1d_0-usb-0_1_2
seat-usb-pci-0000_00_1d_0-usb-0_1_2
         Devices:
                  └ /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2
                    (usb:2-1.2)
                    ├ /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2.1/graphics/fb1
                    │ (graphics:fb1) "udlfb"
                    ├ /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2.3/2-1.2.3:1.0/sound/card1
                    │ (sound:card1) "Device"
                    ├ /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2.3/2-1.2.3:1.3/input/input11
                    │ (input:input11) "C-Media Electronics Inc. USB Multimedia Audio Device"
                    ├ /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2.5/2-1.2.5:1.0/input/input12
                    │ (input:input12) "EL USB Keyboard "
                    ├ /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2.5/2-1.2.5:1.1/input/input13
                    │ (input:input13) "EL USB Keyboard "
                    └ /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2.6/2-1.2.6:1.0/input/input14
                      (input:input14) "Logitech USB-PS/2 Optical Mouse"

You may use "loginctl attach" to assign hardware to a specific seat.

Here's a short code example (no error checking, so don't use this as is) for enumerating all input devices on a specific seat. A display server might use something like this:

int enum_input_on_seat(const char *seat) {
        struct udev *udev;
        struct udev_enumerate *enumerate;
        struct udev_list_entry *item, *first;

        if (!seat)                                                        /* ← Here's the beef */
                seat = "seat0";                                           /* ← Here's the beef */

        udev = udev_new();

        enumerate = udev_enumerate_new(udev);
        udev_enumerate_add_match_subsystem(enumerate, "input");

        if (strcmp(seat, "seat0") != 0)                                   /* ← Here's the beef */
                udev_enumerate_add_match_tag(enumerate, seat);            /* ← Here's the beef */

        udev_enumerate_scan_devices(enumerate);

        first = udev_enumerate_get_list_entry(e);
        udev_list_entry_foreach(item, first) {
                struct udev_device *d;
                const char *d_seat;

                d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));

                d_seat = udev_device_get_property_value(d, "ID_SEAT");    /* ← Here's the beef */
                if (!d_seat)                                              /* ← Here's the beef */
                        d_seat = "seat0";                                 /* ← Here's the beef */

                if (strcmp(seat, d_seat) == 0) {                          /* ← Here's the beef */

                        /* Found a device, yihaaa! */
                        printf("Found device %s\n", udev_device_new_from_syspath(d));

                        do_something_with_our_input_device(d);
                }

                udev_device_unref(d);
        }

        udev_enumerate_unref(enumerate);
        udev_unref(udev);
}

Multi-Seat X

Note that even though XOrg gained, in version 1.12 of xserver, a new -seat switch to make use of multi-seat information it does so only for input devices, not for displays. To work around this systemd includes a tiny wrapper binary in /lib/systemd/systemd-multi-seat-x which emulates the right behaviour by creating a throw-away X configuration file which does the right thing. If you are implementing a multi-seat display manager you probably want to use this binary instead of the real X binary for now. As soon as XOrg upstream gains proper multi-seat support also for displays this binary will be removed from systemd, hence write your DM to fallback to the real X server if systemd's wrapper is not found.