#!/bin/sh
# @description force run agent policies
# @man This command will force the agent to enforce current policies.
# @man You can run *rudder agent update* before to update the policies.
# @man +
# @man *Options*:
# @man +
# @man *-u*: update policy before running the agent (this is now the default)
# @man +
# @man *-l*: do not update policy before running the agent
# @man +
# @man *-i*: run the agent in information mode, prints basic information
# @man +
# @man *-v*: run the agent in verbose mode, prints detailed information (reports won't be sent to the server)
# @man +
# @man *-d*: run the agent in debug mode, prints low-level information (reports won't be sent to the server)
# @man +
# @man *-q*: run the agent in quiet mode (display only error messages)
# @man +
# @man *-g*: run the agent in full compliance mode (even if change only has been configured)
# @man +
# @man *-w*: show full strings, never cut output
# @man +
# @man *-c*: run the agent without color output
# @man +
# @man *-T*: display timing information
# @man +
# @man *-r*: run the agent with raw output
# @man +
# @man *-R*: run the agent in completely unparsed mode, with no return code of 1 in case of error. A little faster.
# @man +
# @man *-N*: do not write log in outputs dir (used when called internally)
# @man +
# @man *-b*: run the agent on a specific bundle, this is a debug command that should generally not be used
# @man +
# @man *-D*: define a class for this run
# @man +
# @man *-f*: run the agent even if it is disabled
# @man +
# @man *-e*: exit with an error if there was an error during policy application
# @man +
# @man *-E*: exit with an error if there a non compliance
# @man +
# @man *Exit codes*:
# @man +
# @man *0*: Agent ran normally
# @man +
# @man *1*: Agent encountered a critical error and could not run properly
# @man +
# @man *2*: Some policy encountered and error and *-e* parameter was passed
# @man +
# @man *3*: Some policy encountered a non compliance and *-E* parameter was passed

. "${BASEDIR}/../lib/common.sh"
. "${BASEDIR}/../lib/cfengine_parsing.sh"
. "${BASEDIR}/../lib/report.sh"

bootstrap_check

UPDATE=true
UPDATE_OPTIONS=""
# CLASSES to define
CLASSES=""
# Ignore disable-agent flag
FORCE=0
PARTIAL_RUN=0
OUTPUT_LOG="true"

# By default, not to mess log file
COLOR="-Cnever"

# Should we send the report to the server?
# When debugging, reports will be huge. We can skip HTTP reporting in verbose/debug
DO_HTTP="true"
[ "${RUDDER_REPORT_MODE}" = "reports-disabled" ] && DO_HTTP="false"

while getopts "uiIvldqgwrRNcTb:D:feE" opt; do
  case $opt in
    u)
      UPDATE=true
      ;;
    l)
      UPDATE=false
      ;;
    i|I)
      DISPLAY_INFO=1
      QUIET=0
      UPDATE_OPTIONS="${UPDATE_OPTIONS} -i"
      ;;
    v)
      VERBOSITY="-v ${VERBOSE_CLASS}"
      DISPLAY_INFO=1
      QUIET=0
      UPDATE_OPTIONS="${UPDATE_OPTIONS} -v"
      COLOR="-Calways"
      DO_HTTP="false"
      ;;
    d)
      VERBOSITY="-d ${DEBUG_CLASS}"
      DISPLAY_INFO=1
      QUIET=0
      UPDATE_OPTIONS="${UPDATE_OPTIONS} -d"
      COLOR="-Calways"
      DO_HTTP="false"
      ;;
    q)
      DISPLAY_INFO=0
      QUIET=1
      UPDATE_OPTIONS="${UPDATE_OPTIONS} -d"
      ;;
    g)
      CLASSES="${CLASSES},force_full_compliance"
      FULL_COMPLIANCE=1
      ;;
    w)
      FULL_STRINGS=1
      ;;
    c)
      clear_colors
      UPDATE_OPTIONS="${UPDATE_OPTIONS} -c"
      ;;
    T)
      TIMING=1
      ;;
    r)
      SUMMARY_ONLY=1
      DISPLAY_INFO=1
      ;;
    R)
      PRETTY="cat"
      ;;
    N)
      OUTPUT_LOG="false"
      ;;
    b)
      BUNDLE="-b ${OPTARG}"
      # Tell the parser we don't expect a full run
      PARTIAL_RUN=1
      ;;
    D)
      CLASSES="${CLASSES},${OPTARG}"
      ;;
    f)
      FORCE=1
      ;;
    e)
      ERROR_FAIL=1
      ;;
    E)
      NONCOMPLIANT_FAIL=1
      ;;
  esac
done

printf "${VERSION}\nNode uuid: ${UUID}\n"

if [ ${FORCE} -eq 0 ] && [ -e ${RUDDER_DIR}/etc/disable-agent ]; then
  printf "\n${RED}error${NORMAL}: The Rudder agent is currently disabled. You can enable it with 'rudder agent enable'.\n" 1>&2
  exit 1
fi

if [ ${FORCE} -eq 1 ]; then
  CLASSES="${CLASSES},ignore_disable_agent"
  UPDATE_OPTIONS="${UPDATE_OPTIONS} -D ignore_disable_agent"
fi

if [ -n "${CLASSES}" ]
then
  # remove the first comma, yes it's posix
  CLASS="-D ${CLASSES#,}"
fi

code1=0
if [ "${UPDATE}" = "true" ]; then
  ${RUDDER_BIN} agent update "${UPDATE_OPTIONS}"
  code1=$?
fi

# If not launched from cf-execd, we need to take care of logging in outputs
if [ "${OUTPUT_LOG}" = "true" ]; then
  # keep same name structure as cf-execd
  logfile=$(echo "cf_$(get_hostname | tr 'A-Z' 'a-z')__$(LANG=C date +%s)_$(LANG=C date +"%a %b %e %H %M %S %Y")_0" | sed 's/[^a-zA-Z0-9]/_/g')
  logdir=${RUDDER_VAR}/cfengine-community/outputs
  touch "${logdir}/${logfile}"
  chmod 600 "${logdir}/${logfile}"
  log_outputs="tee ${logdir}/${logfile}"
else
  log_outputs="cat"
fi

# If we send the report, we need to take care of timestamped logging
if [ "${DO_HTTP}" = "true" ]; then
  timestamp="${BASEDIR}/../lib/timestamp"
  report_file_name=$(LANG=C /bin/date -u "+%Y-%m-%dT%T+00:00")@${UUID}.log
  report_file="${TMP_REPORTS_DIR}/${report_file_name}"
  touch "${report_file}"
  chmod 600 "${report_file}"
  runlog_output="tee ${report_file}"
else
  timestamp="cat"
  runlog_output="cat"
fi

"${RUDDER_DIR}/bin/cf-agent" ${VERBOSITY} ${COLOR} -K ${BUNDLE} ${CLASS} | $log_outputs | $timestamp | $runlog_output | eval ${PRETTY}
code2=$?

if [ "${OUTPUT_LOG}" = "true" ]; then
  ln -sf ${logdir}/${logfile} ${logdir}/previous
fi

# Source again to detect the "post-run" environment
. "${BASEDIR}/../lib/report.sh"

if [ "${DO_HTTP}" = "true" ]; then
  compress_and_sign "${report_file_name}"
fi

# merge exit codes (this is the eval exit code ... POSIX ...)
[ $code1 -ne 0 ] && exit $code1
exit $code2
