xref: /minix/usr.sbin/postinstall/postinstall (revision 84d9c625)
1#!/bin/sh
2#
3# $NetBSD: postinstall,v 1.158 2013/09/09 15:04:12 prlw1 Exp $
4#
5# Copyright (c) 2002-2008 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# XXX BE SURE TO USE ${DEST_DIR} PREFIX BEFORE ALL REAL FILE OPERATIONS XXX
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#	  dhclient.conf, ...) ?
46#	- support quiet/verbose mode ?
47#	- differentiate between failures caused by missing source
48#	  and real failures
49#	- install moduli into usr/share/examples/ssh and use from there?
50#	- differentiate between "needs fix" versus "can't fix" issues
51#
52
53# This script is executed as part of a cross build.  Allow the build
54# environment to override the locations of some tools.
55: ${AWK:=awk}
56: ${DB:=db}
57: ${GREP:=grep}
58: ${HOST_SH:=sh}
59: ${MAKE:=make}
60: ${PWD_MKDB:=/usr/sbin/pwd_mkdb}
61: ${STAT:=stat}
62
63#
64#	helper functions
65#
66
67err()
68{
69	exitval=$1
70	shift
71	echo 1>&2 "${PROGNAME}: $*"
72	if [ -n "${SCRATCHDIR}" ]; then
73	    /bin/rm -rf "${SCRATCHDIR}"
74	fi
75	exit ${exitval}
76}
77
78warn()
79{
80	echo 1>&2 "${PROGNAME}: $*"
81}
82
83msg()
84{
85	echo "	$*"
86}
87
88mkdtemp()
89{
90	# Make sure we don't loop forever if mkdir will always fail.
91	[ -d /tmp ] || err 2 /tmp is not a directory
92	[ -w /tmp ] || err 2 /tmp is not writable
93
94	_base="/tmp/_postinstall.$$"
95	_serial=0
96
97	while true; do
98		_dir="${_base}.${_serial}"
99		mkdir -m 0700 "${_dir}" && break
100		_serial=$((${_serial} + 1))
101	done
102	echo "${_dir}"
103}
104
105# Quote args to make them safe in the shell.
106# Usage: quotedlist="$(shell_quote args...)"
107#
108# After building up a quoted list, use it by evaling it inside
109# double quotes, like this:
110#    eval "set -- $quotedlist"
111# or like this:
112#    eval "\$command $quotedlist \$filename"
113shell_quote()
114{
115	local result=''
116	local arg
117	for arg in "$@" ; do
118		# Append a space if necessary
119		result="${result}${result:+ }"
120		# Convert each embedded ' to '\'',
121		# then insert ' at the beginning of the first line,
122		# and append ' at the end of the last line.
123		result="${result}$(printf "%s\n" "$arg" | \
124			sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/")"
125	done
126	printf "%s\n" "$result"
127}
128
129
130# additem item description
131#	Add item to list of supported items to check/fix,
132#	which are checked/fixed by default if no item is requested by user.
133#
134additem()
135{
136	[ $# -eq 2 ] || err 3 "USAGE: additem item description"
137	defaultitems="${defaultitems}${defaultitems:+ }$1"
138	eval desc_$1=\"$2\"
139}
140
141# adddisableditem item description
142#	Add item to list of supported items to check/fix,
143#	but execute the item only if the user asks for it explicitly.
144#
145adddisableditem()
146{
147	[ $# -eq 2 ] || err 3 "USAGE: adddisableditem item description"
148	otheritems="${otheritems}${otheritems:+ }$1"
149	eval desc_$1=\"$2\"
150}
151
152# checkdir op dir mode
153#	Ensure dir exists, and if not, create it with the appropriate mode.
154#	Returns 0 if ok, 1 otherwise.
155#
156check_dir()
157{
158	[ $# -eq 3 ] || err 3 "USAGE: check_dir op dir mode"
159	_cdop="$1"
160	_cddir="$2"
161	_cdmode="$3"
162	[ -d "${_cddir}" ] && return 0
163	if [ "${_cdop}" = "check" ]; then
164		msg "${_cddir} is not a directory"
165		return 1
166	elif ! mkdir -m "${_cdmode}" "${_cddir}" ; then
167		msg "Can't create missing ${_cddir}"
168		return 1
169	else
170		msg "Missing ${_cddir} created"
171	fi
172	return 0
173}
174
175# check_ids op type file start id [...]
176#	Check if file of type "users" or "groups" contains the relevant IDs
177#	Returns 0 if ok, 1 otherwise.
178#
179check_ids()
180{
181	[ $# -ge 5 ] || err 3 "USAGE: checks_ids op type file start id [...]"
182	_op="$1"
183	_type="$2"
184	_file="$3"
185	_start="$4"
186	shift 4
187	#_ids="$@"
188
189	if [ ! -f "${_file}" ]; then
190		msg "${_file} doesn't exist; can't check for missing ${_type}"
191		return 1
192	fi
193	if [ ! -r "${_file}" ]; then
194		msg "${_file} is not readable; can't check for missing ${_type}"
195		return 1
196	fi
197	_notfixed=""
198	if [ "${_op}" = "fix" ]; then
199		_notfixed="${NOT_FIXED}"
200	fi
201	_missing="$(${AWK} -v start=$_start -F: '
202		BEGIN {
203			for (x = 1; x < ARGC; x++) {
204				if (ARGV[x] == "SKIP")
205					continue;
206				idlist[ARGV[x]]++;
207				value[ARGV[x]] = start + x - 1;
208			}
209			ARGC=1
210		}
211		{
212			found[$1]++
213			number[$1] = $3
214		}
215		END {
216			for (id in idlist) {
217				if (!(id in found))
218					printf("%s (missing)\n", id)
219				else if (number[id] != value[id])
220					printf("%s (%d != %d)\n", id,
221					    number[id], value[id])
222				start++;
223			}
224		}
225	' "$@" < "${_file}")"	|| return 1
226	if [ -n "${_missing}" ]; then
227		msg "Error ${_type}${_notfixed}:" $(echo ${_missing})
228		return 1
229	fi
230	return 0
231}
232
233# populate_dir op onlynew src dest mode file [file ...]
234#	Perform op ("check" or "fix") on files in src/ against dest/
235#	If op = "check" display missing or changed files, optionally with diffs.
236#	If op != "check" copies any missing or changed files.
237#	If onlynew evaluates to true, changed files are ignored.
238#	Returns 0 if ok, 1 otherwise.
239#
240populate_dir()
241{
242	[ $# -ge 5 ] || err 3 "USAGE: populate_dir op onlynew src dest mode file [...]"
243	_op="$1"
244	_onlynew="$2"
245	_src="$3"
246	_dest="$4"
247	_mode="$5"
248	shift 5
249	#_files="$@"
250
251	if [ ! -d "${_src}" ]; then
252		msg "${_src} is not a directory; skipping check"
253		return 1
254	fi
255	check_dir "${_op}" "${_dest}" 755 || return 1
256
257	_cmpdir_rv=0
258	for f in "$@"; do
259		fs="${_src}/${f}"
260		fd="${_dest}/${f}"
261		_error=""
262		if [ ! -f "${fd}" ]; then
263			_error="${fd} does not exist"
264		elif ! cmp -s "${fs}" "${fd}" ; then
265			if $_onlynew; then	# leave existing ${fd} alone
266				continue;
267			fi
268			_error="${fs} != ${fd}"
269		else
270			continue
271		fi
272		if [ "${_op}" = "check" ]; then
273			msg "${_error}"
274			if [ -n "${DIFF_STYLE}" -a -f "${fd}" ]; then
275				diff -${DIFF_STYLE} ${DIFF_OPT} "${fd}" "${fs}"
276			fi
277			_cmpdir_rv=1
278		elif ! rm -f "${fd}" ||
279		     ! cp -f "${fs}" "${fd}"; then
280			msg "Can't copy ${fs} to ${fd}"
281			_cmpdir_rv=1
282		elif ! chmod "${_mode}" "${fd}"; then
283			msg "Can't change mode of ${fd} to ${_mode}"
284			_cmpdir_rv=1
285		else
286			msg "Copied ${fs} to ${fd}"
287		fi
288	done
289	return ${_cmpdir_rv}
290}
291
292# compare_dir op src dest mode file [file ...]
293#	Perform op ("check" or "fix") on files in src/ against dest/
294#	If op = "check" display missing or changed files, optionally with diffs.
295#	If op != "check" copies any missing or changed files.
296#	Returns 0 if ok, 1 otherwise.
297#
298compare_dir()
299{
300	[ $# -ge 4 ] || err 3 "USAGE: compare_dir op src dest mode file [...]"
301	_op="$1"
302	_src="$2"
303	_dest="$3"
304	_mode="$4"
305	shift 4
306	#_files="$@"
307
308	populate_dir "$_op" false "$_src" "$_dest" "$_mode" "$@"
309}
310
311# move_file op src dest --
312#	Check (op == "check") or move (op != "check") from src to dest.
313#	Returns 0 if ok, 1 otherwise.
314#
315move_file()
316{
317	[ $# -eq 3 ] || err 3 "USAGE: move_file op src dest"
318	_fm_op="$1"
319	_fm_src="$2"
320	_fm_dest="$3"
321
322	if [ -f "${_fm_src}" -a ! -f "${_fm_dest}" ]; then
323		if [ "${_fm_op}" = "check" ]; then
324			msg "Move ${_fm_src} to ${_fm_dest}"
325			return 1
326		fi
327		if ! mv "${_fm_src}" "${_fm_dest}"; then
328			msg "Can't move ${_fm_src} to ${_fm_dest}"
329			return 1
330		fi
331		msg "Moved ${_fm_src} to ${_fm_dest}"
332	fi
333	return 0
334}
335
336# rcconf_is_set op name var [verbose] --
337#	Load the rcconf for name, and check if obsolete rc.conf(5) variable
338#	var is defined or not.
339#	Returns 0 if defined (even to ""), otherwise 1.
340#	If verbose != "", print an obsolete warning if the var is defined.
341#
342rcconf_is_set()
343{
344	[ $# -ge 3 ] || err 3 "USAGE: rcconf_is_set op name var [verbose]"
345	_rcis_op="$1"
346	_rcis_name="$2"
347	_rcis_var="$3"
348	_rcis_verbose="$4"
349	_rcis_notfixed=""
350	if [ "${_rcis_op}" = "fix" ]; then
351		_rcis_notfixed="${NOT_FIXED}"
352	fi
353	(
354		for f in \
355		    "${DEST_DIR}/etc/rc.conf" \
356		    "${DEST_DIR}/etc/rc.conf.d/${_rcis_name}"; do
357			[ -f "${f}" ] && . "${f}"
358		done
359		eval echo -n \"\${${_rcis_var}}\" 1>&3
360		if eval "[ -n \"\${${_rcis_var}}\" \
361			    -o \"\${${_rcis_var}-UNSET}\" != \"UNSET\" ]"; then
362			if [ -n "${_rcis_verbose}" ]; then
363				msg \
364    "Obsolete rc.conf(5) variable '\$${_rcis_var}' found.${_rcis_notfixed}"
365			fi
366			exit 0
367		else
368			exit 1
369		fi
370	)
371}
372
373# rcvar_is_enabled var
374#	Check if rcvar is enabled
375#
376rcvar_is_enabled()
377{
378	[ $# -eq 1 ] || err 3 "USAGE: rcvar_is_enabled var"
379	_rcie_var="$1"
380	(
381		[ -f "${DEST_DIR}/etc/rc.conf" ] && . "${DEST_DIR}/etc/rc.conf"
382		eval _rcie_val="\${${_rcie_var}}"
383		case $_rcie_val in
384		#	"yes", "true", "on", or "1"
385		[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
386			exit 0
387			;;
388
389		*)
390			exit 1
391			;;
392		esac
393	)
394}
395
396# find_file_in_dirlist() file message dir1 [...] --
397#	Find which directory file is in, and sets ${dir} to match.
398#	Returns 0 if matched, otherwise 1 (and sets ${dir} to "").
399#
400#	Generally, check the directory for the "checking from source" case,
401#	and then the directory for the "checking from extracted etc.tgz" case.
402#
403find_file_in_dirlist()
404{
405	[ $# -ge 3 ] || err 3 "USAGE: find_file_in_dirlist file msg dir1 [...]"
406
407	_file="$1" ; shift
408	_msg="$1" ; shift
409	_dir1st=	# first dir in list
410	for dir in "$@"; do
411		: ${_dir1st:="${dir}"}
412		if [ -f "${dir}/${_file}" ]; then
413			if [ "${_dir1st}" != "${dir}" ]; then
414				msg \
415    "(Checking for ${_msg} from ${dir} instead of ${_dir1st})"
416			fi
417			return 0
418		fi
419	done
420	msg "Can't find source directory for ${_msg}"
421	return 1
422}
423
424# file_exists_exact path
425#	Returns true if a file exists in the ${DEST_DIR} whose name
426#	is exactly ${path}, interpreted in a case-sensitive way
427#	even if the underlying file system is case-insensitive.
428#
429#	The path must begin with '/' or './', and is interpreted as
430#	being relative to ${DEST_DIR}.
431#
432file_exists_exact()
433{
434	[ -n "$1" ] || err 3 "USAGE: file_exists_exact path"
435	_path="${1#.}"
436	[ -h "${DEST_DIR}${_path}" ] || \
437        	[ -e "${DEST_DIR}${_path}" ] || return 1
438	while [ "${_path}" != "/" ] ; do
439		_dirname="$(dirname "${_path}" 2>/dev/null)"
440		_basename="$(basename "${_path}" 2>/dev/null)"
441		ls -fa "${DEST_DIR}${_dirname}" 2> /dev/null \
442			| ${GREP} -F -x "${_basename}" >/dev/null \
443			|| return 1
444		_path="${_dirname}"
445	done
446	return 0
447}
448
449# obsolete_paths op
450#	Obsolete the list of paths provided on stdin.
451#	Each path is relative to ${DEST_DIR}, and should
452#	be an absolute path or start with `./'.
453#
454obsolete_paths()
455{
456	[ -n "$1" ] || err 3 "USAGE: obsolete_paths  fix|check"
457	op="$1"
458
459	failed=0
460	while read ofile; do
461                if ! file_exists_exact "${ofile}"; then
462                    continue
463                fi
464		ofile="${DEST_DIR}${ofile#.}"
465		cmd="rm"
466		ftype="file"
467		if [ -h "${ofile}" ]; then
468			ftype="link"
469		elif [ -d "${ofile}" ]; then
470			ftype="directory"
471			cmd="rmdir"
472		fi
473		if [ "${op}" = "check" ]; then
474			msg "Remove obsolete ${ftype} ${ofile}"
475			failed=1
476		elif ! eval "${cmd} \${ofile}"; then
477			msg "Can't remove obsolete ${ftype} ${ofile}"
478			failed=1
479		else
480			msg "Removed obsolete ${ftype} ${ofile}"
481		fi
482	done
483	return ${failed}
484}
485
486# obsolete_libs dir
487#	Display the minor/teeny shared libraries in dir that are considered
488#	to be obsolete.
489#
490#	The implementation supports removing obsolete major libraries
491#	if the awk variable AllLibs is set, although there is no way to
492#	enable that in the enclosing shell function as this time.
493#
494obsolete_libs()
495{
496	[ $# -eq 1 ] || err 3 "USAGE: obsolete_libs dir"
497	dir="$1"
498
499	_obsolete_libs "${dir}"
500	_obsolete_libs "/usr/libdata/debug/${dir}"
501}
502
503_obsolete_libs()
504{
505	dir="$1"
506
507	(
508
509	if [ ! -e "${DEST_DIR}/${dir}" ]
510	then
511		return 0
512	fi
513
514	cd "${DEST_DIR}/${dir}" || err 2 "can't cd to ${DEST_DIR}/${dir}"
515	echo lib*.so.* \
516	| tr ' ' '\n' \
517	| ${AWK} -v LibDir="${dir}/" '
518#{
519
520function digit(v, c, n) { return (n <= c) ? v[n] : 0 }
521
522function checklib(results, line, regex) {
523	if (! match(line, regex))
524		return
525	lib = substr(line, RSTART, RLENGTH)
526	rev = substr($0, RLENGTH+1)
527	if (! (lib in results)) {
528		results[lib] = rev
529		return
530	}
531	orevc = split(results[lib], orev, ".")
532	nrevc = split(rev, nrev, ".")
533	maxc = (orevc > nrevc) ? orevc : nrevc
534	for (i = 1; i <= maxc; i++) {
535		res = digit(orev, orevc, i) - digit(nrev, nrevc, i)
536		if (res < 0) {
537			print LibDir lib results[lib]
538			results[lib] = rev
539			return
540		} else if (res > 0) {
541			print LibDir lib rev
542			return
543		}
544	}
545}
546
547/^lib.*\.so\.[0-9]+\.[0-9]+(\.[0-9]+)?(\.debug)?$/ {
548	if (AllLibs)
549		checklib(minor, $0, "^lib.*\\.so\\.")
550	else
551		checklib(found, $0, "^lib.*\\.so\\.[0-9]+\\.")
552}
553
554/^lib.*\.so\.[0-9]+$/ {
555	if (AllLibs)
556		checklib(major, $0, "^lib.*\\.so\\.")
557}
558
559#}'
560
561	)
562}
563
564# modify_file op srcfile scratchfile awkprog
565#	Apply awkprog to srcfile sending output to scratchfile, and
566#	if appropriate replace srcfile with scratchfile.
567#
568modify_file()
569{
570	[ $# -eq 4 ] || err 3 "USAGE: modify_file op file scratch awkprog"
571
572	_mfop="$1"
573	_mffile="$2"
574	_mfscratch="$3"
575	_mfprog="$4"
576	_mffailed=0
577
578	${AWK} "${_mfprog}" < "${_mffile}" > "${_mfscratch}"
579	if ! cmp -s "${_mffile}" "${_mfscratch}"; then
580		diff "${_mffile}" "${_mfscratch}" > "${_mfscratch}.diffs"
581		if [ "${_mfop}" = "check" ]; then
582			msg "${_mffile} needs the following changes:"
583			_mffailed=1
584		elif ! rm -f "${_mffile}" ||
585		     ! cp -f "${_mfscratch}" "${_mffile}"; then
586			msg "${_mffile} changes not applied:"
587			_mffailed=1
588		else
589			msg "${_mffile} changes applied:"
590		fi
591		while read _line; do
592			msg "	${_line}"
593		done < "${_mfscratch}.diffs"
594	fi
595	return ${_mffailed}
596}
597
598
599# contents_owner op directory user group
600#	Make sure directory and contents are owned (and group-owned)
601#	as specified.
602#
603contents_owner()
604{
605	[ $# -eq 4 ] || err 3 "USAGE: contents_owner op dir user group"
606
607	_op="$1"
608	_dir="$2"
609	_user="$3"
610	_grp="$4"
611
612	if [ "${_op}" = "check" ]; then
613		if [ ! -z "`find "${_dir}" \( ! -user "${_user}" \) -o \
614		    \( ! -group "${_grp}" \)`" ]; then
615			msg \
616    "${_dir} and contents not all owned by ${_user}:${_grp}"
617			return 1
618		else
619			return 0
620		fi
621	elif [ "${_op}" = "fix" ]; then
622		find "${_dir}" \( \( ! -user "${_user}" \) -o \
623		\( ! -group "${_grp}" \) \) -a -print0 \
624		| xargs -0 chown "${_user}:${_grp}"
625	fi
626}
627
628# get_makevar var [var ...]
629#	Retrieve the value of a user-settable system make variable
630get_makevar()
631{
632	$SOURCEMODE || err 3 "get_makevar must be used in source mode"
633	[ $# -eq 0 ] && err 3 "USAGE: get_makevar var [var ...]"
634
635	for _var in "$@"; do
636		_value="$(echo '.include <bsd.own.mk>' | \
637		    ${MAKE} -f - -V "${_var}")"
638
639		eval ${_var}=\"${_value}\"
640	done
641}
642
643# detect_x11
644#	Detect if X11 components should be analysed and set values of
645#	relevant variables.
646detect_x11()
647{
648	if $SOURCEMODE; then
649		get_makevar MKX11 X11ROOTDIR X11SRCDIR
650	else
651		if [ -f "${SRC_DIR}/etc/mtree/set.xetc" ]; then
652			MKX11=yes
653			X11ROOTDIR=/this/value/isnt/used/yet
654		else
655			MKX11=no
656			X11ROOTDIR=
657		fi
658		X11SRCDIR=/nonexistent/xsrc
659	fi
660}
661
662#
663#	items
664#	-----
665#
666
667#
668#	Bluetooth
669#
670
671additem bluetooth "Bluetooth configuration is up to date"
672do_bluetooth()
673{
674	[ -n "$1" ] || err 3 "USAGE: do_bluetooth fix|check"
675	op="$1"
676	failed=0
677
678	populate_dir "${op}" true \
679		"${SRC_DIR}/etc/bluetooth" "${DEST_DIR}/etc/bluetooth" 644 \
680		hosts protocols btattach.conf btdevctl.conf
681	failed=$(( ${failed} + $? ))
682
683	move_file "${op}" "${DEST_DIR}/var/db/btdev.xml" \
684			"${DEST_DIR}/var/db/btdevctl.plist"
685	failed=$(( ${failed} + $? ))
686
687	notfixed=""
688	if [ "${op}" = "fix" ]; then
689		notfixed="${NOT_FIXED}"
690	fi
691	for _v in btattach btconfig btdevctl; do
692		if rcvar_is_enabled "${_v}"; then
693			msg \
694    "${_v} is obsolete in rc.conf(5)${notfixed}: use bluetooth=YES"
695			failed=$(( ${failed} + 1 ))
696		fi
697	done
698
699	return ${failed}
700}
701
702#
703#	ddbonpanic
704#
705additem ddbonpanic "verify ddb.onpanic is configured in sysctl.conf"
706do_ddbonpanic()
707{
708	[ -n "$1" ] || err 3 "USAGE: do_ddbonpanic  fix|check"
709
710	if ${GREP} -E '^#*[[:space:]]*ddb\.onpanic[[:space:]]*\??=[[:space:]]*[[:digit:]]+' \
711		"${DEST_DIR}/etc/sysctl.conf" >/dev/null 2>&1
712	then
713		result=0
714	else
715		if [ "$1" = check ]; then
716			msg \
717    "The ddb.onpanic behaviour is not explicitly specified in /etc/sysctl.conf"
718			result=1
719		else
720			echo >> "${DEST_DIR}/etc/sysctl.conf"
721			sed < "${SRC_DIR}/etc/sysctl.conf" \
722			   -e '/^ddb\.onpanic/q' | \
723			       sed -e '1,/^$/d' >> \
724			    "${DEST_DIR}/etc/sysctl.conf"
725			result=$?
726		fi
727	fi
728	return ${result}
729}
730
731#
732#	defaults
733#
734additem defaults "/etc/defaults/ being up to date"
735do_defaults()
736{
737	[ -n "$1" ] || err 3 "USAGE: do_defaults  fix|check"
738	op="$1"
739	failed=0
740
741	# Except for i386 and amd64, rc.conf(5) should be the same as the
742	# one obtained from a source directory
743	extra_scripts="rc.conf"
744	if [ "$MACHINE" = "i386" -o "$MACHINE" = "amd64" ]; then
745		if $SOURCEMODE; then
746			extra_scripts=	# clear
747
748			# Generate and compare the correct rc.conf(5) file
749			mkdir "${SCRATCHDIR}/defaults"
750
751			cat "${SRC_DIR}/etc/defaults/rc.conf" \
752			    "${SRC_DIR}/etc/etc.${MACHINE}/rc.conf.append" \
753			    > "${SCRATCHDIR}/defaults/rc.conf"
754
755			compare_dir "${op}" "${SCRATCHDIR}/defaults" \
756			    "${DEST_DIR}/etc/defaults" \
757			    444 \
758			    "rc.conf"
759			failed=$(( ${failed} + $? ))
760		fi
761	fi
762
763	compare_dir "$op" "${SRC_DIR}/etc/defaults" "${DEST_DIR}/etc/defaults" \
764		444 \
765		daily.conf monthly.conf pkgpath.conf security.conf \
766		weekly.conf ${extra_scripts}
767	failed=$(( ${failed} + $? ))
768
769	find_file_in_dirlist pf.boot.conf "pf.boot.conf" \
770	    "${SRC_DIR}/usr.sbin/pf/etc/defaults" "${SRC_DIR}/etc/defaults" \
771	    || return 1
772			# ${dir} is set by find_file_in_dirlist()
773	compare_dir "$op" "${dir}" "${DEST_DIR}/etc/defaults" 444 pf.boot.conf
774	failed=$(( ${failed} + $? ))
775
776	return ${failed}
777}
778
779#
780#	dhcpcd
781#
782additem dhcpcd "dhcpcd configuration is up to date"
783do_dhcpcd()
784{
785	[ -n "$1" ] || err 3 "USAGE: do_dhcpcd fix|check"
786	op="$1"
787	failed=0
788
789	find_file_in_dirlist dhcpcd.conf "dhcpcd.conf" \
790	    "${SRC_DIR}/external/bsd/dhcpcd/dist" "${SRC_DIR}/etc" || return 1
791			# ${dir} is set by find_file_in_dirlist()
792	populate_dir "$op" true "${dir}" "${DEST_DIR}/etc" 644 dhcpcd.conf
793	failed=$(( ${failed} + $? ))
794
795	return ${failed}
796}
797
798#
799#	envsys
800#
801additem envsys "envsys configuration is up to date"
802do_envsys()
803{
804	[ -n "$1" ] || err 3 "USAGE: do_envsys fix|check"
805	op="$1"
806	failed=0
807
808	populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
809		envsys.conf
810	failed=$(( ${failed} + $? ))
811
812	populate_dir "$op" true "${SRC_DIR}/etc/powerd/scripts" \
813		"${DEST_DIR}/etc/powerd/scripts" 555 sensor_battery \
814		sensor_drive sensor_fan sensor_indicator sensor_power \
815		sensor_resistance sensor_temperature sensor_voltage
816	failed=$(( ${failed} + $? ))
817
818	return ${failed}
819}
820
821#
822#	X11 fontconfig
823#
824additem fontconfig "X11 font configuration is up to date"
825do_fontconfig()
826{
827	[ -n "$1" ] || err 3 "USAGE: do_fontconfig fix|check"
828	op="$1"
829	failed=0
830
831	# First, check for updates we can handle.
832	if ! $SOURCEMODE; then
833		FONTCONFIG_DIR="${SRC_DIR}/etc/fonts/conf.avail"
834	else
835		FONTCONFIG_DIR="${XSRC_DIR}/external/mit/fontconfig/dist/conf.d"
836	fi
837
838	populate_dir "$op" false "${FONTCONFIG_DIR}" "${DEST_DIR}/etc/fonts/conf.avail" 444 \
839		10-autohint.conf \
840		10-no-sub-pixel.conf \
841		10-scale-bitmap-fonts.conf \
842		10-sub-pixel-bgr.conf \
843		10-sub-pixel-rgb.conf \
844		10-sub-pixel-vbgr.conf \
845		10-sub-pixel-vrgb.conf \
846		10-unhinted.conf \
847		11-lcdfilter-default.conf \
848		11-lcdfilter-legacy.conf \
849		11-lcdfilter-light.conf \
850		20-unhint-small-vera.conf \
851		25-unhint-nonlatin.conf \
852		30-metric-aliases.conf \
853		30-urw-aliases.conf \
854		40-nonlatin.conf \
855		45-latin.conf \
856		49-sansserif.conf \
857		50-user.conf \
858		51-local.conf \
859		60-latin.conf \
860		65-fonts-persian.conf \
861		65-khmer.conf \
862		65-nonlatin.conf \
863		69-unifont.conf \
864		70-no-bitmaps.conf \
865		70-yes-bitmaps.conf \
866		80-delicious.conf \
867		90-synthetic.conf
868	failed=$(( ${failed} + $? ))
869
870	# We can't modify conf.d easily; someone might have removed a file.
871
872	conf_d_failed=0
873	# Look for old files that need to be deleted.
874	if [ -f "${DEST_DIR}/etc/fonts/conf.d/10-unhinted.conf" -a \
875	     -f "${DEST_DIR}/etc/fonts/conf.d/10-autohint.conf" ]; then
876		conf_d_failed=1
877		failed=$(( ${failed} + 1 ))
878	fi
879
880	if [ "$conf_d_failed" = 1 ]; then
881		msg \
882    "Broken fontconfig configuration found; please delete these files"
883		msg \
884    "in the ${DESTDIR}/etc/fonts/conf.d/ subdirectory:"
885		msg \
886    "   10-autohint.conf 10-no-sub-pixel.conf 10-sub-pixel-bgr.conf"
887		msg \
888    "   10-sub-pixel-rgb.conf 10-sub-pixel-vbgr.conf"
889		msg \
890    "   10-sub-pixel-vrgb.conf 10-unhinted.conf 25-unhint-nonlatin.conf"
891		msg \
892    "   65-khmer.conf 70-no-bitmaps.conf 70-yes-bitmaps.conf"
893		msg \
894    "(This warning only appears if both the 10-unhinted.conf and"
895		msg \
896    "10-autohint.conf files are present."
897	fi
898
899	return ${failed}
900}
901
902#
903#	gid
904#
905additem gid "required groups in /etc/group"
906do_gid()
907{
908	[ -n "$1" ] || err 3 "USAGE: do_gid  fix|check"
909
910	check_ids "$1" groups "${DEST_DIR}/etc/group" 14 \
911	    named ntpd sshd SKIP _pflogd _rwhod staff _proxy _timedc \
912	    _sdpd _httpd _mdnsd _tests _tcpdump _tss _gpio _rtadvd
913}
914
915#
916#	gpio
917#
918additem gpio "gpio configuration is up to date"
919do_gpio()
920{
921	[ -n "$1" ] || err 3 "USAGE: do_gpio fix|check"
922	op="$1"
923	failed=0
924
925	populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
926		gpio.conf
927	failed=$(( ${failed} + $? ))
928
929	return ${failed}
930}
931
932#
933#	hosts
934#
935additem hosts "/etc/hosts being up to date"
936do_hosts()
937{
938	[ -n "$1" ] || err 3 "USAGE: do_hosts  fix|check"
939
940	modify_file "$1" "${DEST_DIR}/etc/hosts" "${SCRATCHDIR}/hosts" '
941		/^(127\.0\.0\.1|::1)[ 	]+[^\.]*$/ {
942			print $0, "localhost."
943			next
944		}
945		{ print }
946	'
947	return $?
948}
949
950#
951#	iscsi
952#
953additem iscsi "/etc/iscsi is populated"
954do_iscsi()
955{
956	[ -n "$1" ] || err 3 "USAGE: do_iscsi  fix|check"
957
958	populate_dir "${op}" true \
959	    "${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 600 auths
960	populate_dir "${op}" true \
961	    "${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 644 targets
962	return $?
963}
964
965#
966#	makedev
967#
968additem makedev "/dev/MAKEDEV being up to date"
969do_makedev()
970{
971	[ -n "$1" ] || err 3 "USAGE: do_makedev   fix|check"
972	failed=0
973
974	if [ -f "${SRC_DIR}/etc/MAKEDEV.tmpl" ]; then
975			# generate MAKEDEV from source if source is available
976		env MACHINE="${MACHINE}" \
977		    MACHINE_ARCH="${MACHINE_ARCH}" \
978		    NETBSDSRCDIR="${SRC_DIR}" \
979		    ${AWK} -f "${SRC_DIR}/etc/MAKEDEV.awk" \
980		    "${SRC_DIR}/etc/MAKEDEV.tmpl" > "${SCRATCHDIR}/MAKEDEV"
981	fi
982
983	find_file_in_dirlist MAKEDEV "MAKEDEV" \
984	    "${SCRATCHDIR}" "${SRC_DIR}/dev" \
985	    || return 1
986			# ${dir} is set by find_file_in_dirlist()
987	compare_dir "$1" "${dir}" "${DEST_DIR}/dev" 555 MAKEDEV
988	failed=$(( ${failed} + $? ))
989
990	find_file_in_dirlist MAKEDEV.local "MAKEDEV.local" \
991	    "${SRC_DIR}/etc" "${SRC_DIR}/dev" \
992	    || return 1
993			# ${dir} is set by find_file_in_dirlist()
994	compare_dir "$1" "${dir}" "${DEST_DIR}/dev" 555 MAKEDEV.local
995	failed=$(( ${failed} + $? ))
996
997	return ${failed}
998}
999
1000#
1001#	motd
1002#
1003additem motd "contents of motd"
1004do_motd()
1005{
1006	[ -n "$1" ] || err 3 "USAGE: do_motd  fix|check"
1007
1008	if ${GREP} -i 'http://www.NetBSD.org/Misc/send-pr.html' \
1009		"${DEST_DIR}/etc/motd" >/dev/null 2>&1 \
1010	    || ${GREP} -i 'http://www.NetBSD.org/support/send-pr.html' \
1011		"${DEST_DIR}/etc/motd" >/dev/null 2>&1
1012	then
1013		tmp1="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
1014		tmp2="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
1015		sed '1,2d' <"${SRC_DIR}/etc/motd" >"${tmp1}"
1016		sed '1,2d' <"${DEST_DIR}/etc/motd" >"${tmp2}"
1017
1018		if [ "$1" = check ]; then
1019			cmp -s "${tmp1}" "${tmp2}"
1020			result=$?
1021			if [ "${result}" -ne 0 ]; then
1022				msg \
1023    "Bug reporting messages do not seem to match the installed release"
1024			fi
1025		else
1026			head -n 2 "${DEST_DIR}/etc/motd" >"${tmp1}"
1027			sed '1,2d' <"${SRC_DIR}/etc/motd" >>"${tmp1}"
1028			cp "${tmp1}" "${DEST_DIR}/etc/motd"
1029			result=0
1030		fi
1031
1032		rm -f "${tmp1}" "${tmp2}"
1033	else
1034		result=0
1035	fi
1036
1037	return ${result}
1038}
1039
1040#
1041#	mtree
1042#
1043additem mtree "/etc/mtree/ being up to date"
1044do_mtree()
1045{
1046	[ -n "$1" ] || err 3 "USAGE: do_mtree  fix|check"
1047	failed=0
1048
1049	compare_dir "$1" "${SRC_DIR}/etc/mtree" "${DEST_DIR}/etc/mtree" 444 special
1050	failed=$(( ${failed} + $? ))
1051
1052	if ! $SOURCEMODE; then
1053		MTREE_DIR="${SRC_DIR}/etc/mtree"
1054	else
1055		${MAKE} -s -C "${SRC_DIR}/etc/mtree" emit_dist_file > \
1056		    "${SCRATCHDIR}/NetBSD.dist"
1057		MTREE_DIR="${SCRATCHDIR}"
1058	fi
1059	compare_dir "$1" "${MTREE_DIR}" "${DEST_DIR}/etc/mtree" 444 NetBSD.dist
1060	failed=$(( ${failed} + $? ))
1061
1062	return ${failed}
1063}
1064
1065#
1066#	named
1067#
1068additem named "named configuration update"
1069do_named()
1070{
1071	[ -n "$1" ] || err 3 "USAGE: do_named  fix|check"
1072	op="$1"
1073
1074	move_file "${op}" \
1075		"${DEST_DIR}/etc/namedb/named.conf" \
1076		"${DEST_DIR}/etc/named.conf"
1077
1078	compare_dir "${op}" "${SRC_DIR}/etc/namedb" "${DEST_DIR}/etc/namedb" \
1079		644 \
1080		root.cache
1081}
1082
1083#
1084#	pam
1085#
1086additem pam "/etc/pam.d is populated"
1087do_pam()
1088{
1089	[ -n "$1" ] || err 3 "USAGE: do_pam  fix|check"
1090	op="$1"
1091	failed=0
1092
1093	populate_dir "${op}" true "${SRC_DIR}/etc/pam.d" \
1094		"${DEST_DIR}/etc/pam.d" 644 \
1095		README display_manager ftpd gdm imap kde login other passwd \
1096		pop3 ppp rexecd rsh sshd su system telnetd xdm xserver
1097
1098	failed=$(( ${failed} + $? ))
1099
1100	return ${failed}
1101}
1102
1103#
1104#	periodic
1105#
1106additem periodic "/etc/{daily,weekly,monthly,security} being up to date"
1107do_periodic()
1108{
1109	[ -n "$1" ] || err 3 "USAGE: do_periodic  fix|check"
1110
1111	compare_dir "$1" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
1112		daily weekly monthly security
1113}
1114
1115#
1116#	pf
1117#
1118additem pf "pf configuration being up to date"
1119do_pf()
1120{
1121	[ -n "$1" ] || err 3 "USAGE: do_pf  fix|check"
1122	op="$1"
1123	failed=0
1124
1125	find_file_in_dirlist pf.os "pf.os" \
1126	    "${SRC_DIR}/dist/pf/etc" "${SRC_DIR}/etc" \
1127	    || return 1
1128			# ${dir} is set by find_file_in_dirlist()
1129	populate_dir "${op}" true \
1130	    "${dir}" "${DEST_DIR}/etc" 644 \
1131	    pf.conf
1132	failed=$(( ${failed} + $? ))
1133
1134	compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 pf.os
1135	failed=$(( ${failed} + $? ))
1136
1137	return ${failed}
1138}
1139
1140#
1141#	pwd_mkdb
1142#
1143additem pwd_mkdb "passwd database version"
1144do_pwd_mkdb()
1145{
1146	[ -n "$1" ] || err 3 "USAGE: do_pwd_mkdb  fix|check"
1147	op="$1"
1148	failed=0
1149
1150	# XXX Ideally, we should figure out the endianness of the
1151	# target machine, and add "-E B"/"-E L" to the db(1) flags,
1152	# and "-B"/"-L" to the pwd_mkdb(8) flags if the target is not
1153	# the same as the host machine.  It probably doesn't matter,
1154	# because we don't expect "postinstall fix pwd_mkdb" to be
1155	# invoked during a cross build.
1156
1157	set -- $(${DB} -q -Sb -Ub -To -N hash "${DEST_DIR}/etc/pwd.db" \
1158		'VERSION\0')
1159	case "$2" in
1160	'\001\000\000\000') return 0 ;; # version 1, little-endian
1161	'\000\000\000\001') return 0 ;; # version 1, big-endian
1162	esac
1163
1164	if [ "${op}" = "check" ]; then
1165		msg "Update format of passwd database"
1166		failed=1
1167	elif ! ${PWD_MKDB} -V 1 -d "${DEST_DIR:-/}" \
1168			"${DEST_DIR}/etc/master.passwd";
1169	then
1170		msg "Can't update format of passwd database"
1171		failed=1
1172	else
1173		msg "Updated format of passwd database"
1174	fi
1175
1176	return ${failed}
1177}
1178
1179#
1180#	rc
1181#
1182additem rc "/etc/rc* and /etc/rc.d/ being up to date"
1183do_rc()
1184{
1185	[ -n "$1" ] || err 3 "USAGE: do_rc  fix|check"
1186	op="$1"
1187	failed=0
1188	generated_scripts=""
1189	if [ "${MKX11}" != "no" ]; then
1190		generated_scripts="${generated_scripts} xdm xfs"
1191	fi
1192
1193	compare_dir "${op}" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
1194		rc rc.subr rc.shutdown
1195	failed=$(( ${failed} + $? ))
1196
1197	if ! $SOURCEMODE; then
1198		extra_scripts="${generated_scripts}"
1199	else
1200		extra_scripts=""
1201	fi
1202
1203	compare_dir "${op}" "${SRC_DIR}/etc/rc.d" "${DEST_DIR}/etc/rc.d" 555 \
1204		DAEMON DISKS LOGIN NETWORKING SERVERS \
1205		accounting altqd amd apmd \
1206		bluetooth bootconf.sh bootparams \
1207		ccd cgd cleartmp cron devpubd \
1208		dhclient dhcpcd dhcpd dhcrelay dmesg downinterfaces envsys \
1209		fsck fsck_root ftp_proxy ftpd \
1210		gpio \
1211		hostapd httpd \
1212		identd ifwatchd inetd ipfilter ipfs ipmon ipnat ipsec \
1213		irdaattach iscsi_target isdnd isibootd \
1214		kdc \
1215		ldconfig ldpd local lpd lvm \
1216		makemandb mdnsd mixerctl mopd motd mountall mountcritlocal \
1217		mountcritremote mountd moused mrouted \
1218		named ndbootd network newsyslog nfsd nfslocking npf ntpd \
1219		ntpdate \
1220		perusertmp pf pf_boot pflogd postfix powerd ppp pwcheck \
1221		quota \
1222		racoon rpcbind raidframe raidframeparity random_seed rarpd \
1223		rbootd rndctl root route6d routed rtadvd rtclocaltime \
1224		rtsold rwho \
1225		savecore screenblank securelevel sshd \
1226		staticroute swap1 swap2 sysctl sysdb syslogd \
1227		timed tpctl ttys \
1228		veriexec virecover wdogctl wpa_supplicant wscons wsmoused \
1229		ypbind yppasswdd ypserv \
1230		${extra_scripts}
1231	failed=$(( ${failed} + $? ))
1232
1233	if $SOURCEMODE && [ -n "${generated_scripts}" ]; then
1234		# generate scripts
1235		mkdir "${SCRATCHDIR}/rc"
1236		for f in ${generated_scripts}; do
1237			sed -e "s,@X11ROOTDIR@,${X11ROOTDIR},g" \
1238			    < "${SRC_DIR}/etc/rc.d/${f}.in" \
1239			    > "${SCRATCHDIR}/rc/${f}"
1240		done
1241		compare_dir "${op}" "${SCRATCHDIR}/rc" \
1242		    "${DEST_DIR}/etc/rc.d" 555 \
1243		    ${generated_scripts}
1244		failed=$(( ${failed} + $? ))
1245	fi
1246
1247		# check for obsolete rc.d files
1248	for f in NETWORK btattach btconfig btcontrol btdevctl bthcid btuartd \
1249	    fsck.sh kerberos nfsiod sdpd servers \
1250	    systemfs daemon gated login poffd portmap sunndd xntpd; do
1251		fd="/etc/rc.d/${f}"
1252		[ -e "${DEST_DIR}${fd}" ] && echo "${fd}"
1253	done | obsolete_paths "${op}"
1254	failed=$(( ${failed} + $? ))
1255
1256		# check for obsolete rc.conf(5) variables
1257	set --	amd amd_master \
1258		btcontrol btcontrol_devices \
1259		critical_filesystems critical_filesystems_beforenet \
1260		mountcritlocal mountcritremote \
1261		network ip6forwarding \
1262		network nfsiod_flags \
1263		sdpd sdpd_control \
1264		sdpd sdpd_groupname \
1265		sdpd sdpd_username \
1266		sysctl defcorename
1267	while [ $# -gt 1 ]; do
1268		if rcconf_is_set "${op}" "$1" "$2" 1; then
1269			failed=1
1270		fi
1271		shift 2
1272	done
1273
1274	return ${failed}
1275}
1276
1277#
1278#	sendmail
1279#
1280adddisableditem sendmail "remove obsolete sendmail configuration files and scripts"
1281do_sendmail()
1282{
1283	[ -n "$1" ] || err 3 "USAGE: do_sendmail  fix|check"
1284	op="$1"
1285	failed=0
1286
1287	# Don't complain if the "sendmail" package is installed because the
1288	# files might still be in use.
1289	if /usr/sbin/pkg_info -qe sendmail >/dev/null 2>&1; then
1290		return 0
1291	fi
1292
1293	for f in /etc/mail/helpfile /etc/mail/local-host-names \
1294	    /etc/mail/sendmail.cf /etc/mail/submit.cf /etc/rc.d/sendmail \
1295	    /etc/rc.d/smmsp /usr/share/misc/sendmail.hf \
1296	    $(find "${DEST_DIR}/usr/share/sendmail" -type f) \
1297	    $(find "${DEST_DIR}/usr/share/sendmail" -type d) \
1298	    "${DEST_DIR}/var/log/sendmail.st" \
1299	    "${DEST_DIR}/var/spool/clientmqueue" \
1300	    "${DEST_DIR}/var/spool/mqueue"; do
1301		[ -e "${DEST_DIR}${f}" ] && echo "${f}"
1302	done | obsolete_paths "${op}"
1303	failed=$(( ${failed} + $? ))
1304
1305	return ${failed}
1306}
1307
1308#
1309#	mailerconf
1310#
1311adddisableditem mailerconf "update /etc/mailer.conf after sendmail removal"
1312do_mailerconf()
1313{
1314	[ -n "$1" ] || err 3 "USAGE: do_mailterconf  fix|check"
1315	op="$1"
1316
1317	failed=0
1318	mta_path="$(${AWK} '/^sendmail[ \t]/{print$2}' \
1319		"${DEST_DIR}/etc/mailer.conf")"
1320	old_sendmail_path="/usr/libexec/sendmail/sendmail"
1321	if [ "${mta_path}" = "${old_sendmail_path}" ]; then
1322	    if [ "$op" = check ]; then
1323		msg "mailer.conf points to obsolete ${old_sendmail_path}"
1324		failed=1;
1325	    else
1326		populate_dir "${op}" false \
1327		"${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 mailer.conf
1328		failed=$?
1329	    fi
1330	fi
1331
1332	return ${failed}
1333}
1334
1335#
1336#	ssh
1337#
1338additem ssh "ssh configuration update"
1339do_ssh()
1340{
1341	[ -n "$1" ] || err 3 "USAGE: do_ssh  fix|check"
1342	op="$1"
1343
1344	failed=0
1345	_etcssh="${DEST_DIR}/etc/ssh"
1346	if ! check_dir "${op}" "${_etcssh}" 755; then
1347		failed=1
1348	fi
1349
1350	if [ ${failed} -eq 0 ]; then
1351		for f in \
1352			    ssh_known_hosts ssh_known_hosts2 \
1353			    ssh_host_dsa_key ssh_host_dsa_key.pub \
1354			    ssh_host_rsa_key ssh_host_rsa_key.pub \
1355			    ssh_host_key ssh_host_key.pub \
1356		    ; do
1357			if ! move_file "${op}" \
1358			    "${DEST_DIR}/etc/${f}" "${_etcssh}/${f}" ; then
1359				failed=1
1360			fi
1361		done
1362		for f in sshd.conf ssh.conf ; do
1363				# /etc/ssh/ssh{,d}.conf -> ssh{,d}_config
1364				#
1365			if ! move_file "${op}" \
1366			    "${_etcssh}/${f}" "${_etcssh}/${f%.conf}_config" ;
1367			then
1368				failed=1
1369			fi
1370				# /etc/ssh{,d}.conf -> /etc/ssh/ssh{,d}_config
1371				#
1372			if ! move_file "${op}" \
1373			    "${DEST_DIR}/etc/${f}" \
1374			    "${_etcssh}/${f%.conf}_config" ;
1375			then
1376				failed=1
1377			fi
1378		done
1379	fi
1380
1381	sshdconf=""
1382	for f in \
1383	    "${_etcssh}/sshd_config" \
1384	    "${_etcssh}/sshd.conf" \
1385	    "${DEST_DIR}/etc/sshd.conf" ; do
1386		if [ -f "${f}" ]; then
1387			sshdconf="${f}"
1388			break
1389		fi
1390	done
1391	if [ -n "${sshdconf}" ]; then
1392		modify_file "${op}" "${sshdconf}" "${SCRATCHDIR}/sshdconf" '
1393			/^[^#$]/ {
1394				kw = tolower($1)
1395				if (kw == "hostkey" &&
1396				    $2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ ) {
1397					sub(/\/etc\/+/, "/etc/ssh/")
1398				}
1399				if (kw == "rhostsauthentication" ||
1400				    kw == "verifyreversemapping" ||
1401				    kw == "reversemappingcheck") {
1402					sub(/^/, "# DEPRECATED:\t")
1403				}
1404			}
1405			{ print }
1406		'
1407		failed=$(( ${failed} + $? ))
1408	fi
1409
1410	if ! find_file_in_dirlist moduli "moduli" \
1411	    "${SRC_DIR}/crypto/external/bsd/openssh/dist" "${SRC_DIR}/etc" ; then
1412		failed=1
1413			# ${dir} is set by find_file_in_dirlist()
1414	elif ! compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 moduli; then
1415		failed=1
1416	fi
1417
1418	if ! check_dir "${op}" "${DEST_DIR}/var/chroot/sshd" 755 ; then
1419		failed=1
1420	fi
1421
1422	if rcconf_is_set "${op}" sshd sshd_conf_dir 1; then
1423		failed=1
1424	fi
1425
1426	return ${failed}
1427}
1428
1429#
1430#	wscons
1431#
1432additem wscons "wscons configuration file update"
1433do_wscons()
1434{
1435	[ -n "$1" ] || err 3 "USAGE: do_wscons  fix|check"
1436	op="$1"
1437
1438	[ -f "${DEST_DIR}/etc/wscons.conf" ] || return 0
1439
1440	failed=0
1441	notfixed=""
1442	if [ "${op}" = "fix" ]; then
1443		notfixed="${NOT_FIXED}"
1444	fi
1445	while read _type _arg1 _rest; do
1446		if [ "${_type}" = "mux" -a "${_arg1}" = "1" ]; then
1447			msg \
1448    "Obsolete wscons.conf(5) entry \""${_type} ${_arg1}"\" found.${notfixed}"
1449			failed=1
1450		fi
1451	done < "${DEST_DIR}/etc/wscons.conf"
1452
1453	return ${failed}
1454}
1455
1456#
1457#	X11
1458#
1459additem x11 "x11 configuration update"
1460do_x11()
1461{
1462	[ -n "$1" ] || err 3 "USAGE: do_x11  fix|check"
1463	op="$1"
1464
1465	failed=0
1466	_etcx11="${DEST_DIR}/etc/X11"
1467	if [ ! -d "${_etcx11}" ]; then
1468		msg "${_etcx11} is not a directory; skipping check"
1469		return 0
1470	fi
1471	if [ -d "${DEST_DIR}/usr/X11R6/." ]
1472	then
1473		_libx11="${DEST_DIR}/usr/X11R6/lib/X11"
1474		if [ ! -d "${_libx11}" ]; then
1475			msg "${_libx11} is not a directory; skipping check"
1476			return 0
1477		fi
1478	fi
1479
1480	_notfixed=""
1481	if [ "${op}" = "fix" ]; then
1482		_notfixed="${NOT_FIXED}"
1483	fi
1484
1485	for d in \
1486		    fs lbxproxy proxymngr rstart twm xdm xinit xserver xsm \
1487	    ; do
1488		sd="${_libx11}/${d}"
1489		ld="/etc/X11/${d}"
1490		td="${DEST_DIR}${ld}"
1491		if [ -h "${sd}" ]; then
1492			continue
1493		elif [ -d "${sd}" ]; then
1494			tdfiles="$(find "${td}" \! -type d)"
1495			if [ -n "${tdfiles}" ]; then
1496				msg "${sd} exists yet ${td} already" \
1497				    "contains files${_notfixed}"
1498			else
1499				msg "Migrate ${sd} to ${td}${_notfixed}"
1500			fi
1501			failed=1
1502		elif [ -e "${sd}" ]; then
1503			msg "Unexpected file ${sd}${_notfixed}"
1504			continue
1505		else
1506			continue
1507		fi
1508	done
1509
1510	return ${failed}
1511}
1512
1513#
1514#	xkb
1515#
1516# /usr/X11R7/lib/X11/xkb/symbols/pc used to be a directory, but changed
1517# to a file on 2009-06-12.  Fixing this requires removing the directory
1518# (which we can do) and re-extracting the xbase set (which we can't do),
1519# or at least adding that one file (which we may be able to do if X11SRCDIR
1520# is available).
1521#
1522additem xkb "clean up for xkbdata to xkeyboard-config upgrade"
1523do_xkb()
1524{
1525	[ -n "$1" ] || err 3 "USAGE: do_xkb  fix|check"
1526	op="$1"
1527	failed=0
1528
1529	pcpath="/usr/X11R7/lib/X11/xkb/symbols/pc"
1530	pcsrcdir="${X11SRCDIR}/external/mit/xkeyboard-config/dist/symbols"
1531
1532	filemsg="\
1533${pcpath} was a directory, should be a file.
1534    To fix, extract the xbase set again."
1535
1536	_notfixed=""
1537	if [ "${op}" = "fix" ]; then
1538		_notfixed="${NOT_FIXED}"
1539	fi
1540
1541	if [ ! -d "${DESTDIR}${pcpath}" ]; then
1542		return 0
1543	fi
1544
1545	# Delete obsolete files in the directory, and the directory
1546	# itself.  If the directory contains unexpected extra files
1547	# then it will not be deleted.
1548	( [ -f "${DEST_DIR}"/var/db/obsolete/xbase ] \
1549	    &&  sort -ru "${DEST_DIR}"/var/db/obsolete/xbase \
1550	    | ${GREP} -E "^\\.?${pcpath}/" ;
1551	    echo "${pcpath}" ) \
1552	| obsolete_paths "${op}"
1553	failed=$(( ${failed} + $? ))
1554
1555	# If the directory was removed above, then try to replace it with
1556	# a file.
1557	if [ -d "${DESTDIR}${pcpath}" ]; then
1558		msg "${filemsg}${_notfixed}"
1559		failed=$(( ${failed} + 1 ))
1560	else
1561		if ! find_file_in_dirlist pc "${pcpath}" \
1562			"${pcsrcdir}" "${SRC_DIR}${pcpath%/*}"
1563		then
1564			msg "${filemsg}${_notfixed}"
1565			failed=$(( ${failed} + 1 ))
1566		else
1567			# ${dir} is set by find_file_in_dirlist()
1568			populate_dir "${op}" true \
1569				"${dir}" "${DEST_DIR}${pcpath%/*}" 444 \
1570				pc
1571			failed=$(( ${failed} + $? ))
1572		fi
1573	fi
1574
1575	return $failed
1576}
1577
1578#
1579#	uid
1580#
1581additem uid "required users in /etc/master.passwd"
1582do_uid()
1583{
1584	[ -n "$1" ] || err 3 "USAGE: do_uid  fix|check"
1585
1586	check_ids "$1" users "${DEST_DIR}/etc/master.passwd" 12 \
1587	    postfix SKIP named ntpd sshd SKIP _pflogd _rwhod SKIP _proxy \
1588	    _timedc _sdpd _httpd _mdnsd _tests _tcpdump _tss SKIP _rtadvd
1589}
1590
1591
1592#
1593#	varrwho
1594#
1595additem varrwho "required ownership of files in /var/rwho"
1596do_varrwho()
1597{
1598	[ -n "$1" ] || err 3 "USAGE: do_varrwho  fix|check"
1599
1600	contents_owner "$1" "${DEST_DIR}/var/rwho" _rwhod _rwhod
1601}
1602
1603
1604#
1605#	tcpdumpchroot
1606#
1607additem tcpdumpchroot "remove /var/chroot/tcpdump/etc/protocols"
1608do_tcpdumpchroot()
1609{
1610	[ -n "$1" ] || err 3 "USAGE: do_tcpdumpchroot  fix|check"
1611
1612	failed=0;
1613	if [ -r "${DEST_DIR}/var/chroot/tcpdump/etc/protocols" ]; then
1614		if [ "$1" = "fix" ]; then
1615			rm "${DEST_DIR}/var/chroot/tcpdump/etc/protocols"
1616			failed=$(( ${failed} + $? ))
1617			rmdir "${DEST_DIR}/var/chroot/tcpdump/etc"
1618			failed=$(( ${failed} + $? ))
1619		else
1620			failed=1
1621		fi
1622	fi
1623	return ${failed}
1624}
1625
1626
1627#
1628#	atf
1629#
1630additem atf "install missing atf configuration files and validate them"
1631do_atf()
1632{
1633	[ -n "$1" ] || err 3 "USAGE: do_atf  fix|check"
1634	op="$1"
1635	failed=0
1636
1637	# Ensure atf configuration files are in place.
1638	if find_file_in_dirlist NetBSD.conf "NetBSD.conf" \
1639	    "${SRC_DIR}/external/bsd/atf/etc/atf" \
1640	    "${SRC_DIR}/etc/atf"; then
1641			# ${dir} is set by find_file_in_dirlist()
1642		populate_dir "${op}" true "${dir}" "${DEST_DIR}/etc/atf" 644 \
1643		    NetBSD.conf common.conf || failed=1
1644	else
1645		failed=1
1646	fi
1647	if find_file_in_dirlist atf-run.hooks "atf-run.hooks" \
1648	    "${SRC_DIR}/external/bsd/atf/dist/atf-run/sample" \
1649	    "${SRC_DIR}/etc/atf"; then
1650			# ${dir} is set by find_file_in_dirlist()
1651		populate_dir "${op}" true "${dir}" "${DEST_DIR}/etc/atf" 644 \
1652		    atf-run.hooks || failed=1
1653	else
1654		failed=1
1655	fi
1656
1657	# Validate the _atf to _tests user/group renaming.
1658	if [ -f "${DEST_DIR}/etc/atf/common.conf" ]; then
1659		handle_atf_user "${op}" || failed=1
1660	else
1661		failed=1
1662	fi
1663
1664	return ${failed}
1665}
1666
1667handle_atf_user()
1668{
1669	local op="$1"
1670	local failed=0
1671
1672	local conf="${DEST_DIR}/etc/atf/common.conf"
1673	if grep '[^#]*unprivileged-user[ \t]*=.*_atf' "${conf}" >/dev/null
1674	then
1675		if [ "$1" = "fix" ]; then
1676			sed -e \
1677			    "/[^#]*unprivileged-user[\ t]*=/s/_atf/_tests/" \
1678			    "${conf}" >"${conf}.new"
1679			failed=$(( ${failed} + $? ))
1680			mv "${conf}.new" "${conf}"
1681			failed=$(( ${failed} + $? ))
1682			msg "Set unprivileged-user=_tests in ${conf}"
1683		else
1684			msg "unprivileged-user=_atf in ${conf} should be" \
1685			    "unprivileged-user=_tests"
1686			failed=1
1687		fi
1688	fi
1689
1690	return ${failed}
1691}
1692
1693#
1694#	catpages
1695#
1696obsolete_catpages()
1697{
1698	basedir="$2"
1699	section="$3"
1700	mandir="${basedir}/man${section}"
1701	catdir="${basedir}/cat${section}"
1702	test -d "$mandir" || return 0
1703	test -d "$catdir" || return 0
1704	(cd "$mandir" && find . -type f) | {
1705	failed=0
1706	while read manpage; do
1707		manpage="${manpage#./}"
1708		case "$manpage" in
1709		*.Z)
1710			catname="$catdir/${manpage%.*.Z}.0"
1711			;;
1712		*.gz)
1713			catname="$catdir/${manpage%.*.gz}.0"
1714			;;
1715		*)
1716			catname="$catdir/${manpage%.*}.0"
1717			;;
1718		esac
1719		test -e "$catname" -a "$catname" -ot "$mandir/$manpage" || continue
1720		if [ "$1" = "fix" ]; then
1721			rm "$catname"
1722			failed=$(( ${failed} + $? ))
1723			msg "Removed obsolete cat page $catname"
1724		else
1725			msg "Obsolete cat page $catname"
1726			failed=1
1727		fi
1728	done
1729	exit $failed
1730	}
1731}
1732
1733additem catpages "remove outdated cat pages"
1734do_catpages()
1735{
1736	failed=0
1737	for manbase in /usr/share/man /usr/X11R6/man /usr/X11R7/man; do
1738		for sec in 1 2 3 4 5 6 7 8 9; do
1739			obsolete_catpages "$1" "${DEST_DIR}${manbase}" "${sec}"
1740			failed=$(( ${failed} + $? ))
1741			if [ "$1" = "fix" ]; then
1742				rmdir "${DEST_DIR}${manbase}/cat${sec}"/* \
1743					2>/dev/null
1744				rmdir "${DEST_DIR}${manbase}/cat${sec}" \
1745					2>/dev/null
1746			fi
1747		done
1748	done
1749	return $failed
1750}
1751
1752#
1753#	obsolete
1754#	(this item is last to allow other items to move obsolete files)
1755#
1756additem obsolete "remove obsolete file sets and minor libraries"
1757do_obsolete()
1758{
1759	[ -n "$1" ] || err 3 "USAGE: do_obsolete  fix|check"
1760	op="$1"
1761	failed=0
1762
1763	sort -ru "${DEST_DIR}"/var/db/obsolete/* | obsolete_paths "${op}"
1764	failed=$(( ${failed} + $? ))
1765
1766	(
1767		obsolete_libs /lib
1768		obsolete_libs /usr/lib
1769		obsolete_libs /usr/lib/i18n
1770		obsolete_libs /usr/X11R6/lib
1771		obsolete_libs /usr/X11R7/lib
1772		[ "$MACHINE" = "amd64" ] && obsolete_libs /usr/lib/i386
1773		[ "$MACHINE" = "sparc64" ] && obsolete_libs /usr/lib/sparc
1774	) | obsolete_paths "${op}"
1775	failed=$(( ${failed} + $? ))
1776
1777	return ${failed}
1778}
1779
1780
1781#
1782#	ptyfsoldnodes
1783#
1784additem ptyfsoldnodes "remove legacy device nodes when using ptyfs"
1785do_ptyfsoldnodes()
1786{
1787	[ -n "$1" ] || err 3 "USAGE: do_ptyfsoldnodes  fix|check"
1788	_ptyfs_op="$1"
1789
1790	# Check whether ptyfs is in use
1791	failed=0;
1792	if ! ${GREP} -E "^ptyfs" "${DEST_DIR}/etc/fstab" > /dev/null; then
1793		msg "ptyfs is not in use"
1794		return 0
1795	fi
1796
1797	# Find the device major numbers for the pty master and slave
1798	# devices, by parsing the output from "MAKEDEV -s pty0".
1799	#
1800	# Output from MAKEDEV looks like this:
1801	# ./ttyp0 type=char device=netbsd,5,0 mode=666 gid=0 uid=0
1802	# ./ptyp0 type=char device=netbsd,6,0 mode=666 gid=0 uid=0
1803	#
1804	# Output from awk, used in the eval statement, looks like this:
1805	# maj_ptym=6; maj_ptys=5;
1806	#
1807	eval "$(
1808	    ${HOST_SH} "${DEST_DIR}/dev/MAKEDEV" -s pty0 2>/dev/null \
1809	    | ${AWK} '\
1810	    BEGIN { before_re = ".*device=[a-zA-Z]*,"; after_re = ",.*"; }
1811	    /ptyp0/ { maj_ptym = gensub(before_re, "", 1, $0);
1812		      maj_ptym = gensub(after_re, "", 1, maj_ptym); }
1813	    /ttyp0/ { maj_ptys = gensub(before_re, "", 1, $0);
1814		      maj_ptys = gensub(after_re, "", 1, maj_ptys); }
1815	    END { print "maj_ptym=" maj_ptym "; maj_ptys=" maj_ptys ";"; }
1816	    '
1817	    )"
1818	#msg "Major numbers are maj_ptym=${maj_ptym} maj_ptys=${maj_ptys}"
1819	if [ -z "$maj_ptym" ] || [ -z "$maj_ptys" ]; then
1820		msg "Cannot find device major numbers for pty master and slave"
1821		return 1
1822	fi
1823
1824	# look for /dev/[pt]ty[p-zP-T][0-9a-zA-Z], and check that they
1825	# have the expected device major numbers.  ttyv* is typically not a
1826	# pty device, but we check it anyway.
1827	#
1828	# The "for d1" loop is intended to avoid overflowing ARG_MAX;
1829	# otherwise we could have used a single glob pattern.
1830	#
1831	# If there are no files that match a particular pattern,
1832	# then stat prints something like:
1833	#    stat: /dev/[pt]tyx?: lstat: No such file or directory
1834	# and we ignore it.  XXX: We also ignore other error messages.
1835	#
1836	_ptyfs_tmp="$(mktemp /tmp/postinstall.ptyfs.XXXXXXXX)"
1837	for d1 in p q r s t u v w x y z P Q R S T; do
1838		${STAT} -f "%Hr %N" "${DEST_DIR}/dev/"[pt]ty${d1}? 2>&1
1839	done \
1840	| while read -r major node ; do
1841		case "$major" in
1842		${maj_ptym}|${maj_ptys}) echo "$node" ;;
1843		esac
1844	done >"${_ptyfs_tmp}"
1845
1846	_desc="legacy device node"
1847	while read node; do
1848		if [ "${_ptyfs_op}" = "check" ]; then
1849			msg "Remove ${_desc} ${node}"
1850			failed=1
1851		else # "fix"
1852			if rm "${node}"; then
1853				msg "Removed ${_desc} ${node}"
1854			else
1855				warn "Failed to remove ${_desc} ${node}"
1856				failed=1
1857			fi
1858		fi
1859	done < "${_ptyfs_tmp}"
1860	rm "${_ptyfs_tmp}"
1861
1862	return ${failed}
1863}
1864
1865
1866#
1867#	end of items
1868#	------------
1869#
1870
1871
1872usage()
1873{
1874	cat 1>&2 << _USAGE_
1875Usage: ${PROGNAME} [-s srcdir] [-x xsrcdir] [-d destdir] [-m mach] [-a arch] op [item [...]]
1876	Perform post-installation checks and/or fixes on a system's
1877	configuration files.
1878	If no items are provided, a default set of checks or fixes is applied.
1879
1880	Options:
1881	-s {srcdir|tgzfile|tempdir}
1882			Location of the source files.  This may be any
1883			of the following:
1884			* A directory that contains a NetBSD source tree;
1885			* A distribution set file such as "etc.tgz" or
1886			  "xetc.tgz".  Pass multiple -s options to specify
1887                          multiple such files;
1888			* A temporary directory in which one or both of
1889			  "etc.tgz" and "xetc.tgz" have been extracted.
1890							[${SRC_DIR:-/usr/src}]
1891	-x xsrcdir      Location of the X11 source files.  This must be
1892			a directory that contains a NetBSD xsrc tree.
1893							[${XSRC_DIR:-/usr/src/../xsrc}]
1894	-d destdir	Destination directory to check. [${DEST_DIR:-/}]
1895	-m mach		MACHINE.			[${MACHINE}]
1896	-a arch		MACHINE_ARCH.			[${MACHINE_ARCH}]
1897
1898	Operation may be one of:
1899		help	Display this help.
1900		list	List available items.
1901		check	Perform post-installation checks on items.
1902		diff [diff(1) options ...]
1903			Similar to 'check' but also output difference of files.
1904		fix	Apply fixes that 'check' determines need to be applied.
1905		usage	Display this usage.
1906_USAGE_
1907	exit 2
1908}
1909
1910
1911list()
1912{
1913	echo "Default set of items (to apply if no items are provided by user):"
1914	echo "  Item          Description"
1915	echo "  ----          -----------"
1916	for i in ${defaultitems}; do
1917		eval desc=\"\${desc_${i}}\"
1918		printf "  %-12s  %s\n" "${i}" "${desc}"
1919	done
1920	echo "Items disabled by default (must be requested explicitly):"
1921	echo "  Item          Description"
1922	echo "  ----          -----------"
1923	for i in ${otheritems}; do
1924		eval desc=\"\${desc_${i}}\"
1925		printf "  %-12s  %s\n" "${i}" "${desc}"
1926	done
1927
1928}
1929
1930
1931main()
1932{
1933	TGZLIST=		# quoted list list of tgz files
1934	SRC_ARGLIST=		# quoted list of one or more "-s" args
1935	SRC_DIR="${SRC_ARG}"	# set default value for early usage()
1936	XSRC_DIR="${SRC_ARG}/../xsrc"
1937	N_SRC_ARGS=0		# number of "-s" args
1938	TGZMODE=false		# true if "-s" specifies a tgz file
1939	DIRMODE=false		# true if "-s" specified a directory
1940	SOURCEMODE=false	# true if "-s" specified a source directory
1941
1942	while getopts s:x:d:m:a: ch; do
1943		case "${ch}" in
1944		s)
1945			qarg="$(shell_quote "${OPTARG}")"
1946			N_SRC_ARGS=$(( $N_SRC_ARGS + 1 ))
1947			SRC_ARGLIST="${SRC_ARGLIST}${SRC_ARGLIST:+ }-s ${qarg}"
1948			if [ -f "${OPTARG}" ]; then
1949				# arg refers to a *.tgz file.
1950				# This may happen twice, for both
1951				# etc.tgz and xetc.tgz, so we build up a
1952				# quoted list in TGZLIST.
1953				TGZMODE=true
1954				TGZLIST="${TGZLIST}${TGZLIST:+ }${qarg}"
1955				# Note that, when TGZMODE is true,
1956				# SRC_ARG is used only for printing
1957				# human-readable messages.
1958				SRC_ARG="${TGZLIST}"
1959			elif [ -d "${OPTARG}" ]; then
1960				# arg refers to a directory.
1961				# It might be a source directory, or a
1962				# directory where the sets have already
1963				# been extracted.
1964				DIRMODE=true
1965				SRC_ARG="${OPTARG}"
1966				if [ -f "${OPTARG}/etc/Makefile" ]; then
1967					SOURCEMODE=true
1968				fi
1969			else
1970				err 2 "Invalid argument for -s option"
1971			fi
1972			;;
1973		x)
1974			if [ -d "${OPTARG}" ]; then
1975				# arg refers to a directory.
1976				XSRC_DIR="${OPTARG}"
1977			else
1978				err 2 "Not a directory for -x option"
1979			fi
1980			;;
1981		d)
1982			DEST_DIR="${OPTARG}"
1983			;;
1984		m)
1985			MACHINE="${OPTARG}"
1986			;;
1987		a)
1988			MACHINE_ARCH="${OPTARG}"
1989			;;
1990		*)
1991			usage
1992			;;
1993		esac
1994	done
1995	shift $((${OPTIND} - 1))
1996	[ $# -gt 0 ] || usage
1997
1998	if [ "$N_SRC_ARGS" -gt 1 ] && $DIRMODE; then
1999		err 2 "Multiple -s args are allowed only with tgz files"
2000	fi
2001	if [ "$N_SRC_ARGS" -eq 0 ]; then
2002		# The default SRC_ARG was set elsewhere
2003		DIRMODE=true
2004		SOURCEMODE=true
2005		SRC_ARGLIST="-s $(shell_quote "${SRC_ARG}")"
2006	fi
2007
2008	#
2009	# If '-s' arg or args specified tgz files, extract them
2010	# to a scratch directory.
2011	#
2012	if $TGZMODE; then
2013		ETCTGZDIR="${SCRATCHDIR}/etc.tgz"
2014		echo "Note: Creating temporary directory ${ETCTGZDIR}"
2015		if ! mkdir "${ETCTGZDIR}"; then
2016			err 2 "Can't create ${ETCTGZDIR}"
2017		fi
2018		( # subshell to localise changes to "$@"
2019			eval "set -- ${TGZLIST}"
2020			for tgz in "$@"; do
2021				echo "Note: Extracting files from ${tgz}"
2022				cat "${tgz}" | (
2023					cd "${ETCTGZDIR}" &&
2024					tar -zxf -
2025				) || err 2 "Can't extract ${tgz}"
2026			done
2027		)
2028		SRC_DIR="${ETCTGZDIR}"
2029	else
2030		SRC_DIR="${SRC_ARG}"
2031	fi
2032
2033	[ -d "${SRC_DIR}" ]	|| err 2 "${SRC_DIR} is not a directory"
2034	[ -d "${DEST_DIR}" ]	|| err 2 "${DEST_DIR} is not a directory"
2035	[ -n "${MACHINE}" ]	|| err 2 "\${MACHINE} is not defined"
2036	[ -n "${MACHINE_ARCH}" ] || err 2 "\${MACHINE_ARCH} is not defined"
2037	if ! $SOURCEMODE && ! [ -f "${SRC_DIR}/etc/mtree/set.etc" ]; then
2038		err 2 "Files from the etc.tgz set are missing"
2039	fi
2040
2041		# If directories are /, clear them, so various messages
2042		# don't have leading "//".   However, this requires
2043		# the use of ${foo:-/} to display the variables.
2044		#
2045	[ "${SRC_DIR}" = "/" ]	&& SRC_DIR=""
2046	[ "${DEST_DIR}" = "/" ]	&& DEST_DIR=""
2047
2048	detect_x11
2049
2050	op="$1"
2051	shift
2052
2053	case "${op}" in
2054	diff)
2055		op=check
2056		DIFF_STYLE=n			# default style is RCS
2057		OPTIND=1
2058		while getopts bcenpuw ch; do
2059			case "${ch}" in
2060			c|e|n|u)
2061				if [ "${DIFF_STYLE}" != "n" -a \
2062				    "${DIFF_STYLE}" != "${ch}" ]; then
2063					err 2 "conflicting output style: ${ch}"
2064				fi
2065				DIFF_STYLE="${ch}"
2066				;;
2067			b|p|w)
2068				DIFF_OPT="${DIFF_OPT} -${ch}"
2069				;;
2070			*)
2071				err 2 "unknown diff option"
2072				;;
2073			esac
2074		done
2075		shift $((${OPTIND} - 1))
2076		;;
2077	esac
2078
2079	case "${op}" in
2080
2081	usage|help)
2082		usage
2083		;;
2084
2085	list)
2086		echo "Source directory: ${SRC_DIR:-/}"
2087		echo "Target directory: ${DEST_DIR:-/}"
2088		if $TGZMODE; then
2089			echo " (extracted from: ${SRC_ARG})"
2090		fi
2091		list
2092		;;
2093
2094	check|fix)
2095		todo="$*"
2096		: ${todo:="${defaultitems}"}
2097
2098		# ensure that all supplied items are valid
2099		#
2100		for i in ${todo}; do
2101			eval desc=\"\${desc_${i}}\"
2102			[ -n "${desc}" ] || err 2 "Unsupported ${op} '"${i}"'"
2103		done
2104
2105		# perform each check/fix
2106		#
2107		echo "Source directory: ${SRC_DIR:-/}"
2108		if $TGZMODE; then
2109			echo " (extracted from: ${SRC_ARG})"
2110		fi
2111		echo "Target directory: ${DEST_DIR:-/}"
2112		items_passed=
2113		items_failed=
2114		for i in ${todo}; do
2115			echo "${i} ${op}:"
2116			( eval do_${i} ${op} )
2117			if [ $? -eq 0 ]; then
2118				items_passed="${items_passed} ${i}"
2119			else
2120				items_failed="${items_failed} ${i}"
2121			fi
2122		done
2123
2124		if [ "${op}" = "check" ]; then
2125			plural="checks"
2126		else
2127			plural="fixes"
2128		fi
2129
2130		echo "${PROGNAME} ${plural} passed:${items_passed}"
2131		echo "${PROGNAME} ${plural} failed:${items_failed}"
2132		if [ -n "${items_failed}" ]; then
2133		    exitstatus=1;
2134		    if [ "${op}" = "check" ]; then
2135			[ "$MACHINE" = "$(uname -m)" ] && m= || m=" -m $MACHINE"
2136			cat <<_Fix_me_
2137To fix, run:
2138    ${HOST_SH} ${0} ${SRC_ARGLIST} -d ${DEST_DIR:-/}$m fix${items_failed}
2139Note that this may overwrite local changes.
2140_Fix_me_
2141		    fi
2142		fi
2143
2144		;;
2145
2146	*)
2147		warn "Unknown operation '"${op}"'"
2148		usage
2149		;;
2150
2151	esac
2152}
2153
2154# defaults
2155#
2156PROGNAME="${0##*/}"
2157SRC_ARG="/usr/src"
2158DEST_DIR="/"
2159: ${MACHINE:="$( uname -m )"}	# assume native build if $MACHINE is not set
2160: ${MACHINE_ARCH:="$( uname -p )"}# assume native build if not set
2161
2162DIFF_STYLE=
2163NOT_FIXED=" (FIX MANUALLY)"
2164SCRATCHDIR="$( mkdtemp )" || err 2 "Can't create scratch directory"
2165trap "/bin/rm -rf \"\${SCRATCHDIR}\" ; exit 0" 1 2 3 15	# HUP INT QUIT TERM
2166
2167umask 022
2168exec 3>/dev/null
2169exec 4>/dev/null
2170exitstatus=0
2171
2172main "$@"
2173/bin/rm -rf "${SCRATCHDIR}"
2174exit $exitstatus
2175