Skip to content

Instantly share code, notes, and snippets.

@danp
Created April 15, 2011 14:40
Show Gist options
  • Save danp/921804 to your computer and use it in GitHub Desktop.
Save danp/921804 to your computer and use it in GitHub Desktop.
#!/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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id: lvm2create_initrd,v 1.2 2006/11/21 22:41:56 agk Exp $
set -e
TMPMNT=/tmp/mnt.$$
DEVRAM=/tmp/initrd.$$
# set defaults
BINFILES=${BINFILES:-"`which bash` `which busybox` `which pivot_root`"}
BASICDEVICES=${BASICDEVICES:-"std consoleonly fd ptmx"}
BLOCKDEVICES=${BLOCKDEVICES:-"md hda hdb hdc hdd sda sdb sdc sdd"}
MAKEDEV=${MAKEDEV:-"ubuntu"}
# 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 or redhat)"
echo " -o|--moddir Add directory of modules -- last part goes under /lib/modules"
}
verbose () {
[ "$VERBOSE" ] && echo "`echo $cmd | tr '[a-z0-9/_]' ' '` -- $1" || true
}
cleanup () {
[ "`mount | grep $TMPMNT`" ] && verbose "unmounting $TMPMNT" && umount $TMPMNT
[ -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:/usr/local/bin"
PRE="initrd:"
get_kernel_param() {
key=$1
egrep -o "$key=([^[:space:]]+)" /proc/cmdline | cut -d'=' -f2
}
echo "$PRE Remounting / read/write"
mount -t ext3 -o remount,rw /dev/ram0 /
# We need /proc for device mapper
echo "$PRE Mounting /proc"
mount -t proc none /proc
# We need /sys for aoe tuning
echo "$PRE Mounting /sys"
mount -t sysfs none /sys
mkdir -p /dev/pts
mount -t devpts none /dev/pts
# refresh mount list
cat /proc/mounts > /etc/mtab
ip link set lo up
echo 127.0.0.1 localhost > /etc/hosts
modprobe e1000
modprobe mptspi
modprobe nfs
modprobe ext3
modprobe dm_mod
modprobe xfs
portmap
rpc.statd
ip link set eth0 up
set -e
ip link
udhcpc -i eth0 -s migration_udhcpd_script.sh
ip route
. dhcp_env
dropbear -E -p 22
exec >/config_stdout
exec 2>/config_stderr
curl -s http://$siaddr/ey/configuration/script > config.sh
source config.sh
INIT
chmod 555 $TMPMNT/sbin/init
(cd $TMPMNT && ln -s /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;;
-o|--moddir) MODDIR=$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 ""
BINFILES="$BINFILES `which wget` `which curl` `which sfdisk` `which blockdev` `which udhcpc` `which portmap` `which rpc.statd` `which sm-notify` `which strace` `which mount.nfs` `which reboot` `which mkswap` `which mkfs.ext3` `which tar` `which grub` `which lvm` `which dropbear` `which less` `which mkfs.xfs`"
# add modprobe if we declared any modules
if [ -n "$MODULES" ] || [ -n "$MODDIR" ]; then
BINFILES="$BINFILES /sbin/modprobe /sbin/insmod /sbin/rmmod /sbin/lsmod"
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\|linux-vdso\|:\) | 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
LIBFILES="$LIBFILES /lib/libnss_compat.so.2"
verbose "Shared libraries needed: `echo $LIBFILES`"
INITRDFILES="$BINFILES $LIBFILES $MODULES $EXTRAFILES migration_udhcpd_script.sh"
# 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 $MODDIR | tail -1 | cut -f 1`"
verbose "minimum: $INITRDSIZE kB for files + inodes + filesystem metadata"
INITRDSIZE=`expr $INITRDSIZE + 32768` # enough for ext3 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 "mkfs.ext3 -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE"
[ "$VERBOSE" ] && OPT_Q="" || OPT_Q="-q"
mkfs.ext3 $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 lib32 lib64 newroot proc sbin sys var)
if [ $? -ne 0 ]; then
echo "$cmd -- ERROR creating directories in $TMPMNT"
cleanup 1
fi
verbose "symlinking lib to lib64"
(cd $TMPMNT; ln -s lib64 lib)
if [ $? -ne 0 ]; then
echo "$cmd -- ERROR creating lib symlink"
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; /usr/sbin/MAKEDEV $OPT_Q $BASICDEVICES $BLOCKDEVICES)
RETCODE=$?
;;
ubuntu)
(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 -eq 0 ]; then
(cd $TMPMNT/dev; mkdir etherd && mknod etherd/discover c 152 3)
RETCODE=$?
fi
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
if [ -n "$MODDIR" ]; then
echo "$cmd -- copying $MODDIR to ram disk"
mkdir -p $TMPMNT/lib/modules
cp -a $MODDIR $TMPMNT/lib/modules
echo "$cmd -- running depmod"
depmod -b $TMPMNT -a $(basename $MODDIR)
fi
mkdir -p $TMPMNT/var/lib/nfs/{,sm,state}
mkdir -p $TMPMNT/var/run
mkdir -p $TMPMNT/mnt
mkdir -p $TMPMNT/etc
cp -a /etc/dropbear $TMPMNT/etc
cp -a /etc/{passwd,shadow} $TMPMNT/etc
mkdir -p $TMPMNT/var/log
touch $TMPMNT/var/log/lastlog
mkdir -p $TMPMNT/root
echo /bin/bash > $TMPMNT/etc/shells
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
[ ! -e $TMPMNT/bin/$link ] && 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
verbose "removing $TMPMNT/lost+found"
rmdir $TMPMNT/lost+found
echo "$cmd -- ummounting ram disk"
umount $TMPMNT
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment