mergemaster.sh revision 189763
1#!/bin/sh
2
3# mergemaster
4
5# Compare files created by /usr/src/etc/Makefile (or the directory
6# the user specifies) with the currently installed copies.
7
8# Copyright 1998-2009 Douglas Barton
9# DougB@FreeBSD.org
10
11# $FreeBSD: head/usr.sbin/mergemaster/mergemaster.sh 189763 2009-03-13 08:48:33Z dougb $
12
13PATH=/bin:/usr/bin:/usr/sbin
14
15display_usage () {
16  VERSION_NUMBER=`grep "[$]FreeBSD:" $0 | cut -d ' ' -f 4`
17  echo "mergemaster version ${VERSION_NUMBER}"
18  echo 'Usage: mergemaster [-scrvahipCPU]'
19  echo '    [-m /path] [-t /path] [-d] [-u N] [-w N] [-A arch] [-D /path]'
20  echo "Options:"
21  echo "  -s  Strict comparison (diff every pair of files)"
22  echo "  -c  Use context diff instead of unified diff"
23  echo "  -r  Re-run on a previously cleaned directory (skip temproot creation)"
24  echo "  -v  Be more verbose about the process, include additional checks"
25  echo "  -a  Leave all files that differ to merge by hand"
26  echo "  -h  Display more complete help"
27  echo '  -i  Automatically install files that do not exist in destination directory'
28  echo '  -p  Pre-buildworld mode, only compares crucial files'
29  echo '  -C  Compare local rc.conf variables to the defaults'
30  echo '  -P  Preserve files that are overwritten'
31  echo "  -U  Attempt to auto upgrade files that have not been user modified"
32  echo ''
33  echo "  -m /path/directory  Specify location of source to do the make in"
34  echo "  -t /path/directory  Specify temp root directory"
35  echo "  -d  Add date and time to directory name (e.g., /var/tmp/temproot.`date +%m%d.%H.%M`)"
36  echo "  -u N  Specify a numeric umask"
37  echo "  -w N  Specify a screen width in columns to sdiff"
38  echo "  -A architecture  Alternative architecture name to pass to make"
39  echo '  -D /path/directory  Specify the destination directory to install files to'
40  echo ''
41}
42
43display_help () {
44  echo "* To specify a directory other than /var/tmp/temproot for the"
45  echo "  temporary root environment, use -t /path/to/temp/root"
46  echo "* The -w option takes a number as an argument for the column width"
47  echo "  of the screen.  The default is 80."
48  echo '* The -a option causes mergemaster to run without prompting.'
49}
50
51# Loop allowing the user to use sdiff to merge files and display the merged
52# file.
53merge_loop () {
54  case "${VERBOSE}" in
55  '') ;;
56  *)
57      echo "   *** Type h at the sdiff prompt (%) to get usage help"
58      ;;
59  esac
60  echo ''
61  MERGE_AGAIN=yes
62  while [ "${MERGE_AGAIN}" = "yes" ]; do
63    # Prime file.merged so we don't blat the owner/group id's
64    cp -p "${COMPFILE}" "${COMPFILE}.merged"
65    sdiff -o "${COMPFILE}.merged" --text --suppress-common-lines \
66      --width=${SCREEN_WIDTH:-80} "${DESTDIR}${COMPFILE#.}" "${COMPFILE}"
67    INSTALL_MERGED=V
68    while [ "${INSTALL_MERGED}" = "v" -o "${INSTALL_MERGED}" = "V" ]; do
69      echo ''
70      echo "  Use 'i' to install merged file"
71      echo "  Use 'r' to re-do the merge"
72      echo "  Use 'v' to view the merged file"
73      echo "  Default is to leave the temporary file to deal with by hand"
74      echo ''
75      echo -n "    *** How should I deal with the merged file? [Leave it for later] "
76      read INSTALL_MERGED
77
78      case "${INSTALL_MERGED}" in
79      [iI])
80        mv "${COMPFILE}.merged" "${COMPFILE}"
81        echo ''
82        if mm_install "${COMPFILE}"; then
83          echo "     *** Merged version of ${COMPFILE} installed successfully"
84        else
85          echo "     *** Problem installing ${COMPFILE}, it will remain to merge by hand later"
86        fi
87        unset MERGE_AGAIN
88        ;;
89      [rR])
90        rm "${COMPFILE}.merged"
91        ;;
92      [vV])
93        ${PAGER} "${COMPFILE}.merged"
94        ;;
95      '')
96        echo "   *** ${COMPFILE} will remain for your consideration"
97        unset MERGE_AGAIN
98        ;;
99      *)
100        echo "invalid choice: ${INSTALL_MERGED}"
101        INSTALL_MERGED=V
102        ;;
103      esac
104    done
105  done
106}
107
108# Loop showing user differences between files, allow merge, skip or install
109# options
110diff_loop () {
111
112  HANDLE_COMPFILE=v
113
114  while [ "${HANDLE_COMPFILE}" = "v" -o "${HANDLE_COMPFILE}" = "V" -o \
115    "${HANDLE_COMPFILE}" = "NOT V" ]; do
116    if [ -f "${DESTDIR}${COMPFILE#.}" -a -f "${COMPFILE}" ]; then
117      if [ -n "${AUTO_UPGRADE}" ]; then
118        if echo "${CHANGED}" | grep -qsv ${DESTDIR}${COMPFILE#.}; then
119          echo ''
120          echo "  *** ${COMPFILE} has not been user modified."
121          echo ''
122
123          if mm_install "${COMPFILE}"; then
124            echo "   *** ${COMPFILE} upgraded successfully"
125            echo ''
126            # Make the list print one file per line
127            AUTO_UPGRADED_FILES="${AUTO_UPGRADED_FILES}      ${DESTDIR}${COMPFILE#.}
128"
129          else
130          echo "   *** Problem upgrading ${COMPFILE}, it will remain to merge by hand"
131          fi
132          return
133        fi
134      fi
135      if [ "${HANDLE_COMPFILE}" = "v" -o "${HANDLE_COMPFILE}" = "V" ]; then
136	echo ''
137	echo '   ======================================================================   '
138	echo ''
139        (
140          echo "  *** Displaying differences between ${COMPFILE} and installed version:"
141          echo ''
142          diff ${DIFF_FLAG} ${DIFF_OPTIONS} "${DESTDIR}${COMPFILE#.}" "${COMPFILE}"
143        ) | ${PAGER}
144        echo ''
145      fi
146    else
147      echo ''
148      echo "  *** There is no installed version of ${COMPFILE}"
149      echo ''
150      case "${AUTO_INSTALL}" in
151      [Yy][Ee][Ss])
152        echo ''
153        if mm_install "${COMPFILE}"; then
154          echo "   *** ${COMPFILE} installed successfully"
155          echo ''
156          # Make the list print one file per line
157          AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES}      ${DESTDIR}${COMPFILE#.}
158"
159        else
160          echo "   *** Problem installing ${COMPFILE}, it will remain to merge by hand"
161        fi
162        return
163        ;;
164      *)
165        NO_INSTALLED=yes
166        ;;
167      esac
168    fi
169
170    echo "  Use 'd' to delete the temporary ${COMPFILE}"
171    echo "  Use 'i' to install the temporary ${COMPFILE}"
172    case "${NO_INSTALLED}" in
173    '')
174      echo "  Use 'm' to merge the temporary and installed versions"
175      echo "  Use 'v' to view the diff results again"
176      ;;
177    esac
178    echo ''
179    echo "  Default is to leave the temporary file to deal with by hand"
180    echo ''
181    echo -n "How should I deal with this? [Leave it for later] "
182    read HANDLE_COMPFILE
183
184    case "${HANDLE_COMPFILE}" in
185    [dD])
186      rm "${COMPFILE}"
187      echo ''
188      echo "   *** Deleting ${COMPFILE}"
189      ;;
190    [iI])
191      echo ''
192      if mm_install "${COMPFILE}"; then
193        echo "   *** ${COMPFILE} installed successfully"
194      else
195        echo "   *** Problem installing ${COMPFILE}, it will remain to merge by hand"
196      fi
197      ;;
198    [mM])
199      case "${NO_INSTALLED}" in
200      '')
201        # interact with user to merge files
202        merge_loop
203        ;;
204      *)
205        echo ''
206        echo "   *** There is no installed version of ${COMPFILE}"
207        echo ''
208        HANDLE_COMPFILE="NOT V"
209        ;;
210      esac # End of "No installed version of file but user selected merge" test
211      ;;
212    [vV])
213      continue
214      ;;
215    '')
216      echo ''
217      echo "   *** ${COMPFILE} will remain for your consideration"
218      ;;
219    *)
220      # invalid choice, show menu again.
221      echo "invalid choice: ${HANDLE_COMPFILE}"
222      echo ''
223      HANDLE_COMPFILE="NOT V"
224      continue
225      ;;
226    esac  # End of "How to handle files that are different"
227  done
228  unset NO_INSTALLED
229  echo ''
230  case "${VERBOSE}" in
231  '') ;;
232  *)
233    sleep 3
234    ;;
235  esac
236}
237
238press_to_continue () {
239  local DISCARD
240  echo -n ' *** Press the [Enter] or [Return] key to continue '
241  read DISCARD
242}
243
244# Set the default path for the temporary root environment
245#
246TEMPROOT='/var/tmp/temproot'
247
248# Read /etc/mergemaster.rc first so the one in $HOME can override
249#
250if [ -r /etc/mergemaster.rc ]; then
251  . /etc/mergemaster.rc
252fi
253
254# Read .mergemasterrc before command line so CLI can override
255#
256if [ -r "$HOME/.mergemasterrc" ]; then
257  . "$HOME/.mergemasterrc"
258fi
259
260# Assign the location of the mtree database
261#
262MTREEDB=${MTREEDB:-/var/db}
263MTREEFILE="${MTREEDB}/mergemaster.mtree"
264
265# Check the command line options
266#
267while getopts ":ascrvhipCPm:t:du:w:D:A:U" COMMAND_LINE_ARGUMENT ; do
268  case "${COMMAND_LINE_ARGUMENT}" in
269  A)
270    ARCHSTRING='TARGET_ARCH='${OPTARG}
271    ;;
272  U)
273    AUTO_UPGRADE=yes
274    ;;
275  s)
276    STRICT=yes
277    unset DIFF_OPTIONS
278    ;;
279  c)
280    DIFF_FLAG='-c'
281    ;;
282  r)
283    RERUN=yes
284    ;;
285  v)
286    case "${AUTO_RUN}" in
287    '') VERBOSE=yes ;;
288    esac
289    ;;
290  a)
291    AUTO_RUN=yes
292    unset VERBOSE
293    ;;
294  h)
295    display_usage
296    display_help
297    exit 0
298    ;;
299  i)
300    AUTO_INSTALL=yes
301    ;;
302  C)
303    COMP_CONFS=yes
304    ;;
305  P)
306    PRESERVE_FILES=yes
307    ;;
308  p)
309    PRE_WORLD=yes
310    unset COMP_CONFS
311    unset AUTO_RUN
312    ;;
313  m)
314    SOURCEDIR=${OPTARG}
315    ;;
316  t)
317    TEMPROOT=${OPTARG}
318    ;;
319  d)
320    TEMPROOT=${TEMPROOT}.`date +%m%d.%H.%M`
321    ;;
322  u)
323    NEW_UMASK=${OPTARG}
324    ;;
325  w)
326    SCREEN_WIDTH=${OPTARG}
327    ;;
328  D)
329    DESTDIR=${OPTARG}
330    ;;
331  *)
332    display_usage
333    exit 1
334    ;;
335  esac
336done
337
338# Don't force the user to set this in the mergemaster rc file
339if [ -n "${PRESERVE_FILES}" -a -z "${PRESERVE_FILES_DIR}" ]; then
340  PRESERVE_FILES_DIR=/var/tmp/mergemaster/preserved-files-`date +%y%m%d-%H%M%S`
341fi
342
343# Check for the mtree database in DESTDIR
344case "${AUTO_UPGRADE}" in
345'') ;;	# If the option is not set no need to run the test or warn the user
346*)
347  if [ ! -f "${DESTDIR}${MTREEFILE}" ]; then
348    echo ''
349    echo "*** Unable to find mtree database. Skipping auto-upgrade."
350    echo ''
351    press_to_continue
352    unset AUTO_UPGRADE
353  fi
354  ;;
355esac
356
357if [ -e "${DESTDIR}/etc/fstab" ]; then
358  if grep -q nodev ${DESTDIR}/etc/fstab; then
359    echo ''
360    echo "*** You have the deprecated 'nodev' option in ${DESTDIR}/etc/fstab."
361    echo "    This can prevent the filesystem from being mounted on reboot."
362    echo "    Please update your fstab before continuing."
363    echo "    See fstab(5) for more information."
364    echo ''
365    exit 1
366  fi
367fi
368
369echo ''
370
371# If the user has a pager defined, make sure we can run it
372#
373case "${DONT_CHECK_PAGER}" in
374'')
375check_pager () {
376  while ! type "${PAGER%% *}" >/dev/null; do
377    echo " *** Your PAGER environment variable specifies '${PAGER}', but"
378    echo "     due to the limited PATH that I use for security reasons,"
379    echo "     I cannot execute it.  So, what would you like to do?"
380    echo ''
381    echo "  Use 'e' to exit mergemaster and fix your PAGER variable"
382    if [ -x /usr/bin/less -o -x /usr/local/bin/less ]; then
383    echo "  Use 'l' to set PAGER to 'less' for this run"
384    fi
385    echo "  Use 'm' to use plain old 'more' as your PAGER for this run"
386    echo ''
387    echo "  Default is to use plain old 'more' "
388    echo ''
389    echo -n "What should I do? [Use 'more'] "
390    read FIXPAGER
391
392    case "${FIXPAGER}" in
393    [eE])
394       exit 0
395       ;;
396    [lL])
397       if [ -x /usr/bin/less ]; then
398         PAGER=/usr/bin/less
399       elif [ -x /usr/local/bin/less ]; then
400         PAGER=/usr/local/bin/less
401       else
402         echo ''
403         echo " *** Fatal Error:"
404         echo "     You asked to use 'less' as your pager, but I can't"
405         echo "     find it in /usr/bin or /usr/local/bin"
406         exit 1
407       fi
408       ;;
409    [mM]|'')
410       PAGER=more
411       ;;
412    *)
413       echo ''
414       echo "invalid choice: ${FIXPAGER}"
415    esac
416    echo ''
417  done
418}
419  if [ -n "${PAGER}" ]; then
420    check_pager
421  fi
422  ;;
423esac
424
425# If user has a pager defined, or got assigned one above, use it.
426# If not, use more.
427#
428PAGER=${PAGER:-more}
429
430if [ -n "${VERBOSE}" -a ! "${PAGER}" = "more" ]; then
431  echo " *** You have ${PAGER} defined as your pager so we will use that"
432  echo ''
433  sleep 3
434fi
435
436# Assign the diff flag once so we will not have to keep testing it
437#
438DIFF_FLAG=${DIFF_FLAG:--u}
439
440# Assign the source directory
441#
442SOURCEDIR=${SOURCEDIR:-/usr/src}
443if [ ! -f ${SOURCEDIR}/Makefile.inc1 -a \
444   -f ${SOURCEDIR}/../Makefile.inc1 ]; then
445  echo " *** The source directory you specified (${SOURCEDIR})"
446  echo "     will be reset to ${SOURCEDIR}/.."
447  echo ''
448  sleep 3
449  SOURCEDIR=${SOURCEDIR}/..
450fi
451
452# Setup make to use system files from SOURCEDIR
453MM_MAKE="make ${ARCHSTRING} -m ${SOURCEDIR}/share/mk"
454
455# Check DESTDIR against the mergemaster mtree database to see what
456# files the user changed from the reference files.
457#
458CHANGED=
459if [ -n "${AUTO_UPGRADE}" -a -f "${DESTDIR}${MTREEFILE}" ]; then
460	for file in `mtree -eq -f ${DESTDIR}${MTREEFILE} -p ${DESTDIR}/ \
461		2>/dev/null | awk '($2 == "changed") {print $1}'`; do
462		if [ -f "${DESTDIR}/$file" ]; then
463			CHANGED="${CHANGED} ${DESTDIR}/$file"
464		fi
465	done
466fi
467
468# Check the width of the user's terminal
469#
470if [ -t 0 ]; then
471  w=`tput columns`
472  case "${w}" in
473  0|'') ;; # No-op, since the input is not valid
474  *)
475    case "${SCREEN_WIDTH}" in
476    '') SCREEN_WIDTH="${w}" ;;
477    "${w}") ;; # No-op, since they are the same
478    *)
479      echo -n "*** You entered ${SCREEN_WIDTH} as your screen width, but stty "
480      echo "thinks it is ${w}."
481      echo ''
482      echo -n "What would you like to use? [${w}] "
483      read SCREEN_WIDTH
484      case "${SCREEN_WIDTH}" in
485      '') SCREEN_WIDTH="${w}" ;;
486      esac
487      ;;
488    esac
489  esac
490fi
491
492# Define what CVS $Id tag to look for to aid portability.
493#
494CVS_ID_TAG=FreeBSD
495
496delete_temproot () {
497  rm -rf "${TEMPROOT}" 2>/dev/null
498  chflags -R 0 "${TEMPROOT}" 2>/dev/null
499  rm -rf "${TEMPROOT}" || exit 1
500}
501
502case "${RERUN}" in
503'')
504  # Set up the loop to test for the existence of the
505  # temp root directory.
506  #
507  TEST_TEMP_ROOT=yes
508  while [ "${TEST_TEMP_ROOT}" = "yes" ]; do
509    if [ -d "${TEMPROOT}" ]; then
510      echo "*** The directory specified for the temporary root environment,"
511      echo "    ${TEMPROOT}, exists.  This can be a security risk if untrusted"
512      echo "    users have access to the system."
513      echo ''
514      case "${AUTO_RUN}" in
515      '')
516        echo "  Use 'd' to delete the old ${TEMPROOT} and continue"
517        echo "  Use 't' to select a new temporary root directory"
518        echo "  Use 'e' to exit mergemaster"
519        echo ''
520        echo "  Default is to use ${TEMPROOT} as is"
521        echo ''
522        echo -n "How should I deal with this? [Use the existing ${TEMPROOT}] "
523        read DELORNOT
524
525        case "${DELORNOT}" in
526        [dD])
527          echo ''
528          echo "   *** Deleting the old ${TEMPROOT}"
529          echo ''
530          delete_temproot || exit 1
531          unset TEST_TEMP_ROOT
532          ;;
533        [tT])
534          echo "   *** Enter new directory name for temporary root environment"
535          read TEMPROOT
536          ;;
537        [eE])
538          exit 0
539          ;;
540        '')
541          echo ''
542          echo "   *** Leaving ${TEMPROOT} intact"
543          echo ''
544          unset TEST_TEMP_ROOT
545          ;;
546        *)
547          echo ''
548          echo "invalid choice: ${DELORNOT}"
549          echo ''
550          ;;
551        esac
552        ;;
553      *)
554        # If this is an auto-run, try a hopefully safe alternative then
555        # re-test anyway.
556        TEMPROOT=/var/tmp/temproot.`date +%m%d.%H.%M.%S`
557        ;;
558      esac
559    else
560      unset TEST_TEMP_ROOT
561    fi
562  done
563
564  echo "*** Creating the temporary root environment in ${TEMPROOT}"
565
566  if mkdir -p "${TEMPROOT}"; then
567    echo " *** ${TEMPROOT} ready for use"
568  fi
569
570  if [ ! -d "${TEMPROOT}" ]; then
571    echo ''
572    echo "  *** FATAL ERROR: Cannot create ${TEMPROOT}"
573    echo ''
574    exit 1
575  fi
576
577  echo " *** Creating and populating directory structure in ${TEMPROOT}"
578  echo ''
579
580  case "${VERBOSE}" in
581  '') ;;
582  *)
583    press_to_continue
584    ;;
585  esac
586
587  case "${PRE_WORLD}" in
588  '')
589    { cd ${SOURCEDIR} &&
590      case "${DESTDIR}" in
591      '') ;;
592      *)
593        ${MM_MAKE} DESTDIR=${DESTDIR} distrib-dirs
594        ;;
595      esac
596      od=${TEMPROOT}/usr/obj
597      ${MM_MAKE} DESTDIR=${TEMPROOT} distrib-dirs &&
598      MAKEOBJDIRPREFIX=$od ${MM_MAKE} _obj SUBDIR_OVERRIDE=etc &&
599      MAKEOBJDIRPREFIX=$od ${MM_MAKE} everything SUBDIR_OVERRIDE=etc &&
600      MAKEOBJDIRPREFIX=$od ${MM_MAKE} DESTDIR=${TEMPROOT} distribution;} ||
601    { echo '';
602     echo "  *** FATAL ERROR: Cannot 'cd' to ${SOURCEDIR} and install files to";
603      echo "      the temproot environment";
604      echo '';
605      exit 1;}
606    ;;
607  *)
608    # Only set up files that are crucial to {build|install}world
609    { mkdir -p ${TEMPROOT}/etc &&
610      cp -p ${SOURCEDIR}/etc/master.passwd ${TEMPROOT}/etc &&
611      cp -p ${SOURCEDIR}/etc/group ${TEMPROOT}/etc;} ||
612    { echo '';
613      echo '  *** FATAL ERROR: Cannot copy files to the temproot environment';
614      echo '';
615      exit 1;}
616    ;;
617  esac
618
619  # Doing the inventory and removing files that we don't want to compare only
620  # makes sense if we are not doing a rerun, since we have no way of knowing
621  # what happened to the files during previous incarnations.
622  case "${VERBOSE}" in
623  '') ;;
624  *)
625    echo ''
626    echo ' *** The following files exist only in the installed version of'
627    echo "     ${DESTDIR}/etc.  In the vast majority of cases these files"
628    echo '     are necessary parts of the system and should not be deleted.'
629    echo '     However because these files are not updated by this process you'
630    echo '     might want to verify their status before rebooting your system.'
631    echo ''
632    press_to_continue
633    diff -qr ${DESTDIR}/etc ${TEMPROOT}/etc | grep "^Only in ${DESTDIR}/etc" | ${PAGER}
634    echo ''
635    press_to_continue
636    ;;
637  esac
638
639  # Avoid comparing the motd if the user specifies it in .mergemasterrc
640  # Compatibility shim to be removed in FreeBSD 9.x
641  case "${IGNORE_MOTD}" in
642  '') ;;
643  *) IGNORE_FILES="${IGNORE_FILES} /etc/motd"
644     echo ''
645     echo "*** You have the IGNORE_MOTD option set in your mergemaster rc file."
646     echo "    This option is deprecated in favor of the IGNORE_FILES option."
647     echo "    Please update your rc file accordingly."
648     echo ''
649     press_to_continue
650     ;;
651  esac
652
653  # Avoid comparing the following user specified files
654  for file in ${IGNORE_FILES}; do
655    test -e ${TEMPROOT}/${file} && unlink ${TEMPROOT}/${file}
656  done
657  ;; # End of the "RERUN" test
658esac
659
660# We really don't want to have to deal with files like login.conf.db, pwd.db,
661# or spwd.db.  Instead, we want to compare the text versions, and run *_mkdb.
662# Prompt the user to do so below, as needed.
663#
664rm -f ${TEMPROOT}/etc/*.db ${TEMPROOT}/etc/passwd
665
666# We only need to compare things like freebsd.cf once
667find ${TEMPROOT}/usr/obj -type f -delete 2>/dev/null
668
669# Delete 0 length files to make the mtree database as small as possible.
670find ${TEMPROOT} -type f -size 0 -delete 2>/dev/null
671
672# Build the mtree database in a temporary location.
673MTREENEW=`mktemp -t mergemaster.mtree`
674case "${PRE_WORLD}" in
675'') mtree -ci -p ${TEMPROOT} -k size,md5digest > ${MTREENEW} 2>/dev/null
676    ;;
677*) # We don't want to mess with the mtree database on a pre-world run.
678   ;;
679esac
680
681# Get ready to start comparing files
682
683# Check umask if not specified on the command line,
684# and we are not doing an autorun
685#
686if [ -z "${NEW_UMASK}" -a -z "${AUTO_RUN}" ]; then
687  USER_UMASK=`umask`
688  case "${USER_UMASK}" in
689  0022|022) ;;
690  *)
691    echo ''
692    echo " *** Your umask is currently set to ${USER_UMASK}.  By default, this script"
693    echo "     installs all files with the same user, group and modes that"
694    echo "     they are created with by ${SOURCEDIR}/etc/Makefile, compared to"
695    echo "     a umask of 022.  This umask allows world read permission when"
696    echo "     the file's default permissions have it."
697    echo ''
698    echo "     No world permissions can sometimes cause problems.  A umask of"
699    echo "     022 will restore the default behavior, but is not mandatory."
700    echo "     /etc/master.passwd is a special case.  Its file permissions"
701    echo "     will be 600 (rw-------) if installed."
702    echo ''
703    echo -n "What umask should I use? [${USER_UMASK}] "
704    read NEW_UMASK
705
706    NEW_UMASK="${NEW_UMASK:-$USER_UMASK}"
707    ;;
708  esac
709  echo ''
710fi
711
712CONFIRMED_UMASK=${NEW_UMASK:-0022}
713
714#
715# Warn users who still have old rc files
716#
717for file in atm devfs diskless1 diskless2 network network6 pccard \
718  serial syscons sysctl alpha amd64 i386 ia64 sparc64; do
719  if [ -f "${DESTDIR}/etc/rc.${file}" ]; then
720    OLD_RC_PRESENT=1
721    break
722  fi
723done
724
725case "${OLD_RC_PRESENT}" in
7261)
727  echo ''
728  echo " *** There are elements of the old rc system in ${DESTDIR}/etc/."
729  echo ''
730  echo '     While these scripts will not hurt anything, they are not'
731  echo '     functional on an up to date system, and can be removed.'
732  echo ''
733
734  case "${AUTO_RUN}" in
735  '')
736    echo -n 'Move these files to /var/tmp/mergemaster/old_rc? [yes] '
737    read MOVE_OLD_RC
738
739    case "${MOVE_OLD_RC}" in
740    [nN]*) ;;
741    *)
742      mkdir -p /var/tmp/mergemaster/old_rc
743        for file in atm devfs diskless1 diskless2 network network6 pccard \
744          serial syscons sysctl alpha amd64 i386 ia64 sparc64; do
745          if [ -f "${DESTDIR}/etc/rc.${file}" ]; then
746            mv ${DESTDIR}/etc/rc.${file} /var/tmp/mergemaster/old_rc/
747          fi
748        done
749      echo '  The files have been moved'
750      press_to_continue
751      ;;
752    esac
753    ;;
754  *) ;;
755  esac
756esac
757
758# Use the umask/mode information to install the files
759# Create directories as needed
760#
761install_error () {
762  echo "*** FATAL ERROR: Unable to install ${1} to ${2}"
763  echo ''
764  exit 1
765}
766
767do_install_and_rm () {
768  case "${PRESERVE_FILES}" in
769  [Yy][Ee][Ss])
770    if [ -f "${3}/${2##*/}" ]; then
771      mkdir -p ${PRESERVE_FILES_DIR}/${2%/*}
772      cp ${3}/${2##*/} ${PRESERVE_FILES_DIR}/${2%/*}
773    fi
774    ;;
775  esac
776
777  if [ ! -d "${3}/${2##*/}" ]; then
778    if install -m ${1} ${2} ${3}; then
779      unlink ${2}
780    else
781      install_error ${2} ${3}
782    fi
783  else
784    install_error ${2} ${3}
785  fi
786}
787
788# 4095 = "obase=10;ibase=8;07777" | bc
789find_mode () {
790  local OCTAL
791  OCTAL=$(( ~$(echo "obase=10; ibase=8; ${CONFIRMED_UMASK}" | bc) & 4095 &
792    $(echo "obase=10; ibase=8; $(stat -f "%OMp%OLp" ${1})" | bc) ))
793  printf "%04o\n" ${OCTAL}
794}
795
796mm_install () {
797  local INSTALL_DIR
798  INSTALL_DIR=${1#.}
799  INSTALL_DIR=${INSTALL_DIR%/*}
800
801  case "${INSTALL_DIR}" in
802  '')
803    INSTALL_DIR=/
804    ;;
805  esac
806
807  if [ -n "${DESTDIR}${INSTALL_DIR}" -a ! -d "${DESTDIR}${INSTALL_DIR}" ]; then
808    DIR_MODE=`find_mode "${TEMPROOT}/${INSTALL_DIR}"`
809    install -d -o root -g wheel -m "${DIR_MODE}" "${DESTDIR}${INSTALL_DIR}"
810  fi
811
812  FILE_MODE=`find_mode "${1}"`
813
814  if [ ! -x "${1}" ]; then
815    case "${1#.}" in
816    /etc/mail/aliases)
817      NEED_NEWALIASES=yes
818      ;;
819    /etc/login.conf)
820      NEED_CAP_MKDB=yes
821      ;;
822    /etc/master.passwd)
823      do_install_and_rm 600 "${1}" "${DESTDIR}${INSTALL_DIR}"
824      NEED_PWD_MKDB=yes
825      DONT_INSTALL=yes
826      ;;
827    /.cshrc | /.profile)
828    case "${AUTO_INSTALL}" in
829    '')
830      case "${LINK_EXPLAINED}" in
831      '')
832        echo "   *** Historically BSD derived systems have had a"
833        echo "       hard link from /.cshrc and /.profile to"
834        echo "       their namesakes in /root.  Please indicate"
835        echo "       your preference below for bringing your"
836        echo "       installed files up to date."
837        echo ''
838        LINK_EXPLAINED=yes
839        ;;
840      esac
841
842      echo "   Use 'd' to delete the temporary ${COMPFILE}"
843      echo "   Use 'l' to delete the existing ${DESTDIR}${COMPFILE#.} and create the link"
844      echo ''
845      echo "   Default is to leave the temporary file to deal with by hand"
846      echo ''
847      echo -n "  How should I handle ${COMPFILE}? [Leave it to install later] "
848      read HANDLE_LINK
849      ;;
850    *)  # Part of AUTO_INSTALL
851      HANDLE_LINK=l
852      ;;
853    esac
854
855      case "${HANDLE_LINK}" in
856      [dD]*)
857        rm "${COMPFILE}"
858        echo ''
859        echo "   *** Deleting ${COMPFILE}"
860        ;;
861      [lL]*)
862        echo ''
863        rm -f "${DESTDIR}${COMPFILE#.}"
864        if ln "${DESTDIR}/root/${COMPFILE##*/}" "${DESTDIR}${COMPFILE#.}"; then
865          echo "   *** Link from ${DESTDIR}${COMPFILE#.} to ${DESTDIR}/root/${COMPFILE##*/} installed successfully"
866          rm "${COMPFILE}"
867        else
868          echo "   *** Error linking ${DESTDIR}${COMPFILE#.} to ${DESTDIR}/root/${COMPFILE##*/}, ${COMPFILE} will remain to install by hand"
869        fi
870        ;;
871      *)
872        echo "   *** ${COMPFILE} will remain for your consideration"
873        ;;
874      esac
875      DONT_INSTALL=yes
876      ;;
877    esac
878
879    case "${DONT_INSTALL}" in
880    '')
881      do_install_and_rm "${FILE_MODE}" "${1}" "${DESTDIR}${INSTALL_DIR}"
882      ;;
883    *)
884      unset DONT_INSTALL
885      ;;
886    esac
887  else	# File matched -x
888    do_install_and_rm "${FILE_MODE}" "${1}" "${DESTDIR}${INSTALL_DIR}"
889  fi
890  return $?
891}
892
893if [ ! -d "${TEMPROOT}" ]; then
894	echo "*** FATAL ERROR: The temproot directory (${TEMPROOT})"
895	echo '                 has disappeared!'
896	echo ''
897	exit 1
898fi
899
900echo ''
901echo "*** Beginning comparison"
902echo ''
903
904# Pre-world does not populate /etc/rc.d.
905# It is very possible that a previous run would have deleted files in
906# ${TEMPROOT}/etc/rc.d, thus creating a lot of false positives.
907if [ -z "${PRE_WORLD}" -a -z "${RERUN}" ]; then
908  echo "   *** Checking ${DESTDIR}/etc/rc.d for stale files"
909  echo ''
910  cd "${DESTDIR}/etc/rc.d" &&
911  for file in *; do
912    if [ ! -e "${TEMPROOT}/etc/rc.d/${file}" ]; then
913      STALE_RC_FILES="${STALE_RC_FILES} ${file}"
914    fi
915  done
916  case "${STALE_RC_FILES}" in
917  ''|' *')
918    echo '   *** No stale files found'
919    ;;
920  *)
921    echo "   *** The following files exist in ${DESTDIR}/etc/rc.d but not in"
922    echo "       ${TEMPROOT}/etc/rc.d/:"
923    echo ''
924    echo "${STALE_RC_FILES}"
925    echo ''
926    echo '       The presence of stale files in this directory can cause the'
927    echo '       dreaded unpredictable results, and therefore it is highly'
928    echo '       recommended that you delete them.'
929    case "${AUTO_RUN}" in
930    '')
931      echo ''
932      echo -n '   *** Delete them now? [n] '
933      read DELETE_STALE_RC_FILES
934      case "${DELETE_STALE_RC_FILES}" in
935      [yY])
936        echo '      *** Deleting ... '
937        rm ${STALE_RC_FILES}
938        echo '                       done.'
939        ;;
940      *)
941        echo '      *** Files will not be deleted'
942        ;;
943      esac
944      sleep 2
945      ;;
946    esac
947    ;;
948  esac
949  echo ''
950fi
951
952cd "${TEMPROOT}"
953
954if [ -r "${MM_PRE_COMPARE_SCRIPT}" ]; then
955  . "${MM_PRE_COMPARE_SCRIPT}"
956fi
957
958# Using -size +0 avoids uselessly checking the empty log files created
959# by ${SOURCEDIR}/etc/Makefile and the device entries in ./dev, but does
960# check the scripts in ./dev, as we'd like (assuming no devfs of course).
961#
962for COMPFILE in `find . -type f -size +0`; do
963
964  # First, check to see if the file exists in DESTDIR.  If not, the
965  # diff_loop function knows how to handle it.
966  #
967  if [ ! -e "${DESTDIR}${COMPFILE#.}" ]; then
968    case "${AUTO_RUN}" in
969      '')
970        diff_loop
971        ;;
972      *)
973        case "${AUTO_INSTALL}" in
974        '')
975          # If this is an auto run, make it official
976          echo "   *** ${COMPFILE} will remain for your consideration"
977          ;;
978        *)
979          diff_loop
980          ;;
981        esac
982        ;;
983    esac # Auto run test
984    continue
985  fi
986
987  case "${STRICT}" in
988  '' | [Nn][Oo])
989    # Compare CVS $Id's first so if the file hasn't been modified
990    # local changes will be ignored.
991    # If the files have the same $Id, delete the one in temproot so the
992    # user will have less to wade through if files are left to merge by hand.
993    #
994    CVSID1=`grep "[$]${CVS_ID_TAG}:" ${DESTDIR}${COMPFILE#.} 2>/dev/null`
995    CVSID2=`grep "[$]${CVS_ID_TAG}:" ${COMPFILE} 2>/dev/null` || CVSID2=none
996
997    case "${CVSID2}" in
998    "${CVSID1}")
999      echo " *** Temp ${COMPFILE} and installed have the same CVS Id, deleting"
1000      rm "${COMPFILE}"
1001      ;;
1002    esac
1003    ;;
1004  esac
1005
1006  # If the file is still here either because the $Ids are different, the
1007  # file doesn't have an $Id, or we're using STRICT mode; look at the diff.
1008  #
1009  if [ -f "${COMPFILE}" ]; then
1010
1011    # Do an absolute diff first to see if the files are actually different.
1012    # If they're not different, delete the one in temproot.
1013    #
1014    if diff -q ${DIFF_OPTIONS} "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" > \
1015      /dev/null 2>&1; then
1016      echo " *** Temp ${COMPFILE} and installed are the same, deleting"
1017      rm "${COMPFILE}"
1018    else
1019      # Ok, the files are different, so show the user where they differ.
1020      # Use user's choice of diff methods; and user's pager if they have one.
1021      # Use more if not.
1022      # Use unified diffs by default.  Context diffs give me a headache. :)
1023      #
1024      case "${AUTO_RUN}" in
1025      '')
1026        # prompt user to install/delete/merge changes
1027        diff_loop
1028        ;;
1029      *)
1030        # If this is an auto run, make it official
1031        echo "   *** ${COMPFILE} will remain for your consideration"
1032        ;;
1033      esac # Auto run test
1034    fi # Yes, the files are different
1035  fi # Yes, the file still remains to be checked
1036done # This is for the for way up there at the beginning of the comparison
1037
1038echo ''
1039echo "*** Comparison complete"
1040
1041if [ -f "${MTREENEW}" ]; then
1042  echo "*** Saving mtree database for future upgrades"
1043  test -e "${DESTDIR}${MTREEFILE}" && unlink ${DESTDIR}${MTREEFILE}
1044  mv ${MTREENEW} ${DESTDIR}${MTREEFILE}
1045fi
1046
1047echo ''
1048
1049TEST_FOR_FILES=`find ${TEMPROOT} -type f -size +0 2>/dev/null`
1050if [ -n "${TEST_FOR_FILES}" ]; then
1051  echo "*** Files that remain for you to merge by hand:"
1052  find "${TEMPROOT}" -type f -size +0
1053  echo ''
1054fi
1055
1056case "${AUTO_RUN}" in
1057'')
1058  echo -n "Do you wish to delete what is left of ${TEMPROOT}? [no] "
1059  read DEL_TEMPROOT
1060
1061  case "${DEL_TEMPROOT}" in
1062  [yY]*)
1063    if delete_temproot; then
1064      echo " *** ${TEMPROOT} has been deleted"
1065    else
1066      echo " *** Unable to delete ${TEMPROOT}"
1067    fi
1068    ;;
1069  *)
1070    echo " *** ${TEMPROOT} will remain"
1071    ;;
1072  esac
1073  ;;
1074*) ;;
1075esac
1076
1077case "${AUTO_INSTALLED_FILES}" in
1078'') ;;
1079*)
1080  case "${AUTO_RUN}" in
1081  '')
1082    (
1083      echo ''
1084      echo '*** You chose the automatic install option for files that did not'
1085      echo '    exist on your system.  The following were installed for you:'
1086      echo "${AUTO_INSTALLED_FILES}"
1087    ) | ${PAGER}
1088    ;;
1089  *)
1090    echo ''
1091    echo '*** You chose the automatic install option for files that did not'
1092    echo '    exist on your system.  The following were installed for you:'
1093    echo "${AUTO_INSTALLED_FILES}"
1094    ;;
1095  esac
1096  ;;
1097esac
1098
1099case "${AUTO_UPGRADED_FILES}" in
1100'') ;;
1101*)
1102  case "${AUTO_RUN}" in
1103  '')
1104    (
1105      echo ''
1106      echo '*** You chose the automatic upgrade option for files that you did'
1107      echo '    not alter on your system.  The following were upgraded for you:'
1108      echo "${AUTO_UPGRADED_FILES}"
1109    ) | ${PAGER}
1110    ;;
1111  *)
1112    echo ''
1113    echo '*** You chose the automatic upgrade option for files that you did'
1114    echo '    not alter on your system.  The following were upgraded for you:'
1115    echo "${AUTO_UPGRADED_FILES}"
1116    ;;
1117  esac
1118  ;;
1119esac
1120
1121run_it_now () {
1122  case "${AUTO_RUN}" in
1123  '')
1124    unset YES_OR_NO
1125    echo ''
1126    echo -n '    Would you like to run it now? y or n [n] '
1127    read YES_OR_NO
1128
1129    case "${YES_OR_NO}" in
1130    y)
1131      echo "    Running ${1}"
1132      echo ''
1133      eval "${1}"
1134      ;;
1135    ''|n)
1136      echo ''
1137      echo "       *** Cancelled"
1138      echo ''
1139      echo "    Make sure to run ${1} yourself"
1140      ;;
1141    *)
1142      echo ''
1143      echo "       *** Sorry, I do not understand your answer (${YES_OR_NO})"
1144      echo ''
1145      echo "    Make sure to run ${1} yourself"
1146    esac
1147    ;;
1148  *) ;;
1149  esac
1150}
1151
1152case "${NEED_NEWALIASES}" in
1153'') ;;
1154*)
1155  echo ''
1156  if [ -n "${DESTDIR}" ]; then
1157    echo "*** You installed a new aliases file into ${DESTDIR}/etc/mail, but"
1158    echo "    the newaliases command is limited to the directories configured"
1159    echo "    in sendmail.cf.  Make sure to create your aliases database by"
1160    echo "    hand when your sendmail configuration is done."
1161  else
1162    echo "*** You installed a new aliases file, so make sure that you run"
1163    echo "    '/usr/bin/newaliases' to rebuild your aliases database"
1164    run_it_now '/usr/bin/newaliases'
1165  fi
1166  ;;
1167esac
1168
1169case "${NEED_CAP_MKDB}" in
1170'') ;;
1171*)
1172  echo ''
1173  echo "*** You installed a login.conf file, so make sure that you run"
1174  echo "    '/usr/bin/cap_mkdb ${DESTDIR}/etc/login.conf'"
1175  echo "     to rebuild your login.conf database"
1176  run_it_now "/usr/bin/cap_mkdb ${DESTDIR}/etc/login.conf"
1177  ;;
1178esac
1179
1180case "${NEED_PWD_MKDB}" in
1181'') ;;
1182*)
1183  echo ''
1184  echo "*** You installed a new master.passwd file, so make sure that you run"
1185  if [ -n "${DESTDIR}" ]; then
1186    echo "    '/usr/sbin/pwd_mkdb -d ${DESTDIR}/etc -p ${DESTDIR}/etc/master.passwd'"
1187    echo "    to rebuild your password files"
1188    run_it_now "/usr/sbin/pwd_mkdb -d ${DESTDIR}/etc -p ${DESTDIR}/etc/master.passwd"
1189  else
1190    echo "    '/usr/sbin/pwd_mkdb -p /etc/master.passwd'"
1191    echo "     to rebuild your password files"
1192    run_it_now '/usr/sbin/pwd_mkdb -p /etc/master.passwd'
1193  fi
1194  ;;
1195esac
1196
1197echo ''
1198
1199if [ -r "${MM_EXIT_SCRIPT}" ]; then
1200  . "${MM_EXIT_SCRIPT}"
1201fi
1202
1203case "${COMP_CONFS}" in
1204'') ;;
1205*)
1206  . ${DESTDIR}/etc/defaults/rc.conf
1207
1208  (echo ''
1209  echo "*** Comparing conf files: ${rc_conf_files}"
1210
1211  for CONF_FILE in ${rc_conf_files}; do
1212    if [ -r "${DESTDIR}${CONF_FILE}" ]; then
1213      echo ''
1214      echo "*** From ${DESTDIR}${CONF_FILE}"
1215      echo "*** From ${DESTDIR}/etc/defaults/rc.conf"
1216
1217      for RC_CONF_VAR in `grep -i ^[a-z] ${DESTDIR}${CONF_FILE} |
1218        cut -d '=' -f 1`; do
1219        echo ''
1220        grep -w ^${RC_CONF_VAR} ${DESTDIR}${CONF_FILE}
1221        grep -w ^${RC_CONF_VAR} ${DESTDIR}/etc/defaults/rc.conf ||
1222          echo ' * No default variable with this name'
1223      done
1224    fi
1225  done) | ${PAGER}
1226  echo ''
1227  ;;
1228esac
1229
1230case "${PRE_WORLD}" in
1231'') ;;
1232*)
1233  MAKE_CONF="${SOURCEDIR}/share/examples/etc/make.conf"
1234
1235  (echo ''
1236  echo '*** Comparing make variables'
1237  echo ''
1238  echo "*** From ${DESTDIR}/etc/make.conf"
1239  echo "*** From ${MAKE_CONF}"
1240
1241  for MAKE_VAR in `grep -i ^[a-z] ${DESTDIR}/etc/make.conf | cut -d '=' -f 1`; do
1242    echo ''
1243    grep -w ^${MAKE_VAR} ${DESTDIR}/etc/make.conf
1244    grep -w ^#${MAKE_VAR} ${MAKE_CONF} ||
1245      echo ' * No example variable with this name'
1246  done) | ${PAGER}
1247  ;;
1248esac
1249
1250exit 0
1251
1252