#!/usr/bin/env bash

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# The name of the script being executed.
OZONE_SHELL_EXECNAME="ozone"
MYNAME="${BASH_SOURCE-$0}"
bin=$(cd -P -- "$(dirname -- "${MYNAME}")" >/dev/null && pwd -P)
JVM_PID="$$"

## @description  build up the ozone command's usage text.
## @audience     public
## @stability    stable
## @replaceable  no
function ozone_usage
{
  ozone_add_option "--buildpaths" "attempt to add class files from build tree"
  ozone_add_option "--daemon (start|status|stop)" "operate on a daemon"
  ozone_add_option "--hostnames list[,of,host,names]" "hosts to use in worker mode"
  ozone_add_option "--hosts filename" "list of hosts to use in worker mode"
  ozone_add_option "--loglevel level" "set the log4j level for this command"
  ozone_add_option "--workers" "turn on worker mode"
  ozone_add_option "--jvmargs arguments" "append JVM options to any existing options defined in the OZONE_OPTS environment variable. Any defined in OZONE_CLIENT_OPTS will be append after these jvmargs"
  ozone_add_option "--validate (continue)" "validates if all jars as indicated in the corresponding OZONE_RUN_ARTIFACT_NAME classpath file are present, command execution shall continue post validation failure if 'continue' is passed"

  ozone_add_subcommand "auditparser" client "runs audit parser tool"
  ozone_add_subcommand "classpath" client "prints the class path needed for running ozone commands"
  ozone_add_subcommand "datanode" daemon "run a HDDS datanode"
  ozone_add_subcommand "envvars" client "display computed Hadoop environment variables"
  ozone_add_subcommand "daemonlog" admin "get/set the log level for each daemon"
  ozone_add_subcommand "freon" client "runs an ozone data generator"
  ozone_add_subcommand "fs" client "run a filesystem command on Ozone file system. Equivalent to 'hadoop fs'"
  ozone_add_subcommand "genconf" client "generate minimally required ozone configs and output to ozone-site.xml in specified path"
  ozone_add_subcommand "getconf" client "get ozone config values from configuration"
  ozone_add_subcommand "om" daemon "Ozone Manager"
  ozone_add_subcommand "scm" daemon "run the Storage Container Manager service"
  ozone_add_subcommand "s3g" daemon "run the S3 compatible REST gateway"
  ozone_add_subcommand "httpfs" daemon "run the HTTPFS compatible REST gateway"
  ozone_add_subcommand "csi" daemon "run the standalone CSI daemon"
  ozone_add_subcommand "recon" daemon "run the Recon service"
  ozone_add_subcommand "sh" client "command line interface for object store operations"
  ozone_add_subcommand "s3" client "command line interface for s3 related operations"
  ozone_add_subcommand "tenant" client "command line interface for multi-tenant related operations"
  ozone_add_subcommand "insight" client "tool to get runtime operation information"
  ozone_add_subcommand "version" client "print the version"
  ozone_add_subcommand "dtutil" client "operations related to delegation tokens"
  ozone_add_subcommand "admin" client "Ozone admin tool"
  ozone_add_subcommand "debug" client "Ozone debug tool"
  ozone_add_subcommand "checknative" client "checks if native libraries are loaded"

  ozone_generate_usage "${OZONE_SHELL_EXECNAME}" false
}

