#!/bin/bash
#
# save-config
#
# --------------------------------------------------------------------------
# Copyright notice
# --------------------------------------------------------------------------
# Copyright: Rene Mayrhofer, Mar. 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'.
# --------------------------------------------------------------------------
#

# do not edit below this line
# ------------------------------------------------------------

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

umask 077
set -e

beforesave() {
  return 0
}

aftersave() {
  if [ "$PRINT" != "no" ]; then
    if [ $1 -eq 0 ]; then
      # Ok, we tried it X times but the admin has still not supplied a disk for
      # saving the config. Therefore he probably wants to discard the config.
      # Do nothing.
      echo "Warning: The configuration will be lost."
      beepconsole -f 1000 -l 750
    else
      beepconsole -f 750 -l 200
    fi
  fi
}

try_mount_configdisk() {
  rm -f /tmp/mount-errors > /dev/null
  # try to create the mount-errors file and ignore its use if it can not be created
  if touch /tmp/mount-errors; then
    outstream=/tmp/mount-errors
    errstream=/tmp/mount-errors
  else
    echo "Unable to create log file on /tmp - printing error messages directly."
    outstream=/dev/fd/0
    errstream=/dev/fd/2
  fi
  echo "0 0 0 0" > /proc/sys/kernel/printk
  for fs in $2; do
    if mount -t $fs $1 $save_mntpoint >>$outstream 2>>$errstream; then
      echo "$old_printk" > /proc/sys/kernel/printk
      return 0
    fi
  done
  echo "$old_printk" > /proc/sys/kernel/printk
  return 1
}

print_mount_error() {
  if [ -r /tmp/mount-errors ]; then
    echo "Unable to mount $1 - error was:"
    cat /tmp/mount-errors
  fi
}

format_configdisk() {
  echo "0 0 0 0" > /proc/sys/kernel/printk
  if [ "$SAVE_FILESYSTEM" = "vfat" ]; then
    mkdosfs -c -n CONFIG -m $save_txtfile $1 >/dev/null 2>/dev/null
    ret=$?
  else
    mke2fs -c -L CONFIG -q  -r 1 $1 >/dev/null 2>/dev/null
    ret=$?
  fi
  echo "$old_printk" > /proc/sys/kernel/printk

  if [ $ret -eq 0 ]; then
    if mount -t $SAVE_FILESYSTEM $1 $save_mntpoint >/dev/null 2>/dev/null; then
      # successfully created file system - now create the files
      echo "The disk has been successfully formatted. Now copying configuration."
      create_configdisk $save_mntpoint $save_etcimage
      umount $save_mntpoint
      return 0
    fi
  else
    # else: unable to format - probably no disk has been inserted
    echo "Unable to format the disk. Please insert (another) one."
  fi
  return 1
}

# this method takes 2 parameters: the path to save to and the name of the
# saved etc image
create_configdisk() {
  save-etc /etc $2
  cp $save_txtfile $1
  gibraltar_version=`cat /system/etc-static/gibraltar/version`
  echo -n "This disk has been created by Gibraltar version $gibraltar_version on:" \
    > $1/time.txt
  date >> $1/time.txt
}

# all save_config_* methods return 0 on success and 1 if saving was not possible

