#! /bin/sh
# vim:sts=2 sw=2 ts=2 et ai foldmethod=marker
#   Installation script for the backup Agent on Unix

# variable used to check status of agentisrunning 
agentstatus=0
version_format="^[[:digit:]]+\.[[:digit:]]{2}\.[[:digit:]]+"

TermInit() #{{{ Initialize the terminal functions
{
  #{{{
  export b u s i n bu bs su nb ns nu nbu nbs nsu
  b=
  u=
  s=
  i=
  n=
  bu=
  bs=
  su=
  nb=
  ns=
  nu=
  nbu=
  nbs=
  nsu=
  i=
  #}}}

  type tput > /dev/null 2>&1
  [ ${?} -ne 0 ] && return

  b=`tput bold` || b=
  u=`tput smul` || u=
  s=`tput smso` || s=

  #{{{
  bu=${b}${u}
  bs=${b}${s}
  su=${s}${u}
  nb=${n}${b}
  ns=${n}${s}
  nu=${n}${u}
  nbu=${n}${bu}
  nbs=${n}${bs}
  nsu=${n}${su}
  #}}}

  i=`tput setab 0` || i=
  if [ ! -z "${i}" ]; then #{{{
    i="${i}`tput setaf 0`" || i=
  fi #}}}
  n=`tput sgr0` || n=
} #}}}

