Home | History | Annotate | Line # | Download | only in etcupdate
      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 (at] 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 #
     45 PATH="/sbin:/usr/sbin:/bin:/usr/bin:${PATH}"
     46 
     47 # Default settings
     48 PROG="${0##*/}"
     49 DESTDIR=""		# must not have a trailing slash
     50 DESTDIR_BRE=""		# basic regex to match ${DESTDIR}
     51 TEMPROOT="${TEMPROOT:=/tmp/temproot}"
     52 PAGER="${PAGER:=/usr/bin/more}"
     53 SWIDTH="$(stty size | awk '{w=$2}END{if(w==0){w=80}print w}')"
     54 WIDTH="${WIDTH:="${SWIDTH}"}"
     55 DIFF_COMMAND="diff -u"
     56 VERBOSE=false
     57 CONTINUE=false
     58 SOURCEMODE=false	# true for "-s source_dir"
     59 SRCDIR=			# directory for SOURCEMODE
     60 BINARYMODE=false	# true for both BINARYDIRMODE and BINARYTGZMODE
     61 BINARYDIRMODE=false	# true for "-s extracted_dir"
     62 BINARYDIR=		# directory name for BINARYDIRMODE
     63 BINARYTGZMODE=false	# true for "-s etc.tgz"
     64 TGZLIST=		# quoted list list of files for BINARYTGZMODE
     65 SRC_ARGLIST=		# quoted list of "-s" args
     66 N_SRC_ARGS=0		# number of "-s" args
     67 AUTOMATIC=false
     68 LOCALSKIP=false
     69 MACHINE="${MACHINE:="$(uname -m)"}"
     70 export MACHINE
     71 MACHINE_ARCH="${MACHINE_ARCH:="$(uname -p)"}"
     72 export MACHINE_ARCH
     73 
     74 # Settings for post-installation procedures
     75 NEED_ANYTHING=false
     76 NEED_MAKEDEV=false
     77 NEED_MTREE=false
     78 NEED_NEWALIASES=false
     79 NEED_PWD_MKDB=false
     80 NEED_SERVICES_MKDB=false
     81 
     82 
     83 help()
     84 {
     85 	cat << EOF
     86 
     87 Usage: ${PROG} [-alv] [-d DESTDIR] [-p PAGER] [-s SRC_ARG] [-t TEMPROOT] [-w WIDTH]
     88        ${PROG} ( -h | -? )
     89 
     90 Options:
     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 
    111 EOF
    112 }
    113 
    114 usage()
    115 {
    116 	help 1>&2
    117 	exit 1
    118 }
    119 
    120 verbose()
    121 {
    122 	# $* = message to display if in verbose mode
    123 
    124 	${VERBOSE} && echo "${@}"
    125 }
    126 
    127 yesno()
    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 #
    152 shell_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}")"
    196 bre_quote()
    197 {
    198 	local arg="$1"
    199 	local extra="${2-/,}"
    200 	printf "%s\n" "${arg}" | sed -e 's/[][^$.*\\'"${extra}"']/\\&/g'
    201 }
    202 
    203 install_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 
    215 install_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 
    244 install_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.
    263 init_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 
    290 diff_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 
    364 EOF
    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 
    374 EOF
    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 
    385 EOF
    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.
    449 set_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
    472 GLOBALRC="/etc/${PROG}.conf"
    473 [ -r ${GLOBALRC} ] && . ${GLOBALRC}
    474 
    475 # Read user configuration
    476 USERRC="${HOME}/.{PROG}rc"
    477 [ -r ${USERRC} ] && . ${USERRC}
    478 
    479 # Read command line arguments
    480 while getopts :ad:hlp:s:t:vw: i
    481 do
    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
    561 done
    562 shift $((${OPTIND} - 1))
    563 if [ $# -ne 0 ] ; then
    564 	echo 1>&2 "${PROG}: Unknown extra arguments"
    565 	usage
    566 fi
    567 
    568 # Last minute sanity checks
    569 if [ "$(id -u)" -ne 0 ]; then
    570 	echo "*** ERROR: You MUST be root"
    571 	exit 1
    572 fi
    573 DESTDIR="${DESTDIR%/}" # remove trailing slash, if any.  result might be "".
    574 DESTDIR_BRE="$(bre_quote "${DESTDIR}")"
    575 if [ "${N_SRC_ARGS}" -gt 1 ] && ( ${SOURCEMODE} || ${BINARYDIRMODE} ); then
    576 	echo 1>&2 "${PROG}: Multiple -s args are allowed only with tgz files"
    577 	usage
    578 fi
    579 case "${TEMPROOT}" in
    580 /*) : OK ;;
    581 *)  new="${PWD:-$(pwd)}/${TEMPROOT}"
    582     echo "*** NOTE: Using TEMPROOT \"${new}\" instead of \"${TEMPROOT}\""
    583     TEMPROOT="${new}"
    584     ;;
    585 esac
    586 if ${BINARYDIRMODE}; then
    587 	SRCDIR="${TEMPROOT}"
    588 fi
    589 if ${BINARYTGZMODE}; then
    590 	SRCDIR="${TEMPROOT}"
    591 fi
    592 if [ "${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}")"
    597 fi
    598 if [ -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
    605 fi
    606 if [ -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
    616 fi
    617 
    618 if ! ${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
    709 fi
    710 
    711 # Start the comparison
    712 echo "*** Checking for added/modified files"
    713 init_diff_extra_options
    714 exec 3<&0
    715 find "${TEMPROOT}" -type f  -a ! -name \*.etcupdate.\* | \
    716 while read i; do
    717 	D="${i#"${TEMPROOT}"}"
    718 	diff_and_merge_file "${D}" <&3
    719 done
    720 
    721 # Do we have files which were not processed?
    722 REMAINING="$(find "${TEMPROOT}" -type f -a ! -name \*.etcupdate.\*)"
    723 if [ ! -z "${REMAINING}" ]; then
    724 	echo ""
    725 	echo "*** The following files need your attention:"
    726 	echo ""
    727 	echo "${REMAINING}" | sed -e 's/^/  /'
    728 	echo ""
    729 elif ! ${NEED_ANYTHING}; then
    730 	echo ""
    731 	echo "*** No changes were needed"
    732 	echo ""
    733 fi
    734 if yesno "Remove ${TEMPROOT}"; then
    735 	echo "*** Removing ${TEMPROOT}"
    736 	rm -rf "${TEMPROOT}"
    737 else
    738 	echo "*** Keeping ${TEMPROOT}"
    739 fi
    740 
    741 # Clean up after "make distribution"
    742 if ${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
    751 fi
    752 
    753 # Do some post-installation tasks
    754 if ${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
    770 fi
    771 
    772 if ! ${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
    778 fi
    779 
    780 if ${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
    797 fi
    798 if ${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
    805 fi
    806 if ${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
    819 fi
    820 if ${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
    848 fi
    849 if [ -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}"
    854 fi
    855 echo "*** All done"
    856