save_config_floppy() {
  local found_disk=0
  local try=0
  local formatit=0
  local found_disk=0
  local mounted=0

  if ! grep -q "fd" /proc/devices; then
    modprobe floppy
  fi
  
  # many newer systems do not even have a floppy drive
  if [ ! -e "$SAVE_TARGET_FLOPPY" ]; then
    echo "No floppy drive detected."
    # could not save via this method, return with an error
    return 1
  fi

  # also be sure that the floppy disk is not mounted
  umount "$SAVE_TARGET_FLOPPY" >/dev/null 2>/dev/null || true

  echo "Trying to save configuration to floppy disk."
  while true; do
    formatit=0
    # try to mount the disk
    if try_mount_configdisk "$SAVE_TARGET_FLOPPY" "$save_filesystems_floppy"; then
      # check if the disk is write-protected
      #state=`expr "\`mount | grep $floppy\`" : ".*\(r[ow]\).*"`
      state=`cat /proc/mounts | grep $save_mntpoint | cut -d' ' -f4,4`
      if [ $state = "ro" ]; then
        echo "The inserted disk is write-protected. Please insert another one."
      elif [ -w $save_etcimage ]; then
        # perfect, the config disk is still inserted and writable - just
        # overwrite the file
        echo "Found old writeable configuration disk. Overwriting it now."
        create_configdisk $save_mntpoint $save_etcimage
        umount $save_mntpoint
        found_disk=1
        break
      elif ! ls -a $save_mntpoint/* >/dev/null 2>/dev/null; then
        echo "Found empty disk in drive. Using it."
        create_configdisk $save_mntpoint $save_etcimage
        umount $save_mntpoint
        found_disk=1
        break
      else
        # else: if the disk is mountable, but the file does not exist, it
        # could still be the boot disk - do not overwrite it
        echo "The inserted disk is not an old configuration disk and is not"\
             "empty."
        echo "Please insert an old configuration disk or an empty disk."
      fi
      umount $save_mntpoint
    else
      # disk is not mountable - but maybe there is an unformated disk in the
      # drive - just try to format it
      if [ "$SAVE_AUTOFORMAT" = "YES" ] || [ "$SAVE_AUTOFORMAT" = "yes" ]; then
        echo "No already formatted disk found in drive. Trying to format it."
        formatit=1
      else
        echo "Not formatting floppy disk automatically. To do this, set "
	echo "SAVE_AUTOFORMAT=YES in /etc/gibraltar/config."
	echo
	print_mount_error floppy
      fi
    fi

    if [ $formatit -eq 1 ]; then
      if format_configdisk "$SAVE_TARGET_FLOPPY"; then
        found_disk=1
        break
      fi
    fi

    # we don't have to retry if this isn't the automatic saving during
    # shutdown or reboot - if save-config is called "manually", just try once
    if [ $try -ge $MAX_DISK_TRIES ] || [ $runlev -ne 0 -a $runlev -ne 6 ]; then
      break;
    fi

    echo "If you want to pause the save procedure now, press any key."
    echo ""
    beepconsole -f 500 -l 200 -r 2 -d 200
    #sleep 30s
    if read -n 1 -r -s -t $DISK_TIMEOUT; then
      # the user wants to pause
      echo "Save procedure paused. Press enter to continue."
      echo ""
      read -s
    fi

    try=`expr $try + 1`
  done
  aftersave $found_disk

  if [ $found_disk -eq 1 ]; then
    return 0
  else
    return 1
  fi
}

save_config_part() {
  local saved=0

  # check if the partition is already mounted
  if [ ! -e "$SAVE_TARGET_HDD" ]; then
    echo "Error: $SAVE_TARGET_HDD does not exist !"
    return 1
  fi
  local target_mntpath=`get_mount_path "$SAVE_TARGET_HDD"`
  if [ -n "$target_mntpath" ]; then
    # already mounted, just use it
    if [ "$PRINT" != "no" ]; then
      echo "Saving configuration data to $SAVE_TARGET_HDD."
    fi
    # but need to check if it is mounted r/w
    local remount=0
    if ! grep "$target_mntpath" /proc/mounts | awk '{print $4}' \
         egrep -q "(.*,)?rw(,.*)?"; then
      remount=1
      if [ "$PRINT" != "no" ]; then
        echo "Remounting $target_mntpath read/write to save configuration data."
      fi
      mount -o rw "$target_mntpath"
    fi
    create_configdisk $target_mntpath $target_mntpath/$save_etcimage_name
    if [ $remount -eq 1 ]; then
      mount -o ro "$target_mntpath"
    fi
    saved=1
  else
    # not mounted, mount it
    if [ "$PRINT" = "no" ]; then
      echo "Trying to save configuration to partition $SAVE_TARGET_HDD."
    fi
    # try to mount the partition
    if try_mount_configdisk "$SAVE_TARGET_HDD" "$save_filesystems_hdd"; then
      if [ "$PRINT" = "no" ]; then
        echo "Mounted the partition, now saving configuration data."
      fi
      create_configdisk $save_mntpoint $save_etcimage
      umount $save_mntpoint
      saved=1
    else
      # disk is not mountable - but maybe there is an unformated disk in the
      # drive - just try to format it
      #if[ "$SAVE_AUTOFORMAT" = "YES" ] || [ "$SAVE_AUTOFORMAT" = "yes" ]; then
      #  echo "Unable to mount partition. Trying to format it."
      #  if format_configdisk "$SAVE_TARGET_HDD"; then
      #    saved=1
      #  fi
      #else
        print_mount_error partition
      #fi
    fi
  fi

  aftersave $saved

  if [ $saved -eq 1 ]; then
    return 0
  else
    return 1
  fi
}

save_config_usb() {
  local saved=0

  echo "Searching for USB mass storage devices"
  # don't load (and more importantly wait !) if USB support is already loaded
  if ! grep -q "usb-storage" /proc/modules ||
     ! grep -q "sd_mod" /proc/modules; then
    install_usb_storage_mods
    install_usb_controller_mods

    # this seems to be necessary for the kernel to get a list of devices
    sleep 1s
  fi
  
  usb_storage_partitions=`get_usb_storage_partitions`

  PRINT=no

  if [ -n "$usb_storage_partitions" ]; then
    for part in $usb_storage_partitions; do
      echo "    trying $part:"
      SAVE_TARGET_HDD=/dev/$part
      if save_config_part; then
        echo "successful"
	saved=1
	break
      else
        echo "unsuccessful"
      fi
    done
  else
    echo "No device found"
    return 1
  fi

  aftersave $saved

  if [ $saved -eq 1 ]; then
    return 0
  else
    return 1
  fi
}

save_config_ssh() {
  local saved=0

  # first try to create the temporary file - /tmp might not be writable now
  if touch /tmp/etc.tgz; then
    echo "Saving configuration to a temporary location."
    save-etc /etc /tmp/etc.tgz
    echo -n "Trying to copy the configuration via scp..."
    if /usr/bin/scp /tmp/etc.tgz "$SAVE_TARGET_SSH"; then
      echo "done."
      saved=1
    else
      echo "failed."
    fi
    rm /tmp/etc.tgz

    aftersave $saved
  else
    echo "Error: Unable to create temporary file for saving - /tmp is not writable."
    echo "Aborting."
  fi

  if [ $saved -eq 1 ]; then
    return 0
  else
    return 1
  fi
}

save_config_email() {
  local saved=0
  UUENCODE="/usr/bin/uuencode"
  GIBRALTAR_VERSION=`cat /system/etc-static/gibraltar/version`
  HOSTNAME=`hostname`
  DATE=`date`

  # first try to create the temporary file - /tmp might not be writable now
  if touch /tmp/etc.tgz; then
    echo "Saving configuration to a temporary location."
    save-etc /etc /tmp/etc.tgz
    if [ -e /tmp/mail-etc.tgz ]; then
      if ! rm -f /tmp/mail-etc.tgz; then
        echo "Error: could not remove /tmp/mail-etc.tgz - some attack may be happening. Exiting now."
        exit 1
      fi
    fi
    echo -n "Preparing email to send ... "
    echo "To: $SAVE_TARGET_EMAIL" >> /tmp/mail-etc.txt
    echo "Subject: Configuration of '$HOSTNAME' ($DATE)" >> /tmp/mail-etc.txt
    echo "" >> /tmp/mail-etc.txt
    echo "Created: $DATE" >> /tmp/mail-etc.txt
    echo "Host: $HOSTNAME" >> /tmp/mail-etc.txt
    echo "Gibraltar version: $GIBRALTAR_VERSION" >> /tmp/mail-etc.txt
    echo "" >> /tmp/mail-etc.txt
    $UUENCODE /tmp/etc.tgz etc.tgz >> /tmp/mail-etc.txt
    echo -n "sending ... "
    if /usr/lib/sendmail -t < /tmp/mail-etc.txt; then
      echo "done."
      saved=1
    else
      echo "failed."
    fi
    rm /tmp/etc.tgz
    rm /tmp/mail-etc.txt

    aftersave $saved
  else
    echo "Error: Unable to create temporary file for saving - /tmp is not writable."
    echo "Aborting."
  fi

  if [ $saved -eq 1 ]; then
    return 0
  else
    return 1
  fi
}

save_config_source() {
  if [ ! -f /etc/gibraltar/config_source ]; then
    echo "Warning: /etc/gibraltar/config_source can not be found. This configuration"
    echo "does not seem to have been loaded from somewhere, no source has been stored."

    PRINT="no"
    echo "Trying to save to USB mass storage device."
    if save_config_usb; then
      PRINT="yes"
      aftersave 1
      return 0
    fi

    echo "Trying to save to floppy disk."
    if save_config_floppy; then
      PRINT="yes"
      aftersave 1
      return 0
    fi

    PRINT="yes"
    aftersave 0
    return 1
  fi

  source=`cat /etc/gibraltar/config_source`

  if [ ! -e $source ]; then
    echo "Error: $source does not exist, the device may have been removed."
    echo "Please specify another save target."
    return 1
  fi

  if expr "$source" : "/dev/floppy/" >/dev/null; then
    SAVE_TARGET_FLOPPY=$source
    save_config_floppy
  elif expr "$source" : "/dev/sd" >/dev/null ||
       expr "$source" : "/dev/hd" >/dev/null ||
       expr "$source" : "/dev/scsi/.*/part" >/dev/null ||
       expr "$source" : "/dev/ide/.*/part" >/dev/null; then
    SAVE_TARGET_HDD=$source
    save_config_part
  else
    echo "Error: $source is an unkown device, don't know which target option to choose."
    echo "Please specify another save target."
    return 1
  fi
}

