| #!/bin/bash |
| # |
| # lvm2create_initrd |
| # |
| # Miguel Cabeca |
| # cabeca (at) ist (dot) utl (dot) pt |
| # |
| # Inspiration to write this script came from various sources |
| # |
| # Original LVM lvmcreate_initrd: ftp://ftp.sistina.com/pub/LVM/1.0/ |
| # Kernel initrd.txt: http://www.kernel.org/ |
| # EVMS INSTALL.initrd & linuxrc: http://evms.sourceforge.net/ |
| # Jeffrey Layton's lvm2create_initrd: http://poochiereds.net/svn/lvm2create_initrd/ |
| # Christophe Saout's initrd & linuxrc: http://www.saout.de/misc/ |
| # |
| # This script was only tested with kernel 2.6 with everything required to boot |
| # the root filesystem built-in (not as modules). Ex: SCSI or IDE, RAID, device mapper |
| # It does not support devfs as it is deprecated in the 2.6 kernel series |
| # |
| # It needs lvm2 tools, busybox, pivot_root, MAKEDEV |
| # |
| # It has been tested on Debian sid (unstable) only |
| # |
| # Changelog |
| # 26/02/2004 Initial release -- Miguel Cabeca |
| # 27/02/2004 Removed the BUSYBOXSYMLINKS var. The links are now determined at runtime. |
| # some changes in init script to call a shell if something goes wrong. -- Miguel Cabeca |
| # 19/04/2004 Several small changes. Pass args to init so single user mode works. Add some |
| # PATH entries to /sbin/init shell script so chroot works without /usr mounted. Remove |
| # mkdir /initrd so we don't cause problems if root filesystem is corrupted. -- Jeff Layton |
| # 15/05/2004 initial support for modules, create lvm.conf from lvm dumpconfig, other cleanups -- Jeff Layton |
| # 14/11/2006 Update handling of ldd output to handle hardcoded library links and virtual dll linux-gate. |
| # Add support for Gentoo-style MAKEDEV. Remove hardcoded BINUTILS paths -- Douglas Mayle |
| # |
| # Copyright Miguel Cabeca, Jeffrey Layton, 2004 |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write to the Free Software |
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| # |
| # $Id$ |
| |
| TMPMNT=/tmp/mnt.$$ |
| DEVRAM=/tmp/initrd.$$ |
| |
| # set defaults |
| BINFILES=${BINFILES:-"`which lvm` `which bash` `which busybox` `which pivot_root`"} |
| BASICDEVICES=${BASICDEVICES:-"std consoleonly fd"} |
| BLOCKDEVICES=${BLOCKDEVICES:-"md hda hdb hdc hdd sda sdb sdc sdd"} |
| MAKEDEV=${MAKEDEV:-"debian"} |
| |
| # Uncomment this if you want to disable automatic size detection |
| #INITRDSIZE=4096 |
| |
| PATH=/bin:/sbin:/usr/bin:/usr/sbin:$PATH |
| |
| usage () { |
| echo "Create an initial ramdisk image for LVM2 root filesystem" |
| echo "$cmd: [-h] [-v] [-c lvm.conf] [-m modulelist] [-e extrafiles] -r [raiddevs] [-R mdadm.conf] [-M style] [kernel version]" |
| echo " -h|--help print this usage message" |
| echo " -v|--verbose verbose progress messages" |
| echo " -c|--lvmconf path to lvm.conf (/etc/lvm/lvm.conf)" |
| echo " -m|--modules modules to copy to initrd image" |
| echo " -e|--extra extra files to add to initrd" |
| echo " -r|--raid raid devices to start in initrd" |
| echo " -R|--raidconf location of mdadm.conf file to include" |
| echo " -M|--makedev set MAKEDEV type (debian, redhat, gentoo)" |
| } |
| |
| verbose () { |
| [ "$VERBOSE" ] && echo "`echo $cmd | tr '[a-z0-9/_]' ' '` -- $1" || true |
| } |
| |
| cleanup () { |
| [ "`mount | grep $DEVRAM`" ] && verbose "unmounting $DEVRAM" && umount $DEVRAM |
| [ -f $DEVRAM ] && verbose "removing $DEVRAM" && rm $DEVRAM |
| [ -d $TMPMNT ] && verbose "removing $TMPMNT" && rmdir $TMPMNT |
| verbose "exit with code $1" |
| exit $1 |
| } |
| |
| trap " |
| verbose 'Caught interrupt' |
| echo 'Bye bye...' |
| cleanup 1 |
| " 1 2 3 15 |
| |
| create_init () { |
| cat << 'INIT' > $TMPMNT/sbin/init |
| #!/bin/bash |
| |
| # include in the path some dirs from the real root filesystem |
| # for chroot, blockdev |
| PATH="/sbin:/bin:/usr/sbin:/usr/bin:/lib/lvm-200:/initrd/bin:/initrd/sbin" |
| PRE="initrd:" |
| |
| do_shell(){ |
| /bin/echo |
| /bin/echo "*** Entering LVM2 rescue shell. Exit shell to continue booting. ***" |
| /bin/echo |
| /bin/bash |
| } |
| |
| echo "$PRE Remounting / read/write" |
| mount -t ext2 -o remount,rw /dev/ram0 / |
| |
| |
| # We need /proc for device mapper |
| echo "$PRE Mounting /proc" |
| mount -t proc none /proc |
| |
| # plug in modules listed in /etc/modules |
| if [ -f /etc/modules ]; then |
| echo -n "$PRE plugging in kernel modules:" |
| cat /etc/modules | |
| while read module; do |
| echo -n " $module" |
| modprobe $module |
| done |
| echo '.' |
| fi |
| |
| # start raid devices if raid_autostart file exists |
| if [ -f /etc/raid_autostart ]; then |
| if [ ! -f /etc/mdadm/mdadm.conf ]; then |
| mdoptions='--super-minor=dev' |
| fi |
| cat /etc/raid_autostart| |
| while read dev; do |
| echo "Starting RAID device $dev" |
| /sbin/mdadm --assemble $dev $mdoptions |
| done |
| fi |
| |
| # Create the /dev/mapper/control device for the ioctl |
| # interface using the major and minor numbers that have been allocated |
| # dynamically. |
| |
| echo -n "$PRE Finding device mapper major and minor numbers " |
| |
| MAJOR=$(sed -n 's/^ *\([0-9]\+\) \+misc$/\1/p' /proc/devices) |
| MINOR=$(sed -n 's/^ *\([0-9]\+\) \+device-mapper$/\1/p' /proc/misc) |
| if test -n "$MAJOR" -a -n "$MINOR" ; then |
| mkdir -p -m 755 /dev/mapper |
| mknod -m 600 /dev/mapper/control c $MAJOR $MINOR |
| fi |
| |
| echo "($MAJOR,$MINOR)" |
| |
| # Device-Mapper dynamically allocates all device numbers. This means it is possible |
| # that the root volume specified to LILO or Grub may have a different number when the |
| # initrd runs than when the system was last running. In order to make sure the |
| # correct volume is mounted as root, the init script must determine what the |
| # desired root volume name is by getting the LVM2 root volume name from the kernel command line. In order for |
| # this to work correctly, "lvm2root=/dev/Volume_Group_Name/Root_Volume_Name" needs to be passed |
| # to the kernel command line (where Root_Volume_Name is replaced by your actual |
| # root volume's name. |
| for arg in `cat /proc/cmdline`; do |
| echo $arg | grep '^lvm2root=' > /dev/null |
| if [ $? -eq 0 ]; then |
| rootvol=${arg#lvm2root=} |
| break |
| fi |
| done |
| |
| echo "$PRE Activating LVM2 volumes" |
| |
| |
| # run a shell if we're passed lvm2rescue on commandline |
| grep lvm2rescue /proc/cmdline 1>/dev/null 2>&1 |
| if [ $? -eq 0 ]; then |
| lvm vgchange --ignorelockingfailure -P -a y |
| do_shell |
| else |
| lvm vgchange --ignorelockingfailure -a y |
| fi |
| |
| echo "$PRE Mounting root filesystem $rootvol ro" |
| mkdir /rootvol |
| if ! mount -t auto -o ro $rootvol /rootvol; then |
| echo "\t*FAILED*"; |
| do_shell |
| fi |
| |
| echo "$PRE Umounting /proc" |
| umount /proc |
| |
| echo "$PRE Changing roots" |
| cd /rootvol |
| if ! pivot_root . initrd ; then |
| echo "\t*FAILED*" |
| do_shell |
| fi |
| |
| echo "$PRE Proceeding with boot..." |
| |
| exec chroot . /bin/sh -c "umount /initrd; blockdev --flushbufs /dev/ram0 ; exec /sbin/init $*" < dev/console > dev/console 2>&1 |
| |
| INIT |
| chmod 555 $TMPMNT/sbin/init |
| } |
| |
| # create lvm.conf file from dumpconfig. Just use filter options |
| create_lvmconf () { |
| echo 'devices {' > $TMPMNT/etc/lvm/lvm.conf |
| lvm dumpconfig | grep 'filter=' >> $TMPMNT/etc/lvm/lvm.conf |
| echo '}' >> $TMPMNT/etc/lvm/lvm.conf |
| } |
| |
| # |
| # Main |
| # |
| |
| cmd=`basename $0` |
| |
| VERSION=`uname -r` |
| |
| while [ $# -gt 0 ]; do |
| case $1 in |
| -h|--help) usage; exit 0;; |
| -v|--verbose) VERBOSE="y";; |
| -c|--lvmconf) LVMCONF=$2; shift;; |
| -m|--modules) MODULES=$2; shift;; |
| -e|--extra) EXTRAFILES=$2; shift;; |
| -r|--raid) RAID=$2; shift;; |
| -R|--raidconf) RAIDCONF=$2; shift;; |
| -M|--makedev) MAKEDEV=$2; shift;; |
| [2-9].[0-9]*.[0-9]*) VERSION=$1;; |
| *) echo "$cmd -- invalid option '$1'"; usage; exit 0;; |
| esac |
| shift |
| done |
| |
| INITRD=${INITRD:-"/boot/initrd-lvm2-$VERSION.gz"} |
| |
| echo "$cmd -- make LVM initial ram disk $INITRD" |
| echo "" |
| |
| if [ -n "$RAID" ]; then |
| BINFILES="$BINFILES /sbin/mdadm" |
| RAIDCONF=${RAIDCONF:-"/etc/mdadm/mdadm.conf"} |
| if [ -r $RAIDCONF ]; then |
| EXTRAFILES="$EXTRAFILES $RAIDCONF" |
| else |
| echo "$cmd -- WARNING: No $RAIDCONF! Your RAID device minor numbers must match their superblock values!" |
| fi |
| fi |
| |
| # add modprobe if we declared any modules |
| if [ -n "$MODULES" ]; then |
| BINFILES="$BINFILES /sbin/modprobe /sbin/insmod /sbin/rmmod" |
| fi |
| |
| for a in $BINFILES $EXTRAFILES; do |
| if [ ! -r "$a" ] ; then |
| echo "$cmd -- ERROR: you need $a" |
| exit 1; |
| fi; |
| done |
| |
| # Figure out which shared libraries we actually need in our initrd |
| echo "$cmd -- finding required shared libraries" |
| verbose "BINFILES: `echo $BINFILES`" |
| |
| # We need to strip certain lines from ldd output. This is the full output of an example ldd: |
| #lvmhost~ # ldd /sbin/lvm /bin/bash |
| #/sbin/lvm: |
| # not a dynamic executable |
| #/bin/bash: |
| # linux-gate.so.1 => (0xbfffe000) |
| # libncurses.so.5 => /lib/libncurses.so.5 (0xb7ee3000) |
| # libdl.so.2 => /lib/libdl.so.2 (0xb7edf000) |
| # libc.so.6 => /lib/libc.so.6 (0xb7dc1000) |
| # /lib/ld-linux.so.2 (0xb7f28000) |
| # |
| # 1) Lines with a ":" contain the name of the original binary we're examining, and so are unnecessary. |
| # We need to strip them because they contain "/", and can be confused with links with a hardcoded path. |
| # 2) The linux-gate library is a virtual dll that does not exist on disk, but is instead loaded automatically |
| # into the process space, and can't be copied to the ramdisk |
| # |
| # After these lines have been stripped, we're interested in the lines remaining if they |
| # 1) Contain "=>" because they are pathless links, and the value following the token is the path on the disk |
| # 2) Contain "/" because it's a link with a hardcoded path, and so we're interested in the link itself. |
| LIBFILES=`ldd $BINFILES 2>/dev/null |grep -v -E \(linux-gate\|:\) | awk '{if (/=>/) { print $3 } else if (/\//) { print $1 }}' | sort -u` |
| if [ $? -ne 0 ]; then |
| echo "$cmd -- ERROR figuring out needed shared libraries" |
| exit 1 |
| fi |
| |
| verbose "Shared libraries needed: `echo $LIBFILES`" |
| |
| INITRDFILES="$BINFILES $LIBFILES $MODULES $EXTRAFILES" |
| |
| # tack on stuff for modules if we declared any and the files exist |
| if [ -n "$MODULES" ]; then |
| if [ -f "/etc/modprobe.conf" ]; then |
| INITRDFILES="$INITRDFILES /etc/modprobe.conf" |
| fi |
| if [ -f "/lib/modules/modprobe.conf" ]; then |
| INITRDFILES="$INITRDFILES /lib/modules/modprobe.conf" |
| fi |
| fi |
| |
| # Calculate the the size of the ramdisk image. |
| # Don't forget that inodes take up space too, as does the filesystem metadata. |
| echo "$cmd -- calculating initrd filesystem parameters" |
| if [ -z "$INITRDSIZE" ]; then |
| echo "$cmd -- calculating loopback file size" |
| verbose "finding size" |
| INITRDSIZE="`du -Lck $INITRDFILES | tail -1 | cut -f 1`" |
| verbose "minimum: $INITRDSIZE kB for files + inodes + filesystem metadata" |
| INITRDSIZE=`expr $INITRDSIZE + 512` # enough for ext2 fs + a bit |
| fi |
| |
| echo "$cmd -- making loopback file ($INITRDSIZE kB)" |
| verbose "using $DEVRAM as a temporary loopback file" |
| dd if=/dev/zero of=$DEVRAM count=$INITRDSIZE bs=1024 > /dev/null 2>&1 |
| if [ $? -ne 0 ]; then |
| echo "$cmd -- ERROR creating loopback file" |
| cleanup 1 |
| fi |
| |
| echo "$cmd -- making ram disk filesystem" |
| verbose "mke2fs -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE" |
| [ "$VERBOSE" ] && OPT_Q="" || OPT_Q="-q" |
| mke2fs $OPT_Q -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE |
| if [ $? -ne 0 ]; then |
| echo "$cmd -- ERROR making ram disk filesystem" |
| echo "$cmd -- ERROR you need to use mke2fs >= 1.14 or increase INITRDSIZE" |
| cleanup 1 |
| fi |
| |
| verbose "creating mountpoint $TMPMNT" |
| mkdir $TMPMNT |
| if [ $? -ne 0 ]; then |
| echo "$cmd -- ERROR making $TMPMNT" |
| cleanup 1 |
| fi |
| |
| echo "$cmd -- mounting ram disk filesystem" |
| verbose "mount -o loop $DEVRAM $TMPMNT" |
| mount -oloop $DEVRAM $TMPMNT |
| if [ $? -ne 0 ]; then |
| echo "$cmd -- ERROR mounting $DEVRAM on $TMPMNT" |
| cleanup 1 |
| fi |
| |
| verbose "creating basic set of directories in $TMPMNT" |
| (cd $TMPMNT; mkdir bin dev etc lib proc sbin var) |
| if [ $? -ne 0 ]; then |
| echo "$cmd -- ERROR creating directories in $TMPMNT" |
| cleanup 1 |
| fi |
| |
| # Add some /dev files. We have to handle different types of MAKEDEV invocations |
| # here, so this is rather messy. |
| RETCODE=0 |
| echo "$cmd -- adding required /dev files" |
| verbose "BASICDEVICES: `echo $BASICDEVICES`" |
| verbose "BLOCKDEVICES: `echo $BLOCKDEVICES`" |
| [ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="" |
| case "$MAKEDEV" in |
| debian) |
| (cd $TMPMNT/dev; /dev/MAKEDEV $OPT_Q $BASICDEVICES $BLOCKDEVICES) |
| RETCODE=$? |
| ;; |
| redhat) |
| (cd $TMPMNT/dev; /dev/MAKEDEV $OPT_Q -d $TMPMNT/dev -m 2) |
| RETCODE=$? |
| ;; |
| gentoo) |
| (cd $TMPMNT/dev; /sbin/MAKEDEV $OPT_Q $BASICDEVICES $BLOCKDEVICES) |
| RETCODE=$? |
| ;; |
| *) |
| echo "$cmd -- ERROR: $MAKEDEV is not a known MAKEDEV style." |
| RETCODE=1 |
| ;; |
| esac |
| |
| |
| if [ $RETCODE -ne 0 ]; then |
| echo "$cmd -- ERROR adding /dev files" |
| cleanup 1 |
| fi |
| |
| |
| # copy necessary files to ram disk |
| echo "$cmd -- copying initrd files to ram disk" |
| [ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="--quiet" |
| verbose "find \$INITRDFILES | cpio -pdmL $OPT_Q $TMPMNT" |
| find $INITRDFILES | cpio -pdmL $OPT_Q $TMPMNT |
| if [ $? -ne 0 ]; then |
| echo "$cmd -- ERROR cpio to ram disk" |
| cleanup 1 |
| fi |
| |
| |
| echo "$cmd -- creating symlinks to busybox" |
| shopt -s extglob |
| [ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="" |
| BUSYBOXSYMLINKS=`busybox 2>&1| awk '/^Currently defined functions:$/ {i++;next} i'|tr ',\t\n' ' '` |
| for link in ${BUSYBOXSYMLINKS//@(linuxrc|init|busybox)}; do |
| ln -s $OPT_Q busybox $TMPMNT/bin/$link; |
| done |
| shopt -u extglob |
| |
| echo "$cmd -- creating new $TMPMNT/sbin/init" |
| create_init |
| if [ $? -ne 0 ]; then |
| echo "$cmd -- ERROR creating init" |
| cleanup |
| exit 1 |
| fi |
| |
| # copy LVMCONF into place or create a stripped down one from lvm dumpconfig |
| mkdir -p $TMPMNT/etc/lvm |
| if [ -n "$LVMCONF" ]; then |
| echo "$cmd -- copying $LVMCONF to $TMPMNT/etc/lvm/lvm.conf" |
| if [ -f "$LVMCONF" ]; then |
| cp $LVMCONF $TMPMNT/etc/lvm/lvm.conf |
| else |
| echo "$cmd -- ERROR: $LVMCONF does not exist!" |
| cleanup |
| exit 1 |
| fi |
| else |
| echo "$cmd -- creating new $TMPMNT/etc/lvm/lvm.conf" |
| create_lvmconf |
| fi |
| |
| if [ -n "$RAID" ]; then |
| RAIDLIST="$TMPMNT/etc/raid_autostart" |
| echo "$cmd -- creating $RAIDLIST file." |
| for device in $RAID; do |
| echo $device >> $RAIDLIST |
| done |
| fi |
| |
| # create modules.dep and /etc/modules files if needed |
| if [ -n "$MODULES" ]; then |
| echo "$cmd -- creating $MODDIR/modules.dep file and $TMPMNT/etc/modules" |
| depmod -b $TMPMNT $VERSION |
| for module in $MODULES; do |
| basename $module | sed 's/\.k\{0,1\}o$//' >> $TMPMNT/etc/modules |
| done |
| fi |
| |
| verbose "removing $TMPMNT/lost+found" |
| rmdir $TMPMNT/lost+found |
| |
| echo "$cmd -- ummounting ram disk" |
| umount $DEVRAM |
| if [ $? -ne 0 ]; then |
| echo "$cmd -- ERROR umounting $DEVRAM" |
| cleanup 1 |
| fi |
| |
| echo "$cmd -- creating compressed initrd $INITRD" |
| verbose "dd if=$DEVRAM bs=1k count=$INITRDSIZE | gzip -9" |
| dd if=$DEVRAM bs=1k count=$INITRDSIZE 2>/dev/null | gzip -9 > $INITRD |
| if [ $? -ne 0 ]; then |
| echo "$cmd -- ERROR creating $INITRD" |
| cleanup 1 |
| fi |
| |
| |
| cat << FINALTXT |
| -------------------------------------------------------- |
| Your initrd is ready in $INITRD |
| |
| Don't forget to set root=/dev/ram0 in kernel parameters |
| Don't forget to set lvm2root=/dev/VG/LV in kernel parameters, where LV is your root volume |
| If you use lilo try adding/modifying an entry similar to this one in lilo.conf: |
| |
| image=/boot/vmlinuz-lvm2-$VERSION |
| label="ramdisk_LVM" |
| initrd=/boot/initrd-lvm2-$VERSION.gz |
| append="root=/dev/ram0 lvm2root=/dev/system/root <other parameters>" |
| |
| If using grub try adding/modifying an entry similar to this one in menu.lst |
| |
| title ramdisk LVM |
| kernel /boot/vmlinuz-lvm2-$VERSION root=/dev/ram0 lvm2root=/dev/system/root <other parameters> |
| initrd /boot/initrd-lvm2-$VERSION.gz |
| |
| You can also pass lvm2rescue to the kernel to get a shell |
| -------------------------------------------------------- |
| FINALTXT |
| |
| cleanup 0 |
| |