Multiseat

Le multiseat est la possibilité d'offrir un accès simultané à un même ordinateur pour plusieurs utilisateurs physiques avec un clavier, une souris, un écran dans des sessions graphiques étanches pour chacun.

Multiseat

C'est censé réduire la charge de maintenance de l'installation en diminuant le nombre d'OS en mutualisant les installations de programme. Mais dans un premier temps, au contraire, cela a augmenté la difficulté :'(.

01/2009 : j'ai testé le déport d'affichage à partir d'une 2e machine se connectant à Olive. J'ai utilisé Wimpy pour tester la solution en lançant X via un :

X :1 -query olive

X démarre, les applications graphiquement légères fonctionnent bien mais OpenOffice est lent et saccadé et les vidéos ne se jouent pas :/. C'est “normal”, ce n'est qu'un déport d'affichage, le rendu est calculé sur le serveur puis transmis via le réseau au client pour un bête affichage à la différence d'un X avec une carte graphique locale qui déleste le processeur d'un certains nombre de tâches.

Déport d'affichage

Intégration difficile

02/2009 : ce 2e essai nécessite une carte graphique dédiée à chaque place physique.

Par contre, ma carte mère, ne supporte l'utilisation de sa carte graphique intégrée en plus d'une carte additionnelle (limitation du BIOS :'( ) J'ai ajouté une GeForce 7600 GT en PCI-Express et une GeForce FX 5200 (elle chauffait trop) Ati Radeon 7000 sur bus PCI.

Geforce 7600 GT PCI-Express   Ati Radeon 7000 PCI

La version 7.2 de Xorg inclus des options permettant de lancer plusieurs X sur un même VT à l'aide de des options -sharevts et -novtswitch. Cela autorise plusieurs instance de X à s'exécuter réellement simultanément à la différence du lancement traditionnel de plusieurs X sur des displays différents mais sur un même VT et nécessitant de basculer de l'un à l'autre avec les touches CTRL + ALT + F7 ou CTRL + ALT + F8.

Par contre, l'intégration du multiseat dans Debian Lenny est inexistante. Après avoir passé plusieurs très longues soirées à tenter de faire marcher l'ensemble, voici l'état des lieux :

  • il faut configurer le bios pour démarrer sur la carte graphique du bus PCI, sinon celle ci n'est reconnue qu'avec 0 Kbits de ram…
  • à cause de ce paramétrage, il faut lancer un X qui “ping” les cartes graphiques avec l'option -probeonly afin d'initialiser les cartes graphiques;
  • Pour démarrer 2 serveurs avec un même fichier de configuration, il faut spécifier dedans des sections ServerLayout pour chaque place en définissant écran, clavier, souris;
  • GDM n'est pas en mesure de lancer 2 X avec 2 utilisateurs différents, il faut alors s'en passer et tout faire à sa place : lancement du X avec le DISPLAY, le user, le ServerLayout correspondant, lancement du gestionnaire de bureau…

Le problème de cette configuration est qu'elle était très fragile : une mise à jour sur 2 des paquets Xorg ou des drivers NVidia la cassait. A chaque problème, le WAF prends une claque :(. Au final, j'ai verrouillé les mises à jour pour empêcher les problèmes. Mais bien sûr avec le temps, ma distribution vieillissait, je ne pouvais plus installer de nouveaux paquets sans devoir tout mettre à jour…

Configuration manuelle

Voici mes fichiers de configurations et scripts :

le script de démarrage /etc/init.d/multiseat lance les différentes instances de X de la manière suivante :

  • lecture du fichier de configuration /etc/multiseat.conf;
  • pour chaque seat lue, invoque startx-autologin avec le user donné et en lui passant le display et layout;
  • startx-autologin est en charge de lancer le X et de la relancer lorsqu'il plante avec un maximum de 3 tentatives;
  • /etc/X11/xorg.conf.multiseat est la conf de X et contient l'agencement des places Place > Ecran > Carte graphique > Clavier > Souris. Il y a également le chemin du device précis dans /dev pour la souris ou le clavier.
/etc/multiseat.conf
[place1]
layout=Layout1
user=user1
display=:0
 
[place2]
layout=Layout2
user=user2
display=:1
/etc/init.d/multiseat
#! /bin/bash
### BEGIN INIT INFO
# Provides:          multiseat
# Should-Start:      console-screen kbd acpid dbus hal network-manager
# Required-Start:    $local_fs $remote_fs x11-common
# Required-Stop:     $local_fs $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: xorg autologin multiseat
### END INIT INFO
 
# Do NOT "set -e"
 
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
CONF="/etc/multiseat.conf"
DESC="Multiseat"
NAME=multiseat
DAEMON=/usr/local/bin/startx-autologin
 
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
 
#exit 0
 
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
 
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
 
declare -Ax layout
declare -Ax user
declare -Ax display
 
#
# Function that starts the daemon/service
#
do_start()
{
    ARG_SEAT=$1
    # returns
	#   0 if daemon has been started
    echo -n " ${ARG_SEAT}"
    rm -f /var/run/xorg-autologin-${layout[$ARG_SEAT]}.exit
    /bin/su -l ${user[$ARG_SEAT]} -c "${DAEMON} ${display[$ARG_SEAT]} ${layout[$ARG_SEAT]}" >/home/${user[$ARG_SEAT]}/.xsession-errors 2>&1 &
    return 0
}
 
#
# Function that stops the daemon/service
#
do_stop()
{
    ARG_SEAT=$1
	# Return
	#   0 if daemon has been stopped
	#   1 if daemon was already stopped
	#   2 if daemon could not be stopped
	#   other if a failure occurred
    echo -n " ${ARG_SEAT}"
    touch /var/run/xorg-autologin-${layout[$ARG_SEAT]}.exit
    pkill -u ${user[$ARG_SEAT]} startx-autologin
    pkill -u ${user[$ARG_SEAT]} xinit
	return 0
}
 
 
print_usage()
{
    	cat >&2 << EOC
Usage: $SCRIPTNAME 
    start [seat]
    stop [seat]
    restart [seat]}
EOC
    	exit 3
}
 
read_conf()
{
    while IFS='= ' read var val
    do
        if [[ $var == \[*] ]]
        then
            section=$var
        elif [[ $val ]]
        then
            eval "$var$section=$val"
        fi
    done < $CONF
}
 
check_user() 
{
    if [ "${user[$1]}" == "" ];
    then
        echo -n " seat '$1' unknown" 
        log_end_msg 1
        exit 1
    fi
}
 
read_conf
 
case "$1" in
  start)
	log_daemon_msg "Starting $DESC" 
    set +e
    if [ "$#" -eq 2 ];
    then
        check_user $2
        do_start $2
    elif [ "$#" -eq 1 ];
    then
        for K in "${!layout[@]}"; do do_start $K; done
    else 
    	echo 
        print_usage 
    fi
    set -e
	case "$?" in
		0|1) log_end_msg 0 ;;
		2) log_end_msg 1 ;;
	esac
  ;;
  stop)
	log_daemon_msg "Stopping $DESC"
    if [ "$#" -eq 2 ];
    then
        check_user $2
        do_stop $2
    elif [ "$#" -eq 1 ];
    then
        for K in "${!layout[@]}"; do do_stop $K; done
    else 
    	echo 
        print_usage 
    fi
    set -e
	case "$?" in
		0|1) log_end_msg 0 ;;
		2) log_end_msg 1 ;;
	esac
  ;;
  restart)
        shift
        $0 stop $*
        sleep 1
        $0 start $*
  ;;
  *)
    print_usage
	;;
