#!/bin/sh
######################################################################
#
# $Id: webjob-log-roller,v 1.13 2012/01/07 08:01:20 mavrik Exp $
#
######################################################################
#
# Copyright 2003-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: Compress and archive WebJob log files.
#
######################################################################

IFS=' 	
'

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

PROGRAM=`basename ${0}`

XER_OK="0"
XER_Usage="1"
XER_BootStrap="2"
XER_ProcessArguments="3"
XER_DoLogRoll="4"

######################################################################
#
# WlrBootStrap
#
######################################################################

WlrBootStrap()
{
  WLR_ARCHIVE_GID="0"
  WLR_ARCHIVE_MODE="0640"
  WLR_ARCHIVE_UID="0"
  WLR_COMPRESSION_METHOD="gzip"
  WLR_LIST_LOG_FILES="0"
  WLR_LOG_DIR="" # This is a required command line argument.
  WLR_LOG_FILES="
jqd.log
nph-config.log
nph-config-hook.err
nph-config-hook.log
nph-config-hook.out
nph-webjob.log
nph-webjob-hook.err
nph-webjob-hook.log
nph-webjob-hook.out
nph-webjob-trigger.log
register.log
"
  WLR_LOG_GID="0"
  WLR_LOG_MODE="0640"
  WLR_LOG_UID="" # This is a required command line argument.
}

######################################################################
#
# WlrDoLogRoll
#
######################################################################

WlrDoLogRoll()
{
  MY_LOG_DIR=${1}
  MY_LOG_FILES=${2}
  MY_COMPRESSION_METHOD=${3}
  MY_LOG_UID=${4}
  MY_LOG_GID=${5}
  MY_LOG_MODE=${6}
  MY_ARCHIVE_UID=${7}
  MY_ARCHIVE_GID=${8}
  MY_ARCHIVE_MODE=${9}
  MY_HOOK=${10}

  MY_DATETIME=`date +%Y-%m-%d_%H%M`

  ####################################################################
  #
  # Determine the compression method.
  #
  ####################################################################

  case "${MY_COMPRESSION_METHOD}" in
  bzip2)
    MY_COMPRESSOR="bzip2"
    MY_ARCHIVE_EXT=".bz2"
    ;;
  compress)
    MY_COMPRESSOR="compress"
    MY_ARCHIVE_EXT=".Z"
    ;;
  gzip)
    MY_COMPRESSOR="gzip"
    MY_ARCHIVE_EXT=".gz"
    ;;
  none)
    MY_COMPRESSOR=""
    MY_ARCHIVE_EXT=""
    ;;
  *)
    ERROR="Invalid compression method (${MY_COMPRESSION_METHOD}). Use \"bzip2\", \"compress\", \"gzip\", or \"none\"."
    return 1
    ;;
  esac

  ####################################################################
  #
  # Check to see if there are conflicts with any files in the group.
  #
  ####################################################################

  ERRORS="0"
  for MY_LOG_NAME in ${MY_LOG_FILES} ; do
    case "${MY_LOG_NAME}" in
    *.err)
      MY_LOG_EXT=".err"
      MY_LOG_BASE=`basename ${MY_LOG_NAME} .err`
      ;;
    *.log)
      MY_LOG_EXT=".log"
      MY_LOG_BASE=`basename ${MY_LOG_NAME} .log`
      ;;
    *.out)
      MY_LOG_EXT=".out"
      MY_LOG_BASE=`basename ${MY_LOG_NAME} .out`
      ;;
    *)
      ERROR="Log file (${MY_LOG_NAME}) has an unrecognized extension. That shouldn't happen."
      return 1
      ;;
    esac
    MY_LOG_FILE="${MY_LOG_DIR}/${MY_LOG_NAME}"
    if [ -f ${MY_LOG_FILE} ] ; then
      MY_ARCHIVE_FILE="${MY_LOG_DIR}/${MY_LOG_BASE}.${MY_DATETIME}${MY_LOG_EXT}"
      if [ -f ${MY_ARCHIVE_FILE} ] ; then
        echo "${PROGRAM}: Error='${MY_ARCHIVE_FILE} already exists.'" 1>&2
        ERRORS="1"
      fi
      if [ -n "${MY_COMPRESSOR}" -a -f ${MY_ARCHIVE_FILE}${MY_ARCHIVE_EXT} ] ; then
        echo "${PROGRAM}: Error='${MY_ARCHIVE_FILE}${MY_ARCHIVE_EXT} already exists.'" 1>&2
        ERRORS="1"
      fi
    else
      echo "${PROGRAM}: Warning='${MY_LOG_FILE} does not exist, so it will be created.'" 1>&2
    fi
  done

  if [ ${ERRORS} -ne 0 ] ; then
    ERROR="There is a conflict with one or more files in the group. The entire operation has been aborted."
    return 1
  fi

  ####################################################################
  #
  # There were no conflicts, so roll up the group. This code does not
  # handle race conditions. Therefore, if a competing process creates
  # a given output file before this process does, that file will get
  # clobbered by this process -- and vice versa. If this becomes an
  # operational issue, some type of locking mechanism will be needed.
  #
  ####################################################################

  for MY_LOG_NAME in ${MY_LOG_FILES} ; do
    case "${MY_LOG_NAME}" in
    *.err)
      MY_LOG_EXT=".err"
      MY_LOG_BASE=`basename ${MY_LOG_NAME} .err`
      ;;
    *.log)
      MY_LOG_EXT=".log"
      MY_LOG_BASE=`basename ${MY_LOG_NAME} .log`
      ;;
    *.out)
      MY_LOG_EXT=".out"
      MY_LOG_BASE=`basename ${MY_LOG_NAME} .out`
      ;;
    *)
      MY_LOG_EXT=""
      MY_LOG_BASE=`basename ${MY_LOG_NAME}`
      ;;
    esac
    MY_LOG_FILE="${MY_LOG_DIR}/${MY_LOG_NAME}"
    if [ -f ${MY_LOG_FILE} ] ; then
      MY_ARCHIVE_FILE="${MY_LOG_DIR}/${MY_LOG_BASE}.${MY_DATETIME}${MY_LOG_EXT}"
      mv ${MY_LOG_FILE} ${MY_ARCHIVE_FILE}
      if [ $? -eq 0 ] ; then
      (
        chmod ${MY_ARCHIVE_MODE} ${MY_ARCHIVE_FILE}
        chown ${MY_ARCHIVE_UID}:${MY_ARCHIVE_GID} ${MY_ARCHIVE_FILE}
        if [ -n "${MY_HOOK}" ] ; then
          ${MY_HOOK} -f ${MY_ARCHIVE_FILE}
          MY_STATUS=$?
          if [ ${MY_STATUS} -ne 0 ] ; then
            echo "${PROGRAM}: Error='Hook command (${MY_HOOK} -f ${MY_ARCHIVE_FILE}) exited with a nonzero status (${MY_STATUS}).'" 1>&2
          fi
        fi
        if [ -n "${MY_COMPRESSOR}" ] ; then
          nice ${MY_COMPRESSOR} -f ${MY_ARCHIVE_FILE}
        fi
      ) &
      else
        echo "${PROGRAM}: Error='Unable to rename ${MY_LOG_FILE} as ${MY_ARCHIVE_FILE}. Log roll is incomplete.'" 1>&2
      fi
    fi
    if [ ! -f ${MY_LOG_FILE} ] ; then
      touch ${MY_LOG_FILE}
      if [ $? -eq 0 ] ; then
        chmod ${MY_LOG_MODE} ${MY_LOG_FILE}
        chown ${MY_LOG_UID}:${MY_LOG_GID} ${MY_LOG_FILE}
      else
        echo "${PROGRAM}: Error='Unable to create ${MY_LOG_FILE}. Log messages may get lost until a new file can be created.'" 1>&2
      fi
    fi
  done

  return 0
}

