#!/bin/sh # # $NetBSD: postinstall,v 1.75 2004/04/18 04:02:27 dbj Exp $ # # Copyright (c) 2002-2004 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. # 3. All advertising materials mentioning features or use of this software # must display the following acknowledgement: # This product includes software developed by the NetBSD # Foundation, Inc. and its contributors. # 4. Neither the name of The NetBSD Foundation nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # postinstall # check for or fix configuration changes that occur # over time as NetBSD evolves. # # # checks to add: # - obsolete minor/teeny shared libraries in /lib # - sysctl(8) renames # - 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? # # # helper functions # err() { exitval=$1 shift echo 1>&2 "${PROGNAME}: $@" exit ${exitval} } warn() { echo 1>&2 "${PROGNAME}: $@" } msg() { echo " $@" } # additem item description # add item to list of supported items to check/fix # additem() { [ $# -eq 2 ] || err 2 "USAGE: additem item description" items="${items}${items:+ }$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 2 "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 2 "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 } } ' ${_ids} < ${_file}) || return 1 if [ -n "${_missing}" ]; then msg "Missing ${_type}${_notfixed}:" $(echo ${_missing}) return 1 fi return 0 } # compare_dir op src dest mode file [file ...] # perform op ("check" or "fix") on files in src/ against dest/ # returns 0 if ok, 1 otherwise. # compare_dir() { [ $# -ge 5 ] || err 2 "USAGE: compare_dir op src dest mode file [...]" _op=$1 _src=$2 _dest=$3 _mode=$4 shift 4 _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 ${_files}; do fs=${_src}/${f} fd=${_dest}/${f} _error="" if [ ! -f "${fd}" ]; then _error="${fd} does not exist" elif ! cmp -s ${fs} ${fd} ; then _error="${fs} != ${fd}" else continue fi if [ "${_op}" = "check" ]; then msg ${_error} [ -n "${DIFF_STYLE}" -a -f "${fd}" ] && \ diff -${DIFF_STYLE} ${DIFF_OPT} ${fd} ${fs} _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} } # 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 2 "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 2 "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 2 "USAGE: find_file_in_dirlist file msg dir1 [...]" _file=$1 ; shift _msg=$1 ; shift _dir1st= 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 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 $? } # # items # ----- # # # postinstall # additem postinstall "/etc/postinstall being up to date" do_postinstall() { [ -n "$1" ] || err 2 "USAGE: do_postinstall fix|check" compare_dir $1 ${SRC_DIR}/etc ${DEST_DIR}/etc 555 \ postinstall } # # release # additem etc_release "/etc/release being up to date" do_etc_release() { [ -n "$1" ] || err 2 "USAGE: do_etc_release fix|check" if [ -f "${SRC_DIR}/etc/Makefile" -a \ ! -f "${SRC_DIR}/etc/release" ] ; then msg "etc/release is built by ${SRC_DIR}/etc/Makefile; skipping ${op}" return 0 fi compare_dir $1 ${SRC_DIR}/etc ${DEST_DIR}/etc 555 \ release } # # defaults # additem defaults "/etc/defaults/ being up to date" do_defaults() { [ -n "$1" ] || err 2 "USAGE: do_defaults fix|check" compare_dir $1 ${SRC_DIR}/etc/defaults ${DEST_DIR}/etc/defaults 444 \ daily.conf monthly.conf rc.conf security.conf weekly.conf } # # mtree # additem mtree "/etc/mtree/ being up to date" do_mtree() { [ -n "$1" ] || err 2 "USAGE: do_mtree fix|check" compare_dir $1 ${SRC_DIR}/etc/mtree ${DEST_DIR}/etc/mtree 444 \ NetBSD.dist special } # # gid # additem gid "required GIDs" do_gid() { [ -n "$1" ] || err 2 "USAGE: do_gid fix|check" check_ids $1 groups "${DEST_DIR}/etc/group" \ named ntpd sshd smmsp } # # uid # additem uid "required UIDs" do_uid() { [ -n "$1" ] || err 2 "USAGE: do_uid fix|check" check_ids $1 users "${DEST_DIR}/etc/master.passwd" \ named ntpd sshd smmsp } # # periodic # additem periodic "/etc/{daily,weekly,monthly,security} being up to date" do_periodic() { [ -n "$1" ] || err 2 "USAGE: do_periodic fix|check" compare_dir $1 ${SRC_DIR}/etc ${DEST_DIR}/etc 644 \ daily weekly monthly security } # # rc # additem rc "/etc/rc* and /etc/rc.d/ being up to date" do_rc() { [ -n "$1" ] || err 2 "USAGE: do_rc fix|check" op=$1 failed=0 compare_dir ${op} ${SRC_DIR}/etc ${DEST_DIR}/etc 644 \ rc rc.subr rc.shutdown failed=$(( ${failed} + $? )) compare_dir ${op} ${SRC_DIR}/etc/rc.d ${DEST_DIR}/etc/rc.d 555 \ DAEMON LOGIN NETWORKING SERVERS accounting altqd amd \ apmd bootparams bootconf.sh ccd cgd cleartmp cron \ dhclient dhcpd dhcrelay dmesg downinterfaces fixsb fsck \ identd ifwatchd inetd ipfilter ipfs ipmon ipnat ipsec isdnd \ kdc ldconfig lkm1 lkm2 lkm3 local lpd mopd motd \ mountall mountcritlocal mountcritremote mountd moused \ mrouted mixerctl named ndbootd network newsyslog nfsd \ nfslocking ntpd ntpdate poffd postfix powerd ppp pwcheck \ quota racoon rpcbind raidframe raidframeparity rarpd rbootd \ root route6d routed rtadvd rtclocaltime rtsold rwho savecore \ screenblank sendmail securelevel smmsp sshd staticroute \ swap1 swap2 sysdb sysctl syslogd timed ttys veriexec virecover \ wdogctl wscons wsmoused \ xdm xfs ypbind yppasswdd ypserv failed=$(( ${failed} + $? )) # check for obsolete rc.d files for f in NETWORK fsck.sh kerberos nfsiod servers systemfs \ daemon gated login portmap sunndd xntpd; do fd=${DEST_DIR}/etc/rc.d/${f} [ ! -e "${fd}" ] && continue if [ "${op}" = "check" ]; then msg "Remove obsolete ${fd}" failed=1 elif ! rm ${fd}; then msg "Can't remove obsolete ${fd}" failed=1 else msg "Removed obsolete ${fd}" fi done # check for obsolete rc.conf(5) variables set -- amd amd_master \ mountcritlocal critical_filesystems_beforenet \ mountcritremote critical_filesystems \ network ip6forwarding \ sysctl defcorename \ sysctl nfsiod_flags while [ $# -gt 1 ]; do if rcconf_is_set ${op} $1 $2 1; then failed=1 fi shift 2 done return ${failed} } # # ssh # additem ssh "ssh configuration update" do_ssh() { [ -n "$1" ] || err 2 "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 awk ' $1 ~ /^[Hh][Oo][Ss][Tt][Kk][Ee][Yy]$/ && $2 ~ /^\/etc\/+ssh_host(_[dr]sa)?_key$/ { sub(/\/etc\/+/, "/etc/ssh/"); } { print } ' < ${sshdconf} > ${SCRATCHDIR}/sshd_config if ! cmp -s ${sshdconf} ${SCRATCHDIR}/sshd_config; then diff ${sshdconf} ${SCRATCHDIR}/sshd_config > \ ${SCRATCHDIR}/sshd_config.diffs if [ "${op}" = "check" ]; then msg "${sshdconf} needs the following changes:" failed=1 elif ! rm -f ${sshdconf} || ! cp -f ${SCRATCHDIR}/sshd_config ${sshdconf}; then msg "${sshdconf} changes not applied:" failed=1 else msg "${sshdconf} changes applied:" fi while read _line; do msg " ${_line}" done < ${SCRATCHDIR}/sshd_config.diffs fi fi if ! find_file_in_dirlist moduli "moduli" \ ${SRC_DIR}/crypto/dist/ssh ${SRC_DIR}/etc ; then failed=1 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 2 "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} } # # makedev # additem makedev "/dev/MAKEDEV being up to date" do_makedev() { [ -n "$1" ] || err 2 "USAGE: do_makedev fix|check" 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 compare_dir $1 ${dir} ${DEST_DIR}/dev 555 MAKEDEV } # # postfix # additem postfix "/etc/postfix/ being up to date" do_postfix() { [ -n "$1" ] || err 2 "USAGE: do_postfix fix|check" op=$1 failed=0 find_file_in_dirlist postfix-script "postfix scripts" \ ${SRC_DIR}/gnu/dist/postfix/conf \ ${DEST_DIR}/usr/share/examples/postfix \ || return 1 compare_dir ${op} ${dir} ${DEST_DIR}/etc/postfix 555 postfix-script failed=$(( ${failed} + $? )) compare_dir ${op} ${dir} \ ${DEST_DIR}/etc/postfix 444 post-install postfix-files failed=$(( ${failed} + $? )) return ${failed} } # # obsolete # additem obsolete "obsolete file sets" do_obsolete() { [ -n "$1" ] || err 2 "USAGE: do_obsolete fix|check" op=$1 sort -ru ${DEST_DIR}/var/db/obsolete/* | ( failed=0 while read ofile; do ofile=${DEST_DIR}${ofile#.} cmd="rm" ftype="file" if [ -h "${ofile}" ]; then ftype="link" elif [ -d "${ofile}" ]; then ftype="directory" cmd="rmdir" elif [ ! -e "${ofile}" ]; then continue 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 exit ${failed} ) return $? } # # sendmail # additem sendmail "sendmail configuration being up to date" do_sendmail() { [ -n "$1" ] || err 2 "USAGE: do_sendmail fix|check" _op=$1 # # check owner and mode of sendmail (and "root" setting) # check .cf file version # check submit.cf existence (and version) # uid and gid are checked elsewhere # look at clientmqueue's owner, group, and mode # failed=0 _etcmail="${DEST_DIR}/etc/mail" _sendmailcf="${_etcmail}/sendmail.cf" _submitcf="${_etcmail}/submit.cf" _sample_sendmailcf="${DEST_DIR}/usr/share/sendmail/cf/netbsd-proto.cf" _sample_submitcf="${DEST_DIR}/usr/share/sendmail/cf/netbsd-msp.cf" _sendmail=${DEST_DIR}/usr/libexec/sendmail/sendmail # # check for "root" setting first, so that checks can be # adjusted accordingly # _notfixed="" _root=$(rcconf_is_set ${_op} sendmail sendmail_suidroot 3>&1 1>&4) 4>&1 if [ $? != 0 ]; then if [ "${_op}" = "fix" ]; then _notfixed=${NOT_FIXED} fi msg "sendmail_suidroot variable not set (assuming \`\`no'')${_notfixed}" _root=NO failed=1 fi # normalize the "root" setting _root=$(. ${DEST_DIR}/etc/rc.subr warn() {} # eliminate complaints if checkyesno _root; then echo true else echo false fi) if "${_root}"; then _smownerfmt="%p %Su" _smownermode="4555" _smownerreq="root" else _smownerfmt="%p %Su:%Sg" _smownermode="2555" _smownerreq="root:smmsp" fi # check that owner and mode match what it required _notfixed="" if ! stat "${_op}" "${_smownerfmt}" "${_sendmail}" \ "10${_smownermode} ${_smownerreq}"; then if [ "${_op}" = "fix" ]; then _notfixed=${NOT_FIXED} if chown "${_smownerreq}" "${_sendmail}" 2>/dev/null && chmod "${_smownermode}" "${_sendmail}" 2>/dev/null then _notfixed=" [ FIXED ]" else failed=1 fi else _notfixed="" failed=1 fi msg "${_sendmail} binary has wrong owner/mode${_notfixed}" fi _notfixed="" if [ ! -f "${_sendmailcf}" ]; then if [ "${_op}" = "fix" ]; then _notfixed=${NOT_FIXED} if cp "${_sample_sendmailcf}" \ "${_sendmailcf}" 2>/dev/null; then _notfixed=" [ FIXED ]" else failed=1 fi fi msg "${_sendmailcf} is missing${_notfixed}" fi if [ ! -f "${_sendmailcf}" ]; then _cfversion="10" # pretend it's okay else _cfversion=$(sed -n 's/^V *\([0-9]*\).*/\1/p' "${_sendmailcf}") fi _notfixed="" if [ "${_cfversion}" -lt 10 ]; then # XXX no fix here if [ "${_op}" = "fix" ]; then _notfixed=${NOT_FIXED} fi msg "Version of ${_sendmailcf} is ${_cfversion}," \ "should be 10${_notfixed}" failed=1 fi _notfixed="" if $_root; then if [ -f "${_submitcf}" ]; then if [ "${_op}" = "fix" ]; then _notfixed=${NOT_FIXED} if rm -f "${_submitcf}" 2>/dev/null; then _notfixed=" [ FIXED ]" else failed=1 fi else _notfixed="" failed=1 fi msg "${_submitcf} exists${_notfixed}" fi _checkcq=false else if [ ! -f "${_submitcf}" ]; then _notfixed="" if [ "${_op}" = "fix" ]; then _notfixed=${NOT_FIXED} if cp "${_sample_submitcf}" \ "${_submitcf}" 2>/dev/null; then _notfixed=" [ FIXED ]" else failed=1 fi fi msg "${_submitcf} is missing${_notfixed}" fi _checkcq=true fi _cqueuemode="770" _cqueueowner="smmsp:smmsp" if "${_checkcq}"; then if [ -f "${_submitcf}" ]; then _cqueuepath=$(awk 'match($0,"^O *QueueDirectory=") { print(substr($0, RSTART+RLENGTH)) }' "${_submitcf}") else _cqueuepath="/var/spool/clientmqueue" fi _cqueuefmt="%.1Sp %Lp %Su:%Sg" else _cqueuepath="/" _cqueuefmt="d ${_cqueuemode} ${_cqueueowner}" fi _notfixed="" if ! stat "${_op}" "${_cqueuefmt}" "${_cqueuepath}" \ "d ${_cqueuemode} ${_cqueueowner}"; then if [ "${_op}" = "fix" ]; then _notfixed=${NOT_FIXED} mkdir -p "${_cqueuepath}" && chown "${_cqueueowner}" "${_cqueuepath}" && chmod "${_cqueuemode}" "${_cqueuepath}" && _notfixed=" [ FIXED ]" || failed=1 else failed=1 fi msg "Client queue ${_cqueuepath} has wrong owner/mode${_notfixed}" fi return ${failed} } # # end of items # ------------ # usage() { cat 1>&2 << _USAGE_ Usage: ${PROGNAME} [-s srcdir] [-d destdir] [-m machine] operation [item [...]] Perform post-installation checks and/or fixes on a system's configuration files. If no items are provided, all checks or fixes are applied. Options: -s srcdir Source directory to compare from. This can either be src/etc or an extracted copy of "etc.tgz". [${SRC_DIR:-/}] -d destdir Destination directory to check. [${DEST_DIR:-/}] -m machine Machine architecture. [${MACHINE}] -a arch Machine architecture. [${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 1 } list() { echo "Supported items:" echo " Item Description" echo " ---- -----------" for i in ${items}; do eval desc="\${desc_${i}}" printf " %-12s %s\n" "${i}" "${desc}" done } main() { while getopts s:d:m: ch; do case ${ch} in s) SRC_DIR=${OPTARG} ;; d) DEST_DIR=${OPTARG} ;; m) MACHINE=${OPTARG} ;; a) MACHINE_ARCH=${OPTARG} ;; *) usage ;; esac done shift $((${OPTIND} - 1)) [ $# -gt 0 ] || usage [ -d "${SRC_DIR}" ] || err 1 "${SRC_DIR} is not a directory" [ -d "${DEST_DIR}" ] || err 1 "${DEST_DIR} is not a directory" [ -n "${MACHINE}" ] || err 1 "\${MACHINE} is not defined" [ -n "${MACHINE_ARCH}" ] || err 1 "\${MACHINE_ARCH} is not defined" # 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="" 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 1 "conflicting output style: ${ch}" fi DIFF_STYLE=${ch} ;; b|p|w) DIFF_OPT="${DIFF_OPT} -${ch}" ;; *) err 1 "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:-/}" list ;; check|fix) todo="$@" : ${todo:=${items}} # ensure that all supplied items are valid # for i in ${todo}; do eval desc=\"\${desc_${i}}\" [ -n "${desc}" ] || err 1 "Unsupported ${op} '"${i}"'" done # perform each check/fix # echo "Source directory: ${SRC_DIR:-/}" 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}" -a "${op}" = "check" ]; then [ "$MACHINE" = "$(uname -m)" ] && m= || m=" -m $MACHINE" cat <<_Fix_me_ To fix, run: ${0} -s ${SRC_DIR} -d ${DEST_DIR:-/}$m fix${items_failed} _Fix_me_ fi ;; *) warn "Unknown operation '"${op}"'" usage ;; esac } mkdtemp () { # Make sure we don't loop forever if mkdir will always fail. [ -d /tmp ] || err 1 /tmp is not a directory [ -w /tmp ] || err 1 /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} } # defaults # PROGNAME=${0##*/} SRC_DIR="/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 NOT_FIXED=" [NOT FIXED]" SCRATCHDIR=$( mkdtemp ) || err 1 "Can't create scratch directory" trap "/bin/rm -rf ${SCRATCHDIR} ; exit 0" 0 1 2 3 15 # EXIT HUP INT QUIT TERM umask 022 exec 3>/dev/null exec 4>/dev/null main "$@" exit 0