#!/bin/bash
############################################################
#                                                          #
# This code is written for Lunar Linux, see                #
# http://lunar-linux.org                                   #
#                                                          #
############################################################
#                                                          #
# lrm is for removing installed packages                   # 
#                                                          #
# 20020610                                                 #
#                                                          #
############################################################
#                                                          #
# Copyrighted Kagan Kongar 2002 under GPLv2                #
#                                                          #
# Copyrighted Auke Kok 2004 under GPLv2                    #
#                                                          #
############################################################

help() {
cat << END
usage: lrm [options] [module ...]
   -d | --debug         Enables debug messages
   -D | --downgrade [module] [version] downgrades a module
   -h | --help          Displays this help text
   -k | --keepconfig    remove module(s) but keep dependencies and config
   -n | --nosustain     removes module(s) even if they are sustained
   -u | --upgrade       remove the module but do not run scripts etc.
   -v | --verbose       Increases the level of message output
   -p | --purge         Delete all modules that depend on the module(s)
                          being removed as well
   
lrm is a script for removing installed modules.
   If invoked without options, lrm will remove all installed files 
belonging to the module(s) and remove the module(s) from the list
of installed modules. 
   Downgrading a module means removing the current installed version
of the module and installing a specific version; given that the 
specific version is already in $INSTALL_CACHE/.
   Some modules (such as bash and gcc)  are marked "non-removable" by the 
system. In order to remove "sustained" modules, use the nosustain option.
END
exit 1
}


handle_config_files() {
  debug_msg "handle_config_files ($@)"
  if dirname "$1" | grep -q "^/etc" ; then
    # we can safely delete symlinks without md5sum checks
    if [ -L "$1" ] ; then
      return 0
    fi

    debug_msg "considering config file \"$1\""

    TARGET_MD5=$(md5sum "$1" | cut -d " " -f 1-1)
    OLD_MD5=$(grep -w "$1\$" "$MD5_LOG" | cut -d " " -f 1-1)

    if [ -z "$TARGET_MD5" ] ; then
      verbose_msg "Skipping removal of \"$1\" due to problem with md5sum"
      return 1
    fi

    if [ -z "$OLD_MD5" ] ; then
      verbose_msg "Skipping removal of \"$1\" due to missing original md5sum"
      return 1
    fi

    if [ "$TARGET_MD5" == "$OLD_MD5" ] ; then
      debug_msg "removing \"$1\""
      return 0
    fi

    if [ "$TARGET_MD5" != "$OLD_MD5" ] ; then
      verbose_msg "Skipping removal of \"$1\" due to md5sum mismatch"
      if [ "$PRESERVE" == "on" ] ; then
         debug_msg "PRESERVE=on, keeping \"$1\""
         return 1
      else
         verbose_msg "PRESERVE=off, archiving \"$1\""
         mv $1 $1.$(date +%Y%m%d%H%M)
         return 1
      fi
    fi
  fi
  return 0
}


remove_something() {
  debug_msg "remove_something ($@)"
  if [ -z "$1" ]; then
    verbose_msg "remove_something: Nothing to remove!"
    return 1
  fi

  if ! [ -e "$1" ] && ! [ -L "$1" ] ; then
    verbose_msg "remove_something: no such file \"$1\"!"
    return 1
  fi

  if [ -d "$1" ] && ! [ -L "$1" ]; then
    if rmdir "$1" 2> /dev/null ; then
      debug_msg "ok    : rmdir \"$1\""
    else
      debug_msg "failed: rmdir \"$1\""
    fi
  else
    if rm -f "$1" 2> /dev/null ; then
      debug_msg   "ok    : rm -f \"$1\""
    else
      # this might be problematic so verbose_msg:
      verbose_msg "failed: rm -f \"$1\""
    fi
  fi
}


process_directories() {
  debug_msg "process_directories ($@)"

  if [ -z "$1" ]; then
    debug_msg "process_directories: No args, exiting"
    return 1
  fi

  cat "$1" | sort -r | while read LINE ; do
    remove_something "$LINE"
  done
}


