The etcupdate utility is a tool for managing updates to files that are

not updated as part of `make installworld' such as files in /etc.  It
manages updates by doing a three-way merge of changes made to these files
against the local versions.  It is also designed to minimize the amount
of user intervention with the goal of simplifying upgrades for clusters
of machines.

The primary difference from mergemaster is that etcupdate requires less
manual work.  The primary difference from etcmerge is that etcupdate
updates files in-place similar to mergemaster rather than building a
separate /etc tree.

Requested by:	obrien, kib, theraven, joeld (among others)
This commit is contained in:
John Baldwin 2012-07-13 13:23:48 +00:00
parent bc06fa9281
commit 6f9cd2a9dc
9 changed files with 4909 additions and 0 deletions

View File

@ -0,0 +1,608 @@
#!/bin/sh
#
# Copyright (c) 2010 Advanced Computing Technologies LLC
# Written by: John H. Baldwin <jhb@FreeBSD.org>
# All rights reserved.
#
# 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.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
#
# $FreeBSD$
# Various regression tests to test the -A flag to the 'update' command.
WORKDIR=work
usage()
{
echo "Usage: always.sh [-w workdir]"
exit 1
}
# Allow the user to specify an alternate work directory.
while getopts "w:" option; do
case $option in
w)
WORKDIR=$OPTARG
;;
*)
echo
usage
;;
esac
done
shift $((OPTIND - 1))
if [ $# -ne 0 ]; then
usage
fi
CONFLICTS=$WORKDIR/conflicts
OLD=$WORKDIR/old
NEW=$WORKDIR/current
TEST=$WORKDIR/test
# The various states of the comparison of a file between two trees.
states="equal first second difftype difflinks difffiles"
# These tests deal with ignoring certain patterns of files. We run
# the test multiple times forcing the install of different patterns.
build_trees()
{
local i
rm -rf $OLD $NEW $TEST $CONFLICTS
for i in $states; do
for j in $states; do
for k in $states; do
mkdir -p $OLD/$i/$j/$k $NEW/$i/$j/$k \
$TEST/$i/$j/$k
done
done
done
# What follows are the various warning/conflict cases from the
# larger regression tests. These results of many of these
# tests should be changed when installation is forced. The
# cases when these updates should still fail even when forced
# are: 1) it should not force the removal of a modified file
# and 2) it should not remove a subdirectory that contains a
# modified or added file.
# /first/difftype/second: File with different local type
# removed. Should generate a warning.
mkfifo $OLD/first/difftype/second/fifo
mkdir $TEST/first/difftype/second/fifo
# /first/difflinks/second: Modified link removed. Should
# generate a warning.
ln -s "old link" $OLD/first/difflinks/second/link
ln -s "test link" $TEST/first/difflinks/second/link
# /first/difffiles/second: Modified file removed. Should
# generate a warning.
echo "foo" > $OLD/first/difffiles/second/file
echo "bar" > $TEST/first/difffiles/second/file
# /second/second/difftype: Newly added file conflicts with
# existing file in test tree of a different type. Should
# generate a warning.
mkdir $NEW/second/second/difftype/dir
mkfifo $TEST/second/second/difftype/dir
# /second/second/difflinks: Newly added link conflicts with
# existing link in test tree. Should generate a warning.
ln -s "new link" $NEW/second/second/difflinks/link
ln -s "test link" $TEST/second/second/difflinks/link
# /second/second/difffiles: Newly added file conflicts with
# existing file in test tree. Should generate a warning.
echo "new" > $NEW/second/second/difffiles/file
echo "test" > $TEST/second/second/difffiles/file
# /difftype/first/first: A removed file has changed type.
# This should generate a warning.
mkfifo $OLD/difftype/first/first/fifo
mkdir $NEW/difftype/first/first/fifo
# /difftype/difftype/difftype: All three files (old, new, and
# test) are different types from each other. This should
# generate a warning.
mkfifo $OLD/difftype/difftype/difftype/one
mkdir $NEW/difftype/difftype/difftype/one
echo "foo" > $TEST/difftype/difftype/difftype/one
mkdir $OLD/difftype/difftype/difftype/two
echo "baz" > $NEW/difftype/difftype/difftype/two
ln -s "bar" $TEST/difftype/difftype/difftype/two
# /difftype/difftype/difflinks: A file has changed from a
# non-link to a link in both the new and test trees, but the
# target of the new and test links differ. This should
# generate a new link conflict.
mkfifo $OLD/difftype/difftype/difflinks/link
ln -s "new" $NEW/difftype/difftype/difflinks/link
ln -s "test" $TEST/difftype/difftype/difflinks/link
# /difftype/difftype/difffile: A file has changed from a
# non-regular file to a regular file in both the new and test
# trees, but the contents in the new and test files differ.
# This should generate a new file conflict.
ln -s "old" $OLD/difftype/difftype/difffiles/file
echo "foo" > $NEW/difftype/difftype/difffiles/file
echo "bar" > $TEST/difftype/difftype/difffiles/file
# /difflinks/first/first: A modified link is missing in the
# test tree. This should generate a warning.
ln -s "old" $OLD/difflinks/first/first/link
ln -s "new" $NEW/difflinks/first/first/link
# /difflinks/difftype/difftype: An updated link has been
# changed to a different file type in the test tree. This
# should generate a warning.
ln -s "old" $OLD/difflinks/difftype/difftype/link
ln -s "new" $NEW/difflinks/difftype/difftype/link
echo "test" > $TEST/difflinks/difftype/difftype/link
# /difflinks/difflinks/difflinks: An updated link has been
# modified in the test tree and doesn't match either the old
# or new links. This should generate a warning.
ln -s "old" $OLD/difflinks/difflinks/difflinks/link
ln -s "new" $NEW/difflinks/difflinks/difflinks/link
ln -s "test" $TEST/difflinks/difflinks/difflinks/link
# /difffiles/first/first: A removed file has been changed in
# the new tree. This should generate a warning.
echo "foo" > $OLD/difffiles/first/first/file
echo "bar" > $NEW/difffiles/first/first/file
# /difffiles/difftype/difftype: An updated regular file has
# been changed to a different file type in the test tree.
# This should generate a warning.
echo "old" > $OLD/difffiles/difftype/difftype/file
echo "new" > $NEW/difffiles/difftype/difftype/file
mkfifo $TEST/difffiles/difftype/difftype/file
# /difffiles/difffiles/difffiles: A modified regular file was
# updated in the new tree. The changes should be merged into
# to the new file if possible. If the merge fails, a conflict
# should be generated. For this test we just include the
# conflict case.
cat > $OLD/difffiles/difffiles/difffiles/conflict <<EOF
this is an old file
EOF
cat > $NEW/difffiles/difffiles/difffiles/conflict <<EOF
this is a new file
EOF
cat > $TEST/difffiles/difffiles/difffiles/conflict <<EOF
this is a test file
EOF
## Tests for adding directories
mkdir -p $OLD/adddir $NEW/adddir $TEST/adddir
# /adddir/conflict: Add a new file in a directory that already
# exists as a file. This should generate two warnings.
mkdir $NEW/adddir/conflict
touch $NEW/adddir/conflict/newfile
touch $TEST/adddir/conflict
## Tests for removing directories
mkdir -p $OLD/rmdir $NEW/rmdir $TEST/rmdir
# /rmdir/extra: Do not remove a directory with an extra local file.
# This should generate a warning.
for i in $OLD $TEST; do
mkdir $i/rmdir/extra
done
echo "foo" > $TEST/rmdir/extra/localfile.txt
# /rmdir/conflict: Do not remove a directory with a conflicted
# remove file. This should generate a warning.
for i in $OLD $TEST; do
mkdir $i/rmdir/conflict
done
mkfifo $OLD/rmdir/conflict/difftype
mkdir $TEST/rmdir/conflict/difftype
## Tests for converting files to directories and vice versa
for i in $OLD $NEW $TEST; do
for j in already old fromdir todir; do
mkdir -p $i/dirchange/$j
done
done
# /dirchange/fromdir/extradir: Convert a directory tree to a
# file. The test tree includes an extra file in the directory
# that is not present in the old tree. This should generate a
# warning.
for i in $OLD $TEST; do
mkdir $i/dirchange/fromdir/extradir
echo "foo" > $i/dirchange/fromdir/extradir/file
done
mkfifo $TEST/dirchange/fromdir/extradir/fifo
ln -s "bar" $NEW/dirchange/fromdir/extradir
# /dirchange/fromdir/conflict: Convert a directory tree to a
# file. The test tree includes a local change that generates
# a warning and prevents the removal of the directory.
for i in $OLD $TEST; do
mkdir $i/dirchange/fromdir/conflict
done
echo "foo" > $OLD/dirchange/fromdir/conflict/somefile
echo "bar" > $TEST/dirchange/fromdir/conflict/somefile
mkfifo $NEW/dirchange/fromdir/conflict
# /dirchange/todir/difffile: Convert a file to a directory
# tree. The test tree has a locally modified version of the
# file so that the conversion fails with a warning.
echo "foo" > $OLD/dirchange/todir/difffile
mkdir $NEW/dirchange/todir/difffile
echo "baz" > $NEW/dirchange/todir/difffile/file
echo "bar" > $TEST/dirchange/todir/difffile
# /dirchange/todir/difftype: Similar to the previous test, but
# the conflict is due to a change in the file type.
echo "foo" > $OLD/dirchange/todir/difftype
mkdir $NEW/dirchange/todir/difftype
echo "baz" > $NEW/dirchange/todir/difftype/file
mkfifo $TEST/dirchange/todir/difftype
}
# $1 - relative path to file that should be missing from TEST
missing()
{
if [ -e $TEST/$1 -o -L $TEST/$1 ]; then
echo "File $1 should be missing"
fi
}
# $1 - relative path to file that should be present in TEST
present()
{
if ! [ -e $TEST/$1 -o -L $TEST/$1 ]; then
echo "File $1 should be present"
fi
}
# $1 - relative path to file that should be a fifo in TEST
fifo()
{
if ! [ -p $TEST/$1 ]; then
echo "File $1 should be a FIFO"
fi
}
# $1 - relative path to file that should be a directory in TEST
dir()
{
if ! [ -d $TEST/$1 ]; then
echo "File $1 should be a directory"
fi
}
# $1 - relative path to file that should be a symlink in TEST
# $2 - optional value of the link
link()
{
local val
if ! [ -L $TEST/$1 ]; then
echo "File $1 should be a link"
elif [ $# -gt 1 ]; then
val=`readlink $TEST/$1`
if [ "$val" != "$2" ]; then
echo "Link $1 should link to \"$2\""
fi
fi
}
# $1 - relative path to regular file that should be present in TEST
# $2 - optional string that should match file contents
# $3 - optional MD5 of the flie contents, overrides $2 if present
file()
{
local contents sum
if ! [ -f $TEST/$1 ]; then
echo "File $1 should be a regular file"
elif [ $# -eq 2 ]; then
contents=`cat $TEST/$1`
if [ "$contents" != "$2" ]; then
echo "File $1 has wrong contents"
fi
elif [ $# -eq 3 ]; then
sum=`md5 -q $TEST/$1`
if [ "$sum" != "$3" ]; then
echo "File $1 has wrong contents"
fi
fi
}
# $1 - relative path to a regular file that should have a conflict
# $2 - optional MD5 of the conflict file contents
conflict()
{
local sum
if ! [ -f $CONFLICTS/$1 ]; then
echo "File $1 missing conflict"
elif [ $# -gt 1 ]; then
sum=`md5 -q $CONFLICTS/$1`
if [ "$sum" != "$2" ]; then
echo "Conflict $1 has wrong contents"
fi
fi
}
# $1 - relative path to a regular file that should not have a conflict
noconflict()
{
if [ -f $CONFLICTS/$1 ]; then
echo "File $1 should not have a conflict"
fi
}
if [ `id -u` -ne 0 ]; then
echo "must be root"
fi
if [ -r /etc/etcupdate.conf ]; then
echo "WARNING: /etc/etcupdate.conf settings may break some tests."
fi
# First run the test ignoring no patterns.
build_trees
etcupdate -r -d $WORKDIR -D $TEST > $WORKDIR/test.out
cat > $WORKDIR/correct.out <<EOF
D /dirchange/fromdir/extradir/file
C /difffiles/difffiles/difffiles/conflict
C /difftype/difftype/difffiles/file
C /second/second/difffiles/file
Warnings:
Modified regular file remains: /dirchange/fromdir/conflict/somefile
Modified regular file remains: /first/difffiles/second/file
Modified symbolic link remains: /first/difflinks/second/link
Modified directory remains: /first/difftype/second/fifo
Modified directory remains: /rmdir/conflict/difftype
Non-empty directory remains: /rmdir/extra
Non-empty directory remains: /rmdir/conflict
Modified mismatch: /difffiles/difftype/difftype/file (regular file vs fifo file)
Removed file changed: /difffiles/first/first/file
Modified link changed: /difflinks/difflinks/difflinks/link ("old" became "new")
Modified mismatch: /difflinks/difftype/difftype/link (symbolic link vs regular file)
Removed link changed: /difflinks/first/first/link ("old" became "new")
New link conflict: /difftype/difftype/difflinks/link ("new" vs "test")
Modified regular file changed: /difftype/difftype/difftype/one (fifo file became directory)
Modified symbolic link changed: /difftype/difftype/difftype/two (directory became regular file)
Remove mismatch: /difftype/first/first/fifo (fifo file became directory)
Modified directory changed: /dirchange/fromdir/conflict (directory became fifo file)
Modified directory changed: /dirchange/fromdir/extradir (directory became symbolic link)
Modified regular file changed: /dirchange/todir/difffile (regular file became directory)
Modified fifo file changed: /dirchange/todir/difftype (regular file became directory)
New file mismatch: /adddir/conflict (directory vs regular file)
Directory mismatch: $TEST/adddir/conflict (regular file)
Directory mismatch: $TEST/dirchange/todir/difffile (regular file)
Directory mismatch: $TEST/dirchange/todir/difftype (fifo file)
New link conflict: /second/second/difflinks/link ("new link" vs "test link")
New file mismatch: /second/second/difftype/dir (directory vs fifo file)
EOF
echo "Differences for regular:"
diff -u -L "correct" $WORKDIR/correct.out -L "test" $WORKDIR/test.out
## /first/difftype/second:
present /first/difftype/second/fifo
## /first/difflinks/second:
link /first/difflinks/second/link "test link"
## /first/difffiles/second:
file /first/difffiles/second/file "bar"
## /second/second/difftype:
fifo /second/second/difftype/dir
## /second/second/difflinks:
link /second/second/difflinks/link "test link"
## /second/second/difffiles:
file /second/second/difffiles/file "test"
conflict /second/second/difffiles/file 4f2ee8620a251fd53f06bb6112eb6ffa
## /difftype/first/first:
missing /difftype/first/first/fifo
## /difftype/difftype/difftype:
file /difftype/difftype/difftype/one "foo"
link /difftype/difftype/difftype/two "bar"
## /difftype/difftype/difflinks:
link /difftype/difftype/difflinks/link "test"
## /difftype/difftype/difffile:
conflict /difftype/difftype/difffiles/file 117f2bcd1f6491f6044e79e5a57a9229
## /difflinks/first/first:
missing /difflinks/first/first/link
## /difflinks/difftype/difftype:
file /difflinks/difftype/difftype/link "test"
## /difflinks/difflinks/difflinks:
link /difflinks/difflinks/difflinks/link "test"
## /difffiles/first/first:
missing /difffiles/first/first/file
## /difffiles/difftype/difftype:
fifo /difffiles/difftype/difftype/file
## /difffiles/difffiles/difffiles:
file /difffiles/difffiles/difffiles/conflict "this is a test file"
conflict /difffiles/difffiles/difffiles/conflict \
8261cfdd89280c4a6c26e4ac86541fe9
## /adddir/conflict:
file /adddir/conflict
## /rmdir/extra:
dir /rmdir/extra
file /rmdir/extra/localfile.txt "foo"
## /rmdir/conflict:
dir /rmdir/conflict/difftype
present /rmdir/conflict
## /dirchange/fromdir/extradir:
missing /dirchange/fromdir/extradir/file
fifo /dirchange/fromdir/extradir/fifo
## /dirchange/fromdir/conflict:
file /dirchange/fromdir/conflict/somefile "bar"
## /dirchange/todir/difffile:
file /dirchange/todir/difffile "bar"
## /dirchange/todir/difftype:
fifo /dirchange/todir/difftype
# Now test with -A '/first*' -A '/second* /*di*'. This should remove
# most of the warnings and conflicts.
build_trees
etcupdate -r -A '/first*' -A '/second* /*di*' -d $WORKDIR -D $TEST > \
$WORKDIR/test1.out
cat > $WORKDIR/correct1.out <<EOF
D /dirchange/fromdir/extradir/file
U /difffiles/difffiles/difffiles/conflict
U /difffiles/difftype/difftype/file
A /difffiles/first/first/file
U /difflinks/difflinks/difflinks/link
U /difflinks/difftype/difftype/link
A /difflinks/first/first/link
U /difftype/difftype/difffiles/file
U /difftype/difftype/difflinks/link
D /difftype/difftype/difftype/one
U /difftype/difftype/difftype/two
U /dirchange/todir/difffile
U /dirchange/todir/difftype
U /adddir/conflict
A /adddir/conflict/newfile
A /dirchange/todir/difffile/file
A /dirchange/todir/difftype/file
U /second/second/difffiles/file
U /second/second/difflinks/link
D /second/second/difftype/dir
Warnings:
Modified regular file remains: /dirchange/fromdir/conflict/somefile
Modified regular file remains: /first/difffiles/second/file
Modified symbolic link remains: /first/difflinks/second/link
Modified directory remains: /first/difftype/second/fifo
Modified directory remains: /rmdir/conflict/difftype
Non-empty directory remains: /rmdir/extra
Non-empty directory remains: /rmdir/conflict
Modified directory changed: /dirchange/fromdir/conflict (directory became fifo file)
Modified directory changed: /dirchange/fromdir/extradir (directory became symbolic link)
EOF
echo "Differences for -A '/first*' -A '/second* /*di*':"
diff -u -L "correct" $WORKDIR/correct1.out -L "test" $WORKDIR/test1.out
## /first/difftype/second:
present /first/difftype/second/fifo
## /first/difflinks/second:
link /first/difflinks/second/link "test link"
## /first/difffiles/second:
file /first/difffiles/second/file "bar"
## /second/second/difftype:
missing /second/second/difftype/dir
## /second/second/difflinks:
link /second/second/difflinks/link "new link"
## /second/second/difffiles:
file /second/second/difffiles/file "new"
noconflict /second/second/difffiles/file
## /difftype/first/first:
missing /difftype/first/first/fifo
## /difftype/difftype/difftype:
missing /difftype/difftype/difftype/one
file /difftype/difftype/difftype/two "baz"
## /difftype/difftype/difflinks:
link /difftype/difftype/difflinks/link "new"
## /difftype/difftype/difffile:
noconflict /difftype/difftype/difffiles/file
file /difftype/difftype/difffiles/file "foo"
## /difflinks/first/first:
link /difflinks/first/first/link "new"
## /difflinks/difftype/difftype:
link /difflinks/difftype/difftype/link "new"
## /difflinks/difflinks/difflinks:
link /difflinks/difflinks/difflinks/link "new"
## /difffiles/first/first:
file /difffiles/first/first/file "bar"
## /difffiles/difftype/difftype:
file /difffiles/difftype/difftype/file "new"
## /difffiles/difffiles/difffiles:
noconflict /difffiles/difffiles/difffiles/conflict
file /difffiles/difffiles/difffiles/conflict "this is a new file"
## /adddir/conflict:
file /adddir/conflict/newfile
## /rmdir/extra:
dir /rmdir/extra
file /rmdir/extra/localfile.txt "foo"
## /rmdir/conflict:
dir /rmdir/conflict/difftype
present /rmdir/conflict
## /dirchange/fromdir/extradir:
missing /dirchange/fromdir/extradir/file
fifo /dirchange/fromdir/extradir/fifo
## /dirchange/fromdir/conflict:
file /dirchange/fromdir/conflict/somefile "bar"
## /dirchange/todir/difffile:
file /dirchange/todir/difffile/file "baz"
## /dirchange/todir/difftype:
file /dirchange/todir/difftype/file "baz"

View File

@ -0,0 +1,274 @@
#!/bin/sh
#
# Copyright (c) 2010 Advanced Computing Technologies LLC
# Written by: John H. Baldwin <jhb@FreeBSD.org>
# All rights reserved.
#
# 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.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
#
# $FreeBSD$
# Various regression tests to run for the 'resolve' command.
WORKDIR=work
usage()
{
echo "Usage: tests.sh [-w workdir]"
exit 1
}
# Allow the user to specify an alternate work directory.
while getopts "w:" option; do
case $option in
w)
WORKDIR=$OPTARG
;;
*)
echo
usage
;;
esac
done
shift $((OPTIND - 1))
if [ $# -ne 0 ]; then
usage
fi
CONFLICTS=$WORKDIR/conflicts
OLD=$WORKDIR/old
NEW=$WORKDIR/current
TEST=$WORKDIR/test
# These tests deal with conflicts to a single file. For each test, we
# generate a conflict in /etc/login.conf. Each resolve option is tested
# to ensure it DTRT.
build_login_conflict()
{
rm -rf $OLD $NEW $TEST $CONFLICTS
mkdir -p $OLD/etc $NEW/etc $TEST/etc
# Generate a conflict in /etc/login.conf.
cat > $OLD/etc/login.conf <<EOF
default:\\
:passwd_format=md5:
EOF
cat > $NEW/etc/login.conf <<EOF
default:\\
:passwd_format=md5:\\
:copyright=/etc/COPYRIGHT
EOF
cat > $TEST/etc/login.conf <<EOF
default:\\
:passwd_format=md5:\\
:welcome=/etc/motd:
EOF
etcupdate -r -d $WORKDIR -D $TEST >/dev/null
}
# This is used to verify special handling for /etc/mail/aliases and
# the newaliases warning.
build_aliases_conflict()
{
rm -rf $OLD $NEW $TEST $CONFLICTS
mkdir -p $OLD/etc/mail $NEW/etc/mail $TEST/etc/mail
# Generate a conflict in /etc/mail/aliases
cat > $OLD/etc/mail/aliases <<EOF
# root: me@my.domain
# Basic system aliases -- these MUST be present
MAILER-DAEMON: postmaster
postmaster: root
EOF
cat > $NEW/etc/mail/aliases <<EOF
# root: me@my.domain
# Basic system aliases -- these MUST be present
MAILER-DAEMON: postmaster
postmaster: root
# General redirections for pseudo accounts
_dhcp: root
_pflogd: root
EOF
cat > $TEST/etc/mail/aliases <<EOF
root: someone@example.com
# Basic system aliases -- these MUST be present
MAILER-DAEMON: postmaster
postmaster: foo
EOF
etcupdate -r -d $WORKDIR -D $TEST >/dev/null
}
# $1 - relative path to file that should be missing from TEST
missing()
{
if [ -e $TEST/$1 -o -L $TEST/$1 ]; then
echo "File $1 should be missing"
fi
}
# $1 - relative path to file that should be present in TEST
present()
{
if ! [ -e $TEST/$1 -o -L $TEST/$1 ]; then
echo "File $1 should be present"
fi
}
# $1 - relative path to regular file that should be present in TEST
# $2 - optional string that should match file contents
# $3 - optional MD5 of the flie contents, overrides $2 if present
file()
{
local contents sum
if ! [ -f $TEST/$1 ]; then
echo "File $1 should be a regular file"
elif [ $# -eq 2 ]; then
contents=`cat $TEST/$1`
if [ "$contents" != "$2" ]; then
echo "File $1 has wrong contents"
fi
elif [ $# -eq 3 ]; then
sum=`md5 -q $TEST/$1`
if [ "$sum" != "$3" ]; then
echo "File $1 has wrong contents"
fi
fi
}
# $1 - relative path to a regular file that should have a conflict
# $2 - optional MD5 of the conflict file contents
conflict()
{
local sum
if ! [ -f $CONFLICTS/$1 ]; then
echo "File $1 missing conflict"
elif [ $# -gt 1 ]; then
sum=`md5 -q $CONFLICTS/$1`
if [ "$sum" != "$2" ]; then
echo "Conflict $1 has wrong contents"
fi
fi
}
# $1 - relative path to a regular file that should no longer have a conflict
resolved()
{
if [ -f $CONFLICTS/$1 ]; then
echo "Conflict $1 should be resolved"
fi
}
if [ `id -u` -ne 0 ]; then
echo "must be root"
fi
if [ -r /etc/etcupdate.conf ]; then
echo "WARNING: /etc/etcupdate.conf settings may break some tests."
fi
# Test each of the following resolve options: 'p', 'mf', 'tf', 'r'.
build_login_conflict
# Verify that 'p' doesn't do anything.
echo "Checking 'p':"
echo 'p' | etcupdate resolve -d $WORKDIR -D $TEST >/dev/null
file /etc/login.conf "" 95de92ea3f1bb1bf4f612a8b5908cddd
missing /etc/login.conf.db
conflict /etc/login.conf
# Verify that 'mf' removes the conflict, but does nothing else.
echo "Checking 'mf':"
echo 'mf' | etcupdate resolve -d $WORKDIR -D $TEST >/dev/null
file /etc/login.conf "" 95de92ea3f1bb1bf4f612a8b5908cddd
missing /etc/login.conf.db
resolved /etc/login.conf
build_login_conflict
# Verify that 'tf' installs the new version of the file.
echo "Checking 'tf':"
echo 'tf' | etcupdate resolve -d $WORKDIR -D $TEST >/dev/null
file /etc/login.conf "" 7774a0f9a3a372c7c109c32fd31c4b6b
file /etc/login.conf.db
resolved /etc/login.conf
build_login_conflict
# Verify that 'r' installs the resolved version of the file. To
# simulate this, manually edit the merged file so that it doesn't
# contain conflict markers.
echo "Checking 'r':"
cat > $CONFLICTS/etc/login.conf <<EOF
default:\\
:passwd_format=md5:\\
:copyright=/etc/COPYRIGHT\\
:welcome=/etc/motd:
EOF
echo 'r' | etcupdate resolve -d $WORKDIR -D $TEST >/dev/null
file /etc/login.conf "" 966e25984b9b63da8eaac8479dcb0d4d
file /etc/login.conf.db
resolved /etc/login.conf
build_aliases_conflict
# Verify that 'p' and 'mf' do not generate the newaliases warning.
echo "Checking newalias warning for 'p'":
echo 'p' | etcupdate resolve -d $WORKDIR -D $TEST | grep -q newalias
if [ $? -eq 0 ]; then
echo "+ Extra warning"
fi
echo "Checking newalias warning for 'mf'":
echo 'mf' | etcupdate resolve -d $WORKDIR -D $TEST | grep -q newalias
if [ $? -eq 0 ]; then
echo "+ Extra warning"
fi
# Verify that 'tf' and 'r' do generate the newaliases warning.
build_aliases_conflict
echo "Checking newalias warning for 'tf'":
echo 'tf' | etcupdate resolve -d $WORKDIR -D $TEST | grep -q newalias
if [ $? -ne 0 ]; then
echo "- Missing warning"
fi
build_aliases_conflict
cp $TEST/etc/mail/aliases $CONFLICTS/etc/mail/aliases
echo 'r' | etcupdate resolve -d $WORKDIR -D $TEST | grep -q newalias
if [ $? -ne 0 ]; then
echo "- Missing warning"
fi

View File

@ -0,0 +1,327 @@
#!/bin/sh
#
# Copyright (c) 2010 Advanced Computing Technologies LLC
# Written by: John H. Baldwin <jhb@FreeBSD.org>
# All rights reserved.
#
# 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.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
#
# $FreeBSD$
# Various regression tests to test the -F flag to the 'update' command.
WORKDIR=work
usage()
{
echo "Usage: fbsdid.sh [-w workdir]"
exit 1
}
# Allow the user to specify an alternate work directory.
while getopts "w:" option; do
case $option in
w)
WORKDIR=$OPTARG
;;
*)
echo
usage
;;
esac
done
shift $((OPTIND - 1))
if [ $# -ne 0 ]; then
usage
fi
CONFLICTS=$WORKDIR/conflicts
OLD=$WORKDIR/old
NEW=$WORKDIR/current
TEST=$WORKDIR/test
# Store a FreeBSD ID string in a specified file. The first argument
# is the file, the remaining arguments are the comment to use.
store_id()
{
local file
file=$1
shift
echo -n '# $FreeBSD' >> $file
echo -n "$@" >> $file
echo '$' >> $file
}
# These tests deal with FreeBSD ID string conflicts. We run the test
# twice, once without -F and once with -F.
build_trees()
{
local i
rm -rf $OLD $NEW $TEST $CONFLICTS
mkdir -p $OLD $NEW $TEST
# remove: Remove a file where the only local difference is a
# change in the FreeBSD ID string.
store_id $OLD/remove
store_id $TEST/remove ": head/remove 12345 jhb "
# old: Modify a file where the only local difference between
# the old and test files is a change in the FreeBSD ID string.
store_id $OLD/old ": src/old,v 1.1 jhb Exp "
store_id $NEW/old ": head/old 12345 jhb "
store_id $TEST/old ": head/old 12000 jhb "
for i in $OLD $TEST; do
cat >> $i/old <<EOF
an old file
EOF
done
cat >> $NEW/old <<EOF
a new file
EOF
# already: Modify a file where the local file already matches
# the new file except for a change in the FreeBSD ID string.
store_id $OLD/already ": src/already,v 1.1 jhb Exp "
store_id $NEW/already ": head/already 12345 jhb "
store_id $TEST/already ": src/already,v 1.2 jhb Exp "
cat >> $OLD/already <<EOF
another old file
EOF
for i in $NEW $TEST; do
cat >> $i/already <<EOF
another new file
EOF
done
# add: Add a file that already exists where the only local
# difference is a change in the FreeBSD ID string.
store_id $NEW/add ": head/add 12345 jhb "
store_id $TEST/add ""
# conflict: Modify a file where the local file has a different
# FreeBSD ID string. This should still generate a conflict
# even in the -F case.
store_id $OLD/conflict ": head/conflict 12000 jhb "
store_id $NEW/conflict ": head/conflict 12345 jhb "
store_id $TEST/conflict ""
cat >> $OLD/conflict <<EOF
this is the old file
EOF
cat >> $NEW/conflict <<EOF
this is the new file
EOF
cat >> $TEST/conflict <<EOF
this is the local file
EOF
# local: A file with local modifications has a different
# FreeBSD ID string and the only differences between the old
# and new versions are a change in the FreeBSD ID string.
# This will just update the FreeBSD ID string in the -F case.
for i in $OLD $NEW $TEST; do
cat >> $i/local <<EOF
# Some leading text
#
EOF
done
store_id $OLD/local ": head/local 12000 jhb "
store_id $NEW/local ": head/local 12345 jhb "
store_id $TEST/local ": src/local,v 1.5 jhb Exp "
for i in $OLD $NEW $TEST; do
cat >> $i/local <<EOF
this is a file
EOF
done
cat >> $TEST/local <<EOF
these are some local mods to the file
EOF
# local-already: A file with local modifications has the same
# FreeBSD ID string as the new version of the file and the
# only differences between the old and new versions are a
# change in the FreeBSD ID string. Nothing should happen in
# the -F case.
store_id $OLD/local-already ": head/local 12000 jhb "
for i in $NEW $TEST; do
store_id $i/local-already ": head/local 12345 jhb "
done
for i in $OLD $NEW $TEST; do
cat >> $i/local-already <<EOF
this is a file
EOF
done
cat >> $TEST/local-already <<EOF
these are some local mods to the file
EOF
}
# $1 - relative path to file that should be missing from TEST
missing()
{
if [ -e $TEST/$1 -o -L $TEST/$1 ]; then
echo "File $1 should be missing"
fi
}
# $1 - relative path to file that should be present in TEST
present()
{
if ! [ -e $TEST/$1 -o -L $TEST/$1 ]; then
echo "File $1 should be present"
fi
}
# $1 - relative path to regular file that should be present in TEST
# $2 - optional string that should match file contents
# $3 - optional MD5 of the flie contents, overrides $2 if present
file()
{
local contents sum
if ! [ -f $TEST/$1 ]; then
echo "File $1 should be a regular file"
elif [ $# -eq 2 ]; then
contents=`cat $TEST/$1`
if [ "$contents" != "$2" ]; then
echo "File $1 has wrong contents"
fi
elif [ $# -eq 3 ]; then
sum=`md5 -q $TEST/$1`
if [ "$sum" != "$3" ]; then
echo "File $1 has wrong contents"
fi
fi
}
# $1 - relative path to a regular file that should have a conflict
# $2 - optional MD5 of the conflict file contents
conflict()
{
local sum
if ! [ -f $CONFLICTS/$1 ]; then
echo "File $1 missing conflict"
elif [ $# -gt 1 ]; then
sum=`md5 -q $CONFLICTS/$1`
if [ "$sum" != "$2" ]; then
echo "Conflict $1 has wrong contents"
fi
fi
}
# $1 - relative path to a regular file that should not have a conflict
noconflict()
{
if [ -f $CONFLICTS/$1 ]; then
echo "File $1 should not have a conflict"
fi
}
if [ `id -u` -ne 0 ]; then
echo "must be root"
fi
if [ -r /etc/etcupdate.conf ]; then
echo "WARNING: /etc/etcupdate.conf settings may break some tests."
fi
# First run the test without -F.
build_trees
etcupdate -r -d $WORKDIR -D $TEST > $WORKDIR/test.out
cat > $WORKDIR/correct.out <<EOF
C /already
C /conflict
C /local
M /local-already
C /old
C /add
Warnings:
Modified regular file remains: /remove
EOF
echo "Differences for regular:"
diff -u -L "correct" $WORKDIR/correct.out -L "test" $WORKDIR/test.out
file /remove "" 1bb4776213af107077be78fead8a351c
file /old "" 2f799a7addc4132563ef9b44adc66157
conflict /old 8441be64a1540f2ff584015279682425
file /already "" aa53bd506f65d01d766e7ba028585e1d
conflict /already f44105abb1fa3293e95c5d77e428d418
file /add "" 1dc8c617e541d1fd1b4c70212f71d8ae
conflict /add f99081e0da9a07f3cfebb430c0414941
file /conflict "" dc27978df125b0daeb7d9b93265f03fd
conflict /conflict 868452f666fea1c60ffb918ad9ad9607
file /local "" aa33e614b5e749449f230e2a2b0072eb
conflict /local 3df93e64043c8e348fc625b93ea220f4
file /local-already "" 0298b958a603049f45ae6a109c4f7fea
# Now test with -F.
build_trees
etcupdate -rF -d $WORKDIR -D $TEST > $WORKDIR/testF.out
cat > $WORKDIR/correctF.out <<EOF
D /remove
U /already
C /conflict
M /local
U /old
U /add
EOF
echo "Differences for -F:"
diff -u -L "correct" $WORKDIR/correctF.out -L "test" $WORKDIR/testF.out
missing /remove
file /old "" 6a9f34f109d94406a4de3bc5d72de259
noconflict /old
file /already "" 21f4eca3aacc702c49878c8da7afd3d0
noconflict /already
file /add "" 0208bd647111fedf6318511712ab9e97
noconflict /add
file /conflict "" dc27978df125b0daeb7d9b93265f03fd
conflict /conflict 868452f666fea1c60ffb918ad9ad9607
file /local "" 3ed5a35e380c8a93fb5f599d4c052713
file /local-already "" 0298b958a603049f45ae6a109c4f7fea

View File

@ -0,0 +1,255 @@
#!/bin/sh
#
# Copyright (c) 2010 Advanced Computing Technologies LLC
# Written by: John H. Baldwin <jhb@FreeBSD.org>
# All rights reserved.
#
# 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.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
#
# $FreeBSD$
# Various regression tests to test the -I flag to the 'update' command.
WORKDIR=work
usage()
{
echo "Usage: ignore.sh [-w workdir]"
exit 1
}
# Allow the user to specify an alternate work directory.
while getopts "w:" option; do
case $option in
w)
WORKDIR=$OPTARG
;;
*)
echo
usage
;;
esac
done
shift $((OPTIND - 1))
if [ $# -ne 0 ]; then
usage
fi
CONFLICTS=$WORKDIR/conflicts
OLD=$WORKDIR/old
NEW=$WORKDIR/current
TEST=$WORKDIR/test
# These tests deal with ignoring certain patterns of files. We run the
# test multiple times ignoring different patterns.
build_trees()
{
local i
rm -rf $OLD $NEW $TEST $CONFLICTS
mkdir -p $OLD $NEW $TEST
for i in $OLD $NEW $TEST; do
mkdir -p $i/tree
done
# tree: Test three different cases (add, modify, remove) that all
# match the tree/* glob.
echo "foo" > $NEW/tree/add
for i in $OLD $TEST; do
echo "old" > $i/tree/modify
done
echo "new" > $NEW/tree/modify
for i in $OLD $TEST; do
echo "old" > $i/tree/remove
done
# rmdir: Remove a whole tree.
for i in $OLD $TEST; do
mkdir $i/rmdir
echo "foo" > $i/rmdir/file
done
}
# $1 - relative path to file that should be missing from TEST
missing()
{
if [ -e $TEST/$1 -o -L $TEST/$1 ]; then
echo "File $1 should be missing"
fi
}
# $1 - relative path to file that should be present in TEST
present()
{
if ! [ -e $TEST/$1 -o -L $TEST/$1 ]; then
echo "File $1 should be present"
fi
}
# $1 - relative path to file that should be a directory in TEST
dir()
{
if ! [ -d $TEST/$1 ]; then
echo "File $1 should be a directory"
fi
}
# $1 - relative path to regular file that should be present in TEST
# $2 - optional string that should match file contents
# $3 - optional MD5 of the flie contents, overrides $2 if present
file()
{
local contents sum
if ! [ -f $TEST/$1 ]; then
echo "File $1 should be a regular file"
elif [ $# -eq 2 ]; then
contents=`cat $TEST/$1`
if [ "$contents" != "$2" ]; then
echo "File $1 has wrong contents"
fi
elif [ $# -eq 3 ]; then
sum=`md5 -q $TEST/$1`
if [ "$sum" != "$3" ]; then
echo "File $1 has wrong contents"
fi
fi
}
# $1 - relative path to a regular file that should have a conflict
# $2 - optional MD5 of the conflict file contents
conflict()
{
local sum
if ! [ -f $CONFLICTS/$1 ]; then
echo "File $1 missing conflict"
elif [ $# -gt 1 ]; then
sum=`md5 -q $CONFLICTS/$1`
if [ "$sum" != "$2" ]; then
echo "Conflict $1 has wrong contents"
fi
fi
}
# $1 - relative path to a regular file that should not have a conflict
noconflict()
{
if [ -f $CONFLICTS/$1 ]; then
echo "File $1 should not have a conflict"
fi
}
if [ `id -u` -ne 0 ]; then
echo "must be root"
fi
if [ -r /etc/etcupdate.conf ]; then
echo "WARNING: /etc/etcupdate.conf settings may break some tests."
fi
# First run the test ignoring no patterns.
build_trees
etcupdate -r -d $WORKDIR -D $TEST > $WORKDIR/test.out
cat > $WORKDIR/correct.out <<EOF
D /rmdir/file
D /tree/remove
D /rmdir
U /tree/modify
A /tree/add
EOF
echo "Differences for regular:"
diff -u -L "correct" $WORKDIR/correct.out -L "test" $WORKDIR/test.out
missing /tree/remove
file /tree/modify "new"
file /tree/add "foo"
missing /rmdir/file
missing /rmdir
# Now test with -I '/tree/*'. This should preserve the /tree files.
build_trees
etcupdate -r -I '/tree/*' -d $WORKDIR -D $TEST > $WORKDIR/test1.out
cat > $WORKDIR/correct1.out <<EOF
D /rmdir/file
D /rmdir
EOF
echo "Differences for -I '/tree/*':"
diff -u -L "correct" $WORKDIR/correct1.out -L "test" $WORKDIR/test1.out
file /tree/remove "old"
file /tree/modify "old"
missing /tree/add
missing /rmdir/file
missing /rmdir
# Now test with two patterns. This should preserve everything.
build_trees
etcupdate -r -I '/tree/*' -I '/rmdir*' -d $WORKDIR -D $TEST > \
$WORKDIR/test2.out
cat > $WORKDIR/correct2.out <<EOF
EOF
echo "Differences for -I '/tree/*' -I '/rmdir*':"
diff -u -L "correct" $WORKDIR/correct2.out -L "test" $WORKDIR/test2.out
file /tree/remove "old"
file /tree/modify "old"
missing /tree/add
file /rmdir/file "foo"
# Now test with a pattern that should cause a warning on /rmdir by
# only ignoring the files under that directory. Note that this also
# tests putting two patterns into a single -I argument.
build_trees
etcupdate -r -I '/tree/* /rmdir/*' -d $WORKDIR -D $TEST > \
$WORKDIR/test3.out
cat > $WORKDIR/correct3.out <<EOF
Warnings:
Non-empty directory remains: /rmdir
EOF
echo "Differences for -I '/tree/* /rmdir/*':"
diff -u -L "correct" $WORKDIR/correct3.out -L "test" $WORKDIR/test3.out
file /tree/remove "old"
file /tree/modify "old"
missing /tree/add
file /rmdir/file "foo"
dir /rmdir

View File

@ -0,0 +1,979 @@
#!/bin/sh
#
# Copyright (c) 2010 Advanced Computing Technologies LLC
# Written by: John H. Baldwin <jhb@FreeBSD.org>
# All rights reserved.
#
# 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.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
#
# $FreeBSD$
# Various regression tests to run for the 'update' command.
WORKDIR=work
usage()
{
echo "Usage: tests.sh [-w workdir]"
exit 1
}
# Allow the user to specify an alternate work directory.
while getopts "w:" option; do
case $option in
w)
WORKDIR=$OPTARG
;;
*)
echo
usage
;;
esac
done
shift $((OPTIND - 1))
if [ $# -ne 0 ]; then
usage
fi
CONFLICTS=$WORKDIR/conflicts
OLD=$WORKDIR/old
NEW=$WORKDIR/current
TEST=$WORKDIR/test
# The various states of the comparison of a file between two trees.
states="equal first second difftype difflinks difffiles"
build_trees()
{
local i j k
rm -rf $OLD $NEW $TEST $CONFLICTS
mkdir -p $OLD/etc $NEW/etc $TEST/etc
# For an given file, there are three different pair-wise
# relations between the three threes (old, new, and test): old
# vs new, old vs test, and new vs test. Each of these
# relations takes on one of six different states from the
# 'compare()' function in etcupdate: equal, onlyfirst,
# onlysecond, difftype, difflinks, difffiles. In addition,
# there are special considerations for considering cases such
# as a file merge that results in conflicts versus one that
# does not, special treatment of directories, etc. The tests
# below attempt to enumerate the three dimensional test matrix
# by having the path name use the three different tree states
# for the parent directories.
#
# Note that if the old and new files are identical (so first
# compare is "equal"), then the second and third comparisons
# will be the same.
#
# Note also that etcupdate only cares about files that are
# present in at least one of the old or new trees. Thus, none
# of the '*/second/second' cases are relevant.
for i in $states; do
for j in $states; do
for k in $states; do
mkdir -p $OLD/$i/$j/$k $NEW/$i/$j/$k \
$TEST/$i/$j/$k
done
done
done
# /equal/equal/equal: Everything is equal. Nothing should happen.
for i in $OLD $NEW $TEST; do
mkfifo $i/equal/equal/equal/fifo
echo "foo" > $i/equal/equal/equal/file
mkdir $i/equal/equal/equal/dir
ln -s "bar" $i/equal/equal/equal/link
done
# /equal/first/first: The file is missing from the test
# directory. Nothing should happen.
for i in $OLD $NEW; do
mkfifo $i/equal/first/first/fifo
echo "foo" > $i/equal/first/first/file
mkdir $i/equal/first/first/dir
ln -s "bar" $i/equal/first/first/link
done
# /equal/difftype/difftype: The local file is a different
# type. Nothing should happen.
for i in $OLD $NEW; do
mkfifo $i/equal/difftype/difftype/fifo
mkdir $i/equal/difftype/difftype/fromdir
done
echo "bar" > $TEST/equal/difftype/difftype/fifo
ln -s "test" $TEST/equal/difftype/difftype/fromdir
# /equal/difflinks/difflinks: The local file is a modified
# link. Nothing should happen.
for i in $OLD $NEW; do
ln -s "foo" $i/equal/difflinks/difflinks/link
done
ln -s "bar" $TEST/equal/difflinks/difflinks/link
# /equal/difffiles/difffiles: The local file is a modified
# file. Nothing should happen.
for i in $OLD $NEW; do
echo "foo" > $i/equal/difffiles/difffiles/file
done
echo "bar" > $TEST/equal/difffiles/difffiles/file
# /first/equal/second: Remove unmodified files. The files
# should all be removed.
for i in $OLD $TEST; do
mkfifo $i/first/equal/second/fifo
echo "foo" > $i/first/equal/second/file
mkdir $i/first/equal/second/emptydir
ln -s "bar" $i/first/equal/second/link
mkdir $i/first/equal/second/fulldir
echo "foo" > $i/first/equal/second/fulldir/file
done
# /first/equal/*: Cannot occur. If the file is missing from
# new, then new vs test will always be 'second'.
# /first/first/equal: Removed files are already removed.
# Nothing should happen.
mkfifo $OLD/first/first/equal/fifo
echo "foo" > $OLD/first/first/equal/file
mkdir $OLD/first/first/equal/dir
ln -s "bar" $OLD/first/first/equal/link
# /first/first/*: Cannot occur. The files are missing from
# both new and test.
# /first/second/*: Cannot happen, if the file is in old for
# old vs new, it cannot be missing for old vs test.
# /first/difftype/second: File with different local type
# removed. Should generate a warning.
mkfifo $OLD/first/difftype/second/fifo
mkdir $TEST/first/difftype/second/fifo
# /first/difftype/*: Cannot happen since the file is missing
# from new but present in test.
# /first/difflinks/second: Modified link removed. Should
# generate a warning.
ln -s "old link" $OLD/first/difflinks/second/link
ln -s "test link" $TEST/first/difflinks/second/link
# /first/difflinks/*: Cannot happen since the file is missing
# from new but present in test.
# /first/difffiles/second: Modified file removed. Should
# generate a warning.
echo "foo" > $OLD/first/difffiles/second/file
echo "bar" > $TEST/first/difffiles/second/file
# /first/difffiles/*: Cannot happen since the file is missing
# from new but present in test.
# /second/equal/first: Added a new file that isn't present in
# test. The empty directory should be ignored.
echo "bar" > $NEW/second/equal/first/file
mkfifo $NEW/second/equal/first/fifo
ln -s "new" $NEW/second/equal/first/link
mkdir $NEW/second/equal/first/emptydir
mkdir $NEW/second/equal/first/fulldir
echo "foo" > $NEW/second/equal/first/fulldir/file
# /second/equal/*: Cannot happen since the file is missing
# from test but present in new.
# /second/first/*: Cannot happen since the file is missing
# from old.
# /second/second/equal: Newly added file is already present in
# the test directory and identical to the new file. Nothing
# should happen.
for i in $NEW $TEST; do
mkfifo $i/second/second/equal/fifo
echo "foo" > $i/second/second/equal/file
mkdir $i/second/second/equal/dir
ln -s "bar" $i/second/second/equal/link
done
# /second/second/first: Cannot happen. The file is in dest in
# the second test, so it can't be missing from the third test.
# /second/second/second: Cannot happen. The file is in new in
# the first test, so it can't be missing from the third test.
# /second/second/difftype: Newly added file conflicts with
# existing file in test tree of a different type. Should
# generate a warning.
mkdir $NEW/second/second/difftype/dir
mkfifo $TEST/second/second/difftype/dir
# /second/second/difflinks: Newly added link conflicts with
# existing link in test tree. Should generate a warning.
ln -s "new link" $NEW/second/second/difflinks/link
ln -s "test link" $TEST/second/second/difflinks/link
# /second/second/difffiles: Newly added file conflicts with
# existing file in test tree. Should generate a warning.
echo "new" > $NEW/second/second/difffiles/file
echo "test" > $TEST/second/second/difffiles/file
# /second/difftype/*: Cannot happen since the file is missing
# from old.
# /second/difflinks/*: Cannot happen since the file is missing
# from old.
# /second/difffiles/*: Cannot happen since the file is missing
# from old.
# /difftype/equal/difftype: Unmodified file has changed type.
# File should be updated to the new file. In the 'todir' case
# the directory won't actually be created because it is empty.
for i in $OLD $TEST; do
echo "foo" > $i/difftype/equal/difftype/file
mkdir $i/difftype/equal/difftype/fromdir
ln -s "old" $i/difftype/equal/difftype/todir
done
ln -s "test" $NEW/difftype/equal/difftype/file
mkfifo $NEW/difftype/equal/difftype/fromdir
mkdir $NEW/difftype/equal/difftype/todir
# /difftype/equal/*: Cannot happen. Since the old file is a
# difftype from the new file and the test file is identical to
# the old file, the test file must be a difftype from the new
# file.
# /difftype/first/first: A removed file has changed type.
# This should generate a warning.
mkfifo $OLD/difftype/first/first/fifo
mkdir $NEW/difftype/first/first/fifo
# /difftype/first/*: Cannot happen. Since the new file exists
# and the dest file is missing, the last test must be 'first'.
# /difftype/second/*: Cannot happen. The old file exists in
# the first test, so it cannot be missing in the second test.
# /difftype/difftype/equal: A file has changed type, but the
# file in the test directory already matches the new file. Do
# nothing.
echo "foo" > $OLD/difftype/difftype/equal/fifo
mkfifo $OLD/difftype/difftype/equal/file
for i in $NEW $TEST; do
mkfifo $i/difftype/difftype/equal/fifo
echo "bar" > $i/difftype/difftype/equal/file
done
# /difftype/difftype/first: Cannot happen. The dest file
# exists in the second test.
# /difftype/difftype/second: Cannot happen. The new file
# exists in the first test.
# /difftype/difftype/difftype: All three files (old, new, and
# test) are different types from each other. This should
# generate a warning.
mkfifo $OLD/difftype/difftype/difftype/one
mkdir $NEW/difftype/difftype/difftype/one
echo "foo" > $TEST/difftype/difftype/difftype/one
mkdir $OLD/difftype/difftype/difftype/two
echo "baz" > $NEW/difftype/difftype/difftype/two
ln -s "bar" $TEST/difftype/difftype/difftype/two
# /difftype/difftype/difflinks: A file has changed from a
# non-link to a link in both the new and test trees, but the
# target of the new and test links differ. This should
# generate a new link conflict.
mkfifo $OLD/difftype/difftype/difflinks/link
ln -s "new" $NEW/difftype/difftype/difflinks/link
ln -s "test" $TEST/difftype/difftype/difflinks/link
# /difftype/difftype/difffile: A file has changed from a
# non-regular file to a regular file in both the new and test
# trees, but the contents in the new and test files differ.
# This should generate a new file conflict.
ln -s "old" $OLD/difftype/difftype/difffiles/file
echo "foo" > $NEW/difftype/difftype/difffiles/file
echo "bar" > $TEST/difftype/difftype/difffiles/file
# /difflinks/equal/difflinks: An unmodified symlink has
# changed. The link should be updated.
for i in $OLD $TEST; do
ln -s "old" $i/difflinks/equal/difflinks/link
done
ln -s "new" $NEW/difflinks/equal/difflinks/link
# /difflinks/equal/*: Cannot happen. Since old is identical
# to test, the third test must be 'difflinks'.
# /difflinks/first/first: A modified link is missing in the
# test tree. This should generate a warning.
ln -s "old" $OLD/difflinks/first/first/link
ln -s "new" $NEW/difflinks/first/first/link
# /difflinks/first/*: Cannot happen. Since the test file is
# missing in the second test, it must be missing in the third
# test.
# /difflinks/second/*: Cannot happen. The old link is present
# in the first test, so it cannot be missing in the second
# test.
# /difflinks/difftype/difftype: An updated link has been
# changed to a different file type in the test tree. This
# should generate a warning.
ln -s "old" $OLD/difflinks/difftype/difftype/link
ln -s "new" $NEW/difflinks/difftype/difftype/link
echo "test" > $TEST/difflinks/difftype/difftype/link
# /difflinks/difftype/*: Cannot happen. The old and new files
# are both links and the test file is not a link, so the third
# test must be 'difftype'.
# /difflinks/difflinks/equal: An updated link has already been
# updated to the new target in the test tree. Nothing should
# happen.
ln -s "old" $OLD/difflinks/difflinks/equal/link
for i in $NEW $TEST; do
ln -s "new" $i/difflinks/difflinks/equal/link
done
# /difflinks/difflinks/difflinks: An updated link has been
# modified in the test tree and doesn't match either the old
# or new links. This should generate a warning.
ln -s "old" $OLD/difflinks/difflinks/difflinks/link
ln -s "new" $NEW/difflinks/difflinks/difflinks/link
ln -s "test" $TEST/difflinks/difflinks/difflinks/link
# /difflinks/difflinks/*: Cannot happen. All three files are
# links from the first two tests, so the third test can only
# be 'equal' or 'difflink'.
# /difflinks/difffiles/*: Cannot happen. The old file is a
# link in the first test, so it cannot be a regular file in
# the second.
# /difffiles/equal/difffiles: An unmodified file has been
# changed in new tree. The file should be updated to the new
# version.
for i in $OLD $TEST; do
echo "foo" > $i/difffiles/equal/difffiles/file
done
echo "bar" > $NEW/difffiles/equal/difffiles/file
# /difffiles/equal/*: Cannot happen. Since the old file is
# identical to the test file, the third test must be
# 'difffiles'.
# /difffiles/first/first: A removed file has been changed in
# the new tree. This should generate a warning.
echo "foo" > $OLD/difffiles/first/first/file
echo "bar" > $NEW/difffiles/first/first/file
# /difffiles/first/*: Cannot happen. The new file is a
# regular file from the first test and the test file is
# missing in the second test, so the third test must be
# 'first'.
# /difffiles/second/*: Cannot happen. The old file is present
# in the first test, so it must be present in the second test.
# /difffiles/difftype/difftype: An updated regular file has
# been changed to a different file type in the test tree.
# This should generate a warning.
echo "old" > $OLD/difffiles/difftype/difftype/file
echo "new" > $NEW/difffiles/difftype/difftype/file
mkfifo $TEST/difffiles/difftype/difftype/file
# /difffiles/difftype/*: Cannot happen. The new file is known
# to be a regular file from the first test, and the test file
# is known to exist as a different file type from the second
# test. The third test must be 'difftype'.
# /difffiles/difflink/*: Cannot happen. The old file is known
# to be a regular file from the first test, so it cannot be a
# link in the second test.
# /difffiles/difffiles/equal: An updated regular file has
# already been updated to match the new file in the test tree.
# Nothing should happen.
echo "foo" > $OLD/difffiles/difffiles/equal/file
for i in $NEW $TEST; do
echo "bar" > $i/difffiles/difffiles/equal/file
done
# /difffiles/difffiles/difffiles: A modified regular file was
# updated in the new tree. The changes should be merged into
# to the new file if possible. If the merge fails, a conflict
# should be generated.
cat > $OLD/difffiles/difffiles/difffiles/simple <<EOF
this is an old line
EOF
cat > $NEW/difffiles/difffiles/difffiles/simple <<EOF
this is a new line
EOF
cat > $TEST/difffiles/difffiles/difffiles/simple <<EOF
this is an old line
this is a local line
EOF
cat > $OLD/difffiles/difffiles/difffiles/conflict <<EOF
this is an old file
EOF
cat > $NEW/difffiles/difffiles/difffiles/conflict <<EOF
this is a new file
EOF
cat > $TEST/difffiles/difffiles/difffiles/conflict <<EOF
this is a test file
EOF
# /difffiles/difffiles/*: Cannot happen. From the first three
# tests, all three files are regular files. The test file can
# either be identical to the new file ('equal') or not
# ('difffiles').
## Tests for adding directories
mkdir -p $OLD/adddir $NEW/adddir $TEST/adddir
# /adddir/conflict: Add a new file in a directory that already
# exists as a file. This should generate two warnings.
mkdir $NEW/adddir/conflict
touch $NEW/adddir/conflict/newfile
touch $TEST/adddir/conflict
# /adddir/partial: Add a new file in a directory. The
# directory already exists in the test tree and contains a
# different local file. The new file from the new tree should
# be added.
for i in $NEW $TEST; do
mkdir $i/adddir/partial
done
echo "foo" > $NEW/adddir/partial/file
mkfifo $TEST/adddir/partial/fifo
## Tests for removing directories
mkdir -p $OLD/rmdir $NEW/rmdir $TEST/rmdir
# /rmdir/extra: Do not remove a directory with an extra local file.
# This should generate a warning.
for i in $OLD $TEST; do
mkdir $i/rmdir/extra
done
echo "foo" > $TEST/rmdir/extra/localfile.txt
# /rmdir/conflict: Do not remove a directory with a conflicted
# remove file. This should generate a warning.
for i in $OLD $TEST; do
mkdir $i/rmdir/conflict
done
mkfifo $OLD/rmdir/conflict/difftype
mkdir $TEST/rmdir/conflict/difftype
# /rmdir/partial: Remove a complete hierarchy when part of the
# tree has already been removed locally.
for i in $OLD $TEST; do
mkdir -p $i/rmdir/partial/subdir
mkfifo $i/rmdir/partial/subdir/fifo
done
echo "foo" > $OLD/rmdir/partial/subdir/file
## Tests for converting files to directories and vice versa
for i in $OLD $NEW $TEST; do
for j in already old fromdir todir; do
mkdir -p $i/dirchange/$j
done
done
# /dirchange/already/fromdir: Convert a directory tree to a
# file without conflicts where the test tree already has the
# new file. Nothing should happen.
mkdir $OLD/dirchange/already/fromdir
echo "blah" > $OLD/dirchange/already/fromdir/somefile
for i in $NEW $TEST; do
echo "bar" > $i/dirchange/already/fromdir
done
# /dirchange/already/todir: Convert an unmodified file to a
# directory tree where the test tree already has the new
# tree. Nothing should happen.
echo "baz" > $OLD/dirchange/already/todir
for i in $NEW $TEST; do
mkdir $i/dirchange/already/todir
echo "blah" > $i/dirchange/already/todir/somefile
done
# /dirchange/old/fromdir: Convert a directory tree to a file.
# The old files are unmodified and should be changed to the new tree.
for i in $OLD $TEST; do
mkdir $i/dirchange/old/fromdir
echo "blah" > $i/dirchange/old/fromdir/somefile
done
echo "bar" > $NEW/dirchange/old/fromdir
# /dirchange/old/todir: Convert a file to a directory tree.
# The old file is unmodified and should be changed to the new
# tree.
for i in $OLD $TEST; do
echo "foo" > $i/dirchange/old/todir
done
mkdir $NEW/dirchange/old/todir
echo "bar" > $NEW/dirchange/old/todir/file
# /dirchange/fromdir/extradir: Convert a directory tree to a
# file. The test tree includes an extra file in the directory
# that is not present in the old tree. This should generate a
# warning.
for i in $OLD $TEST; do
mkdir $i/dirchange/fromdir/extradir
echo "foo" > $i/dirchange/fromdir/extradir/file
done
mkfifo $TEST/dirchange/fromdir/extradir/fifo
ln -s "bar" $NEW/dirchange/fromdir/extradir
# /dirchange/fromdir/conflict: Convert a directory tree to a
# file. The test tree includes a local change that generates
# a warning and prevents the removal of the directory.
for i in $OLD $TEST; do
mkdir $i/dirchange/fromdir/conflict
done
echo "foo" > $OLD/dirchange/fromdir/conflict/somefile
echo "bar" > $TEST/dirchange/fromdir/conflict/somefile
mkfifo $NEW/dirchange/fromdir/conflict
# /dirchange/todir/difffile: Convert a file to a directory
# tree. The test tree has a locally modified version of the
# file so that the conversion fails with a warning.
echo "foo" > $OLD/dirchange/todir/difffile
mkdir $NEW/dirchange/todir/difffile
echo "baz" > $NEW/dirchange/todir/difffile/file
echo "bar" > $TEST/dirchange/todir/difffile
# /dirchange/todir/difftype: Similar to the previous test, but
# the conflict is due to a change in the file type.
echo "foo" > $OLD/dirchange/todir/difftype
mkdir $NEW/dirchange/todir/difftype
echo "baz" > $NEW/dirchange/todir/difftype/file
mkfifo $TEST/dirchange/todir/difftype
## Tests for post-install actions
# - Adding /etc/master.passwd should cause pwd_mkdb to be run
echo "foo:*:16000:100::0:0:& user:/home/foo:/bin/tcsh" > \
$NEW/etc/master.passwd
# - Verify that updating an unmodified /etc/login.conf builds
# /etc/login.conf.db.
cat > $OLD/etc/login.conf <<EOF
default:\\
:passwd_format=md5:
EOF
cat > $NEW/etc/login.conf <<EOF
default:\\
:passwd_format=md5:\\
:copyright=/etc/COPYRIGHT
EOF
cp $OLD/etc/login.conf $TEST/etc/login.conf
# - Verify that a merge without conflicts to /etc/mail/aliases
# will trigger a newaliases run request.
mkdir -p $OLD/etc/mail $NEW/etc/mail $TEST/etc/mail
cat > $OLD/etc/mail/aliases <<EOF
# root: me@my.domain
# Basic system aliases -- these MUST be present
MAILER-DAEMON: postmaster
postmaster: root
EOF
cat > $NEW/etc/mail/aliases <<EOF
# root: me@my.domain
# Basic system aliases -- these MUST be present
MAILER-DAEMON: postmaster
postmaster: root
# General redirections for pseudo accounts
_dhcp: root
_pflogd: root
EOF
cat > $TEST/etc/mail/aliases <<EOF
root: someone@example.com
# Basic system aliases -- these MUST be present
MAILER-DAEMON: postmaster
postmaster: root
EOF
}
# $1 - relative path to file that should be missing from TEST
missing()
{
if [ -e $TEST/$1 -o -L $TEST/$1 ]; then
echo "File $1 should be missing"
fi
}
# $1 - relative path to file that should be present in TEST
present()
{
if ! [ -e $TEST/$1 -o -L $TEST/$1 ]; then
echo "File $1 should be present"
fi
}
# $1 - relative path to file that should be a fifo in TEST
fifo()
{
if ! [ -p $TEST/$1 ]; then
echo "File $1 should be a FIFO"
fi
}
# $1 - relative path to file that should be a directory in TEST
dir()
{
if ! [ -d $TEST/$1 ]; then
echo "File $1 should be a directory"
fi
}
# $1 - relative path to file that should be a symlink in TEST
# $2 - optional value of the link
link()
{
local val
if ! [ -L $TEST/$1 ]; then
echo "File $1 should be a link"
elif [ $# -gt 1 ]; then
val=`readlink $TEST/$1`
if [ "$val" != "$2" ]; then
echo "Link $1 should link to \"$2\""
fi
fi
}
# $1 - relative path to regular file that should be present in TEST
# $2 - optional string that should match file contents
# $3 - optional MD5 of the flie contents, overrides $2 if present
file()
{
local contents sum
if ! [ -f $TEST/$1 ]; then
echo "File $1 should be a regular file"
elif [ $# -eq 2 ]; then
contents=`cat $TEST/$1`
if [ "$contents" != "$2" ]; then
echo "File $1 has wrong contents"
fi
elif [ $# -eq 3 ]; then
sum=`md5 -q $TEST/$1`
if [ "$sum" != "$3" ]; then
echo "File $1 has wrong contents"
fi
fi
}
# $1 - relative path to a regular file that should have a conflict
# $2 - optional MD5 of the conflict file contents
conflict()
{
local sum
if ! [ -f $CONFLICTS/$1 ]; then
echo "File $1 missing conflict"
elif [ $# -gt 1 ]; then
sum=`md5 -q $CONFLICTS/$1`
if [ "$sum" != "$2" ]; then
echo "Conflict $1 has wrong contents"
fi
fi
}
check_trees()
{
echo "Checking tree for correct results:"
## /equal/equal/equal:
fifo /equal/equal/equal/fifo
file /equal/equal/equal/file "foo"
dir /equal/equal/equal/dir
link /equal/equal/equal/link "bar"
## /equal/first/first:
missing /equal/first/first/fifo
missing /equal/first/first/file
missing /equal/first/first/dir
missing /equal/first/first/link
## /equal/difftype/difftype:
file /equal/difftype/difftype/fifo "bar"
link /equal/difftype/difftype/fromdir "test"
## /equal/difflinks/difflinks:
link /equal/difflinks/difflinks/link "bar"
## /equal/difffiles/difffiles:
file /equal/difffiles/difffiles/file "bar"
## /first/equal/second:
missing /first/equal/second/fifo
missing /first/equal/second/file
missing /first/equal/second/emptydir
missing /first/equal/second/link
missing /first/equal/second/fulldir
## /first/first/equal:
missing /first/first/equal/fifo
missing /first/first/equal/file
missing /first/first/equal/dir
missing /first/first/equal/link
## /first/difftype/second:
present /first/difftype/second/fifo
## /first/difflinks/second:
link /first/difflinks/second/link "test link"
## /first/difffiles/second:
file /first/difffiles/second/file "bar"
## /second/equal/first:
file /second/equal/first/file "bar"
fifo /second/equal/first/fifo
link /second/equal/first/link "new"
missing /second/equal/first/emptydir
file /second/equal/first/fulldir/file "foo"
## /second/second/equal:
fifo /second/second/equal/fifo
file /second/second/equal/file "foo"
dir /second/second/equal/dir
link /second/second/equal/link "bar"
## /second/second/difftype:
fifo /second/second/difftype/dir
## /second/second/difflinks:
link /second/second/difflinks/link "test link"
## /second/second/difffiles:
file /second/second/difffiles/file "test"
conflict /second/second/difffiles/file 4f2ee8620a251fd53f06bb6112eb6ffa
## /difftype/equal/difftype:
link /difftype/equal/difftype/file "test"
fifo /difftype/equal/difftype/fromdir
missing /difftype/equal/difftype/todir
## /difftype/first/first:
missing /difftype/first/first/fifo
## /difftype/difftype/equal:
fifo /difftype/difftype/equal/fifo
file /difftype/difftype/equal/file "bar"
## /difftype/difftype/difftype:
file /difftype/difftype/difftype/one "foo"
link /difftype/difftype/difftype/two "bar"
## /difftype/difftype/difflinks:
link /difftype/difftype/difflinks/link "test"
## /difftype/difftype/difffile:
conflict /difftype/difftype/difffiles/file \
117f2bcd1f6491f6044e79e5a57a9229
## /difflinks/equal/difflinks:
link /difflinks/equal/difflinks/link "new"
## /difflinks/first/first:
missing /difflinks/first/first/link
## /difflinks/difftype/difftype:
file /difflinks/difftype/difftype/link "test"
## /difflinks/difflinks/equal:
link /difflinks/difflinks/equal/link "new"
## /difflinks/difflinks/difflinks:
link /difflinks/difflinks/difflinks/link "test"
## /difffiles/equal/difffiles:
file /difffiles/equal/difffiles/file "bar"
## /difffiles/first/first:
missing /difffiles/first/first/file
## /difffiles/difftype/difftype:
fifo /difffiles/difftype/difftype/file
## /difffiles/difffiles/equal:
file /difffiles/difffiles/equal/file "bar"
## /difffiles/difffiles/difffiles:
file /difffiles/difffiles/difffiles/simple "" \
cabc7e5e80b0946d79edd555e9648486
file /difffiles/difffiles/difffiles/conflict "this is a test file"
conflict /difffiles/difffiles/difffiles/conflict \
8261cfdd89280c4a6c26e4ac86541fe9
## /adddir/conflict:
file /adddir/conflict
## /adddir/partial:
file /adddir/partial/file "foo"
fifo /adddir/partial/fifo
## /rmdir/extra:
dir /rmdir/extra
file /rmdir/extra/localfile.txt "foo"
## /rmdir/conflict:
dir /rmdir/conflict/difftype
present /rmdir/conflict
## /rmdir/partial:
missing /rmdir/partial
## /dirchange/already/fromdir:
file /dirchange/already/fromdir "bar"
## /dirchange/already/todir:
file /dirchange/already/todir/somefile "blah"
## /dirchange/old/fromdir:
file /dirchange/old/fromdir "bar"
## /dirchange/old/todir
file /dirchange/old/todir/file "bar"
## /dirchange/fromdir/extradir:
missing /dirchange/fromdir/extradir/file
fifo /dirchange/fromdir/extradir/fifo
## /dirchange/fromdir/conflict:
file /dirchange/fromdir/conflict/somefile "bar"
## /dirchange/todir/difffile:
file /dirchange/todir/difffile "bar"
## /dirchange/todir/difftype:
fifo /dirchange/todir/difftype
## Tests for post-install actions
file /etc/master.passwd
file /etc/passwd
file /etc/pwd.db
file /etc/spwd.db
file /etc/login.conf "" 7774a0f9a3a372c7c109c32fd31c4b6b
file /etc/login.conf.db
file /etc/mail/aliases "" 7d598f89ec040ab56af54011bdb83337
}
if [ `id -u` -ne 0 ]; then
echo "must be root"
fi
if [ -r /etc/etcupdate.conf ]; then
echo "WARNING: /etc/etcupdate.conf settings may break some tests."
fi
build_trees
etcupdate -nr -d $WORKDIR -D $TEST > $WORKDIR/testn.out
cat > $WORKDIR/correct.out <<EOF
D /dirchange/fromdir/extradir/file
D /dirchange/old/fromdir/somefile
D /first/equal/second/fifo
D /first/equal/second/file
D /first/equal/second/fulldir/file
D /first/equal/second/link
D /rmdir/partial/subdir/fifo
D /rmdir/partial/subdir
D /rmdir/partial
D /first/equal/second/fulldir
D /first/equal/second/emptydir
C /difffiles/difffiles/difffiles/conflict
M /difffiles/difffiles/difffiles/simple
U /difffiles/equal/difffiles/file
U /difflinks/equal/difflinks/link
C /difftype/difftype/difffiles/file
U /difftype/equal/difftype/file
U /difftype/equal/difftype/fromdir
D /difftype/equal/difftype/todir
U /dirchange/old/fromdir
U /dirchange/old/todir
U /etc/login.conf
M /etc/mail/aliases
A /adddir/partial/file
A /dirchange/old/todir/file
A /etc/master.passwd
A /second/equal/first/fifo
A /second/equal/first/file
A /second/equal/first/fulldir/file
A /second/equal/first/link
C /second/second/difffiles/file
Warnings:
Modified regular file remains: /dirchange/fromdir/conflict/somefile
Modified regular file remains: /first/difffiles/second/file
Modified symbolic link remains: /first/difflinks/second/link
Modified directory remains: /first/difftype/second/fifo
Modified directory remains: /rmdir/conflict/difftype
Non-empty directory remains: /rmdir/extra
Non-empty directory remains: /rmdir/conflict
Modified mismatch: /difffiles/difftype/difftype/file (regular file vs fifo file)
Removed file changed: /difffiles/first/first/file
Modified link changed: /difflinks/difflinks/difflinks/link ("old" became "new")
Modified mismatch: /difflinks/difftype/difftype/link (symbolic link vs regular file)
Removed link changed: /difflinks/first/first/link ("old" became "new")
New link conflict: /difftype/difftype/difflinks/link ("new" vs "test")
Modified regular file changed: /difftype/difftype/difftype/one (fifo file became directory)
Modified symbolic link changed: /difftype/difftype/difftype/two (directory became regular file)
Remove mismatch: /difftype/first/first/fifo (fifo file became directory)
Modified directory changed: /dirchange/fromdir/conflict (directory became fifo file)
Modified directory changed: /dirchange/fromdir/extradir (directory became symbolic link)
Modified regular file changed: /dirchange/todir/difffile (regular file became directory)
Modified fifo file changed: /dirchange/todir/difftype (regular file became directory)
New file mismatch: /adddir/conflict (directory vs regular file)
Directory mismatch: $TEST/adddir/conflict (regular file)
Directory mismatch: $TEST/dirchange/todir/difffile (regular file)
Directory mismatch: $TEST/dirchange/todir/difftype (fifo file)
New link conflict: /second/second/difflinks/link ("new link" vs "test link")
New file mismatch: /second/second/difftype/dir (directory vs fifo file)
Needs update: /etc/mail/aliases.db (requires manual update via newaliases(1))
EOF
echo "Differences for -n:"
diff -u -L "correct" $WORKDIR/correct.out -L "test" $WORKDIR/testn.out
etcupdate -r -d $WORKDIR -D $TEST > $WORKDIR/test.out
echo "Differences for real:"
diff -u -L "correct" $WORKDIR/correct.out -L "test" $WORKDIR/test.out
check_trees

View File

@ -23,6 +23,7 @@ SUBDIR= adduser \
digictl \
diskinfo \
dumpcis \
etcupdate \
extattr \
extattrctl \
fifolog \

View File

@ -0,0 +1,6 @@
# $FreeBSD$
SCRIPTS=etcupdate.sh
MAN= etcupdate.8
.include <bsd.prog.mk>

View File

@ -0,0 +1,784 @@
.\" Copyright (c) 2010-2012 Advanced Computing Technologies LLC
.\" Written by: John H. Baldwin <jhb@FreeBSD.org>
.\" All rights reserved.
.\"
.\" 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.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
.\"
.\" $FreeBSD$
.\"
.Dd March 16, 2012
.Dt ETCUPDATE 8
.Os
.Sh NAME
.Nm etcupdate
.Nd "manage updates to system files not updated by installworld"
.Sh SYNOPSIS
.Nm
.Op Fl nBF
.Op Fl d Ar workdir
.Op Fl r | Fl s Ar source | Fl t Ar tarball
.Op Fl A Ar patterns
.Op Fl D Ar destdir
.Op Fl I Ar patterns
.Op Fl L Ar logfile
.Op Fl M Ar options
.Nm
.Cm build
.Op Fl B
.Op Fl d Ar workdir
.Op Fl s Ar source
.Op Fl L Ar logfile
.Op Fl M Ar options
.Ar tarball
.Nm
.Cm diff
.Op Fl d Ar workdir
.Op Fl D Ar destdir
.Op Fl I Ar patterns
.Op Fl L Ar logfile
.Nm
.Cm extract
.Op Fl B
.Op Fl d Ar workdir
.Op Fl s Ar source | Fl t Ar tarball
.Op Fl L Ar logfile
.Op Fl M Ar options
.Nm
.Cm resolve
.Op Fl d Ar workdir
.Op Fl D Ar destdir
.Op Fl L Ar logfile
.Nm
.Cm status
.Op Fl d Ar workdir
.Op Fl D Ar destdir
.Sh DESCRIPTION
The
.Nm
utility is a tool for managing updates to files that are not updated as
part of
.Sq make installworld
such as files in
.Pa /etc .
It manages updates by doing a three-way merge of changes made to these
files against the local versions.
It is also designed to minimize the amount of user intervention with
the goal of simplifying upgrades for clusters of machines.
.Pp
To perform a three-way merge,
.Nm
keeps copies of the current and previous versions of files that it manages.
These copies are stored in two trees known as the
.Dq current
and
.Dq previous
trees.
During a merge,
.Nm
compares the
.Dq current
and
.Dq previous
copies of each file to determine which changes need to be merged into the
local version of each file.
If a file can be updated without generating a conflict,
.Nm
will update the file automatically.
If the local changes to a file conflict with the changes made to a file in
the source tree,
then a merge conflict is generated.
The conflict must be resolved after the merge has finished.
The
.Nm
utility will not perform a new merge until all conflicts from an earlier
merge are resolved.
.Sh MODES
.Pp
The
.Nm
utility supports several modes of operation.
The mode is specified via an optional command argument.
If present, the command must be the first argument on the command line.
If a command is not specified, the default mode is used.
.Ss Default Mode
The default mode merges changes from the source tree to the destination
directory.
First,
it updates the
.Dq current
and
.Dq previous
trees.
Next,
it compares the two trees merging changes into the destination directory.
Finally,
it displays warnings for any conditions it could not handle automatically.
.Pp
If the
.Fl r
option is not specified,
then the first step taken is to update the
.Dq current
and
.Dq previous
trees.
If a
.Dq current
tree already exists,
then that tree is saved as the
.Dq previous
tree.
An older
.Dq previous
tree is removed if it exists.
By default the new
.Dq current
tree is built from a source tree.
However,
if a tarball is specified via the
.Fl t
option,
then the tree is extracted from that tarball instead.
.Pp
Next,
.Nm
compares the files in the
.Dq current
and
.Dq previous
trees.
If a file was removed from the
.Dq current
tree,
then it will be removed from the destination directory only if it
does not have any local modifications.
If a file was added to the
.Dq current
tree,
then it will be copied to the destination directory only if it
would not clobber an existing file.
If a file is changed in the
.Dq current
tree,
then
.Nm
will attempt to merge the changes into the version of the file in the
destination directory.
If the merge encounters conflicts,
then a version of the file with conflict markers will be saved for
future resolution.
If the merge does not encounter conflicts,
then the merged version of the file will be saved in the destination
directory.
If
.Nm
is not able to safely merge in changes to a file other than a merge conflict,
it will generate a warning.
.Pp
For each file that is updated a line will be output with a leading character
to indicate the action taken.
The possible actions follow:
.Pp
.Bl -tag -width "A" -compact -offset indent
.It A
Added
.It C
Conflict
.It D
Deleted
.It M
Merged
.It U
Updated
.El
.Pp
Finally,
if any warnings were encountered they are displayed after the merge has
completed.
.Pp
Note that for certain files
.Nm
will perform post-install actions any time that the file is updated.
Specifically,
.Xr pwd_mkdb 8
is invoked if
.Pa /etc/master.passwd
is changed,
.Xr cap_mkdb 1
is invoked to update
.Pa /etc/login.conf.db
if
.Pa /etc/login.conf
is changed,
.Xr newaliases 1
is invoked if
.Pa /etc/mail/aliases
is changed,
and
.Pa /etc/rc.d/motd
is invoked if
.Pa /etc/motd
is changed.
One exception is that if
.Pa /etc/mail/aliases
is changed and the destination directory is not the default,
then a warning will be issued instead.
This is due to a limitation of the
.Xr newaliases 1
command.
Similarly,
if
.Pa /etc/motd
is changed and the destination directory is not the default,
then
.Pa /etc/rc.d/motd
will not be executed due to a limitation of that script.
In this case no warning is issued as the result of
.Pa /etc/rc.d/motd
is merely cosmetic and will be corrected on the next reboot.
.Ss Build Mode
The
.Cm build
mode is used to build a tarball that contains a snapshot of a
.Dq current
tree.
This tarball can be used by the default and extract modes.
Using a tarball can allow
.Nm
to perform a merge without requiring a source tree that matches the
currently installed world.
The
.Fa tarball
argument specifies the name of the file to create.
The file will be a
.Xr tar 5
file compressed with
.Xr bzip2 1 .
.Ss Diff Mode
The
.Cm diff
mode compares the versions of files in the destination directory to the
.Dq current
tree and generates a unified format diff of the changes.
This can be used to determine which files have been locally modified and how.
Note that
.Nm
does not manage files that are not maintained in the source tree such as
.Pa /etc/fstab
and
.Pa /etc/rc.conf .
.Ss Extract Mode
The
.Cm extract
mode generates a new
.Dq current
tree.
Unlike the default mode,
it does not save any existing
.Dq current
tree and does not modify any existing
.Dq previous
tree.
The new
.Dq current
tree can either be built from a source tree or extracted from a tarball.
.Ss Resolve Mode
The
.Cm resolve
mode is used to resolve any conflicts encountered during a merge.
In this mode,
.Nm
iterates over any existing conflicts prompting the user for actions to take
on each conflicted file.
For each file, the following actions are available:
.Pp
.Bl -tag -width "(tf) theirs-full" -compact
.It (p) postpone
Ignore this conflict for now.
.It (df) diff-full
Show all changes made to the merged file as a unified diff.
.It (e) edit
Change the merged file in an editor.
.It (r) resolved
Install the merged version of the file into the destination directory.
.It (mf) mine-full
Use the version of the file in the destination directory and ignore any
changes made to the file in the
.Dq current
tree.
.It (tf) theirs-full
Use the version of the file from the
.Dq current
tree and discard any local changes made to the file.
.It (h) help
Display the list of commands.
.El
.Ss Status Mode
The
.Cm status
mode shows a summary of the results of the most recent merge.
First it lists any files for which there are unresolved conflicts.
Next it lists any warnings generated during the last merge.
If the last merge did not generate any conflicts or warnings,
then nothing will be output.
.Sh OPTIONS
The following options are available.
Note that most options do not apply to all modes.
.Bl -tag -width ".Fl d Ar workdir"
.It Fl B
Do not build generated files in a private object tree.
Instead,
reuse the generated files from a previously built object tree that matches
the source tree.
This can be useful to avoid gratuitous conflicts in sendmail configuration
files when bootstrapping.
It can also be useful for building a tarball that matches a specific
world build.
.It Fl d Ar workdir
Specify an alternate directory to use as the work directory.
The work directory is used to store the
.Dq current
and
.Dq previous
trees as well as unresolved conflicts.
The default work directory is
.Pa <destdir>/var/db/etcupdate .
.It Fl A Ar patterns
Always install the new version of any files that match any of the patterns
listed in
.Ar patterns .
Each pattern is evaluated as an
.Xr sh 1
shell pattern.
This option may be specified multiple times to specify multiple patterns.
Multiple space-separated patterns may also be specified in a single
option.
Note that ignored files specified via the
.Ev IGNORE_FILES
variable or the
.Fl I
option will not be installed.
.It Fl D Ar destdir
Specify an alternate destination directory as the target of a merge.
This is analagous to the
.Dv DESTDIR
variable used with
.Sq make installworld .
The default destination directory is an empty string which results in
merges updating
.Pa /etc
on the local machine.
.It Fl F
Ignore changes in the FreeBSD ID string when comparing files in the
destination directory to files in either of the
.Dq current
or
.Dq previous
trees.
In
.Cm diff
mode,
this reduces noise due to FreeBSD ID string changes in the output.
During an update this can simplify handling for harmless conflicts caused
by FreeBSD ID string changes.
.Pp
Specifically,
if a file in the destination directory is identical to the same file in the
.Dq previous
tree modulo the FreeBSD ID string,
then the file is treated as if it was unmodified and the
.Dq current
version of the file will be installed.
Similarly,
if a file in the destination directory is identical to the same file in the
.Dq current
tree modulo the FreeBSD ID string,
then the
.Dq current
version of the file will be installed to update the ID string.
If the
.Dq previous
and
.Dq current
versions of the file are identical,
then
.Nm
will not change the file in the destination directory.
.Pp
Due to limitations in the
.Xr diff 1
command,
this option may not have an effect if there are other changes in a file that
are close to the FreeBSD ID string.
.It Fl I Ar patterns
Ignore any files that match any of the patterns listed in
.Ar patterns .
No warnings or other messages will be generated for those files during a
merge.
Each pattern is evaluated as an
.Xr sh 1
shell pattern.
This option may be specified multiple times to specify multiple patterns.
Multiple space-separated patterns may also be specified in a single
option.
.It Fl L Ar logfile
Specify an alternate path for the log file.
The
.Nm
utility logs each command that it invokes along with the standard output
and standard error to this file.
By default the log file is stored in a file named
.Pa log
in the work directory.
.It Fl M Ar options
Pass
.Ar options
as additional parameters to
.Xr make 1
when building a
.Dq current
tree.
This can be used for to set the
.Dv TARGET
or
.Dv TARGET_ARCH
variables for a cross-build.
.It Fl n
Enable
.Dq dry-run
mode.
Do not merge any changes to the destination directory.
Instead,
report what actions would be taken during a merge.
Note that the existing
.Dq current
and
.Dq previous
trees will not be changed.
If the
.Fl r
option is not specified,
then a temporary
.Dq current
tree will be extracted to perform the comparison.
.It Fl r
Do not update the
.Dq current
and
.Dq previous
trees during a merge.
This can be used to
.Dq re-run
a previous merge operation.
.It Fl s Ar source
Specify an alternate source tree to use when building or extracting a
.Dq current
tree.
The default source tree is
.Pa /usr/src .
.It Fl t Ar tarball
Extract a new
.Dq current
tree from a tarball previously generated by the
.Cm build
command rather than building the tree from a source tree.
.El
.Sh CONFIG FILE
The
.Nm
utility can also be configured by setting variables in an optional
configuration file named
.Pa /etc/etcupdate.conf .
Note that command line options override settings in the configuration file.
The configuration file is executed by
.Xr sh 1 ,
so it uses that syntax to set configuration variables.
The following variables can be set:
.Bl -tag -width ".Ev ALWAYS_INSTALL"
.It Ev ALWAYS_INSTALL
Always install files that match any of the patterns listed in this variable
similar to the
.Fl A
option.
.It Ev DESTDIR
Specify an alternate destination directory similar to the
.Fl D
option.
.It Ev EDITOR
Specify a program to edit merge conflicts.
.It Ev FREEBSD_ID
Ignore changes in the FreeBSD ID string similar to the
.Fl F
option.
This is enabled by setting the variable to a non-empty value.
.It Ev IGNORE_FILES
Ignore files that match any of the patterns listed in this variable
similar to the
.Fl I
option.
.It Ev LOGFILE
Specify an alternate path for the log file similar to the
.Fl L
option.
.It Ev MAKE_OPTIONS
Pass additional options to
.Xr make 1
when building a
.Dq current
tree similar to the
.Fl M
option.
.It Ev SRCDIR
Specify an alternate source tree similar to the
.Fl s
option.
.It Ev WORKDIR
Specify an alternate work directory similar to the
.Fl d
option.
.El
.Sh ENVIRONMENT
The
.Nm
utility uses the program identified in the
.Ev EDITOR
environment variable to edit merge conflicts.
If
.Ev EDITOR
is not set,
.Xr vi 1
is used as the default editor.
.Sh FILES
.Bl -tag -width ".Pa /var/db/etcupdate/log" -compact
.It Pa /etc/etcupdate.conf
Optional config file.
.It Pa /var/db/etcupdate
Default work directory used to store trees and other data.
.It Pa /var/db/etcupdate/log
Default log file.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
If the source tree matches the currently installed world,
then the following can be used to bootstrap
.Nm
so that it can be used for future upgrades:
.Pp
.Dl "etcupdate extract"
.Pp
To merge changes after an upgrade via the buildworld and installworld process:
.Pp
.Dl "etcupdate"
.Pp
To resolve any conflicts generated during a merge:
.Pp
.Dl "etcupdate resolve"
.Sh DIAGNOSTICS
The following warning messages may be generated during a merge.
Note that several of these warnings cover obscure cases that should occur
rarely if at all in practice.
For example,
if a file changes from a file to a directory in the
.Dq current
tree
and the file was modified in the destination directory,
then a warning will be triggered.
In general,
when a warning references a pathname,
the corresponding file in the destination directory is not changed by a
merge operation.
.Bl -diag
.It "Directory mismatch: <path> (<type>)"
An attempt was made to create a directory at
.Pa path
but an existing file of type
.Dq type
already exists for that path name.
.It "Modified link changed: <file> (<old> became <new>)"
The target of a symbolic link named
.Pa file
was changed from
.Dq old
to
.Dq new
in the
.Dq current
tree.
The symbolic link has been modified to point to a target that is neither
.Dq old
nor
.Dq new
in the destination directory.
.It "Modified mismatch: <file> (<new> vs <dest>)"
A file named
.Pa file
of type
.Dq new
was modified in the
.Dq current
tree,
but the file exists as a different type
.Dq dest
in the destination directory.
.It "Modified <type> changed: <file> (<old> became <new>)"
A file named
.Pa file
changed type from
.Dq old
in the
.Dq previous
tree to type
.Dq new
in the
.Dq current
tree.
The file in the destination directory of type
.Dq type
has been modified,
so it could not be merged automatically.
.It "Modified <type> remains: <file>"
The file of type
.Dq type
named
.Pa file
has been removed from the
.Dq current
tree,
but it has been locally modified.
The modified version of the file remains in the destination directory.
.It "Needs update: /etc/mail/aliases.db (required manual update via newaliases(1))"
The file
.Pa /etc/mail/aliases
was updated during a merge with a non-empty destination directory.
Due to a limitation of the
.Xr newaliases 1
command,
.Nm
was not able to automatically update the corresponding aliases database.
.It "New file mismatch: <file> (<new> vs <dest>)"
A new file named
.Pa file
of type
.Dq new
has been added to the
.Dq current
tree.
A file of that name already exists in the destination directory,
but it is of a different type
.Dq dest .
.It "New link conflict: <file> (<new> vs <dest>)"
A symbolic link named
.Pa file
has been added to the
.Dq current
tree that links to
.Dq new .
A symbolic link of the same name already exists in the destination
directory,
but it links to a different target
.Dq dest .
.It "Non-empty directory remains: <file>"
The directory
.Pa file
was removed from the
.Dq current
tree,
but it contains additional files in the destination directory.
These additional files as well as the directory remain.
.It "Remove mismatch: <file> (<old> became <new>)"
A file named
.Pa file
changed from type
.Dq old
in the
.Dq previous
tree to type
.Dq new
in the
.Dq current
tree,
but it has been removed in the destination directory.
.It "Removed file changed: <file>"
A file named
.Pa file
was modified in the
.Dq current
tree,
but it has been removed in the destination directory.
.It "Removed link changed: <file> (<old> became <new>)"
The target of a symbolic link named
.Pa file
was changed from
.Dq old
to
.Dq new
in the
.Dq current
tree,
but it has been removed in the destination directory.
.El
.Sh SEE ALSO
.Xr cap_mkdb 1 ,
.Xr diff 1 ,
.Xr make 1 ,
.Xr newaliases 1 ,
.Xr sh 1 ,
.Xr pwd_mkdb 8
.\".Sh HISTORY
.Sh AUTHORS
The
.Nm
utility was written by
.An John Baldwin Aq jhb@FreeBSD.org .
.Sh BUGS
Rerunning a merge does not automatically delete conflicts left over from a
previous merge.
Any conflicts must be resolved before the merge can be rerun.
It it is not clear if this is a feature or a bug.
.Pp
There is no way to easily automate conflict resolution for specific files.
For example, one can imagine a syntax along the lines of
.Pp
.Dl "etcupdate resolve tf /some/file"
.Pp
to resolve a specific conflict in an automated fashion.
.Pp
It might be nice to have something like a
.Sq revert
command to replace a locally modified version of a file with the stock
version of the file.
For example:
.Pp
.Dl "etcupdate revert /etc/mail/freebsd.cf"
.Pp
Bootstrapping
.Nm
often results in gratuitous diffs in
.Pa /etc/mail/*.cf
that cause conflicts in the first merge.
If an object tree that matches the source tree is present when bootstrapping,
then passing the
.Fl B
flag to the
.Cm extract
command can work around this.

1675
usr.sbin/etcupdate/etcupdate.sh Executable file

File diff suppressed because it is too large Load Diff