]> Zhao Yanbai Git Server - minix.git/commitdiff
Importing postinstall
authorLionel Sambuc <lionel@minix3.org>
Thu, 11 Oct 2012 08:46:39 +0000 (10:46 +0200)
committerLionel Sambuc <lionel@minix3.org>
Mon, 22 Oct 2012 09:37:43 +0000 (11:37 +0200)
Change-Id: I5813064164e2d6ca4cf353fb184194b2aaa8caa3

releasetools/nbsd_ports
usr.sbin/postinstall/Makefile [new file with mode: 0644]
usr.sbin/postinstall/postinstall [new file with mode: 0755]
usr.sbin/postinstall/postinstall.8 [new file with mode: 0644]

index 241c1c00cc2e7ea488dbbb50fc97d32f3452d94d..010aaa6a6a88bf89b54e40f91a724fbd20dc9f12 100644 (file)
@@ -2,6 +2,7 @@
 # Timestamp in UTC,minixpath,netbsdpath
 # minixpath:  path in Minix source tree (starting from /usr/src/)
 # netbsdpath: path in BSD source tree (starting from src/)
+2012/10/17 12:00:00,usr.sbin/postinstall
 2012/05/01 16:16:12,external/bsd/libarchive
 2012/02/10 16:16:12,usr.sbin/chroot
 2011/01/17 18:11:10,usr.bin/ldd
