Multiseat

A multiseat system is Article description::a computer system with multiple hardware terminals, allowing two or more users to use a single computer. While Linux systems running Xorg always supported multiseat, the level of support changed a lot over time: sometimes it was hardware that changed, and lately, it is software changes that cause documentation to become obsolete pretty fast.

Graphics
When designing a multiseat system, the first consideration should be support for multiple graphics adapters. The simplest way to build a system is to make all graphics adapters the same (i.e. use the same chipset, they don't need to be identical). All adapters will use the same graphics drivers and no complications should arise.

It is also possible to use entirely different graphics adapters. Such configurations do work, but issues may arise. While it is OK to mix different adapters that all have e.g. Mesa drivers, it is a bad idea to mix adapters with different proprietary drivers with each other or with a mesa driver. Proprietary drivers bring with them their own OpenGL drivers. Thus, Nvidia and AMD drivers are incompatible both with each other and with Mesa. A mixed system will still work, but since only one OpenGL stack can be used at once (chosen by eselect opengl), only one kind of graphics card will have any kind of acceleration.

If ultimate performance is not required (as is the case most of the time &mdash; multiseat systems are usually not meant to be gaming rigs), it is recommended to go for an all-mesa stack using AMD cards because it is the easiest to set up and offers acceptable performance and functionality. Otherwise, choose Nvidia with proprietary drivers. The free nouveau drivers for Nvidia cards are, as of 2020, required for older graphics cards no longer supported by proprietary drivers.

Pick a motherboard with multiple large PCIE slots (x16 or x8), depending on how many seats are desired. Slower graphics cards that fit into smaller PCIE slots are available, as are cards for the PCI bus, but they are rarer and thus more expensive so it is prudent to instead invest in a more capable motherboard.

Input devices
Xorg stack supports USB device hotplugging in a multiseat configuration. To make this easier, it is recommended to provide each seat with its own USB hub, preferably powered. This way, unknown devices can be bound to a specific seat depending on which hub they are plugged into. Otherwise, only devices known when configuring the system can be bound to their seats.

Sound
There are several options:


 * use one sound card per seat;
 * use a single sound card, sound is shared between seats;
 * use a single multi-channel sound card and split channels into virtual sound cards.

All options work well and are described below, so choose according to the necessary requirements of the system. The easiest way to get the first option is to either use USB sound cards or to use graphics cards with HDMI outputs, since they can provide sound as well.

Multiseat Configuration
These Desktop Managers support Multiseat Configuration out of the box if either systemd or Elogind is used:
 * LightDM
 * SDDM

Identify devices
Look at the default seat (seat0) status, here is an example:

If unsure, try comparing with or  to identify devices.

Assign devices
Seats are created based on graphics cards. Hence, first start by assigning a graphics card:

Then, add input devices:

With multiple sound cards, assign one per seat that will run out of the box with per user session pulseaudio:

Finally, review the status to verify the hardware has been partitioned into two seats:

Troubleshooting
To reset all the previously assigned rules and start over with an empty config use:

X server
Traditionally, a multiseat X server configuration was specified in a single xorg.conf file in terms of devices, screens and layouts. Nowadays, it's better to leave as much as possible unconfigured and rely on autodetection, which is easier to do if options specific to each seat are provided in separate config files located in. Additionally, all files in are used when the X server starts; this directory can be used to specify options not specific to a seat.

Here is an example for two seats:

The device sections are self-explanatory: take the driver and bind it to a specific device. The PCI number can be obtained using the tool. The "Identifier" stuff is superfluous, but provided anyway.

The input sections are more interesting. The first turns on the "Ignore" option for all devices. The next section then disables "Ignore" selectively based on a special tag assigned by udev as described below.

Notice the left seat uses the "input_default" tag. The section is semantically the same as for the right seat so it does the same thing, but the name of the tag is chosen to convey our intention. As described in the next section, we wish to have the left seat as the "default" seat for unknown input devices. If a device doesn't belong anywhere else, it is assigned to the left seat. But that is up to udev to decide, and has nothing to do with Xorg.

Options common to all seats should reside in files in. These files are always read after the main file. An example of such a setting could be keyboard layouts.

In case xorg-server 1.13.0 and above is in use, the following must be used to maintain the gpu separation:

udev
udev is the component of modern Linux systems that allows automatic manipulation of devices according to a set of rules provided in. It is very versatile, which comes at the cost of considerable complexity. This section is a crash course of the bare minimum neccessary to assign tags to input devices. While it should be sufficient in most cases, be aware that udev is very flexible and if you have an idea on how to identify devices, you can probably do it (unless the device refuses to provide information about itself, as is, sadly, often the case). Use your imagination and documentation ( is nice, but there are many blogs and forums which may be easier to comprehend).

Daniel Drake has written a good guide which explains how to write udev rules

An udev rule is simply a comma-separated list of keys in a single line. First come the keys which identify a device, then come the keys which manipulate the device. In our case, "manipulate" means assigning a tag for Xorg to use.

Identifying an input device
The simplest way to figure out which device to refer to is to uplug it, then plug it back in and check the kernel output using. An example for a Logitech mouse:

The "input:" line is important as it specifies the device path in the /sys directory.

Discovering device attributes
The directory is used to communicate with the kernel. Device properties can be read by reading the files in proper directories and manipulated by writing to the files. This way, properties such as serial numbers can be read.

Browsing the directory manually can be tedious since it is a complicated labyrinth. Instead, the {{c|udevadm|| tool can be used. It is very convenient, since it prints device properties in the form which can be copy-pasted directly into udev rules. The command to follow prints not just the properties of the mouse itself, but also properties of all its parent devices:

{{Cmd|udevadm info --query{{=}}all --path{{=}}/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1:1.0/input/input8 --attribute-walk}}

This is most useful: often, a device refuses to provide enough information about itself to identify it well, so a parent device must be used to get additional information.

Let's return to our example system. It has two seats: the right one has a dedicated USB hub and will accept all input devices plugged into it, and the left one will accept all other input devices. Let's take a look at all the USB devices.

{{Cmd|lsusb|output= Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 002 Device 004: ID 0409:005a NEC Corp. HighSpeed Hub Bus 005 Device 002: ID 046d:c30e Logitech, Inc. UltraX Keyboard (Y-BL49) Bus 005 Device 003: ID 046d:c069 Logitech, Inc. Bus 002 Device 005: ID 046d:c069 Logitech, Inc. Bus 002 Device 006: ID 046d:c30e Logitech, Inc. UltraX Keyboard (Y-BL49) }}

There are obviously two Logitech keyboards, two other Logitech devices (presumably mice), a hub and several root hubs which represent physical ports on the computer. Now we pick a keyboard plugged into the USB hub using the {{c|dmesg}} method above and run {{c|udevadm info}} against it.

{{Invocation|udevadm info --query{{=}}all --path{{=}}/devices/pci0000:00/0000:00:13.2/usb2/2-4/2-4.2/2-4.2:1.1/input/input9 --attribute-walk|output= looking at device '/devices/pci0000:00/0000:00:13.2/usb2/2-4/2-4.2/2-4.2:1.1/input/input9': KERNEL=="input9" SUBSYSTEM=="input" DRIVER=="" ATTR{name}=="Logitech HID compliant keyboard" ATTR{phys}=="usb-0000:00:13.2-4.2/input1" ATTR{uniq}=="" ATTR{modalias}=="input:b0003v046DpC30Ee0110-e0,1,4,k71,72,73,74,80,8C,8E,8F,9B,9C,9E,9F,A1,A3,A4,A5,A6,AB,AC,AD,D9,100,101,102,103,104,105,106,107,108,109,10A,10B,10C,10D,10E,10F,110,111,112,113,114,115,116,117,118,119,11A,11B,11C,11D,11E,11F,120,121,122,123,124,125,126,127,128,129,12A,12B,12C,12D,12E,12F,130,131,132,133,134,135,136,137,138,139,13A,13B,13C,13D,13E,13F,140,141,142,143,144,145,146,147,148,149,14A,14B,ram4,lsfw" ATTR{properties}=="0"

looking at parent device '/devices/pci0000:00/0000:00:13.2/usb2/2-4/2-4.2/2-4.2:1.1': KERNELS=="2-4.2:1.1" SUBSYSTEMS=="usb" DRIVERS=="usbhid" ATTRS{bInterfaceNumber}=="01" ATTRS{bAlternateSetting}==" 0" ATTRS{bNumEndpoints}=="01" ATTRS{bInterfaceClass}=="03" ATTRS{bInterfaceSubClass}=="00" ATTRS{bInterfaceProtocol}=="00" ATTRS{modalias}=="usb:v046DpC30Ed0180dc00dsc00dp00ic03isc00ip00" ATTRS{supports_autosuspend}=="1"

looking at parent device '/devices/pci0000:00/0000:00:13.2/usb2/2-4/2-4.2': KERNELS=="2-4.2" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{configuration}=="" ATTRS{bNumInterfaces}==" 2" ATTRS{bConfigurationValue}=="1" ATTRS{bmAttributes}=="a0" ATTRS{bMaxPower}=="100mA" ATTRS{urbnum}=="20" ATTRS{idVendor}=="046d" ATTRS{idProduct}=="c30e" ATTRS{bcdDevice}=="0180" ATTRS{bDeviceClass}=="00" ATTRS{bDeviceSubClass}=="00" ATTRS{bDeviceProtocol}=="00" ATTRS{bNumConfigurations}=="1" ATTRS{bMaxPacketSize0}=="8" ATTRS{speed}=="1.5" ATTRS{busnum}=="2" ATTRS{devnum}=="9" ATTRS{devpath}=="4.2" ATTRS{version}==" 1.10" ATTRS{maxchild}=="0" ATTRS{quirks}=="0x0" ATTRS{avoid_reset_quirk}=="0" ATTRS{authorized}=="1" ATTRS{manufacturer}=="Logitech" ATTRS{product}=="HID compliant keyboard"

looking at parent device '/devices/pci0000:00/0000:00:13.2/usb2/2-4': KERNELS=="2-4" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{configuration}=="" ATTRS{bNumInterfaces}==" 1" ATTRS{bConfigurationValue}=="1" ATTRS{bmAttributes}=="e0" ATTRS{bMaxPower}=="100mA" ATTRS{urbnum}=="168" ATTRS{idVendor}=="0409" ATTRS{idProduct}=="005a" ATTRS{bcdDevice}=="0100" ATTRS{bDeviceClass}=="09" ATTRS{bDeviceSubClass}=="00" ATTRS{bDeviceProtocol}=="01" ATTRS{bNumConfigurations}=="1" ATTRS{bMaxPacketSize0}=="64" ATTRS{speed}=="480" ATTRS{busnum}=="2" ATTRS{devnum}=="4" ATTRS{devpath}=="4" ATTRS{version}==" 2.00" ATTRS{maxchild}=="4" ATTRS{quirks}=="0x0" ATTRS{avoid_reset_quirk}=="0" ATTRS{authorized}=="1"

looking at parent device '/devices/pci0000:00/0000:00:13.2/usb2': KERNELS=="usb2" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{configuration}=="" ATTRS{bNumInterfaces}==" 1" ATTRS{bConfigurationValue}=="1" ATTRS{bmAttributes}=="e0" ATTRS{bMaxPower}==" 0mA" ATTRS{urbnum}=="86" ATTRS{idVendor}=="1d6b" ATTRS{idProduct}=="0002" ATTRS{bcdDevice}=="0301" ATTRS{bDeviceClass}=="09" ATTRS{bDeviceSubClass}=="00" ATTRS{bDeviceProtocol}=="00" ATTRS{bNumConfigurations}=="1" ATTRS{bMaxPacketSize0}=="64" ATTRS{speed}=="480" ATTRS{busnum}=="2" ATTRS{devnum}=="1" ATTRS{devpath}=="0" ATTRS{version}==" 2.00" ATTRS{maxchild}=="6" ATTRS{quirks}=="0x0" ATTRS{avoid_reset_quirk}=="0" ATTRS{authorized}=="1" ATTRS{manufacturer}=="Linux 3.1.4-gentoo ehci_hcd" ATTRS{product}=="EHCI Host Controller" ATTRS{serial}=="0000:00:13.2" ATTRS{authorized_default}=="1"

looking at parent device '/devices/pci0000:00/0000:00:13.2': KERNELS=="0000:00:13.2" SUBSYSTEMS=="pci" DRIVERS=="ehci_hcd" ATTRS{vendor}=="0x1002" ATTRS{device}=="0x4396" ATTRS{subsystem_vendor}=="0x1458" ATTRS{subsystem_device}=="0x5004" ATTRS{class}=="0x0c0320" ATTRS{irq}=="19" ATTRS{local_cpus}=="ff" ATTRS{local_cpulist}=="0-7" ATTRS{modalias}=="pci:v00001002d00004396sv00001458sd00005004bc0Csc03i20" ATTRS{dma_mask_bits}=="32" ATTRS{consistent_dma_mask_bits}=="32" ATTRS{broken_parity_status}=="0" ATTRS{msi_bus}=="" ATTRS{companion}=="" ATTRS{uframe_periodic_max}=="100"

looking at parent device '/devices/pci0000:00': KERNELS=="pci0000:00" SUBSYSTEMS=="" DRIVERS=="" }}

The first three devices represent the mouse: there's an input device, a usbhid device and the low-level USB device; the latter has the "idVendor" and "idProduct" attributes that lsusb also tells us. Evidently, no unique identifier such as a serial number is available.

The fourth device is most interesting to us: its attributes tell us that this is the hub we are interested in. Again, there are no attributes to uniquely identify it. If there is more than one hub of the same model plugged in, you can't tell which is which. Therefore, we must resort to using the device path to identify it. The "looking at parent device" line tells us what it is.

The problem with using device paths is that they change if you plug the device into a different port. So the assumption we make is that the hub will always be plugged into the same port on the computer. If the port is changed, the udev rules need to be updated.

Tagging
The udev rules for this configuration are only two:

The first one simply assigns the "input_default" tag to all input devices. The second one overrides this in case that the path prefix matches the path of the hub. If there were useful attributes to identify it, we could copy-paste the relevant "ATTR" and/or "ATTRS" lines from udevadm output instead of using the "DEVPATH".

In the udev rules, the value of ENV{ID_INPUT.tags} is what MatchTag matches against in the X server configuration. The value of ENV{ID_SEAT} is the seat name used by loginctl. Both are needed, but their names can be different from each other as shown here.

Sound cards detection and assignment
If one wants to assign a card per seat, it is doable even if one of the cards isn't USB. First, decide what cards goes to which seat, then assign the USB cards the same as above to each seat. In order to assign the O\B cards, run the following command, I'm attaching the output of my system:

After matching the cards to the actual adapter, one can conclude that card0 is the O\B and card 2 is usb one (card 1 seem to be GPU HDMI card) so assuming that we decide to add the O\B card to input_right, should look like this:

Single shared sound card
Nowadays, ALSA is configured for software audio mixing by default, so nothing special needs to be done to support a shared sound configuration.

Single split sound card and/or multiple sound cards
Suppose the system has a 7.1 sound card integrated on the motherboard, while each graphics card also supports audio output through HDMI. This section describes how to configure all of them at once; cull the examples to your needs.

We will take the 7.1 sound card with several output jacks and configure it so that two of its jacks will act as separate PCM devices while the microphone jack is shared. This makes sense since presumably both users sit in the same room and can use only one microphone.

Next, the default sound cards are configured to be routed to PulseAudio. Since each user will have two sound cards at their disposal, using pulseaudio makes it easy to move audio streams between them. In addition, since all users sit in the same room, it makes sense for them to use headphones. If, however, one has speakers at his disposal, PulseAudio allows others to use them if they need to by pointing their programs to his PulseAudio server. PulseAudio thus allows a great deal of flexibility for audio usage.

ALSA
The ALSA configuration is global and resides in :

The first four blocks simply enable the PulseAudio plugin and set the defaults to go through it.

The pcm.int block gives the .int name to the "SB" sound card, which is our example integrated sound card. Different ways can be used to identify sound cards, such as "hw0,0". You can get this information using the utility's   and   options.

The .dshare pcm is a software mixing (dmix) device, which spans two physical jacks. The IPC key needs to be defined so that all programs from all users share the same dmix device, and permissions must allow this too. The .dshare device is defined to use only 4 channels of the .int device and channel bindings are made explicit.

The .rshare device is a dsnoop device, which is a software mixer like dmix but for audio input. This is the microphone jack which all users share.

The .righto and .lefto devices use the .dshare device, but limit themselves to two channels each, using a transfer table specification. They need to specify 4 channels, but the table maps only channels 0 and 1 to channels 0,1 and 2,3 for left and right seats, respectively.

The .left_sound and .right_sound are the actual PCMs the users should use since they combine the channel-mapped outputs with the common .rshare input. If there was no PulseAudio in the mix, these devices should be set as default in the per-user files.

Note that there is no mention of HDMI cards. We leave handling those to PulseAudio.

PulseAudio
Each user runs their own PulseAudio server. This server needs to be configured according to the seat the user is at. We therefore need to prepare per-seat configurations. But pointing the users' PulseAudio servers to the correct files is not trivial: while the config file can be provided on the command line, PulseAudio servers tend to be started as needed by the programs that output audio and we don't have much control over their command lines. A more persistent solution is then to point the default per-user configuration file to a seat-specific config file.

First, we prepare the seat-specific files in /etc/pulse. Change the example default.pa file provided by pulseaudio to disable the udev autodetection module, then add the sound cards as below.

The specification is straightforward. The options for the HDMI sound cards can be obtained by running a pulseaudio instance with autodetection and dumping its settings.

Next, we configure the desktop manager to choose the right configuration at login. The XDM-based desktop managers such as KDM have an file which runs after the X server starts but before the user's session is started. It runs as root, but points the HOME variable to the user's home. More information is available in the XDM's man page.

This script simply checks whether the $HOME/.pulse/default.pa file is a symlink and whether it points to the correct config file. If not, it recreates the correct symlink. It then restarts the PulseAudio daemon; they tend to hang around.

To prevent PulseAudio and possibly other user-launched audio servers such as Music Player Daemon from running while the user is logged in and preventing audio access to other users, we can stop them using the Xreset script.

Single sound card per seat
For the ones who don't like PulseAudio and want to assign sound card per seat, here is possible way. Verify is installed or the system. Take note that the output of will decide which card to attach to which seat so make sure you attach the cards in the right order.

All users should have at least the following file in their home folder (thanks to the guys at alsa mailing list for the help):

this file defines which sound card to use.

Now under, put the next file:

Now whenever a user logs in to a seat, the user's local will be updated to the seat's sound card.

There is another more user friendly option, this script: http://www.4shared.com/file/blo0ratQ/multi_seat_alsa_utils.html will allow user to assign a specific card to a seat. the script assumes that the num val of DISPLAY represents the seat's id. just put it in /usr/bin as root:users, 754, run it as root and assign a each seat a card.

Next put in any user's home the .asoundrc from before and relog into the session.

If one needs to use different devices under the same card (for example, seat 0 uses O\B sound card and seat 1 uses the igp to stream movies to tv, the hdmi card is part if the O/B card), this script http://www.4shared.com/file/SxouTU-_/multi_seat_alsa_utils_4.html supports it.

The latest version of the script can be found at http://pastebin.com/nUwsxYEx

Bypassing polkit
As of early 2012, ConsoleKit fails to serve its purpose of identifying which users are physically preset. It can only track one local user, which is the one using the first seat. The other users are identified as "inactive", which causes polkit to deny them access to local resources. Put simply, only the first seat can perform actions such as mounting a USB memory device through udisks (which is the interface desktop environments use).

Implementing a good security policy governing local users is difficult. While it may be hoped that systemd will at some point provide a better solution, it is currently best to assume that all local users are trusted.

Sadly, major desktops now depend on polkit and will force its installation. As a workaround, we can enable access to local devices to inactive users. Create a new policy file:

This will allow those users that are in the plugdev group to perform actions in the udisks category. Available actions can be listed using the "pkaction" tool, so it is easy to add rules for other categories.