Home  About TUX  TUX Gallery Games Articles  Links
How to create a live boot CD containing your favorite Linux Distro!

Buddhika Siddhisena <bud@babytux.org>

Motivation - Why?

Wouldn't it be cool to have a bootable version of Linux running totally off a CD, no hard disk required!. You could probably pack your collection of mp3s along with a small linux desktop containing xmms. Perhaps load a couple of games like Tuxracer, Tuxpuck or Frozen Bubble to play at a friends house who doesn't have linux. May be your friends hard disk running that NO GOOD OS, crashed with a blue screen and wont boot and needs to be rescued. Better yet you want to introduce your friends to the wonderful world of Linux.

But most of the above mentioned can be achieved using some great projects such as Knoppix (www.knoppix.org) and Demo Linux (www.demolinux.org). So why go through all the trouble to build your own live CD from scratch? For one thing its great learning experience that can help you to get an insight into what makes linux tick, under the hood! Then there is the advantage of being familiar with your favorite distro and the tools it provides and a chance to let your friends try it out too.

Before we start - What you'll need

  1. Installation CDs of your favorite distro.
  2. Free hard disk space -
    1 partition will be used to install a full linux distro
    1 partition will be used to install the distro to be put on the CD (about 700MB)
    1 partition for swap
  3. Download the latest version of Busybox from http://www.busybox.net. It enables us to run a few essential linux commands easily requiring less hard disk space.
  4. A CD-RW to test burning various images and a CD burner (duuuh!)
  5. Optionally a virtual machine like VMware (commercial www.vmware.com) or Bochs (opensource bochs.sourceforge.net) will help in testing the iso image
  6. Optionally download my sample scripts and initrd file to save some time.
  7. Working knowledge on using linux at a command line level.
  8. Lots of patience :)
DISCLAIMER: The steps outlined in this document come with no guarantee (or warranty) that it will work, and what ever you do to your system is at your own risk! I hold no responsibility. Please back up your data always (or better yet use a different hard disk).If this makes you uncomfortable stop reading any further!

phew! now thats out of the way lets get to it!

Steps involved - Overview

I've made my linux LiveCD using Mandrake Linux 9.0 (and later 9.1 with some changes), so I will explain it for that. I doubt it will be any different for Redhat. For other distros such as Debian or Suse you will have to improvise as necessary (commands for package management, file paths etc.)

  1. Install a full version of Mandrake Linux with kernel sources,development tools (such as gcc, make, etc.), cd burning tools (cdrecord, mkisofs) (Lets call $MAIN)
  2. Install a 700MB version of Mandrake Linux to a fresh partition containing only the required packages for the LiveCD (Lets call $LIVECD)
  3. Boot into $MAIN and compile a new kernel with support for ISO9660, initrd and ramdisk built in.
  4. Create an entry in the boot loader (lilo) to use the new kernel we compiled to boot $LIVECD
  5. Compile busy box so that it can later be copied to our $INITRD
  6. Create an initial RAM disk (initrd), format it using ext2 (lets call $INITRD) and mount it
  7. Make necessary changes to $LIVECD so that it boot in read-only mode as required for a Live CD and test setup
  8. Modify $INITRD so that it can boot off a $LIVECD and test it
  9. Make a bootable Linux CD ISO image of $LIVECD using the isolinux/syslinux method and burn it.
  10. Put in further configuration/optimization scripts to streamline and improve the system

Installing Mandrake ($MAIN) linux

This is a straight forward installation of linux. I choose development tools so that I would get gcc, make, perl etc., kernel sources, console tools, one or more desktop environments, cd burning tools such as mkisofs, cdrecord and optionally X based cd burning software (xcdroast, KonCD). I installed lilo as my boot loader and booted into my new shiny MAIN linux.

Installing the to be LiveCD Mandrake ($LIVECD)

This time I created only one root partition / of size 700MB (you can make it larger, if you want, as long as your software fits within 700MB of space or it wont fit the CD). I defined the same swap that I made for $MAIN (Actually not needed in Mandrake to proceed to next step but I think required in Redhat). I carefully choose only the packages I wanted -- KDE, Console tools (who can live without it!), Games (Tuxracer, Frozen bubble), Xine, XMMS and other multimedia players, Perl (usually standard on Mandrake) is required if you want to use my automation scripts as described in step 10, network clients, dialup utilities (ppp) and hdparm so I can turn on DMA for my DVD ROM.

Once the packages had been installed and it was time to create users I created one user called livecd with no password and set password as admin123 for root. If you are using Redhat you might even consider using root itself without creating another user as it makes some tasks easier, but on Mandrake root logins on X looks terrible with a Red screen and missing icons (actually thats a good thing -- you should never login as root), so I created a separate user.

For X configuration I choose a VESA driver, so that it will work with many different video cards. You could instead choose the fbdev (Frame Buffer Device) which is slightly faster than VESA but not as compatible with all cards. In any case, later on we will use a different probing technique that comes with XFree86-4 to detect a suitable X driver for a particular video card.

