tinyramfs/tinyramfs
2020-03-08 03:28:00 +03:00

484 lines
11 KiB
Bash
Executable File

#!/bin/sh
#
# tiny initramfs generation tool
msg() {
# print message
case "$1" in
info)
printf "info >> %s\n" "$2" >&2
;;
warn)
printf "warning >> %s\n" "$2" \
"are you sure you want to continue?" \
"press enter to continue or ctrl+c to exit" >&2
read -r _
;;
panic)
printf "panic >> %s\n" "$2" >&2
exit 1
;;
esac
}
usage() {
# TODO more options
cat << EOF
usage: $0 [options]
-o, --output <file> set initramfs image name
-c, --config <file> set config file path
-m, --moddir <dir> set drivers directory
-k, --kernel <ver> set kernel version
-F, --files <dir> set files directory
-d, --debug enable debug mode
-f, --force overwrite existing initramfs image
EOF
}
parse_args() {
while [ "$1" ]; do
case "$1" in
-o | --output)
_initramfs="${2:?}"
shift 2
;;
-c | --config)
_config="${2:?}"
shift 2
;;
-m | --moddir)
_moddir="${2:?}"
shift 2
;;
-k | --kernel)
_kernel="${2:?}"
shift 2
;;
-F | --files)
_filesdir="${2:?}"
shift 2
;;
-d | --debug)
_debug=1
shift 1
;;
-f | --force)
_force=1
shift 1
;;
-h | --help)
usage
exit 0
;;
*)
printf "%s\n\n" "invalid option: '$1'"
usage
exit 1
;;
esac
done
}
parse_conf() {
. "${_config:-./config}" ||
msg panic "failed to parse config"
kernel="${_kernel:-${kernel:-$(uname -r)}}"
moddir="${_moddir:-${moddir:-/lib/modules}}"
filesdir="${_filesdir:-./usr/share/tinyramfs}"
debug="${_debug:-${debug:-0}}"
force="${_force:-${force:-0}}"
initramfs="${_initramfs:-${initramfs:-./initramfs-${kernel}}}"
modker="${moddir}/${kernel}"
}
create_workdir() {
msg info "creating working directory"
workdir="${XDG_CACHE_HOME:-${TMPDIR:-/tmp}}/initramfs.$$"
mkdir -p "$workdir" ||
msg panic "failed to create working directory"
}
remove_workdir() {
msg info "removing working directory"
rm -rf "$workdir"
}
install_requirements() {
msg info "installing requirements"
# install user specified binaries
[ "$binaries" ] &&
for _binary in $binaries; do
install_binary "$_binary"
done
# install util-linux binaries
[ "$util_linux" = 1 ] &&
for _binary in mount blkid; do
install_binary "$_binary"
done
# install mandatory binaries
for _binary in busybox modprobe; do
install_binary "$_binary"
done
}
create_structure() {
msg info "creating directory structure"
for _dir in dev tmp var run etc usr/lib usr/bin mnt/root proc root sys; do
mkdir -p "${workdir}/${_dir}"
done
}
create_symlinks() {
msg info "creating symlinks"
ln -s usr/lib "${workdir}/lib"
ln -s usr/lib "${workdir}/lib64"
ln -s usr/bin "${workdir}/bin"
ln -s usr/bin "${workdir}/sbin"
ln -s ../run "${workdir}/var/run"
ln -s bin "${workdir}/usr/sbin"
ln -s lib "${workdir}/usr/lib64"
}
install_devmgr() {
msg info "installing device manager"
case "$devmgr" in
udev)
for _binary in udevd udevadm dmsetup; do
install_binary "$_binary"
done
find /usr/lib/udev \
! -path "*rc_keymaps*" \
! -path "*hwdb.d*" \
-type f |
cpio -pd "$workdir" > /dev/null 2>&1
;;
mdev)
install -m644 "${filesdir}/mdev.conf" "${workdir}/etc/mdev.conf"
install -Dm755 "${filesdir}/storage-device" "${workdir}/lib/mdev/storage-device"
;;
mdevd)
for _binary in mdevd mdevd-coldplug; do
install_binary "$_binary"
done
install -m644 "${filesdir}/mdev.conf" "${workdir}/etc/mdev.conf"
install -Dm755 "${filesdir}/storage-device" "${workdir}/lib/mdev/storage-device"
;;
esac
}
install_lvm() {
msg info "installing LVM"
install_binary lvm
[ "$hostonly" = 1 ] &&
for _driver in dm-thin-pool dm-multipath dm-snapshot dm-cache dm-log dm-mirror; do
install_driver "$_driver"
done
# install lvm config
if [ "$lvm_config" = 1 ]; then
mkdir -p "${workdir}/etc/lvm"
cp /etc/lvm/*.conf "${workdir}/etc/lvm"
else
mkdir -p "${workdir}/etc/lvm"
cat << EOF > "${workdir}/etc/lvm/lvm.conf"
devices {
# block discard support
issue_discards = ${lvm_discard:-0}
}
global {
# disable lvmetad
use_lvmetad = 0
}
EOF
fi
}
install_luks() {
msg info "installing LUKS"
install_binary cryptsetup
[ "$hostonly" = 1 ] &&
for _driver in aes dm-crypt sha256 sha512 wp512 ecb lrw xts twofish serpent; do
install_driver "$_driver"
done
# avoid "locking directory missing" warning
# message and libgcc_s.so.1 missing error
# see https://bugs.archlinux.org/task/56771
mkdir -p "${workdir}/run/cryptsetup"
[ -e /usr/lib/libgcc_s.so.1 ] &&
install_library /usr/lib/libgcc_s.so.1
# copy luks header
[ -f "$luks_header" ] && {
install -m400 "$luks_header" "${workdir}/root/luks_header"
luks_args="--header=/root/luks_header $luks_args"
}
# copy luks keyfile
[ -f "$luks_keyfile" ] && {
install -m400 "$luks_keyfile" "${workdir}/root/luks_keyfile"
luks_args="--key-file=/root/luks_keyfile $luks_args"
}
}
install_driver() {
driver="$1"
modprobe -S "$kernel" -D "$driver" 2> /dev/null |
while read -r driver; do
# strip unneeded stuff
driver="${driver##*builtin*}"
driver="${driver##*net*}"
driver="${driver#insmod }"
# exclude user specified drivers
[ "$drivers_exclude" ] &&
for _exclude_driver in $drivers_exclude; do
driver="${driver##*${_exclude_driver}*}"
done
# check empty
[ "$driver" ] || continue
# check if driver already installed
[ -e "${workdir}${driver}" ] ||
install -Dm644 "$driver" "${workdir}${driver}"
done
}
install_hostonly_drivers() {
msg info "installing hostonly drivers"
# perform autodetection of drivers via /sys
find /sys -name modalias -exec sort -u {} + |
while read -r _driver; do
install_driver "$_driver"
done
# install user specified drivers
[ "$drivers" ] &&
for _driver in $drivers; do
install_driver "$_driver"
done
}
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 2> /dev/null |
while read -r _driver; do
# strip path and extension
_driver="${_driver##*/}"
_driver="${_driver%%.*}"
install_driver "$_driver"
done
}
generate_depmod() {
msg info "generating drivers list"
cp "${modker}/modules.builtin" \
"${modker}/modules.order" \
"${workdir}${modker}"
depmod -b "$workdir" "$kernel"
}
install_binary() {
binary=$(command -v "$1")
workdirbin="${workdir}/usr/bin/"
# check if binary exists
[ "$binary" ] || msg panic "$binary doesn't exists"
# check if binary already installed
[ -e "${workdirbin}${binary##*/}" ] && return
# install and strip binary
install -s -m755 "$binary" "${workdirbin}${binary##*/}"
# check static
ldd "$binary" > /dev/null 2>&1 || return
# exract paths to libraries
ldd "$binary" |
while read -r _library; do
# strip unneeded stuff
_library="${_library##*vdso*}"
_library="${_library#* => }"
_library="${_library% *}"
# check empty
[ "$_library" ] || continue
install_library "$_library"
done
}
install_library() {
library="$1"
reallib="/usr/lib/$(readlink "$library")"
fulllib=$(readlink -f "$library")
workdirlib="${workdir}/usr/lib/"
# check if library already installed
[ -e "${workdirlib}${fulllib##*/}" ] &&
[ -e "${workdirlib}${library##*/}" ] &&
return
# install symlinks if any
[ -h "$library" ] && {
# symlink may link to symlink
[ -h "$reallib" ] &&
cp -a "$reallib" "$workdirlib"
cp -a "$library" "$workdirlib"
}
# install library
install -s -m755 "$fulllib" "${workdirlib}${fulllib##*/}"
}
install_files() {
msg info "installing files"
cat << EOF > "${workdir}/config"
debug="$debug"
init="$init"
root="$root"
root_type="$root_type"
root_opts="$root_opts"
devmgr="$devmgr"
#drivers
lvm="$lvm"
lvm_name="$lvm_name"
lvm_group="$lvm_group"
#lvm_discard
lvm_args="$lvm_args"
luks="$luks"
luks_root="$luks_root"
luks_name="$luks_name"
#luks_header
#luks_keyfile
luks_discard="$luks_discard"
luks_args="$luks_args"
EOF
install -m644 "${filesdir}/passwd" "${workdir}/etc/passwd"
install -m644 "${filesdir}/group" "${workdir}/etc/group"
install -m755 "${filesdir}/init" "${workdir}/init"
}
create_initramfs() {
msg info "creating initramfs image"
# TODO add uncompressed option
# check if image already exist
[ "$force" = 0 ] && [ -e "$initramfs" ] &&
msg warn "looks like you already have initramfs image"
(
cd "$workdir"
find . | cpio -oH newc | ${compress:-gzip -9}
) > "$initramfs" 2> /dev/null ||
msg panic "failed to generate initramfs image"
}
# check root
[ "$(id -u)" = 0 ] || msg panic "must be run as root"
parse_args "$@"
parse_conf
# remove workdir on signals
# we are doing unset EXIT signal to avoid endless loop
# because afterwards we execute 'exit' command.
# also some shells (dash,mksh,etc) doesn't exit on INT
# signal. as workaround we manually execute exit command.
# tested bash,dash,mksh,busybox sh
# TODO fix zsh, ref https://www.zsh.org/mla/users/2015/msg00436.html
trap "remove_workdir && trap - EXIT && exit" EXIT INT TERM HUP
[ "$debug" = 1 ] && {
# debug shell commands
set -x
# don't remove anything
trap - EXIT INT TERM HUP
}
create_workdir
create_structure
create_symlinks
[ "$lvm" = 1 ] && install_lvm
[ "$luks" = 1 ] && install_luks
# check if moddir exists
if [ -d "$moddir" ]; then
# check hostonly mode
if [ "$hostonly" = 1 ]; then
install_hostonly_drivers
else
install_all_drivers
fi
generate_depmod
else
msg warn "looks like you have monolithic kernel"
fi
install_devmgr
install_files
install_requirements
create_initramfs
msg info "done! check out $initramfs"