lrm_module() {
  debug_msg "lrm_module ($@)"

  export TMP_DIRS=$(temp_create "$1.directories")

  run_details $1 &> /dev/null

  VERSION=$(installed_version $MODULE)
  INST_LOG=$INSTALL_LOGS/$MODULE-$VERSION
  MD5_LOG=$MD5SUM_LOGS/$MODULE-$VERSION

  # time-out 1: PRE_REMOVE
  if [ "$UPGRADE" != "on" ]; then
    plugin_call BUILD_PRE_REMOVE $MODULE
    run_module_file $MODULE PRE_REMOVE
  fi

  if [ "$MODULE" != "moonbase" ]; then
    update_plugin $MODULE remove
  fi

  # init.d scripts stop before rm phase, but only if ! upgrading
  if [ "$KEEPCONFIG" != "on" -a "$UPGRADE" != "on" ] ; then
    # pre-read these variables!
    SERVICES=$(get_module_config "SERVICES")
    XINETD=$(get_other_module_config "xinetd" "INITDSCRIPTS")
    for INITDSCRIPT in $(get_module_config "INITDSCRIPTS") ; do
      verbose_msg "Stopping service $INITDSCRIPT"
      ( cd / && /etc/init.d/$INITDSCRIPT stop )
    done
  fi

  # grep -v the install logs over the protected stuff, this
  # yields the files that may be removed
  if [ "$REAP" == "on" ] ; then
    cat "$INST_LOG" | grep -v -f "$PROTECTED" | while read TARGET ; do
      if [ -e "$TARGET" ] || [ -L "$TARGET" ]; then
        if [ -d "$TARGET" ] ; then
          echo "$TARGET" >> $TMP_DIRS
        else
          handle_config_files "$TARGET" &&
          remove_something "$TARGET"
        fi
      fi
    done
    process_directories $TMP_DIRS
  else
    verbose_msg "Skipping removal of files completely (REAP=$REAP)"
  fi

  # administration duty time:
  remove_module $MODULE

  # time-out 2: POST_REMOVE
  if [ "$UPGRADE" != "on" ] ; then
    plugin_call BUILD_POST_REMOVE $MODULE
    run_module_file $MODULE POST_REMOVE
  fi
  if [ "$KEEPCONFIG" == "on" -o "$UPGRADE" == "on" ] ; then
    debug_msg "skipping removal of dependency listing and configs"
  else
    verbose_msg "removing module from dependency listing and configs"
    remove_depends $MODULE
    # remove alias if required
    if [ $MODULE != moonbase ] ; then
      for ALIAS in `cut -d: -f1 "$MOONBASE/aliases"`; do
        if [ "$(get_local_config `echo LUNAR_ALIAS_${ALIAS:1}`)" == "$MODULE" ]; then
          verbose_msg "removing alias mapping from \"$ALIAS\" to \"$MODULE\""
          unset_local_config `echo LUNAR_ALIAS_${ALIAS:1}`
        fi
      done
    fi
    # restart xinetd after xinetd.confs have been removed by lrm...
    if [ -n "$SERVICES" -a -n "$XINETD" ] ; then
      verbose_msg "restarting xinetd"
      /etc/init.d/xinetd restart
    fi
  fi

  message  "${LRM_COLOR}Removed${EXTEMP} module:"  \
           "${MODULE_COLOR}${MODULE}${DEFAULT_COLOR}"
  activity_log  "lrm"  "$MODULE" "$VERSION"  "success"

temp_destroy $TMP_DIRS
}


check_status() {
  if ! module_installed $MODULE ; then
    message  "${MODULE_COLOR}${MODULE}"  \
    "${PROBLEM_COLOR}is not installed."  \
    "${DEFAULT_COLOR}"
    return 1
  fi

  if [ "$NOSUSTAIN" != "on" -a "$UPGRADE" != "on" ] ; then
    debug_msg "checking if \"$1\" is sustained"
    if grep -q "^$MODULE$" "$SUSTAINED" ; then
      message  "${MODULE_COLOR}${MODULE}"       \
               "${PROBLEM_COLOR}is sustained."  \
               "${DEFAULT_COLOR}"
      return 1
    fi
  fi
}


