#!/bin/sh

# Shell script to collect and compare configuration data for SCC.
# Copyright (C) 2001-2004 Open Challenge B.V.
# Copyright (C) 2004-2005 OpenEyeT Professional Services.
# Copyright (C) 2005-2009 QNH.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.
# If not, write to the Free Software Foundation,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Contact information: http://scc.qnh-infrastructure.nl/index.html 

# SCC-release:	1.9.28
# file-version:	$Revision: 1.154 $


set -u

ProgName=${0##*/};			export ProgName

SCC_BIN=/opt/scc/bin
SCC_TOP=/var/opt/scc
export SCC_BIN SCC_TOP

# Perform the security settings before calling any program.
PATH=/sbin:/usr/sbin:/usr/bin:/bin:${SCC_BIN};	export PATH

# Add the directories containing collections of symlinks to programs on Syllable:
for dir in /boot/system/bin /usr/indexes/bin /usr/indexes/sbin /system/indexes/bin /system/indexes/sbin
do
	if [ -d "${dir}" ]
	then
		PATH="${PATH}:${dir}"
	fi
done

export SCC_DATA=${SCC_TOP}/data
export SCC_TMP=${SCC_TOP}/tmp
export SCC_CONF=/etc/opt/scc/conf

export TMPDIR=${SCC_TMP}
export TMP=${SCC_TMP}

export SHELL=/bin/sh
export LANG=C

umask 077

mkdir -p ${SCC_DATA}/transfer ${SCC_TMP} 2>/dev/null

cd ${SCC_TMP}

. ${SCC_BIN}/scc_modules/scc_utils

# PuppyLinux does not provide nice.
n_exe="$(which nice 2>/dev/null)"
if [ ! -x "${n_exe}" ]
then
	alias nice=""
fi

name=$(hostname)
name=${name%%.*}
if [ -z "${name}" ]
then
	name="empty"			# same value as in scc and scc-collect
fi

index=${SCC_DATA}/index.html
lock_cfg=${SCC_DATA}/scc.${name}.lock
ito_log=${SCC_DATA}/cfg.log

split_file=${SCC_CONF}/scc-split.conf

export TMP1_FILE=${SCC_TMP}/scc_log1_$$
export TMP2_FILE=${SCC_TMP}/scc_log2_$$
export TMP3_FILE=${SCC_TMP}/scc_log3_$$

export PROC_FILE=${SCC_TMP}/scc_log_ps_$$

# File, used in release 1.1.30, no longer needed
rm -f ${SCC_DATA}/scc_rpm_keep

# Produce head for html-file on stdout.
# Argument: title
scc_html_head( )
{
	echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">'
	echo "<HTML>"
	echo "<HEAD>"
	echo '	<LINK HREF="style.css" REL="stylesheet" TYPE="text/css">'
	echo "	<TITLE>Index for ${1}</TITLE>"
	echo "</HEAD>"
}

scc_check_snapshot()
{
	awk '/^fix:messages:/	{
					# When a module signals an error, the syntax of the message-line is correct
					visited[ "fix:messages::" ] = 1;
					compare[ ":messages:" ] = 1;
			}
		/^fix:|^var:|^hlp:/	{
				class=$0;
				sub( /::.*/, "::", class );
				cnt=split( class, part, ":" );

				if ( visited[ class ] )			# Each visited class already has been checked.
				{
					print $0;			# print, but do not check again.
					next;
				}

				# line with classification with minimal number of elements:
				#	fix:general::			# cnt is 4
				# line with classification with maximal number of elements:
				#	fix:1:2:3:4:5:6:7:8:9:10::	# cnt is 13
				if ( cnt < 4 )
				{
					print "fix:messages::not enough fields in classification:", $0;
				}
				else if ( cnt > 13 )
				{
					print "fix:messages::too many fields in classification:", $0;
				}
				else if ( ( class == $0 ) && ( class !~ "::$" ) )
				{
					print "fix:messages::no end of classification:", $0;
				}
				else
				{
					visited[ class ]=1;

					# A class conflict occurs in the following case:
					# fix:a:b::data
					# fix:a:b:c::data
					# Either a:b is the classification of configuration data,
					# or it is the entry-point for a sub-menu.
					# Help-info can be available for a sub-menu.
					# Therefore no class conflicts for help-info.
					if ( class !~ "^hlp:" )
					{
						sub( /^.../, "", class );	# erase fix or var or hlp
						sub( /::$/, ":", class );	# map :a:: to :a: to check for :a:b:
						for ( c in compare )
						{
							if ( c == class )
							{
								break;
							}
							if ( index( c, class ) == 1 )
							{
								print "fix:messages::class conflict for \"" class "\" and \"" c "\"";
								break;
							}
							if ( index( class, c ) == 1 )
							{
								print "fix:messages::class conflict for \"" c "\" and \"" class "\"";
								break;
							}
						}
						compare[ class ]=1;
					}
					print $0;
				}
				next;
			}
			{
				print "fix:messages::unknown prefix:" $0;
			}'
	return 0;
}

