An introduction to X structure, configuration and customization.
When the X Window System was first released, many complained that the system was big, slow and complicated. My first experience with X was installing one of the earliest releases available for Intel hardware on my 386 running UNIX with 4MB of RAM and an 80MB hard drive. The installation took up most of the drive, and X ran so slowly (with much thrashing of virtual memory) that it was simply unusable. I quickly decided to remove it from my system and went on to “real work”.
However, I got a taste of what X was like and appreciated how the developers took the “high road” in their design. They combined a high degree of versatility and a client-server architecture, at the noticeable expense of performance on what is now generally considered to be archaic hardware.
Today, most computers running Linux have more than sufficient hardware resources to run X with good performance, so running X on an inexpensive desktop system is commonplace. Now that we've all got X running on our desktops, the next hurdle is configuring X and customizing it to meet our needs.
I will start out by presenting a brief description of how X is structured and how the parts interoperate. Once you know this, it will be much easier to make sense of the implementation details.
One of the most basic facts to be aware of is that X is not part of the kernel. Unlike other operating systems, where applications make requests to put up windows, menus, et al., to the operating system API, X is entirely contained in user space, running as ordinary processes. These processes are classed into two groups: client processes and server processes.
The job of the X server is to handle the interface to the hardware (graphics adapter, keyboard and mouse) and a few additional low-level services such as drawing, color allocation, event handling, inter-client communication and managing a resource database of user preferences.
Clients communicate with the X server through the X protocol, which can be run over interprocess communication (IPC) on one system, or between systems using TCP/IP. This allows an X client to run on one system and use the display, keyboard and mouse on another. Yes, it is even possible to run an X protocol over the Internet.
For example, if I use TELNET to access my ISP and run the command xeyes, the program will start up and display a window on my Linux system. The program seems to be running locally, even though it is actually running on the remote system and just using my system for the display. The way this works is that the remote system is running xeyes, while the client (requesting services) and my Linux system are running the X server (which fulfills the client's requests). This, of course, requires that my system be set up to allow it; I use the xhost command to allow my ISP's system to use the X server on my system. Also, the $DISPLAY environment variable in my TELNET session must be set correctly (see below). I am using this example only as a demonstration; if you want your system to be secure, you will probably not want to allow people to access your X server from an outside network. One trick used by crackers is to put up an invisible window covering your display that catches all keyboard input, including passwords.
Each display is handled by one server process, but many clients can use a display at the same time. One of the most fundamental client types is the window manager, which allows the user to manipulate windows. A window manager performs actions such as drawing “decorations” around windows (borders, title bars and buttons), and provides functionality such as pop-up menus and the iconization of running clients. Desktop environments such as KDE and Gnome are implemented as user-space X clients, as are all other applications that run under X. These applications can be of any level of complexity, from xlogo to Netscape Navigator.
In the remainder of this article, I will cover some basic X Window System administration. A full treatment would take a whole book, so I'm going to discuss only what I consider the most important points for the novice X administrator. For the most part, I will assume you have X already configured and running. I will skip over advanced topics such as security and running X over a network (on X terminals or remote systems) as much as possible. This article covers XFree86, which comes with most Linux distributions. If you are running a commercial X server, you may want to skip the section below on configuring the X server, but the rest of the material covered is independent of the server you are using.
Most files for the X Window System including executables, libraries, manual pages, include files and miscellaneous other files are kept in the /usr/X11R6 directory tree. There is usually a symbolic link called /usr/X11 to that directory.
The systemwide configuration files for X are in the directory /etc/X11. If you get a listing of that directory, the output will look something like this:
X fs mwm xdm XF86Config fvwm twm xinit XF86Config.0 fvwm2 wmconfig xsm
In the above listing, X is a symbolic link to the X server executable, which is in the directory /usr/X11R6/bin. Note that there is also a symbolic link called X in /usr/X11R6/bin, which points to /etc/X11/X, rather than the X server in the same directory. (Yes, there is a reason for doing it that way. Can you figure out why?) XF86Config is the file read by the XFree86 X server while starting up, and contains information about the mouse, graphics card, monitor and a few other things the server needs in order to run. Most of the other objects in /etc/X11 are directories containing sample startup files for programs that have the same name as the directory. The /xdm directory contains startup files for xdm, the X display manager. I will discuss the files in that directory later.
The XF86Config file is usually created during the installation of Linux. If you do not yet have X installed (or working properly), you can use the xf86config command to create it. Make sure you have collected detailed information about your mouse, graphics card and monitor first. Once you have a working XF86Config, you can modify it to change X's behavior. There are two warnings I want to give before saying anything else.
First, make a backup of the file before modifying it in any way. In fact, this is a good idea even if you do not plan to make any changes. Notice on the previous page that I have the file named XF86Config.0 in /etc/X11. That is simply a copy of the original XF86Config made immediately after Linux was installed. If anything untoward ever happens to my XF86Config file, I can quickly restore it from that backup file. Before making any changes to the “real” file, I make a backup using a numbered extension, such as XF86Config.1, XF86Config.2, XF86Config.3 and so on. That way, I create a history of my modifications, and can restore the configuration to its original state or to any previous state. This is a fairly common practice among system administrators that I suggest you adopt immediately, if you haven't already. It is also a good idea to make a backup of this type for the version of the file you are currently using, in case it is unintentionally overwritten by XF86Config or a reinstallation of the X software.
The second warning about XF86Config is that the part beginning with Section "Monitor" and ending with "EndSection" contains very sensitive information. If you mess it up, you could cause physical damage to your monitor! So don't change any of it, at least not until after you have read the man page for XF86Config and the XFree86-Video-Timings-HOWTO.
Now let's have some fun. A couple of simple (and safe) things can be modified in your XF86Config file relating to how your X session appears and functions. Both of these appear near the bottom of the file, in the parts beginning with Section "Screen" and ending with "EndSection". Take a look at your XF86Config file. Note that there are a few Screen sections. Each corresponds to a specific X server, labelled by the “Driver” tag. You will need to identify the one that goes with the server you are running. If your graphics card is reasonably new, you are probably running one of the accelerated servers, which correspond to the section that looks like the one shown in Listing 1. Most newer systems use the accelerated server, but if yours does not, don't worry—the sections for the other servers (svga, vga16 and vga2) are similar, just much simpler.
The strings that go with the Device and Monitor tags are descriptive in nature and not critical. Notice the DefaultColorDepth tag, which did not appear in my XF86Config immediately after installation. I added it to set X's default to something more interesting than the usual of 8-bits/pixel, which may run the fastest, but allows for only 256 colors. 16-bits/pixel allows many more (65536) colors, and 24-bits/pixel allows for “true color” of 8 (or more) bits for each of the red, green and blue color components.
Next come the Display subsections. Each has a different Depth tag, one for each mode (bits/pixel) the server can handle.
The Modes tag defines the physical screen resolutions the monitor supports. These must correspond to the Modeline entries in the Monitor section of XF86Config or they will be ignored. The first valid entry is the default resolution for X server initialization. I am often working with development projects where I target the least common denominator of 640x480, so I use that as my default; however, you might want to start out at 800x600 or higher. If you want to switch resolutions after the server starts up, hold down both alt and ctrl on the keyboard and press either the + or - key on the numeric keypad to cycle forward or backward through the list.
The ViewPort tag sets the upper left corner of the virtual display and controls which part of it will appear centered on the screen when X first starts up. This works only if your virtual area is larger than your physical resolution. I prefer to leave this at 0 0, but try using a few different non-negative values and see what happens. You'll notice the screen is shifted by that many pixels (horizontal and vertical) from normal.
Last, there is the Virtual tag, which is used to set the size of the virtual screen area. In 16-bit mode, I may use a physical resolution of 640x480, but I still have the ability to pan around (using the mouse) within a larger virtual area of 1024x768. The virtual screen area is limited by the amount of video memory on the video card. For example, at 1024x768 pixels and 16 bits/pixel (or 2 bytes per pixel), a total of 1.5MB of video RAM is being used. Some cards have only one megabyte of video memory and would not be able to support that configuration. Also, note that the X server may use a small amount of video memory for other purposes, so you may not be able to use all of it for your bit-planes.
Many more things can be done with the XF86Config file than I can describe here. For additional details, check out the XF86Config manual page.
There are a few different ways to get the X server started. One of the first methods found by many new users is the startx command, which is actually a shell script wrapper for the xinit program. Using startx with no arguments should be enough to start up an X session; however, unless you have set the DefaultColorDepth as described above, you will probably get only 8 bit-planes. If you want better colors, you will need to add some arguments, like this:
startx -- -bpp 16
This starts X with 16 bit-planes. The first two dashes cause the -bpp 16 to be passed as arguments to the X server, rather than the xinit program. If you get tired of entering the whole command each time, you can create a file named .xserverrc in your home directory and put in it the command to start the X server, like this:
exec X -bpp 16 :0See the xinit(1) manual page for details.
When X starts, it will switch to the first available virtual screen and use that for the display. Most Linux systems come with the first six virtual screens assigned as virtual consoles, so the user can switch among them by pressing the alt key and a function key (f1-f6) at the same time. To get to one of those from the X display, it is necessary to add the ctrl key, i.e., use ctrl-alt-f1 to get from X to the first virtual console. Then, to return to the X session, press ctrl-alt-f7. If your system doesn't have six virtual consoles enabled, you will have to use a different function key.
Now, if we can have six (or more) virtual consoles, why not have more than one X session? This is done by providing startx with more information. The X server needs to know which virtual screen to use and what to name the display.
For example, to start a 24-bit display on virtual screen 8, type:
startx -- :1 -bpp 24 vt8
and to start an 8-bit display on virtual screen 9, type:
startx -- :2 -bpp 8 vt9The :1 and :2 are the names that X uses to refer to the displays. The full format for the name is host:N.M, where host is the host name of the system, N is the number of the display on that system, and M is the number of the screen (in multi-headed displays using more than one monitor).
The designation :0 is simply shorthand for the first display on the local system, localhost:0.0. The names :1 and :2 refer to the second and third displays. To switch among them, simply use the ctrl-alt-fn combination.
To see how these work, start up the :1 and :2 displays as shown above, and switch to your first display (:0) using ctrl-alt-f7. Then from a virtual terminal (e.g., xterm, rxvt), run the command
xeyes -display :1
The xeyes program will run (you won't get another shell prompt), but it is not visible on the screen. Now switch to the second display (ctrl-alt-f8) and you will see it. When xeyes exits, you will get another prompt in your shell session on :0.
Many X programs support the -display option to specify the display to use. Note that the environment variable DISPLAY is set to the default display. If you run the command echo $DISPLAY from a virtual terminal in each display, you can see how it is set differently on each one.
When xinit starts up, it starts the X server and then looks for a file called .xinitrc in the user's home directory, which is a shell script that xinit runs. That file usually contains, as a minimum, lines like:
xterm & exec fvwm
which start an xterm terminal emulator, then it replaces the xinit process with the FVWM window manager. In turn, fvwm looks for its startup file called ~/.fvwmrc. A default for this file can be found in the /etc/X11/fvwm directory. Notice that the xterm process starts running without a window manager. A window manager is not a required part of an X session, but you will probably want to have one.
Using startx is easy, but if you use X a lot you will probably want to log into it directly without the complication of having to log into a text console first. Direct logins to X are handled using XDM. The files in /etc/X11/xdm are used to define a configuration, and then the simple command xdm starts X with an xlogin screen to allow someone to enter their user name and password. Like startx, the xdm command can be entered from a command prompt (as superuser). This is good for testing, but xdm is actually meant to be run automatically during the boot sequence—more on that later. Typing ls to get a listing of my /etc/X11/xdm directory outputs:
GiveConsole Xresources.0 Xsession.0 Xsetup_2 xdm-config.0 TakeConsole Xservers Xsetup_0 authdir Xaccess Xservers.0 Xsetup_0.0 chooser Xresources Xsession Xsetup_1 xdm-config
The key file among these is xdm-config, which is the default configuration file for xdm. The xdm-config file defines the basic configuration, including which files to look in for further setup information. The contents of xdm-config look like Listing 2. Note that the names of other files used by XDM are defined here, so it is possible to use different file names or put the files in other directories. The defaults work fine, but be aware that on other UNIX systems, or even different Linux distributions, files may be in a different location. In any case, you can familiarize yourself with the system's configuration by looking at the xdm-config file.
The information in xdm-config is specified using X resources, which is a bit like setting values of data structure fields in a programming language.
The first line of the file sets DisplayManager.errorLogFile, which is where xdm writes its error messages. If xdm is not starting properly, take a look at the error file. You will probably find some useful messages there. On older Red Hat systems and other UNIX systems, the file was placed in /etc/X11/xdm, but in more recent versions (e.g., Red Hat 5.1), it is in /var/log/xdm-error.log. This is in accordance with the Linux File System Hierarchy Standard (FSSTND).
DisplayManager.pidFile (/var/run/xdm.pid) is a file to which xdm writes its process ID. This can be handy if you are adding customizations and you want to restart X to check if they work. Type the command:
kill -TERM `cat /var/run/xdm.pid`
to kill the xdm process before restarting it. Actually, I prefer the command:
killall -TERM xdmwhich does the same thing. A variation is to replace the TERM (terminate) signal with HUP (hangup); this does not shut down any running X sessions, but does restart xdm with the new configuration (used for any new sessions that are started). If you are doing your X administration from within an X session, you may want to use that method to avoid discontinuities in your GUI services.
The file pointed to by DisplayManager.servers (Xservers) is used by xdm to start the X server processes. It contains information that tells xdm how to start each X server process. For example, the line
:0 local /usr/X11R6/bin/X -bpp 16 vt7 :0
in my Xservers file will start display :0 on the local system using the command and arguments as provided. To start more than one display, simply add lines to the Xservers file in this same format. If the Xservers file contains these lines:
:0 local /usr/X11R6/bin/X -bpp 16 vt7 :0 :1 local /usr/X11R6/bin/X -bpp 24 vt8 :1 :2 local /usr/X11R6/bin/X -bpp 8 vt9 :2three displays will start up when xdm is run—a 16-bit display on virtual terminal 7, a 24-bit display on vt8 and 8-bit on vt9. The -bpp 16 option is redundant, since I've defined DefaultColorDepth to be 16 in my XF86Config file.
Notice the asterisk in the last few lines of xdm-config. This mechanism is called “loose binding” and is a wild card character used to match all possible field names. The field names in this case are the names of the displays. Display :0 is referred to as DisplayManager._0. (It is _0 for display :0, _1 for display :1 and so on.) The underscore is used instead of the colon because in a resource, the colon is a separator between the resource name and its setting. An asterisk means the same file is used for all of the displays, but when the display is specified explicitly (called “tight binding”), the file is used just for that display. Of course, it would be possible to use only tight bindings and specify the same file each time, but the loose binding method is easier.
After the X server starts and before the xlogin program is run, xdm looks in the file defined by the DisplayManager._0.setup resource (Xsetup_0). This is a shell script containing arbitrary commands, so it has a great deal of versatility. I like to put a more pleasing background behind the xlogin window than the default black and white pattern, so I might use a command like this:
/usr/X11/bin/xsetroot -solid darkcyan
to make the background (root window) a solid color. To make things more interesting, the lines:
/usr/X11/bin/xloadimage -onroot \ /usr/local/images/tiles/purpleblue2.giftile the background with an image of my own design. Be sure the xloadimage program is on your system before doing this.
Again, a warning about security. The program(s) run out of Xsetup._* files may have their keyboard and mouse inputs disabled, but if they do not exit before the user successfully logs in, they will continue to run with superuser (root) permissions. For example, if the line
were in the Xsetup_0 file, the user who logs in on display :0 is granted a superuser shell, which is not a desired condition. This is an obvious example, but others may not be as obvious, so be careful.
Around Christmas, it might seem cute (and harmless) to put
into the Xsetup file to make snow appear to fall while the computer is waiting for a login; however, since it will be running with the user ID of root, the user will not have permission to kill the process after login. Also, many people will get tired of seeing snow falling in the background of their X sessions after awhile. Fortunately, there is a way to make the xsnow process exit before the user's login session begins. First, add a line that saves the process ID of xsnow immediately after the one that starts it, like this:
/usr/X11/bin/xsnow & echo $! >/var/run/xlogin_xsnow.pidAfter the user is authenticated by xlogin and before his X session starts, xdm runs the shell script named in the DisplayManager._0.startup resource (GiveConsole). This is normally used to change the ownership of /dev/console to the user, so that error messages directed to the console can be displayed in the X session, using xterm or rxvt with the -C option, or with xconsole. However, you can add whatever you want to the script. For example, the following lines force xsnow to exit:
kill -9 `cat /var/run/xlogin_xsnow.pid` rm -f /var/run/xlogin_xsnow.pidNow, here's an exercise for you. Many sites want the ability to shut down the system directly from the xlogin screen without requiring the user to log in or su with the root password. With your favorite text editor, create a Tcl/Tk script named xlogin_buttons that contains the lines shown in Listing 3, and make it executable with the command:
chmod +x xlogin_buttons
Now follow the above xsnow example to modify your Xsetup_0 and GiveButtons scripts to use the Tcl/Tk script instead of xsnow. I put the script in /etc/X11/xdm and put these lines in my Xsetup_0 script:
/etc/X11/xdm/xlogin_buttons & echo $! >/var/run/xlogin_buttons_0.pid
and these two lines in my GiveConsole script:
kill -9 `cat /var/run/xlogin_buttons_0.pid` rm -f /var/run/xlogin_buttons_0.pidBe sure to check that the Xsetup_0 and GiveConsole files are defined in the xdm-config file.
Along with the DisplayManager._0.startup resource is DisplayManager._0.reset (TakeConsole), invoked after the X session ends and before xdm resets the X server prior to the next login. Normally, this simply changes the ownership of /dev/console back to root, but you can add customizations there too.
The xlogin program identifies and authorizes the user by accepting the user name and password. These are entered at the prompts in the xlogin window. If you want to change the xlogin display, take a look at the file pointed to by DisplayManager*resources, which on my system is called /etc/X11/xdm/Xresources. That file contains resource definitions for the xlogin and other programs started by xdm before the user's X session begins. Rather than having xlogin display the host name of my system, I prefer the message “Welcome to Linux” colored blue. To do this, I define the xlogin*greeting and xlogin*greetColor resources as shown in Listing 4.
A security consultant might wince at seeing a system that is configured to say “Welcome” to any user who happens to pass by and “Try Again” if they don't guess the right user name/password combination. I do this only on my home system. If you're working in an academic or corporate environment, you might want to use something like:
xlogin*greeting: CLIENTHOST xlogin*fail: Authorized Users ONLY!
In the above example, most of the colors are specified as RGB triplets in the form of #rrggbb. You can use 1 to 4 hexadecimal digits for each primary color, so to specify a not-totally-bright red, #c00, #c00000 and #c00000000000 are all equivalent. You can use color names like white (equivalent to #ffffff) or black (equivalent to #000), as in the above example. To get a list of color names X knows about, use the showrgb command. These two methods have been available in the X system since its first public release and are somewhat limited.
In release 5 of X11, new methods were added. One of the main problems with the older method is that a color specified in the 3-digit format, which provides only 4 bits for each primary color, may work fine for a display with an 8-bit color depth, but on a 16-bit or 24-bit display it will not look right. For example, #fff will display as bright white on an 8-bit display, but will be an off-white (#f0f0f0) on displays with 16- or 24-bits/pixel. You can get around this as I did above by always using at least 6-digit color specifications or using the new Xcms RGB method,
which automatically expands to #ffffff for displays of more than 8-bits/pixel. As with the original method, 1 to 4 hexadecimal digits are specified for each color. But with the new method, you can use a different number of digits for each. Instead of being taken as absolute numbers, the digits are used as scaling factors. For example, a single digit 9 represents 9/15, and 09 represents 9/255.
If you don't like using hexadecimal digits, you can use the RGBi (RGB intensity) format, like this:
That will also produce a bright white. The values for red, green and blue are specified as floating point numbers between 0.0 and 1.0, inclusive. There are also other, much more complex color spaces such as TekHVC (hue, value, chroma) and several CIE formats.
By now, you should have a very good idea of how to configure xdm, so I want to tie up a few loose ends before covering how to start xdm automatically.
One file you might want to take a look at is one named by the DisplayManager*session resource in xdm-config. This file (Xsession) is yet another script. It is run by xdm to create the user's X session. Typically, it defines the file .xsession-errors in the user's home directory to be the error log file for X programs (the “clients” of the client-server architecture). The .xsession-errors file is truncated to avoid confusion with errors that happened in the previous session, then both standard output and standard error output is redirected to it. In addition to your xdm error file, the .xsession-errors file is a good place to check for clues if your X session is not starting properly.
Next, the file .xsession in the user's home directory is executed. From the user's perspective, xdm uses the .xsession file in the same way startx uses .xinitrc. However, there are a couple of differences. First, .xinitrc must be a shell script, but .xsession can be any executable program (and must have its execute bit set). This allows for additional flexibility, although .xsession will usually be a shell script that is very similar (and possibly identical) to .xinitrc. It is possible to make one a symbolic link to the other to simplify management and to ensure that startx and xdm both create the same working environment.
Second, when the X session is started by xdm, the user has not yet started a login shell, and the shell's startup scripts (e.g., .bash_profile and .bashrc) have not been run. Because of this, it is necessary to set (in .xsession) those environment variables, such as PATH, that must be available for any programs run from .xsession or any window manager or other program started from that script.
I've just briefly described the default behavior of the /etc/xdm/Xsession script. Usually it is left alone, and customization on a per-user basis is done with the .xsession program in the user's home directory. However, it is also possible to create system-wide customizations by modifying Xsession.
After you have used xdm from root's command line to successfully start an X session, the next step is to run xdm automatically during system initialization. This can be done in several different ways. I will describe three—the normal way, an odd way and a weird way. Take a look at your /etc/inittab file. You should find these two lines:
id:3:initdefault: x:5:respawn:/usr/bin/X11/xdm -nodaemon
The first line sets the default runlevel to 3 (full multi-user mode, with networking) when the system is booted, and the second tells the init process to run xdm when the system's runlevel is 5. On some Linux systems, such as Slackware, this may be 4.
The normal way to have the system run xdm automatically is by changing the first line to:
This will cause the system to boot to runlevel 5 instead of runlevel 3. In the second line, “respawn” tells init that if xdm exits, to immediately restart it. Startup scripts will be run from /etc/rc.d/rc5.d rather than /etc/rc.d/rc3.d. This means if you have configured your runlevel 3 daemons just the way you want them, you will have to do it again for runlevel 5.
If that seems like too much bother, use the odd method and change the second line instead of the first one, like this:
This will start up the xdm process in runlevel 3 instead of runlevel 5, preserving your runlevel setup.
Finally, the weird way is to start xdm like any other daemon process and ignore the /etc/inittab file entirely. Add a script to the directory /etc/rc.d/init.d that looks like this:
#!/bin/sh # /etc/rc.d/init.d/X.init - Start X Window System echo "Starting X Window Services: xdm" /usr/X11/bin/xdm
Then, put a symbolic link to the script in the directory /etc/rc.d/rc3.d. When the system is booted, init runs these scripts in the same alphanumerically sorted order that the ls command would display them. On my system, I put in a link called S97X that causes X to be started after almost everything else. Take a look at the other files in the rc3.d directory (using ls -l) and follow their examples. This method can be handy, because it doesn't restart xdm each time xdm exits, and sometimes that might be desired. A simpler way to do the same thing using inittab is by typing the line:
x:3:once:/usr/bin/X11/xdm -nodaemonOne note of caution is needed here. The /etc/inittab file is one of the most critical files on your system. If you mess up your inittab file, your system may not be able to boot, so maybe that weird method isn't so bad after all.
Well, there you have it. I did my best to crunch a book on X Window System administration into one magazine article. I've covered most of the basics of managing X, but also left out quite a bit. If you want more information, check out the sources of definitive documentation listed in the “Resources” sidebar.