Virtual Consoles in Linux



Virtual Consoles are a key feature of Linux (and FreeBSD etc) and possibly the most undersold feature of the whole operating system. You get the same effect as a screenful of xterms, without running up a X11 server!

What is a "Virtual Console" ?

When the any Unix system first boots up, you get a normal Unix login: prompt, so you can log in, and start X11 or do whatever else you would do with a single Unix shell.

Linux is pretty much the same, except that instead of just one such login:, you get several. These are accessed using the 2-key combinations <ALT><F1>, <ALT><F2>, <ALT><F3> etc.

Each of the "screens" with you see is known as a "Virtual Console".

Virtual Consoles are Independant

Apart from the fact that you use the same screen and keyboard, each virtual console is quite independant of the others. It's a lot like having several VT100-terminals connected (except that it's much easier to switch between them). This point is emphasised by the fact that each console has it's own CAPS LOCK, NUM LOCK and SCROLL LOCK states, and this is reflected in the LED's too.

Try it: if you set CAPS LOCK on in one console, and then switch to another, the led goes out, and you're back to lower case. When you switch back, presto, you're back in CAPS LOCK mode.

Mouse support for Copy and Paste - Introducing gpm

Virtual Consoles have one feature which you just can't get on a regular VT100 terminal: mouse support.

Most Linux systems come bundled with a program called gpm This allows you to copy and paste text within a console, and even between different consoles. By default, the buttons are usually set up to mimic those of an Xterm.
(Left=select, Middle=paste, Right=adjust).

The gpm program used to be called "selection".

GPM and X11

The problem with having a program like gpm which reads the mouse-device, is that you cannot have more than one such program running at a time. So if, for example, you are running gpm, and want to run X11 (which also reads the mouse device), you are in trouble. Or rather, you used to be in trouble.

The current release of gpm supports a Repeater Mode where it will write mouse-data to a named pipe, /dev/gpmdata in addition to it's normal operation. Any other program, such as an X11 server, can be read this pipe as if it were a regular mouse-device.

To utilise this feature, you should:

Gpm also gives you one more important feature: Multiple Mode the -M parameter.
This lets you can merge input from multiple, different mouse devices.

This is particularly useful if you have a laptop computer with some exotic but frustrating pointing device, and want to connect a normal serial mouse whenever possible. Running gpm means you can do this without having to edit config files every time you change pointers.

The combination of gpm and virtual consoles is very much like having a X11-server and an number of xterms - so much so that you may not want to start up X11 at all!

Distinguishing between Virtual Consoles

The only problem with having a lot of virtual consoles is that you can get lost. There are two things I like to do to distinguish virtual consoles.

  1. add the tty name to the login message
  2. set the color of the prompt

Putting the tty in the login message

The login message consists of a banner which put up by the getty program (or mgetty or agetty etc), plus the login-prompt supplied by the login program.

The banner which is supplied by getty consists of the contents of the file /etc/issue. This file may also contain "escapes". These allow us to include some variable-text in the /etc/issue file, such as the hostname, or (you guessed it) the tty name.

If you are using agetty, use \l to insert the tty name.

If you are using mgetty, use @L to insert the tty name.

For example, you /etc/issue file make look like this:

Welcome to \l on \n

See the man page for your getty program for details of escapes which may be used in /etc/issue

Setting a color prompt

The linux console also supports escape sequences for rendering color text. These are used by the command ls --color These escape sequences are common to both linux consoles and (color) xterms, so we can use the same prompt for both environments.

The complete range of escape sequences supported by the console is described in the man page console_codes(4)

The following line, when included in, say, /etc/csh.cshrc will set a color-prompt for the tcsh:

