summaryrefslogtreecommitdiffstats
path: root/scripts/lvm2create_initrd/lvm2create_initrd
blob: 36034cf7cec50f7c7054dff613a452b831090d4a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
#!/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$

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