#!/bin/bash
# Filename:      save-config
# Purpose:       generate grml configuration archive and store it anywhere
# Authors:       grml-team (grml.org), (c) Michael Prokop <mika@grml.org>
# Bug-Reports:   see http://grml.org/bugs/
# License:       This file is licensed under the GPL v2.
################################################################################

# shellcheck source=/dev/null
{
  . /etc/grml/lsb-functions
  . /etc/grml/script-functions
}

# set variables  {{{
  DEBUG=0
  LANG=C
  LC_ALL=C
  [ "$UID" != 0 ] && runas='sudo' # important for /etc

  if [ -d /run/live/overlay/rw ] ; then
    CHANGE_DIR='/run/live/overlay/rw'
  else
    echo "Error: no overlay directories found (expected /run/live/overlay/rw)." >&2
    bailout; exit 1
  fi

  if [ -d /run/live/rootfs ] ; then
    ORIG_DIR="$(find /run/live/rootfs/ -maxdepth 1 -name \*.squashfs | head -1)"
  else
    echo "Error: no rootfs directories found in '/run/live/rootfs'." >&2
    bailout; exit 1
  fi

  check4progs mutt &>/dev/null || echo "Warning, mutt not available for mail handling.">&2
  check4progs tar || { echo "The tar program is required." >&2 ; bailout ; exit 1 ; }

  CONFIG=/etc/grml/saveconfig
  [ -r "$CONFIG" ] && . "$CONFIG"

  PROGRAMNAME=${0##*/}
  HOSTNAME=$(hostname)
  DATE=$(date)
  GRML_VERSION=$(awk '{print $1}' /etc/grml_version 2>/dev/null || echo "not a grml system")

  TMPDIR='/tmp'
  MAILFILE="${TMPDIR}/mail.txt"

  [ -n "$FILELIST" ] || FILELIST=$(mktemp "$TMPDIR/filelist.XXXXXX")
# }}}

# functions {{{
debug(){
  if [ "$DEBUG" -gt 0 ] ; then
    echo "debug: $*"
  fi
}

bailout(){
  rm -f "$FILELIST"
  rm -f "$MAILFILE"
}

trap bailout 1 2 3 15

findchanged() {
  if [ -d "$1" ]; then
    for i in $(cd "$1" || exit 1; find . -type f 2>/dev/null | sed 's,^\./,,g' | grep -v ' ' ); do
      cmp -s "$1/$i" "$2/$i" || echo "$1/$i"
    done
  elif [ -e "$1" ]; then
    cmp -s "$1" "$2" || echo "$1"
  fi
}
# }}}

# usage information {{{
usage()
{
  cat >&2 <<EOF
${GREEN}${PROGRAMNAME} - save configuration of grml system${NORMAL}

${BLUE}Usage:${NORMAL}
  $PROGRAMNAME [-target_options] -{all,home,etc,configdir}

${BLUE}Target options:${NORMAL}
  -ssh user@host:/path/to/file  copy configuration via ssh/scp to remote host
  -mail <recipient>             send configuration via mail
  -file foo_bar_config.tbz      save configuration in specified file

  Notice: if no option is specified the default is assumed:
          create file config.tbz in current directory

${BLUE}Files-to-store options:${NORMAL}
  -home                         store hidden files from \$HOME (\$HOME/.*)
  -grmlhome                     store hidden files from \$HOME (\$HOME/.*) of user grml [use as user root]
  -etc                          store modified files from /etc
  -configdir                    store \$HOME/config
  -all                          store all configuration files (:= -home, -configdir and -etc)

  Notice: it is also possible to use environment variables:
          \$SAVE_HOME, \$SAVE_GRMLHOME, \$SAVE_ETC, \$SAVE_CONFIGDIR and \$SAVE_ALL

${BLUE}Usage examples:${NORMAL}
  $PROGRAMNAME -all                                  => store all configuration files in config.tbz in current dir
  $PROGRAMNAME -home -mail  devnull@grml.org         => store \$HOME/.* in config.tbz and send it via mail
  $PROGRAMNAME -etc  -ssh   devnull@grml.org:/path/  => store /etc in config.tbz and scp it to specified host
  $PROGRAMNAME -all  -file  foo.tbz                  => store all configuration files in foo.tbz
  SAVE_ALL=yes $PROGRAMNAME -file /path/foo.tbz      => store all configuration files in /path/foo.tbz

More information on save-config can be found in the manual page: man save-config

See also: restore-config(1), bootoptions: myconfig=/dev/ice, extract=PATH,
          netconfig=server.tld/path/to/config.tbz

Report bugs, send wishes and feedback to the grml team:
http://grml.org/bugs/ - contact (at) grml.org
EOF
}
# }}}

# what do we want to store? {{{
save_home(){
  debug "save home"
  for i in "$HOME"/.* ; do findchanged "$i" "/etc/skel/$(basename "$i")"; done >> "$FILELIST"
  debug "debug: $FILELIST"
}

save_grmlhome(){
  debug "save grmlhome"
  if [ -d /home/grml/ ] ; then
     for i in /home/grml/.* ; do findchanged "$i" "/etc/skel/$(basename "$i")"; done >> "$FILELIST"
  fi
  debug "debug: $FILELIST"
}

save_etc(){
  debug "save etc"
  if [ -n "$NEWLAYOUT" ] ; then
     $runas find "${CHANGE_DIR}/etc" | sed -e "s#${CHANGE_DIR}## ; /etc$/d" >> "$FILELIST"
  else
     $runas findchanged /etc "${ORIG_DIR}/etc" >> "$FILELIST"
  fi
}

save_configdir(){
  debug "save configdir"
  if [ -d "$HOME/config" ] ; then
     ls "$HOME"/config/* >> "$FILELIST" 2>/dev/null
     ls "$HOME"/config/.* >> "$FILELIST" 2>/dev/null
  fi
}
# }}}

# create configuration file {{{
create_config(){
  if ! [ -r "$FILELIST" ]; then
    echo "Filelist $FILELIST could not be read." >&2
    echo "Error when generating $FILENAME." >&2
    bailout ; exit 1
  else
    # GNU tar sucks so much, really. Avoid the "file changed as we read it":
    tar cf /dev/null /etc
    # now really execute the according tar command:
    if BZIP2=-9 $runas tar -T - -cpPjf "$FILENAME" <"$FILELIST" ; then
      echo "Successfully stored configuration in file $FILENAME"
    else
      echo "Error when generating $FILENAME." >&2
      bailout ; exit 1
    fi
  fi
}
# }}}

# commandline parsing {{{
parse_options()
{
   if [ "$#" -eq 0 ]; then
      usage ; exit
   fi

   FILENAME="config.tbz"  # default filename

   while [ "$#" -gt 0 ]; do
     case "$1" in
       -h|--help)
         usage ; exit ;;
       -file)
         shift
         if [ -z "$1" ]; then
           echo "Error: -file requires an argument" >&2
           exit 1
         fi
         FILENAME="$1" ;;
       -home)
         debug "home is set"
         SAVE_HOME="yes" ;;
       -grmlhome)
         debug "grmlhome is set"
         SAVE_GRMLHOME="yes" ;;
       -etc)
         debug "etc is set"
         SAVE_ETC="yes" ;;
       -configdir)
         debug "configdir is set"
         SAVE_CONFIGDIR="yes" ;;
       -all)
         debug "home, grmlhome, etc and configdir are set"
         SAVE_HOME="yes"
         SAVE_GRMLHOME="yes"
         SAVE_ETC="yes"
         SAVE_CONFIGDIR="yes" ;;
       -ssh)
         shift
         if [ -z "$1" ]; then
           echo "Error: -ssh requires an argument" >&2
           exit 1
         fi
         SSH_TARGET="$1"
         debug "scp $FILENAME $SSH_TARGET" ;;
       -mail)
         shift
         if [ -z "$1" ]; then
           echo "Error: -mail requires an argument" >&2
           exit 1
         fi
         check4progs mutt || { echo "Sorry, mutt not available for sending mail. Exiting." >&2 ; exit 1 ; }
         MAIL_RECIPIENT="$1"
         debug "send mail to $MAIL_RECIPIENT" ;;
       -*)
         echo "Error: Unknown option: $1" >&2
         exit 1 ;;
       *)
         echo "Error: Unexpected argument: $1" >&2
         exit 1 ;;
     esac
     shift
   done

   # Handle SSH and mail after file creation
   if [ -n "$SSH_TARGET" ]; then
     DO_SSH="yes"
   fi
   if [ -n "$MAIL_RECIPIENT" ]; then
     DO_MAIL="yes"
   fi
}
parse_options "$@"
# }}}

# execution wrapper {{{
runit(){
   if [ "$SAVE_HOME" = "yes" ]; then
     debug "running save_home"
     save_home
     SETSAVE=1
   fi
   if [ "$SAVE_GRMLHOME" = "yes" ]; then
     debug "running save_grmlhome"
     save_grmlhome
     SETSAVE=1
   fi
   if [ "$SAVE_ETC" = "yes" ] ; then
     debug "running save_etc"
     save_etc
     SETSAVE=1
   fi
   if [ "$SAVE_CONFIGDIR" = "yes" ] ; then
     debug "running save_configdir"
     save_configdir
     SETSAVE=1
   fi
   if [ -z "$SETSAVE" ] ; then
     echo "Sorry, you did not select any configuration which should be saved. Exiting."
     bailout ; exit 1
   fi
}
# }}}

# now run it
  runit
  create_config

  # Handle SSH and mail after successful file creation
  if [ "$DO_SSH" = "yes" ] && [ -n "$SSH_TARGET" ]; then
    debug "scp $FILENAME $SSH_TARGET"
    scp "$FILENAME" "$SSH_TARGET"
  fi

  if [ "$DO_MAIL" = "yes" ] && [ -n "$MAIL_RECIPIENT" ]; then
    debug "send mail to $MAIL_RECIPIENT"
    echo "Created on $DATE on host $HOSTNAME running grml $GRML_VERSION" > "$MAILFILE"
    mutt -s "configuration of $HOSTNAME ($DATE)" -a "$FILENAME" "$MAIL_RECIPIENT" < "$MAILFILE"
  fi

  bailout

## END OF FILE #################################################################
# vim:foldmethod=marker