## @description  Default command handler for ozone command
## @audience     public
## @stability    stable
## @replaceable  no
## @param        CLI arguments
function ozonecmd_case
{
  subcmd=$1
  shift

  # Add JVM parameter (org.apache.ratis.thirdparty.io.netty.allocator.useCacheForAllThreads=false)
  # for disabling netty PooledByteBufAllocator thread caches for non-netty threads.
  # This parameter significantly reduces GC pressure for Datanode.
  # Corresponding Ratis issue https://issues.apache.org/jira/browse/RATIS-534.
  RATIS_OPTS="-Dorg.apache.ratis.thirdparty.io.netty.allocator.useCacheForAllThreads=false ${RATIS_OPTS}"
  # Add JVM parameter for Java 17
  OZONE_MODULE_ACCESS_ARGS=""

  # Get the version string
  JAVA_VERSION_STRING=$(java -version 2>&1 | head -n 1)

  # Extract the major version number
  JAVA_MAJOR_VERSION=$(echo "$JAVA_VERSION_STRING" | sed -E -n 's/.* version "([^.-]*).*"/\1/p' | cut -d' ' -f1)

  # populate JVM args based on java version
  if [[ "${JAVA_MAJOR_VERSION}" == "17" ]]; then
      OZONE_MODULE_ACCESS_ARGS="--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED --add-exports java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED"
  fi
  if [[ "${JAVA_MAJOR_VERSION}" -ge 9 ]]; then
    OZONE_MODULE_ACCESS_ARGS="${OZONE_MODULE_ACCESS_ARGS} --add-opens java.base/java.nio=ALL-UNNAMED"
  fi

  case ${subcmd} in
    auditparser)
      OZONE_CLASSNAME=org.apache.hadoop.ozone.audit.parser.AuditParser
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    classpath)
      if [[ "$#" -gt 0 ]]; then
        OZONE_RUN_ARTIFACT_NAME="$1"
        OZONE_CLASSNAME="org.apache.hadoop.util.Classpath"
        #remove the artifact name and replace it with glob
        # (We need at least one argument to execute the Classpath helper class)
        OZONE_SUBCMD_ARGS[0]="--glob"
      else
        ozone_finalize
        echo "Usage: ozone classpath <ARTIFACTNAME>"
        echo "Where the artifact name is one of:"
        echo ""
        ls -1 ${OZONE_HOME}/share/ozone/classpath/ | sed 's/.classpath//'
        exit -1
      fi
    ;;
    datanode)
      OZONE_SUBCMD_SUPPORTDAEMONIZATION="true"
      ozone_deprecate_envvar HDDS_DN_OPTS OZONE_DATANODE_OPTS
      OZONE_DATANODE_OPTS="${RATIS_OPTS} ${OZONE_DATANODE_OPTS}"
      OZONE_DATANODE_OPTS="-Dlog4j.configurationFile=${OZONE_CONF_DIR}/dn-audit-log4j2.properties,${OZONE_CONF_DIR}/dn-container-log4j2.properties ${OZONE_DATANODE_OPTS}"
      OZONE_DATANODE_OPTS="-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector ${OZONE_DATANODE_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"
      OZONE_CLASSNAME=org.apache.hadoop.ozone.HddsDatanodeService
      OZONE_RUN_ARTIFACT_NAME="ozone-datanode"
    ;;
    envvars)
      echo "JAVA_HOME='${JAVA_HOME}'"
      echo "OZONE_HOME='${OZONE_HOME}'"
      echo "OZONE_CONF_DIR='${OZONE_CONF_DIR}'"
      echo "OZONE_LIBEXEC_DIR='${OZONE_LIBEXEC_DIR}'"
      echo "HDDS_LIB_JARS_DIR='${HDDS_LIB_JARS_DIR}'"
      if [[ -n "${QATESTMODE}" ]]; then
        echo "MYNAME=${MYNAME}"
        echo "OZONE_SHELL_EXECNAME=${OZONE_SHELL_EXECNAME}"
      fi
      exit 0
    ;;
    freon)
      OZONE_CLASSNAME=org.apache.hadoop.ozone.freon.Freon
      OZONE_FREON_OPTS="${OZONE_FREON_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    getconf)
      OZONE_CLASSNAME=org.apache.hadoop.ozone.conf.OzoneGetConf;
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    om)
      OZONE_SUBCMD_SUPPORTDAEMONIZATION="true"
      OZONE_CLASSNAME=org.apache.hadoop.ozone.om.OzoneManagerStarter
      ozone_deprecate_envvar HDFS_OM_OPTS OZONE_OM_OPTS
      OZONE_OM_OPTS="${RATIS_OPTS} ${OZONE_OM_OPTS}"
      OZONE_OM_OPTS="${OZONE_OM_OPTS} -Dlog4j.configurationFile=${OZONE_CONF_DIR}/om-audit-log4j2.properties"
      OZONE_OM_OPTS="-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector ${OZONE_OM_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"
      OZONE_RUN_ARTIFACT_NAME="ozone-manager"
    ;;
    sh | shell)
      OZONE_CLASSNAME=org.apache.hadoop.ozone.shell.OzoneShell
      ozone_deprecate_envvar HDFS_OM_SH_OPTS OZONE_SH_OPTS
      OZONE_SH_OPTS="${OZONE_SH_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    s3)
      OZONE_CLASSNAME=org.apache.hadoop.ozone.shell.s3.S3Shell
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    scm)
      OZONE_SUBCMD_SUPPORTDAEMONIZATION="true"
      OZONE_CLASSNAME='org.apache.hadoop.hdds.scm.server.StorageContainerManagerStarter'
      ozone_deprecate_envvar HDFS_STORAGECONTAINERMANAGER_OPTS OZONE_SCM_OPTS
      OZONE_SCM_OPTS="${RATIS_OPTS} ${OZONE_SCM_OPTS}"
      OZONE_SCM_OPTS="${OZONE_SCM_OPTS} -Dlog4j.configurationFile=${OZONE_CONF_DIR}/scm-audit-log4j2.properties"
      OZONE_SCM_OPTS="-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector ${OZONE_SCM_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"
      OZONE_RUN_ARTIFACT_NAME="hdds-server-scm"
    ;;
    s3g)
      OZONE_SUBCMD_SUPPORTDAEMONIZATION="true"
      OZONE_CLASSNAME='org.apache.hadoop.ozone.s3.Gateway'
      OZONE_S3G_OPTS="${OZONE_S3G_OPTS} -Dlog4j.configurationFile=${OZONE_CONF_DIR}/s3g-audit-log4j2.properties ${OZONE_MODULE_ACCESS_ARGS}"
      OZONE_RUN_ARTIFACT_NAME="ozone-s3gateway"
    ;;
    httpfs)
      OZONE_SUBCMD_SUPPORTDAEMONIZATION="true"
      OZONE_OPTS="${OZONE_OPTS} -Dhttpfs.home.dir=${OZONE_HOME} -Dhttpfs.config.dir=${OZONE_CONF_DIR} -Dhttpfs.log.dir=${OZONE_HOME}/log -Dhttpfs.temp.dir=${OZONE_HOME}/temp ${OZONE_MODULE_ACCESS_ARGS}"
      OZONE_CLASSNAME='org.apache.ozone.fs.http.server.HttpFSServerWebServer'
      OZONE_RUN_ARTIFACT_NAME="ozone-httpfsgateway"
    ;;
    tenant)
      OZONE_CLASSNAME=org.apache.hadoop.ozone.shell.tenant.TenantShell
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    csi)
      OZONE_SUBCMD_SUPPORTDAEMONIZATION="true"
      OZONE_CLASSNAME='org.apache.hadoop.ozone.csi.CsiServer'
      OZONE_RUN_ARTIFACT_NAME="ozone-csi"
    ;;
    recon)
      OZONE_SUBCMD_SUPPORTDAEMONIZATION="true"
      OZONE_CLASSNAME='org.apache.hadoop.ozone.recon.ReconServer'
      OZONE_RECON_OPTS="${OZONE_RECON_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"
      OZONE_RUN_ARTIFACT_NAME="ozone-recon"
    ;;
    fs)
      OZONE_CLASSNAME=org.apache.hadoop.fs.ozone.OzoneFsShell
      OZONE_FS_OPTS="${OZONE_FS_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    daemonlog)
      OZONE_CLASSNAME=org.apache.hadoop.log.LogLevel
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    insight)
      OZONE_CLASSNAME=org.apache.hadoop.ozone.insight.Insight
      OZONE_RUN_ARTIFACT_NAME="ozone-insight"
    ;;
    version)
      OZONE_CLASSNAME=org.apache.hadoop.ozone.util.OzoneVersionInfo
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    genconf)
      OZONE_CLASSNAME=org.apache.hadoop.ozone.genconf.GenerateOzoneRequiredConfigurations
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    dtutil)
      OZONE_CLASSNAME=org.apache.hadoop.security.token.DtUtilShell
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    admin)
      OZONE_CLASSNAME=org.apache.hadoop.hdds.cli.OzoneAdmin
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    debug)
      OZONE_CLASSNAME=org.apache.hadoop.ozone.debug.OzoneDebug
      OZONE_DEBUG_OPTS="${OZONE_DEBUG_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"
      OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    checknative)
        OZONE_CLASSNAME=org.apache.hadoop.ozone.shell.checknative.CheckNative
        OZONE_RUN_ARTIFACT_NAME="ozone-tools"
    ;;
    *)
      OZONE_CLASSNAME="${subcmd}"
      if ! ozone_validate_classname "${OZONE_CLASSNAME}"; then
        ozone_exit_with_usage 1
      fi
    ;;
  esac
}

