#!/bin/bash # Copyright 2024 Patrick J. Volkerding, Sebeka, Minnesota, USA # All rights reserved. # # Redistribution and use of this script, with or without modification, is # permitted provided that the following conditions are met: # # 1. Redistributions of this script must retain the above copyright # notice, this list of conditions and the following disclaimer. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # This script creates a virtual installed package named # kernel-backup-${KERNEL_VERSION}-${ARCH}-${BUILD}, which # consists of a backup of /boot/vmlinuz-${KERNEL_VERSION} and the modules # that go with that kernel. # Also included will be one or two symlinks: # /boot/vmlinuz-backup pointing to /boot/vmlinuz-${KERNEL_VERSION}, # and, if /boot/initrd-${KERNEL_VERSION}.img exists, we will have # /boot/initrd-backup.img pointing to that. # # If $1 is a kernel, we'll use that. Otherwise, we'll use $KERNEL_FILE, # which defaults to /boot/vmlinuz-generic. We will include the kernel # as well as the related tree of kernel modules. Once backed up, this # kernel can be added to your bootloader as a fallback kernel, and removing # or upgrading the original kernel-generic package will not remove it. But, # you can remove the kernel-backup package later with removepkg if you wish. # # The $KERNEL_FILE doesn't need to have any particular naming scheme. # You could point at /usr/src/linux/arch/x86/boot/bzImage for all this # script cares. The kernel modules do need to be installed first though. BUILD=${BUILD:-1} # If KERNEL_FILE is already set, it overrides what's in # /etc/default/make-kernel-backup: if [ ! -z "$KERNEL_FILE" ]; then KERNEL_FILE_OVERRIDE=$KERNEL_FILE fi # If we were given a kernel file as a command-line parameter, that gets the # highest priority: if [ ! -z "$1" ]; then KERNEL_FILE_OVERRIDE=$1 fi # If /etc/default/make-kernel-backup exists, source it to set initial values # for for any of: # BUILD, KERNEL_FILE, BACKUP_NAME, and MAKE_SYMLINKS: if [ -r /etc/default/make-kernel-backup ]; then . /etc/default/make-kernel-backup fi # If we have an override for the kernel file, use it: if [ ! -z "$KERNEL_FILE_OVERRIDE" ]; then KERNEL_FILE=$KERNEL_FILE_OVERRIDE fi # If we don't have KERNEL_FILE set by now, use this: KERNEL_FILE=${KERNEL_FILE:-/boot/vmlinuz-generic} # In case this is a symlink, get the real file: if [ -L "$KERNEL_FILE" ]; then if [ ! -e "$KERNEL_FILE" ]; then echo "error: $KERNEL_FILE is not a valid symlink." exit 1 fi elif [ ! -e "$KERNEL_FILE" ]; then echo "error: $KERNEL_FILE does not exist." exit 1 fi KERNEL_FILE="$(readlink -f $KERNEL_FILE)" # Is this a Linux kernel? if ! file "$KERNEL_FILE" | grep -q "Linux kernel" ; then echo "ERROR: $KERNEL_FILE does not appear to be a Linux kernel." exit 1 fi # Find the kernel version: if [ -r $KERNEL_FILE ]; then KERNEL_VERSION=$(strings $KERNEL_FILE | grep '([^ ]*@[^ ]*) #' | cut -f1 -d' ') else echo "ERROR: $KERNEL_FILE not found." exit 1 fi # This is the kernel name for the backup kernel: BACKUP_NAME=${BACKUP_NAME:-backup} # Do we want to make unversioned symlinks to the backup kernel and (if used) # initrd? With the default BACKUP_NAME, these would be named vmlinuz-backup # and initrd-backup.img. MAKE_SYMLINKS=${MAKE_SYMLINKS:-true} # We require the modules for this kernel version to be installed: if [ ! -d /lib/modules/${KERNEL_VERSION} ]; then echo "ERROR: directory /lib/modules/${KERNEL_VERSION} does not exist." echo "Refusing to back up a kernel without modules." exit 1 fi # Guess the $ARCH: if file /bin/bash | grep -wq x86-64 ; then ARCH="x86_64" elif file /bin/bash | grep -wq 386 ; then ARCH="i686" else ARCH="$(uname -m)" fi if [ "$MAKE_SYMLINKS" = "true" ]; then # Make an unversioned symlink: rm -f /boot/vmlinuz-${BACKUP_NAME} ln -sf vmlinuz-${KERNEL_VERSION} /boot/vmlinuz-${BACKUP_NAME} echo "Created backup kernel symlink: /boot/vmlinuz-${BACKUP_NAME} -> vmlinuz-${KERNEL_VERSION}" fi ### ACTUALLY, it is better to not include this, since the initrds are ### not normally part of some other package that might get removed ## If there is an initrd for this kernel version, we will assume that's ## the one we should use: #if [ -r /boot/initrd-${KERNEL_VERSION}.img ]; then # INITBLURB="Backed up /boot/initrd-${KERNEL_VERSION}.img." #else # INITBLURB="Not including an initrd because a matching version was not found." #fi # OK, an initrd could be made automatically if you don't have one, but that's # not really this script's job. ;-) If you don't already have one, we will # assume that's because you don't use one, and don't want one. #else # # Make an initrd for this: # sh /usr/share/mkinitrd/mkinitrd_command_generator.sh -k ${KERNEL_VERSION} -a "-L -o /boot/initrd-${KERNEL_VERSION}.img" | bash 1> /dev/null 2> /dev/null #fi if [ "$MAKE_SYMLINKS" = "true" ]; then # Make an initrd symlink if there is an initrd: if [ -f /boot/initrd-${KERNEL_VERSION}.img ]; then rm -f /boot/initrd-${BACKUP_NAME}.img ln -sf initrd-${KERNEL_VERSION}.img /boot/initrd-${BACKUP_NAME}.img echo "Created backup initrd symlink: /boot/initrd-${BACKUP_NAME}.img -> initrd-${KERNEL_VERSION}.img" fi fi # Some shortcuts to avoid redundancy: PACKAGE_NAME="kernel-${BACKUP_NAME}-$(echo ${KERNEL_VERSION} | tr - _)-${ARCH}-${BUILD}" PACKAGES_ENTRY=/var/lib/pkgtools/packages/${PACKAGE_NAME} SCRIPTS_ENTRY=/var/lib/pkgtools/scripts/${PACKAGE_NAME} # Now we need to make a "virtual" package. We'll start with the "pkgtools/packages/" entry: cat << EOF > $PACKAGES_ENTRY PACKAGE NAME: ${PACKAGE_NAME} COMPRESSED PACKAGE SIZE: 93M UNCOMPRESSED PACKAGE SIZE: 444M PACKAGE LOCATION: ./${PACKAGE_NAME} PACKAGE DESCRIPTION: kernel-${BACKUP_NAME}: kernel-${BACKUP_NAME} (backup of ${KERNEL_FILE}) kernel-${BACKUP_NAME}: kernel-${BACKUP_NAME}: This is a backup of ${KERNEL_FILE} and modules. kernel-${BACKUP_NAME}: kernel-${BACKUP_NAME}: kernel-${BACKUP_NAME}: kernel-${BACKUP_NAME}: kernel-${BACKUP_NAME}: kernel-${BACKUP_NAME}: kernel-${BACKUP_NAME}: kernel-${BACKUP_NAME}: FILE LIST: ./ boot/ EOF echo boot/vmlinuz-${KERNEL_VERSION} >> $PACKAGES_ENTRY #if [ -f /boot/initrd-${KERNEL_VERSION}.img ]; then # echo boot/initrd-${KERNEL_VERSION}.img >> $PACKAGES_ENTRY #fi echo "lib/" >> $PACKAGES_ENTRY echo "lib/modules/" >> $PACKAGES_ENTRY # First the files: find /lib/modules/${KERNEL_VERSION} -type f | cut -b2- > ${PACKAGES_ENTRY}-tmp # Then the directories: find /lib/modules/${KERNEL_VERSION} -type d | cut -b2- | sed "s|$|/|" >> ${PACKAGES_ENTRY}-tmp cat ${PACKAGES_ENTRY}-tmp | sort >> $PACKAGES_ENTRY rm -f ${PACKAGES_ENTRY}-tmp # Finally, store the symlinks in the "pkgtools/scripts/" entry: make_install_script() { TAB="$(echo -e "\t")" COUNT=1 while :; do LINE="$(sed -n "$COUNT p" $1)" if [ "$LINE" = "" ]; then break fi LINKGOESIN="$(echo "$LINE" | cut -f 1 -d "$TAB")" LINKGOESIN="$(dirname "$LINKGOESIN")" LINKNAMEIS="$(echo "$LINE" | cut -f 1 -d "$TAB")" LINKNAMEIS="$(basename "$LINKNAMEIS")" LINKPOINTSTO="$(echo "$LINE" | cut -f 2 -d "$TAB")" echo "( cd $LINKGOESIN ; rm -rf $LINKNAMEIS )" echo "( cd $LINKGOESIN ; ln -sf $LINKPOINTSTO $LINKNAMEIS )" COUNT=$(expr $COUNT + 1) done } rm -f ${SCRIPTS_ENTRY} find /boot -type l -name vmlinuz-${BACKUP_NAME} -printf "%p\t%l\n" | cut -b2- | LC_COLLATE=C sort | sed 's,^\./,,; s,[ "#$&\x27()*;<>?[\\`{|~],\\&,g;' | make_install_script >> ${SCRIPTS_ENTRY} find /boot -type l -name initrd-${BACKUP_NAME}.img -printf "%p\t%l\n" | cut -b2- | LC_COLLATE=C sort | sed 's,^\./,,; s,[ "#$&\x27()*;<>?[\\`{|~],\\&,g;' | make_install_script >> ${SCRIPTS_ENTRY} find /lib/modules/${KERNEL_VERSION} -type l -printf "%p\t%l\n" | cut -b2- | LC_COLLATE=C sort | sed 's,^\./,,; s,[ "#$&\x27()*;<>?[\\`{|~],\\&,g;' | make_install_script >> ${SCRIPTS_ENTRY} echo "Backed up ${KERNEL_FILE}." echo "Backed up kernel modules in /lib/modules/${KERNEL_VERSION}/" if [ ! -z "$INITBLURB" ]; then echo $INITBLURB fi echo "Installed as package ${PACKAGE_NAME}."