#!/bin/sh
#
# --------------------------------------------------------------------------
# Copyright notice
# --------------------------------------------------------------------------
# Copyright: Rene Mayrhofer, Nov. 2000
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.  If not, write to
# the Free Software Foundation, 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
# On Debian GNU/Linux systems, the complete text of the GNU General
# Public License can be found in `/usr/share/common-licenses/GPL'.
# --------------------------------------------------------------------------
#
# This script tries to update the config files stored on an existing config
# disk with the default config files from the Gibraltar CD-ROM. Only files
# that have not been changed by the user (i.e. are still the same as
# distributed on the older, previous Gibraltar CD-ROM as defaults) can be
# replaced by the new default files automatically, changed files will be
# reported. New files that were not present in the old default configuration
# will be taken from the new default.
# File that are neither in the old nor the new default configuration image are
# simply ignored (these are all files that the user has created).
#
# When this script is called it is expected that the old configuration image
# is already mounted and the new packed default image is available. Both
# should be given as parameters.

# do not edit below this line
# =================================================

if [ -n "$TESTING_UPDATE" ] && [ $TESTING_UPDATE -eq 1 ]; then
  force=1
else
  force=0
fi

if [ $# -ne 3 ] ; then
  echo "Usage: $0 <mountpoint of old image> <new default image name> <logfile>"
  exit 1
fi

. /usr/lib/gibraltar-bootsupport/common-definitions.sh

umask 077
set -e

mountpoint=$1
image=$2
logfile=`pwd`/$3
new_mountpoint=$temp_mntpoint

outmsg() {
  echo "$*" | tee -a $logfile
}

if [ ! -d $mountpoint ]; then
  echo "Error: $mountpoint is not a directory."
  exit 1
fi

if [ ! -f $mountpoint/gibraltar/version -a ! -f $mountpoint/gibraltar_version ]; then
  echo "Error: neither $mountpoint/gibraltar/version nor $mountpoint/gibraltar_version exist."
  exit 1
fi

if [ ! -f $image ]; then
  echo "Error: $image does not exist or is not a regular file."
  exit 1
fi

# first check if the Gibraltar version currently running is newer than the
# version the configuration disk has been created with
if [ -f $mountpoint/gibraltar/version ]; then
  old_version=`cat $mountpoint/gibraltar/version`
else
  old_version=`cat $mountpoint/gibraltar_version`
fi
new_version=`cat /system/etc-static/gibraltar/version`
if [ $force -ne 1 ] && [ `expr "$old_version" \< "$new_version"` -ne 1 ]; then
  echo "Error: the old version '$old_version' is newer than the currently " \
       "running version '$new_version'."
  exit 2
fi

# now mount the new default config image to get to the files
if [ ! -d $new_mountpoint ] || mount | grep $new_mountpoint >/dev/null; then
  echo "Error: can not mount on $new_mountpoint."
  exit 2
fi
# use the default size of 32 MB for unpacking the default config (just to be sure)
if [ -z "$TESTING_UPDATE" ] || [ $TESTING_UPDATE -ne 1 ]; then
  restore-etc $image $new_mountpoint 32m
else
  /gibraltar/sbin/restore-etc $image $new_mountpoint 32m
fi

# just a sanity check
new_image_version=`cat $new_mountpoint/gibraltar/version`
if [ $force -ne 1 ] && [ "$new_image_version" != "$new_version" ]; then
  echo "Error: the default image version does not match the version of the " \
       "currently running system."
  echo "This is terribly wrong. Exiting now."
  umount $new_mountpoint
  exit 2
fi

# first remount the new image with a larger maximum so that the update can run
if [ -z "$TESTING_UPDATE" ] || [ $TESTING_UPDATE -ne 1 ]; then
  remount_tmpfs 32m $mountpoint
fi

echo "Updating the old configuration files with new default values ... "
# write a header in the logfile (and a newline in front if the file exists)
if [ -e $logfile ]; then
  echo >> $logfile
fi
echo "Automatic configuration update from version $old_version to $new_version" \
     >> $logfile
echo "started at `date`" >> $logfile
olddir=`pwd`
cd $mountpoint

# in this version, it is better to get error messages than to halt the update
set +e

# remember if the currently mounted (old) image is in unconfigured state
if [ -e ./unconfigured ]; then
  old_unconfigured=1
else
  old_unconfigured=0
fi

# now for every file in the new default configuration.....
cat $new_mountpoint/etc-defaults.list |
  while read depth filename size uid gid perms linktarget; do
  # skip the file if it is in our list of excludes (typically files which are
  # created or updated by some other software)
  if ( [ -e "$new_mountpoint/gibraltar/update-excludes" ] &&
       echo "$filename" | grep -q --file="$new_mountpoint/gibraltar/update-excludes" ) ||
     ( [ -e "/system/etc-static/gibraltar/update-excludes" ] &&
       echo "$filename" | grep -q --file="/system/etc-static/gibraltar/update-excludes" ); then
    continue
  fi

    # first a sanity check - does the listed new file really exist in the new
	# default config ?
    if [ ! -e $new_mountpoint/$filename -a ! -L $new_mountpoint/$filename ]; then
	  outmsg "    $filename is listed in the new etc-defaults.list, but does not exist in the new default config"
	  continue
	fi

    if [ ! -e $filename -a ! -L $filename ]; then
      # if the file is new (it does not exist in the old image), simply copy it
      # but we may have to create the directory for the new file first
      directory=`dirname $filename`
      if [ ! -d $directory ]; then
        if [ ! -L $directory ]; then
          # OK, this is really a new directory
          outmsg "    Creating new directory $directory"
          mkdir -p $directory
        else
          # this is a symbolic link to another (most probably read-only)
          # directory, therefore remove the link and create the directory
          outmsg "    Removing old symbolic link $directory and creating a "\
                 "subdir instead"
          rm $directory
          mkdir -p $directory
        fi
      fi
      outmsg "    Creating file $filename"
      cp -a $new_mountpoint/$filename $filename
    elif [ -f $filename -a ! -L $filename ]; then
      # if the file already exists, check if it has changed in any way
      # first check the content, if it is a regular file
      new_md5sum=`cat $new_mountpoint/etc-defaults.md5sums | grep ".* $filename\$" | cut -d' ' -f1,1`
      cur_md5sum=`md5sum $filename | cut -d' ' -f1,1`
      if [ "$new_md5sum" != "$cur_md5sum" ]; then
        # the content of the file has changed - now check if it is still the
        # old default configuration
        old_md5sum=`cat etc-defaults.md5sums | grep ".* $filename\$" | cut -d' ' -f1,1`
        if [ "$old_md5sum" = "$cur_md5sum" -a ! -L $filename ]; then
          # yes, it is still the old default - the user has not changed
          # anything in this file
          # therefore we can assume that the user was happy with the
          # default and will also be happy with the new defaults - simply
          # overwrite the file
          outmsg "    Updating file $filename"
          cp -a $new_mountpoint/$filename $filename
        elif [ "$old_md5sum" != "$new_md5sum" ]; then
          # the user has changed the file and its default contents have also
          # changed - there is nothing we can do about it
          # print a message and leave the file alone - manual work is needed
          outmsg "    Not updating changed file $filename, please update manually"
          cp -a $new_mountpoint/$filename $filename-${new_version}
       #else
         # the user has changed the file, but its default did not change -
	 # simply leave it as it is
        fi
      #else
        # the file content is unchanged - ignore it
      fi
    fi

    # now check the other attributes (uid, gid, perms, link target)
    new_fileattr="$depth $filename $size $uid $gid $perms $linktarget"
    cur_fileattr=`find . -regex "$filename" -printf '%d %p %s %U %G %m "%l"\n'`
    if [ "$new_fileattr" != "$cur_fileattr" ]; then
      # the attributes have changed - check if they conform to the default
      # configuration
      old_fileattr=`cat etc-defaults.list | grep "\w\+ $filename \w\+ \w\+ \w\+ \w\+ \".*\""`
       if [ -n "$old_fileattr" ]; then
        set $old_fileattr
        old_uid=$4
        old_gid=$5
        old_perms=$6
        old_linktarget=$7
        realtarget=`echo $linktarget | tr -d '"'`
        old_realtarget=`echo $old_linktarget | tr -d '"'`
        if [ "$old_fileattr" = "$cur_fileattr" ]; then
          # still the old default - set the file attributes to the new default
          if [ "$old_uid" != "$uid" ]; then
            outmsg "    Updating owner of $filename to $uid"
            chown $uid $filename
          fi
          if [ "$old_gid" != "$gid" ]; then
            outmsg "    Updating group of $filename to $gid"
            chgrp $gid $filename
          fi
          if [ "$old_perms" != "$perms" ]; then
            outmsg "    Updating permissions of $filename to $perms"
            chmod $perms $filename
          fi
          if [ "$old_linktarget" != "$linktarget" -a -L $filename -a \
               -n "$realtarget" -a -n "$old_realtarget" ]; then
            outmsg "    Updating link target of $filename to $realtarget"
            ln -sf $realtarget $filename
          fi
        elif [ "$old_fileattr" != "$new_fileattr" ]; then
          # changed by the user - just report what has been changed and let
          # the user decide
          if [ "$old_uid" != "$uid" ]; then
            outmsg "    Not updating owner of $filename from $old_uid to $uid"
          fi
          if [ "$old_gid" != "$gid" ]; then
            outmsg "    Not updating group of $filename from $old_gid to $gid"
          fi
          if [ "$old_perms" != "$perms" ]; then
            outmsg "    Not updating permissions of $filename from $old_perms to $perms"
          fi
          if [ "$old_linktarget" != "$linktarget" ]; then
            outmsg "    Not updating link target of $filename from $old_linktarget to $linktarget"
          fi
        #else
	  # the default attributes haven't changed, so just ignore that the user has updated them
        fi
	  #else
	    # old file attributes unavailable - don't know what to do in that case
	  fi
    #else
      # the file attributes are unchanged - ignore them
    fi
  done

# and for every file in the current configuration.....
deleted_dir=/etc/deleted_files
if [ -e $deleted_dir ] && [ ! -d $deleted_dir ]; then
  rm $deleted_dir
fi
mkdir -p $deleted_dir
cat etc-defaults.list |
  while read depth filename size uid gid perms linktarget; do
    # also skip the excluded files here
    if ( [ -e "$new_mountpoint/gibraltar/update-excludes" ] &&
         echo "$filename" | grep -q --file="$new_mountpoint/gibraltar/update-excludes" ) ||
       ( [ -e "/system/etc-static/gibraltar/update-excludes" ] &&
         echo "$filename" | grep -q --file="/system/etc-static/gibraltar/update-excludes" ); then
      continue
    fi

    if ! grep "$filename" $new_mountpoint/etc-defaults.list >/dev/null &&
       [ -f $filename ]; then
      # the file has been deleted in the new default configuration, but is
      # present now
      cur_md5sum=`md5sum $filename | cut -d' ' -f1,1`
      old_md5sum=`cat etc-defaults.md5sums | grep ".* $filename\$" | cut -d' ' -f1,1`
      if [ "$cur_md5sum" = "$old_md5sum" ]; then
	# the file was not changed by the user - but we will not delete it
	# immediately, but rather move it to a temporary location from where
	# it can be deleted by the user afterwards
	outmsg "    Moving $filename to $deleted_dir for deletion"
	mv $filename $deleted_dir
      else
	# the file was changed by the user - leave it alone
	outmsg "    Not deleting changed file $filename, please delete manually"
      fi
    fi
  done

# now copy the information about the new default values to the current image
# (but first copy the old information to deleted_files, just in case they
# are needed before the update procedure is really "committed" by deleting the
# directory)
mv etc-defaults.* $deleted_dir
cp -a $new_mountpoint/etc-defaults.* .
# the same for gibraltar/version
if [ -f gibraltar/version ]; then
  mv gibraltar/version $deleted_dir
else
  mv gibraltar_version $deleted_dir
fi
cp -a $new_mountpoint/gibraltar/version gibraltar/

# and this isn't unconfigured anymore if the old image was already configured
if [ -e ./unconfigured ] && [ $old_unconfigured -eq 0 ]; then
  rm ./unconfigured
fi

cd $olddir

# remount the config disk back with its new size
. /usr/lib/gibraltar-bootsupport/common-definitions.sh
if [ -z $TESTING_UPDATE ] || [ $TESTING_UPDATE -ne 1 ]; then
  remount_tmpfs $ETCDISK_SIZE $mountpoint
fi

echo "ended at `date`" >> $logfile
echo "Automatic updated completed. The update process has been logged to $logfile"

# cleanup
umount $new_mountpoint

exit 0
