#!/bin/sh
######################################################################
#
# $Id: webjob-update-client,v 1.10 2012/01/07 08:01:24 mavrik Exp $
#
######################################################################
#
# Copyright 2007-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: Update a WebJob client via WebJob.
#
######################################################################

IFS=' 	
'

PATH=/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin

XER_OK=0
XER_Usage=1
XER_UnsupportedOs=2
XER_FailedTestMode=3
XER_MissingFile=4
XER_FailedCompare=5
XER_FailedImport=6
XER_FailedTestJob=7
XER_FailedBackup=8
XER_FailedDeploy=9
XER_FailedTestVersion=10
XER_UnsupportedProcessor=11

######################################################################
#
# WucMain
#
######################################################################

WucMain()
{
  ####################################################################
  #
  # Punch in and go to work.
  #
  ####################################################################

  PROGRAM=`basename $0`

  ####################################################################
  #
  # Process command line arguments.
  #
  ####################################################################

  SLAVE_MODE=0

  TEST_MODE=0

  WEBJOB_CLIENT=${WEBJOB_CLIENTID-unknown}

  WEBJOB_GROUP=

  WEBJOB_HOME=

  WEBJOB_MODE=0755

  WEBJOB_USER=root

  WEBJOB_TEST_JOB=testenv

  WEBJOB_VERSION=

  while getopts "f:g:H:j:m:stu:v:" OPTION ; do
    case "${OPTION}" in
    f)
      WEBJOB_CONFIG="${OPTARG}"
      ;;
    g)
      WEBJOB_GROUP="${OPTARG}"
      ;;
    H)
      WEBJOB_HOME="${OPTARG}"
      ;;
    j)
      WEBJOB_TEST_JOB="${OPTARG}"
      ;;
    m)
      WEBJOB_MODE="${OPTARG}"
      ;;
    s)
      SLAVE_MODE=1
      ;;
    t)
      TEST_MODE=1
      ;;
    u)
      WEBJOB_USER="${OPTARG}"
      ;;
    v)
      WEBJOB_VERSION="${OPTARG}"
      ;;
    *)
      WucUsage
      ;;
    esac
  done

  if [ ${OPTIND} -le $# ] ; then
    WucUsage
  fi

  if [ -z "${WEBJOB_VERSION}" ] ; then
    WucUsage
  fi

  PATH=${PATH}:${WEBJOB_HOME-/usr/local/webjob}/bin ; export PATH

  WEBJOB=${WEBJOB_HOME}/bin/webjob

  WEBJOB_NEW=${WEBJOB_HOME}/bin/webjob.new

  WEBJOB_OLD=${WEBJOB_HOME}/bin/webjob.old

  if [ -z "${WEBJOB_CONFIG}" ] ; then
    WEBJOB_CONFIG=${WEBJOB_HOME}/etc/upload.cfg
  fi

  ####################################################################
  #
  # Determine the current webjob version. Set a flag if it's 1.9.0
  # (ds17) or higher. Older clients use slightly different syntax.
  #
  ####################################################################

  ${WEBJOB} --version | awk '{ split($2,a,"."); major=a[1]; minor=a[2]; patch=a[3]; len=length($3); s=substr($3,2,2); if (s=="ds"){ state=0 } else if (s=="rc") { state=1 } else if (s=="sr") { state=2 } else { state=3 } build=substr($3,4,len-4); version=(major*268435456)+(minor*1048576)+(patch*4096)+(state*1024)+build; if (version >= 277872657) { exit 0 } else { exit 1 } }'
  if [ $? -eq 0 ] ; then
    NO_UPLOAD=1
  else
    NO_UPLOAD=0
  fi

  ####################################################################
  #
  # Determine the OS and processor architecture.
  #
  ####################################################################

  WEBJOB_OS=`uname -s 2> /dev/null`

  WEBJOB_PROCESSOR=`uname -p 2> /dev/null`

  case "${WEBJOB_OS}" in
  AIX)
    WEBJOB_BINARY="webjob-${WEBJOB_VERSION}-aix"
    WEBJOB_GROUP=system
    ;;
  FreeBSD)
    WEBJOB_BINARY="webjob-${WEBJOB_VERSION}-freebsd"
    WEBJOB_GROUP=wheel
    ;;
  Darwin)
    case "${WEBJOB_PROCESSOR}" in
    i386)
      WEBJOB_BINARY="webjob-${WEBJOB_VERSION}-macos-i386"
      ;;
    powerpc)
      WEBJOB_BINARY="webjob-${WEBJOB_VERSION}-macos-ppc"
      ;;
    *)
      echo "${PROGRAM}: Error='Unsupported processor architecture (${WEBJOB_PROCESSOR}).'" 1>&2
      exit ${XER_UnsupportedProcessor}
      ;;
    esac
    WEBJOB_GROUP=wheel
    ;;
  Linux)
    WEBJOB_BINARY="webjob-${WEBJOB_VERSION}-linux"
    WEBJOB_GROUP=root
    ;;
  SunOS)
    case "${WEBJOB_PROCESSOR}" in
    i386)
      WEBJOB_BINARY="webjob-${WEBJOB_VERSION}-solaris-i386"
      ;;
    sparc)
      WEBJOB_BINARY="webjob-${WEBJOB_VERSION}-solaris-sparc"
      ;;
    *)
      echo "${PROGRAM}: Error='Unsupported processor architecture (${WEBJOB_PROCESSOR}).'" 1>&2
      exit ${XER_UnsupportedProcessor}
      ;;
    esac
    WEBJOB_GROUP=other
    ;;
  *)
    echo "${PROGRAM}: Error='Unsupported OS (${WEBJOB_OS}).'" 1>&2
    exit ${XER_UnsupportedOs}
    ;;
  esac

  WEBJOB_PAD=${WEBJOB_BINARY}.pad

  ####################################################################
  #
  # Check for a deployed config file.
  #
  ####################################################################

  if [ ! -f ${WEBJOB_CONFIG} ] ; then
    echo "${PROGRAM}: Error='Unable to locate ${WEBJOB_CONFIG}. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
    exit ${XER_MissingFile}
  fi

  ####################################################################
  #
  # Check for a deployed binary.
  #
  ####################################################################

  if [ ! -f ${WEBJOB} ] ; then
    echo "${PROGRAM}: Error='Unable to locate ${WEBJOB}. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
    exit ${XER_MissingFile}
  fi

  ####################################################################
  #
  # If in test mode, import the new binary, and run a test job.
  #
  ####################################################################

  if [ ${TEST_MODE} -eq 1 ] ; then
    EXIT_CODE=${XER_OK}
    WucInstall "${WEBJOB}" "${WEBJOB_CONFIG}" "${WEBJOB_PAD}" "${WEBJOB_NEW}" "${WEBJOB_MODE}" "${WEBJOB_USER}" "${WEBJOB_GROUP}" "${NO_UPLOAD}"
    if [ $? -ne 0 ] ; then
      echo "${PROGRAM}: Error='Unable to import the new binary. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
      EXIT_CODE=${XER_FailedImport}
    else
      if [ ${NO_UPLOAD} -eq 1 ] ; then
        ${WEBJOB_NEW} -e -f ${WEBJOB_CONFIG} --NoUpload ${WEBJOB_TEST_JOB}
      else
        ${WEBJOB_NEW} -e -f ${WEBJOB_CONFIG} ${WEBJOB_TEST_JOB}
      fi
      if [ $? -ne 0 ] ; then
        echo "${PROGRAM}: Error='The new (imported) binary failed to run a test job. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
        EXIT_CODE=${XER_FailedTestJob}
      else
        MY_WEBJOB_VERSION=`${WEBJOB_NEW} -v`
        if [ $? -ne 0 ] ; then
          echo "${PROGRAM}: Error='The new (imported) binary failed to execute. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
          EXIT_CODE=${XER_FailedTestVersion}
        else
          echo "WEBJOB_VERSION=${MY_WEBJOB_VERSION}"
        fi
      fi
    fi
  fi

  ####################################################################
  #
  # Wipe the new slate clean.
  #
  ####################################################################

  if [ -f ${WEBJOB_NEW} ] ; then
    rm -f ${WEBJOB_NEW}
  fi

  ####################################################################
  #
  # If in test mode, it's time to exit stage left.
  #
  ####################################################################

  if [ ${TEST_MODE} -eq 1 ] ; then
    exit ${EXIT_CODE}
  fi

  ####################################################################
  #
  # Wipe the old slate clean. Note that Solaris ln(1) will fail if the
  # link already exists.
  #
  ####################################################################

  if [ -f ${WEBJOB_OLD} ] ; then
    rm -f ${WEBJOB_OLD}
  fi

  ####################################################################
  #
  # Make a backup of the deployed binary. Spawn a background job (if
  # not in slave mode) using the backup binary to finish the update.
  # This dance is used to avoid a core dump on Solaris platforms. An
  # alternate name for the update script is required to avoid a bug
  # that exits in WebJob clients prior to 1.7.0. Note that the eval
  # statement must be run in the background.
  #
  ####################################################################

  if [ ${SLAVE_MODE} -eq 1 ] ; then
    ln -f ${WEBJOB} ${WEBJOB_OLD}
    if [ $? -ne 0 ] ; then
      echo "${PROGRAM}: Error='Unable to backup the old binary. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
      exit ${XER_FailedBackup}
    fi
  else
    rm -f ${WEBJOB_OLD} # Solaris ln(1) will fail if the link already exists.
    ln -f ${WEBJOB} ${WEBJOB_OLD}
    if [ $? -ne 0 ] ; then
      echo "${PROGRAM}: Error='Unable to backup the old binary. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
      exit ${XER_FailedBackup}
    fi
    WEBJOB_COMMAND="${WEBJOB_OLD} -e -f ${WEBJOB_CONFIG} ${PROGRAM}.slave $* -s"
    echo "${PROGRAM}: Info='Spawning a slave in the background to finish the update... ${WEBJOB_COMMAND}'"
    eval ${WEBJOB_COMMAND} &
    exit ${XER_OK}
  fi

  ####################################################################
  #
  # Verify that the deployed and backup binaries are the same. This
  # is insurance, in case we need to restore the binary later.
  #
  ####################################################################

  cmp ${WEBJOB} ${WEBJOB_OLD} > /dev/null 2>&1
  if [ $? -ne 0 ] ; then
    echo "${PROGRAM}: Error='The deployed and backup binaries differ, so it is not safe to proceed. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
    exit ${XER_FailedCompare}
  fi

  ####################################################################
  #
  # Download and install the new binary using the backup binary.
  #
  ####################################################################

  WucInstall "${WEBJOB_OLD}" "${WEBJOB_CONFIG}" "${WEBJOB_PAD}" "${WEBJOB_NEW}" "${WEBJOB_MODE}" "${WEBJOB_USER}" "${WEBJOB_GROUP}" "${NO_UPLOAD}"
  if [ $? -ne 0 ] ; then
    echo "${PROGRAM}: Error='Unable to import the new binary. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
    exit ${XER_FailedImport}
  fi

  ####################################################################
  #
  # Verify that the new and deployed binaries differ. If they're the
  # same, no update is required.
  #
  ####################################################################

  cmp ${WEBJOB_NEW} ${WEBJOB} > /dev/null 2>&1
  if [ $? -eq 0 ] ; then
    echo "${PROGRAM}: Info='The new and deployed binaries are identical, so no update is required (client=${WEBJOB_CLIENT}).'"
    rm -f ${WEBJOB_NEW}
    exit ${XER_OK}
  fi

  ####################################################################
  #
  # Test the new binary with the existing config file.
  #
  ####################################################################

  if [ ${NO_UPLOAD} -eq 1 ] ; then
    ${WEBJOB_NEW} -e -f ${WEBJOB_CONFIG} --NoUpload ${WEBJOB_TEST_JOB}
  else
    ${WEBJOB_NEW} -e -f ${WEBJOB_CONFIG} ${WEBJOB_TEST_JOB}
  fi
  if [ $? -ne 0 ] ; then
    echo "${PROGRAM}: Error='The new (imported) binary failed to run a test job. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
    rm -f ${WEBJOB_NEW}
    exit ${XER_FailedTestJob}
  fi

  ####################################################################
  #
  # Move the new binary into place.
  #
  ####################################################################

  ln -f ${WEBJOB_NEW} ${WEBJOB}
  if [ $? -ne 0 ] ; then
    echo "${PROGRAM}: Error='Unable to deploy the new binary. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
    exit ${XER_FailedDeploy}
  fi
  rm -f ${WEBJOB_NEW}

  ####################################################################
  #
  # Test the new binary again. If this fails, attempt to restore the
  # original. If the deployed and backup binaries are identical, the
  # Solaris verson of ln(1) will fail, so review error messages with
  # care to determine if a manual recovery is required.
  #
  ####################################################################

  MY_WEBJOB_VERSION=`${WEBJOB} -v`
  if [ $? -ne 0 ] ; then
    echo "${PROGRAM}: Error='The new (deployed) binary failed to execute. Job aborted (client=${WEBJOB_CLIENT}).'" 1>&2
    ln -f ${WEBJOB_OLD} ${WEBJOB}
    if [ $? -ne 0 ] ; then
      echo "${PROGRAM}: Error='Unable to restore the old binary -- a manual recovery may be required. Update failed (${WEBJOB_CLIENT}).'" 1>&2
    fi
    exit ${XER_FailedTestVersion}
  fi
  echo "WEBJOB_VERSION=${MY_WEBJOB_VERSION}"

  ####################################################################
  #
  # Cleanup and go home.
  #
  ####################################################################

  exit ${XER_OK}
}