esac
 
:
/usr/local/bin/startx-autologin
#!/bin/sh
NB_RETRY=3
EXIT_FILE=/var/run/xorg-autologin-$2.exit
 
start() {
    NB=0
    while [ $NB_RETRY -eq 0 ] || [ $NB -lt $NB_RETRY ]
    do
        echo "Exec nb $NB $*" 
        $*
        #if [ $? -eq 0 -o -f $EXIT_FILE ];
        # le process est tjs relance
        if [ -f $EXIT_FILE ];
        then
            exit
        fi
        ps=$(ps auxww | grep -v grep)
        # reboot
        echo $ps | grep -q "/etc/rc.d/rc 6"
        ret=$?
        if [ $ret -eq 0 ]
        then
            exit
        fi
        # shutdown
        echo $ps | grep -q "/etc/rc.d/rc 0"
        ret=$?
        if [ $ret -eq 0 ]
        then
            exit
        fi
        NB=$(($NB + 1))
        sleep 2
    done
}
 
CMD="/usr/bin/xinit -- $1 -layout $2 -sharevts -novtswitch"
start $CMD
/etc/X11/xorg.conf.multiseat
Section "ServerLayout"
    Identifier     "Layout2"
    Screen 0       "LCD2" 0 0
    InputDevice    "Mouse2" "CorePointer"
    InputDevice    "Keyboard2" "CoreKeyboard"