I choose not to install a boot loader as it will screw up my previous boot loader installed in $MAIN and instead created a boot floppy (not required -- just in case).

I booted into $MAIN and viewed /etc/lilo.conf and just copied what was like

        append="quiet devfs=mount hdc=ide-scsi"

changing the label to livecd and root= to point to the $LIVECD (/dev/hda9 for me)

        append="quiet devfs=mount hdc=ide-scsi"

Saved the file and typed lilo as root to install the changes and booted to the $LIVECD and tested it.

Getting ready for some action

Before we get our hands dirty, lets create some mount points to mount out $LIVECD and $INITRD as well as export these environment variables to point to the mount points.

  # mkdir -p /mnt/mklivecd /mnt/initrd
  # vi /etc/profile

export LIVECD=/mnt/mklivecd export INITRD=/mnt/initrd
# source /etc/profile

Compiling a custom kernel

Now begins the fun stuff! In order to mount the CDROM in the boot process, we need to have CDROM and ISO9660 support built into the kernel. Stock kernel that came with Mandrake had iso9660 as a kernel module which also happened to depend on a lot of other kernel modules (especially native language support modules) that made it difficult to extract and include in the initial ram disk. After struggling with mkinitrd command and trying to include all the required kernel modules for CDROM support, I came to the conclusion building your own kernel was the best.

I went into the kernel source directory

 # cd /usr/src/linux
 # make xconfig (or make menuconfig)

I didnt want to start from a scratch kernel and just wanted to make few modifications to the stock kernel so I didnt do a

# make mrproper

before make xconfig

Made sure the following settings were there for the new kernel as built-in (not loadable module)

Block Devices ->
  Loopback device support
  RAM disk support
  Default RAM disk size (8192) which is 8MB you could set to even 16MB (16384)
  Initial RAM disk support (initrd)

File Systems ->
  EXT3 support
  Compressed ROM file support
  ISO9660 CDROM file system support
  Transparent Decompression extensions (nice to have, I havent used it yet)
  /proc file system support
  EXT2 support

  IDE,ATA and ATAPI Block devices ->
     Include IDE/ATAPI CDROM support

Before compiling I vi ed /usr/src/linux/Makefile and modified EXTRAVERSION so that I wont end up screwing up my current loadable modules.

EXTRAVERSION = -16mdklivecd

I compiled the kernel and copied it to /boot

# make dep clean bzImage modules modules_install
# cp /usr/src/linux/arch/i386/boot/bzImage /boot/vmlinuz-2.4.19-16mdklivecd

Then modified /etc/lilo.conf to use the new kernel on $LIVECD as follows ...

       # initrd=/boot/initrd.img
       # append="quiet devfs=mount hdc=ide-scsi"

Don't forget to copy the kernel modules over to the $LIVECD in order for it to boot properly. You might even consider removing the current modules directory (used with the stock kernel) later on, if disk space becomes an issue.

# cp -a /lib/modules/2.4.19-16mdklivecd/ $LIVECD/lib/modules

Rebooted the system to $LIVECD and tested that everything worked fine.

Compiling Busy Box

I choose to put busy box into the initial ram disk inorder to be able to run general linux commands (ln,rm,ls,mount,mkdir etc.), which can be very useful when you want to debug when the LIVECD doesn't boot. Busybox is basically a single executable that supports many popular linux commands. By creating symlinks called, for example ls, that points to busybox we can make busybox behave like the real ls command.

# tar zxvf busybox-0.60.0.tar.gz
# cd busybox-0.60.0
# vi Config.h

You can modify Config.h to specify what commands you want busybox to emulate. The less options you have the smaller busybox binary turns out to be once compiled. I kept the defaults and made sure that there was support for sh, mknod, modprobe, lsmod, rmmod, ps, mount, fdisk, chroot, more, cat (or vi). By default busybox is set to compile with dynamic linking to glibc but since glibc can get pretty big (as much as 1-2MB) its better to compile it statically. Otherwise we would have to copy the glibc libraries to our $INITRD for busybox to work on our $LIVECD.

# vi Makefile

# If you want a static binary, turn this on. DOSTATIC = true