## @description  turn off logging for CLI by default
## @audience     private
function ozone_suppress_shell_log
{
  if [[ "${OZONE_RUN_ARTIFACT_NAME}" == "ozone-tools" ]] \
      && [[ "${OZONE_CLASSNAME}" != "org.apache.hadoop.ozone.freon.Freon" ]] \
      && [[ -z "${OZONE_ORIGINAL_LOGLEVEL}" ]] \
      && [[ -z "${OZONE_ORIGINAL_ROOT_LOGGER}" ]]; then
    OZONE_LOGLEVEL=OFF
    OZONE_ROOT_LOGGER="${OZONE_LOGLEVEL},console"
  fi
}

# load functions
for dir in "${OZONE_LIBEXEC_DIR}" "${OZONE_HOME}/libexec" "${HADOOP_LIBEXEC_DIR}" "${HADOOP_HOME}/libexec" "${bin}/../libexec"; do
  if [[ -e "${dir}/ozone-functions.sh" ]]; then
    . "${dir}/ozone-functions.sh"
    if declare -F ozone_bootstrap >& /dev/null; then
      break
    fi
  fi
done

if ! declare -F ozone_bootstrap >& /dev/null; then
  echo "ERROR: Cannot find ozone-functions.sh." 2>&1
  exit 1