EndSection
 
Section "ServerLayout"
    Identifier     "Layout1"
    Screen 0       "LCD1" 0 0
    InputDevice    "Mouse1" "CorePointer"
    InputDevice    "Keyboard1" "CoreKeyboard"
EndSection
 
Section "Files"
    FontPath        "/usr/X11R6/lib/X11/fonts/local/"
    FontPath        "/usr/X11R6/lib/X11/fonts/misc/"
    FontPath        "/usr/X11R6/lib/X11/fonts/75dpi/:unscaled"
    FontPath        "/usr/X11R6/lib/X11/fonts/100dpi/:unscaled"
    FontPath        "/usr/X11R6/lib/X11/fonts/Type1/"
    FontPath        "/usr/X11R6/lib/X11/fonts/Speedo/"
    FontPath        "/usr/X11R6/lib/X11/fonts/75dpi/"
    FontPath        "/usr/X11R6/lib/X11/fonts/100dpi/"
EndSection
 
Section "InputDevice"
    Identifier     "Mouse1"
    Driver         "evdev"
    Option         "Protocol" "auto"
    Option         "Device" "/dev/input/by-id/usb-Microsoft_Corporation_Microsoft_®_Laser_Mouse_6000-event-mouse"
    Option         "Emulate3Buttons" "no"
    Option         "ZAxisMapping" "4 5"
    Option         "GrabDevice" "on"
EndSection
 
Section "InputDevice"
    Identifier     "Keyboard1"
    Driver         "evdev"
    Option         "Device" "/dev/input/by-path/platform-i8042-serio-0-event-kbd"
    Option         "XkbModel" "microsoft"
    Option         "XkbRules" "xorg"
    Option         "XkbLayout" "fr"
    Option         "XkbOptions" "altwin:super_win"
    Option         "GrabDevice" "on"
EndSection
 
Section "InputDevice"
    Identifier     "Mouse2"
    Driver         "evdev"
    Option         "Protocol" "auto"
    Option         "Device" "/dev/input/by-id/usb-Logitech_USB_Optical_Mouse-event-mouse"
    Option         "Emulate3Buttons" "no"
    Option         "ZAxisMapping" "4 5"
    Option         "GrabDevice" "on"
EndSection
 
Section "InputDevice"
    Identifier     "Keyboard2"
    Driver         "evdev"
    Option         "Device" "/dev/input/by-id/usb-_USB_Keyboard-event-kbd"
    Option         "XkbModel" "pc105"
    Option         "XkbLayout" "fr"
    Option         "GrabDevice" "on"
EndSection
 
Section "Monitor"
    Identifier     "Monitor_iiyama"
    ModeLine       "1680x1050_60.00" 147.14 1680 1784 1968 2256 1050 1051 1054 1087 -hsync +vsync
    ModelName      "Idek Iiyama PLE2003WSV"
    HorizSync       31.0 - 83.0
    VertRefresh     55.0 - 76.0    
EndSection
 
Section "Monitor"
    Identifier     "Monitor_iiyama2"
    VendorName     "Unknown"
    ModelName      "Idek Iiyama PL2480H"
    HorizSync       30.0 - 83.0
    VertRefresh     55.0 - 76.0
EndSection
 
Section "Device"
    Identifier     "nvidia 7600 GT"
    Driver         "nvidia"
    Option         "NoLogo" "true"
    Option         "NvAGP" "1"
    Option         "TripleBuffer" "true"
    BusID          "PCI:1:0:0"
EndSection
 
