User-Mode Linux

by Matthew E. Hoskins

Computers run programs. From the earliest behemoth computers with their hard-wired programs to modern-day disposable calculators and desktop PCs, all our computers run some kind of program. John von Neumann cooked up the concept of storing a computer's program just like any other piece of data, making way for computers to become multipurpose tools no longer locked in to one hard-wired function. Soon the concept of an operating system, or a program to abstract common system-level details like device management and program execution, was born. It didn't take long for some crafty system programmers to realize that a single CPU could be made to perform multiple tasks seemingly at the same time. This gave rise to the first time-sharing and multitasking/multiuser operating systems. All modern computers still operate on this same stored program concept. In the case of a modern personal computer, after switching it on, it runs the stored program in the BIOS, or firmware, which eventually hands off to a multistage bootloader, which in turn loads the OS kernel. The kernel executes and sets up an operating environment in which system resources like CPU time, memory and devices can be used by programs executed beneath the kernel. It's all a long chain of stored programs.

The kernel is a program just like any other (albeit a rather complex one). So, what stops you from executing the kernel just like any other program? Actually, not much at all. This is what user-mode Linux (UML) is all about.

The Linux kernel normally runs with special privileges, because it needs direct access to your hardware. User-mode Linux provides a way to compile the normal Linux kernel sources so that it can be invoked as a regular binary program on top of the base Linux kernel. When you run a kernel on top of the base Linux kernel, you are really running one or more “guest” Linux systems without any special privileges. (There are some exceptions. Some software must be installed as root for user-mode Linux to work.) These guest Linux systems are complete systems that run in a (mostly) safe environment.

How to Do It

In the remainder of this article, I provide a recipe for getting a UML system up and running on your host Linux box. Then, we explore some features and have some fun. The host system I am using for this demonstration is Fedora Core 4 on an Intel P4 with 1GB of memory, but almost any system and distribution will work, provided it is running a recent 2.6 kernel and has a minimum of 256MB of memory.

A guest UML system is just like any other Linux system. It is a combination of a Linux kernel and a collection of small programs, libraries and files that make up the operating system. These are provided in two parts, the kernel and a filesystem image. A filesystem image is a virtual disk partition. This is what will be mounted and used as the root filesystem of our UML system. You have the choice to create these two parts yourself or download them off the Net, ready made from popular distributions. In the interest of instant gratification, we take the ready-made route; take a look at the UML Wiki for more information on building your own filesystems (see the on-line Resources).

Kernels and root filesystem images are available in a number of versions and distributions. Images of Red Hat, Fedora Core, Debian and a number of special-purpose distributions are available. I use Debian 3.1 for this demonstration.

COWs Are Your Friends

User-mode Linux has one very special feature called a Copy-On-Write file or COW. Copy on write is a common computer science concept that defines a mechanism for a chunk of data to remain read-only yet allows modification by writing changed data blocks to an alternate location. The filesystem image you download always remains read-only. Changes made to the filesystem in our running UML system are written to the COW file. This allows us to boot up multiple UML systems from the same read-only root filesystem image, provided they all have separate COW files. Also, if our UML system becomes corrupted, we simply clear the COW file to start over. The COW files are what is called sparse files; even though they may appear to be big when viewing the file size, only non-null data is actually allocated space on the disk.

Collecting the Pieces

Let's start by collecting the components in a freshly created empty directory. Make sure sufficient disk space is available; after all, we need to house an entire Debian installation. Three gigabytes should be sufficient for the basic system. Download the Debian-3.1-x86-root_fs.bz2 file from uml.nagafix.co.uk. Then, grab the 2.6.14.3-bs3 UML kernel from www.user-mode-linux.org/~blaisorblade/binaries. Finally, grab the UML utilities sources from user-mode-linux.sourceforge.net/dl-sf.html. If any of these files are missing, you can find alternate download locations in the Resources for this article.

Below is a script of the commands for collecting all the parts and compiling the UML utilities that are available only in source form. If you are not interested in setting up networking, you can omit the uml_utilities tarball and skip the compile. All the steps below can be performed as a normal user except the installation of the UML utilities, which requires an su to root:


mkdir /tmp/UML-Demo
cd /tmp/UML-Demo
wget http://uml.nagafix.co.uk/Debian-3.1/
-->Debian-3.1-x86-root_fs.bz2
bunzip2 Debian-3.1-x86-root_fs.bz2
wget http://www.user-mode-linux.org/~blaisorblade/binaries/
-->2.6.14.3-bs3/uml-release-2.6.14.3-bs3.tar.bz2
tar -xvjf uml-release-2.6.14.3-bs3.tar.bz2
cp um32-2.6.14-release-mod/vmlinux-2.6.14.3-bs3  .
wget http://mirror.usermodelinux.org/uml/
-->uml_utilities_20040406.tar.bz2
tar -xvjf uml_utilities_20040406.tar.bz2
cd tools
make all
su root
make install DESTDIR=/
exit
cd ..

