March 10, 2008

Recovering from portupgrade(1) crashes

Categories: FreeBSD, Sysadmin.

According to FreshPorts, portupgrade is a FreeBSD ports/packages administration and management tool suite. It features a program named portupgrade(1) that can help system administrators in updating third party software packages on FreeBSD.

In the beginning of February 2008, portupgrade was updated to the 2.4 branch which used to be the -devel one. This major update was already available as ports-mngt/portupgrade-devel from a while but serious problems where discovered only after the program was updated.

When upgrading my computer to FreeBSD 7.0, I run into such problems and considered various ways of recovering: I was recompiling all my ports and I really didn't want to compile anything that had already been rebuilt.

Parse portupgrade(1) output

For critical updates, it can be a good idea to save portupgrade(1)'s output so that you can check it if some service goes wrong (maybe an information message about manual action to perform was displayed after install). You can achieve this either using the script(1) utility or standard shell redirections.

# portupgrade -fa 2>&1 | tee portupgrade.log

If something goes wrong, it is possible to have a list of all reinstalled ports searching for lines beginning with ===> Registering installation for and tell portupgrade(1) to exclude the port from the update:

# portupgrade -fa `awk '
  /^===>   Registering installation for/ {
    print "-x "$5
	}' portupgrade.log` 2>&1 | tee -a portupgrade.log

Note the extra -a argument to tee(1) to avoid loosing the log contents.

Guess what is left to be done

When portupgrade(1) crashes, it is easy to scroll up the output and see what port caused the problem. Once it is fixed, it is possible run the upgrade command again excluding already installed ports. The idea is so to get the update sequence. This can be done this way:

# portupgrade -fan | awk \
  ' /	+/ { 
    print $3;
  }' > /tmp/update-seq

Using this list, it is possible to tell portupgrade(1) which packages not to update:

# portupgrade -fa $( awk "
  /$(head -n1 /tmp/update-seq)/,/evince-2.21.91_1/ {
    print '-x '$1
  } " /tmp/update-seq )

The main drawback of this method is that the update order depends on the installed ports dependencies. If during the update a new port is installed (new dependency) or a dependency is changed, the global update sequence is changed and the update-seq file is wrong and this method is likely to fail.

Use your packages

If you build packages for your ports, a good way of seeing what is not up-to-date is to compare packages last modification time and a file that should be older. In the case of a system update, your kernel is a good candidate ;-).

First, cleanup pour package directory so that the symbolic links in /usr/ports/packages/Latest only reference installed ports:

# rm `portsclean -P | awk \
  ' $0 ~ /^Origin not found:/ {
    print $5

You can then recompile all ports older than the kernel:

# cd /usr/ports/packages/Latest
# portupgrade -f `find . '!' -newer /boot/kernel/kernel |
                    sed 's/^.*\/\(.*\)\.tbz$/\1/'`

No Comments Yet

Comments RSS feed | Leave a Reply…