Jay's home page · Jay's Linux page

Using LILO to boot a machine as an X terminal

(NOTE: I'm not claiming any of this is rocket science; it's all very straightforward. I just thought the concept might be interesting to people, and if I make my config files available, other people can use them.)


LILO, the most common Linux boot loader, is commonly used to let the user choose at boot-time which operating system to load (e.g., to choose between Linux and Windows, or between OS/2 and DOS). For instance, I have LILO set up on my laptop to let me choose at boot time whether to run Windows 95 (which I almost never do :-) or Linux. (If you aren't familiar with how LILO works, you should probably read the documentation for LILO on your Linux system or the LILO mini-HOWTO document at linux.org for background before you continue.)

LILO distinguishes different configurations (e.g., Windows or Linux) based on labels; each label has some information associated with it, such as a disk partition to boot from. For Linux partitions, there's also information about what file (kernel) to boot and about arguments to pass the kernel.

More than one label can specify the same partition to boot from. In fact, different labels can be identical; I have labels called `linux' and `l' which are otherwise identical, so they're essentially aliases for each other.

My situation

I most commonly use my laptop (let's call it sputnik.example.net) when I'm travelling, either working on it directly or using it as a terminal (with a modem). For that purpose, obviously, I need a completely standalone environment that doesn't depend on a network being present. I run X, but I generally want to run all my X clients locally.

However, lately I (and my housemates) have had a need to use my laptop essentially as an X terminal to access another Linux computer (call it zemlya.example.net) on the house net. So when I'm at home, I generally want the laptop on the network, and I'd like it to boot and start X, but I'd like all my clients to run on zemlya. Ideally, I'd like the X server running on the laptop, but xdm (which displays the login window and starts the user's X session) running on zemlya.

Either of these configurations is easy to arrange; for a standalone machine, I want to run xdm on sputnik (which I do out of /etc/inittab; the default RedHat 5.2 inittab runs

        /usr/bin/X11/xdm -nodaemon
in runlevel 5, so I just edit /etc/inittab to make runlevel 5 the default runlevel. (xdm starts the X server by default when it runs, and if the X server dies, xdm will restart it. Likewise, init, the process that reads inittab, will typically restart xdm itself if it dies.)

To have sputnik run as an X terminal, I can just start the X server and have it query zemlya for xdm service, asking zemlya to display a login window. This can be done (assuming X isn't already running) with the line

        /usr/bin/X11/X -query zemlya.example.net

(If there's already an X server running, you can start a second one by specifying display :1, as `X :1 -query zemlya.example.net'.

So my situation is that in addition to choosing at boot time between Linux and Windows, I'd like to be able to choose between two different configurations of Linux. One way to do that would be to have two completely independent installations of Linux, and choose between them just as I choose between Linux and Windows, but that would be an inefficient use of disk space (and of my time, since I'd need to install and configure both of them).

Using runlevels

Linux has inherited from System V the concept of runlevels, or independent states the machine can boot to. For example, single-user mode (typically used for fixing a broken system) is one run-level. Under RedHat, the default runlevels are as follows (from RedHat's /etc/inittab):
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
So for instance, under RedHat, whether X is running or not depends (by default) on what runlevel the machine is booted too; the same system, identically configured, will boot without X if you tell it to go to runlevel 3, or with X if you tell it to go to runlevel 5.

Warning: Different Linux distributions use the various runlevels for different things. If you're not using RedHat Linux - or even if you're using a different version - you should check what that distribution's conventions are for the runlevels before trying to use them. Also, some distributions use some other mechanism besides runlevels (e.g. an rc.local file) to decide whether to run X at boot time. However, any distribution that supports runlevels, and I think all the major ones do at this point, can be coerced to work like RedHat without too much effort if you need to.

The kernel knows what runlevel to go to based on arguments it is passed when it starts. If you're using LILO to boot, those arguments are passed by LILO. An argument can be passed to the kernel either through LILO's `append=' directive (associated with a label) or by the user on the LILO boot command line, after the label that specifies which configuration (partition, kernel, etc.) to boot from. For instance, if you want to boot into runlevel 3, and the LILO label for your Linux partition is `linux', you can issue

        boot: linux 3
at LILO's `boot:' prompt.

But you can also associate kernel arguments with a particular label. For instance, if you want to pass the arguments `root=/dev/sda4' to the kernel whenever you boot a particular configuration, you can put

after the appropriate label in your /etc/lilo.conf file.

So at this point I have two ways to choose which runlevel to boot into: I can specify it by hand at LILO's boot prompt, after the label (as `linux 3' or `linux 1', which latter is one of the ways to boot single-user), or I can set up separate labels in my /etc/lilo.conf, at least one of which has an `append=' line to specify a runlevel.


Well, that's great - so far, I'd be able to choose whether or not to run X (since runlevel 3 doesn't and runlevel 5 does). But that's not what I want: I want to distinguish between two different runlevels that start X in different ways. Basically, I want to create a new runlevel that just starts the X server (with -query zemlya.example.net).

Note in the list of runlevels above that in RedHat, runlevel 4 is `unused' by default. Voilą; I can define a new runlevel 4 that does what I want. That is done in /etc/inittab, which lists processes to be run in each runlevel. (It's read at boot time by init, the special process started by the kernel, which is PID 1.)

Here's my /etc/inittab for the laptop:

The first field on each line is just a unique identifier for that line. (A few of these, like `ca', are special.) The second field lists the runlevels to which that line applies; for instance, in runlevel 3, only the lines that have `3' in the second field are run (with a few special exceptions like the `si' line). As you can see, more than one line can apply to a given runlevel. The third field controls how and whether init starts, monitors, and restarts the process. And the last field is the command line to run.

The line with the identifier `xt' is new, and defines how my new runlevel 4 is different from the other runlevels; it runs the command `/usr/bin/X11/X -query zemlya.example.net'. (The respawn says that if X dies, init should restart it.)

So with this inittab file, runlevel 4 will cause the machine to act as an X terminal connecting to zemlya (although all the other services are still running, and you can telnet to the machine normally, or switch virtual consoles and type locally), and runlevel 5 is a normal standalone X environment with xdm running locally.


Here's the /etc/lilo.conf file I use: It's about twice as long as it needs to be, because I have at least two labels for each configuration. In addition to an ordinary boot into runlevel 5 (which I've set as the default) and the boot into runlevel 4 to act as an X terminal, I added a label to boot into runlevel 3 (text only, without X running). At the end are the labels to boot Windows 95.

The `timeout=100' line causes LILO to wait ten seconds for me to choose a label to boot; if I don't, the first label is used (so a machine can boot unattended). The `message=/boot/message' line tells LILO to display a message describing the options. For completeness, here's that file:

After editing your lilo.conf, you have to re-run /sbin/lilo to read it and write appropriate boot information to the boot blocks of your hard disk (or wherever your lilo.conf says to write it; you can install LILO to a floppy, for instance).

Another way to do it

There are a number of other approaches to what I wanted to do. For one thing, unrecognized options of the form `variable=value' set environment variables for init; I think those variables are available to the processes init starts (although I haven't tested this), so I could have append='ed, for instance, `XMODE=standalone' and `XMODE=xterminal' in the two different configurations, and edited the various startup scripts to do different things based on the value of that variable. (I'd have needed to replace the simple invocation of xdm for runlevel 5 with a script that looks at that environment variable and does different things based on it.) That approach might be better if you want two configurations that are largely similar, but have several differences, affecting several different startup scripts and services. (For example, in one configuration, you might want to act as an Ethernet router and accept logins on several serial ports, and mount /home from a server on the LAN, while in another you might not have Ethernet, not accept logins on the serial ports, and mount a ZIP disk as /home.


If you have comments or suggestions about this document, please let me know at <js@aq.org>.
Jay's home page · Jay's Linux page
Jay Sekora <js@aq.org>
last modified 1999.01.17