Now we have all the parts collected and are ready to rock 'n' roll. All Linux systems have a kernel command line. In most systems, this command line is invoked by the bootloader (GRUB, LILO and so on). In our case, we compose the command line ourselves to instruct the kernel to use the Debian root filesystem image and a COW file named Debian1.cow as its root (/) filesystem. Your current terminal becomes the console of the guest UML system:


cd /tmp/UML-Demo
./vmlinux-2.6.14.3-bs3
-->ubd0=Debian1.cow,Debian-3.1-x86-root_fs root=/dev/ubda

After that command is executed, we see the familiar Linux kernel boot messages ending with a Debian system waiting for someone to log in. We can log in as root (there is no password) and poke around as shown in Listing 1.

Listing 1. Debian 3.1 UML Guest Boot Demonstration


Debian GNU/Linux testing/unstable (none) tty0

(none) login: root  <ENTER>
Linux (none) 2.6.14.3-bs3 #7 Fri Dec 16 17:47:00 CET 2005 i686 GNU/Linux

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

(none):~# ps -ef <ENTER>
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 22:56 ?        00:00:00 init [2]
root         2     1  0 22:56 ?        00:00:00 [ksoftirqd/0]
root         3     1  0 22:56 ?        00:00:00 [watchdog/0]
root         4     1  0 22:56 ?        00:00:00 [events/0]
root         5     1  0 22:56 ?        00:00:00 [khelper]
root         6     1  0 22:56 ?        00:00:00 [kthread]
root         7     6  0 22:56 ?        00:00:00 [kblockd/0]
root         8     6  0 22:56 ?        00:00:00 [pdflush]
root         9     6  0 22:56 ?        00:00:00 [pdflush]
root        11     6  0 22:56 ?        00:00:00 [aio/0]
root        10     1  0 22:56 ?        00:00:00 [kswapd0]
root        12     1  0 22:56 ?        00:00:00 [kjournald]
root       299     1  0 22:56 ?        00:00:00 /sbin/syslogd
root       305     1  0 22:56 ?        00:00:00 /sbin/klogd
root       343     1  0 22:56 ?        00:00:00 /usr/sbin/exim4 -bd -q30m
root       348     1  0 22:56 ?        00:00:00 /usr/sbin/inetd
daemon     361     1  0 22:56 ?        00:00:00 /usr/sbin/atd
root       364     1  0 22:56 ?        00:00:00 /usr/sbin/cron
root       379     1  0 22:56 tty0     00:00:00 /bin/login --
root       380   379  0 22:56 tty0     00:00:00 -bash
root       384   380  0 22:57 tty0     00:00:00 ps -ef

(none):~# df -h <ENTER>
Filesystem            Size  Used Avail Use% Mounted on
/dev/ubda            1008M  264M  694M  28% /
tmpfs                 768M     0  768M   0% /tmp
tmpfs                  14M     0   14M   0% /dev/shm

(none):~# halt <ENTER>

Broadcast message from root (tty0) (Sun Jan 15 22:57:17 2006):

The system is going down for system halt NOW!

Pretty cool, eh? It's your very own Debian 3.1 sandbox to make or break as you like. You can ignore warnings about hwclock and tty0, as these are normal for most UML systems because some hardware features are not supported by UML kernels. Feel free at this point to change the root password to anything you like.

Next, let's set up networking. You need two free static IP addresses, one for each side of a tunnel that will be created by the UML utilities we compiled earlier. I use 192.168.1.100 and 192.168.1.101 here. Use anything appropriate for your local network. To get started, boot up your Debian UML again, and use the following command:


cd /tmp/UML-Demo
./vmlinux-2.6.14.3-bs3  ubd0=Debian1.cow,Debian-3.1-x86-root_fs
-->root=/dev/ubda eth0=tuntap,,,192.168.1.100

After our Debian guest system is booted, log in as root again and modify the network configuration as follows.

Edit the /etc/network/interfaces file to contain only the following lines:


auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
 address 192.168.1.101
 netmask 255.255.255.0
 gateway 192.168.1.1

Enter a hostname of your choice in the /etc/hostname file and, finally, copy your resolver settings from /etc/resolv.conf on the host system to the guest Debian system. Halt the guest system and reboot.

After the guest system is booted, you will be able to ping it from anywhere on your network. I would suggest doing a couple things to your newly networked Debian system. First, install OpenSSH, and then update all installed packages to current versions. To do so, execute the following commands and answer the simple questions when asked:


apt-get install openssh-server
apt-get upgrade

The possibilities at this point are wide open. Any network service or application can be run under this guest Debian install. You can use UML to test applications across many kernel versions and Linux distributions all on one box. You can place the filesystem image and COW file on a USB thumbdrive, giving you a stable development environment across all the computers you use. User-mode Linux makes it easy and painless to test system changes that otherwise might make a system unbootable.