Section "Device"
    Identifier     "radeon 7000"
    Driver         "ati"
    Screen         0
    BusID          "PCI:7:0:0"
EndSection
 
Section "Screen"
    Identifier     "LCD1"
    Device         "nvidia 7600 GT"
    Monitor        "Monitor_iiyama2"
    DefaultDepth    24
    Option         "metamodes" "1920x1080_60_0 +0+0; 1680x1050_60_0 +0+0"
    SubSection     "Display"
        Depth       24
    EndSubSection
EndSection
 
Section "Screen"
    Identifier     "LCD2"
    Device         "radeon 9200"
    Monitor        "Monitor_iiyama"
    SubSection     "Display"
        Viewport   0 0
        Modes      "1680x1050_60.00" "1440x900"
    EndSubSection
EndSection
 
Section "ServerFlags"
    Option  "HandleSpecialKeys" "Always"
    Option  "AutoAddDevices"    "off"
    Option  "AutoEnableDevices" "off"
    Option  "AllowEmptyInput"   "off"  
EndSection

Works out of the box

J'ai refait ma configuration multiseat sous Ubuntu en m'aidant de cette doc.

La configuration s'est grandement simplifiée depuis Xorg 7.2 et est maintenant très bien intégrée dans la distribution, il suffit de :

  • tagguer les périphériques souris, clavier, carte graphique à l'aide de règles UDEV
  • ajouter un fichier de configuration Xorg par seat
  • configurer LightDM

Plus besoin d'écrire de script de lancement de X ni de se battre avec un “ping” des cartes graphiques pendant le boot…

Configuration

Voici un extrait de mes règles UDEV, l'objectif est d'identifier le périphérique voulu et de le tagguer avec seat0, seat1 ou seat2. Pour les cartes graphiques, il faut ajouter en plus le tag master-of-seat.

/etc/udev/rules.d/99-multiseat.rules
# périphériques HID
# Souris Logitech noir
ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c05b", ENV{ID_SEAT}="seat1", TAG+="seat1"
 
# Souris Microsoft Laser 6000
ATTRS{idVendor}=="045e", ATTRS{idProduct}=="00f0", ENV{ID_SEAT}="seat0", TAG+="seat0"
 
# Souris verte
ATTRS{idVendor}=="15d9", ATTRS{idProduct}=="0a4f", ENV{ID_SEAT}="seat2", TAG+="seat2"
 
# il est également possible d'affecter une clés USB à une place
ATTRS{idVendor}=="13fe", ATTRS{idProduct}=="3e00", ENV{ID_SEAT}="seat1", TAG+="seat1"
# ou même un port USB particulier d'un HUB
DEVPATH=="/devices/pci0000:00/0000:00:14.0/usb3/3-4/3-4.1/3-4.1.4/*", ENV{ID_SEAT}="seat1", TAG+="seat1"
 
# cartes graphiques
# carte nvidia
SUBSYSTEM=="drm", KERNEL=="card[0-9]*", ATTRS{vendor}=="0x10de", TAG+="master-of-seat"
DEVPATH=="/devices/pci0000:00/0000:00:01.0/0000:01:00.0/*", ENV{ID_SEAT}="seat0", TAG+="seat0"
DEVPATH=="/devices/pci0000:00/0000:00:1c.4/0000:06:00.0/*", ENV{ID_SEAT}="seat1", TAG+="seat1"
 
# carte intel integrée
SUBSYSTEM=="drm", KERNEL=="card[0-9]*", ATTRS{vendor}=="0x8086", TAG+="master-of-seat"
DEVPATH=="/devices/pci0000:00/0000:00:02.0/*", ENV{ID_SEAT}="seat2", TAG+="seat2"

Pour identifier un périphérique plusieurs méthodes peuvent être utilisées :

udevadm info --export-db

Ne choisir que les devices avec TAGS=:seat: et récupérer les valeurs ATTRS{idVendor} et ATTRS{idProduct}.
ou lancer

udevadm monitor

puis brancher le device et tenter de l'identifier. Pour avoir tout les détails :

udevadm info -a -p /devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.2

Optionnellement, un fichier de conf Xorg par seat permet de surcharger la configuration par défaut.