An even better way to get a very small busybox binary is to statically link against dietlibc (http://www.fefe.de/dietlibc), which is esentially as the name says, a lightweight implementation of glibc.

To compile and install I did a make and make install which created a directory called _install containing busybox executables.

The Linux boot process in a nutshell

An understanding of the linux boot process is important before we continue as it will help us to understand and debug if something doesn't work.

First lets see how the usual linux boot process works.

  • When you turn on the machine as part of the boot process the BIOS looks for a bootable media and loads the bootloader from a special area in the disk called the MBR (Master Boot Record). The bootloader in the case of linux will usually be LILO or GRUB.
  • LILO (or GRUB) will attempt to load the kernel and an initial RAM disk (initrd) if that was defined in the config file (/etc/lilo.conf).
  • Control is passed to the kernel, which will decompress the initrd and mount it as root device. You have to enable INITRD and Ram disk support in the kernel to have this happen.
  • The kernel executes /linuxrc if it exists in the root device which will perform various pre-boot tasks such as the loading of additional drivers.
  • Once linuxrc exits control is passed back to the kernel which will then mount the real root device as the root file system (like /dev/hda1). The real root device of the kernel can either be hard coded to the kernel using the rdev command or passed an argument from lilo (root=/dev/hda1)
  • /sbin/init from the real root system is executed which uses /etc/inittab configuration to boot rest of the system. The important thing to note here is that /etc/inittab defines the default runlevel (usually 3 or 5) and as part of the boot process all scripts in /etc/rcX.d/ are executed.

Next lets see how our LiveCD boot process works.

  • Similar to the usual boot process, the BIOS finds a bootable CDROM and loads the bootloader (ISOLINUX). Before the ISOLINUX bootloader was available, you needed to create a special bootable floppy image called an El Torito image (more on this later)
  • ISOLINUX will load the initrd and the kernel image and transfer control to the kernel
  • The kernel decompresses initrd and mounts it as root and executes our custom /linuxrc script.
  • Our /linuxrc script will attempt to guess the CDROM device containing our LIVECD and will mount it (this is where busybox comes in handy!). /linuxrc will finally change the real root device of the kernel to that of the initrd itself and exit.
  • Since the real root device is initrd it self the kernel will load /sbin/init from initrd which will be symlinked to the LIVECD.

Building the initial RAM Disk ($INITRD)

I created an initial ram disk as an 8MB (8192k) image file and set it up as a loop back device so that I can format it using ext2 file system. Then I mounted the loop back device under /mnt/initrd

# cd /root
# dd if=/dev/zero of=initrd bs=1k count=8192
# losetup /dev/loop0 /root/initrd
# mkfs -t ext2 /dev/loop0
# mount /dev/loop0 /mnt/initrd
# cd /mnt/initrd
# rmdir lost+found
Then I copied over the busybox file system found under _install to the root of $INITRD. I also made some other directories that resembles my $LIVECD (or $MAIN for that matter) / file system and deleted the linuxrc default file that came with busybox and made my own.
# cp -a /usr/src/busybox-0.60.0/_install/* .
# rm linuxrc
# mkdir -p dev lib mnt/cdrom proc boot etc home/livecd opt root tmp var initrd
# chmod 1777 tmp
# vi linuxrc

#!/bin/sh echo Mounting /proc filesystem mount -t proc /proc /proc echo Launching a demo shell sh umount /proc
# chmod 755 $INITRD/linuxrc

Its now a good idea to test that the busybox will work within the RAM disk environment. This can easily be done by chrooting to the $INITRD as follows.

# chroot $INITRD sh

BusyBox v0.60.5 (2003.07.15-17:18+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

# ls && exit

Now for those of you eager to try something, lets take a break and try to boot from this initial ram disk. First we compress our new initrd and place it in /boot. Then we modify the LiveCD entry in lilo.conf to say we want to use the new initrd.

# gzip -9 </root/initrd>/boot/initrd-livecd.img
# vi /etc/lilo.conf && /sbin/lilo

image=/boot/vmlinuz-2.4.19-16mdklivecd label=livecd root=/dev/hda9 initrd=/boot/initrd-livecd.img #append="quiet devfs=mount hdc=ide-scsi" vga=788 read-only

A very important point you should always remember is that when ever we update the $INITRD, we need to gzip and copy it to /boot and issue /sbin/lilo so as to install it properly.

Now when you boot your machine and select the liveCD from the lilo menu, you should get dropped into a busybox shell where you can try ls, cd etc. commands. Since we also mounted /proc, you should be able to see various info found in /proc. When you are done playing :) type exit and your linux distro should load up from the hard disk. You might see some warning and error messages, because we are now not using the stock initrd, which might have loaded some kernel modules, but ignore them for the moment.

Modify $LIVECD to boot as a Read-only File system

We need to make some modifications so that our $LIVECD can boot to a read-only file system (as in the case of a CDROM).

1) Comment out swap partition entry from $LIVECD/etc/fstab. Because we can't swap on the read-only cdrom and it makes little sense swapping to a ram disk anyways.

# vi $LIVECD/etc/fstab

#/dev/hda6 swap swap defaults 0 0

2) We don't need to execute depmod each time the machine boots to build the kernel module dependency file (modules.dep), since this is on a LiveCD and nothing will change. On the other hand it will significantly take longer as depmod scans through the /lib/modules to build the dependency file when booting from a CD (as opposed to a hard disk). So we stop depmod from executing by removing execute permission

# chmod a-x $LIVECD/sbin/depmod

