#!/bin/sh

# Copyright (C) 2015, Wazuh Inc.
# wazuh-manager-control        This shell script takes care of starting
#                      or stopping wazuh-hids
# Author: Daniel B. Cid <daniel.cid@gmail.com>

# Getting where we are installed
LOCAL=`dirname $0`;
cd ${LOCAL}
PWD=`pwd`
DIR=`dirname $PWD`;
PLIST=${DIR}/bin/.process_list;
WAZUH_CONF="${WAZUH_CONF:-wazuh-manager.conf}"
# Reader of the manager configuration: validation and effective values (schema defaults applied).
MCONF="${DIR}/bin/wazuh-manager-conf -H ${DIR} -f ${DIR}/etc/${WAZUH_CONF}"

# Installation info
VERSION="v5.0.0"
REVISION="rc1"
TYPE="manager"

###  Do not modify below here ###

# Getting additional processes
if [ -f "${PLIST}" ]; then
. ${PLIST};
fi

AUTHOR="Wazuh Inc."
USE_JSON=false
DAEMONS="wazuh-manager-clusterd wazuh-manager-modulesd wazuh-manager-remoted wazuh-manager-analysisd wazuh-manager-db wazuh-manager-authd wazuh-manager-apid"

# Reverse order of daemons
SDAEMONS=$(echo $DAEMONS | awk '{ for (i=NF; i>1; i--) printf("%s ",$i); print $1; }')

## Locking for the start/stop
LOCK="${DIR}/var/start-script-lock"
LOCK_PID="${LOCK}/pid"

# Seconds the lock loop waits. Below the unit's TimeoutSec (45,
# templates/wazuh-manager.service), or systemd reports a timeout instead of the real reason.
MAX_ITERATION="40"

MAX_KILL_TRIES=30

lock()
{
    i=0;
    unreachable=0;
    marker_busy=0;

    # Providing a lock.
    while [ 1 ]; do
        if mkdir ${LOCK} > /dev/null 2>&1; then
            # Lock acquired (setting the pid)
            echo "$$" > ${LOCK_PID}
            return;
        fi

        # Waiting 1 second before trying again
        sleep 1;
        i=`expr $i + 1`;
        pid=$(cat ${LOCK_PID} 2>/dev/null)

        # Only consecutive pid-less rounds may open the gate below. Neither
        # "$i" (a caller queued behind a live owner would inherit an open
        # gate the moment that owner is gone) nor a round spent on a dead
        # pid says anything about a lock that is still mid-acquisition.
        if [ -z "${pid}" ]; then
            unreachable=`expr ${unreachable} + 1`
        else
            unreachable=0
        fi

        # An empty pid (no pid file) fails kill -0 just like a dead one.
        kill -0 ${pid} >/dev/null 2>&1
        if [ "$?" != "0" ]; then
            # A dead pid is stale right away; a missing one may still be
            # mid-acquisition, so it needs a few consecutive rounds first.
            if [ -n "${pid}" ] || [ "${unreachable}" -gt 2 ]; then
                # mkdir on this marker is exclusive like ${LOCK}'s own, so
                # only one caller at a time may unlock and recreate ${LOCK}.
                if mkdir "${LOCK}.reclaim" > /dev/null 2>&1; then
                    marker_busy=0
                    # Another caller may have reclaimed ${LOCK} meanwhile.
                    rpid=$(cat ${LOCK_PID} 2>/dev/null)
                    kill -0 ${rpid} >/dev/null 2>&1
                    if [ ! $? = 0 ]; then
                        unlock;
                        if mkdir ${LOCK} > /dev/null 2>&1; then
                            echo "$$" > ${LOCK_PID}
                            rmdir "${LOCK}.reclaim" > /dev/null 2>&1
                            return;
                        fi
                    fi
                    rmdir "${LOCK}.reclaim" > /dev/null 2>&1
                else
                    # The marker is held only briefly; one still busy after
                    # several consecutive rounds was left by a dead caller.
                    marker_busy=`expr ${marker_busy} + 1`
                    if [ "${marker_busy}" -gt 4 ]; then
                        rmdir "${LOCK}.reclaim" > /dev/null 2>&1
                        marker_busy=0
                    fi
                fi
            fi
        fi

        # We tried MAX_ITERATION times to acquire the lock.
        if [ "$i" = "${MAX_ITERATION}" ]; then
            echo "ERROR: Another instance is locking this process."
            echo "If you are sure that no other instance is running, please remove ${LOCK}"
            exit 1
        fi
    done
}