fi

ozone_bootstrap
. "${OZONE_LIBEXEC_DIR}/ozone-config.sh"

# now that we have support code, let's abs MYNAME so we can use it later
MYNAME=$(ozone_abs "${MYNAME}")

if [[ $# = 0 ]]; then
  ozone_exit_with_usage 1
fi

OZONE_SUBCMD=$1
shift


if ozone_need_reexec ozone "${OZONE_SUBCMD}"; then
  ozone_uservar_su ozone "${OZONE_SUBCMD}" \
    "${MYNAME}" \
    "--reexec" \
    "${OZONE_USER_PARAMS[@]}"
  exit $?
fi

ozone_verify_user_perm "${OZONE_SHELL_EXECNAME}" "${OZONE_SUBCMD}"

OZONE_SUBCMD_ARGS=("$@")

if declare -f ozone_subcommand_"${OZONE_SUBCMD}" >/dev/null 2>&1; then
  ozone_debug "Calling dynamically: ozone_subcommand_${OZONE_SUBCMD} ${OZONE_SUBCMD_ARGS[*]}"
  "ozone_subcommand_${OZONE_SUBCMD}" "${OZONE_SUBCMD_ARGS[@]}"
else
  ozonecmd_case "${OZONE_SUBCMD}" "${OZONE_SUBCMD_ARGS[@]}"
fi

ozone_validate_classpath

ozone_suppress_shell_log
ozone_assemble_classpath

ozone_add_client_opts

if [[ ${OZONE_WORKER_MODE} = true ]]; then
  ozone_worker_mode_execute "${OZONE_HOME}/bin/ozone" "${OZONE_USER_PARAMS[@]}"
  exit $?
fi

ozone_subcommand_opts "${OZONE_SHELL_EXECNAME}" "${OZONE_SUBCMD}"

ozone_add_default_gc_opts

# everything is in globals at this point, so call the generic handler
ozone_generic_java_subcmd_handler
