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