diff --git a/update-initramfs b/update-initramfs new file mode 100644 index 0000000..8a0f597 --- /dev/null +++ b/update-initramfs @@ -0,0 +1,398 @@ +#!/bin/sh + +BOOTDIR=/boot +CONF=/etc/initramfs-tools/update-initramfs.conf +mode="" +version="" +update_initramfs=yes +backup_initramfs=no + +set -e + +[ -r ${CONF} ] && . ${CONF} + +if [ -n "$DPKG_MAINTSCRIPT_PACKAGE" ] && [ $# = 1 ] && [ "$1" = -u ]; then + if dpkg-trigger --no-await update-initramfs; then + echo "update-initramfs: deferring update (trigger activated)" + exit 0 + fi +fi + +usage() +{ + cat << EOF + +Usage: update-initramfs {-c|-d|-u} [-k version] [-v] [-b directory] + +Options: + -k version Specify kernel version or 'all' + -c Create a new initramfs + -u Update an existing initramfs + -d Remove an existing initramfs + -b directory Set alternate boot directory + -v Be verbose + +See update-initramfs(8) for further details. + +EOF +} + +usage_error() +{ + if [ -n "${1:-}" ]; then + printf "%s\\n\\n" "${*}" >&2 + fi + usage >&2 + exit 2 +} + +mild_panic() +{ + if [ -n "${1:-}" ]; then + printf "%s\\n" "${*}" >&2 + fi + exit 0 +} + +panic() +{ + if [ -n "${1:-}" ]; then + printf "%s\\n" "${*}" >&2 + fi + exit 1 +} + +verbose() +{ + if [ "${verbose}" = 1 ]; then + printf "%s\\n" "${*}" + fi +} + +set_initramfs() +{ + initramfs="${BOOTDIR}/booster.img-${version}" +} + + +# backup initramfs while running +backup_initramfs() +{ + [ ! -r "${initramfs}" ] && return 0 + initramfs_bak="${initramfs}.dpkg-bak" + [ -r "${initramfs_bak}" ] && rm -f "${initramfs_bak}" + ln -f "${initramfs}" "${initramfs_bak}" \ + || cp -a "${initramfs}" "${initramfs_bak}" + verbose "Keeping ${initramfs_bak}" +} + +# keep booted initramfs +backup_booted_initramfs() +{ + initramfs_bak="${initramfs}.dpkg-bak" + + # first time run thus no backup + [ ! -r "${initramfs_bak}" ] && return 0 + + # chroot with no /proc + [ ! -r /proc/uptime ] && rm -f "${initramfs_bak}" && return 0 + + # no kept backup wanted + [ "${backup_initramfs}" = "no" ] && rm -f "${initramfs_bak}" && return 0 + + # no backup yet + if [ ! -r "${initramfs}.bak" ]; then + mv -f "${initramfs_bak}" "${initramfs}.bak" + verbose "Backup ${initramfs}.bak" + return 0 + fi + + # keep booted initramfs + boot_initramfs= + uptime_days=$(awk '{printf "%d", $1 / 3600 / 24}' /proc/uptime) + if [ -n "$uptime_days" ]; then + boot_initramfs=$(find "${initramfs}.bak" -mtime "+${uptime_days}") + fi + if [ -n "${boot_initramfs}" ]; then + mv -f "${initramfs_bak}" "${initramfs}.bak" + verbose "Backup ${initramfs}.bak" + return 0 + fi + verbose "Removing current backup ${initramfs_bak}" + rm -f "${initramfs_bak}" +} + +# nuke generated copy +remove_initramfs_bak() +{ + [ -z "${initramfs_bak:-}" ] && return 0 + rm -f "${initramfs_bak}" + verbose "Removing ${initramfs_bak}" +} + + +generate_initramfs() +{ + echo "update-initramfs: Generating ${initramfs}" + OPTS="-o" + if [ "${verbose}" = 1 ]; then + OPTS="-v ${OPTS}" + fi + # shellcheck disable=SC2086 + if booster build --force --kernel-version "${version}" "${initramfs}.new" ; then + mv -f "${initramfs}.new" "${initramfs}" + # Guard against an unclean shutdown + sync -f "${initramfs}" + else + mkinitramfs_return="$?" + remove_initramfs_bak + rm -f "${initramfs}.new" + echo "update-initramfs: failed for ${initramfs} with $mkinitramfs_return." >&2 + exit $mkinitramfs_return + fi +} + +# Invoke bootloader +run_bootloader() +{ + # invoke policy conformant bootloader hooks + if [ -d /etc/initramfs/post-update.d/ ]; then + run-parts --arg="${version}" --arg="${initramfs}" \ + /etc/initramfs/post-update.d/ + return 0 + fi +} + +# ro /boot is not modified +ro_boot_check() +{ + # check irrelevant inside of a chroot + if [ ! -r /proc/mounts ] || ischroot; then + return 0 + fi + + # shellcheck disable=SC1004 + boot_opts=$(awk '/boot/{if ((match($4, /^ro/) || match($4, /,ro/)) \ + && $2 == "/boot") print "ro"}' /proc/mounts) + if [ -n "${boot_opts}" ]; then + echo "W: /boot is ro mounted." >&2 + echo "W: update-initramfs: Not updating ${initramfs}" >&2 + exit 0 + fi +} + +get_sorted_versions() +{ + version_list="$( + linux-version list | + while read -r version; do + test -e "${BOOTDIR}/initrd.img-$version" && echo "$version" + done | + linux-version sort --reverse + )" + verbose "Available versions: ${version_list}" +} + +set_current_version() +{ + if [ -f "/boot/initrd.img-$(uname -r)" ]; then + version=$(uname -r) + fi +} + +set_linked_version() +{ + linktarget= + if [ -e /initrd.img ] && [ -L /initrd.img ]; then + linktarget="$(basename "$(readlink /initrd.img)")" + fi + + if [ -e /boot/initrd.img ] && [ -L /boot/initrd.img ]; then + linktarget="$(basename "$(readlink /boot/initrd.img)")" + fi + + if [ -z "${linktarget}" ]; then + return + fi + + version="${linktarget##initrd.img-}" +} + +set_highest_version() +{ + get_sorted_versions + if [ -z "${version_list}" ]; then + version= + return + fi + # shellcheck disable=SC2086 + set -- ${version_list} + version=${1} +} + +create() +{ + if [ -z "${version}" ]; then + usage_error "Create mode requires a version argument" + fi + + set_initramfs + + generate_initramfs + + run_bootloader +} + +update() +{ + if [ "${update_initramfs}" = "no" ]; then + echo "update-initramfs: Not updating initramfs." + exit 0 + fi + + if [ -z "${version}" ]; then + set_highest_version + fi + + if [ -z "${version}" ]; then + set_linked_version + fi + + if [ -z "${version}" ]; then + set_current_version + fi + + if [ -z "${version}" ]; then + verbose "Nothing to do, exiting." + exit 0 + fi + + set_initramfs + + ro_boot_check + + backup_initramfs + + generate_initramfs + + run_bootloader + + backup_booted_initramfs +} + +delete() +{ + if [ -z "${version}" ]; then + usage_error "Delete mode requires a version argument" + fi + + set_initramfs + + echo "update-initramfs: Deleting ${initramfs}" + + rm -f "${initramfs}" "${initramfs}.bak" +} + +# Defaults +verbose=0 + +## + +OPTIONS=$(getopt -o "k:cudvtb:h?" --long help -n "$0" -- "$@") || usage_error + +eval set -- "$OPTIONS" + +while true; do + case "$1" in + -k) + version="$2" + shift 2 + ;; + -c) + mode="c" + shift + ;; + -d) + mode="d" + shift + ;; + -u) + mode="u" + shift + ;; + -v) + verbose="1" + shift + ;; + -t) + # accepted for compatibility, but ignored + shift + ;; + -b) + BOOTDIR="$2" + if [ ! -d "${BOOTDIR}" ]; then + echo "E: ${BOOTDIR} is not a directory." >&2 + exit 1 + fi + shift 2 + ;; + -h|-\?|--help) + usage + exit 0 + ;; + --) + shift + break + ;; + esac +done + +if [ $# -ne 0 ]; then + printf "Extra argument '%s'\\n\\n" "$1" >&2 + usage_error +fi + +# Validate arguments +if [ -z "${mode}" ]; then + usage_error "You must specify at least one of -c, -u, or -d." +fi + +if [ "${version}" = "all" ] \ + || { [ "${update_initramfs}" = "all" ] && [ -z "${version}" ]; }; then + case "${mode}" in + c) + version_list="$(linux-version list)" + ;; + d | u) + get_sorted_versions + ;; + esac + if [ -z "${version_list}" ]; then + verbose "Nothing to do, exiting." + exit 0 + fi + + OPTS="-b ${BOOTDIR}" + if [ "${verbose}" = "1" ]; then + OPTS="${OPTS} -v" + fi + for u_version in ${version_list}; do + verbose "Execute: ${0} -${mode} -k \"${u_version}\" ${OPTS}" + # shellcheck disable=SC2086 + "${0}" -${mode} -k "${u_version}" ${OPTS} + done + exit 0 +fi + + +case "${mode}" in + c) + create + ;; + d) + delete + ;; + u) + update + ;; +esac