User-Mode Linux
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.
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.
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.
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.
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.
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.
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.