AUTHOR: Mike Hernandez <mike at culmination dot org>

DATE: 2005-09-03

LICENSE: GNU Free Documentation License Version 1.2

SYNOPSIS: Beginners Guide To Creating A Live CD With LFS 6.0
 
ALTERNATE LOCATION: For most recent edition of this hint you can check
                    http://www.culmination.org/Mike/2.6-udev-nptl-bootcd.txt

DESCRIPTION:
  This hint is a detailed description of how I made a live cd from a system 
  built with LFS 6.0. The reader should be able to follow the hint to create
  a live cd of his or her own. It was adapted from the "Easy" hint to work with
  LFS 6.0. It is my intention to make it as easy as possible for a first time 
  live cd builder.

PREREQUISITES:
  1. A working LFS System (and cdrtools if you want to burn the cd, of course)
  2. An LFS system built for the purpose of creating a live cd (see below)
  3. CD Writer + Media (but you knew that, I bet)
  4. Syslinux  (http://freshmeat.net/projects/syslinux/)

HINT:

  Before we get started here, let me make some things clear. 
  
  I call this section:

                         ------------------------------
                        | What it is and what it ain't |
                         ------------------------------

                                 ------------
                                | What it is |
                                 ------------

  This is a hint for creating a simple live cd. To be more clear:
  Upon booting from the cd, a user will be presented with a linux prompt.

  If you build it from an LFS system (no BLFS stuff) then the cd will be
  helpful for repairing an LFS system that got messed up (menu.lst typos
  /etc/fstab weirdness, things like that).

  Creating live cd's can be fun, and knowing how to do so opens up the door 
  for many possibilities. This hint is a good starting point for that 
  "ultimate rescue cd" idea you've had or maybe can help you answer the 
  "I wonder if Linux will run on my _____" question =)

                               ---------------
                              | What it ain't |
                               ---------------

  As I said above, this will help you make a simple livecd. The user will be 
  presented with a linux prompt and not much else beyond that unless you add 
  the functionality yourself.

  This hint does not describe how to put tons of software on the cd as you 
  might find on knoppix or the official LFS boot CD.
  
  This hint does not describe how to create an install program to copy the
  contents of the cd onto a hard drive.

  Also, you will not find any cross-compiling info here, so if you want your
  CD to boot on a completely different architecture than your own, you have
  to do that research yourself.

               --------------------------------------------------
              | Why it is what it is, and it ain't what it ain't |
               --------------------------------------------------

  This hint is designed to help a first time live cd builder. I tried to keep
  it as simple as possible, so that it wouldn't require too much advanced 
  knowledge of LFS to complete (the hint it's based on is called the "Easy"
  boot CD hint, after all.) Of course you need to know a bit, but expert 
  knowledge is not a requirement.  Creating an install CD, or using 
  filesystems such as squashfs, may require more info than a beginner might 
  have, and attempting to include all of it might make this hint more 
  confusing than helpful.

                               ---------------
                              | The Beginning |
                               ---------------
  
  This hint, like the hint it is based on, requires that the reader have two
  (yes 2) systems built. One system is your usual working system, which you
  will use to burn the cd when you are done. The other is the system created
  specifically for the purpose of putting on the live CD. If you just want 
  the CD to boot on the machine you built it on, then making a copy of your
  LFS system to another partition is fine.

  You might be wondering why you need two systems to create a live CD. The 
  reason is actually quite simple. If you are like me, you optimized your LFS 
  system to suit your pc (or in my case, laptop). I have a Pentium 4 system, 
  so all of the programs which can handle optimization have been built with:

        CFLAGS='-march=pentium4' 

  So what's the issue? It's that I want my live CD to run on just about 
  anything that can boot a CD! The programs on my current system won't run on 
  any machine that isn't a pentium 4. I actually use many more optimizations 
  when I build, but here I mention the architecture because it brings up an 
  important point:

          ----------------------------------------------------------
         | Know in advance what hardware you want the CD to run on! |
          ----------------------------------------------------------

  Before you start you should also decide what you want your live CD to do. 
  You may want to consider optimizing everything in your live CD system for 
  size, or for a specific type of machine, or not to optimize at all.
  Which is best? That's up to you. 

  Another reason you want to have a separate system is because in order to
  create the CD we make some directories and move very important files
  around. In the event that something goes wrong, having at least your
  initial working system (if not a backup of the live CD system) can be a
  life saver.

                          ----------------------------
                         | Setting up the environment |
                          ----------------------------

  To make things easier, you should set the LIVECD variable to point to the 
  mount point of the system you are going to use for the CD:

        export LIVECD=/mnt/livecd 

  You also might want to set CDDEV as well (actually, you should):

        export CDDEV=/dev/your-drive+partition

  Set the ISODIR to the location where you would like to keep the image:

        export ISODIR=/where/you/have/space

                        --------------------------------
                       | Configuring the live CD system |
                        --------------------------------

  Now that the environment is all set up, it's time to do some final
  configuration of the live CD system. This includes making sure the kernel
  is how you want it, and maybe adding some other programs if you so desire.

  If you are absolutely sure that the kernel for your CD system will work for 
  the system you want the CD to run on, and you don't need to install any
  other software for your CD, then you can skip to the section called
  "Moving around the furniture".

  Before we can configure and install a new kernel for the live CD, we have 
  to chroot into the system:

  1. Mount your live CD system:

     mkdir -p $LIVECD
     mount $CDDEV $LIVECD

  2. As per the directions of the LFS-6.0 book, 
     mount the virtual file systems prior to entering the chroot:
		
     mount -t proc proc $LIVECD/proc
     mount -t sysfs sysfs $LIVECD/sys
     mount -f -t ramfs ramfs $LIVECD/dev
     mount -f -t tmpfs tmpfs $LIVECD/dev/shm
     mount -f -t devpts -o gid=4,mode=620 devpts $LIVECD/dev/pts

  3. chroot into the live CD system with the command given at the end of
     chapter 6. (NOT THE CHROOT COMMAND AT THE BEGINNING!!!):

     chroot $LIVECD /usr/bin/env -i \
     HOME=/root TERM=$TERM PS1='\u:\w\$ ' \
     PATH=/bin:/usr/bin:/sbin:/usr/sbin \
     /bin/bash --login

  4. mount ramfs and populate /dev

     mount -n -t ramfs none /dev
     /sbin/udevstart

  5. Create essential symlinks and directories not created by udev:

     ln -s /proc/self/fd /dev/fd
     ln -s /proc/self/fd/0 /dev/stdin
     ln -s /proc/self/fd/1 /dev/stdout
     ln -s /proc/self/fd/2 /dev/stderr
     ln -s /proc/kcore /dev/core
     mkdir /dev/pts
     mkdir /dev/shm

  6. Perform the mounting of the proper virtual (kernel) file systems:

     mount -t devpts -o gid=4,mode=620 none /dev/pts
     mount -t tmpfs none /dev/shm

  Now you are in the chroot, so you can configure and build the kernel for
  the CD. If you are absolutely sure that the kernel for your CD system
  will work for the system you want the CD to run on, then you can skip this
  part and just install the software you need.

  Building the kernel for your live CD is not something to be taken lightly
  by even a veteran! There are options which need to be built in and others
  which can be built as modules. I assume that if you feel you are ready to
  create a live CD out of your system, that you know what you are doing. I do
  suggest not using modules, because it can create issues if you aren't
  familiar with all the work that goes into using modules with the 2.6 kernel
  and udev. Be Careful! =)

  When you are done installing your kernel you might want to test it to be 
  sure it works. Once you are all set, then you should exit the chroot and
  run the rest of the commands from your working system, as root.

                       -----------------------------
                      | Moving around the furniture |
                       ----------------------------- 

  A.K.A. Moving /dev /etc /home /root /tmp /var  to /fake/needwrite

  When I followed the "Easy" hint, I came to this point and wondered: 
  What is /fake/needwrite? (Actually it was more like "what the hell is this 
  /fake/needwrite stuff?") Here is an answer for those of you wondering the 
  same:

  The /fake/needwrite directory is used to hold files that must be writable 
  while the live CD is running. Obviously, these files can not remain on the 
  CD-ROM (due to the RO part of ROM), and so they are moved to a location from 
  which they will be copied into a ramdisk during the CD boot process. Having 
  the files in a ram disk allows us to modify them. Also, we can create files in
  the directories which reside on the ram disk, such as in /tmp, or /home.

  First we have to create the directory and a mount point for the ramdisk:

    * NOTE: If you haven't already mounted your CD system, do so now:
    * mount $CDDEV $LIVECD
    * thanks Bernard, for pointing this out ;)

    mkdir -p $LIVECD/fake/{needwrite,ramdisk}

  Then we can move the directories which we want to have write access there:

    cd $LIVECD/
    mv dev/ etc/ home/ root/ tmp/ var/ fake/needwrite/

  Now we have to create symlinks so that everything seems to be	as before.

    cd $LIVECD/
    ln -s fake/needwrite/dev dev
    ln -s fake/needwrite/var var
    ln -s fake/needwrite/tmp tmp
    ln -s fake/needwrite/root root
    ln -s fake/needwrite/home home
    ln -s fake/needwrite/etc etc

  At this point, execute ls -l 
  The output should say:
  
    dev  -> fake/needwrite/dev
    etc  -> fake/needwrite/etc
    home -> fake/needwrite/home
    root -> fake/needwrite/root
    tmp  -> fake/needwrite/tmp
    var  -> fake/needwrite/var

                 ---------------------------------------------
                | Tailoring the CD boot process for our needs |
                 ---------------------------------------------

  Ok, we have /etc /dev /var /tmp /root /home linked to /fake/needwrite which 
  is read-only (because it's on the CD). To be able to login (and to run 
  services which need write access to /dev /var /tmp /root /home or /etc) we 
  must call a script from our /etc/rc.d/init.d/ directory which mounts a ram 
  disk on /fake/needwrite with write access.

  The following script creates 2 ram disks, a temporary one, and one that 
  will house the directories which need write permission. It copies the files 
  from the CD to the temporary ram disk, and then from there to the final ram 
  disk.

  The original hint used 1 ram disk, but this caused a serious problem for me. 
  First of all, the initrd which is loaded at boot time uses the first ramdisk 
  (/dev/ram0). Therefore trying to mount /dev/ram0 somewhere else leads to 
  "device already mounted" errors. Secondly, unmounting a ram disk causes
  all of the files to be lost. The original hint unmounted the ram disk and 
  remounted it, assuming the files would still be there. That did not work for 
  me, which is why I made sure that /dev/ram{0,1,2} are all present, and 
  suggest that you do the same. If they aren't present you should check your
  udev setup. You might try using mknod to create the files, but for now
  I will say if you don't have /dev/ram devices, you are going to have to
  do some research on your own to figure out why.

  * Bernard suggested a for loop for doing this, which you might try:
  * for i in 0 1 2 3 4 5 6 7; do
  *  mknod $LIVECD/dev/ram$i b 1 $i;
  * done

  Copy and paste the script, and tailor it to your needs as necessary:

