#!/bin/sh
#
# Copyright 2016-2025 VMware, Inc. All rights reserved.
#
# nsx-nestdb:
#   Start/Stop the NSX NestDB Server
#
### BEGIN INIT INFO
# Provides:          nsx-nestdb
# Required-Start:    $network $local_fs
# Required-Stop:     $network $local_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: NSX NestDB Server
### END INIT INFO

# lsb hook for systemd
. /lib/lsb/init-functions

export LD_LIBRARY_PATH=/opt/vmware/nsx-nestdb/lib
export LC_ALL=C

NSX_ISSUE_FILE=/etc/nsx_issue
NSX_VMC_CONFIG=/config/petronas
NESTDB_METRICS_FOLDER=/var/log/vmware/nsx-nestdb

if [ -f $NSX_ISSUE_FILE ] && grep -q -E "nsx-edge|nsx-autonomous-edge|hcx-edge|nsx-public-cloud-gateway" $NSX_ISSUE_FILE; then
   NESTDB_DB_PREFIX=/config/vmware/nsx/nestdb
else
   NESTDB_DB_PREFIX=/var/lib/vmware/nsx/nestdb
fi

if [ -f $NSX_ISSUE_FILE ] && grep -q -E "nsx-public-cloud-gateway" $NSX_ISSUE_FILE; then
   NESTDB_LISTEN_ENDPOINTS="--listen unix:///var/run/vmware/nestdb/nestdb-server.sock \
      --listen tcp://127.0.0.1:2480 \
      --listen ssl-unix:///var/run/vmware/nestdb/nestdb-server-ssl.sock"
elif [ -f $NSX_ISSUE_FILE ] && grep -q -E "nsx-autonomous-edge|hcx-edge" $NSX_ISSUE_FILE ;then
   NESTDB_LISTEN_ENDPOINTS="--listen unix:///var/run/vmware/nestdb/nestdb-server.sock \
      --listen ssl-tcp://0.0.0.0:2480 \
      --listen ssl-unix:///var/run/vmware/nestdb/nestdb-server-ssl.sock"
elif [ -f $NSX_ISSUE_FILE ] && grep -q -E "nsx-edge" $NSX_ISSUE_FILE; then
   NESTDB_LISTEN_ENDPOINTS="--listen unix:///var/run/vmware/nestdb/nestdb-server.sock \
      --listen ssl-unix:///var/run/vmware/nestdb/nestdb-server-ssl.sock"
else
   NESTDB_LISTEN_ENDPOINTS="--listen unix:///var/run/vmware/nestdb/nestdb-server.sock \
      --listen ssl-unix:///var/run/vmware/nestdb/nestdb-server-ssl.sock"
fi

if [ -f $NSX_VMC_CONFIG ]; then
   LISTEN_IP=$(sed -n 's/mgmt_ip:\(.*\)$/\1/p' $NSX_VMC_CONFIG)
   LISTEN_PORT=$(sed -n 's/nestdb_port:\(.*\)$/\1/p' $NSX_VMC_CONFIG)
   if [ ! -z $LISTEN_IP ] && [ ! -z $LISTEN_PORT ]; then
       VMC_ENDPOINT=" --listen tcp://$LISTEN_IP:$LISTEN_PORT"
       NESTDB_LISTEN_ENDPOINTS="${NESTDB_LISTEN_ENDPOINTS}${VMC_ENDPOINT}"
   fi
fi

WATCHDOG=/opt/vmware/nsx-nestdb/bin/watchdog.sh

PROG=/opt/vmware/nsx-nestdb/bin/nestdb-server
PROG_TAG=NSX-NESTDB
PROG_ARGS="--schema /opt/vmware/nsx-nestdb/schema/nestdb.schema \
    --database ${NESTDB_DB_PREFIX}/db \
    --txn_log_size 209715200 \
    --mem_stats_interval 300 \
    --mem_release_interval 86400 \
    --metrics_text_publisher \
    --metrics_rpc_publisher ${NESTDB_LISTEN_ENDPOINTS}"

PROG_MAX_QUICK_RESTARTS="20"
WATCHDOG_ARGS="-d -s ${PROG_TAG} -q ${PROG_MAX_QUICK_RESTARTS}"

nsx_group="nsx"
nsx_sha_group="nsx-sha"

nsx_nestdb_user="nestdb"
nsx_nestdb_group="nestdb"

