#!/bin/sh # $NetBSD: _NetBSD-pkgdb,v 1.2 2020/12/29 17:56:14 gdt Exp $ # Copyright The NetBSD Foundation 2020 # This script is for NetBSD only. # This script is intended to help with the process of migration of # PKG_DBDIR from /var/db/pkg to /usr/pkg/pkgdb on NetBSD. It can: # # "check" the system, changing nothing # # "fix" the system, leaving PKG_DBDIR as is and setting config files # to point to it. Thus, one can easily postpone migration. # # "migrate" the system, moving the contents of PKG_DBDIR to the new # location, and then setting pointers. # # In all cases, the script attempts to check assumptions it makes, and # avoid trouble if those assumptions are not true. # Note that PKG_DBDIR can be set in multiple places: # /etc/pkg_install.conf (base tools) # /usr/pkg/etc/pkg_install.conf (pkg tools) # /etc/mk.conf (variable when buidling) # /usr/pkgsrc/mk/ (default value of variable) # This script takes the position that the PKG_DBDIR should be # explicitly configured in pkg_install.conf in all cases, even if this # is provably not necessary. The theory is that a bit of explicit # config is much less work than mysterious debugging. If you don't # like this, you are welcome to undo it, only use "check", or do the # migration (or not) by hand. # This script is aimed at the common case where the database was in # /var/db/pkg and is moving to /usr/pkg/pkgdb, and sources are in # /usr/pkgsrc (or a place that /usr/pkgsrc is symlinked to). # This script should work to fix/migrate even on systems with older # pkgsrc, e.g. if one wanted to stay on 2020Q3 on a system where the # base system tools were updated. "Fix" will set variables so # everything should work correctly. # The script is designed to do incremental fixes, so if you migrate by # hand but do not patch up the refcount directory, or set variables, # it should (NB: This is UNTESTED!) do those steps when run. # The script is intentionally written to be easy to understand (as # long as one is comfortable with shell functions), rather than # compact, avoid repeated code, or elegant. # This script assumes: # NetBSD 8 or newer # pkgsrc after about 2020-12-20 (including 2020Q4) # pkgsrc PREFIX as /usr/pkg # old PKG_DBDIR as /var/db/pkg # new/future PKG_DBDIR as /usr/pkg # no symlinks in old or new PKG_DBDIR location # base pkg_install may be old, or may be after the 2020-12 commit/pullups # pkgsrc sources, if they exist, are in /usr/pkgsrc (perhaps a symlink) # Set pathnames. Do not expect to set these differently without # significant testing and thought. OLDDB=/var/db/pkg NEWDB=/usr/pkg/pkgdb CONF_BASE=/etc/pkg_install.conf CONF_PKG=/usr/pkg/etc/pkg_install.conf PKGSRC=/usr/pkgsrc # Shell functions to print messages of varying severity. (We choose # to avoid fancy formatting entirely in this script.) msg () { echo "$@" >&2 } warn () { echo "WARNING: $@" >&2 } fatal () { echo "FATAL: $@" >&2 exit 1 } usage () { msg "_NetBSD-pkgdb: Usage:" msg " check Check pkgdb status and explain it." msg " fix Point config files at pkgdb location (don't move it)." msg " migrate Check if migration can be done automatically, and if so do it." exit 1 } # Count problems. (Be sure not to use subshells when calling this.) problems=0 problem_pp () { problems=`expr $problems + 1` } problem_count () { echo $problems } dir_exists () { if [ ! -h "$1" -a -d "$1" ]; then echo true else echo false fi } # require_match string bool1 bool2 require_match () { if [ "$2" != "$3" ]; then fatal "mismatch $1" problem_pp fi } # Read pkg_install.conf PKG_DBDIR. One arg: config file get_pkg_install_conf_DBDIR () { CONF=$1 if [ "${CONF}" = "" ]; then fatal "missing argument" # dead code, left in case error is removed echo "" elif [ ! -f ${CONF} ]; then warn "${CONF} does not exist" echo "" else line=`egrep "^PKG_DBDIR.*=" ${CONF}` if [ "$line" = "" ]; then warn "${CONF} does not set PKG_DBDIR" echo "" else value=`echo $line | awk -F= '{print $2}'` echo $value fi fi } # Ensure prerequisites. do_prereq () { if [ `uname` != "NetBSD" ];then fatal "OS is not NetBSD" fi # Check a subdir, in case /usr/pkg is a symlink. if [ ! -d /usr/pkg/bin ]; then fatal "/usr/pkg/bin is not a directory" fi } # Check and maybe fix or migrate, depending on if fix=true or # migrate=true. migrate=true and fix=false is not allowed. do_steps () { ## STEP 0: Check if it is ok to operate at all. do_prereq ## STEP 1: Sanity check database and figure out where it is. # See if old or new exists, and check that they are in pairs. OLD_DB1=`dir_exists ${OLDDB}` OLD_DB2=`dir_exists ${OLDDB}.refcount` require_match "$OLDDB directory pair" $OLD_DB1 $OLD_DB2 NEW_DB1=`dir_exists ${NEWDB}` NEW_DB2=`dir_exists ${NEWDB}.refcount` require_match "$NEWDB directory pair" $NEW_DB1 $NEW_DB2 # Check that we have one PKG_DBDIR, not zero and not two if [ "$OLD_DB1" = false -a "$NEW_DB1" = false ]; then fatal "No PKG_DBDIR found" problem_pp fi if [ "$OLD_DB1" = true -a "$NEW_DB1" = true ]; then fatal "TWO copies of PKG_DBDIR found" problem_pp fi # Set a variable to the DBDIR we should be using, and the one we aren't. if [ "$OLD_DB1" = true ]; then PKG_DBDIR=$OLDDB PKG_DBDIR_NOT=$NEWDB elif [ "$NEW_DB1" = true ]; then PKG_DBDIR=$NEWDB PKG_DBDIR_NOT=$OLDDB else fatal "LOGIC ERROR IN SCRIPT 001" exit 1 fi unset OLD_DB1 unset OLD_DB2 unset NEW_DB1 unset OLD_DB2 echo "Found PKG_DBDIR as $PKG_DBDIR." ## Step 2a: check migrate/fix prereqs if [ "$fix" = true ]; then warn "THIS SCRIPT IS NOT YET ADEQUATELY TESTED." warn "Hit ^C NOW if you do not have a good backup." warn "Proceeding in 10 seconds..." sleep 10 if [ `id -u` != 0 ]; then fatal "Not root: will not be able to fix or migrate" fi fi ## Step 2: migrate if requested if [ "$migrate" = true ]; then if [ "$fix" != true ]; then fatal "LOGIC ERROR IN SCRIPT 002" fi if [ "$PKG_DBDIR" = "${NEWDB}" ]; then warn "migrate requested when already done" # This is not really an error. else msg "Doing migration..." msg "NB: refcount will have many fixups; this is normal." # Actual migration is easy. mv $OLDDB $NEWDB mv $OLDDB.refcount $NEWDB.refcount # Change our tracking variables so that later fixups do # the right thing. PKG_DBDIR=$NEWDB PKG_DBDIR_NOT=$OLDDB msg "Migration done, except for refcount fixups." fi fi ## Step 3: Check/fix refcount if [ "$fix" = true ]; then # Check all files, even though only some have paths. find ${PKG_DBDIR}.refcount -type f | while read file; do if egrep $PKG_DBDIR_NOT $file > /dev/null; then echo WRONG PATH $file sed -i -e "s,${PKG_DBDIR_NOT},${PKG_DBDIR}," $file problem_pp fi done fi ## Step 4: Check PKG_DBDIR variables and fix if requested. # Check that both pkg_install configs point to the actual PKG_DBDIR. conf_base_DBDIR=`get_pkg_install_conf_DBDIR $CONF_BASE` if [ "$conf_base_DBDIR" != "${PKG_DBDIR}" ]; then warn "$CONF_BASE does not set PKG_DBDIR to ${PKG_DBDIR}" if [ "$fix" = true ]; then [ -f ${CONF_BASE} ] && cp -p ${CONF_BASE} ${CONF_BASE}.pre-migration sed -i -e '/^PKG_DBDIR=/d' ${CONF_BASE} echo "PKG_DBDIR=${PKG_DBDIR}" >> ${CONF_BASE} fi problem_pp fi conf_pkg_DBDIR=`get_pkg_install_conf_DBDIR $CONF_PKG` if [ "$conf_pkg_DBDIR" != "${PKG_DBDIR}" ]; then warn "$CONF_PKG does not set PKG_DBDIR to ${PKG_DBDIR}" if [ "$fix" = true ]; then [ -f ${CONF_PKG} ] && cp -p ${CONF_PKG} ${CONF_PKG}.pre-migration sed -i -e '/^PKG_DBDIR=/d' ${CONF_PKG} echo "PKG_DBDIR=${PKG_DBDIR}" >> ${CONF_PKG} fi problem_pp fi # Check mk.conf via pkgsrc. Really, check that the variable is # set when running in an example package, because it is relatively # normal to use .include and we decline to re-implement make. if [ -d ${PKGSRC}/mk ]; then # Avoid a subshell so problem_pp works. SAVE=`pwd` cd $PKGSRC PKGSRC_DBDIR=`cd devel/m4 && make show-var VARNAME=PKG_DBDIR` if [ "$PKGSRC_DBDIR" != "${PKG_DBDIR}" ]; then warn "$PKG_DBDIR via pkgsrc/mk does not set PKG_DBDIR to ${PKG_DBDIR}" # This may have failed for other reasons, but if so running again won't # fix it. if [ "$fix" = true ]; then sed -i -e '/^PKG_DBDIR=/d' /etc/mk.conf echo "PKG_DBDIR=${PKG_DBDIR}" >> /etc/mk.conf fi problem_pp fi cd $SAVE unset SAVE else msg "NB: Did not find ${PKGSRC} -- did not check mk.conf" msg " If your system does not have sources, this is ok. If it does, symlink" msg " ${PKGSRC} to the sources and rerun the check." fi } main() { if [ "$#" != 1 ]; then usage fi case "$1" in check) do_steps ;; fix) fix=true; do_steps ;; migrate) fix=true migrate=true; do_steps ;; *) warn Unknown option $1 usage ;; esac count=`problem_count` if [ "$count" = 0 ]; then msg "No problems found." else msg "TOTAL PROBLEMS FOUND: $count" msg "Re-run script until there are no problems!" fi } main "$@"