xref: /openbsd-src/usr.sbin/sysmerge/sysmerge.sh (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1#!/bin/ksh -
2#
3# $OpenBSD: sysmerge.sh,v 1.84 2012/03/19 10:52:57 ajacoutot Exp $
4#
5# Copyright (c) 1998-2003 Douglas Barton <DougB@FreeBSD.org>
6# Copyright (c) 2008, 2009, 2010, 2011 Antoine Jacoutot <ajacoutot@openbsd.org>
7#
8# Permission to use, copy, modify, and distribute this software for any
9# purpose with or without fee is hereby granted, provided that the above
10# copyright notice and this permission notice appear in all copies.
11#
12# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19#
20
21umask 0022
22
23unset AUTO_INSTALLED_FILES BATCHMODE DIFFMODE ETCSUM NEED_NEWALIASES
24unset NEWGRP NEWUSR NEED_REBOOT SRCDIR SRCSUM TGZ TGZURL XETCSUM
25unset XTGZ XTGZURL
26
27WRKDIR=$(mktemp -d -p ${TMPDIR:=/var/tmp} sysmerge.XXXXXXXXXX) || exit 1
28SWIDTH=$(stty size | awk '{w=$2} END {if (w==0) {w=80} print w}')
29MERGE_CMD="${MERGE_CMD:=sdiff -as -w ${SWIDTH} -o}"
30REPORT="${REPORT:=${WRKDIR}/sysmerge.log}"
31DBDIR="${DBDIR:=/var/db/sysmerge}"
32
33PAGER="${PAGER:=/usr/bin/more}"
34
35# clean leftovers created by make in src
36clean_src() {
37	if [ "${SRCDIR}" ]; then
38		cd ${SRCDIR}/gnu/usr.sbin/sendmail/cf/cf && make cleandir >/dev/null
39	fi
40}
41
42# restore files from backups or remove the newly generated sum files if
43# they did not exist
44restore_bak() {
45	for i in ${DESTDIR}/${DBDIR}/.{${SRCSUM},${ETCSUM},${XETCSUM}}.bak; do
46		_i=$(basename ${i} .bak)
47		if [ -f "${i}" ]; then
48			mv ${i} ${DESTDIR}/${DBDIR}/${_i#.}
49		elif [ -f "${DESTDIR}/${DBDIR}/${_i#.}" ]; then
50			rm ${DESTDIR}/${DBDIR}/${_i#.}
51		fi
52	done
53}
54
55# remove newly created work directory and exit with status 1
56error_rm_wrkdir() {
57	rmdir ${WRKDIR} 2>/dev/null
58	exit 1
59}
60
61usage() {
62	echo "usage: ${0##*/} [-bd] [-s [src | etcXX.tgz]] [-x xetcXX.tgz]" >&2
63}
64
65trap "restore_bak; clean_src; rm -rf ${WRKDIR}; exit 1" 1 2 3 13 15
66
67if [ "$(id -u)" -ne 0 ]; then
68	echo "\t*** ERROR: need root privileges to run this script"
69	usage
70	error_rm_wrkdir
71fi
72
73if [ -z "${FETCH_CMD}" ]; then
74	if [ -z "${FTP_KEEPALIVE}" ]; then
75		FTP_KEEPALIVE=0
76	fi
77	FETCH_CMD="/usr/bin/ftp -V -m -k ${FTP_KEEPALIVE}"
78fi
79
80do_populate() {
81	mkdir -p ${DESTDIR}/${DBDIR} || error_rm_wrkdir
82	echo "===> Populating temporary root under ${TEMPROOT}"
83	mkdir -p ${TEMPROOT}
84	if [ "${SRCDIR}" ]; then
85		SRCSUM=srcsum
86		cd ${SRCDIR}/etc
87		make DESTDIR=${TEMPROOT} distribution-etc-root-var >/dev/null 2>&1
88		(cd ${TEMPROOT} && find . -type f | xargs cksum > ${WRKDIR}/${SRCSUM})
89	fi
90
91	if [ "${TGZ}" -o "${XTGZ}" ]; then
92		for i in ${TGZ} ${XTGZ}; do
93			tar -xzphf ${i} -C ${TEMPROOT};
94		done
95		if [ "${TGZ}" ]; then
96			ETCSUM=etcsum
97			_E=$(cd $(dirname ${TGZ}) && pwd)/$(basename ${TGZ})
98			(cd ${TEMPROOT} && tar -tzf ${_E} | xargs cksum > ${WRKDIR}/${ETCSUM})
99		fi
100		if [ "${XTGZ}" ]; then
101			XETCSUM=xetcsum
102			_X=$(cd $(dirname ${XTGZ}) && pwd)/$(basename ${XTGZ})
103			(cd ${TEMPROOT} && tar -tzf ${_X} | xargs cksum > ${WRKDIR}/${XETCSUM})
104		fi
105	fi
106
107	for i in ${SRCSUM} ${ETCSUM} ${XETCSUM}; do
108		if [ -f ${DESTDIR}/${DBDIR}/${i} ]; then
109			# delete file in temproot if it has not changed since last release
110			# and is present in current installation
111			if [ -z "${DIFFMODE}" ]; then
112				_R=$(cd ${TEMPROOT} && \
113					cksum -c ${DESTDIR}/${DBDIR}/${i} 2>/dev/null | grep OK | awk '{ print $2 }' | sed 's/[:]//')
114				for _r in ${_R}; do
115					if [ -f ${DESTDIR}/${_r} -a -f ${TEMPROOT}/${_r} ]; then
116						rm -f ${TEMPROOT}/${_r}
117					fi
118				done
119			fi
120
121			# set auto-upgradable files
122			_D=$(diff -u ${WRKDIR}/${i} ${DESTDIR}/${DBDIR}/${i} | grep -E '^\+' | sed '1d' | awk '{print $3}')
123			for _d in ${_D}; do
124				CURSUM=$(cd ${DESTDIR:=/} && cksum ${_d} 2>/dev/null)
125				if [ -n "$(grep "${CURSUM}" ${DESTDIR}/${DBDIR}/${i})" -a -z "$(grep "${CURSUM}" ${WRKDIR}/${i})" ]; then
126					local _array="${_array} ${_d}"
127				fi
128			done
129			if [ -n "${_array}" ]; then
130				set -A AUTO_UPG -- ${_array}
131			fi
132
133			mv ${DESTDIR}/${DBDIR}/${i} ${DESTDIR}/${DBDIR}/.${i}.bak
134		fi
135		mv ${WRKDIR}/${i} ${DESTDIR}/${DBDIR}/${i}
136	done
137
138	# files we don't want/need to deal with
139	IGNORE_FILES="/etc/*.db
140		      /etc/mail/*.db
141		      /etc/passwd
142		      /etc/motd
143		      /etc/myname
144		      /var/db/locate.database
145		      /var/db/sysmerge/{etc,xetc}sum
146		      /var/games/tetris.scores
147		      /var/mail/root"
148	CF_FILES="/etc/mail/localhost.cf /etc/mail/sendmail.cf /etc/mail/submit.cf"
149	for cf in ${CF_FILES}; do
150		CF_DIFF=$(diff -q -I "##### " ${TEMPROOT}/${cf} ${DESTDIR}/${cf} 2>/dev/null)
151		if [ -z "${CF_DIFF}" ]; then
152			IGNORE_FILES="${IGNORE_FILES} ${cf}"
153		fi
154	done
155	if [ -r /etc/sysmerge.ignore ]; then
156		while read i; do \
157			IGNORE_FILES="${IGNORE_FILES} $(echo ${i} | sed -e 's,\.\.,,g' -e 's,#.*,,g')"
158		done < /etc/sysmerge.ignore
159	fi
160	for i in ${IGNORE_FILES}; do
161		rm -rf ${TEMPROOT}/${i};
162	done
163}
164
165do_install_and_rm() {
166	if [ -f "${5}/${4##*/}" ]; then
167		mkdir -p ${BKPDIR}/${4%/*}
168		cp ${5}/${4##*/} ${BKPDIR}/${4%/*}
169	fi
170
171	if ! install -m "${1}" -o "${2}" -g "${3}" "${4}" "${5}" 2>/dev/null; then
172		rm -f ${BKPDIR}/${4%/*}/${4##*/}
173		return 1
174	fi
175	rm -f "${4}"
176}
177
178mm_install() {
179	local INSTDIR
180	INSTDIR=${1#.}
181	INSTDIR=${INSTDIR%/*}
182
183	if [ -z "${INSTDIR}" ]; then INSTDIR=/; fi
184
185	DIR_MODE=$(stat -f "%OMp%OLp" "${TEMPROOT}/${INSTDIR}")
186	eval $(stat -f "FILE_MODE=%OMp%OLp FILE_OWN=%Su FILE_GRP=%Sg" ${1})
187
188	if [ "${DESTDIR}${INSTDIR}" -a ! -d "${DESTDIR}${INSTDIR}" ]; then
189		install -d -o root -g wheel -m "${DIR_MODE}" "${DESTDIR}${INSTDIR}"
190	fi
191
192	do_install_and_rm "${FILE_MODE}" "${FILE_OWN}" "${FILE_GRP}" "${1}" "${DESTDIR}${INSTDIR}" || return
193
194	case "${1#.}" in
195	/dev/MAKEDEV)
196		echo " (running MAKEDEV(8))"
197		(cd ${DESTDIR}/dev && /bin/sh MAKEDEV all)
198		export NEED_REBOOT=1
199		;;
200	/etc/login.conf)
201		if [ -f ${DESTDIR}/etc/login.conf.db ]; then
202			echo " (running cap_mkdb(1))"
203			cap_mkdb ${DESTDIR}/etc/login.conf
204		else
205			echo ""
206		fi
207		export NEED_REBOOT=1
208		;;
209	/etc/mail/access|/etc/mail/genericstable|/etc/mail/mailertable|/etc/mail/virtusertable)
210		echo " (running makemap(8))"
211		DBFILE=$(echo ${1} | sed -e 's,.*/,,')
212		/usr/libexec/sendmail/makemap hash ${DESTDIR}/${1#.} < ${DESTDIR}/${1#.}
213		;;
214	/etc/mail/aliases)
215		echo " (running newaliases(8))"
216		if [ "${DESTDIR}" ]; then
217			chroot ${DESTDIR} newaliases >/dev/null || export NEED_NEWALIASES=1
218		else
219			newaliases >/dev/null
220		fi
221		;;
222	/etc/master.passwd)
223		echo " (running pwd_mkdb(8))"
224		pwd_mkdb -d ${DESTDIR}/etc -p ${DESTDIR}/etc/master.passwd
225		;;
226	*)
227		echo ""
228		;;
229	esac
230}
231
232mm_install_link() {
233	_LINKT=$(readlink ${COMPFILE})
234	_LINKF=$(dirname ${DESTDIR}${COMPFILE#.})
235
236	DIR_MODE=$(stat -f "%OMp%OLp" "${TEMPROOT}/${_LINKF}")
237	[ ! -d "${_LINKF}" ] && \
238		install -d -o root -g wheel -m "${DIR_MODE}" "${_LINKF}"
239
240	rm -f ${COMPFILE}
241	(cd ${_LINKF} && ln -sf ${_LINKT} .)
242	return
243}
244
245merge_loop() {
246	if [ "$(expr "${MERGE_CMD}" : ^sdiff.*)" -gt 0 ]; then
247		echo "===> Type h at the sdiff prompt (%) to get usage help\n"
248	fi
249	MERGE_AGAIN=1
250	while [ "${MERGE_AGAIN}" ]; do
251		cp -p "${COMPFILE}" "${COMPFILE}.merged"
252		${MERGE_CMD} "${COMPFILE}.merged" \
253			"${DESTDIR}${COMPFILE#.}" "${COMPFILE}"
254		INSTALL_MERGED=v
255		while [ "${INSTALL_MERGED}" = "v" ]; do
256			echo ""
257			echo "  Use 'e' to edit the merged file"
258			echo "  Use 'i' to install the merged file"
259			echo "  Use 'n' to view a diff between the merged and new files"
260			echo "  Use 'o' to view a diff between the old and merged files"
261			echo "  Use 'r' to re-do the merge"
262			echo "  Use 'v' to view the merged file"
263			echo "  Use 'x' to delete the merged file and go back to previous menu"
264			echo "  Default is to leave the temporary file to deal with by hand"
265			echo ""
266			echo -n "===> How should I deal with the merged file? [Leave it for later] "
267			read INSTALL_MERGED
268			case "${INSTALL_MERGED}" in
269			[eE])
270				echo "editing merged file...\n"
271				if [ -z "${VISUAL}" ]; then
272					EDIT="${EDITOR:=/usr/bin/vi}"
273				else
274					EDIT="${VISUAL}"
275				fi
276				${EDIT} ${COMPFILE}.merged
277				INSTALL_MERGED=v
278				;;
279			[iI])
280				mv "${COMPFILE}.merged" "${COMPFILE}"
281				echo -n "\n===> Merging ${COMPFILE#.}"
282				if ! mm_install "${COMPFILE}"; then
283					echo "\t*** WARNING: problem merging ${COMPFILE#.}"
284				fi
285				unset MERGE_AGAIN
286				;;
287			[nN])
288				(
289					echo "comparison between merged and new files:\n"
290					diff -u ${COMPFILE}.merged ${COMPFILE}
291				) | ${PAGER}
292				INSTALL_MERGED=v
293				;;
294			[oO])
295				(
296					echo "comparison between old and merged files:\n"
297					diff -u ${DESTDIR}${COMPFILE#.} ${COMPFILE}.merged
298				) | ${PAGER}
299				INSTALL_MERGED=v
300				;;
301			[rR])
302				rm "${COMPFILE}.merged"
303				;;
304			[vV])
305				${PAGER} "${COMPFILE}.merged"
306				;;
307			[xX])
308				rm "${COMPFILE}.merged"
309				return 1
310				;;
311			'')
312				echo "===> ${COMPFILE} will remain for your consideration"
313				unset MERGE_AGAIN
314				;;
315			*)
316				echo "invalid choice: ${INSTALL_MERGED}"
317				INSTALL_MERGED=v
318				;;
319			esac
320		done
321	done
322}
323
324diff_loop() {
325	if [ "${BATCHMODE}" ]; then
326		HANDLE_COMPFILE=todo
327	else
328		HANDLE_COMPFILE=v
329	fi
330
331	unset NO_INSTALLED
332	unset CAN_INSTALL
333	unset FORCE_UPG
334
335	while [ "${HANDLE_COMPFILE}" = "v" -o "${HANDLE_COMPFILE}" = "todo" ]; do
336		if [ -f "${DESTDIR}${COMPFILE#.}" -a -f "${COMPFILE}" -a -z "${IS_LINK}" ]; then
337			if [ -z "${DIFFMODE}" ]; then
338				# automatically install files if current != new and current = old
339				for i in "${AUTO_UPG[@]}"; do
340					if [ "${i}" = "${COMPFILE}" ]; then
341						FORCE_UPG=1
342					fi
343				done
344				# automatically install files which differ only by CVS Id or that are binaries
345				if [ -z "$(diff -q -I'[$]OpenBSD:.*$' "${DESTDIR}${COMPFILE#.}" "${COMPFILE}")" -o -n "${FORCE_UPG}" -o -n "${IS_BINFILE}" ]; then
346					echo -n "===> Updating ${COMPFILE#.}"
347					if mm_install "${COMPFILE}"; then
348						AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n"
349					else
350						echo "\t*** WARNING: problem updating ${COMPFILE#.}"
351					fi
352					return
353				fi
354				# automatically install missing users
355				if [ "${COMPFILE}" = "./etc/master.passwd" ]; then
356					local _merge_pwd
357					while read l; do
358						_u=$(echo ${l} | awk -F ':' '{ print $1 }')
359						if [ "${_u}" != "root" ]; then
360							if [ -z "$(grep -E "^${_u}:" ${DESTDIR}${COMPFILE#.})" ]; then
361								echo "===> Adding the ${_u} user"
362								if [ "${DESTDIR}" ]; then
363									chroot ${DESTDIR} chpass -la "${l}"
364								else
365									chpass -la "${l}"
366								fi
367								if [ $? -eq 0 ]; then
368									set -A NEWUSR -- ${NEWUSR[@]} ${_u}
369								else
370									_merge_pwd=1
371								fi
372							fi
373						fi
374					done < ${COMPFILE}
375					if [ -z ${_merge_pwd} ]; then
376						rm "${TEMPROOT}${COMPFILE#.}"
377						return
378					fi
379				fi
380				# automatically install missing groups
381				if [ "${COMPFILE}" = "./etc/group" ]; then
382					local _merge_grp
383					while read l; do
384						_g=$(echo ${l} | awk -F ':' '{ print $1 }')
385						_gid=$(echo ${l} | awk -F ':' '{ print $3 }')
386						if [ -z "$(grep -E "^${_g}:" ${DESTDIR}${COMPFILE#.})" ]; then
387							echo "===> Adding the ${_g} group"
388							if [ "${DESTDIR}" ]; then
389								chroot ${DESTDIR} groupadd -g "${_gid}" "${_g}"
390							else
391								groupadd -g "${_gid}" "${_g}"
392							fi
393							if [ $? -eq 0 ]; then
394								set -A NEWGRP -- ${NEWGRP[@]} ${_g}
395							else
396								_merge_grp=1
397							fi
398						fi
399					done < ${COMPFILE}
400					if [ -z ${_merge_grp} ]; then
401						rm "${TEMPROOT}${COMPFILE#.}"
402						return
403					fi
404				fi
405			fi
406			if [ "${HANDLE_COMPFILE}" = "v" ]; then
407				(
408					echo "\n========================================================================\n"
409					echo "===> Displaying differences between ${COMPFILE} and installed version:"
410					echo ""
411					diff -u "${DESTDIR}${COMPFILE#.}" "${COMPFILE}"
412				) | ${PAGER}
413				echo ""
414			fi
415		else
416			# file does not exist on the target system
417			if [ "${IS_LINK}" ]; then
418				if [ -n "${DIFFMODE}" ]; then
419					echo ""
420					NO_INSTALLED=1
421				else
422					if mm_install_link; then
423						echo "===> ${COMPFILE#.} link created successfully"
424						AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n"
425					else
426						echo "\t*** WARNING: problem creating ${COMPFILE#.} link"
427					fi
428					return
429				fi
430			fi
431			if [ -n "${DIFFMODE}" ]; then
432				echo ""
433				NO_INSTALLED=1
434			else
435				echo -n "===> Installing ${COMPFILE#.}"
436				if mm_install "${COMPFILE}"; then
437					AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n"
438				else
439					echo "\t*** WARNING: problem installing ${COMPFILE#.}"
440				fi
441				return
442			fi
443		fi
444
445		if [ -z "${BATCHMODE}" ]; then
446			echo "  Use 'd' to delete the temporary ${COMPFILE}"
447			if [ "${COMPFILE}" != "./etc/master.passwd" -a "${COMPFILE}" != "./etc/group" -a "${COMPFILE}" != "./etc/hosts" ]; then
448				CAN_INSTALL=1
449				echo "  Use 'i' to install the temporary ${COMPFILE}"
450			fi
451			if [ -z "${NO_INSTALLED}" -a -z "${IS_BINFILE}" -a -z "${IS_LINK}" ]; then
452				echo "  Use 'm' to merge the temporary and installed versions"
453				echo "  Use 'v' to view the diff results again"
454			fi
455			echo ""
456			echo "  Default is to leave the temporary file to deal with by hand"
457			echo ""
458			echo -n "How should I deal with this? [Leave it for later] "
459			read HANDLE_COMPFILE
460		else
461			unset HANDLE_COMPFILE
462		fi
463
464		case "${HANDLE_COMPFILE}" in
465		[dD])
466			rm "${COMPFILE}"
467			echo "\n===> Deleting ${COMPFILE}"
468			;;
469		[iI])
470			if [ -n "${CAN_INSTALL}" ]; then
471				echo ""
472				if [ -n "${IS_LINK}" ]; then
473					if mm_install_link; then
474						echo "===> ${COMPFILE#.} link created successfully"
475						AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n"
476					else
477						echo "\t*** WARNING: problem creating ${COMPFILE#.} link"
478					fi
479				else
480					echo -n "===> Updating ${COMPFILE#.}"
481					if ! mm_install "${COMPFILE}"; then
482						echo "\t*** WARNING: problem updating ${COMPFILE#.}"
483					fi
484				fi
485			else
486				echo "invalid choice: ${HANDLE_COMPFILE}\n"
487				HANDLE_COMPFILE="todo"
488			fi
489
490			;;
491		[mM])
492			if [ -z "${NO_INSTALLED}" -a -z "${IS_BINFILE}" -a -z "${IS_LINK}" ]; then
493				merge_loop || HANDLE_COMPFILE="todo"
494			else
495				echo "invalid choice: ${HANDLE_COMPFILE}\n"
496				HANDLE_COMPFILE="todo"
497			fi
498			;;
499		[vV])
500			if [ -z "${NO_INSTALLED}" -a -z "${IS_BINFILE}" -a -z "${IS_LINK}" ]; then
501				HANDLE_COMPFILE="v"
502			else
503				echo "invalid choice: ${HANDLE_COMPFILE}\n"
504				HANDLE_COMPFILE="todo"
505			fi
506			;;
507		'')
508			echo "\n===> ${COMPFILE} will remain for your consideration"
509			;;
510		*)
511			echo "invalid choice: ${HANDLE_COMPFILE}\n"
512			HANDLE_COMPFILE="todo"
513			continue
514			;;
515		esac
516	done
517}
518
519do_compare() {
520	echo "===> Starting comparison"
521
522	cd ${TEMPROOT} || error_rm_wrkdir
523
524	# use -size +0 to avoid comparing empty log files and device nodes;
525	# however, we want to keep the symlinks; group and master.passwd
526	# need to be handled first in case mm_install needs a new user/group
527	local _c1="./etc/group ./etc/master.passwd"
528	local _c2=$(find . -type f -size +0 -or -type l | grep -vE '(./etc/group|./etc/master.passwd)')
529	for COMPFILE in ${_c1} ${_c2}; do
530		unset IS_BINFILE
531		unset IS_LINK
532		# links need to be treated in a different way
533		if [ -h "${COMPFILE}" ]; then
534			IS_LINK=1
535		fi
536		if [ ! -e "${DESTDIR}${COMPFILE#.}" ]; then
537			diff_loop
538			continue
539		fi
540
541		# compare CVS $Id's first so if the file hasn't been modified,
542		# it will be deleted from temproot and ignored from comparison.
543		# several files are generated from scripts so CVS ID is not a
544		# reliable way of detecting changes; leave for a full diff.
545		if [ -z "${DIFFMODE}" -a "${COMPFILE}" != "./etc/fbtab" \
546		    -a "${COMPFILE}" != "./etc/login.conf" \
547		    -a "${COMPFILE}" != "./etc/sysctl.conf" \
548		    -a "${COMPFILE}" != "./etc/ttys" -a -z "${IS_LINK}" ]; then
549			CVSID1=$(grep "[$]OpenBSD:" ${DESTDIR}${COMPFILE#.} 2>/dev/null)
550			CVSID2=$(grep "[$]OpenBSD:" ${COMPFILE} 2>/dev/null) || CVSID2=none
551			if [ "${CVSID2}" = "${CVSID1}" ]; then rm "${COMPFILE}"; fi
552		fi
553
554		if [ -f "${COMPFILE}" -a -z "${IS_LINK}" ]; then
555			# make sure files are different; if not, delete the one in temproot
556			if diff -q "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" >/dev/null 2>&1; then
557				rm "${COMPFILE}"
558			# xetcXX.tgz contains binary files; set IS_BINFILE to disable sdiff
559			elif diff -q "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" | grep "Binary" >/dev/null 2>&1; then
560				IS_BINFILE=1
561				diff_loop
562			else
563				diff_loop
564			fi
565		fi
566	done
567
568	echo "===> Comparison complete"
569}
570
571do_post() {
572	echo "===> Checking directory hierarchy permissions (running mtree(8))"
573	mtree -qdef ${DESTDIR}/etc/mtree/4.4BSD.dist -p ${DESTDIR:=/} -U >/dev/null
574	if [ -n "${XTGZ}" ]; then
575		mtree -qdef ${DESTDIR}/etc/mtree/BSD.x11.dist -p ${DESTDIR:=/} -U >/dev/null
576	fi
577
578	if [ "${NEED_NEWALIASES}" ]; then
579		echo "===> A new ${DESTDIR}/etc/mail/aliases file was installed." >> ${REPORT}
580		echo "However ${DESTDIR}/usr/bin/newaliases could not be run," >> ${REPORT}
581		echo "you will need to rebuild your aliases database manually.\n" >> ${REPORT}
582		unset NEED_NEWALIASES
583	fi
584
585	FILES_IN_TEMPROOT=$(find ${TEMPROOT} -type f ! -name \*.merged -size +0 2>/dev/null)
586	FILES_IN_BKPDIR=$(find ${BKPDIR} -type f -size +0 2>/dev/null)
587	if [ "${AUTO_INSTALLED_FILES}" ]; then
588		echo "===> Automatically installed file(s)" >> ${REPORT}
589		echo "${AUTO_INSTALLED_FILES}" >> ${REPORT}
590	fi
591	if [ "${FILES_IN_BKPDIR}" ]; then
592		echo "===> Backup of replaced file(s) can be found under" >> ${REPORT}
593		echo "${BKPDIR}\n" >> ${REPORT}
594	fi
595	if [ "${NEWUSR}" -o "${NEWGRP}" ]; then
596		echo "===> The following user(s)/group(s) have been added" >> ${REPORT}
597		if [ "${NEWUSR}" ]; then
598			echo -n "user(s): ${NEWUSR[@]}\n" >> ${REPORT}
599		fi
600		if [ "${NEWGRP}" ]; then
601			echo -n "group(s): ${NEWGRP[@]}\n" >> ${REPORT}
602		fi
603		echo "" >> ${REPORT}
604	fi
605	if [ "${FILES_IN_TEMPROOT}" ]; then
606		echo "===> File(s) remaining for you to merge by hand" >> ${REPORT}
607		echo "${FILES_IN_TEMPROOT}" >> ${REPORT}
608	fi
609
610	if [ -e "${REPORT}" ]; then
611		echo "===> Output log available at ${REPORT}"
612	else
613		echo "===> Removing ${WRKDIR}"
614		rm -rf "${WRKDIR}"
615	fi
616
617	if [ "${FILES_IN_TEMPROOT}" ]; then
618		echo "\t*** WARNING: some files are still left for comparison"
619	fi
620
621	if [ "${NEED_NEWALIASES}" ]; then
622		echo "\t*** WARNING: newaliases(8) failed to run properly"
623	fi
624
625	if [ "${NEED_REBOOT}" ]; then
626		echo "\t*** WARNING: some new/updated file(s) may require a reboot"
627	fi
628
629	unset FILES_IN_TEMPROOT NEED_NEWALIASES NEED_REBOOT
630
631	clean_src
632	rm -f ${DESTDIR}/${DBDIR}/.*.bak
633}
634
635while getopts bds:x: arg; do
636	case ${arg} in
637	b)
638		BATCHMODE=1
639		;;
640	d)
641		DIFFMODE=1
642		;;
643	s)
644		if [ -d "${OPTARG}" ]; then
645			SRCDIR=${OPTARG}
646		elif echo ${OPTARG} | \
647		    grep -qE '^(file|ftp|http|https)://.*/etc[0-9][0-9]\.tgz$'; then
648			TGZ=${WRKDIR}/etc.tgz
649			TGZURL=${OPTARG}
650			if ! ${FETCH_CMD} -o ${TGZ} ${TGZURL}; then
651				echo "\t*** ERROR: could not retrieve ${TGZURL}"
652				error_rm_wrkdir
653			fi
654		else
655			TGZ=${OPTARG}
656		fi
657		;;
658	x)
659		if echo ${OPTARG} | \
660		    grep -qE '^(file|ftp|http|https)://.*/xetc[0-9][0-9]\.tgz$'; then
661			XTGZ=${WRKDIR}/xetc.tgz
662			XTGZURL=${OPTARG}
663			if ! ${FETCH_CMD} -o ${XTGZ} ${XTGZURL}; then
664				echo "\t*** ERROR: could not retrieve ${XTGZURL}"
665				error_rm_wrkdir
666			fi
667		else
668			XTGZ=${OPTARG}
669		fi
670		;;
671	*)
672		usage
673		error_rm_wrkdir
674		;;
675	esac
676done
677
678shift $(( OPTIND -1 ))
679if [ $# -ne 0 ]; then
680	usage
681	error_rm_wrkdir
682fi
683
684if [ -z "${SRCDIR}" -a -z "${TGZ}" -a -z "${XTGZ}" ]; then
685	if [ -f "/usr/src/etc/Makefile" ]; then
686		SRCDIR=/usr/src
687	else
688		echo "\t*** ERROR: please specify a valid path to src or (x)etcXX.tgz"
689		usage
690		error_rm_wrkdir
691	fi
692fi
693
694if [ -n "${SRCDIR}" -a ! -f "${SRCDIR}/etc/Makefile" ]; then
695	echo "\t*** ERROR: ${SRCDIR} is not a valid path to src"
696	error_rm_wrkdir
697fi
698
699if [ -n "${TGZ}" ] && ! tar tzf ${TGZ} ./var/db/sysmerge/etcsum >/dev/null 2>&1; then
700	echo "\t*** ERROR: ${TGZ} is not a valid etcXX.tgz set"
701	error_rm_wrkdir
702fi
703
704if [ -n "${XTGZ}" ] && ! tar tzf ${XTGZ} ./var/db/sysmerge/xetcsum >/dev/null 2>&1; then
705	echo "\t*** ERROR: ${XTGZ} is not a valid xetcXX.tgz set"
706	error_rm_wrkdir
707fi
708
709TEMPROOT="${WRKDIR}/temproot"
710BKPDIR="${WRKDIR}/backups"
711
712do_populate
713do_compare
714do_post
715