# Check for the conditions in a split-file.
# ARGUMENTS
#	<condition>	"absent" or "present"
#	<arg>		possible formats are:
#			<file>
#			<user>|<process>
check_split_condition()
{
	if [ "${2}" != "${2#*|}" ]
	then
		# This is a very simple process checker.
		user="${2%|*}"		# Split $2 into <user> and <proc>
		proc="${2#*|}"
		awk	'{
				if ( $1 != u )
				{
					next;
				}
				if ( $0 ~ p )
				{
					exit 2;
				}
			}' u="${user}" p="${proc}" ${PROC_FILE}
		ret=$?
		if [ "${1}" = "present" -a "${ret}" -eq 2 ]
		then
			return 1;
		fi
		if [ "${1}" = "absent" -a "${ret}" -ne 2 ]
		then
			return 1;
		fi
	else
		if [ "${1}" = "present" -a -f "${2}" ]
		then
			return 1;
		fi
		if [ "${1}" = "absent" -a ! -f "${2}" ]
		then
			return 1;
		fi
	fi

	return 0
}

# Some commands might "hang", use a lock-file to avoid that more and more
# invocations of Scc start and swamp the system.
if [ -f ${lock_cfg} ]
then
	running=1
	pid=$(<${lock_cfg})
	if [ -n "${pid}" ]
	then
		# Check in a non-destructive way whether the process is still running.
		kill -0 ${pid} 2>/dev/null
		if [ $? -ne 0 ]
		then
			# Unable to signal the process: it is no longer active.
			# Maybe hard-killed during a shutdown.
			running=0
		fi
	fi
	if [ ${running} -eq 1 ]
	then
		echo "${ProgName}: another instance is active, check process ID in ${lock_cfg}" >&2
		exit 2
	fi

	rm -f ${lock_cfg}
fi

# Do not install the traps earlier, as the above exit will remove the
# lock file from the other, running invocation of this program.
trap "rm -f ${TMP1_FILE} ${TMP2_FILE} ${TMP3_FILE} ${lock_cfg} ${PROC_FILE}" 0
trap "exit 2" 1 2 3 15

# Record our PID in the lock-file
echo "$$" > ${lock_cfg}
interactive=""
remark=""
max_months=0			# No limit
mods=""
restart=0
uninstall=0
virtual_host=0
while [ $# -gt 0 ]
do
	if [ $# -eq 1 ]
	then
		# All options, except the ones in the following list, should have an argument.
		if [ "x${1}" != "x-r" -a "x${1}" != "x-u" ]
		then
			echo "${ProgName}: Syntax error, missing argument for option: $1" 
			exit 2
		fi
	fi

	case ${1} in
	-c)	remark=$(echo "${2}" | tr -d "\012")	# be sure that the remark is single-line
		shift 2;;

	-e)	mods="${2}";
		shift 2;;

	-m)	case "${2}" in
		[0-9]*)	max_months=${2};;
		*)	echo "${ProgName}: non-numeric argument for -m option: ${2}" >&2
			exit 1;;
		esac
		shift 2;;

	-r)	restart=1
		shift 1;;

	-u)	uninstall=1
		shift 1;;

	-v)	interactive=" -i "
		virtual_host=1
		name="${2}"		# Act as data is originating from virtual host.
		shift 2;;

	*)	echo "Syntax error, use ${ProgName} [ -c <remark> ] [ -e <mod> ] [ -m <max> ] [ -r | -u ] [ -v <host>" >&2
		exit 1;;
	esac
