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