Wednesday, December 12, 2012

My All-Purpose Init Script for initrd / initramfs (Busybox edition)

Here I post my init script. It is used in my initrd/initramfs boot images for the following purposes. The busybox package should be installed, and sh must be a symbolic link to busybox.

  • Boot from a live CD / DVD
  • Boot from a read-only filesystem image, compressed in SquashFS
  • Copy the filesystem image to a RAM disk and run Linux entirely on memory
  • Boot Linux from a USB flash drive
  • Boot Linux from a local disk partition
  • Run a rescue shell without booting Linux

I have yet to implement network booting. Right now it is good enough for my current needs. It takes the following boot parameters.

  • root=
    Specifies the root partition to boot Linux from. For example, /dev/sda2.
  • label=
    Specifies the label of the device to boot from. For example, label=DEBIAN.
  • uuid=
    Specifies the UUID of the device to boot from. For example, uuid=dcfd6a0a-2a0f-4b3d-8a1a-5e7d642ebfbd
  • boot=
    Can be cdrom, loop, ram, usb or ata.
  • vmode=
    Specifies the screen resolution of the framebuffer video. For example, vmode=640x480
  • single
    Boot into the single-user mode.
  • nox
    Boot into the console mode in runlevel 2
#!/bin/sh

# Define a function to parse kernel command line options.
get_opt() {
  echo $@ | cut -d "=" -f 2-
}

# Define a function to load drivers for ATA and USB controllers.
loadmod() {
  for i; do
    case $i in
      ata)
        for j in ata_ ahci pdc_adma; do
          for k in $(grep $j /tmp/pcimodules.txt); do modprobe $k; done
        done
        ;;
      usb)
        lspci -n | grep -q "0c03: " &&
        modprobe -a xhci-hcd ehci-hcd uhci-hcd ohci-hcd usb-storage
        ;;
    esac
  done
}