3) Remove $LIVECD/etc/mtab and instead symlink it to /proc/mounts. Each time a mount or unmount occurs it will write an entry in the /etc/mtab and so we avoid this situation.

# rm $LIVECD/etc/mtab && ln -s /proc/mounts $LIVECD/etc/mtab

4) Move subdirectories in /var to /tmp/var/ using symlinks, since on our LIVECD /var needs to have write permission. We do this by mapping the /tmp to that of the initial ram disk, thus /var/lock will point to /tmp/var/lock and /var/run to /tmp/var/run which will be writable. We link /var/tmp to /tmp instead of /tmp/var/tmp since /tmp is already in ram disk.

# cd $LIVECD/var
# alias ls=ls
# for dir in `ls`
    mv $dir $dir-0
    ln -s /tmp/var/$dir .

# rm tmp && ln -s /tmp .
# mkdir -p $INITRD/tmp/var && cd $INITRD/tmp/var
# mkdir -p cache/man catman db local lock/subsys lock/console \
log/kernel log/mail nis opt preserve run/console spool/mail yp lib

I didnt create everything under my $LIVECD/var but most of the main required once and some extra ones. Its a lotta typing .. may be you can write a shell script for this :)

5) Modify the halt script by not allowing the fuser command to execute which will hang our system during shutdown/reboot of our liveCD system due to the way in which we mount the liveCD.

# vi $LIVECD/etc/init.d/halt

search for fuser and add a break above it, thus breaking out of the block.

# Added to avoid hang at shutdown
/sbin/fuser -k -m $sig $remaining >/dev/null
sleep 5

6) Though not necessary, I got annoyed seeing an error message about not being able to write /etc/issue and just commented out echo > /etc/issue and echo > /etc/issue.net bit.

# vi $LIVECD/etc/rc.local

# Commented out writing issue file echo "Welcome to Live CD" #echo "Welcome to ${HOST}" > /etc/issue.net #echo "$R" >> /etc/issue.net #echo "Kernel $(uname -r) on $a $SMP$(uname -m)" >> /etc/issue.net

7) I also commented out the entries in $LIVECD/etc/modules.conf since I didnt want to auto load any drivers which can depend on the machine hardware.

probeall scsi_hostadapter ide-scsi
#alias eth0 8139too
#above snd-emu10k1 snd-pcm-oss
#alias sound-slot-0 snd-emu10k1
probeall usb-interface usb-uhci
#alias sound-slot-0 emu10k1

At this point I would recommend you set the default runlevel to 3 so that X windows wont start automatically. We can change this back to 5 later once everything is working or leave it at 3 and just do a init 5.

# vi $LIVECD/etc/inittab

# cp -a $INITRD/tmp $LIVECD/tmp

Before we reboot we will have to copy the files in $INITRD/tmp since that wont be available to us when we boot fron the hard drive and therefour our symlinks in /var will point to no where. You could now go for a reboot and check that it boots to console without X. Remember to install the new $INITRD !

Modify $INITRD to boot from a CD

Now if you move to your $INITRD and do a ls you should see something similar to a root partition.

# cd $INITRD && ls

bin   dev  lib      mnt  proc  sbin  usr
boot  etc  home      linuxrc  opt  root  tmp   var

Since our method of booting the kernel involves keeping the $INITRD as the final / (root) of the system (as opposed to mounting /dev/hda1 or another /dev/ram1 as root partition) we need to have bin, sbin, lib, etc, dev to point to the version found on the mounted CD. Therefore we move most of these directories to mnt/cdrom and make symlinks to that from /. The exception to this is when we need to have write permission for a particular directory (/root,/dev, /home, /mnt, /tmp, /proc). We leave these directories alone.

# cd $INITRD
# mv bin sbin lib usr boot etc opt var mnt/cdrom
# ln -s mnt/cdrom/* .

An interesting point to note is that when we actually mount /mnt/cdrom to the real CDROM drive containing the Live linux image, all the symlinked directories (and therefore commands) will start getting executed from the LIVECD instead of busybox.

Next we need to copy some device files to the /dev/ directory in order to be able to boot linux properly in a read-only system. The reason being certain devices need to be written to (such as your console, terminal, video card, sound card, pseduo terminals used inside X etc.). Having copied the read/write device files (as we understand to be required :) we finally symlink to the rest of the device files. But since the symlink should point to /mnt/cdrom/dev/ when inside the LIVECD, we temporarly point our /mnt/cdrom to $LIVECD. Assuming most people would be using a PS/2 mouse, I made a symlink from mouse to psaux since some applications look for /dev/mouse.

# cd $INITRD/dev
# cp -a $LIVECD/dev/hd{a*,b*,c*,d*} .
# cp -a $LIVECD/dev/{console,null,mem,dri,tty?,pty*,dsp*,audio*,mixer,midi,sequencer*,ppp,psaux,random,pts} .
# mv /mnt/cdrom /mnt/cdrom-0
# ln -s $LIVECD /mnt/cdrom
# ln -s /mnt/cdrom/dev/* .
# rm /mnt/cdrom
# mv /mnt/cdrom-0 /mnt/cdrom
# ln -s psaux mouse

