#!/bin/sh # geninitrd # # by PLD Linux Team # # based on mkinitrd from RedHat Linux # VERSION='devel' PROGRAM=${0##*/} . /etc/rc.d/init.d/functions . /lib/geninitrd/functions . /etc/sysconfig/system # list of geninitrd modules which need setup routine after commandline args parsing GENINITRD_MODS="" COMPRESS=yes STRIP=/usr/bin/strip target="" kernel="" force="" verbose="" MODULES="" img_vers="" fstab=/etc/fstab modext=.o rootdev_nr=0 # device node for rootfs from fstab rootdev="" # internal variables # is /dev on tmpfs dev_mounted=no # is /proc mounted proc_mounted=no # is /sys mounted sys_mounted=no # is /tmp mounted on tmpfs tmp_mounted=no # are /dev nodes already created from /proc/devices info? proc_partitions=no usage() { echo "Usage: $PROGRAM [--version] [-v] [-f] [--ifneeded] [--preload ]" echo " [--with=] [--image-version] [--fstab=] [--nocompress]" echo " [--compress=yes|zstd|xz|lzma|bzip2|gzip|lzo]" echo " [--nostrip ] [--strip PATH/strip] [--strip=PATH/strip]" echo " [--initrdfs=rom|initramfs|ext2|cram] [--modules-conf=]" echo " [--with-bootsplash] [--without-bootsplash]" echo " [--with-fbsplash] [--without-fbsplash]" echo " [--with-fbcondecor] [--without-fbcondecor]" echo " [--lvmtoolsversion=1|2] [--with-udev] [--without-udev]" echo " [--with-suspend] [--without-suspend]" echo " [--with-tuxonice] [--without-tuxonice]" echo " [--without-dmraid]" echo " [--with-multipath=DEVPATH] [--without-multipath]" echo " [--without-blkid] [--without-luks]" echo " " echo "" echo "Example:" local kdir kver dir=${target:-/boot} for kdir in /lib/modules/*; do [ -d $kdir ] || continue kver=${kdir##*/} echo " $0 -f --initrdfs=initramfs $dir/initrd-$kver.gz $kver $verbose" done | sort -V } msg() { echo "$PROGRAM: $*" } warn() { msg "WARNING: $*" >&2 } verbose() { [ -n "$verbose" ] && msg "$*" >&3 } debug() { [ x"$verbose" = x"-v -v" ] && msg "$*" >&3 } # add initrd code to print to kmsg # @param string message # @param int loglevel. defaults to "6" (info) # Log levels can be: # Name String Meaning # KERN_EMERG "0" Emergency messages, system is about to crash or is unstable # KERN_ALERT "1" Something bad happened and action must be taken immediately # KERN_CRIT "2" A critical condition occurred like a serious hardware/software failure # KERN_ERR "3" An error condition, often used by drivers to indicate difficulties with the hardware # KERN_WARNING "4" A warning, meaning nothing serious by itself but might indicate problems # KERN_NOTICE "5" Nothing serious, but notably nevertheless. Often used to report security events. # KERN_INFO "6" Informational message e.g. startup information at driver initialization # KERN_DEBUG "7" Debug messages # KERN_CONT "c" "continued" line of log printout (only done after a line that had no enclosing \n) kmsg() { local msg="$1" level=${2:-6} echo "echo '<$level>$msg' > /dev/kmsg" | add_linuxrc } # aborts program abnormally die() { local rc=${2:-1} msg "ERROR: $1" >&2 exit $rc } # find program from specified paths find_tool() { local x p b n local paths="$initrd_dirs /bin /sbin /usr/bin /usr/sbin" for x in "$@"; do debug "find_tool: checking $x" if [ -x "$x" ]; then echo $x verbose "find_tool: found $x" return 0 fi n="$x" for p in $paths; do b=$(basename $x) debug "find_tool: checking $p/$b" if [ -x "$p/$b" ]; then echo $p/$b verbose "find_tool: found $p/$b" return 0 fi n="$n $p/$b" done done debug "find_tool: did not find any of: $n" return 1 } # loads geninitrd modules geninitrd_load_mods() { local mod for mod in "$@"; do if [ ! -f /lib/geninitrd/mod-$mod.sh ]; then die "$mod geninitrd module can't be loaded" fi . /lib/geninitrd/mod-$mod.sh GENINITRD_MODS="$GENINITRD_MODS $mod" done } # setup geninitrd modules geninitrd_setup_mods() { local mod for mod in $GENINITRD_MODS; do debug "# $mod" # some mods want init if type setup_mod_$mod > /dev/null; then eval setup_mod_$mod fi done } # append text to /linuxrc # takes STDIN as input add_linuxrc() { cat >> "$RCFILE" } # generate code to mount /dev on tmpfs and create initial nodes # can be called multiple times. /dev is cleaned up (umounted) automatically at # the end of script. mount_dev() { # we already generated tmpfs code; return if is_yes "$dev_mounted"; then return fi dev_mounted=yes busybox_applet mount mknod mkdir add_linuxrc <<-EOF : 'Creating /dev' if ! mount -t devtmpfs -o mode=0755,nosuid devtmpfs /dev > /dev/null 2>&1; then mount -o mode=0755,nosuid -t tmpfs tmpfs /dev mknod -m 600 /dev/console c 5 1 mknod -m 666 /dev/null c 1 3 mknod -m 666 /dev/zero c 1 5 mknod -m 666 /dev/random c 1 8 mknod -m 600 /dev/snapshot c 10 231 mknod -m 666 /dev/urandom c 1 9 mknod -m 666 /dev/ptmx c 5 2 mknod -m 644 /dev/kmsg c 1 11 fi mkdir /dev/pts mkdir /dev/shm EOF } # load font load_font() { local font [ ! -r /etc/sysconfig/console ] && return . /etc/sysconfig/console if [ -n "$CONSOLEFONT" ]; then font=$(ls -1 /lib/kbd/consolefonts/${CONSOLEFONT}*.gz 2> /dev/null) if [ -n "$font" ]; then verbose "Loading font $font" busybox_applet loadfont inst_d "/lib/kbd/consolefonts" cp -a "$font" "$DESTDIR/lib/kbd/consolefonts/" gunzip ${DESTDIR}/lib/kbd/consolefonts/${CONSOLEFONT}*.gz font=${font%.gz} echo "loadfont < $font" | add_linuxrc fi fi } # generate code to mount /proc on initrd # can be called multiple times mount_proc() { if is_yes "$proc_mounted"; then return fi proc_mounted=yes if [ "$INITRDFS" = "initramfs" ]; then # /proc is mounted with initramfs 2.6.22.14 kernel # XXX: remove when it is clear why proc was already mounted echo "[ -f /proc/cmdline ] || mount -t proc none /proc" | add_linuxrc else echo "mount -t proc none /proc" | add_linuxrc fi } # generate code to mount /sys on initrd # can be called multiple times mount_sys() { if is_yes "$sys_mounted"; then return fi sys_mounted=yes echo "mount -t sysfs none /sys" | add_linuxrc } # generate code to mount /tmp on initrd # can be called multiple times mount_tmp() { if [ "$INITRDFS" = "initramfs" ]; then # initramfs is read-write filesystem, no need for tmpfs return fi if is_yes "$tmp_mounted"; then return fi tmp_mounted=yes echo "mount -t tmpfs none /tmp" | add_linuxrc } # generate code to mount /run on initrd # can be called multiple times mount_run() { if is_yes "$run_mounted"; then return fi run_mounted=yes echo "mount -t tmpfs run /run -o mode=0755,noexec,nosuid,nodev" | add_linuxrc } # unmount all mountpoints mounted by geninitrd # try to move pseudo filesystems to newroot if possible umount_all() { add_linuxrc <<-'EOF' : Last shell before umounting all and giving control over to real init. debugshell EOF if is_yes "$run_mounted"; then add_linuxrc <<-EOF mount --bind /run /newroot/run umount /run EOF run_mounted=no fi if is_yes "$dev_mounted"; then add_linuxrc <<-EOF mount --bind /dev /newroot/dev umount /dev EOF dev_mounted=no fi if is_yes "$sys_mounted"; then add_linuxrc <<-EOF mount --bind /sys /newroot/sys umount /sys EOF sys_mounted=no fi if is_yes "$proc_mounted"; then add_linuxrc <<-EOF mount --bind /proc /newroot/proc umount /proc EOF proc_mounted=no fi if is_yes "$tmp_mounted"; then echo 'umount /tmp' | add_linuxrc tmp_mounted=no fi } # Checks if busybox has support for APPLET(s) # Exits from geninitrd if the support is not present. # # NB! XXX do not output to STDOUT, it will appear in initrd images in some cases! busybox_applet() { local err=0 applet if [ -z "$busybox_functions" ]; then local tmp=$($busybox 2>&1) # BusyBox v1.1.3 says applet not found if it's not called 'busybox'. if [[ "$tmp" = *applet\ not\ found* ]]; then local t=$(mktemp -d) ln -s $busybox $t/busybox local tmp=$($t/busybox 2>&1) rm -rf $t fi busybox_functions=$(echo "$tmp" | \ sed -ne '/Currently defined functions:/,$p' | \ xargs | sed -e 's,.*Currently defined functions: ,,' ) fi for applet in $*; do local have # try cache eval have='$'busybox_have_$applet if [ -z "$have" ]; then have=$(echo "$busybox_functions" | grep -Ec "( |^)$applet(,|$)") if [ "$have" = 0 ]; then warn "This setup requires busybox-initrd compiled with applet '$applet' support" err=1 fi eval busybox_have_$applet=$have fi done if [ $err = 1 ]; then die "Aborted" fi } # Extract the .config file from a kernel image # uses extract-ikconfig from kernel sources (scripts/extract-ikconfig) ikconfig() { local kofile=$(modinfo -k $kernel -n configs 2> /dev/null) if [ -n "$kofile" ]; then /lib/geninitrd/extract-ikconfig $kofile return fi # see if config available as separate file if [ -f /boot/config-$kernel ]; then cat /boot/config-$kernel return fi # finally try vmlinuz itself /lib/geninitrd/extract-ikconfig /boot/vmlinuz-$kernel } # @param $module basename_module() { local module=$1 module=${module##*/} module=${module%$modext*} echo $module } # Finds module dependencies # # @param $module # # Outputs full path to module and it's dependencies find_depmod() { local module="$1" local skiperrors=0 # if module is prefixed with dash, we should ignore errors if the module # can't be found. if [ ${module#-} != $module ]; then skiperrors=1 module=${module#-} fi # This works when user has module-init-tools installed even on 2.4 kernels local modprobe modprobe=$(modprobe --set-version $kernel --show-depends $module --ignore-install 2>&1) if [ $? != 0 ]; then if [ $skiperrors = 1 ]; then return 0 fi echo >&2 "$modprobe" if ! is_no "$EXIT_IF_MISSING"; then die "$module: module not found for $kernel kernel" fi warn "$module: module not found for $kernel kernel" warn "If $module isn't compiled in kernel then this initrd may not start your system." fi local smodule echo "$modprobe" | \ while read insmod modpath options; do if [ "$insmod" = "insmod" ]; then # XXX: find a away to autodetect smodule=$(basename_module $modpath) case "$smodule" in btrfs) warn "mounting multidevice btrfs volume requires rootfsflags=device=/dev/...,device=/dev/... kernel option" find_depmod "-libcrc32c" ;; ext4) find_depmod "-libcrc32c" ;; crc-t10dif) find_depmod "-crct10dif-pclmul" find_depmod "-crct10dif" ;; libcrc32c) find_depmod "-crc32c-intel" find_depmod "-crc32c" ;; virtio_blk|virtio_scsi) find_depmod "-virtio_pci" find_depmod "-virtio_mmio" ;; esac echo $modpath fi done return 0 } find_firmware() { local module="$1" # no firmware support in 2.4 kernels if [ "$kernel_version_long" -lt "002005048" ]; then return fi echo -n $(NEW_MODINFO=1 modinfo -k $kernel -F firmware $module 2>/dev/null | xargs) } # @param $module find_module() { local mod depmod module=$1 depmod=$(find_depmod $module) || exit 1 for mod in $depmod; do mod=${mod#/lib/modules/$kernel/} # add each module only once local m have=0 for m in $MODULES; do [ $m = $mod ] && have=1 done if [ $have = 0 ]; then MODULES="$MODULES $mod" fi done } # install a file to temporary mount image. # it will operate recursively (copying directories) # and will symlink destinations if source is symlink. inst() { if [ $# -lt 2 ]; then die 'Usage: inst [] ' fi local src i=0 c=$(($# - 1)) while [ $i -lt $c ]; do src="$src $1" i=$((i + 1)) shift done local dest=$1 set -- $src local parentDir=$(dirname $DESTDIR$dest) if [ ! -d "$parentDir" ]; then verbose "+ mkdir -p DESTDIR${parentDir#$DESTDIR}" mkdir -p $parentDir fi verbose "+ cp $* DESTDIR$dest" cp -HRp "$@" "$DESTDIR$dest" } inst_d() { if [ $# = 0 ]; then die 'Usage: inst_d ' fi local dir for dir in "$@"; do install -d "$DESTDIR$dir" done } # install executable and it's shared libraries inst_exec() { if [ $# -lt 2 ]; then die "Invalid params ($@), Usage: inst_exec [, ] " fi local src i=0 c=$(($# - 1)) while [ $i -lt $c ]; do src="$src $1" i=$((i + 1)) shift done local dest=$1 set -- $src inst "$@" $dest local obj lib libs libs_additional libdir for obj in "$@"; do case "$obj" in /lib/ld-linux.so.2 | /lib64/ld-linux-x86-64.so.2 | /libx32/ld-linux-x32.so.2) continue ;; /lib/libpthread.so* | /lib64/libpthread.so* | /libx32/libpthread.so*) libs_additional="${obj%/libpthread*}/libgcc_s.so.1" ;; esac libs=$(ldd "$obj" | awk '/statically|linux-(gate|vdso)\.so/{next} NF == 2 {print $1} /=/{print $3}' | sort -u) for lib in $libs $libs_additional; do libdir=$(cd $(dirname "$lib"); pwd) if [ ! -f "$DESTDIR/$lib" ]; then inst_d $libdir inst_exec $lib $libdir fi done done # hack for uclibc linked binaries requiring this fixed path # XXX: shouldn't rpath be used here instead so th for _lib in $(get_libdir LIBDIR); do if [ -f $DESTDIR/$_lib/libc.so.0 ]; then lib=$DESTDIR/$_lib/libc.so.0 lib=$(ldd "$lib" | awk '/statically|linux-(gate|vdso)\.so/{next} NF == 2 {print $1} /=/{print $3}' | sort -u) libdir=$(cd $(dirname "$lib"); pwd) if [ ! -e $DESTDIR$libdir ]; then libdir=$(dirname "$libdir") inst_d $libdir verbose "+ ln -s /$_lib $DESTDIR$libdir" ln -s /$_lib $DESTDIR$libdir break fi fi done } # output modules.conf / modprobe.conf modprobe_conf() { echo "$modprobe_conf_cache" } # return options for MODULE # @param $1 module name modprobe_options() { local module=$1 local options=$(modprobe_conf | awk -vmodule="$module" '{ if ($1 == "options" && $2 == module) { for(i=3;i<=NF;i++) printf("%s ",$i); }}') echo ${options# } } # # defaults to modprobe -c if not told otherwise, this means include statements # work from there. cache_modprobe_conf() { if [ "$kernel_version" -lt "002005" ]; then modulefile=/etc/modules.conf if [ ! -f "$modulefile" -a -f /etc/conf.modules ]; then modulefile=/etc/conf.modules fi fi if [ -n "$modulefile" ]; then debug "Using $modulefile for modules config" modprobe_conf_cache=$(cat $modulefile | awk '!/^[\t ]*#/ { print }') else debug "Using modprobe -c to get modules config" modprobe_conf_cache=$(modprobe -c --set-version $kernel | awk '!/^[\t ]*#/ { print }') fi } # find modules for $devpath find_modules_for_devpath() { local devpath="$1" if [ -z "$devpath" ]; then die "No argument passed to find_modules_for_devpath() - is your /etc/fstab correct?" fi if [[ "$devpath" = /dev/dm-* ]]; then # /dev/dm-3 -> /dev/mapper/sil_ahbgadcbchfc3 devpath=$(dm_node "$devpath") fi if [ -L "$devpath" ] && ! is_lvm "$devpath" && ! is_luks "$devpath"; then # sanitize things like: # /dev/block/104:2 -> /dev/cciss/c0d0p2 devpath=$(readlink -f "$devpath") fi verbose "Finding modules for device path $devpath" if is_luks "$devpath"; then find_modules_luks "$devpath" return fi if is_nfs "$devpath"; then find_modules_nfs "$devpath" return fi if is_md "$devpath"; then find_modules_md "$devpath" return fi if is_multipath "$devpath"; then if find_modules_multipath "$devpath"; then return fi # fallback fi if is_dmraid "$devpath"; then if find_modules_dmraid "$devpath"; then return fi # fallback fi if is_scsi "$devpath"; then find_modules_scsi "$devpath" return fi if is_ide "$devpath"; then find_modules_ide "$devpath" return fi if [[ "$devpath" == /dev/bcache* ]]; then find_modules_bcache "$devpath" return fi if [[ "$devpath" == /dev/rd/* ]]; then find_module "DAC960" rootdev_add=/dev/rd/ return fi if [[ "$devpath" == /dev/ida/* ]]; then find_module "cpqarray" rootdev_add=/dev/ida/ return fi if [[ "$devpath" == /dev/cciss/* ]]; then rootdev_add=/dev/cciss/ # load hpsa for future kernels, cciss for backwards compat if [ "$kernel_version_long" -ge "003000000" ]; then find_module "hpsa" "-cciss" find_modules_scsi "$devpath" else find_module "cciss" fi return fi if [[ "$devpath" == /dev/ataraid/* ]]; then find_modules_ide find_module "ataraid" ataraidmodules=$(modprobe_conf | awk '/ataraid_hostadapter/ { print $3 }') if [ -n "$ataraidmodules" ]; then # FIXME: think about modules compiled in kernel die "ataraid_hostadapter alias not defined in modprobe.conf! Please set it and run $PROGRAM again." fi for n in $ataraidmodules; do find_module "$n" done rootdev_add=/dev/ataraid/ return fi # check to see if we need to set up a loopback filesystem if [[ "$devpath" == /dev/loop* ]]; then die "Sorry, root on loop device isn't supported." # TODO: rewrite for bsp and make nfs ready if [ ! -x /sbin/losetup ]; then die "losetup is missing" fi key="^# $(echo $devpath | awk -F/ '{print($3);}' | tr '[a-z]' '[A-Z]'):" if ! is_yes "`awk '/'$key'/ { print( "yes"); }' $fstab`"; then die "The root filesystem is on a $devpath, but there is no magic entry in $fstab for this device. Consult the $PROGRAM man page for more information" fi line="`awk '/'$key'/ { print $0; }' $fstab`" loopDev="$(echo $line | awk '{print $3}')" loopFs="$(echo $line | awk '{print $4}')" loopFile="$(echo $line | awk '{print $5}')" BASICMODULES="$BASICMODULES -loop" find_module "-$loopFs" BASICMODULES="$BASICMODULES -${loopFs}" return fi if is_lvm "$devpath"; then find_modules_lvm "$devpath" return fi } firmware_install_module() { local module="$1" local firmware_files="$2" verbose "Adding Firmwares ($firmware_files) to initrd for module $module" # firmware not yet installed if [ ! -f "$DESTDIR/lib/firmware/firmware.sh" ]; then inst_d /lib/firmware cat << 'EOF' >> "$DESTDIR/lib/firmware/firmware.sh" #!/bin/sh -e # handle only firmware add requests if [ "$SUBSYSTEM" != "firmware" ]; then exit 0 fi if [ "$ACTION" != "add" ]; then exit 0 fi echo 1 > /sys$DEVPATH/loading cat "/lib/firmware/$FIRMWARE" > /sys$DEVPATH/data echo 0 > /sys$DEVPATH/loading exit 0 EOF chmod 755 "$DESTDIR/lib/firmware/firmware.sh" # setup firmware loader agent echo "echo -n "/lib/firmware/firmware.sh" > /proc/sys/kernel/hotplug" | add_linuxrc fi for firmware in $firmware_files; do if [ -f "/lib/firmware/$kernel/$firmware" ]; then FIRMWAREDIR=${firmware%/*} [ "$FIRMWAREDIR" != "$firmware" ] && inst_d /lib/firmware/$FIRMWAREDIR inst /lib/firmware/$kernel/$firmware /lib/firmware/$firmware elif [ -f "/lib/firmware/$firmware" ]; then FIRMWAREDIR=${firmware%/*} [ "$FIRMWAREDIR" != "$firmware" ] && inst_d /lib/firmware/$FIRMWAREDIR inst /lib/firmware/$firmware /lib/firmware/$firmware else warn "Possible missing firmware file /lib/firmware/$firmware or /lib/firmware/$kernel/$firmware for module $module." fi done mount_sys } modules_install() { local modules="$1" local mod for mod in $modules; do MODULEDIR=${mod%/*} inst_d "/lib/modules/$kernel/$MODULEDIR" cp -a "/lib/modules/$kernel/$mod" "$DESTDIR/lib/modules/$kernel/$mod" case $mod in *.gz) gunzip "$DESTDIR/lib/modules/$kernel/$mod" || die "Can't uncompress gz" mod=${mod%.gz} ;; *.xz) xz -d "$DESTDIR/lib/modules/$kernel/$mod" || die "Can't uncompress xz" mod=${mod%.xz} ;; *.bz2) bzip2 -d "$DESTDIR/lib/modules/$kernel/$mod" || die "Can't uncompress bz2" mod=${mod%.bz2} ;; esac if [ "$STRIP" ] && [ -x "$STRIP" ]; then $STRIP -g --remove-section=.comment "$DESTDIR/lib/modules/$kernel/${mod}" fi done } modules_add_linuxrc() { local mod modpath for mod in "$@"; do # module path without optional compression modpath=${mod%.gz} modpath=${modpath%.xz} modpath=${modpath%.bz2} # name of the module local module=${modpath##*/}; module=${module%$modext} local options=$(modprobe_options "$module") local genericname=$(echo $module | tr - _) local usleep=$(eval echo \$MODULE_${genericname}_USLEEP) local firmware=$(eval echo \$MODULE_${genericname}_FIRMWARE) if [ "$module" = "scsi_mod" -a "$kernel_version_long" -ge "002006030" ]; then options="scan=sync $options" fi if [ x"$verbose" = x"-v" ]; then s="" if [ "$options" ]; then s="$s with options [$options]" fi if [ "$usleep" ]; then s="$s and $usleep usleep" fi verbose "Loading module [$module]$s" fi if [ -n "$firmware" ]; then firmware_install_module "$module" "$firmware" else for file in $(find_firmware "$module"); do firmware_install_module "$module" "$file" done fi echo "insmod /lib/modules/$kernel/$modpath $options" | add_linuxrc if [ -n "$usleep" ]; then echo "usleep $usleep" | add_linuxrc fi if [ "$module" = "scsi_wait_scan" ]; then if [ "$(busybox_applet rmmod 2>/dev/null; echo $?)" = 0 ]; then echo "rmmod scsi_wait_scan" | add_linuxrc fi fi done } # Generates /dev nodes based on /proc/partitions information. # Needs /proc mounted. # Can be called multiple times. initrd_gen_devices() { if is_yes "$proc_partitions"; then return fi proc_partitions=yes mount_dev add_linuxrc <<-'EOF' : 'Making device nodes' cat /proc/partitions | ( # ignore first two lines: header, empty line read b; read b while read major minor blocks dev rest; do node=/dev/$dev mkdir -p ${node%/*} [ -e $node ] || mknod -m 660 $node b $major $minor done ) EOF } initrd_gen_setrootdev() { verbose "Adding rootfs finding based on kernel cmdline root= option support." busybox_applet ls debug "Current /proc/partitions:\n$(sed -e 's,^,| ,' /proc/partitions)" add_linuxrc <<-'EOF' if [ "${ROOT##/dev/}" != "${ROOT}" ]; then rootnr="$(busybox awk -v rootnode="${ROOT##/dev/}" '$4 == rootnode { print 256 * $1 + $2 }' /proc/partitions)" # fallback to ls, try two different formats # http://lists.pld-linux.org/mailman/pipermail/pld-devel-en/2014-May/023915.html if [ "${rootnr:-0}" = 0 -a -e "$ROOT" ]; then # busybox up to 1.22 rootnr="$(busybox ls -lL ${ROOT} | busybox awk '{if (/^b/) { print 256 * $3 + $4; }}')" fi if [ "${rootnr:-0}" = 0 -a -e "$ROOT" ]; then # busybox 1.22 and upwards rootnr="$(busybox ls -lL ${ROOT} | busybox awk '{if (/^b/) { print 256 * $5 + $6; }}')" fi if [ "${rootnr:-0}" -gt 0 ]; then echo "$rootnr" > /proc/sys/kernel/real-root-dev fi fi EOF } initrd_gen_initramfs_switchroot() { inst_d /newroot if [ "$rootdev" = "/dev/nfs" ]; then echo "rootfs on NFS root=/dev/nfs" else [ ! -e "$DESTDIR/$rootdev" ] && inst $rootdev $rootdev fi # parse 'root=xxx' kernel commandline # We support passing root as hda3 /dev/hda3 0303 0x0303 and 303 # from lilo-23.2/readme/README: # root= changes the root device. This overrides settings that may # have been made in the boot image and on the LILO command line. is # either the hexadecimal device number or the full path name of the device, # e.g. /dev/hda3 [*] # # * The device names are hard-coded in the kernel. Therefore, only the # "standard" names are supported and some less common devices may not be # recognized. In those cases, only numbers can be used. busybox_applet cat add_linuxrc <<-'EOF' device= eval "$(busybox awk -v root="$ROOT" ' function h2d(str, hstr, res, num, n, digit, i) { # http://9fans.net/archive/2006/09/261 hstr = "0123456789abdcef"; res = 0; n = split(tolower(str), digit, ""); for (i = 1; i <= n; i++) { num = index(hstr, digit[i]) - 1; res = res + (num * 16 ^ (n - i)); } return res; } BEGIN { num_pattern_short = "[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]"; num_pattern = "[0-9a-fA-F]" num_pattern_short; dev_pattern = "[hms][a-z][a-z]([0-9])+"; partition = ""; min = -1; maj = -1; if (root ~ "^\/dev\/" dev_pattern "$" || root ~ "^" dev_pattern "$") { # see if we have /dev/hdX or hdX, we can just take partition name partition = root; sub("^/dev/", "", partition); } else { # unify values first if (root ~ "^" num_pattern_short "$") { # change "303" => "0x0303" root = "0x0" root } else if (root ~ "^" num_pattern "$") { # change "0303" => "0x0303" root = "0x" root } maj = h2d(substr(root, 3, 2)); min = h2d(substr(root, 5, 2)); } } partition && $4 == partition { maj = $1; min = $2; } $1 == maj && $2 == min { partition = $4; } END { if (maj >= 0 && min >= 0) { printf("maj=%s; min=%s;\n", maj, min); } if (partition) { printf("device=/dev/%s;\n", partition); } }' /proc/partitions)" if [ -z "$device" ]; then if [ "$DEBUGINITRD" -a "$DEBUGINITRD" != 'sh' ]; then cat /proc/partitions fi device=$ROOT fi if [ "$device" -a ! -b $device -a "$maj$min" ]; then mknod -m 660 $device b $maj $min fi # XXX hack, fallback to rootdev from geninitrd time if [ ! -e "$device" ]; then EOF add_linuxrc <<-EOF device="$rootdev" EOF add_linuxrc <<-'EOF' echo "DEVICE set to $device based on fstab entry from initrd gen time" fi # XXX hack, if no device, try to parse it from /proc/partitions using /proc/sys/kernel/real-root-dev if [ ! -e "$device" ]; then rrd=$(cat /proc/sys/kernel/real-root-dev) major=$(($rrd / 256)) minor=$(($rrd % 256)) while read pmajor pminor blocks dev rest; do # skip header and empty line [ -z "$pmajor" -o "$pmajor" = "major" ] && continue if [ $pmajor = $major -a $pminor = $minor ]; then device=/dev/$dev echo "DEVICE set to $device based on real-root-dev" fi done < /proc/partitions fi [ -n "$ROOTFLAGS" ] && ROOTFLAGS="-o $ROOTFLAGS" mount -t $ROOTFS -r $device $ROOTFLAGS /newroot || echo "Mount of rootfs failed." init=$INIT if [ -z "$init" -o ! -x "/newroot$init" ]; then init=/sbin/init fi EOF busybox_applet dmesg busybox_applet tail add_linuxrc <<-'EOF' if [ "$DEBUGINITRD" -a "$DEBUGINITRD" != 'sh' ]; then echo "Last 20 lines of dmesg:" dmesg | tail -n 20 fi EOF kmsg "geninitrd/$VERSION switching root" umount_all busybox_applet switch_root usleep add_linuxrc <<-'EOF' [ ! -e /newroot/dev/console ] && mknod -m 660 /newroot/dev/console c 5 1 # switch root to empty dir will make kernel panic, so sleep 10s before it # switch_root needs to be pid 1, so there's no other way to recover from here # if /dev is missing, switch root will likely fail, give debug shell before that if [ ! -d /newroot/dev ]; then echo "/dev is missing, switch_root will likely fail" echo "if you booted with debugrd=sh, then you be given shell and you might able to recover this situation" debugshell [ "$DEBUGINITRD" ] || usleep 10000000 fi # systemd[1]: /usr appears to be on its own filesytem and is not # already mounted. This is not a supported setup. Some things will # probably break (sometimes even silently) in mysterious ways. Consult # http://freedesktop.org/wiki/Software/systemd/separate-usr-is-broken # for more information. echo trying to mount /usr chroot /newroot mount -n /usr exec switch_root /newroot $init ${1:+"$@"} # FIXME: this code is never executed, as "exec" does not return! echo "Error! initramfs should not reach this place." echo "It probably means you've got old version of busybox, with broken" echo "initramfs support. Trying to boot anyway, but won't promise anything." exec chroot /newroot $init ${1:+"$@"} echo "Failed to chroot!" debugshell EOF # we need /init being real file, not symlink, otherwise the initramfs will # not be ran by pid 1 which is required for switch_root mv $DESTDIR/linuxrc $DESTDIR/init ln -s init $DESTDIR/linuxrc } # find if $symbol exists in System.map $mapfile sym_exists() { local mapfile="$1" local symbol="$2" if [ ! -f $mapfile ]; then # missing mapfile (not a pld kernel?) return 1 fi awk -vc=1 -vsymbol="$symbol" '($2 == "T" || $2 == "t") && $3 == symbol {c = 0} END {exit c}' $mapfile } # find best compressor (or forced one) for initrd find_compressor() { local mode="$1" local compressors='zstd xz lzma bzip2 gzip lzo' # a specified one, take it if ! is_yes "$mode"; then compressors="$mode" fi verbose "finding compressor: $compressors (via $mode)" # check for compressor validity local c prog map=/boot/System.map-$kernel for c in $compressors; do case $c in xz) sym=unxz prog=/usr/bin/xz ;; lzma) sym=unlzma prog=/usr/bin/xz ;; bzip2) sym=bzip2 prog=/usr/bin/bzip2 ;; gzip) sym=gunzip prog=/bin/gzip ;; lzo) sym=unlzo prog=/usr/bin/lzop ;; zstd) sym=zstd prog=/usr/bin/zstd ;; none|no) # any existing sym will work sym=initrd_load prog=/bin/cat ;; *) die "Unknown compressor $c" ;; esac if sym_exists $map $sym && [ -x $prog ]; then echo $c return fi done verbose "using gzip for compressor (fallback)" echo gzip } # compresses kernel image image # in function so we could retry with other compressor on failure compress_image() { local compressor="$1" IMAGE="$2" target="$3" tmp tmp=$(mktemp "$target".XXXXXX) || die "mktemp failed" case "$compressor" in xz) # don't use -9 here since kernel won't understand it xz --format=xz --check=crc32 --lzma2=preset=6e,dict=1MiB < "$IMAGE" > "$tmp" || return $? ;; lzma) xz --format=lzma -9 < "$IMAGE" > "$tmp" || return $? ;; bzip2) bzip2 -9 < "$IMAGE" > "$tmp" || return $? ;; gzip) gzip -9 < "$IMAGE" > "$tmp" || return $? ;; lzo) lzop -9 < "$IMAGE" > "$tmp" || return $? ;; zstd) zstd -9 < "$IMAGE" > "$tmp" || return $? ;; none|no) cat < "$IMAGE" > "$tmp" || return $? ;; esac mv -f "$tmp" "$target" } if [ -r /etc/sysconfig/geninitrd ]; then . /etc/sysconfig/geninitrd fi if [ ! -f /proc/mounts ]; then warn "/proc filesystem not mounted, may cause wrong results or failure." fi geninitrd_load_mods ide luks multipath dmraid lvm md blkid udev tuxonice suspend fbsplash condecor bootsplash uvesafb nfs sata scsi usbkbd bcache while [ $# -gt 0 ]; do case $1 in --fstab=*) fstab=${1#--fstab=} ;; --fstab) fstab=$2 shift ;; --modules-conf=*) modulefile=${1#--modules-conf=} ;; --modules-conf) modulefile=$2 shift ;; --with-bootsplash) BOOT_SPLASH=yes ;; --without-bootsplash) BOOT_SPLASH=no ;; --with-fbsplash) FB_SPLASH=yes ;; --without-fbsplash) FB_SPLASH=no ;; --with-fbcondecor) FB_CON_DECOR=yes ;; --without-fbcondecor) FB_CON_DECOR=no ;; --with-suspend) USE_SUSPEND=yes ;; --without-suspend) USE_SUSPEND=no ;; --with-suspend2 | --with-tuxonice) USE_TUXONICE=yes ;; --without-suspend2 | --without-tuxonice) USE_TUXONICE=no ;; --lvmversion=*) LVMTOOLSVERSION=${1#--lvmversion=} ;; --lvmtoolsversion=*) LVMTOOLSVERSION=${1#--lvmtoolsversion=} ;; --lvmtoolsversion|--lvmversion) LVMTOOLSVERSION=$2 shift ;; --without-udev) USE_UDEV=no ;; --with-udev) USE_UDEV=yes ;; --without-dmraid) USE_DMRAID=no ;; --without-multipath) USE_MULTIPATH=no ;; --with-multipath=*) USE_MULTIPATH=${1#--with-multipath=} ;; --without-blkid) USE_BLKID=no ;; --without-luks) USE_LUKS=no ;; --with=*) BASICMODULES="$BASICMODULES ${1#--with=}" ;; --with) BASICMODULES="$BASICMODULES $2" shift ;; --version) echo "$PROGRAM: version $VERSION" exit 0 ;; -v) if [ x"$verbose" = x"-v" ]; then verbose="-v -v" else verbose="-v" fi exec 3>&1 ;; --compress) COMPRESS=$2 ;; --compress=*) COMPRESS="${1#--compress=}" ;; --nocompress) COMPRESS=no ;; --nostrip) STRIP= ;; --strip=*) STRIP="${1#--strip=}" ;; --strip) STRIP=$2 shift ;; --ifneeded) ifneeded=1 ;; -f) force=1 ;; --preload=*) PREMODS="$PREMODS ${1#--preload=}" ;; --preload) PREMODS="$PREMODS $2" shift ;; --fs=* | --fs) die "--fs option is obsoleted. Use --initrdfs instead" ;; --initrdfs=*) INITRDFS=${1#--initrdfs=} ;; --initrdfs) INITRDFS=$2 shift ;; --image-version) img_vers=yes ;; --ide-only-root) ide_only_root="yes" ;; *) if [ -z "$target" ]; then target="$1" elif [ -z "$kernel" ]; then kernel="$1" else usage exit 1 fi ;; esac shift done if [ -z "$target" -o -z "$kernel" ]; then usage exit 1 fi # main() if [ "$(id -u)" != 0 ]; then die "You need to be root to generate initrd" fi for dir in libx32 lib64 lib; do initrd_dir=/usr/$dir/initrd if [ -d "$initrd_dir" ]; then initrd_dirs="$initrd_dirs $initrd_dir" fi done kernel_version=$(echo "$kernel" | awk -F. '{gsub(/[_-].*/, "", $0); print sprintf("%03d%03d",$1,$2)}') kernel_version_long=$(echo "$kernel" | awk -F. '{gsub(/[_-].*/, "", $0); print sprintf("%03d%03d%03d",$1,$2,$3)}') verbose "# geninitrd $VERSION" debug "Using initrd_dir: $initrd_dir" busybox=$(find_tool $initrd_dir/busybox $initrd_dir/initrd-busybox /bin/initrd-busybox) || die "Couldn't find busybox suitable for initrd" # we setup mods after parsing command line args geninitrd_setup_mods if [ ! -f /boot/vmlinuz-"$kernel" ]; then warn "/boot/vmlinuz-$kernel doesn't exist, is your /boot mounted?" fi if [ -z "$INITRDFS" ]; then if [ -n "$FS" ]; then # FS= can came only via /etc/sysconfig/geninitrd likely? die "FS configuration option is obsoleted. Use INITRDFS instead" fi # default value if [ "$kernel_version" -ge "002005" ]; then INITRDFS="initramfs" else INITRDFS="rom" fi fi check_initrd_fs() { local s sfound sym p prog map=/boot/System.map-$kernel case "$INITRDFS" in ext2) # TODO: symbols to check in case of ext2 used via ext3/4 subsystem sym=init_ext2_fs prog=/sbin/mke2fs ;; rom|romfs) sym=init_romfs_fs prog=/sbin/genromfs ;; cram|cramfs) sym=init_cramfs_fs prog=/sbin/mkcramfs ;; initramfs) sym=__initramfs_start prog="/bin/cpio /usr/bin/find" ;; *) die "Filesystem $INITRDFS on initrd is not supported by geninitrd" ;; esac # only one is needed (for cases like ext2 via ext2 or via ext3 or via ext4 subsysytem) sfound=0 for s in $sym; do sym_exists $map $s && sfound=1 break done if [ "$sfound" -eq "0" ]; then die "Filesystem $INITRDFS on initrd is not supported by kernel" fi for p in $prog; do [ ! -x "$p" ] && die "$prog is missing" done } check_initrd_fs if [ -L "$target" ]; then target=$(readlink -f "$target") fi if [ -n "$img_vers" ]; then target="$target-$kernel" fi if [ -z "$force" -a -f "$target" ]; then die "$target already exists." fi if [ ! -d "/lib/modules/$kernel" ]; then die "/lib/modules/$kernel is not a directory." fi if [ "$kernel_version" -ge "002005" ]; then modext=".ko" fi cache_modprobe_conf for n in $PREMODS; do find_module "$n" done if [ "$FBMODULE" ]; then find_module "$FBMODULE" fi # autodetect USB keyboards find_modules_usbkbd # allow forcing loading SCSI and/or IDE modules # XXX: where ADDSCSI cames from? drop? if is_yes "$ADDSCSI"; then find_modules_scsi fi # autodetect SATA modules find_modules_sata # XXX: where ADDIDE cames from? drop? if is_yes "$ADDIDE"; then find_modules_ide fi if is_yes "$USE_SUSPEND"; then find_modules_suspend fi find_root "$fstab" || exit verbose "Using $rootdev as device for rootfs" find_modules_for_devpath "$rootdev" # if USE_MULTIPATH is path to device, scan that too # this is to bootstrap multipath setup into initrd. if ! is_no "$USE_MULTIPATH" && ! is_yes "$USE_MULTIPATH"; then find_modules_multipath $USE_MULTIPATH fi find_module "-$rootFs" for n in $BASICMODULES; do find_module "$n" done if is_yes "$USE_TUXONICE"; then find_module "-lzf" fi find_modules_uvesafb find_modules_fbsplash if [ -n "$ifneeded" -a -z "$MODULES" ]; then verbose "No modules are needed -- not building initrd image." exit 0 fi verbose "Building initrd..." DESTDIR=$(mktemp -d -t initrd.XXXXXX) || die "mktemp failed" RCFILE="$DESTDIR/linuxrc" > "$RCFILE" chmod a+rx "$RCFILE" ln -s linuxrc $DESTDIR/init # create dirs that we really need inst_d /{lib,bin,sbin,etc,dev{,/pts,/shm},loopfs,var,proc,run,sys,tmp} modules_install "$MODULES" # mknod'ing the devices instead of copying them works both with and # without devfs... mknod -m 600 "$DESTDIR/dev/console" c 5 1 mknod -m 666 "$DESTDIR/dev/null" c 1 3 mknod -m 666 "$DESTDIR/dev/zero" c 1 5 mknod -m 666 "$DESTDIR/dev/random" c 1 8 mknod -m 666 "$DESTDIR/dev/urandom" c 1 9 mknod -m 644 "$DESTDIR/dev/kmsg" c 1 11 inst_exec $busybox /bin/busybox ln -s busybox $DESTDIR/bin/sh # for older busyboxes who had /bin/initrd-busybox as EXEPATH ln -s busybox $DESTDIR/bin/initrd-busybox add_linuxrc <> "$RCFILE" # echo "mount -t $loopFs $loopDev /loopfs" >> "$RCFILE" # echo "echo Setting up loopback device $rootdev" >> $RCFILE # echo "losetup $rootdev /loopfs$loopFile" >> "$RCFILE" #fi if is_yes "$USE_UDEV"; then initrd_gen_udev else initrd_gen_mdev fi initrd_gen_uvesafb initrd_gen_luks initrd_gen_dmraid initrd_gen_multipath initrd_gen_blkid if is_yes "$have_nfs"; then initrd_gen_nfs else initrd_gen_md initrd_gen_lvm initrd_gen_bcache initrd_gen_blkid initrd_gen_luks initrd_gen_setrootdev fi # additional devs always needed [ ! -e "$DESTDIR/$rootdev_add" ] && inst $rootdev_add /dev initrd_gen_stop_udevd initrd_gen_stop_mdev initrd_gen_stop_uvesafb # resume after killing local processes initrd_gen_tuxonice initrd_gen_suspend # clean up env add_linuxrc <<-'EOF' if [ ! "$DEBUGINITRD" ]; then ifs=$IFS IFS=" " for i in $(export -p); do i=${i#declare -x } # ksh/bash i=${i#export } # busybox case "$i" in *=*) : ;; *) continue ;; esac i=${i%%=*} [ -z "$i" ] && continue case "$i" in ROOT|PATH|HOME|TERM) : ;; *) unset $i ;; esac done IFS=$ifs fi EOF if [ "$INITRDFS" = "initramfs" ]; then initrd_gen_initramfs_switchroot else umount_all fi initrd_gen_fbsplash initrd_gen_fbcondecor debug "Current /linuxrc:\n$(sed -e 's,^,| ,' $DESTDIR/linuxrc)" IMAGE=$(mktemp -t initrd.img-XXXXXX) || die "mktemp failed" IMAGESIZE=$(du -ks $DESTDIR | awk '{print int(($1+1023+512)/1024)*1024}') verbose "image size: $IMAGESIZE KiB ($DESTDIR)" verbose "Creating $INITRDFS image $IMAGE" case "$INITRDFS" in ext2) dd if=/dev/zero of="$IMAGE" bs=1k count="$IMAGESIZE" 2> /dev/null # NOTE: ext2 label is max 16 chars mke2fs -q -F -b 1024 -m 0 -L "PLD/$kernel" "$IMAGE" 2>/dev/null 1>&2 tune2fs -i 0 "$IMAGE" >/dev/null 2>&1 local tmpmnt=$(mktemp -d -t initrd.mnt-XXXXXX) debug "Mounting ext2 image $IMAGE to $tmpmnt" mount -o loop -t ext2 "$IMAGE" "$tmpmnt" || die "mount failed, check dmesg(1)" # We don't need this directory, so let's save space rm -rf "$tmpmnt"/lost+found debug "Copy recursively $DESTDIR -> $tmpmnt" cp -a $DESTDIR/* $tmpmnt umount "$IMAGE" rmdir "$tmpmnt" ;; rom|romfs) genromfs -f "$IMAGE" -d "$DESTDIR" -V "PLD Linux/$kernel (geninitrd/$VERSION)" ;; cram|cramfs) mkcramfs "$DESTDIR" "$IMAGE" ;; initramfs) (cd $DESTDIR; find . | cpio --quiet -H newc -o > "$IMAGE") ;; *) die "Filesystem $INITRDFS not supported by $PROGRAM" esac if [ "$INITRDFS" != "initramfs" ]; then CONFIG_BLK_DEV_RAM_SIZE=$(ikconfig | awk -F= '/^CONFIG_BLK_DEV_RAM_SIZE/{print $2}') if [ -z "$CONFIG_BLK_DEV_RAM_SIZE" ]; then CONFIG_BLK_DEV_RAM_SIZE=4096 warn "No CONFIG_BLK_DEV_RAM_SIZE detected, fallback to $CONFIG_BLK_DEV_RAM_SIZE" fi if [ "$IMAGESIZE" -gt $CONFIG_BLK_DEV_RAM_SIZE ]; then warn "Your image size is larger than $CONFIG_BLK_DEV_RAM_SIZE, Be sure to boot kernel with ramdisk_size=$IMAGESIZE!" fi fi if ! is_no "$COMPRESS"; then compressor=$(find_compressor "$COMPRESS") verbose "Compressing $target with $compressor" # TODO: the image name (specified from kernel.spec) already contains # extension, which is .gz most of the time. compress_image "$compressor" "$IMAGE" "$target" || { if [ $compressor != gzip ]; then warn "Could not compress with $compressor, retrying with gzip" compress_image gzip "$IMAGE" "$target" || die "compress failed with gzip" $? else die "Could not compress image with $compressor" fi } else cp -a "$IMAGE" "$target" fi # XXX. check if bootsplash can output data to tmp dir not directly to initramfs image. initrd_gen_bootsplash "$target" rm -rf "$DESTDIR" "$IMAGE" # vim:ts=4:sw=4:noet:fdm=marker