cat > $LIVECD/etc/rc.d/init.d/create_ramdisk << "EOF"
#!/bin/sh

# SET UP SOME VARIABLES FOR DEVICES AND DIRECTORIES

dev_ram=/dev/ram1
dev_ram2=/dev/ram2
dir_ramdisk=/fake/ramdisk
dir_needwrite=/fake/needwrite

# SOURCE THE FUNCTIONS FILE

source /etc/rc.d/init.d/functions

case "$1" in
  start)
          
# CREATE THE RAM DISK
          
    echo "Creating ext2fs on $dev_ram..."
    /sbin/mke2fs -m 0 -i 1024 -q $dev_ram > /dev/null 2>&1
    evaluate_retval
    sleep 1

# MOUNT THE RAM DISK

    echo "Mounting ramdisk on $dir_ramdisk..."
    mount -n $dev_ram $dir_ramdisk -t ext2
    evaluate_retval
    sleep 1

# COPY FILES TO THE RAM DISK

    echo "Copying files to ramdisk..."
    cp -a $dir_needwrite/* $dir_ramdisk > /dev/null 2>&1
    evaluate_retval
    sleep 1

# CREATE SECOND RAMDISK

    echo "Creating second ramdisk"
    /sbin/mke2fs -m 0 -i 1024 -q $dev_ram2 > /dev/null 2>&1
    evaluate_retval
    sleep 1
          
# MOUNT SECOND RAMDISK

    echo "Mounting second ram disk"
    mount -n $dev_ram2 $dir_needwrite -t ext2
    evaluate_retval
    sleep 1
          
# COPY FILES TO THE SECOND RAMDISK
          
    echo "Copying files to the second ram disk"
    cp -a $dir_ramdisk/* $dir_needwrite
    evaluate_retval
    sleep 1
          
# UNMOUNT THE FIRST RAMDISK

    echo "Unmounting and clearing first ram disk"
    umount -n $dir_ramdisk > /dev/null 2>&1
    blockdev --flushbufs /dev/ram1
    evaluate_retval
    sleep 1
  ;;
  *)
    echo "Usage: $0 {start}"
    exit 1
  ;;
esac

EOF

  Make the script executable with the following command:

    chmod 0755 $LIVECD/etc/rc.d/init.d/create_ramdisk

  Gabe Munoz pointed out that this symlink can be S11, where it used to be at 
  S00. Of course feel free to number the symlink as you see fit. (This is LFS 
  after all!) Just make sure that you dont start any scripts that will try to 
  write to one of the directories that needs write permission before the 
  files are copied over.

    cd $LIVECD/etc/rc.d/rcsysinit.d
    ln -s ../init.d/create_ramdisk S00create_ramdisk


  Next we can install the bootloader, isolinux.  It is available with the
  syslinux package. You can find syslinux on freshmeat:

    http://freshmeat.net/projects/syslinux/

  The directions below assume the tarball syslinux-2.11.tar.bz2 is already 
  placed in $LIVECD/usr/src. (use the current version, 2.11 might be old by 
  the time you read this)

    cd $LIVECD/usr/src
    tar xzf syslinux-2.11.tar.gz
    mkdir $LIVECD/isolinux
    cp syslinux-2.11/isolinux.bin $LIVECD/isolinux

    mv $LIVECD/boot/* $LIVECD/isolinux
    cd $LIVECD/
    rmdir boot
    ln -s isolinux boot

  The bootloader needs a configuration file. The cat command below creates
  this file.

  * Note: Be careful with your kernel name!
  * See http://syslinux.zytor.com/errors.php
  * Keep the name short and sweet.

cat > $LIVECD/isolinux/isolinux.cfg << "EOF"
default livecd

label livecd
  kernel lfskernel
  append initrd=initrd.gz root=/dev/ram0 init=/linuxrc ramdisk_size=16384
EOF


  At this point it's a good idea to change /etc/fstab of the live CD system.
  Delete all of the entries that you don't need. (e.g. all /dev/hd* entries)
  You only need proc and devpts.

  vi $LIVECD/etc/fstab

  Don't worry about mounting root filesystem "/".
  This will be mounted by the linuxrc script from the initial ram disk.

  You may find it helpful to remove the following links:

    rm $LIVECD/etc/rc.d/rc3.d/S20network
    rm $LIVECD/etc/rc.d/rc0.d/K80network
    rm $LIVECD/etc/rc.d/rc6.d/K80network
    rm $LIVECD/etc/rc.d/rcsysinit.d/S40mountfs
    rm $LIVECD/etc/rc.d/rcsysinit.d/S30checkfs

  Ok! So far so good right?! In order for the boot process to work the way
  we want, we create an initial ramdisk. More information about initial
  ram disks (initrd for short) can be found in your kernel documentation.
  
  The directions below create the initial ram disk (initrd):

    dd if=/dev/zero of=$LIVECD/boot/initrd bs=1024 count=6144
    mke2fs -m 0 -i 1024 -F $LIVECD/boot/initrd

    mount -o loop $LIVECD/boot/initrd $LIVECD/mnt
    cd $LIVECD/mnt
    mkdir bin sbin lib dev proc mnt sys etc
    
    cp -a $LIVECD/bin/{bash,mount,grep,umount,echo,ln,mkdir} bin/
    cp -a $LIVECD/sbin/udev* sbin/
    cp -a $(find $LIVECD -name "test" -type f) bin/
    cp -a $(find $LIVECD -name "chroot" -type f) bin/
    cp -a $(find $LIVECD -name "pivot_root" -type f) bin/
    cp -H $LIVECD/lib/{libncurses.so.5,libdl.so.2,libc.so.6,ld-linux.so.2} lib/
    cp -H $LIVECD/lib/{libreadline.so.5.0,libhistory.so.5.0} lib/
    cp -a $LIVECD/dev/{console,null,ram{0,1,2}} dev/
    cp -a $LIVECD/etc/{udev,dev.d,hotplug.d} etc/

    ln -s bash bin/sh
    ln -s test bin/[


  The first program executed by the kernel is /linuxrc. As it does not
  exist we create it. Our script will find the CD in the correct CD-ROM drive 
  and then mount it as the root file system / and run /sbin/init 3.

  Copy and paste the script and tailor it to your needs if necessary:

cat > $LIVECD/mnt/linuxrc << "EOF"
#!/bin/sh
                                                                                
# ID is a file in root of the LFS boot CD, used to identify the CD.

ID="livecd"

TMP_MOUNT="/mnt"
                                                                                
PATH="/bin:/sbin:/usr/bin:/usr/sbin"
                                                                                
CHECK_TYPE="try_mount"
                                                                                
# MOUNT KERNEL FILESYSTEMS

# Create the proc directory if it does not exist

if [ ! -d "/proc/" ]; then
  mkdir /proc
fi

# Mount the proc filesystem

mount -n proc /proc -t proc

# If sysfs is listed as a valid filesystem type in /proc
# then mount it (if it doesnt then udev wont work
# and you wont have the devices you need)
                                                                              
if grep -q '[[:space:]]sysfs' /proc/filesystems; then
    if [ ! -d /sys/block ]; then
    mount -n sysfs /sys -t sysfs
    fi
fi
                                                                                
# Create some things that sysfs does not, and should not export for us.  Feel
# free to add devices to this list.

make_extra_nodes() {
        ln -s /proc/self/fd /dev/fd
        ln -s /proc/self/fd/0 /dev/stdin
        ln -s /proc/self/fd/1 /dev/stdout
        ln -s /proc/self/fd/2 /dev/stderr
        ln -s /proc/kcore /dev/core
        mkdir /dev/pts
        mkdir /dev/shm
}
                                                                                
                                                                                
if [ ! -x /sbin/hotplug ]; then
    echo /sbin/udev > /proc/sys/kernel/hotplug
fi
                                                                                
# Mount a temporary file system over /dev, so that any devices
# made or removed during this boot don't affect the next one.
# The reason we don't write to mtab is because we don't ever
# want /dev to be unavailable (such as by `umount -a').
                                                                                
mount -n ramfs /dev -t ramfs
                                                                                
/sbin/udevstart
                                                                                
make_extra_nodes                                                                

# Detecting the live CD is pretty complicated, 
# but is a very logical process
                                                                                
# Search for cdrom devices and add them to CDROM_LIST
                                                                                
CDROM_LIST=""
                                                                                
# Search in proc tree for ide cdrom devices
# There used to be a section for devfs, but this was
# edited for udev. Actually we should probably not
# use /proc anymore, but use sysfs instead...
# Perhaps in the future;)
    
# Check for ide channels.
 
for ide_channel in /proc/ide/ide[0-9]
do

# If there are no ide channels found, then skip this

  if [ ! -d "$ide_channel" ]; then
    break
  fi

# Try each ide device to see if we can find the cd-rom drive

  for ide_device in hda hdb hdc hdd hde hdf hdg hdh hdi hdj hdk hdl hdm hdn
  do
    device_media_file="$ide_channel/$ide_device/media"
    if [ -e "$device_media_file" ]; then
      grep -i "cdrom" $device_media_file > /dev/null 2>&1
      if [ $? -eq 0 ]; then
        CDROM_LIST="$CDROM_LIST /dev/$ide_device"
      fi
    fi
  done
done

# Check for scsi cds

for scsi_cdrom in /dev/scd[0-99]
do
  if [ -e "$scsi_cdrom" ]; then
    CDROM_LIST="$CDROM_LIST $scsi_cdrom"
  fi
done
                                                                                
# Now we try to find the LFS boot CD (we use ID as identification)
                                                                                
LFS_CDROM_DEVICE=""

for cdrom_device in $CDROM_LIST
do
  if [ "$CHECK_TYPE" = "try_mount" ]; then
    mount -n -t iso9660 ${cdrom_device} $TMP_MOUNT
    # > /dev/null 2>&1
    media_found=$?
  fi

  if [ $media_found -eq 0 ]; then
    echo -n "media found"
    if [ "$CHECK_TYPE" = "try_mount" ]; then
      [ -e "$TMP_MOUNT/$ID" ]
      media_lfs=$?
    fi
    
    if [ "$CHECK_TYPE" = "try_mount" ]; then
      umount -n $cdrom_device > /dev/null 2>&1
    fi
                                                                                
    if [ $media_lfs -eq 0 ]; then
      echo ", LFS boot CD found. Ready!"
      LFS_CDROM_DEVICE="$cdrom_device"
      break;
    else
      echo ", not LFS boot CD."
    fi
                                                                                
  else
    echo "no media "
  fi
done
                                                                                
# Mount LFS CD as / (root fs)
if [ "$LFS_CDROM_DEVICE" = "" ]; then
  echo "No LFS boot CD found!!!"
  exit 1
else
  echo "Booting from $LFS_CDROM_DEVICE..."
                                                    
# This is the magical part that makes a live CD live!
# The cd is mounted and pivot_root+chroot commands
# are used to start the system.
# If you really want to know what is going on here,
# You should read the chroot and pivot_root man pages

  mount -n -o ro -t iso9660 $LFS_CDROM_DEVICE $TMP_MOUNT
  cd $TMP_MOUNT
  pivot_root . mnt
  umount -n /mnt/proc >/dev/null 2>&1
  exec chroot . sh -c 'umount -n /mnt >/dev/null 2>&1; 
                exec -a init.new /sbin/init 3' <dev/console >dev/console 2>&1

fi                            
 
EOF


  To make this script executable run:

    chmod 0755 $LIVECD/mnt/linuxrc

  Ok, that's it. Unmount the image and compress it.

    cd $LIVECD/
    umount $LIVECD/mnt
    gzip $LIVECD/boot/initrd

                          ---------------------
                         | Burning the live CD |
                          ---------------------

  If you have a CD-RW you should use it for testing. When you know for sure
  that it works well from the CD-RW you can burn it on a CD-R.(I give you 
  this advice, because I got the clue after burning about 10 CD-Rs that 
  didn't work ;-) [that goes for myself, and the author of the "Easy" hint ;)]

  Before you start burning, check the size of your LFS tree:

    du -sh $LIVECD/ 

  Delete all the stuff you don't need on a live CD. (e.g. /usr/src/*)

  Because linuxrc must be able to identify the CD you have to create a file 
  called "livecd".

    touch $LIVECD/livecd

  Now burn the LFS system on CD

  Note!!
  
  * dev=/dev/hdc is the device number of your CD-Writer
  * Check your devices with "cdrecord -scanbus"
  * (as of this writing scsi-emulation is no longer required)
  * speed=4 should be changed to (max) speed of your CD-Writer.
  * If you are not using a CD-RW remove blank=fast from the cdrecord-command!

    cd $LIVECD/
    mkisofs -R -l -L -D -b isolinux/isolinux.bin -o $ISODIR/livecd_image.iso \ 
    -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -V \
    "livecd" $LIVECD && cdrecord -v -eject dev=/dev/hdc blank=fast \
    $ISODIR/livecd_image.iso



ACKNOWLEDGEMENTS:

Thanks to:

 * Thomas Foecking <thomas@foecking.de> and Christian Hesse <mail@earthworm.de>
   For writing the "Easy Boot CD of your LFS" hint which confused me so much
   when I tried it with udev that I decided to write this one =o)

 * Gabriel Aneceto Munoz
   For bring syntax errors and other updates to my attention

 * Bernard Robbins
   For giving some good suggestions

  Feel free to email me comments, point out typos, etc. 

CHANGELOG:
[2004-05-17]
  * Initial hint completed.
[2004-05-19]
  * Added environment variables for more flexibility
[2004-07-24]
  * Finally found the time to proofread and submit the hint;)
[2004-08-04]
  * Fixed syntax errors and made other updates
[2004-09-30]
  * Tons of text changes and typo fixes, and various other stuff
[2005-09-03]
  * Edited to include Bernard's suggestions