######################################################################
#
# WucInstall <webjob> <config> <pad> <path> <mode> <owner> <group>
#
######################################################################

WucInstall()
{
  MY_WEBJOB=$1
  MY_CFG_FILE=$2
  MY_PAD_FILE=$3
  MY_TARGET_PATH=$4 # Full path including filename.
  MY_TARGET_MODE=$5
  MY_TARGET_OWNER=$6
  MY_TARGET_GROUP=$7
  MY_NO_UPLOAD=$8

  MY_CP_CMD="{ cp -f %payload ${MY_TARGET_PATH} ; }"
  MY_CHMOD_CMD="{ chmod ${MY_TARGET_MODE} ${MY_TARGET_PATH} ; }"
  MY_CHOWN_CMD="{ chown ${MY_TARGET_OWNER}:${MY_TARGET_GROUP} ${MY_TARGET_PATH} ; }"
  MY_RM_CMD="{ rm -f %payload ; }"

  MY_ID=`id | sed 's/^uid=\([0-9]\{1,5\}\)(.*$/\1/;'`

  if [ X"${MY_ID}" = X"0" ] ; then
    MY_PAD_CMD="{ ${MY_CP_CMD} && ${MY_CHMOD_CMD} && ${MY_CHOWN_CMD} } ; ${MY_RM_CMD}"
  else
    MY_PAD_CMD="{ ${MY_CP_CMD} && ${MY_CHMOD_CMD} } ; ${MY_RM_CMD}"
  fi

  if [ ${MY_NO_UPLOAD} -eq 1 ] ; then
    ${MY_WEBJOB} -e -f ${MY_CFG_FILE} --NoUpload -- ${MY_PAD_FILE} ${MY_PAD_CMD}
  else
    ${MY_WEBJOB} -e -f ${MY_CFG_FILE} ${MY_PAD_FILE} ${MY_PAD_CMD}
  fi
}


######################################################################
#
# WucUsage
#
######################################################################

WucUsage()
{
  echo 1>&2
  echo "Usage: ${PROGRAM} [-st] [-f webjob-cfg] [-H webjob-home] [-j test-job] [-m mode] [-u user] [-g group] -v version" 1>&2
  echo 1>&2
  exit ${XER_Usage}
}

WucMain $*
