xref: /openbsd-src/usr.sbin/sysmerge/sysmerge.sh (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1#!/bin/ksh -
2#
3# $OpenBSD: sysmerge.sh,v 1.82 2011/09/01 07:52:46 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		fi
205		export NEED_REBOOT=1
206		;;
207	/etc/mail/access|/etc/mail/genericstable|/etc/mail/mailertable|/etc/mail/virtusertable)
208		echo " (running makemap(8))"
209		DBFILE=`echo ${1} | sed -e 's,.*/,,'`
210		/usr/libexec/sendmail/makemap hash ${DESTDIR}/${1#.} < ${DESTDIR}/${1#.}
211		;;
212	/etc/mail/aliases)
213		echo " (running newaliases(8))"
214		if [ "${DESTDIR}" ]; then
215			chroot ${DESTDIR} newaliases >/dev/null || export NEED_NEWALIASES=1
216		else
217			newaliases >/dev/null
218		fi
219		;;
220	/etc/master.passwd)
221		echo " (running pwd_mkdb(8))"
222		pwd_mkdb -d ${DESTDIR}/etc -p ${DESTDIR}/etc/master.passwd
223		;;
224	*)
225		echo ""
226		;;
227	esac
228}
229
230mm_install_link() {
231	_LINKT=`readlink ${COMPFILE}`
232	_LINKF=`dirname ${DESTDIR}${COMPFILE#.}`
233
234	DIR_MODE=`stat -f "%OMp%OLp" "${TEMPROOT}/${_LINKF}"`
235	[ ! -d "${_LINKF}" ] && \
236		install -d -o root -g wheel -m "${DIR_MODE}" "${_LINKF}"
237
238	rm -f ${COMPFILE}
239	(cd ${_LINKF} && ln -sf ${_LINKT} .)
240	return
241}
242
243merge_loop() {
244	if [ "`expr "${MERGE_CMD}" : ^sdiff.*`" -gt 0 ]; then
245		echo "===> Type h at the sdiff prompt (%) to get usage help\n"
246	fi
247	MERGE_AGAIN=1
248	while [ "${MERGE_AGAIN}" ]; do
249		cp -p "${COMPFILE}" "${COMPFILE}.merged"
250		${MERGE_CMD} "${COMPFILE}.merged" \
251			"${DESTDIR}${COMPFILE#.}" "${COMPFILE}"
252		INSTALL_MERGED=v
253		while [ "${INSTALL_MERGED}" = "v" ]; do
254			echo ""
255			echo "  Use 'e' to edit the merged file"
256			echo "  Use 'i' to install the merged file"
257			echo "  Use 'n' to view a diff between the merged and new files"
258			echo "  Use 'o' to view a diff between the old and merged files"
259			echo "  Use 'r' to re-do the merge"
260			echo "  Use 'v' to view the merged file"
261			echo "  Use 'x' to delete the merged file and go back to previous menu"
262			echo "  Default is to leave the temporary file to deal with by hand"
263			echo ""
264			echo -n "===> How should I deal with the merged file? [Leave it for later] "
265			read INSTALL_MERGED
266			case "${INSTALL_MERGED}" in
267			[eE])
268				echo "editing merged file...\n"
269				if [ -z "${VISUAL}" ]; then
270					EDIT="${EDITOR:=/usr/bin/vi}"
271				else
272					EDIT="${VISUAL}"
273				fi
274				${EDIT} ${COMPFILE}.merged
275				INSTALL_MERGED=v
276				;;
277			[iI])
278				mv "${COMPFILE}.merged" "${COMPFILE}"
279				echo -n "\n===> Merging ${COMPFILE#.}"
280				if ! mm_install "${COMPFILE}"; then
281					echo "\t*** WARNING: problem merging ${COMPFILE#.}"
282				fi
283				unset MERGE_AGAIN
284				;;
285			[nN])
286				(
287					echo "comparison between merged and new files:\n"
288					diff -u ${COMPFILE}.merged ${COMPFILE}
289				) | ${PAGER}
290				INSTALL_MERGED=v
291				;;
292			[oO])
293				(
294					echo "comparison between old and merged files:\n"
295					diff -u ${DESTDIR}${COMPFILE#.} ${COMPFILE}.merged
296				) | ${PAGER}
297				INSTALL_MERGED=v
298				;;
299			[rR])
300				rm "${COMPFILE}.merged"
301				;;
302			[vV])
303				${PAGER} "${COMPFILE}.merged"
304				;;
305			[xX])
306				rm "${COMPFILE}.merged"
307				return 1
308				;;
309			'')
310				echo "===> ${COMPFILE} will remain for your consideration"
311				unset MERGE_AGAIN
312				;;
313			*)
314				echo "invalid choice: ${INSTALL_MERGED}"
315				INSTALL_MERGED=v
316				;;
317			esac
318		done
319	done
320}
321
322diff_loop() {
323	if [ "${BATCHMODE}" ]; then
324		HANDLE_COMPFILE=todo
325	else
326		HANDLE_COMPFILE=v
327	fi
328
329	unset NO_INSTALLED
330	unset CAN_INSTALL
331	unset FORCE_UPG
332
333	while [ "${HANDLE_COMPFILE}" = "v" -o "${HANDLE_COMPFILE}" = "todo" ]; do
334		if [ -f "${DESTDIR}${COMPFILE#.}" -a -f "${COMPFILE}" -a -z "${IS_LINK}" ]; then
335			if [ -z "${DIFFMODE}" ]; then
336				# automatically install files if current != new and current = old
337				for i in "${AUTO_UPG[@]}"; do
338					if [ "${i}" = "${COMPFILE}" ]; then
339						FORCE_UPG=1
340					fi
341				done
342				# automatically install files which differ only by CVS Id or that are binaries
343				if [ -z "`diff -q -I'[$]OpenBSD:.*$' "${DESTDIR}${COMPFILE#.}" "${COMPFILE}"`" -o -n "${FORCE_UPG}" -o -n "${IS_BINFILE}" ]; then
344					echo -n "===> Updating ${COMPFILE#.}"
345					if mm_install "${COMPFILE}"; then
346						AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n"
347					else
348						echo "\t*** WARNING: problem updating ${COMPFILE#.}"
349					fi
350					return
351				fi
352				# automatically install missing users
353				if [ "${COMPFILE}" = "./etc/master.passwd" ]; then
354					local _merge_pwd
355					while read l; do
356						_u=`echo ${l} | awk -F ':' '{ print $1 }'`
357						if [ "${_u}" != "root" ]; then
358							if [ -z "`grep -E "^${_u}:" ${DESTDIR}${COMPFILE#.}`" ]; then
359								echo "===> Adding the ${_u} user"
360								if [ "${DESTDIR}" ]; then
361									chroot ${DESTDIR} chpass -la "${l}"
362								else
363									chpass -la "${l}"
364								fi
365								if [ $? -eq 0 ]; then
366									set -A NEWUSR -- ${NEWUSR[@]} ${_u}
367								else
368									_merge_pwd=1
369								fi
370							fi
371						fi
372					done < ${COMPFILE}
373					if [ -z ${_merge_pwd} ]; then
374						rm "${TEMPROOT}${COMPFILE#.}"
375						return
376					fi
377				fi
378				# automatically install missing groups
379				if [ "${COMPFILE}" = "./etc/group" ]; then
380					local _merge_grp
381					while read l; do
382						_g=`echo ${l} | awk -F ':' '{ print $1 }'`
383						_gid=`echo ${l} | awk -F ':' '{ print $3 }'`
384						if [ -z "`grep -E "^${_g}:" ${DESTDIR}${COMPFILE#.}`" ]; then
385							echo "===> Adding the ${_g} group"
386							if [ "${DESTDIR}" ]; then
387								chroot ${DESTDIR} groupadd -g "${_gid}" "${_g}"
388							else
389								groupadd -g "${_gid}" "${_g}"
390							fi
391							if [ $? -eq 0 ]; then
392								set -A NEWGRP -- ${NEWGRP[@]} ${_g}
393							else
394								_merge_grp=1
395							fi
396						fi
397					done < ${COMPFILE}
398					if [ -z ${_merge_grp} ]; then
399						rm "${TEMPROOT}${COMPFILE#.}"
400						return
401					fi
402				fi
403			fi
404			if [ "${HANDLE_COMPFILE}" = "v" ]; then
405				(
406					echo "\n========================================================================\n"
407					echo "===> Displaying differences between ${COMPFILE} and installed version:"
408					echo ""
409					diff -u "${DESTDIR}${COMPFILE#.}" "${COMPFILE}"
410				) | ${PAGER}
411				echo ""
412			fi
413		else
414			# file does not exist on the target system
415			if [ "${IS_LINK}" ]; then
416				if [ -n "${DIFFMODE}" ]; then
417					echo ""
418					NO_INSTALLED=1
419				else
420					if mm_install_link; then
421						echo "===> ${COMPFILE#.} link created successfully"
422						AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n"
423					else
424						echo "\t*** WARNING: problem creating ${COMPFILE#.} link"
425					fi
426					return
427				fi
428			fi
429			if [ -n "${DIFFMODE}" ]; then
430				echo ""
431				NO_INSTALLED=1
432			else
433				echo -n "===> Installing ${COMPFILE#.}"
434				if mm_install "${COMPFILE}"; then
435					AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n"
436				else
437					echo "\t*** WARNING: problem installing ${COMPFILE#.}"
438				fi
439				return
440			fi
441		fi
442
443		if [ -z "${BATCHMODE}" ]; then
444			echo "  Use 'd' to delete the temporary ${COMPFILE}"
445			if [ "${COMPFILE}" != "./etc/master.passwd" -a "${COMPFILE}" != "./etc/group" -a "${COMPFILE}" != "./etc/hosts" ]; then
446				CAN_INSTALL=1
447				echo "  Use 'i' to install the temporary ${COMPFILE}"
448			fi
449			if [ -z "${NO_INSTALLED}" -a -z "${IS_BINFILE}" -a -z "${IS_LINK}" ]; then
450				echo "  Use 'm' to merge the temporary and installed versions"
451				echo "  Use 'v' to view the diff results again"
452			fi
453			echo ""
454			echo "  Default is to leave the temporary file to deal with by hand"
455			echo ""
456			echo -n "How should I deal with this? [Leave it for later] "
457			read HANDLE_COMPFILE
458		else
459			unset HANDLE_COMPFILE
460		fi
461
462		case "${HANDLE_COMPFILE}" in
463		[dD])
464			rm "${COMPFILE}"
465			echo "\n===> Deleting ${COMPFILE}"
466			;;
467		[iI])
468			if [ -n "${CAN_INSTALL}" ]; then
469				echo ""
470				if [ -n "${IS_LINK}" ]; then
471					if mm_install_link; then
472						echo "===> ${COMPFILE#.} link created successfully"
473						AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n"
474					else
475						echo "\t*** WARNING: problem creating ${COMPFILE#.} link"
476					fi
477				else
478					echo -n "===> Updating ${COMPFILE#.}"
479					if ! mm_install "${COMPFILE}"; then
480						echo "\t*** WARNING: problem updating ${COMPFILE#.}"
481					fi
482				fi
483			else
484				echo "invalid choice: ${HANDLE_COMPFILE}\n"
485				HANDLE_COMPFILE="todo"
486			fi
487
488			;;
489		[mM])
490			if [ -z "${NO_INSTALLED}" -a -z "${IS_BINFILE}" -a -z "${IS_LINK}" ]; then
491				merge_loop || HANDLE_COMPFILE="todo"
492			else
493				echo "invalid choice: ${HANDLE_COMPFILE}\n"
494				HANDLE_COMPFILE="todo"
495			fi
496			;;
497		[vV])
498			if [ -z "${NO_INSTALLED}" -a -z "${IS_BINFILE}" -a -z "${IS_LINK}" ]; then
499				HANDLE_COMPFILE="v"
500			else
501				echo "invalid choice: ${HANDLE_COMPFILE}\n"
502				HANDLE_COMPFILE="todo"
503			fi
504			;;
505		'')
506			echo "\n===> ${COMPFILE} will remain for your consideration"
507			;;
508		*)
509			echo "invalid choice: ${HANDLE_COMPFILE}\n"
510			HANDLE_COMPFILE="todo"
511			continue
512			;;
513		esac
514	done
515}
516
517do_compare() {
518	echo "===> Starting comparison"
519
520	cd ${TEMPROOT} || error_rm_wrkdir
521
522	# use -size +0 to avoid comparing empty log files and device nodes;
523	# however, we want to keep the symlinks; group and master.passwd
524	# need to be handled first in case mm_install needs a new user/group
525	local _c1="./etc/group ./etc/master.passwd"
526	local _c2=`find . -type f -size +0 -or -type l | grep -vE '(./etc/group|./etc/master.passwd)'`
527	for COMPFILE in ${_c1} ${_c2}; do
528		unset IS_BINFILE
529		unset IS_LINK
530		# links need to be treated in a different way
531		if [ -h "${COMPFILE}" ]; then
532			IS_LINK=1
533		fi
534		if [ ! -e "${DESTDIR}${COMPFILE#.}" ]; then
535			diff_loop
536			continue
537		fi
538
539		# compare CVS $Id's first so if the file hasn't been modified,
540		# it will be deleted from temproot and ignored from comparison.
541		# several files are generated from scripts so CVS ID is not a
542		# reliable way of detecting changes; leave for a full diff.
543		if [ -z "${DIFFMODE}" -a "${COMPFILE}" != "./etc/fbtab" \
544		    -a "${COMPFILE}" != "./etc/login.conf" \
545		    -a "${COMPFILE}" != "./etc/sysctl.conf" \
546		    -a "${COMPFILE}" != "./etc/ttys" -a -z "${IS_LINK}" ]; then
547			CVSID1=`grep "[$]OpenBSD:" ${DESTDIR}${COMPFILE#.} 2>/dev/null`
548			CVSID2=`grep "[$]OpenBSD:" ${COMPFILE} 2>/dev/null` || CVSID2=none
549			if [ "${CVSID2}" = "${CVSID1}" ]; then rm "${COMPFILE}"; fi
550		fi
551
552		if [ -f "${COMPFILE}" -a -z "${IS_LINK}" ]; then
553			# make sure files are different; if not, delete the one in temproot
554			if diff -q "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" >/dev/null 2>&1; then
555				rm "${COMPFILE}"
556			# xetcXX.tgz contains binary files; set IS_BINFILE to disable sdiff
557			elif diff -q "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" | grep "Binary" >/dev/null 2>&1; then
558				IS_BINFILE=1
559				diff_loop
560			else
561				diff_loop
562			fi
563		fi
564	done
565
566	echo "===> Comparison complete"
567}
568
569do_post() {
570	echo "===> Checking directory hierarchy permissions (running mtree(8))"
571	mtree -qdef ${DESTDIR}/etc/mtree/4.4BSD.dist -p ${DESTDIR:=/} -U >/dev/null
572	if [ -n "${XTGZ}" ]; then
573		mtree -qdef ${DESTDIR}/etc/mtree/BSD.x11.dist -p ${DESTDIR:=/} -U >/dev/null
574	fi
575
576	if [ "${NEED_NEWALIASES}" ]; then
577		echo "===> A new ${DESTDIR}/etc/mail/aliases file was installed." >> ${REPORT}
578		echo "However ${DESTDIR}/usr/bin/newaliases could not be run," >> ${REPORT}
579		echo "you will need to rebuild your aliases database manually.\n" >> ${REPORT}
580		unset NEED_NEWALIASES
581	fi
582
583	FILES_IN_TEMPROOT=`find ${TEMPROOT} -type f ! -name \*.merged -size +0 2>/dev/null`
584	FILES_IN_BKPDIR=`find ${BKPDIR} -type f -size +0 2>/dev/null`
585	if [ "${AUTO_INSTALLED_FILES}" ]; then
586		echo "===> Automatically installed file(s)" >> ${REPORT}
587		echo "${AUTO_INSTALLED_FILES}" >> ${REPORT}
588	fi
589	if [ "${FILES_IN_BKPDIR}" ]; then
590		echo "===> Backup of replaced file(s) can be found under" >> ${REPORT}
591		echo "${BKPDIR}\n" >> ${REPORT}
592	fi
593	if [ "${NEWUSR}" -o "${NEWGRP}" ]; then
594		echo "===> The following user(s)/group(s) have been added" >> ${REPORT}
595		if [ "${NEWUSR}" ]; then
596			echo -n "user(s): ${NEWUSR[@]}\n" >> ${REPORT}
597		fi
598		if [ "${NEWGRP}" ]; then
599			echo -n "group(s): ${NEWGRP[@]}\n" >> ${REPORT}
600		fi
601		echo "" >> ${REPORT}
602	fi
603	if [ "${FILES_IN_TEMPROOT}" ]; then
604		echo "===> File(s) remaining for you to merge by hand" >> ${REPORT}
605		echo "${FILES_IN_TEMPROOT}" >> ${REPORT}
606	fi
607
608	if [ -e "${REPORT}" ]; then
609		echo "===> Output log available at ${REPORT}"
610	else
611		echo "===> Removing ${WRKDIR}"
612		rm -rf "${WRKDIR}"
613	fi
614
615	if [ "${FILES_IN_TEMPROOT}" ]; then
616		echo "\t*** WARNING: some files are still left for comparison"
617	fi
618
619	if [ "${NEED_NEWALIASES}" ]; then
620		echo "\t*** WARNING: newaliases(8) failed to run properly"
621	fi
622
623	if [ "${NEED_REBOOT}" ]; then
624		echo "\t*** WARNING: some new/updated file(s) may require a reboot"
625	fi
626
627	unset FILES_IN_TEMPROOT NEED_NEWALIASES NEED_REBOOT
628
629	clean_src
630	rm -f ${DESTDIR}/${DBDIR}/.*.bak
631}
632
633while getopts bds:x: arg; do
634	case ${arg} in
635	b)
636		BATCHMODE=1
637		;;
638	d)
639		DIFFMODE=1
640		;;
641	s)
642		if [ -d "${OPTARG}" ]; then
643			SRCDIR=${OPTARG}
644		elif echo ${OPTARG} | \
645		    grep -qE '^(file|ftp|http|https)://.*/etc[0-9][0-9]\.tgz$'; then
646			TGZ=${WRKDIR}/etc.tgz
647			TGZURL=${OPTARG}
648			if ! ${FETCH_CMD} -o ${TGZ} ${TGZURL}; then
649				echo "\t*** ERROR: could not retrieve ${TGZURL}"
650				error_rm_wrkdir
651			fi
652		else
653			TGZ=${OPTARG}
654		fi
655		;;
656	x)
657		if echo ${OPTARG} | \
658		    grep -qE '^(file|ftp|http|https)://.*/xetc[0-9][0-9]\.tgz$'; then
659			XTGZ=${WRKDIR}/xetc.tgz
660			XTGZURL=${OPTARG}
661			if ! ${FETCH_CMD} -o ${XTGZ} ${XTGZURL}; then
662				echo "\t*** ERROR: could not retrieve ${XTGZURL}"
663				error_rm_wrkdir
664			fi
665		else
666			XTGZ=${OPTARG}
667		fi
668		;;
669	*)
670		usage
671		error_rm_wrkdir
672		;;
673	esac
674done
675
676shift $(( OPTIND -1 ))
677if [ $# -ne 0 ]; then
678	usage
679	error_rm_wrkdir
680fi
681
682if [ -z "${SRCDIR}" -a -z "${TGZ}" -a -z "${XTGZ}" ]; then
683	if [ -f "/usr/src/etc/Makefile" ]; then
684		SRCDIR=/usr/src
685	else
686		echo "\t*** ERROR: please specify a valid path to src or (x)etcXX.tgz"
687		usage
688		error_rm_wrkdir
689	fi
690fi
691
692if [ -n "${SRCDIR}" -a ! -f "${SRCDIR}/etc/Makefile" ]; then
693	echo "\t*** ERROR: ${SRCDIR} is not a valid path to src"
694	error_rm_wrkdir
695fi
696
697if [ -n "${TGZ}" ] && ! tar tzf ${TGZ} ./var/db/sysmerge/etcsum >/dev/null 2>&1; then
698	echo "\t*** ERROR: ${TGZ} is not a valid etcXX.tgz set"
699	error_rm_wrkdir
700fi
701
702if [ -n "${XTGZ}" ] && ! tar tzf ${XTGZ} ./var/db/sysmerge/xetcsum >/dev/null 2>&1; then
703	echo "\t*** ERROR: ${XTGZ} is not a valid xetcXX.tgz set"
704	error_rm_wrkdir
705fi
706
707TEMPROOT="${WRKDIR}/temproot"
708BKPDIR="${WRKDIR}/backups"
709
710do_populate
711do_compare
712do_post
713