Fun—As in Destruction!

Okay, you know you've always wanted to do it. Now, here is your chance. Bring up a new standalone guest Debian UML system, and do an rm -rf /. If you are like me, your fingers start to curl under as you even consider typing that command. To begin, boot up the new Debian guest using the following command (notice we are using a different COW file, because we do not want to disturb the nice networked setup we created previously):


cd /tmp/UML-Demo
./vmlinux-2.6.14.3-bs3  ubd0=DangerDanger.cow,Debian-3.1-x86-root_fs
-->root=/dev/ubda

After our doomed friend boots up, let 'em have it. Make sure you double- (perhaps even triple-) check that you are still typing in the guest Debian system (Listing 2)!

Listing 2. Destruction can be fun if you are just testing.


Debian GNU/Linux testing/unstable (none) tty0

(none) login: root
Linux (none) 2.6.14.3-bs3 #7 Fri Dec 16 17:47:00 CET 2005 i686 GNU/Linux

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

(none):~#  rm -rf /
rm: cannot remove `//proc/meminfo': Operation not permitted
rm: cannot remove `//proc/uptime': Operation not permitted
(... Many warnings about read-only filesystems omitted ...)
rm: cannot remove `//proc/loadavg': Operation not permitted
rm: `//proc/self' changed dev/ino: Operation not permitted
(none):~# df -k
-bash: df: command not found
(none):~# ps -ef
-bash: ps: command not found
(none):~# halt
-bash: /sbin/halt: No such file or directory
(none):~#

It's hosed up pretty good at this point. In fact, you can't even run halt, because the halt program itself is gone. From another command window, kill the system with:


killall -9 vmlinux-2.6.14.3-bs3

Then, see what happens when you try to boot it up again using the same command (Listing 3).

Listing 3. A Very Ill Debian UML Guest


(... boot messages omitted ...)
EXT3-fs: INFO: recovery required on readonly filesystem.
EXT3-fs: write access will be enabled during recovery.
kjournald starting.  Commit interval 5 seconds
EXT3-fs: ubda: orphan cleanup on readonly fs
EXT3-fs: ubda: 66 orphan inodes deleted
EXT3-fs: recovery complete.
EXT3-fs: mounted filesystem with ordered data mode.
VFS: Mounted root (ext3 filesystem) readonly.
Warning: unable to open an initial console.
Kernel panic - not syncing: No init found. Try passing init= option to kernel.
EIP: 0073:[<a01c6691>] CPU: 0 Not tainted ESP: 007b:b7f3afac EFLAGS: 00000282
Not tainted
EAX: 00000000 EBX: 000012eb ECX: 00000013 EDX: 000012eb
ESI: 000012e8 EDI: 00000000 EBP: b7f3afb8 DS: 007b ES: 007b
a10afb80:  [<a0032d2a>] show_regs+0x21a/0x230
a10afbb0:  [<a0016c8c>] panic_exit+0x2c/0x50
a10afbc0:  [<a004a275>] notifier_call_chain+0x25/0x40
a10afbe0:  [<a0037501>] panic+0x71/0x100
a10afc00:  [<a000e2c0>] init+0x100/0x170
a10afc20:  [<a002bf59>] run_kernel_thread+0x39/0x50
a10afce0:  [<a001c3d4>] new_thread_handler+0xc4/0x120
a10afd20:  [<b7f3b420>] 0xb7f3b420

That's gotta hurt. So, as a lesson, do not do that on a real system. But because this is a UML guest with a COW file, you simply can delete the DangerDanger.cow file, and this guest system will boot up back to its initial state.

More on COW Files

The utility uml_moo included in the UML utilities will read a filesystem image and an associated COW file and create a new merged filesystem image. This allows you to merge changes stored in the COW file into a new master filesystem image. This makes it easy to clone working guest filesystem images when you have them set up the way you want.

Conclusion

User-mode Linux is fun to play with, but it also has some real-world uses. You can use it to test unknown or untrusted applications while limiting possible damage to the running host system. You can create virtual networks of UMLs by starting up multiple guests at once. This allows you to create a test-lab-in-a-box environment with very little time and effort, so you can try all those “Stupid Linux Tricks” you were afraid to try on a real system!

The code for this article is available at ftp.linuxjournal.com/pub/lj/listings/issue145/8803.tgz.

Resources for this article: /article/8883.

Matthew E. Hoskins is a Senior UNIX System Administrator for The New Jersey Institute of Technology where he maintains many of the corporate administrative systems. He enjoys trying to get wildly different systems and software working together, usually with a thin layer of Perl (locally known as “MattGlue”). When not hacking systems, he often can be found hacking in the kitchen. Matt is a member of the Society of Professional Journalists. He is eager to hear your feedback and can be reached at matt@njit.edu.

Load Disqus comments