Home | History | Annotate | Line # | Download | only in postinstall
postinstall.in revision 1.57
      1 #!/bin/sh
      2 #
      3 # $NetBSD: postinstall.in,v 1.57 2023/10/18 13:10:34 riastradh 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 libraries 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-scale-bitmap-fonts.conf
   1322 10-sub-pixel-bgr.conf
   1323 10-sub-pixel-none.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 #	opensslcertsconf
   1666 #
   1667 
   1668 additem opensslcertsconf "ensure TLS trust anchor configuration exists"
   1669 do_opensslcertsconf()
   1670 {
   1671 	local certsdir certsconf defaultconf manualmsg
   1672 
   1673 	[ -n "$1" ] || err 3 "USAGE: do_opensslcertsconf fix|check"
   1674 
   1675 	certsdir="${DEST_DIR}/etc/openssl/certs"
   1676 	certsconf="${DEST_DIR}/etc/openssl/certs.conf"
   1677 	defaultconf="${DEST_DIR}/usr/share/examples/certctl/certs.conf"
   1678 
   1679 	case $1 in
   1680 	check)	if [ ! -r "$certsconf" ]; then
   1681 			msg "/etc/openssl/certs.conf missing; see certctl(8)"
   1682 			return 1
   1683 		fi
   1684 		;;
   1685 	fix)	# If /etc/openssl/certs.conf is already there, nothing
   1686 		# to do.
   1687 		if [ -r "$certsconf" ]; then
   1688 			msg "/etc/openssl/certs.conf already exists"
   1689 			return 0
   1690 		fi
   1691 
   1692 		# If /etc/openssl/certs is a symlink, or exists but is
   1693 		# not a directory, or is a directory but is nonempty,
   1694 		# then either it's managed by someone else or something
   1695 		# fishy is afoot.  So set it manual in that case.
   1696 		# Otherwise, install the default config file.
   1697 		if [ -h "$certsdir" ] ||
   1698 		    [ -e "$certsdir" -a ! -d "$certsdir" ] ||
   1699 		    ([ -d "$certsdir" ] &&
   1700 			find -f "$certsdir" -- \
   1701 			    -maxdepth 0 -type d -empty -exit 1)
   1702 		then
   1703 			msg "/etc/openssl/certs appears manually configured"
   1704 			manualmsg="[existing /etc/openssl/certs configuration"
   1705 			manualmsg="$manualmsg detected by postinstall(8)]"
   1706                         # Change the commented-out `#manual' line to
   1707                         # uncommented `manual', or print an error
   1708                         # message if there is no `#manual' line and put
   1709                         # `manual' at the end.
   1710                         awk -v defaultconf="$defaultconf" \
   1711 			    -v manualmsg="$manualmsg" '
   1712 				BEGIN {
   1713 					manual = 0
   1714 				}
   1715 				/^#manual/ && !manual {
   1716 					manual = 1
   1717 					sub(/^#/, "")
   1718 					print
   1719 					print "#", manualmsg
   1720 					next
   1721 				}
   1722 				{
   1723 					print
   1724 				}
   1725 				END {
   1726 					if (!manual) {
   1727 						printf "warning: %s %s?\n", \
   1728 						    "corrupt", defaultconf \
   1729 						    >"/dev/stderr"
   1730 						print "manual"
   1731 						print "#", manualmsg
   1732 					}
   1733 				}
   1734 			' <$defaultconf >${certsconf}.tmp
   1735 		else
   1736 			msg "installing default /etc/openssl/certs.conf"
   1737 			cat <$defaultconf >${certsconf}.tmp
   1738 		fi && mv -f -- "${certsconf}.tmp" "$certsconf"
   1739 		;;
   1740 	*)	err 3 "USAGE: do_opensslcerts fix|check"
   1741 		;;
   1742 	esac
   1743 }
   1744 
   1745 
   1746 #
   1747 #	opensslcertsrehash
   1748 #
   1749 
   1750 additem opensslcertsrehash "make /etc/openssl/certs cache of TLS trust anchors"
   1751 do_opensslcertsrehash()
   1752 {
   1753 	local mtreekeys scratchdir
   1754 
   1755 	[ -n "$1" ] || err 3 "USAGE: do_opensslcertsrehash fix|check"
   1756 
   1757 	if [ ! -r "${DEST_DIR}/etc/openssl/certs.conf" ]; then
   1758 		msg "/etc/openssl/certs.conf missing; see certctl(8)"
   1759 		return 1
   1760 	fi
   1761 
   1762 	case $1 in
   1763 	check)	# Create a scratch rehash for comparison.
   1764 		mtreekeys="type,link"
   1765 		scratchdir="${SCRATCHDIR}/opensslcerts"
   1766 		/usr/sbin/certctl -c "$scratchdir" rehash || return $?
   1767 
   1768 		# This will create ${scratchdir}/.certctl unless the
   1769 		# configuration is manual.  If the configuration is
   1770 		# manual, stop here; nothing to do.  certctl(8) will
   1771 		# have already printed a message about that.
   1772 		#
   1773 		# XXX Grody to rely on the internal structure used by
   1774 		# certctl(8), but less bad than having two versions of
   1775 		# the config parsing logic.
   1776 		if [ ! -f "${scratchdir}/.certctl" ]; then
   1777 			return 0
   1778 		fi
   1779 
   1780 		# Do a dry run of rehashing into the real
   1781 		# /etc/openssl/certs.  This curious extra step ensures
   1782 		# that we report a failure if /etc/openssl/certs
   1783 		# appears to be managed manually, but `manual' was not
   1784 		# specified in /etc/openssl/certs.conf.
   1785 		/usr/sbin/certctl -n rehash || return $?
   1786 
   1787 		# Compare the trees with mtree(8).  Inconveniently,
   1788 		# mtree returns status zero even if there are missing
   1789 		# or extra files.  So instead of examining the return
   1790 		# status, test for any output.  Empty output means
   1791 		# everything matches; otherwise the mismatch, missing,
   1792 		# or extra files are output.
   1793 		mtree -p "$scratchdir" -c -k "$mtreekeys" \
   1794 		| mtree -p "${DEST_DIR}/etc/openssl/certs" 2>&1 \
   1795 		| {
   1796 			while read -r line; do
   1797 				# mismatch, missing, or extra
   1798 				msg "/etc/openssl/certs needs rehash"
   1799 				exit 1
   1800 			done
   1801 			exit 0
   1802 		}
   1803 		;;
   1804 	fix)	# This runs openssl(1), which is not available as a
   1805 		# build-time tool.  So for now, restrict it to running
   1806 		# on the installed system.
   1807 		case $DEST_DIR in
   1808 		''|/)	;;
   1809 		*)	msg "opensslcertsrehash limited to DEST_DIR=/"
   1810 			return 1
   1811 			;;
   1812 		esac
   1813 		/usr/sbin/certctl rehash
   1814 		;;
   1815 	*)	err 3 "USAGE: do_opensslcerts fix|check"
   1816 		;;
   1817 	esac
   1818 }
   1819 
   1820 
   1821 #
   1822 #	pam
   1823 #
   1824 
   1825 additem pam "/etc/pam.d is populated"
   1826 do_pam()
   1827 {
   1828 	[ -n "$1" ] || err 3 "USAGE: do_pam fix|check"
   1829 	op="$1"
   1830 	failed=0
   1831 
   1832 	populate_dir "${op}" true "${SRC_DIR}/etc/pam.d" \
   1833 		"${DEST_DIR}/etc/pam.d" 644 \
   1834 		README cron display_manager ftpd gdm imap kde login other \
   1835 		passwd pop3 ppp racoon rexecd rsh sshd su system telnetd \
   1836 		xdm xserver
   1837 
   1838 	failed=$(( ${failed} + $? ))
   1839 
   1840 	return ${failed}
   1841 }
   1842 
   1843 
   1844 #
   1845 #	periodic
   1846 #
   1847 
   1848 additem periodic "/etc/{daily,weekly,monthly,security} being up to date"
   1849 do_periodic()
   1850 {
   1851 	[ -n "$1" ] || err 3 "USAGE: do_periodic fix|check"
   1852 
   1853 	compare_dir "$1" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
   1854 		daily weekly monthly security
   1855 }
   1856 
   1857 
   1858 #
   1859 #	pf
   1860 #
   1861 
   1862 additem pf "pf configuration being up to date"
   1863 do_pf()
   1864 {
   1865 	[ -n "$1" ] || err 3 "USAGE: do_pf fix|check"
   1866 	op="$1"
   1867 	failed=0
   1868 
   1869 	find_file_in_dirlist pf.os "pf.os" \
   1870 	    "${SRC_DIR}/dist/pf/etc" "${SRC_DIR}/etc" \
   1871 	    || return 1
   1872 			# ${dir} is set by find_file_in_dirlist()
   1873 	populate_dir "${op}" true \
   1874 	    "${dir}" "${DEST_DIR}/etc" 644 \
   1875 	    pf.conf
   1876 	failed=$(( ${failed} + $? ))
   1877 
   1878 	compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 pf.os
   1879 	failed=$(( ${failed} + $? ))
   1880 
   1881 	return ${failed}
   1882 }
   1883 
   1884 
   1885 #
   1886 #	ptyfsoldnodes
   1887 #
   1888 
   1889 additem ptyfsoldnodes "remove legacy device nodes when using ptyfs"
   1890 do_ptyfsoldnodes()
   1891 {
   1892 	[ -n "$1" ] || err 3 "USAGE: do_ptyfsoldnodes fix|check"
   1893 	_ptyfs_op="$1"
   1894 
   1895 	# Check whether ptyfs is in use
   1896 	failed=0;
   1897 	if ! ${GREP} -E "^ptyfs" "${DEST_DIR}/etc/fstab" > /dev/null; then
   1898 		msg "ptyfs is not in use"
   1899 		return 0
   1900 	fi
   1901 
   1902 	if [ ! -e "${DEST_DIR}/dev/pts" ]; then
   1903 		msg "ptyfs is not properly configured: missing /dev/pts"
   1904 		return 1
   1905 	fi
   1906 
   1907 	# Find the device major numbers for the pty master and slave
   1908 	# devices, by parsing the output from "MAKEDEV -s pty0".
   1909 	#
   1910 	# Output from MAKEDEV looks like this:
   1911 	# ./ttyp0 type=char device=netbsd,5,0 mode=666 gid=0 uid=0
   1912 	# ./ptyp0 type=char device=netbsd,6,0 mode=666 gid=0 uid=0
   1913 	#
   1914 	# Output from awk, used in the eval statement, looks like this:
   1915 	# maj_ptym=6; maj_ptys=5;
   1916 	#
   1917 	find_makedev
   1918 	eval "$(
   1919 	    ${HOST_SH} "${MAKEDEV_DIR}/MAKEDEV" -s pty0 2>/dev/null \
   1920 	    | ${AWK} '\
   1921 	    BEGIN { before_re = ".*device=[a-zA-Z]*,"; after_re = ",.*"; }
   1922 	    /ptyp0/ { maj_ptym = gensub(before_re, "", 1, $0);
   1923 		      maj_ptym = gensub(after_re, "", 1, maj_ptym); }
   1924 	    /ttyp0/ { maj_ptys = gensub(before_re, "", 1, $0);
   1925 		      maj_ptys = gensub(after_re, "", 1, maj_ptys); }
   1926 	    END { print "maj_ptym=" maj_ptym "; maj_ptys=" maj_ptys ";"; }
   1927 	    '
   1928 	    )"
   1929 	#msg "Major numbers are maj_ptym=${maj_ptym} maj_ptys=${maj_ptys}"
   1930 	if [ -z "$maj_ptym" ] || [ -z "$maj_ptys" ]; then
   1931 		msg "Cannot find device major numbers for pty master and slave"
   1932 		return 1
   1933 	fi
   1934 
   1935 	# look for /dev/[pt]ty[p-zP-T][0-9a-zA-Z], and check that they
   1936 	# have the expected device major numbers.  ttyv* is typically not a
   1937 	# pty device, but we check it anyway.
   1938 	#
   1939 	# The "for d1" loop is intended to avoid overflowing ARG_MAX;
   1940 	# otherwise we could have used a single glob pattern.
   1941 	#
   1942 	# If there are no files that match a particular pattern,
   1943 	# then stat prints something like:
   1944 	#    stat: /dev/[pt]tyx?: lstat: No such file or directory
   1945 	# and we ignore it.  XXX: We also ignore other error messages.
   1946 	#
   1947 	_ptyfs_tmp="$(mktemp /tmp/postinstall.ptyfs.XXXXXXXX)"
   1948 	for d1 in p q r s t u v w x y z P Q R S T; do
   1949 		${STAT} -f "%Hr %N" "${DEST_DIR}/dev/"[pt]ty${d1}? 2>&1
   1950 	done \
   1951 	| while read -r major node ; do
   1952 		case "$major" in
   1953 		${maj_ptym}|${maj_ptys}) echo "$node" ;;
   1954 		esac
   1955 	done >"${_ptyfs_tmp}"
   1956 
   1957 	_desc="legacy device node"
   1958 	while read node; do
   1959 		if [ "${_ptyfs_op}" = "check" ]; then
   1960 			msg "Remove ${_desc} ${node}"
   1961 			failed=1
   1962 		else # "fix"
   1963 			if rm "${node}"; then
   1964 				msg "Removed ${_desc} ${node}"
   1965 			else
   1966 				warn "Failed to remove ${_desc} ${node}"
   1967 				failed=1
   1968 			fi
   1969 		fi
   1970 	done < "${_ptyfs_tmp}"
   1971 	rm "${_ptyfs_tmp}"
   1972 
   1973 	return ${failed}
   1974 }
   1975 
   1976 
   1977 #
   1978 #	pwd_mkdb
   1979 #
   1980 
   1981 additem pwd_mkdb "passwd database version"
   1982 do_pwd_mkdb()
   1983 {
   1984 	[ -n "$1" ] || err 3 "USAGE: do_pwd_mkdb fix|check"
   1985 	op="$1"
   1986 	failed=0
   1987 
   1988 	# XXX Ideally, we should figure out the endianness of the
   1989 	# target machine, and add "-E B"/"-E L" to the db(1) flags,
   1990 	# and "-B"/"-L" to the pwd_mkdb(8) flags if the target is not
   1991 	# the same as the host machine.  It probably doesn't matter,
   1992 	# because we don't expect "postinstall fix pwd_mkdb" to be
   1993 	# invoked during a cross build.
   1994 
   1995 	set -- $(${DB} -q -Sb -Ub -To -N hash "${DEST_DIR}/etc/pwd.db" \
   1996 		'VERSION\0')
   1997 	case "$2" in
   1998 	'\001\000\000\000') return 0 ;; # version 1, little-endian
   1999 	'\000\000\000\001') return 0 ;; # version 1, big-endian
   2000 	esac
   2001 
   2002 	if [ "${op}" = "check" ]; then
   2003 		msg "Update format of passwd database"
   2004 		failed=1
   2005 	elif ! ${PWD_MKDB} -V 1 -d "${DEST_DIR:-/}" \
   2006 			"${DEST_DIR}/etc/master.passwd";
   2007 	then
   2008 		msg "Can't update format of passwd database"
   2009 		failed=1
   2010 	else
   2011 		msg "Updated format of passwd database"
   2012 	fi
   2013 
   2014 	return ${failed}
   2015 }
   2016 
   2017 
   2018 #
   2019 #	rc
   2020 #
   2021 
   2022 # There is no info in src/distrib or /etc/mtree which rc* files
   2023 # can be overwritten unconditionally on upgrade. See PR/54741.
   2024 rc_644_files="
   2025 rc
   2026 rc.subr
   2027 rc.shutdown
   2028 "
   2029 
   2030 rc_obsolete_vars="
   2031 amd amd_master
   2032 btcontrol btcontrol_devices
   2033 critical_filesystems critical_filesystems_beforenet
   2034 mountcritlocal mountcritremote
   2035 network ip6forwarding
   2036 network nfsiod_flags
   2037 sdpd sdpd_control
   2038 sdpd sdpd_groupname
   2039 sdpd sdpd_username
   2040 sysctl defcorename
   2041 "
   2042 
   2043 update_rc()
   2044 {
   2045 	local op=$1
   2046 	local dir=$2
   2047 	local name=$3
   2048 	local bindir=$4
   2049 	local rcdir=$5
   2050 
   2051 	if [ ! -x "${DEST_DIR}/${bindir}/${name}" ]; then
   2052 		return 0
   2053 	fi
   2054 
   2055 	if ! find_file_in_dirlist "${name}" "${name}" \
   2056 	    "${rcdir}" "${SRC_DIR}/etc/rc.d"; then
   2057 		return 1
   2058 	fi
   2059 	populate_dir "${op}" false "${dir}" "${DEST_DIR}/etc/rc.d" 555 "${name}"
   2060 	return $?
   2061 }
   2062 
   2063 # select non-obsolete files in a sets file
   2064 # $1: directory pattern
   2065 # $2: file pattern
   2066 # $3: filename
   2067 select_set_files()
   2068 {
   2069 	local qdir="$(echo $1 | ${SED} -e s@/@\\\\/@g -e s/\\./\\\\./g)"
   2070 	${SED} -n -e /obsolete/d \
   2071 	    -e "/^\.${qdir}/s@^.$2[[:space:]].*@\1@p" $3
   2072 }
   2073 
   2074 # select obsolete files in a sets file
   2075 # $1: directory pattern
   2076 # $2: file pattern
   2077 # $3: setname
   2078 select_obsolete_files()
   2079 {
   2080 	if $SOURCEMODE; then
   2081 		${SED} -n -e "/obsolete/s@\.$1$2[[:space:]].*@\1@p" \
   2082 		    "${SRC_DIR}/distrib/sets/lists/$3/mi"
   2083 		return
   2084 	fi
   2085 
   2086 	# On upgrade builds we don't extract the "etc" set so we
   2087 	# try to use the source set instead. See PR/54730 for
   2088 	# ways to better handle this.
   2089 
   2090 	local obsolete_dir
   2091 
   2092 	if [ $3 = "etc" ] ;then
   2093 		obsolete_dir="${SRC_DIR}/var/db/obsolete"
   2094 	else
   2095 		obsolete_dir="${DEST_DIR}/var/db/obsolete"
   2096 	fi
   2097 	${SED} -n -e "s@\.$1$2\$@\1@p" "${obsolete_dir}/$3"
   2098 }
   2099 
   2100 getetcsets()
   2101 {
   2102 	if $SOURCEMODE; then
   2103 		echo "${SRC_DIR}/distrib/sets/lists/etc/mi"
   2104 	else
   2105 		echo "${SRC_DIR}/etc/mtree/set.etc"
   2106 	fi
   2107 }
   2108 
   2109 additem rc "/etc/rc* and /etc/rc.d/ being up to date"
   2110 do_rc()
   2111 {
   2112 	[ -n "$1" ] || err 3 "USAGE: do_rc fix|check"
   2113 	local op="$1"
   2114 	local failed=0
   2115 	local generated_scripts=""
   2116 	local etcsets=$(getetcsets)
   2117 	if [ "${MKX11}" != "no" ]; then
   2118 		generated_scripts="${generated_scripts} xdm xfs"
   2119 	fi
   2120 
   2121 	# Directories of external programs that have rc files (in bsd)
   2122 	local rc_external_files="blocklist nsd unbound"
   2123 
   2124 	# rc* files in /etc/
   2125 	# XXX: at least rc.conf and rc.local shouldn't be updated. PR/54741
   2126 	#local rc_644_files="$(select_set_files /etc/rc \
   2127 	#    "/etc/\(rc[^[:space:]/]*\)" ${etcsets})"
   2128 
   2129 	# no-obsolete rc files in /etc/rc.d
   2130 	local rc_555_files="$(select_set_files /etc/rc.d/ \
   2131 	    "/etc/rc\.d/\([^[:space:]]*\)" ${etcsets} | \
   2132 	    exclude ${rc_external_files})"
   2133 
   2134 	# obsolete rc file in /etc/rc.d
   2135 	local rc_obsolete_files="$(select_obsolete_files /etc/rc.d/ \
   2136 	    "\([^[:space:]]*\)" etc)"
   2137 
   2138 	compare_dir "${op}" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
   2139 		${rc_644_files}
   2140 	failed=$(( ${failed} + $? ))
   2141 
   2142 	local extra_scripts
   2143 	if ! $SOURCEMODE; then
   2144 		extra_scripts="${generated_scripts}"
   2145 	else
   2146 		extra_scripts=""
   2147 	fi
   2148 
   2149 	compare_dir "${op}" "${SRC_DIR}/etc/rc.d" "${DEST_DIR}/etc/rc.d" 555 \
   2150 		${rc_555_files} \
   2151 		${extra_scripts}
   2152 	failed=$(( ${failed} + $? ))
   2153 
   2154 	for i in ${rc_external_files}; do
   2155 	    local rc_file
   2156 	    case $i in
   2157 	    *d) rc_file=${i};;
   2158 	    *)	rc_file=${i}d;;
   2159 	    esac
   2160 
   2161 	    update_rc "${op}" "${dir}" ${rc_file} /sbin \
   2162 		"${SRC_DIR}/external/bsd/$i/etc/rc.d"
   2163 	    failed=$(( ${failed} + $? ))
   2164 	done
   2165 
   2166 	if $SOURCEMODE && [ -n "${generated_scripts}" ]; then
   2167 		# generate scripts
   2168 		mkdir "${SCRATCHDIR}/rc"
   2169 		for f in ${generated_scripts}; do
   2170 			${SED} -e "s,@X11ROOTDIR@,${X11ROOTDIR},g" \
   2171 			    < "${SRC_DIR}/etc/rc.d/${f}.in" \
   2172 			    > "${SCRATCHDIR}/rc/${f}"
   2173 		done
   2174 		compare_dir "${op}" "${SCRATCHDIR}/rc" \
   2175 		    "${DEST_DIR}/etc/rc.d" 555 \
   2176 		    ${generated_scripts}
   2177 		failed=$(( ${failed} + $? ))
   2178 	fi
   2179 
   2180 		# check for obsolete rc.d files
   2181 	for f in ${rc_obsolete_files}; do
   2182 		local fd="/etc/rc.d/${f}"
   2183 		[ -e "${DEST_DIR}${fd}" ] && echo "${fd}"
   2184 	done | obsolete_paths "${op}"
   2185 	failed=$(( ${failed} + $? ))
   2186 
   2187 		# check for obsolete rc.conf(5) variables
   2188 	set -- ${rc_obsolete_vars}
   2189 	while [ $# -gt 1 ]; do
   2190 		if rcconf_is_set "${op}" "$1" "$2" 1; then
   2191 			failed=1
   2192 		fi
   2193 		shift 2
   2194 	done
   2195 
   2196 	return ${failed}
   2197 }
   2198 
   2199 
   2200 #
   2201 #	sendmail
   2202 #
   2203 
   2204 adddisableditem sendmail "remove obsolete sendmail configuration files and scripts"
   2205 do_sendmail()
   2206 {
   2207 	[ -n "$1" ] || err 3 "USAGE: do_sendmail fix|check"
   2208 	op="$1"
   2209 	failed=0
   2210 
   2211 	# Don't complain if the "sendmail" package is installed because the
   2212 	# files might still be in use.
   2213 	if /usr/sbin/pkg_info -qe sendmail >/dev/null 2>&1; then
   2214 		return 0
   2215 	fi
   2216 
   2217 	for f in /etc/mail/helpfile /etc/mail/local-host-names \
   2218 	    /etc/mail/sendmail.cf /etc/mail/submit.cf /etc/rc.d/sendmail \
   2219 	    /etc/rc.d/smmsp /usr/share/misc/sendmail.hf \
   2220 	    $( ( find "${DEST_DIR}/usr/share/sendmail" -type f ; \
   2221 	         find "${DEST_DIR}/usr/share/sendmail" -type d \
   2222 	       ) | unprefix "${DEST_DIR}" ) \
   2223 	    /var/log/sendmail.st \
   2224 	    /var/spool/clientmqueue \
   2225 	    /var/spool/mqueue
   2226 	do
   2227 		[ -e "${DEST_DIR}${f}" ] && echo "${f}"
   2228 	done | obsolete_paths "${op}"
   2229 	failed=$(( ${failed} + $? ))
   2230 
   2231 	return ${failed}
   2232 }
   2233 
   2234 
   2235 #
   2236 #	ssh
   2237 #
   2238 
   2239 additem ssh "ssh configuration update"
   2240 do_ssh()
   2241 {
   2242 	[ -n "$1" ] || err 3 "USAGE: do_ssh fix|check"
   2243 	op="$1"
   2244 
   2245 	failed=0
   2246 	_etcssh="${DEST_DIR}/etc/ssh"
   2247 	if ! check_dir "${op}" "${_etcssh}" 755; then
   2248 		failed=1
   2249 	fi
   2250 
   2251 	if [ ${failed} -eq 0 ]; then
   2252 		for f in \
   2253 			    ssh_known_hosts ssh_known_hosts2 \
   2254 			    ssh_host_dsa_key ssh_host_dsa_key.pub \
   2255 			    ssh_host_rsa_key ssh_host_rsa_key.pub \
   2256 			    ssh_host_key ssh_host_key.pub \
   2257 		    ; do
   2258 			if ! move_file "${op}" \
   2259 			    "${DEST_DIR}/etc/${f}" "${_etcssh}/${f}" ; then
   2260 				failed=1
   2261 			fi
   2262 		done
   2263 		for f in sshd.conf ssh.conf ; do
   2264 				# /etc/ssh/ssh{,d}.conf -> ssh{,d}_config
   2265 				#
   2266 			if ! move_file "${op}" \
   2267 			    "${_etcssh}/${f}" "${_etcssh}/${f%.conf}_config" ;
   2268 			then
   2269 				failed=1
   2270 			fi
   2271 				# /etc/ssh{,d}.conf -> /etc/ssh/ssh{,d}_config
   2272 				#
   2273 			if ! move_file "${op}" \
   2274 			    "${DEST_DIR}/etc/${f}" \
   2275 			    "${_etcssh}/${f%.conf}_config" ;
   2276 			then
   2277 				failed=1
   2278 			fi
   2279 		done
   2280 	fi
   2281 
   2282 	sshdconf=""
   2283 	for f in \
   2284 	    "${_etcssh}/sshd_config" \
   2285 	    "${_etcssh}/sshd.conf" \
   2286 	    "${DEST_DIR}/etc/sshd.conf" ; do
   2287 		if [ -f "${f}" ]; then
   2288 			sshdconf="${f}"
   2289 			break
   2290 		fi
   2291 	done
   2292 	if [ -n "${sshdconf}" ]; then
   2293 		modify_file "${op}" "${sshdconf}" "${SCRATCHDIR}/sshdconf" '
   2294 			/^[^#$]/ {
   2295 				kw = tolower($1)
   2296 				if (kw == "hostkey" &&
   2297 				    $2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ ) {
   2298 					sub(/\/etc\/+/, "/etc/ssh/")
   2299 				}
   2300 				if (kw == "rhostsauthentication" ||
   2301 				    kw == "verifyreversemapping" ||
   2302 				    kw == "reversemappingcheck") {
   2303 					sub(/^/, "# DEPRECATED:\t")
   2304 				}
   2305 			}
   2306 			{ print }
   2307 		'
   2308 		failed=$(( ${failed} + $? ))
   2309 	fi
   2310 
   2311 	if ! find_file_in_dirlist moduli "moduli" \
   2312 	    "${SRC_DIR}/crypto/external/bsd/openssh/dist" "${SRC_DIR}/etc" ; then
   2313 		failed=1
   2314 			# ${dir} is set by find_file_in_dirlist()
   2315 	elif ! compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 moduli; then
   2316 		failed=1
   2317 	fi
   2318 
   2319 	if ! check_dir "${op}" "${DEST_DIR}/var/chroot/sshd" 755 ; then
   2320 		failed=1
   2321 	fi
   2322 
   2323 	if rcconf_is_set "${op}" sshd sshd_conf_dir 1; then
   2324 		failed=1
   2325 	fi
   2326 
   2327 	return ${failed}
   2328 }
   2329 
   2330 
   2331 #
   2332 #	tcpdumpchroot
   2333 #
   2334 
   2335 additem tcpdumpchroot "remove /var/chroot/tcpdump/etc/protocols"
   2336 do_tcpdumpchroot()
   2337 {
   2338 	[ -n "$1" ] || err 3 "USAGE: do_tcpdumpchroot fix|check"
   2339 
   2340 	failed=0;
   2341 	if [ -r "${DEST_DIR}/var/chroot/tcpdump/etc/protocols" ]; then
   2342 		if [ "$1" = "fix" ]; then
   2343 			rm "${DEST_DIR}/var/chroot/tcpdump/etc/protocols"
   2344 			failed=$(( ${failed} + $? ))
   2345 			rmdir "${DEST_DIR}/var/chroot/tcpdump/etc"
   2346 			failed=$(( ${failed} + $? ))
   2347 		else
   2348 			failed=1
   2349 		fi
   2350 	fi
   2351 	return ${failed}
   2352 }
   2353 
   2354 
   2355 #
   2356 #	uid
   2357 #
   2358 
   2359 additem uid "required users in /etc/master.passwd"
   2360 do_uid()
   2361 {
   2362 	[ -n "$1" ] || err 3 "USAGE: do_uid fix|check"
   2363 
   2364 	check_ids "$1" users "${DEST_DIR}/etc/master.passwd" \
   2365 	    "${SRC_DIR}/etc/master.passwd" 12 \
   2366 	    postfix SKIP named ntpd sshd SKIP _pflogd _rwhod SKIP _proxy \
   2367 	    _timedc _sdpd _httpd _mdnsd _tests _tcpdump _tss SKIP _rtadvd \
   2368 	    SKIP _unbound _nsd SKIP _dhcpcd
   2369 }
   2370 
   2371 
   2372 #
   2373 #	varrwho
   2374 #
   2375 
   2376 additem varrwho "required ownership of files in /var/rwho"
   2377 do_varrwho()
   2378 {
   2379 	[ -n "$1" ] || err 3 "USAGE: do_varrwho fix|check"
   2380 
   2381 	contents_owner "$1" "${DEST_DIR}/var/rwho" _rwhod _rwhod
   2382 }
   2383 
   2384 
   2385 #
   2386 #	varshm
   2387 #
   2388 
   2389 additem varshm "check for a tmpfs mounted on /var/shm"
   2390 do_varshm()
   2391 {
   2392 	[ -n "$1" ] || err 3 "USAGE: do_varshm fix|check"
   2393 	op="$1"
   2394 	failed=0
   2395 
   2396 	[ -f "${DEST_DIR}/etc/fstab" ] || return 0
   2397 	if ${GREP} -E "^var_shm_symlink" "${DEST_DIR}/etc/rc.conf" >/dev/null 2>&1;
   2398 	then
   2399 		failed=0;
   2400 	elif ${GREP} -w "/var/shm" "${DEST_DIR}/etc/fstab" >/dev/null 2>&1;
   2401 	then
   2402 		failed=0;
   2403 	else
   2404 		if [ "${op}" = "check" ]; then
   2405 			failed=1
   2406 			msg "No /var/shm mount found in ${DEST_DIR}/etc/fstab"
   2407 		elif [ "${op}" = "fix" ]; then
   2408 			printf '\ntmpfs\t/var/shm\ttmpfs\trw,-m1777,-sram%%25\n' \
   2409 				>> "${DEST_DIR}/etc/fstab"
   2410 			msg "Added tmpfs with 25% ram limit as /var/shm"
   2411 
   2412 		fi
   2413 	fi
   2414 
   2415 	return ${failed}
   2416 }
   2417 
   2418 
   2419 #
   2420 #	wscons
   2421 #
   2422 
   2423 additem wscons "wscons configuration file update"
   2424 do_wscons()
   2425 {
   2426 	[ -n "$1" ] || err 3 "USAGE: do_wscons fix|check"
   2427 	op="$1"
   2428 
   2429 	[ -f "${DEST_DIR}/etc/wscons.conf" ] || return 0
   2430 
   2431 	failed=0
   2432 	notfixed=""
   2433 	if [ "${op}" = "fix" ]; then
   2434 		notfixed="${NOT_FIXED}"
   2435 	fi
   2436 	while read _type _arg1 _rest; do
   2437 		if [ "${_type}" = "mux" -a "${_arg1}" = "1" ]; then
   2438 			msg \
   2439     "Obsolete wscons.conf(5) entry \""${_type} ${_arg1}"\" found.${notfixed}"
   2440 			failed=1
   2441 		fi
   2442 	done < "${DEST_DIR}/etc/wscons.conf"
   2443 
   2444 	return ${failed}
   2445 }
   2446 
   2447 
   2448 #
   2449 #	x11
   2450 #
   2451 
   2452 additem x11 "x11 configuration update"
   2453 do_x11()
   2454 {
   2455 	[ -n "$1" ] || err 3 "USAGE: do_x11 fix|check"
   2456 	op="$1"
   2457 
   2458 	failed=0
   2459 	_etcx11="${DEST_DIR}/etc/X11"
   2460 	_libx11=""
   2461 	if [ ! -d "${_etcx11}" ]; then
   2462 		msg "${_etcx11} is not a directory; skipping check"
   2463 		return 0
   2464 	fi
   2465 	if [ -d "${DEST_DIR}/usr/X11R6/." ]
   2466 	then
   2467 		_libx11="${DEST_DIR}/usr/X11R6/lib/X11"
   2468 		if [ ! -d "${_libx11}" ]; then
   2469 			msg "${_libx11} is not a directory; skipping check"
   2470 			return 0
   2471 		fi
   2472 	fi
   2473 
   2474 	_notfixed=""
   2475 	if [ "${op}" = "fix" ]; then
   2476 		_notfixed="${NOT_FIXED}"
   2477 	fi
   2478 
   2479 	# check if /usr/X11R6/lib/X11 needs to migrate to /etc/X11
   2480 	if [ -n "${_libx11}" ]; then
   2481 		for d in \
   2482 			    fs lbxproxy proxymngr rstart twm xdm xinit xserver xsm \
   2483 		    ; do
   2484 			sd="${_libx11}/${d}"
   2485 			ld="/etc/X11/${d}"
   2486 			td="${DEST_DIR}${ld}"
   2487 			if [ -h "${sd}" ]; then
   2488 				continue
   2489 			elif [ -d "${sd}" ]; then
   2490 				tdfiles="$(find "${td}" \! -type d)"
   2491 				if [ -n "${tdfiles}" ]; then
   2492 					msg "${sd} exists yet ${td} already" \
   2493 					    "contains files${_notfixed}"
   2494 				else
   2495 					msg "Migrate ${sd} to ${td}${_notfixed}"
   2496 				fi
   2497 				failed=1
   2498 			elif [ -e "${sd}" ]; then
   2499 				msg "Unexpected file ${sd}${_notfixed}"
   2500 				continue
   2501 			else
   2502 				continue
   2503 			fi
   2504 		done
   2505 	fi
   2506 
   2507 	# check if xdm resources have been updated
   2508 	if [ -r ${_etcx11}/xdm/Xresources ] && \
   2509 	    ! ${GREP} 'inpColor:' ${_etcx11}/xdm/Xresources > /dev/null; then
   2510 		msg "Update ${_etcx11}/xdm/Xresources${_notfixed}"
   2511 		failed=1
   2512 	fi
   2513 
   2514 	return ${failed}
   2515 }
   2516 
   2517 
   2518 #
   2519 #	xkb
   2520 #
   2521 # /usr/X11R7/lib/X11/xkb/symbols/pc used to be a directory, but changed
   2522 # to a file on 2009-06-12.  Fixing this requires removing the directory
   2523 # (which we can do) and re-extracting the xbase set (which we can't do),
   2524 # or at least adding that one file (which we may be able to do if X11SRCDIR
   2525 # is available).
   2526 #
   2527 
   2528 additem xkb "clean up for xkbdata to xkeyboard-config upgrade"
   2529 do_xkb()
   2530 {
   2531 	[ -n "$1" ] || err 3 "USAGE: do_xkb fix|check"
   2532 	op="$1"
   2533 	failed=0
   2534 
   2535 	pcpath="/usr/X11R7/lib/X11/xkb/symbols/pc"
   2536 	pcsrcdir="${X11SRCDIR}/external/mit/xkeyboard-config/dist/symbols"
   2537 
   2538 	filemsg="\
   2539 ${pcpath} was a directory, should be a file.
   2540     To fix, extract the xbase set again."
   2541 
   2542 	_notfixed=""
   2543 	if [ "${op}" = "fix" ]; then
   2544 		_notfixed="${NOT_FIXED}"
   2545 	fi
   2546 
   2547 	if [ ! -d "${DEST_DIR}${pcpath}" ]; then
   2548 		return 0
   2549 	fi
   2550 
   2551 	# Delete obsolete files in the directory, and the directory
   2552 	# itself.  If the directory contains unexpected extra files
   2553 	# then it will not be deleted.
   2554 	( [ -f "${DEST_DIR}"/var/db/obsolete/xbase ] \
   2555 	    &&  ${SORT} -ru "${DEST_DIR}"/var/db/obsolete/xbase \
   2556 	    | ${GREP} -E "^\\.?${pcpath}/" ;
   2557 	    echo "${pcpath}" ) \
   2558 	| obsolete_paths "${op}"
   2559 	failed=$(( ${failed} + $? ))
   2560 
   2561 	# If the directory was removed above, then try to replace it with
   2562 	# a file.
   2563 	if [ -d "${DEST_DIR}${pcpath}" ]; then
   2564 		msg "${filemsg}${_notfixed}"
   2565 		failed=$(( ${failed} + 1 ))
   2566 	else
   2567 		if ! find_file_in_dirlist pc "${pcpath}" \
   2568 			"${pcsrcdir}" "${SRC_DIR}${pcpath%/*}"
   2569 		then
   2570 			msg "${filemsg}${_notfixed}"
   2571 			failed=$(( ${failed} + 1 ))
   2572 		else
   2573 			# ${dir} is set by find_file_in_dirlist()
   2574 			populate_dir "${op}" true \
   2575 				"${dir}" "${DEST_DIR}${pcpath%/*}" 444 \
   2576 				pc
   2577 			failed=$(( ${failed} + $? ))
   2578 		fi
   2579 	fi
   2580 
   2581 	return $failed
   2582 }
   2583 
   2584 
   2585 #
   2586 #	obsolete_stand
   2587 #	obsolete_stand_debug
   2588 #
   2589 
   2590 obsolete_stand_internal()
   2591 {
   2592 	local prefix="$1"
   2593 	shift
   2594 	[ -n "$1" ] || err 3 "USAGE: do_obsolete_stand fix|check"
   2595 	local op="$1"
   2596 	local failed=0
   2597 
   2598 	for dir in \
   2599 	    ${prefix}/stand/${MACHINE} \
   2600 	    ${prefix}/stand/${MACHINE}-4xx \
   2601 	    ${prefix}/stand/${MACHINE}-booke \
   2602 	    ${prefix}/stand/${MACHINE}-xen \
   2603 	    ${prefix}/stand/${MACHINE}pae-xen
   2604 	do
   2605 		[ -d "${DEST_DIR}${dir}" ] && obsolete_stand "${dir}"
   2606 	done | obsolete_paths "${op}"
   2607 	failed=$(( ${failed} + $? ))
   2608 
   2609 	return ${failed}
   2610 }
   2611 
   2612 adddisableditem obsolete_stand "remove obsolete files from /stand"
   2613 do_obsolete_stand()
   2614 {
   2615 	obsolete_stand_internal "" "$@"
   2616 	return $?
   2617 }
   2618 
   2619 adddisableditem obsolete_stand_debug "remove obsolete files from /usr/libdata/debug/stand"
   2620 do_obsolete_stand_debug()
   2621 {
   2622 	obsolete_stand_internal "/usr/libdata/debug" "$@"
   2623 	return $?
   2624 }
   2625 
   2626 
   2627 #
   2628 #	obsolete
   2629 #
   2630 # NOTE: This item is last to allow other items to move obsolete files.
   2631 #
   2632 
   2633 listarchsubdirs()
   2634 {
   2635 	if ! $SOURCEMODE; then
   2636 		echo "@ARCHSUBDIRS@"
   2637 	else
   2638 		${SED} -n -e '/ARCHDIR_SUBDIR/s/[[:space:]]//gp' \
   2639 		    "${SRC_DIR}/compat/archdirs.mk"
   2640 	fi
   2641 }
   2642 
   2643 getarchsubdirs()
   2644 {
   2645 	local m
   2646 	case ${MACHINE_ARCH} in
   2647 	*arm*|*aarch64*)	m=arm;;
   2648 	x86_64)			m=amd64;;
   2649 	*)			m=${MACHINE_ARCH};;
   2650 	esac
   2651 
   2652 	for i in $(listarchsubdirs); do
   2653 		echo $i
   2654 	done | ${SORT} -u | ${SED} -n -e "/=${m}/s@.*=${m}/\(.*\)@\1@p"
   2655 }
   2656 
   2657 getcompatlibdirs()
   2658 {
   2659 	for i in $(getarchsubdirs); do
   2660 		if [ -d "${DEST_DIR}/usr/lib/$i" ]; then
   2661 			echo /usr/lib/$i
   2662 		fi
   2663 	done
   2664 }
   2665 
   2666 additem obsolete "remove obsolete file sets and minor libraries"
   2667 do_obsolete()
   2668 {
   2669 	[ -n "$1" ] || err 3 "USAGE: do_obsolete fix|check"
   2670 	op="$1"
   2671 	failed=0
   2672 
   2673 	${SORT} -ru "${DEST_DIR}"/var/db/obsolete/* | obsolete_paths "${op}"
   2674 	failed=$(( ${failed} + $? ))
   2675 
   2676 	(
   2677 		obsolete_libs /lib
   2678 		obsolete_libs /usr/lib
   2679 		obsolete_libs /usr/lib/i18n
   2680 		obsolete_libs /usr/X11R6/lib
   2681 		obsolete_libs /usr/X11R7/lib
   2682 		for i in $(getcompatlibdirs); do
   2683 			obsolete_libs $i
   2684 		done
   2685 	) | obsolete_paths "${op}"
   2686 	failed=$(( ${failed} + $? ))
   2687 
   2688 	return ${failed}
   2689 }
   2690 
   2691 
   2692 #
   2693 #	end of items
   2694 #	------------
   2695 #
   2696 
   2697 
   2698 help()
   2699 {
   2700 	cat << _USAGE_
   2701 Usage: ${PROGNAME} [-a ARCH] [-d DEST_DIR] [-m MACHINE] [-s SRC_ARG] [-x XSRC_DIR] OPERATION ...
   2702        ${PROGNAME} -?
   2703 
   2704 	Perform post-installation checks and/or fixes on a system's
   2705 	configuration files.
   2706 	If no items are provided, a default set of checks or fixes is applied.
   2707 
   2708 	Options:
   2709 	-?		Display this help, and exit.
   2710 	-a ARCH		Set \$MACHINE_ARCH to ARCH.	[${MACHINE_ARCH}]
   2711 	-d DEST_DIR	Destination directory to check. [${DEST_DIR:-/}]
   2712 	-m MACHINE	Set \$MACHINE to MACHINE.	[${MACHINE}]
   2713 	-s SRC_ARG	Location of the source files.  This may be any of
   2714 			the following:
   2715 			-s SRC_DIR	A directory that contains a NetBSD
   2716 					source tree.
   2717 			-s TGZ_DIR	A directory in which one or both of
   2718 					"etc.tgz" and "xetc.tgz" have been
   2719 					extracted.
   2720 			-s TGZ_FILE	A distribution set file such as
   2721 					"etc.tgz" or "xetc.tgz".
   2722 					May be specified multipled times.
   2723 							[${SRC_DIR:-/usr/src}]
   2724 	-x XSRC_DIR	Location of the X11 source files.  This must be
   2725 			a directory that contains a NetBSD xsrc tree.
   2726 							[${XSRC_DIR:-/usr/src/../xsrc}]
   2727 
   2728 	Supported values for OPERATION:
   2729 	help		Display this help, and exit.
   2730 	list		List available items.
   2731 	check ITEM ...	Perform post-installation checks on ITEMs.
   2732 	diff [-bcenpuw] ITEM ...
   2733 			Similar to 'check' but also output difference of files,
   2734 			using diff with the provided options.
   2735 	fix ITEM ...	Apply fixes that 'check' determines need to be applied.
   2736 	usage		Display this help, and exit.
   2737 _USAGE_
   2738 }
   2739 
   2740 usage()
   2741 {
   2742 	help 1>&2
   2743 	exit 2
   2744 }
   2745 
   2746 
   2747 list()
   2748 {
   2749 	echo "Default set of items (to apply if no items are provided by user):"
   2750 	echo " Item                 Description"
   2751 	echo " ----                 -----------"
   2752 	for i in ${defaultitems}; do
   2753 		eval desc=\"\${desc_${i}}\"
   2754 		printf " %-20s %s\n" "${i}" "${desc}"
   2755 	done
   2756 	echo "Items disabled by default (must be requested explicitly):"
   2757 	echo " Item                 Description"
   2758 	echo " ----                 -----------"
   2759 	for i in ${otheritems}; do
   2760 		eval desc=\"\${desc_${i}}\"
   2761 		printf " %-20s %s\n" "${i}" "${desc}"
   2762 	done
   2763 }
   2764 
   2765 
   2766 main()
   2767 {
   2768 	DIRMODE=false		# true if "-s" specified a directory
   2769 	ITEMS=			# items to check|diff|fix. [${defaultitems}]
   2770 	N_SRC_ARGS=0		# number of "-s" args in SRC_ARGLIST
   2771 	SOURCEMODE=false	# true if "-s" specified a source directory
   2772 	SRC_ARGLIST=		# quoted list of one or more "-s" args
   2773 	SRC_DIR="${SRC_ARG}"	# set default value for early usage()
   2774 	TGZLIST=		# quoted list list of tgz files
   2775 	TGZMODE=false		# true if "-s" specifies a tgz file
   2776 	XSRC_DIR="${SRC_ARG}/../xsrc"
   2777 	XSRC_DIR_FIX=
   2778 
   2779 	case "$(uname -s)" in
   2780 	Darwin)
   2781 		# case sensitive match for case insensitive fs
   2782 		file_exists_exact=file_exists_exact
   2783 		;;
   2784 	*)
   2785 		file_exists_exact=:
   2786 		;;
   2787 	esac
   2788 
   2789 		# Validate options.
   2790 		#
   2791 	while getopts :a:d:m:s:x: ch; do
   2792 		case "${ch}" in
   2793 		a)
   2794 			MACHINE_ARCH="${OPTARG}"
   2795 			;;
   2796 		d)
   2797 			DEST_DIR="${OPTARG}"
   2798 			;;
   2799 		m)
   2800 			MACHINE="${OPTARG}"
   2801 			;;
   2802 		s)
   2803 			qarg="$(shell_quote "${OPTARG}")"
   2804 			N_SRC_ARGS=$(( $N_SRC_ARGS + 1 ))
   2805 			SRC_ARGLIST="${SRC_ARGLIST}${SRC_ARGLIST:+ }-s ${qarg}"
   2806 			if [ -f "${OPTARG}" ]; then
   2807 				# arg refers to a *.tgz file.
   2808 				# This may happen twice, for both
   2809 				# etc.tgz and xetc.tgz, so we build up a
   2810 				# quoted list in TGZLIST.
   2811 				TGZMODE=true
   2812 				TGZLIST="${TGZLIST}${TGZLIST:+ }${qarg}"
   2813 				# Note that, when TGZMODE is true,
   2814 				# SRC_ARG is used only for printing
   2815 				# human-readable messages.
   2816 				SRC_ARG="${TGZLIST}"
   2817 			elif [ -d "${OPTARG}" ]; then
   2818 				# arg refers to a directory.
   2819 				# It might be a source directory, or a
   2820 				# directory where the sets have already
   2821 				# been extracted.
   2822 				DIRMODE=true
   2823 				SRC_ARG="${OPTARG}"
   2824 				if [ -f "${OPTARG}/etc/Makefile" ]; then
   2825 					SOURCEMODE=true
   2826 				fi
   2827 			else
   2828 				err 2 "Invalid argument for -s option"
   2829 			fi
   2830 			;;
   2831 		x)
   2832 			if [ -d "${OPTARG}" ]; then
   2833 				# arg refers to a directory.
   2834 				XSRC_DIR="${OPTARG}"
   2835 				XSRC_DIR_FIX="-x ${OPTARG} "
   2836 			else
   2837 				err 2 "Not a directory for -x option"
   2838 			fi
   2839 			;;
   2840 		"?")
   2841 			if [ "${OPTARG}" = "?" ]; then
   2842 				help
   2843 				return	# no further processing or validation
   2844 			fi
   2845 			warn "Unknown option -${OPTARG}"
   2846 			usage
   2847 			;;
   2848 
   2849 		:)
   2850 			warn "Missing argument for option -${OPTARG}"
   2851 			usage
   2852 			;;
   2853 
   2854 		*)
   2855 			err 3 "Unimplemented option -${ch}"
   2856 			;;
   2857 		esac
   2858 	done
   2859 	shift $((${OPTIND} - 1))
   2860 	if [ $# -eq 0 ] ; then
   2861 		warn "Missing operation"
   2862 		usage
   2863 	fi
   2864 	op="$1"
   2865 	shift
   2866 
   2867 	if [ "$N_SRC_ARGS" -gt 1 ] && $DIRMODE; then
   2868 		err 2 "Multiple -s args are allowed only with tgz files"
   2869 	fi
   2870 	if [ "$N_SRC_ARGS" -eq 0 ]; then
   2871 		# The default SRC_ARG was set elsewhere
   2872 		DIRMODE=true
   2873 		SOURCEMODE=true
   2874 		SRC_ARGLIST="-s $(shell_quote "${SRC_ARG}")"
   2875 	fi
   2876 
   2877 		# Validate 'diff' first, as it becomes 'check'
   2878 		#
   2879 	case "${op}" in
   2880 
   2881 	diff)
   2882 		op=check
   2883 		DIFF_STYLE=n			# default style is RCS
   2884 		OPTIND=1
   2885 		while getopts :bcenpuw ch; do
   2886 			case "${ch}" in
   2887 			c|e|n|u)
   2888 				if [ "${DIFF_STYLE}" != "n" -a \
   2889 				    "${DIFF_STYLE}" != "${ch}" ]; then
   2890 					warn "diff: conflicting output style: -${ch}"
   2891 					usage
   2892 				fi
   2893 				DIFF_STYLE="${ch}"
   2894 				;;
   2895 			b|p|w)
   2896 				DIFF_OPT="${DIFF_OPT} -${ch}"
   2897 				;;
   2898 			"?")
   2899 				# NOTE: not supporting diff -?
   2900 				warn "diff: Unknown option -${OPTARG}"
   2901 				usage
   2902 				;;
   2903 			:)
   2904 				warn "diff: Missing argument for option -${OPTARG}"
   2905 				usage
   2906 				;;
   2907 			*)
   2908 				err 3 "diff: Unimplemented option -${ch}"
   2909 				;;
   2910 			esac
   2911 		done
   2912 		shift $((${OPTIND} - 1))
   2913 		;;
   2914 
   2915 	esac
   2916 
   2917 		# Validate operation and items.
   2918 		#
   2919 	case "${op}" in
   2920 
   2921 	check|fix)
   2922 		ITEMS="$*"
   2923 		: ${ITEMS:="${defaultitems}"}
   2924 
   2925 		# ensure that all supplied items are valid
   2926 		#
   2927 		for i in ${ITEMS}; do
   2928 			eval desc=\"\${desc_${i}}\"
   2929 			[ -n "${desc}" ] || err 2 "Unsupported ${op} '"${i}"'"
   2930 		done
   2931 		;;
   2932 
   2933 	help|usage)
   2934 		help
   2935 		return	# no further processing or validation
   2936 		;;
   2937 
   2938 	list)
   2939 		# processed below
   2940 		;;
   2941 
   2942 	*)
   2943 		warn "Unknown operation '"${op}"'"
   2944 		usage
   2945 		;;
   2946 
   2947 	esac
   2948 
   2949 	#
   2950 	# If '-s' arg or args specified tgz files, extract them
   2951 	# to a scratch directory.
   2952 	#
   2953 	if $TGZMODE; then
   2954 		ETCTGZDIR="${SCRATCHDIR}/etc.tgz"
   2955 		echo "Note: Creating temporary directory ${ETCTGZDIR}"
   2956 		if ! mkdir "${ETCTGZDIR}"; then
   2957 			err 2 "Can't create ${ETCTGZDIR}"
   2958 		fi
   2959 		( # subshell to localise changes to "$@"
   2960 			eval "set -- ${TGZLIST}"
   2961 			for tgz in "$@"; do
   2962 				echo "Note: Extracting files from ${tgz}"
   2963 				cat "${tgz}" | (
   2964 					cd "${ETCTGZDIR}" &&
   2965 					tar -zxf -
   2966 				) || err 2 "Can't extract ${tgz}"
   2967 			done
   2968 		)
   2969 		SRC_DIR="${ETCTGZDIR}"
   2970 	else
   2971 		SRC_DIR="${SRC_ARG}"
   2972 	fi
   2973 
   2974 	[ -d "${SRC_DIR}" ]	|| err 2 "${SRC_DIR} is not a directory"
   2975 	[ -d "${DEST_DIR}" ]	|| err 2 "${DEST_DIR} is not a directory"
   2976 	[ -n "${MACHINE}" ]	|| err 2 "\${MACHINE} is not defined"
   2977 	[ -n "${MACHINE_ARCH}" ] || err 2 "\${MACHINE_ARCH} is not defined"
   2978 	if ! $SOURCEMODE && ! [ -f "${SRC_DIR}/etc/mtree/set.etc" ]; then
   2979 		err 2 "Files from the etc.tgz set are missing"
   2980 	fi
   2981 
   2982 		# If directories are /, clear them, so various messages
   2983 		# don't have leading "//".   However, this requires
   2984 		# the use of ${foo:-/} to display the variables.
   2985 		#
   2986 	[ "${SRC_DIR}" = "/" ]	&& SRC_DIR=""
   2987 	[ "${DEST_DIR}" = "/" ]	&& DEST_DIR=""
   2988 
   2989 	detect_x11
   2990 
   2991 		# Perform operation.
   2992 		#
   2993 	case "${op}" in
   2994 
   2995 	check|fix)
   2996 		[ -n "${ITEMS}" ] || err 2 "${op}: missing items"
   2997 
   2998 		echo "Source directory: ${SRC_DIR:-/}"
   2999 		if $TGZMODE; then
   3000 			echo " (extracted from: ${SRC_ARG})"
   3001 		fi
   3002 		echo "Target directory: ${DEST_DIR:-/}"
   3003 		items_passed=
   3004 		items_failed=
   3005 		for i in ${ITEMS}; do
   3006 			echo "${i} ${op}:"
   3007 			( eval do_${i} ${op} )
   3008 			if [ $? -eq 0 ]; then
   3009 				items_passed="${items_passed} ${i}"
   3010 			else
   3011 				items_failed="${items_failed} ${i}"
   3012 			fi
   3013 		done
   3014 
   3015 		if [ "${op}" = "check" ]; then
   3016 			plural="checks"
   3017 		else
   3018 			plural="fixes"
   3019 		fi
   3020 
   3021 		echo "${PROGNAME} ${plural} passed:${items_passed}"
   3022 		echo "${PROGNAME} ${plural} failed:${items_failed}"
   3023 		if [ -n "${items_failed}" ]; then
   3024 		    exitstatus=1;
   3025 		    if [ "${op}" = "check" ]; then
   3026 			[ "$MACHINE" = "$(uname -m)" ] && m= || m=" -m $MACHINE"
   3027 			cat <<_Fix_me_
   3028 To fix, run:
   3029     ${HOST_SH} ${0} ${SRC_ARGLIST} ${XSRC_DIR_FIX}-d ${DEST_DIR:-/}$m fix${items_failed}
   3030 Note that this may overwrite local changes.
   3031 _Fix_me_
   3032 		    fi
   3033 		fi
   3034 		;;
   3035 
   3036 	list)
   3037 		echo "Source directory: ${SRC_DIR:-/}"
   3038 		echo "Target directory: ${DEST_DIR:-/}"
   3039 		if $TGZMODE; then
   3040 			echo " (extracted from: ${SRC_ARG})"
   3041 		fi
   3042 		list
   3043 		;;
   3044 
   3045 	*)
   3046 			# diff, help, usage handled during operation validation
   3047 		err 3 "Unimplemented operation '"${op}"'"
   3048 		;;
   3049 
   3050 	esac
   3051 }
   3052 
   3053 if [ -n "$POSTINSTALL_FUNCTION" ]; then
   3054 	eval "$POSTINSTALL_FUNCTION"
   3055 	exit 0
   3056 fi
   3057 
   3058 # defaults
   3059 #
   3060 PROGNAME="${0##*/}"
   3061 SRC_ARG="/usr/src"
   3062 DEST_DIR="/"
   3063 : ${MACHINE:="$( uname -m )"}	# assume native build if $MACHINE is not set
   3064 : ${MACHINE_ARCH:="$( uname -p )"}# assume native build if not set
   3065 
   3066 DIFF_STYLE=
   3067 DIFF_OPT=
   3068 NOT_FIXED=" (FIX MANUALLY)"
   3069 SCRATCHDIR="$( mkdtemp )" || err 2 "Can't create scratch directory"
   3070 trap "/bin/rm -rf \"\${SCRATCHDIR}\" ; exit 0" 1 2 3 15	# HUP INT QUIT TERM
   3071 
   3072 umask 022
   3073 exec 3>/dev/null
   3074 exec 4>/dev/null
   3075 exitstatus=0
   3076 
   3077 main "$@"
   3078 /bin/rm -rf "${SCRATCHDIR}"
   3079 exit $exitstatus
   3080