LogMessage() #{{{
{
  if [ ${quiet} -eq 0 ]; then
    [ ${#} -gt 0 ] && echo "${@}" | tee -a "${logfile}" || cat - | tee -a "${logfile}"
  else
    [ ${#} -gt 0 ] && echo "${@}" >> "${logfile}" || cat - >> "${logfile}"
  fi
} #}}}

LogError() #{{{
{
  [ ${#} -gt 0 ] && echo "Error: ${@}" | tee -a "${logfile}" || cat - | tee -a "${logfile}"
} #}}}

Message() #{{{
{
  if [ ${quiet} -eq 0 ]; then
    [ ${#} -gt 0 ] && echo "${@}" || cat -
  fi
} #}}}

Error() #{{{
{
  Message "${b}${bn0}${n}: ${@}"
} #}}}

DownCase() #{{{
{
  echo "${1}" | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'
} #}}}

ParamName() #{{{
{
  echo "${1}" | cut -f1 -d=
} #}}}

ParamValue() #{{{
{
  echo "${1}" | cut -f2 -d=
} #}}}

Usage() #{{{
{
  cat <<EOS
Usage: ${bn0} [options]
  -shutdown | -s          Force the agent shutdown, if running.
  -force | -F             Force the installation; skip the initial free
                          space check.
  -defaults | -D          Use the default values for installation.
  -force-defaults         Force the installation using the defaults
                          (assumes -s, -F and -D).
  -web-registration=off   Turns off web console registration.
  -W-
  -web-registration=FILE  Attempts to register to the web console with
  -W=FILE                 the values in FILE.
  -quiet | -Q             Quiet install; does not echo output to the
                          screen. If user interaction is required in
                          quiet mode, the install will fail unless
                          -force-defaults is specified.

  -log=NAME | -L=NAME     Writes the installation log to the specified
                          file with NAME.

  -backup=DIR | -B=DIR    Backs up the current installation of the Agent
                          to the specified directory.
EOS

  if [ -f "${dn0}/bmrregister" ]; then #{{{
    cat <<EOS
  -enable-bmr=y/n         Enables or disables Bare Metal Restore (BMR).
  -rear-path=PATH         Path to the Relax-and-Recover (rear) binary. Required for BMR.
EOS
  fi #}}}

  if [ "`Internationalized`" = "true" ]; then #{{{
    cat <<EOS
  -lang=NAME | -l=NAME    Selects NAME as the language. Must begin with
                          an ISO language code. May optionally be followed
                          by a dash or underscore and an ISO country code
                          (e.g., fr, fr-FR, and fr_FR are acceptable).
                          Character set markers (e.g., UTF-8) are ignored.
                          Languages that cannot be matched will report an
                          error and the language will be defaulted to en-US
                          [English (US)]. If not specified, the language
                          will be guessed from your system value of
                          "${default_lang}".
EOS
  fi #}}}

  cat <<EOS
  -verify | -V            Verifies the integrity of the installation kit.

  -help                   Shows this text.

EOS
} #}}}

CopyLog() #{{{
{
  target="${1}/Install.log"

  if [ -f "${target}" ]; then
    printf "==================================================\n" >> "${target}"
    cat "${logfile}" >> "${target}"
  else
    cp -p "${logfile}" "${target}" > /dev/null 2>&1
  fi
} #}}}

TellLog() #{{{
{
  [ ${#} -eq 1 ] && logfile="${1}/Install.log"

  Message <<EOS

The installation has been recorded in ${logfile}.
EOS
} #}}}

RequestShutdown() #{{{
{
  stop_agent=0
  if [ "${1}" = "${progVV}" ]; then #{{{
    FailInstall ${INSTALL_FAILED} "There are one or more active jobs running. Terminate them and run install.sh again."
  else
    if [ ${force_stop_agent} -eq 1 ]; then #{{{
      stop_agent=1
    else
      printf '%s is still running. Do you wish to stop it? ([Y]/n) ' ${1}
      read res
      if [ "${res}" = "" -o "`DownCase ${res}`" = "y" ]; then #{{{
        stop_agent=1
      fi #}}}
    fi #}}}
  fi #}}}

  if [ ${stop_agent} -eq 1 ]; then #{{{
    StopAgent
  else
    FailInstall ${INSTALL_FAILED} "${1} is currently running. Terminate it and run install.sh again."
  fi #}}}
} #}}}

IsRunning() #{{{
{
  for program in ${*}; do #{{{
    if [ `TestRunning ${program}` -ne 0 ]; then #{{{
      RequestShutdown ${program}
    fi #}}}
  done #}}}
} #}}}

CheckRunning() #{{{
{
  IsRunning ${progVV} ${progVVAgent}
  # Delay needed for agent upgrade from 5.6 to 6. Wait up to 30secs for buagent.
  retry=15
  while [ ${retry} -gt 0 -a `TestRunning ${progbuagent}` -ne 0 ]; do #{{{
    sleep 2
    retry=`expr ${retry} - 1`
  done  #}}}
  [ ${with_buagent} -eq 1 ] && IsRunning ${progbuagent}
} #}}}

CheckPermissions() #{{{
{
  ${TraceF-}
  ok=0

  if [ -d ${init_dir} ]; then #{{{
    [ -w ${init_dir} ] || ok=1
  fi #}}}

  if [ -f /etc/inittab ]; then #{{{
    if [ -r /etc/inittab ]; then #{{{
      [ -w /etc/inittab ] || ok=1
    fi #}}}
  fi #}}}

  if [ ${ok} -ne 0 ]; then #{{{
    FailInstall ${INSTALL_FAILED} "Insufficient privileges to install Agent. Login as root and try again."
  fi #}}}
} #}}}

ReadUntilSet() #{{{
{
  res=
  Prompt=${1}
  Cancel=${2}
  Variable=${3}
  Secure=${4}

  if [ "${Secure}" = "-secure" ]; then #{{{
    i=`tput setab 0` || i=
    if [ ! -z "${i}" ]; then #{{{
      i="${i}`tput setaf 0`" || i=
    fi #}}}
    n=`tput sgr0` || n=
  else
    i=
    n=
  fi #}}}

  while [ -z "${res}" ]; do #{{{
    printf "${n}${Prompt}${i}"

    read res

    if [ "${res}" = "${Cancel}" ]; then #{{{
      return 255
    fi #}}}

    if [ "${res}" = "" ]; then #{{{
      printf "Must provide a value.\n${Prompt}"
    fi #}}}
  done #}}}

  eval ${Variable}=${res}
  export ${Variable}
  return 0
} #}}}

# Possible states for the installation, in order of priority:
# 1. The rc script is properly installed.
# 2. An old installation exists, e.g., the startup of VVAgent is recorded
#    directly in inittab.
#
# If we find more than one of these, we need to mark them for cleanup and
# should print a message.
FindPreviousInstallInRCScript() #{{{
{
  local_init_file="${init_dir}/vvagent"
  [ ${#} -eq 1 ] && local_init_file="${1}/vvagent"

  oldPath_RC=

  [ ! -f "${local_init_file}" ] && return

  tmp=`grep agentDir= "${local_init_file}" | cut -d= -f2`
  c1=`echo "${tmp}" | cut -c1`
  [ "${c1}" = '"' ] && tmp=`echo "${tmp}" | cut -d\" -f2`
  [ "${c1}" = "'" ] && tmp=`echo "${tmp}" | cut -d\' -f2`
  local_init_file=
  oldPath_RC="${tmp}"
} #}}}

FindPreviousInstallInInitTab() #{{{
{
  oldPath_InitTab=

  [ ! -r /etc/inittab ] && return

  ic=`grep -c "${progVVAgent} -d" /etc/inittab`

  if [ ${ic} -ge 2 ]; then #{{{
    LogMessage <<EOS

`grep ${progVVAgent} /etc/inittab`

Found too many references to ${progVVAgent} in /etc/inittab. Please clean up
/etc/inittab before installing. Aborting installation.

EOS
    FailInstall ${INSTALL_FAILED}
  fi #}}}

  tmp=`grep "${progVVAgent} -d" /etc/inittab`
  ora=`echo "${tmp}" | grep -c ORACLE_HOME`
  shm=`echo "${tmp}" | grep -c EXTSHM`

  if [ "${ora}" -ge 1 ]; then #{{{
    tmp=`echo "${tmp}" | ${binawk} -F: '{ print $4; }' | ${binawk} -F\; '{ print $2; }' | ${binawk} '{ print $1; }'`
  elif [ "${shm}" -ge 1 ]; then
    tmp=`echo "${tmp}" | ${binawk} '{ print $6; }'`
  elif [ "${ic}" -ge 1 ]; then
    tmp=`echo "${tmp}" | ${binawk} '{ print $1; }' | ${binawk} -F: '{ print $4; }'`
  else
    tmp=
  fi #}}}

  oldPath_InitTab="${tmp}"
} #}}}

FindPreviousInstall_Override() #{{{
{
  : # This is a noop for most installs.
} #}}}

GetPreviousInstallDir() #{{{
{
  FindPreviousInstallInRCScript
  FindPreviousInstallInInitTab
  FindPreviousInstall_Override

  oldPath="${oldPath_RC}"

  if [ -n "${oldPath}" -a -n "${oldPath_InitTab}" ]; then #{{{
    LogMessage <<EOS

Found one or more references to ${progVVAgent} in /etc/inittab. These will be
ignored and removed after successful installation.

EOS
  fi #}}}

  [ -z "${oldPath}" ] && oldPath="${oldPath_InitTab}"

  if [ -n "${oldPath}" ]; then #{{{
    [ `echo "${oldPath}" | grep -c VVAgent` -ge 1 ] && oldPath=`dirname "${oldPath}"`
    default_dir="${oldPath}"

    [ -z "${install_dir}" ] && LogMessage <<EOS

NOTE:    To upgrade jobs and binaries  from a previous installation
         of the Agent, the  installation directory must  remain the
         same as that of the previous Agent installation directory.

EOS
  fi #}}}
} #}}}

FailInstall() #{{{ The installation failed. The data must be rolled back.
{
  errorCode=${1}      # The reason the install failed.
  shift
  errorMessage="${*}" # The rest of the parameters are why the install failed.

  LogError <<EOS

Installation failed.
    ${errorMessage}

EOS

  # Do not print the message if agent was not shutdown.
  if [ ${agentstatus} -ne 0 ]; then #{{{
    LogMessage <<EOS
The Agent was not started.

EOS
  fi  #}}}

  # Attempt to do a rollback.
  if [ ${rollback} -gt 0 ]; then #{{{
    # Move the installation directory to the failed directory.
    if mv -f "${install_dir}" "${install_dir}.Failed.${install_time}" > /dev/null 2>&1; then
        if [ -d "${backup_dir}" ]; then
            if mv -f "${backup_dir}" "${install_dir}" > /dev/null 2>&1; then
                LogMessage <<EOS
Installation successfully rolled back.  The failed installation has been saved at:
    "${install_dir}.Failed.${install_time}"
EOS
                StartAgent
            else
                LogError <<EOS
Rollback failed:
mv -f "${backup_dir}" "${install_dir}"
`mv -f "${backup_dir}" "${install_dir}" 2>&1`

Backup of original BUAgent directory is ${backup_dir}.
It must be renamed to ${install_dir} manually.
EOS
            fi
        fi
    else
        LogError <<EOS
Rollback failed.

Failed to move failed installation aside:
mv -f "${install_dir}" "${install_dir}.Failed.${install_time}"
`mv -f "${install_dir}" "${install_dir}.Failed.${install_time}" 2>&1`

${install_dir} must be moved aside manually.
EOS
        if [ -d "${backup_dir}" ]; then
            LogError <<EOS
Backup of original BUAgent directory is ${backup_dir}.
It must be renamed to ${install_dir} manually.
EOS
        fi
    fi

    if [ ${rollback} -gt 1 ]; then #{{{
      # If using the runlevel control system, we need to see if we are
      # restoring something that was already present or if we're removing it
      # entirely.
      if [ -s "${init_dir}/vvagent.${install_time}" ]; then #{{{
        mv -f "${init_dir}/vvagent.${install_time}" "${init_dir}/vvagent" > /dev/null 2>&1
      else
        rm -f "${init_dir}/vvagent" 2> /dev/null > /dev/null
        # Remove the runlevel entries only if we're removing it entirely.
        for level in ${run_levels}; do #{{{
          rm -f "${rc_root}/rc${level}.d/"[SK][012345679][012345679]vvagent > /dev/null 2>&1
        done #}}}
      fi #}}}
    fi #}}}

    # We may be modifying inittab even if we are using the runlevel
    # control system.
    [ -s /etc/inittab.${install_time} ] && mv -f /etc/inittab.${install_time} /etc/inittab 2> /dev/null > /dev/null
    [ -f /etc/inittab.tmp ] && rm -f /etc/inittab.tmp 2>/dev/null >/dev/null

    # Copy or append the logfile to the destination directory; also
    # append it to the failed install directory.
    if [ ${moveLog} -eq 1 ]; then #{{{
      CopyLog "${install_dir}.Failed.${install_time}" quiet
      CopyLog "${install_dir}"
    fi #}}}

    errorCode=${INSTALL_FAILED_ROLLBACK}
  elif [ -n "${install_dir}" ]; then
    # Copy or append the logfile to the destination directory, if it
    # exists. Otherwise it just stays in the install directory.
    CopyLog "${install_dir}"
  fi #}}}

  exit ${errorCode}
} #}}}

ExprError() #{{{
{
  if [ ${1} -ge 2 ]; then #{{{
    if [ ${quiet} -ne 0 ]; then #{{{
      LogMessage <<EOS
An error occurred during the free space check and the installer cannot prompt
for user input.

If there is enough space on the desired device, run the installer again with
the "-force" option to skip the free space check.
EOS
      FailInstall ${INSTALL_FAILED}
    fi #}}}

    LogMessage <<EOS
An error occurred during the free space check. The install may fail because
of a lack of disk space.
EOS

    printf "Do you wish to continue installing anyway? [yN] "
    read res
    if [ -z "${res}" -o "`DownCase ${res}`" = "n" ]; then #{{{
      FailInstall ${INSTALL_CANCELED} "The user canceled the install."
    fi #}}}

    LogMessage "User continued install."
    return 0
  fi #}}}

  return 1
} #}}}

CheckInstallSpace() #{{{
{
  [ ${ignore_free_space_check} -eq 1 ] && return 0

  required_space=0
  [ -d "${install_dir}" ] && required_space=`GetSpace "${install_dir}"`
  [ ${required_space} -eq 0 ] && required_space=`GetSpace "${source_dir}"`

  cushion=25600 # 25Mb cushion space
  required_space=`expr ${required_space} \* 2 2> /dev/null`
  ExprError ${?} && return 0

  required_space=`expr ${required_space} + ${cushion} 2> /dev/null`
  ExprError ${?} && return 0

  required_space=`expr ${required_space} / 1024  2> /dev/null`
  ExprError ${?} && return 0

  required_space=`expr ${required_space} + 1  2> /dev/null`
  ExprError ${?} && return 0

  base_dir="${backup_dir}"

  while [ ! -d "${base_dir}" ]; do #{{{
    base_dir=`dirname "${base_dir}"`
  done #}}}

  free_space=`GetFreeSpace "${base_dir}"`
  free_space=`expr ${free_space} / 1024   2> /dev/null`
  ExprError ${?} && return 0

  free_space=`expr ${free_space} + 1    2> /dev/null`
  ExprError ${?} && return 0

  LogMessage <<EOS

Installing `InstallName` ${version} for ${platform}.

Directory           :   ${install_dir}
Disk Space Required :   ${required_space} MB (estimated)
Available           :   ${free_space} MB

EOS

  if [ ${free_space} -lt ${required_space} ]; then #{{{
    FailInstall ${INSTALL_FAILED} "Insufficient space to install to ${install_dir}."
  fi #}}}
} #}}}

CheckPreviousVersion() #{{{
{
  # Look for an existing Global.vvc in the target directory
  if [ ! -f ${install_dir}/Global.vvc ] ; then #{{{
    LogMessage "Fresh install. Skipping version check."
    return 0
  fi # }}}

  # Extract the version of the existing agent
  existing_version=$(grep '^BuildVersion' ${install_dir}/Global.vvc | sed 's/[[:space:]]//g' | cut -d '=' -f 2)

  # Validate version string
  echo ${existing_version} | egrep -q ${version_format}

  if [ ${?} -ne 0 ] ; then
    LogMessage <<EOS
A prior installation of this product has been detected, but its version
cannot be determined.

EOS
    overwrite_existing=
    if [ ${use_defaults} -eq 0 ] ; then
      printf "Would you like to overwrite it with this one? (y/[N]) "
      read overwrite_existing
    fi

    case ${overwrite_existing} in
    [yY])
      return 0;
      ;;
    *)
      FailInstall 1 "Installation canceled"
      ;;
    esac
  fi

  LogMessage "Installed version detected: ${existing_version}"

  existing_major=$(echo ${existing_version} | cut -d '.' -f 1)
  existing_minor_patch=$(echo ${existing_version} | cut -d '.' -f 2)
  existing_build=$(echo ${existing_version} | cut -d '.' -f 3)

  # Extract the version of this agent
  this_version=$(cat ${dn0}/${distro}/.VERSION)

  # Sanity check the version string
  echo ${this_version} | egrep -q ${version_format}
  if [ ${?} -ne 0 ] ; then
    FailInstall 1 "Installer version information is corrupt."
  fi

  this_major=$(echo ${this_version} | cut -d '.' -f 1)
  this_minor_patch=$(echo ${this_version} | cut -d '.' -f 2)
  this_build=$(echo ${this_version} | cut -d '.' -f 3)

  # Check whether this is a downgrade
  is_downgrade=0
  if [ ${existing_major} -gt ${this_major} ] ; then
    is_downgrade=1
  elif [ ${existing_major} -eq ${this_major} ] ; then
    if [ ${existing_minor_patch} -gt ${this_minor_patch} ] ; then
      is_downgrade=1
    elif [ ${existing_minor_patch} -eq ${this_minor_patch} ] ; then
      if [ ${existing_build} -gt ${this_build} ] ; then
        is_downgrade=1
      fi
    fi
  fi

  # Fail if this is a downgrade
  if [ $is_downgrade -ne 0 ] ; then
    FailInstall 1 "Downgrading is not supported. Uninstall existing agent before installing this one."
  fi

  # Determine whether to proceed with an upgradability test
  if [ -z "${earliest_upgradable_version}" ] ; then
    # No version has been specified, skip this test
    return 0
  fi

  # Sanity test earliest upgradable version string
  echo ${earliest_upgradable_version} | egrep -q ${version_format}
  if [ ${?} -ne 0 ] ; then
    FailInstall 1 "Internal error: Invalid version string specified for earliest upgradable version."
  fi

  earliest_upgradable_major=$(echo ${earliest_upgradable_version} | cut -d '.' -f 1)
  earliest_upgradable_minor_patch=$(echo ${earliest_upgradable_version} | cut -d '.' -f 2)
  earliest_upgradable_build=$(echo ${earliest_upgradable_version} | cut -d '.' -f 3)


  # Check whether the installed version is greater than or equal to earliest
  # upgradable version
  is_upgradable=0
  if [ ${existing_major} -gt ${earliest_upgradable_major} ] ; then
    is_upgradable=1
  elif [ ${existing_major} -eq ${earliest_upgradable_major} ] ; then
    if [ ${existing_minor_patch} -gt ${earliest_upgradable_minor_patch} ] ; then
      is_upgradable=1
    elif [ ${existing_minor_patch} -eq ${earliest_upgradable_minor_patch} ] ; then
      if [ ${existing_build} -ge ${earliest_upgradable_build} ] ; then
        is_upgradable=1
      fi
    fi
  fi

  # Fail if the existing agent cannot be upgraded
  if [ $is_upgradable -eq 0 ] ; then
    FailInstall 1 "The existing Agent is too old to upgrade directly. Contact support for further instructions."
  fi

  return 0
} #}}}

Initialize() #{{{
{
  PlatformSetup

  trap "FailInstall ${?}" 2 3 15

  INSTALL_FAILED=2
  INSTALL_FAILED_ROLLBACK=3
  INSTALL_CANCELED=4

  source_dir="${dn0}"
  logfile="${source_dir}/Install.log"

  rollback=0
  started=0

  install_dir=
  backup_dir=

  quiet=0
  use_defaults=0

  [ -z "${sh_ext}" ] && sh_ext=so

  ignore_free_space_check=0

  keepBackupDir=0
  moveLog=1

  with_buagent=0
  oracle=0
  vmware=0
  pluginInstall=0
  verifyOnly=0
  __support__=0

  progVVAgent=VVAgent
  progbuagent=buagent
  progVV=VV
  web_registration=1
  web_regfile=
  force_stop_agent=0

  DetermineSystemDefaultLanguage

  default_lang=""
  license_file=License.txt
  acknowledgements_file=Acknowledgements.txt

  for arg in "${@}"; do #{{{
    case ${arg} in
      -shutdown|-s)               force_stop_agent=1 ;;
      -force|-F)                  ignore_free_space_check=1 ;;
      -quiet|-Q)                  quiet=1 ;;
      -defaults|-D)               use_defaults=1 ;;
      -force-defaults|-FD|-DF)    ignore_free_space_check=1
                                  use_defaults=1
                                  force_stop_agent=1
                                  ;;
      -web-registration=off|-W-)  web_registration=0 ;;
      -web-registration=*|-W=*)   web_registration=2
                                  web_regfile=`ParamValue ${arg}`
                                  ;;
      -web-registration|-W)       web_registration=1 ;;
      -backup=*|-B=*)             backup_dir=`ParamValue ${arg}` ;;
      -log=*|-L=*)                logfile=`ParamValue ${arg}` ;;
      -lang=*|-l=*)               default_lang=`ParamValue ${arg}` ;;
      __support__)                __support__=1 ;;
      -verify|-V)                 exit 0 ;;
      -help|-?|-h|-H)             Usage
                                  exit 0
                                  ;;
      -ignore)                    ;; # this is handled in an earlier point
      -setaccess=*|-AC=*)         ;; # handled by accesscontrol script
      -enable-bmr=*)              ;; # handled by bmrregister script
      -rear-path=*)               ;; # handled by bmrregister script
      *)                          Error "Unknown parameter ${arg}."
                                  Usage
                                  exit 1
                                  ;;
    esac
  done #}}}

  [  ! -z "${default_lang}" ] || default_lang="en-US"

  install_time=`date +%Y%m%d%H%M%S`
  LogMessage "Install started at `date +'%H:%M:%S %Y.%m.%d'`"

  [ -z "${rc_vvagent}" ] && rc_vvagent=rc.vvagent

  version=`cat "${dn0}/.VERSION"`
  dist_version=`cat "${dn0}/${distro}/.VERSION"`
  export version dist_version
  export PATH=/usr/bin/:${PATH}
} #}}}