done

if [ ${virtual_host} -eq 1 -a -z "${mods}" ]
then
	echo "${ProgName}: use -e option with -v option" >&2
	exit 1
fi

log_cfg=${SCC_DATA}/scc.${name}.log
keep=${SCC_DATA}/scc.${name}.keep

split_hosts=""
if [ -s "${split_file}" -a ${virtual_host} -eq 0 ]
then
	split_hosts="$(awk -F"|" '/^host\|/ { print $2 }' ${split_file} | sort -u)"
fi

if [ ${restart} -eq 1 ]
then
	# Erase the current snapshot(s) to indicate the restart.
	rm -f ${SCC_DATA}/scc.${name}.cur
	if [ ${virtual_host} -eq 0 ]
	then
		for h in ${split_hosts}
		do
			rm -f ${SCC_DATA}/scc.${h}.cur
		done
	fi
fi

if [ ${uninstall} -eq 1 ]
then
	for h in ${name} ${split_hosts}
	do
		rm -f ${SCC_DATA}/scc.${h}.*
	done
	rm -f ${ito_log} ${lock_cfg} ${keep}
	exit 0
fi

if [ ${virtual_host} -eq 0 ]
then
	# For some programs that inspect the logbook, "fixed" filenames are easier. 
	# Use a symbolic link to save disk-space.
	if [ -h ${ito_log} ]
	then
		# Check whether the target of the symlink corresponds to ${log_cfg}.
		# When this is not the case, the hostname of the system changed.
		prev_host="$(ls -l ${ito_log}	|
			awk '{ sub( /\.log$/, "", $NF ); sub( /.*scc\./, "", $NF ); print $NF }')"
		if [ "${prev_host}" != "${name}" ]
		then
			# We have to move the files to reflect the new hostname.
			for suffix in cur html log log.html keep old
			do
				mv ${SCC_DATA}/scc.${prev_host}.${suffix} ${SCC_DATA}/scc.${name}.${suffix} 2>/dev/null
			done

			# The next files are present when sort is implemented by busybox.
			for suffix in flat.html flat.log.html
			do
				if [ -f ${SCC_DATA}/scc.${prev_host}_${suffix} ]
				then
					mv ${SCC_DATA}/scc.${prev_host}_${suffix} ${SCC_DATA}/scc.${name}_${suffix}
				fi
			done

			if [ -z "${remark}" ]		# Do not change remark supplied with -c option.
			then
				remark="Hostname changed from ${prev_host} to ${name}"
			fi
		fi
	fi
	rm -f ${ito_log}
	touch ${log_cfg}
	ln -s ${log_cfg} ${ito_log}
fi

# Reduce the log file to the specified number of months.
if [ ${max_months} -gt 0 ]
then
	limit=$(( ( $(date '+%Y') * 12 ) + $(date '+%m') - ${max_months} ))

	for h in ${name} ${split_hosts}
	do
		echo "${SCC_DATA}/scc.${h}.log"
	done				|
	sort -u				|
	while read logfile
	do
		first=$(head -n 1 ${logfile} | awk -F- '{ print ( ( $1 * 12 ) + $2 ) }' )
		if [ -n "${first}" -a ${first} -lt ${limit} ]
		then
			# Some of the log-entries are too old.
			awk -F- '{
					if ( ( ( $1 * 12 ) + $2 ) >= l )
					{
						print;
					}
			}' l="${limit}" ${logfile} >${TMP1_FILE}

			# The html-version of the log file is generated from scratch out of the shorter log file.
			mv ${TMP1_FILE} ${logfile}
		fi
	done
fi	# if [ ${max_months} -gt 0 ]

