#!/bin/sh # mkinitrd # # Written by Erik Troan # # Contributors: # Elliot Lee # Miguel de Icaza # Christian 'Dr. Disk' Hechelmann # Michael K. Johnson # Pierre Habraken # Jakub Jelinek # Carlo Arenas Belon (carenas@chasqui.lared.net.pe> # Keith Owens # # Rewritten by Dmitry V. Levin # # Contributors: # Nick S. Grechukh : # Aug 15, 2004 improved raid support: # - using LVM over raid # - arrays created with mdadm (without /etc/raidtab) # - loading raid support to initrd can be forced (for autodetect at bootstrap) # bugs: raid-over-raid (level 10) will not working this way. however, formerly it wasn't working at all. PROG=mkinitrd VERSION=2.9.1 CATCHED= # override PATH export PATH="/bin:/sbin:/usr/bin:/usr/sbin" umask 077 Exit() { local rc=$? [ -z "$1" ] || rc="$1" CATCHED=1 exit $rc } Fatal() { echo "$PROG: $*" >&2 Exit 1 } verbose= Verbose() { [ -n "$verbose" ] || return 0 echo "$PROG: $*" } debug= Debug() { [ -n "$debug" ] || return 0 echo "$PROG: $*" } is_yes() { # Test syntax if [ $# = 0 ]; then return 2 fi # Check value case "$1" in yes|Yes|YES|true|True|TRUE|on|On|ON|Y|y|1) # true returns zero return 0 ;; *) # false returns one return 1 ;; esac } is_no() { # Test syntax if [ $# = 0 ] ; then return 2 fi case "$1" in no|No|NO|false|False|FALSE|off|Off|OFF|N|n|0) # true returns zero return 0 ;; *) # false returns one return 1 ;; esac } WORKDIR= IMAGE= MNTDIR= MNTPOINT= IMAGESIZE= INODES= exit_handler() { local rc=$? trap - EXIT [ -n "$CATCHED" -o $rc -eq 0 ] || echo "$PROG: unhandled error, exiting..." [ -n "$MNTPOINT" ] && umount "$MNTPOINT" >/dev/null 2>&1 ||: [ -z "$WORKDIR" ] || rm -rf "$WORKDIR" exit $rc } signal_handler() { echo 'Interrupted!' >&2 Exit 1 } trap exit_handler EXIT trap signal_handler SIGHUP SIGPIPE SIGINT SIGTERM SIGQUIT NormalizedName() { echo -n "X$1" | sed -e '1s/^X//' -e 'y/-/_/' } USE_COMPRESS=1 TARGET= KERNEL= strict= noscsi= noide= noraid= pause= MODULES= MODULES_DIR= MODULES_CONF= HAVE_RAID= PVSCAN=/sbin/pvscan MDADM=/usr/sbin/mdadm FORCE_RAID= FSTAB_FILE=/etc/fstab BIN_SPLASH=/bin/splash [ -x "$BIN_SPLASH" ] && ADD_BOOTSPLASH=1 || ADD_BOOTSPLASH= PRE_SCSI_MODNAMES="scsi_mod sd_mod unknown" IGNORE_MODNAMES="`NormalizedName "$IGNORE_MODNAMES"`" IGNORE_MODNAMES=" $IGNORE_MODNAMES ppa imm ide_scsi usb_storage" LOADED_MODNAMES= loopDev= loopFs= loopFile= uname_r=`uname -r` ModuleAlreadyLoaded() { local name="$1" m for m in $LOADED_MODNAMES; do [ "$m" != "$name" ] || return 0 done LOADED_MODNAMES="$LOADED_MODNAMES $name" return 1 } IgnoredModule() { local name="$1" m for m in off null $IGNORE_MODNAMES; do [ "$m" != "$name" ] || return 0 done return 1 } PreScsiModule() { local name="$1" m for m in $PRE_SCSI_MODNAMES; do [ "$m" != "$name" ] || return 0 done return 1 } AddModuleFile() { local path="$1" name="$1" name="${name##*/}" name="${name%.gz}" name="${name%.o}" name="${name%.ko}" name="`NormalizedName "$name"`" if ModuleAlreadyLoaded "$name"; then return 0 fi if IgnoredModule "$name"; then Debug "Ignoring \"$name\" module" return 0 fi Debug "Found module \"$name\" as $path" MODULES="$MODULES $path" } FindModule() { local skip_errors= name="$1" if [ -z "${name##-*}" ]; then skip_errors=1 name="${name#-*}" fi name="${name%.gz}" name="${name%.o}" name="${name%.ko}" name="`NormalizedName "$name"`" Debug "Looking for \"$name\" module" local list list=`modprobe --kernel-release "$KERNEL" --list-module-files "$name" 2>/dev/null` if [ $? -ne 0 ]; then if [ -n "$skip_errors" ]; then Debug "Ignoring missing \"$name\" module" return 0 fi if PreScsiModule "$name"; then Debug "Ignoring missing \"$name\" SCSI module" return 0 fi echo "No module \"$name\" found for kernel $KERNEL" >&2 [ -z "$strict" ] && return 1 || Exit 1 fi local m for m in $list; do [ -z "${m##/lib/modules/*}" ] || continue AddModuleFile "$m" done } FindModules() { local n for n in "$@"; do FindModule "$n" done } FindScsiModules() { [ -z "$noscsi" ] || return local scsimodules scsimodules=`egrep -s '(alias|probeall)[ ]+scsi_hostadapter' "$MODULES_CONF" | grep -v '^[ ]*#' | LC_COLLATE=C sort -u | awk '{$1=$2="";print}'` local n for n in $scsimodules; do n="`NormalizedName "$n"`" if IgnoredModule "$n"; then Debug "Ignoring \"$n\" module" else local m for m in $PRE_SCSI_MODNAMES; do FindModule "$m" done FindModule "$n" fi done } FindIdeModules() { [ -z "$noide" ] || return local ide ide=/proc/ide/ide* if [ -n "$ide" ]; then FindModule -ide-mod FindModule -ide-probe-mod FindModule -ide-disk fi } FindRaidModules() { [ -z "$noraid" ] || return if egrep -s '^/dev/md/?[0-9]+[ ]' "$FSTAB_FILE" | fgrep -qsv noauto \ || [ -x $PVSCAN ] && $PVSCAN grep ACTIVE | egrep -q '"/dev/md/?[0-9]"' \ || [ -n "$FORCE_RAID" ]; then #### detects indirect using of raid arrays (e.g. via LVM) HAVE_RAID=1 FindModule -md if [ -f /etc/raidtab ]; then for number in $(grep '^[ ]*raid-level' /etc/raidtab | awk '{print $2}' |LC_COLLATE=C sort -u); do case "$number" in [015]) FindModule "raid$number" ;; 4) FindModule "raid5" ;; -1|linear) FindModule "linear" ;; *) echo "raid level $number (in /etc/raidtab) not recognized" >&2 ;; esac done fi #### detects arrays _without_ raidtab (e.g. created with mdadm). maybe rewrite to /proc/mdstat, heh? #### of cource, it will detect only running arrays if [ -x $MDADM ]; then for raidmod in `$MDADM --detail --scan|awk '{print $3}'|awk -F '=' '{print $2}'`; do case "$raidmod" in linear|raid[015]) FindModule "$raidmod" ;; *) echo "raid level $raidmod (in output of mdadm) not recognized" >&2 ;; esac done fi #### and just for perfection: allow to _manually_ specify raid level, even if it is not running now. case "$FORCE_RAID" in [015]) FindModule "raid$FORCE_RAID" ;; 4) FindModule "raid5" ;; -1|linear) FindModule "linear" ;; *) echo "raid level $FORCE_RAID (in --force-raid) not recognized" >&2 ;; esac fi if grep -s '^/dev/ataraid' "$FSTAB_FILE" |fgrep -qsv noauto; then local ataraidmodules ataraidmodules=`egrep -s '(alias|probeall)[ ]+ataraid_hostadapter' "$MODULES_CONF" | grep -v '^[ ]*#' | LC_COLLATE=C sort -u | awk '{$1=$2="";print}'` local n for n in $ataraidmodules; do FindModule "$n" done fi } FindRootModules() { # In case the root filesystem is modular. #rootdev=$(awk '{if (($2 == "/") && ($1 !~ /^[ \t]*#/) {print $1}}' "$FSTAB_FILE") local rootfs rootfs=$(awk '{if (($2 == "/") && ($1 !~ /^[ \t]*#/)) {print $3}}' "$FSTAB_FILE") [ -z "$rootfs" ] || FindModule -"$rootfs" } FindLoopModules() { # check to see if we need to set up a loopback filesystem local full full=$(awk '$2 == "/" && $4 ~ "loop" {print $1}' "$FSTAB_FILE") if [ -n "$full" ]; then local dir="$full" line= while [ -n "$dir" -a -z "$line" ]; do dir="${dir%/*}" line=$(awk -v "dir=$dir" '$2 == dir {print $0}' "$FSTAB_FILE") done [ -n "$line" -a "$dir" != / ] || Fatal "bad fstab, loopback file doesn't belong to any device." loopDev=$(echo "$line" |awk '{print $1}') loopFs=$(echo "$line" |awk '{print $3}') loopFile=$(echo "$full" |sed "s|$dir||") FindModule -loop FindModule -"$loopFs" fi } Install() { install -D $debug "$1" "$2" || Fatal "Failed to install \"$1\" file." } Cp() { cp $debug "$@" } Ln() { ln $debug "$@" } Mkdir() { mkdir $debug "$@" } Mknod() { file="$1" shift mknod "$file" "$@" && Debug "Created $file device" || Fatal "Failed to create \"$file\" device." } MakeMountDir() { MNTDIR="$WORKDIR/tree" RCFILE="$MNTDIR/linuxrc" Mkdir -p $MNTDIR/{etc,dev,safedev,loopfs} || Fatal "Failed to create directories." Install /lib/mkinitrd/busybox "$MNTDIR/bin/sh" Ln -s sh "$MNTDIR/bin/echo" Ln -s sh "$MNTDIR/bin/insmod" Ln -s sh "$MNTDIR/bin/modprobe" local m for m in $MODULES; do Install "$m" "$MNTDIR/$m" || Fatal "Failed to install $m module." m="$MNTDIR/$m" if [ -z "${m%%*.gz}" ]; then gunzip $debug "$m" || Fatal "Failed to uncompress \"$m\" module." fi done Mknod "$MNTDIR/dev/console" c 5 1 Mknod "$MNTDIR/dev/null" c 1 3 Mknod "$MNTDIR/dev/ram" b 1 1 Mknod "$MNTDIR/dev/systty" c 4 0 Mknod "$MNTDIR/dev/tty1" c 4 1 cat >"$RCFILE" <>"$RCFILE" <>"$RCFILE" <>"$RCFILE" <&2 <<__EOF__ You can now edit initrd manually. WORKDIR: $WORKDIR Press ENTER to continue automatic initrd generation. __EOF__ read fi chmod +x "$RCFILE" if [ -n "$verbose" ]; then echo "Contents of linuxrc:" cat "$RCFILE" fi INODES=`find "$MNTDIR" |wc -l` || Fatal "Failed to calculate inodes." INODES=$[INODES+20] Verbose "Inode count: $[INODES]" IMAGESIZE=`du -ks --block-size=4096 "$MNTDIR" |cut -f1` || Fatal "Failed to calculate image size." # Add more 20% IMAGESIZE=$[IMAGESIZE*6/5] Verbose "Image size: $[IMAGESIZE*4]K" } MakeImageFile_ext2() { dd if=/dev/zero of="$IMAGE" bs=4k count="$IMAGESIZE" 2>/dev/null && Verbose "Created image file" || Fatal "Failed to create image file." mke2fs -q -m 0 -F -N "$INODES" "$IMAGE" && Verbose "Created filesystem for ramdisk" || Fatal "Failed to create filesystem." mount $verbose -t ext2 "$IMAGE" "$MNTPOINT" -o loop,noexec,nosuid,nodev || Fatal "Failed to mount loopback device." # We don't need this directory, so let's save space. rmdir "$MNTPOINT/lost+found" (cd "$MNTDIR"; tar cf - .) | (cd "$MNTPOINT"; tar xf -) && Verbose "Installed directory tree: $MNTDIR --> $MNTPOINT" || Fatal "Failed to copy directory tree." umount "$MNTPOINT" || Fatal "Failed to unmount loopback device." } MakeImageFile_romfs() { genromfs -f "$IMAGE" -d "$MNTDIR" && Verbose "Created image from tree: $MNTDIR --> $IMAGE" || Fatal "Failed to create romfs image." } MakeImageFile() { if fgrep -qs romfs "/boot/System.map-$KERNEL"; then MakeImageFile_romfs Verbose "Created romfs image file" return fi if fgrep -qs ext2 "/boot/System.map-$KERNEL"; then MakeImageFile_ext2 Verbose "Created ext2 image file" return fi Fatal "Failed to create image file: neither romfs nor ext2 support found in your kernel" } AddBootSplash() { local config="/etc/sysconfig/bootsplash" local fbresolution= [ -s "$config" ] && . "$config" && is_yes "$SPLASH" && [ -n "$THEME" ] && fbresolution=`fbresolution` ||: local themefile themefile="/usr/share/splash/themes/$THEME/config/bootsplash-$fbresolution.cfg" if [ -f "$themefile" ]; then "$BIN_SPLASH" -f -s "$themefile" >>"$TARGET" && Verbose "Added bootsplash \"$themefile\" to the $TARGET" || Verbose "Failed to add bootsplash \"$themefile\" to the $TARGET." fi } Usage() { cat >&2 < Valid options are: --fstab FILENAME use FILENAME instead of /etc/fstab. --preload MODULENAME load MODULENAME before all found automatically. --with MODULENAME load MODULENAME after all found automatically. --omit-scsi-modules do not load any SCSI modules. --omit-ide-modules do not load any IDE modules. --omit-raid-modules do not load any raid modules. --pause pause for manual initrd editing. --nocompress do not compress initrd image. --nobootsplash do not add bootsplash to the initrd image. --strict abort on errors. --image-version make image name based on kernel name. --ifneeded create initrd image only if needed. --version print version number and exit. -f, --force force initrd image creation. -v, --verbose be more verbose. -d, --debug print debug information. -h, --help show this text. Example: $PROG /boot/initrd-$uname_r.img $uname_r EOF [ -n "$1" ] && Exit "$1" || Exit } TEMP=`getopt -n "$PROG" -o fhvd -l help,version,verbose,debug,force,ifneeded,omit-scsi-modules,omit-ide-modules,omit-raid-modules,pause,image-version,nocompress,nobootsplash,strict,fstab:,before:,preload:,with:,after:,force-raid: -- "$@"` || Usage 1 eval set -- "$TEMP" img_vers= force= while :; do case "$1" in --force-raid) shift FORCE_RAID=$1 shift ;; --fstab) shift FSTAB_FILE=$1 shift ;; --before|--preload) shift PRELOAD_MODNAMES="$PRELOAD_MODNAMES $1" shift ;; --after|--with) shift POSTLOAD_MODNAMES="$POSTLOAD_MODNAMES $1" shift ;; --strict) strict=1 shift ;; --ifneeded) ifneeded=1 shift ;; --nocompress) USE_COMPRESS= shift ;; --nobootsplash) ADD_BOOTSPLASH= shift ;; --omit-scsi-modules) noscsi=1 shift ;; --omit-ide-modules) noide=1 shift ;; --omit-raid-modules) noraid=1 shift ;; --pause) pause=1 shift ;; --image-version) img_vers=1 shift ;; -v|--verbose) verbose=-v shift ;; -d|--debug) verbose=-v debug=-v shift ;; -f|--force) force=1 shift ;; --version) echo "$PROG: version $VERSION" exit 0 ;; -h|--help) Usage 0 ;; --) shift break ;; *) Fatal "$PROG: unrecognized option: $1" ;; esac done TARGET=$1 [ -n "$TARGET" ] || Fatal "Target image not specified." shift KERNEL=$1 [ -n "$KERNEL" ] || Fatal "Kernel version not specified." shift if [ -n "$img_vers" ]; then TARGET="$TARGET-$KERNEL.img" fi [ -n "$force" -o ! -f "$TARGET" ] || Fatal "$TARGET already exists." MODULES_DIR="/lib/modules/$KERNEL" [ -d "$MODULES_DIR" ] || Fatal "Directory \"$MODULES_DIR\" doesn't exist or not accessible." WORKDIR=`mktemp -td initrd.XXXXXXXXXX` || Fatal "Failed to create working directory." Verbose "Generating module dependencies..." depmod -a -F "/boot/System.map-$KERNEL" "$KERNEL" && Verbose "...done." || Fatal "Failed to generate module dependencies." MODULES_CONF="$WORKDIR/modules.conf" modprobe -c >"$MODULES_CONF" || Fatal "Failed to parse modutils configuration." ### Begin module lookup. FindModules $PRELOAD_MODNAMES FindScsiModules FindIdeModules FindRaidModules FindRootModules FindLoopModules FindModules $POSTLOAD_MODNAMES ### End module lookup. if [ -n "$ifneeded" -a -z "$MODULES" ]; then Verbose "No modules are needed - not building initrd image." exit 0 fi Verbose "Using modules: $MODULES" MakeMountDir MNTPOINT="$WORKDIR/mnt" Mkdir -p "$MNTPOINT" || Fatal "Failed to create mount point." IMAGE="$WORKDIR/img" :>"$IMAGE" || Fatal "Failed to create image file." MakeImageFile if [ -n "$USE_COMPRESS" ]; then gzip -9n <"$IMAGE" >"$TARGET" && Verbose "Installed ramdisk into $TARGET" || Fatal "Failed to install ramdisk into $TARGET." else Cp "$IMAGE" "$TARGET" && Verbose "Installed ramdisk into $TARGET" || Fatal "Failed to install ramdisk into $TARGET." fi if [ -n "$ADD_BOOTSPLASH" ]; then AddBootSplash fi if [ -n "$verbose" ]; then size=`du -hs $TARGET |cut -f1` echo "Ramdisk size: $size" fi