MakeRollbackDirectory() #{{{
{
  # Future feature request: back up selectively.
  cp -rp "${install_dir}" "${backup_dir}" 2> /dev/null > /dev/null
  if [ ${?} -ne 0 ]; then #{{{
    rm -rf "${backup_dir}" 2> /dev/null > /dev/null
    FailInstall ${INSTALL_FAILED} "Unable to back up ${install_dir}. Installation cannot continue."
  else
    LogMessage "Prepared installation rollback directory."
  fi #}}}
  rollback=1
} #}}}

PrepareInstallation() #{{{
{
  if [ -d "${install_dir}" ]; then #{{{
    MakeRollbackDirectory
  else
    create=0
    if [ ${use_defaults} -eq 0 ]; then #{{{
      printf "${install_dir} doesn't exist. Create it? ([Y]/n) "
      read res
      if [ "${res}" = "" -o "`DownCase ${res}`" = "y" ]; then #{{{
        create=1
      fi #}}}
    else
      create=1
    fi #}}}

    if [ ${create} -eq 1 ]; then #{{{
      mkdir -p "${install_dir}"
      if [ ${?} -ne 0 ]; then #{{{
        FailInstall ${INSTALL_FAILED} "Unable to create directory ${install_dir}. Installation cannot continue."
      fi #}}}
    else
      FailInstall ${INSTALL_FAILED} "Aborting installation."
    fi #}}}
  fi #}}}

  if [ ! -w "${install_dir}" ]; then #{{{
    FailInstall ${INSTALL_FAILED} "You do not have write permission to ${install_dir}."
  fi #}}}

  for m in .installed .installed.gz; do #{{{
    [ -f ${m} ] && rm -f ${m}
  done #}}}

  rollback=1
} #}}}

