NetBSD/usr.sbin/etcupdate/etcupdate

536 lines
13 KiB
Bash

#!/bin/sh
#
# $NetBSD: etcupdate,v 1.19 2003/07/14 23:16:19 lukem Exp $
#
# Copyright (c) 2001 The NetBSD Foundation, Inc.
# All rights reserved.
#
# This code is derived from software contributed to The NetBSD Foundation
# by Martti Kuparinen.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
# must display the following acknowledgement:
# This product includes software developed by the NetBSD
# Foundation, Inc. and its contributors.
# 4. Neither the name of The NetBSD Foundation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
#
# This script helps you to update the configuration files in /etc
# after an operating system upgrade. Instead of running "make distribution"
# in /usr/src/etc (and losing your current configuration) you can easily
# see the modifications and either install the new version or merge the
# changes in to your current configuration files.
#
# This script was written by Martti Kuparinen <martti@netbsd.org> and
# improved by several other NetBSD users.
#
# The idea for this script (including code fragments, variable names etc.)
# came from the FreeBSD mergemaster (by Douglas Barton).
#
PATH="/sbin:/usr/sbin:/bin:/usr/bin:${PATH}"
# Default settings
TEMPROOT="${TEMPROOT:=/tmp/temproot}"
SRCDIR="${SRCDIR:=/usr/src/etc}"
PAGER="${PAGER:=/usr/bin/more}"
SWIDTH=`stty -a | awk '/columns/{w=$6}END{if(w==0){w=80}print w}'`
WIDTH="${WIDTH:=${SWIDTH}}"
VERBOSE=
CONTINUE=
BINARY=
AUTOMATIC=
LOCALSKIP=
# Settings for post-installation procedures
NEED_MTREE=
NEED_MAKEDEV=
NEED_NEWALIASES=
NEED_PWD_MKDB=
usage() {
cat << EOF
Usage: `basename $0` [options]
Options:
-b srcdir Location of the extracted sets
-p pager Which pager to use (default: /usr/bin/more)
-s srcdir Location of the source files (default: /usr/src/etc)
-t temproot Where to store temporary files (default: /tmp/temproot)
-w width Screen width (default: 80)
-a Automatically update unmodified files
-l Automatically skip files with strictly local changes
(this option has no effect on files lacking RCS Ids)
-h This help text
-v Be more verbose
EOF
exit 1
}
verbose() {
# $* = message to display if in verbose mode
[ ! -z "${VERBOSE}" ] && echo ${*}
}
yesno() {
# $* = message to display
echo -n "${*}? (y/[n]) "
read ANSWER
case "${ANSWER}" in
y|Y)
return 0
;;
*)
return 1
;;
esac
}
install_dir() {
# $1 = target directory
if yesno "Create ${1}"; then
verbose "Creating ${1}"
mkdir -p "${1}" || exit 1
NEED_MTREE=YES
fi
}
install_file() {
# $1 = target file
# Install the new file
verbose "Installing ${1}"
cp -p "${TEMPROOT}${1}" "${1}" && rm -f "${TEMPROOT}${1}"
# Check if this was a special file
case "${1}" in
/dev/MAKEDEV)
NEED_MAKEDEV=YES
;;
/dev/MAKEDEV.local)
NEED_MAKEDEV=YES
;;
/etc/mail/aliases)
NEED_NEWALIASES=YES
;;
/etc/master.passwd)
NEED_PWD_MKDB=YES
;;
esac
}
install_checksum() {
# $1 = target file
[ "${AUTOMATIC}" != "YES" ] && return
D=`dirname "${1}"`
mkdir -p "/var/etcupdate/${D}"
md5 "${1}" > "/var/etcupdate/${1}"
}
diff_and_merge_file() {
# $1 = target file
if cmp -s "${TEMPROOT}${1}" "${1}"; then
verbose "===> ${1} (ok)"
rm -f "${TEMPROOT}${1}"
install_checksum "${1}"
return
fi
if [ "${AUTOMATIC}" = "YES" -a -f "/var/etcupdate/${1}" ] ; then
SUM1=`md5 "${1}"`
SUM2=`cat "/var/etcupdate/${1}"`
if [ "${SUM1}" = "${SUM2}" ] ; then
install_file "${1}"
install_checksum "${1}"
return
fi
fi
if [ "${LOCALSKIP}" = "YES" ] ; then
ID1=`ident -q "${TEMPROOT}${1}" | sed -n 2p`
ID1="${ID1:-0}"
ID2=`ident -q "${1}" | sed -n 2p`
ID2="${ID2:-1}"
if [ "${ID1}" = "${ID2}" ] ; then
verbose "===> ${1} (ok:RCS)"
rm -f "${TEMPROOT}${1}"
return
fi
fi
clear
if [ ! -f "${1}" ]; then
verbose "===> ${1} (missing)"
DOES_EXIST=
else
verbose "===> ${1} (modified)"
verbose ""
DOES_EXIST=YES
diff -u "${1}" "${TEMPROOT}${1}" | ${PAGER}
fi
STAY_HERE=YES
ALREADY_MERGED=
# Determine name for the backup file (/foo/._etcupdate.bar)
D=`dirname "${TEMPROOT}${1}"`
F=`basename "${TEMPROOT}${1}"`
B="${D}/.etcupdate.${F}"
F="${D}/${F}"
while [ "x${STAY_HERE}" = "xYES" ]; do
# Ask the user if (s)he wants to install the new
# version or perform a more complicated manual work.
echo ""
echo -n "File: ${1}"
if [ ! -f "${1}" ]; then
echo -n " (missing)"
else
echo -n " (modified)"
fi
echo ""
echo ""
echo "Please select one of the following operations:"
echo ""
if [ -z "${DOES_EXIST}" ]; then
cat << EOF
d Don't install the missing file
i Install the missing file
v Show the missing file
EOF
elif [ -z "${ALREADY_MERGED}" ]; then
cat << EOF
d Don't install the new file
i Install the new file (overwrites your modifications!)
m Merge the currently installed and new files
s Show the differences between the currently installed and new files
v Show the new file
EOF
else
cat << EOF
d Don't install the new file
i Install the new file (overwrites your modifications!)
m Merge again the currently installed and new files
s Show the differences between the currently installed and new files
u Undo merge and restore the temporary file from backup
v Show the merged file
EOF
fi
echo -n "What do you want to do? [Leave it for later] "
read ANSWER
case "${ANSWER}" in
[dD])
verbose "Removing ${TEMPROOT}${1}"
rm -f "${TEMPROOT}${1}"
STAY_HERE=NO
;;
[iI])
install_file "${1}"
if [ -z "${ALREADY_MERGED}" ]; then
install_checksum "${1}"
fi
STAY_HERE=NO
;;
[mM])
[ -z "${DOES_EXIST}" ] && continue
[ ! -f "${B}" ] && cp "${F}" "${B}"
cp "${TEMPROOT}${1}" "${TEMPROOT}${1}.merged"
sdiff -o "${TEMPROOT}${1}.merged" \
--width=${WIDTH} \
--suppress-common-lines --text \
"${1}" "${TEMPROOT}${1}"
mv -f "${TEMPROOT}${1}.merged" "${TEMPROOT}${1}"
ALREADY_MERGED=YES
;;
[sS])
[ -z "${DOES_EXIST}" ] && continue
diff -u "${1}" "${TEMPROOT}${1}" | ${PAGER}
;;
[uU])
if [ -f "${B}" ]; then
echo "*** Restoring ${F}"
mv -f "${B}" "${F}"
fi
ALREADY_MERGED=
;;
[vV])
${PAGER} "${TEMPROOT}${1}"
;;
"")
STAY_HERE=NO
;;
*)
echo "*** Invalid selection!"
;;
esac
done
rm -f "._etcupdate_${TEMPROOT}${1}"
}
#
# main()
#
# Read global configuration
GLOBALRC="/etc/`basename $0`.conf"
[ -r ${GLOBALRC} ] && . ${GLOBALRC}
# Read user configuration
USERRC="${HOME}/.`basename $0`rc"
[ -r ${USERRC} ] && . ${USERRC}
# Read command line arguments
ARGV=`getopt ab:hlp:s:t:vw: $*`
[ $? != 0 ] && usage
set -- ${ARGV}
for i; do
case "${i}" in
-a)
AUTOMATIC=YES
shift
;;
-b)
BINARY=YES
SRCDIR="${2}"
shift 2
;;
-h)
usage
;;
-l)
LOCALSKIP=YES
shift
;;
-p)
PAGER="${2}"
shift 2
;;
-s)
SRCDIR="${2}"
shift 2
;;
-t)
TEMPROOT="${2}"
shift 2
;;
-v)
VERBOSE=YES
shift
;;
-w)
WIDTH="${2}"
shift 2
;;
--)
shift
break
;;
esac
done
# Last minute sanity checks
if [ `id -u` -ne 0 ]; then
echo "*** ERROR: You MUST be root"
exit 1
fi
if [ ! -z "${BINARY}" ]; then
TEMPROOT="${SRCDIR}"
fi
if [ -z "${SRCDIR}" -o -z "${TEMPROOT}" ]; then
echo "*** ERROR: One of the following variables is undefined"
echo ""
echo "SRCDIR=\"${SRCDIR}\""
echo "TEMPROOT=\"${TEMPROOT}\""
echo ""
exit 1
fi
if [ -z "${BINARY}" -a -r "${TEMPROOT}" ]; then
echo ""
echo "*** WARNING: ${TEMPROOT} already exists"
echo ""
if yesno "Continue previously aborted update"; then
CONTINUE=YES
elif yesno "Remove the old ${TEMPROOT}"; then
echo "*** Removing ${TEMPROOT}"
rm -rf "${TEMPROOT}"
fi
fi
if [ -z "${CONTINUE}" ]; then
# Are we using the sources or binaries?
if [ -z "${BINARY}" ]; then
# Create the temporary root directory
echo "*** Creating ${TEMPROOT}"
mkdir -p "${TEMPROOT}"
if [ ! -d "${TEMPROOT}" ]; then
echo "*** ERROR: Unable to create ${TEMPROOT}"
exit 1
fi
# Populate ${TEMPROOT} from ${SRCDIR}
if [ ! -f "${SRCDIR}/Makefile" ]; then
echo "*** ERROR: Unable to find ${SRCDIR}/Makefile"
exit 1
fi
# Set the environment for make
MAKE_ENV=" \
DESTDIR=${TEMPROOT} \
MAKE=make \
MTREE=mtree \
TOOL_MTREE=mtree \
INSTALL_DONE=1 \
USETOOLS=never"
echo "*** Populating ${TEMPROOT} from ${SRCDIR}"
cd ${SRCDIR}
if [ -z "${VERBOSE}" ]; then
eval ${MAKE_ENV} make distribution > /dev/null
else
eval ${MAKE_ENV} make distribution
fi
[ $? -ne 0 ] && exit 1
elif [ ! -f "${TEMPROOT}/dev/MAKEDEV" ]; then
echo ""
echo "*** WARNING: ${TEMPROOT}/dev/MAKEDEV not found"
echo "Make sure you update /dev/MAKEDEV later and run"
echo "(cd /dev && ./MAKEDEV all) to rebuild the device nodes"
echo ""
fi
# Ignore the following files during comparision
rm -f "${TEMPROOT}"/etc/passwd
rm -f "${TEMPROOT}"/etc/pwd.db
rm -f "${TEMPROOT}"/etc/spwd.db
find "${TEMPROOT}" -type f -size 0 -exec rm {} \;
# Ignore files we're told to ignore
if [ ! -z "${IGNOREFILES}" ]; then
echo "*** Ignoring files: ${IGNOREFILES}"
for file in ${IGNOREFILES}; do
rm -f "${TEMPROOT}"${file}
done
fi
# Are there any new directories?
echo "*** Checking for new directories"
for i in `find ${TEMPROOT} -type d`; do
D=`echo ${i} | sed "s#${TEMPROOT}##"`
[ "x${i}" = "x${TEMPROOT}" ] && continue
[ ! -d "${D}" ] && install_dir "${D}"
done
fi
# Start the comparision
echo "*** Checking for added/modified files"
for i in `find ${TEMPROOT} -type f -a ! -name \*.etcupdate.\*`; do
D=`echo ${i} | sed "s#${TEMPROOT}##"`
diff_and_merge_file "${D}"
done
# Do we have files which were not processed?
REMAINING=`find "${TEMPROOT}" -type f -a ! -name \*.etcupdate.\*`
if [ ! -z "${REMAINING}" ]; then
echo ""
echo "*** The following files need your attention:"
echo ""
for i in ${REMAINING}; do
echo " ${i}"
done
echo ""
fi
if yesno "Remove ${TEMPROOT}"; then
echo "*** Removing ${TEMPROOT}"
rm -rf "${TEMPROOT}"
else
echo "*** Keeping ${TEMPROOT}"
fi
# Do some post-installation tasks
if [ ! -z "${NEED_MTREE}" ]; then
if yesno "You have created new directories. Run mtree to set" \
"permissions"
then
(cd / && mtree -Udef /etc/mtree/NetBSD.dist)
fi
fi
if [ ! -z "${NEED_MAKEDEV}" ]; then
if yesno "Do you want to rebuild the device nodes in /dev"; then
verbose "Running MAKEDEV in /dev"
(cd "/dev" && ./MAKEDEV all)
else
echo ""
echo "*** You SHOULD rebuild the device nodes in /dev"
echo "*** This is done by running \"(cd /dev &&" \
"./MAKEDEV all)\" as root".
echo ""
fi
fi
if [ ! -z "${NEED_NEWALIASES}" ]; then
if yesno "Do you want to rebuild the mail alias database"; then
verbose "Running newaliases"
newaliases
else
echo ""
echo "*** You MUST rebuild the mail alias database to make" \
"the changes visible"
echo "*** This is done by running \"newaliases\" as root"
echo ""
fi
fi
if [ ! -z "${NEED_PWD_MKDB}" ]; then
if yesno "Do you want to rebuild the password databases from the" \
"new master.passwd"
then
verbose "Running pwd_mkdb"
pwd_mkdb -p "/etc/master.passwd"
else
echo ""
echo "*** You MUST rebuild the password databases to make" \
"the changes visible"
echo "*** This is done by running \"pwd_mkdb -p" \
"/etc/master.passwd\" as root"
echo ""
fi
fi
if [ -x /etc/postinstall -a -z "${BINARY}" ]; then
S=`echo ${SRCDIR} | sed 's+/etc++'`
echo "*** Running /etc/postinstall"
/etc/postinstall -s "${S}" check
fi
echo "*** All done"