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