...making Linux just a little more fun!
Here is a short course (with some excursions) to help you take your existing Desktop, roll it into an ISO pipe and smoke a USB stick or CD. This is not a task for the faint-hearted - there are no automated scripts in here!
Part of the reason for this hands-on approach is that each desktop system will have its own quirks, so writing scripts which handle the various conditions which might arise is painful. Secondly, there are numerous choices possible - you must mix your own. Finally, I must confess that I do not feel energetic enough to write the scripts at this point. Instead, you have this article!
So if you have spent a lot of time fine-tuning your configuration and want to waste some more time putting it onto a (re)movable drive - read on.
Since the chosen medium for our ``live'' portable system is a CD or USB stick we do not want to write to it often. In fact, in the case of a CD-R we can only write to it once. It should be obvious, however, that a ``live'' system does need to write something if it is to be counted as among the living!
While many mechanisms have been suggested to handle this, we
will (essentially) follow the system chosen by bootcd. To do this,
we need to create a directory /wraith
, an archive
/wraith.cpio.gz
and a script /etc/rcS.d/S01undead.sh.
The contents of the archive are rather system specific so you will need to choose its contents. However, if you are really, really impatient you can download the listing of the archive that I use and run the following:
cd / cat wraith.lst | cpio -o -H crc | gzip -c -9 > /wraith.cpio.gzDid you get a lot of error messages? No? Are you really sure that your system is almost identical to mine? Yes? Then you can skip the next subsection.
How does one find out what files need to be written to on a live system?
One way would be to find
all the files that have been written
to on your current desktop. To do this first find out when the system was
booted - a good measure of this is when the root filesystem was last mounted.
So for example
# Use your real root device in place of /dev/root. last_boot_time=$(dumpe2fs -h /dev/root | \ sed -n -e's/Last mount time: * //p')You may wish to use the log files or the
last
command instead.
You only need to know the last boot time approximately; subtract a minute
or so from it just to be on the safe side - unless you boot the system
more often than that! Now create a file with that time stamp using the
touch
command:
touch -d "$last_boot_time" /tmp/lastbootYou can now create the list of all files that were modified since that last boot (for simplicity we will only bother with the directories
/etc
and /var
; you can add some other directories if
you so desire):
find /etc /var -newer /tmp/lastboot > /tmp/changed # And, just for fun... find /home -newer /tmp/lastboot > /tmp/home_changedHave a look at these lists but don't delete them just yet. You should notice that there are three types of files that are written to on a running system.
mozilla
cache directories.
/tmp/changed
, according to this
classification: /tmp/write
will consist of those files (mostly
directories) that are empty at boot time but get written to as the system
runs; /tmp/links
will consist of the files that will be
quasi-static - we will keep a static version of these files at boot time
but we might want to change them on a running system. We will include the
third category of files in /tmp/links
as well, since we will not
treat them differently - but ultimately you may want to change this.
We first create a directory to hold the files that will be modifiable
at run-time - say /wraith
. Mount a RAM-based file system on it
by mount -t tmpfs tmpfs /wraith
. Big Fat
Warning: This file system is ephemeral and will be lost
when you halt the system. If you wish, you can use the directory as-is
(without the tmpfs
mount) during this subsection, but don't
forget to clean up its contents once you have created the archive as
explained below.
In /wraith
, we will create the top-level directories like
etc
, var
, tmp
and so on that we will
want to write to. In these directories we will create the files as per the
classification above. First, we'll do the writable but empty files:
cd / cat /tmp/write | cpio -pdum /wraithWe expect these files to be empty at start-up so we will ``zero'' them. Do this only to the files for which you don't want to keep the contents. For now I assume these are all the files in the list
/tmp/write
:
for file in $(cat /tmp/write) do if [ -f $file ] then > /wraith/$file fi doneOf course, we also need an empty
tmp
directory:
mkdir /wraith/tmp chmod 1777 /wraith/tmpNext, we create the links.
cd / for i in $(cat /tmp/links) do dir=$(dirname $i) top=$(echo $dir | cut -f2 -d'/') rest=$(echo $dir | cut -f3- -d'/') name=$(basename $i) mkdir -p /wraith/$dir ln -s /wraith/$top.ro/$rest/$name /wraith/$i done # As a safety measure to ensure that all configuration files # have been created mkdir -p /wraith/etc cd /etc for i in * do ln -s /wraith/etc.ro/$i /wraith/etc/$i doneThis is more complicated and needs further explanation. The idea is to make the ``static'' versions of the files available under the
.ro
top-level directories. So, for example /wraith/etc/hostname
will be
a link to /wraith/etc.ro/hostname
.
To see this at work create etc.ro
and var.ro
as
sub-directories in /wraith
. For each of these directories (say etc
)
we run a pair of commands like the following. (Warning: Be careful here. If
you haven't created all the links in /wraith/etc
as above you may crash
your running system).
mount --bind /etc /wraith/etc.ro mount --bind /wraith/etc /etcAfter these mounts, the file
/etc/hostname
is a link to
the original hostname
file which is now available as
/wraith/etc.ro/hostname
. Since the left-half of this
link is on the RAM disk we can perform replacement surgery on it:
vi /etc/hostname.new mv /etc/hostname.new /etc/hostnameOn the other hand, if you want to change a file in a sub-directory of
/etc
, it's a little more complicated:
mkdir /etc/X11.new ln -s /ram/etc.ro/etc/X11/* /etc/X11.new mv /etc/X11.new /etc/X11After this you can edit the files in
/etc/X11
. Yes, this is quite
twisted but (once you get the hang of it) not difficult to
manage - especially since we expect that we will edit these files
only rarely. An alternate approach is to create the directory tree under
/etc
in its entirety only leaving links to the files.
You can use the above mounts to test your choices of
/tmp/links
and /tmp/write
, but the real test will
come later. For now, undo the above mounts by a pair of commands like:
umount /etc umount /wraith/etc.roYou can also remove the
.ro
directories if you like.
Finally, we create an archive of this directory:
cd /wraith find . -xdev | cpio -o -H crc | gzip -c -9 > /wraith.cpio.gzThe
cpio
command will tell you how many 512-byte blocks you
wrote. If the archive is really large (more than 1MB or so) then you
probably need to re-do your choices.
We need a mechanism to bring the /wraith
directory into
operation at boot time. To do this, install a script like the following
one to run early at boot time. For example you could install the script as
/etc/rcS.d/S01undead.sh
.
# # undead.sh Mount and load up the /wraith directory for use # # Version: 0.3 01-Feb-2005 # # If this has already been run then don't run it again. # We can't handle two wraiths! [ -f /wraith/live ] && exit 0 # Create writable space mount -n -t tmpfs tmpfs /wraith # Create the directory structure cd /wraith gzip -dc /wraith.cpio.gz | cpio -idum cd / # Perform the cross mounts with bind # which is like a directory hard link. cd /wraith for i in * do mkdir $i.ro # We use mount with the -n # To avoid confusing the mtab mount -n --bind /$i /wraith/$i.ro mount -n --bind /wraith/$i /$i done cd / touch /wraith/live : exitFinally, you edit
/etc/fstab
so that the root filesystem is
mounted read-only at the next boot - just change defaults
to
read ro,defaults
in the appropriate entry.
Reboot and that's it! You have a read-only root system... or
almost. Actually, it is likely that you will find a number of places where
you didn't create the links you need or created the wrong links. Don't
worry. You can modify the /wraith.cpio.gz
archive to your heart's content.
Make the changes you need to the ``live'' /wraith
directory.
Now copy all the changes from /wraith
into /tmp/ghost
. The command
cd /wraith find . -xdev | grep -E -v '^./((live)|([^/]*\.ro))' > /tmp/listwill generate the newer list of files. You can unpack the older archive and compare its list of files with
/tmp/list
.
mkdir /tmp/ghost cd /tmp/ghost zcat /wraith.cpio.gz | cpio -idum find . -xdev > /tmp/oldlist wdiff -a /tmp/list /tmp/oldlistUsing the differences you can see what files you need to create in
/tmp/ghost
in order to match it up with the running
/wraith
. You can save your changes by something like
cd /tmp/ghost find . -xdev | cpio -o -H crc | gzip -c -9 > /tmp/wraith.cpio.gz mv /tmp/wraith.cpio.gz /wraith.cpio.gzThe changes will become automatic at the next boot. Of course, once you write the filesystem to a CD, you will have no chance to revise it again!
All this looks too complicated and life is too short? Just remove the
script /etc/rcS.d/S01undead.sh
, the archive /wraith.cpio.gz
and the directory /wraith
. You will have your system as pristine
as before.
We want our system to ``run anywhere'' - in particular, we should be
able to mount our root file system whether it resides on a CD or USB
stick (or perhaps even hard disk). If we use a CD then on
most systems this will be on the device /dev/hdb
or /dev/hdc
.
The USB stick usually shows up as /dev/sda
or
/dev/sdb
. It should be relatively simple to just
create a kernel which supports IDE CD drives and USB block devices.
When we boot such a kernel (with the correct root=<device>
parameter) the system will start up as expected on 90% of all systems
that one is likely to encounter. If this is OK with you then you
don't need an initrd so skip the rest of this section and read
the
HOWTO
on building the kernel with IDE CD and US support - don't forget
support for the ISO 9660 (CD), ext2 and vfat (Win95) file systems.
What about the remaining 10%? That will take 90% of the work as usual. One possible solution could be to build all the drivers of all possible CD drives, USB readers and the like into the kernel. Unfortunately, automatically probing for some of these devices will occasionally cause other devices to choke-up. It also seems like a bit of a waste to take up such enormous amounts of kernel memory for unused drivers. The solution provided by our intrepid kernel hackers is the modules+initrd mechanism which allows you to write a script that chooses which drivers to load depending on the devices found.
The boot loader (see the next section) will load the kernel and the
initrd into memory. We will use a ``standard''
Debian kernel image
which has essentially everything modularized (``essentially'' since we
must have support for at least one file-system built into the kernel
in order to load the init RAM disk - this could change if I understand
initramfs
better).
After the kernel has done its thing, it sets up the file-system with root
on the initrd and executes /linuxrc
but doesn't quite fully let
go - when /linuxrc
exits, the kernel executes /sbin/init
.
We follow Debian's choices when we visualize the boot process as follows:
/sbin/init
(still on the initrd).
/sbin/init
on the initrd is a script
that will run the following scripts:
mount_root
to recognise and mount our chosen file system on /mnt
.
init
are
/mnt
and cd
to it.
pivot_root
which makes the current directory the
root directory for the kernel and mounts the old root directory at
/initrd
. After this our ``real'' root file-system is
mounted as root.
chroot .
to change the root device of the
current process so that /initrd
is free to be unmounted. We
must do this so that the RAM disk is free to be unmounted which frees
its space for use by other processes.
/sbin/init
on the real
root file system. This is the ``real'' init
program which will
initialise the live system.
/linuxrc
and /sbin/init
needed for the initrd. So we
only need to provide the scripts loadmodules
and
script
.
Writing these scripts was one of the most complex steps for me as it deals with the aspect of Linux that I usually encounter the least - at least on a working system - booting! On the other hand, this is the job for which most installers and other forms of pre-install detection tools have been written. So we follow the ``teacher'' Tom Lehrer's dictum:
Plagiarize, Let no one else's work evade your eyes, Remember why the good Lord made your eyes, So don't shade your eyes, But plagiarize, plagiarize, plagiarize... (Only be sure always to call it please, "research".) -- Tom Lehrer, "Lobachevsky"
There is a good IDE driver detection routine that is part of the standard
Debian initrd. The Knoppix initrd gives us a safe order in which to load
all the SCSI modules. The Linux-Live initrd has a list of the necessary USB
modules to boot off a stick. So we put all these together to get routines
which I call loadmodules_ide
, loadmodules_scsi
and
loadmodules_usb
. The loadmodules
script on the initrd will then
act as a dispatcher - it will choose which routine to run depending
on what boot time parameters we give.
Still, we should do some work. So (plagiarising from the
hotplug scripts)
I also wrote a procedure loadmodules_pci
that loads only those modules which correspond to devices
in /sys/bus/pci/devices
which match the data found in
/lib/modules/kernel-version/modules.pcimap
. This procedure
makes use of the sysfs
file system that was introduced with Linux
2.6.x but something similar may be possible using /proc/bus/pci
in
Linux version 2.4.x. The principle is that the kernel does
provide a list of all the PCI devices that it found; for each such
device it also provides some device information - the interface for
this is the sysfs
file system or (in 2.4.x) the proc
file system.
On the other hand, each module writer makes a list of all devices that
the driver is known/expected to work for - the kernel build process writes
these to modules.pcimap
. By matching the two lists we should be
able to load only those modules which have a matching device. This only
works with PCI devices but most devices on PC's nowadays
(including SCSI cards and the USB controller) are PCI devices.
Here is the script to loadmodules that resulted from the above deliberations. This scripts depends on a list of modules that are related to block devices.
The second script we will use
provides the routine to mount the root device. Again the sysfs
file system provided by the 2.6.x Linux kernel comes to the rescue. Under
/sys/block
we find a list of all the block devices on the
system. If the root=
option is given to the kernel we can
check whether this block device is available. Otherwise we check each
available block device to find evidence that it is our root file system -
by checking for the existence of the archive, directory and script that we
created above.
The Debian initrd-tools package is
a collection of scripts and so can be installed on any GNU/linux system
(for example use the source package directly). The main script is
mkinitrd
which will create the standard Debian initrd. We will
run this script and make some changes in order to create our ``special''
initrd. First off all create some directory say
/tmp/mkinitrd.confdir
. In this directory we will create the
file exe
containing the list of executables that we want in
addition to the ``standard'' ones like /bin/sh
- in our case
we need /bin/grep
. Next we create a list of all the additional
files that we want to include; this is mainly the list of all modules that
are in some way connected with the use of block devices; here is my list. Finally, we also need a configuration file. We are set to
use mkinitrd
with this directory as our configuration
directory.
mkinitrd -r "" -k -d /tmp/mkinitrd.confdir -o /dev/nullThis will tell you the name of the working directory which will be something like
/tmp/mkinitrd.1234
. Now you need to edit the
/tmp/mkinitrd.1234/initrd/linuxrc.conf
file to reflect the
various file systems that you may use for your root file system.
Finally, you copy the scripts you created above and generate the
initrd with mkcramfs
.
dir=/tmp/mkinitrd.1234 rm $dir/initrd/scripts/* cp allmod.list $dir/initrd/etc cp loadmodules $dir/initrd cp script $dir/initrd chmod +x $dir/initrd/loadmodules chmod +x $dir/initrd/script mkcramfs $dir/initrd initrd.imgIf you build a kernel with
ext2
filesystem support instead of
cramfs
, then you need to create an ext2
filesystem
image based on the /tmp/mkinitrd.1234
directory instead.
We now combine the ideas of the previous two sections. I assume that you have managed to make your root filesystem boot in a ``read-only'' mode and that you are currently running in that mode. I also assume that you have created an initrd that can boot on ``any'' machine.
I know that the latter requirement is hard to check given that you have access to only one machine at a time. Moreover, it is difficult to find friends who will agree if you say ``I have on this floppy an initrd and kernel that I would like to test on your system''; those few will not remain friends if your kernel+initrd manages to fry their system.
In order to boot off a CD or USB stick we need some software that can
do that. The nominees are isolinux,
loadlin and grub... and the winner,
in this case, is... grub
.
The main file for booting using grub
is called stage2
or
stage2_eltorito
in the case of booting CD's. When these files
are properly installed (see below how this is done for CD's), they are
loaded and run by the booting machine. They look for a configuration
file /boot/grub/menu.lst
. We use a menu.lst
file that
looks like:
default 0Other than the kernel and the initrd, we needtimeout 5
# Pretty colours color cyan/blue white/blue
title Debian GNU/Linux with myinitrd root (cd) kernel /boot/vmlinuz-2.6.8-2-686 root=auto ro quiet vga=791 initrd /boot/initrd.img boot
stage2
and
menu.lst
in order to complete the list of steps given at the
beginning.
First you need a ``pristine'' copy of the root file system.
If you used the bind
mounts procedure to make the root
file-system read-only, then you can just do
mkdir /tmp/pristine mount --bind / /tmp/pristineYou then make a compressed tree of this file system:
mkzftree -x /tmp/pristine /hugeroomwhere
/hugeroom
is some place with a lot of disk space.
Remove the directories /hugeroom/lost+found
and
/hugeroom/boot
from under this directory. Create an empty
/hugeroom/boot
directory to which we copy the kernel image and
initrd. Into the /hugeroom/boot/grub
directory goes the file
stage2_eltorito
along with menu.lst
. These files
will not be compressed.
We now create the CD image:
mkisofs -R -J -z -hide-rr-moved -cache-inodes \ -b boot/grub/stage2_eltorito -b boot/boot.cat \ -boot-info-table -boot-load-size 32 \ -no-emul-boot -o mylivecd.iso /hugeroomThen we blank a CD (if necessary) and write our image to it. For a USB stick, we just create a partition and dump the entire image to this partition using
dd
. Since I do not have a system that can
boot off a USB, I can only check the floppy based boot for such a
system. Perhaps one of the readers can enlighten me on how this is to
be handled for USB-booting BIOSes.
You'll probably want to add a writable /home
directory to
your system. You need to further customise wraith.cpio.gz
for
that. Another thing that you probably want to do is to fix the
/etc/fstab
file that goes onto the CD. Other config files may
also need to be customised; /etc/X11/XF86config-4
comes to
mind - for this to work ``anywhere'' it is best to use the
vesa
driver. Similarly, use dhcp
to configure
ethernet rather than a hard-coded IP address in
/etc/network/interfaces
. On most systems there is a
hard disk and it is shame not to use it. You can set-up a swap partition
after you boot from the CD - be careful not to trash the host machine
though!
Today one can find a number of GNU/Linux systems that work off Live
CD's. There is Knoppix - and then
there are its Klones.
There is LNX-BBC, tomsrtbt, LTSP and even one called Puppy! There are the CD-based
installers for the common distributions. But, I am still not
satisfied. Each of these make choices that I am not comfortable with. They
choose KDE, Gnome or fluxbox
, when what I want is
fvwm
; or they choose xmms
when what I want is
alsaplayer
(in daemon mode)... and so on.
What's wrong with Sunil's excellent
article then? - just take a minimal Knoppix-like DSL and re-master it.
I would object that Knoppix puts everything in a cloop
image
which makes it difficult to read the ``real'' contents of the CD on a
generic system; further this also makes it difficult to master and/or
re-master.
There are other approaches like that taken by Gibraltar, bootcd or dfsbuild or linux-live.
The first two keep the files in a compressed ISO file-system. That makes it usable ``anywhere''. I did try these but for one reason or another they didn't work for me. For example they required the installation of additional packages on my desktop.
Ultimately, it comes down to this: I'm a terribly nit-picky kind of person, and I have spent a lot of time fine-tuning my system and no one is allowed to dictate what packages I must install and how they must be configured.
[ I like this Kapil guy, and the way he thinks. :) -- Ben ]
I enjoy tinkering with such things, and so I must have a system that I understand fully. People also mentioned additional kernel features in late 2.4.x and early 2.6.x that simplify the building of a live CD. Finally, isn't it fun to ``roll your own''?This document was translated from the LaTeX Source by HEVEA.
Kapil Hari Paranjape has been a ``hack''-er since his punch-card days.
Specifically, this means that he has never written a ``real'' program.
He has merely tinkered with programs written by others. After playing
with Minix in 1990-91 he thought of writing his first program---a
``genuine'' *nix kernel for the x86 class of machines. Luckily for him a
certain L. Torvalds got there first---thereby saving him the trouble
(once again) of actually writing code. In eternal gratitude he has spent
a lot of time tinkering with and promoting Linux and GNU since those
days---much to the dismay of many around him who think he should
concentrate on mathematical research---which is his paying job. The
interplay between actual running programs, what can be computed in
principle and what can be shown to exist continues to fascinate him.