1#!/bin/bash 2# 3# lvm2create_initrd 4# 5# Miguel Cabeca 6# cabeca (at) ist (dot) utl (dot) pt 7# 8# Inspiration to write this script came from various sources 9# 10# Original LVM lvmcreate_initrd: ftp://ftp.sistina.com/pub/LVM/1.0/ 11# Kernel initrd.txt: http://www.kernel.org/ 12# EVMS INSTALL.initrd & linuxrc: http://evms.sourceforge.net/ 13# Jeffrey Layton's lvm2create_initrd: http://poochiereds.net/svn/lvm2create_initrd/ 14# Christophe Saout's initrd & linuxrc: http://www.saout.de/misc/ 15# 16# This script was only tested with kernel 2.6 with everything required to boot 17# the root filesystem built-in (not as modules). Ex: SCSI or IDE, RAID, device mapper 18# It does not support devfs as it is deprecated in the 2.6 kernel series 19# 20# It needs lvm2 tools, busybox, pivot_root, MAKEDEV 21# 22# It has been tested on Debian sid (unstable) only 23# 24# Changelog 25# 26/02/2004 Initial release -- Miguel Cabeca 26# 27/02/2004 Removed the BUSYBOXSYMLINKS var. The links are now determined at runtime. 27# some changes in init script to call a shell if something goes wrong. -- Miguel Cabeca 28# 19/04/2004 Several small changes. Pass args to init so single user mode works. Add some 29# PATH entries to /sbin/init shell script so chroot works without /usr mounted. Remove 30# mkdir /initrd so we don't cause problems if root filesystem is corrupted. -- Jeff Layton 31# 15/05/2004 initial support for modules, create lvm.conf from lvm dumpconfig, other cleanups -- Jeff Layton 32# 14/11/2006 Update handling of ldd output to handle hardcoded library links and virtual dll linux-gate. 33# Add support for Gentoo-style MAKEDEV. Remove hardcoded BINUTILS paths -- Douglas Mayle 34# 35# Copyright Miguel Cabeca, Jeffrey Layton, 2004 36# 37# This program is free software; you can redistribute it and/or modify 38# it under the terms of the GNU General Public License as published by 39# the Free Software Foundation; either version 2 of the License, or 40# (at your option) any later version. 41# 42# This program is distributed in the hope that it will be useful, 43# but WITHOUT ANY WARRANTY; without even the implied warranty of 44# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 45# GNU General Public License for more details. 46# 47# You should have received a copy of the GNU General Public License 48# along with this program; if not, write to the Free Software 49# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 50# 51# $Id: lvm2create_initrd,v 1.1.1.1 2008/12/22 00:18:58 haad Exp $ 52 53TMPMNT=/tmp/mnt.$$ 54DEVRAM=/tmp/initrd.$$ 55 56# set defaults 57BINFILES=${BINFILES:-"`which lvm` `which bash` `which busybox` `which pivot_root`"} 58BASICDEVICES=${BASICDEVICES:-"std consoleonly fd"} 59BLOCKDEVICES=${BLOCKDEVICES:-"md hda hdb hdc hdd sda sdb sdc sdd"} 60MAKEDEV=${MAKEDEV:-"debian"} 61 62# Uncomment this if you want to disable automatic size detection 63#INITRDSIZE=4096 64 65PATH=/bin:/sbin:/usr/bin:/usr/sbin:$PATH 66 67usage () { 68 echo "Create an initial ramdisk image for LVM2 root filesystem" 69 echo "$cmd: [-h] [-v] [-c lvm.conf] [-m modulelist] [-e extrafiles] -r [raiddevs] [-R mdadm.conf] [-M style] [kernel version]" 70 echo " -h|--help print this usage message" 71 echo " -v|--verbose verbose progress messages" 72 echo " -c|--lvmconf path to lvm.conf (/etc/lvm/lvm.conf)" 73 echo " -m|--modules modules to copy to initrd image" 74 echo " -e|--extra extra files to add to initrd" 75 echo " -r|--raid raid devices to start in initrd" 76 echo " -R|--raidconf location of mdadm.conf file to include" 77 echo " -M|--makedev set MAKEDEV type (debian or redhat)" 78} 79 80verbose () { 81 [ "$VERBOSE" ] && echo "`echo $cmd | tr '[a-z0-9/_]' ' '` -- $1" || true 82} 83 84cleanup () { 85 [ "`mount | grep $DEVRAM`" ] && verbose "unmounting $DEVRAM" && umount $DEVRAM 86 [ -f $DEVRAM ] && verbose "removing $DEVRAM" && rm $DEVRAM 87 [ -d $TMPMNT ] && verbose "removing $TMPMNT" && rmdir $TMPMNT 88 verbose "exit with code $1" 89 exit $1 90} 91 92trap " 93 verbose 'Caught interrupt' 94 echo 'Bye bye...' 95 cleanup 1 96" 1 2 3 15 97 98create_init () { 99 cat << 'INIT' > $TMPMNT/sbin/init 100#!/bin/bash 101 102# include in the path some dirs from the real root filesystem 103# for chroot, blockdev 104PATH="/sbin:/bin:/usr/sbin:/usr/bin:/lib/lvm-200:/initrd/bin:/initrd/sbin" 105PRE="initrd:" 106 107do_shell(){ 108 /bin/echo 109 /bin/echo "*** Entering LVM2 rescue shell. Exit shell to continue booting. ***" 110 /bin/echo 111 /bin/bash 112} 113 114echo "$PRE Remounting / read/write" 115mount -t ext2 -o remount,rw /dev/ram0 / 116 117 118# We need /proc for device mapper 119echo "$PRE Mounting /proc" 120mount -t proc none /proc 121 122# plug in modules listed in /etc/modules 123if [ -f /etc/modules ]; then 124 echo -n "$PRE plugging in kernel modules:" 125 cat /etc/modules | 126 while read module; do 127 echo -n " $module" 128 modprobe $module 129 done 130 echo '.' 131fi 132 133# start raid devices if raid_autostart file exists 134if [ -f /etc/raid_autostart ]; then 135 if [ ! -f /etc/mdadm/mdadm.conf ]; then 136 mdoptions='--super-minor=dev' 137 fi 138 cat /etc/raid_autostart| 139 while read dev; do 140 echo "Starting RAID device $dev" 141 /sbin/mdadm --assemble $dev $mdoptions 142 done 143fi 144 145# Create the /dev/mapper/control device for the ioctl 146# interface using the major and minor numbers that have been allocated 147# dynamically. 148 149echo -n "$PRE Finding device mapper major and minor numbers " 150 151MAJOR=$(sed -n 's/^ *\([0-9]\+\) \+misc$/\1/p' /proc/devices) 152MINOR=$(sed -n 's/^ *\([0-9]\+\) \+device-mapper$/\1/p' /proc/misc) 153if test -n "$MAJOR" -a -n "$MINOR" ; then 154 mkdir -p -m 755 /dev/mapper 155 mknod -m 600 /dev/mapper/control c $MAJOR $MINOR 156fi 157 158echo "($MAJOR,$MINOR)" 159 160# Device-Mapper dynamically allocates all device numbers. This means it is possible 161# that the root volume specified to LILO or Grub may have a different number when the 162# initrd runs than when the system was last running. In order to make sure the 163# correct volume is mounted as root, the init script must determine what the 164# desired root volume name is by getting the LVM2 root volume name from the kernel command line. In order for 165# this to work correctly, "lvm2root=/dev/Volume_Group_Name/Root_Volume_Name" needs to be passed 166# to the kernel command line (where Root_Volume_Name is replaced by your actual 167# root volume's name. 168for arg in `cat /proc/cmdline`; do 169 echo $arg | grep '^lvm2root=' > /dev/null 170 if [ $? -eq 0 ]; then 171 rootvol=${arg#lvm2root=} 172 break 173 fi 174done 175 176echo "$PRE Activating LVM2 volumes" 177 178 179# run a shell if we're passed lvm2rescue on commandline 180grep lvm2rescue /proc/cmdline 1>/dev/null 2>&1 181if [ $? -eq 0 ]; then 182 lvm vgchange --ignorelockingfailure -P -a y 183 do_shell 184else 185 lvm vgchange --ignorelockingfailure -a y 186fi 187 188echo "$PRE Mounting root filesystem $rootvol ro" 189mkdir /rootvol 190if ! mount -t auto -o ro $rootvol /rootvol; then 191 echo "\t*FAILED*"; 192 do_shell 193fi 194 195echo "$PRE Umounting /proc" 196umount /proc 197 198echo "$PRE Changing roots" 199cd /rootvol 200if ! pivot_root . initrd ; then 201 echo "\t*FAILED*" 202 do_shell 203fi 204 205echo "$PRE Proceeding with boot..." 206 207exec chroot . /bin/sh -c "umount /initrd; blockdev --flushbufs /dev/ram0 ; exec /sbin/init $*" < dev/console > dev/console 2>&1 208 209INIT 210 chmod 555 $TMPMNT/sbin/init 211} 212 213# create lvm.conf file from dumpconfig. Just use filter options 214create_lvmconf () { 215 echo 'devices {' > $TMPMNT/etc/lvm/lvm.conf 216 lvm dumpconfig | grep 'filter=' >> $TMPMNT/etc/lvm/lvm.conf 217 echo '}' >> $TMPMNT/etc/lvm/lvm.conf 218} 219 220# 221# Main 222# 223 224cmd=`basename $0` 225 226VERSION=`uname -r` 227 228while [ $# -gt 0 ]; do 229 case $1 in 230 -h|--help) usage; exit 0;; 231 -v|--verbose) VERBOSE="y";; 232 -c|--lvmconf) LVMCONF=$2; shift;; 233 -m|--modules) MODULES=$2; shift;; 234 -e|--extra) EXTRAFILES=$2; shift;; 235 -r|--raid) RAID=$2; shift;; 236 -R|--raidconf) RAIDCONF=$2; shift;; 237 -M|--makedev) MAKEDEV=$2; shift;; 238 [2-9].[0-9]*.[0-9]*) VERSION=$1;; 239 *) echo "$cmd -- invalid option '$1'"; usage; exit 0;; 240 esac 241 shift 242done 243 244INITRD=${INITRD:-"/boot/initrd-lvm2-$VERSION.gz"} 245 246echo "$cmd -- make LVM initial ram disk $INITRD" 247echo "" 248 249if [ -n "$RAID" ]; then 250 BINFILES="$BINFILES /sbin/mdadm" 251 RAIDCONF=${RAIDCONF:-"/etc/mdadm/mdadm.conf"} 252 if [ -r $RAIDCONF ]; then 253 EXTRAFILES="$EXTRAFILES $RAIDCONF" 254 else 255 echo "$cmd -- WARNING: No $RAIDCONF! Your RAID device minor numbers must match their superblock values!" 256 fi 257fi 258 259# add modprobe if we declared any modules 260if [ -n "$MODULES" ]; then 261 BINFILES="$BINFILES /sbin/modprobe /sbin/insmod /sbin/rmmod" 262fi 263 264for a in $BINFILES $EXTRAFILES; do 265 if [ ! -r "$a" ] ; then 266 echo "$cmd -- ERROR: you need $a" 267 exit 1; 268 fi; 269done 270 271# Figure out which shared libraries we actually need in our initrd 272echo "$cmd -- finding required shared libraries" 273verbose "BINFILES: `echo $BINFILES`" 274 275# We need to strip certain lines from ldd output. This is the full output of an example ldd: 276#lvmhost~ # ldd /sbin/lvm /bin/bash 277#/sbin/lvm: 278# not a dynamic executable 279#/bin/bash: 280# linux-gate.so.1 => (0xbfffe000) 281# libncurses.so.5 => /lib/libncurses.so.5 (0xb7ee3000) 282# libdl.so.2 => /lib/libdl.so.2 (0xb7edf000) 283# libc.so.6 => /lib/libc.so.6 (0xb7dc1000) 284# /lib/ld-linux.so.2 (0xb7f28000) 285# 286# 1) Lines with a ":" contain the name of the original binary we're examining, and so are unnecessary. 287# We need to strip them because they contain "/", and can be confused with links with a hardcoded path. 288# 2) The linux-gate library is a virtual dll that does not exist on disk, but is instead loaded automatically 289# into the process space, and can't be copied to the ramdisk 290# 291# After these lines have been stripped, we're interested in the lines remaining if they 292# 1) Contain "=>" because they are pathless links, and the value following the token is the path on the disk 293# 2) Contain "/" because it's a link with a hardcoded path, and so we're interested in the link itself. 294LIBFILES=`ldd $BINFILES 2>/dev/null |grep -v -E \(linux-gate\|:\) | awk '{if (/=>/) { print $3 } else if (/\//) { print $1 }}' | sort -u` 295if [ $? -ne 0 ]; then 296 echo "$cmd -- ERROR figuring out needed shared libraries" 297 exit 1 298fi 299 300verbose "Shared libraries needed: `echo $LIBFILES`" 301 302INITRDFILES="$BINFILES $LIBFILES $MODULES $EXTRAFILES" 303 304# tack on stuff for modules if we declared any and the files exist 305if [ -n "$MODULES" ]; then 306 if [ -f "/etc/modprobe.conf" ]; then 307 INITRDFILES="$INITRDFILES /etc/modprobe.conf" 308 fi 309 if [ -f "/lib/modules/modprobe.conf" ]; then 310 INITRDFILES="$INITRDFILES /lib/modules/modprobe.conf" 311 fi 312fi 313 314# Calculate the the size of the ramdisk image. 315# Don't forget that inodes take up space too, as does the filesystem metadata. 316echo "$cmd -- calculating initrd filesystem parameters" 317if [ -z "$INITRDSIZE" ]; then 318 echo "$cmd -- calculating loopback file size" 319 verbose "finding size" 320 INITRDSIZE="`du -Lck $INITRDFILES | tail -1 | cut -f 1`" 321 verbose "minimum: $INITRDSIZE kB for files + inodes + filesystem metadata" 322 INITRDSIZE=`expr $INITRDSIZE + 512` # enough for ext2 fs + a bit 323fi 324 325echo "$cmd -- making loopback file ($INITRDSIZE kB)" 326verbose "using $DEVRAM as a temporary loopback file" 327dd if=/dev/zero of=$DEVRAM count=$INITRDSIZE bs=1024 > /dev/null 2>&1 328if [ $? -ne 0 ]; then 329 echo "$cmd -- ERROR creating loopback file" 330 cleanup 1 331fi 332 333echo "$cmd -- making ram disk filesystem" 334verbose "mke2fs -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE" 335[ "$VERBOSE" ] && OPT_Q="" || OPT_Q="-q" 336mke2fs $OPT_Q -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE 337if [ $? -ne 0 ]; then 338 echo "$cmd -- ERROR making ram disk filesystem" 339 echo "$cmd -- ERROR you need to use mke2fs >= 1.14 or increase INITRDSIZE" 340 cleanup 1 341fi 342 343verbose "creating mountpoint $TMPMNT" 344mkdir $TMPMNT 345if [ $? -ne 0 ]; then 346 echo "$cmd -- ERROR making $TMPMNT" 347 cleanup 1 348fi 349 350echo "$cmd -- mounting ram disk filesystem" 351verbose "mount -o loop $DEVRAM $TMPMNT" 352mount -oloop $DEVRAM $TMPMNT 353if [ $? -ne 0 ]; then 354 echo "$cmd -- ERROR mounting $DEVRAM on $TMPMNT" 355 cleanup 1 356fi 357 358verbose "creating basic set of directories in $TMPMNT" 359(cd $TMPMNT; mkdir bin dev etc lib proc sbin var) 360if [ $? -ne 0 ]; then 361 echo "$cmd -- ERROR creating directories in $TMPMNT" 362 cleanup 1 363fi 364 365# Add some /dev files. We have to handle different types of MAKEDEV invocations 366# here, so this is rather messy. 367RETCODE=0 368echo "$cmd -- adding required /dev files" 369verbose "BASICDEVICES: `echo $BASICDEVICES`" 370verbose "BLOCKDEVICES: `echo $BLOCKDEVICES`" 371[ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="" 372case "$MAKEDEV" in 373debian) 374 (cd $TMPMNT/dev; /dev/MAKEDEV $OPT_Q $BASICDEVICES $BLOCKDEVICES) 375 RETCODE=$? 376 ;; 377redhat) 378 (cd $TMPMNT/dev; /dev/MAKEDEV $OPT_Q -d $TMPMNT/dev -m 2) 379 RETCODE=$? 380 ;; 381gentoo) 382 (cd $TMPMNT/dev; /usr/sbin/MAKEDEV $OPT_Q $BASICDEVICES $BLOCKDEVICES) 383 RETCODE=$? 384 ;; 385*) 386 echo "$cmd -- ERROR: $MAKEDEV is not a known MAKEDEV style." 387 RETCODE=1 388 ;; 389esac 390 391 392if [ $RETCODE -ne 0 ]; then 393 echo "$cmd -- ERROR adding /dev files" 394 cleanup 1 395fi 396 397 398# copy necessary files to ram disk 399echo "$cmd -- copying initrd files to ram disk" 400[ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="--quiet" 401verbose "find \$INITRDFILES | cpio -pdmL $OPT_Q $TMPMNT" 402find $INITRDFILES | cpio -pdmL $OPT_Q $TMPMNT 403if [ $? -ne 0 ]; then 404 echo "$cmd -- ERROR cpio to ram disk" 405 cleanup 1 406fi 407 408 409echo "$cmd -- creating symlinks to busybox" 410shopt -s extglob 411[ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="" 412BUSYBOXSYMLINKS=`busybox 2>&1| awk '/^Currently defined functions:$/ {i++;next} i'|tr ',\t\n' ' '` 413for link in ${BUSYBOXSYMLINKS//@(linuxrc|init|busybox)}; do 414 ln -s $OPT_Q busybox $TMPMNT/bin/$link; 415done 416shopt -u extglob 417 418echo "$cmd -- creating new $TMPMNT/sbin/init" 419create_init 420if [ $? -ne 0 ]; then 421 echo "$cmd -- ERROR creating init" 422 cleanup 423 exit 1 424fi 425 426# copy LVMCONF into place or create a stripped down one from lvm dumpconfig 427mkdir -p $TMPMNT/etc/lvm 428if [ -n "$LVMCONF" ]; then 429 echo "$cmd -- copying $LVMCONF to $TMPMNT/etc/lvm/lvm.conf" 430 if [ -f "$LVMCONF" ]; then 431 cp $LVMCONF $TMPMNT/etc/lvm/lvm.conf 432 else 433 echo "$cmd -- ERROR: $LVMCONF does not exist!" 434 cleanup 435 exit 1 436 fi 437else 438 echo "$cmd -- creating new $TMPMNT/etc/lvm/lvm.conf" 439 create_lvmconf 440fi 441 442if [ -n "$RAID" ]; then 443 RAIDLIST="$TMPMNT/etc/raid_autostart" 444 echo "$cmd -- creating $RAIDLIST file." 445 for device in $RAID; do 446 echo $device >> $RAIDLIST 447 done 448fi 449 450# create modules.dep and /etc/modules files if needed 451if [ -n "$MODULES" ]; then 452 echo "$cmd -- creating $MODDIR/modules.dep file and $TMPMNT/etc/modules" 453 depmod -b $TMPMNT $VERSION 454 for module in $MODULES; do 455 basename $module | sed 's/\.k\{0,1\}o$//' >> $TMPMNT/etc/modules 456 done 457fi 458 459verbose "removing $TMPMNT/lost+found" 460rmdir $TMPMNT/lost+found 461 462echo "$cmd -- ummounting ram disk" 463umount $DEVRAM 464if [ $? -ne 0 ]; then 465 echo "$cmd -- ERROR umounting $DEVRAM" 466 cleanup 1 467fi 468 469echo "$cmd -- creating compressed initrd $INITRD" 470verbose "dd if=$DEVRAM bs=1k count=$INITRDSIZE | gzip -9" 471dd if=$DEVRAM bs=1k count=$INITRDSIZE 2>/dev/null | gzip -9 > $INITRD 472if [ $? -ne 0 ]; then 473 echo "$cmd -- ERROR creating $INITRD" 474 cleanup 1 475fi 476 477 478cat << FINALTXT 479-------------------------------------------------------- 480Your initrd is ready in $INITRD 481 482Don't forget to set root=/dev/ram0 in kernel parameters 483Don't forget to set lvm2root=/dev/VG/LV in kernel parameters, where LV is your root volume 484If you use lilo try adding/modifying an entry similar to this one in lilo.conf: 485 486image=/boot/vmlinuz-lvm2-$VERSION 487 label="ramdisk_LVM" 488 initrd=/boot/initrd-lvm2-$VERSION.gz 489 append="root=/dev/ram0 lvm2root=/dev/system/root <other parameters>" 490 491If using grub try adding/modifying an entry similar to this one in menu.lst 492 493title ramdisk LVM 494 kernel /boot/vmlinuz-lvm2-$VERSION root=/dev/ram0 lvm2root=/dev/system/root <other parameters> 495 initrd /boot/initrd-lvm2-$VERSION.gz 496 497You can also pass lvm2rescue to the kernel to get a shell 498-------------------------------------------------------- 499FINALTXT 500 501cleanup 0 502 503