set prompt = "%{\e[0;3${tty:s/tty//:s/p//}m%}%n@%m %C2 %\! %#%{\e[0;0m%} "
However, for the sake of completeness, we'll include some provision for xterm-escapes, too.
if ( $?tcsh ) then
    if ($term == xterm ) then
        set xterm_hdr = '\e]2\;%m:%/\a\e]1\;%c\a'
	set xterm_hdr
    set color_s = "\e[0;3${tty:s/tty//:s/p//}m"
    set color_e = '\e[0;0m'
    set prompt = "%{$xterm_hdr$color_s%}%n@%m %C2 %\! %#%{$color_e%} "
    unset xterm_hdr color_s color_e

Similarly, for bash, you can use the following in ~/.bashrc

if [ "$BASH" != "" ]; then
    if [ "$TERM" == "xterm" ]; then
    PS1="\[$XTERM_HDR$COLOR_S\]\u@\h \w \\! \\$\[$COLOR_E\] "

Practical Uses for Virtual Consoles

So far, we've only considered virtual consoles as being additional login terminals. By default, this is the way they are setup. In fact, virtual consoles can be used for other things, too.

Logging to a virtual console

One of the best uses for spare virtual consoles is to display system messages. The Unix system generates quite an array of possible messages, most of which are sent to the syslog facility. These messages may be anything from a simple information message that a particular kernel-module is being loaded or unloaded, to important notices, such as "the system is going down".

The syslog is handled by a deamon, syslogd which controls the distribution of these messages based on their facility and priority. The syslogd reads the file /etc/syslog.conf to determine how to distribute these messages.


The priority is an indication of the "urgency" of a message. The following eight priorities are defined (in order of priority):

The syslogd generally treats priorities as meaning "equal or greater than" the stated priority. Consider the /etc/syslog.conf entry:
	*.error		/var/log/error.log

This will log messages from all facilities which have a priority of error or greater to the file /var/log/error.log

If we wanted only the messages with priority error (and not the higher priority messages), we could specify:

	*.=error	/var/log/error.log


The "facility" of a message is a means of categorising messages, so that we can divide them into "broad areas of interest". The following facilities are defined:

Facilities are specified explicity. There is no "heirarchy" of facilities, like there is for priorities. ie

	mail.warning		/dev/tty10

refers to messages of priority warning or higher, but only for the facility mail, and no others.

Sample /etc/syslog.conf file

It is quite simple to divide our different syslog messages amongst a number of different, otherwise unused, virtual consoles. I like to use tty9-tty12 for this purpose, as it doesn't interfer with my normal "login" consoles on tty1-tty6, or the default X11 console, tty7 Note also, that directing messages towards virtual consoles does not divert these messages away from log files. The syslogd lets you direct messages as many (multiple) destinations as you ,like.

Here's an example of some useful additions to your default /etc/syslog.conf:		/dev/tty9		/dev/tty10
	*.info;daemon.none;kern.none;auth.none;authpriv.none \

Don't forget to 'kill -1' your syslogd after changing syslog.conf

I have been told that, when directing syslog output directly to a tty like this, that pressing ^S or Scroll Lock in the tty can "block" the syslog daemon, and any daemons which subscribe to the syslog service. I haven't had the problem myself, probably because I rarely use Scroll Lock. Caution is advised, particularly if your system is being used in a multi-user role. Using tail -f logfile may be preferable, although it would be more complex to set up.

Logs and Security

You may notice that, by default, /var/log is not world-readable. This is because log files can contain security-sensitive information, especially if debugging is turned on.

One example of happens when you type an incorrect password during login. This results is a message to the syslog, notifying of the bad login attempt, and quoting the userid. If it just so happens that you've pressed Enter once too often, you may end up entering your password in the userid field. Some versions of login may log this faithfully - and your password inadvertently ends up in a log file! Or, just as bad, in plain view on a virtual console!

So, convinient as it is to log messages to virtual consoles, it is important to be aware of what kind of information is openly visible on your virtual consoles. This applies particularly if your computer is being shared amongst several users, or is located anywhere other than a locked room.

Adding things like auth.none;authpriv.none (in the above example) allows us to keep sensitive information out of plain sight. Likewise, it's best not to display debug messages, unless you are actively debugging your system.

Isolating logs from specific daemons

Given that there are 8 "spare" facility categories (local0-7), it would be nice if we could, for example, devote one virtual console to the tcpd messages, and another to ppp mesages, etc. Unfortunately, most programs are hard-wired to report their messages to a specific facility (eg daemon). This means that you would need to recompile the program in question in order to make it use a different syslog facility.

Running processes from Init

By default, most Linux systems are set up to run only getty or xdm processes on virtual consoles. There is no technical reason why you can't run something else.

For example, if you just want to run top, so that it's always "there", right from boot-up, you just have to modify your /etc/inittab to include a line like this:

5:2345:respawn:/bin/open -c 5 -w -- /usr/bin/top -s 

Which will run top on tty5. Don't forget to 'kill -1' the init process after modifying /etc/inittab.

Note in particular the use of the -s flag on top. This put top into "secure mode". This mode disables the interactive commands 'kill' and 'renice'. This makes it "safe" to run in this mode. Without the '-s' flag, anyone could walk up to the computer and use top to kill other people's processes.

The "secure mode" of top is just ideal for running top in this way.

Init and Security

The biggest problem starting processes from init (as we did the above example) is that the process runs with the userid root. This is not a problem for programs like getty, which are designed to run as root, and top, which has a special mode for this case.

But what do we do when we want to run something that wasn't designed for this kind of thing? There is a solution, though it's not a pretty one.

Let's say that we want to run a lynx web-browser in the same way as we have done with top. Lynx does not have a "secure-mode". It can write files to the disk, and can even spawn shells (using the ! keystroke). One solution is to run lynx as another user (other than root) by using:

6:23:respawn:/bin/open -c 5 -w -- /bin/su nobody --command="/usr/bin/lynx"

This runs lynx as user "nobody", by executing it via the su program.

Even this may be considered "insecure", as lynx is capable of "escaping" to an sh(1) shell (using the ! keystroke). Once an "unfriendly" has access to a shell, they have a "foot in the door", and can hunt for any weak-spots in your system's security.

The best solution is to isolate such "anonymous" users, in the same way that the ftpd does with it's anonymous users. That is, by using chroot to isolate them in a subdirectory.

First, we create a minimal directory tree where our program can run:

mkdir /home/sandpit
mkdir /home/sandpit/bin
cp /usr/bin/lynx /home/sandpit/bin
cp /bin/su /home/sandpit/bin
mkdir /home/sandpit/etc
cat <<EOF > /home/sandpit/etc/passwd
cat <<EOF > /home/sandpit/etc/group
cat <<EOF > /home/sandpit/etc/hosts	localhost
cat <<EOF > /home/sandpit/etc/host.conf
order hosts
mkdir -p /home/sandpit/usr/lib/terminfo/l
cp /usr/lib/terminfo/l/linux /home/sandpit/usr/lib/terminfo/l

Next we copy the files needed to run our program into our newly created directory tree, and set up the most restrictive permissions we can.