InstallFileImpl() #{{{
{
  inst_src="${1}"
  inst_dst="${2}"

  [ -f "${inst_dst}" ] && rm -f "${inst_dst}" 2> /dev/null > /dev/null

  cp ${cp_args} "${inst_src}" "${inst_dst}" 2>> "${logfile}" >> "${logfile}"
  err=${?}
  [ ${err} -ne 0 ] && FailInstall ${err} "Error installing ${inst_src} to ${inst_dst}."

  target="${inst_dst}"
  [ -d "${target}" ] && target="${target}/`basename "${inst_src}"`"
  echo "${target}" >> "${install_dir}/.installed"

  return 0
} #}}} 

InstallFile() #{{{
{
  inst_src="${1}"

  # Check to see if the file to be installed is in the source directory since
  # we already checked the manifest, it's ok if it's not there
  [ -f "${inst_src}" ] || return 0

  if [ ${#} -eq 2 ]; then #{{{
    inst_dst="${2}"
  else
    inst_dst="${install_dir}"
  fi #}}}

  InstallFileImpl "${inst_src}" "${inst_dst}"
  return 0
} #}}}

InstallFileRenamed() #{{{
{
  inst_src="${1}"
  new_name="${2}"

  # Check to see if the file to be installed is in the source directory since
  # we already checked the manifest, it's ok if it's not there
  [ -f "${inst_src}" ] || return 0

  if [ ${#} -eq 3 ]; then #{{{
    inst_dst="${3}/${new_name}"
  else
    inst_dst="${install_dir}/${new_name}"
  fi #}}}

  InstallFileImpl "${inst_src}" "${inst_dst}"
  return 0
} #}}}

InstallLanguageFiles() #{{{
{
  lng_src="${1}/Languages"
  lng_dst="${install_dir}/Languages"
  mkdir -p "${lng_dst}"

  lng_flist=`ls -m "${lng_src}"/*.lng`

  numfiles=`echo ${lng_flist} | ${binawk} -F, '{print NF}'`

  i=0

  while [ ${i} -lt ${numfiles} ]; do #{{{
    i=`expr ${i} + 1`
    filename=`echo ${lng_flist} | ${binawk} -F, -v num=${i} '{print $num}'`
    filename=`echo ${filename}`
    InstallFile "${filename}" "${lng_dst}"
  done #}}}

  if [ "`Internationalized`" = "true" ]; then #{{{
    InstallFile "${lng_src}/language.info" "${lng_dst}"

    if [ ${i18n_specified} -gt 0 ]; then #{{{
      SetCmdLineLanguageOption "${install_dir}" "${default_lang}" 
    else
      SetDefaultLanguage "${install_dir}" "${default_lang}"
    fi #}}}
  else
    InitLanguageFunctions "${install_dir}"
    LinkDefaultLanguage "${langdir}" "en-US"
  fi #}}}
} #}}}

CheckValidVaults() #{{{
{
  if [ -f "${install_dir}/Global.vvc" ]; then #{{{
    vc=`grep -c "Server[0-9] {" "${install_dir}"/Global.vvc`

    [ ${vc} -eq 0 ] && return

    LogMessage <<EOS
Verifying Director registrations to supported versions. This version of the
Agent requires Director 5.52 or later. Review VaultVerify.XLOG for logged
messages.

EOS

    Message "Verifying vaults: "${source_dir}/${distro}/AgtUpgd" ${install_dir} ${install_dir}/AgtUpgd.backup ${dist_version} VaultVerify.XLOG /checkvaultversions" >> "${logfile}"
    here=`pwd`
    cd "${install_dir}"
    "${source_dir}/${distro}/AgtUpgd" ${install_dir} ${install_dir}/AgtUpgd.backup ${dist_version} VaultVerify.XLOG /checkvaultversions 2>&1 >> ${logfile}
    RES=${?}
    cd "${here}"

    case ${RES} in
      0)  LogMessage "All Director registrations are OK." ;;
      1)  LogMessage <<EOS
The upgrade process has failed. The upgrade program was unable to determine the
versions of Infostage Director that the Agent was previously registered to. (A
common cause of this is network connnectivity issues to the Director). Please
verify and fix the Director registrations and try the upgrade again.
EOS
          FailInstall ${INSTALL_FAILED_ROLLBACK} "One or more registrations to Director versions could not be validated."
          ;;
      2)  LogMessage <<EOS
The upgrade process has failed. The Agent is registered with an incompatible
version of the Director. This Agent is compatible with InfoStage Director
version 5.52 and later.
EOS
          FailInstall ${INSTALL_FAILED_ROLLBACK} "One or more registrations are to unsupported Director versions."
          ;;
    esac
  fi #}}}
} #}}}

UpgradeControlFiles() #{{{
{
  if [ -d /var/run/buagent ]; then #{{{
    rm -rf /var/run/buagent
  fi #}}}

  if [ -f "${install_dir}/Global.vvc" ]; then #{{{
    if [ -f "${install_dir}/Schedule.cfg" ]; then #{{{
      LogMessage <<EOS
Upgrading existing Agent jobs.
Review AgtUpgd.XLOG for logged messages.

EOS
      Message "Running upgrade: "${source_dir}/${distro}/AgtUpgd" "${install_dir}" "${install_dir}/AgtUpgd.backup" ${dist_version} AgtUpgd.XLOG" >> ${logfile}
      here=`pwd`
      cd "${install_dir}"
      "${source_dir}/${distro}/AgtUpgd" "${install_dir}"  "${install_dir}/AgtUpgd.backup" ${dist_version} AgtUpgd.XLOG 2>&1 >> ${logfile}
      RES=${?}
      cd "${here}"
      [ ${RES} -ne 0 ] && FailInstall ${INSTALL_FAILED_ROLLBACK}
    fi #}}}
  fi #}}}
} #}}}

InstallCommonFiles() #{{{
{
  for file in "${source_dir}/${distro}/"*; do
    InstallFile "${file}"
  done
  InstallFile "${source_dir}/uninstall.sh"
  [ "`Internationalized`" = "true" ] && InstallFile "${source_dir}/set_language"
  InstallLanguageFiles "${source_dir}"
} #}}}

InstallXlogmore() #{{{
{
  xlogmore_file="${install_dir}/xlogmore"

  rm -f "${xlogmore_file}"
  ${binawk} -v binsh=${binsh} -v install_dir="${install_dir}" '/^#!/ { print "#!" binsh } /^[\t ]*agentDir=/ { print "agentDir=\"" install_dir "\""; } !/^[\t ]*agentDir=|^#!/ { print $0; }' "${source_dir}/xlogmore" > ${xlogmore_file}
  chmod 0 "${xlogmore_file}"
  chmod u+rwx "${xlogmore_file}"
  chmod go+rx "${xlogmore_file}"
} #}}}

InstallStartup() #{{{
{
  init_file=${init_dir}/vvagent
  init_backup=${init_file}.${install_time}

  [ -f ${init_file} ] && cp -p ${init_file} ${init_backup} > /dev/null 2>&1

  ${binawk} -v binsh=${binsh} -v install_dir="${install_dir}" '/^#!/ { print "#!" binsh } /^[\t ]*agentDir=/ { print "agentDir=\"" install_dir "\""; } !/^[\t ]*agentDir=|^#!/ { print $0; }' "${dn0}/${rc_vvagent}" > ${init_file}
  chmod 0 ${init_file}
  chmod u+rwx ${init_file}
  chmod go+rx ${init_file}

  RegisterRCScript
  rollback=2

  # Does the /etc/inittab refer to VVAgent? If so, we need to modify it.
  if [ -r /etc/inittab ]; then #{{{
    update_init=`grep -c ${progVVAgent} /etc/inittab`

    if [ ${update_init} -ne 0 ]; then #{{{
      cp -p /etc/inittab /etc/inittab.${install_time} 2> /dev/null > /dev/null
      if [ ${?} -ne 0 ]; then #{{{
        FailInstall ${?} "Cannot create a backup copy of /etc/inittab."
      fi #}}}

      cd "${install_dir}"
      install_dir=`pwd`

      # clean up the entries in the inittab
      grep -v ${progVVAgent} /etc/inittab.${install_time} > /etc/inittab

      LogMessage <<EOS

Copied old /etc/inittab to /etc/inittab.${install_time}.
Removed ${progVVAgent} entry from /etc/inittab.

EOS
    fi #}}}
  fi #}}}
} #}}}

RegisterRCScript() #{{{
{
  for level in ${run_levels}; do #{{{
    if [ -d ${rc_root}/rc${level}.d ]; then #{{{
      sf=${rc_root}/rc${level}.d/S${start_level}vvagent
      kf=${rc_root}/rc${level}.d/K${stop_level}vvagent
    elif [ -d ${rc_root}/rc.d/rc${level}.d ]; then
      sf=${rc_root}/rc.d/rc${level}.d/S${start_level}vvagent
      kf=${rc_root}/rc.d/rc${level}.d/K${stop_level}vvagent
    fi #}}}

    if [ ! -h ${sf} ]; then #{{{
      if [ -f ${sf} ]; then #{{{
        LogMessage "Cannot create ${sf}; already exists as a file."
      else
        ln -s ${init_dir}/vvagent ${sf}
      fi #}}}
    fi #}}}

    if [ ! -h ${kf} ]; then #{{{
      if [ -f ${kf} ]; then #{{{
        LogMessage "Cannot create ${kf}; already exists as a file."
      else
        ln -s ${init_dir}/vvagent ${kf}
      fi #}}}
    fi #}}}
  done #}}}

  # chkconfig on Linux
} #}}}

ChangeInstallationOwnership() #{{{
{
  chown -R 0:0 "${install_dir}"
  chmod -R o-rwx "${install_dir}"
  chmod ug+rwx "${install_dir}"
  dirlist=`ls -R "${install_dir}" | grep "^.*:$" | sed -e "s/:$/,/g"`
  numdir=`echo ${dirlist} | ${binawk} -F, '{print NF}'`
  i=0
  maxdir=`expr ${numdir} - 1`
  while [ ${i} -lt ${maxdir} ]; do #{{{
    i=`expr ${i} + 1`
    dirname=`echo ${dirlist} | ${binawk} -F, -v num=${i} '{print $num}'`
    dirname=`echo ${dirname}`
    [ ! -z "${dirname}" ] && chmod ug+rwx "${dirname}"
  done #}}}

  # chmod -R ug+rwx "${install_dir}"
} #}}}

StopAgent() #{{{
{
  command="${init_dir}/vvagent stop quiet"
  LogMessage "Stopping Agent: ${command}"
  sh -c "${command}" 2>/dev/null
} #}}}

StartAgent() #{{{
{
  if [ ${started} -eq 1 ]; then #{{{
    start_cmd="${init_dir}/vvagent restart quiet"
  else
    start_cmd="${init_dir}/vvagent start quiet"
  fi #}}}
  
  # (re)start VVAgent
  LogMessage "Starting Agent: ${start_cmd}"

  running=0
  timeout=0

  start_buagent=0
  [ ${with_buagent} -eq 1 -a -f "${install_dir}"/buagent.cfg ] && start_buagent=1

  while [ ${running} -eq 0 -a ${timeout} -le 10 ]; do #{{{
    [ ${timeout} -ge 1 ] && LogMessage "Starting Agent (retry ${timeout})"

    timeout=`expr ${timeout} + 1`

    OLDLIB_PATH=`GetLibraryPath`
    SetLibraryPath "${install_dir}"
        
    sh -c "${start_cmd}" > /dev/null 2>&1

    SetLibraryPath "${OLDLIB_PATH}"

    sleep 3

    cvv=`TestRunning ${progVVAgent}`

    if [ ${start_buagent} -eq 1 ]; then #{{{
      cbu=`TestRunning ${progbuagent}`

      if [ ${cbu} -ne 0 -a ${cvv} -ne 0 ]; then #{{{
        running=2
      elif [ ${cbu} -ne 0 -o ${cvv} -ne 0 ]; then
        running=1
      else
        running=0
      fi #}}}
    else
      if [ ${cvv} -ne 0 ]; then #{{{
        running=1
      else
        running=0
      fi #}}}
    fi #}}}
  done #}}}

  if [ ${running} -eq 0 ]; then #{{{
    LogMessage <<EOS

Installation complete. Agent failed to start. Check the log files for more
details. See the Agent release notes for possible solutions.

EOS
  else
    if [ ${start_buagent} -eq 0 -o ${running} -eq 2 ]; then #{{{
      LogMessage <<EOS

Installation complete. Agent started successfully.

EOS
      agentstatus=0
    else
      LogMessage <<EOS

Installation complete. Not all Agent services started successfully. Check the
log files for more details. See the Agent release notes for possible solutions.

EOS
    fi #}}}
  fi #}}}
} #}}}

Centre() #{{{
{
  s=`echo ${@} | wc -c`
  s=`expr ${s}`

  if [ ${s} -ge 40 ]; then #{{{
    LogMessage "${@}"
  else
    o=`expr ${s} / 2`
    o=`expr 40 - ${o}`

    LogMessage "`printf \"%${o}s%s\" \" \" \"${@}\"`"
  fi #}}}
} #}}}

InstallBanner() #{{{
{
  LogMessage <<EOS


EOS
  Centre "Installing `InstallName`"
  LogMessage <<EOS


EOS
} #}}}

InstallName() #{{{
{
  echo "Backup Agent"
} #}}}

GetInstallDir() #{{{
{
  if [ ${use_defaults} -eq 1 ]; then #{{{
    install_dir=${default_dir}
  elif [ -z "${install_dir}" ]; then
    isValid=0
    while [ ${isValid} -eq 0 ]; do #{{{
      printf "Installation directory? [${default_dir}] "
      read install_dir

      [ -z "${install_dir}" ] && install_dir=${default_dir}
      # Check that the path is absolute and not relative
      echo "${install_dir}" | grep "^\/" > /dev/null
      if [ $? -ne 0 ]
      then
         Message <<-EOS

Invalid installation directory. The installation directory  must be the full
or absolute path.

EOS
         isValid=0
      else
         CheckValidInstallDir "${install_dir}"
         isValid=${?}
      fi
    done #}}}
  fi #}}}

  if [ -z "${backup_dir}" ]; then #{{{
    backup_dir=${install_dir}.${install_time}
  else
    backup_dir="${backup_dir}/`basename "${install_dir}"`".${install_time}
  fi #}}}
} #}}}

Internationalized() #{{{
{
  echo true
} #}}}

SetLibraryPath() #{{{
{
    LD_LIBRARY_PATH="${1}"
} #}}}

GetLibraryPath() #{{{
{
    echo "${LD_LIBRARY_PATH}"
} #}}}

CheckConfigFileWriteable() #{{{
{
  local filename="${1}" message="${2}"

  if [ -f "${filename}" ]; then
    if [ -r "${filename}" ]; then
      if [ -w "${filename}" ]; then
        return 0
      else
        echo "Error: cannot write ${message} ${filename}" >&2
      fi
    else
      echo "Error: cannot read ${message} ${filename}" >&2
    fi
  elif [ -e "${filename}" ]; then
    echo "Error: not a file: ${message} ${filename}" >&2
  else
    echo "Error: cannot find ${message} ${filename}" >&2
  fi

  return 1
} #}}}

SwapConfigFiles() #{{{
{
  local config_file="${1}"
  local replacement="${2}"

  cp "${replacement}" "${config_file}-$$+" &&
    ln -f "${config_file}" "${config_file}-$$-" &&
    ln -f "${config_file}-$$+" "${config_file}" &&
    rm -f "${config_file}-$$+"
} #}}}

SelectEncryption() #{{{
{
  if ! [ -f ${dn0}/.installer/VVCModifier ] ; then
    return
  fi
  if [ ${use_defaults} -eq 1 ]; then #{{{
    cd ${install_dir} && ${dn0}/.installer/VVCModifier -f ${install_dir}/Global.vvc -g -b 110 True && cd - > /dev/null
  fi #}}}
LogMessage <<EOS
By default, the Agent encrypts data using an encryption method that is integrated
in the Agent. For audit purposes, some organizations require the Agent to use an
external encyption library that is provided. Using the external encryption library
can degrade Agent performance.

Please select one of the following:
[A] Encrypt data using the Integrated encryption method. Select this encryption method
    for the best Agent performance.
[B] Encrypt data using the External encryption library. Select this encryption method
    if it is required for audit purposes.

Note: To change the encryption method that is used, you must reinstall the Agent.
EOS

  enc_type=
  if [ ${use_defaults} -eq 0 ] ; then
    printf "Select option (A|B) (default A) "
    read enc_type
  fi

  case $enc_type in
    b|B)
    LogMessage "selecting B"
      cd ${install_dir} && ${dn0}/.installer/VVCModifier -f ${install_dir}/Global.vvc -g -b 110 False && cd - > /dev/null
    ;;
    *)
    LogMessage "selecting A"
      cd ${install_dir} && ${dn0}/.installer/VVCModifier -f ${install_dir}/Global.vvc -g -b 110 True && cd - > /dev/null
    ;;
  esac
} #}}}