# Record the start of collecting.
d_n=$(date '+%Y-%m-%d')
t_n_b=$(date '+%H.%M.%S')
s=${t_n_b#*.*.}		# seconds
s=${s#0}		# strip leading "0"
h=${t_n_b%.*.*}		# hours
h=${h#0}		# strip leading "0"
m=${t_n_b#*.}		# minutes and seconds
m=${m%.*}		# minutes
m=${m#0}		# strip leading "0"
tick_start=$(( ${s} + ( 60 * ${m} ) + ( 3600 * ${h} ) ))

# Run scc-collect in a "clean" environment.
# It needs TZ to use the correct time-settings.
# It needs ODMDIR to use the correct object database for AIX.
# It uses SCC_PROFILING to determine whether to add performance data to the snapshot.
# It uses SCC_IGNORE_STM to determine whether to run stm or not.
# It uses SCC_INSTALL_PHASE to determine whether SCC is called during installation.
# It uses SCC_INSTALL_METHOD to know whether source installs should be recorded.
nice env -i						\
	TZ="${TZ:-}"					\
	ODMDIR="${ODMDIR:-}"				\
	SCC_PROFILING="${SCC_PROFILING:-yes}"		\
	SCC_IGNORE_STM="${SCC_IGNORE_STM:-}"		\
	SCC_INSTALL_PHASE="${SCC_INSTALL_PHASE:-}"	\
	SCC_INSTALL_METHOD="${SCC_INSTALL_METHOD:-}"	\
	LOGNAME="root"					\
		${SCC_BIN}/scc-collect -e "${mods}" ${interactive} </dev/null >${TMP3_FILE} 2>&1 

# Record the end of collecting.
t_n_e=$(date '+%H.%M.%S')

s=${t_n_e#*.*.}		# seconds
s=${s#0}		# strip leading "0"
h=${t_n_e%.*.*}		# hours
h=${h#0}		# strip leading "0"
m=${t_n_e#*.}		# minutes and seconds
m=${m%.*}		# minutes
m=${m#0}		# strip leading "0"
tick_end=$(( ${s} + ( 60 * ${m} ) + ( 3600 * ${h} ) ))
if [ ${tick_end} -lt ${tick_start} ]
then
	tick_end=$(( ${tick_end} + ( 24 * 3600 ) ))
fi
runtime="$(( ${tick_end} - ${tick_start} ))"

# Check the syntax of the new snapshot.
rm -f ${TMP1_FILE}
sed	-e 's/[ 	][ 	]*$//'	\
	-e '/^$/d' ${TMP3_FILE}				|
scc_check_snapshot >${TMP2_FILE}

rm -f ${TMP1_FILE} ${TMP3_FILE}

# Do not use the split-file when -v option is used.
if [ -s ${split_file} -a ${virtual_host} -eq 0 ]
then
	# The file ${PROC_FILE} only exists during the collection of data.
	# Extract its contents from the new snapshot.
	sed -n -e "s/^var:system:processes:://p" <${TMP2_FILE} >${PROC_FILE}

	# Split the new snapshot according to the split-file.
	# Produce the new snapshots for all pseudo hosts in the splitfile.
	# ${TMP2_FILE} contains the collected data.
	for host in ${split_hosts}
	do
		rm -f ${SCC_DATA}/scc.${host}.new

		# Check whether pseudo host is active/present.
		check="$(sed -n -e "s/^host|${host}|present|//p" ${split_file})"
		if [ -n "${check}" ]
		then
			check_split_condition present "${check}"
			if [ $? -eq 0 ]
			then
				continue
			fi
		fi
		check="$(sed -n -e "s/^host|${host}|absent|//p" ${split_file})"
		if [ -n "${check}" ]
		then
			check_split_condition absent "${check}"
			if [ $? -eq 0 ]
			then
				continue
			fi
		fi

		# Collect all the classifications that have to be moved/copied.
		cp_file=${SCC_TMP}/${host}.cp.$$
		del_file=${SCC_TMP}/${host}.del.$$
		rm -f ${cp_file} ${del_file}

		awk -F"|"	'/^[ 	]*#/	{ next }
				/^[ 	]*$/	{ next }
						{
							if ( $2 != h )
							{
								next;
							}
							data = "";
							n_class = "";
							del = 0;
							new = 0;
						}
				/^class\|.*\|rm\|/	{
							data = $4;
							del = 1;
						}
				/^class\|.*\|cp\|/	{
							data = $4;
							n_class = $4;
							if ( NF == 5 )
							{
								n_class = $5;
							}
							new = 1;
						}
				/^class\|.*\|mv\|/	{
							data = $4;
							n_class = $4;
							if ( NF == 5 )
							{
								n_class = $5;
							}
							del = 1;
							new = 1;
						}
						{
							if ( new == 0 && del == 0 )
							{
								next;
							}

							if ( length( n_class ) == 0 )
							{
								n_class = ":"
							}

							if ( data ~ "^fix:" || data ~ "^var:" )
							{
								# Do not extend.
								if ( new )
								{
									# n_class should also start with "fix:" or "var:"
									printf( "s%s^%s%s%s%sp\n",	\
										sep, data, sep,		\
										n_class, sep ) >>cp_file
								}
								if ( del )
								{
									print data >>del_file;
								}
							}
							else
							{
								# Extend optional with fix, var and hlp.
								col = ":"
								if ( data ~ "^:" )
								{
									col = "";
								}
								if ( new )
								{
									printf( "s%s^fix%s%s%sfix%s%s%sp\n",	\
										sep, col, data, sep,		\
										col, n_class, sep ) >>cp_file
									printf( "s%s^var%s%s%svar%s%s%sp\n",	\
										sep, col, data, sep,		\
										col, n_class, sep ) >>cp_file
									printf( "s%s^hlp%s%s%shlp%s%s%sp\n",	\
										sep, col, data, sep,		\
										col, n_class, sep ) >>cp_file
								}

								if ( del )
								{
									print "fix" col data >>del_file;
									print "var" col data >>del_file;
									print "hlp" col data >>del_file;
								}
							}
							next;
						}'					\
							cp_file="${cp_file}"		\
							del_file="${del_file}"		\
							h="${host}"			\
							sep='\001'			\
								${split_file}

		if [ -s ${cp_file} ]		# Something to copy?
		then
			sed -n -f ${cp_file} ${TMP2_FILE}		|
			scc_check_snapshot >${TMP1_FILE}

			{
				grep -l "^fix:messages::" ${TMP1_FILE} >/dev/null 2>/dev/null
				if [ $? -eq 0 ]
				then
					echo "fix:messages::inspect scc.${host}.cur to determine cause of messages in system/user-modules"
				fi
				echo "var:general::date:${d_n}"
				echo "var:general::start time:${t_n_b}"
				echo "var:general::stop time :${t_n_e}"
				echo "var:general::runtime:${runtime}"
				echo "fix:general::hostname:${host}"
				echo "var:general::remark:${remark}"
				if [ ${restart} -eq 1 ]
				then
					echo "var:general::restart:true"
				fi

				# Get the specification for this host from the split-file.
				# Put them in the general part of the snapshot.
				awk -F"|"	'/^[ 	]*#/	{ next }
								{
									if ( $2 != h )
									{
										next;
									}
									print "fix:general::split-file:" $0;
								}' h="${host}" ${split_file}

				cat ${TMP1_FILE}
				rm -f ${TMP1_FILE}
			} >${SCC_DATA}/scc.${host}.new
		fi
		if [ -s ${del_file} ]		# Something to remove?
		then
			grep -v -f ${del_file} ${TMP2_FILE} >${TMP2_FILE}.tmp
			mv ${TMP2_FILE}.tmp ${TMP2_FILE}
		fi

		rm -f ${cp_file} ${del_file}

	done	# while read host
fi	# if [ -s ${split_file} ]

# The data of the pseudo hosts has been split off from the entire scc-data of the host.
# Now we can generate the new snapshot for the local host.
{
	grep -l "^fix:messages::" ${TMP2_FILE} >/dev/null 2>/dev/null
	if [ $? -eq 0 ]
	then
		echo "fix:messages::inspect scc.${name}.cur to determine cause of messages in system/user-modules"
	fi
	echo "var:general::date:${d_n}"
	echo "var:general::start time:${t_n_b}"
	echo "var:general::stop time :${t_n_e}"
	echo "var:general::runtime:${runtime}"
	cat ${TMP2_FILE}
	rm -f ${TMP2_FILE}
} >${SCC_DATA}/scc.${name}.new

# When diff is implemented by BusyBox, we cannot (yet) parse the differences for the logbook.
# In that case, all compares should be done on the server.
export REMOTE_COMPARE=""
if [ -x /bin/busybox ]
then
	busybox_check diff
	if [ $? -ne 0 ]
	then
		export REMOTE_COMPARE=yes
	fi
fi

# Compare with previous snapshot and generate html-files.
{
	echo "${name} local"
	if [ -s ${split_file} -a ${virtual_host} -eq 0 ]
	then
		awk -F"|" '/^host\|/ { if ( $3 == "local" || $3 == "remote" ) print $2, $3 }' ${split_file}
	fi
}				|
sort -u				|
while read host compare
do
	if [ "${REMOTE_COMPARE}" ]
	then
		compare=remote
	fi

	new_snapshot=${SCC_DATA}/scc.${host}.new
	prev_snapshot=${SCC_DATA}/scc.${host}.cur
	old_snapshot=${SCC_DATA}/scc.${host}.old
	logfile=${SCC_DATA}/scc.${host}.log

	if [ ! -f ${SCC_DATA}/scc.${host}.new ]
	then
		# No data split off.
		if [ "${compare}" = "local" ]
		then
			# No new data available, do nothing. Previous data will be send to server.
			:
		else
			# Remove all files, to avoid that anything is send to the server.
			rm -f	${SCC_DATA}/scc.${host}.cur	\
				${SCC_DATA}/scc.${host}.log	\
				${SCC_DATA}/scc.${host}.html	\
				${SCC_DATA}/scc.${host}.log.html
		fi
		continue
	fi

	if [ "${compare}" = "local" ]
	then
		# During the post-install run of each install, rpm and pkg_info cannot be used and
		# the snapshot does not contain any rpm- and pkg_info data. During runs after the
		# installs, rpm- and pkg_info data is collected and copied to the keep-file. During 
		# post-install runs of upgrades, the data (of the previous run) is copies from the
		# keep file. This strategy does not work immediately after the initial install as
		# the keep-file is empty in this case. Ignore corresponding log-messages, but signal
		# this in the remark in the logfile (when the -c option was not used).
		log_remark="${remark}"

		# To avoid some superfluous changes, we ignore some data.
		# We use a sed file to remove data from the snapshot prior to comparing.
		# Paul te Vaanholt indicated problems with the previous construction.
		>${TMP1_FILE} 

		# During install, the RPM-database is locked and cannot return
		# the installed rpm's. After the installation, the new snapshot
		# contains rpm-data, but the old snapshot does not contain
		# rpm-data. Avoid messages in the log file, by ignoring all
		# rpm-data when the old (current) snapshot does not contain
		# any rpm-data. 
		# Ignore this when rpm is implemented by busybox.
		busybox_check rpm
		if [ $? -eq 0 -a -x /bin/rpm -a -f "${prev_snapshot}" -a -z "${SCC_INSTALL_PHASE:-}" ]
		then
			if [ "$(/bin/rpm -qa | wc -l)" -ne 0 ]
			then
				grep -l "^fix:software:installed-rpms:" "${prev_snapshot}" >/dev/null 2>/dev/null
				if [ $? -ne 0 ]
				then
					# No rpm-data in current (previous) snapshot.
					echo "/^new::software:installed-rpms:/d" >>${TMP1_FILE}

					[ -z "${log_remark}" ] && log_remark="rpm changes are ignored on the first run after the initial install"
				fi
				grep -l "^fix:software:rpm - imported keys:" "${prev_snapshot}" >/dev/null 2>/dev/null
				if [ $? -ne 0 ]
				then
					# No rpm-data in current (previous) snapshot.
					echo "/^new::software:rpm - imported keys:/d" >>${TMP1_FILE}

					[ -z "${log_remark}" ] && log_remark="rpm changes are ignored on the first run after the initial install"
				fi
			fi
		fi

		# During install, the BSD package database is locked and cannot return
		# the installed packages. After the installation, the new snapshot
		# contains package-data, but the old snapshot does not contain
		# package-data. Avoid messages in the log file, by ignoring all
		# package-data when the old (current) snapshot does not contain
		# any package-data. 
		if [ -x /usr/sbin/pkg_info -o -x /usr/pkg/sbin/pkg_info ]
		then
			if [ -f "${prev_snapshot}" ]
			then
				grep -l "^fix:software:installed-BSD-packages:" "${prev_snapshot}" >/dev/null 2>/dev/null
				if [ $? -ne 0 ]
				then
					# No package-data in current (previous) snapshot.
					echo "/^new::software:installed-BSD-packages:/d" >>${TMP1_FILE}

					[ -z "${log_remark}" ] && log_remark="pkginfo changes are ignored on the first run after the initial install"
				fi
			fi
		fi

		# Moving data from the main-snapshot through the split-file should not
		# add to the differences when the split-file is created or extended.
		# Only appplicable for the "main"-snapshot.
		if [ "${host}" = "${name}" -a -f ${split_file} -a -f "${prev_snapshot}" ]
		then
			# Retrieve the classifications of data that is moved.
			# Adjust them to delete-statements in a sed-file.
			# Replace all '/' by '\/' to avoid syntax errors.
			sed	-n					\
				-e 's/|$//'				\
				-e 's@^class|.*|mv|\(.*\)|.*@\1@p'	\
				-e 's@^class|.*|mv|\(.*\)@\1@p'		\
					<${split_file}				|
			sed	-e 's@/@\\/@g'	\
				-e 's@^@/@'	\
				-e 's@$@/d@'	\
					>>${TMP1_FILE}
		fi
		ignore="-f ${TMP1_FILE}"

		${SCC_BIN}/scc-cmp "${prev_snapshot}" "${new_snapshot}" "${log_remark}" "${ignore}" >>${logfile}
		rm -f ${TMP1_FILE}
	else	# if [ "${compare}" = "local" ]
		# Just empty the logfile to indicate to the server a remote compare.
		>${logfile};
	fi
	if [ -f ${prev_snapshot} ]
	then
		rm -f ${old_snapshot}
		mv -f ${prev_snapshot} ${old_snapshot}
	fi
	mv -f ${new_snapshot} ${prev_snapshot}

	if [ -s ${logfile} ]
	then
		nice ${SCC_BIN}/scc-log2html ${host} <${logfile} >${logfile}.html
	else
		> ${logfile}.html
	fi

	# The file with the previous snapshot has been replaced by the new (current) snapshot.
	nice ${SCC_BIN}/scc-snap2html ${host} <${prev_snapshot} >${SCC_DATA}/scc.${host}.html
done

if [ ${virtual_host} -eq 0 ]
then
	suffix=""
	busybox_check sort
	if [ $? -eq 1 ]
	then
		# We cannot produce html-files when sort is implemented by BusyBox.
		# This should be done by scc-srv after transfer of the snapshot and logbook.
		# For local inspection we will produce html-files with all data as
		# pre-formatted text in it.
		# The generated index.html will point to these special files.
		suffix="_flat"
		{
			scc_html_head "Configuration: ${name}"

			echo "<BODY><PRE>"
			sed	-e '/^var:MoDuLe:start::/d'	\
				-e '/^var:MoDuLe:end::/d'	\
				-e 's/&/\&amp;/g'		\
				-e 's/</\&lt;/g'		\
				-e 's/>/\&gt;/g'		\
				-e "s/'/\&#39;/g"		\
				-e 's/"/\&quot;/g'		\
					<${SCC_DATA}/scc.${name}.cur
			echo "</PRE></BODY></HTML>"
		} >${SCC_DATA}/scc.${name}${suffix}.html

		{
			scc_html_head "Logbook: ${name}"

			echo "<BODY><PRE>"
			sed	-e 's/&/\&amp;/g'		\
				-e 's/</\&lt;/g'		\
				-e 's/>/\&gt;/g'		\
				-e "s/'/\&#39;/g"		\
				-e 's/"/\&quot;/g'		\
					<${SCC_DATA}/scc.${name}.log
			echo "</PRE></BODY></HTML>"
		} >${SCC_DATA}/scc.${name}${suffix}.log.html

	fi
	# To ease navigation on the server, the html-file of the snapshot contains a reference
	# to index.html (the main file for a realm). To avoid an 404 error when browsing on the client,
	# we create a simple index.html on the client.
	scc_html_head "Index for ${name}" >${index}
	cat <<-_X_ >>${index}
		<BODY>
			<A HREF=scc.${name}${suffix}.html>snapshot of ${name}</A>
			<BR>
			<A HREF=scc.${name}${suffix}.log.html>logbook of ${name}</A>
			<BR>
			<A HREF="/opt/scc/doc/scc-5.html">manual pages</A>
		</BODY>
		</HTML>
	_X_
fi

exit 0
