NetBSD/usr.sbin/postinstall/postinstall
2011-03-12 23:04:16 +00:00

1805 lines
42 KiB
Bash
Executable File

#!/bin/sh
#
# $NetBSD: postinstall,v 1.115 2011/03/12 23:04:16 erh Exp $
#
# Copyright (c) 2002-2008 The NetBSD Foundation, Inc.
# All rights reserved.
#
# This code is derived from software contributed to The NetBSD Foundation
# by Luke Mewburn.
#
# 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 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.
#
# postinstall
# Check for or fix configuration changes that occur
# over time as NetBSD evolves.
#
#
# checks to add:
# - sysctl(8) renames (net.inet6.ip6.bindv6only -> net.inet6.ip6.v6only)
# - de* -> tlp* migration (/etc/ifconfig.de*, $ifconfig_de*,
# dhclient.conf, ...) ?
# - support quiet/verbose mode ?
# - differentiate between failures caused by missing source
# and real failures
# - install moduli into usr/share/examples/ssh and use from there?
# - differentiate between "needs fix" versus "can't fix" issues
#
# This script is executed as part of a cross build. Allow the build
# environment to override the locations of some tools.
: ${AWK:=awk}
: ${EGREP:=egrep}
: ${FGREP:=fgrep}
: ${GREP:=grep}
: ${MAKE:=make}
#
# helper functions
#
err()
{
exitval=$1
shift
echo 1>&2 "${PROGNAME}: $*"
if [ -n "${SCRATCHDIR}" ]; then
/bin/rm -rf "${SCRATCHDIR}"
fi
exit ${exitval}
}
warn()
{
echo 1>&2 "${PROGNAME}: $*"
}
msg()
{
echo " $*"
}
mkdtemp()
{
# Make sure we don't loop forever if mkdir will always fail.
[ -d /tmp ] || err 2 /tmp is not a directory
[ -w /tmp ] || err 2 /tmp is not writable
_base="/tmp/_postinstall.$$"
_serial=0
while true; do
_dir="${_base}.${_serial}"
mkdir -m 0700 "${_dir}" && break
_serial=$((${_serial} + 1))
done
echo "${_dir}"
}
# Quote args to make them safe in the shell.
# Usage: quotedlist="$(shell_quote args...)"
#
# After building up a quoted list, use it by evaling it inside
# double quotes, like this:
# eval "set -- $quotedlist"
# or like this:
# eval "\$command $quotedlist \$filename"
shell_quote()
{
local result=''
local arg
for arg in "$@" ; do
# Append a space if necessary
result="${result}${result:+ }"
# Convert each embedded ' to '\'',
# then insert ' at the beginning of the first line,
# and append ' at the end of the last line.
result="${result}$(printf "%s\n" "$arg" | \
sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/")"
done
printf "%s\n" "$result"
}
# additem item description
# Add item to list of supported items to check/fix,
# which are checked/fixed by default if no item is requested by user.
#
additem()
{
[ $# -eq 2 ] || err 3 "USAGE: additem item description"
defaultitems="${defaultitems}${defaultitems:+ }$1"
eval desc_$1=\"$2\"
}
# adddisableditem item description
# Add item to list of supported items to check/fix,
# but execute the item only if the user asks for it explicitly.
#
adddisableditem()
{
[ $# -eq 2 ] || err 3 "USAGE: adddisableditem item description"
otheritems="${otheritems}${otheritems:+ }$1"
eval desc_$1=\"$2\"
}
# checkdir op dir mode
# Ensure dir exists, and if not, create it with the appropriate mode.
# Returns 0 if ok, 1 otherwise.
#
check_dir()
{
[ $# -eq 3 ] || err 3 "USAGE: check_dir op dir mode"
_cdop="$1"
_cddir="$2"
_cdmode="$3"
[ -d "${_cddir}" ] && return 0
if [ "${_cdop}" = "check" ]; then
msg "${_cddir} is not a directory"
return 1
elif ! mkdir -m "${_cdmode}" "${_cddir}" ; then
msg "Can't create missing ${_cddir}"
return 1
else
msg "Missing ${_cddir} created"
fi
return 0
}
# check_ids op type file id [...]
# Check if file of type "users" or "groups" contains the relevant IDs
# Returns 0 if ok, 1 otherwise.
#
check_ids()
{
[ $# -ge 4 ] || err 3 "USAGE: checks_ids op type file id [...]"
_op="$1"
_type="$2"
_file="$3"
shift 3
#_ids="$@"
if [ ! -f "${_file}" ]; then
msg "${_file} doesn't exist; can't check for missing ${_type}"
return 1
fi
if [ ! -r "${_file}" ]; then
msg "${_file} is not readable; can't check for missing ${_type}"
return 1
fi
_notfixed=""
if [ "${_op}" = "fix" ]; then
_notfixed="${NOT_FIXED}"
fi
_missing="$(${AWK} -F: '
BEGIN {
for (x = 1; x < ARGC; x++)
idlist[ARGV[x]]++
ARGC=1
}
{
found[$1]++
}
END {
for (id in idlist) {
if (! (id in found))
print id
}
}
' "$@" < "${_file}")" || return 1
if [ -n "${_missing}" ]; then
msg "Missing ${_type}${_notfixed}:" $(echo ${_missing})
return 1
fi
return 0
}
# populate_dir op onlynew src dest mode file [file ...]
# Perform op ("check" or "fix") on files in src/ against dest/
# If op = "check" display missing or changed files, optionally with diffs.
# If op != "check" copies any missing or changed files.
# If onlynew evaluates to true, changed files are ignored.
# Returns 0 if ok, 1 otherwise.
#
populate_dir()
{
[ $# -ge 5 ] || err 3 "USAGE: populate_dir op onlynew src dest mode file [...]"
_op="$1"
_onlynew="$2"
_src="$3"
_dest="$4"
_mode="$5"
shift 5
#_files="$@"
if [ ! -d "${_src}" ]; then
msg "${_src} is not a directory; skipping check"
return 1
fi
check_dir "${_op}" "${_dest}" 755 || return 1
_cmpdir_rv=0
for f in "$@"; do
fs="${_src}/${f}"
fd="${_dest}/${f}"
_error=""
if [ ! -f "${fd}" ]; then
_error="${fd} does not exist"
elif ! cmp -s "${fs}" "${fd}" ; then
if $_onlynew; then # leave existing ${fd} alone
continue;
fi
_error="${fs} != ${fd}"
else
continue
fi
if [ "${_op}" = "check" ]; then
msg "${_error}"
if [ -n "${DIFF_STYLE}" -a -f "${fd}" ]; then
diff -${DIFF_STYLE} ${DIFF_OPT} "${fd}" "${fs}"
fi
_cmpdir_rv=1
elif ! rm -f "${fd}" ||
! cp -f "${fs}" "${fd}"; then
msg "Can't copy ${fs} to ${fd}"
_cmpdir_rv=1
elif ! chmod "${_mode}" "${fd}"; then
msg "Can't change mode of ${fd} to ${_mode}"
_cmpdir_rv=1
else
msg "Copied ${fs} to ${fd}"
fi
done
return ${_cmpdir_rv}
}
# compare_dir op src dest mode file [file ...]
# Perform op ("check" or "fix") on files in src/ against dest/
# If op = "check" display missing or changed files, optionally with diffs.
# If op != "check" copies any missing or changed files.
# Returns 0 if ok, 1 otherwise.
#
compare_dir()
{
[ $# -ge 4 ] || err 3 "USAGE: compare_dir op src dest mode file [...]"
_op="$1"
_src="$2"
_dest="$3"
_mode="$4"
shift 4
#_files="$@"
populate_dir "$_op" false "$_src" "$_dest" "$_mode" "$@"
}
# move_file op src dest --
# Check (op == "check") or move (op != "check") from src to dest.
# Returns 0 if ok, 1 otherwise.
#
move_file()
{
[ $# -eq 3 ] || err 3 "USAGE: move_file op src dest"
_fm_op="$1"
_fm_src="$2"
_fm_dest="$3"
if [ -f "${_fm_src}" -a ! -f "${_fm_dest}" ]; then
if [ "${_fm_op}" = "check" ]; then
msg "Move ${_fm_src} to ${_fm_dest}"
return 1
fi
if ! mv "${_fm_src}" "${_fm_dest}"; then
msg "Can't move ${_fm_src} to ${_fm_dest}"
return 1
fi
msg "Moved ${_fm_src} to ${_fm_dest}"
fi
return 0
}
# rcconf_is_set op name var [verbose] --
# Load the rcconf for name, and check if obsolete rc.conf(5) variable
# var is defined or not.
# Returns 0 if defined (even to ""), otherwise 1.
# If verbose != "", print an obsolete warning if the var is defined.
#
rcconf_is_set()
{
[ $# -ge 3 ] || err 3 "USAGE: rcconf_is_set op name var [verbose]"
_rcis_op="$1"
_rcis_name="$2"
_rcis_var="$3"
_rcis_verbose="$4"
_rcis_notfixed=""
if [ "${_rcis_op}" = "fix" ]; then
_rcis_notfixed="${NOT_FIXED}"
fi
(
for f in \
"${DEST_DIR}/etc/rc.conf" \
"${DEST_DIR}/etc/rc.conf.d/${_rcis_name}"; do
[ -f "${f}" ] && . "${f}"
done
eval echo -n \"\${${_rcis_var}}\" 1>&3
if eval "[ -n \"\${${_rcis_var}}\" \
-o \"\${${_rcis_var}-UNSET}\" != \"UNSET\" ]"; then
if [ -n "${_rcis_verbose}" ]; then
msg \
"Obsolete rc.conf(5) variable '\$${_rcis_var}' found.${_rcis_notfixed}"
fi
exit 0
else
exit 1
fi
)
}
# find_file_in_dirlist() file message dir1 [...] --
# Find which directory file is in, and sets ${dir} to match.
# Returns 0 if matched, otherwise 1 (and sets ${dir} to "").
#
# Generally, check the directory for the "checking from source" case,
# and then the directory for the "checking from extracted etc.tgz" case.
#
find_file_in_dirlist()
{
[ $# -ge 3 ] || err 3 "USAGE: find_file_in_dirlist file msg dir1 [...]"
_file="$1" ; shift
_msg="$1" ; shift
_dir1st= # first dir in list
for dir in "$@"; do
: ${_dir1st:="${dir}"}
if [ -f "${dir}/${_file}" ]; then
if [ "${_dir1st}" != "${dir}" ]; then
msg \
"(Checking for ${_msg} from ${dir} instead of ${_dir1st})"
fi
return 0
fi
done
msg "Can't find source directory for ${_msg}"
return 1
}
# stat op format target value
# Call stat(1) on the given target according to the given format,
# if stat(1) is available (it is presumed to live in /usr/bin).
# If it is not available, this routine will always succeed, otherwise
# it returns 0 or 1, depending on whether or not the output from
# stat(1) matches the expected value.
#
stat()
{
_stop="$1"
_stfmt="$2"
_sttgt="$3"
_stval="$4"
if [ ! -x /usr/bin/stat ]; then
msg \
"(/usr/bin/stat not available; skipping ${_stop} on ${_sttgt})"
return 0
fi
_stres="$(/usr/bin/stat -q -f "${_stfmt}" "${_sttgt}")"
[ "${_stres}" = "${_stval}" ]
return $?
}
# file_exists_exact path
# Returns true if a file exists in the ${DEST_DIR} whose name
# is exactly ${path}, interpreted in a case-sensitive way
# even if the underlying file system is case-insensitive.
#
# The path must begin with '/' or './', and is interpreted as
# being relative to ${DEST_DIR}.
#
file_exists_exact()
{
[ -n "$1" ] || err 3 "USAGE: file_exists_exact path"
_path="${1#.}"
[ -h "${DEST_DIR}${_path}" ] || \
[ -e "${DEST_DIR}${_path}" ] || return 1
while [ "${_path}" != "/" ] ; do
_dirname="$(dirname "${_path}" 2>/dev/null)"
_basename="$(basename "${_path}" 2>/dev/null)"
ls -fa "${DEST_DIR}${_dirname}" 2> /dev/null \
| ${FGREP} -x "${_basename}" >/dev/null \
|| return 1
_path="${_dirname}"
done
return 0
}
# obsolete_paths op
# Obsolete the list of paths provided on stdin.
# Each path is relative to ${DEST_DIR}, and should
# be an absolute path or start with `./'.
#
obsolete_paths()
{
[ -n "$1" ] || err 3 "USAGE: obsolete_paths fix|check"
op="$1"
failed=0
while read ofile; do
if ! file_exists_exact "${ofile}"; then
continue
fi
ofile="${DEST_DIR}${ofile#.}"
cmd="rm"
ftype="file"
if [ -h "${ofile}" ]; then
ftype="link"
elif [ -d "${ofile}" ]; then
ftype="directory"
cmd="rmdir"
fi
if [ "${op}" = "check" ]; then
msg "Remove obsolete ${ftype} ${ofile}"
failed=1
elif ! eval "${cmd} \${ofile}"; then
msg "Can't remove obsolete ${ftype} ${ofile}"
failed=1
else
msg "Removed obsolete ${ftype} ${ofile}"
fi
done
return ${failed}
}
# obsolete_libs dir
# Display the minor/teeny shared libraries in dir that are considered
# to be obsolete.
#
# The implementation supports removing obsolete major libraries
# if the awk variable AllLibs is set, although there is no way to
# enable that in the enclosing shell function as this time.
#
obsolete_libs()
{
[ $# -eq 1 ] || err 3 "USAGE: obsolete_libs dir"
dir="$1"
_obsolete_libs "${dir}"
_obsolete_libs "/usr/libdata/debug/${dir}"
}
_obsolete_libs()
{
dir="$1"
(
if [ ! -e "${DEST_DIR}/${dir}" ]
then
return 0
fi
cd "${DEST_DIR}/${dir}" || err 2 "can't cd to ${DEST_DIR}/${dir}"
echo lib*.so.* \
| tr ' ' '\n' \
| ${AWK} -v LibDir="${dir}/" '
#{
function digit(v, c, n) { return (n <= c) ? v[n] : 0 }
function checklib(results, line, regex) {
if (! match(line, regex))
return
lib = substr(line, RSTART, RLENGTH)
rev = substr($0, RLENGTH+1)
if (! (lib in results)) {
results[lib] = rev
return
}
orevc = split(results[lib], orev, ".")
nrevc = split(rev, nrev, ".")
maxc = (orevc > nrevc) ? orevc : nrevc
for (i = 1; i <= maxc; i++) {
res = digit(orev, orevc, i) - digit(nrev, nrevc, i)
if (res < 0) {
print LibDir lib results[lib]
results[lib] = rev
return
} else if (res > 0) {
print LibDir lib rev
return
}
}
}
/^lib.*\.so\.[0-9]+\.[0-9]+(\.[0-9]+)?(\.debug)?$/ {
if (AllLibs)
checklib(minor, $0, "^lib.*\\.so\\.")
else
checklib(found, $0, "^lib.*\\.so\\.[0-9]+\\.")
}
/^lib.*\.so\.[0-9]+$/ {
if (AllLibs)
checklib(major, $0, "^lib.*\\.so\\.")
}
#}'
)
}
# modify_file op srcfile scratchfile awkprog
# Apply awkprog to srcfile sending output to scratchfile, and
# if appropriate replace srcfile with scratchfile.
#
modify_file()
{
[ $# -eq 4 ] || err 3 "USAGE: modify_file op file scratch awkprog"
_mfop="$1"
_mffile="$2"
_mfscratch="$3"
_mfprog="$4"
_mffailed=0
${AWK} "${_mfprog}" < "${_mffile}" > "${_mfscratch}"
if ! cmp -s "${_mffile}" "${_mfscratch}"; then
diff "${_mffile}" "${_mfscratch}" > "${_mfscratch}.diffs"
if [ "${_mfop}" = "check" ]; then
msg "${_mffile} needs the following changes:"
_mffailed=1
elif ! rm -f "${_mffile}" ||
! cp -f "${_mfscratch}" "${_mffile}"; then
msg "${_mffile} changes not applied:"
_mffailed=1
else
msg "${_mffile} changes applied:"
fi
while read _line; do
msg " ${_line}"
done < "${_mfscratch}.diffs"
fi
return ${_mffailed}
}
# contents_owner op directory user group
# Make sure directory and contents are owned (and group-owned)
# as specified.
#
contents_owner()
{
[ $# -eq 4 ] || err 3 "USAGE: contents_owner op dir user group"
_op="$1"
_dir="$2"
_user="$3"
_grp="$4"
if [ "${_op}" = "check" ]; then
if [ ! -z "`find "${_dir}" \( ! -user "${_user}" \) -o \
\( ! -group "${_grp}" \)`" ]; then
msg \
"${_dir} and contents not all owned by ${_user}:${_grp}"
return 1
else
return 0
fi
elif [ "${_op}" = "fix" ]; then
find "${_dir}" \( \( ! -user "${_user}" \) -o \
\( ! -group "${_grp}" \) \) -a -print0 \
| xargs -0 chown "${_user}:${_grp}"
fi
}
# get_makevar var [var ...]
# Retrieve the value of a user-settable system make variable
get_makevar()
{
$SOURCEMODE || err 3 "get_makevar must be used in source mode"
[ $# -eq 0 ] && err 3 "USAGE: get_makevar var [var ...]"
for _var in "$@"; do
_value="$(echo '.include <bsd.own.mk>' | \
${MAKE} -f - -V "${_var}")"
eval ${_var}=\"${_value}\"
done
}
# detect_x11
# Detect if X11 components should be analysed and set values of
# relevant variables.
detect_x11()
{
if $SOURCEMODE; then
get_makevar MKX11 X11ROOTDIR X11SRCDIR
else
if [ -f "${SRC_DIR}/etc/mtree/set.xetc" ]; then
MKX11=yes
X11ROOTDIR=/this/value/isnt/used/yet
else
MKX11=no
X11ROOTDIR=
fi
X11SRCDIR=/nonexistent/xsrc
fi
}
#
# items
# -----
#
#
# bluetooth
#
additem bluetooth "bluetooth configuration is up to date"
do_bluetooth()
{
[ -n "$1" ] || err 3 "USAGE: do_bluetooth fix|check"
op="$1"
failed=0
populate_dir "${op}" true \
"${SRC_DIR}/etc/bluetooth" "${DEST_DIR}/etc/bluetooth" 644 \
hosts protocols btattach.conf btdevctl.conf
failed=$(( ${failed} + $? ))
move_file "${op}" "${DEST_DIR}/var/db/btdev.xml" \
"${DEST_DIR}/var/db/btdevctl.plist"
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# ddbonpanic
#
additem ddbonpanic "verify ddb.onpanic is configured in sysctl.conf"
do_ddbonpanic()
{
[ -n "$1" ] || err 3 "USAGE: do_ddbonpanic fix|check"
if ${GREP} -E '^#*[[:space:]]*ddb\.onpanic[[:space:]]*\??=[[:space:]]*[[:digit:]]+' \
"${DEST_DIR}/etc/sysctl.conf" >/dev/null 2>&1
then
result=0
else
if [ "$1" = check ]; then
msg \
"The ddb.onpanic behaviour is not explicitly specified in /etc/sysctl.conf"
result=1
else
echo >> "${DEST_DIR}/etc/sysctl.conf"
sed < "${SRC_DIR}/etc/sysctl.conf" \
-e '/^ddb\.onpanic/q' | \
sed -e '1,/^$/d' >> \
"${DEST_DIR}/etc/sysctl.conf"
result=$?
fi
fi
return ${result}
}
#
# defaults
#
additem defaults "/etc/defaults/ being up to date"
do_defaults()
{
[ -n "$1" ] || err 3 "USAGE: do_defaults fix|check"
op="$1"
failed=0
compare_dir "$op" "${SRC_DIR}/etc/defaults" "${DEST_DIR}/etc/defaults" \
444 \
daily.conf monthly.conf rc.conf security.conf weekly.conf
failed=$(( ${failed} + $? ))
find_file_in_dirlist pf.boot.conf "pf.boot.conf" \
"${SRC_DIR}/usr.sbin/pf/etc/defaults" "${SRC_DIR}/etc/defaults" \
|| return 1
# ${dir} is set by find_file_in_dirlist()
compare_dir "$op" "${dir}" "${DEST_DIR}/etc/defaults" 444 pf.boot.conf
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# dhcpcd
#
additem dhcpcd "dhcpcd configuration is up to date"
do_dhcpcd()
{
[ -n "$1" ] || err 3 "USAGE: do_dhcpcd fix|check"
op="$1"
failed=0
find_file_in_dirlist dhcpcd.conf "dhcpcd.conf" \
"${SRC_DIR}/external/bsd/dhcpcd/dist" "${SRC_DIR}/etc" || return 1
# ${dir} is set by find_file_in_dirlist()
populate_dir "$op" true "${dir}" "${DEST_DIR}/etc" 644 dhcpcd.conf
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# envsys
#
additem envsys "envsys configuration is up to date"
do_envsys()
{
[ -n "$1" ] || err 3 "USAGE: do_envsys fix|check"
op="$1"
failed=0
populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
envsys.conf
failed=$(( ${failed} + $? ))
populate_dir "$op" true "${SRC_DIR}/etc/powerd/scripts" \
"${DEST_DIR}/etc/powerd/scripts" 555 sensor_battery \
sensor_drive sensor_fan sensor_indicator sensor_power \
sensor_resistance sensor_temperature sensor_voltage
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# X11 fontconfig
#
additem fontconfig "X11 font configuration is up to date"
do_fontconfig()
{
[ -n "$1" ] || err 3 "USAGE: do_fontconfig fix|check"
op="$1"
failed=0
if [ -f "${DEST_DIR}/etc/fonts/conf.d/10-unhinted.conf" -a \
-f "${DEST_DIR}/etc/fonts/conf.d/10-autohint.conf" ]; then
failed=1
fi
if [ "$failed" = 1 ]; then
msg \
"Broken fontconfig configuration found; please delete these files"
msg \
"in the ${DESTDIR}/etc/fonts/conf.d/ subdirectory:"
msg \
" 10-autohint.conf 10-no-sub-pixel.conf 10-sub-pixel-bgr.conf"
msg \
" 10-sub-pixel-rgb.conf 10-sub-pixel-vbgr.conf"
msg \
" 10-sub-pixel-vrgb.conf 10-unhinted.conf 25-unhint-nonlatin.conf"
msg \
" 65-khmer.conf 70-no-bitmaps.conf 70-yes-bitmaps.conf"
msg \
"(This warning only appears if both the 10-unhinted.conf and"
msg \
"10-autohint.conf files are present."
fi
return ${failed}
}
#
# gid
#
additem gid "required groups in /etc/group"
do_gid()
{
[ -n "$1" ] || err 3 "USAGE: do_gid fix|check"
check_ids "$1" groups "${DEST_DIR}/etc/group" \
named ntpd sshd authpf _pflogd _rwhod _proxy _timedc \
_sdpd _httpd _mdnsd _atf _tcpdump
}
#
# gpio
#
additem gpio "gpio configuration is up to date"
do_gpio()
{
[ -n "$1" ] || err 3 "USAGE: do_gpio fix|check"
op="$1"
failed=0
populate_dir "$op" true "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
gpio.conf
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# hosts
#
additem hosts "/etc/hosts being up to date"
do_hosts()
{
[ -n "$1" ] || err 3 "USAGE: do_hosts fix|check"
modify_file "$1" "${DEST_DIR}/etc/hosts" "${SCRATCHDIR}/hosts" '
/^(127\.0\.0\.1|::1)[ ]+[^\.]*$/ {
print $0, "localhost."
next
}
{ print }
'
return $?
}
#
# iscsi
#
additem iscsi "/etc/iscsi is populated"
do_iscsi()
{
[ -n "$1" ] || err 3 "USAGE: do_iscsi fix|check"
populate_dir "${op}" true \
"${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 600 auths
populate_dir "${op}" true \
"${SRC_DIR}/etc/iscsi" "${DEST_DIR}/etc/iscsi" 644 targets
return $?
}
#
# makedev
#
additem makedev "/dev/MAKEDEV being up to date"
do_makedev()
{
[ -n "$1" ] || err 3 "USAGE: do_makedev fix|check"
failed=0
if [ -f "${SRC_DIR}/etc/MAKEDEV.tmpl" ]; then
# generate MAKEDEV from source if source is available
env MACHINE="${MACHINE}" \
MACHINE_ARCH="${MACHINE_ARCH}" \
NETBSDSRCDIR="${SRC_DIR}" \
${AWK} -f "${SRC_DIR}/etc/MAKEDEV.awk" \
"${SRC_DIR}/etc/MAKEDEV.tmpl" > "${SCRATCHDIR}/MAKEDEV"
fi
find_file_in_dirlist MAKEDEV "MAKEDEV" \
"${SCRATCHDIR}" "${SRC_DIR}/dev" \
|| return 1
# ${dir} is set by find_file_in_dirlist()
compare_dir "$1" "${dir}" "${DEST_DIR}/dev" 555 MAKEDEV
failed=$(( ${failed} + $? ))
find_file_in_dirlist MAKEDEV.local "MAKEDEV.local" \
"${SRC_DIR}/etc" "${SRC_DIR}/dev" \
|| return 1
# ${dir} is set by find_file_in_dirlist()
compare_dir "$1" "${dir}" "${DEST_DIR}/dev" 555 MAKEDEV.local
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# motd
#
additem motd "contents of motd"
do_motd()
{
[ -n "$1" ] || err 3 "USAGE: do_motd fix|check"
if ${GREP} -i 'http://www.NetBSD.org/Misc/send-pr.html' \
"${DEST_DIR}/etc/motd" >/dev/null 2>&1 \
|| ${GREP} -i 'http://www.NetBSD.org/support/send-pr.html' \
"${DEST_DIR}/etc/motd" >/dev/null 2>&1
then
tmp1="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
tmp2="$(mktemp /tmp/postinstall.motd.XXXXXXXX)"
sed '1,2d' <"${SRC_DIR}/etc/motd" >"${tmp1}"
sed '1,2d' <"${DEST_DIR}/etc/motd" >"${tmp2}"
if [ "$1" = check ]; then
cmp -s "${tmp1}" "${tmp2}"
result=$?
if [ "${result}" -ne 0 ]; then
msg \
"Bug reporting messages do not seem to match the installed release"
fi
else
head -n 2 "${DEST_DIR}/etc/motd" >"${tmp1}"
sed '1,2d' <"${SRC_DIR}/etc/motd" >>"${tmp1}"
cp "${tmp1}" "${DEST_DIR}/etc/motd"
result=0
fi
rm -f "${tmp1}" "${tmp2}"
else
result=0
fi
return ${result}
}
#
# mtree
#
additem mtree "/etc/mtree/ being up to date"
do_mtree()
{
[ -n "$1" ] || err 3 "USAGE: do_mtree fix|check"
failed=0
compare_dir "$1" "${SRC_DIR}/etc/mtree" "${DEST_DIR}/etc/mtree" 444 special
failed=$(( ${failed} + $? ))
if ! $SOURCEMODE; then
MTREE_DIR="${SRC_DIR}/etc/mtree"
else
${MAKE} -C ${SRC_DIR}/etc/mtree emit_dist_file > \
"${SCRATCHDIR}/NetBSD.dist"
MTREE_DIR="${SCRATCHDIR}"
fi
compare_dir "$1" "${MTREE_DIR}" "${DEST_DIR}/etc/mtree" 444 NetBSD.dist
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# named
#
additem named "named configuration update"
do_named()
{
[ -n "$1" ] || err 3 "USAGE: do_named fix|check"
op="$1"
move_file "${op}" \
"${DEST_DIR}/etc/namedb/named.conf" \
"${DEST_DIR}/etc/named.conf"
compare_dir "${op}" "${SRC_DIR}/etc/namedb" "${DEST_DIR}/etc/namedb" \
644 \
root.cache
}
#
# pam
#
additem pam "/etc/pam.d is populated"
do_pam()
{
[ -n "$1" ] || err 3 "USAGE: do_pam fix|check"
op="$1"
failed=0
populate_dir "${op}" true "${SRC_DIR}/etc/pam.d" \
"${DEST_DIR}/etc/pam.d" 644 \
README display_manager ftpd gdm imap kde login other passwd \
pop3 ppp rexecd rsh sshd su system telnetd xdm xserver
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# periodic
#
additem periodic "/etc/{daily,weekly,monthly,security} being up to date"
do_periodic()
{
[ -n "$1" ] || err 3 "USAGE: do_periodic fix|check"
compare_dir "$1" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
daily weekly monthly security
}
#
# pf
#
additem pf "pf configuration being up to date"
do_pf()
{
[ -n "$1" ] || err 3 "USAGE: do_pf fix|check"
op="$1"
failed=0
find_file_in_dirlist pf.os "pf.os" \
"${SRC_DIR}/dist/pf/etc" "${SRC_DIR}/etc" \
|| return 1
# ${dir} is set by find_file_in_dirlist()
populate_dir "${op}" true \
"${dir}" "${DEST_DIR}/etc" 644 \
pf.conf
failed=$(( ${failed} + $? ))
compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 pf.os
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# rc
#
additem rc "/etc/rc* and /etc/rc.d/ being up to date"
do_rc()
{
[ -n "$1" ] || err 3 "USAGE: do_rc fix|check"
op="$1"
failed=0
generated_scripts=""
if [ "${MKX11}" != "no" ]; then
generated_scripts="${generated_scripts} xdm xfs"
fi
compare_dir "${op}" "${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 \
rc rc.subr rc.shutdown
failed=$(( ${failed} + $? ))
if ! $SOURCEMODE; then
extra_scripts="${generated_scripts}"
else
extra_scripts=""
fi
compare_dir "${op}" "${SRC_DIR}/etc/rc.d" "${DEST_DIR}/etc/rc.d" 555 \
DAEMON DISKS LOGIN NETWORKING SERVERS \
accounting altqd amd apmd \
bootconf.sh bootparams btattach btconfig btdevctl bthcid \
ccd cgd cleartmp cron \
dhclient dhcpcd dhcpd dhcrelay dmesg downinterfaces envsys \
fsck fsck_root ftp_proxy ftpd \
gpio \
hostapd httpd \
identd ifwatchd inetd ipfilter ipfs ipmon ipnat ipsec \
irdaattach iscsi_target isdnd \
kdc \
ldconfig local lpd lvm\
mdnsd mixerctl mopd motd mountall mountcritlocal \
mountcritremote mountd moused mrouted \
named ndbootd network newsyslog nfsd nfslocking npf \
ntpd ntpdate \
perusertmp pf pf_boot pflogd postfix powerd ppp pwcheck \
quota \
racoon rpcbind raidframe raidframeparity rarpd rbootd rndctl \
root route6d routed rtadvd rtclocaltime rtsold rwho \
savecore screenblank sdpd securelevel sshd \
staticroute swap1 swap2 sysctl sysdb syslogd \
timed tpctl ttys \
veriexec virecover wdogctl wpa_supplicant wscons wsmoused \
ypbind yppasswdd ypserv \
${extra_scripts}
failed=$(( ${failed} + $? ))
if $SOURCEMODE && [ -n "${generated_scripts}" ]; then
# generate scripts
mkdir "${SCRATCHDIR}/rc"
for f in ${generated_scripts}; do
sed -e "s,@X11ROOTDIR@,${X11ROOTDIR},g" \
< "${SRC_DIR}/etc/rc.d/${f}.in" \
> "${SCRATCHDIR}/rc/${f}"
done
compare_dir "${op}" "${SCRATCHDIR}/rc" \
"${DEST_DIR}/etc/rc.d" 555 \
${generated_scripts}
failed=$(( ${failed} + $? ))
fi
# check for obsolete rc.d files
for f in NETWORK btcontrol btuartd fsck.sh kerberos nfsiod servers \
systemfs daemon gated login poffd portmap sunndd xntpd; do
fd="/etc/rc.d/${f}"
[ -e "${DEST_DIR}${fd}" ] && echo "${fd}"
done | obsolete_paths "${op}"
failed=$(( ${failed} + $? ))
# check for obsolete rc.conf(5) variables
set -- amd amd_master \
btcontrol btcontrol_devices \
critical_filesystems critical_filesystems_beforenet \
defcorename \
ip6forwarding \
mountcritlocal mountcritremote \
network nfsiod_flags \
sdpd sdpd_control \
sdpd sdpd_groupname \
sdpd sdpd_username \
sysctl
while [ $# -gt 1 ]; do
if rcconf_is_set "${op}" "$1" "$2" 1; then
failed=1
fi
shift 2
done
return ${failed}
}
#
# sendmail
#
adddisableditem sendmail "remove obsolete sendmail configuration files and scripts"
do_sendmail()
{
[ -n "$1" ] || err 3 "USAGE: do_sendmail fix|check"
op="$1"
failed=0
# Don't complain if the "sendmail" package is installed because the
# files might still be in use.
if /usr/sbin/pkg_info -qe sendmail >/dev/null 2>&1; then
return 0
fi
for f in /etc/mail/helpfile /etc/mail/local-host-names \
/etc/mail/sendmail.cf /etc/mail/submit.cf /etc/rc.d/sendmail \
/etc/rc.d/smmsp /usr/share/misc/sendmail.hf \
$(find /usr/share/sendmail -type f) \
$(find /usr/share/sendmail -type d) /var/log/sendmail.st \
/var/spool/clientmqueue /var/spool/mqueue; do
[ -e "${DEST_DIR}${f}" ] && echo "${f}"
done | obsolete_paths "${op}"
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# mailerconf
#
adddisableditem mailerconf "update /etc/mailer.conf after sendmail removal"
do_mailerconf()
{
[ -n "$1" ] || err 3 "USAGE: do_mailterconf fix|check"
op="$1"
failed=0
mta_path="$(${AWK} '/^sendmail[ \t]/{print$2}' /etc/mailer.conf)"
old_sendmail_path="/usr/libexec/sendmail/sendmail"
if [ "${mta_path}" = "${old_sendmail_path}" ]; then
if [ "$op" = check ]; then
msg "mailer.conf points to obsolete ${old_sendmail_path}"
failed=1;
else
populate_dir "${op}" false \
"${SRC_DIR}/etc" "${DEST_DIR}/etc" 644 mailer.conf
failed=$?
fi
fi
return ${failed}
}
#
# ssh
#
additem ssh "ssh configuration update"
do_ssh()
{
[ -n "$1" ] || err 3 "USAGE: do_ssh fix|check"
op="$1"
failed=0
_etcssh="${DEST_DIR}/etc/ssh"
if ! check_dir "${op}" "${_etcssh}" 755; then
failed=1
fi
if [ ${failed} -eq 0 ]; then
for f in \
ssh_known_hosts ssh_known_hosts2 \
ssh_host_dsa_key ssh_host_dsa_key.pub \
ssh_host_rsa_key ssh_host_rsa_key.pub \
ssh_host_key ssh_host_key.pub \
; do
if ! move_file "${op}" \
"${DEST_DIR}/etc/${f}" "${_etcssh}/${f}" ; then
failed=1
fi
done
for f in sshd.conf ssh.conf ; do
# /etc/ssh/ssh{,d}.conf -> ssh{,d}_config
#
if ! move_file "${op}" \
"${_etcssh}/${f}" "${_etcssh}/${f%.conf}_config" ;
then
failed=1
fi
# /etc/ssh{,d}.conf -> /etc/ssh/ssh{,d}_config
#
if ! move_file "${op}" \
"${DEST_DIR}/etc/${f}" \
"${_etcssh}/${f%.conf}_config" ;
then
failed=1
fi
done
fi
sshdconf=""
for f in \
"${_etcssh}/sshd_config" \
"${_etcssh}/sshd.conf" \
"${DEST_DIR}/etc/sshd.conf" ; do
if [ -f "${f}" ]; then
sshdconf="${f}"
break
fi
done
if [ -n "${sshdconf}" ]; then
modify_file "${op}" "${sshdconf}" "${SCRATCHDIR}/sshdconf" '
/^[^#$]/ {
kw = tolower($1)
if (kw == "hostkey" &&
$2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ ) {
sub(/\/etc\/+/, "/etc/ssh/")
}
if (kw == "rhostsauthentication" ||
kw == "verifyreversemapping" ||
kw == "reversemappingcheck") {
sub(/^/, "# DEPRECATED:\t")
}
}
{ print }
'
failed=$(( ${failed} + $? ))
fi
if ! find_file_in_dirlist moduli "moduli" \
"${SRC_DIR}/crypto/external/bsd/openssh/dist" "${SRC_DIR}/etc" ; then
failed=1
# ${dir} is set by find_file_in_dirlist()
elif ! compare_dir "${op}" "${dir}" "${DEST_DIR}/etc" 444 moduli; then
failed=1
fi
if ! check_dir "${op}" "${DEST_DIR}/var/chroot/sshd" 755 ; then
failed=1
fi
if rcconf_is_set "${op}" sshd sshd_conf_dir 1; then
failed=1
fi
return ${failed}
}
#
# wscons
#
additem wscons "wscons configuration file update"
do_wscons()
{
[ -n "$1" ] || err 3 "USAGE: do_wscons fix|check"
op="$1"
[ -f "${DEST_DIR}/etc/wscons.conf" ] || return 0
failed=0
notfixed=""
if [ "${op}" = "fix" ]; then
notfixed="${NOT_FIXED}"
fi
while read _type _arg1 _rest; do
if [ "${_type}" = "mux" -a "${_arg1}" = "1" ]; then
msg \
"Obsolete wscons.conf(5) entry \""${_type} ${_arg1}"\" found.${notfixed}"
failed=1
fi
done < "${DEST_DIR}/etc/wscons.conf"
return ${failed}
}
#
# X11
#
additem x11 "x11 configuration update"
do_x11()
{
[ -n "$1" ] || err 3 "USAGE: do_x11 fix|check"
op="$1"
failed=0
_etcx11="${DEST_DIR}/etc/X11"
if [ ! -d "${_etcx11}" ]; then
msg "${_etcx11} is not a directory; skipping check"
return 0
fi
if [ -d "${DEST_DIR}/usr/X11R6/." ]
then
_libx11="${DEST_DIR}/usr/X11R6/lib/X11"
if [ ! -d "${_libx11}" ]; then
msg "${_libx11} is not a directory; skipping check"
return 0
fi
fi
_notfixed=""
if [ "${op}" = "fix" ]; then
_notfixed="${NOT_FIXED}"
fi
for d in \
fs lbxproxy proxymngr rstart twm xdm xinit xserver xsm \
; do
sd="${_libx11}/${d}"
ld="/etc/X11/${d}"
td="${DEST_DIR}${ld}"
if [ -h "${sd}" ]; then
continue
elif [ -d "${sd}" ]; then
tdfiles="$(find "${td}" \! -type d)"
if [ -n "${tdfiles}" ]; then
msg "${sd} exists yet ${td} already" \
"contains files${_notfixed}"
else
msg "Migrate ${sd} to ${td}${_notfixed}"
fi
failed=1
elif [ -e "${sd}" ]; then
msg "Unexpected file ${sd}${_notfixed}"
continue
else
continue
fi
done
return ${failed}
}
#
# xkb
#
# /usr/X11R7/lib/X11/xkb/symbols/pc used to be a directory, but changed
# to a file on 2009-06-12. Fixing this requires removing the directory
# (which we can do) and re-extracting the xbase set (which we can't do),
# or at least adding that one file (which we may be able to do if X11SRCDIR
# is available).
#
additem xkb "clean up for xkbdata to xkeyboard-config upgrade"
do_xkb()
{
[ -n "$1" ] || err 3 "USAGE: do_xkb fix|check"
op="$1"
failed=0
pcpath="/usr/X11R7/lib/X11/xkb/symbols/pc"
pcsrcdir="${X11SRCDIR}/external/mit/xkeyboard-config/dist/symbols"
filemsg="\
${pcpath} was a directory, should be a file.
To fix, extract the xbase set again."
_notfixed=""
if [ "${op}" = "fix" ]; then
_notfixed="${NOT_FIXED}"
fi
if [ ! -d "${DESTDIR}${pcpath}" ]; then
return 0
fi
# Delete obsolete files in the directory, and the directory
# itself. If the directory contains unexpected extra files
# then it will not be deleted.
( [ -f "${DEST_DIR}"/var/db/obsolete/xbase ] \
&& sort -ru "${DEST_DIR}"/var/db/obsolete/xbase \
| ${EGREP} "^\\.?${pcpath}/" ;
echo "${pcpath}" ) \
| obsolete_paths "${op}"
failed=$(( ${failed} + $? ))
# If the directory was removed above, then try to replace it with
# a file.
if [ -d "${DESTDIR}${pcpath}" ]; then
msg "${filemsg}${_notfixed}"
failed=$(( ${failed} + 1 ))
else
if ! find_file_in_dirlist pc "${pcpath}" \
"${pcsrcdir}" "${SRC_DIR}${pcpath%/*}"
then
msg "${filemsg}${_notfixed}"
failed=$(( ${failed} + 1 ))
else
# ${dir} is set by find_file_in_dirlist()
populate_dir "${op}" true \
"${dir}" "${DEST_DIR}${pcpath%/*}" 444 \
pc
failed=$(( ${failed} + $? ))
fi
fi
return $failed
}
#
# uid
#
additem uid "required users in /etc/master.passwd"
do_uid()
{
[ -n "$1" ] || err 3 "USAGE: do_uid fix|check"
check_ids "$1" users "${DEST_DIR}/etc/master.passwd" \
named ntpd postfix sshd _pflogd _rwhod _proxy _timedc \
_sdpd _httpd _mdnsd _atf _tcpdump
}
#
# varrwho
#
additem varrwho "required ownership of files in /var/rwho"
do_varrwho()
{
[ -n "$1" ] || err 3 "USAGE: do_varrwho fix|check"
contents_owner "$1" "${DEST_DIR}/var/rwho" _rwhod _rwhod
}
#
# obsolete
# (this item is last to allow other items to move obsolete files)
#
additem obsolete "remove obsolete file sets and minor libraries"
do_obsolete()
{
[ -n "$1" ] || err 3 "USAGE: do_obsolete fix|check"
op="$1"
failed=0
sort -ru "${DEST_DIR}"/var/db/obsolete/* | obsolete_paths "${op}"
failed=$(( ${failed} + $? ))
(
obsolete_libs /lib
obsolete_libs /usr/lib
obsolete_libs /usr/lib/i18n
obsolete_libs /usr/X11R6/lib
obsolete_libs /usr/X11R7/lib
[ "$MACHINE" = "amd64" ] && obsolete_libs /usr/lib/i386
[ "$MACHINE" = "sparc64" ] && obsolete_libs /usr/lib/sparc
) | obsolete_paths "${op}"
failed=$(( ${failed} + $? ))
return ${failed}
}
#
# end of items
# ------------
#
usage()
{
cat 1>&2 << _USAGE_
Usage: ${PROGNAME} [-s srcdir] [-d destdir] [-m mach] [-a arch] op [item [...]]
Perform post-installation checks and/or fixes on a system's
configuration files.
If no items are provided, a default set of checks or fixes is applied.
Options:
-s {srcdir|tgzfile|tempdir}
Location of the source files. This may be any
of the following:
* A directory that contains a NetBSD source tree;
* A distribution set file such as "etc.tgz" or
"xetc.tgz". Pass multiple -s options to specify
multiple such files;
* A temporary directory in which one or both of
"etc.tgz" and "xetc.tgz" have been extracted.
[${SRC_DIR:-/}]
-d destdir Destination directory to check. [${DEST_DIR:-/}]
-m mach MACHINE. [${MACHINE}]
-a arch MACHINE_ARCH. [${MACHINE_ARCH}]
Operation may be one of:
help Display this help.
list List available items.
check Perform post-installation checks on items.
diff [diff(1) options ...]
Similar to 'check' but also output difference of files.
fix Apply fixes that 'check' determines need to be applied.
usage Display this usage.
_USAGE_
exit 2
}
list()
{
echo "Default set of items (to apply if no items are provided by user):"
echo " Item Description"
echo " ---- -----------"
for i in ${defaultitems}; do
eval desc=\"\${desc_${i}}\"
printf " %-12s %s\n" "${i}" "${desc}"
done
echo "Items disabled by default (must be requested explicitly):"
echo " Item Description"
echo " ---- -----------"
for i in ${otheritems}; do
eval desc=\"\${desc_${i}}\"
printf " %-12s %s\n" "${i}" "${desc}"
done
}
main()
{
TGZLIST= # quoted list list of tgz files
SRC_ARGLIST= # quoted list of one or more "-s" args
N_SRC_ARGS=0 # number of "-s" args
TGZMODE=false # true if "-s" specifies a tgz file
DIRMODE=false # true if "-s" specified a directory
SOURCEMODE=false # true if "-s" specified a source directory
while getopts s:d:m:a: ch; do
case "${ch}" in
s)
qarg="$(shell_quote "${OPTARG}")"
N_SRC_ARGS=$(( $N_SRC_ARGS + 1 ))
SRC_ARGLIST="${SRC_ARGLIST}${SRC_ARGLIST:+ }-s ${qarg}"
if [ -f "${OPTARG}" ]; then
# arg refers to a *.tgz file.
# This may happen twice, for both
# etc.tgz and xetc.tgz, so we build up a
# quoted list in TGZLIST.
TGZMODE=true
TGZLIST="${TGZLIST}${TGZLIST:+ }${qarg}"
# Note that, when TGZMODE is true,
# SRC_ARG is used only for printing
# human-readable messages.
SRC_ARG="${TGZLIST}"
elif [ -d "${OPTARG}" ]; then
# arg refers to a directory.
# It might be a source directory, or a
# directory where the sets have already
# been extracted.
DIRMODE=true
SRC_ARG="${OPTARG}"
if [ -f "${OPTARG}/etc/Makefile" ]; then
SOURCEMODE=true
fi
elif [ -f "${OPTARG%%:*}" -a -f "${OPTARG##*:}" \
-a ! -f "${OPTARG}" ]
then
# Backward compatibility: allow arg to refer
# to a colon-separated list of tgz files.
# Remove this after NetBSD-5.0 is released.
cat >&2 <<EOF
*** WARNING: The "-s tgzfile1:tgzfile2" option is deprecated. Please use
"-s tgzfile1 -s tgzfile2" in future.
EOF
TGZMODE=true
TGZLIST="${TGZLIST}${TGZLIST:+ }$( \
IFS=: eval shell_quote \${OPTARG} )"
else
err 2 "Invalid argument for -s option"
fi
;;
d)
DEST_DIR="${OPTARG}"
;;
m)
MACHINE="${OPTARG}"
;;
a)
MACHINE_ARCH="${OPTARG}"
;;
*)
usage
;;
esac
done
shift $((${OPTIND} - 1))
[ $# -gt 0 ] || usage
if [ "$N_SRC_ARGS" -gt 1 ] && $DIRMODE; then
err 2 "Multiple -s args are allowed only with tgz files"
fi
if [ "$N_SRC_ARGS" -eq 0 ]; then
# The default SRC_ARG was set elsewhere
DIRMODE=true
SOURCEMODE=true
SRC_ARGLIST="-s $(shell_quote "${SRC_ARG}")"
fi
#
# If '-s' arg or args specified tgz files, extract them
# to a scratch directory.
#
if $TGZMODE; then
ETCTGZDIR="${SCRATCHDIR}/etc.tgz"
echo "Note: Creating temporary directory ${ETCTGZDIR}"
if ! mkdir "${ETCTGZDIR}"; then
err 2 "Can't create ${ETCTGZDIR}"
fi
( # subshell to localise changes to "$@"
eval "set -- ${TGZLIST}"
for tgz in "$@"; do
echo "Note: Extracting files from ${tgz}"
cat "${tgz}" | (
cd "${ETCTGZDIR}" &&
tar -zxf -
) || err 2 "Can't extract ${tgz}"
done
)
SRC_DIR="${ETCTGZDIR}"
else
SRC_DIR="${SRC_ARG}"
fi
[ -d "${SRC_DIR}" ] || err 2 "${SRC_DIR} is not a directory"
[ -d "${DEST_DIR}" ] || err 2 "${DEST_DIR} is not a directory"
[ -n "${MACHINE}" ] || err 2 "\${MACHINE} is not defined"
[ -n "${MACHINE_ARCH}" ] || err 2 "\${MACHINE_ARCH} is not defined"
if ! $SOURCEMODE && ! [ -f "${SRC_DIR}/etc/mtree/set.etc" ]; then
err 2 "Files from the etc.tgz set are missing"
fi
# If directories are /, clear them, so various messages
# don't have leading "//". However, this requires
# the use of ${foo:-/} to display the variables.
#
[ "${SRC_DIR}" = "/" ] && SRC_DIR=""
[ "${DEST_DIR}" = "/" ] && DEST_DIR=""
detect_x11
op="$1"
shift
case "${op}" in
diff)
op=check
DIFF_STYLE=n # default style is RCS
OPTIND=1
while getopts bcenpuw ch; do
case "${ch}" in
c|e|n|u)
if [ "${DIFF_STYLE}" != "n" -a \
"${DIFF_STYLE}" != "${ch}" ]; then
err 2 "conflicting output style: ${ch}"
fi
DIFF_STYLE="${ch}"
;;
b|p|w)
DIFF_OPT="${DIFF_OPT} -${ch}"
;;
*)
err 2 "unknown diff option"
;;
esac
done
shift $((${OPTIND} - 1))
;;
esac
case "${op}" in
usage|help)
usage
;;
list)
echo "Source directory: ${SRC_DIR:-/}"
echo "Target directory: ${DEST_DIR:-/}"
if [ "${SRC_DIR}" != "${SRC_ARG}" ]; then
echo " (extracted from: ${SRC_ARG})"
fi
list
;;
check|fix)
todo="$*"
: ${todo:="${defaultitems}"}
# ensure that all supplied items are valid
#
for i in ${todo}; do
eval desc=\"\${desc_${i}}\"
[ -n "${desc}" ] || err 2 "Unsupported ${op} '"${i}"'"
done
# perform each check/fix
#
echo "Source directory: ${SRC_DIR:-/}"
if [ "${SRC_DIR}" != "${SRC_ARG}" ]; then
echo " (extracted from: ${SRC_ARG})"
fi
echo "Target directory: ${DEST_DIR:-/}"
items_passed=
items_failed=
for i in ${todo}; do
echo "${i} ${op}:"
( eval do_${i} ${op} )
if [ $? -eq 0 ]; then
items_passed="${items_passed} ${i}"
else
items_failed="${items_failed} ${i}"
fi
done
if [ "${op}" = "check" ]; then
plural="checks"
else
plural="fixes"
fi
echo "${PROGNAME} ${plural} passed:${items_passed}"
echo "${PROGNAME} ${plural} failed:${items_failed}"
if [ -n "${items_failed}" ]; then
exitstatus=1;
if [ "${op}" = "check" ]; then
[ "$MACHINE" = "$(uname -m)" ] && m= || m=" -m $MACHINE"
cat <<_Fix_me_
To fix, run:
${0} ${SRC_ARGLIST} -d ${DEST_DIR:-/}$m fix${items_failed}
Note that this may overwrite local changes.
_Fix_me_
fi
fi
;;
*)
warn "Unknown operation '"${op}"'"
usage
;;
esac
}
# defaults
#
PROGNAME="${0##*/}"
SRC_ARG="/usr/src"
DEST_DIR="/"
: ${MACHINE:="$( uname -m )"} # assume native build if $MACHINE is not set
: ${MACHINE_ARCH:="$( uname -p )"}# assume native build if not set
DIFF_STYLE=
NOT_FIXED=" (FIX MANUALLY)"
SCRATCHDIR="$( mkdtemp )" || err 2 "Can't create scratch directory"
trap "/bin/rm -rf \"\${SCRATCHDIR}\" ; exit 0" 1 2 3 15 # HUP INT QUIT TERM
umask 022
exec 3>/dev/null
exec 4>/dev/null
exitstatus=0
main "$@"
/bin/rm -rf "${SCRATCHDIR}"
exit $exitstatus