An easier method here could be to use the newer devfs file system to mount /dev (from perhaps within $INITRD/linuxrc script) so that the kernel will automatically create the device files for you based on your detected hardware. I've only experimented with this method slightly and got mixed results so I decided to stick with the conventional method for now.

We should also set permissions of $INITRD/home/livecd so that it is writable by the livecd user.

# chmod 777 $INITRD/home/livecd

Modify $INITRD/linuxrc

We now need to adapt the linuxrc script to attempt to mount the CDROM to /mnt/cdrom and tell the kernel to make INITRD as the final root file system.

# vi $INITRD/linuxrc

#!/bin/sh echo Mounting /proc filesystem mount -t proc /proc /proc echo Attempting to Mount CD-ROM (mount -t iso9660 /dev/hdb /mnt/cdrom && ln -s /dev/hdb /dev/cdrom) || (mount -t iso9660 /dev/hdc /mnt/cdrom && ln -s /dev/hdc /dev/cdrom) || (mount -t iso9660 /dev/hdd /mnt/cdrom && ln -s /dev/hdd /dev/cdrom) || (mount -t iso9660 /dev/hda /mnt/cdrom && ln -s /dev/hda /dev/cdrom) || ( echo 'cannot mount CD-ROM, dropping you to a shell' sh ) echo 0x0100 > /proc/sys/kernel/real-root-dev umount /proc

First we mount the proc file system and then attempt to guess where the CDROM might be staring from /dev/hdb. If the mount suceeds then we symlink /dev/cdrom to that device so that later on we can simply refer to /dev/cdrom to access the cdrom device. One point worth mentioning here is that since we try to mount the CDROM starting from /dev/hdb, if you happened to have two CDROM drives and have inserted the LIVECD in /dev/hdc drive while another different CD in /dev/hdb then the boot process will fail as a result of /dev/hdb getting mounted as the LIVECD. A workaround to this problem could be to extend the linuxrc script to use some sort of grep for a particular string (such as the string "Live CD" on the /etc/issue file) of the mounted LIVECD to make sure we have mounted the correct one.

If we were unable to mount a CDROM then we would drop the user into a shell so that they can perform additional debugging and perform the mount manually. Once the CDROM has been mounted we write the device number of the INITRD (0x0100) to /proc/sys/kernel/real-root-dev to that from now on the real root device is INITRD. We then unmount /proc and the script exists handing control back to the kernel that starts the boot process by running /sbin/init script off the LIVECD.

Now we can perform a final test that everything works before we burn the LIVECD to a real CDRW. We basically have to reinstall the modified initial ram disk using the bootloader by just executing lilo at the prompt.

# gzip -9 </root/initrd>/boot/initrd-livecd.img
# lilo

But before we perform the final test, we might as well comment out any unwanted entries(pretty much everything except for /proc and /dev/pts) in $LIVECD/etc/fstab.

# vi $LIVECD/etc/fstab

#/dev/hda9 / ext3 defaults 1 1 none /dev/pts devpts mode=0620 0 0 #/dev/hda6 swap swap defaults 0 0 #none /mnt/cdrom supermount dev=/dev/hdb, fs=auto,ro,--.isocharset=iso8859-1,umask=0 0 0 none /proc proc defaults 0 0

We remove all CDROMS from the drives and reboot the system and boot into LIVECD from lilo. At this point the CD mounting process will fail from within the linuxrc script and we will get dumped to a shell.

cannot mount CD-ROM, dropping you to a shell

