Home | History | Annotate | Line # | Download | only in postinstall
postinstall.in revision 1.68
      1 #!/bin/sh
      2 #
      3 # $NetBSD: postinstall.in,v 1.68 2024/10/12 07:00:17 spz Exp $
      4 #
      5 # Copyright (c) 2002-2022 The NetBSD Foundation, Inc.
      6 # All rights reserved.
      7 #
      8 # This code is derived from software contributed to The NetBSD Foundation
      9 # by Luke Mewburn.
     10 #
     11 # Redistribution and use in source and binary forms, with or without
     12 # modification, are permitted provided that the following conditions
     13 # are met:
     14 # 1. Redistributions of source code must retain the above copyright
     15 #    notice, this list of conditions and the following disclaimer.
     16 # 2. Redistributions in binary form must reproduce the above copyright
     17 #    notice, this list of conditions and the following disclaimer in the
     18 #    documentation and/or other materials provided with the distribution.
     19 #
     20 # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21 # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23 # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30 # POSSIBILITY OF SUCH DAMAGE.
     31 #
     32 # postinstall
     33 #	Check for or fix configuration changes that occur
     34 #	over time as NetBSD evolves.
     35 #
     36 
     37 #
     38 # NOTE: Be sure to use ${DEST_DIR} prefix before all real file operations.
     39 #
     40 
     41 #
     42 # checks to add:
     43 #	- sysctl(8) renames (net.inet6.ip6.bindv6only -> net.inet6.ip6.v6only)
     44 #	- de* -> tlp* migration (/etc/ifconfig.de*, $ifconfig_de*, ...) ?
     45 #	- support quiet/verbose mode ?
     46 #	- differentiate between failures caused by missing source
     47 #	  and real failures
     48 #	- install moduli into usr/share/examples/ssh and use from there?
     49 #	- differentiate between "needs fix" versus "can't fix" issues
     50 #
     51 
     52 # This script is executed as part of a cross build.  Allow the build
     53 # environment to override the locations of some tools.
     54 : ${AWK:=awk}
     55 : ${DB:=db}
     56 : ${GREP:=grep}
     57 : ${HOST_SH:=sh}
     58 : ${MAKE:=make}
     59 : ${PWD_MKDB:=/usr/sbin/pwd_mkdb}
     60 : ${SED:=sed}
     61 : ${SORT:=sort}
     62 : ${STAT:=stat}
     63 : ${RM:=rm}
     64 
     65 #
     66 #	helper functions
     67 #
     68 
     69 err()
     70 {
     71 	local exitval=$1
     72 	shift
     73 	echo 1>&2 "${PROGNAME}: $*"
     74 	if [ -n "${SCRATCHDIR}" ]; then
     75 	    ${RM} -rf "${SCRATCHDIR}"
     76 	fi
     77 	exit ${exitval}
     78 }
     79 
     80 warn()
     81 {
     82 	echo 1>&2 "${PROGNAME}: $*"
     83 }
     84 
     85 msg()
     86 {
     87 	echo "	$*"
     88 }
     89 
     90 mkdtemp()
     91 {
     92 	# Make sure we don't loop forever if mkdir will always fail.
     93 	[ -d /tmp ] || err 2 /tmp is not a directory
     94 	[ -w /tmp ] || err 2 /tmp is not writable
     95 
     96 	local base="/tmp/_postinstall.$$"
     97 	local serial=0
     98 	local dir
     99 
    100 	while true; do
    101 		dir="${base}.${serial}"
    102 		mkdir -m 0700 "${dir}" && break
    103 		serial=$((${serial} + 1))
    104 	done
    105 	echo "${dir}"
    106 }
    107 
    108 # Quote args to make them safe in the shell.
    109 # Usage: quotedlist="$(shell_quote args...)"
    110 #
    111 # After building up a quoted list, use it by evaling it inside
    112 # double quotes, like this:
    113 #    eval "set -- $quotedlist"
    114 # or like this:
    115 #    eval "\$command $quotedlist \$filename"
    116 #
    117 shell_quote()
    118 {(
    119 	local result=''
    120 	local arg qarg
    121 	LC_COLLATE=C ; export LC_COLLATE # so [a-zA-Z0-9] works in ASCII
    122 	for arg in "$@" ; do
    123 		case "${arg}" in
    124 		'')
    125 			qarg="''"
    126 			;;
    127 		*[!-./a-zA-Z0-9]*)
    128 			# Convert each embedded ' to '\'',
    129 			# then insert ' at the beginning of the first line,
    130 			# and append ' at the end of the last line.
    131 			# Finally, elide unnecessary '' pairs at the
    132 			# beginning and end of the result and as part of
    133 			# '\'''\'' sequences that result from multiple
    134 			# adjacent quotes in he input.
    135 			qarg="$(printf "%s\n" "$arg" | \
    136 			    ${SED} -e "s/'/'\\\\''/g" \
    137 				-e "1s/^/'/" -e "\$s/\$/'/" \
    138 				-e "1s/^''//" -e "\$s/''\$//" \
    139 				-e "s/'''/'/g"
    140 				)"
    141 			;;
    142 		*)
    143 			# Arg is not the empty string, and does not contain
    144 			# any unsafe characters.  Leave it unchanged for
    145 			# readability.
    146 			qarg="${arg}"
    147 			;;
    148 		esac
    149 		result="${result}${result:+ }${qarg}"
    150 	done
    151 	printf "%s\n" "$result"
    152 )}
    153 
    154 # Convert arg $1 to a basic regular expression (as in sed)
    155 # that will match the arg.  This works by inserting backslashes
    156 # before characters that are special in basic regular expressions.
    157 # It also inserts backslashes before the extra characters specified
    158 # in $2 (which defaults to "/,").
    159 # XXX: Does not handle embedded newlines.
    160 # Usage: regex="$(bre_quote "${string}")"
    161 bre_quote()
    162 {
    163 	local arg="$1"
    164 	local extra="${2-/,}"
    165 	printf "%s\n" "${arg}" | ${SED} -e 's/[][^$.*\\'"${extra}"']/\\&/g'
    166 }
    167 
    168 # unprefix dir
    169 #	Remove any dir prefix from a list of paths on stdin,
    170 #	and write the result to stdout.  Useful for converting
    171 #	from ${DEST_DIR}/path to /path.
    172 #
    173 unprefix()
    174 {
    175 	[ $# -eq 1 ] || err 3 "USAGE: unprefix dir"
    176 	local prefix="${1%/}"
    177 	prefix="$(bre_quote "${prefix}")"
    178 
    179 	${SED} -e "s,^${prefix}/,/,"
    180 }
    181 
    182 # additem item description
    183 #	Add item to list of supported items to check/fix,
    184 #	which are checked/fixed by default if no item is requested by user.
    185 #
    186 additem()
    187 {
    188 	[ $# -eq 2 ] || err 3 "USAGE: additem item description"
    189 	defaultitems="${defaultitems}${defaultitems:+ }$1"
    190 	eval desc_$1=\"\$2\"
    191 }
    192 
    193 # adddisableditem item description
    194 #	Add item to list of supported items to check/fix,
    195 #	but execute the item only if the user asks for it explicitly.
    196 #
    197 adddisableditem()
    198 {
    199 	[ $# -eq 2 ] || err 3 "USAGE: adddisableditem item description"
    200 	otheritems="${otheritems}${otheritems:+ }$1"
    201 	eval desc_$1=\"\$2\"
    202 }
    203 
    204 # checkdir op dir mode
    205 #	Ensure dir exists, and if not, create it with the appropriate mode.
    206 #	Returns 0 if ok, 1 otherwise.
    207 #
    208 check_dir()
    209 {
    210 	[ $# -eq 3 ] || err 3 "USAGE: check_dir op dir mode"
    211 	local op="$1"
    212 	local dir="$2"
    213 	local mode="$3"
    214 	[ -d "${dir}" ] && return 0
    215 	if [ "${op}" = "check" ]; then
    216 		msg "${dir} is not a directory"
    217 		return 1
    218 	elif ! mkdir -m "${mode}" "${dir}" ; then
    219 		msg "Can't create missing ${dir}"
    220 		return 1
    221 	else
    222 		msg "Missing ${dir} created"
    223 	fi
    224 	return 0
    225 }
    226 
    227 # check_ids op type file srcfile start id ...
    228 #	Check if file of type "users" or "groups" contains the relevant IDs.
    229 #	Use srcfile as a reference for the expected contents.
    230 #	The specified "id" names should be given in numerical order,
    231 #	with the first name corresponding to numerical value "start",
    232 #	and with the special name "SKIP" being used to mark gaps in the
    233 #	sequence.
    234 #	Returns 0 if ok, 1 otherwise.
    235 #
    236 check_ids()
    237 {
    238 	[ $# -ge 6 ] || err 3 "USAGE: checks_ids op type file srcfile start id ..."
    239 	local op="$1"
    240 	local type="$2"
    241 	local file="$3"
    242 	local srcfile="$4"
    243 	local start="$5"
    244 	shift 5
    245 	#local ids="$@"
    246 
    247 	if [ ! -f "${file}" ]; then
    248 		msg "${file} doesn't exist; can't check for missing ${type}"
    249 		return 1
    250 	fi
    251 	if [ ! -r "${file}" ]; then
    252 		msg "${file} is not readable; can't check for missing ${type}"
    253 		return 1
    254 	fi
    255 	local notfixed=""
    256 	if [ "${op}" = "fix" ]; then
    257 		notfixed="${NOT_FIXED}"
    258 	fi
    259 	local missing="$(${AWK} -v start=$start -F: '
    260 		BEGIN {
    261 			for (x = 1; x < ARGC; x++) {
    262 				if (ARGV[x] == "SKIP")
    263 					continue;
    264 				idlist[ARGV[x]]++;
    265 				value[ARGV[x]] = start + x - 1;
    266 			}
    267 			ARGC=1
    268 		}
    269 		{
    270 			found[$1]++
    271 			number[$1] = $3
    272 		}
    273 		END {
    274 			for (id in idlist) {
    275 				if (!(id in found))
    276 					printf("%s (missing)\n", id)
    277 				else if (number[id] != value[id])
    278 					printf("%s (%d != %d)\n", id,
    279 					    number[id], value[id])
    280 				start++;
    281 			}
    282 		}
    283 	' "$@" < "${file}")"	|| return 1
    284 	if [ -n "${missing}" ]; then
    285 		msg "Error ${type}${notfixed}:" $(echo ${missing})
    286 		msg "Use the following as a template:"
    287 		set -- ${missing}
    288 		while [ $# -gt 0 ]
    289 		do
    290 			${GREP} -E "^${1}:" ${srcfile}
    291 			shift 2
    292 		done | sort -t: -k3n
    293 		msg "and adjust if necessary."
    294 		return 1
    295 	fi
    296 	return 0
    297 }
    298 
    299 # populate_dir op onlynew src dst mode file ...
    300 #	Perform op ("check" or "fix") on files in src/ against dst/
    301 #	If op = "check" display missing or changed files, optionally with diffs.
    302 #	If op != "check" copies any missing or changed files.
    303 #	If onlynew evaluates to true, changed files are ignored.
    304 #	Returns 0 if ok, 1 otherwise.
    305 #
    306 populate_dir()
    307 {
    308 	[ $# -ge 5 ] || err 3 "USAGE: populate_dir op onlynew src dst mode file ..."
    309 	local op="$1"
    310 	local onlynew="$2"
    311 	local src="$3"
    312 	local dst="$4"
    313 	local mode="$5"
    314 	shift 5
    315 	#local files="$@"
    316 
    317 	if [ ! -d "${src}" ]; then
    318 		msg "${src} is not a directory; skipping check"
    319 		return 1
    320 	fi
    321 	check_dir "${op}" "${dst}" 755 || return 1
    322 
    323 	local cmpdir_rv=0
    324 	local f fs fd error
    325 	for f in "$@"; do
    326 		fs="${src}/${f}"
    327 		fd="${dst}/${f}"
    328 		error=""
    329 		if [ ! -f "${fd}" ]; then
    330 			error="${fd} does not exist"
    331 		elif ! cmp -s "${fs}" "${fd}" ; then
    332 			if $onlynew; then	# leave existing ${fd} alone
    333 				continue;
    334 			fi
    335 			error="${fs} != ${fd}"
    336 		else
    337 			continue
    338 		fi
    339 		if [ "${op}" = "check" ]; then
    340 			msg "${error}"
    341 			if [ -n "${DIFF_STYLE}" -a -f "${fd}" ]; then
    342 				diff -${DIFF_STYLE} ${DIFF_OPT} "${fd}" "${fs}"
    343 			fi
    344 			cmpdir_rv=1
    345 		elif ! ${RM} -f "${fd}" ||
    346 		     ! cp -f "${fs}" "${fd}"; then
    347 			msg "Can't copy ${fs} to ${fd}"
    348 			cmpdir_rv=1
    349 		elif ! chmod "${mode}" "${fd}"; then
    350 			msg "Can't change mode of ${fd} to ${mode}"
    351 			cmpdir_rv=1
    352 		else
    353 			msg "Copied ${fs} to ${fd}"
    354 		fi
    355 	done
    356 	return ${cmpdir_rv}
    357 }
    358 
    359 # compare_dir op src dst mode file ...
    360 #	Perform op ("check" or "fix") on files in src/ against dst/
    361 #	If op = "check" display missing or changed files, optionally with diffs.
    362 #	If op != "check" copies any missing or changed files.
    363 #	Returns 0 if ok, 1 otherwise.
    364 #
    365 compare_dir()
    366 {
    367 	[ $# -ge 4 ] || err 3 "USAGE: compare_dir op src dst mode file ..."
    368 	local op="$1"
    369 	local src="$2"
    370 	local dst="$3"
    371 	local mode="$4"
    372 	shift 4
    373 	#local files="$@"
    374 
    375 	populate_dir "$op" false "$src" "$dst" "$mode" "$@"
    376 }
    377 
    378 # move_file op src dst --
    379 #	Check (op == "check") or move (op != "check") from src to dst.
    380 #	Returns 0 if ok, 1 otherwise.
    381 #
    382 move_file()
    383 {
    384 	[ $# -eq 3 ] || err 3 "USAGE: move_file op src dst"
    385 	local op="$1"
    386 	local src="$2"
    387 	local dst="$3"
    388 
    389 	if [ -f "${src}" -a ! -f "${dst}" ]; then
    390 		if [ "${op}" = "check" ]; then
    391 			msg "Move ${src} to ${dst}"
    392 			return 1
    393 		fi
    394 		if ! mv "${src}" "${dst}"; then
    395 			msg "Can't move ${src} to ${dst}"
    396 			return 1
    397 		fi
    398 		msg "Moved ${src} to ${dst}"
    399 	fi
    400 	return 0
    401 }
    402 
    403 # rcconf_is_set op name var [verbose] --
    404 #	Load the rcconf for name, and check if obsolete rc.conf(5) variable
    405 #	var is defined or not.
    406 #	Returns 0 if defined (even to ""), otherwise 1.
    407 #	If verbose != "", print an obsolete warning if the var is defined.
    408 #
    409 rcconf_is_set()
    410 {
    411 	[ $# -ge 3 ] || err 3 "USAGE: rcconf_is_set op name var [verbose]"
    412 	local op="$1"
    413 	local name="$2"
    414 	local var="$3"
    415 	local verbose="$4"
    416 	local notfixed=""
    417 	if [ "${op}" = "fix" ]; then
    418 		notfixed="${NOT_FIXED}"
    419 	fi
    420 	(
    421 		for f in \
    422 		    "${DEST_DIR}/etc/rc.conf" \
    423 		    "${DEST_DIR}/etc/rc.conf.d/${name}"; do
    424 			[ -f "${f}" ] && . "${f}"
    425 		done
    426 		eval echo -n \"\${${var}}\" 1>&3
    427 		if eval "[ -n \"\${${var}+SET}\" ]"; then
    428 			if [ -n "${verbose}" ]; then
    429 				msg \
    430     "Obsolete rc.conf(5) variable '\$${var}' found.${notfixed}"
    431 			fi
    432 			exit 0
    433 		else
    434 			exit 1
    435 		fi
    436 	)
    437 }
    438 
    439 # rcvar_is_enabled var
    440 #	Check if rcvar is enabled
    441 #
    442 rcvar_is_enabled()
    443 {
    444 	[ $# -eq 1 ] || err 3 "USAGE: rcvar_is_enabled var"
    445 	local var="$1"
    446 	(
    447 		[ -f "${DEST_DIR}/etc/rc.conf" ] && . "${DEST_DIR}/etc/rc.conf"
    448 		eval val=\"\${${var}}\"
    449 		case $val in
    450 		#	"yes", "true", "on", or "1"
    451 		[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
    452 			exit 0
    453 			;;
    454 
    455 		*)
    456 			exit 1
    457 			;;
    458 		esac
    459 	)
    460 }
    461 
    462 # find_file_in_dirlist() file message dir1 ... --
    463 #	Find which directory file is in, and sets ${dir} to match.
    464 #	Returns 0 if matched, otherwise 1 (and sets ${dir} to "").
    465 #
    466 #	Generally, check the directory for the "checking from source" case,
    467 #	and then the directory for the "checking from extracted etc.tgz" case.
    468 #
    469 find_file_in_dirlist()
    470 {
    471 	[ $# -ge 3 ] || err 3 "USAGE: find_file_in_dirlist file msg dir1 ..."
    472 
    473 	local file="$1" ; shift
    474 	local msg="$1" ; shift
    475 	local dir1st=	# first dir in list
    476 	# returns dir
    477 	for dir in "$@"; do
    478 		: ${dir1st:="${dir}"}
    479 		if [ -f "${dir}/${file}" ]; then
    480 			if [ "${dir1st}" != "${dir}" ]; then
    481 				msg \
    482     "(Checking for ${msg} from ${dir} instead of ${dir1st})"
    483 			fi
    484 			return 0
    485 		fi
    486 	done
    487 	msg "Can't find source directory for ${msg}"
    488 	return 1
    489 }
    490 
    491 # file_exists_exact path
    492 #	Returns true if a file exists in the ${DEST_DIR} whose name
    493 #	is exactly ${path}, interpreted in a case-sensitive way
    494 #	even if the underlying file system is case-insensitive.
    495 #
    496 #	The path must begin with '/' or './', and is interpreted as
    497 #	being relative to ${DEST_DIR}.
    498 #
    499 file_exists_exact()
    500 {
    501 	[ -n "$1" ] || err 3 "USAGE: file_exists_exact path"
    502 	local path="${1#.}"
    503 	[ -h "${DEST_DIR}${path}" ] || \
    504 		[ -e "${DEST_DIR}${path}" ] || return 1
    505 	while [ "${path}" != "/" -a "${path}" != "." ] ; do
    506 		local dirname="$(dirname "${path}" 2>/dev/null)"
    507 		local basename="$(basename "${path}" 2>/dev/null)"
    508 		ls -fa "${DEST_DIR}${dirname}" 2> /dev/null \
    509 			| ${GREP} -q -F -x "${basename}" \
    510 			|| return 1
    511 		path="${dirname}"
    512 	done
    513 	return 0
    514 }
    515 
    516 # obsolete_paths op
    517 #	Obsolete the list of paths provided on stdin.
    518 #	Each path should start with '/' or './', and
    519 #	will be interpreted relative to ${DEST_DIR}.
    520 #
    521 obsolete_paths()
    522 {
    523 	[ -n "$1" ] || err 3 "USAGE: obsolete_paths fix|check"
    524 	local op="$1"
    525 	local failed=0
    526 	local ofile cmd ftype
    527 
    528 	while read ofile; do
    529 		if ! ${file_exists_exact} "${ofile}"; then
    530 			continue
    531 		fi
    532 		ofile="${DEST_DIR}${ofile#.}"
    533 		cmd="${RM}"
    534 		ftype="file"
    535 		if [ -h "${ofile}" ]; then
    536 			ftype="link"
    537 		elif [ -d "${ofile}" ]; then
    538 			ftype="directory"
    539 			cmd="rmdir"
    540 		elif [ ! -e "${ofile}" ]; then
    541 			continue
    542 		fi
    543 		if [ "${op}" = "check" ]; then
    544 			msg "Remove obsolete ${ftype} ${ofile}"
    545 			failed=1
    546 		elif ! eval "${cmd} \"\${ofile}\""; then
    547 			msg "Can't remove obsolete ${ftype} ${ofile}"
    548 			failed=1
    549 		else
    550 			msg "Removed obsolete ${ftype} ${ofile}"
    551 		fi
    552 	done
    553 	return ${failed}
    554 }
    555 
    556 # obsolete_libs dir
    557 #	Display the minor/teeny shared libraries in dir that are considered
    558 #	to be obsolete.
    559 #
    560 #	The implementation supports removing obsolete major libraries
    561 #	if the awk variable AllLibs is set, although there is no way to
    562 #	enable that in the enclosing shell function as this time.
    563 #
    564 obsolete_libs()
    565 {
    566 	[ $# -eq 1 ] || err 3 "USAGE: obsolete_libs dir"
    567 	local dir="$1"
    568 
    569 	_obsolete_libs "${dir}"
    570 	_obsolete_libs "/usr/libdata/debug/${dir}"
    571 }
    572 
    573 exclude()
    574 {
    575 	if [ -z "$*" ]; then
    576 		cat
    577 	else
    578 		eval ${GREP} -v -E "'(^$(echo $* | sed -e 's/ /|^/'g))'"
    579 	fi
    580 }
    581 
    582 #
    583 # Find all the symlink targets of shared libraries and exclude them
    584 # from consideration for removal.
    585 #
    586 # This protects "new old" libs after downgrading a system.  Say, you
    587 # had a more recent system that comes with libfoo.so.1.2 and then
    588 # downgraded to an older version that had libfoo.so.1.1.  You still
    589 # have 1.2 that "wins" the competition for the "libfoo.so.1" stem and
    590 # thus 1.1 is considered obsolete by the awk script in _obsolete_libs
    591 # below.  But your libfoo.so.1 now points to 1.1 so deleting 1.1 will
    592 # render your system broken.  exclude_libs is fed the output of
    593 # potentially obsolete libs and absolves those (libfoo.so.1.1) that
    594 # are a target of a symlink (libfoo.so.1 -> libfoo.so.1.1)
    595 #
    596 exclude_libs()
    597 {
    598 	local targets="$(find lib*.so.* -prune -type l -exec readlink '{}' + 2> /dev/null \
    599 	    | ${SED} -e 's@.*/@@' | ${SORT} -u)"
    600 	${GREP} -F -v -x "$targets"
    601 }
    602 
    603 _obsolete_libs()
    604 {
    605 	local dir="$1"
    606 
    607 	(
    608 
    609 	[ -e "${DEST_DIR}/${dir}" ] || return 0
    610 	cd "${DEST_DIR}/${dir}" || err 2 "can't cd to ${DEST_DIR}/${dir}"
    611 
    612 	# TODO: make this selectable with a command line option?
    613 	local maybe_purge_major
    614 	#maybe_purge_major='-v PurgeOldMajor=1'
    615 
    616 	printf '%s\n' lib*.so.* | ${AWK} ${maybe_purge_major} '
    617 #{
    618 
    619 BEGIN {
    620 	BASE_REGEX  = "^lib.*\\.so\\."
    621 	MAJOR_REGEX = (BASE_REGEX "[0-9]+\\.")
    622 
    623 	# in the usual case different major versions of the same
    624 	# library are considered different stems and do not compete
    625 	# with each other, we keep one of each, but you may request to
    626 	# purge old majors, in which case all versions compete for the
    627 	# single basename stem
    628 	if (PurgeOldMajor)
    629 		keepone = BASE_REGEX
    630 	else
    631 		keepone = MAJOR_REGEX
    632 }
    633 
    634 # major version symlink
    635 PurgeOldMajor && /^lib.*\.so\.[0-9]+$/ {
    636 	checklib(major, $0, BASE_REGEX)
    637 }
    638 
    639 # specific minor version of a library
    640 /^lib.*\.so\.[0-9]+\.[0-9]+(\.[0-9]+)?(\.debug)?$/ {
    641 	checklib(minor, $0, keepone)
    642 }
    643 
    644 function checklib(latest, libname, stem_regex) {
    645 	if (! match(libname, stem_regex))
    646 		return
    647 	stem = substr(libname, RSTART, RLENGTH)
    648 	vers = substr(libname, RLENGTH + 1)
    649 
    650 	# first time we see this stem? just record the version
    651 	if (! (stem in latest)) {
    652 		latest[stem] = vers
    653 		return
    654 	}
    655 
    656 	# split version suffixes into the list of numeric components
    657 	oversc = split(latest[stem], overs, ".")
    658 	nversc = split(vers,         nvers, ".")
    659 	maxc = (oversc > nversc) ? oversc : nversc
    660 
    661 	# is the new version "later" than the one we have seen?
    662 	for (i = 1; i <= maxc; ++i) {
    663 		cmp = (overs[i]+0) - (nvers[i]+0)
    664 
    665 		if (cmp < 0) {
    666 			# the one we have seen is older, so report it
    667 			# as obsolete and update the latest seen
    668 			# version to this new one
    669 			print stem latest[stem]
    670 			latest[stem] = vers
    671 			return
    672 		}
    673 		else if (cmp > 0) {
    674 			# the one we have just read is older than the
    675 			# one we have seen previously, so report this
    676 			# "new" one as obsolete
    677 			print libname
    678 			return
    679 		}
    680 	}
    681 }
    682 
    683 	# the ouput is further filtered by exclude_libs that protects
    684 	# libraries that have symlinks pointing to them, which one
    685 	# encounters when downgrading
    686 #}'	\
    687 	| exclude_libs \
    688 	| ${SED} "s|^|${dir}/|"
    689 
    690 	)
    691 }
    692 
    693 # obsolete_stand dir
    694 #	Prints the names of all obsolete files and subdirs below the
    695 #	provided dir.  dir should be something like /stand/${MACHINE}.
    696 #	The input dir and all output paths are interpreted
    697 #	relative to ${DEST_DIR}.
    698 #
    699 #	Assumes that the numerically largest subdir is current, and all
    700 #	others are obsolete.
    701 #
    702 obsolete_stand()
    703 {
    704 	[ $# -eq 1 ] || err 3 "USAGE: obsolete_stand dir"
    705 	local dir="$1"
    706 	local subdir
    707 
    708 	if ! [ -d "${DEST_DIR}${dir}" ]; then
    709 		msg "${DEST_DIR}${dir} doesn't exist; can't check for obsolete files"
    710 		return 1
    711 	fi
    712 
    713 	( cd "${DEST_DIR}${dir}" && ls -1d [0-9]*[0-9]/. ) \
    714 	| ${GREP} -v '[^0-9./]' \
    715 	| sort -t. -r -n -k1,1 -k2,2 -k3,3 \
    716 	| tail -n +2 \
    717 	| while read subdir ; do
    718 		subdir="${subdir%/.}"
    719 		find "${DEST_DIR}${dir}/${subdir}" -depth -print
    720 	done \
    721 	| unprefix "${DEST_DIR}"
    722 }
    723 
    724 # modify_file op srcfile scratchfile awkprog
    725 #	Apply awkprog to srcfile sending output to scratchfile, and
    726 #	if appropriate replace srcfile with scratchfile.
    727 #
    728 modify_file()
    729 {
    730 	[ $# -eq 4 ] || err 3 "USAGE: modify_file op file scratch awkprog"
    731 
    732 	local op="$1"
    733 	local file="$2"
    734 	local scratch="$3"
    735 	local prog="$4"
    736 	local failed=0
    737 	local line
    738 
    739 	${AWK} "${prog}" < "${file}" > "${scratch}"
    740 	if ! cmp -s "${file}" "${scratch}"; then
    741 		diff "${file}" "${scratch}" > "${scratch}.diffs"
    742 		if [ "${op}" = "check" ]; then
    743 			msg "${file} needs the following changes:"
    744 			mffailed=1
    745 		elif ! ${RM} -f "${file}" ||
    746 		     ! cp -f "${scratch}" "${file}"; then
    747 			msg "${file} changes not applied:"
    748 			mffailed=1
    749 		else
    750 			msg "${file} changes applied:"
    751 		fi
    752 		while read line; do
    753 			msg "	${line}"
    754 		done < "${scratch}.diffs"
    755 	fi
    756 	return ${failed}
    757 }
    758 
    759 
    760 # contents_owner op directory user group
    761 #	Make sure directory and contents are owned (and group-owned)
    762 #	as specified.
    763 #
    764 contents_owner()
    765 {
    766 	[ $# -eq 4 ] || err 3 "USAGE: contents_owner op dir user group"
    767 
    768 	local op="$1"
    769 	local dir="$2"
    770 	local user="$3"
    771 	local grp="$4"
    772 	local files error
    773 
    774 	if [ "${op}" = "check" ]; then
    775 		files=$(find "${dir}" \( \( ! -user "${user}" \) -o \
    776 		                \( ! -group "${grp}" \) \) )
    777 		error=$?
    778 		if [ ! -z "$files" ] || [ $error != 0 ]; then
    779 			msg "${dir} and contents not all owned by" \
    780 			    "${user}:${grp}"
    781 			return 1
    782 		else
    783 			return 0
    784 		fi
    785 	elif [ "${op}" = "fix" ]; then
    786 		find "${dir}" \( \( ! -user "${user}" \) -o \
    787 		\( ! -group "${grp}" \) \) \
    788 		-exec chown "${user}:${grp}" -- {} \;
    789 	fi
    790 }
    791 
    792 # get_makevar var ...
    793 #	Retrieve the value of a user-settable system make variable
    794 get_makevar()
    795 {
    796 	local var value
    797 	$SOURCEMODE || err 3 "get_makevar must be used in source mode"
    798 	[ $# -eq 0 ] && err 3 "USAGE: get_makevar var ..."
    799 
    800 	for var in "$@"; do
    801 		value="$(echo '.include <bsd.own.mk>' | \
    802 		    ${MAKE} -f - -V "\${${var}}")"
    803 
    804 		eval ${var}=\"\${value}\"
    805 	done
    806 }
    807 
    808 # detect_x11
    809 #	Detect if X11 components should be analysed and set values of
    810 #	relevant variables.
    811 detect_x11()
    812 {
    813 	if $SOURCEMODE; then
    814 		get_makevar MKX11 X11ROOTDIR X11SRCDIR
    815 	else
    816 		if [ -f "${SRC_DIR}/etc/mtree/set.xetc" ]; then
    817 			MKX11=yes
    818 			X11ROOTDIR=/this/value/isnt/used/yet
    819 		else
    820 			MKX11=no
    821 			X11ROOTDIR=
    822 		fi
    823 		X11SRCDIR=/nonexistent/xsrc
    824 	fi
    825 }
    826 
    827 #
    828 #	find out where MAKEDEV lives, set MAKEDEV_DIR appropriately
    829 #
    830 find_makedev()
    831 {
    832 	if [ -e "${DEST_DIR}/dev/MAKEDEV" ]; then
    833 		MAKEDEV_DIR="${DEST_DIR}/dev"
    834 	elif [ -e "${DEST_DIR}/etc/MAKEDEV" ]; then
    835 		MAKEDEV_DIR="${DEST_DIR}/etc"
    836 	else
    837 		MAKEDEV_DIR="${DEST_DIR}/dev"
    838 	fi
    839 }
    840 
    841 
    842 #
    843 #	items
    844 #	-----
    845 #
    846 # NOTE: Keep these items sorted, except for obsolete* which are listed last.
    847 #
    848 
    849 #
    850 #	atf
    851 #
    852 
    853 handle_atf_user()
    854 {
    855 	local op="$1"
    856 	local conf="$2"
    857 	local option="unprivileged-user"
    858 	local old="_atf"
    859 	local new="_tests"
    860 	local failed=0
    861 
    862 	local c=$(readlink -f "${conf}")
    863 	if ${GREP} -q "[^#]*${option}[ \t]*=.*${old}" "${c}"
    864 	then
    865 		if [ "${op}" = "fix" ]; then
    866 			${SED} -e "/[^#]*${option}[\ t]*=/s/${old}/${new}/" \
    867 			    "${c}" >"${c}.new"
    868 			failed=$(( ${failed} + $? ))
    869 			mv "${c}.new" "${c}"
    870 			failed=$(( ${failed} + $? ))
    871 			msg "Set ${option}=${new} in ${c}"
    872 		else
    873 			msg "${option}=${old} in ${c} should be " \
    874 			    "${option}=${new}"
    875 			failed=1
    876 		fi
    877 	fi
    878 
    879 	return ${failed}
    880 }
    881 
    882 additem atf "install missing atf configuration files and validate them"
    883 do_atf()
    884 {
    885 	[ -n "$1" ] || err 3 "USAGE: do_atf fix|check"
    886 	local conf="${DEST_DIR}/etc/atf/common.conf"
    887 	local atfdir="${DEST_DIR}/etc/atf"
    888 	local op="$1"
    889 	local failed=0
    890 
    891 	# Ensure atf configuration files are in place.
    892 	if find_file_in_dirlist NetBSD.conf "NetBSD.conf" \
    893 	    "${SRC_DIR}/external/bsd/atf/etc/atf" \
    894 	    "${SRC_DIR}/etc/atf"; then
    895 		    # ${dir} is set by find_file_in_dirlist()
    896 		    populate_dir "${op}" true "${dir}" "${atfdir}" 644 \
    897 		    NetBSD.conf common.conf || failed=1
    898 	else
    899 		failed=1
    900 	fi
    901 	if find_file_in_dirlist atf-run.hooks "atf-run.hooks" \
    902 	    "${SRC_DIR}/external/bsd/atf/dist/tools/sample" \
    903 	    "${SRC_DIR}/etc/atf"; then
    904 		# ${dir} is set by find_file_in_dirlist()
    905 		populate_dir "${op}" true "${dir}" "${atfdir}" 644 \
    906 		    atf-run.hooks || failed=1
    907 	else
    908 		failed=1
    909 	fi
    910 
    911 	# Validate the _atf to _tests user/group renaming.
    912 	if [ -f "${conf}" ]; then
    913 		handle_atf_user "${op}" "${conf}" || failed=1
    914 	else
    915 		failed=1
    916 	fi
    917 
    918 	return ${failed}
    919 }
    920 
    921 
    922 #
    923 #	autofsconfig
    924 #
    925 
    926 additem autofsconfig "automounter configuration files"
    927 do_autofsconfig()
    928 {
    929 	[ -n "$1" ] || err 3 "USAGE: do_autofsconfig fix|check"
    930 	local autofs_files="
    931 include_ldap
    932 include_nis
    933 special_hosts
    934 special_media
    935 special_noauto
    936 special_null
    937 "
    938 	local op="$1"
    939 	local failed=0
    940 
    941 	if [ "$op" = "fix" ]; then
    942 		mkdir -p "${DEST_DIR}/etc/autofs"
    943 	fi
    944 	failed=$(( ${failed} + $? ))
    945 	populate_dir "$op" true "${SRC_DIR}/etc" \
    946 	    "${DEST_DIR}/etc" \
    947 	    644 \
    948 	    auto_master
    949 	failed=$(( ${failed} + $? ))
    950 	populate_dir "$op" true "${SRC_DIR}/etc/autofs" \
    951 	    "${DEST_DIR}/etc/autofs" \
    952 	    644 \
    953 	    ${autofs_files}
    954 	return ${failed}
    955 }
    956 
    957 
    958 #
    959 #	blocklist
    960 #
    961 
    962 fixblock()
    963 {
    964 	local op="$1"
    965 	local target="${DEST_DIR}$2"
    966 
    967 	if [ ! -f "${target}" ]; then
    968 		continue
    969 	fi
    970 
    971 	if ${GREP} '[bB]lack' "${target}" > /dev/null; then
    972 		if [ "$1" = "check" ]; then
    973 			msg "Fix old configuration file(s)."
    974 			return 1
    975 		else
    976 			local p=$(${STAT} -f %Lp "${target}")
    977 			chmod u+w "${target}" || return 1
    978 			if [ "$2" = "/etc/npf.conf" ]; then
    979 				${SED} -i -e 's/"blacklistd"/"blocklistd"/g' "${target}"
    980 			else
    981 				${SED} -i -e 's/\([bB]\)lacklist/\1locklist/g' "${target}"
    982 			fi
    983 			chmod "${p}" "${target}"
    984 		fi
    985 	fi
    986 }
    987 
    988 additem blocklist "rename old files to blocklist"
    989 do_blocklist()
    990 {
    991 	[ -n "$1" ] || err 3 "USAGE: do_blocklist fix|check"
    992 	local op="$1"
    993 	local i old
    994 
    995 	# if we are actually using blocklistd
    996 	for i in /var/db/blacklistd.db /etc/blacklistd.conf; do
    997 		old="${DEST_DIR}${i}"
    998 		if [ ! -f "${old}" ]; then
    999 			continue
   1000 		elif [ "$1" = "check" ]; then
   1001 			msg "Rename old file(s)."
   1002 			return 1
   1003 		fi
   1004 		local new=$(echo "${old}" | ${SED} s/black/block/)
   1005 		mv "${old}" "${new}" || return 1
   1006 	done
   1007 
   1008 	for i in /etc/rc.conf /etc/npf.conf /etc/blocklistd.conf \
   1009 	    /etc/defaults/rc.conf; do
   1010 		fixblock "${op}" "${i}" || return 1
   1011 	done
   1012 }
   1013 
   1014 
   1015 #
   1016 #	bluetooth
   1017 #
   1018 
   1019 additem bluetooth "Bluetooth configuration is up to date"
   1020 do_bluetooth()
   1021 {
   1022 	[ -n "$1" ] || err 3 "USAGE: do_bluetooth fix|check"
   1023 	local op="$1"
   1024 	local failed=0
   1025 
   1026 	populate_dir "${op}" true \
   1027 		"${SRC_DIR}/etc/bluetooth" "${DEST_DIR}/etc/bluetooth" 644 \
   1028 		hosts protocols btattach.conf btdevctl.conf
   1029 	failed=$(( ${failed} + $? ))
   1030 
   1031 	move_file "${op}" "${DEST_DIR}/var/db/btdev.xml" \
   1032 			"${DEST_DIR}/var/db/btdevctl.plist"
   1033 	failed=$(( ${failed} + $? ))
   1034 
   1035 	local notfixed=""
   1036 	if [ "${op}" = "fix" ]; then
   1037 		notfixed="${NOT_FIXED}"
   1038 	fi
   1039 	for _v in btattach btconfig btdevctl; do
   1040 		if rcvar_is_enabled "${_v}"; then
   1041 			msg \
   1042     "${_v} is obsolete in rc.conf(5)${notfixed}: use bluetooth=YES"
   1043 			failed=$(( ${failed} + 1 ))
   1044 		fi
   1045 	done
   1046 
   1047 	return ${failed}
   1048 }
   1049 
   1050 
   1051 #
   1052 #	catpages
   1053 #
   1054 
   1055 obsolete_catpages()
   1056 {
   1057 	local op="$1"
   1058 	local basedir="$2"
   1059 	local section="$3"
   1060 	local mandir="${basedir}/man${section}"
   1061 	local catdir="${basedir}/cat${section}"
   1062 	test -d "$mandir" || return 0
   1063 	test -d "$catdir" || return 0
   1064 	(cd "$mandir" && find . -type f) | {
   1065 	local failed=0
   1066 	while read manpage; do
   1067 		manpage="${manpage#./}"
   1068 		case "$manpage" in
   1069 		*.Z)
   1070 			catname="$catdir/${manpage%.*.Z}.0"
   1071 			;;
   1072 		*.gz)
   1073 			catname="$catdir/${manpage%.*.gz}.0"
   1074 			;;
   1075 		*)
   1076 			catname="$catdir/${manpage%.*}.0"
   1077 			;;
   1078 		esac
   1079 		test -e "$catname" -a "$catname" -ot "$mandir/$manpage" || continue
   1080 		if [ "${op}" = "fix" ]; then
   1081 			${RM} "$catname"
   1082 			failed=$(( ${failed} + $? ))
   1083 			msg "Removed obsolete cat page $catname"
   1084 		else
   1085 			msg "Obsolete cat page $catname"
   1086 			failed=1
   1087 		fi
   1088 	done
   1089 	exit $failed
   1090 	}
   1091 }
   1092 
   1093 additem catpages "remove outdated cat pages"
   1094 do_catpages()
   1095 {
   1096 	local op="$1"
   1097 	local failed=0
   1098 	local manbase sec
   1099 	for manbase in /usr/share/man /usr/X11R6/man /usr/X11R7/man; do
   1100 		for sec in 1 2 3 4 5 6 7 8 9; do
   1101 			obsolete_catpages "$1" "${DEST_DIR}${manbase}" "${sec}"
   1102 			failed=$(( ${failed} + $? ))
   1103 			if [ "${op}" = "fix" ]; then
   1104 				rmdir "${DEST_DIR}${manbase}/cat${sec}"/* \
   1105 					2>/dev/null
   1106 				rmdir "${DEST_DIR}${manbase}/cat${sec}" \
   1107 					2>/dev/null
   1108 			fi
   1109 		done
   1110 	done
   1111 	return $failed
   1112 }
   1113 
   1114 
   1115 #
   1116 #	ddbonpanic
   1117 #
   1118 
   1119 additem ddbonpanic "verify ddb.onpanic is configured in sysctl.conf"
   1120 do_ddbonpanic()
   1121 {
   1122 	[ -n "$1" ] || err 3 "USAGE: do_ddbonpanic fix|check"
   1123 
   1124 	if ${GREP} -E '^#*[[:space:]]*ddb\.onpanic[[:space:]]*\??=[[:space:]]*[[:digit:]]+' \
   1125 		"${DEST_DIR}/etc/sysctl.conf" >/dev/null 2>&1
   1126 	then
   1127 		result=0
   1128 	else
   1129 		if [ "$1" = check ]; then
   1130 			msg \
   1131     "The ddb.onpanic behaviour is not explicitly specified in /etc/sysctl.conf"
   1132 			result=1
   1133 		else
   1134 			echo >> "${DEST_DIR}/etc/sysctl.conf"
   1135 			${SED} < "${SRC_DIR}/etc/sysctl.conf" \
   1136 			   -e '/^ddb\.onpanic/q' | \
   1137 			       ${SED} -e '1,/^$/d' >> \
   1138 			    "${DEST_DIR}/etc/sysctl.conf"
   1139 			result=$?
   1140 		fi
   1141 	fi
   1142 	return ${result}
   1143 }
   1144 
   1145 
   1146 #
   1147 #	defaults
   1148 #
   1149 
   1150 additem defaults "/etc/defaults/ being up to date"
   1151 do_defaults()
   1152 {
   1153 	[ -n "$1" ] || err 3 "USAGE: do_defaults fix|check"
   1154 	local op="$1"
   1155 	local failed=0
   1156 	local etcsets=$(getetcsets)
   1157 
   1158 	local rc_exclude_scripts=""
   1159 	if $SOURCEMODE; then
   1160 		# For most architectures rc.conf(5) should be the same as the
   1161 		# one obtained from a source directory, except for the ones
   1162 		# that have an append file for it.
   1163 		local rc_conf_app="${SRC_DIR}/etc/etc.${MACHINE}/rc.conf.append"
   1164 		if [ -f "${rc_conf_app}" ]; then
   1165 			rc_exclude_scripts="rc.conf"
   1166 
   1167 			# Generate and compare the correct rc.conf(5) file
   1168 			mkdir "${SCRATCHDIR}/defaults"
   1169 
   1170 			cat "${SRC_DIR}/etc/defaults/rc.conf" "${rc_conf_app}" \
   1171 			    > "${SCRATCHDIR}/defaults/rc.conf"
   1172 
   1173 			compare_dir "${op}" "${SCRATCHDIR}/defaults" \
   1174 			    "${DEST_DIR}/etc/defaults" \
   1175 			    444 \
   1176 			    "rc.conf"
   1177 			failed=$(( ${failed} + $? ))
   1178 		fi
   1179 	fi
   1180 
   1181 	find_file_in_dirlist pf.boot.conf "pf.boot.conf" \
   1182 	    "${SRC_DIR}/usr.sbin/pf/etc/defaults" "${SRC_DIR}/etc/defaults" \
   1183 	    || return 1
   1184 	# ${dir} is set by find_file_in_dirlist()
   1185 	compare_dir "$op" "${dir}" "${DEST_DIR}/etc/defaults" 444 pf.boot.conf
   1186 	failed=$(( ${failed} + $? ))
   1187 
   1188 	rc_exclude_scripts="${rc_exclude_scripts} pf.boot.conf"
   1189 
   1190 	local rc_default_conf_files="$(select_set_files /etc/defaults/ \
   1191 	    "/etc/defaults/\([^[:space:]]*\.conf\)" ${etcsets} | \
   1192 	    exclude ${rc_exclude_scripts})"
   1193 	compare_dir "$op" "${SRC_DIR}/etc/defaults" "${DEST_DIR}/etc/defaults" \
   1194 		444 \
   1195 		${rc_default_conf_files}
   1196 	failed=$(( ${failed} + $? ))
   1197 
   1198 
   1199 	return ${failed}
   1200 }
   1201 
   1202 
   1203 #
   1204 #	dhcpcd
   1205 #
   1206 
   1207 additem dhcpcd "dhcpcd configuration is up to date"
   1208 do_dhcpcd()
   1209 {
   1210 	[ -n "$1" ] || err 3 "USAGE: do_dhcpcd fix|check"
   1211 	local op="$1"
   1212 	local failed=0
   1213 
   1214 	find_file_in_dirlist dhcpcd.conf "dhcpcd.conf" \
   1215 	    "${SRC_DIR}/external/bsd/dhcpcd/dist/src" \
   1216 	    "${SRC_DIR}/etc" || return 1
   1217 			# ${dir} is set by find_file_in_dirlist()
   1218 	populate_dir "$op" true "${dir}" "${DEST_DIR}/etc" 644 dhcpcd.conf
   1219 	failed=$(( ${failed} + $? ))
   1220 
   1221 	check_dir "${op}" "${DEST_DIR}/var/db/dhcpcd" 755
   1222 	failed=$(( ${failed} + $? ))
   1223 
   1224 	move_file "${op}" \
   1225 		"${DEST_DIR}/etc/dhcpcd.duid" \
   1226 		"${DEST_DIR}/var/db/dhcpcd/duid"
   1227 	failed=$(( ${failed} + $? ))
   1228 
   1229 	move_file "${op}" \
   1230 		"${DEST_DIR}/etc/dhcpcd.secret" \
   1231 		"${DEST_DIR}/var/db/dhcpcd/secret"
   1232 	failed=$(( ${failed} + $? ))
   1233 
   1234 	move_file "${op}" \
   1235 		"${DEST_DIR}/var/db/dhcpcd-rdm.monotonic" \
   1236 		"${DEST_DIR}/var/db/dhcpcd/rdm_monotonic"
   1237 	failed=$(( ${failed} + $? ))
   1238 
   1239 	for lease in "${DEST_DIR}/var/db/dhcpcd-"*.lease*; do
   1240 		[ -f "${lease}" ] || continue
   1241 		new_lease=$(basename "${lease}" | ${SED} -e 's/dhcpcd-//')
   1242 		new_lease="${DEST_DIR}/var/db/dhcpcd/${new_lease}"
   1243 		move_file "${op}" "${lease}" "${new_lease}"
   1244 		failed=$(( ${failed} + $? ))
   1245 	done
   1246 
   1247 	chroot_dir="${DEST_DIR}/var/chroot/dhcpcd"
   1248 	move_file "${op}" \
   1249 		"${chroot_dir}/var/db/dhcpcd/duid" \
   1250 		"${DEST_DIR}/var/db/dhcpcd/duid"
   1251 	failed=$(( ${failed} + $? ))
   1252 
   1253 	move_file "${op}" \
   1254 		"${chroot_dir}/var/db/dhcpcd/secret" \
   1255 		"${DEST_DIR}/var/db/dhcpcd/secret"
   1256 	failed=$(( ${failed} + $? ))
   1257 
   1258 	move_file "${op}" \
   1259 		"${chroot_dir}/var/db/dhcpcd/rdm_monotonic" \
   1260 		"${DEST_DIR}/var/db/dhcpcd/rdm_monotonic"
   1261 	failed=$(( ${failed} + $? ))
   1262 
   1263 	for lease in "${chroot_dir}/var/db/dhcpcd/"*.lease*; do
   1264 		[ -f "${lease}" ] || continue
   1265 		new_lease="${DEST_DIR}/var/db/dhcpcd/$(basename ${lease})"
   1266 		move_file "${op}" "${lease}" "${new_lease}"
   1267 		failed=$(( ${failed} + $? ))
   1268 	done
   1269 
   1270 	# Ensure chroot is now empty
   1271 	for dir in \
   1272 		$(find ${chroot_dir} ! -type d) \
   1273 		$(find ${chroot_dir} -type d -mindepth 1 | sort -r)
   1274 	do
   1275 		echo "/var/chroot/dhcpcd${dir##${chroot_dir}}"
   1276 	done | obsolete_paths "${op}"
   1277 	failed=$(( ${failed} + $? ))
   1278 
   1279 	contents_owner "${op}" "${DEST_DIR}/var/db/dhcpcd" root wheel
   1280 	failed=$(( ${failed} + $? ))
   1281 
   1282 	return ${failed}
   1283 }
   1284 
   1285 
   1286 #
   1287 #	dhcpcdrundir
   1288 #
   1289 
   1290 additem dhcpcdrundir "accidentally created /@RUNDIR@ does not exist"
   1291 do_dhcpcdrundir()
   1292 {
   1293 	[ -n "$1" ] || err 3 "USAGE: do_dhcpcdrundir fix|check"
   1294 	local op="$1"
   1295 	local failed=0
   1296 
   1297 	if [ -d "${DEST_DIR}/@RUNDIR@" ]; then
   1298 		if [ "${op}" = "check" ]; then
   1299 			msg "Remove erroneously created /@RUNDIR@"
   1300 			failed=1
   1301 		elif ! ${RM} -r "${DEST_DIR}/@RUNDIR@"; then
   1302 			msg "Failed to remove ${DEST_DIR}/@RUNDIR@"
   1303 			failed=1
   1304 		else
   1305 			msg "Removed erroneously created ${DEST_DIR}/@RUNDIR@"
   1306 		fi
   1307 	fi
   1308 	return ${failed}
   1309 }
   1310 
   1311 
   1312 #
   1313 #	envsys
   1314 #
   1315 
   1316 additem envsys "envsys configuration is up to date"
   1317 do_envsys()
   1318 {
   1319 	[ -n "$1" ] || err 3 "USAGE: do_envsys fix|check"
   1320 	local op="$1"
   1321 	local failed=0
   1322 	local etcsets=$(getetcsets)
   1323 
   1324 	populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
   1325 		envsys.conf
   1326 	failed=$(( ${failed} + $? ))
   1327 
   1328 	local powerd_scripts="$(select_set_files /etc/powerd/scripts/ \
   1329 	    "/etc/powerd/scripts/\([^[:space:]/]*\)" ${etcsets})"
   1330 
   1331 	populate_dir "$op" true "${SRC_DIR}/etc/powerd/scripts" \
   1332 		"${DEST_DIR}/etc/powerd/scripts" \
   1333 		555 \
   1334 		${powerd_scripts}
   1335 	failed=$(( ${failed} + $? ))
   1336 
   1337 	return ${failed}
   1338 }
   1339 
   1340 
   1341 #
   1342 #	fontconfig
   1343 #
   1344 
   1345 additem fontconfig "X11 font configuration is up to date"
   1346 do_fontconfig()
   1347 {
   1348 	[ -n "$1" ] || err 3 "USAGE: do_fontconfig fix|check"
   1349 	local op="$1"
   1350 	local failed=0
   1351 
   1352 	# First, check for updates we can handle.
   1353 	if ! $SOURCEMODE; then
   1354 		FONTCONFIG_DIR="${SRC_DIR}/etc/fonts/conf.avail"
   1355 	else
   1356 		FONTCONFIG_DIR="${XSRC_DIR}/external/mit/fontconfig/dist/conf.d"
   1357 	fi
   1358 
   1359 	if [ ! -d "${FONTCONFIG_DIR}" ]; then
   1360 		msg "${FONTCONFIG_DIR} is not a directory; skipping check"
   1361 		return 0
   1362 	fi
   1363 	local regular_fonts="
   1364 10-autohint.conf
   1365 10-scale-bitmap-fonts.conf
   1366 10-sub-pixel-bgr.conf
   1367 10-sub-pixel-none.conf
   1368 10-sub-pixel-rgb.conf
   1369 10-sub-pixel-vbgr.conf
   1370 10-sub-pixel-vrgb.conf
   1371 10-unhinted.conf
   1372 11-lcdfilter-default.conf
   1373 11-lcdfilter-legacy.conf
   1374 11-lcdfilter-light.conf
   1375 20-unhint-small-vera.conf
   1376 25-unhint-nonlatin.conf
   1377 30-metric-aliases.conf
   1378 40-nonlatin.conf
   1379 45-generic.conf
   1380 45-latin.conf
   1381 49-sansserif.conf
   1382 50-user.conf
   1383 51-local.conf
   1384 60-generic.conf
   1385 60-latin.conf
   1386 65-fonts-persian.conf
   1387 65-khmer.conf
   1388 65-nonlatin.conf
   1389 69-unifont.conf
   1390 70-no-bitmaps.conf
   1391 70-yes-bitmaps.conf
   1392 80-delicious.conf
   1393 90-synthetic.conf
   1394 "
   1395 	populate_dir "$op" false "${FONTCONFIG_DIR}" \
   1396 	    "${DEST_DIR}/etc/fonts/conf.avail" \
   1397 	    444 \
   1398 	    ${regular_fonts}
   1399 	failed=$(( ${failed} + $? ))
   1400 
   1401 	if ! $SOURCEMODE; then
   1402 		FONTS_DIR="${SRC_DIR}/etc/fonts"
   1403 	else
   1404 		FONTS_DIR="${SRC_DIR}/external/mit/xorg/lib/fontconfig/etc"
   1405 	fi
   1406 
   1407 	populate_dir "$op" false "${FONTS_DIR}" "${DEST_DIR}/etc/fonts" 444 \
   1408 		fonts.conf
   1409 	failed=$(( ${failed} + $? ))
   1410 
   1411 	# We can't modify conf.d easily; someone might have removed a file.
   1412 
   1413 	# Look for old files that need to be deleted.
   1414 	local obsolete_fonts="
   1415 10-autohint.conf
   1416 10-no-sub-pixel.conf
   1417 10-sub-pixel-bgr.conf
   1418 10-sub-pixel-rgb.conf
   1419 10-sub-pixel-vbgr.conf
   1420 10-sub-pixel-vrgb.conf
   1421 10-unhinted.conf
   1422 25-unhint-nonlatin.conf
   1423 65-khmer.conf
   1424 70-no-bitmaps.conf
   1425 70-yes-bitmaps.conf
   1426 "
   1427 	local failed_fonts=""
   1428 	for i in ${obsolete_fonts}; do
   1429 	    if [ -f "${DEST_DIR}/etc/fonts/conf.d/$i" ]; then
   1430 		    conf_d_failed=1
   1431 		    failed_fonts="$failed_fonts $i"
   1432 	    fi
   1433 	done
   1434 
   1435 	if [ -n "$failed_fonts" ]; then
   1436 		msg \
   1437     "Broken fontconfig configuration found; please delete these files:"
   1438 		msg "[$failed_fonts]"
   1439 		failed=$(( ${failed} + 1 ))
   1440 	fi
   1441 
   1442 	return ${failed}
   1443 }
   1444 
   1445 
   1446 #
   1447 #	gid
   1448 #
   1449 
   1450 additem gid "required groups in /etc/group"
   1451 do_gid()
   1452 {
   1453 	[ -n "$1" ] || err 3 "USAGE: do_gid fix|check"
   1454 
   1455 	check_ids "$1" groups "${DEST_DIR}/etc/group" \
   1456 	    "${SRC_DIR}/etc/group" 14 \
   1457 	    named ntpd sshd SKIP _pflogd _rwhod staff _proxy _timedc \
   1458 	    _sdpd _httpd _mdnsd _tests _tcpdump _tss _gpio _rtadvd SKIP \
   1459 	    _unbound _nsd nvmm _dhcpcd
   1460 }
   1461 
   1462 
   1463 #
   1464 #	gpio
   1465 #
   1466 
   1467 additem gpio "gpio configuration is up to date"
   1468 do_gpio()
   1469 {
   1470 	[ -n "$1" ] || err 3 "USAGE: do_gpio fix|check"
   1471 	local op="$1"
   1472 	local failed=0
   1473 
   1474 	populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
   1475 		gpio.conf
   1476 	failed=$(( ${failed} + $? ))
   1477 
   1478 	return ${failed}
   1479 }
   1480 
   1481 
   1482 #
   1483 #	hosts
   1484 #
   1485 
   1486 additem hosts "/etc/hosts being up to date"
   1487 do_hosts()
   1488 {
   1489 	[ -n "$1" ] || err 3 "USAGE: do_hosts fix|check"
   1490 
   1491 	modify_file "$1" "${DEST_DIR}/etc/hosts" "${SCRATCHDIR}/hosts" '
   1492 		/^(127\.0\.0\.1|::1)[ 	]+[^\.]*$/ {
   1493 			print $0, "localhost."
   1494 			next
   1495 		}
   1496 		{ print }
   1497 	'
   1498 	return $?
   1499 }
   1500 
   1501 
   1502 #
   1503 #	iscsi
   1504 #
   1505 
   1506 additem iscsi "/etc/iscsi is populated"
   1507 do_iscsi()
   1508 {
   1509 	[ -n "$1" ] || err 3 "USAGE: do_iscsi fix|check"
   1510 
   1511 	populate_dir "${op}" true \
   1512 	    "${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 600 auths
   1513 	populate_dir "${op}" true \
   1514 	    "${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 644 targets
   1515 	return $?
   1516 }
   1517 
   1518 
   1519 #
   1520 #	mailerconf
   1521 #
   1522 
   1523 adddisableditem mailerconf "update /etc/mailer.conf after sendmail removal"
   1524 do_mailerconf()
   1525 {
   1526 	[ -n "$1" ] || err 3 "USAGE: do_mailterconf fix|check"
   1527 	local op="$1"
   1528 
   1529 	local failed=0
   1530 	mta_path="$(${AWK} '/^sendmail[ \t]/{print$2}' \
   1531 		"${DEST_DIR}/etc/mailer.conf")"
   1532 	old_sendmail_path="/usr/libexec/sendmail/sendmail"
   1533 	if [ "${mta_path}" = "${old_sendmail_path}" ]; then
   1534 	    if [ "$op" = check ]; then
   1535 		msg "mailer.conf points to obsolete ${old_sendmail_path}"
   1536 		failed=1;
   1537 	    else
   1538 		populate_dir "${op}" false \
   1539 		"${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 mailer.conf
   1540 		failed=$?
   1541 	    fi
   1542 	fi
   1543 
   1544 	return ${failed}
   1545 }
   1546 
   1547 
   1548 #
   1549 #	makedev
   1550 #
   1551 
   1552 additem makedev "/dev/MAKEDEV being up to date"
   1553 do_makedev()
   1554 {
   1555 	[ -n "$1" ] || err 3 "USAGE: do_makedev fix|check"
   1556 	local failed=0
   1557 
   1558 	if [ -f "${SRC_DIR}/etc/MAKEDEV.tmpl" ]; then
   1559 			# generate MAKEDEV from source if source is available
   1560 		env MACHINE="${MACHINE}" \
   1561 		    MACHINE_ARCH="${MACHINE_ARCH}" \
   1562 		    NETBSDSRCDIR="${SRC_DIR}" \
   1563 		    ${AWK} -f "${SRC_DIR}/etc/MAKEDEV.awk" \
   1564 		    "${SRC_DIR}/etc/MAKEDEV.tmpl" > "${SCRATCHDIR}/MAKEDEV"
   1565 	fi
   1566 
   1567 	find_file_in_dirlist MAKEDEV "MAKEDEV" \
   1568 	    "${SCRATCHDIR}" "${SRC_DIR}/dev" \
   1569 	    || return 1
   1570 			# ${dir} is set by find_file_in_dirlist()
   1571 	find_makedev
   1572 	compare_dir "$1" "${dir}" "${MAKEDEV_DIR}" 555 MAKEDEV
   1573 	failed=$(( ${failed} + $? ))
   1574 
   1575 	find_file_in_dirlist MAKEDEV.local "MAKEDEV.local" \
   1576 	    "${SRC_DIR}/etc" "${SRC_DIR}/dev" \
   1577 	    || return 1
   1578 			# ${dir} is set by find_file_in_dirlist()
   1579 	compare_dir "$1" "${dir}" "${DEST_DIR}/dev" 555 MAKEDEV.local
   1580 	failed=$(( ${failed} + $? ))
   1581 
   1582 	return ${failed}
   1583 }
   1584 
   1585 
   1586 #
   1587 #	man.conf
   1588 #
   1589 
   1590 additem manconf "check for a mandoc usage in /etc/man.conf"
   1591 do_manconf()
   1592 {
   1593 	[ -n "$1" ] || err 3 "USAGE: do_manconf fix|check"
   1594 	local op="$1"
   1595 	local failed=0
   1596 
   1597 	[ -f "${DEST_DIR}/etc/man.conf" ] || return 0
   1598 	if ${GREP} -w "mandoc" "${DEST_DIR}/etc/man.conf" >/dev/null 2>&1;
   1599 	then
   1600 		failed=0;
   1601 	else
   1602 		failed=1
   1603 		notfixed=""
   1604 		if [ "${op}" = "fix" ]; then
   1605 			notfixed="${NOT_FIXED}"
   1606 		fi
   1607 		msg "The file /etc/man.conf has not been adapted to mandoc usage; you"
   1608 		msg "probably want to copy a new version over. ${notfixed}"
   1609 	fi
   1610 
   1611 	return ${failed}
   1612 }
   1613 
   1614 
   1615 #
   1616 #	motd
   1617 #
   1618 
   1619 additem motd "contents of motd"
   1620 do_motd()
   1621 {
   1622 	[ -n "$1" ] || err 3 "USAGE: do_motd fix|check"
   1623 
   1624 	if ${GREP} -i 'http://www.NetBSD.org/Misc/send-pr.html' \
   1625 		"${DEST_DIR}/etc/motd" >/dev/null 2>&1 \
   1626 	    || ${GREP} -i 'https*://www.NetBSD.org/support/send-pr.html' \
   1627 		"${DEST_DIR}/etc/motd" >/dev/null 2>&1
   1628 	then
   1629 		tmp1="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
   1630 		tmp2="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
   1631 		${SED} '1,2d' <"${SRC_DIR}/etc/motd" >"${tmp1}"
   1632 		${SED} '1,2d' <"${DEST_DIR}/etc/motd" >"${tmp2}"
   1633 
   1634 		if [ "$1" = check ]; then
   1635 			cmp -s "${tmp1}" "${tmp2}"
   1636 			result=$?
   1637 			if [ "${result}" -ne 0 ]; then
   1638 				msg \
   1639     "Bug reporting messages do not seem to match the installed release"
   1640 			fi
   1641 		else
   1642 			head -n 2 "${DEST_DIR}/etc/motd" >"${tmp1}"
   1643 			${SED} '1,2d' <"${SRC_DIR}/etc/motd" >>"${tmp1}"
   1644 			cp "${tmp1}" "${DEST_DIR}/etc/motd"
   1645 			result=0
   1646 		fi
   1647 
   1648 		${RM} -f "${tmp1}" "${tmp2}"
   1649 	else
   1650 		result=0
   1651 	fi
   1652 
   1653 	return ${result}
   1654 }
   1655 
   1656 
   1657 #
   1658 #	mtree
   1659 #
   1660 
   1661 additem mtree "/etc/mtree/ being up to date"
   1662 do_mtree()
   1663 {
   1664 	[ -n "$1" ] || err 3 "USAGE: do_mtree fix|check"
   1665 	local failed=0
   1666 
   1667 	compare_dir "$1" "${SRC_DIR}/etc/mtree" "${DEST_DIR}/etc/mtree" 444 special
   1668 	failed=$(( ${failed} + $? ))
   1669 
   1670 	if ! $SOURCEMODE; then
   1671 		MTREE_DIR="${SRC_DIR}/etc/mtree"
   1672 	else
   1673 		${RM} -rf "${SCRATCHDIR}/obj"
   1674 		mkdir "${SCRATCHDIR}/obj"
   1675 		${MAKE} -s -C "${SRC_DIR}/etc/mtree" TOOL_AWK="${AWK}" \
   1676 		    MAKEOBJDIR="${SCRATCHDIR}/obj" emit_dist_file > \
   1677 		    "${SCRATCHDIR}/NetBSD.dist"
   1678 		MTREE_DIR="${SCRATCHDIR}"
   1679 		${RM} -rf "${SCRATCHDIR}/obj"
   1680 	fi
   1681 	compare_dir "$1" "${MTREE_DIR}" "${DEST_DIR}/etc/mtree" 444 NetBSD.dist
   1682 	failed=$(( ${failed} + $? ))
   1683 
   1684 	return ${failed}
   1685 }
   1686 
   1687 
   1688 #
   1689 #	named
   1690 #
   1691 handle_named_conf()
   1692 {
   1693 	local op="$1"
   1694 	local option="dnssec-enable"
   1695 	local failed=0
   1696 	local conf
   1697 
   1698 	shift
   1699 
   1700 	for conf; do
   1701 		local c=$(readlink -f "${conf}")
   1702 		if ! ${GREP} -qs "${option}" "${c}"
   1703 		then
   1704 			continue
   1705 		fi
   1706 
   1707 		if [ "${op}" = "fix" ]; then
   1708 			${SED} -e "/${option}/d" "${c}" > "${c}.new"
   1709 			failed=$(( ${failed} + $? ))
   1710 			mv "${c}.new" "${c}"
   1711 			failed=$(( ${failed} + $? ))
   1712 			msg "Removed obsolete '${option}' in ${c}"
   1713 		else
   1714 			msg "'${option}' option in ${c} should be removed"
   1715 			failed=$(( ${failed} + 1 ))
   1716 		fi
   1717 	done
   1718 
   1719 	return ${failed}
   1720 }
   1721 
   1722 additem named "named configuration update"
   1723 do_named()
   1724 {
   1725 	local oldconf="${DEST_DIR}/etc/namedb/named.conf"
   1726 	local conf="${DEST_DIR}/etc/named.conf"
   1727 	[ -n "$1" ] || err 3 "USAGE: do_named fix|check"
   1728 	local op="$1"
   1729 
   1730 	move_file "${op}" "${oldconf}" "${conf}"
   1731 	handle_named_conf "${op}" "${oldconf}" "${conf}"
   1732 
   1733 	compare_dir "${op}" "${SRC_DIR}/etc/namedb" "${DEST_DIR}/etc/namedb" \
   1734 		644 \
   1735 		root.cache
   1736 
   1737 	local od="${DEST_DIR}/usr/libexec/named"
   1738 	if [ -d "$od" ]; then
   1739 		rm -fr "$od"
   1740 		msg "Removed obsolete '${od}'"
   1741 	fi
   1742 }
   1743 
   1744 
   1745 #
   1746 #	opensslcertsconf
   1747 #
   1748 
   1749 additem opensslcertsconf "ensure TLS trust anchor configuration exists"
   1750 do_opensslcertsconf()
   1751 {
   1752 	local certsdir certsconf defaultconf manualmsg
   1753 
   1754 	[ -n "$1" ] || err 3 "USAGE: do_opensslcertsconf fix|check"
   1755 
   1756 	certsdir="${DEST_DIR}/etc/openssl/certs"
   1757 	certsconf="${DEST_DIR}/etc/openssl/certs.conf"
   1758 	defaultconf="${DEST_DIR}/usr/share/examples/certctl/certs.conf"
   1759 
   1760 	case $1 in
   1761 	check)	if [ ! -r "$certsconf" ]; then
   1762 			msg "/etc/openssl/certs.conf missing; see certctl(8)"
   1763 			return 1
   1764 		fi
   1765 		;;
   1766 	fix)	# If /etc/openssl/certs.conf is already there, nothing
   1767 		# to do.
   1768 		if [ -r "$certsconf" ]; then
   1769 			return 0
   1770 		fi
   1771 
   1772 		# If /etc/openssl/certs is a symlink, or exists but is
   1773 		# not a directory, or is a directory but is nonempty,
   1774 		# then either it's managed by someone else or something
   1775 		# fishy is afoot.  So set it manual in that case.
   1776 		# Otherwise, install the default config file.
   1777 		if [ -h "$certsdir" ] ||
   1778 		    [ -e "$certsdir" -a ! -d "$certsdir" ] ||
   1779 		    ([ -d "$certsdir" ] &&
   1780 			find -f "$certsdir" -- \
   1781 			    -maxdepth 0 -type d -empty -exit 1)
   1782 		then
   1783 			msg "/etc/openssl/certs appears manually configured"
   1784 			manualmsg="[existing /etc/openssl/certs configuration"
   1785 			manualmsg="$manualmsg detected by postinstall(8)]"
   1786                         # Change the commented-out `#manual' line to
   1787                         # uncommented `manual', or print an error
   1788                         # message if there is no `#manual' line and put
   1789                         # `manual' at the end.
   1790                         awk -v defaultconf="$defaultconf" \
   1791 			    -v manualmsg="$manualmsg" '
   1792 				BEGIN {
   1793 					manual = 0
   1794 				}
   1795 				/^#manual/ && !manual {
   1796 					manual = 1
   1797 					sub(/^#/, "")
   1798 					print
   1799 					print "#", manualmsg
   1800 					next
   1801 				}
   1802 				{
   1803 					print
   1804 				}
   1805 				END {
   1806 					if (!manual) {
   1807 						printf "warning: %s %s?\n", \
   1808 						    "corrupt", defaultconf \
   1809 						    >"/dev/stderr"
   1810 						print "manual"
   1811 						print "#", manualmsg
   1812 					}
   1813 				}
   1814 			' <$defaultconf >${certsconf}.tmp
   1815 		else
   1816 			msg "installing default /etc/openssl/certs.conf"
   1817 			cat <$defaultconf >${certsconf}.tmp
   1818 		fi && mv -f -- "${certsconf}.tmp" "$certsconf"
   1819 		;;
   1820 	*)	err 3 "USAGE: do_opensslcerts fix|check"
   1821 		;;
   1822 	esac
   1823 }
   1824 
   1825 
   1826 #
   1827 #	opensslcertsrehash
   1828 #
   1829 
   1830 additem opensslcertsrehash "make /etc/openssl/certs cache of TLS trust anchors"
   1831 do_opensslcertsrehash()
   1832 {
   1833 	local mtreekeys scratchdir
   1834 
   1835 	[ -n "$1" ] || err 3 "USAGE: do_opensslcertsrehash fix|check"
   1836 
   1837 	if [ ! -r "${DEST_DIR}/etc/openssl/certs.conf" ]; then
   1838 		msg "/etc/openssl/certs.conf missing; see certctl(8)"
   1839 		return 1
   1840 	fi
   1841 
   1842 	case $1 in
   1843 	check)	# Create a scratch rehash for comparison.
   1844 		mtreekeys="type,link"
   1845 		scratchdir="${SCRATCHDIR}/opensslcerts"
   1846 		/usr/sbin/certctl -c "$scratchdir" rehash || return $?
   1847 
   1848 		# This will create ${scratchdir}/.certctl unless the
   1849 		# configuration is manual.  If the configuration is
   1850 		# manual, stop here; nothing to do.  certctl(8) will
   1851 		# have already printed a message about that.
   1852 		#
   1853 		# XXX Grody to rely on the internal structure used by
   1854 		# certctl(8), but less bad than having two versions of
   1855 		# the config parsing logic.
   1856 		if [ ! -f "${scratchdir}/.certctl" ]; then
   1857 			return 0
   1858 		fi
   1859 
   1860 		# Do a dry run of rehashing into the real
   1861 		# /etc/openssl/certs.  This curious extra step ensures
   1862 		# that we report a failure if /etc/openssl/certs
   1863 		# appears to be managed manually, but `manual' was not
   1864 		# specified in /etc/openssl/certs.conf.
   1865 		/usr/sbin/certctl -n rehash || return $?
   1866 
   1867 		# Compare the trees with mtree(8).  Inconveniently,
   1868 		# mtree returns status zero even if there are missing
   1869 		# or extra files.  So instead of examining the return
   1870 		# status, test for any output.  Empty output means
   1871 		# everything matches; otherwise the mismatch, missing,
   1872 		# or extra files are output.
   1873 		mtree -p "$scratchdir" -c -k "$mtreekeys" \
   1874 		| mtree -p "${DEST_DIR}/etc/openssl/certs" 2>&1 \
   1875 		| {
   1876 			while read -r line; do
   1877 				# mismatch, missing, or extra
   1878 				msg "/etc/openssl/certs needs rehash"
   1879 				exit 1
   1880 			done
   1881 			exit 0
   1882 		}
   1883 		;;
   1884 	fix)	# This runs openssl(1), which is not available as a
   1885 		# build-time tool.  So for now, restrict it to running
   1886 		# on the installed system.
   1887 		case $DEST_DIR in
   1888 		''|/)	;;
   1889 		*)	msg "opensslcertsrehash limited to DEST_DIR=/"
   1890 			return 1
   1891 			;;
   1892 		esac
   1893 		/usr/sbin/certctl rehash
   1894 		;;
   1895 	*)	err 3 "USAGE: do_opensslcerts fix|check"
   1896 		;;
   1897 	esac
   1898 }
   1899 
   1900 
   1901 #
   1902 #	pam
   1903 #
   1904 
   1905 additem pam "/etc/pam.d is populated"
   1906 do_pam()
   1907 {
   1908 	[ -n "$1" ] || err 3 "USAGE: do_pam fix|check"
   1909 	local op="$1"
   1910 	local failed=0
   1911 
   1912 	populate_dir "${op}" true "${SRC_DIR}/etc/pam.d" \
   1913 		"${DEST_DIR}/etc/pam.d" 644 \
   1914 		README cron display_manager ftpd gdm imap kde login other \
   1915 		passwd pop3 ppp racoon rexecd rsh sshd su system telnetd \
   1916 		xdm xserver
   1917 
   1918 	failed=$(( ${failed} + $? ))
   1919 
   1920 	return ${failed}
   1921 }
   1922 
   1923 
   1924 #
   1925 #	periodic
   1926 #
   1927 
   1928 additem periodic "/etc/{daily,weekly,monthly,security} being up to date"
   1929 do_periodic()
   1930 {
   1931 	[ -n "$1" ] || err 3 "USAGE: do_periodic fix|check"
   1932 
   1933 	compare_dir "$1" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
   1934 		daily weekly monthly security
   1935 }
   1936 
   1937 
   1938 #
   1939 #	pf
   1940 #
   1941 
   1942 additem pf "pf configuration being up to date"
   1943 do_pf()
   1944 {
   1945 	[ -n "$1" ] || err 3 "USAGE: do_pf fix|check"
   1946 	local op="$1"
   1947 	local failed=0
   1948 
   1949 	find_file_in_dirlist pf.os "pf.os" \
   1950 	    "${SRC_DIR}/dist/pf/etc" "${SRC_DIR}/etc" \
   1951 	    || return 1
   1952 			# ${dir} is set by find_file_in_dirlist()
   1953 	populate_dir "${op}" true \
   1954 	    "${dir}" "${DEST_DIR}/etc" 644 \
   1955 	    pf.conf
   1956 	failed=$(( ${failed} + $? ))
   1957 
   1958 	compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 pf.os
   1959 	failed=$(( ${failed} + $? ))
   1960 
   1961 	return ${failed}
   1962 }
   1963 
   1964 
   1965 #
   1966 #	ptyfsoldnodes
   1967 #
   1968 
   1969 additem ptyfsoldnodes "remove legacy device nodes when using ptyfs"
   1970 do_ptyfsoldnodes()
   1971 {
   1972 	[ -n "$1" ] || err 3 "USAGE: do_ptyfsoldnodes fix|check"
   1973 	local op="$1"
   1974 
   1975 	# Check whether ptyfs is in use
   1976 	local failed=0;
   1977 	if ! ${GREP} -E "^ptyfs" "${DEST_DIR}/etc/fstab" > /dev/null; then
   1978 		msg "ptyfs is not in use"
   1979 		return 0
   1980 	fi
   1981 
   1982 	if [ ! -e "${DEST_DIR}/dev/pts" ]; then
   1983 		msg "ptyfs is not properly configured: missing /dev/pts"
   1984 		return 1
   1985 	fi
   1986 
   1987 	# Find the device major numbers for the pty master and slave
   1988 	# devices, by parsing the output from "MAKEDEV -s pty0".
   1989 	#
   1990 	# Output from MAKEDEV looks like this:
   1991 	# ./ttyp0 type=char device=netbsd,5,0 mode=666 gid=0 uid=0
   1992 	# ./ptyp0 type=char device=netbsd,6,0 mode=666 gid=0 uid=0
   1993 	#
   1994 	# Output from awk, used in the eval statement, looks like this:
   1995 	# maj_ptym=6; maj_ptys=5;
   1996 	#
   1997 	local maj_ptym maj_ptys
   1998 	find_makedev
   1999 	eval "$(
   2000 	    ${HOST_SH} "${MAKEDEV_DIR}/MAKEDEV" -s pty0 2>/dev/null \
   2001 	    | ${AWK} '\
   2002 	    BEGIN { before_re = ".*device=[a-zA-Z]*,"; after_re = ",.*"; }
   2003 	    /ptyp0/ { maj_ptym = gensub(before_re, "", 1, $0);
   2004 		      maj_ptym = gensub(after_re, "", 1, maj_ptym); }
   2005 	    /ttyp0/ { maj_ptys = gensub(before_re, "", 1, $0);
   2006 		      maj_ptys = gensub(after_re, "", 1, maj_ptys); }
   2007 	    END { print "maj_ptym=" maj_ptym "; maj_ptys=" maj_ptys ";"; }
   2008 	    '
   2009 	    )"
   2010 	#msg "Major numbers are maj_ptym=${maj_ptym} maj_ptys=${maj_ptys}"
   2011 	if [ -z "$maj_ptym" ] || [ -z "$maj_ptys" ]; then
   2012 		msg "Cannot find device major numbers for pty master and slave"
   2013 		return 1
   2014 	fi
   2015 
   2016 	# look for /dev/[pt]ty[p-zP-T][0-9a-zA-Z], and check that they
   2017 	# have the expected device major numbers.  ttyv* is typically not a
   2018 	# pty device, but we check it anyway.
   2019 	#
   2020 	# The "for d1" loop is intended to avoid overflowing ARG_MAX;
   2021 	# otherwise we could have used a single glob pattern.
   2022 	#
   2023 	# If there are no files that match a particular pattern,
   2024 	# then stat prints something like:
   2025 	#    stat: /dev/[pt]tyx?: lstat: No such file or directory
   2026 	# and we ignore it.  XXX: We also ignore other error messages.
   2027 	#
   2028 	local d1 major node
   2029 	local tmp="$(mktemp /tmp/postinstall.ptyfs.XXXXXXXX)"
   2030 
   2031 	for d1 in p q r s t u v w x y z P Q R S T; do
   2032 		${STAT} -f "%Hr %N" "${DEST_DIR}/dev/"[pt]ty${d1}? 2>&1
   2033 	done \
   2034 	| while read -r major node ; do
   2035 		case "$major" in
   2036 		${maj_ptym}|${maj_ptys}) echo "$node" ;;
   2037 		esac
   2038 	done > "${tmp}"
   2039 
   2040 	local desc="legacy device node"
   2041 	while read node; do
   2042 		if [ "${op}" = "check" ]; then
   2043 			msg "Remove ${desc} ${node}"
   2044 			failed=1
   2045 		else # "fix"
   2046 			if ${RM} "${node}"; then
   2047 				msg "Removed ${desc} ${node}"
   2048 			else
   2049 				warn "Failed to remove ${desc} ${node}"
   2050 				failed=1
   2051 			fi
   2052 		fi
   2053 	done < "${tmp}"
   2054 	${RM} "${tmp}"
   2055 
   2056 	return ${failed}
   2057 }
   2058 
   2059 
   2060 #
   2061 #	pwd_mkdb
   2062 #
   2063 
   2064 additem pwd_mkdb "passwd database version"
   2065 do_pwd_mkdb()
   2066 {
   2067 	[ -n "$1" ] || err 3 "USAGE: do_pwd_mkdb fix|check"
   2068 	local op="$1"
   2069 	local failed=0
   2070 
   2071 	# XXX Ideally, we should figure out the endianness of the
   2072 	# target machine, and add "-E B"/"-E L" to the db(1) flags,
   2073 	# and "-B"/"-L" to the pwd_mkdb(8) flags if the target is not
   2074 	# the same as the host machine.  It probably doesn't matter,
   2075 	# because we don't expect "postinstall fix pwd_mkdb" to be
   2076 	# invoked during a cross build.
   2077 
   2078 	set -- $(${DB} -q -Sb -Ub -To -N hash "${DEST_DIR}/etc/pwd.db" \
   2079 		'VERSION\0')
   2080 	case "$2" in
   2081 	'\001\000\000\000') return 0 ;; # version 1, little-endian
   2082 	'\000\000\000\001') return 0 ;; # version 1, big-endian
   2083 	esac
   2084 
   2085 	if [ "${op}" = "check" ]; then
   2086 		msg "Update format of passwd database"
   2087 		failed=1
   2088 	elif ! ${PWD_MKDB} -V 1 -d "${DEST_DIR:-/}" \
   2089 			"${DEST_DIR}/etc/master.passwd";
   2090 	then
   2091 		msg "Can't update format of passwd database"
   2092 		failed=1
   2093 	else
   2094 		msg "Updated format of passwd database"
   2095 	fi
   2096 
   2097 	return ${failed}
   2098 }
   2099 
   2100 
   2101 #
   2102 #	rc
   2103 #
   2104 
   2105 # There is no info in src/distrib or /etc/mtree which rc* files
   2106 # can be overwritten unconditionally on upgrade. See PR/54741.
   2107 rc_644_files="
   2108 rc
   2109 rc.subr
   2110 rc.shutdown
   2111 "
   2112 
   2113 rc_obsolete_vars="
   2114 amd amd_master
   2115 btcontrol btcontrol_devices
   2116 critical_filesystems critical_filesystems_beforenet
   2117 mountcritlocal mountcritremote
   2118 network ip6forwarding
   2119 network nfsiod_flags
   2120 sdpd sdpd_control
   2121 sdpd sdpd_groupname
   2122 sdpd sdpd_username
   2123 sysctl defcorename
   2124 "
   2125 
   2126 update_rc()
   2127 {
   2128 	local op=$1
   2129 	local dir=$2
   2130 	local name=$3
   2131 	local bindir=$4
   2132 	local rcdir=$5
   2133 
   2134 	if [ ! -x "${DEST_DIR}/${bindir}/${name}" ]; then
   2135 		return 0
   2136 	fi
   2137 
   2138 	if ! find_file_in_dirlist "${name}" "${name}" \
   2139 	    "${rcdir}" "${SRC_DIR}/etc/rc.d"; then
   2140 		return 1
   2141 	fi
   2142 	populate_dir "${op}" false "${dir}" "${DEST_DIR}/etc/rc.d" 555 "${name}"
   2143 	return $?
   2144 }
   2145 
   2146 # select non-obsolete files in a sets file
   2147 # $1: directory pattern
   2148 # $2: file pattern
   2149 # $3: filename
   2150 select_set_files()
   2151 {
   2152 	local qdir="$(echo $1 | ${SED} -e s@/@\\\\/@g -e s/\\./\\\\./g)"
   2153 	${SED} -n -e /obsolete/d \
   2154 	    -e "/^\.${qdir}/s@^.$2[[:space:]].*@\1@p" $3
   2155 }
   2156 
   2157 # select obsolete files in a sets file
   2158 # $1: directory pattern
   2159 # $2: file pattern
   2160 # $3: setname
   2161 select_obsolete_files()
   2162 {
   2163 	if $SOURCEMODE; then
   2164 		${SED} -n -e "/obsolete/s@\.$1$2[[:space:]].*@\1@p" \
   2165 		    "${SRC_DIR}/distrib/sets/lists/$3/mi"
   2166 		return
   2167 	fi
   2168 
   2169 	# On upgrade builds we don't extract the "etc" set so we
   2170 	# try to use the source set instead. See PR/54730 for
   2171 	# ways to better handle this.
   2172 
   2173 	local obsolete_dir
   2174 
   2175 	if [ $3 = "etc" ] ;then
   2176 		obsolete_dir="${SRC_DIR}/var/db/obsolete"
   2177 	else
   2178 		obsolete_dir="${DEST_DIR}/var/db/obsolete"
   2179 	fi
   2180 	${SED} -n -e "s@\.$1$2\$@\1@p" "${obsolete_dir}/$3"
   2181 }
   2182 
   2183 getetcsets()
   2184 {
   2185 	if $SOURCEMODE; then
   2186 		echo "${SRC_DIR}/distrib/sets/lists/etc/mi"
   2187 	else
   2188 		echo "${SRC_DIR}/etc/mtree/set.etc"
   2189 	fi
   2190 }
   2191 
   2192 additem rc "/etc/rc* and /etc/rc.d/ being up to date"
   2193 do_rc()
   2194 {
   2195 	[ -n "$1" ] || err 3 "USAGE: do_rc fix|check"
   2196 	local op="$1"
   2197 	local failed=0
   2198 	local generated_scripts=""
   2199 	local etcsets=$(getetcsets)
   2200 	if [ "${MKX11}" != "no" ]; then
   2201 		generated_scripts="${generated_scripts} xdm xfs"
   2202 	fi
   2203 
   2204 	# Directories of external programs that have rc files (in bsd)
   2205 	local rc_external_files="blocklist nsd unbound"
   2206 
   2207 	# rc* files in /etc/
   2208 	# XXX: at least rc.conf and rc.local shouldn't be updated. PR/54741
   2209 	#local rc_644_files="$(select_set_files /etc/rc \
   2210 	#    "/etc/\(rc[^[:space:]/]*\)" ${etcsets})"
   2211 
   2212 	# no-obsolete rc files in /etc/rc.d
   2213 	local rc_555_files="$(select_set_files /etc/rc.d/ \
   2214 	    "/etc/rc\.d/\([^[:space:]]*\)" ${etcsets} | \
   2215 	    exclude ${rc_external_files})"
   2216 
   2217 	# obsolete rc file in /etc/rc.d
   2218 	local rc_obsolete_files="$(select_obsolete_files /etc/rc.d/ \
   2219 	    "\([^[:space:]]*\)" etc)"
   2220 
   2221 	compare_dir "${op}" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
   2222 		${rc_644_files}
   2223 	failed=$(( ${failed} + $? ))
   2224 
   2225 	local extra_scripts
   2226 	if ! $SOURCEMODE; then
   2227 		extra_scripts="${generated_scripts}"
   2228 	else
   2229 		extra_scripts=""
   2230 	fi
   2231 
   2232 	compare_dir "${op}" "${SRC_DIR}/etc/rc.d" "${DEST_DIR}/etc/rc.d" 555 \
   2233 		${rc_555_files} \
   2234 		${extra_scripts}
   2235 	failed=$(( ${failed} + $? ))
   2236 
   2237 	local i rc_file
   2238 	for i in ${rc_external_files}; do
   2239 	    case $i in
   2240 	    *d) rc_file=${i};;
   2241 	    *)	rc_file=${i}d;;
   2242 	    esac
   2243 
   2244 	    update_rc "${op}" "${dir}" ${rc_file} /sbin \
   2245 		"${SRC_DIR}/external/bsd/$i/etc/rc.d"
   2246 	    failed=$(( ${failed} + $? ))
   2247 	done
   2248 
   2249 	if $SOURCEMODE && [ -n "${generated_scripts}" ]; then
   2250 		# generate scripts
   2251 		mkdir "${SCRATCHDIR}/rc"
   2252 		for f in ${generated_scripts}; do
   2253 			${SED} -e "s,@X11ROOTDIR@,${X11ROOTDIR},g" \
   2254 			    < "${SRC_DIR}/etc/rc.d/${f}.in" \
   2255 			    > "${SCRATCHDIR}/rc/${f}"
   2256 		done
   2257 		compare_dir "${op}" "${SCRATCHDIR}/rc" \
   2258 		    "${DEST_DIR}/etc/rc.d" 555 \
   2259 		    ${generated_scripts}
   2260 		failed=$(( ${failed} + $? ))
   2261 	fi
   2262 
   2263 		# check for obsolete rc.d files
   2264 	for f in ${rc_obsolete_files}; do
   2265 		local fd="/etc/rc.d/${f}"
   2266 		[ -e "${DEST_DIR}${fd}" ] && echo "${fd}"
   2267 	done | obsolete_paths "${op}"
   2268 	failed=$(( ${failed} + $? ))
   2269 
   2270 		# check for obsolete rc.conf(5) variables
   2271 	set -- ${rc_obsolete_vars}
   2272 	while [ $# -gt 1 ]; do
   2273 		if rcconf_is_set "${op}" "$1" "$2" 1; then
   2274 			failed=1
   2275 		fi
   2276 		shift 2
   2277 	done
   2278 
   2279 	return ${failed}
   2280 }
   2281 
   2282 
   2283 #
   2284 #	sendmail
   2285 #
   2286 
   2287 adddisableditem sendmail "remove obsolete sendmail configuration files and scripts"
   2288 do_sendmail()
   2289 {
   2290 	[ -n "$1" ] || err 3 "USAGE: do_sendmail fix|check"
   2291 	op="$1"
   2292 	failed=0
   2293 
   2294 	# Don't complain if the "sendmail" package is installed because the
   2295 	# files might still be in use.
   2296 	if /usr/sbin/pkg_info -qe sendmail >/dev/null 2>&1; then
   2297 		return 0
   2298 	fi
   2299 
   2300 	for f in /etc/mail/helpfile /etc/mail/local-host-names \
   2301 	    /etc/mail/sendmail.cf /etc/mail/submit.cf /etc/rc.d/sendmail \
   2302 	    /etc/rc.d/smmsp /usr/share/misc/sendmail.hf \
   2303 	    $( ( find "${DEST_DIR}/usr/share/sendmail" -type f ; \
   2304 	         find "${DEST_DIR}/usr/share/sendmail" -type d \
   2305 	       ) | unprefix "${DEST_DIR}" ) \
   2306 	    /var/log/sendmail.st \
   2307 	    /var/spool/clientmqueue \
   2308 	    /var/spool/mqueue
   2309 	do
   2310 		[ -e "${DEST_DIR}${f}" ] && echo "${f}"
   2311 	done | obsolete_paths "${op}"
   2312 	failed=$(( ${failed} + $? ))
   2313 
   2314 	return ${failed}
   2315 }
   2316 
   2317 
   2318 #
   2319 #	ssh
   2320 #
   2321 
   2322 additem ssh "ssh configuration update"
   2323 do_ssh()
   2324 {
   2325 	[ -n "$1" ] || err 3 "USAGE: do_ssh fix|check"
   2326 	local op="$1"
   2327 
   2328 	local failed=0
   2329 	local etcssh="${DEST_DIR}/etc/ssh"
   2330 	if ! check_dir "${op}" "${etcssh}" 755; then
   2331 		failed=1
   2332 	fi
   2333 
   2334 	if [ ${failed} -eq 0 ]; then
   2335 		for f in \
   2336 			    ssh_known_hosts ssh_known_hosts2 \
   2337 			    ssh_host_dsa_key ssh_host_dsa_key.pub \
   2338 			    ssh_host_rsa_key ssh_host_rsa_key.pub \
   2339 			    ssh_host_key ssh_host_key.pub \
   2340 		    ; do
   2341 			if ! move_file "${op}" \
   2342 			    "${DEST_DIR}/etc/${f}" "${etcssh}/${f}" ; then
   2343 				failed=1
   2344 			fi
   2345 		done
   2346 		for f in sshd.conf ssh.conf ; do
   2347 				# /etc/ssh/ssh{,d}.conf -> ssh{,d}_config
   2348 				#
   2349 			if ! move_file "${op}" \
   2350 			    "${etcssh}/${f}" "${etcssh}/${f%.conf}_config" ;
   2351 			then
   2352 				failed=1
   2353 			fi
   2354 				# /etc/ssh{,d}.conf -> /etc/ssh/ssh{,d}_config
   2355 				#
   2356 			if ! move_file "${op}" \
   2357 			    "${DEST_DIR}/etc/${f}" \
   2358 			    "${etcssh}/${f%.conf}_config" ;
   2359 			then
   2360 				failed=1
   2361 			fi
   2362 		done
   2363 	fi
   2364 
   2365 	local sshdconf=""
   2366 	local f
   2367 	for f in \
   2368 	    "${etcssh}/sshd_config" \
   2369 	    "${etcssh}/sshd.conf" \
   2370 	    "${DEST_DIR}/etc/sshd.conf" ; do
   2371 		if [ -f "${f}" ]; then
   2372 			sshdconf="${f}"
   2373 			break
   2374 		fi
   2375 	done
   2376 	if [ -n "${sshdconf}" ]; then
   2377 		modify_file "${op}" "${sshdconf}" "${SCRATCHDIR}/sshdconf" '
   2378 			/^[^#$]/ {
   2379 				kw = tolower($1)
   2380 				if (kw == "hostkey" &&
   2381 				    $2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ ) {
   2382 					sub(/\/etc\/+/, "/etc/ssh/")
   2383 				}
   2384 				if (kw == "rhostsauthentication" ||
   2385 				    kw == "verifyreversemapping" ||
   2386 				    kw == "reversemappingcheck") {
   2387 					sub(/^/, "# DEPRECATED:\t")
   2388 				}
   2389 			}
   2390 			{ print }
   2391 		'
   2392 		failed=$(( ${failed} + $? ))
   2393 	fi
   2394 
   2395 	if ! find_file_in_dirlist moduli "moduli" \
   2396 	    "${SRC_DIR}/crypto/external/bsd/openssh/dist" "${SRC_DIR}/etc" ; then
   2397 		failed=1
   2398 			# ${dir} is set by find_file_in_dirlist()
   2399 	elif ! compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 moduli; then
   2400 		failed=1
   2401 	fi
   2402 
   2403 	if ! check_dir "${op}" "${DEST_DIR}/var/chroot/sshd" 755 ; then
   2404 		failed=1
   2405 	fi
   2406 
   2407 	if rcconf_is_set "${op}" sshd sshd_conf_dir 1; then
   2408 		failed=1
   2409 	fi
   2410 
   2411 	return ${failed}
   2412 }
   2413 
   2414 
   2415 #
   2416 #	tcpdumpchroot
   2417 #
   2418 
   2419 additem tcpdumpchroot "remove /var/chroot/tcpdump/etc/protocols"
   2420 do_tcpdumpchroot()
   2421 {
   2422 	[ -n "$1" ] || err 3 "USAGE: do_tcpdumpchroot fix|check"
   2423 	local op="$1"
   2424 
   2425 	local failed=0;
   2426 	if [ -r "${DEST_DIR}/var/chroot/tcpdump/etc/protocols" ]; then
   2427 		if [ "${op}" = "fix" ]; then
   2428 			${RM} "${DEST_DIR}/var/chroot/tcpdump/etc/protocols"
   2429 			failed=$(( ${failed} + $? ))
   2430 			rmdir "${DEST_DIR}/var/chroot/tcpdump/etc"
   2431 			failed=$(( ${failed} + $? ))
   2432 		else
   2433 			failed=1
   2434 		fi
   2435 	fi
   2436 	return ${failed}
   2437 }
   2438 
   2439 
   2440 #
   2441 #	uid
   2442 #
   2443 
   2444 additem uid "required users in /etc/master.passwd"
   2445 do_uid()
   2446 {
   2447 	[ -n "$1" ] || err 3 "USAGE: do_uid fix|check"
   2448 
   2449 	check_ids "$1" users "${DEST_DIR}/etc/master.passwd" \
   2450 	    "${SRC_DIR}/etc/master.passwd" 12 \
   2451 	    postfix SKIP named ntpd sshd SKIP _pflogd _rwhod SKIP _proxy \
   2452 	    _timedc _sdpd _httpd _mdnsd _tests _tcpdump _tss SKIP _rtadvd \
   2453 	    SKIP _unbound _nsd SKIP _dhcpcd
   2454 }
   2455 
   2456 
   2457 #
   2458 #	varrwho
   2459 #
   2460 
   2461 additem varrwho "required ownership of files in /var/rwho"
   2462 do_varrwho()
   2463 {
   2464 	[ -n "$1" ] || err 3 "USAGE: do_varrwho fix|check"
   2465 
   2466 	contents_owner "$1" "${DEST_DIR}/var/rwho" _rwhod _rwhod
   2467 }
   2468 
   2469 
   2470 #
   2471 #	varshm
   2472 #
   2473 
   2474 additem varshm "check for a tmpfs mounted on /var/shm"
   2475 do_varshm()
   2476 {
   2477 	[ -n "$1" ] || err 3 "USAGE: do_varshm fix|check"
   2478 	op="$1"
   2479 	failed=0
   2480 
   2481 	[ -f "${DEST_DIR}/etc/fstab" ] || return 0
   2482 	if ${GREP} -E "^var_shm_symlink" "${DEST_DIR}/etc/rc.conf" >/dev/null 2>&1;
   2483 	then
   2484 		failed=0;
   2485 	elif ${GREP} -w "/var/shm" "${DEST_DIR}/etc/fstab" >/dev/null 2>&1;
   2486 	then
   2487 		failed=0;
   2488 	else
   2489 		if [ "${op}" = "check" ]; then
   2490 			failed=1
   2491 			msg "No /var/shm mount found in ${DEST_DIR}/etc/fstab"
   2492 		elif [ "${op}" = "fix" ]; then
   2493 			printf '\ntmpfs\t/var/shm\ttmpfs\trw,-m1777,-sram%%25\n' \
   2494 				>> "${DEST_DIR}/etc/fstab"
   2495 			msg "Added tmpfs with 25% ram limit as /var/shm"
   2496 
   2497 		fi
   2498 	fi
   2499 
   2500 	return ${failed}
   2501 }
   2502 
   2503 
   2504 #
   2505 #	wscons
   2506 #
   2507 
   2508 additem wscons "wscons configuration file update"
   2509 do_wscons()
   2510 {
   2511 	[ -n "$1" ] || err 3 "USAGE: do_wscons fix|check"
   2512 	op="$1"
   2513 
   2514 	[ -f "${DEST_DIR}/etc/wscons.conf" ] || return 0
   2515 
   2516 	failed=0
   2517 	notfixed=""
   2518 	if [ "${op}" = "fix" ]; then
   2519 		notfixed="${NOT_FIXED}"
   2520 	fi
   2521 	while read _type _arg1 _rest; do
   2522 		if [ "${_type}" = "mux" -a "${_arg1}" = "1" ]; then
   2523 			msg \
   2524     "Obsolete wscons.conf(5) entry \""${_type} ${_arg1}"\" found.${notfixed}"
   2525 			failed=1
   2526 		fi
   2527 	done < "${DEST_DIR}/etc/wscons.conf"
   2528 
   2529 	return ${failed}
   2530 }
   2531 
   2532 
   2533 #
   2534 #	x11
   2535 #
   2536 
   2537 additem x11 "x11 configuration update"
   2538 do_x11()
   2539 {
   2540 	[ -n "$1" ] || err 3 "USAGE: do_x11 fix|check"
   2541 	local p="$1"
   2542 
   2543 	local failed=0
   2544 	local etcx11="${DEST_DIR}/etc/X11"
   2545 	local libx11=""
   2546 	if [ ! -d "${etcx11}" ]; then
   2547 		msg "${etcx11} is not a directory; skipping check"
   2548 		return 0
   2549 	fi
   2550 	if [ -d "${DEST_DIR}/usr/X11R6/." ]
   2551 	then
   2552 		libx11="${DEST_DIR}/usr/X11R6/lib/X11"
   2553 		if [ ! -d "${libx11}" ]; then
   2554 			msg "${libx11} is not a directory; skipping check"
   2555 			return 0
   2556 		fi
   2557 	fi
   2558 
   2559 	local notfixed=""
   2560 	if [ "${op}" = "fix" ]; then
   2561 		notfixed="${NOT_FIXED}"
   2562 	fi
   2563 
   2564 	local d
   2565 	# check if /usr/X11R6/lib/X11 needs to migrate to /etc/X11
   2566 	if [ -n "${libx11}" ]; then
   2567 		for d in \
   2568 		    fs lbxproxy proxymngr rstart twm xdm xinit xserver xsm \
   2569 		    ; do
   2570 			sd="${libx11}/${d}"
   2571 			ld="/etc/X11/${d}"
   2572 			td="${DEST_DIR}${ld}"
   2573 			if [ -h "${sd}" ]; then
   2574 				continue
   2575 			elif [ -d "${sd}" ]; then
   2576 				tdfiles="$(find "${td}" \! -type d)"
   2577 				if [ -n "${tdfiles}" ]; then
   2578 					msg "${sd} exists yet ${td} already" \
   2579 					    "contains files${notfixed}"
   2580 				else
   2581 					msg "Migrate ${sd} to ${td}${notfixed}"
   2582 				fi
   2583 				failed=1
   2584 			elif [ -e "${sd}" ]; then
   2585 				msg "Unexpected file ${sd}${notfixed}"
   2586 				continue
   2587 			else
   2588 				continue
   2589 			fi
   2590 		done
   2591 	fi
   2592 
   2593 	# check if xdm resources have been updated
   2594 	if [ -r ${etcx11}/xdm/Xresources ] && \
   2595 	    ! ${GREP} -q 'inpColor:' ${etcx11}/xdm/Xresources; then
   2596 		msg "Update ${etcx11}/xdm/Xresources${notfixed}"
   2597 		failed=1
   2598 	fi
   2599 
   2600 	return ${failed}
   2601 }
   2602 
   2603 
   2604 #
   2605 #	xkb
   2606 #
   2607 # /usr/X11R7/lib/X11/xkb/symbols/pc used to be a directory, but changed
   2608 # to a file on 2009-06-12.  Fixing this requires removing the directory
   2609 # (which we can do) and re-extracting the xbase set (which we can't do),
   2610 # or at least adding that one file (which we may be able to do if X11SRCDIR
   2611 # is available).
   2612 #
   2613 
   2614 additem xkb "clean up for xkbdata to xkeyboard-config upgrade"
   2615 do_xkb()
   2616 {
   2617 	[ -n "$1" ] || err 3 "USAGE: do_xkb fix|check"
   2618 	local op="$1"
   2619 	local failed=0
   2620 
   2621 	local pcpath="/usr/X11R7/lib/X11/xkb/symbols/pc"
   2622 	local pcsrcdir="${X11SRCDIR}/external/mit/xkeyboard-config/dist/symbols"
   2623 
   2624 	local filemsg="\
   2625 ${pcpath} was a directory, should be a file.
   2626     To fix, extract the xbase set again."
   2627 
   2628 	local notfixed=""
   2629 	if [ "${op}" = "fix" ]; then
   2630 		notfixed="${NOT_FIXED}"
   2631 	fi
   2632 
   2633 	if [ ! -d "${DEST_DIR}${pcpath}" ]; then
   2634 		return 0
   2635 	fi
   2636 
   2637 	# Delete obsolete files in the directory, and the directory
   2638 	# itself.  If the directory contains unexpected extra files
   2639 	# then it will not be deleted.
   2640 	( [ -f "${DEST_DIR}"/var/db/obsolete/xbase ] \
   2641 	    &&  ${SORT} -ru "${DEST_DIR}"/var/db/obsolete/xbase \
   2642 	    | ${GREP} -E "^\\.?${pcpath}/" ;
   2643 	    echo "${pcpath}" ) \
   2644 	| obsolete_paths "${op}"
   2645 	failed=$(( ${failed} + $? ))
   2646 
   2647 	# If the directory was removed above, then try to replace it with
   2648 	# a file.
   2649 	if [ -d "${DEST_DIR}${pcpath}" ]; then
   2650 		msg "${filemsg}${notfixed}"
   2651 		failed=$(( ${failed} + 1 ))
   2652 	else
   2653 		if ! find_file_in_dirlist pc "${pcpath}" \
   2654 			"${pcsrcdir}" "${SRC_DIR}${pcpath%/*}"
   2655 		then
   2656 			msg "${filemsg}${notfixed}"
   2657 			failed=$(( ${failed} + 1 ))
   2658 		else
   2659 			# ${dir} is set by find_file_in_dirlist()
   2660 			populate_dir "${op}" true \
   2661 				"${dir}" "${DEST_DIR}${pcpath%/*}" 444 \
   2662 				pc
   2663 			failed=$(( ${failed} + $? ))
   2664 		fi
   2665 	fi
   2666 
   2667 	return $failed
   2668 }
   2669 
   2670 
   2671 #
   2672 #	obsolete_stand
   2673 #	obsolete_stand_debug
   2674 #
   2675 
   2676 obsolete_stand_internal()
   2677 {
   2678 	local prefix="$1"
   2679 	shift
   2680 	[ -n "$1" ] || err 3 "USAGE: do_obsolete_stand fix|check"
   2681 	local op="$1"
   2682 	local failed=0
   2683 	local dir
   2684 
   2685 	for dir in \
   2686 	    ${prefix}/stand/${MACHINE} \
   2687 	    ${prefix}/stand/${MACHINE}-4xx \
   2688 	    ${prefix}/stand/${MACHINE}-booke \
   2689 	    ${prefix}/stand/${MACHINE}-xen \
   2690 	    ${prefix}/stand/${MACHINE}pae-xen
   2691 	do
   2692 		[ -d "${DEST_DIR}${dir}" ] && obsolete_stand "${dir}"
   2693 	done | obsolete_paths "${op}"
   2694 	failed=$(( ${failed} + $? ))
   2695 
   2696 	return ${failed}
   2697 }
   2698 
   2699 adddisableditem obsolete_stand "remove obsolete files from /stand"
   2700 do_obsolete_stand()
   2701 {
   2702 	obsolete_stand_internal "" "$@"
   2703 	return $?
   2704 }
   2705 
   2706 adddisableditem obsolete_stand_debug "remove obsolete files from /usr/libdata/debug/stand"
   2707 do_obsolete_stand_debug()
   2708 {
   2709 	obsolete_stand_internal "/usr/libdata/debug" "$@"
   2710 	return $?
   2711 }
   2712 
   2713 
   2714 #
   2715 #	obsolete
   2716 #
   2717 # NOTE: This item is last to allow other items to move obsolete files.
   2718 #
   2719 
   2720 listarchsubdirs()
   2721 {
   2722 	if ! $SOURCEMODE; then
   2723 		echo "@ARCHSUBDIRS@"
   2724 	else
   2725 		${SED} -n -e '/ARCHDIR_SUBDIR/s/[[:space:]]//gp' \
   2726 		    "${SRC_DIR}/compat/archdirs.mk"
   2727 	fi
   2728 }
   2729 
   2730 getarchsubdirs()
   2731 {
   2732 	local m
   2733 	local i
   2734 
   2735 	case ${MACHINE_ARCH} in
   2736 	*arm*|*aarch64*)	m=arm;;
   2737 	x86_64)			m=amd64;;
   2738 	*)			m=${MACHINE_ARCH};;
   2739 	esac
   2740 
   2741 	for i in $(listarchsubdirs); do
   2742 		echo $i
   2743 	done | ${SORT} -u | ${SED} -n -e "/=${m}/s@.*=${m}/\(.*\)@\1@p"
   2744 }
   2745 
   2746 getcompatlibdirs()
   2747 {
   2748 	local i
   2749 
   2750 	for i in $(getarchsubdirs); do
   2751 		if [ -d "${DEST_DIR}/usr/lib/$i" ]; then
   2752 			echo /usr/lib/$i
   2753 		fi
   2754 	done
   2755 }
   2756 
   2757 additem obsolete "remove obsolete file sets and minor libraries"
   2758 do_obsolete()
   2759 {
   2760 	[ -n "$1" ] || err 3 "USAGE: do_obsolete fix|check"
   2761 	local op="$1"
   2762 	local failed=0
   2763 	local i
   2764 
   2765 	${SORT} -ru "${DEST_DIR}"/var/db/obsolete/* | obsolete_paths "${op}"
   2766 	failed=$(( ${failed} + $? ))
   2767 
   2768 	(
   2769 		obsolete_libs /lib
   2770 		obsolete_libs /usr/lib
   2771 		obsolete_libs /usr/lib/i18n
   2772 		obsolete_libs /usr/X11R6/lib
   2773 		obsolete_libs /usr/X11R7/lib
   2774 		for i in $(getcompatlibdirs); do
   2775 			obsolete_libs $i
   2776 		done
   2777 	) | obsolete_paths "${op}"
   2778 	failed=$(( ${failed} + $? ))
   2779 
   2780 	return ${failed}
   2781 }
   2782 
   2783 
   2784 #
   2785 #	end of items
   2786 #	------------
   2787 #
   2788 
   2789 
   2790 help()
   2791 {
   2792 	cat << _USAGE_
   2793 Usage: ${PROGNAME} [-a ARCH] [-d DEST_DIR] [-m MACHINE] [-s SRC_ARG] [-x XSRC_DIR] OPERATION ...
   2794        ${PROGNAME} -?
   2795 
   2796 	Perform post-installation checks and/or fixes on a system's
   2797 	configuration files.
   2798 	If no items are provided, a default set of checks or fixes is applied.
   2799 
   2800 	Options:
   2801 	-?		Display this help, and exit.
   2802 	-a ARCH		Set \$MACHINE_ARCH to ARCH.	[${MACHINE_ARCH}]
   2803 	-d DEST_DIR	Destination directory to check. [${DEST_DIR:-/}]
   2804 	-m MACHINE	Set \$MACHINE to MACHINE.	[${MACHINE}]
   2805 	-s SRC_ARG	Location of the source files.  This may be any of
   2806 			the following:
   2807 			-s SRC_DIR	A directory that contains a NetBSD
   2808 					source tree.
   2809 			-s TGZ_DIR	A directory in which one or both of
   2810 					"etc.tgz" and "xetc.tgz" have been
   2811 					extracted.
   2812 			-s TGZ_FILE	A distribution set file such as
   2813 					"etc.tgz" or "xetc.tgz".
   2814 					May be specified multipled times.
   2815 							[${SRC_DIR:-/usr/src}]
   2816 	-x XSRC_DIR	Location of the X11 source files.  This must be
   2817 			a directory that contains a NetBSD xsrc tree.
   2818 							[${XSRC_DIR:-/usr/src/../xsrc}]
   2819 
   2820 	Supported values for OPERATION:
   2821 	help		Display this help, and exit.
   2822 	list		List available items.
   2823 	check ITEM ...	Perform post-installation checks on ITEMs.
   2824 	diff [-bcenpuw] ITEM ...
   2825 			Similar to 'check' but also output difference of files,
   2826 			using diff with the provided options.
   2827 	fix ITEM ...	Apply fixes that 'check' determines need to be applied.
   2828 	usage		Display this help, and exit.
   2829 _USAGE_
   2830 }
   2831 
   2832 usage()
   2833 {
   2834 	help 1>&2
   2835 	exit 2
   2836 }
   2837 
   2838 
   2839 list()
   2840 {
   2841 	local i
   2842 	echo "Default set of items (to apply if no items are provided by user):"
   2843 	echo " Item                 Description"
   2844 	echo " ----                 -----------"
   2845 	for i in ${defaultitems}; do
   2846 		eval desc=\"\${desc_${i}}\"
   2847 		printf " %-20s %s\n" "${i}" "${desc}"
   2848 	done
   2849 	echo "Items disabled by default (must be requested explicitly):"
   2850 	echo " Item                 Description"
   2851 	echo " ----                 -----------"
   2852 	for i in ${otheritems}; do
   2853 		eval desc=\"\${desc_${i}}\"
   2854 		printf " %-20s %s\n" "${i}" "${desc}"
   2855 	done
   2856 }
   2857 
   2858 
   2859 main()
   2860 {
   2861 	DIRMODE=false		# true if "-s" specified a directory
   2862 	ITEMS=			# items to check|diff|fix. [${defaultitems}]
   2863 	N_SRC_ARGS=0		# number of "-s" args in SRC_ARGLIST
   2864 	SOURCEMODE=false	# true if "-s" specified a source directory
   2865 	SRC_ARGLIST=		# quoted list of one or more "-s" args
   2866 	SRC_DIR="${SRC_ARG}"	# set default value for early usage()
   2867 	TGZLIST=		# quoted list list of tgz files
   2868 	TGZMODE=false		# true if "-s" specifies a tgz file
   2869 	XSRC_DIR="${SRC_ARG}/../xsrc"
   2870 	XSRC_DIR_FIX=
   2871 
   2872 	case "$(uname -s)" in
   2873 	Darwin)
   2874 		# case sensitive match for case insensitive fs
   2875 		file_exists_exact=file_exists_exact
   2876 		;;
   2877 	*)
   2878 		file_exists_exact=:
   2879 		;;
   2880 	esac
   2881 
   2882 		# Validate options.
   2883 		#
   2884 	while getopts :a:d:m:s:x: ch; do
   2885 		case "${ch}" in
   2886 		a)
   2887 			MACHINE_ARCH="${OPTARG}"
   2888 			;;
   2889 		d)
   2890 			DEST_DIR="${OPTARG}"
   2891 			;;
   2892 		m)
   2893 			MACHINE="${OPTARG}"
   2894 			;;
   2895 		s)
   2896 			qarg="$(shell_quote "${OPTARG}")"
   2897 			N_SRC_ARGS=$(( $N_SRC_ARGS + 1 ))
   2898 			SRC_ARGLIST="${SRC_ARGLIST}${SRC_ARGLIST:+ }-s ${qarg}"
   2899 			if [ -f "${OPTARG}" ]; then
   2900 				# arg refers to a *.tgz file.
   2901 				# This may happen twice, for both
   2902 				# etc.tgz and xetc.tgz, so we build up a
   2903 				# quoted list in TGZLIST.
   2904 				TGZMODE=true
   2905 				TGZLIST="${TGZLIST}${TGZLIST:+ }${qarg}"
   2906 				# Note that, when TGZMODE is true,
   2907 				# SRC_ARG is used only for printing
   2908 				# human-readable messages.
   2909 				SRC_ARG="${TGZLIST}"
   2910 			elif [ -d "${OPTARG}" ]; then
   2911 				# arg refers to a directory.
   2912 				# It might be a source directory, or a
   2913 				# directory where the sets have already
   2914 				# been extracted.
   2915 				DIRMODE=true
   2916 				SRC_ARG="${OPTARG}"
   2917 				if [ -f "${OPTARG}/etc/Makefile" ]; then
   2918 					SOURCEMODE=true
   2919 				fi
   2920 			else
   2921 				err 2 "Invalid argument for -s option"
   2922 			fi
   2923 			;;
   2924 		x)
   2925 			if [ -d "${OPTARG}" ]; then
   2926 				# arg refers to a directory.
   2927 				XSRC_DIR="${OPTARG}"
   2928 				XSRC_DIR_FIX="-x ${OPTARG} "
   2929 			else
   2930 				err 2 "Not a directory for -x option"
   2931 			fi
   2932 			;;
   2933 		"?")
   2934 			if [ "${OPTARG}" = "?" ]; then
   2935 				help
   2936 				return	# no further processing or validation
   2937 			fi
   2938 			warn "Unknown option -${OPTARG}"
   2939 			usage
   2940 			;;
   2941 
   2942 		:)
   2943 			warn "Missing argument for option -${OPTARG}"
   2944 			usage
   2945 			;;
   2946 
   2947 		*)
   2948 			err 3 "Unimplemented option -${ch}"
   2949 			;;
   2950 		esac
   2951 	done
   2952 	shift $((${OPTIND} - 1))
   2953 	if [ $# -eq 0 ] ; then
   2954 		warn "Missing operation"
   2955 		usage
   2956 	fi
   2957 	op="$1"
   2958 	shift
   2959 
   2960 	if [ "$N_SRC_ARGS" -gt 1 ] && $DIRMODE; then
   2961 		err 2 "Multiple -s args are allowed only with tgz files"
   2962 	fi
   2963 	if [ "$N_SRC_ARGS" -eq 0 ]; then
   2964 		# The default SRC_ARG was set elsewhere
   2965 		DIRMODE=true
   2966 		SOURCEMODE=true
   2967 		SRC_ARGLIST="-s $(shell_quote "${SRC_ARG}")"
   2968 	fi
   2969 
   2970 		# Validate 'diff' first, as it becomes 'check'
   2971 		#
   2972 	case "${op}" in
   2973 
   2974 	diff)
   2975 		op=check
   2976 		DIFF_STYLE=n			# default style is RCS
   2977 		OPTIND=1
   2978 		while getopts :bcenpuw ch; do
   2979 			case "${ch}" in
   2980 			c|e|n|u)
   2981 				if [ "${DIFF_STYLE}" != "n" -a \
   2982 				    "${DIFF_STYLE}" != "${ch}" ]; then
   2983 					warn "diff: conflicting output style: -${ch}"
   2984 					usage
   2985 				fi
   2986 				DIFF_STYLE="${ch}"
   2987 				;;
   2988 			b|p|w)
   2989 				DIFF_OPT="${DIFF_OPT} -${ch}"
   2990 				;;
   2991 			"?")
   2992 				# NOTE: not supporting diff -?
   2993 				warn "diff: Unknown option -${OPTARG}"
   2994 				usage
   2995 				;;
   2996 			:)
   2997 				warn "diff: Missing argument for option -${OPTARG}"
   2998 				usage
   2999 				;;
   3000 			*)
   3001 				err 3 "diff: Unimplemented option -${ch}"
   3002 				;;
   3003 			esac
   3004 		done
   3005 		shift $((${OPTIND} - 1))
   3006 		;;
   3007 
   3008 	esac
   3009 
   3010 		# Validate operation and items.
   3011 		#
   3012 	case "${op}" in
   3013 
   3014 	check|fix)
   3015 		ITEMS="$*"
   3016 		: ${ITEMS:="${defaultitems}"}
   3017 
   3018 		# ensure that all supplied items are valid
   3019 		#
   3020 		for i in ${ITEMS}; do
   3021 			eval desc=\"\${desc_${i}}\"
   3022 			[ -n "${desc}" ] || err 2 "Unsupported ${op} '"${i}"'"
   3023 		done
   3024 		;;
   3025 
   3026 	help|usage)
   3027 		help
   3028 		return	# no further processing or validation
   3029 		;;
   3030 
   3031 	list)
   3032 		# processed below
   3033 		;;
   3034 
   3035 	*)
   3036 		warn "Unknown operation '"${op}"'"
   3037 		usage
   3038 		;;
   3039 
   3040 	esac
   3041 
   3042 	#
   3043 	# If '-s' arg or args specified tgz files, extract them
   3044 	# to a scratch directory.
   3045 	#
   3046 	if $TGZMODE; then
   3047 		ETCTGZDIR="${SCRATCHDIR}/etc.tgz"
   3048 		echo "Note: Creating temporary directory ${ETCTGZDIR}"
   3049 		if ! mkdir "${ETCTGZDIR}"; then
   3050 			err 2 "Can't create ${ETCTGZDIR}"
   3051 		fi
   3052 		( # subshell to localise changes to "$@"
   3053 			eval "set -- ${TGZLIST}"
   3054 			for tgz in "$@"; do
   3055 				echo "Note: Extracting files from ${tgz}"
   3056 				cat "${tgz}" | (
   3057 					cd "${ETCTGZDIR}" &&
   3058 					tar -zxf -
   3059 				) || err 2 "Can't extract ${tgz}"
   3060 			done
   3061 		)
   3062 		SRC_DIR="${ETCTGZDIR}"
   3063 	else
   3064 		SRC_DIR="${SRC_ARG}"
   3065 	fi
   3066 
   3067 	[ -d "${SRC_DIR}" ]	|| err 2 "${SRC_DIR} is not a directory"
   3068 	[ -d "${DEST_DIR}" ]	|| err 2 "${DEST_DIR} is not a directory"
   3069 	[ -n "${MACHINE}" ]	|| err 2 "\${MACHINE} is not defined"
   3070 	[ -n "${MACHINE_ARCH}" ] || err 2 "\${MACHINE_ARCH} is not defined"
   3071 	if ! $SOURCEMODE && ! [ -f "${SRC_DIR}/etc/mtree/set.etc" ]; then
   3072 		err 2 "Files from the etc.tgz set are missing"
   3073 	fi
   3074 
   3075 		# If directories are /, clear them, so various messages
   3076 		# don't have leading "//".   However, this requires
   3077 		# the use of ${foo:-/} to display the variables.
   3078 		#
   3079 	[ "${SRC_DIR}" = "/" ]	&& SRC_DIR=""
   3080 	[ "${DEST_DIR}" = "/" ]	&& DEST_DIR=""
   3081 
   3082 	detect_x11
   3083 
   3084 		# Perform operation.
   3085 		#
   3086 	case "${op}" in
   3087 
   3088 	check|fix)
   3089 		[ -n "${ITEMS}" ] || err 2 "${op}: missing items"
   3090 
   3091 		echo "Source directory: ${SRC_DIR:-/}"
   3092 		if $TGZMODE; then
   3093 			echo " (extracted from: ${SRC_ARG})"
   3094 		fi
   3095 		echo "Target directory: ${DEST_DIR:-/}"
   3096 		items_passed=
   3097 		items_failed=
   3098 		for i in ${ITEMS}; do
   3099 			echo "${i} ${op}:"
   3100 			( eval do_${i} ${op} )
   3101 			if [ $? -eq 0 ]; then
   3102 				items_passed="${items_passed} ${i}"
   3103 			else
   3104 				items_failed="${items_failed} ${i}"
   3105 			fi
   3106 		done
   3107 
   3108 		if [ "${op}" = "check" ]; then
   3109 			plural="checks"
   3110 		else
   3111 			plural="fixes"
   3112 		fi
   3113 
   3114 		echo "${PROGNAME} ${plural} passed:${items_passed}"
   3115 		echo "${PROGNAME} ${plural} failed:${items_failed}"
   3116 		if [ -n "${items_failed}" ]; then
   3117 		    exitstatus=1;
   3118 		    if [ "${op}" = "check" ]; then
   3119 			[ "$MACHINE" = "$(uname -m)" ] && m= || m=" -m $MACHINE"
   3120 			cat <<_Fix_me_
   3121 To fix, run:
   3122     ${HOST_SH} ${0} ${SRC_ARGLIST} ${XSRC_DIR_FIX}-d ${DEST_DIR:-/}$m fix${items_failed}
   3123 Note that this may overwrite local changes.
   3124 _Fix_me_
   3125 		    fi
   3126 		fi
   3127 		;;
   3128 
   3129 	list)
   3130 		echo "Source directory: ${SRC_DIR:-/}"
   3131 		echo "Target directory: ${DEST_DIR:-/}"
   3132 		if $TGZMODE; then
   3133 			echo " (extracted from: ${SRC_ARG})"
   3134 		fi
   3135 		list
   3136 		;;
   3137 
   3138 	*)
   3139 			# diff, help, usage handled during operation validation
   3140 		err 3 "Unimplemented operation '"${op}"'"
   3141 		;;
   3142 
   3143 	esac
   3144 }
   3145 
   3146 if [ -n "$POSTINSTALL_FUNCTION" ]; then
   3147 	eval "$POSTINSTALL_FUNCTION"
   3148 	exit 0
   3149 fi
   3150 
   3151 # defaults
   3152 #
   3153 PROGNAME="${0##*/}"
   3154 SRC_ARG="/usr/src"
   3155 DEST_DIR="/"
   3156 : ${MACHINE:="$( uname -m )"}	# assume native build if $MACHINE is not set
   3157 : ${MACHINE_ARCH:="$( uname -p )"}# assume native build if not set
   3158 
   3159 DIFF_STYLE=
   3160 DIFF_OPT=
   3161 NOT_FIXED=" (FIX MANUALLY)"
   3162 SCRATCHDIR="$( mkdtemp )" || err 2 "Can't create scratch directory"
   3163 trap "${RM} -rf \"\${SCRATCHDIR}\" ; exit 0" 1 2 3 15	# HUP INT QUIT TERM
   3164 
   3165 umask 022
   3166 exec 3>/dev/null
   3167 exec 4>/dev/null
   3168 exitstatus=0
   3169 
   3170 main "$@"
   3171 ${RM} -rf "${SCRATCHDIR}"
   3172 exit $exitstatus
   3173