Terminals, Gettys and Display Managers

Categories: Linux

Overview

The concept of a “terminal” (aka tty) for text-based input and output is encountered in many corners of unix/linux - even when running in graphical mode. Interestingly, graphical login (via a Display Manager) is also quite closely related to terminals. This article looks at the concept of terminals (including virtual terminals), how Linux supports them, and how software interacts with them.

Topics covered by this article include:

  • Old serial-attached terminals
  • USB keyboards and text-mode screen output
  • Virtual terminals
  • Multi-seat systems
  • KVM keboard switches
  • Telnet/ssh servers
  • The System Consoles
  • The getty program and PAM authentication
  • The relevant /dev nodes (eg /dev/tty*, /dev/ptmx, /dev/vcs*) and the kernel code behind them
  • Graphical Display Managers and the XDMCP protocol

Text Mode and Serial Terminals

In the early days of unix, a computer was physically quite large. It usually included multiple serial-io ports, with a terminal (aka teletype or tty) attached to each in order to support multiple concurrent users (“timesharing”). Each terminal was a single device that included a keyboard and a text-mode display. Pressing a key on the keyboard would cause a single byte to be sent over the serial cable to the computer, and each byte written by the computer to the serial port would be received by the terminal and update a local display buffer. The display buffer was an array of X*Y bytes (characters) which was then rendered on the terminal’s screen using a built-in font. The terminal had a concept of “cursor position” which is where new bytes (characters) received from the computer were written into the buffer; some special characters could be used to update the cursor position (eg move up a line, or erase the character preceding the cursor - aka backspace). Unfortunately, different terminals could send different codes for specific keypresses (eg backspace key), and interpreted some characters differently when displaying (particularly related to cursor movement), so the computer needed to be told which “terminal type” was connected to each serial IO port. The serial connection also had to be appropriately set up, eg the computer and terminal had to use the same baud rate and stop-bits settings. Some ttys supported not just one-byte-per-character, but instead two bytes per character, where one of the bytes specified character attributes such as color, bold/italic, etc. A common example of such terminals is the VT-100; the V stands for Video (not virtual) because the terminal has a screen.

Serial ports on computers can still be used to attach “terminals”. Network routers sometimes still have a serial-port, and run software which allows a “terminal” to be attached to that port - though the terminal is now usually a computer running a terminal emulator application. Some tablets/phones also have some connection that can be configured as a serial-port for a terminal, mainly in order to perform debugging of serious system issues. True serial ports are becoming rare on desktops/laptops now, but there are USB-to-serial adapters that (with the correct software) can be used to connect a device with a USB port to a device that provides a serial port.

Unix/Linux systems still provide (and heavily rely on) this terminal/tty concept - ie a device that can provide a stream of bytes (keystrokes) and consume a stream of bytes (characters to display). Of course modern systems typically have a USB-attached keyboard, and either a graphics card or graphics support built into the CPU. The kernel therefore ensures that on boot such keyboards/graphics-devices appear to be a terminal - ie provide a device file under /dev which can be read/written by software as if it were an attached serial terminal; the kernel maps USB keyboard events into “terminal keyboard strokes” that software can read, and forwards characters written to the device to the appropriate graphics device driver.

Virtual Terminals

Most people do not have a set of physical terminals hanging around the house or office, but it is often convenient to have multiple independent login sessions on the same computer. Linux therefore sets up multiple virtual terminals on boot. A device-file is created under /dev for each virtual terminal, and these can be opened/read/written by userspace software as if it were a real serial terminal. However only one of these virtual terminals is “active” at a time; keyboard strokes coming from the “console keyboard” are forwarded over the currently-active virtual terminal device file. Each virtual terminal has its own “text display buffer”, but only the buffer for the currently-active virtual terminal is connected to the actual graphics system. Special keystrokes (usually ctrl-alt-F{n}) changes the active virtual terminal, ie tells the kernel to remap keyboard input events to a different virtual terminal device file, and to reconfigure the graphics system to match the settings for the newly active virtual terminal.