NESTDB_CERT_DIR=/config/vmware/nsx-nestdb
NESTDB_SSL_CNF=$NESTDB_CERT_DIR/openssl.cnf
NESTDB_KEY_FILE=$NESTDB_CERT_DIR/nestdb-privkey.pem
NESTDB_CERT_FILE=$NESTDB_CERT_DIR/nestdb-cert.pem

MAKE_CERT=/opt/vmware/nsx-common/python/nsx_utils/make_cert.py

SHUTDOWN_TIMEOUT=5
TERMINATE_ATTEMPTS=5

# <instrumentation> #

prog_log() {
   /opt/vmware/nsx-common/logging/nsx_logger -f daemon -s "nsx-nestdb" "${1}"
}

start() {
   groupadd -f $nsx_group
   groupadd -f $nsx_nestdb_group
   groupadd -f $nsx_sha_group
   getent passwd $nsx_nestdb_user >/dev/null || \
      useradd --system -N \
      -g $nsx_nestdb_group \
      --shell /usr/sbin/nologin -M --comment "NSX NestDB" $nsx_nestdb_user
   usermod -a -G $nsx_group $nsx_nestdb_user
   usermod -a -G $nsx_sha_group $nsx_nestdb_user

   # Remove unused files and folders from CH deployment
   rm -rf /var/log/vmware/nsx/nestdb
   rm -rf /var/log/vmware/nestdb-transaction*

   mkdir -m 0755 -p /var/run/vmware
   mkdir -m 0770 -p /var/run/vmware/nestdb
   chgrp $nsx_nestdb_group /var/run/vmware/nestdb
   mkdir -p $NESTDB_DB_PREFIX/db
   mkdir -p $NESTDB_METRICS_FOLDER
   chmod -R 700 $NESTDB_DB_PREFIX
   chown -R $nsx_nestdb_user:$nsx_nestdb_group $NESTDB_DB_PREFIX
   chown -R $nsx_nestdb_user:$nsx_nestdb_group $NESTDB_METRICS_FOLDER
   ulimit -c unlimited

   # Check if it is already running
   if [ -z "$(pgrep -f "${PROG}")" ]; then
      # Remove any existing files/directories at
      # /var/run/vmware/nestdb/nestdb-server.sock
      rm -rf /var/run/vmware/nestdb/nestdb-server.sock

      if [ -f $NSX_ISSUE_FILE ] && grep -q -E "nsx-edge|nsx-autonomous-edge|hcx-edge|nsx-public-cloud-gateway" $NSX_ISSUE_FILE; then
         mkdir -p $NESTDB_CERT_DIR
         chown -R $nsx_nestdb_user:$nsx_nestdb_group $NESTDB_CERT_DIR

         if [ -f $NESTDB_CERT_FILE -a -f $NESTDB_KEY_FILE ]; then
            prog_log "NestDB cert and priv-key exist"
         else
            prog_log "Generating NestDB certificate"
            # will keep retry if certificate generation failed.
            while [ 1 ]; do
               $MAKE_CERT openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -keyout \
                  $NESTDB_KEY_FILE -out $NESTDB_CERT_FILE -config $NESTDB_SSL_CNF
               if [ $? -ne 0 ]; then
                  prog_log "openssl req command execution failed"
                  # key and cert file maybe incomplete, delete it
                  rm -f $NESTDB_KEY_FILE
                  rm -f $NESTDB_CERT_FILE
                  sleep 2
               else
                  if [ -s $NESTDB_KEY_FILE -a -s $NESTDB_CERT_FILE ]; then
                     break
                  else
                     prog_log "Empty key/certificate file detected, re-generate key and certficate file"
                     rm -f $NESTDB_KEY_FILE
                     rm -f $NESTDB_CERT_FILE
                     sleep 2
                  fi
               fi
            done
         fi
         chown $nsx_nestdb_user:$nsx_nestdb_group "${NESTDB_SSL_CNF}"
         chown $nsx_nestdb_user:$nsx_nestdb_group "${NESTDB_KEY_FILE}"
         chown $nsx_nestdb_user:$nsx_nestdb_group "${NESTDB_CERT_FILE}"
         chmod 600 "${NESTDB_SSL_CNF}"
         chmod 600 "${NESTDB_KEY_FILE}"
         chmod 640 "${NESTDB_CERT_FILE}"
      fi
      runuser -m $nsx_nestdb_user -s /bin/bash \
         -c "${WATCHDOG} ${WATCHDOG_ARGS} '${PROG} ${PROG_ARGS}' >/dev/null 2>&1"
      sleep 5
      local START_ATTEMPT=0
      while [ -z "$(pgrep -f "${PROG}")" ] ; do
         if [ $((START_ATTEMPT)) -eq 5 ]; then
            prog_log "Unable to start ${PROG_TAG}"
            break
         fi
         runuser -m $nsx_nestdb_user -s /bin/bash \
            -c "${WATCHDOG} ${WATCHDOG_ARGS} '${PROG} ${PROG_ARGS}' >/dev/null 2>&1"
         prog_log "Starting ${PROG_TAG} service again. Attempt #$((START_ATTEMPT))"
         START_ATTEMPT=$((START_ATTEMPT+1))
         sleep 5
      done
      if [ ! -z "$(pgrep -f "${PROG}")" ]; then
         prog_log "${PROG_TAG} started"
      fi
   else
      prog_log "${PROG_TAG} is already running "
   fi
}

