1#!/bin/ksh - 2# 3# $OpenBSD: sysmerge.sh,v 1.142 2014/07/18 10:43:29 ajacoutot Exp $ 4# 5# Copyright (c) 2008-2014 Antoine Jacoutot <ajacoutot@openbsd.org> 6# Copyright (c) 1998-2003 Douglas Barton <DougB@FreeBSD.org> 7# 8# Permission to use, copy, modify, and distribute this software for any 9# purpose with or without fee is hereby granted, provided that the above 10# copyright notice and this permission notice appear in all copies. 11# 12# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19# 20 21umask 0022 22 23unset AUTO_INSTALLED_FILES BATCHMODE DIFFMODE EGSUM ETCSUM NEED_NEWALIASES 24unset NEWGRP NEWUSR NEED_REBOOT NOSIGCHECK SRCDIR SRCSUM TGZ XETCSUM XTGZ 25 26# forced variables 27WRKDIR=$(mktemp -d -p ${TMPDIR:=/var/tmp} sysmerge.XXXXXXXXXX) || exit 1 28SWIDTH=$(stty size | awk '{w=$2} END {if (w==0) {w=80} print w}') 29RELINT=$(uname -r | tr -d '.') 30if [ -z "${VISUAL}" ]; then 31 EDIT="${EDITOR:=/usr/bin/vi}" 32else 33 EDIT="${VISUAL}" 34fi 35 36# sysmerge specific variables (overridable) 37MERGE_CMD="${MERGE_CMD:=sdiff -as -w ${SWIDTH} -o}" 38REPORT="${REPORT:=${WRKDIR}/sysmerge.log}" 39DBDIR="${DBDIR:=/usr/share/sysmerge}" 40 41# system-wide variables (overridable) 42PAGER="${PAGER:=/usr/bin/more}" 43 44# clean leftovers created by make in src 45clean_src() { 46 [[ -n ${SRCDIR} ]] && \ 47 cd ${SRCDIR}/gnu/usr.sbin/sendmail/cf/cf && make cleandir >/dev/null 48} 49 50# restore sum files from backups or remove the newly generated ones if 51# they did not exist 52restore_sum() { 53 local i _i 54 for i in ${DESTDIR}/${DBDIR}/.{${SRCSUM},${ETCSUM},${XETCSUM},${EGSUM}}.bak; do 55 _i=$(basename ${i} .bak) 56 if [ -f "${i}" ]; then 57 mv ${i} ${DESTDIR}/${DBDIR}/${_i#.} 58 elif [ -f "${DESTDIR}/${DBDIR}/${_i#.}" ]; then 59 rm ${DESTDIR}/${DBDIR}/${_i#.} 60 fi 61 done 62 rm -f ${WRKDIR}/*sum 63} 64 65usage() { 66 echo "usage: ${0##*/} [-bdS] [-s [src | etcXX.tgz]] [-x xetcXX.tgz]" >&2 67} 68 69warn() { 70 echo "**** WARNING: $@" 71} 72 73report() { 74 echo "$@" >> ${REPORT} 75} 76 77# remove newly created work directory and exit with status 1 78error_rm_wrkdir() { 79 (($#)) && echo "**** ERROR: $@" 80 restore_sum 81 clean_src 82 # do not remove the entire WRKDIR in case sysmerge stopped half 83 # way since it contains our backup files 84 rm -rf ${TEMPROOT} 85 rm -f ${WRKDIR}/*.tgz 86 rm -f ${WRKDIR}/SHA256.sig 87 rmdir ${WRKDIR} 2>/dev/null 88 exit 1 89} 90 91trap "error_rm_wrkdir; exit 1" 1 2 3 13 15 92 93if (($(id -u) != 0)); then 94 usage 95 error_rm_wrkdir "need root privileges" 96fi 97 98# extract (x)etcXX.tgz and create cksum file(s); 99# stores sum filename in ETCSUM or XETCSUM (see eval); 100extract_sets() { 101 [[ -n ${SRCDIR} ]] && return 102 local _e _x _set _tgz 103 104 [[ -f ${WRKDIR}/${TGZ##*/} ]] && _e=etc 105 [[ -f ${WRKDIR}/${XTGZ##*/} ]] && _x=xetc 106 107 for _set in ${_e} ${_x}; do 108 typeset -u _SETSUM=${_set}sum 109 eval ${_SETSUM}=${_set}sum 110 [[ ${_set} == etc ]] && _tgz=${WRKDIR}/${TGZ##*/} 111 [[ ${_set} == xetc ]] && _tgz=${WRKDIR}/${XTGZ##*/} 112 113 tar -tzf "${_tgz}" .${DBDIR}/${_set}sum >/dev/null || 114 error_rm_wrkdir "${_tgz##*/}: badly formed \"${_set}\" set, lacks .${DBDIR}/${_set}sum" 115 116 (cd ${TEMPROOT} && tar -xzphf "${_tgz}" && \ 117 find . -type f | xargs sha256 -h ${WRKDIR}/${_set}sum) || \ 118 error_rm_wrkdir "failed to extract ${_tgz} and create checksum file" 119 rm "${_tgz}" 120 done 121} 122 123# fetch and verify sets, abort on failure 124sm_fetch_and_verify() { 125 [[ -n ${SRCDIR} ]] && return 126 local _file _sigdone _url; 127 local _key="/etc/signify/openbsd-${RELINT}-base.pub" 128 129 for _url in ${TGZ} ${XTGZ}; do 130 [[ -f ${_url} ]] && _url="file://$(readlink -f ${_url})" 131 _file=${WRKDIR}/${_url##*/} 132 [[ ${_url} == @(file|ftp|http|https)://*/*[!/] ]] || 133 error_rm_wrkdir "${_url}: invalid URL" 134 echo "===> Fetching ${_url}" 135 /usr/bin/ftp -Vm -k "${FTP_KEEPALIVE-0}" -o "${_file}" "${_url}" >/dev/null || \ 136 error_rm_wrkdir "could not retrieve ${_url##*/}" 137 if [ -z "${NOSIGCHECK}" ]; then 138 if [ -z ${_sigdone} ]; then 139 echo "===> Fetching ${_url%/*}/SHA256.sig" 140 /usr/bin/ftp -Vm -k "${FTP_KEEPALIVE-0}" -o "${WRKDIR}/SHA256.sig" "${_url%/*}/SHA256.sig" >/dev/null || \ 141 error_rm_wrkdir "could not retrieve SHA256.sig" 142 [[ ${TGZ%/*} == ${XTGZ%/*} ]] && _sigdone=1 143 fi 144 echo "===> Verifying ${_url##*/} against ${_key}" 145 (cd ${WRKDIR} && /usr/bin/signify -qC -p ${_key} -x SHA256.sig ${_url##*/}) || \ 146 error_rm_wrkdir "${_url##*/}: signature/checksum failed" 147 fi 148 done 149 150 [[ -z ${NOSIGCHECK} ]] && rm ${WRKDIR}/SHA256.sig 151} 152 153# prepare TEMPROOT content from a src dir and create cksum file 154prepare_src() { 155 [[ -z ${SRCDIR} ]] && return 156 SRCSUM=srcsum 157 # 2>/dev/null: distribution-etc-root-var complains /var/tmp is world writable 158 (cd ${SRCDIR}/etc && \ 159 make DESTDIR=${TEMPROOT} distribution-etc-root-var >/dev/null 2>&1 && \ 160 cd ${TEMPROOT} && find . -type f | xargs sha256 -h ${WRKDIR}/${SRCSUM}) || \ 161 error_rm_wrkdir "failed to populate from ${SRCDIR} and create checksum file" 162} 163 164sm_populate() { 165 local cf i _array _d _r _D _R CF_DIFF CF_FILES CURSUM IGNORE_FILES 166 echo "===> Populating temporary root under ${TEMPROOT}" 167 mkdir -p ${TEMPROOT} 168 169 if [ ! -d ${DESTDIR}/${DBDIR} ]; then 170 mkdir -p ${DESTDIR}/${DBDIR} || exit 1 171 fi 172 173 # automatically install missing user(s) and group(s) from the 174 # new master.passwd and group files: 175 # - after extracting the sets (so we have the new files) 176 # - before running distribution-etc-root-var (using files from SRCDIR) 177 extract_sets 178 install_user_group 179 prepare_src 180 181 for i in ${SRCSUM} ${ETCSUM} ${XETCSUM}; do 182 if [ -f ${DESTDIR}/${DBDIR}/${i} ]; then 183 # delete file in temproot if it has not changed since last release 184 # and is present in current installation 185 if [ -z "${DIFFMODE}" ]; then 186 # 2>/dev/null: if file got removed manually but is still in the sum file 187 _R=$(cd ${TEMPROOT} && \ 188 sha256 -c ${DESTDIR}/${DBDIR}/${i} 2>/dev/null | awk '/OK/ { print $2 }' | sed 's/[:]//') 189 for _r in ${_R}; do 190 if [ -f ${DESTDIR}/${_r} -a -f ${TEMPROOT}/${_r} ]; then 191 rm -f ${TEMPROOT}/${_r} 192 fi 193 done 194 fi 195 196 # set auto-upgradable files 197 _D=$(diff -u ${WRKDIR}/${i} ${DESTDIR}/${DBDIR}/${i} | grep -E '^\+' | sed '1d' | awk '{print $3}') 198 for _d in ${_D}; do 199 # 2>/dev/null: if file got removed manually but is still in the sum file 200 CURSUM=$(cd ${DESTDIR:=/} && sha256 ${_d} 2>/dev/null) 201 [ -n "$(grep "${CURSUM}" ${DESTDIR}/${DBDIR}/${i})" -a -z "$(grep "${CURSUM}" ${WRKDIR}/${i})" ] && \ 202 _array="${_array} ${_d}" 203 done 204 [[ -n ${_array} ]] && set -A AUTO_UPG -- ${_array} 205 206 mv ${DESTDIR}/${DBDIR}/${i} ${DESTDIR}/${DBDIR}/.${i}.bak 207 fi 208 mv ${WRKDIR}/${i} ${DESTDIR}/${DBDIR}/${i} 209 done 210 211 # files we don't want/need to deal with 212 IGNORE_FILES="/etc/*.db 213 /etc/group 214 /etc/localtime 215 /etc/mail/*.db 216 /etc/master.passwd 217 /etc/passwd 218 /etc/motd 219 /etc/myname 220 /usr/share/sysmerge/{etc,examples,xetc}sum 221 /var/db/locate.database 222 /var/mail/root" 223 CF_FILES="/etc/mail/localhost.cf /etc/mail/sendmail.cf /etc/mail/submit.cf" 224 for cf in ${CF_FILES}; do 225 CF_DIFF=$(diff -q -I "##### " ${TEMPROOT}/${cf} ${DESTDIR}/${cf} 2>/dev/null) 226 [[ -z ${CF_DIFF} ]] && IGNORE_FILES="${IGNORE_FILES} ${cf}" 227 done 228 if [ -r /etc/sysmerge.ignore ]; then 229 while read i; do \ 230 IGNORE_FILES="${IGNORE_FILES} $(echo ${i} | sed -e 's,\.\.,,g' -e 's,#.*,,g')" 231 done < /etc/sysmerge.ignore 232 fi 233 for i in ${IGNORE_FILES}; do 234 rm -rf ${TEMPROOT}/${i}; 235 done 236} 237 238install_and_rm() { 239 if [ -f "${5}/${4##*/}" ]; then 240 mkdir -p ${BKPDIR}/${4%/*} 241 cp ${5}/${4##*/} ${BKPDIR}/${4%/*} 242 fi 243 244 if ! install -m "${1}" -o "${2}" -g "${3}" "${4}" "${5}"; then 245 rm -f ${BKPDIR}/${4%/*}/${4##*/} 246 return 1 247 fi 248 rm -f "${4}" 249} 250 251install_file() { 252 local DIR_MODE FILE_MODE FILE_OWN FILE_GRP INSTDIR 253 INSTDIR=${1#.} 254 INSTDIR=${INSTDIR%/*} 255 256 [[ -z ${INSTDIR} ]] && INSTDIR=/ 257 258 DIR_MODE=$(stat -f "%OMp%OLp" "${TEMPROOT}/${INSTDIR}") 259 eval $(stat -f "FILE_MODE=%OMp%OLp FILE_OWN=%Su FILE_GRP=%Sg" ${1}) 260 261 [ -n "${DESTDIR}${INSTDIR}" -a ! -d "${DESTDIR}${INSTDIR}" ] && \ 262 install -d -o root -g wheel -m "${DIR_MODE}" "${DESTDIR}${INSTDIR}" 263 264 install_and_rm "${FILE_MODE}" "${FILE_OWN}" "${FILE_GRP}" "${1}" "${DESTDIR}${INSTDIR}" || return 265 266 case "${1#.}" in 267 /dev/MAKEDEV) 268 echo " (running MAKEDEV(8))" 269 (cd ${DESTDIR}/dev && /bin/sh MAKEDEV all) 270 export NEED_REBOOT=1 271 ;; 272 /etc/login.conf) 273 if [ -f ${DESTDIR}/etc/login.conf.db ]; then 274 echo " (running cap_mkdb(1))" 275 cap_mkdb ${DESTDIR}/etc/login.conf 276 else 277 echo "" 278 fi 279 export NEED_REBOOT=1 280 ;; 281 /etc/mail/@(access|genericstable|mailertable|virtusertable)) 282 echo " (running makemap(8))" 283 /usr/libexec/sendmail/makemap hash ${DESTDIR}/${1#.} < ${DESTDIR}/${1#.} 284 ;; 285 /etc/mail/aliases) 286 echo " (running newaliases(8))" 287 ${DESTDIR:+chroot ${DESTDIR}} newaliases >/dev/null || export NEED_NEWALIASES=1 288 ;; 289 *) 290 echo "" 291 ;; 292 esac 293} 294 295install_link() { 296 local _LINKF _LINKT DIR_MODE 297 _LINKT=$(readlink ${COMPFILE}) 298 _LINKF=$(dirname ${DESTDIR}${COMPFILE#.}) 299 300 DIR_MODE=$(stat -f "%OMp%OLp" "${TEMPROOT}/${_LINKF}") 301 [[ ! -d ${_LINKF} ]] && \ 302 install -d -o root -g wheel -m "${DIR_MODE}" "${_LINKF}" 303 304 rm -f ${COMPFILE} 305 (cd ${_LINKF} && ln -sf ${_LINKT} .) 306 return 307} 308 309install_user_group() { 310 local _g _gid _u 311 if [ -n "${SRCDIR}" ]; then 312 local _pw="${SRCDIR}/etc/master.passwd" 313 local _gr="${SRCDIR}/etc/group" 314 else 315 local _pw="${TEMPROOT}/etc/master.passwd" 316 local _gr="${TEMPROOT}/etc/group" 317 fi 318 319 # when running with '-x' only 320 [ ! -f ${_pw} -o ! -f ${_gr} ] && return 321 322 while read l; do 323 _u=$(echo ${l} | awk -F ':' '{ print $1 }') 324 if [ "${_u}" != "root" ]; then 325 if [ -z "$(grep -E "^${_u}:" ${DESTDIR}/etc/master.passwd)" ]; then 326 echo "===> Adding the ${_u} user" 327 if ${DESTDIR:+chroot ${DESTDIR}} chpass -la "${l}"; then 328 set -A NEWUSR -- ${NEWUSR[@]} ${_u} 329 fi 330 fi 331 fi 332 done < ${_pw} 333 334 while read l; do 335 _g=$(echo ${l} | awk -F ':' '{ print $1 }') 336 _gid=$(echo ${l} | awk -F ':' '{ print $3 }') 337 if [ -z "$(grep -E "^${_g}:" ${DESTDIR}/etc/group)" ]; then 338 echo "===> Adding the ${_g} group" 339 if ${DESTDIR:+chroot ${DESTDIR}} groupadd -g "${_gid}" "${_g}"; then 340 set -A NEWGRP -- ${NEWGRP[@]} ${_g} 341 fi 342 fi 343 done < ${_gr} 344} 345 346merge_loop() { 347 local INSTALL_MERGED MERGE_AGAIN 348 [[ ${MERGE_CMD} == sdiff* ]] && \ 349 echo "===> Type h at the sdiff prompt (%) to get usage help\n" 350 MERGE_AGAIN=1 351 while [ -n "${MERGE_AGAIN}" ]; do 352 cp -p "${COMPFILE}" "${COMPFILE}.merged" 353 ${MERGE_CMD} "${COMPFILE}.merged" \ 354 "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" 355 INSTALL_MERGED=v 356 while [ "${INSTALL_MERGED}" = "v" ]; do 357 echo "" 358 echo " Use 'e' to edit the merged file" 359 echo " Use 'i' to install the merged file" 360 echo " Use 'n' to view a diff between the merged and new files" 361 echo " Use 'o' to view a diff between the old and merged files" 362 echo " Use 'r' to re-do the merge" 363 echo " Use 'v' to view the merged file" 364 echo " Use 'x' to delete the merged file and go back to previous menu" 365 echo " Default is to leave the temporary file to deal with by hand" 366 echo "" 367 echo -n "===> How should I deal with the merged file? [Leave it for later] " 368 read INSTALL_MERGED 369 case "${INSTALL_MERGED}" in 370 [eE]) 371 echo "editing merged file...\n" 372 ${EDIT} ${COMPFILE}.merged 373 INSTALL_MERGED=v 374 ;; 375 [iI]) 376 mv "${COMPFILE}.merged" "${COMPFILE}" 377 echo -n "\n===> Merging ${COMPFILE#.}" 378 install_file "${COMPFILE}" || \ 379 warn "problem merging ${COMPFILE#.}" 380 unset MERGE_AGAIN 381 ;; 382 [nN]) 383 ( 384 echo "comparison between merged and new files:\n" 385 diff -u ${COMPFILE}.merged ${COMPFILE} 386 ) | ${PAGER} 387 INSTALL_MERGED=v 388 ;; 389 [oO]) 390 ( 391 echo "comparison between old and merged files:\n" 392 diff -u ${DESTDIR}${COMPFILE#.} ${COMPFILE}.merged 393 ) | ${PAGER} 394 INSTALL_MERGED=v 395 ;; 396 [rR]) 397 rm "${COMPFILE}.merged" 398 ;; 399 [vV]) 400 ${PAGER} "${COMPFILE}.merged" 401 ;; 402 [xX]) 403 rm "${COMPFILE}.merged" 404 return 1 405 ;; 406 '') 407 echo "===> ${COMPFILE} will remain for your consideration" 408 unset MERGE_AGAIN 409 ;; 410 *) 411 echo "invalid choice: ${INSTALL_MERGED}" 412 INSTALL_MERGED=v 413 ;; 414 esac 415 done 416 done 417} 418 419diff_loop() { 420 local i CAN_INSTALL HANDLE_COMPFILE NO_INSTALLED 421 if [ -n "${BATCHMODE}" ]; then 422 HANDLE_COMPFILE=todo 423 else 424 HANDLE_COMPFILE=v 425 fi 426 427 unset NO_INSTALLED CAN_INSTALL FORCE_UPG 428 429 while [[ ${HANDLE_COMPFILE} == @(v|todo) ]]; do 430 if [ -f "${DESTDIR}${COMPFILE#.}" -a -f "${COMPFILE}" -a -z "${IS_LINK}" ]; then 431 if [ -z "${DIFFMODE}" ]; then 432 # automatically install files if current != new and current = old 433 for i in "${AUTO_UPG[@]}"; do 434 [[ ${i} == ${COMPFILE} ]] && FORCE_UPG=1 435 done 436 # automatically install files which differ only by CVS Id or that are binaries 437 if [ -z "$(diff -q -I'[$]OpenBSD:.*$' "${DESTDIR}${COMPFILE#.}" "${COMPFILE}")" -o -n "${FORCE_UPG}" -o -n "${IS_BINFILE}" ]; then 438 echo -n "===> Updating ${COMPFILE#.}" 439 if install_file "${COMPFILE}"; then 440 AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n" 441 else 442 warn "problem updating ${COMPFILE#.}" 443 fi 444 return 445 fi 446 fi 447 if [ "${HANDLE_COMPFILE}" = "v" ]; then 448 ( 449 echo "\n========================================================================\n" 450 echo "===> Displaying differences between ${COMPFILE} and installed version:" 451 echo "" 452 diff -u "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" 453 ) | ${PAGER} 454 echo "" 455 fi 456 else 457 # file does not exist on the target system 458 if [ -n "${IS_LINK}" ]; then 459 if [ -n "${DIFFMODE}" ]; then 460 echo "" 461 NO_INSTALLED=1 462 else 463 if install_link; then 464 echo "===> ${COMPFILE#.} link created successfully" 465 AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n" 466 else 467 warn "problem creating ${COMPFILE#.} link" 468 fi 469 return 470 fi 471 fi 472 if [ -n "${DIFFMODE}" ]; then 473 echo "" 474 NO_INSTALLED=1 475 else 476 echo -n "===> Installing ${COMPFILE#.}" 477 if install_file "${COMPFILE}"; then 478 AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}${DESTDIR}${COMPFILE#.}\n" 479 else 480 warn "problem installing ${COMPFILE#.}" 481 fi 482 return 483 fi 484 fi 485 486 if [ -z "${BATCHMODE}" ]; then 487 echo " Use 'd' to delete the temporary ${COMPFILE}" 488 if [ "${COMPFILE}" != ./etc/hosts ]; then 489 CAN_INSTALL=1 490 echo " Use 'i' to install the temporary ${COMPFILE}" 491 fi 492 if [ -z "${NO_INSTALLED}" -a -z "${IS_BINFILE}" -a -z "${IS_LINK}" ]; then 493 echo " Use 'm' to merge the temporary and installed versions" 494 echo " Use 'v' to view the diff results again" 495 fi 496 echo "" 497 echo " Default is to leave the temporary file to deal with by hand" 498 echo "" 499 echo -n "How should I deal with this? [Leave it for later] " 500 read HANDLE_COMPFILE 501 else 502 unset HANDLE_COMPFILE 503 fi 504 505 case "${HANDLE_COMPFILE}" in 506 [dD]) 507 rm "${COMPFILE}" 508 echo "\n===> Deleting ${COMPFILE}" 509 ;; 510 [iI]) 511 if [ -n "${CAN_INSTALL}" ]; then 512 echo "" 513 if [ -n "${IS_LINK}" ]; then 514 if install_link; then 515 echo "===> ${COMPFILE#.} link created successfully" 516 MERGED_FILES="${MERGED_FILES}${DESTDIR}${COMPFILE#.}\n" 517 else 518 warn "problem creating ${COMPFILE#.} link" 519 fi 520 else 521 echo -n "===> Updating ${COMPFILE#.}" 522 if install_file "${COMPFILE}"; then 523 MERGED_FILES="${MERGED_FILES}${DESTDIR}${COMPFILE#.}\n" 524 else 525 warn "problem updating ${COMPFILE#.}" 526 fi 527 fi 528 else 529 echo "invalid choice: ${HANDLE_COMPFILE}\n" 530 HANDLE_COMPFILE="todo" 531 fi 532 533 ;; 534 [mM]) 535 if [ -z "${NO_INSTALLED}" -a -z "${IS_BINFILE}" -a -z "${IS_LINK}" ]; then 536 merge_loop && \ 537 MERGED_FILES="${MERGED_FILES}${DESTDIR}${COMPFILE#.}\n" || \ 538 HANDLE_COMPFILE="todo" 539 else 540 echo "invalid choice: ${HANDLE_COMPFILE}\n" 541 HANDLE_COMPFILE="todo" 542 fi 543 ;; 544 [vV]) 545 if [ -z "${NO_INSTALLED}" -a -z "${IS_BINFILE}" -a -z "${IS_LINK}" ]; then 546 HANDLE_COMPFILE="v" 547 else 548 echo "invalid choice: ${HANDLE_COMPFILE}\n" 549 HANDLE_COMPFILE="todo" 550 fi 551 ;; 552 '') 553 echo "===> ${COMPFILE} will remain for your consideration" 554 ;; 555 *) 556 echo "invalid choice: ${HANDLE_COMPFILE}\n" 557 HANDLE_COMPFILE="todo" 558 continue 559 ;; 560 esac 561 done 562} 563 564sm_compare() { 565 local _c1 _c2 COMPFILE CVSID1 CVSID2 566 echo "===> Starting comparison" 567 568 cd ${TEMPROOT} || error_rm_wrkdir "cannot enter ${TEMPROOT}" 569 570 # aliases(5) needs to be handled last in case smtpd.conf(5) syntax changes 571 _c1=$(find . -type f -or -type l | grep -vE '^./etc/mail/aliases$') 572 _c2=$(find . -type f -name aliases) 573 for COMPFILE in ${_c1} ${_c2}; do 574 unset IS_BINFILE IS_LINK 575 # treat empty files the same as IS_BINFILE to avoid comparing them; 576 # only process them (i.e. install) if they don't exist on the target system 577 if [ ! -s "${COMPFILE}" ]; then 578 if [ -f "${DESTDIR}${COMPFILE#.}" ]; then 579 [[ -f ${COMPFILE} ]] && rm ${COMPFILE} 580 else 581 IS_BINFILE=1 582 fi 583 fi 584 585 # links need to be treated in a different way 586 [[ -h ${COMPFILE} ]] && IS_LINK=1 587 if [ -n "${IS_LINK}" -a -h "${DESTDIR}${COMPFILE#.}" ]; then 588 IS_LINK=1 589 # if links target are the same, remove from temproot 590 if [ "$(readlink ${COMPFILE})" = "$(readlink ${DESTDIR}${COMPFILE#.})" ]; then 591 rm "${COMPFILE}" 592 else 593 diff_loop 594 fi 595 continue 596 fi 597 598 # file not present on the system 599 if [ ! -e "${DESTDIR}${COMPFILE#.}" ]; then 600 diff_loop 601 continue 602 fi 603 604 # compare CVS $Id's first so if the file hasn't been modified, 605 # it will be deleted from temproot and ignored from comparison. 606 # several files are generated from scripts so CVS ID is not a 607 # reliable way of detecting changes; leave for a full diff. 608 if [[ -z ${DIFFMODE} && \ 609 ${COMPFILE} != ./etc/@(fbtab|sysctl.conf|ttys) && \ 610 -z ${IS_LINK} ]]; then 611 CVSID1=$(sed -n "/[$]OpenBSD:.*Exp [$]/{p;q;}" ${DESTDIR}${COMPFILE#.} 2>/dev/null) 612 CVSID2=$(sed -n "/[$]OpenBSD:.*Exp [$]/{p;q;}" ${COMPFILE} 2>/dev/null) || CVSID2=none 613 [[ ${CVSID2} == ${CVSID1} ]] && rm "${COMPFILE}" 614 fi 615 616 if [ -f "${COMPFILE}" -a -z "${IS_LINK}" ]; then 617 # make sure files are different; if not, delete the one in temproot 618 if diff -q "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" >/dev/null; then 619 rm "${COMPFILE}" 620 # xetcXX.tgz contains binary files; set IS_BINFILE to disable sdiff 621 elif diff -q "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" | grep -q Binary; then 622 IS_BINFILE=1 623 diff_loop 624 else 625 diff_loop 626 fi 627 fi 628 done 629} 630 631sm_check_an_eg() { 632 EGSUM=examplessum 633 local _egmods _i _managed 634 if [ -f "${DESTDIR}/${DBDIR}/${EGSUM}" ]; then 635 EGMODS="$(sha256 -c ${DESTDIR}/${DBDIR}/${EGSUM} 2>/dev/null | grep 'FAILED$' | awk '{ print $2 }' | sed -e "s,:,,")" 636 mv ${DESTDIR}/${DBDIR}/${EGSUM} ${DESTDIR}/${DBDIR}/.${EGSUM}.bak 637 fi 638 for _i in ${EGMODS}; do 639 _managed=$(echo ${_i} | sed -e "s,etc/examples,etc,") 640 if [ -f "${DESTDIR}/${_managed}" ]; then 641 _egmods="${_egmods} ${_managed##*/}" 642 fi 643 done 644 if [ -n "${_egmods}" ]; then 645 warn "example file(s) changed for:${_egmods}" 646 else 647 # example changed but we have no corresponding file under /etc 648 unset EGMODS 649 fi 650 cd ${DESTDIR:=/} && \ 651 find ./etc/examples -type f | xargs sha256 -h ${DESTDIR}/${DBDIR}/${EGSUM} || \ 652 error_rm_wrkdir "failed to create ${EGSUM} checksum file" 653 if [ -f "${DESTDIR}/${DBDIR}/.${EGSUM}.bak" ]; then 654 rm ${DESTDIR}/${DBDIR}/.${EGSUM}.bak 655 fi 656} 657 658sm_post() { 659 local FILES_IN_TEMPROOT FILES_IN_BKPDIR 660 661 FILES_IN_TEMPROOT=$(find ${TEMPROOT} -type f ! -name \*.merged -size +0) 662 [[ -d ${BKPDIR} ]] && FILES_IN_BKPDIR=$(find ${BKPDIR} -type f -size +0) 663 664 if [ -n "${NEED_NEWALIASES}" ]; then 665 report "===> A new ${DESTDIR}/etc/mail/aliases file was installed." 666 report "However ${DESTDIR}/usr/bin/newaliases could not be run," 667 report "you will need to rebuild your aliases database manually.\n" 668 fi 669 670 if [ -n "${AUTO_INSTALLED_FILES}" ]; then 671 report "===> Automatically installed file(s)" 672 report "${AUTO_INSTALLED_FILES}" 673 fi 674 if [ -n "${MERGED_FILES}" ]; then 675 report "===> Manually merged/installed file(s)" 676 report "${MERGED_FILES}" 677 fi 678 if [ -n "${FILES_IN_BKPDIR}" ]; then 679 report "===> Backup of replaced file(s) can be found under" 680 report "${BKPDIR}\n" 681 fi 682 if [ -n "${NEWUSR}" -o -n "${NEWGRP}" ]; then 683 report "===> The following user(s)/group(s) have been added" 684 [[ -n ${NEWUSR} ]] && report "user(s): ${NEWUSR[@]}" 685 [[ -n ${NEWGRP} ]] && report "group(s): ${NEWGRP[@]}" 686 report "" 687 fi 688 if [ -n "${EGMODS}" ]; then 689 report "===> Example(s) with corresponding used files modified since last run" 690 report "${EGMODS}" 691 report "" 692 fi 693 if [ -n "${FILES_IN_TEMPROOT}" ]; then 694 report "===> File(s) remaining for you to merge by hand" 695 report "${FILES_IN_TEMPROOT}" 696 fi 697 698 [[ -n ${FILES_IN_TEMPROOT} ]] && \ 699 warn "some files are still left for comparison" 700 701 [[ -n ${NEED_NEWALIASES} ]] && \ 702 warn "newaliases(8) failed to run properly" 703 704 [[ -n ${NEED_REBOOT} ]] && \ 705 warn "some new/updated file(s) may require a reboot" 706 707 echo "===> Checking directory hierarchy permissions (running mtree(8))" 708 mtree -qdef ${DESTDIR}/etc/mtree/4.4BSD.dist -p ${DESTDIR:=/} -U >/dev/null 709 [[ -n ${XTGZ} ]] && \ 710 mtree -qdef ${DESTDIR}/etc/mtree/BSD.x11.dist -p ${DESTDIR:=/} -U >/dev/null 711 712 if [ -e "${REPORT}" ]; then 713 echo "===> Output log available at ${REPORT}" 714 find ${TEMPROOT} -type f -empty | xargs -r rm 715 find ${TEMPROOT} -type d | sort -r | xargs -r rmdir 2>/dev/null 716 else 717 echo "===> Removing ${WRKDIR}" 718 rm -rf "${WRKDIR}" 719 fi 720 721 unset NEED_NEWALIASES NEED_REBOOT 722 723 clean_src 724 rm -f ${DESTDIR}/${DBDIR}/.*.bak 725} 726 727 728while getopts bdSs:x: arg; do 729 case ${arg} in 730 b) 731 BATCHMODE=1 732 ;; 733 d) 734 DIFFMODE=1 735 ;; 736 s) 737 if [ -d "${OPTARG}" ]; then 738 SRCDIR="${OPTARG}" 739 [[ -f ${SRCDIR}/etc/Makefile ]] || \ 740 error_rm_wrkdir "${SRCDIR}: invalid \"src\" tree, missing ${SRCDIR}/etc/Makefile" 741 continue 742 fi 743 TGZ="${OPTARG}" 744 ;; 745 S) 746 NOSIGCHECK=1 747 ;; 748 x) 749 XTGZ="${OPTARG}" 750 ;; 751 *) 752 usage 753 error_rm_wrkdir 754 ;; 755 esac 756done 757 758shift $(( OPTIND -1 )) 759if (($# != 0)); then 760 usage 761 error_rm_wrkdir 762fi 763 764if [ -z "${SRCDIR}" -a -z "${TGZ}" -a -z "${XTGZ}" ]; then 765 if [ -n "${SM_PATH}" ]; then 766 TGZ="${SM_PATH}/etc${RELINT}.tgz" 767 if [ -d ${DESTDIR}/etc/X11 ]; then 768 XTGZ="${SM_PATH}/xetc${RELINT}.tgz" 769 fi 770 elif [ -f "/usr/src/etc/Makefile" ]; then 771 SRCDIR=/usr/src 772 else 773 usage 774 error_rm_wrkdir "please specify a valid path to src or (x)etcXX.tgz" 775 fi 776fi 777 778TEMPROOT="${WRKDIR}/temproot" 779BKPDIR="${WRKDIR}/backups" 780 781sm_fetch_and_verify 782sm_populate 783sm_compare 784sm_check_an_eg 785sm_post 786