Any virtual terminal can be in “graphics mode” as well as “text mode”, in which case switching to that virtual terminal is a more significant reconfiguration of the graphics driver. This used to be quite tricky/unreliable when graphics modes were set via X user-space drivers, but the in-kernel KMS (kernel modeswitching) drivers have fixed that issue.

The default number of virtual terminals is 7. By convention, a graphical login “greeter” is configured to run on virtual terminal 7 - ie ctrl-alt-F1 (or F2..F6) switches to a text console while ctrl-alt-F7 switches to the “graphical session”.

Background and Foreground Processes

Multiple applications can potentially be connected to the same TTY (virtual or real). In particular, when a shell (eg the bash program) is using a TTY for input/output and it starts a process “in the foreground” then that new process inherits the STDIN/STDOUT of its parent process (the shell). However keystrokes should only go to one process, and to avoid confusion only one process should be able to write to the TTY display. Therefore the kernel scheduler and kernel-level TTY driver tracks all processes using a specific TTY, and has the concept of a “foreground job” - ie the process(es) which receive(s) input and may generate output. When any process that is not “foreground” tries to read from the TTY device, or write to it, that call blocks until the device becomes the “foreground process”.

See this companion article for more detail on managing foreground and background processes.

Thin Clients and Multiseat Setups

Modern computers are powerful enough to multiple users at the same time. There are two modern ways to share a powerful computer across multiple users:

  • thin-client (graphical terminals)
  • multiseat

In the thin client approach, each users sits in front of a device which is a full computer - CPU, ram, keyboard, mouse, screen, GPU and ethernet port. However the CPU is usually a cheap, low-power one - just enough to run a basic operating system. Usually such a device does not have any local storage (hard-drive), instead having a small amount of flash storage which allows it to boot to the point that it can load an operating system over the network. It then runs that operating system locally, and a graphical display server (eg Xorg or Wayland). All other applications run remotely on the shared server, sending their graphical output back to the terminal. This is a very similar architecture to the old text-based terminals except that an IP network is used for terminal/server communications rather than a serial line, and the terminal is somewhat smarter. This architecture was quite popular around the year 2000 (see Sun’s “the network is the computer” concept for example).

A cheaper approach than thin-client is to attach multiple keyboards, mice, and screens to a single physical computer; this is called “multi-seat”. These days, even basic desktop systems are powerful enough to support multiple users concurrently as long as those users are not doing CPU-intensive tasks. Attaching multiple USB-based keyboards and USB-based mice to a single computer is not complicated. Attaching multiple displays is currently more difficult, and is usually done by inserting multiple PCI/PCIe graphics cards into the shared computer. Each (keyboard,mouse,screen) combination is referred to as a “seat”. When properly configured, the linux-based operating system on the shared computer can then support multiple “seats” (users) concurrently, as if each “seat” were attached to a separate computer. The recently-specified USB Type C connector and the associated DisplayPort Alternate Mode allows video data to be piped over a USB connection rather than a dedicated video cable (eg HDMI or DisplayPort) which should make multi-seat setups simpler. The latest graphics cards (2015/2016) are also gaining “virtualization” features which allow a single card to be partitioned across multiple users (similar to the way that “preemption” allows a CPU to be “timeshared” across multiple processes); this could make multi-seat support possible with a single graphics card. Of course all “seats” need to be physically within a few meters of the shared computer - unlike the “thin client” approach where the users can be much further away.

The thin-client/multi-seat approaches are particularly popular in schools and libraries, reducing setup costs and system administration effort.

A slightly different approach is taken by Google’s chromebook devices. They are full computers, and have local storage which holds both an operating-system and optionally some user files. They also have reasonably powerful CPUs and reasonable amounts of RAM. However they have few or no applications installed locally. Instead, they mostly run a web browser locally which then connects to remote http servers in order to provide services that the user wants. The remote servers may end up doing much of the work, or the remote server may provide large amounts of javascript code that the chromebook device then runs locally.

Thin-client/multi-seat setups are logically related to the early “terminals”, but are not really related to the topic of this article and so will not be discussed further here.