cp /etc/lynx.cfg /home/sandpit/etc
mkdir /home/sandpit/lib
cp /lib/ /lib/ /home/sandpit/lib
cp /lib/ /usr/lib/ /home/sandpit/lib
chown -R root.root /home/sandpit
chmod -R go= /home/sandpit
chmod a+x /home/sandpit /home/sandpit/* /home/sandpit/bin/*
chmod a+r /home/sandpit/etc/*    /home/sandpit/lib/*
chmod -R a+rX /home/sandpit/usr

Note: you may require slightly different runtime libraries for your particular version of lynx.

Now we can safely spawn a lynx browser from init, without compromising our system security. Our inittab entry would look like this:

6:23:respawn:/bin/open -c 6 -w -- /usr/sbin/chroot /home/sandpit /bin/su lynxuser

The open(1) command

If you like the idea of virtual consoles, but do not like going through the login process more than once, the open command if for you.

The open command starts a new shell on the next available virtual console, without giving a login prompt first. This is not a security risk, as you have to login before you can use the open command in the first place.

The only complication arises from what is considered the "next available" virtual console. If a getty is being run on a console, it is unavailable. Likewise, if you are logging syslog messages to a console, it is also unavailable. So, if you are using the open command a lot, you may wish to disable some of the getty processes in your /etc/inittab file.

(Note: I had to start both top and lynx by way of the open command, since they had a tendancy to use the wrong tty if I just ran them directly from init)

Changing Fonts

A number of different fonts are available for virtual consoles, and these are to be found in the directory:

	  -- or --

and are invoked by the program:


Note that there some fonts have different pixel dimensions. If you are running in EXTENDED VGA mode (ie by using the boot paramter "vga=2"), you will get an 8x8 font (which has quite ugly descenders).

A better option is to use some of the SVGA modes that your video card may support. First, you should boot your system, specifying vga=ask. Then try the different modes one by one, until you find one that suits you.

After booting up in a particular mode, you may want to "save" that mode by using the command:

	restoretextmode -w COLSxROWS

This will save the parameters of the current video mode in the file named COLSxROWS, such that you can switch back to that mode without rebooting, by using:

	restoretextmode -r COLSxROWS

You will still have to go through the boot-it-and-see process at least once for each video mode.

Once you have a suitable set of video mode files, named according to the COLSxROWS convention, you can use the command:

	resizecons COLSxROWS

This will invoke restoretextmode and invoke the appropriate stty command to ensure that the tty driver is "aware" of the new size. (Try changing from an 80-column mode to an 132-column mode using just restoretextmode, and you'll see what I mean).

Keyboard mapping in virtual consoles

The default keyboard map in the Linux console is defined by the file:

However, it is not necessary (or desirable) to edit this file directly.

If you want to load a completely new map (such as a dvorak keymap) you only have to use the command:

	loadkeymap  /usr/share/keytables/

There are numerous different keymaps already available, including all the common European ones. Use the dumpkeys command to view the current keyboard map.

If you just want to change a few keys, you should use the command loadkeys

For example, if you have a 104-key keyboard, you will have the special "windows" keys. You can put these to good use, by mapping them onto special functions.

loadkeys - << EOF
keycode 125  = Decr_Console      # Left  ~# key
keycode 126  = Incr_Console      # Right ~# key

This will remap the left and right "windows" keys, such that they will step through the range of virtual consoles which have been activated. To find out what the keycodes are for any given key, use the command showkey

The other situation you may encounter is that you have some keys on your keyboard which do not generate a keycode when pressed. For example, I have a "multimedia" keyboard, which has 12 rubber buttons in addition to the 104-keys. With the default keyboard mapping, these do not generate keycodes at all. We can assign them keycodes, but first we have to find out what codes the keyboard is sending out. This is done using the command:

	showkey -s

This will tell us the "scancodes" generated by our unmapped keys. Each key generates a one- or two-byte scancode when it is pressed. When the key is released, it sends the same code, but with the most significant bit set (for two-byte codes, the MSB is set on the second byte).

Next, we have to assign the scancode to a keycode. This is done using the command setkeycodes. This requires some care, as it is not obvious which keycodes are actually free for use. You can get some idea by looking in the file. All the keycodes up to 83 are in use. Above that, some are used, some aren't.

For my multimedia keyboard, I use the following command to set up the keycodes:

#       front<->back   www  Calculator Xfer
#       alt-shift-tab  e032    e021    e023
#                      120     121     122
#         |<<     |>||     []     >>|
#         e010    e022    e024    e019
#          92      93      94      95
#        Coffee   Menu    Mute    Vol-  Vol+
#         e07a    e026    e020    e02e  e030
#         123     124      89      90    91
setkeycodes e010 92  e019 95  e020 89  e021 121 \
            e022 93  e023 122 e024 94  e026 124 \
            e02e 90  e030 91  e032 120 e07a 123

Special keyboard actions

Once I have assigned keycodes, I can map the keys to some of the special keyboard functions, or arbitrary characters or strings.

loadkeys - << EOF
Alt ShiftL keycode 15 = Incr_Console
Control Alt Shift keycode 15 = Decr_Console
keycode 124 = Spawn_Console
keycode 120 = Show_Memory
keycode 121 = Show_State
keycode 89  = F100      # Mute
string  F100 = " Mute "
keycode 90  = Scroll_Forward    # VolDown
keycode 91  = Scroll_Backward   # VolUp
keycode 92  = Decr_Console      # CdPrev
keycode 93  = F104      # CdPlay
string  F104 = " CdPlay "
keycode 94  = F105      # CdStop
string  F105 = " CdStop "
keycode 90  = Scroll_Forward    # VolDown
keycode 91  = Scroll_Backward   # VolUp
keycode 92  = Decr_Console      # CdPrev
keycode 95  = Incr_Console      # CdNext

In this example, I have mapped some keys to special functions, and some to strings. For a full description of the syntax, see the man page keytables(5).

There is no convinient summary of what special keyboard functions are available. You need to look at:

This tells us the following special keyboard functions are available:

Like Num Lock, but...
ie the "three-fingered-salute"
Send "break" to tty
Toggle Caps_Lock (ie the usual one)
Turn ON Caps_Lock, (not a toggle)
Same as Shift, but also releases Caps_Lock
for unusual characters
Go straight to virtual console n
This is the default mapping for <ALT><F1> <ALT><F2> etc
Go to the previous virtual console, ie n-1
Go to the next virtual console, ie n+1
Runs the command specified by the kb: line in /etc/inittab
Previous console used
The keypad Num Lock
The "Secure Attention Key". See
"Kills all processes associated with this tty".
Can be used as a "Panic Button" if you think you've run a trojan-horse.
By pressing this key before logging on, you can be resonably sure that the login banner is not just a user-program masquerading as a login, for the purpose of capturing passwords.
Scroll up within a virtual console (same as <SHIFT><PageUp>)
Scroll down within a virtual console (same as <SHIFT><PageDown>)
Normal mapping for Scroll Lock key
Suspends tty output and input (buffering still takes place).
Similar to Caps_Lock, but more like the Shift_Lock on a mechanical typewriter. It also shifts the numeric keys (where as Caps_Lock doesn't).
To work properly, Shift_Lock should be assigned to a shifted key. It is activated by pressing <Shift><Shift_Lock> and is released by pressing <Shift_Lock>
Prints some memory stats
Prints some CPU registers
Prints some ps-like information
Same as KeyboardSignal
Does nothing. Useful for un-assigning a key.

The above is not a comprehensive guide to all possible keyboard mappings. It just covers some of the more exotic ones.

I had some trouble making sense of Shift_Lock. There's probably more to it than the above brief note. I noticed that Shift_Lock and Caps_Lock do not co-operate: their results are additive. Shift_Lock does not affect the Caps_Lock LED.

Control of Keyboard leds

Virtual consoles under Linux have one more feature which you should be aware of:
- the ability to place the keyboard leds under software or kernel control.

By use of the ioctl() calls KDGETLED/KDSETLED it is possible to arbitrarily control the keyboard leds from within an application

Alternately, by use of the internal kernel function register_leds() it is possible to use the keyboard leds to monitor an arbitrary location in memory.

Unfortunately, direct manipulation of the keyboard leds by either of these methods defeats the normal operation of the leds in all of the virtual consoles.

Nevertheless, the benefits of this control may be worth the cost, especially if you rarely use CAPS_LOCK (like myself).

One example of such a program is the tleds program, which uses the keyboard leds to monitor network activity. The program comes in two flavors: tleds for running under the virtual consoles, and xtleds for running under X11 (additionally).

The tleds program is available from:

This document may be freely distributed.

The information contained in this document was correct at the time of writing, but may be out-of-date by the time you are reading it.


Last modified: 8th July 1998