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