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