BusyBox v0.60.5 (2003.07.15-17:18+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

# mount -o ro /dev/hda9 /mnt/cdrom
# exit

To emulate the CDROM we mount the LIVECD partition on the hard disk as read-only to /mnt/cdrom and exit out of the shell. This will inturn give back the control to linuxrc script which will set the root device, umount /proc and exit. This should now boot the system and present you with the familiar login prompt. Login as root and try a few commands and type startx to see if X windows loads correctly. Once you are satisfied reboot the system and come back to this document.

Creating the ISO image

We use ISOLINUX which is a part of syslinux (a bootloader) that simplfies the process of booting off a CDROM. Before ISOLINUX you had to create a boot floppy image (containing the kernel and initrd) and place it in the CDROM as a El Torito image. El Torito is the standard used to boot from a CDROM where the BIOS would treat it as an ordinary bootable floppy by looking for the floppy image. With ISOLINUX we can by pass having to exclusivly create this boot image and instead specify isolinux.bin as the boot image.

# cd $LIVECD && mkdir isolinux
# cp /usr/lib/syslinux/isolinux.bin isolinux
# cp /boot/vmlinuz-2.4.19-16mdklivecd isolinux/vmlinux
# cp /boot/initrd-livecd.img isolinux/initrd.gz

Find the isolinux.bin on your system (you might need to install syslinux depending on your distro) and copy it to $LIVECD/isolinux directory. Then copy the kernel and initrd images using the default names that isolinux expects. Next we create the isolinux config file (performs a similar task to that of lilo.conf).

# vi isolinux/isolinux.cfg

label linux kernel vmlinux append initrd=initrd.gz vga=788

You would simply copy the options in /etc/lilo.conf and append them to isolinux.cfg so they get passed to the kernel.

We create the ISO image according to the instructions given in /usr/share/doc/syslinux/isolinux.doc.

# mkisofs -o /root/livecd.iso -b isolinux/isolinux.bin -c isolinux/boot.cat \
-no-emul-boot -boot-load-size 4 -boot-info-table -l -R -r $LIVECD

The -l , -R and -r options are for RockRidge extensions which allow us to have softlinks on the CD and mixed case filenames. You could mount the /root/livecd.iso as a loop back device or better yet if you have vmware or bosche try booting from the iso image to test it.

Burning the ISO image

You can use cdrecord to burn the ISO image on Linux or alternatively use nero under windows. Inorder to use cdrecord you have to make sure that your CD Burner is supported.

# cdrecord -scanbus

This should give you the device of which your cd burner is attached to. To burn on a device attached to 0,1,0 (as reported by -scanbus) using 4x speed you could issue ...

# cdrecord -v dev=0,1,0 speed=4 -pad -dao /root/cdimg.iso

Improving our LIVECD

Now that we have a satisfactory system which boots up to text mode and being able to run X windows as root, we can spend some time improving things.

1) Ability to su

Since I am using a user called livecd to run most applications, sometimes I will need to su to perform other admin operations. The reason one is able to become root using su is because su itself is executed as the root user using a special permission bit called the suid bit. But as a result of creating an ISO9660 image the $LIVECD/bin/su loose its suid bit and therefore we find ourself unable to su. The solution to this is to copy su to our $INITRD/root that is a ext2 file system and will therefore perserve the suid bit.

 # cd $LIVECD/bin
 # cp su $INITRD/root && mv su su-0 && ln -s /root/su .

2) Getting an optimized video card driver

While the SVGA driver works well with most systems, it would be nice to be able to detect the video card and load the apropriate driver. With the new XFree86 4.x this can easily be achieved by running the XFree86 -configure to detect our video hardware and write a sample /root/XF86Config.new file. Unfortunately this file doesn't contain screen resolutions so we end up loading the highest possible resolution n 256 colors so I wrote a small perl script that will add the resolution and default color depth. To save space on RAM disk I create the file on the LIVECD/root directory and symlink to it from the INITRD/root.

# vi $LIVECD/root/xconfig

#!/usr/bin/perl $x=`/usr/bin/X11/XFree86 -configure`; open(INP,"/root/XF86Config.new"); open(OUT,">/root/XF86Config-4"); while(<INP>){$x=$_; if($x=~/Monitor[ ]+\"/){ print OUT $x; print OUT " DefaultDepth 16\n"; } elsif($x=~/\bDepth[ ]+/){ print OUT $x; print OUT qq~Modes "1024x768" "800x600"\n~; } else{ print OUT $x; } } close(INP); close(OUT); unlink("/root/XF86Config.new");
# chmod 755 $LIVECD/root/xconfig # cd $INITRD/root && ln -s /mnt/cdrom/root/xconfig .

3) Loading various drivers such as for the sound card and network card

This is a bit tricky and I suppose I have to come up with a better way to handle this. For the moment I used the lspcidrake which only comes with Mandrake to perform this task. lspcidrake as opposed to plain old lspci displays the device name as well as the kernel module associated with that which makes things easier.

# lspcidrake

agpgart         : VIA Technologies|VT82C691 [Apollo PRO]
unknown         : VIA Technologies|VT82C598 [Apollo MVP3 AGP]
unknown         : VIA Technologies|VT82C596 ISA [Apollo PRO]
unknown         : VIA Technologies|VT82C586 IDE [Apollo]
usb-uhci        : VIA Technologies|VT82C586B USB
unknown         : VIA Technologies Inc|Power Management Controller
8139too         : Realtek|RTL-8139
snd-emu10k1     : Creative Labs|SB Live! (audio)
emu10k1-gp      : Creative Labs|SB Live! (joystick)

# vi $LIVECD/root/driverconfig