While on the topic of multi-seat systems, it seems relevant to mention the reverse problem - needing to control multiple physical computers from a single keyboard/mouse/screen. This happens most frequently in small “server rooms” which may contain a few to a few dozen “server” computers. Such computers are normally accessed over a network, eg via SSH text sessions, graphical remote desktop applications, or similar. But there are a few occasions where such servers really need to be accessed directly - eg when there are network problems. Having to make space for a keyboard/mouse/screen for each computer in the server-room is costly and a nuisance; the alternative of plugging in the keyboard/mouse/screen only when necessary is also a nuisance - particularly for rack-mounted servers where the connections are on the rear of the device. In this situation, a KVM switch can be used; it is a box which has multiple groups of (keyboard,mouse,video) sockets, where each group is permanently connected to a target computer (server). A single real keyboard/mouse/screen are then attached and a physical dial selects which “group” the real devices get directed to, ie which server they are currently connected to. The arrival of USB Type C connectors supporting DisplayPort Alternate Mode may simplify KVM switches in future. KVM switches are invisible to the operating-system, so are not discussed further in this article.

Telnet and SSH Sessions

A common way to obtain a text-mode “login” on a computer is to connect to it over a network from a different computer (or the same computer for experimentation). The common protocols used are telnet (insecure and mostly obsolete) or ssh. This requires the target computer to be running a telnet server or an ssh server - or for it to configure something like inetd or systemd-init to start such a server on an incoming network connection to a specific port.

Remote graphical logins are discussed later in this article.

The System Console

One of the terminals attached to a computer (whether real or virtual) is considered to be the “system console”; the kernel displays messages on this terminal (including the annoying “thread terminated” messages which are not errors). On a typical desktop running graphics, virtual terminal #1 (ctrl-alt-F1) is usually the “system console”.

Pseudoterminals

The kernel provides a mechanism for a userspace application to emulate an attached serial terminal; this is called a “pseudo-terminal” (aka PTS). The primary users of this are applications such as “xterm” which provide a terminal in a graphical window. The text-mode applications started from the window (particularly text-mode shells) should think they have been started from a traditional text-mode login session which is attached to a traditional terminal device.