diff --git a/usr.sbin/postinstall/Makefile b/usr.sbin/postinstall/Makefile
new file mode 100644 (file)
index 0000000..47f8c6b
--- /dev/null
@@ -0,0 +1,9 @@
+# $NetBSD: Makefile,v 1.1 2005/04/17 15:15:49 lukem Exp $
+
+FILES=         postinstall
+MAN=           postinstall.8
+
+FILESDIR=      /usr/sbin
+FILESMODE=     ${BINMODE}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/postinstall/postinstall b/usr.sbin/postinstall/postinstall
new file mode 100755 (executable)
index 0000000..40ba131
--- /dev/null
@@ -0,0 +1,2108 @@
+#!/bin/sh
+#
+# $NetBSD: postinstall,v 1.147 2012/09/22 09:20:06 ast Exp $
+#
+# Copyright (c) 2002-2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Luke Mewburn.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``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 FOUNDATION OR CONTRIBUTORS
+# 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.
+#
+# postinstall
+#      Check for or fix configuration changes that occur
+#      over time as NetBSD evolves.
+#
+
+#
+# XXX BE SURE TO USE ${DEST_DIR} PREFIX BEFORE ALL REAL FILE OPERATIONS XXX
+#
+
+#
+# checks to add:
+#      - sysctl(8) renames (net.inet6.ip6.bindv6only -> net.inet6.ip6.v6only)
+#      - de* -> tlp* migration (/etc/ifconfig.de*, $ifconfig_de*,
+#        dhclient.conf, ...) ?
+#      - support quiet/verbose mode ?
+#      - differentiate between failures caused by missing source
+#        and real failures
+#      - install moduli into usr/share/examples/ssh and use from there?
+#      - differentiate between "needs fix" versus "can't fix" issues
+#
+
+# This script is executed as part of a cross build.  Allow the build
+# environment to override the locations of some tools.
+: ${AWK:=awk}
+: ${DB:=db}
+: ${GREP:=grep}
+: ${HOST_SH:=sh}
+: ${MAKE:=make}
+: ${PWD_MKDB:=/usr/sbin/pwd_mkdb}
+: ${STAT:=stat}
+
+#
+#      helper functions
+#
+
+err()
+{
+       exitval=$1
+       shift
+       echo 1>&2 "${PROGNAME}: $*"
+       if [ -n "${SCRATCHDIR}" ]; then
+           /bin/rm -rf "${SCRATCHDIR}"
+       fi
+       exit ${exitval}
+}
+
+warn()
+{
+       echo 1>&2 "${PROGNAME}: $*"
+}
+
+msg()
+{
+       echo "  $*"
+}
+
+mkdtemp()
+{
+       # Make sure we don't loop forever if mkdir will always fail.
+       [ -d /tmp ] || err 2 /tmp is not a directory
+       [ -w /tmp ] || err 2 /tmp is not writable
+
+       _base="/tmp/_postinstall.$$"
+       _serial=0
+
+       while true; do
+               _dir="${_base}.${_serial}"
+               mkdir -m 0700 "${_dir}" && break
+               _serial=$((${_serial} + 1))
+       done
+       echo "${_dir}"
+}
+
+# Quote args to make them safe in the shell.
+# Usage: quotedlist="$(shell_quote args...)"
+#
+# After building up a quoted list, use it by evaling it inside
+# double quotes, like this:
+#    eval "set -- $quotedlist"
+# or like this:
+#    eval "\$command $quotedlist \$filename"
+shell_quote()
+{
+       local result=''
+       local arg
+       for arg in "$@" ; do
+               # Append a space if necessary
+               result="${result}${result:+ }"
+               # Convert each embedded ' to '\'',
+               # then insert ' at the beginning of the first line,
+               # and append ' at the end of the last line.
+               result="${result}$(printf "%s\n" "$arg" | \
+                       sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/")"
+       done
+       printf "%s\n" "$result"
+}
+
+
+# additem item description
+#      Add item to list of supported items to check/fix,
+#      which are checked/fixed by default if no item is requested by user.
+#
+additem()
+{
+       [ $# -eq 2 ] || err 3 "USAGE: additem item description"
+       defaultitems="${defaultitems}${defaultitems:+ }$1"
+       eval desc_$1=\"$2\"
+}
+
+# adddisableditem item description
+#      Add item to list of supported items to check/fix,
+#      but execute the item only if the user asks for it explicitly.
+#
+adddisableditem()
+{
+       [ $# -eq 2 ] || err 3 "USAGE: adddisableditem item description"
+       otheritems="${otheritems}${otheritems:+ }$1"
+       eval desc_$1=\"$2\"
+}
+
+# checkdir op dir mode
+#      Ensure dir exists, and if not, create it with the appropriate mode.
+#      Returns 0 if ok, 1 otherwise.
+#
+check_dir()
+{
+       [ $# -eq 3 ] || err 3 "USAGE: check_dir op dir mode"
+       _cdop="$1"
+       _cddir="$2"
+       _cdmode="$3"
+       [ -d "${_cddir}" ] && return 0
+       if [ "${_cdop}" = "check" ]; then
+               msg "${_cddir} is not a directory"
+               return 1
+       elif ! mkdir -m "${_cdmode}" "${_cddir}" ; then
+               msg "Can't create missing ${_cddir}"
+               return 1
+       else
+               msg "Missing ${_cddir} created"
+       fi
+       return 0
+}
+
+# check_ids op type file id [...]
+#      Check if file of type "users" or "groups" contains the relevant IDs
+#      Returns 0 if ok, 1 otherwise.
+#      
+check_ids()
+{
+       [ $# -ge 4 ] || err 3 "USAGE: checks_ids op type file id [...]"
+       _op="$1"
+       _type="$2"
+       _file="$3"
+       shift 3
+       #_ids="$@"
+
+       if [ ! -f "${_file}" ]; then
+               msg "${_file} doesn't exist; can't check for missing ${_type}"
+               return 1
+       fi
+       if [ ! -r "${_file}" ]; then
+               msg "${_file} is not readable; can't check for missing ${_type}"
+               return 1
+       fi
+       _notfixed=""
+       if [ "${_op}" = "fix" ]; then
+               _notfixed="${NOT_FIXED}"
+       fi
+       _missing="$(${AWK} -F: '
+               BEGIN {
+                       for (x = 1; x < ARGC; x++)
+                               idlist[ARGV[x]]++
+                       ARGC=1
+               }
+               {
+                       found[$1]++
+               }
+               END {
+                       for (id in idlist) {
+                               if (! (id in found))
+                                       print id
+                       }
+               }
+       ' "$@" < "${_file}")"   || return 1
+       if [ -n "${_missing}" ]; then
+               msg "Missing ${_type}${_notfixed}:" $(echo ${_missing})
+               return 1
+       fi
+       return 0
+}
+
+# populate_dir op onlynew src dest mode file [file ...]
+#      Perform op ("check" or "fix") on files in src/ against dest/
+#      If op = "check" display missing or changed files, optionally with diffs.
+#      If op != "check" copies any missing or changed files.
+#      If onlynew evaluates to true, changed files are ignored.
+#      Returns 0 if ok, 1 otherwise.
+#
+populate_dir()
+{
+       [ $# -ge 5 ] || err 3 "USAGE: populate_dir op onlynew src dest mode file [...]"
+       _op="$1"
+       _onlynew="$2"
+       _src="$3"
+       _dest="$4"
+       _mode="$5"
+       shift 5
+       #_files="$@"
+
+       if [ ! -d "${_src}" ]; then
+               msg "${_src} is not a directory; skipping check"
+               return 1
+       fi
+       check_dir "${_op}" "${_dest}" 755 || return 1
+
+       _cmpdir_rv=0
+       for f in "$@"; do
+               fs="${_src}/${f}"
+               fd="${_dest}/${f}"
+               _error=""
+               if [ ! -f "${fd}" ]; then
+                       _error="${fd} does not exist"
+               elif ! cmp -s "${fs}" "${fd}" ; then
+                       if $_onlynew; then      # leave existing ${fd} alone
+                               continue;
+                       fi
+                       _error="${fs} != ${fd}"
+               else
+                       continue
+               fi
+               if [ "${_op}" = "check" ]; then
+                       msg "${_error}"
+                       if [ -n "${DIFF_STYLE}" -a -f "${fd}" ]; then
+                               diff -${DIFF_STYLE} ${DIFF_OPT} "${fd}" "${fs}"
+                       fi
+                       _cmpdir_rv=1
+               elif ! rm -f "${fd}" ||
+                    ! cp -f "${fs}" "${fd}"; then
+                       msg "Can't copy ${fs} to ${fd}"
+                       _cmpdir_rv=1
+               elif ! chmod "${_mode}" "${fd}"; then
+                       msg "Can't change mode of ${fd} to ${_mode}"
+                       _cmpdir_rv=1
+               else
+                       msg "Copied ${fs} to ${fd}"
+               fi
+       done
+       return ${_cmpdir_rv}
+}
+
+# compare_dir op src dest mode file [file ...]
+#      Perform op ("check" or "fix") on files in src/ against dest/
+#      If op = "check" display missing or changed files, optionally with diffs.
+#      If op != "check" copies any missing or changed files.
+#      Returns 0 if ok, 1 otherwise.
+#
+compare_dir()
+{
+       [ $# -ge 4 ] || err 3 "USAGE: compare_dir op src dest mode file [...]"
+       _op="$1"
+       _src="$2"
+       _dest="$3"
+       _mode="$4"
+       shift 4
+       #_files="$@"
+
+       populate_dir "$_op" false "$_src" "$_dest" "$_mode" "$@"
+}
+
+# move_file op src dest --
+#      Check (op == "check") or move (op != "check") from src to dest.
+#      Returns 0 if ok, 1 otherwise.
+#
+move_file()
+{
+       [ $# -eq 3 ] || err 3 "USAGE: move_file op src dest"
+       _fm_op="$1"
+       _fm_src="$2"
+       _fm_dest="$3"
+
+       if [ -f "${_fm_src}" -a ! -f "${_fm_dest}" ]; then
+               if [ "${_fm_op}" = "check" ]; then
+                       msg "Move ${_fm_src} to ${_fm_dest}"
+                       return 1
+               fi
+               if ! mv "${_fm_src}" "${_fm_dest}"; then
+                       msg "Can't move ${_fm_src} to ${_fm_dest}"
+                       return 1
+               fi
+               msg "Moved ${_fm_src} to ${_fm_dest}"
+       fi
+       return 0
+}
+
+# rcconf_is_set op name var [verbose] --
+#      Load the rcconf for name, and check if obsolete rc.conf(5) variable
+#      var is defined or not.
+#      Returns 0 if defined (even to ""), otherwise 1.
+#      If verbose != "", print an obsolete warning if the var is defined.
+#
+rcconf_is_set()
+{
+       [ $# -ge 3 ] || err 3 "USAGE: rcconf_is_set op name var [verbose]"
+       _rcis_op="$1"
+       _rcis_name="$2"
+       _rcis_var="$3"
+       _rcis_verbose="$4"
+       _rcis_notfixed=""
+       if [ "${_rcis_op}" = "fix" ]; then
+               _rcis_notfixed="${NOT_FIXED}"
+       fi
+       (
+               for f in \
+                   "${DEST_DIR}/etc/rc.conf" \
+                   "${DEST_DIR}/etc/rc.conf.d/${_rcis_name}"; do
+                       [ -f "${f}" ] && . "${f}"
+               done
+               eval echo -n \"\${${_rcis_var}}\" 1>&3
+               if eval "[ -n \"\${${_rcis_var}}\" \
+                           -o \"\${${_rcis_var}-UNSET}\" != \"UNSET\" ]"; then
+                       if [ -n "${_rcis_verbose}" ]; then
+                               msg \
+    "Obsolete rc.conf(5) variable '\$${_rcis_var}' found.${_rcis_notfixed}"
+                       fi
+                       exit 0
+               else
+                       exit 1
+               fi
+       )
+}
+
+# rcvar_is_enabled var
+#      Check if rcvar is enabled
+#
+rcvar_is_enabled()
+{
+       [ $# -eq 1 ] || err 3 "USAGE: rcvar_is_enabled var"
+       _rcie_var="$1"
+       (
+               [ -f "${DEST_DIR}/etc/rc.conf" ] && . "${DEST_DIR}/etc/rc.conf"
+               eval _rcie_val="\${${_rcie_var}}"
+               case $_rcie_val in
+               #       "yes", "true", "on", or "1"
+               [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
+                       exit 0
+                       ;;
+
+               *)
+                       exit 1
+                       ;;
+               esac
+       )
+}
+
+# find_file_in_dirlist() file message dir1 [...] --
+#      Find which directory file is in, and sets ${dir} to match.
+#      Returns 0 if matched, otherwise 1 (and sets ${dir} to "").
+#
+#      Generally, check the directory for the "checking from source" case,
+#      and then the directory for the "checking from extracted etc.tgz" case.
+#
+find_file_in_dirlist()
+{
+       [ $# -ge 3 ] || err 3 "USAGE: find_file_in_dirlist file msg dir1 [...]"
+
+       _file="$1" ; shift
+       _msg="$1" ; shift
+       _dir1st=        # first dir in list
+       for dir in "$@"; do
+               : ${_dir1st:="${dir}"}
+               if [ -f "${dir}/${_file}" ]; then
+                       if [ "${_dir1st}" != "${dir}" ]; then
+                               msg \
+    "(Checking for ${_msg} from ${dir} instead of ${_dir1st})"
+                       fi
+                       return 0
+               fi
+       done
+       msg "Can't find source directory for ${_msg}"
+       return 1
+}
+
+# file_exists_exact path
+#      Returns true if a file exists in the ${DEST_DIR} whose name
+#      is exactly ${path}, interpreted in a case-sensitive way
+#      even if the underlying file system is case-insensitive.
+#
+#      The path must begin with '/' or './', and is interpreted as
+#      being relative to ${DEST_DIR}.
+#
+file_exists_exact()
+{
+       [ -n "$1" ] || err 3 "USAGE: file_exists_exact path"
+       _path="${1#.}"
+       [ -h "${DEST_DIR}${_path}" ] || \
+               [ -e "${DEST_DIR}${_path}" ] || return 1
+       while [ "${_path}" != "/" ] ; do
+               _dirname="$(dirname "${_path}" 2>/dev/null)"
+               _basename="$(basename "${_path}" 2>/dev/null)"
+               ls -fa "${DEST_DIR}${_dirname}" 2> /dev/null \
+                       | ${GREP} -F -x "${_basename}" >/dev/null \
+                       || return 1
+               _path="${_dirname}"
+       done
+       return 0
+}
+
+# obsolete_paths op
+#      Obsolete the list of paths provided on stdin.
+#      Each path is relative to ${DEST_DIR}, and should
+#      be an absolute path or start with `./'.
+#
+obsolete_paths()
+{
+       [ -n "$1" ] || err 3 "USAGE: obsolete_paths  fix|check"
+       op="$1"
+
+       failed=0
+       while read ofile; do
+                if ! file_exists_exact "${ofile}"; then
+                    continue
+                fi
+               ofile="${DEST_DIR}${ofile#.}"
+               cmd="rm"
+               ftype="file"
+               if [ -h "${ofile}" ]; then
+                       ftype="link"
+               elif [ -d "${ofile}" ]; then
+                       ftype="directory"
+                       cmd="rmdir"
+               fi
+               if [ "${op}" = "check" ]; then
+                       msg "Remove obsolete ${ftype} ${ofile}"
+                       failed=1
+               elif ! eval "${cmd} \${ofile}"; then
+                       msg "Can't remove obsolete ${ftype} ${ofile}"
+                       failed=1
+               else
+                       msg "Removed obsolete ${ftype} ${ofile}"
+               fi
+       done
+       return ${failed}
+}
+
+# obsolete_libs dir
+#      Display the minor/teeny shared libraries in dir that are considered
+#      to be obsolete.
+#
+#      The implementation supports removing obsolete major libraries
+#      if the awk variable AllLibs is set, although there is no way to
+#      enable that in the enclosing shell function as this time.
+#
+obsolete_libs()
+{
+       [ $# -eq 1 ] || err 3 "USAGE: obsolete_libs dir"
+       dir="$1"
+
+       _obsolete_libs "${dir}"
+       _obsolete_libs "/usr/libdata/debug/${dir}"
+}
+
+_obsolete_libs()
+{ 
+       dir="$1"
+
+       (
+
+       if [ ! -e "${DEST_DIR}/${dir}" ]
+       then
+               return 0
+       fi
+
+       cd "${DEST_DIR}/${dir}" || err 2 "can't cd to ${DEST_DIR}/${dir}"
+       echo lib*.so.* \
+       | tr ' ' '\n' \
+       | ${AWK} -v LibDir="${dir}/" '
+#{
+
+function digit(v, c, n) { return (n <= c) ? v[n] : 0 }
+
+function checklib(results, line, regex) {
+       if (! match(line, regex))
+               return
+       lib = substr(line, RSTART, RLENGTH)
+       rev = substr($0, RLENGTH+1)
+       if (! (lib in results)) {
+               results[lib] = rev
+               return
+       }
+       orevc = split(results[lib], orev, ".")
+       nrevc = split(rev, nrev, ".")
+       maxc = (orevc > nrevc) ? orevc : nrevc
+       for (i = 1; i <= maxc; i++) {
+               res = digit(orev, orevc, i) - digit(nrev, nrevc, i)
+               if (res < 0) {
+                       print LibDir lib results[lib]
+                       results[lib] = rev
+                       return
+               } else if (res > 0) {
+                       print LibDir lib rev
+                       return
+               }
+       }
+}
+
+/^lib.*\.so\.[0-9]+\.[0-9]+(\.[0-9]+)?(\.debug)?$/ {
+       if (AllLibs)
+               checklib(minor, $0, "^lib.*\\.so\\.")
+       else
+               checklib(found, $0, "^lib.*\\.so\\.[0-9]+\\.")
+}
+
+/^lib.*\.so\.[0-9]+$/ {
+       if (AllLibs)
+               checklib(major, $0, "^lib.*\\.so\\.")
+}
+
+#}'
+       
+       )
+}
+
+# modify_file op srcfile scratchfile awkprog
+#      Apply awkprog to srcfile sending output to scratchfile, and
+#      if appropriate replace srcfile with scratchfile.
+#
+modify_file()
+{
+       [ $# -eq 4 ] || err 3 "USAGE: modify_file op file scratch awkprog"
+
+       _mfop="$1"
+       _mffile="$2"
+       _mfscratch="$3"
+       _mfprog="$4"
+       _mffailed=0
+
+       ${AWK} "${_mfprog}" < "${_mffile}" > "${_mfscratch}"
+       if ! cmp -s "${_mffile}" "${_mfscratch}"; then
+               diff "${_mffile}" "${_mfscratch}" > "${_mfscratch}.diffs"
+               if [ "${_mfop}" = "check" ]; then
+                       msg "${_mffile} needs the following changes:"
+                       _mffailed=1
+               elif ! rm -f "${_mffile}" ||
+                    ! cp -f "${_mfscratch}" "${_mffile}"; then
+                       msg "${_mffile} changes not applied:"
+                       _mffailed=1
+               else
+                       msg "${_mffile} changes applied:"
+               fi
+               while read _line; do
+                       msg "   ${_line}"
+               done < "${_mfscratch}.diffs"
+       fi
+       return ${_mffailed}
+}
+
+
+# contents_owner op directory user group
+#      Make sure directory and contents are owned (and group-owned)
+#      as specified.
+#
+contents_owner()
+{
+       [ $# -eq 4 ] || err 3 "USAGE: contents_owner op dir user group"
+
+       _op="$1"
+       _dir="$2"
+       _user="$3"
+       _grp="$4"
+
+       if [ "${_op}" = "check" ]; then
+               if [ ! -z "`find "${_dir}" \( ! -user "${_user}" \) -o \
+                   \( ! -group "${_grp}" \)`" ]; then
+                       msg \
+    "${_dir} and contents not all owned by ${_user}:${_grp}"
+                       return 1
+               else
+                       return 0
+               fi
+       elif [ "${_op}" = "fix" ]; then
+               find "${_dir}" \( \( ! -user "${_user}" \) -o \
+               \( ! -group "${_grp}" \) \) -a -print0 \
+               | xargs -0 chown "${_user}:${_grp}"
+       fi      
+}
+
+# get_makevar var [var ...]
+#      Retrieve the value of a user-settable system make variable
+get_makevar()
+{
+       $SOURCEMODE || err 3 "get_makevar must be used in source mode"
+       [ $# -eq 0 ] && err 3 "USAGE: get_makevar var [var ...]"
+
+       for _var in "$@"; do
+               _value="$(echo '.include <bsd.own.mk>' | \
+                   ${MAKE} -f - -V "${_var}")"
+
+               eval ${_var}=\"${_value}\"
+       done
+}
+
+# detect_x11
+#      Detect if X11 components should be analysed and set values of
+#      relevant variables.
+detect_x11()
+{
+       if $SOURCEMODE; then
+               get_makevar MKX11 X11ROOTDIR X11SRCDIR
+       else
+               if [ -f "${SRC_DIR}/etc/mtree/set.xetc" ]; then
+                       MKX11=yes
+                       X11ROOTDIR=/this/value/isnt/used/yet
+               else
+                       MKX11=no
+                       X11ROOTDIR=
+               fi
+               X11SRCDIR=/nonexistent/xsrc
+       fi
+}
+
+#
+#      items
+#      -----
+#
+
+#
+#      Bluetooth
+#
+
+additem bluetooth "Bluetooth configuration is up to date"
+do_bluetooth()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_bluetooth fix|check"
+       op="$1"
+       failed=0
+
+       populate_dir "${op}" true \
+               "${SRC_DIR}/etc/bluetooth" "${DEST_DIR}/etc/bluetooth" 644 \
+               hosts protocols btattach.conf btdevctl.conf
+       failed=$(( ${failed} + $? ))
+
+       move_file "${op}" "${DEST_DIR}/var/db/btdev.xml" \
+                       "${DEST_DIR}/var/db/btdevctl.plist"
+       failed=$(( ${failed} + $? ))
+
+       notfixed=""
+       if [ "${op}" = "fix" ]; then
+               notfixed="${NOT_FIXED}"
+       fi
+       for _v in btattach btconfig btdevctl; do
+               if rcvar_is_enabled "${_v}"; then
+                       msg \
+    "${_v} is obsolete in rc.conf(5)${notfixed}: use bluetooth=YES"
+                       failed=$(( ${failed} + 1 ))
+               fi
+       done
+
+       return ${failed}
+}
+
+#
+#      ddbonpanic
+#
+additem ddbonpanic "verify ddb.onpanic is configured in sysctl.conf"
+do_ddbonpanic()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_ddbonpanic  fix|check"
+
+       if ${GREP} -E '^#*[[:space:]]*ddb\.onpanic[[:space:]]*\??=[[:space:]]*[[:digit:]]+' \
+               "${DEST_DIR}/etc/sysctl.conf" >/dev/null 2>&1
+       then
+               result=0
+       else
+               if [ "$1" = check ]; then
+                       msg \
+    "The ddb.onpanic behaviour is not explicitly specified in /etc/sysctl.conf"
+                       result=1
+               else
+                       echo >> "${DEST_DIR}/etc/sysctl.conf"
+                       sed < "${SRC_DIR}/etc/sysctl.conf" \
+                          -e '/^ddb\.onpanic/q' | \
+                              sed -e '1,/^$/d' >> \
+                           "${DEST_DIR}/etc/sysctl.conf"
+                       result=$?
+               fi
+       fi
+       return ${result}
+}
+
+#
+#      defaults
+#
+additem defaults "/etc/defaults/ being up to date"
+do_defaults()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_defaults  fix|check"
+       op="$1"
+       failed=0
+
+       # Except for i386 and amd64, rc.conf(5) should be the same as the
+       # one obtained from a source directory
+       extra_scripts="rc.conf"
+       if [ "$MACHINE" = "i386" -o "$MACHINE" = "amd64" ]; then
+               if $SOURCEMODE; then
+                       extra_scripts=  # clear
+
+                       # Generate and compare the correct rc.conf(5) file
+                       mkdir "${SCRATCHDIR}/defaults"
+
+                       cat "${SRC_DIR}/etc/defaults/rc.conf" \
+                           "${SRC_DIR}/etc/etc.${MACHINE}/rc.conf.append" \
+                           > "${SCRATCHDIR}/defaults/rc.conf"
+
+                       compare_dir "${op}" "${SCRATCHDIR}/defaults" \
+                           "${DEST_DIR}/etc/defaults" \
+                           444 \
+                           "rc.conf"
+                       failed=$(( ${failed} + $? ))
+               fi
+       fi
+
+       compare_dir "$op" "${SRC_DIR}/etc/defaults" "${DEST_DIR}/etc/defaults" \
+               444 \
+               daily.conf monthly.conf security.conf \
+               weekly.conf ${extra_scripts}
+       failed=$(( ${failed} + $? ))
+
+       find_file_in_dirlist pf.boot.conf "pf.boot.conf" \
+           "${SRC_DIR}/usr.sbin/pf/etc/defaults" "${SRC_DIR}/etc/defaults" \
+           || return 1
+                       # ${dir} is set by find_file_in_dirlist()
+       compare_dir "$op" "${dir}" "${DEST_DIR}/etc/defaults" 444 pf.boot.conf
+       failed=$(( ${failed} + $? ))
+
+       return ${failed}
+}
+
+#
+#      dhcpcd
+#
+additem dhcpcd "dhcpcd configuration is up to date"
+do_dhcpcd()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_dhcpcd fix|check"
+       op="$1"
+       failed=0
+
+       find_file_in_dirlist dhcpcd.conf "dhcpcd.conf" \
+           "${SRC_DIR}/external/bsd/dhcpcd/dist" "${SRC_DIR}/etc" || return 1
+                       # ${dir} is set by find_file_in_dirlist()
+       populate_dir "$op" true "${dir}" "${DEST_DIR}/etc" 644 dhcpcd.conf
+       failed=$(( ${failed} + $? ))
+
+       return ${failed}
+}
+
+#
+#      envsys
+#
+additem envsys "envsys configuration is up to date"
+do_envsys()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_envsys fix|check"
+       op="$1"
+       failed=0
+
+       populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
+               envsys.conf
+       failed=$(( ${failed} + $? ))
+
+       populate_dir "$op" true "${SRC_DIR}/etc/powerd/scripts" \
+               "${DEST_DIR}/etc/powerd/scripts" 555 sensor_battery \
+               sensor_drive sensor_fan sensor_indicator sensor_power \
+               sensor_resistance sensor_temperature sensor_voltage
+       failed=$(( ${failed} + $? ))
+
+       return ${failed}
+}
+
+#
+#      X11 fontconfig
+#
+additem fontconfig "X11 font configuration is up to date"
+do_fontconfig()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_fontconfig fix|check"
+       op="$1"
+       failed=0
+
+       if [ -f "${DEST_DIR}/etc/fonts/conf.d/10-unhinted.conf" -a \
+            -f "${DEST_DIR}/etc/fonts/conf.d/10-autohint.conf" ]; then
+               failed=1
+       fi
+
+       if [ "$failed" = 1 ]; then
+               msg \
+    "Broken fontconfig configuration found; please delete these files"
+               msg \
+    "in the ${DESTDIR}/etc/fonts/conf.d/ subdirectory:"
+               msg \
+    "   10-autohint.conf 10-no-sub-pixel.conf 10-sub-pixel-bgr.conf"
+               msg \
+    "   10-sub-pixel-rgb.conf 10-sub-pixel-vbgr.conf"
+               msg \
+    "   10-sub-pixel-vrgb.conf 10-unhinted.conf 25-unhint-nonlatin.conf"
+               msg \
+    "   65-khmer.conf 70-no-bitmaps.conf 70-yes-bitmaps.conf"
+               msg \
+    "(This warning only appears if both the 10-unhinted.conf and"
+               msg \
+    "10-autohint.conf files are present."
+       fi
+
+       return ${failed}
+}
+
+#
+#      gid
+#
+additem gid "required groups in /etc/group"
+do_gid()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_gid  fix|check"
+
+       check_ids "$1" groups "${DEST_DIR}/etc/group" \
+           named ntpd sshd authpf _pflogd _rwhod _proxy _timedc \
+           _sdpd _httpd _mdnsd _tests _tcpdump _tss
+}
+
+#
+#      gpio
+#
+additem gpio "gpio configuration is up to date"
+do_gpio()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_gpio fix|check"
+       op="$1"
+       failed=0
+
+       populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
+               gpio.conf
+       failed=$(( ${failed} + $? ))
+
+       return ${failed}
+}
+
+#
+#      hosts
+#
+additem hosts "/etc/hosts being up to date"
+do_hosts()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_hosts  fix|check"
+
+       modify_file "$1" "${DEST_DIR}/etc/hosts" "${SCRATCHDIR}/hosts" '
+               /^(127\.0\.0\.1|::1)[   ]+[^\.]*$/ {
+                       print $0, "localhost."
+                       next
+               }
+               { print }
+       '
+       return $?
+}
+
+#
+#      iscsi
+#
+additem iscsi "/etc/iscsi is populated"
+do_iscsi()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_iscsi  fix|check"
+
+       populate_dir "${op}" true \
+           "${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 600 auths
+       populate_dir "${op}" true \
+           "${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 644 targets
+       return $?
+}
+
+#
+#      makedev
+#
+additem makedev "/dev/MAKEDEV being up to date"
+do_makedev()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_makedev   fix|check"
+       failed=0
+
+       if [ -f "${SRC_DIR}/etc/MAKEDEV.tmpl" ]; then
+                       # generate MAKEDEV from source if source is available
+               env MACHINE="${MACHINE}" \
+                   MACHINE_ARCH="${MACHINE_ARCH}" \
+                   NETBSDSRCDIR="${SRC_DIR}" \
+                   ${AWK} -f "${SRC_DIR}/etc/MAKEDEV.awk" \
+                   "${SRC_DIR}/etc/MAKEDEV.tmpl" > "${SCRATCHDIR}/MAKEDEV"
+       fi
+
+       find_file_in_dirlist MAKEDEV "MAKEDEV" \
+           "${SCRATCHDIR}" "${SRC_DIR}/dev" \
+           || return 1
+                       # ${dir} is set by find_file_in_dirlist()
+       compare_dir "$1" "${dir}" "${DEST_DIR}/dev" 555 MAKEDEV
+       failed=$(( ${failed} + $? ))
+
+       find_file_in_dirlist MAKEDEV.local "MAKEDEV.local" \
+           "${SRC_DIR}/etc" "${SRC_DIR}/dev" \
+           || return 1
+                       # ${dir} is set by find_file_in_dirlist()
+       compare_dir "$1" "${dir}" "${DEST_DIR}/dev" 555 MAKEDEV.local
+       failed=$(( ${failed} + $? ))
+
+       return ${failed}
+}
+
+#
+#      motd
+#
+additem motd "contents of motd"
+do_motd()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_motd  fix|check"
+
+       if ${GREP} -i 'http://www.NetBSD.org/Misc/send-pr.html' \
+               "${DEST_DIR}/etc/motd" >/dev/null 2>&1 \
+           || ${GREP} -i 'http://www.NetBSD.org/support/send-pr.html' \
+               "${DEST_DIR}/etc/motd" >/dev/null 2>&1
+       then
+               tmp1="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
+               tmp2="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
+               sed '1,2d' <"${SRC_DIR}/etc/motd" >"${tmp1}"
+               sed '1,2d' <"${DEST_DIR}/etc/motd" >"${tmp2}"
+
+               if [ "$1" = check ]; then
+                       cmp -s "${tmp1}" "${tmp2}"
+                       result=$?
+                       if [ "${result}" -ne 0 ]; then
+                               msg \
+    "Bug reporting messages do not seem to match the installed release"
+                       fi
+               else
+                       head -n 2 "${DEST_DIR}/etc/motd" >"${tmp1}"
+                       sed '1,2d' <"${SRC_DIR}/etc/motd" >>"${tmp1}"
+                       cp "${tmp1}" "${DEST_DIR}/etc/motd"
+                       result=0
+               fi
+
+               rm -f "${tmp1}" "${tmp2}"
+       else
+               result=0
+       fi
+
+       return ${result}
+}
+
+#
+#      mtree
+#
+additem mtree "/etc/mtree/ being up to date"
+do_mtree()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_mtree  fix|check"
+       failed=0
+
+       compare_dir "$1" "${SRC_DIR}/etc/mtree" "${DEST_DIR}/etc/mtree" 444 special
+       failed=$(( ${failed} + $? ))
+
+       if ! $SOURCEMODE; then
+               MTREE_DIR="${SRC_DIR}/etc/mtree"
+       else
+               ${MAKE} -C "${SRC_DIR}/etc/mtree" emit_dist_file > \
+                   "${SCRATCHDIR}/NetBSD.dist"
+               MTREE_DIR="${SCRATCHDIR}"
+       fi
+       compare_dir "$1" "${MTREE_DIR}" "${DEST_DIR}/etc/mtree" 444 NetBSD.dist
+       failed=$(( ${failed} + $? ))
+
+       return ${failed}
+}
+
+#
+#      named
+#
+additem named "named configuration update"
+do_named()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_named  fix|check"
+       op="$1"
+
+       move_file "${op}" \
+               "${DEST_DIR}/etc/namedb/named.conf" \
+               "${DEST_DIR}/etc/named.conf"
+
+       compare_dir "${op}" "${SRC_DIR}/etc/namedb" "${DEST_DIR}/etc/namedb" \
+               644 \
+               root.cache
+}
+
+#
+#      pam
+#
+additem pam "/etc/pam.d is populated"
+do_pam()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_pam  fix|check"
+       op="$1"
+       failed=0
+
+       populate_dir "${op}" true "${SRC_DIR}/etc/pam.d" \
+               "${DEST_DIR}/etc/pam.d" 644 \
+               README display_manager ftpd gdm imap kde login other passwd \
+               pop3 ppp rexecd rsh sshd su system telnetd xdm xserver
+
+       failed=$(( ${failed} + $? ))
+
+       return ${failed}
+}
+
+#
+#      periodic
+#
+additem periodic "/etc/{daily,weekly,monthly,security} being up to date"
+do_periodic()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_periodic  fix|check"
+
+       compare_dir "$1" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
+               daily weekly monthly security
+}
+
+#
+#      pf
+#
+additem pf "pf configuration being up to date"
+do_pf()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_pf  fix|check"
+       op="$1"
+       failed=0
+
+       find_file_in_dirlist pf.os "pf.os" \
+           "${SRC_DIR}/dist/pf/etc" "${SRC_DIR}/etc" \
+           || return 1
+                       # ${dir} is set by find_file_in_dirlist()
+       populate_dir "${op}" true \
+           "${dir}" "${DEST_DIR}/etc" 644 \
+           pf.conf
+       failed=$(( ${failed} + $? ))
+
+       compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 pf.os
+       failed=$(( ${failed} + $? ))
+
+       return ${failed}
+}
+
+#
+#      pwd_mkdb
+#
+additem pwd_mkdb "passwd database version"
+do_pwd_mkdb()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_pwd_mkdb  fix|check"
+       op="$1"
+       failed=0
+
+       # XXX Ideally, we should figure out the endianness of the
+       # target machine, and add "-E B"/"-E L" to the db(1) flags,
+       # and "-B"/"-L" to the pwd_mkdb(8) flags if the target is not
+       # the same as the host machine.  It probably doesn't matter,
+       # because we don't expect "postinstall fix pwd_mkdb" to be
+       # invoked during a cross build.
+
+       set -- $(${DB} -q -Sb -Ub -To -N hash "${DEST_DIR}/etc/pwd.db" \
+               'VERSION\0')
+       case "$2" in
+       '\001\000\000\000') return 0 ;; # version 1, little-endian
+       '\000\000\000\001') return 0 ;; # version 1, big-endian
+       esac
+
+       if [ "${op}" = "check" ]; then
+               msg "Update format of passwd database"
+               failed=1
+       elif ! ${PWD_MKDB} -V 1 -d "${DEST_DIR:-/}" \
+                       "${DEST_DIR}/etc/master.passwd";
+       then
+               msg "Can't update format of passwd database"
+               failed=1
+       else
+               msg "Updated format of passwd database"
+       fi
+
+       return ${failed}
+}
+
+#
+#      rc
+#
+additem rc "/etc/rc* and /etc/rc.d/ being up to date"
+do_rc()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_rc  fix|check"
+       op="$1"
+       failed=0
+       generated_scripts=""
+       if [ "${MKX11}" != "no" ]; then
+               generated_scripts="${generated_scripts} xdm xfs"
+       fi
+
+       compare_dir "${op}" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
+               rc rc.subr rc.shutdown
+       failed=$(( ${failed} + $? ))
+
+       if ! $SOURCEMODE; then
+               extra_scripts="${generated_scripts}"
+       else
+               extra_scripts=""
+       fi
+
+       compare_dir "${op}" "${SRC_DIR}/etc/rc.d" "${DEST_DIR}/etc/rc.d" 555 \
+               DAEMON DISKS LOGIN NETWORKING SERVERS \
+               accounting altqd amd apmd \
+               bluetooth bootconf.sh bootparams \
+               ccd cgd cleartmp cron \
+               dhclient dhcpcd dhcpd dhcrelay dmesg downinterfaces envsys \
+               fsck fsck_root ftp_proxy ftpd \
+               gpio \
+               hostapd httpd \
+               identd ifwatchd inetd ipfilter ipfs ipmon ipnat ipsec \
+               irdaattach iscsi_target isdnd isibootd \
+               kdc \
+               ldconfig ldpd local lpd lvm\
+               makemandb mdnsd mixerctl mopd motd mountall mountcritlocal \
+               mountcritremote mountd moused mrouted \
+               named ndbootd network newsyslog nfsd nfslocking npf \
+               ntpd ntpdate \
+               perusertmp pf pf_boot pflogd postfix powerd ppp pwcheck \
+               quota \
+               racoon rpcbind raidframe raidframeparity random_seed \
+               rarpd rbootd rndctl \
+               root route6d routed rtadvd rtclocaltime rtsold rwho \
+               savecore screenblank securelevel sshd \
+               staticroute swap1 swap2 sysctl sysdb syslogd \
+               timed tpctl ttys \
+               veriexec virecover wdogctl wpa_supplicant wscons wsmoused \
+               ypbind yppasswdd ypserv \
+               ${extra_scripts}
+       failed=$(( ${failed} + $? ))
+
+       if $SOURCEMODE && [ -n "${generated_scripts}" ]; then
+               # generate scripts
+               mkdir "${SCRATCHDIR}/rc"
+               for f in ${generated_scripts}; do
+                       sed -e "s,@X11ROOTDIR@,${X11ROOTDIR},g" \
+                           < "${SRC_DIR}/etc/rc.d/${f}.in" \
+                           > "${SCRATCHDIR}/rc/${f}"
+               done
+               compare_dir "${op}" "${SCRATCHDIR}/rc" \
+                   "${DEST_DIR}/etc/rc.d" 555 \
+                   ${generated_scripts}
+               failed=$(( ${failed} + $? ))
+       fi
+
+               # check for obsolete rc.d files
+       for f in NETWORK btattach btconfig btcontrol btdevctl bthcid btuartd \
+           fsck.sh kerberos nfsiod sdpd servers \
+           systemfs daemon gated login poffd portmap sunndd xntpd; do
+               fd="/etc/rc.d/${f}"
+               [ -e "${DEST_DIR}${fd}" ] && echo "${fd}"
+       done | obsolete_paths "${op}"
+       failed=$(( ${failed} + $? ))
+
+               # check for obsolete rc.conf(5) variables
+       set --  amd amd_master \
+               btcontrol btcontrol_devices \
+               critical_filesystems critical_filesystems_beforenet \
+               mountcritlocal mountcritremote \
+               network ip6forwarding \
+               network nfsiod_flags \
+               sdpd sdpd_control \
+               sdpd sdpd_groupname \
+               sdpd sdpd_username \
+               sysctl defcorename
+       while [ $# -gt 1 ]; do
+               if rcconf_is_set "${op}" "$1" "$2" 1; then
+                       failed=1
+               fi
+               shift 2
+       done
+
+       return ${failed}
+}
+
+#
+#      sendmail
+#
+adddisableditem sendmail "remove obsolete sendmail configuration files and scripts"
+do_sendmail()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_sendmail  fix|check"
+       op="$1"
+       failed=0
+
+       # Don't complain if the "sendmail" package is installed because the
+       # files might still be in use.
+       if /usr/sbin/pkg_info -qe sendmail >/dev/null 2>&1; then
+               return 0
+       fi
+
+       for f in /etc/mail/helpfile /etc/mail/local-host-names \
+           /etc/mail/sendmail.cf /etc/mail/submit.cf /etc/rc.d/sendmail \
+           /etc/rc.d/smmsp /usr/share/misc/sendmail.hf \
+           $(find "${DEST_DIR}/usr/share/sendmail" -type f) \
+           $(find "${DEST_DIR}/usr/share/sendmail" -type d) \
+           "${DEST_DIR}/var/log/sendmail.st" \
+           "${DEST_DIR}/var/spool/clientmqueue" \
+           "${DEST_DIR}/var/spool/mqueue"; do
+               [ -e "${DEST_DIR}${f}" ] && echo "${f}"
+       done | obsolete_paths "${op}"
+       failed=$(( ${failed} + $? ))
+
+       return ${failed}
+}
+
+#
+#      mailerconf
+#
+adddisableditem mailerconf "update /etc/mailer.conf after sendmail removal"
+do_mailerconf()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_mailterconf  fix|check"
+       op="$1"
+
+       failed=0
+       mta_path="$(${AWK} '/^sendmail[ \t]/{print$2}' \
+               "${DEST_DIR}/etc/mailer.conf")"
+       old_sendmail_path="/usr/libexec/sendmail/sendmail"
+       if [ "${mta_path}" = "${old_sendmail_path}" ]; then
+           if [ "$op" = check ]; then
+               msg "mailer.conf points to obsolete ${old_sendmail_path}"
+               failed=1;
+           else
+               populate_dir "${op}" false \
+               "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 mailer.conf
+               failed=$?
+           fi
+       fi
+       
+       return ${failed}
+}
+
+#
+#      ssh
+#
+additem ssh "ssh configuration update"
+do_ssh()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_ssh  fix|check"
+       op="$1"
+
+       failed=0
+       _etcssh="${DEST_DIR}/etc/ssh"
+       if ! check_dir "${op}" "${_etcssh}" 755; then
+               failed=1
+       fi
+
+       if [ ${failed} -eq 0 ]; then
+               for f in \
+                           ssh_known_hosts ssh_known_hosts2 \
+                           ssh_host_dsa_key ssh_host_dsa_key.pub \
+                           ssh_host_rsa_key ssh_host_rsa_key.pub \
+                           ssh_host_key ssh_host_key.pub \
+                   ; do
+                       if ! move_file "${op}" \
+                           "${DEST_DIR}/etc/${f}" "${_etcssh}/${f}" ; then
+                               failed=1
+                       fi
+               done
+               for f in sshd.conf ssh.conf ; do
+                               # /etc/ssh/ssh{,d}.conf -> ssh{,d}_config
+                               #
+                       if ! move_file "${op}" \
+                           "${_etcssh}/${f}" "${_etcssh}/${f%.conf}_config" ;
+                       then
+                               failed=1
+                       fi
+                               # /etc/ssh{,d}.conf -> /etc/ssh/ssh{,d}_config
+                               #
+                       if ! move_file "${op}" \
+                           "${DEST_DIR}/etc/${f}" \
+                           "${_etcssh}/${f%.conf}_config" ;
+                       then
+                               failed=1
+                       fi
+               done
+       fi
+
+       sshdconf=""
+       for f in \
+           "${_etcssh}/sshd_config" \
+           "${_etcssh}/sshd.conf" \
+           "${DEST_DIR}/etc/sshd.conf" ; do
+               if [ -f "${f}" ]; then
+                       sshdconf="${f}"
+                       break
+               fi
+       done
+       if [ -n "${sshdconf}" ]; then
+               modify_file "${op}" "${sshdconf}" "${SCRATCHDIR}/sshdconf" '
+                       /^[^#$]/ {
+                               kw = tolower($1)
+                               if (kw == "hostkey" &&
+                                   $2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ ) {
+                                       sub(/\/etc\/+/, "/etc/ssh/")
+                               }
+                               if (kw == "rhostsauthentication" ||
+                                   kw == "verifyreversemapping" ||
+                                   kw == "reversemappingcheck") {
+                                       sub(/^/, "# DEPRECATED:\t")
+                               }
+                       }
+                       { print }
+               '
+               failed=$(( ${failed} + $? ))
+       fi
+
+       if ! find_file_in_dirlist moduli "moduli" \
+           "${SRC_DIR}/crypto/external/bsd/openssh/dist" "${SRC_DIR}/etc" ; then
+               failed=1
+                       # ${dir} is set by find_file_in_dirlist()
+       elif ! compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 moduli; then
+               failed=1
+       fi
+
+       if ! check_dir "${op}" "${DEST_DIR}/var/chroot/sshd" 755 ; then
+               failed=1
+       fi
+
+       if rcconf_is_set "${op}" sshd sshd_conf_dir 1; then
+               failed=1
+       fi
+
+       return ${failed}
+}
+
+#
+#      wscons
+#
+additem wscons "wscons configuration file update"
+do_wscons()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_wscons  fix|check"
+       op="$1"
+
+       [ -f "${DEST_DIR}/etc/wscons.conf" ] || return 0
+
+       failed=0
+       notfixed=""
+       if [ "${op}" = "fix" ]; then
+               notfixed="${NOT_FIXED}"
+       fi
+       while read _type _arg1 _rest; do
+               if [ "${_type}" = "mux" -a "${_arg1}" = "1" ]; then
+                       msg \
+    "Obsolete wscons.conf(5) entry \""${_type} ${_arg1}"\" found.${notfixed}"
+                       failed=1
+               fi
+       done < "${DEST_DIR}/etc/wscons.conf"
+
+       return ${failed}
+}
+
+#
+#      X11
+#
+additem x11 "x11 configuration update"
+do_x11()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_x11  fix|check"
+       op="$1"
+
+       failed=0
+       _etcx11="${DEST_DIR}/etc/X11"
+       if [ ! -d "${_etcx11}" ]; then
+               msg "${_etcx11} is not a directory; skipping check"
+               return 0
+       fi
+       if [ -d "${DEST_DIR}/usr/X11R6/." ]
+       then
+               _libx11="${DEST_DIR}/usr/X11R6/lib/X11"
+               if [ ! -d "${_libx11}" ]; then
+                       msg "${_libx11} is not a directory; skipping check"
+                       return 0
+               fi
+       fi
+
+       _notfixed=""
+       if [ "${op}" = "fix" ]; then
+               _notfixed="${NOT_FIXED}"
+       fi
+
+       for d in \
+                   fs lbxproxy proxymngr rstart twm xdm xinit xserver xsm \
+           ; do
+               sd="${_libx11}/${d}"
+               ld="/etc/X11/${d}"
+               td="${DEST_DIR}${ld}"
+               if [ -h "${sd}" ]; then
+                       continue
+               elif [ -d "${sd}" ]; then
+                       tdfiles="$(find "${td}" \! -type d)"
+                       if [ -n "${tdfiles}" ]; then
+                               msg "${sd} exists yet ${td} already" \
+                                   "contains files${_notfixed}"
+                       else
+                               msg "Migrate ${sd} to ${td}${_notfixed}"
+                       fi
+                       failed=1
+               elif [ -e "${sd}" ]; then
+                       msg "Unexpected file ${sd}${_notfixed}"
+                       continue
+               else
+                       continue
+               fi
+       done
+
+       return ${failed}
+}
+
+#
+#      xkb
+#
+# /usr/X11R7/lib/X11/xkb/symbols/pc used to be a directory, but changed
+# to a file on 2009-06-12.  Fixing this requires removing the directory
+# (which we can do) and re-extracting the xbase set (which we can't do),
+# or at least adding that one file (which we may be able to do if X11SRCDIR
+# is available).
+#
+additem xkb "clean up for xkbdata to xkeyboard-config upgrade"
+do_xkb()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_xkb  fix|check"
+       op="$1"
+       failed=0
+
+       pcpath="/usr/X11R7/lib/X11/xkb/symbols/pc"
+       pcsrcdir="${X11SRCDIR}/external/mit/xkeyboard-config/dist/symbols"
+
+       filemsg="\
+${pcpath} was a directory, should be a file.
+    To fix, extract the xbase set again."
+
+       _notfixed=""
+       if [ "${op}" = "fix" ]; then
+               _notfixed="${NOT_FIXED}"
+       fi
+
+       if [ ! -d "${DESTDIR}${pcpath}" ]; then
+               return 0
+       fi
+
+       # Delete obsolete files in the directory, and the directory
+       # itself.  If the directory contains unexpected extra files
+       # then it will not be deleted.
+       ( [ -f "${DEST_DIR}"/var/db/obsolete/xbase ] \
+           &&  sort -ru "${DEST_DIR}"/var/db/obsolete/xbase \
+           | ${GREP} -E "^\\.?${pcpath}/" ;
+           echo "${pcpath}" ) \
+       | obsolete_paths "${op}"
+       failed=$(( ${failed} + $? ))
+
+       # If the directory was removed above, then try to replace it with
+       # a file.
+       if [ -d "${DESTDIR}${pcpath}" ]; then
+               msg "${filemsg}${_notfixed}"
+               failed=$(( ${failed} + 1 ))
+       else
+               if ! find_file_in_dirlist pc "${pcpath}" \
+                       "${pcsrcdir}" "${SRC_DIR}${pcpath%/*}"
+               then
+                       msg "${filemsg}${_notfixed}"
+                       failed=$(( ${failed} + 1 ))
+               else
+                       # ${dir} is set by find_file_in_dirlist()
+                       populate_dir "${op}" true \
+                               "${dir}" "${DEST_DIR}${pcpath%/*}" 444 \
+                               pc
+                       failed=$(( ${failed} + $? ))
+               fi
+       fi
+
+       return $failed
+}
+
+#
+#      uid
+#
+additem uid "required users in /etc/master.passwd"
+do_uid()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_uid  fix|check"
+
+       check_ids "$1" users "${DEST_DIR}/etc/master.passwd" \
+           named ntpd postfix sshd _pflogd _rwhod _proxy _timedc \
+           _sdpd _httpd _mdnsd _tests _tcpdump _tss
+}
+
+
+#
+#      varrwho
+#
+additem varrwho "required ownership of files in /var/rwho"
+do_varrwho()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_varrwho  fix|check"
+
+       contents_owner "$1" "${DEST_DIR}/var/rwho" _rwhod _rwhod
+}
+
+
+#
+#      tcpdumpchroot
+#
+additem tcpdumpchroot "remove /var/chroot/tcpdump/etc/protocols"
+do_tcpdumpchroot()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_tcpdumpchroot  fix|check"
+
+       failed=0;
+       if [ -r "${DEST_DIR}/var/chroot/tcpdump/etc/protocols" ]; then
+               if [ "$1" = "fix" ]; then
+                       rm "${DEST_DIR}/var/chroot/tcpdump/etc/protocols"
+                       failed=$(( ${failed} + $? ))
+                       rmdir "${DEST_DIR}/var/chroot/tcpdump/etc"
+                       failed=$(( ${failed} + $? ))
+               else
+                       failed=1
+               fi
+       fi
+       return ${failed}
+}
+
+
+#
+#      atf
+#
+additem atf "install missing atf configuration files and validate them"
+do_atf()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_atf  fix|check"
+       op="$1"
+       failed=0
+
+       # Ensure atf configuration files are in place.
+       if find_file_in_dirlist NetBSD.conf "NetBSD.conf" \
+           "${SRC_DIR}/external/bsd/atf/etc/atf" \
+           "${SRC_DIR}/etc/atf"; then
+                       # ${dir} is set by find_file_in_dirlist()
+               populate_dir "${op}" true "${dir}" "${DEST_DIR}/etc/atf" 644 \
+                   NetBSD.conf common.conf || failed=1
+       else
+               failed=1
+       fi
+       if find_file_in_dirlist atf-run.hooks "atf-run.hooks" \
+           "${SRC_DIR}/external/bsd/atf/dist/atf-run/sample" \
+           "${SRC_DIR}/etc/atf"; then
+                       # ${dir} is set by find_file_in_dirlist()
+               populate_dir "${op}" true "${dir}" "${DEST_DIR}/etc/atf" 644 \
+                   atf-run.hooks || failed=1
+       else
+               failed=1
+       fi
+
+       # Validate the _atf to _tests user/group renaming.
+       if [ -f "${DEST_DIR}/etc/atf/common.conf" ]; then
+               handle_atf_user "${op}" || failed=1
+       else
+               failed=1
+       fi
+
+       return ${failed}
+}
+
+handle_atf_user()
+{
+       local op="$1"
+       local failed=0
+
+       local conf="${DEST_DIR}/etc/atf/common.conf"
+       if grep '[^#]*unprivileged-user[ \t]*=.*_atf' "${conf}" >/dev/null
+       then
+               if [ "$1" = "fix" ]; then
+                       sed -e \
+                           "/[^#]*unprivileged-user[\ t]*=/s/_atf/_tests/" \
+                           "${conf}" >"${conf}.new"
+                       failed=$(( ${failed} + $? ))
+                       mv "${conf}.new" "${conf}"
+                       failed=$(( ${failed} + $? ))
+                       msg "Set unprivileged-user=_tests in ${conf}"
+               else
+                       msg "unprivileged-user=_atf in ${conf} should be" \
+                           "unprivileged-user=_tests"
+                       failed=1
+               fi
+       fi
+
+       return ${failed}
+}
+
+#
+#      catpages
+#
+obsolete_catpages()
+{
+       basedir="$2"
+       section="$3"
+       mandir="${basedir}/man${section}"
+       catdir="${basedir}/cat${section}"
+       test -d "$mandir" || return 0
+       test -d "$catdir" || return 0
+       (cd "$mandir" && find . -type f) | {
+       failed=0
+       while read manpage; do
+               manpage="${manpage#./}"
+               case "$manpage" in
+               *.Z)
+                       catname="$catdir/${manpage%.*.Z}.0"
+                       ;;
+               *.gz)
+                       catname="$catdir/${manpage%.*.gz}.0"
+                       ;;
+               *)
+                       catname="$catdir/${manpage%.*}.0"
+                       ;;
+               esac
+               test -e "$catname" -a "$catname" -ot "$mandir/$manpage" || continue
+               if [ "$1" = "fix" ]; then
+                       rm "$catname"
+                       failed=$(( ${failed} + $? ))
+                       msg "Removed obsolete cat page $catname"
+               else
+                       msg "Obsolete cat page $catname"
+                       failed=1
+               fi
+       done
+       exit $failed
+       }
+}
+
+additem catpages "remove outdated cat pages"
+do_catpages()
+{
+       failed=0
+       for manbase in /usr/share/man /usr/X11R6/man /usr/X11R7/man; do
+               for sec in 1 2 3 4 5 6 7 8 9; do
+                       obsolete_catpages "$1" "${DEST_DIR}${manbase}" "${sec}"
+                       failed=$(( ${failed} + $? ))
+                       if [ "$1" = "fix" ]; then
+                               rmdir "${DEST_DIR}${manbase}/cat${sec}"/* \
+                                       2>/dev/null
+                               rmdir "${DEST_DIR}${manbase}/cat${sec}" \
+                                       2>/dev/null
+                       fi
+               done
+       done
+       return $failed
+}
+
+#
+#      obsolete
+#      (this item is last to allow other items to move obsolete files)
+#
+additem obsolete "remove obsolete file sets and minor libraries"
+do_obsolete()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_obsolete  fix|check"
+       op="$1"
+       failed=0
+
+       sort -ru "${DEST_DIR}"/var/db/obsolete/* | obsolete_paths "${op}"
+       failed=$(( ${failed} + $? ))
+
+       (
+               obsolete_libs /lib
+               obsolete_libs /usr/lib
+               obsolete_libs /usr/lib/i18n
+               obsolete_libs /usr/X11R6/lib
+               obsolete_libs /usr/X11R7/lib
+               [ "$MACHINE" = "amd64" ] && obsolete_libs /usr/lib/i386
+               [ "$MACHINE" = "sparc64" ] && obsolete_libs /usr/lib/sparc
+       ) | obsolete_paths "${op}"
+       failed=$(( ${failed} + $? ))
+
+       return ${failed}
+}
+
+
+#
+#      ptyfsoldnodes
+#
+additem ptyfsoldnodes "remove legacy device nodes when using ptyfs"
+do_ptyfsoldnodes()
+{
+       [ -n "$1" ] || err 3 "USAGE: do_ptyfsoldnodes  fix|check"
+       _ptyfs_op="$1"
+
+       # Check whether ptyfs is in use
+       failed=0;
+       if ! ${GREP} -E "^ptyfs" "${DEST_DIR}/etc/fstab" > /dev/null; then
+               msg "ptyfs is not in use"
+               return 0
+       fi
+
+       # Find the device major numbers for the pty master and slave
+       # devices, by parsing the output from "MAKEDEV -s pty0".
+       #
+       # Output from MAKEDEV looks like this:
+       # ./ttyp0 type=char device=netbsd,5,0 mode=666 gid=0 uid=0
+       # ./ptyp0 type=char device=netbsd,6,0 mode=666 gid=0 uid=0
+       #
+       # Output from awk, used in the eval statement, looks like this:
+       # maj_ptym=6; maj_ptys=5;
+       #
+       eval "$(
+           ${HOST_SH} "${DEST_DIR}/dev/MAKEDEV" -s pty0 2>/dev/null \
+           | ${AWK} '\
+           BEGIN { before_re = ".*device=[a-zA-Z]*,"; after_re = ",.*"; }
+           /ptyp0/ { maj_ptym = gensub(before_re, "", 1, $0);
+                     maj_ptym = gensub(after_re, "", 1, maj_ptym); }
+           /ttyp0/ { maj_ptys = gensub(before_re, "", 1, $0);
+                     maj_ptys = gensub(after_re, "", 1, maj_ptys); }
+           END { print "maj_ptym=" maj_ptym "; maj_ptys=" maj_ptys ";"; }
+           '
+           )"
+       #msg "Major numbers are maj_ptym=${maj_ptym} maj_ptys=${maj_ptys}"
+       if [ -z "$maj_ptym" ] || [ -z "$maj_ptys" ]; then
+               msg "Cannot find device major numbers for pty master and slave"
+               return 1
+       fi
+
+       # look for /dev/[pt]ty[p-zP-T][0-9a-zA-Z], and check that they
+       # have the expected device major numbers.  ttyv* is typically not a
+       # pty device, but we check it anyway.
+       #
+       # The "for d1" loop is intended to avoid overflowing ARG_MAX;
+       # otherwise we could have used a single glob pattern.
+       #
+       # If there are no files that match a particular pattern,
+       # then stat prints something like:
+       #    stat: /dev/[pt]tyx?: lstat: No such file or directory
+       # and we ignore it.  XXX: We also ignore other error messages.
+       #
+       _ptyfs_tmp="$(mktemp /tmp/postinstall.ptyfs.XXXXXXXX)"
+       for d1 in p q r s t u v w x y z P Q R S T; do
+               ${STAT} -f "%Hr %N" "${DEST_DIR}/dev/"[pt]ty${d1}? 2>&1
+       done \
+       | while read -r major node ; do
+               case "$major" in
+               ${maj_ptym}|${maj_ptys}) echo "$node" ;;
+               esac
+       done >"${_ptyfs_tmp}"
+
+       _desc="legacy device node"
+       while read node; do
+               if [ "${_ptyfs_op}" = "check" ]; then
+                       msg "Remove ${_desc} ${node}"
+                       failed=1
+               else # "fix"
+                       if rm "${node}"; then
+                               msg "Removed ${_desc} ${node}"
+                       else
+                               warn "Failed to remove ${_desc} ${node}"
+                               failed=1
+                       fi
+               fi
+       done < "${_ptyfs_tmp}"
+       rm "${_ptyfs_tmp}"
+
+       return ${failed}
+}
+
+
+#
+#      end of items
+#      ------------
+#
+
+
+usage()
+{
+       cat 1>&2 << _USAGE_
+Usage: ${PROGNAME} [-s srcdir] [-d destdir] [-m mach] [-a arch] op [item [...]]
+       Perform post-installation checks and/or fixes on a system's
+       configuration files.
+       If no items are provided, a default set of checks or fixes is applied.
+
+       Options:
+       -s {srcdir|tgzfile|tempdir}
+                       Location of the source files.  This may be any
+                       of the following:
+                       * A directory that contains a NetBSD source tree;
+                       * A distribution set file such as "etc.tgz" or
+                         "xetc.tgz".  Pass multiple -s options to specify
+                          multiple such files;
+                       * A temporary directory in which one or both of
+                         "etc.tgz" and "xetc.tgz" have been extracted.
+                                                       [${SRC_DIR:-/}]
+       -d destdir      Destination directory to check. [${DEST_DIR:-/}]
+       -m mach         MACHINE.                        [${MACHINE}]
+       -a arch         MACHINE_ARCH.                   [${MACHINE_ARCH}]
+
+       Operation may be one of:
+               help    Display this help.
+               list    List available items.
+               check   Perform post-installation checks on items.
+               diff [diff(1) options ...]
+                       Similar to 'check' but also output difference of files.
+               fix     Apply fixes that 'check' determines need to be applied.
+               usage   Display this usage.
+_USAGE_
+       exit 2
+}
+
+
+list()
+{
+       echo "Default set of items (to apply if no items are provided by user):"
+       echo "  Item          Description"
+       echo "  ----          -----------"
+       for i in ${defaultitems}; do
+               eval desc=\"\${desc_${i}}\"
+               printf "  %-12s  %s\n" "${i}" "${desc}"
+       done
+       echo "Items disabled by default (must be requested explicitly):"
+       echo "  Item          Description"
+       echo "  ----          -----------"
+       for i in ${otheritems}; do
+               eval desc=\"\${desc_${i}}\"
+               printf "  %-12s  %s\n" "${i}" "${desc}"
+       done
+
+}
+
+
+main()
+{
+       TGZLIST=                # quoted list list of tgz files
+       SRC_ARGLIST=            # quoted list of one or more "-s" args
+       SRC_DIR="${SRC_ARG}"    # set default value for early usage()
+       N_SRC_ARGS=0            # number of "-s" args
+       TGZMODE=false           # true if "-s" specifies a tgz file
+       DIRMODE=false           # true if "-s" specified a directory
+       SOURCEMODE=false        # true if "-s" specified a source directory
+
+       while getopts s:d:m:a: ch; do
+               case "${ch}" in
+               s)
+                       qarg="$(shell_quote "${OPTARG}")"
+                       N_SRC_ARGS=$(( $N_SRC_ARGS + 1 ))
+                       SRC_ARGLIST="${SRC_ARGLIST}${SRC_ARGLIST:+ }-s ${qarg}"
+                       if [ -f "${OPTARG}" ]; then
+                               # arg refers to a *.tgz file.
+                               # This may happen twice, for both
+                               # etc.tgz and xetc.tgz, so we build up a
+                               # quoted list in TGZLIST.
+                               TGZMODE=true
+                               TGZLIST="${TGZLIST}${TGZLIST:+ }${qarg}"
+                               # Note that, when TGZMODE is true,
+                               # SRC_ARG is used only for printing
+                               # human-readable messages.
+                               SRC_ARG="${TGZLIST}"
+                       elif [ -d "${OPTARG}" ]; then
+                               # arg refers to a directory.
+                               # It might be a source directory, or a
+                               # directory where the sets have already
+                               # been extracted.
+                               DIRMODE=true
+                               SRC_ARG="${OPTARG}"
+                               if [ -f "${OPTARG}/etc/Makefile" ]; then
+                                       SOURCEMODE=true
+                               fi
+                       else
+                               err 2 "Invalid argument for -s option"
+                       fi
+                       ;;
+               d)
+                       DEST_DIR="${OPTARG}"
+                       ;;
+               m)
+                       MACHINE="${OPTARG}"
+                       ;;
+               a)
+                       MACHINE_ARCH="${OPTARG}"
+                       ;;
+               *)
+                       usage
+                       ;;
+               esac
+       done
+       shift $((${OPTIND} - 1))
+       [ $# -gt 0 ] || usage
+
+       if [ "$N_SRC_ARGS" -gt 1 ] && $DIRMODE; then
+               err 2 "Multiple -s args are allowed only with tgz files"
+       fi
+       if [ "$N_SRC_ARGS" -eq 0 ]; then
+               # The default SRC_ARG was set elsewhere
+               DIRMODE=true
+               SOURCEMODE=true
+               SRC_ARGLIST="-s $(shell_quote "${SRC_ARG}")"
+       fi
+
+       #
+       # If '-s' arg or args specified tgz files, extract them
+       # to a scratch directory.
+       #
+       if $TGZMODE; then
+               ETCTGZDIR="${SCRATCHDIR}/etc.tgz"
+               echo "Note: Creating temporary directory ${ETCTGZDIR}"
+               if ! mkdir "${ETCTGZDIR}"; then
+                       err 2 "Can't create ${ETCTGZDIR}"
+               fi
+               ( # subshell to localise changes to "$@"
+                       eval "set -- ${TGZLIST}"
+                       for tgz in "$@"; do
+                               echo "Note: Extracting files from ${tgz}"
+                               cat "${tgz}" | (
+                                       cd "${ETCTGZDIR}" &&
+                                       tar -zxf -
+                               ) || err 2 "Can't extract ${tgz}"
+                       done
+               )
+               SRC_DIR="${ETCTGZDIR}"
+       else
+               SRC_DIR="${SRC_ARG}"
+       fi
+
+       [ -d "${SRC_DIR}" ]     || err 2 "${SRC_DIR} is not a directory"
+       [ -d "${DEST_DIR}" ]    || err 2 "${DEST_DIR} is not a directory"
+       [ -n "${MACHINE}" ]     || err 2 "\${MACHINE} is not defined"
+       [ -n "${MACHINE_ARCH}" ] || err 2 "\${MACHINE_ARCH} is not defined"
+       if ! $SOURCEMODE && ! [ -f "${SRC_DIR}/etc/mtree/set.etc" ]; then
+               err 2 "Files from the etc.tgz set are missing"
+       fi
+
+               # If directories are /, clear them, so various messages
+               # don't have leading "//".   However, this requires
+               # the use of ${foo:-/} to display the variables.
+               #
+       [ "${SRC_DIR}" = "/" ]  && SRC_DIR=""
+       [ "${DEST_DIR}" = "/" ] && DEST_DIR=""
+
+       detect_x11
+
+       op="$1"
+       shift
+
+       case "${op}" in
+       diff)
+               op=check
+               DIFF_STYLE=n                    # default style is RCS
+               OPTIND=1
+               while getopts bcenpuw ch; do
+                       case "${ch}" in
+                       c|e|n|u)
+                               if [ "${DIFF_STYLE}" != "n" -a \
+                                   "${DIFF_STYLE}" != "${ch}" ]; then
+                                       err 2 "conflicting output style: ${ch}"
+                               fi
+                               DIFF_STYLE="${ch}"
+                               ;;
+                       b|p|w)
+                               DIFF_OPT="${DIFF_OPT} -${ch}"
+                               ;;
+                       *)
+                               err 2 "unknown diff option"
+                               ;;
+                       esac
+               done
+               shift $((${OPTIND} - 1))
+               ;;
+       esac
+
+       case "${op}" in
+
+       usage|help)
+               usage
+               ;;
+
+       list)
+               echo "Source directory: ${SRC_DIR:-/}"
+               echo "Target directory: ${DEST_DIR:-/}"
+               if $TGZMODE; then
+                       echo " (extracted from: ${SRC_ARG})"
+               fi
+               list
+               ;;
+
+       check|fix)
+               todo="$*"
+               : ${todo:="${defaultitems}"}
+
+               # ensure that all supplied items are valid
+               #
+               for i in ${todo}; do
+                       eval desc=\"\${desc_${i}}\"
+                       [ -n "${desc}" ] || err 2 "Unsupported ${op} '"${i}"'"
+               done
+
+               # perform each check/fix
+               #
+               echo "Source directory: ${SRC_DIR:-/}"
+               if $TGZMODE; then
+                       echo " (extracted from: ${SRC_ARG})"
+               fi
+               echo "Target directory: ${DEST_DIR:-/}"
+               items_passed=
+               items_failed=
+               for i in ${todo}; do
+                       echo "${i} ${op}:"
+                       ( eval do_${i} ${op} )
+                       if [ $? -eq 0 ]; then
+                               items_passed="${items_passed} ${i}"
+                       else
+                               items_failed="${items_failed} ${i}"
+                       fi
+               done
+
+               if [ "${op}" = "check" ]; then
+                       plural="checks"
+               else
+                       plural="fixes"
+               fi
+
+               echo "${PROGNAME} ${plural} passed:${items_passed}"
+               echo "${PROGNAME} ${plural} failed:${items_failed}"
+               if [ -n "${items_failed}" ]; then
+                   exitstatus=1;
+                   if [ "${op}" = "check" ]; then
+                       [ "$MACHINE" = "$(uname -m)" ] && m= || m=" -m $MACHINE"
+                       cat <<_Fix_me_
+To fix, run:
+    ${0} ${SRC_ARGLIST} -d ${DEST_DIR:-/}$m fix${items_failed}
+Note that this may overwrite local changes.
+_Fix_me_
+                   fi
+               fi
+
+               ;;
+               
+       *)
+               warn "Unknown operation '"${op}"'"
+               usage
+               ;;
+
+       esac
+}
+
+# defaults
+#
+PROGNAME="${0##*/}"
+SRC_ARG="/usr/src"
+DEST_DIR="/"
+: ${MACHINE:="$( uname -m )"}  # assume native build if $MACHINE is not set
+: ${MACHINE_ARCH:="$( uname -p )"}# assume native build if not set
+
+DIFF_STYLE=
+NOT_FIXED=" (FIX MANUALLY)"
+SCRATCHDIR="$( mkdtemp )" || err 2 "Can't create scratch directory"
+trap "/bin/rm -rf \"\${SCRATCHDIR}\" ; exit 0" 1 2 3 15        # HUP INT QUIT TERM
+
+umask 022
+exec 3>/dev/null
+exec 4>/dev/null
+exitstatus=0
+
+main "$@"
+/bin/rm -rf "${SCRATCHDIR}"
+exit $exitstatus
diff --git a/usr.sbin/postinstall/postinstall.8 b/usr.sbin/postinstall/postinstall.8
new file mode 100644 (file)
index 0000000..30db841
--- /dev/null
@@ -0,0 +1,192 @@
+.\"    $NetBSD: postinstall.8,v 1.16 2012/08/15 16:21:41 apb Exp $
+.\"
+.\" Copyright (c) 2005-2008 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Thomas Klausner.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``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 FOUNDATION OR CONTRIBUTORS
+.\" 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.
+.\"
+.Dd August 15, 2012
+.Dt POSTINSTALL 8
+.Os
+.Sh NAME
+.Nm postinstall
+.Nd check and fix installation after system upgrades
+.Sh SYNOPSIS
+.Nm postinstall
+.Op Fl a Ar arch
+.Op Fl d Ar destdir
+.Op Fl m Ar machine
+.Op Fl s Brq Ar srcdir | Ar tgzdir | Ar tgzfile
+.Ar operation
+.Op Ar item Op ...
+.Sh DESCRIPTION
+The
+.Nm
+utility performs post-installation checks and/or fixes on a system's
+configuration files.
+It is especially useful after system upgrades, e.g. after updating
+from
+.Nx 1.6.2
+to
+.Nx 2.0 .
+The items to check or fix are divided in two groups: enabled by
+default and disabled by default.
+The latter are items that are dangerous for some reason, for example
+because they remove files which may be still in use.
+If no
+.Ar items
+are provided, the default checks or fixes are applied.
+Those which are disabled by default must be provided explicitly.
+.Pp
+Supported options:
+.Bl -tag -width XsXsrcdirXXX -offset indent
+.It Fl a Ar arch
+MACHINE_ARCH.
+Defaults to machine of the host operating system.
+.It Fl d Ar destdir
+Destination directory to check.
+Defaults to
+.Pa / .
+.It Fl m Ar machine
+MACHINE.
+Defaults to machine of the host operating system.
+.It Fl s Brq Ar srcdir | Ar tgzdir | Ar tgzfile
+The location of the reference files, or the
+.Nx
+source files used to create the reference files.
+This may be specified in one of three ways:
+.Bl -tag -width XXsXtgzfileXX
+.It Fl s Ar srcdir
+The top level directory of the
+.Nx
+source tree.
+By default this is
+.Pa /usr/src .
+.It Fl s Ar tgzdir
+A directory in which reference files have been
+extracted from a binary distribution of
+.Nx .
+The files that are distributed in the
+.Dq Pa etc.tgz
+set file must be present.
+The files that are distributed in the
+.Dq Pa xetc.tgz
+set file are optional.
+.It Fl s Ar tgzfile
+The location of a set file
+(or
+.Dq "tgz file" )
+such as
+.Dq Pa etc.tgz
+or
+.Dq Pa xetc.tgz
+from a binary distribution of
+.Nx .
+Each set file is a compressed archive containing reference files,
+which will be extracted to the
+.Pa temproot
+directory.
+Multiple
+.Fl s
+options may be used to specify multiple set files.
+The
+.Dq Pa etc.tgz
+set file must be specified.
+The
+.Dq Pa xetc.tgz
+set file is optional.
+.El
+.El
+.Pp
+The
+.Ar operation
+argument may be one of:
+.Bl -tag -width usageXX -offset indent
+.It Cm check
+Perform post-installation checks on items.
+.It Cm diff Op Xr diff 1 Li options
+Similar to
+.Cm check ,
+but also show the differences between the files.
+.It Cm fix
+Apply fixes that
+.Cm check
+determines need to be applied.
+Not all items can be automatically fixed by
+.Nm ,
+and in some cases an error will be reported,
+after which manual intervention will be required.
+.Pp
+Conflicts between existing files in the target file system
+and new files from the
+.Nx
+distribution are resolved by replacing the existing file
+with the new file; there is no attempt to merge the files.
+See
+.Xr etcupdate 8
+for an alternative update method that is able to merge files.
+.It Cm help
+Display a short help.
+.It Cm list
+List available
+.Ar items ,
+showing if they are enabled or disabled by default.
+.It Cm usage
+Same as
+.Cm help .
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, and \*[Gt]0 if an error occurs
+or a problem was found.
+.Sh SEE ALSO
+.Xr etcupdate 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Nx 1.6 .
+.Pp
+In
+.Nx 4.0 ,
+the
+.Fl s Ar tgzfile
+option was added.
+.Pp
+In
+.Nx 5.0 ,
+the ability to specify multiple colon-separated files with a single
+.Fl s
+option was deprecated.
+.Pp
+In
+.Nx 7.0 ,
+the ability to specify multiple colon-separated files with a single
+.Fl s
+option was removed.
+Multiple
+.Fl s
+options must be used instead.