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