######################################################################
#
# WlrProcessArguments
#
######################################################################

WlrProcessArguments()
{
  while getopts "c:d:G:g:h:lM:m:U:u:" OPTION ; do
    case "${OPTION}" in
    c)
      WLR_COMPRESSION_METHOD="${OPTARG}"
      ;;
    d)
      WLR_LOG_DIR="${OPTARG}"
      ;;
    G)
      WLR_ARCHIVE_GID="${OPTARG}"
      ;;
    g)
      WLR_LOG_GID="${OPTARG}"
      ;;
    h)
      WLR_HOOK="${OPTARG}"
      ;;
    l)
      WLR_LIST_LOG_FILES="1"
      ;;
    M)
      WLR_ARCHIVE_MODE="${OPTARG}"
      ;;
    m)
      WLR_LOG_MODE="${OPTARG}"
      ;;
    U)
      WLR_ARCHIVE_UID="${OPTARG}"
      ;;
    u)
      WLR_LOG_UID="${OPTARG}"
      ;;
    *)
      WlrUsage
      ;;
    esac
  done

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

  if [ -z "${WLR_LOG_DIR}" ] ; then
    WlrUsage
  fi

  if [ -z "${WLR_LOG_UID}" ] ; then
    WlrUsage
  fi

  return 0
}

######################################################################
#
# WlrUsage
#
######################################################################

WlrUsage()
{
  echo 1>&2
  echo "Usage: ${PROGRAM} [-l] [-c {bzip2|compress|gzip|none}] [-G archive-gid] [-g log-gid] [-h hook-command] [-M archive-mode] [-m log-mode] [-U archive-uid] -d log-dir -u log-uid" 1>&2
  echo 1>&2
  exit ${XER_Usage}
}

######################################################################
#
# WlrMain
#
######################################################################

WlrMain()
{
  WlrBootStrap
  if [ $? -ne 0 ] ; then
    echo "${PROGRAM}: Error='${ERROR-?}'" 1>&2 ; return ${XER_BootStrap}
  fi
  WlrProcessArguments $*
  if [ $? -ne 0 ] ; then
    echo "${PROGRAM}: Error='${ERROR-?}'" 1>&2 ; return ${XER_ProcessArguments}
  fi
  WLR_LOG_CONF="${WLR_LOG_DIR}/log-roller.conf"
  if [ -f ${WLR_LOG_CONF} -a -s ${WLR_LOG_CONF} ] ; then
    WLR_LOG_FILES=`egrep '^[0-9A-Za-z._-]+$' ${WLR_LOG_CONF} 2> /dev/null`
  fi
  if [ ${WLR_LIST_LOG_FILES} -eq 1 ] ; then
    for MY_LOG_NAME in ${WLR_LOG_FILES} ; do
      echo "${MY_LOG_NAME}"
    done
    exit 0
  fi
  WlrDoLogRoll "${WLR_LOG_DIR}" "${WLR_LOG_FILES}" "${WLR_COMPRESSION_METHOD}" "${WLR_LOG_UID}" "${WLR_LOG_GID}" "${WLR_LOG_MODE}" "${WLR_ARCHIVE_UID}" "${WLR_ARCHIVE_GID}" "${WLR_ARCHIVE_MODE}" "${WLR_HOOK}"
  if [ $? -ne 0 ] ; then
    echo "${PROGRAM}: Error='${ERROR-?}'" 1>&2 ; return ${XER_DoLogRoll}
  fi
  return ${XER_OK}
}

WlrMain $* ; exit $?