# Define a function to guess the partition type.
gpart() {
  for i in $(blkid | grep -i $1 | head -n 1); do
    case $i in
      *\=*)
        eval $i
        ;;
      /dev/*:)
        [ "$root" ] || root=$(echo $i | tr -d ':')
        ;;
    esac
  done
}

# Define a function for mounting the root partition.
mountr() {
  modprobe $TYPE
  case $TYPE in
    ext*)
      e2fsck -p $root
      [ $# = 1 ] && mount $root $1 || mount $root /mnt
      ;;
    jfs)
      jfs_fsck $root
      modprobe nls_utf8
      if [ $# = 1 ]; then
        mount -t jfs -o ro,iocharset=utf8 $root $1
      else mount -t jfs -o ro,iocharset=utf8 $root /mnt
      fi
      ;;
    vfat)
      modprobe -a nls_cp437 nls_iso8859-1
      if [ $# = 1 ]; then
        mount -t vfat -o ro,gid=100,dmask=2,fmask=113 $root $1
      else mount -t vfat -o ro,gid=100,dmask=2,fmask=113 $root /mnt
      fi
      ;;
    *)
      [ $# = 1 ] && mount -r $root $1 || mount -r $root /mnt
      ;;
  esac
}

# Define a function for creating a union filesystem.
# A read-only filesystem is assumed to be already mounted on /opt.
union() {
  mount -t tmpfs none /opt/tmp
# Check for unionfs module
  [ "$(modprobe -l unionfs)" ] &&
  mount -t unionfs -o dirs=/opt/tmp=rw:/opt=ro none /mnt ||
# Otherwise, use unionfs-fuse
  ( 
    mkdir /opt/tmp/.change
    modprobe fuse
    unionfs-fuse -o allow_other,use_ino,suid,dev,nonempty,kernel_cache \
     -o cow,chroot=/opt,max_files=32768 /tmp/.change=RW:/=RO /mnt
  )
  mount -o rbind /opt /mnt/opt
}

# Mount proc and sysfs.
mount -t proc none /proc
mount -t sysfs none /sys

# Populate /dev (Needs kernel >= 2.6.32)
mount -t devtmpfs none /dev
mkdir -m 755 /dev/pts
mount -t devpts -o gid=5,mode=620 none /dev/pts

# Find modules required to use available PCI hardware
mount -t tmpfs none /tmp
for f in $(lspci -n | awk '{print toupper($3)}' | sed -e 's/:/d0000/g')
  do grep $f /lib/modules/$(uname -r)/modules.alias \
    | awk '{print $3}' >> /tmp/pcimodules.txt
done
lspci -n | awk '$2 == "0c00:" {print "firewire-ohci"}' >> /tmp/modules.txt

# Find the root=, label=, uuid= and boot= values on kernel command line.
for i in $(cat /proc/cmdline); do
  case $i in
    root\=*)
        root=$(get_opt $i)
        case $root in
          /dev/cdr* | /dev/dvd* | /dev/sr* | /dev/scd*)
            boot=cdrom
            ;;
          0x200)
            root=/dev/fd0
            ;;
          label\=* | LABEL\=* | uuid\=* | UUID\=* )
            eval $(echo $root | tr 'A-Z' 'a-z')
            unset root
            ;;
        esac
        ;;
    label\=* | uuid\=* | boot\=* | vmode\=* )
        eval $i
        ;;
    single)
        RUNLEVEL=single
        ;;
    nox)
        RUNLEVEL=2
        ;;
  esac
done

# Activate framebuffer display devices.
grep -q i915 /tmp/pcimodules.txt ||
if [ "$vmode" -a "$boot" = cdrom ]; then
    modprobe uvesafb scroll=ywrap mode_option=$vmode-16
elif [ $vmode ]; then
  for i in $(grep fb /tmp/pcimodules.txt); do
    case $i in
      atyfb)
        modprobe $i mode=$vmode-16
        ;;
      nvidiafb | rivafb)
        modprobe nvidiafb mode_option=$vmode bpp=16 hwcur=1
        ;;
      radeonfb | savagefb)
        modprobe $i mode_option=$vmode-16
        ;;
      sisfb)
        modprobe $i mode=$vmodex16 mem=12288 font=SUN12x22
        ;;
      viafb | vt8623fb)
        modprobe viafb viafb_mode=$vmode viafb_bpp=16
        ;;
      *)
        modprobe $i
        ;;
    esac
  done
  [ -c /dev/fb0 ] || modprobe uvesafb scroll=ywrap mode_option=$vmode-16
fi

case $boot in
    cdrom)
        # Boot Linux from a live CD.
        loadmod ata usb &&
        modprobe sr_mod &&
        sleep 7
        modprobe isofs
        mount -t iso9660 /dev/sr0 /media || mount -t iso9660 /dev/sr1 /media
        if [ -f /media/*.[Ss][Qq]* ]; then
          SQF=$(ls -t /media/*.[Ss][Qq]* | head -n 1)
          modprobe squashfs
          if [ $root = /dev/ram ]; then 
               echo "Please wait until the RAM disk is ready."
               dd if=$SQF of=/dev/ram1 bs=2048 &&
               mount -t squashfs /dev/ram1 /opt
          else modprobe loop
               mount -t squashfs $SQF /opt
          fi
        else
          mount -o move /media /opt
        fi
        union
        ;;
    loop)
        # Boot Linux from an image file.
        loadmod ata usb &&
        modprobe sd_mod &&
        sleep 7
        gpart "$uuid$label$root"
        mountr /media
        modprobe loop
        if [ -f /media/*.[Ss][Qq]* ]; then
          SQF=$(ls -t /media/*.[Ss][Qq]* | head -n 1)
          modprobe squashfs && mount -t squashfs $SQF /opt
        elif [ -f /media/*.[Ii][Ss][Oo] ]; then
          ISO=$(ls -t /media/*.[Ii][Ss][Oo] | head -n 1)
          modprobe isofs
          mount -t iso9660 $ISO /opt
        fi
        union
        ;;
    ram)
        # Boot Linux from ramdisk.
        loadmod ata usb &&
        modprobe sd_mod &&
        sleep 7
        gpart "$uuid$label$root"
        mountr /media
        echo "Please wait until the RAM disk is ready."
        if [ -f /media/*.[Ss][Qq]* ]; then
          SQF=$(ls -t /media/*.[Ss][Qq]* | head -n 1)
          dd if=$SQF of=/dev/ram1 &&
          modprobe squashfs && mount -t squashfs /dev/ram1 /opt
        elif [ -f /media/*.[Ii][Ss][Oo] ]; then
          ISO=$(ls -t /media/*.[Ii][Ss][Oo] | head -n 1)
          dd if=$ISO of=/dev/ram1 bs=2048 &&
          modprobe isofs
          mount -t iso9660 /dev/ram1 /opt
        fi
        union
        ;;
    usb*)
        # Boot Linux from a USB drive.
        loadmod usb &&
        modprobe sd_mod &&
        sleep 7
        gpart "$uuid$label$root"
        mountr
        ;;
    *)
        loadmod ata &&
        modprobe sd_mod &&
        gpart "$uuid$label$root"
        mountr
        ;;
esac

# Make sure that init exists and is executable.
if [ -x /mnt/sbin/init ]; then
  mount -o move /dev /mnt/dev
  mount -o move /proc /mnt/proc
  mount -o move /sys /mnt/sys
  umount /tmp

# Start init from the root filesystem.
  cd /mnt
  [ -f /media/updates.zip ] && /usr/bin/unzip -oq /media/updates.zip
  case $boot in
    cdrom)
      [ $root = /dev/ram ] && umount /media
      [ $RUNLEVEL ] || RUNLEVEL=3
      ;;
    loop | ram)
      umount /media
      [ $RUNLEVEL ] || RUNLEVEL=3
      ;;
    *)
      [ $RUNLEVEL ] || RUNLEVEL=5
      ;;
  esac
  [ -d initrd ] && pivot_root . initrd
  exec switch_root -c /dev/console . /sbin/init $RUNLEVEL
fi

# Start a shell as a last resort.
echo "Error booting from the root filesystem. Starting a shell."
exec /bin/sh

The following are examples of boot parameters that can be used with my init script.

  • Boot Linux from the local hard drive partition /dev/sda8
    boot=ata root=/dev/sda8
  • Boot Linux from the latest squashfs file (*.sq*) on /dev/sda1
    boot=loop root=/dev/sda1
  • Boot Linux from the CD-ROM with 1024x768 video resolution
    boot=cdrom vmode=1024x768
  • Copy the squashfs image from CD-ROM into memory and run Linux on memory
    boot=cdrom root=/dev/ram ramdisk_size=573440 vmode=800x600
  • Boot Linux from the second partition of a USB drive
    boot=usb root=/dev/sda2

My All-Purpose Init Script for initrd / initramfs Boot Images (Dash Shell Edition)

If you want an init script that does not require busybox, here's the patch to make it a dash edition.

--- initrc-bb.txt       2013-01-31 22:50:09.418996459 -1000
+++ initrc-sh.txt       2013-01-31 22:52:25.048301150 -1000
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/dash

 # Define a function to parse kernel command line options.
 get_opt() {
@@ -70,7 +70,7 @@
 union() {
   mount -t tmpfs none /opt/tmp
 # Check for unionfs module
-  [ "$(modprobe -l unionfs)" ] &&
+  modinfo unionfs > /dev/null 2>&1 &&
   mount -t unionfs -o dirs=/opt/tmp=rw:/opt=ro none /mnt ||
 # Otherwise, use unionfs-fuse
   (
@@ -79,7 +79,7 @@
     unionfs-fuse -o allow_other,use_ino,suid,dev,nonempty,kernel_cache \
      -o cow,chroot=/opt,max_files=32768 /tmp/.change=RW:/=RO /mnt
   )
-  mount -o rbind /opt /mnt/opt
+  mount --rbind /opt /mnt/opt
 }

 # Mount proc and sysfs.
@@ -178,7 +178,7 @@
                mount -t squashfs $SQF /opt
           fi
         else
-          mount -o move /media /opt
+          mount --move /media /opt
         fi
         union
         ;;
@@ -238,9 +238,9 @@

 # Make sure that init exists and is executable.
 if [ -x /mnt/sbin/init ]; then
-  mount -o move /dev /mnt/dev
-  mount -o move /proc /mnt/proc
-  mount -o move /sys /mnt/sys
+  mount --move /dev /mnt/dev
+  mount --move /proc /mnt/proc
+  mount --move /sys /mnt/sys
   umount /tmp

 # Start init from the root filesystem.
@@ -265,4 +265,4 @@

 # Start a shell as a last resort.
 echo "Error booting from the root filesystem. Starting a shell."
-exec /bin/sh
+exec /bin/dash

Also read:

About This Blog

KBlog logo This blog seeks to provide useful information to people, based on the author's knowledge and experience. Thanks for visiting the blog and posting your comments.

© Contents by KBlog

© Blogger template by Emporium Digital 2008

Follow by Email

Total Pageviews