#!/usr/bin/gawk -f # # rm-old-kernels, convert to awk program 2008-07-25, last edit 2008-07-29 # (was a bash script last updated 2006-12-20) # # remove old kernel files that are no longer referenced by /etc/lilo.conf # # Copyright (C) 2006, 2008 Grant Coady GPLv2 # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details: # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html # #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # WARNING: This script deletes kernel files! Take care. #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # assumptions # ```````````` # You build your own custom kernels and use the conventional kernel naming # scheme like this example: # # /boot/config-2.6.16.20 # /boot/bzImage-2.6.16.20 (or vmlinuz-, vmlinux-, ...) # /boot/System.map-2.6.16.20 # /lib/modules/2.6.16.20 # # and the next older kernel would be deleted by: # rm -f /boot/*2.6.16.19; rm -rf /lib/modules/2.6.16.19 # # You have not changed slackware's default 'ls -l' listing format. # #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # notes # `````` # The script recognises the odd slackware kernel names like # 'vmlinuz-ide-2.4.33.3' from slackware-11.0 and supports slackware's # use of kernel name symlink for the distribution kernels. # # Not tested with initrds as I don't use them, if your initrd files follow # the initrd-$(uname -r) naming scheme they will be removed if obsolete, # feedback very welcome if this is not the case. # # Written on slackware-11.0 with gawk 3.1.5, tested on slackware-11.0 and # slackware-12.1 systems. This program also runs from an NFS export. # # Program must be run as root, it fails when run as normal user with a # (Permission denied) error when attempting to read /etc/lilo.conf. # #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # how it works # ````````````` # Scan the /etc/lilo.conf file and collect kernel references from the # 'image = /boot/...' lines, this reference may be to a kernel image file # or to a symlink. # # Get a list of 'symlink -> kernel_version' from /boot. # # Get a list of kernel_versions from /boot. # # Get a list of kernel_versions from /lib/modules/*. # # Process symlinks and warn if unused in lilo.conf # # Warn about commented out kernel_versions in lilo.conf that trigger removals # # Scan the lilo list and remove referenced items from the boot and module # lists. What is left in the boot and module lists are the non-referenced # files to be removed. # # Confirm removal of the items, then remove them. # #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # changelog # `````````` # 2008-08-12 # o deal with slack-11.0's use of bare 'vmlinuz' instead of symlink # # 2008-07-29 # o fix an off-by-one in found kernels display # # 2008-07-28 # o add optional selective removal of non-referenced items: # "Use all|y|n|q for all, yes, no or quit, default no:" # # 2008-07-27 # o better symlink processing, tell user if symlink not used in lilo.conf # o warn if commented out 'image = /boot/some_kernel' will cause removal # o side by side listing of kernel versions found # # 2008-07-26 # o first release as gawk program # #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # BEGIN { print "\n rm-old-kernels:" print " remove old kernel files not referenced by /etc/lilo.conf" # get /boot file listing, doing it properly with unique temp file cmd = "mktemp /tmp/rm-old.XXXXXX"; cmd | getline bootlist; close(cmd) system("ls -l /boot > " bootlist) # get /lib/modules directory listing cmd = "mktemp /tmp/rm-old.XXXXXX"; cmd | getline modslist; close(cmd) system("ls /lib/modules > " modslist) # setup the file parser ARGV[ARGC++] = "/etc/lilo.conf" ARGV[ARGC++] = bootlist ARGV[ARGC++] = modslist print "\n Reading:" } function get_kernel_version(k, a, n, i, s) { # do it this way 'cos a simple regexp was too greedy n = split(k, a, "-"); s = ""; i = 2 while (a[i] !~ /^[1-9]/) ++i for (; i <= n; i++) s = (s ? s "-" a[i] : a[i]) return s } FNR == 1 && FILENAME == "/etc/lilo.conf" { print " /etc/lilo.conf" } FNR == 1 && FILENAME == bootlist { print " /boot" } FNR == 1 && FILENAME == modslist { print " /lib/modules" } # Read lilo.conf, find symlink references and kernel version references # Allow for leading whitespace before 'image = ...', optional whitespace # around the '=' character, and a possible trailing '# comment' FNR == NR && /image/ { gsub(/[ \t]*/, "", $0) # eat whitespace # process a commented out 'image = /boot/...' line if ($0 ~ /^#/ && $0 ~ /image=\/boot\//) { sub(/image=\/boot\//, " ", $0) sub(/#.*$/, "", $2) ++rwarn[get_kernel_version($2)] next } # skip if the word 'image' not at the start of the line if ($0 !~ /^image/) next # get kernel version, it may be symlink or filename sub(/^image=\/boot\//, "", $0) sub(/#.*$/, "", $0) # process symlink reference: vmlinux or vmlinuz if ($1 ~ /^vmlinu[xz]$/) { ++rlilo[$1] next } ++rlilo[get_kernel_version($1)] next } # Slack-11.0 has bare vmlinuz, deal with it: 1 of 2 FILENAME == bootlist && $8 ~ /^vmlinu[xz]$/ && $9 != "->" { ++rboot[$8] next } # Slack-11.0 has bare vmlinuz, deal with it: 2 of 2 FILENAME == bootlist && $8 ~ /^vmlinu[xz]-/ && $8 ~ /-[0-9\.]+$/ { kv = get_kernel_version($8) ++rlilo[kv] # pretend kernel version was also found in lilo ++rboot[kv] next } # Read /boot listing, find any kernel symlinks: 'symlink -> kernel-version', # only recognise 'vmlinux' or 'vmlinuz' as valid symlink names. FILENAME == bootlist && $8 ~ /^vmlinu[xz]$/ && $9 == "->" { rlink[$8] = get_kernel_version($10) } # Read /boot listing, find kernel versions. FILENAME == bootlist && $8 ~ /-[1-9]/ { ++rboot[get_kernel_version($8)] next } # Read /lib/modules listing, find kernel versions. FILENAME == modslist { ++rmods[$1] } END { # Done with directory listing temporary files, remove them system("rm -f " bootlist " " modslist) # Process symlinks n = 0; for (i in rlink) ++n if (n) { print "\n Process symlinks:" for (i in rlink) { printf " %s -> %s: ", i, rlink[i] if (rlilo[i]) { # replace symlink with target kernel_version delete rlilo[i] ++rlilo[rlink[i]] print "okay" } else { print "not used in lilo.conf" delete rlilo[i] } } } # Process any commented out kernel versions # okay if the kernel_version is referenced elsewhere in lilo for (i in rlilo) delete rwarn[i] # not okay if the commented kernel_version references files for (i in rwarn) { if (i in rboot || i in rmods) ++warn[i] } n = 0; for (i in warn) ++n if (n) { print "\n Warning: the following kernel versions are present but" print " commented out in lilo.conf, they reference /boot and/or" print " /lib/modules, and will cause file loss if you continue." # present list unsorted for (i in warn) print " " i } # Collect all kernel_versions found for (i in rboot) ++rversion[i] for (i in rlilo) ++rversion[i] for (i in rmods) ++rversion[i] # Print in multi-column format, sorted nr_version = asorti(rversion, version) print "\n Kernel versions found:" printf " %-25s %-25s %-25s\n", " /etc/lilo.conf", " /boot", " /lib/modules" for (i = 1; i <= nr_version; i++) { l = b = m = "-" for (j in rlilo) if (j == version[i]) l = j for (j in rboot) if (j == version[i]) b = j for (j in rmods) if (j == version[i]) m = j printf " %-25s %-25s %-25s\n", " " l, " " b, " " m } # Scan lilo kernel versions and remove them from boot and modules # removal candidate lists, this is the serious part, too easy! for (i in rlilo) { delete rboot[i] delete rmods[i] } # Count remaining removal candidates, all done if nothing to remove n = 0; for (i in rboot) ++n; for (i in rmods) ++n if (!n) { print "\n Nothing to remove, done.\n" exit 0 } # List candidates for removal, sorted nr_boot = asorti(rboot, boot); nr_mods = asorti(rmods, mods) nr_remove = 0 for (i = 1; i <= nr_boot; i++) remove[++nr_remove] = "rm -f /boot/*" boot[i] for (i = 1; i <= nr_mods; i++) remove[++nr_remove] = "rm -rf /lib/modules/" mods[i] print ""; x = " Candidates for removal:" for (i = 1; i <= nr_version; i++) { b = m = "-" for (j in boot) if (boot[j] == version[i]) b = boot[j] for (j in mods) if (mods[j] == version[i]) m = mods[j] if (b == "-" && m == "-") continue printf " %-25s %-25s %-25s\n", x, " " b, " " m x = "" } print "\n Removal: use all|y|n|q for all, yes, no or quit, default no:" cmd = "read; echo $REPLY" for (i = 1; i <= nr_remove; i++) { printf " %s ? ", remove[i] cmd | getline x; close(cmd); x = tolower(x) if (x ~ /^q/) { print " quit" break } if (x ~ /^y/) { if (!test) system(remove[i]) print " removed" continue } if (x == "all") { if (!test) system(remove[i]) print " removed" ++i for (; i <= nr_remove; i++) { print " " remove[i] if (!test) system(remove[i]) } break } print " skip" } print " Done!" } # end