downgrade() {
  local CACHE_XZ

  # a local function for listing the contents of install cache
  list_available_cache() {
    ls -1 $INSTALL_CACHE/$MODULE-* | while read LINE ; do
      echo "$LINE"
    done
    exit 1
  }
   
  root_check

  verbose_msg "running \"$MODULE\" DETAILS file"
  run_details $MODULE &> /dev/null || return 1

  if [ -z "$1" ] ; then
    message "No version is entered. The below is the list of available files."
    list_available_cache   
  fi

  CACHE_XZ=$(xzbz_file "$INSTALL_CACHE/$MODULE-$1-$BUILD.tar")

  if [ ! -s $CACHE_XZ ] ; then 
    message "Desired version is not available. Here is the available files"
    list_available_cache
  fi

  verbose_msg "checking if \"$MODULE\" is installed"

  if [ -n "$(installed_version $MODULE)" ] ; then 
    verbose_msg "\"$(installed_version $MODULE)\" of \"$MODULE\" is installed at the moment"

    if [ "$1" == "$I_VERSION" ] ; then
      message "The downgrade version is the same as the installed version"
      message "Here is the available files." 
      list_available_cache
    fi

    verbose_msg "removing \"$MODULE-$(installed_version $MODULE)\""
    lrm -ts $MODULE || lrm -s $MODULE

  else
    verbose_msg "\"$MODULE\" is NOT installed at the moment"
  fi

  verbose_msg "installing \"$MODULE-$1\""
  verbose_msg "extracting cached install file..."
  
  xzbz -dc $CACHE_XZ | tar -kt $TAR_P -C / >/dev/null
  verbose_msg "adding module to installed list"

  xzbz -dc $CACHE_XZ | tar -kx $TAR_P -C / 2>/dev/null >/dev/null
  verbose_msg "adding module to installed list"
  add_module $MODULE installed  $1
  activity_log  "lrm downgrade"  "$MODULE"  "$1"  "success"

  message  "${RESURRECT_COLOR}Downgraded module:"  \
           "${MODULE_COLOR}${MODULE}"              \
           "${DEFAULT_COLOR}"                      \
           "version"                               \
           "${VERSION_COLOR}${1}"                  \
           "${DEFAULT_COLOR}" 
   
  exit
}


. /etc/lunar/config
. $BOOTSTRAP


GETOPT_ARGS=$(getopt -q -n lrm -o "dD:hknuvp" -l "debug,downgrade:,help,keepconfig,nosustain,upgrade,verbose,purge" -- "$@")

if [ -z "$?" ] ; then
  help | view_file
  exit
else
  eval set -- $GETOPT_ARGS

  root_check 
  enviro_check
  set_priority

  while true ; do
   case "$1" in
     -d|--debug      ) (( LUNAR_DEBUG++ )) ; export LUNAR_DEBUG ; shift ;;
     -D|--downgrade  ) export DOWNGRADE="on"
                       export MODULE="$2"
                       shift 3
                       downgrade "${@#-}"
                       exit
                       break;;
     -h|--help       ) help  ;;
     -k|--keepconfig ) export KEEPCONFIG="on" ; shift ;;
     -n|--nosustain  ) export NOSUSTAIN="on" ; shift ;;
     -v|--verbose    ) export VERBOSE="on" ; shift ;;
     -u|--upgrade    ) export UPGRADE="on" ; shift ;;
     -p|--purge      ) export PURGE="on" ; shift ;;
     --) shift; break ;;
      *) help;  break ;;
   esac
  done

  for MODULE in "${@#-}" ; do
    if check_status "$MODULE" ; then
      # assemble purge list if requested
      if [ "$PURGE" == "on" ] ; then
        DEPS=$(list_installed_depending "$MODULE" | sort | uniq)
        for DEP in $DEPS ; do
          # skip held modules
          if ! module_held $DEP ; then
	    # no duplicates
	    if ! $(echo $LIST | grep -qw $DEP) ; then
              message "${MESSAGE_COLOR}Inserting ${MODULE_COLOR}$DEP${DEFAULT_COLOR}${MESSAGE_COLOR} into the removal queue${DEFAULT_COLOR}"
              LIST="$LIST $DEP"
	    fi
          fi
        done
	# last append the module itself
	LIST="$LIST $MODULE"
      else
        # no fancy checks, just add to the list
        LIST="$LIST $MODULE"
      fi
    fi
  done
  for MODULE in $LIST ; do
    lrm_module $MODULE
  done
fi