DisplayEULA() #{{{
{
  if [ ${use_defaults} -eq 1 ]; then
     LogMessage "'-defaults' option implies user has read and agreed to the EULA."
     return
  fi

  if [ -f ${source_dir}/${license_file} ]; then
     clear
     echo "--------------------------------------------------------------------------------"
     echo "Before installing ${progVVAgent}, please read and agree to the terms"
     echo "and conditions of its user license."
     echo "--------------------------------------------------------------------------------"
     echo "Press <Enter> to continue."
     read CONTINUE
     more ${source_dir}/${license_file}
     GetEULAAnswer
  else
     LogMessage "EULA file is missing, abort the installation."
     exit 2
  fi
} #}}}

GetEULAAnswer() #{{{
{
  while true
  do
     local answer=""

     echo "Do you accept the terms and conditions of the license agreement?"
     echo -n "If yes, enter ‘y’ to accept the license agreement. If no, enter ‘n’ to cancel the installation: "
     read answer
     case "$answer" in
       [yY])
         LogMessage "user accepted license agreement."
         return
         ;;
       [nN])
         LogMessage "user declined license agreement."
         exit 2
         ;;
       *) ;;
     esac
  done
} #}}}

CopyEULA() #{{{
{
  if [ -f ${source_dir}/${license_file} ]; then
    cp ${source_dir}/${license_file} ${install_dir}/${license_file}
    LogMessage "copy EULA to installation directory."
  fi
} #}}}

CopyAcknowledgements() #{{{
{
  if [ -f ${source_dir}/${acknowledgements_file} ]; then
    cp ${source_dir}/${acknowledgements_file} ${install_dir}/${acknowledgements_file}
    LogMessage "copy acknowledgements to installation directory."
  fi
} #}}}