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