usage() {
  echo "$0 [--target=(source|floppy|usb|hdd|ssh|email) [--to=<target>]]"
  echo "    where <target> is dependent on the target option"
  echo
  echo "    source .... save to the device where it has been loaded from"
  echo "                no \"to\" parameter accepted"
  echo "    floppy .... save to a floppy disk"
  echo "                \"to\" optional, specifies the floppy device (e.g. /dev/fd/0)"
  echo "    usb ....... save to an USB mass storage device"
  echo "                \"to\" optional, specifies the USB device (e.g. /dev/sda1)"
  echo "    hdd ....... save to an harddisk partition"
  echo "                \"to\" optional, specifies the partition (e.g. /dev/hda1)"
  echo "    ssh ....... save to a remote SSH server"
  echo "                \"to\" optional, specifies the location"
  echo "                (e.g. user@host.tld:/configpath/)"
  echo "    email ..... sends the configuration via email as attachement"
  echo "                \"to\" optional, specifies email address"
  echo "                Warning: this target sends the configuration unencryption"
  echo "                over a network!"
  exit 1
}


old_printk=`cat /proc/sys/kernel/printk`

# get the current runlevel (this works even when not started from INIT - i.e.
# when the environment variable $RUNLEVEL is not set)
runlev=`runlevel | cut -d' ' -f2,2`