#!/usr/bin/perl # Get list of hardware $x=`lspcidrake`; @dev=split(/[\n\r]/,$x); # Get list of all sound drivers compiled for kernel $y=`modprobe -l -t drivers/sound`; @sound=split(/[\n\r]/,$y); for ($i=0;$i<$#sound+1;$i++){ $sound[$i]=~s!.*\/!!g; $sound[$i]=~s!\.o!!g; } while(@dev){$x=shift @dev; if(!($x=~/unknown/i) && $x=~/([^\s]+)\s+\:/i){ $dev=$1; print "Attempting to Load driver for : $dev\n"; if($y=is_sound($dev)){ $y=`modprobe $y`; } else{ $y=`modprobe $dev`; } } } sub is_sound{my($dev)=shift @_; # returns driver if match as a sound driver my($ret); for ($i=0;$i<$#sound+1;$i++){ if($dev=~/$sound[$i]/ || $sound[$i]=~/$dev/){ # remove that device from list and return ture $ret=$sound[$i]; $sound[$i]=''; return $ret; } } return 0; }
# chmod 755 $LIVECD/root/driverconfig && ln -s /mnt/cdrom/root/driverconfig $INITRD/root/driverconfig

I got into some trouble with sound drivers trying to blindly load a driver with the name that lspcidrake returns so instead ended up writing a slightly complex script that first gets a list of available sound drivers compiled in to the kernel as modules (modprobe -l -t drivers/sound) and then rolls through the list, testing if the current lspci entry is a possible sound sub kernel module.

For other distros here are some suggestions you could try out.
- Try loading the first matchng kernel driver for a particular device. This wont always work or give you the optimum driver

     # /sbin/modprobe -t drivers/net \*
     # /sbin/modprobe -t drivers/sound \*

- Install kudzu and make sure all files that it writes to reside on /root of $INITRD and symlink to the correct path.
- Try extending my perl script so that it uses a similar method as is_sound function to load sound, network,usb etc. drivers by pattern matching against currenly available kernel modules.
- Perhaps try installing the mandrake lcpcidrake on your distro

4) Configuring the network

Once the network card drivers have been loaded we can try to configure the network. For this I copied /etc/hosts, /etc/resolv.conf /etc/sysconfig directory to $INITRD and symlinked to it from $LIVECD

   # cd $INITRD/root
   # cp -a $LIVECD/etc/{hosts,resolv.conf,sysconfig} .
   # cd $LIVECD/etc
   # mv hosts hosts-0 && mv resolv.conf resolv.conf-0 && mv sysconfig sysconfig-0
   # ln -s /root/hosts . && ln -s /root/resolv.conf . && ln -s /root/sysconfig .
   # vi $LIVECD/root/netconfig

#!/usr/bin/perl print "Running Net config\n\n Press enter to accept default"; print "\nEnter IP Address ( "; $ip=<STDIN>;chomp($ip); $ip=(length($ip)==0)?'':$ip; print "\nEnter Subnet mask ( "; $subnet=<STDIN>;chomp($subnet); $subnet=(length($subnet)==0)?'':$subnet; print "\nEnter Gateway ( "; $gw=<STDIN>;chomp($gw); $gw=(length($gw)==0)?'':$gw; print "\nEnter DNS ( "; $dns=<STDIN>;chomp($dns); $dns=(length($dns)==0)?'':$dns; # write gateway info /root/sysconfig/network $out=qq~NETWORKING=yes FORWARD_IPV4=false HOSTNAME=mdkdemo DOMAINNAME=localdomain GATEWAY=$gw ~; open (OUT,">/root/sysconfig/network"); print OUT $out; close OUT; # write IP info /root/sysconfig/network-scripts/ifcfg-eth0 $out=qq~DEVICE=eth0 BOOTPROTO=static IPADDR=$ip NETMASK=$subnet ONBOOT=yes ~; open (OUT,">/root/sysconfig/network-scripts/ifcfg-eth0"); print OUT $out; close OUT; # write DNS info /root/resolv.conf $out=qq~search localdomain nameserver $dns ~; open (OUT,">/root/resolv.conf"); print OUT $out; close OUT; # Bring up network print "\nAttempting to bring up network ...\n"; system("/sbin/service network restart"); system ("/sbin/ifconfig");
# chmod 755 $LIVECD/root/netconfig && ln -s /mnt/cdrom/root/driverconfig $INITRD/root/netconfig

5) Configuring swap

Adding an option to use a linux swap partition or a swap file on a windows partition can often improve performance on machines with limited memory. The following script will try to auto detect a linux swap partiton and use it. Failing that it will attempt to use a FAT32 partition to create a swap file.

   # cd $INITRD/root
   # vi $LIVECD/root/swapconfig