unlock()
{
    rm -rf ${LOCK}
}

help()
{
    # Help message
    echo ""
    echo "Usage: $0 [-j] {start|stop|restart|reload|status|enable|disable|info [-v -r -t]}";
    echo ""
    echo "    -j    Use JSON output."
    exit 1;
}

# Enables additional daemons
enable()
{
    if [ -z "$1" ]; then
        echo ""
        echo "Enable options: debug"
        echo "Usage: $0 enable debug"
        exit 1;
    fi

    if [ "$1" = "debug" ]; then
        echo "DEBUG_CLI=\"-d\"" >> ${PLIST};
    else
        echo ""
        echo "Invalid enable option."
        echo ""
        echo "Enable options: debug"
        echo "Usage: $0 enable debug"
        exit 1;
    fi
}


# Disables additional daemons
disable()
{
    if [ -z "$1" ]; then
        echo ""
        echo "Disable options: debug"
        echo "Usage: $0 disable debug]"
        exit 1;
    fi

    if [ "$1" = "debug" ]; then
        echo "DEBUG_CLI=\"\"" >> ${PLIST};
    else
        echo ""
        echo "Invalid disable option."
        echo ""
        echo "Disable options: debug"
        echo "Usage: $0 disable debug"
        exit 1;
    fi
}

get_node_type()
{
    ${MCONF} get cluster.node_type 2>/dev/null
}

status()
{
    RETVAL=0
    first=true

    node_type=$(get_node_type);

    if [ $USE_JSON = true ]; then
        echo -n '{"error":0,"data":['
    fi
    for i in ${DAEMONS}; do
        ## The API daemon only runs on the master node
        if [ "$i" = "wazuh-manager-apid" ] && [ "$node_type" != "master" ]; then
            continue
        fi

        ## Mirror start_service(): authd is not started when auth.disabled is true.
        if [ "$i" = "wazuh-manager-authd" ] && [ "$(${MCONF} get auth.disabled 2>/dev/null)" = "true" ]; then
            continue
        fi

        if [ $USE_JSON = true ] && [ $first = false ]; then
            echo -n ','
        else
            first=false
        fi
        pstatus ${i};
        if [ $? = 0 ]; then
            # The marker testconfig() leaves on a refused configuration, and the same one the
            # framework reads as 'failed'. Without it a rejected value is indistinguishable from a
            # daemon that was never started.
            if [ -f ${DIR}/var/run/${i}.failed ]; then
                if [ $USE_JSON = true ]; then
                    echo -n '{"daemon":"'${i}'","status":"failed"}'
                elif [ "`cat ${DIR}/var/run/${i}.failed 2>/dev/null`" = "refused" ]; then
                    echo "${i} refused its configuration..."
                else
                    echo "${i} failed to start..."
                fi
            elif [ $USE_JSON = true ]; then
                echo -n '{"daemon":"'${i}'","status":"stopped"}'
            else
                echo "${i} not running..."
            fi
            RETVAL=1
        else
            if [ $USE_JSON = true ]; then
                echo -n '{"daemon":"'${i}'","status":"running"}'
            else
                echo "${i} is running..."
            fi
        fi
    done
    if [ $USE_JSON = true ]; then
        echo -n ']}'
    fi
}