if [ $runlev -eq 0 -o $runlev -eq 6 ] && [ "$SAVE_SHUTDOWN" = "no" ]; then
  echo "Automatic saving during shutdown disabled in /etc/gibraltar/config - skipping."
  exit 10
fi

# be sure that the mountpoint is available to mount onto it
umount $save_mntpoint >/dev/null 2>/dev/null || true

# check parameters
if [ $# -ge 1 ]; then
  if expr "$1" : "--target=.*" > /dev/null; then
    SAVE_TARGET=`expr "$1" : "--target=\(.*\)"`
  elif [ "$1" = "stop" ]; then
    # simply ignore it - this parameter is set by init when called on shutdown
    true
  else
    usage
  fi

  if [ $# -ge 2 ]; then
    if expr "$2" : "--to=.*" > /dev/null; then
      to=`expr "$2" : "--to=\(.*\)"`
      case $SAVE_TARGET in
        "source")   echo "Error: target \"source\" does not support the option \"--to\""
	            exit 1
		    ;;
        "floppy")   SAVE_TARGET_FLOPPY=$to
                    ;;
	"usb")	    SAVE_TARGET_USB=$to
		    ;;
        "hdd")      SAVE_TARGET_HDD=$to
                    ;;
        "ssh")      SAVE_TARGET_SSH=$to
                    ;;
        "email")    SAVE_TARGET_EMAIL=$to
                    ;;
      esac
    else
      usage
    fi
  fi
fi

# check some parameters
if [ "$SAVE_TARGET" != "source" -a \
     "$SAVE_TARGET" != "floppy" -a \
     "$SAVE_TARGET" != "usb" -a \
     "$SAVE_TARGET" != "hdd" -a \
     "$SAVE_TARGET" != "ssh" -a \
     "$SAVE_TARGET" != "email" ]; then
  echo "Error: unsupported target (you might need to check /etc/gibraltar/config)"
  exit 1
fi
if [ "$SAVE_FILESYSTEM" != "vfat" -a "$SAVE_FILESYSTEM" != "ext3" -a "$SAVE_FILESYSTEM" != "ext2" ]; then
  echo "Error: unsupported filesystem type SAVE_FILESYSTEM in /etc/gibraltar/config"
  exit 1
fi

case $SAVE_TARGET in
  "source")   save_config_source
              ;;
  "floppy")   save_config_floppy
              ;;
  "usb")      save_config_usb
  	      ;;
  "hdd")      save_config_part
              ;;
  "ssh")      save_config_ssh
              ;;
  "email")    save_config_email
              ;;
esac

exit 0