Userspace code sets up a pseudoterminal by opening a character-special file, usually “/dev/ptmx” or “/dev/pts/*”.

Pseudoterminals can act as “controlling terminals”, participate in “job control”, etc., as real or virtual terminals would. See this companion article for more on these topics.

Getty and PAM

The getty program opens a tty device, writes a prompt like “login:” to it, and waits for a username/password to be entered. It then validates the username/password (usually by invoking functions from the PAM library), and executes (ie replaces itself with) whatever “initial program” is associated with that user account - usually a shell.

When the user logs out (ie that initial program terminates), something needs to run a new instance of getty on that same tty device, so that another login prompt is displayed. A getty instance also terminates when a user enters an invalid username, or fails to enter a suitable password after N attempts; again some external component must start a replacement getty process on the same terminal.

In an operating system using systemd-init, the init system automatically does this when the control group for that login session becomes empty.

For systems using sysv-init, file /etc/inittab includes entries for getty with the “respawn” flag; sysv-init will then take care of restarting any getty process that terminates.

On my system, looking for processes with a “controlling tty” produces:

$ ps -efl | grep tty
4 S root       869     1  0  80   0 -  3602 ?      Oct05 tty1     00:00:00 /sbin/agetty --noclear tty1 linux
4 S root       921   887  0  80   0 - 394108 ?     Oct05 tty7     00:36:02 /usr/bin/Xorg :0 -novtswitch ... vt7
...

Note that on this systemd-init-based system, although the kernel has created 7 virtual terminals, getty is only running on virtual terminal tty1 - and an X server is associated with virtual terminal vt7 (aka tty7). Systemd-init starts new getty instances on-demand; pressing ctrl-alt-F2 then switching back to another terminal and running “ps” again will show a getty instance now running on tty2. Other init-systems start a getty process on a fixed number of virtual terminals on startup.

The PAM (Pluggable Authentication Module) framework consists of a configuration-file and a library that uses it. Any application that wishes to confirm an identity (authentication), or to verify the rights of some identity (authorization) may link to the pam library and make relevant calls into it, passing relevant parameters such as username/password. The pam configuration-file then defines which tests will be applied, and which “side-effects” should be triggered. Modules exist to test usernames/passwords against local config-files (eg /etc/password or /etc/shadow), to make calls to remote LDAP servers, etc. Modules also exist to perform side-effects such as informing a daemon process of successful logins (logind), or adding names/IP addresses to blacklists on repeated unsuccessful logins.

The getty program is obviously a primary user of the PAM framework, but graphical logins via a “display manager” also use PAM.

Kernel Device Drivers For TTYs

A Quick Review of Device Drivers

Userspace can communicate with specific kernel modules via the “device” mechanism. Kernel code associates some callbacks with a device number range of form (major-from, minor-from, count). Usually something then ensures that a special device node (ie a file) is created in the filesystem which is tagged with a (major,minor) number pair; the “something” is usually devtmpfs or udev, but such files can also be created via the OS init-system or even by the sysadmin “by hand”. Such files are called char-special or block-special device files. Userspace code can then open such a file, obtaining a file-descriptor that is “linked to” whichever set of callbacks was registered for that (major,minor) pair. Any systemcalls made using that filedescriptor trigger those callbacks, and pass the minor-device-number as a parameter. See the Linux Device Drivers book for more details.

This approach is used both for “character-based” and “block-based” devices, the major/minor numbers are in separate “namespaces”, ie char-device major number 1 is quite separate from block-device major number 1. The kernel functions used by character/block drivers to reserve ranges and associate callbacks with them are also different. As terminal(tty) drivers are character-based, the rest of this section (and indeed this whole article) discusses only character-based devices/drivers.

Although it is technically possible for two ranges with the same major-number to be registered by different “drivers”, in practice that is rarely done - ie different drivers don’t usually share a common major-device-number (though some drivers do use more than one major device number). Interestingly, the TTY_MAJOR value is such a case: the “vt” driver uses range (TTY_MAJOR, 0..63) while the various serial-port drivers use range (TTY_MAJOR, 64..255).

Some kernel drivers (older and more “important” ones) use a statically-allocated major device number, while others allocate an unused one at runtime. Static major numbers are allocated from 1 upwards in usr/include/linux/major.h while dynamically-allocated major numbers currently start from 255 and count down (though this might change at some time). The kernel function register_chrdev_region is used to reserve static ranges, while alloc_chrdev_region is used to allocate ranges dynamically. Once a range has been allocated, function cdev_add binds chrdev_open callbacks to that range.

File /proc/devices lists all the major/minor device number ranges reserved by kernel code calls to register_chrdev_region or alloc_chrdev_region. Each call to these methods specified a different “name”, and that name is also shown in /proc/devices. Some kernel code uses range-names that look like file-paths (eg “/dev/tty”) but that doesn’t necessarily mean there is such a file in the filesystem. Sadly, the output only shows the “major” number of each range-entry.

AFAIK, there is no direct way of determining which kernel code (driver) reserved which major/minor device ranges - but grepping the kernel sourcecode for _chrdev_region and the range-name from /proc/devices finds the appropriate place pretty quickly. See also function register_chrdev which is defined inline in linux/include/linux/fs.h as a wrapper which reserves a block of minor numbers and calls cdev_add at the same time.

Statically-allocated major device numbers that are of interest for this article include:

  • VCS_MAJOR = 7
  • TTY_MAJOR = 4
  • TTYAUX_MAJOR = 5
  • UNIX98_PTY_MASTER_MAJOR = 128..135 // aka “ptm”
  • UNIX98_PTY_SLAVE_MAJOR = 136 // aka “pts”

The /proc/devices content includes in the “Character devices” section:

  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
128 ptm
136 pts

and the /dev directory on my system has the following entries related to ttys:

  • VCS_MAJOR devices (ie those with major=7)
    • /dev/vcs{N} – access to screen content (without text attributes) of console #N
    • /dev/vcsa{N} – access to screen content (with text attributes) of console #N
  • TTY_MAJOR devices (ie those with major=4)
    • /dev/tty{N} – has (major=4,minor=N) where 0<=N<64 – “virtual” ttys
  • TTYAUX_MAJOR devices (ie those with major=5)
    • /dev/tty has (major=5,minor=0)
    • /dev/console has (major=5,minor=1)
    • /dev/ptmx has (major=5,minor=2)

As noted above, different devices can theoretically share major-numbers: and in fact there are files with names of form /dev/ttyS{N} referencing (major=4,minor=N+64). These are (on my system) created by the code under “drivers/tty/serial/8250”; while serial-ports are categorised as “tty code”, this isn’t really what this article is about and will not be discussed further here.

Interestingly, the TTYAUX_MAJOR also is shared between two “drivers”: minor numbers 0 and 1 are handled by code in drivers/tty/tty_io.c; opening a char-special file with number (TTYAUX_MAJOR,0) is basically an alias for opening the file /dev/tty{N} where N is the “currently active” tty. Similarly, minor-number 1 gives access to “the currently active console”. However minor-number 2 is instead handled by code in drivers/tty/pty.c; see the section on pseudo-terminals below.

As I currently understand it:

  • The keyboard-drivers and display-drivers form the lowest layer related to terminal-handling.
  • The “virtual console” code then builds on top of those, by creating one or more “vc” datastructures which hold a text-buffer containing screen contents, and a reference to a keyboard device.
  • The “virtual terminal” code builds on the “virtual console”, adding VT102-specific features so that a userspace application expecting to deal with a VT102 will work unchanged.
  • the “pseudo-terminal” code is not really related to any of the above; it does not use a keyboard (data comes from a userspace app) or display device (output goes to a userspace app), nor does it maintain a screen-buffer in memory.

The virtual console code (“vc”)

AIUI, the virtual-console kernel code was originally quite separate from the tty code, hence the distinct acronyms “vc” and “vt”. However a comment in file “vt.c” states “Merge with the abstract console driver .. 1997”; this presumably lead to the somewhat inconsistent use of “vc” and “vt” in this area.

The “vc” code appears to have the following functionality:

  • creates some “struct vc” datastructures to represent consoles
  • listens for keyboard devices being registered, and determines which VC to “bind” it to
  • map from keypresses to unicode-glyph-ids depending on the selected “terminal type” (eg vt100)
  • register “notifier functions” which get invoked when a keystroke is entered at the console keyboard (register_keyboard_notifier). This hook is used by the braille and speakup drivers.
  • map scancodes to keycodes, handle LEDs on keyboards, handle capslock/numlock/key-repeat-rate etc
  • handle copy/paste on console text buffer
  • and other very low-level stuff

Major number VCS_MAJOR is associated wth kernel file drivers/tty/vt/vc_screen.c.

As noted earlier, a terminal’s display has an associated buffer holding an array of characters (and their attributes), of size nrows*ncolumns; these buffers are accessable as char-special files. Each local “virtual terminal” set up by the kernel on boot has a local display buffer, and opening a char-special file with major=VCS_MAJOR and minor=N will give access to the buffer for virtual terminal N, so that the display of that virtual terminal can be read/modified directly. Opening a file with major=VCS_MAJOR and minor=N+128 gives access to the buffer for virtual terminal N, but with char-attribute info too.

On my system, files /dev/vcsN and /dev/vcsaN exist for N=1..7 and are bound to the appropriate major/minor numbers.

A console can be in “raw mode” or “cooked mode”; in raw-mode keystrokes are passed through almost unaltered while in cooked-mode much more “remapping” and handling of special keystrokes is done within the driver.

AFAIK there is no way for userspace to obtain keystrokes directly from this layer; use the vt driver (via the /dev/tty{N}) instead.

The virtual terminal code (“vt”)

As noted earlier, the VT100 terminal (and related products) were very popular; the “V” stands for video. The kernel “vt” (virtual terminal) code emulates a vt102 (video terminal).

Major number TTY_MAJOR is associated with kernel file drivers/tty/vt/vt.c - which registers one range under name “/dev/vc/0”. However (at least on my system) there are no files with names of form “/dev/vc*”; the files are named “/dev/tty*” instead. Presumably this is a side-effect of the vc/vt “code merge” referenced in the previous section.

The vt support builds on top of the vc support, ie each vt datastructure holds a pointer to a vc_data structure from the lower layer. The “current screen contents” is held at the vc layer.

When key sequence “ctrl-alt-F{N}” is used to “switch virtual terminals”, then data from the physical console keyboard gets redirected to a different /dev/tty node, and the display-device is reconfigured to take its data from a different vc screen buffer. However I am not currently sure if that “switching” logic is implemented at “vc” or “vt” level.

The virtual-terminal layer can also wrap a serial port rather than a virtual-console. Question: is there much logic in the tty-driver for this case?

Function register_vt_notifier can be used to register callbacks for events related to any virtual terminal. The braille and speakup drivers use these hooks (in addition to the similar hooks available at the virtual-console layer).

The /dev/tty{N} files can be opened and then read (to obtain keyboard input) or written (to place characters into the associated text buffer). Whether the read actually returns keystrokes or not depends on whether that tty is “active”; the same applies to data written to the tty.

The pseudo-terminal code

The difference between a virtual terminal and a pseudo-terminal is:

  • for a terminal the source of keystrokes is a kernel keyboard device and the sink is a kernel display device;
  • for a pseudo-terminal the source and sink are a userspace program.

Device number (TTYAUX_MAJOR, 2) is normally found as file “/dev/ptmx” and is associated with kernel file drivers/tty/pty.c - although the TTYAUX_MAJOR is shared with file “tty_io.c”. Opening “/dev/ptmx” is the way to allocate a new master pseudo-terminal.

The UNIX98PTY* major numbers are associated with kernel file drivers/tty/pty.c. These provide “pseudo-terminals” so that userspace applications (eg xterm) can emulate terminals that other commandline applications can then talk to.

AFAIK, the /dev filesystem never has an actual file with major-number in the UNIX98PTY* range; these numbers are managed via the /dev/ptmx file instead. Opening the ptmx file registers a new kernel “driver” using a free major number in the UNIX98PTY_MASTER range - though I’m not sure how the caller is supposed to know _which one. See function pty.c:ptmx_open.

Other Information

There are some related files in the /proc filesystem:

  • cat /proc/consoles shows tty0. I’m not sure when there would be more than one console.
  • cat /proc/tty/drivers shows more interesting into.

Also interesting to look at:

  • /sys/devices/virtual/tty/*
  • /sys/devices/virtual/vc/* (major=7)
  • /sys/devices/virtual/vtconsole (major=?)
    • vtcon0 is named “(S) dummy device” - where S means statically-compiled-in?
    • vtcon1 is named “(M) frame buffer device” - where M means dynamically-loaded module?
  • /sys/class/tty
  • /sys/class/vc
  • /sys/class/vtconsole

Text-Mode Session Managers

As an alternative to having the kernel manage virtual terminals, the screen application can be used to do it in userspace.

Run screen once, and it takes over the current terminal window. Running it again in the same window creates “new buffers”. The sequence “ctrl-a” is used to send commands to the screen program; the most important is “ctrl-a ?” which shows help.

The screen program is particularly useful when using a network connection to a remote server; the network connection can be terminated and applications running under “screen” continue to run (rather than being terminated). After reconnecting to the server, running “screen” again will “reconnect” to the original session, with all applications still present.

Graphical Display Managers

This section covers how an X server starts up, the role of the display-manager, greeter, xinirc files, XDMCP and similar things.

If you are not familiar with X and graphics under Linux, then this article might be helpful.

This section assumes the computer which a user physically sits in front of has a graphics card.

The original concept of X is that a user would log in via a console (ie a physically-attached keyboard and screen), resulting in a text-only session. The user would then start an X server instance (resulting in a graphical display), and a “window manager” application which connects to the X server as a client. The user can then interact with the window manager to launch further graphical applications. Exactly which client applications are started when the X server instance is started is controlled by file “$HOME/.xinitrc”; when that script terminates the X server is also terminated.

Alternatively, the user first interacts with a graphical greeter program, aka a “display manager”. The greeter is traditionally an X client application ie connects to some X server (and thus receives input from whatever keyboard that X server is attached to). When a desktop system boots, it starts a greeter which in turn starts an X server attached to the system console, then the greeter connects to that X server as a client. When username/credentials are entered into the greeter, it invokes the normal login process (ie verifies the credentials), then terminates the X server it is attached to and starts a new one. It also starts some X applications as clients of this new server, in particular a Window Manager or similar; exactly what gets started depends upon the desktop environment the greeter launches. Traditionally, file “$HOME/.xsession” was used (similarly to the .xinitrc file) but many desktop environments do not use this file.

It is also possible for a desktop system to connect to a remote greeter program. The XDMCP protocol is designed for this; an X server starting up on a user’s desktop can be configured to send out an XDMCP broadcast and then listen for remote greeter programs to respond with their addresses. The local X server on the desktop then provides a list of available systems to log in to; the user enters username/password which are sent to the selected greeter and if that target system accepts the credentials then the remote greeter sets $DISPLAY to point back to the X server on the user’s desktop and then starts whichever graphical apps are configured to run on login. These will run on the remote system but send X drawing commands to the user’s X server (and accept input from there); the remote system may not even have a graphics card. This approach was reasonably popular in the 1980s and 1990s but is less common now and many modern greeter implementations do not support XDMCP at all.

In modern desktop systems it is typical for the operating-system to start an X server instance on bootup, with that instance taking input from the console keyboard. In addition, a local X greeter application is started which connects as a client to the X server (and thus receives keystrokes from the console keyboard via the X server). When a user at the console keyboard enters the correct credentials into the greeter application, the greeter terminates and the normal login process is executed - which would then start a window-manager as a client of the same X server. Actually, although a simple desktop system has only one keyboard, the Linux kernel typically creates multiple “virtual terminals” (VTs) on boot, and forwards keystrokes from the real keyboard to whichever virtual terminal is active; programs then connect to a VT to read input. Similarly, each VT has a small amount of memory for a text-only output buffer, and the display shows the buffer from the active terminal. The “getty” program is then run on multiple virtual terminals, allowing the user to press a sequence such as “ctrl-alt-f{n}” to activate a particular virtual terminal and log in with text-mode - very useful on occasion. The X server is connected to one of these virtual terminals, ie sees keystrokes only when that virtual terminal is active (normally, ctrl-alt-f7 selects that terminal). Of course the kernel needs to change graphics-card setting when switching between a VT in “text mode” and one in graphical mode (ie with an attached X server). Switching mode is easy with a KMS (kernel modesetting) graphics driver (modern style), but was difficult/unreliable with the older X userspace modesetting approach; see later for further details.

X server instances

Traditionally, a single X server is started (as user root) on system bootup, and remains running until system shutdown. When a user logs in, it may need to reconfigure the screen to match the user’s desired setting (resolution, etc).

In the last few years, effort has been put into running “rootless X”, ie running the X server as a non-root user. In this case, the X server running when nobody is logged-in uses a non-root “system” account. When a user logs in, that X server instance is terminated and a new one running as the logged-in-user is started. On logout, the current X server instance is again terminated and a new one started (to support the greeter program).

TODO: how is graphical “fast user switching” supported? Is a new X server started on a second virtual terminal?

Traditionally, the bootup sequence executes /etc/X11/default-display-manager or /etc/sysconfig/desktop. However on my debian8 systemd-init-based system, there is a “gdm.service” unit which starts the gdm3 display-manager directly.

Some Display Manager implementations support “remote login”, ie XDMCP, while others have dropped support for this.

References