# The credentials file itself, checked before anything else on the start and restart paths.
#
# Read-only: the resolver's --check asks the shared helper whether the credentials file, its
# directory and every directory above it are acceptable, and touches neither the keystore nor the
# credentials lock. That is what lets it run ahead of testconfig() -- and so ahead of the stop on the
# restart path -- where resolvecredentials() cannot (see below). It has to come first: an install
# that met an unsafe file issued no certificates, and testconfig() would otherwise stop the start on
# "(1244) file not found" for remoted.pem without ever naming the file that caused it. It refuses
# the file even when this start would not read it (everything already resolved), as the indexer and
# the dashboard do: an unsafe credentials file is a problem whether or not anything needs it today.
#
# Any non-zero answer refuses the start, including the resolver failing to find its helpers: a
# verdict we could not obtain is not a reason to start.
checkcredentials()
{
    # Each marker is a verdict from a previous run, and this run's verdict replaces them; testconfig()
    # clears them too, but this function may exit before it runs. Without this, a refusal here would
    # leave `status` reporting another run's "refused its configuration".
    rm -f ${DIR}/var/run/*.failed

    if [ ! -x ${DIR}/bin/wazuh-manager-resolve-credentials ]; then
        return 0
    fi

    # Captured and relayed on stderr, as in resolvecredentials(), so that `-j` keeps writing a single
    # JSON document to stdout. The helper names the rule and the path, never a value.
    CHECK_VERDICT=$(${DIR}/bin/wazuh-manager-resolve-credentials --check -H ${DIR} 2>&1)
    if [ $? = 0 ]; then
        return 0
    fi
    if [ -n "${CHECK_VERDICT}" ]; then
        echo "${CHECK_VERDICT}" >&2
    fi
    # The resolver says UNSAFE when the helper refused the file; anything else (its helpers missing,
    # not run as root) means the check itself could not run, which refuses the start just the same
    # but must not be reported as a verdict about the file.
    case "${CHECK_VERDICT}" in
        *UNSAFE*) CHECK_REASON="Unsafe credentials file"; CHECK_LOG="unsafe credentials file" ;;
        *)        CHECK_REASON="Cannot check the credentials file"; CHECK_LOG="cannot check the credentials file" ;;
    esac
    echo "$(date '+%Y/%m/%d %H:%M:%S') wazuh-manager-control: ERROR: ${CHECK_LOG}" >> ${DIR}/logs/wazuh-manager.log 2>/dev/null
    if [ -n "${CHECK_VERDICT}" ]; then
        echo "${CHECK_VERDICT}" >> ${DIR}/logs/wazuh-manager.log 2>/dev/null
    fi
    if [ $USE_JSON = true ]; then
        echo -n '{"error":22,"message":"'"${CHECK_REASON}"'."}'
    else
        echo "${CHECK_REASON}. Exiting"
    fi
    rm -f ${DIR}/var/run/*.start
    rm -f ${DIR}/var/run/.restart
    # Like testconfig(), this runs before lock() on both paths: unlock() is `rm -rf ${LOCK}`, so it
    # releases nothing of ours -- and, as there, it would also remove a lock another invocation holds.
    unlock;
    exit 1;
}

# Credential resolution, run immediately before the daemons and while we are still root.
#
# Deliberately NOT part of testconfig(), and deliberately not run while any daemon is up: the
# resolver asks the keystore whether the indexer credential is already stored, and that opens the
# `queue/keystore` RocksDB read-write. keystore_server answers the framework's KeystoreClient from
# the same database -- once per API request that reaches the indexer -- so a probe issued while the
# manager is running races it for RocksDB's directory lock. The loser does not merely fail: the
# wrapper treats an IOError as corruption and runs rocksdb::RepairDB() over a database another
# process has open. restart_service() therefore calls this AFTER stop_service(), and the start path
# calls it with nothing running.
#
# --prestart does NOT touch the certificates: those are issued once, at installation, and an
# operator who replaced them with their own PKI must not have them re-examined at every start
# (issuing one is a signature, not a lookup, so re-deriving the chain would make the shared CA
# directory a standing dependency of the manager). Missing or unreadable certificates are caught by
# checkSemantics() in testconfig() and by remoted's own access(R_OK) preflight after it drops
# privileges; the resolver only answers for the passwords and the keystore.
#
# The credentials file's ownership and mode are not this function's job: checkcredentials() has
# already refused an unsafe one before testconfig().
#
# Unresolved credentials fail here rather than at the daemon's own -t: a missing indexer password is
# not a configuration error and has no JSON pointer to report, and the resolver has already named
# the key and where to set it.
resolvecredentials()
{
    if [ ! -x ${DIR}/bin/wazuh-manager-resolve-credentials ]; then
        return 0
    fi

    # Belt and braces for the call ordering above, so that a future caller cannot reintroduce the
    # race by moving this. keystore_server lives inside modulesd and answers the framework's
    # KeystoreClient from the very database this step opens; pstatus returns 1 when it is up. If it
    # is, this is not a pre-start -- a `start` against a running manager, or a caller out of order --
    # and there is nothing to resolve, because whatever is running resolved it when it started.
    pstatus wazuh-manager-modulesd "quiet"
    if [ $? = 1 ]; then
        return 0
    fi

    # Captured rather than left on stdout, for the same reason testconfig() captures the
    # configuration validator's verdict: `wazuh-manager-control -j start` writes exactly one JSON
    # document to stdout, and the resolver's progress lines would be prepended to it and break every
    # parser. They go to stderr instead -- the journal keeps both streams, which is where someone
    # looks when a unit will not start. The resolver never prints a value, only key names, so
    # relaying it in full leaks nothing.
    CREDENTIALS_VERDICT=$(${DIR}/bin/wazuh-manager-resolve-credentials --prestart -H ${DIR} 2>&1)
    CREDENTIALS_STATUS=$?
    if [ -n "${CREDENTIALS_VERDICT}" ]; then
        echo "${CREDENTIALS_VERDICT}" >&2
    fi
    if [ ${CREDENTIALS_STATUS} != 0 ]; then
        echo "$(date '+%Y/%m/%d %H:%M:%S') wazuh-manager-control: ERROR: unresolved credentials" >> ${DIR}/logs/wazuh-manager.log 2>/dev/null
        # No daemon starts, so nothing else records which key was missing where operators (and the
        # integration tests) look for it.
        echo "${CREDENTIALS_VERDICT}" >> ${DIR}/logs/wazuh-manager.log 2>/dev/null
        if [ $USE_JSON = true ]; then
            echo -n '{"error":21,"message":"Unresolved credentials."}'
        else
            echo "Unresolved credentials. Exiting"
        fi
        rm -f ${DIR}/var/run/*.start
        rm -f ${DIR}/var/run/.restart
        # unlock() is `rm -rf ${LOCK}`, so calling it on the start path -- which has not taken the
        # lock yet -- is harmless, and on the restart path it releases the lock we are holding.
        unlock;
        exit 1;
    fi
}

testconfig()
{
    # Each marker is a verdict from a previous run and this one replaces all of them. Cleared
    # here, not in start_service(): this function exits 1 on the FIRST failure, so start_service()
    # may never run to clear a marker left by an earlier, unrelated one.
    rm -f ${DIR}/var/run/*.failed

    # The whole configuration file (XML, schema, cross-field rules and the files it references):
    # fails fast with the JSON pointer of the offending option before any daemon runs its own -t.
    # This stays ahead of stop_service() on the restart path, so a configuration mistake is refused
    # while the manager is still up rather than after it has been taken down.
    MCONF_VERDICT=$(${MCONF} validate 2>&1)
    if [ $? != 0 ]; then
        echo "${MCONF_VERDICT}" >&2
        # With the fail-fast no daemon starts, so nothing else records the reason where operators
        # (and the integration tests) look for it: surface the verdict in the manager log too.
        echo "$(date '+%Y/%m/%d %H:%M:%S') wazuh-manager-control: ERROR: ${MCONF_VERDICT}" >> ${DIR}/logs/wazuh-manager.log 2>/dev/null
        if [ $USE_JSON = true ]; then
            echo -n '{"error":20,"message":"'${WAZUH_CONF}': Configuration error."}'
        else
            echo "${WAZUH_CONF}: Configuration error. Exiting"
        fi
        rm -f ${DIR}/var/run/*.start
        rm -f ${DIR}/var/run/.restart
        unlock;
        exit 1;
    fi

    # Then each daemon checks what is not configuration (files, sockets, keys).
    for i in ${SDAEMONS}; do
        ${DIR}/bin/${i} -t ${DEBUG_CLI};
        if [ $? != 0 ]; then
            if [ $USE_JSON = true ]; then
                echo -n '{"error":20,"message":"'${i}': Configuration error."}'
            else
                echo "${i}: Configuration error. Exiting"
            fi
            # Unconditionally: the wipe at the top of this function already replaced the old
            # .restart guard's job, and keeping the guard here would leave a restart with a
            # refused configuration reporting plain "not running".
            echo "refused" > ${DIR}/var/run/${i}.failed
            rm -f ${DIR}/var/run/*.start
            rm -f ${DIR}/var/run/.restart
            unlock;
            exit 1;
        fi
    done
}
get_wazuh_engine_pid()
{
    local max_ticks=100
    local ticks=0
    local pidfile

    ${DIR}/bin/wazuh-manager-analysisd

    while [ $ticks -lt $max_ticks ]; do
        pidfile=$(ls ${DIR}/var/run/wazuh-manager-analysisd-*.pid 2>/dev/null | head -n1)
        if [ -n "$pidfile" ]; then
            echo "${pidfile##*-}" | sed 's/\.pid$//'
            return 0
        fi
        ticks=$((ticks + 1))
        sleep 0.1
    done

    return 1  # timeout
}

wait_for_wazuh_engine_ready()
{
    local attempts=0
    local max_attempts=120

    ENGINE_PID=$(get_wazuh_engine_pid)
    if [ $? -ne 0 ]; then
        echo "Failed to obtain PID for wazuh-manager-analysisd"
        return 1
    fi

    while [ $attempts -lt $max_attempts ]; do
        curl --silent --fail --unix-socket ${DIR}/queue/sockets/engine-api-http.sock \
            -X POST -H "Content-Type: application/json" \
            -d '{}' \
            http://localhost/_internal/event-dumper/status \
            > /dev/null 2>&1
        if [ $? -eq 0 ]; then
            return 0
        fi

        if ! kill -0 "$ENGINE_PID" 2>/dev/null; then
            echo "wazuh-manager-analysisd died during event dumper check."
            return 1
        fi

        attempts=$((attempts + 1))
        sleep 1
    done

    echo "wazuh-manager-analysisd did not respond correctly after $max_attempts attempts."
    kill $ENGINE_PID
    return 1
}

# Start function
start_service()
{

    if [ $USE_JSON = false ]; then
        echo "Starting Wazuh $VERSION..."
    fi

    # Delete all files in temporary folder
    TO_DELETE="$DIR/tmp"
    find "$TO_DELETE" -mindepth 1 -delete

    node_type=$(get_node_type);
    if [ -z $node_type ]; then
        echo "Invalid cluster configuration, check the $DIR/etc/${WAZUH_CONF} file."
        unlock;
        exit 1;
    fi

    # We actually start them now.
    first=true
    if [ $USE_JSON = true ]; then
        echo -n '{"error":0,"data":['
    fi
    for i in ${SDAEMONS}; do
        ## Only start the API daemon on the master node
        if [ "$i" = "wazuh-manager-apid" ] && [ "$node_type" != "master" ]; then
            continue
        fi

        ## If wazuh-manager-authd is disabled (auth.disabled: true), don't try to start it.
        if [ "$i" = "wazuh-manager-authd" ]; then
             if [ "$(${MCONF} get auth.disabled 2>/dev/null)" = "true" ]; then
                continue
             fi
        fi
        if [ $USE_JSON = true ] && [ $first = false ]; then
            echo -n ','
        else
            first=false
        fi

        pstatus ${i};
        if [ $? = 0 ]; then
            ## Create starting flag
            failed=false
            touch ${DIR}/var/run/${i}.start

            if [ "$i" = "wazuh-manager-analysisd" ]; then
                wait_for_wazuh_engine_ready
            elif [ $USE_JSON = true ]; then
                ${DIR}/bin/${i} ${DEBUG_CLI} > /dev/null 2>&1;
            else
                ${DIR}/bin/${i} ${DEBUG_CLI};
            fi

            if [ $? != 0 ]; then
                failed=true
            fi
            if [ $failed = true ]; then
                if [ $USE_JSON = true ]; then
                    echo -n '{"daemon":"'${i}'","status":"error"}'
                else
                    echo "${i} did not start correctly.";
                fi
                rm -f ${DIR}/var/run/${i}.start
                # Same marker the framework reads as 'failed'; the content tells status why.
                echo "start" > ${DIR}/var/run/${i}.failed
                rm -f ${DIR}/var/run/*.start
                rm -f ${DIR}/var/run/.restart
                unlock;
                exit 1;
            fi
            if [ $USE_JSON = true ]; then
                echo -n '{"daemon":"'${i}'","status":"running"}'
            else
                echo "Started ${i}..."
            fi
        else
            if [ $USE_JSON = true ]; then
                echo -n '{"daemon":"'${i}'","status":"running"}'
            else
                echo "${i} already running..."
            fi
        fi
    done

    # After we start we give 2 seconds for the daemons
    # to internally create their PID files.
    sleep 2;

    if [ $USE_JSON = true ]; then
        echo -n ']}'
    else
        echo "Completed."
    fi
    rm -f ${DIR}/var/run/*.start
}

pstatus()
{
    pfile=$1;
    _pstatus_quiet=${2:-""}
    # pfile must be set
    if [ -z "${pfile}" ]; then
        return 0;
    fi

    ls ${DIR}/var/run/${pfile}-*.pid > /dev/null 2>&1
    if [ $? = 0 ]; then
        for pid in `cat ${DIR}/var/run/${pfile}-*.pid 2>/dev/null`; do
            ps -p ${pid} > /dev/null 2>&1
            if [ ! $? = 0 ]; then
                if [ $USE_JSON = false ] && [ -z "${_pstatus_quiet}" ]; then
                    echo "${pfile}: Process ${pid} not used by Wazuh, removing..."
                fi
                rm -f ${DIR}/var/run/${pfile}-${pid}.pid
                continue;
            fi

            kill -0 ${pid} > /dev/null 2>&1
            if [ $? = 0 ]; then
                return 1;
            fi
        done
    fi

    return 0;
}

wait_pid() {
    wp_counter=1

    while kill -0 $1 2> /dev/null
    do
        if [ "$wp_counter" = "$MAX_KILL_TRIES" ]
        then
            return 1
        else
            sleep 1
            wp_counter=`expr $wp_counter + 1`
        fi
    done

    return 0
}

stop_service()
{
    # First pass: send kill signal to all running daemons
    for i in ${DAEMONS}; do
        pstatus ${i};
        if [ $? = 1 ]; then
            if [ $USE_JSON != true ]
            then
                echo "Killing ${i}...";
            fi
            pid=`cat ${DIR}/var/run/${i}-*.pid`
            kill $pid
        else
            if [ $USE_JSON != true ]
            then
                echo "${i} not running...";
            fi
        fi
    done

    # Second pass: wait for all processes that are still alive
    first=true
    if [ $USE_JSON = true ]; then
        echo -n '{"error":0,"data":['
    fi
    for i in ${DAEMONS}; do
        if [ $USE_JSON = true ] && [ $first = false ]; then
            echo -n ','
        else
            first=false
        fi

        pstatus ${i} "quiet";

        if [ $? = 1 ]; then
            pid=`cat ${DIR}/var/run/${i}-*.pid`

            if wait_pid $pid
            then
                if [ $USE_JSON = true ]; then
                    echo -n '{"daemon":"'${i}'","status":"stopped"}'
                fi
            else
                if [ $USE_JSON = true ]; then
                    echo -n '{"daemon":"'${i}'","status":"killed"}'
                else
                    echo "Process ${i} couldn't be terminated. It will be killed.";
                fi
                kill -9 $pid
            fi
        else
            if [ $USE_JSON = true ]; then
                echo -n '{"daemon":"'${i}'","status":"stopped"}'
            fi
        fi
        rm -f ${DIR}/var/run/${i}-*.pid
    done

    if [ $USE_JSON = true ]; then
        echo -n ']}'
    else
        echo "Wazuh $VERSION Stopped"
    fi
}

info()
{
    if [ -z "${1}" ]; then
        if [ $USE_JSON = true ]; then
            echo -n '{"error":0,"data":['
            echo -n '{"WAZUH_VERSION":"'${VERSION}'"},'
            echo -n '{"WAZUH_REVISION":"'${REVISION}'"},'
            echo -n '{"WAZUH_TYPE":"'${TYPE}'"}'
            echo -n ']}'
        else
            echo "WAZUH_VERSION=\"${VERSION}\""
            echo "WAZUH_REVISION=\"${REVISION}\""
            echo "WAZUH_TYPE=\"${TYPE}\""
        fi
    else
        case "${1}" in
            -v) echo "${VERSION}" ;;
            -r) echo "${REVISION}" ;;
            -t) echo "${TYPE}" ;;
             *) echo "Invalid flag: ${1}" && help ;;
        esac
    fi
}

restart_service()
{
    touch ${DIR}/var/run/.restart
    checkcredentials
    testconfig
    lock
    if [ $USE_JSON = true ]; then
        stop_service > /dev/null 2>&1
    else
        stop_service
    fi
    # After the stop, never before it: see resolvecredentials(). Probing the keystore while
    # keystore_server is still answering requests races it for the RocksDB directory lock.
    resolvecredentials
    start_service
    rm -f ${DIR}/var/run/.restart
    unlock
}

### MAIN HERE ###

if [ "$1" = "-j" ]; then
    USE_JSON=true
    action=$2
    arg=$3
else
    action=$1
    arg=$2
fi

case "$action" in
start)
    checkcredentials
    testconfig
    resolvecredentials
    lock
    start_service
    unlock
    RETVAL=0
    ;;
stop)
    lock
    stop_service
    unlock
    RETVAL=0
    ;;
restart)
    restart_service
    RETVAL=0
    ;;
reload)
    DAEMONS=$(echo $DAEMONS | sed 's/wazuh-manager-remoted//')
    SDAEMONS=$(echo $DAEMONS | awk '{ for (i=NF; i>1; i--) printf("%s ",$i); print $1; }')
    restart_service
    RETVAL=0
    ;;
status)
    lock
    status
    unlock
    ;;
enable)
    lock
    enable "$arg";
    unlock
    RETVAL=0
    ;;
disable)
    lock
    disable "$arg";
    unlock
    RETVAL=0
    ;;
info)
    info $arg
    RETVAL=0
    ;;
help)
    help
    ;;
*)
    if [ -n "$action" ]; then
        echo "Invalid action: ${action}"
    fi
    help
esac

exit ${RETVAL:-0}
