xref: /netbsd-src/usr.sbin/etcupdate/etcupdate (revision 4c6c9aca3435234aa0fb2fd473d07ac524901456)
1#!/bin/sh
2#
3# $NetBSD: etcupdate,v 1.65 2022/01/17 08:47:03 lukem Exp $
4#
5# Copyright (c) 2001-2022 The NetBSD Foundation, Inc.
6# All rights reserved.
7#
8# This code is derived from software contributed to The NetBSD Foundation
9# by Martti Kuparinen.
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#
33# This script helps you to update the configuration files in /etc
34# after an operating system upgrade. Instead of running "make distribution"
35# in /usr/src/etc (and losing your current configuration) you can easily
36# see the modifications and either install the new version or merge the
37# changes in to your current configuration files.
38#
39# This script was written by Martti Kuparinen <martti@NetBSD.org> and
40# improved by several other NetBSD users.
41#
42# The idea for this script (including code fragments, variable names etc.)
43# came from the FreeBSD mergemaster (by Douglas Barton).
44#
45PATH="/sbin:/usr/sbin:/bin:/usr/bin:${PATH}"
46
47# Default settings
48PROG="${0##*/}"
49DESTDIR=""		# must not have a trailing slash
50DESTDIR_BRE=""		# basic regex to match ${DESTDIR}
51TEMPROOT="${TEMPROOT:=/tmp/temproot}"
52PAGER="${PAGER:=/usr/bin/more}"
53SWIDTH="$(stty size | awk '{w=$2}END{if(w==0){w=80}print w}')"
54WIDTH="${WIDTH:="${SWIDTH}"}"
55DIFF_COMMAND="diff -u"
56VERBOSE=false
57CONTINUE=false
58SOURCEMODE=false	# true for "-s source_dir"
59SRCDIR=			# directory for SOURCEMODE
60BINARYMODE=false	# true for both BINARYDIRMODE and BINARYTGZMODE
61BINARYDIRMODE=false	# true for "-s extracted_dir"
62BINARYDIR=		# directory name for BINARYDIRMODE
63BINARYTGZMODE=false	# true for "-s etc.tgz"
64TGZLIST=		# quoted list list of files for BINARYTGZMODE
65SRC_ARGLIST=		# quoted list of "-s" args
66N_SRC_ARGS=0		# number of "-s" args
67AUTOMATIC=false
68LOCALSKIP=false
69MACHINE="${MACHINE:="$(uname -m)"}"
70export MACHINE
71MACHINE_ARCH="${MACHINE_ARCH:="$(uname -p)"}"
72export MACHINE_ARCH
73
74# Settings for post-installation procedures
75NEED_ANYTHING=false
76NEED_MAKEDEV=false
77NEED_MTREE=false
78NEED_NEWALIASES=false
79NEED_PWD_MKDB=false
80NEED_SERVICES_MKDB=false
81
82
83help()
84{
85	cat << EOF
86
87Usage: ${PROG} [-alv] [-d DESTDIR] [-p PAGER] [-s SRC_ARG] [-t TEMPROOT] [-w WIDTH]
88       ${PROG} ( -h | -? )
89
90Options:
91
92  -a           Automatically update unmodified files.
93  -d DESTDIR   Destination directory to check. [/]
94  -h           Display this help, and exit.
95  -l           Automatically skip files with strictly local changes
96               (this option has no effect on files lacking RCS Ids).
97  -p PAGER     Which pager to use              [/usr/bin/more]
98  -s SRC_ARG   Location of the source files.   [/usr/src]
99               This may be any of the following:
100                 -s SRC_DIR   A directory that contains a NetBSD
101                              source tree.
102                 -s TGZ_DIR   A directory in which one or both of
103                              "etc.tgz" and "xetc.tgz" have been extracted.
104                 -s TGZ_FILE  A distribution set file such as "etc.tgz" or
105                              "xetc.tgz".  May be specified multiple times.
106  -t TEMPROOT  Where to store temporary files. [/tmp/temproot]
107  -w WIDTH     Screen width.                   [80]
108  -v           Be more verbose.
109  -?           Display this help, and exit.
110
111EOF
112}
113
114usage()
115{
116	help 1>&2
117	exit 1
118}
119
120verbose()
121{
122	# $* = message to display if in verbose mode
123
124	${VERBOSE} && echo "${@}"
125}
126
127yesno()
128{
129	# $* = message to display
130
131	echo -n "${@}? (y/[n]) "
132	read ANSWER
133	case "${ANSWER}" in
134		y|Y)
135			return 0
136			;;
137		*)
138			return 1
139			;;
140	esac
141}
142
143# Quote args to make them safe in the shell.
144# Usage: quotedlist="$(shell_quote args...)"
145#
146# After building up a quoted list, use it by evaling it inside
147# double quotes, like this:
148#    eval "set -- $quotedlist"
149# or like this:
150#    eval "\$command $quotedlist \$filename"
151#
152shell_quote()
153{(
154	local result=''
155	local arg qarg
156	LC_COLLATE=C ; export LC_COLLATE # so [a-zA-Z0-9] works in ASCII
157	for arg in "$@" ; do
158		case "${arg}" in
159		'')
160			qarg="''"
161			;;
162		*[!-./a-zA-Z0-9]*)
163			# Convert each embedded ' to '\'',
164			# then insert ' at the beginning of the first line,
165			# and append ' at the end of the last line.
166			# Finally, elide unnecessary '' pairs at the
167			# beginning and end of the result and as part of
168			# '\'''\'' sequences that result from multiple
169			# adjacent quotes in he input.
170			qarg="$(printf "%s\n" "$arg" | \
171			    ${SED:-sed} -e "s/'/'\\\\''/g" \
172				-e "1s/^/'/" -e "\$s/\$/'/" \
173				-e "1s/^''//" -e "\$s/''\$//" \
174				-e "s/'''/'/g"
175				)"
176			;;
177		*)
178			# Arg is not the empty string, and does not contain
179			# any unsafe characters.  Leave it unchanged for
180			# readability.
181			qarg="${arg}"
182			;;
183		esac
184		result="${result}${result:+ }${qarg}"
185	done
186	printf "%s\n" "$result"
187)}
188
189# Convert arg $1 to a basic regular expression (as in sed)
190# that will match the arg.  This works by inserting backslashes
191# before characters that are special in basic regular expressions.
192# It also inserts backslashes before the extra characters specified
193# in $2 (which defaults to "/,").
194# XXX: Does not handle embedded newlines.
195# Usage: regex="$(bre_quote "${string}")"
196bre_quote()
197{
198	local arg="$1"
199	local extra="${2-/,}"
200	printf "%s\n" "${arg}" | sed -e 's/[][^$.*\\'"${extra}"']/\\&/g'
201}
202
203install_dir()
204{
205	# $1 = target directory (relative to ${DESTDIR})
206
207	NEED_ANYTHING=true
208	if yesno "Create ${DESTDIR}${1}"; then
209		verbose "Creating ${DESTDIR}${1}"
210		mkdir -p "${DESTDIR}${1}" || exit 1
211		NEED_MTREE=true
212	fi
213}
214
215install_file()
216{
217	# $1 = target file (relative to ${DESTDIR})
218
219	NEED_ANYTHING=true
220	# Install the new file
221	verbose "Installing ${DESTDIR}${1}"
222	cp -p "${TEMPROOT}${1}" "${DESTDIR}${1}" && rm -f "${TEMPROOT}${1}"
223
224	# Check if this was a special file
225	case "${1}" in
226	/dev/MAKEDEV)
227		NEED_MAKEDEV=true
228		;;
229	/dev/MAKEDEV.local)
230		NEED_MAKEDEV=true
231		;;
232	/etc/mail/aliases)
233		NEED_NEWALIASES=true
234		;;
235	/etc/master.passwd)
236		NEED_PWD_MKDB=true
237		;;
238	/etc/services)
239		NEED_SERVICES_MKDB=true
240		;;
241	esac
242}
243
244install_checksum()
245{
246	# $1 = target file (relative to ${DESTDIR})
247
248	${AUTOMATIC} || return
249
250	NEED_ANYTHING=true
251	D="$(dirname "${1}")"
252	mkdir -p "${DESTDIR}/var/etcupdate/${D}"
253	verbose "Saving MD5 checksum for ${DESTDIR}${1} to" \
254	    "${DESTDIR}/var/etcupdate/${1}"
255	# The sed part of the following pipeline changes things like
256	# "MD5 (/path/to/dest/dir/etc/filename) = abc123" to
257	# "MD5 (/etc/filename) = abc123".
258	md5 "${DESTDIR}${1}" | sed -e "s,(${DESTDIR_BRE},(," \
259	    > "${DESTDIR}/var/etcupdate/${1}"
260}
261
262# Initialise the DIFF_EXTRA_OPTIONS variable.
263init_diff_extra_options()
264{
265	#
266	# Start with a few options that are always available.
267	#
268	DIFF_EXTRA_OPTIONS=\
269"  su  Show differences in unified format (\"diff -u\")
270  sc  Show differences in context format (\"diff -c\")
271  ss  Show differences side by side (\"sdiff -w${WIDTH}\")"
272	#
273	# wdiff is not part of the base system, but the
274	# user might have installed it from pkgsrc.  It is
275	# useful to show differences on a word by word basis
276	# instead of line by line.  If it is executable
277	# then offer to use it in the menu.
278	#
279	if (wdiff /dev/null /dev/null) >/dev/null 2>&1 ; then
280		DIFF_EXTRA_OPTIONS="${DIFF_EXTRA_OPTIONS}
281  sw  Show differences word by word (\"wdiff -n -l\")"
282	fi
283	#
284	# End with an option to use a user-specified diff-like command.
285	#
286	DIFF_EXTRA_OPTIONS="${DIFF_EXTRA_OPTIONS}
287  scommand Show differences using the specified diff-like command"
288}
289
290diff_and_merge_file()
291{
292	# $1 = target file (relative to ${DESTDIR})
293
294	if cmp -s "${TEMPROOT}${1}" "${DESTDIR}${1}"; then
295		verbose "===> ${1} (ok)"
296		rm -f "${TEMPROOT}${1}"
297		install_checksum "${1}"
298		return
299	fi
300
301	if ${AUTOMATIC} && [ -f "${DESTDIR}/var/etcupdate/${1}" ]; then
302		SUM1="$(md5 "${1}")"
303		SUM2="$(cat "${DESTDIR}/var/etcupdate/${1}")"
304		if [ "${SUM1}" = "${SUM2}" ]; then
305			install_file "${1}"
306			install_checksum "${1}"
307			return
308		fi
309	fi
310
311	if ${LOCALSKIP}; then
312		ID1="$(ident -q "${TEMPROOT}${1}" | sed -n 2p)"
313		ID1="${ID1:-0}"
314		ID2="$(ident -q "${DESTDIR}${1}" | sed -n 2p)"
315		ID2="${ID2:-1}"
316		if [ "${ID1}" = "${ID2}" ]; then
317			verbose "===> ${1} (ok:RCS)"
318			rm -f "${TEMPROOT}${1}"
319			return
320		fi
321	fi
322
323	clear
324	if [ ! -f "${DESTDIR}${1}" ]; then
325		verbose "===> ${DESTDIR}${1} (missing)"
326		DOES_EXIST=false
327	else
328		verbose "===> ${DESTDIR}${1} (modified)"
329		verbose ""
330		DOES_EXIST=true
331		diff -u "${DESTDIR}${1}" "${TEMPROOT}${1}" | ${PAGER}
332	fi
333
334	STAY_HERE=true
335	ALREADY_MERGED=false
336
337	# Determine name for the backup file (/foo/._etcupdate.bar)
338	D="$(dirname  "${TEMPROOT}${1}")"
339	F="$(basename "${TEMPROOT}${1}")"
340	B="${D}/.etcupdate.${F}"
341	F="${D}/${F}"
342
343	while ${STAY_HERE}; do
344
345		# Ask the user if (s)he wants to install the new
346		# version or perform a more complicated manual work.
347		echo ""
348		echo -n "File: ${DESTDIR}${1}"
349		if [ ! -f "${DESTDIR}${1}" ]; then
350			echo -n " (missing)"
351		else
352			echo -n " (modified)"
353		fi
354		echo ""
355		echo ""
356		echo "Please select one of the following operations:"
357		echo ""
358		if ! ${DOES_EXIST}; then
359			cat << EOF
360  d  Don't install the missing file
361  i  Install the missing file
362  v  Show the missing file
363
364EOF
365		elif ! ${ALREADY_MERGED}; then
366			cat << EOF
367  d  Don't install the new file (keep your old file)
368  i  Install the new file (overwrites your local modifications!)
369  m  Merge the currently installed and new files
370  s  Show the differences between the currently installed and new files
371${DIFF_EXTRA_OPTIONS}
372  v  Show the new file
373
374EOF
375		else
376			cat << EOF
377  d  Don't install the merged file (keep your old file)
378  i  Install the merged file (overwrites your old file)
379  m  Merge again (your old file against the result from the previous merge)
380  s  Show the differences between the currently installed and new merged files
381${DIFF_EXTRA_OPTIONS}
382  u  Undo merge (start again with the original version of the new file)
383  v  Show the merged file
384
385EOF
386		fi
387		echo -n "What do you want to do? [Leave it for later] "
388		read ANSWER
389		case "${ANSWER}" in
390
391		[dD])
392			verbose "Removing ${TEMPROOT}${1}"
393			rm -f "${TEMPROOT}${1}"
394			STAY_HERE=false
395			;;
396		[iI])
397			install_file "${1}"
398			if ! ${ALREADY_MERGED}; then
399				install_checksum "${1}"
400			fi
401			STAY_HERE=false
402			;;
403		[mM])
404			${DOES_EXIST} || continue
405			[ ! -f "${B}" ] && cp "${F}" "${B}"
406			cp "${TEMPROOT}${1}" "${TEMPROOT}${1}.merged"
407			sdiff -o "${TEMPROOT}${1}.merged"	\
408				--width=${WIDTH}		\
409				--suppress-common-lines --text	\
410				"${DESTDIR}${1}" "${TEMPROOT}${1}"
411			mv -f "${TEMPROOT}${1}.merged" "${TEMPROOT}${1}"
412			ALREADY_MERGED=true
413			;;
414		[sS]*)
415			${DOES_EXIST} || continue
416			case "${ANSWER}" in
417			[sS])	: no change ;;
418			[sS]u)	DIFF_COMMAND="diff -u" ;;
419			[sS]c)	DIFF_COMMAND="diff -c" ;;
420			[sS]s)	DIFF_COMMAND="sdiff -w${WIDTH}" ;;
421			[sS]w)	DIFF_COMMAND="wdiff -n -l" ;;
422			[sS]*)	DIFF_COMMAND="${ANSWER#?}" ;;
423			esac
424			${DIFF_COMMAND} "${DESTDIR}${1}" "${TEMPROOT}${1}" \
425				| ${PAGER}
426			;;
427		[uU])
428			if [ -f "${B}" ]; then
429				echo "*** Restoring ${F}"
430				mv -f "${B}" "${F}"
431			fi
432			ALREADY_MERGED=false
433			;;
434		[vV])
435			${PAGER} "${TEMPROOT}${1}"
436			;;
437		"")
438			STAY_HERE=false
439			;;
440		*)
441			echo "*** Invalid selection!"
442			;;
443		esac
444	done
445	rm -f "$B"
446}
447
448# Set the environment for make.
449set_makeenv()
450{
451	#
452	# INSTALL_DONE=1 prevents installation of unwanted
453	# files (things that are not part of the etc set).
454	# BUILD=1 allows building of files that are wanted.
455	#
456	MAKE_ENV=" 			\
457		NETBSDSRCDIR=$(shell_quote "${SRCDIR}")	\
458		DESTDIR=$(shell_quote "${TEMPROOT}")	\
459		MAKE=make		\
460		MTREE=mtree		\
461		TOOL_MTREE=mtree	\
462		INSTALL_DONE=1		\
463		BUILD=1			\
464		USETOOLS=never"
465}
466
467#
468# main()
469#
470
471# Read global configuration
472GLOBALRC="/etc/${PROG}.conf"
473[ -r ${GLOBALRC} ] && . ${GLOBALRC}
474
475# Read user configuration
476USERRC="${HOME}/.{PROG}rc"
477[ -r ${USERRC} ] && . ${USERRC}
478
479# Read command line arguments
480while getopts :ad:hlp:s:t:vw: i
481do
482	case "${i}" in
483	a)
484		AUTOMATIC=true
485		;;
486	d)
487		DESTDIR="${OPTARG}"
488		;;
489	h)
490		help
491		exit 0
492		;;
493	l)
494		LOCALSKIP=true
495		;;
496	p)
497		PAGER="${OPTARG}"
498		;;
499	s)
500		# Three cases:
501		# -s tgzfile       (may be repeated)
502		# -s srcdir        (may not be repeated)
503		# -s extracted_dir (may not be repeated)
504		arg="${OPTARG}"
505		qarg="$(shell_quote "${OPTARG}")"
506		N_SRC_ARGS=$(( N_SRC_ARGS + 1 ))
507		SRC_ARGLIST="${SRC_ARGLIST}${SRC_ARGLIST:+ }-s ${qarg}"
508		if [ -f "${arg}" ]; then
509			# arg refers to a *.tgz file.
510			# This may happen twice, for both etc.tgz and
511			# xetc.tgz, so we build up a list in TGZLIST.
512			BINARYMODE=true
513			BINARYTGZMODE=true
514			TGZLIST="${TGZLIST}${TGZLIST:+ }${qarg}"
515		elif [ -d "${arg}" ] && [ -f "${arg}/etc/Makefile" ]; then
516			# arg refers to a source directory
517			SOURCEMODE=true
518			SRCDIR="${arg}"
519		elif [ -d "${arg}" ] && [ -d "${arg}/etc" ] \
520			&& ! [ -f "${arg}/etc/Makefile" ]
521		then
522			# arg refers to a directory where the
523			# sets have already been extracted
524			BINARYMODE=true
525			BINARYDIRMODE=true
526			BINARYDIR="${arg}"
527		else
528			echo "*** Nonexistent or invalid file or directory" \
529			     "for -s ${arg}"
530			usage
531		fi
532		;;
533	t)
534		TEMPROOT="${OPTARG}"
535		;;
536	v)
537		VERBOSE=true
538		;;
539	w)
540		WIDTH="${OPTARG}"
541		;;
542	"?")
543		if [ "${OPTARG}" = "?" ]; then
544			help
545			exit 0
546		fi
547		echo 1>&2 "${PROG}: Unknown option -${OPTARG}"
548		usage
549		;;
550
551	:)
552		echo 1>&2 "${PROG}: Missing argument for option -${OPTARG}"
553		usage
554		;;
555
556	*)
557		echo 1>&2 "${PROG}: Unimplemented option -${i}"
558		exit 3
559		;;
560	esac
561done
562shift $((${OPTIND} - 1))
563if [ $# -ne 0 ] ; then
564	echo 1>&2 "${PROG}: Unknown extra arguments"
565	usage
566fi
567
568# Last minute sanity checks
569if [ "$(id -u)" -ne 0 ]; then
570	echo "*** ERROR: You MUST be root"
571	exit 1
572fi
573DESTDIR="${DESTDIR%/}" # remove trailing slash, if any.  result might be "".
574DESTDIR_BRE="$(bre_quote "${DESTDIR}")"
575if [ "${N_SRC_ARGS}" -gt 1 ] && ( ${SOURCEMODE} || ${BINARYDIRMODE} ); then
576	echo 1>&2 "${PROG}: Multiple -s args are allowed only with tgz files"
577	usage
578fi
579case "${TEMPROOT}" in
580/*) : OK ;;
581*)  new="${PWD:-$(pwd)}/${TEMPROOT}"
582    echo "*** NOTE: Using TEMPROOT \"${new}\" instead of \"${TEMPROOT}\""
583    TEMPROOT="${new}"
584    ;;
585esac
586if ${BINARYDIRMODE}; then
587	SRCDIR="${TEMPROOT}"
588fi
589if ${BINARYTGZMODE}; then
590	SRCDIR="${TEMPROOT}"
591fi
592if [ "${N_SRC_ARGS}" -eq 0 ]; then
593	# default if no "-s" option was specified
594	SOURCEMODE=true
595	SRCDIR="/usr/src"
596	SRC_ARGLIST="-s $(shell_quote "${SRCDIR}")"
597fi
598if [ -z "${SRCDIR}" -o -z "${TEMPROOT}" ]; then
599	echo "*** ERROR: One of the following variables is undefined"
600	echo ""
601	echo "SRCDIR=\"${SRCDIR}\""
602	echo "TEMPROOT=\"${TEMPROOT}\""
603	echo ""
604	exit 1
605fi
606if [ -r "${TEMPROOT}" ]; then
607	echo ""
608	echo "*** WARNING: ${TEMPROOT} already exists"
609	echo ""
610	if yesno "Continue previously aborted update"; then
611		CONTINUE=true
612	elif yesno "Remove the old ${TEMPROOT}"; then
613		echo "*** Removing ${TEMPROOT}"
614		rm -rf "${TEMPROOT}"
615	fi
616fi
617
618if ! ${CONTINUE}; then
619	# Create the temporary root directory
620	echo "*** Creating ${TEMPROOT}"
621	mkdir -p "${TEMPROOT}"
622	if [ ! -d "${TEMPROOT}" ]; then
623		echo "*** ERROR: Unable to create ${TEMPROOT}"
624		exit 1
625	fi
626	# Are we using the sources or binaries?
627	if ${BINARYTGZMODE}; then
628		# Populate ${TEMPROOT} from ${TGZLIST}
629		eval "set -- ${TGZLIST}"
630		for tgz in "$@"; do
631			if [ ! -f "${tgz}" ]; then
632				echo "*** ERROR: Unable to find ${tgz}"
633				exit 1
634			fi
635			echo "*** Populating ${TEMPROOT} from ${tgz}"
636			tar -zxpf "${tgz}" -C "${TEMPROOT}"
637			[ $? -ne 0 ] && exit 1
638		done
639	elif ${BINARYDIRMODE}; then
640		# Populate ${TEMPROOT} from ${SRCDIR} by copying.
641		# Copy only the files that belong to the etc and xetc sets.
642		echo "*** Populating ${TEMPROOT} from ${BINARYDIR} (copying)"
643		for setname in etc xetc; do
644			mtreefile="${BINARYDIR}/etc/mtree/set.${setname}"
645			if ${VERBOSE}; then vflag="-v"; else vflag=""; fi
646			if [ -f "${mtreefile}" ]; then
647				echo "*** Copying files belonging to" \
648				     "${setname} set"
649				(cd "${BINARYDIR}" \
650				 && pax -rwdM ${vflag} "${TEMPROOT%/}/."
651				) <"${mtreefile}"
652				[ $? -ne 0 ] && exit 1
653			else
654				echo "*** Not copying files belonging to" \
655				     "${setname} set: ${mtreefile} not found"
656			fi
657		done
658	elif ${SOURCEMODE}; then
659		# Populate ${TEMPROOT} from ${SRCDIR} by running make
660		if [ ! -f "${SRCDIR}/etc/Makefile" ]; then
661			echo "*** ERROR: Unable to find ${SRCDIR}/etc/Makefile"
662			exit 1
663		fi
664		set_makeenv
665		echo "*** Populating ${TEMPROOT} from ${SRCDIR} (make distribution)"
666		cd ${SRCDIR}/etc
667		if ! ${VERBOSE}; then
668			eval "${MAKE_ENV} make distribution > /dev/null"
669		else
670			eval "${MAKE_ENV} make distribution"
671		fi
672		[ $? -ne 0 ] && exit 1
673	fi
674	if ! [ -f "${TEMPROOT}/etc/mtree/set.etc" ]; then
675		echo "*** ERROR: Files from the etc.tgz set are missing"
676		exit 1
677	fi
678	if [ ! -f "${TEMPROOT}/dev/MAKEDEV" ]; then
679		echo ""
680		echo "*** WARNING: ${TEMPROOT}/dev/MAKEDEV not found"
681		echo "Make sure you update /dev/MAKEDEV later and run"
682		echo "(cd /dev && ./MAKEDEV all) to rebuild the device nodes"
683		echo ""
684	fi
685
686	# Ignore the following files during comparison
687	rm -f "${TEMPROOT}"/etc/passwd
688	rm -f "${TEMPROOT}"/etc/pwd.db
689	rm -f "${TEMPROOT}"/etc/spwd.db
690	find "${TEMPROOT}" -type f -size 0 -exec rm {} \;
691
692	# Ignore files we're told to ignore
693	if [ ! -z "${IGNOREFILES}" ]; then
694		echo "*** Ignoring files: ${IGNOREFILES}"
695		for file in ${IGNOREFILES}; do
696			rm -f "${TEMPROOT}"${file}
697		done
698	fi
699
700	# Are there any new directories?
701	echo "*** Checking for new directories"
702	exec 3<&0
703	find "${TEMPROOT}" -type d | \
704	while read i; do
705		D="${i#"${TEMPROOT}"}"
706		[ "x${i}" = "x${TEMPROOT}" ] && continue
707		[ ! -d "${D}" ] && install_dir "${D}" <&3
708	done
709fi
710
711# Start the comparison
712echo "*** Checking for added/modified files"
713init_diff_extra_options
714exec 3<&0
715find "${TEMPROOT}" -type f  -a ! -name \*.etcupdate.\* | \
716while read i; do
717	D="${i#"${TEMPROOT}"}"
718	diff_and_merge_file "${D}" <&3
719done
720
721# Do we have files which were not processed?
722REMAINING="$(find "${TEMPROOT}" -type f -a ! -name \*.etcupdate.\*)"
723if [ ! -z "${REMAINING}" ]; then
724	echo ""
725	echo "*** The following files need your attention:"
726	echo ""
727	echo "${REMAINING}" | sed -e 's/^/  /'
728	echo ""
729elif ! ${NEED_ANYTHING}; then
730	echo ""
731	echo "*** No changes were needed"
732	echo ""
733fi
734if yesno "Remove ${TEMPROOT}"; then
735	echo "*** Removing ${TEMPROOT}"
736	rm -rf "${TEMPROOT}"
737else
738	echo "*** Keeping ${TEMPROOT}"
739fi
740
741# Clean up after "make distribution"
742if ${SOURCEMODE}; then
743	echo "*** Cleaning up in ${SRCDIR}/etc"
744	set_makeenv
745	cd ${SRCDIR}/etc
746	if ! ${VERBOSE}; then
747		eval "${MAKE_ENV} make clean > /dev/null"
748	else
749		eval "${MAKE_ENV} make clean"
750	fi
751fi
752
753# Do some post-installation tasks
754if ${NEED_PWD_MKDB}; then
755	pwd_mkdb_cmd="$(shell_quote \
756	    pwd_mkdb ${DESTDIR:+-d "${DESTDIR}"} \
757	    -p "${DESTDIR}/etc/master.passwd")"
758	if yesno "Do you want to rebuild the password databases from the" \
759	         "new ${DESTDIR}/etc/master.passwd"
760	then
761		verbose "Running pwd_mkdb"
762		eval "${pwd_mkdb_cmd}"
763	else
764		echo ""
765		echo "*** You MUST rebuild the password databases to make" \
766		     "the changes visible"
767		echo "*** This is done by running \"${pwd_mkdb_cmd}\" as root"
768		echo ""
769	fi
770fi
771
772if ! ${NEED_SERVICES_MKDB}; then
773	if test -e "${DESTDIR}/var/db/services.db" \
774	   -a ! -e "${DESTDIR}/var/db/services.cdb"
775	then
776		NEED_SERVICES_MKDB=true
777	fi
778fi
779
780if ${NEED_SERVICES_MKDB}; then
781	services_mkdb_cmd="$(shell_quote services_mkdb -V cdb \
782	    -o "${DESTDIR}/var/db/services.cdb" \
783	    "${DESTDIR}/etc/services")"
784	if yesno "Do you want to rebuild the services databases from the" \
785	         "new ${DESTDIR}/etc/services"
786	then
787		verbose "Running services_mkdb"
788		eval "${services_mkdb_cmd}"
789	else
790		echo ""
791		echo "*** You SHOULD rebuild the services databases to make" \
792		     "the changes visible"
793		echo "*** This is done by running \"${services_mkdb_cmd}\"" \
794		     "as root"
795		echo ""
796	fi
797fi
798if ${NEED_MTREE}; then
799	if yesno "You have created new directories. Run mtree to set" \
800	         "permissions"
801	then
802		(cd "${DESTDIR:-/}" && \
803		    mtree -Udef "${DESTDIR}/etc/mtree/NetBSD.dist")
804	fi
805fi
806if ${NEED_MAKEDEV}; then
807	makedev_cmd="($(shell_quote cd "${DESTDIR}/dev") && ./MAKEDEV all)"
808	if yesno "Do you want to rebuild the device nodes in ${DESTDIR}/dev"
809	then
810		verbose "Running MAKEDEV in /dev"
811		eval "${makedev_cmd}"
812	else
813		echo ""
814		echo "*** You SHOULD rebuild the device nodes in" \
815		     "${DESTDIR}/dev"
816		echo "*** This is done by running \"${makedev_cmd}\" as root."
817		echo ""
818	fi
819fi
820if ${NEED_NEWALIASES}; then
821	newaliases_cmd="newaliases"
822	# XXX newaliases doesn't work with DESTDIR.
823	# We could check whether the system configuration is
824	# sufficiently standard, and then run postalias(1) with the
825	# right args to make it work, but changes to /etc/mail/aliases
826	# are so rare that it doesn't seem worth the effort of checking
827	# that the system's mail configuration is standard.
828	if [ -z "${DESTDIR}" ] && \
829	    yesno "Do you want to rebuild the mail alias database"
830	then
831		verbose "Running newaliases"
832		eval "${newaliases_cmd}"
833	else
834		echo ""
835		echo "*** You MUST rebuild the mail alias database to make" \
836		     "the changes visible"
837		echo "*** This is done by running \"${newaliases_cmd}\" as root"
838		if [ -n "${DESTDIR}" ]; then
839		    postalias_cmd="$(shell_quote \
840			postalias "hash:${DESTDIR}/etc/mail/aliases")"
841		    echo "*** but it won't work with DESTDIR=${DESTDIR}."
842		    echo "*** If you use postfix(1) with the default" \
843			 "configuration, then you can try"
844		    echo "*** running \"${postalias_cmd}\" as root."
845		fi
846		echo ""
847	fi
848fi
849if [ -x "${DESTDIR}/usr/sbin/postinstall" ]; then
850	postinstall_cmd="$(shell_quote "${DESTDIR}/usr/sbin/postinstall" \
851	    ${DESTDIR:+-d "${DESTDIR}"}) ${SRC_ARGLIST} check"
852	echo "*** Running ${DESTDIR}/usr/sbin/postinstall"
853	eval "${postinstall_cmd}"
854fi
855echo "*** All done"
856