#!/usr/bin/perl print "Running Swap config\n\n Press enter to accept defaults"; print "\nLisiting partitions on first hard disk (/dev/hda) :\n"; $part=`/sbin/fdisk -l /dev/hd[ab]`; print $part; @part=split(/[\r\n]/,$part); # Scan for Linux swap $swap=''; for($i=0;$i<$#part+1;$i++){ if($part[$i]=~/(\/dev[^\s]+).*Linux[ ]+swap/){ $swap=$1; print "\n\nFound Linux swap partition\n"; print "\nTurning on swap for [$swap]\n"; exec "swapon $swap"; exit 1; } }#end for($i=0;$i<$#part+1;$i++) # No Linux swap found lets try to create on FAT32 for($i=0;$i<$#part+1;$i++){ if($part[$i]=~/(\/dev[^\s]+).*Win95[ ]+FAT32/){ $swap=$1; print "\n\nEnter Windows FAT32 partition to create swap file livecd.swp [$swap] :"; $inp=<STDIN>;chomp($inp); $swap=(length($inp)==0)?$swap:$inp; if(-e $swap){ system "mkdir /mnt/swap;mount -t vfat $swap /mnt/swap"; if(!(-s "/mnt/swap/livecd.swp")){ # Create swapfile print "\nEnter size of swap file [60-128MB] (96MB):"; $inp=<STDIN>;chomp($inp); $swapsize=($inp==0 || $inp=~/[^0-9]+/)?98304:$inp*1024; system "losetup /dev/loop0 /mnt/swap/livecd.swp || losetup /dev/loop1 /mnt/swap/livecd.swp"; print "\nCreating livecd.swp file ($swapsize KB). This can take a while...\n"; system ("dd if=/dev/zero of=/mnt/swap/livecd.swp bs=1k count=$swapsize;mkswap /mnt/swap/livecd.swp;"); print "\nSwap created !\n"; } print "\nUsing swap file ..\n"; system "swapon /mnt/swap/livecd.swp"; exit 1; }else{ print "\nInvalid partition!\n"; exit 0; }#end if(!(-s "/mnt/swap/livecd.swp")) }#end if($part[$i]=~/(\/dev[^\s]+).*Win95[ ]+FAT32/) }#end for($i=0;$i<$#part+1;$i++)
# chmod 755 $LIVECD/root/swapconfig && ln -s /mnt/cdrom/root/swapconfig $INITRD/root/swapconfig

6) PPP dialup configuration

Inorder to dialout, we need to copy over /etc/ppp to INITRD and symlink to it. Then to dial we can su and issue kppp dialer to setup an account and dial to the internet.

   # cd $LIVECD/etc && cp -a ppp $INITRD/root && mv ppp ppp-0 && ln -s /root/ppp .

7) PCMCIA configuration

For notebook computers we can load PCMCIA drivers and load the cardmanager that will look for card insertions and removal.

   # vi $LIVECD/root/pcmciaconfig

echo "Loading pcmcia drivers ..." /sbin/modprobe -t pcmcia \* echo "Starting pcmcia card manager" /sbin/cardmgr&
# chmod 755 $LIVECD/root/pcmciaconfig && ln -s /mnt/cdrom/root/pcmciaconfig $INITRD/root/pcmciaconfig

8) Making it a little user-friendly

Now that we have a lot of scripts we can ask the user whether they like to execute them during system bootup. For this I created a simple autorun script and included it in /etc/rc.local so it runs at bootup.

  # vi $LIVECD/root/autorun

#!/usr/bin/perl # Load drivers system("/root/driverconfig"); system("clear"); print "Lets configure the system. Press enter to accept default in caps\n\n"; # script to autoconfig swap print "Do you want me to configure swap space -- can improve performance (y/N) :"; $res=<STDIN>; chomp $res; if($res=~/y/i){ system("/root/swapconfig"); } # script to automatically config network print "Do you want me to configure your network card? (y/N) :"; $res=<STDIN>; chomp $res; if($res=~/y/i){ system("/root/netconfig"); } # script to automatically load drivers and X print "Do you want me to load the graphical X mode? (Y/n) :"; $res=<STDIN>; chomp $res; if($res=~/n/i){ exit; } # Config X server system ("su -c /root/xconfig"); # Load graphical mode exec "/etc/X11/prefdm";
# chmod 755 $LIVECD/root/autorun && ln -s /mnt/cdrom/root/autorun $INITRD/root/autorun # cat >> $LIVECD/etc/rc.local clear /root/autorun ^D (ctrl + D)

I also modified /etc/motd to give a simple welcome message and describe the various scripts they can use to configure the system as well as /etc/issue file telling them to login as root admin123.

Final Thoughts

Obviously there are tons of improvements that can be made to extend functionality and usability. Now that we have a basic understanding of how bootable CDs operate we could easily look at other similar high profile projects like Knoppix to learn a few more tricks. Some needed improvements include the ability to save/restore various config files to disk, ability to create a presistant home directory and improve security. I hope this article will be an inspiration for you to continue experimenting with Linux live CDs.

Happy hacking :)


These are my latest script files and initrd.gz I used to build a Mandrake 9.1 livecd.

References and Resources

Buddhika Siddhisena <bud@babytux.org>Updated : September 16, 2003
Created : August 7, 2003
© 2004 by BabyTUX.org - Site by: http://www.samanala.comWCL   Home | About Tux | Tux Gallery | Games | Articles | Links