#!/bin/sh # # tiny initramfs generation tool # print message msg() { case "$1" in info) printf "info >> %s\n" "$2" >&2 ;; panic) printf "panic >> %s\n" "$2" >&2 exit 1 ;; esac } # remove tmpdir remove_tmpdir() { msg info "removing work dir" rm -rf "$tmpdir" } # change current directory to script directory if user haven't do it check_currentdir() { script_dir=$(dirname $(readlink -f -- "$0")) [ "$PWD" = "$script_dir" ] || { msg info "changing directory to script dir" cd "$script_dir" || msg panic "failed to change directory" } } # check needed files check_requirements() { msg info "checking requirements" # TODO use system busybox for f in ./init ./busybox; do [ -e "$f" ] || msg panic "$f doesn't exists" done # TODO handle busybox requirements ( busybox --list | grep ash ) } # create FHS directory structure create_structure() { msg info "creating directory structure" for d in dev tmp var run etc usr/lib usr/bin mnt/root proc root sys; do mkdir -p "${tmpdir}/${d}" done } # some dynamically linked libraries and binaries compiled with hardcoded # dependencies path. to make it worked we need create symlinks for them. # also POSIX ln doesn't have --relative flag like in GNU ln. as workaround # we change directory to tmpdir and make needed symlinks. create_symlinks() { msg info "creating symlinks" ( cd "$tmpdir" && { ln -s usr/lib lib ln -s usr/lib lib64 ln -s usr/bin bin ln -s usr/bin sbin ln -s ../run var/run cd "${tmpdir}/usr" ln -s bin sbin ln -s lib lib64 } ) } #parse_fstab() { # TODO parse fstab #while [ "$use_fstab" -eq 1 ] && read fs dir type opts; do thing; done < /etc/fstab #} #parse_crypttab() { # TODO parse crypttab #} # install mdev install_mdev() { msg info "installing mdev" install -m644 mdev.conf -t "${tmpdir}/etc" install -Dm755 storage-device -t "${tmpdir}/lib/mdev" } # install mdevd install_mdevd() { msg info "installing mdevd" install_binaries mdevd mdevd-coldplug install -m644 mdev.conf -t "${tmpdir}/etc" install -Dm755 storage-device -t "${tmpdir}/lib/mdev" } # install udev install_udev() { msg info "installing udev" install_binaries udevd udevadm dmsetup # FIXME rewrite this piece of crap find /usr/lib/udev -type f | grep -v "rc_keymaps\|hwdb.d" | cpio -pd "$tmpdir" >/dev/null 2>&1 } # handle lvm install_lvm() { msg info "installing LVM" install_binaries lvm # if hostonly mode enabled install only needed drivers [ "$hostonly" = 1 ] && { for lvm_driver in dm-thin-pool dm-multipath dm-snapshot dm-cache dm-log dm-mirror; do for lvm_driver_dep in $(modprobe -D "$lvm_driver" 2>/dev/null | grep -v builtin | cut -d " " -f 2); do install -Dm644 "$lvm_driver_dep" "${tmpdir}${lvm_driver_dep}" done done } if [ "$lvm_conf" = 1 ]; then install -Dm644 /etc/lvm/*.conf -t "${tmpdir}/etc/lvm" || msg panic "failed to install LVM config" else mkdir "${tmpdir}/etc/lvm" cat < "${tmpdir}/etc/lvm/lvmlocal.conf" local { issue_discards = ${lvm_discard:-0} use_lvmetad = 0 } EOF fi } # handle luks install_luks() { msg info "installing LUKS" install_binaries cryptsetup # if hostonly mode enabled install only needed drivers [ "$hostonly" = 1 ] && { for luks_driver in aes dm-crypt sha256 sha512 wp512 ecb lrw xts twofish serpent; do for luks_driver_dep in $(modprobe -D "$luks_driver" 2>/dev/null | grep -v builtin | cut -d " " -f 2); do install -Dm644 "$luks_driver_dep" "${tmpdir}${luks_driver_dep}" done done } # avoid locking directory missing warning message mkdir "${tmpdir}/run/cryptsetup" # TODO get rid of this workaround # workaround for luks2 install -s -m755 /usr/lib/libgcc_s.so.1 -t "${tmpdir}/usr/lib" || msg panic "failed to install LUKS libraries" # block discard support [ "$luks_discard" = 1 ] && luks_args="--allow-discards $luks_args" # copy luks header [ -f "$luks_header" ] && { install -m400 "$luks_header" "${tmpdir}/root/luks_header" || msg panic "failed to copy LUKS header" luks_args="--header=/root/luks_header $luks_args" } # copy luks keyfile [ -f "$luks_keyfile" ] && { install -m400 "$luks_keyfile" "${tmpdir}/root/luks_keyfile" || msg panic "failed to copy LUKS keyfile" luks_args="--key-file=/root/luks_keyfile $luks_args" } } # install drivers install_drivers() { msg info "installing hostonly drivers" [ -n "$root_type" ] || msg panic "hostonly mode required root_type option to be configured" # perform autodetection of drivers via /sys for driver in $(find /sys/devices -name modalias -exec sort -u "{}" "+"); do for driver_dep in $(modprobe -D "$driver" 2>/dev/null | grep -v builtin | cut -d " " -f 2); do install -Dm644 "$driver_dep" "${tmpdir}${driver_dep}" done done # TODO autodetect root fs driver # TODO separate root type option # install root fs driver for root_driver in $(modprobe -D "$root_type" 2>/dev/null | grep -v builtin | cut -d " " -f 2); do install -Dm644 "$root_driver" "${tmpdir}${root_driver}" done # install user specified drivers [ -n "$drivers" ] && { for custom_driver in $(printf "%s\n" "$drivers" | tr " " "\n"); do for custom_driver_dep in $(modprobe -D "$custom_driver" 2>/dev/null | grep -v builtin | cut -d " " -f 2); do install -Dm644 "$custom_driver_dep" "${tmpdir}${custom_driver_dep}" done done } } # find and install all drivers install_all_drivers() { msg info "installing all drivers" find \ "${modker}/kernel/arch" \ "${modker}/kernel/crypto" \ "${modker}/kernel/fs" \ "${modker}/kernel/lib" \ "${modker}/kernel/drivers/block" \ "${modker}/kernel/drivers/ata" \ "${modker}/kernel/drivers/md" \ "${modker}/kernel/drivers/scsi" \ "${modker}/kernel/drivers/usb/storage" \ "${modker}/kernel/drivers/usb/host" \ "${modker}/kernel/drivers/virtio" \ -type f | cpio -pd "$tmpdir" >/dev/null 2>&1 } # generate "modules" files generate_depmod() { msg info "generating dependendies list of drivers" # install list of drivers cp "${modker}/modules.softdep" "${modker}/modules.builtin" "${modker}/modules.order" "${tmpdir}/${modker}" # generate dependencies list of drivers depmod -b "$tmpdir" "$kernel" } # TODO make strip optional # handle binaries install_binaries() { for b in $(printf "%s\n" "$@" | tr " " "\n"); do msg info "installing binary $b" # check binary existence command -v "$b" >/dev/null 2>&1 || msg panic "$b doesn't exists" # install and strip binary install -s -m755 "$(command -v $b)" -t "${tmpdir}/usr/bin" # check statically linking ldd "$(command -v $b)" >/dev/null 2>&1 || continue # install libraries install_libraries $b done } # TODO make strip optional # handle libraries install_libraries() { for l in $(ldd "$(command -v $1)" | sed -nre 's,.* (/.*lib.*/.*.so.*) .*,\1,p' -e 's,.*(/lib.*/ld.*.so.*) .*,\1,p'); do msg info "installing library $l" # check symlink if [ -h "$l" ]; then # check lib already existence if [ ! -e "${tmpdir}/usr/lib/${l##*/}" ] && [ ! -e "${tmpdir}/$(readlink -f $l)" ]; then # regular install -s -m755 "$(readlink -f $l)" -t "${tmpdir}/usr/lib" # FIXME handle all symlinks # symlink may link to symlink [ -h "/usr/lib/$(readlink $l)" ] && cp -a "/usr/lib/$(readlink $l)" "${tmpdir}/usr/lib" # symlink cp -a "$l" "${tmpdir}/usr/lib" fi else if [ ! -e "${tmpdir}/usr/lib/${l##*/}" ]; then install -s -m755 "$l" -t "${tmpdir}/usr/lib" fi fi done } # install important files used by init install_files() { msg info "installing files" # FIXME eof broken # initialize config cat < "${tmpdir}/config" debug="$debug" root="$root" root_type="$root_type" root_args="$root_args" devmgr="$devmgr" #drivers="$drivers" use_lvm="$use_lvm" use_luks="$use_luks" luks_root="$luks_root" luks_args="$luks_args" EOF # needed for devmgr cat < "${tmpdir}/etc/group" root:x:0: tty:x:5: dialout:x:11: uucp:x:14: kmem:x:3: input:x:25: video:x:13: audio:x:12: lp:x:10: disk:x:9: cdrom:x:16: tape:x:6: kvm:x:24: floppy:x:8: EOF # needed for devmgr cat < "${tmpdir}/etc/passwd" root:x:0:0::/root:/bin/sh nobody:x:99:99::/:/bin/false EOF # install init script install -m755 ./init -t "$tmpdir" } # TODO add more compession tools # create and compress cpio archive create_initramfs() { msg info "creating initramfs image" { ( cd "$tmpdir" && { find . | cpio -oH newc | gzip -9 } ) > "${script_dir}/initramfs-${kernel}.img.gz" } >/dev/null 2>&1 [ "$?" = 0 ] || msg panic "failed to generate initramfs image" } # check root [ "$(id -u)" = 0 ] || msg panic "must be run as root" # remove tmpdir on exit or unexpected error trap remove_tmpdir EXIT INT check_currentdir # source config . ./config || msg panic "./config doesn't exists" # handle debug mode [ "$debug" = 1 ] && { # debug shell commands set -x # don't remove anything trap : EXIT INT } # variables tmpdir="$(mktemp -d /tmp/initramfs.XXXXXXXX)" || msg panic "failed to create working directory" kernel="$(uname -r)" moddir="/lib/modules/" modker="${moddir}${kernel}" check_requirements create_structure create_symlinks #parse_fstab #parse_crypttab install_binaries $binaries if [ "$hostonly" = 1 ]; then install_drivers else install_all_drivers fi generate_depmod # handle device manager case "$devmgr" in mdev) install_mdev ;; mdevd) install_mdevd ;; udev) install_udev ;; *) msg panic "devmgr option broken" ;; esac [ "$use_luks" = 1 ] && install_luks [ "$use_lvm" = 1 ] && install_lvm install_files create_initramfs msg info "done! check out initramfs-${kernel}.img.gz"