/etc/X11/xorg.conf.d/90-seat0.conf
Section "Device"
    Identifier "card0"
    Driver "nvidia"
    Option "NoLogo" "True"
    MatchSeat "seat0"
    Option "ProbeAllGpus" "false"
    BusID "PCI:1:0:0"
EndSection
/etc/X11/xorg.conf.d/90-seat2.conf
Section "Device"
    Identifier  "card0"
    Driver      "intel"
    BusID       "PCI:0:2:0"
EndSection

LightDM supporte désormais le lancement de plusieurs sessions en parallèle avec le user correspondant et les bonnes options pour X :

/etc/lightdm/lightdm.conf
[LightDM]
logind-load-seats=true
logind-check-graphical=true
 
[Seat:seat0]
xserver-config=/etc/X11/xorg.conf.d/90-seat0.conf
autologin-user=user1
 
[Seat:seat1]
xserver-config=/etc/X11/xorg.conf.d/90-seat1.conf
autologin-user=user2
 
[Seat:seat2]
autologin-user=user3

Aster

Pour jouer à Battlefield, il faut impérativement un Windows, donc j'ai mis un dual boot. Mais pendant que je joue, le WAF est au plus bas :'(. J'ai recherché des logiciels faisant la même chose sous Windows et j'ai acheté Aster. Via une interface graphique assez simple, il est possible de configurer plusieurs utilisateurs tout en gardant la possibilité de jouer sur la place principale.

Configuration

L'interface permet d'affecter un clavier / souris / carte vidéo à une seat et de sélectionner les utilisateurs correspondant. L'ensemble démarre automatiquement au boot de Windows.

VM Ubuntu

Le WAF n'est pas encore suffisant : l'accès sous Windows aux fichiers stockés sur la partition Linux n'est pas simple. De plus, je ne voulais pas imposer un changement d'interface graphique Windows ou Gnome lorsque je joue. J'ai testé et mis en place une VM Ubuntu possédant un disque virtuel mappé sur la partition Linux du disque physique.

Ainsi, je démarre un VM Ubuntu qui monte le /home du disque physique en lecture / écriture comme un disque normal. En installant les mêmes paquets entre l'Ubuntu natif et l'Ubuntu VM, l'interface graphique est identique entre les 2. De plus, la VM s'affiche en plein écran et la perte de réactivité est à peine perceptible sur la VM.

La procédure pour créer un disque virtuel sur un disque physique est censée être simple :

C:\Program Files\Oracle\VirtualBox>VBoxManage.exe internalcommands createrawvmdk -filename "C:\LinuxRAW.vmdk" -rawdisk "\\.\PhysicalDrive0" -partitions 3

Cela créer un disque vmdk de quelques ko qui est ensuite utilisable dans n'importe quelle VM. Il faut faire très attention à ne pas mettre un disque virtuel qui accède aux mêmes données que l'OS hôte : les systèmes de fichiers ne sont pas conçus pour être lus et écrit par 2 noyaux (Windows ou Linux) en même temps.

Malheureusement, la commande plante :

VBoxManage.exe: error: Cannot open the raw disk '\\.\PhysicalDrive0': VERR_SHARING_VIOLATION
VBoxManage.exe: error: The raw disk vmdk file was not created

J'ai ce bug qui n'était toujours pas résolu :@. Pas de possibilité de créer le disque sous Windows ⇒ reboot sous Linux et création du disque virtuel sur la partition qui héberge mon linux en priant très fort pour qu'il n'accède pas aux données car le disque est déjà monté et … ça marche sans casse ! :whew:

La configuration de la VM Ubuntu sous Windows est traditionnelle avec 3 Go de RAM, ce qui est suffisant pour un Firefox + OpenOffice. Les disques sont finalement les suivants :

  1. 1 disque virtuel à allocation dynamique contenant le / de la distribution;
  2. 1 disque virtuel raw mappé sur le LVM de mon linux contenant notamment le /home.

Configuration Virtual Box pour Ubuntu avec un disque raw

En sus, pour m'assurer une bonne fluidité dans les jeux, je n'ai alloué que 90% maximum de la CPU à cette VM :-p.

Bridage de la CPU