Mageia Bugzilla – Attachment 13901 Details for
Bug 31969
remove-old-kernels run as non root should not ask to remove anything, only list and exit
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
New Account
|
Forgot Password
script to test
remove-old-kernels (text/plain), 21.66 KB, created by
Barry Jackson
on 2023-06-30 12:11:35 CEST
(
hide
)
Description:
script to test
Filename:
MIME Type:
Creator:
Barry Jackson
Created:
2023-06-30 12:11:35 CEST
Size:
21.66 KB
patch
obsolete
>#!/usr/bin/bash ># Remove old kernels safely and cleanly using urpme. ># ># LISTK contains the list of kernels to analyse ># The script keeps NBK most recent kernels ># (c) Pierre Jarillon - 2018-04-03 - 2021-11-27 ># (c) Jean-Baptiste Biernacki 2021 ># (c) Barry C Jackson 2022-2023 >#################################### ># >######################### Functions ######################### > ># Perform translations >i18n() { >gettext "$prog" "$1" >} > ># Display -help >usage() { >echo "$(i18n "Usage: <remove-old kernels|rok> [-a] [-A 0|1] [-c] [-t] [-f 0-9] [-F 0-9] [-n number] [-N number] [-p] [-q] [-Q 0|1][-l] [-m] [-v] [-h|-?]")" >echo "$(i18n " -a = automatic, no questions. Interactive if not specified. (must be root)")" >echo "$(i18n " -A value = 1 or 0 to turn ON or OFF automatic weekly removal of kernels. (e.g. -A1)")" >echo "$(i18n " -c = as automatic but also checks for CRON=1 in the cfg file or exits. (must be root)")" >echo "$(i18n " -t = Test mode, nothing is removed, urpme is simulated.")" >echo "$(i18n " -f value = number of the alternate .cfg file to use this time only. Exits if missing.")" >echo "$(i18n " -F value = number of alt .cfg file to use. This changes the ALTCFG= setting in the main .cfg file.")" >echo "$(i18n " -n value = number of kernels to keep this time only. (-n5 or -n 5 keep 5 kernels), Min=2, Default=3")" >echo "$(i18n " -N value = number of kernels to keep. This changes the config file setting. (e.g. -N 5)")" >echo "$(i18n " -p = preview the urpme commands which would be used.")" >echo "$(i18n " -q = advanced mode, this time only - see man page.")" >echo "$(i18n " -Q value = 1 or 0 (1 = ON) advanced mode, persistent. (e.g. -Q1)")" >echo "$(i18n " -l = list the last 1000 lines of the log.")" >echo "$(i18n " -m = mono. No colours in screen output.")" >echo "$(i18n " -v = version.")" >echo "$(i18n " -? or -h = show this help.")" >echo "" >echo "$(i18n "KEY for column 3:")" >echo "$(i18n " U = currently (U)sed running kernel.")" >echo "$(i18n " V = keeping for (V)irtualbox -latest. (only in advanced mode)")" >echo "$(i18n " X = keeping for (X)tables-addons -latest. (only in advanced mode)")" >echo "$(i18n " K = keeping kernel-*-devel for installed (K)ernel. (only advanced mode)")" >echo "" >} > ># Function to check we are root or exit >chkroot() { >if (( UID > 0 )); then > echo -e "${RemvCol}$(i18n "Must be root to edit configuration")${Normal}\n$(i18n "Tap spacebar")" > read -r -n1; echo -en "${ClearLine}" > exit 0 >fi >} > ># Function to handle -f or -F options >altcfg() { >cfgno=$1 ># Called with f >if [[ $ed -ne 1 ]]; then > # Check for cfg 0 > if (( cfgno == 0 )); then > source "${maincfg}" > ALTCFG="${cfgno}" > else > # If alt cfg file exists source it > if [[ -f /etc/remove-old-kernels_"${cfgno}".cfg ]]; then > source "/etc/remove-old-kernels_${cfgno}.cfg" > ALTCFG=${cfgno} > else > echo -e "${RemvCol}$(i18n "No such alternative configuration file:")${Normal} /etc/remove-old-kernels_${cfgno}.cfg" > exit 1 > fi > fi >else > # Called with F > # If ALTCFG var is missing from the main .cfg file, add it > if ! grep -qF "ALTCFG=" "${maincfg}"; then > echo "ALTCFG=" >> "${maincfg}" > fi > # Switch back to .cfg 0 > if (( cfgno == 0 )); then > /usr/bin/sed -i "s/ALTCFG=.*/ALTCFG=${cfgno}/" "${maincfg}" > source "${maincfg}"; currcfg="${maincfg}" > else > # If alt cfg file exists > if [[ -f /etc/remove-old-kernels_${cfgno}.cfg ]]; then > # Add it in main .cfg > /usr/bin/sed -i "s/ALTCFG=.*/ALTCFG=${cfgno}/" "${maincfg}" > ALTCFG=${cfgno}; > currcfg="${cfgpath}${prog}_${ALTCFG}.cfg" > source "$currcfg" > else > echo -e "${RemvCol}$(i18n "No such alternative configuration file:")${Normal} /etc/remove-old-kernels_${cfgno}.cfg" > exit 1 > fi > fi >fi >} > ># Check cfg file(s) and source >chk_cfg() { ># Source main .cfg file if it exists, or issue warning and abort. >source ${maincfg} || { echo -e "${RemvCol}$(i18n "FATAL: Failed to read:")${Normal} ${maincfg}"; exit 1; } > ># If an alt .cfg is set in main .cfg >if grep -qF "ALTCFG=" "${maincfg}" && (( ALTCFG > 0 )); then > # Check it really exists > if [[ -f "${cfgpath}${prog}_$ALTCFG.cfg" ]]; then > # Set current cfg to it > currcfg="${cfgpath}${prog}_${ALTCFG}.cfg" > source "$currcfg" > else > echo -e "${RemvCol}$(i18n "Your alternative configuration file does not exist:")${Normal}/etc/remove-old-kernels_$ALTCFG.cfg\n$(i18n "Either replace it or set ALTCFG=0 in") /etc/remove-old-kernels.cfg" > exit 1 > fi >fi > ># The following config file checks need root to update files if needed >if (( UID == 0 )); then ># Check for CRON variable in config or add default. (Some users may have early versions) > if ! grep -qF -e "CRON=" "${currcfg}"; then > echo -e "\n# Allow cron to run remove-old-kernels. 0=OFF 1=ON.\nCRON=1" >> "${currcfg}" > fi > ># Update comment in config file if needed since $CRON change to 0|1 > /usr/bin/sed -i 's/^.*\blower case\b.*$/# Allow cron to run remove-old-kernels. 0=OFF 1=ON./' "${currcfg}" > ># Check for CRON set to old y/n values and update cfg > if (( ${#CRON} == 1 )) && [[ "ny" =~ $CRON ]]; then > [[ $CRON == y ]] && CRON=1 > [[ $CRON == n ]] && CRON=0 > /usr/bin/sed -i "s/CRON=.*/CRON=${CRON}/" "${currcfg}" > fi >fi >} > >################ Main program starts here ################# ># Do not edit the values below unless you know 'exactly' what you are doing. ># You can pass parameters on the command line to acheive similar functionality, ># or edit the configuration file/s, See man rok or rok -h for more information. > >NBK=3 # Default number of kernels to keep. >DEBUG=0 # 1 for test mode, urpme is simulated and not applied >MODE="I" # mode A)utomatic, I)nteractve (DO NOT CHANGE THIS) >VISU=0 # If VISU=1, show commands which can be used >LANGUAGE="${LANG}:${LANGUAGE}" > ># Colours for display Defaults Used for >Normal="\e[0m" # System default f/g colour >KeepCol="\e[92m" # Light Green Keep text >RemvCol="\e[91m" # Light Red Remove text >InfoCol="\e[33m" # Orange Information text >HdBgCol="\e[102;30m" # Light Green Background Heading background >WarnBgCol="\e[101m" # Light Red Background Warnings in heading >ClearLine="\r\e[2K" # Clear the line > ># List of kernel types to include. ># This list is only used if LISTK is omitted from the .cfg file in use. >LISTK=\ >"kernel-desktop586 >kernel-desktop >kernel-desktop-devel >kernel-server >kernel-server-devel >kernel-source >kernel-tmb-desktop >kernel-tmb-desktop-devel >kernel-tmb-source >kernel-linus >kernel-linus-devel >kernel-linus-source >" >prog="remove-old-kernels" >cfgpath="/etc/" >maincfg="${cfgpath}${prog}.cfg" >currcfg="${maincfg}" > ># Check for -h option before running chk_cfg (issue10) >o=$(echo "$1"|cut -d- -f2) >[[ "$o" == "?" ]] && o="h" >if (( ${#@} == 1 )) && [[ "h" =~ $o ]]; then > clear; usage; exit 0 >fi > ># Check dnf limit before option parsing >dnfNBK=0; dnfmsg=false >[[ -f /etc/dnf/dnf.conf ]] && dnfNBK=$(grep "installonly_limit=" /etc/dnf/dnf.conf|cut -d= -f2) > ># Run all config file checks before option parsing >chk_cfg > ># Parse command line options which take precedence over script and cfg file >if (( ${#} > 0 )) ; then > while getopts aA:ctf:F:plmvn:N:qQ:?h NAME; do > case ${NAME} in > a) MODE="A"; > ;; > A) chkroot > # Allow y/n and 0/1 for compatability with older versions > CRONN=${OPTARG} > if [[ ${#CRONN} = 1 ]] && [[ "01ny" =~ $CRONN ]]; then > if [[ "ny" =~ $CRONN ]]; then > echo "$(i18n "Please use 0 or 1 for OFF and ON")" > exit 0 > else > /usr/bin/sed -i "s/CRON=.*/CRON=${CRONN}/" "${currcfg}" && CRON=$CRONN > fi > else > echo "$(i18n "Bad input value")"; exit 1 > fi > ;; > c) MODE="A" && (( ${#CRON} > 0 )) && [[ "1y" =~ $CRON ]] || exit 0 > ;; > t) DEBUG=1 > ;; > f) [[ ${OPTARG} =~ ^[0-9] ]] && (( ${#OPTARG} == 1 )) && altcfg "${OPTARG}" > ;; > F) chkroot > [[ ${OPTARG} =~ ^[0-9] ]] && (( ${#OPTARG} == 1 )) && ed=1 && altcfg "${OPTARG}" > ;; > p) VISU=1 > ;; > q) QA=1 > ;; > Q) chkroot > [[ ${OPTARG} =~ ^[0-1] ]] && (( ${#OPTARG} == 1 )) && QAN="${OPTARG}" > if grep -qF "QA=" "${currcfg}"; then > /usr/bin/sed -i "s/QA=.*/QA=${QAN}/" "${currcfg}" && QA="${QAN}" > else > echo "QA=${QAN}" >> "${currcfg}" && QA="${QAN}" > fi > ;; > n) [[ ${OPTARG} =~ ^[0-9]+$ ]] && NBK="${OPTARG}" > ;; > N) chkroot > NBKN=${OPTARG} > if [[ $NBKN =~ ^[0-9]+$ ]] && (( NBKN >= dnfNBK )); then > /usr/bin/sed -i "s/NBK=.*/NBK=$NBKN/" "${currcfg}" && NBK="$NBKN" > else > dnfmsg=1 > fi > ;; > l) tail -n1000 /var/log/remove-old-kernels.log && { echo -e "\n$(i18n "Tap spacebar to exit")\n"; read -r -n1; echo -en "${ClearLine}"; } > ;; > m) Normal=""; RemvCol=""; KeepCol=""; InfoCol=""; HdBgCol=""; WarnBgCol=""; ClearLine="" > ;; > v) printf '%s'"$(rpm -q remove-old-kernels)\n" > exit 2 > ;; > h|?) clear; usage; exit 0 > ;; > esac > done >fi > ># Don't allow NBK < 2 whether it comes from CLI, script edit, config file or the moon >if (( NBK < 2 )); then > NBK=2 >fi > ># Only use greeting in interactive mode >if [[ "$MODE" == "I" ]]; then >clear >echo -e " $(i18n "Welcome to 'remove-old-kernels' Interactive")\n" >fi > ># Use dnf kernel 'number to keep' if installed and greater than ours >if (( dnfNBK > NBK )) || (( dnfmsg == 1 )); then > NBK=$dnfNBK > dnfmssg="${InfoCol}$(i18n "INFO: Number to keep is restricted to ")${dnfNBK}\ >$(i18n ", by the dnf 'installonly_limit' set in /etc/dnf/dnf.conf")${Normal}" >fi > ># Get info for status message >if (( CRON == 1 )); then autostat="1"; else autostat="${Normal}${WarnBgCol}0${HdBgCol}"; fi >if (( QA == 1 )) ; then qamssg="| Q "; fi >if (( ALTCFG > 0 )); then cfgmssg="|${Normal}${WarnBgCol}F:${ALTCFG}${HdBgCol} "; fi > ># Check that the running kernel is still installed: https://bugs.mageia.org/show_bug.cgi?id=31015 >[[ -e /lib/modules/$(uname -r) ]] || { echo -e "${RemvCol}$(i18n "FATAL: Has the running kernel been uninstalled since last boot? - Aborting.")"; exit 1; } > ># Get the full name of the current running kernel >CURK=$(rpm -qf /lib/modules/"$(uname -r)") > ># Storage for the list of kernels to remove >TMPKTR=$(mktemp) > ># Check storage usage on root partition >OCCDISK1=$(df -B 1M -l --output=used / | tail -n1 | awk '{ print $1 }') > ># Pad translated strings for column 2 > keepstr="$(i18n "Keep")" > remstr="$(i18n "Remove")" > if (( ${#keepstr} != ${#remstr} )); then > if (( ${#keepstr} > ${#remstr} )); then > while (( ${#remstr} < ${#keepstr} )); do remstr="${remstr} "; done > else > while (( ${#remstr} > ${#keepstr} )); do keepstr="${keepstr} "; done > fi > fi > >#========================= Analyse /boot/ ============================= > >NK=$(find /boot -name "vmlinuz*.mga[0-9]" | wc -l) > ># In automatic mode exit immediately if kernels in boot <= number allowed to be removed > [[ "$MODE" = "A" ]] && (( NK < NBK )) && exit 0 > >#================================ Show status ============================== > >echo -e "${HdBgCol} System: $(cat /etc/mageia-release) | $(i18n "Kernels in") /boot/:${NK} | AUTO:$autostat | $(i18n "KEEP"):$NBK ${qamssg}${cfgmssg}${Normal} " > >#================================= Analyse rpms ============================= ># Get master list from rpm -qa --last >allkernels="$(rpm -qa --last|grep "kernel-")" > ># Get the kernel version and release required by the installed virtualbox-flavour-latest >get_vbox_kern() { >if echo "$allkernels"|grep -q "virtualbox-${kernelType}-latest"; then > vb_latest_ver=$(echo "$allkernels"|cut -d' ' -f1|grep "virtualbox-${kernelType}-latest"|cut -d- -f5-|rev|cut -d. -f2-|rev) >else > qavkern=; return >fi >depkern=$(echo "$allkernels"|cut -d' ' -f1|grep "$vb_latest_ver"|grep -v latest|grep "virtualbox-kernel-[0-9]") >kern_ver=$(echo "$depkern"|cut -d- -f3-|cut -d- -f1) >kern_rel=$(echo "$depkern"|cut -d- -f5-|cut -d. -f1) >(( ${#kern_ver} > 0 )) && (( ${#kern_rel} > 0 )) && qavkern="${kern_ver}-${kern_rel}" >} > ># Get the kernel version and release required by the installed xtables-addons-flavour-latest >get_xtables_kern() { >if echo "$allkernels"|grep -q "xtables-addons-${kernelType}-latest"; then > xt_latest_ver=$(echo "$allkernels"|cut -d' ' -f1|grep kernel-|grep "xtables-addons-kernel"|grep "latest"|cut -d- -f6-|rev|cut -d. -f2-|rev) >else > qaxkern=; return >fi >depkern=$(echo "$allkernels"|cut -d' ' -f1|grep "$xt_latest_ver"|grep "xtables-addons-kernel-[0-9]"|cut -d ' ' -f1) >kern_ver=$(echo "$depkern"|cut -d- -f4) >kern_rel=$(echo "$depkern"|cut -d- -f6 -|cut -d. -f1) >(( ${#kern_ver} > 0 )) && (( ${#kern_rel} > 0 )) && qaxkern="${kern_ver}-${kern_rel}" >} > ># Keep the -devel package for any installed kernel package >depdev() { ># Reset some vars >pkg=; kpkgname=; nnk="" > # Is current pkg a -devel > if echo "${installedKernelPackage}" | grep -q "\-devel"; then > # Get matching kernel package name > kpkgname=$(echo "${installedKernelPackage}"| /usr/bin/sed -e 's/\-devel//') > # Find kernel in installed list without timestamp > for pkg in $(echo "$allkernels"|cut -d ' ' -f1); do > # Look through installed kernels for kernel package > if [[ ${pkg} == "${kpkgname}" ]]; then > nnk="K"; REMVBL=0 > # Is kernel flagged for removal? > if grep -q "$kpkgname" "${TMPKTR}" ; then > # Then this -devel is also removable > nnk=""; REMVBL=1 > fi > # We are done here > break > fi > done > fi >} > ># Exclude 'latest' packages from installed kernel list >rpmqaList=$(echo "$allkernels"|grep -v latest) > ># Loop through kernel types in LISTK >for kernelType in ${LISTK}; do > (( QA == 1 )) && get_vbox_kern && get_xtables_kern > installedKernelCounter=0; ># Scan through installed kernels to match with kernel type in LISTK > echo "$rpmqaList"|grep "${kernelType}-[0-9]"|while read -r installedKernel; do ># Clear these for each loop > NOTA=""; REMVBL=1 ># Increment installedKernelCounter > installedKernelCounter=$(( installedKernelCounter + 1)) > ># Add heading if there exists at least one installedKernel of this kernelType > if (( installedKernelCounter == 1 )) ; then > echo -ne "\r ==> ${kernelType}" > echo "" > fi > ># Remove time stamp > installedKernelPackage="$(echo "${installedKernel}" | cut -d ' ' -f 1 )" > ># Check for current kernel > if (( $(echo "${installedKernel}" | grep -c "${CURK}") == 1 )) ; then > NOTA="U" # current kernel > REMVBL=0 # not removable > fi > ># Check if kernel is required by vbox latest kernel > if (( QA == 1 )) && (( ${#qavkern} > 0 )) && (( $(echo "${installedKernel}" | grep -c "$qavkern") == 1 )); then > NOTA="${NOTA}V" # Required by VBox > REMVBL=0 # not removable > fi > ># Check if kernel is required by xtables latest kernel > if (( QA == 1 )) && (( ${#qaxkern} > 0 )) && (( $(echo "${installedKernel}" | grep -c "$qaxkern") == 1 )); then > NOTA="${NOTA}X" # Required by xtables > REMVBL=0 # not removable > fi > ># If a kernel devel package has the corresponding kernel package keep it > if (( QA == 1 )); then > depdev > NOTA="${NOTA}${nnk}" > fi > ># Pad NOTA to 4 > while (( ${#NOTA} < 4 )); do NOTA="${NOTA} "; done > ># Pad installedKernelCounter (col 1) to 3 > while (( ${#installedKernelCounter} < 3 )); do installedKernelCounter="${installedKernelCounter} "; done > > if (( installedKernelCounter > NBK )); then > if (( REMVBL != 1 )) ; then > echo -e "\r ${installedKernelCounter}${KeepCol} : $keepstr: ${NOTA}: ${installedKernel} ${Normal}" > else > echo -e "\r ${installedKernelCounter}${RemvCol} : $remstr: ${NOTA}: ${installedKernel} ${Normal}" > # Add package to removal list > echo "${installedKernelPackage}" >> "${TMPKTR}" > fi > else > echo -e "\r ${installedKernelCounter}${KeepCol} : $keepstr: ${NOTA}: ${installedKernel} ${Normal}" > fi > done >done > ># Position 'in use' key under symbol below column 3 >padUse=$((${#keepstr}+9)); padstr="" >while (( ${#padstr} < padUse )); do padstr="${padstr} "; done > >echo -en "${ClearLine}" >echo -e "${padstr}${KeepCol}U${InfoCol} = $(i18n "In use now")${Normal}" >if (( ${#dnfmssg} > 0 ));then echo -e "${dnfmssg}"; fi > >#================================= Mode of execution =================== > ># Check there are kernels to remove >nbktr=$(grep -vc devel < "${TMPKTR}") >nbt=$(wc -l < "${TMPKTR}") >if (( nbt != 0 )) ; then > # Check if we are in preview mode > if (( VISU == 1 )); then > plural="s"; (( nbt == 1 )) && plural="" > # Get singular/plural message without getting obfuscated by gettext > if [[ "$plural" == "s" ]]; then > echo "$(i18n "Commands that would be used"):" > else > echo "$(i18n "Command that would be used"):" > fi > # Print out the kernel commands > for f in $(tac "${TMPKTR}"); do > echo "urpme ${f}" > done > echo "$(i18n "Tap spacebar to exit")" > read -n1 > echo -en "${ClearLine}" > # Delete temp file list of removeable kernels > rm -f "${TMPKTR}" > exit 0 > fi > # Warn if we are not root and exit hang until spacebar > if (( UID > 0 )); then > echo -e "${RemvCol}$(i18n "Must be root to allow removal")${Normal}\n$(i18n "Tap spacebar")" > read -r -n1; echo -en "${ClearLine}" > exit 0 > fi > if [[ "${MODE}" != "A" ]] ; then > if (( DEBUG == 1 )) ; then > echo -e "\n${KeepCol}>> $(i18n "Test mode is on - kernels will not be removed") <<${Normal}" > fi > plural="s"; (( nbktr == 1 )) && plural="" > if [[ "$plural" == "s" ]]; then > kernstr="$(i18n "kernels"):" > else > kernstr="$(i18n "kernel"):" > fi > if (( nbt == nbktr )); then > read -p "$(i18n "Remove") ${nbt} $kernstr ? $(i18n "y/N/i (i=confirm for each)") " -n 1 response > else > read -p "$(i18n "Remove") ${nbt} (${nbktr} ${kernstr}) ? $(i18n "y/N/i (i=confirm for each)") " -n 1 response > fi > if [[ -z ${response} ]] ; then response="n" ; fi > case ${response} in > [Yy]) > AUTO="--auto" > MODE="A" > echo -e " \n" > ;; > [Ii]) > AUTO="--auto" > MODE="I" > echo " $(i18n "interactive")" > ;; > *) > echo -e "\n$(i18n "Aborted")\n" > rm -f "${TMPKTR}" > exit 0 > ;; > esac > fi > >#================================= Execution =========================== > > for installedKernelPackage in $(tac "${TMPKTR}") ; do > if [[ ${MODE} = "I" ]] ; then # --- interactive mode --- > read -p "$(i18n "Remove") ${installedKernelPackage} ? $(i18n "y/N/q (q=quit)") " -n 1 response > echo -e " \n" > if [[ -z ${response} ]]; then > response="N" > fi > case ${response} in > [Yy]) > if (( DEBUG == 1 )) ; then > echo -e "\n${InfoCol}$(i18n "DEBUG: Could execute: urpme") ${AUTO} ${installedKernelPackage}${Normal}" > ((nbt--)) > else > urpme "${installedKernelPackage}" > ((nbt--)) > fi > ;; > [qQ]) > echo -e "\n$(i18n "Aborted")" > rm -f "${TMPKTR}"; > exit 0 > ;; > *) > echo " " > ;; > esac > else # --- automatic mode --- > AUTO="--auto" > if (( DEBUG == 1 )) ; then > echo -e "${InfoCol}$(i18n "DEBUG: Could execute: urpme") ${AUTO} ${installedKernelPackage}${Normal}" > ((nbt--)) > else > # --- automatic execution --- > # preload y and CR in auto mode > echo -ne 'y\n' | urpme "${AUTO}" "${installedKernelPackage}" > ((nbt--)) > fi > fi > done > NK=$(find /boot -name "vmlinuz*.mga[0-9]" | wc -l) > OCCDISK2=$(df -B 1M -l --output=used / | tail -n1 | awk '{ print $1 }') > echo -e "${HdBgCol} $(i18n "Gain"):$((OCCDISK1 - OCCDISK2)) MB - $(i18n "Kernels in") /boot/: ${NK} ${Normal}" >fi > >[[ $MODE == "I" ]] && { echo "$(i18n "Tap spacebar to exit")"; read -n1; echo -en "${ClearLine}"; } > >rm -f "${TMPKTR}" > ># Run again if some removable kernels are left >(( UID > 0 )) && exit 0 # Not root user so exit >[[ ${MODE} != "I" ]] && exit 0 # Auto so exit >(( nbt > 0 )) && ${0} # Root user and interactive so run again if removable kernels left
#!/usr/bin/bash # Remove old kernels safely and cleanly using urpme. # # LISTK contains the list of kernels to analyse # The script keeps NBK most recent kernels # (c) Pierre Jarillon - 2018-04-03 - 2021-11-27 # (c) Jean-Baptiste Biernacki 2021 # (c) Barry C Jackson 2022-2023 #################################### # ######################### Functions ######################### # Perform translations i18n() { gettext "$prog" "$1" } # Display -help usage() { echo "$(i18n "Usage: <remove-old kernels|rok> [-a] [-A 0|1] [-c] [-t] [-f 0-9] [-F 0-9] [-n number] [-N number] [-p] [-q] [-Q 0|1][-l] [-m] [-v] [-h|-?]")" echo "$(i18n " -a = automatic, no questions. Interactive if not specified. (must be root)")" echo "$(i18n " -A value = 1 or 0 to turn ON or OFF automatic weekly removal of kernels. (e.g. -A1)")" echo "$(i18n " -c = as automatic but also checks for CRON=1 in the cfg file or exits. (must be root)")" echo "$(i18n " -t = Test mode, nothing is removed, urpme is simulated.")" echo "$(i18n " -f value = number of the alternate .cfg file to use this time only. Exits if missing.")" echo "$(i18n " -F value = number of alt .cfg file to use. This changes the ALTCFG= setting in the main .cfg file.")" echo "$(i18n " -n value = number of kernels to keep this time only. (-n5 or -n 5 keep 5 kernels), Min=2, Default=3")" echo "$(i18n " -N value = number of kernels to keep. This changes the config file setting. (e.g. -N 5)")" echo "$(i18n " -p = preview the urpme commands which would be used.")" echo "$(i18n " -q = advanced mode, this time only - see man page.")" echo "$(i18n " -Q value = 1 or 0 (1 = ON) advanced mode, persistent. (e.g. -Q1)")" echo "$(i18n " -l = list the last 1000 lines of the log.")" echo "$(i18n " -m = mono. No colours in screen output.")" echo "$(i18n " -v = version.")" echo "$(i18n " -? or -h = show this help.")" echo "" echo "$(i18n "KEY for column 3:")" echo "$(i18n " U = currently (U)sed running kernel.")" echo "$(i18n " V = keeping for (V)irtualbox -latest. (only in advanced mode)")" echo "$(i18n " X = keeping for (X)tables-addons -latest. (only in advanced mode)")" echo "$(i18n " K = keeping kernel-*-devel for installed (K)ernel. (only advanced mode)")" echo "" } # Function to check we are root or exit chkroot() { if (( UID > 0 )); then echo -e "${RemvCol}$(i18n "Must be root to edit configuration")${Normal}\n$(i18n "Tap spacebar")" read -r -n1; echo -en "${ClearLine}" exit 0 fi } # Function to handle -f or -F options altcfg() { cfgno=$1 # Called with f if [[ $ed -ne 1 ]]; then # Check for cfg 0 if (( cfgno == 0 )); then source "${maincfg}" ALTCFG="${cfgno}" else # If alt cfg file exists source it if [[ -f /etc/remove-old-kernels_"${cfgno}".cfg ]]; then source "/etc/remove-old-kernels_${cfgno}.cfg" ALTCFG=${cfgno} else echo -e "${RemvCol}$(i18n "No such alternative configuration file:")${Normal} /etc/remove-old-kernels_${cfgno}.cfg" exit 1 fi fi else # Called with F # If ALTCFG var is missing from the main .cfg file, add it if ! grep -qF "ALTCFG=" "${maincfg}"; then echo "ALTCFG=" >> "${maincfg}" fi # Switch back to .cfg 0 if (( cfgno == 0 )); then /usr/bin/sed -i "s/ALTCFG=.*/ALTCFG=${cfgno}/" "${maincfg}" source "${maincfg}"; currcfg="${maincfg}" else # If alt cfg file exists if [[ -f /etc/remove-old-kernels_${cfgno}.cfg ]]; then # Add it in main .cfg /usr/bin/sed -i "s/ALTCFG=.*/ALTCFG=${cfgno}/" "${maincfg}" ALTCFG=${cfgno}; currcfg="${cfgpath}${prog}_${ALTCFG}.cfg" source "$currcfg" else echo -e "${RemvCol}$(i18n "No such alternative configuration file:")${Normal} /etc/remove-old-kernels_${cfgno}.cfg" exit 1 fi fi fi } # Check cfg file(s) and source chk_cfg() { # Source main .cfg file if it exists, or issue warning and abort. source ${maincfg} || { echo -e "${RemvCol}$(i18n "FATAL: Failed to read:")${Normal} ${maincfg}"; exit 1; } # If an alt .cfg is set in main .cfg if grep -qF "ALTCFG=" "${maincfg}" && (( ALTCFG > 0 )); then # Check it really exists if [[ -f "${cfgpath}${prog}_$ALTCFG.cfg" ]]; then # Set current cfg to it currcfg="${cfgpath}${prog}_${ALTCFG}.cfg" source "$currcfg" else echo -e "${RemvCol}$(i18n "Your alternative configuration file does not exist:")${Normal}/etc/remove-old-kernels_$ALTCFG.cfg\n$(i18n "Either replace it or set ALTCFG=0 in") /etc/remove-old-kernels.cfg" exit 1 fi fi # The following config file checks need root to update files if needed if (( UID == 0 )); then # Check for CRON variable in config or add default. (Some users may have early versions) if ! grep -qF -e "CRON=" "${currcfg}"; then echo -e "\n# Allow cron to run remove-old-kernels. 0=OFF 1=ON.\nCRON=1" >> "${currcfg}" fi # Update comment in config file if needed since $CRON change to 0|1 /usr/bin/sed -i 's/^.*\blower case\b.*$/# Allow cron to run remove-old-kernels. 0=OFF 1=ON./' "${currcfg}" # Check for CRON set to old y/n values and update cfg if (( ${#CRON} == 1 )) && [[ "ny" =~ $CRON ]]; then [[ $CRON == y ]] && CRON=1 [[ $CRON == n ]] && CRON=0 /usr/bin/sed -i "s/CRON=.*/CRON=${CRON}/" "${currcfg}" fi fi } ################ Main program starts here ################# # Do not edit the values below unless you know 'exactly' what you are doing. # You can pass parameters on the command line to acheive similar functionality, # or edit the configuration file/s, See man rok or rok -h for more information. NBK=3 # Default number of kernels to keep. DEBUG=0 # 1 for test mode, urpme is simulated and not applied MODE="I" # mode A)utomatic, I)nteractve (DO NOT CHANGE THIS) VISU=0 # If VISU=1, show commands which can be used LANGUAGE="${LANG}:${LANGUAGE}" # Colours for display Defaults Used for Normal="\e[0m" # System default f/g colour KeepCol="\e[92m" # Light Green Keep text RemvCol="\e[91m" # Light Red Remove text InfoCol="\e[33m" # Orange Information text HdBgCol="\e[102;30m" # Light Green Background Heading background WarnBgCol="\e[101m" # Light Red Background Warnings in heading ClearLine="\r\e[2K" # Clear the line # List of kernel types to include. # This list is only used if LISTK is omitted from the .cfg file in use. LISTK=\ "kernel-desktop586 kernel-desktop kernel-desktop-devel kernel-server kernel-server-devel kernel-source kernel-tmb-desktop kernel-tmb-desktop-devel kernel-tmb-source kernel-linus kernel-linus-devel kernel-linus-source " prog="remove-old-kernels" cfgpath="/etc/" maincfg="${cfgpath}${prog}.cfg" currcfg="${maincfg}" # Check for -h option before running chk_cfg (issue10) o=$(echo "$1"|cut -d- -f2) [[ "$o" == "?" ]] && o="h" if (( ${#@} == 1 )) && [[ "h" =~ $o ]]; then clear; usage; exit 0 fi # Check dnf limit before option parsing dnfNBK=0; dnfmsg=false [[ -f /etc/dnf/dnf.conf ]] && dnfNBK=$(grep "installonly_limit=" /etc/dnf/dnf.conf|cut -d= -f2) # Run all config file checks before option parsing chk_cfg # Parse command line options which take precedence over script and cfg file if (( ${#} > 0 )) ; then while getopts aA:ctf:F:plmvn:N:qQ:?h NAME; do case ${NAME} in a) MODE="A"; ;; A) chkroot # Allow y/n and 0/1 for compatability with older versions CRONN=${OPTARG} if [[ ${#CRONN} = 1 ]] && [[ "01ny" =~ $CRONN ]]; then if [[ "ny" =~ $CRONN ]]; then echo "$(i18n "Please use 0 or 1 for OFF and ON")" exit 0 else /usr/bin/sed -i "s/CRON=.*/CRON=${CRONN}/" "${currcfg}" && CRON=$CRONN fi else echo "$(i18n "Bad input value")"; exit 1 fi ;; c) MODE="A" && (( ${#CRON} > 0 )) && [[ "1y" =~ $CRON ]] || exit 0 ;; t) DEBUG=1 ;; f) [[ ${OPTARG} =~ ^[0-9] ]] && (( ${#OPTARG} == 1 )) && altcfg "${OPTARG}" ;; F) chkroot [[ ${OPTARG} =~ ^[0-9] ]] && (( ${#OPTARG} == 1 )) && ed=1 && altcfg "${OPTARG}" ;; p) VISU=1 ;; q) QA=1 ;; Q) chkroot [[ ${OPTARG} =~ ^[0-1] ]] && (( ${#OPTARG} == 1 )) && QAN="${OPTARG}" if grep -qF "QA=" "${currcfg}"; then /usr/bin/sed -i "s/QA=.*/QA=${QAN}/" "${currcfg}" && QA="${QAN}" else echo "QA=${QAN}" >> "${currcfg}" && QA="${QAN}" fi ;; n) [[ ${OPTARG} =~ ^[0-9]+$ ]] && NBK="${OPTARG}" ;; N) chkroot NBKN=${OPTARG} if [[ $NBKN =~ ^[0-9]+$ ]] && (( NBKN >= dnfNBK )); then /usr/bin/sed -i "s/NBK=.*/NBK=$NBKN/" "${currcfg}" && NBK="$NBKN" else dnfmsg=1 fi ;; l) tail -n1000 /var/log/remove-old-kernels.log && { echo -e "\n$(i18n "Tap spacebar to exit")\n"; read -r -n1; echo -en "${ClearLine}"; } ;; m) Normal=""; RemvCol=""; KeepCol=""; InfoCol=""; HdBgCol=""; WarnBgCol=""; ClearLine="" ;; v) printf '%s'"$(rpm -q remove-old-kernels)\n" exit 2 ;; h|?) clear; usage; exit 0 ;; esac done fi # Don't allow NBK < 2 whether it comes from CLI, script edit, config file or the moon if (( NBK < 2 )); then NBK=2 fi # Only use greeting in interactive mode if [[ "$MODE" == "I" ]]; then clear echo -e " $(i18n "Welcome to 'remove-old-kernels' Interactive")\n" fi # Use dnf kernel 'number to keep' if installed and greater than ours if (( dnfNBK > NBK )) || (( dnfmsg == 1 )); then NBK=$dnfNBK dnfmssg="${InfoCol}$(i18n "INFO: Number to keep is restricted to ")${dnfNBK}\ $(i18n ", by the dnf 'installonly_limit' set in /etc/dnf/dnf.conf")${Normal}" fi # Get info for status message if (( CRON == 1 )); then autostat="1"; else autostat="${Normal}${WarnBgCol}0${HdBgCol}"; fi if (( QA == 1 )) ; then qamssg="| Q "; fi if (( ALTCFG > 0 )); then cfgmssg="|${Normal}${WarnBgCol}F:${ALTCFG}${HdBgCol} "; fi # Check that the running kernel is still installed: https://bugs.mageia.org/show_bug.cgi?id=31015 [[ -e /lib/modules/$(uname -r) ]] || { echo -e "${RemvCol}$(i18n "FATAL: Has the running kernel been uninstalled since last boot? - Aborting.")"; exit 1; } # Get the full name of the current running kernel CURK=$(rpm -qf /lib/modules/"$(uname -r)") # Storage for the list of kernels to remove TMPKTR=$(mktemp) # Check storage usage on root partition OCCDISK1=$(df -B 1M -l --output=used / | tail -n1 | awk '{ print $1 }') # Pad translated strings for column 2 keepstr="$(i18n "Keep")" remstr="$(i18n "Remove")" if (( ${#keepstr} != ${#remstr} )); then if (( ${#keepstr} > ${#remstr} )); then while (( ${#remstr} < ${#keepstr} )); do remstr="${remstr} "; done else while (( ${#remstr} > ${#keepstr} )); do keepstr="${keepstr} "; done fi fi #========================= Analyse /boot/ ============================= NK=$(find /boot -name "vmlinuz*.mga[0-9]" | wc -l) # In automatic mode exit immediately if kernels in boot <= number allowed to be removed [[ "$MODE" = "A" ]] && (( NK < NBK )) && exit 0 #================================ Show status ============================== echo -e "${HdBgCol} System: $(cat /etc/mageia-release) | $(i18n "Kernels in") /boot/:${NK} | AUTO:$autostat | $(i18n "KEEP"):$NBK ${qamssg}${cfgmssg}${Normal} " #================================= Analyse rpms ============================= # Get master list from rpm -qa --last allkernels="$(rpm -qa --last|grep "kernel-")" # Get the kernel version and release required by the installed virtualbox-flavour-latest get_vbox_kern() { if echo "$allkernels"|grep -q "virtualbox-${kernelType}-latest"; then vb_latest_ver=$(echo "$allkernels"|cut -d' ' -f1|grep "virtualbox-${kernelType}-latest"|cut -d- -f5-|rev|cut -d. -f2-|rev) else qavkern=; return fi depkern=$(echo "$allkernels"|cut -d' ' -f1|grep "$vb_latest_ver"|grep -v latest|grep "virtualbox-kernel-[0-9]") kern_ver=$(echo "$depkern"|cut -d- -f3-|cut -d- -f1) kern_rel=$(echo "$depkern"|cut -d- -f5-|cut -d. -f1) (( ${#kern_ver} > 0 )) && (( ${#kern_rel} > 0 )) && qavkern="${kern_ver}-${kern_rel}" } # Get the kernel version and release required by the installed xtables-addons-flavour-latest get_xtables_kern() { if echo "$allkernels"|grep -q "xtables-addons-${kernelType}-latest"; then xt_latest_ver=$(echo "$allkernels"|cut -d' ' -f1|grep kernel-|grep "xtables-addons-kernel"|grep "latest"|cut -d- -f6-|rev|cut -d. -f2-|rev) else qaxkern=; return fi depkern=$(echo "$allkernels"|cut -d' ' -f1|grep "$xt_latest_ver"|grep "xtables-addons-kernel-[0-9]"|cut -d ' ' -f1) kern_ver=$(echo "$depkern"|cut -d- -f4) kern_rel=$(echo "$depkern"|cut -d- -f6 -|cut -d. -f1) (( ${#kern_ver} > 0 )) && (( ${#kern_rel} > 0 )) && qaxkern="${kern_ver}-${kern_rel}" } # Keep the -devel package for any installed kernel package depdev() { # Reset some vars pkg=; kpkgname=; nnk="" # Is current pkg a -devel if echo "${installedKernelPackage}" | grep -q "\-devel"; then # Get matching kernel package name kpkgname=$(echo "${installedKernelPackage}"| /usr/bin/sed -e 's/\-devel//') # Find kernel in installed list without timestamp for pkg in $(echo "$allkernels"|cut -d ' ' -f1); do # Look through installed kernels for kernel package if [[ ${pkg} == "${kpkgname}" ]]; then nnk="K"; REMVBL=0 # Is kernel flagged for removal? if grep -q "$kpkgname" "${TMPKTR}" ; then # Then this -devel is also removable nnk=""; REMVBL=1 fi # We are done here break fi done fi } # Exclude 'latest' packages from installed kernel list rpmqaList=$(echo "$allkernels"|grep -v latest) # Loop through kernel types in LISTK for kernelType in ${LISTK}; do (( QA == 1 )) && get_vbox_kern && get_xtables_kern installedKernelCounter=0; # Scan through installed kernels to match with kernel type in LISTK echo "$rpmqaList"|grep "${kernelType}-[0-9]"|while read -r installedKernel; do # Clear these for each loop NOTA=""; REMVBL=1 # Increment installedKernelCounter installedKernelCounter=$(( installedKernelCounter + 1)) # Add heading if there exists at least one installedKernel of this kernelType if (( installedKernelCounter == 1 )) ; then echo -ne "\r ==> ${kernelType}" echo "" fi # Remove time stamp installedKernelPackage="$(echo "${installedKernel}" | cut -d ' ' -f 1 )" # Check for current kernel if (( $(echo "${installedKernel}" | grep -c "${CURK}") == 1 )) ; then NOTA="U" # current kernel REMVBL=0 # not removable fi # Check if kernel is required by vbox latest kernel if (( QA == 1 )) && (( ${#qavkern} > 0 )) && (( $(echo "${installedKernel}" | grep -c "$qavkern") == 1 )); then NOTA="${NOTA}V" # Required by VBox REMVBL=0 # not removable fi # Check if kernel is required by xtables latest kernel if (( QA == 1 )) && (( ${#qaxkern} > 0 )) && (( $(echo "${installedKernel}" | grep -c "$qaxkern") == 1 )); then NOTA="${NOTA}X" # Required by xtables REMVBL=0 # not removable fi # If a kernel devel package has the corresponding kernel package keep it if (( QA == 1 )); then depdev NOTA="${NOTA}${nnk}" fi # Pad NOTA to 4 while (( ${#NOTA} < 4 )); do NOTA="${NOTA} "; done # Pad installedKernelCounter (col 1) to 3 while (( ${#installedKernelCounter} < 3 )); do installedKernelCounter="${installedKernelCounter} "; done if (( installedKernelCounter > NBK )); then if (( REMVBL != 1 )) ; then echo -e "\r ${installedKernelCounter}${KeepCol} : $keepstr: ${NOTA}: ${installedKernel} ${Normal}" else echo -e "\r ${installedKernelCounter}${RemvCol} : $remstr: ${NOTA}: ${installedKernel} ${Normal}" # Add package to removal list echo "${installedKernelPackage}" >> "${TMPKTR}" fi else echo -e "\r ${installedKernelCounter}${KeepCol} : $keepstr: ${NOTA}: ${installedKernel} ${Normal}" fi done done # Position 'in use' key under symbol below column 3 padUse=$((${#keepstr}+9)); padstr="" while (( ${#padstr} < padUse )); do padstr="${padstr} "; done echo -en "${ClearLine}" echo -e "${padstr}${KeepCol}U${InfoCol} = $(i18n "In use now")${Normal}" if (( ${#dnfmssg} > 0 ));then echo -e "${dnfmssg}"; fi #================================= Mode of execution =================== # Check there are kernels to remove nbktr=$(grep -vc devel < "${TMPKTR}") nbt=$(wc -l < "${TMPKTR}") if (( nbt != 0 )) ; then # Check if we are in preview mode if (( VISU == 1 )); then plural="s"; (( nbt == 1 )) && plural="" # Get singular/plural message without getting obfuscated by gettext if [[ "$plural" == "s" ]]; then echo "$(i18n "Commands that would be used"):" else echo "$(i18n "Command that would be used"):" fi # Print out the kernel commands for f in $(tac "${TMPKTR}"); do echo "urpme ${f}" done echo "$(i18n "Tap spacebar to exit")" read -n1 echo -en "${ClearLine}" # Delete temp file list of removeable kernels rm -f "${TMPKTR}" exit 0 fi # Warn if we are not root and exit hang until spacebar if (( UID > 0 )); then echo -e "${RemvCol}$(i18n "Must be root to allow removal")${Normal}\n$(i18n "Tap spacebar")" read -r -n1; echo -en "${ClearLine}" exit 0 fi if [[ "${MODE}" != "A" ]] ; then if (( DEBUG == 1 )) ; then echo -e "\n${KeepCol}>> $(i18n "Test mode is on - kernels will not be removed") <<${Normal}" fi plural="s"; (( nbktr == 1 )) && plural="" if [[ "$plural" == "s" ]]; then kernstr="$(i18n "kernels"):" else kernstr="$(i18n "kernel"):" fi if (( nbt == nbktr )); then read -p "$(i18n "Remove") ${nbt} $kernstr ? $(i18n "y/N/i (i=confirm for each)") " -n 1 response else read -p "$(i18n "Remove") ${nbt} (${nbktr} ${kernstr}) ? $(i18n "y/N/i (i=confirm for each)") " -n 1 response fi if [[ -z ${response} ]] ; then response="n" ; fi case ${response} in [Yy]) AUTO="--auto" MODE="A" echo -e " \n" ;; [Ii]) AUTO="--auto" MODE="I" echo " $(i18n "interactive")" ;; *) echo -e "\n$(i18n "Aborted")\n" rm -f "${TMPKTR}" exit 0 ;; esac fi #================================= Execution =========================== for installedKernelPackage in $(tac "${TMPKTR}") ; do if [[ ${MODE} = "I" ]] ; then # --- interactive mode --- read -p "$(i18n "Remove") ${installedKernelPackage} ? $(i18n "y/N/q (q=quit)") " -n 1 response echo -e " \n" if [[ -z ${response} ]]; then response="N" fi case ${response} in [Yy]) if (( DEBUG == 1 )) ; then echo -e "\n${InfoCol}$(i18n "DEBUG: Could execute: urpme") ${AUTO} ${installedKernelPackage}${Normal}" ((nbt--)) else urpme "${installedKernelPackage}" ((nbt--)) fi ;; [qQ]) echo -e "\n$(i18n "Aborted")" rm -f "${TMPKTR}"; exit 0 ;; *) echo " " ;; esac else # --- automatic mode --- AUTO="--auto" if (( DEBUG == 1 )) ; then echo -e "${InfoCol}$(i18n "DEBUG: Could execute: urpme") ${AUTO} ${installedKernelPackage}${Normal}" ((nbt--)) else # --- automatic execution --- # preload y and CR in auto mode echo -ne 'y\n' | urpme "${AUTO}" "${installedKernelPackage}" ((nbt--)) fi fi done NK=$(find /boot -name "vmlinuz*.mga[0-9]" | wc -l) OCCDISK2=$(df -B 1M -l --output=used / | tail -n1 | awk '{ print $1 }') echo -e "${HdBgCol} $(i18n "Gain"):$((OCCDISK1 - OCCDISK2)) MB - $(i18n "Kernels in") /boot/: ${NK} ${Normal}" fi [[ $MODE == "I" ]] && { echo "$(i18n "Tap spacebar to exit")"; read -n1; echo -en "${ClearLine}"; } rm -f "${TMPKTR}" # Run again if some removable kernels are left (( UID > 0 )) && exit 0 # Not root user so exit [[ ${MODE} != "I" ]] && exit 0 # Auto so exit (( nbt > 0 )) && ${0} # Root user and interactive so run again if removable kernels left
View Attachment As Raw
Actions:
View
Attachments on
bug 31969
: 13901