do_stop() {
   PIDOF="pidof -s -z"
   $PIDOF "${PROG}"
   if [ $? -ne 0 ]; then
     # Either "-z" is not supported or PROG is not running. In either case, it is
     # ok to switch PIDOF to "pidof -s". This is because if "-z" is actually supported,
     # PROG is not running, which is good.
     # We are forced to do this because systemd does not let us capture pidof's stderr
     # to determine if -z is unsupported.
     prog_log "${PROG_TAG} -z may not be supported for pidof"
     PIDOF="pidof -s"
   fi
   prog_log "${PROG_TAG} Using $PIDOF"

   if [ -z $($PIDOF "${PROG}") ]; then
     prog_log "${PROG_TAG} service is not running"
     return 0
   fi

   # This only stops the watchdog process.
   prog_log "Shutting down ${PROG_TAG} watchdog"
   ${WATCHDOG} -k "${PROG_TAG}"

   prog_log "Shutting down ${PROG_TAG} service"
   kill $($PIDOF "${PROG}") >/dev/null 2>&1

   local TERMINATE_ATTEMPT=0
   while [ $((TERMINATE_ATTEMPT)) -le $((TERMINATE_ATTEMPTS)) ] ; do
      local TIMEOUT=$SHUTDOWN_TIMEOUT
      while [ $((TIMEOUT)) -gt 0 ]; do
         if [ -z $($PIDOF "${PROG}") ]; then
            prog_log "${PROG_TAG} service is stopped, breaking from loop"
            break
         fi
         sleep 1
         echo -n "."
         TIMEOUT=$((TIMEOUT-1))
      done
      echo ""

      if [ -z $($PIDOF "${PROG}") ]; then
         prog_log "${PROG_TAG} service is stopped"
         break
      fi
      if [ $((TERMINATE_ATTEMPT)) -eq $((TERMINATE_ATTEMPTS)) ];then
         prog_log "Terminating ${PROG_TAG} Already attempted $TERMINATE_ATTEMPT/$TERMINATE_ATTEMPTS"
         break
      fi

      prog_log "Terminating ${PROG_TAG} (attempt $((TERMINATE_ATTEMPT+1))/$TERMINATE_ATTEMPTS)"
      kill -9 $($PIDOF "${PROG}")

      TERMINATE_ATTEMPT=$((TERMINATE_ATTEMPT+1))
   done

   if [ -z $($PIDOF "${PROG}") ]; then
      prog_log "${PROG_TAG} service is stopped"
      return 0
   else
      prog_log "Unable to stop ${PROG_TAG} service"
      return 1
   fi
}

stop() {
   do_stop

   # cleanup /var/run/vmware/nestdb/nestdb-server.sock
   rm -rf /var/run/vmware/nestdb/nestdb-server.sock
}

status() {
   local NAME="${1:-${PROG_TAG}}"
   if [ -n "$(pidof -xs "${PROG}")" ] ; then
      echo "${NAME} is running"
      exit 0
   else
      echo "${NAME} is not running"
      exit 3
   fi
}

#
# main
#
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        status
        ;;
  restart)
        stop
        start
        ;;
  clear)
        stop
        clear
        ;;
  *)
        echo $"Usage: $0 {start|stop|restart|status}"
        exit 3
esac

