/* Copyright (C) 2007-2022 Open Information Security Foundation
 *
 * You can copy, redistribute or modify this Program under the terms of
 * the GNU General Public License version 2 as published by the Free
 * Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * version 2 along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

/**
 * \file
 *
 * \author Victor Julien <victor@inliniac.net>
 */

#include "suricata-common.h"

#if HAVE_GETOPT_H
#include <getopt.h>
#endif

#if HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifndef OS_WIN32
#ifdef HAVE_SYS_RESOURCE_H
// setrlimit
#include <sys/resource.h>
#endif
#endif

#if HAVE_LIBSYSTEMD
#include <systemd/sd-daemon.h>
#endif

#include "suricata.h"

#include "conf.h"
#include "conf-yaml-loader.h"

#include "decode.h"
#include "defrag.h"
#include "flow.h"
#include "stream-tcp.h"
#include "ippair.h"

#include "detect.h"
#include "detect-parse.h"
#include "detect-engine.h"
#include "detect-engine-address.h"
#include "detect-engine-alert.h"
#include "detect-engine-port.h"
#include "detect-engine-tag.h"
#include "detect-engine-threshold.h"
#include "detect-fast-pattern.h"

#include "datasets.h"

#include "feature.h"

#include "flow-bypass.h"
#include "flow-manager.h"
#include "flow-timeout.h"
#include "flow-worker.h"

#include "flow-bit.h"
#include "host-bit.h"
#include "ippair-bit.h"

#include "app-layer.h"
#include "app-layer-parser.h"
#include "app-layer-htp.h"
#include "app-layer-htp-range.h"

#include "output.h"
#include "output-filestore.h"

#include "respond-reject.h"

#include "runmode-af-packet.h"
#include "runmode-af-xdp.h"
#include "runmode-netmap.h"
#include "runmode-unittests.h"

#include "source-nfq.h"
#include "source-nfq-prototypes.h"
#include "source-nflog.h"
#include "source-ipfw.h"
#include "source-pcap.h"
#include "source-pcap-file.h"
#include "source-pcap-file-helper.h"
#include "source-pfring.h"
#include "source-erf-file.h"
#include "source-erf-dag.h"
#include "source-napatech.h"
#include "source-af-packet.h"
#include "source-af-xdp.h"
#include "source-netmap.h"
#include "source-dpdk.h"
#include "source-windivert.h"
#include "source-windivert-prototypes.h"

#include "unix-manager.h"

#include "util-classification-config.h"
#include "util-threshold-config.h"
#include "util-reference-config.h"

#include "tmqh-packetpool.h"
#include "tm-queuehandlers.h"

#include "util-byte.h"
#include "util-conf.h"
#include "util-coredump-config.h"
#include "util-cpu.h"
#include "util-daemon.h"
#include "util-device.h"
#include "util-dpdk.h"
#include "util-ebpf.h"
#include "util-exception-policy.h"
#include "util-host-os-info.h"
#include "util-hugepages.h"
#include "util-ioctl.h"
#include "util-landlock.h"
#include "util-luajit.h"
#include "util-macset.h"
#include "util-misc.h"
#include "util-mpm-hs.h"
#include "util-path.h"
#include "util-pidfile.h"
#include "util-plugin.h"
#include "util-privs.h"
#include "util-profiling.h"
#include "util-proto-name.h"
#include "util-running-modes.h"
#include "util-signal.h"
#include "util-time.h"
#include "util-validate.h"
#include "util-var-name.h"

#ifdef WINDIVERT
#include "decode-sll.h"
#include "win32-syscall.h"
#endif

/*
 * we put this here, because we only use it here in main.
 */
volatile sig_atomic_t sigint_count = 0;
volatile sig_atomic_t sighup_count = 0;
volatile sig_atomic_t sigterm_count = 0;
volatile sig_atomic_t sigusr2_count = 0;

/*
 * Flag to indicate if the engine is at the initialization
 * or already processing packets. 3 stages: SURICATA_INIT,
 * SURICATA_RUNTIME and SURICATA_FINALIZE
 */
SC_ATOMIC_DECLARE(unsigned int, engine_stage);

/* Max packets processed simultaneously per thread. */
#define DEFAULT_MAX_PENDING_PACKETS 1024

/** suricata engine control flags */
volatile uint8_t suricata_ctl_flags = 0;

/** Run mode selected */
int run_mode = RUNMODE_UNKNOWN;

/** Engine mode: inline (ENGINE_MODE_IPS) or just
  * detection mode (ENGINE_MODE_IDS by default) */
static enum EngineMode g_engine_mode = ENGINE_MODE_UNKNOWN;

/** Host mode: set if box is sniffing only
 * or is a router */
uint8_t host_mode = SURI_HOST_IS_SNIFFER_ONLY;

/** Maximum packets to simultaneously process. */
uint16_t max_pending_packets;

/** global indicating if detection is enabled */
int g_detect_disabled = 0;

/** set caps or not */
int sc_set_caps = FALSE;

bool g_system = false;

/** disable randomness to get reproducible results across runs */
#ifndef AFLFUZZ_NO_RANDOM
int g_disable_randomness = 0;
#else
int g_disable_randomness = 1;
#endif

/** determine (without branching) if we include the vlan_ids when hashing or
  * comparing flows */
uint16_t g_vlan_mask = 0xffff;

/** determine (without branching) if we include the livedev ids when hashing or
 * comparing flows */
uint16_t g_livedev_mask = 0xffff;

/* flag to disable hashing almost globally, to be similar to disabling nss
 * support */
bool g_disable_hashing = false;

/** Suricata instance */
SCInstance suricata;

int SuriHasSigFile(void)
{
    return (suricata.sig_file != NULL);
}

int EngineModeIsUnknown(void)
{
    return (g_engine_mode == ENGINE_MODE_UNKNOWN);
}

int EngineModeIsIPS(void)
{
    DEBUG_VALIDATE_BUG_ON(g_engine_mode == ENGINE_MODE_UNKNOWN);
    return (g_engine_mode == ENGINE_MODE_IPS);
}

int EngineModeIsIDS(void)
{
    DEBUG_VALIDATE_BUG_ON(g_engine_mode == ENGINE_MODE_UNKNOWN);
    return (g_engine_mode == ENGINE_MODE_IDS);
}

void EngineModeSetIPS(void)
{
    g_engine_mode = ENGINE_MODE_IPS;
}

void EngineModeSetIDS(void)
{
    g_engine_mode = ENGINE_MODE_IDS;
}

#ifdef UNITTESTS
int RunmodeIsUnittests(void)
{
    if (run_mode == RUNMODE_UNITTEST)
        return 1;

    return 0;
}
#endif

int RunmodeGetCurrent(void)
{
    return run_mode;
}

/** signal handlers
 *
 *  WARNING: don't use the SCLog* API in the handlers. The API is complex
 *  with memory allocation possibly happening, calls to syslog, json message
 *  construction, etc.
 */

#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
static void SignalHandlerSigint(/*@unused@*/ int sig)
{
    sigint_count = 1;
}
static void SignalHandlerSigterm(/*@unused@*/ int sig)
{
    sigterm_count = 1;
}
#ifndef OS_WIN32
#if HAVE_LIBUNWIND
#define UNW_LOCAL_ONLY
#include <libunwind.h>
static void SignalHandlerUnexpected(int sig_num, siginfo_t *info, void *context)
{
    char msg[SC_LOG_MAX_LOG_MSG_LEN];
    unw_cursor_t cursor;
    /* Restore defaults for signals to avoid loops */
    signal(SIGABRT, SIG_DFL);
    signal(SIGSEGV, SIG_DFL);
    int r;
    if ((r = unw_init_local(&cursor, (unw_context_t *)(context)) != 0)) {
        SCLogError("unable to obtain stack trace: unw_init_local: %s", unw_strerror(r));
        goto terminate;
    }

    char *temp = msg;
    int cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - msg), "stacktrace:sig %d:", sig_num);
    temp += cw;
    r = 1;
    while (r > 0) {
        if (unw_is_signal_frame(&cursor) == 0) {
            unw_word_t off;
            char name[256];
            if (unw_get_proc_name(&cursor, name, sizeof(name), &off) == UNW_ENOMEM) {
                cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - msg), "[unknown]:");
            } else {
                cw = snprintf(
                        temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - msg), "%s+0x%08" PRIx64, name, off);
            }
            temp += cw;
        }

        r = unw_step(&cursor);
        if (r > 0) {
            cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - msg), ";");
            temp += cw;
        }
    }
    SCLogError("%s", msg);

terminate:
    // Propagate signal to watchers, if any
    kill(getpid(), sig_num);
}
#undef UNW_LOCAL_ONLY
#endif /* HAVE_LIBUNWIND */
#endif /* !OS_WIN32 */
#endif

#ifndef OS_WIN32
/**
 * SIGUSR2 handler.  Just set sigusr2_count.  The main loop will act on
 * it.
 */
static void SignalHandlerSigusr2(int sig)
{
    if (sigusr2_count < 2)
        sigusr2_count++;
}

/**
 * SIGHUP handler.  Just set sighup_count.  The main loop will act on
 * it.
 */
static void SignalHandlerSigHup(/*@unused@*/ int sig)
{
    sighup_count = 1;
}
#endif

void GlobalsInitPreConfig(void)
{
    TimeInit();
    SupportFastPatternForSigMatchTypes();
    SCThresholdConfGlobalInit();
    SCProtoNameInit();
    FrameConfigInit();
}

static void GlobalsDestroy(SCInstance *suri)
{
    HostShutdown();
    HTPFreeConfig();
    HTPAtExitPrintStats();

    AppLayerHtpPrintStats();

    /* TODO this can do into it's own func */
    DetectEngineCtx *de_ctx = DetectEngineGetCurrent();
    if (de_ctx) {
        DetectEngineMoveToFreeList(de_ctx);
        DetectEngineDeReference(&de_ctx);
    }
    DetectEngineClearMaster();

    AppLayerDeSetup();
    DatasetsSave();
    DatasetsDestroy();
    TagDestroyCtx();

    LiveDeviceListClean();
    OutputDeregisterAll();
    FeatureTrackingRelease();
    SCProtoNameRelease();
    TimeDeinit();
    TmqhCleanup();
    TmModuleRunDeInit();
    ParseSizeDeinit();

#ifdef HAVE_DPDK
    DPDKCleanupEAL();
#endif

#ifdef HAVE_AF_PACKET
    AFPPeersListClean();
#endif

#ifdef NFQ
    NFQContextsClean();
#endif

#ifdef BUILD_HYPERSCAN
    MpmHSGlobalCleanup();
#endif

    ConfDeInit();
#ifdef HAVE_LUAJIT
    LuajitFreeStatesPool();
#endif
    DetectParseFreeRegexes();

    SCPidfileRemove(suri->pid_filename);
    SCFree(suri->pid_filename);
    suri->pid_filename = NULL;

    VarNameStoreDestroy();
    SCLogDeInitLogModule();
}

/**
 * \brief Used to send OS specific notification of running threads
 *
 * \retval TmEcode TM_ECODE_OK on success; TM_ECODE_FAILED on failure.
 */
static void OnNotifyRunning(void)
{
#if HAVE_LIBSYSTEMD
    if (sd_notify(0, "READY=1") < 0) {
        SCLogWarning("failed to notify systemd");
        /* Please refer to:
         * https://www.freedesktop.org/software/systemd/man/sd_notify.html#Return%20Value
         * for discussion on why failure should not be considered an error */
    }
#endif
}

/** \brief make sure threads can stop the engine by calling this
 *  function. Purpose: pcap file mode needs to be able to tell the
 *  engine the file eof is reached. */
void EngineStop(void)
{
    suricata_ctl_flags |= SURICATA_STOP;
}

/**
 * \brief Used to indicate that the current task is done.
 *
 * This is mainly used by pcap-file to tell it has finished
 * to treat a pcap files when running in unix-socket mode.
 */
void EngineDone(void)
{
    suricata_ctl_flags |= SURICATA_DONE;
}

static int SetBpfString(int argc, char *argv[])
{
    char *bpf_filter = NULL;
    uint32_t bpf_len = 0;
    int tmpindex = 0;

    /* attempt to parse remaining args as bpf filter */
    tmpindex = argc;
    while(argv[tmpindex] != NULL) {
        bpf_len+=strlen(argv[tmpindex]) + 1;
        tmpindex++;
    }

    if (bpf_len == 0)
        return TM_ECODE_OK;

    bpf_filter = SCMalloc(bpf_len);
    if (unlikely(bpf_filter == NULL))
        return TM_ECODE_FAILED;
    memset(bpf_filter, 0x00, bpf_len);

    tmpindex = optind;
    while(argv[tmpindex] != NULL) {
        strlcat(bpf_filter, argv[tmpindex],bpf_len);
        if(argv[tmpindex + 1] != NULL) {
            strlcat(bpf_filter," ", bpf_len);
        }
        tmpindex++;
    }

    if(strlen(bpf_filter) > 0) {
        if (ConfSetFinal("bpf-filter", bpf_filter) != 1) {
            SCLogError("Failed to set bpf filter.");
            SCFree(bpf_filter);
            return TM_ECODE_FAILED;
        }
    }
    SCFree(bpf_filter);

    return TM_ECODE_OK;
}

static void SetBpfStringFromFile(char *filename)
{
    char *bpf_filter = NULL;
    char *bpf_comment_tmp = NULL;
    char *bpf_comment_start =  NULL;
    size_t bpf_len = 0;
    SCStat st;
    FILE *fp = NULL;
    size_t nm = 0;

    fp = fopen(filename, "r");
    if (fp == NULL) {
        SCLogError("Failed to open file %s", filename);
        exit(EXIT_FAILURE);
    }

    if (SCFstatFn(fileno(fp), &st) != 0) {
        SCLogError("Failed to stat file %s", filename);
        exit(EXIT_FAILURE);
    }
    // st.st_size is signed on Windows
    bpf_len = ((size_t)(st.st_size)) + 1;

    bpf_filter = SCMalloc(bpf_len);
    if (unlikely(bpf_filter == NULL)) {
        SCLogError("Failed to allocate buffer for bpf filter in file %s", filename);
        exit(EXIT_FAILURE);
    }
    memset(bpf_filter, 0x00, bpf_len);

    nm = fread(bpf_filter, 1, bpf_len - 1, fp);
    if ((ferror(fp) != 0) || (nm != (bpf_len - 1))) {
        SCLogError("Failed to read complete BPF file %s", filename);
        SCFree(bpf_filter);
        fclose(fp);
        exit(EXIT_FAILURE);
    }
    fclose(fp);
    bpf_filter[nm] = '\0';

    if(strlen(bpf_filter) > 0) {
        /*replace comments with space*/
        bpf_comment_start = bpf_filter;
        while((bpf_comment_tmp = strchr(bpf_comment_start, '#')) != NULL) {
            while((*bpf_comment_tmp !='\0') &&
                (*bpf_comment_tmp != '\r') && (*bpf_comment_tmp != '\n'))
            {
                *bpf_comment_tmp++ = ' ';
            }
            bpf_comment_start = bpf_comment_tmp;
        }
        /*remove remaining '\r' and '\n' */
        while((bpf_comment_tmp = strchr(bpf_filter, '\r')) != NULL) {
            *bpf_comment_tmp = ' ';
        }
        while((bpf_comment_tmp = strchr(bpf_filter, '\n')) != NULL) {
            *bpf_comment_tmp = ' ';
        }
        /* cut trailing spaces */
        while (strlen(bpf_filter) > 0 &&
                bpf_filter[strlen(bpf_filter)-1] == ' ')
        {
            bpf_filter[strlen(bpf_filter)-1] = '\0';
        }
        if (strlen(bpf_filter) > 0) {
            if (ConfSetFinal("bpf-filter", bpf_filter) != 1) {
                SCFree(bpf_filter);
                FatalError("failed to set bpf filter");
            }
        }
    }
    SCFree(bpf_filter);
}

static void PrintUsage(const char *progname)
{
#ifdef REVISION
    printf("%s %s (%s)\n", PROG_NAME, PROG_VER, xstr(REVISION));
#else
    printf("%s %s\n", PROG_NAME, PROG_VER);
#endif
    printf("USAGE: %s [OPTIONS] [BPF FILTER]\n\n", progname);
    printf("\t-c <path>                            : path to configuration file\n");
    printf("\t-T                                   : test configuration file (use with -c)\n");
    printf("\t-i <dev or ip>                       : run in pcap live mode\n");
    printf("\t-F <bpf filter file>                 : bpf filter file\n");
    printf("\t-r <path>                            : run in pcap file/offline mode\n");
#ifdef NFQ
    printf("\t-q <qid[:qid]>                       : run in inline nfqueue mode (use colon to specify a range of queues)\n");
#endif /* NFQ */
#ifdef IPFW
    printf("\t-d <divert port>                     : run in inline ipfw divert mode\n");
#endif /* IPFW */
    printf("\t-s <path>                            : path to signature file loaded in addition to suricata.yaml settings (optional)\n");
    printf("\t-S <path>                            : path to signature file loaded exclusively (optional)\n");
    printf("\t-l <dir>                             : default log directory\n");
#ifndef OS_WIN32
    printf("\t-D                                   : run as daemon\n");
#else
    printf("\t--service-install                    : install as service\n");
    printf("\t--service-remove                     : remove service\n");
    printf("\t--service-change-params              : change service startup parameters\n");
#endif /* OS_WIN32 */
    printf("\t-k [all|none]                        : force checksum check (all) or disabled it (none)\n");
    printf("\t-V                                   : display Suricata version\n");
    printf("\t-v                                   : be more verbose (use multiple times to increase verbosity)\n");
#ifdef UNITTESTS
    printf("\t-u                                   : run the unittests and exit\n");
    printf("\t-U, --unittest-filter=REGEX          : filter unittests with a regex\n");
    printf("\t--list-unittests                     : list unit tests\n");
    printf("\t--fatal-unittests                    : enable fatal failure on unittest error\n");
    printf("\t--unittests-coverage                 : display unittest coverage report\n");
#endif /* UNITTESTS */
    printf("\t--list-app-layer-protos              : list supported app layer protocols\n");
    printf("\t--list-keywords[=all|csv|<kword>]    : list keywords implemented by the engine\n");
    printf("\t--list-runmodes                      : list supported runmodes\n");
    printf("\t--runmode <runmode_id>               : specific runmode modification the engine should run.  The argument\n"
           "\t                                       supplied should be the id for the runmode obtained by running\n"
           "\t                                       --list-runmodes\n");
    printf("\t--engine-analysis                    : print reports on analysis of different sections in the engine and exit.\n"
           "\t                                       Please have a look at the conf parameter engine-analysis on what reports\n"
           "\t                                       can be printed\n");
    printf("\t--pidfile <file>                     : write pid to this file\n");
    printf("\t--init-errors-fatal                  : enable fatal failure on signature init error\n");
    printf("\t--disable-detection                  : disable detection engine\n");
    printf("\t--dump-config                        : show the running configuration\n");
    printf("\t--dump-features                      : display provided features\n");
    printf("\t--build-info                         : display build information\n");
    printf("\t--pcap[=<dev>]                       : run in pcap mode, no value select interfaces from suricata.yaml\n");
    printf("\t--pcap-file-continuous               : when running in pcap mode with a directory, continue checking directory for pcaps until interrupted\n");
    printf("\t--pcap-file-delete                   : when running in replay mode (-r with directory or file), will delete pcap files that have been processed when done\n");
    printf("\t--pcap-file-recursive                : will descend into subdirectories when running in replay mode (-r)\n");
#ifdef HAVE_PCAP_SET_BUFF
    printf("\t--pcap-buffer-size                   : size of the pcap buffer value from 0 - %i\n",INT_MAX);
#endif /* HAVE_SET_PCAP_BUFF */
#ifdef HAVE_DPDK
    printf("\t--dpdk                               : run in dpdk mode, uses interfaces from "
           "suricata.yaml\n");
#endif
#ifdef HAVE_AF_PACKET
    printf("\t--af-packet[=<dev>]                  : run in af-packet mode, no value select interfaces from suricata.yaml\n");
#endif
#ifdef HAVE_AF_XDP
    printf("\t--af-xdp[=<dev>]                     : run in af-xdp mode, no value select "
           "interfaces from suricata.yaml\n");
#endif
#ifdef HAVE_NETMAP
    printf("\t--netmap[=<dev>]                     : run in netmap mode, no value select interfaces from suricata.yaml\n");
#endif
#ifdef HAVE_PFRING
    printf("\t--pfring[=<dev>]                     : run in pfring mode, use interfaces from suricata.yaml\n");
    printf("\t--pfring-int <dev>                   : run in pfring mode, use interface <dev>\n");
    printf("\t--pfring-cluster-id <id>             : pfring cluster id \n");
    printf("\t--pfring-cluster-type <type>         : pfring cluster type for PF_RING 4.1.2 and later cluster_round_robin|cluster_flow\n");
#endif /* HAVE_PFRING */
    printf("\t--simulate-ips                       : force engine into IPS mode. Useful for QA\n");
#ifdef HAVE_LIBCAP_NG
    printf("\t--user <user>                        : run suricata as this user after init\n");
    printf("\t--group <group>                      : run suricata as this group after init\n");
#endif /* HAVE_LIBCAP_NG */
    printf("\t--erf-in <path>                      : process an ERF file\n");
#ifdef HAVE_DAG
    printf("\t--dag <dagX:Y>                       : process ERF records from DAG interface X, stream Y\n");
#endif
#ifdef HAVE_NAPATECH
    printf("\t--napatech                           : run Napatech Streams using the API\n");
#endif
#ifdef BUILD_UNIX_SOCKET
    printf("\t--unix-socket[=<file>]               : use unix socket to control suricata work\n");
#endif
#ifdef WINDIVERT
    printf("\t--windivert <filter>                 : run in inline WinDivert mode\n");
    printf("\t--windivert-forward <filter>         : run in inline WinDivert mode, as a gateway\n");
#endif
#ifdef HAVE_LIBNET11
    printf("\t--reject-dev <dev>                   : send reject packets from this interface\n");
#endif
    printf("\t--include <path>                     : additional configuration file\n");
    printf("\t--set name=value                     : set a configuration value\n");
    printf("\n");
    printf("\nTo run the engine with default configuration on "
            "interface eth0 with signature file \"signatures.rules\", run the "
            "command as:\n\n%s -c suricata.yaml -s signatures.rules -i eth0 \n\n",
            progname);
}

static void PrintBuildInfo(void)
{
    const char *bits;
    const char *endian;
    char features[2048] = "";
    const char *tls;

    printf("This is %s version %s\n", PROG_NAME, GetProgramVersion());
#ifdef DEBUG
    strlcat(features, "DEBUG ", sizeof(features));
#endif
#ifdef DEBUG_VALIDATION
    strlcat(features, "DEBUG_VALIDATION ", sizeof(features));
#endif
#ifdef UNITTESTS
    strlcat(features, "UNITTESTS ", sizeof(features));
#endif
#ifdef NFQ
    strlcat(features, "NFQ ", sizeof(features));
#endif
#ifdef IPFW
    strlcat(features, "IPFW ", sizeof(features));
#endif
#ifdef HAVE_PCAP_SET_BUFF
    strlcat(features, "PCAP_SET_BUFF ", sizeof(features));
#endif
#ifdef HAVE_PFRING
    strlcat(features, "PF_RING ", sizeof(features));
#endif
#ifdef HAVE_AF_PACKET
    strlcat(features, "AF_PACKET ", sizeof(features));
#endif
#ifdef HAVE_NETMAP
    strlcat(features, "NETMAP ", sizeof(features));
#endif
#ifdef HAVE_PACKET_FANOUT
    strlcat(features, "HAVE_PACKET_FANOUT ", sizeof(features));
#endif
#ifdef HAVE_DAG
    strlcat(features, "DAG ", sizeof(features));
#endif
#ifdef HAVE_LIBCAP_NG
    strlcat(features, "LIBCAP_NG ", sizeof(features));
#endif
#ifdef HAVE_LIBNET11
    strlcat(features, "LIBNET1.1 ", sizeof(features));
#endif
#ifdef HAVE_HTP_URI_NORMALIZE_HOOK
    strlcat(features, "HAVE_HTP_URI_NORMALIZE_HOOK ", sizeof(features));
#endif
#ifdef PCRE2_HAVE_JIT
    strlcat(features, "PCRE_JIT ", sizeof(features));
#endif
    /* For compatibility, just say we have HAVE_NSS. */
    strlcat(features, "HAVE_NSS ", sizeof(features));
    /* HTTP2_DECOMPRESSION is not an optional feature in this major version */
    strlcat(features, "HTTP2_DECOMPRESSION ", sizeof(features));
#ifdef HAVE_LUA
    strlcat(features, "HAVE_LUA ", sizeof(features));
#endif
#ifdef HAVE_JA3
    strlcat(features, "HAVE_JA3 ", sizeof(features));
#endif
#ifdef HAVE_JA4
    strlcat(features, "HAVE_JA4 ", sizeof(features));
#endif
#ifdef HAVE_LUAJIT
    strlcat(features, "HAVE_LUAJIT ", sizeof(features));
#endif
    strlcat(features, "HAVE_LIBJANSSON ", sizeof(features));
#ifdef PROFILING
    strlcat(features, "PROFILING ", sizeof(features));
#endif
#ifdef PROFILE_LOCKING
    strlcat(features, "PROFILE_LOCKING ", sizeof(features));
#endif
#if defined(TLS_C11) || defined(TLS_GNU)
    strlcat(features, "TLS ", sizeof(features));
#endif
#if defined(TLS_C11)
    strlcat(features, "TLS_C11 ", sizeof(features));
#elif defined(TLS_GNU)
    strlcat(features, "TLS_GNU ", sizeof(features));
#endif
#ifdef HAVE_MAGIC
    strlcat(features, "MAGIC ", sizeof(features));
#endif
    strlcat(features, "RUST ", sizeof(features));
#if defined(SC_ADDRESS_SANITIZER)
    strlcat(features, "ASAN ", sizeof(features));
#endif
#if defined(HAVE_POPCNT64)
    strlcat(features, "POPCNT64 ", sizeof(features));
#endif
    if (strlen(features) == 0) {
        strlcat(features, "none", sizeof(features));
    }

    printf("Features: %s\n", features);

    /* SIMD stuff */
    memset(features, 0x00, sizeof(features));
#if defined(__SSE4_2__)
    strlcat(features, "SSE_4_2 ", sizeof(features));
#endif
#if defined(__SSE4_1__)
    strlcat(features, "SSE_4_1 ", sizeof(features));
#endif
#if defined(__SSE3__)
    strlcat(features, "SSE_3 ", sizeof(features));
#endif
#if defined(__SSE2__)
    strlcat(features, "SSE_2 ", sizeof(features));
#endif
    if (strlen(features) == 0) {
        strlcat(features, "none", sizeof(features));
    }
    printf("SIMD support: %s\n", features);

    /* atomics stuff */
    memset(features, 0x00, sizeof(features));
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1)
    strlcat(features, "1 ", sizeof(features));
#endif
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2)
    strlcat(features, "2 ", sizeof(features));
#endif
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
    strlcat(features, "4 ", sizeof(features));
#endif
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)
    strlcat(features, "8 ", sizeof(features));
#endif
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)
    strlcat(features, "16 ", sizeof(features));
#endif
    if (strlen(features) == 0) {
        strlcat(features, "none", sizeof(features));
    } else {
        strlcat(features, "byte(s)", sizeof(features));
    }
    printf("Atomic intrinsics: %s\n", features);

#if __WORDSIZE == 64
    bits = "64-bits";
#elif __WORDSIZE == 32
    bits = "32-bits";
#else
    bits = "<unknown>-bits";
#endif

#if __BYTE_ORDER == __BIG_ENDIAN
    endian = "Big-endian";
#elif __BYTE_ORDER == __LITTLE_ENDIAN
    endian = "Little-endian";
#else
    endian = "<unknown>-endian";
#endif

    printf("%s, %s architecture\n", bits, endian);
#ifdef __GNUC__
    printf("GCC version %s, C version %"PRIiMAX"\n", __VERSION__, (intmax_t)__STDC_VERSION__);
#else
    printf("C version %"PRIiMAX"\n", (intmax_t)__STDC_VERSION__);
#endif

#if __SSP__ == 1
    printf("compiled with -fstack-protector\n");
#endif
#if __SSP_ALL__ == 2
    printf("compiled with -fstack-protector-all\n");
#endif
/*
 * Workaround for special defines of _FORTIFY_SOURCE like
 * FORTIFY_SOURCE=((defined __OPTIMIZE && OPTIMIZE > 0) ? 2 : 0)
 * which is used by Gentoo for example and would result in the error
 * 'defined' undeclared when _FORTIFY_SOURCE used via %d in printf func
 *
 */
#if _FORTIFY_SOURCE == 2
    printf("compiled with _FORTIFY_SOURCE=2\n");
#elif _FORTIFY_SOURCE == 1
    printf("compiled with _FORTIFY_SOURCE=1\n");
#elif _FORTIFY_SOURCE == 0
    printf("compiled with _FORTIFY_SOURCE=0\n");
#endif
#ifdef CLS
    printf("L1 cache line size (CLS)=%d\n", CLS);
#endif
#if defined(TLS_C11)
    tls = "_Thread_local";
#elif defined(TLS_GNU)
    tls = "__thread";
#else
#error "Unsupported thread local"
#endif
    printf("thread local storage method: %s\n", tls);

    printf("compiled with %s, linked against %s\n",
           HTP_VERSION_STRING_FULL, htp_get_version());
    printf("\n");
#include "build-info.h"
}

int coverage_unittests;
int g_ut_modules;
int g_ut_covered;

void RegisterAllModules(void)
{
    /* commanders */
    TmModuleUnixManagerRegister();
    /* managers */
    TmModuleFlowManagerRegister();
    TmModuleFlowRecyclerRegister();
    TmModuleBypassedFlowManagerRegister();
    /* nfq */
    TmModuleReceiveNFQRegister();
    TmModuleVerdictNFQRegister();
    TmModuleDecodeNFQRegister();
    /* ipfw */
    TmModuleReceiveIPFWRegister();
    TmModuleVerdictIPFWRegister();
    TmModuleDecodeIPFWRegister();
    /* pcap live */
    TmModuleReceivePcapRegister();
    TmModuleDecodePcapRegister();
    /* pcap file */
    TmModuleReceivePcapFileRegister();
    TmModuleDecodePcapFileRegister();
    /* af-packet */
    TmModuleReceiveAFPRegister();
    TmModuleDecodeAFPRegister();
    /* af-xdp */
    TmModuleReceiveAFXDPRegister();
    TmModuleDecodeAFXDPRegister();
    /* netmap */
    TmModuleReceiveNetmapRegister();
    TmModuleDecodeNetmapRegister();
    /* pfring */
    TmModuleReceivePfringRegister();
    TmModuleDecodePfringRegister();
    /* dag file */
    TmModuleReceiveErfFileRegister();
    TmModuleDecodeErfFileRegister();
    /* dag live */
    TmModuleReceiveErfDagRegister();
    TmModuleDecodeErfDagRegister();
    /* napatech */
    TmModuleNapatechStreamRegister();
    TmModuleNapatechDecodeRegister();

    /* flow worker */
    TmModuleFlowWorkerRegister();
    /* respond-reject */
    TmModuleRespondRejectRegister();

    /* log api */
    TmModuleLoggerRegister();
    TmModuleStatsLoggerRegister();

    TmModuleDebugList();
    /* nflog */
    TmModuleReceiveNFLOGRegister();
    TmModuleDecodeNFLOGRegister();

    /* windivert */
    TmModuleReceiveWinDivertRegister();
    TmModuleVerdictWinDivertRegister();
    TmModuleDecodeWinDivertRegister();

    /* Dpdk */
    TmModuleReceiveDPDKRegister();
    TmModuleDecodeDPDKRegister();
}

static TmEcode LoadYamlConfig(SCInstance *suri)
{
    SCEnter();

    if (suri->conf_filename == NULL)
        suri->conf_filename = DEFAULT_CONF_FILE;

    if (ConfYamlLoadFile(suri->conf_filename) != 0) {
        /* Error already displayed. */
        SCReturnInt(TM_ECODE_FAILED);
    }

    if (suri->additional_configs) {
        for (int i = 0; suri->additional_configs[i] != NULL; i++) {
            SCLogConfig("Loading additional configuration file %s", suri->additional_configs[i]);
            ConfYamlHandleInclude(ConfGetRootNode(), suri->additional_configs[i]);
        }
    }

    SCReturnInt(TM_ECODE_OK);
}

static TmEcode ParseInterfacesList(const int runmode, char *pcap_dev)
{
    SCEnter();

    /* run the selected runmode */
    if (runmode == RUNMODE_PCAP_DEV) {
        if (strlen(pcap_dev) == 0) {
            int ret = LiveBuildDeviceList("pcap");
            if (ret == 0) {
                SCLogError("No interface found in config for pcap");
                SCReturnInt(TM_ECODE_FAILED);
            }
        }
    } else if (runmode == RUNMODE_PFRING) {
        /* FIXME add backward compat support */
        /* iface has been set on command line */
        if (strlen(pcap_dev)) {
            if (ConfSetFinal("pfring.live-interface", pcap_dev) != 1) {
                SCLogError("Failed to set pfring.live-interface");
                SCReturnInt(TM_ECODE_FAILED);
            }
        } else {
            /* not an error condition if we have a 1.0 config */
            LiveBuildDeviceList("pfring");
        }
#ifdef HAVE_DPDK
    } else if (runmode == RUNMODE_DPDK) {
        char iface_selector[] = "dpdk.interfaces";
        int ret = LiveBuildDeviceList(iface_selector);
        if (ret == 0) {
            SCLogError("No interface found in config for %s", iface_selector);
            SCReturnInt(TM_ECODE_FAILED);
        }
#endif
#ifdef HAVE_AF_PACKET
    } else if (runmode == RUNMODE_AFP_DEV) {
        /* iface has been set on command line */
        if (strlen(pcap_dev)) {
            if (ConfSetFinal("af-packet.live-interface", pcap_dev) != 1) {
                SCLogError("Failed to set af-packet.live-interface");
                SCReturnInt(TM_ECODE_FAILED);
            }
        } else {
            int ret = LiveBuildDeviceList("af-packet");
            if (ret == 0) {
                SCLogError("No interface found in config for af-packet");
                SCReturnInt(TM_ECODE_FAILED);
            }
        }
#endif
#ifdef HAVE_AF_XDP
    } else if (runmode == RUNMODE_AFXDP_DEV) {
        /* iface has been set on command line */
        if (strlen(pcap_dev)) {
            if (ConfSetFinal("af-xdp.live-interface", pcap_dev) != 1) {
                SCLogError("Failed to set af-xdp.live-interface");
                SCReturnInt(TM_ECODE_FAILED);
            }
        } else {
            int ret = LiveBuildDeviceList("af-xdp");
            if (ret == 0) {
                SCLogError("No interface found in config for af-xdp");
                SCReturnInt(TM_ECODE_FAILED);
            }
        }
#endif
#ifdef HAVE_NETMAP
    } else if (runmode == RUNMODE_NETMAP) {
        /* iface has been set on command line */
        if (strlen(pcap_dev)) {
            if (ConfSetFinal("netmap.live-interface", pcap_dev) != 1) {
                SCLogError("Failed to set netmap.live-interface");
                SCReturnInt(TM_ECODE_FAILED);
            }
        } else {
            int ret = LiveBuildDeviceList("netmap");
            if (ret == 0) {
                SCLogError("No interface found in config for netmap");
                SCReturnInt(TM_ECODE_FAILED);
            }
        }
#endif
#ifdef HAVE_NFLOG
    } else if (runmode == RUNMODE_NFLOG) {
        int ret = LiveBuildDeviceListCustom("nflog", "group");
        if (ret == 0) {
            SCLogError("No group found in config for nflog");
            SCReturnInt(TM_ECODE_FAILED);
        }
#endif
    }

    SCReturnInt(TM_ECODE_OK);
}

static void SCInstanceInit(SCInstance *suri, const char *progname)
{
    memset(suri, 0x00, sizeof(*suri));

    suri->progname = progname;
    suri->run_mode = RUNMODE_UNKNOWN;

    memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
    suri->sig_file = NULL;
    suri->sig_file_exclusive = FALSE;
    suri->pid_filename = NULL;
    suri->regex_arg = NULL;

    suri->keyword_info = NULL;
    suri->runmode_custom_mode = NULL;
#ifndef OS_WIN32
    suri->user_name = NULL;
    suri->group_name = NULL;
    suri->do_setuid = FALSE;
    suri->do_setgid = FALSE;
#endif /* OS_WIN32 */
    suri->userid = 0;
    suri->groupid = 0;
    suri->delayed_detect = 0;
    suri->daemon = 0;
    suri->offline = 0;
    suri->verbose = 0;
    /* use -1 as unknown */
    suri->checksum_validation = -1;
#if HAVE_DETECT_DISABLED==1
    g_detect_disabled = suri->disabled_detect = 1;
#else
    g_detect_disabled = suri->disabled_detect = 0;
#endif
}

const char *GetDocURL(void)
{
    const char *prog_ver = GetProgramVersion();
    if (strstr(prog_ver, "RELEASE") != NULL) {
        return DOC_URL "suricata-" PROG_VER;
    }
    return DOC_URL "latest";
}

/** \brief get string with program version
 *
 *  Get the program version as passed to us from AC_INIT
 *
 *  Add 'RELEASE' is no '-dev' in the version. Add the REVISION if passed
 *  to us.
 *
 *  Possible outputs:
 *  release:      '5.0.1 RELEASE'
 *  dev with rev: '5.0.1-dev (64a789bbf 2019-10-18)'
 *  dev w/o rev:  '5.0.1-dev'
 */
const char *GetProgramVersion(void)
{
    if (strstr(PROG_VER, "-dev") == NULL) {
        return PROG_VER " RELEASE";
    } else {
#ifdef REVISION
        return PROG_VER " (" xstr(REVISION) ")";
#else
        return PROG_VER;
#endif
    }
}

static TmEcode PrintVersion(void)
{
    printf("This is %s version %s\n", PROG_NAME, GetProgramVersion());
    return TM_ECODE_OK;
}

static TmEcode LogVersion(SCInstance *suri)
{
    const char *mode = suri->system ? "SYSTEM" : "USER";
    SCLogNotice("This is %s version %s running in %s mode",
            PROG_NAME, GetProgramVersion(), mode);
    return TM_ECODE_OK;
}

static void SCSetStartTime(SCInstance *suri)
{
    memset(&suri->start_time, 0, sizeof(suri->start_time));
    gettimeofday(&suri->start_time, NULL);
}

static void SCPrintElapsedTime(struct timeval *start_time)
{
    if (start_time == NULL)
        return;
    struct timeval end_time;
    memset(&end_time, 0, sizeof(end_time));
    gettimeofday(&end_time, NULL);
    uint64_t milliseconds = ((end_time.tv_sec - start_time->tv_sec) * 1000) +
        (((1000000 + end_time.tv_usec - start_time->tv_usec) / 1000) - 1000);
    SCLogInfo("time elapsed %.3fs", (float)milliseconds/(float)1000);
}

static int ParseCommandLineAfpacket(SCInstance *suri, const char *in_arg)
{
#ifdef HAVE_AF_PACKET
    if (suri->run_mode == RUNMODE_UNKNOWN) {
        suri->run_mode = RUNMODE_AFP_DEV;
        if (in_arg) {
            LiveRegisterDeviceName(in_arg);
            memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
            strlcpy(suri->pcap_dev, in_arg, sizeof(suri->pcap_dev));
        }
    } else if (suri->run_mode == RUNMODE_AFP_DEV) {
        if (in_arg) {
            LiveRegisterDeviceName(in_arg);
        } else {
            SCLogInfo("Multiple af-packet option without interface on each is useless");
        }
    } else {
        SCLogError("more than one run mode "
                   "has been specified");
        PrintUsage(suri->progname);
        return TM_ECODE_FAILED;
    }
    return TM_ECODE_OK;
#else
    SCLogError("AF_PACKET not enabled. On Linux "
               "host, make sure to pass --enable-af-packet to "
               "configure when building.");
    return TM_ECODE_FAILED;
#endif
}

static int ParseCommandLineAfxdp(SCInstance *suri, const char *in_arg)
{
#ifdef HAVE_AF_XDP
    if (suri->run_mode == RUNMODE_UNKNOWN) {
        suri->run_mode = RUNMODE_AFXDP_DEV;
        if (in_arg) {
            LiveRegisterDeviceName(in_arg);
            memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
            strlcpy(suri->pcap_dev, in_arg, sizeof(suri->pcap_dev));
        }
    } else if (suri->run_mode == RUNMODE_AFXDP_DEV) {
        if (in_arg) {
            LiveRegisterDeviceName(in_arg);
        } else {
            SCLogInfo("Multiple af-xdp options without interface on each is useless");
        }
    } else {
        SCLogError("more than one run mode "
                   "has been specified");
        PrintUsage(suri->progname);
        return TM_ECODE_FAILED;
    }
    return TM_ECODE_OK;
#else
    SCLogError("AF_XDP not enabled. On Linux "
               "host, make sure correct libraries are installed,"
               " see documentation for information.");
    return TM_ECODE_FAILED;
#endif
}

static int ParseCommandLineDpdk(SCInstance *suri, const char *in_arg)
{
#ifdef HAVE_DPDK
    if (suri->run_mode == RUNMODE_UNKNOWN) {
        suri->run_mode = RUNMODE_DPDK;
    } else if (suri->run_mode == RUNMODE_DPDK) {
        SCLogInfo("Multiple dpdk options have no effect on Suricata");
    } else {
        SCLogError("more than one run mode "
                   "has been specified");
        PrintUsage(suri->progname);
        return TM_ECODE_FAILED;
    }
    return TM_ECODE_OK;
#else
    SCLogError("DPDK not enabled. On Linux "
               "host, make sure to pass --enable-dpdk to "
               "configure when building.");
    return TM_ECODE_FAILED;
#endif
}

static int ParseCommandLinePcapLive(SCInstance *suri, const char *in_arg)
{
#if defined(OS_WIN32) && !defined(HAVE_LIBWPCAP)
    /* If running on Windows without Npcap, bail early as live capture is not supported. */
    FatalError("Live capture not available. To support live capture compile against Npcap.");
#endif
    memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));

    if (in_arg != NULL) {
        /* some windows shells require escaping of the \ in \Device. Otherwise
         * the backslashes are stripped. We put them back here. */
        if (strlen(in_arg) > 9 && strncmp(in_arg, "DeviceNPF", 9) == 0) {
            snprintf(suri->pcap_dev, sizeof(suri->pcap_dev), "\\Device\\NPF%s", in_arg+9);
        } else {
            strlcpy(suri->pcap_dev, in_arg, sizeof(suri->pcap_dev));
            PcapTranslateIPToDevice(suri->pcap_dev, sizeof(suri->pcap_dev));
        }

        if (strcmp(suri->pcap_dev, in_arg) != 0) {
            SCLogInfo("translated %s to pcap device %s", in_arg, suri->pcap_dev);
        } else if (strlen(suri->pcap_dev) > 0 && isdigit((unsigned char)suri->pcap_dev[0])) {
            SCLogError("failed to find a pcap device for IP %s", in_arg);
            return TM_ECODE_FAILED;
        }
    }

    if (suri->run_mode == RUNMODE_UNKNOWN) {
        suri->run_mode = RUNMODE_PCAP_DEV;
        if (in_arg) {
            LiveRegisterDeviceName(suri->pcap_dev);
        }
    } else if (suri->run_mode == RUNMODE_PCAP_DEV) {
        LiveRegisterDeviceName(suri->pcap_dev);
    } else {
        SCLogError("more than one run mode "
                   "has been specified");
        PrintUsage(suri->progname);
        return TM_ECODE_FAILED;
    }
    return TM_ECODE_OK;
}

/**
 * Helper function to check if log directory is writable
 */
static bool IsLogDirectoryWritable(const char* str)
{
    if (access(str, W_OK) == 0)
        return true;
    return false;
}

static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri)
{
    int opt;

    int dump_config = 0;
    int dump_features = 0;
    int list_app_layer_protocols = 0;
    int list_unittests = 0;
    int list_runmodes = 0;
    int list_keywords = 0;
    int build_info = 0;
    int conf_test = 0;
    int engine_analysis = 0;
    int ret = TM_ECODE_OK;

#ifdef UNITTESTS
    coverage_unittests = 0;
    g_ut_modules = 0;
    g_ut_covered = 0;
#endif

    // clang-format off
    struct option long_opts[] = {
        {"dump-config", 0, &dump_config, 1},
        {"dump-features", 0, &dump_features, 1},
        {"pfring", optional_argument, 0, 0},
        {"pfring-int", required_argument, 0, 0},
        {"pfring-cluster-id", required_argument, 0, 0},
        {"pfring-cluster-type", required_argument, 0, 0},
#ifdef HAVE_DPDK
        {"dpdk", 0, 0, 0},
#endif
        {"af-packet", optional_argument, 0, 0},
        {"af-xdp", optional_argument, 0, 0},
        {"netmap", optional_argument, 0, 0},
        {"pcap", optional_argument, 0, 0},
        {"pcap-file-continuous", 0, 0, 0},
        {"pcap-file-delete", 0, 0, 0},
        {"pcap-file-recursive", 0, 0, 0},
        {"simulate-ips", 0, 0 , 0},
        {"no-random", 0, &g_disable_randomness, 1},
        {"strict-rule-keywords", optional_argument, 0, 0},

        {"capture-plugin", required_argument, 0, 0},
        {"capture-plugin-args", required_argument, 0, 0},

#ifdef BUILD_UNIX_SOCKET
        {"unix-socket", optional_argument, 0, 0},
#endif
        {"pcap-buffer-size", required_argument, 0, 0},
        {"unittest-filter", required_argument, 0, 'U'},
        {"list-app-layer-protos", 0, &list_app_layer_protocols, 1},
        {"list-unittests", 0, &list_unittests, 1},
        {"list-runmodes", 0, &list_runmodes, 1},
        {"list-keywords", optional_argument, &list_keywords, 1},
        {"runmode", required_argument, NULL, 0},
        {"engine-analysis", 0, &engine_analysis, 1},
#ifdef OS_WIN32
		{"service-install", 0, 0, 0},
		{"service-remove", 0, 0, 0},
		{"service-change-params", 0, 0, 0},
#endif /* OS_WIN32 */
        {"pidfile", required_argument, 0, 0},
        {"init-errors-fatal", 0, 0, 0},
        {"disable-detection", 0, 0, 0},
        {"disable-hashing", 0, 0, 0},
        {"fatal-unittests", 0, 0, 0},
        {"unittests-coverage", 0, &coverage_unittests, 1},
        {"user", required_argument, 0, 0},
        {"group", required_argument, 0, 0},
        {"erf-in", required_argument, 0, 0},
        {"dag", required_argument, 0, 0},
        {"napatech", 0, 0, 0},
        {"build-info", 0, &build_info, 1},
        {"data-dir", required_argument, 0, 0},
#ifdef WINDIVERT
        {"windivert", required_argument, 0, 0},
        {"windivert-forward", required_argument, 0, 0},
#endif
#ifdef HAVE_LIBNET11
        {"reject-dev", required_argument, 0, 0},
#endif
        {"set", required_argument, 0, 0},
#ifdef HAVE_NFLOG
        {"nflog", optional_argument, 0, 0},
#endif
        {"simulate-packet-flow-memcap", required_argument, 0, 0},
        {"simulate-applayer-error-at-offset-ts", required_argument, 0, 0},
        {"simulate-applayer-error-at-offset-tc", required_argument, 0, 0},
        {"simulate-packet-loss", required_argument, 0, 0},
        {"simulate-packet-tcp-reassembly-memcap", required_argument, 0, 0},
        {"simulate-packet-tcp-ssn-memcap", required_argument, 0, 0},
        {"simulate-packet-defrag-memcap", required_argument, 0, 0},
        {"simulate-alert-queue-realloc-failure", 0, 0, 0},
        {"include", required_argument, 0, 0},

        {NULL, 0, NULL, 0}
    };
    // clang-format on

    /* getopt_long stores the option index here. */
    int option_index = 0;

    char short_opts[] = "c:TDhi:l:q:d:r:us:S:U:VF:vk:";

    while ((opt = getopt_long(argc, argv, short_opts, long_opts, &option_index)) != -1) {
        switch (opt) {
        case 0:
            if (strcmp((long_opts[option_index]).name , "pfring") == 0 ||
                strcmp((long_opts[option_index]).name , "pfring-int") == 0) {
#ifdef HAVE_PFRING
                suri->run_mode = RUNMODE_PFRING;
                if (optarg != NULL) {
                    memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
                    strlcpy(suri->pcap_dev, optarg,
                            ((strlen(optarg) < sizeof(suri->pcap_dev)) ?
                             (strlen(optarg) + 1) : sizeof(suri->pcap_dev)));
                    LiveRegisterDeviceName(optarg);
                }
#else
                SCLogError("PF_RING not enabled. Make sure "
                           "to pass --enable-pfring to configure when building.");
                return TM_ECODE_FAILED;
#endif /* HAVE_PFRING */
            }
            else if(strcmp((long_opts[option_index]).name , "pfring-cluster-id") == 0){
#ifdef HAVE_PFRING
                if (ConfSetFinal("pfring.cluster-id", optarg) != 1) {
                    SCLogError("failed to set pfring.cluster-id");
                    return TM_ECODE_FAILED;
                }
#else
                SCLogError("PF_RING not enabled. Make sure "
                           "to pass --enable-pfring to configure when building.");
                return TM_ECODE_FAILED;
#endif /* HAVE_PFRING */
            }
            else if(strcmp((long_opts[option_index]).name , "pfring-cluster-type") == 0){
#ifdef HAVE_PFRING
                if (ConfSetFinal("pfring.cluster-type", optarg) != 1) {
                    SCLogError("failed to set pfring.cluster-type");
                    return TM_ECODE_FAILED;
                }
#else
                SCLogError("PF_RING not enabled. Make sure "
                           "to pass --enable-pfring to configure when building.");
                return TM_ECODE_FAILED;
#endif /* HAVE_PFRING */
            }
            else if (strcmp((long_opts[option_index]).name , "capture-plugin") == 0){
                suri->run_mode = RUNMODE_PLUGIN;
                suri->capture_plugin_name = optarg;
            }
            else if (strcmp((long_opts[option_index]).name , "capture-plugin-args") == 0){
                suri->capture_plugin_args = optarg;
            } else if (strcmp((long_opts[option_index]).name, "dpdk") == 0) {
                if (ParseCommandLineDpdk(suri, optarg) != TM_ECODE_OK) {
                    return TM_ECODE_FAILED;
                }
            } else if (strcmp((long_opts[option_index]).name, "af-packet") == 0) {
                if (ParseCommandLineAfpacket(suri, optarg) != TM_ECODE_OK) {
                    return TM_ECODE_FAILED;
                }
            } else if (strcmp((long_opts[option_index]).name, "af-xdp") == 0) {
                if (ParseCommandLineAfxdp(suri, optarg) != TM_ECODE_OK) {
                    return TM_ECODE_FAILED;
                }
            } else if (strcmp((long_opts[option_index]).name, "netmap") == 0) {
#ifdef HAVE_NETMAP
                if (suri->run_mode == RUNMODE_UNKNOWN) {
                    suri->run_mode = RUNMODE_NETMAP;
                    if (optarg) {
                        LiveRegisterDeviceName(optarg);
                        memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
                        strlcpy(suri->pcap_dev, optarg,
                                ((strlen(optarg) < sizeof(suri->pcap_dev)) ?
                                 (strlen(optarg) + 1) : sizeof(suri->pcap_dev)));
                    }
                } else if (suri->run_mode == RUNMODE_NETMAP) {
                    if (optarg) {
                        LiveRegisterDeviceName(optarg);
                    } else {
                        SCLogInfo("Multiple netmap option without interface on each is useless");
                        break;
                    }
                } else {
                    SCLogError("more than one run mode "
                               "has been specified");
                    PrintUsage(argv[0]);
                    return TM_ECODE_FAILED;
                }
#else
                SCLogError("NETMAP not enabled.");
                return TM_ECODE_FAILED;
#endif
            } else if (strcmp((long_opts[option_index]).name, "nflog") == 0) {
#ifdef HAVE_NFLOG
                if (suri->run_mode == RUNMODE_UNKNOWN) {
                    suri->run_mode = RUNMODE_NFLOG;
                    LiveBuildDeviceListCustom("nflog", "group");
                }
#else
                SCLogError("NFLOG not enabled.");
                return TM_ECODE_FAILED;
#endif /* HAVE_NFLOG */
            } else if (strcmp((long_opts[option_index]).name, "pcap") == 0) {
                if (ParseCommandLinePcapLive(suri, optarg) != TM_ECODE_OK) {
                    return TM_ECODE_FAILED;
                }
            } else if (strcmp((long_opts[option_index]).name, "simulate-ips") == 0) {
                SCLogInfo("Setting IPS mode");
                EngineModeSetIPS();
            } else if (strcmp((long_opts[option_index]).name, "init-errors-fatal") == 0) {
                if (ConfSetFinal("engine.init-failure-fatal", "1") != 1) {
                    SCLogError("failed to set engine init-failure-fatal");
                    return TM_ECODE_FAILED;
                }
#ifdef BUILD_UNIX_SOCKET
            } else if (strcmp((long_opts[option_index]).name , "unix-socket") == 0) {
                if (suri->run_mode == RUNMODE_UNKNOWN) {
                    suri->run_mode = RUNMODE_UNIX_SOCKET;
                    if (optarg) {
                        if (ConfSetFinal("unix-command.filename", optarg) != 1) {
                            SCLogError("failed to set unix-command.filename");
                            return TM_ECODE_FAILED;
                        }

                    }
                } else {
                    SCLogError("more than one run mode "
                               "has been specified");
                    PrintUsage(argv[0]);
                    return TM_ECODE_FAILED;
                }
#endif
            }
            else if(strcmp((long_opts[option_index]).name, "list-app-layer-protocols") == 0) {
                /* listing all supported app layer protocols */
            }
            else if(strcmp((long_opts[option_index]).name, "list-unittests") == 0) {
#ifdef UNITTESTS
                suri->run_mode = RUNMODE_LIST_UNITTEST;
#else
                SCLogError("unit tests not enabled. Make sure to pass --enable-unittests to "
                           "configure when building");
                return TM_ECODE_FAILED;
#endif /* UNITTESTS */
            } else if (strcmp((long_opts[option_index]).name, "list-runmodes") == 0) {
                suri->run_mode = RUNMODE_LIST_RUNMODES;
                return TM_ECODE_OK;
            } else if (strcmp((long_opts[option_index]).name, "list-keywords") == 0) {
                if (optarg) {
                    if (strcmp("short",optarg)) {
                        suri->keyword_info = optarg;
                    }
                }
            } else if (strcmp((long_opts[option_index]).name, "runmode") == 0) {
                suri->runmode_custom_mode = optarg;
            } else if(strcmp((long_opts[option_index]).name, "engine-analysis") == 0) {
                // do nothing for now
            }
#ifdef OS_WIN32
            else if(strcmp((long_opts[option_index]).name, "service-install") == 0) {
                suri->run_mode = RUNMODE_INSTALL_SERVICE;
                return TM_ECODE_OK;
            }
            else if(strcmp((long_opts[option_index]).name, "service-remove") == 0) {
                suri->run_mode = RUNMODE_REMOVE_SERVICE;
                return TM_ECODE_OK;
            }
            else if(strcmp((long_opts[option_index]).name, "service-change-params") == 0) {
                suri->run_mode = RUNMODE_CHANGE_SERVICE_PARAMS;
                return TM_ECODE_OK;
            }
#endif /* OS_WIN32 */
            else if(strcmp((long_opts[option_index]).name, "pidfile") == 0) {
                suri->pid_filename = SCStrdup(optarg);
                if (suri->pid_filename == NULL) {
                    SCLogError("strdup failed: %s", strerror(errno));
                    return TM_ECODE_FAILED;
                }
            }
            else if(strcmp((long_opts[option_index]).name, "disable-detection") == 0) {
                g_detect_disabled = suri->disabled_detect = 1;
            } else if (strcmp((long_opts[option_index]).name, "disable-hashing") == 0) {
                g_disable_hashing = true;
            } else if (strcmp((long_opts[option_index]).name, "fatal-unittests") == 0) {
#ifdef UNITTESTS
                unittests_fatal = 1;
#else
                SCLogError("unit tests not enabled. Make sure to pass --enable-unittests to "
                           "configure when building");
                return TM_ECODE_FAILED;
#endif /* UNITTESTS */
            } else if (strcmp((long_opts[option_index]).name, "user") == 0) {
#ifndef HAVE_LIBCAP_NG
                SCLogError("libcap-ng is required to"
                           " drop privileges, but it was not compiled into Suricata.");
                return TM_ECODE_FAILED;
#else
                suri->user_name = optarg;
                suri->do_setuid = TRUE;
#endif /* HAVE_LIBCAP_NG */
            } else if (strcmp((long_opts[option_index]).name, "group") == 0) {
#ifndef HAVE_LIBCAP_NG
                SCLogError("libcap-ng is required to"
                           " drop privileges, but it was not compiled into Suricata.");
                return TM_ECODE_FAILED;
#else
                suri->group_name = optarg;
                suri->do_setgid = TRUE;
#endif /* HAVE_LIBCAP_NG */
            } else if (strcmp((long_opts[option_index]).name, "erf-in") == 0) {
                suri->run_mode = RUNMODE_ERF_FILE;
                if (ConfSetFinal("erf-file.file", optarg) != 1) {
                    SCLogError("failed to set erf-file.file");
                    return TM_ECODE_FAILED;
                }
            } else if (strcmp((long_opts[option_index]).name, "dag") == 0) {
#ifdef HAVE_DAG
                if (suri->run_mode == RUNMODE_UNKNOWN) {
                    suri->run_mode = RUNMODE_DAG;
                }
                else if (suri->run_mode != RUNMODE_DAG) {
                    SCLogError("more than one run mode has been specified");
                    PrintUsage(argv[0]);
                    return TM_ECODE_FAILED;
                }
                LiveRegisterDeviceName(optarg);
#else
                SCLogError("libdag and a DAG card are required"
                           " to receive packets using --dag.");
                return TM_ECODE_FAILED;
#endif /* HAVE_DAG */
            } else if (strcmp((long_opts[option_index]).name, "napatech") == 0) {
#ifdef HAVE_NAPATECH
                suri->run_mode = RUNMODE_NAPATECH;
#else
                SCLogError("libntapi and a Napatech adapter are required"
                           " to capture packets using --napatech.");
                return TM_ECODE_FAILED;
#endif /* HAVE_NAPATECH */
            } else if (strcmp((long_opts[option_index]).name, "pcap-buffer-size") == 0) {
#ifdef HAVE_PCAP_SET_BUFF
                if (ConfSetFinal("pcap.buffer-size", optarg) != 1) {
                    SCLogError("failed to set pcap-buffer-size");
                    return TM_ECODE_FAILED;
                }
#else
                SCLogError("The version of libpcap you have"
                           " doesn't support setting buffer size.");
#endif /* HAVE_PCAP_SET_BUFF */
            } else if (strcmp((long_opts[option_index]).name, "build-info") == 0) {
                suri->run_mode = RUNMODE_PRINT_BUILDINFO;
                return TM_ECODE_OK;
            } else if (strcmp((long_opts[option_index]).name, "windivert-forward") == 0) {
#ifdef WINDIVERT
                if (suri->run_mode == RUNMODE_UNKNOWN) {
                    suri->run_mode = RUNMODE_WINDIVERT;
                    if (WinDivertRegisterQueue(true, optarg) == -1) {
                        exit(EXIT_FAILURE);
                    }
                } else if (suri->run_mode == RUNMODE_WINDIVERT) {
                    if (WinDivertRegisterQueue(true, optarg) == -1) {
                        exit(EXIT_FAILURE);
                    }
                } else {
                    SCLogError("more than one run mode "
                               "has been specified");
                    PrintUsage(argv[0]);
                    exit(EXIT_FAILURE);
                }
            }
            else if(strcmp((long_opts[option_index]).name, "windivert") == 0) {
                if (suri->run_mode == RUNMODE_UNKNOWN) {
                    suri->run_mode = RUNMODE_WINDIVERT;
                    if (WinDivertRegisterQueue(false, optarg) == -1) {
                        exit(EXIT_FAILURE);
                    }
                } else if (suri->run_mode == RUNMODE_WINDIVERT) {
                    if (WinDivertRegisterQueue(false, optarg) == -1) {
                        exit(EXIT_FAILURE);
                    }
                } else {
                    SCLogError("more than one run mode "
                               "has been specified");
                    PrintUsage(argv[0]);
                    exit(EXIT_FAILURE);
                }
#else
                SCLogError("WinDivert not enabled. Make sure to pass --enable-windivert to "
                           "configure when building.");
                return TM_ECODE_FAILED;
#endif /* WINDIVERT */
            } else if(strcmp((long_opts[option_index]).name, "reject-dev") == 0) {
#ifdef HAVE_LIBNET11
                BUG_ON(optarg == NULL); /* for static analysis */
                extern char *g_reject_dev;
                extern uint16_t g_reject_dev_mtu;
                g_reject_dev = optarg;
                int mtu = GetIfaceMTU(g_reject_dev);
                if (mtu > 0) {
                    g_reject_dev_mtu = (uint16_t)mtu;
                }
#else
                SCLogError("Libnet 1.1 support not enabled. Compile Suricata with libnet support.");
                return TM_ECODE_FAILED;
#endif
            }
            else if (strcmp((long_opts[option_index]).name, "set") == 0) {
                if (optarg != NULL) {
                    /* Quick validation. */
                    char *val = strchr(optarg, '=');
                    if (val == NULL) {
                        FatalError("Invalid argument for --set, must be key=val.");
                    }
                    if (!ConfSetFromString(optarg, 1)) {
                        FatalError("failed to set configuration value %s", optarg);
                    }
                }
            }
            else if (strcmp((long_opts[option_index]).name, "pcap-file-continuous") == 0) {
                if (ConfSetFinal("pcap-file.continuous", "true") != 1) {
                    SCLogError("Failed to set pcap-file.continuous");
                    return TM_ECODE_FAILED;
                }
            }
            else if (strcmp((long_opts[option_index]).name, "pcap-file-delete") == 0) {
                if (ConfSetFinal("pcap-file.delete-when-done", "true") != 1) {
                    SCLogError("Failed to set pcap-file.delete-when-done");
                    return TM_ECODE_FAILED;
                }
            }
            else if (strcmp((long_opts[option_index]).name, "pcap-file-recursive") == 0) {
                if (ConfSetFinal("pcap-file.recursive", "true") != 1) {
                    SCLogError("failed to set pcap-file.recursive");
                    return TM_ECODE_FAILED;
                }
            }
            else if (strcmp((long_opts[option_index]).name, "data-dir") == 0) {
                if (optarg == NULL) {
                    SCLogError("no option argument (optarg) for -d");
                    return TM_ECODE_FAILED;
                }

                if (ConfigSetDataDirectory(optarg) != TM_ECODE_OK) {
                    SCLogError("Failed to set data directory.");
                    return TM_ECODE_FAILED;
                }
                if (ConfigCheckDataDirectory(optarg) != TM_ECODE_OK) {
                    SCLogError("The data directory \"%s\""
                               " supplied at the command-line (-d %s) doesn't "
                               "exist. Shutting down the engine.",
                            optarg, optarg);
                    return TM_ECODE_FAILED;
                }
                suri->set_datadir = true;
            } else if (strcmp((long_opts[option_index]).name , "strict-rule-keywords") == 0){
                if (optarg == NULL) {
                    suri->strict_rule_parsing_string = SCStrdup("all");
                } else {
                    suri->strict_rule_parsing_string = SCStrdup(optarg);
                }
                if (suri->strict_rule_parsing_string == NULL) {
                    FatalError("failed to duplicate 'strict' string");
                }
            } else if (strcmp((long_opts[option_index]).name, "include") == 0) {
                if (suri->additional_configs == NULL) {
                    suri->additional_configs = SCCalloc(2, sizeof(char *));
                    if (suri->additional_configs == NULL) {
                        FatalError(
                                "Failed to allocate memory for additional configuration files: %s",
                                strerror(errno));
                    }
                    suri->additional_configs[0] = optarg;
                } else {
                    for (int i = 0;; i++) {
                        if (suri->additional_configs[i] == NULL) {
                            const char **additional_configs =
                                    SCRealloc(suri->additional_configs, (i + 2) * sizeof(char *));
                            if (additional_configs == NULL) {
                                FatalError("Failed to allocate memory for additional configuration "
                                           "files: %s",
                                        strerror(errno));
                            } else {
                                suri->additional_configs = additional_configs;
                            }
                            suri->additional_configs[i] = optarg;
                            suri->additional_configs[i + 1] = NULL;
                            break;
                        }
                    }
                }
            } else {
                int r = ExceptionSimulationCommandLineParser(
                        (long_opts[option_index]).name, optarg);
                if (r < 0)
                    return TM_ECODE_FAILED;
            }
            break;
        case 'c':
            suri->conf_filename = optarg;
            break;
        case 'T':
            conf_test = 1;
            if (ConfSetFinal("engine.init-failure-fatal", "1") != 1) {
                SCLogError("failed to set engine init-failure-fatal");
                return TM_ECODE_FAILED;
            }
            break;
#ifndef OS_WIN32
        case 'D':
            suri->daemon = 1;
            break;
#endif /* OS_WIN32 */
        case 'h':
            suri->run_mode = RUNMODE_PRINT_USAGE;
            return TM_ECODE_OK;
        case 'i':
            if (optarg == NULL) {
                SCLogError("no option argument (optarg) for -i");
                return TM_ECODE_FAILED;
            }
#ifdef HAVE_AF_PACKET
            if (ParseCommandLineAfpacket(suri, optarg) != TM_ECODE_OK) {
                return TM_ECODE_FAILED;
            }
#else /* not afpacket */
            /* warn user if netmap or pf-ring are available */
#if defined HAVE_PFRING || HAVE_NETMAP
            int i = 0;
#ifdef HAVE_PFRING
            i++;
#endif
#ifdef HAVE_NETMAP
            i++;
#endif
            SCLogWarning("faster capture "
                         "option%s %s available:"
#ifdef HAVE_PFRING
                         " PF_RING (--pfring-int=%s)"
#endif
#ifdef HAVE_NETMAP
                         " NETMAP (--netmap=%s)"
#endif
                         ". Use --pcap=%s to suppress this warning",
                    i == 1 ? "" : "s", i == 1 ? "is" : "are"
#ifdef HAVE_PFRING
                    ,
                    optarg
#endif
#ifdef HAVE_NETMAP
                    ,
                    optarg
#endif
                    ,
                    optarg);
#endif /* have faster methods */
            if (ParseCommandLinePcapLive(suri, optarg) != TM_ECODE_OK) {
                return TM_ECODE_FAILED;
            }
#endif
            break;
        case 'l':
            if (optarg == NULL) {
                SCLogError("no option argument (optarg) for -l");
                return TM_ECODE_FAILED;
            }

            if (ConfigSetLogDirectory(optarg) != TM_ECODE_OK) {
                SCLogError("Failed to set log directory.");
                return TM_ECODE_FAILED;
            }
            if (ConfigCheckLogDirectoryExists(optarg) != TM_ECODE_OK) {
                SCLogError("The logging directory \"%s\""
                           " supplied at the command-line (-l %s) doesn't "
                           "exist. Shutting down the engine.",
                        optarg, optarg);
                return TM_ECODE_FAILED;
            }
            if (!IsLogDirectoryWritable(optarg)) {
                SCLogError("The logging directory \"%s\""
                           " supplied at the command-line (-l %s) is not "
                           "writable. Shutting down the engine.",
                        optarg, optarg);
                return TM_ECODE_FAILED;
            }
            suri->set_logdir = true;

            break;
        case 'q':
#ifdef NFQ
            if (suri->run_mode == RUNMODE_UNKNOWN) {
                suri->run_mode = RUNMODE_NFQ;
                EngineModeSetIPS();
                if (NFQParseAndRegisterQueues(optarg) == -1)
                    return TM_ECODE_FAILED;
            } else if (suri->run_mode == RUNMODE_NFQ) {
                if (NFQParseAndRegisterQueues(optarg) == -1)
                    return TM_ECODE_FAILED;
            } else {
                SCLogError("more than one run mode "
                           "has been specified");
                PrintUsage(argv[0]);
                return TM_ECODE_FAILED;
            }
#else
            SCLogError("NFQUEUE not enabled. Make sure to pass --enable-nfqueue to configure when "
                       "building.");
            return TM_ECODE_FAILED;
#endif /* NFQ */
            break;
        case 'd':
#ifdef IPFW
            if (suri->run_mode == RUNMODE_UNKNOWN) {
                suri->run_mode = RUNMODE_IPFW;
                EngineModeSetIPS();
                if (IPFWRegisterQueue(optarg) == -1)
                    return TM_ECODE_FAILED;
            } else if (suri->run_mode == RUNMODE_IPFW) {
                if (IPFWRegisterQueue(optarg) == -1)
                    return TM_ECODE_FAILED;
            } else {
                SCLogError("more than one run mode "
                           "has been specified");
                PrintUsage(argv[0]);
                return TM_ECODE_FAILED;
            }
#else
            SCLogError("IPFW not enabled. Make sure to pass --enable-ipfw to configure when "
                       "building.");
            return TM_ECODE_FAILED;
#endif /* IPFW */
            break;
        case 'r':
            BUG_ON(optarg == NULL); /* for static analysis */
            if (suri->run_mode == RUNMODE_UNKNOWN) {
                suri->run_mode = RUNMODE_PCAP_FILE;
            } else {
                SCLogError("more than one run mode "
                           "has been specified");
                PrintUsage(argv[0]);
                return TM_ECODE_FAILED;
            }
            SCStat buf;
            if (SCStatFn(optarg, &buf) != 0) {
                SCLogError("pcap file '%s': %s", optarg, strerror(errno));
                return TM_ECODE_FAILED;
            }
            if (ConfSetFinal("pcap-file.file", optarg) != 1) {
                SCLogError("ERROR: Failed to set pcap-file.file\n");
                return TM_ECODE_FAILED;
            }

            break;
        case 's':
            if (suri->sig_file != NULL) {
                SCLogError("can't have multiple -s options or mix -s and -S.");
                return TM_ECODE_FAILED;
            }
            suri->sig_file = optarg;
            break;
        case 'S':
            if (suri->sig_file != NULL) {
                SCLogError("can't have multiple -S options or mix -s and -S.");
                return TM_ECODE_FAILED;
            }
            suri->sig_file = optarg;
            suri->sig_file_exclusive = TRUE;
            break;
        case 'u':
#ifdef UNITTESTS
            if (suri->run_mode == RUNMODE_UNKNOWN) {
                suri->run_mode = RUNMODE_UNITTEST;
            } else {
                SCLogError("more than one run mode has"
                           " been specified");
                PrintUsage(argv[0]);
                return TM_ECODE_FAILED;
            }
#else
            SCLogError("unit tests not enabled. Make sure to pass --enable-unittests to configure "
                       "when building.");
            return TM_ECODE_FAILED;
#endif /* UNITTESTS */
            break;
        case 'U':
#ifdef UNITTESTS
            suri->regex_arg = optarg;

            if(strlen(suri->regex_arg) == 0)
                suri->regex_arg = NULL;
#endif
            break;
        case 'V':
            suri->run_mode = RUNMODE_PRINT_VERSION;
            return TM_ECODE_OK;
        case 'F':
            if (optarg == NULL) {
                SCLogError("no option argument (optarg) for -F");
                return TM_ECODE_FAILED;
            }

            SetBpfStringFromFile(optarg);
            break;
        case 'v':
            suri->verbose++;
            break;
        case 'k':
            if (optarg == NULL) {
                SCLogError("no option argument (optarg) for -k");
                return TM_ECODE_FAILED;
            }
            if (!strcmp("all", optarg))
                suri->checksum_validation = 1;
            else if (!strcmp("none", optarg))
                suri->checksum_validation = 0;
            else {
                SCLogError("option '%s' invalid for -k", optarg);
                return TM_ECODE_FAILED;
            }
            break;
        default:
            PrintUsage(argv[0]);
            return TM_ECODE_FAILED;
        }
    }

    if (suri->disabled_detect && suri->sig_file != NULL) {
        SCLogError("can't use -s/-S when detection is disabled");
        return TM_ECODE_FAILED;
    }

    /* save the runmode from the command-line (if any) */
    suri->aux_run_mode = suri->run_mode;

    if (list_app_layer_protocols)
        suri->run_mode = RUNMODE_LIST_APP_LAYERS;
    if (list_keywords)
        suri->run_mode = RUNMODE_LIST_KEYWORDS;
    if (list_unittests)
        suri->run_mode = RUNMODE_LIST_UNITTEST;
    if (dump_config)
        suri->run_mode = RUNMODE_DUMP_CONFIG;
    if (dump_features)
        suri->run_mode = RUNMODE_DUMP_FEATURES;
    if (conf_test)
        suri->run_mode = RUNMODE_CONF_TEST;
    if (engine_analysis)
        suri->run_mode = RUNMODE_ENGINE_ANALYSIS;

    suri->offline = IsRunModeOffline(suri->run_mode);
    g_system = suri->system = IsRunModeSystem(suri->run_mode);

    ret = SetBpfString(optind, argv);
    if (ret != TM_ECODE_OK)
        return ret;

    return TM_ECODE_OK;
}

#ifdef OS_WIN32
static int WindowsInitService(int argc, char **argv)
{
    if (SCRunningAsService()) {
        char path[MAX_PATH];
        char *p = NULL;
        strlcpy(path, argv[0], MAX_PATH);
        if ((p = strrchr(path, '\\'))) {
            *p = '\0';
        }
        if (!SetCurrentDirectory(path)) {
            SCLogError("Can't set current directory to: %s", path);
            return -1;
        }
        SCLogInfo("Current directory is set to: %s", path);
        SCServiceInit(argc, argv);
    }

    /* Windows socket subsystem initialization */
    WSADATA wsaData;
    if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)) {
        SCLogError("Can't initialize Windows sockets: %d", WSAGetLastError());
        return -1;
    }

    return 0;
}
#endif /* OS_WIN32 */

static int MayDaemonize(SCInstance *suri)
{
    if (suri->daemon == 1 && suri->pid_filename == NULL) {
        const char *pid_filename;

        if (ConfGet("pid-file", &pid_filename) == 1) {
            SCLogInfo("Use pid file %s from config file.", pid_filename);
        } else {
            pid_filename = DEFAULT_PID_FILENAME;
        }
        /* The pid file name may be in config memory, but is needed later. */
        suri->pid_filename = SCStrdup(pid_filename);
        if (suri->pid_filename == NULL) {
            SCLogError("strdup failed: %s", strerror(errno));
            return TM_ECODE_FAILED;
        }
    }

    if (suri->pid_filename != NULL && SCPidfileTestRunning(suri->pid_filename) != 0) {
        SCFree(suri->pid_filename);
        suri->pid_filename = NULL;
        return TM_ECODE_FAILED;
    }

    if (suri->daemon == 1) {
        Daemonize();
    }

    if (suri->pid_filename != NULL) {
        if (SCPidfileCreate(suri->pid_filename) != 0) {
            SCFree(suri->pid_filename);
            suri->pid_filename = NULL;
            SCLogError("Unable to create PID file, concurrent run of"
                       " Suricata can occur.");
            SCLogError("PID file creation WILL be mandatory for daemon mode"
                       " in future version");
        }
    }

    return TM_ECODE_OK;
}

/* Initialize the user and group Suricata is to run as. */
static int InitRunAs(SCInstance *suri)
{
#ifndef OS_WIN32
    /* Try to get user/group to run suricata as if
       command line as not decide of that */
    if (suri->do_setuid == FALSE && suri->do_setgid == FALSE) {
        const char *id;
        if (ConfGet("run-as.user", &id) == 1) {
            suri->do_setuid = TRUE;
            suri->user_name = id;
        }
        if (ConfGet("run-as.group", &id) == 1) {
            suri->do_setgid = TRUE;
            suri->group_name = id;
        }
    }
    /* Get the suricata user ID to given user ID */
    if (suri->do_setuid == TRUE) {
        SCGetUserID(suri->user_name, suri->group_name, &suri->userid, &suri->groupid);
        sc_set_caps = TRUE;
    /* Get the suricata group ID to given group ID */
    } else if (suri->do_setgid == TRUE) {
        SCGetGroupID(suri->group_name, &suri->groupid);
        sc_set_caps = TRUE;
    }
#endif
    return TM_ECODE_OK;
}

static int InitSignalHandler(SCInstance *suri)
{
    /* registering signals we use */
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    UtilSignalHandlerSetup(SIGINT, SignalHandlerSigint);
    UtilSignalHandlerSetup(SIGTERM, SignalHandlerSigterm);
#if HAVE_LIBUNWIND
    int enabled;
    if (ConfGetBool("logging.stacktrace-on-signal", &enabled) == 0) {
        enabled = 1;
    }

    if (enabled) {
        SCLogInfo("Preparing unexpected signal handling");
        struct sigaction stacktrace_action;
        memset(&stacktrace_action, 0, sizeof(stacktrace_action));
        stacktrace_action.sa_sigaction = SignalHandlerUnexpected;
        stacktrace_action.sa_flags = SA_SIGINFO;
        sigaction(SIGSEGV, &stacktrace_action, NULL);
        sigaction(SIGABRT, &stacktrace_action, NULL);
    }
#endif /* HAVE_LIBUNWIND */
#endif
#ifndef OS_WIN32
    UtilSignalHandlerSetup(SIGHUP, SignalHandlerSigHup);
    UtilSignalHandlerSetup(SIGPIPE, SIG_IGN);
    UtilSignalHandlerSetup(SIGSYS, SIG_IGN);
#endif /* OS_WIN32 */

    return TM_ECODE_OK;
}

/* initialization code for both the main modes and for
 * unix socket mode.
 *
 * Will be run once per pcap in unix-socket mode */
void PreRunInit(const int runmode)
{
    HttpRangeContainersInit();
    if (runmode == RUNMODE_UNIX_SOCKET)
        return;

    StatsInit();
#ifdef PROFILE_RULES
    SCProfilingRulesGlobalInit();
#endif
#ifdef PROFILING
    SCProfilingKeywordsGlobalInit();
    SCProfilingPrefilterGlobalInit();
    SCProfilingSghsGlobalInit();
#endif /* PROFILING */
#ifdef PROFILE_RULES
    SCProfilingInit();
#endif
    DefragInit();
    FlowInitConfig(FLOW_QUIET);
    IPPairInitConfig(FLOW_QUIET);
    StreamTcpInitConfig(STREAM_VERBOSE);
    AppLayerParserPostStreamSetup();
    AppLayerRegisterGlobalCounters();
    OutputFilestoreRegisterGlobalCounters();
}

/* tasks we need to run before packets start flowing,
 * but after we dropped privs */
void PreRunPostPrivsDropInit(const int runmode)
{
    StatsSetupPostConfigPreOutput();
    RunModeInitializeOutputs();
    DatasetsInit();

    if (runmode == RUNMODE_UNIX_SOCKET) {
        /* As the above did some necessary startup initialization, it
         * also setup some outputs where only one is allowed, so
         * deinitialize to the state that unix-mode does after every
         * pcap. */
        PostRunDeinit(RUNMODE_PCAP_FILE, NULL);
        return;
    }

    StatsSetupPostConfigPostOutput();
}

/* clean up / shutdown code for both the main modes and for
 * unix socket mode.
 *
 * Will be run once per pcap in unix-socket mode */
void PostRunDeinit(const int runmode, struct timeval *start_time)
{
    if (runmode == RUNMODE_UNIX_SOCKET)
        return;

    /* needed by FlowForceReassembly */
    PacketPoolInit();

    /* handle graceful shutdown of the flow engine, it's helper
     * threads and the packet threads */
    FlowDisableFlowManagerThread();
    TmThreadDisableReceiveThreads();
    FlowForceReassembly();
    TmThreadDisablePacketThreads();
    SCPrintElapsedTime(start_time);
    FlowDisableFlowRecyclerThread();

    /* kill the stats threads */
    TmThreadKillThreadsFamily(TVT_MGMT);
    TmThreadClearThreadsFamily(TVT_MGMT);

    /* kill packet threads -- already in 'disabled' state */
    TmThreadKillThreadsFamily(TVT_PPT);
    TmThreadClearThreadsFamily(TVT_PPT);

    PacketPoolDestroy();

    /* mgt and ppt threads killed, we can run non thread-safe
     * shutdown functions */
    StatsReleaseResources();
    DecodeUnregisterCounters();
    RunModeShutDown();
    FlowShutdown();
    IPPairShutdown();
    HostCleanup();
    StreamTcpFreeConfig(STREAM_VERBOSE);
    DefragDestroy();
    HttpRangeContainersDestroy();

    TmqResetQueues();
#ifdef PROFILING
    if (profiling_rules_enabled)
        SCProfilingDump();
    SCProfilingDestroy();
#endif
}


static int StartInternalRunMode(SCInstance *suri, int argc, char **argv)
{
    /* Treat internal running mode */
    switch(suri->run_mode) {
        case RUNMODE_LIST_KEYWORDS:
            return ListKeywords(suri->keyword_info);
        case RUNMODE_LIST_APP_LAYERS:
            if (suri->conf_filename != NULL) {
                return ListAppLayerProtocols(suri->conf_filename);
            } else {
                return ListAppLayerProtocols(DEFAULT_CONF_FILE);
            }
        case RUNMODE_PRINT_VERSION:
            PrintVersion();
            return TM_ECODE_DONE;
        case RUNMODE_PRINT_BUILDINFO:
            PrintBuildInfo();
            return TM_ECODE_DONE;
        case RUNMODE_PRINT_USAGE:
            PrintUsage(argv[0]);
            return TM_ECODE_DONE;
        case RUNMODE_LIST_RUNMODES:
            RunModeListRunmodes();
            return TM_ECODE_DONE;
        case RUNMODE_LIST_UNITTEST:
            RunUnittests(1, suri->regex_arg);
        case RUNMODE_UNITTEST:
            RunUnittests(0, suri->regex_arg);
#ifdef OS_WIN32
        case RUNMODE_INSTALL_SERVICE:
            if (SCServiceInstall(argc, argv)) {
                return TM_ECODE_FAILED;
            }
            SCLogInfo("Suricata service has been successfully installed.");
            return TM_ECODE_DONE;
        case RUNMODE_REMOVE_SERVICE:
            if (SCServiceRemove(argc, argv)) {
                return TM_ECODE_FAILED;
            }
            SCLogInfo("Suricata service has been successfully removed.");
            return TM_ECODE_DONE;
        case RUNMODE_CHANGE_SERVICE_PARAMS:
            if (SCServiceChangeParams(argc, argv)) {
                return TM_ECODE_FAILED;
            }
            SCLogInfo("Suricata service startup parameters has been successfully changed.");
            return TM_ECODE_DONE;
#endif /* OS_WIN32 */
        default:
            /* simply continue for other running mode */
            break;
    }
    return TM_ECODE_OK;
}

static int FinalizeRunMode(SCInstance *suri, char **argv)
{
    switch (suri->run_mode) {
        case RUNMODE_UNKNOWN:
            PrintUsage(argv[0]);
            return TM_ECODE_FAILED;
        default:
            break;
    }
    /* Set the global run mode and offline flag. */
    run_mode = suri->run_mode;

    if (!CheckValidDaemonModes(suri->daemon, suri->run_mode)) {
        return TM_ECODE_FAILED;
    }

    return TM_ECODE_OK;
}

static void SetupDelayedDetect(SCInstance *suri)
{
    /* In offline mode delayed init of detect is a bad idea */
    if (suri->offline) {
        suri->delayed_detect = 0;
    } else {
        if (ConfGetBool("detect.delayed-detect", &suri->delayed_detect) != 1) {
            ConfNode *denode = NULL;
            ConfNode *decnf = ConfGetNode("detect-engine");
            if (decnf != NULL) {
                TAILQ_FOREACH(denode, &decnf->head, next) {
                    if (strcmp(denode->val, "delayed-detect") == 0) {
                        (void)ConfGetChildValueBool(denode, "delayed-detect", &suri->delayed_detect);
                    }
                }
            }
        }
    }

    SCLogConfig("Delayed detect %s", suri->delayed_detect ? "enabled" : "disabled");
    if (suri->delayed_detect) {
        SCLogInfo("Packets will start being processed before signatures are active.");
    }

}

static int LoadSignatures(DetectEngineCtx *de_ctx, SCInstance *suri)
{
    if (SigLoadSignatures(de_ctx, suri->sig_file, suri->sig_file_exclusive) < 0) {
        SCLogError("Loading signatures failed.");
        if (de_ctx->failure_fatal)
            return TM_ECODE_FAILED;
    }

    return TM_ECODE_OK;
}

static int ConfigGetCaptureValue(SCInstance *suri)
{
    /* Pull the max pending packets from the config, if not found fall
     * back on a sane default. */
    intmax_t tmp_max_pending_packets;
    if (ConfGetInt("max-pending-packets", &tmp_max_pending_packets) != 1)
        tmp_max_pending_packets = DEFAULT_MAX_PENDING_PACKETS;
    if (tmp_max_pending_packets < 1 || tmp_max_pending_packets >= UINT16_MAX) {
        SCLogError("Maximum max-pending-packets setting is 65534 and must be greater than 0. "
                   "Please check %s for errors",
                suri->conf_filename);
        return TM_ECODE_FAILED;
    } else {
        max_pending_packets = (uint16_t)tmp_max_pending_packets;
    }

    SCLogDebug("Max pending packets set to %" PRIu16, max_pending_packets);

    /* Pull the default packet size from the config, if not found fall
     * back on a sane default. */
    const char *temp_default_packet_size;
    if ((ConfGet("default-packet-size", &temp_default_packet_size)) != 1) {
        int lthread;
        int nlive;
        int strip_trailing_plus = 0;
        switch (suri->run_mode) {
#ifdef WINDIVERT
            case RUNMODE_WINDIVERT: {
                /* by default, WinDivert collects from all devices */
                const int mtu = GetGlobalMTUWin32();

                if (mtu > 0) {
                    /* SLL_HEADER_LEN is the longest header + 8 for VLAN */
                    default_packet_size = mtu + SLL_HEADER_LEN + 8;
                    break;
                }
                default_packet_size = DEFAULT_PACKET_SIZE;
                break;
            }
#endif /* WINDIVERT */
            case RUNMODE_NETMAP:
                /* in netmap igb0+ has a special meaning, however the
                 * interface really is igb0 */
                strip_trailing_plus = 1;
                /* fall through */
            case RUNMODE_PCAP_DEV:
            case RUNMODE_AFP_DEV:
            case RUNMODE_AFXDP_DEV:
            case RUNMODE_PFRING:
                nlive = LiveGetDeviceCount();
                for (lthread = 0; lthread < nlive; lthread++) {
                    const char *live_dev = LiveGetDeviceName(lthread);
                    char dev[128]; /* need to be able to support GUID names on Windows */
                    (void)strlcpy(dev, live_dev, sizeof(dev));

                    if (strip_trailing_plus) {
                        size_t len = strlen(dev);
                        if (len &&
                                (dev[len-1] == '+' ||
                                 dev[len-1] == '^' ||
                                 dev[len-1] == '*'))
                        {
                            dev[len-1] = '\0';
                        }
                    }
                    LiveDevice *ld = LiveGetDevice(dev);
                    unsigned int iface_max_packet_size = GetIfaceMaxPacketSize(ld);
                    if (iface_max_packet_size > default_packet_size)
                        default_packet_size = iface_max_packet_size;
                }
                if (default_packet_size)
                    break;
                /* fall through */
            default:
                default_packet_size = DEFAULT_PACKET_SIZE;
        }
    } else {
        if (ParseSizeStringU32(temp_default_packet_size, &default_packet_size) < 0) {
            SCLogError("Error parsing max-pending-packets "
                       "from conf file - %s.  Killing engine",
                    temp_default_packet_size);
            return TM_ECODE_FAILED;
        }
    }

    SCLogDebug("Default packet size set to %"PRIu32, default_packet_size);

    return TM_ECODE_OK;
}

static void PostRunStartedDetectSetup(const SCInstance *suri)
{
#ifndef OS_WIN32
    /* registering signal handlers we use. We setup usr2 here, so that one
     * can't call it during the first sig load phase or while threads are still
     * starting up. */
    if (DetectEngineEnabled() && suri->delayed_detect == 0) {
        UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2);
        UtilSignalUnblock(SIGUSR2);
    }
#endif
    if (suri->delayed_detect) {
        /* force 'reload', this will load the rules and swap engines */
        DetectEngineReload(suri);
        SCLogNotice("Signature(s) loaded, Detect thread(s) activated.");
#ifndef OS_WIN32
        UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2);
        UtilSignalUnblock(SIGUSR2);
#endif
    }
}

void PostConfLoadedDetectSetup(SCInstance *suri)
{
    DetectEngineCtx *de_ctx = NULL;
    if (!suri->disabled_detect) {
        SetupDelayedDetect(suri);
        int mt_enabled = 0;
        (void)ConfGetBool("multi-detect.enabled", &mt_enabled);
        int default_tenant = 0;
        if (mt_enabled)
            (void)ConfGetBool("multi-detect.default", &default_tenant);
        if (DetectEngineMultiTenantSetup(suri->unix_socket_enabled) == -1) {
            FatalError("initializing multi-detect "
                       "detection engine contexts failed.");
        }
        if (suri->delayed_detect && suri->run_mode != RUNMODE_CONF_TEST) {
            de_ctx = DetectEngineCtxInitStubForDD();
        } else if (mt_enabled && !default_tenant && suri->run_mode != RUNMODE_CONF_TEST) {
            de_ctx = DetectEngineCtxInitStubForMT();
        } else {
            de_ctx = DetectEngineCtxInit();
        }
        if (de_ctx == NULL) {
            FatalError("initializing detection engine failed.");
        }

        if (de_ctx->type == DETECT_ENGINE_TYPE_NORMAL) {
            if (LoadSignatures(de_ctx, suri) != TM_ECODE_OK)
                exit(EXIT_FAILURE);
        }

        gettimeofday(&de_ctx->last_reload, NULL);
        DetectEngineAddToMaster(de_ctx);
        DetectEngineBumpVersion();
    }
}

static void PostConfLoadedSetupHostMode(void)
{
    const char *hostmode = NULL;

    if (ConfGet("host-mode", &hostmode) == 1) {
        if (!strcmp(hostmode, "router")) {
            host_mode = SURI_HOST_IS_ROUTER;
        } else if (!strcmp(hostmode, "sniffer-only")) {
            host_mode = SURI_HOST_IS_SNIFFER_ONLY;
        } else {
            if (strcmp(hostmode, "auto") != 0) {
                WarnInvalidConfEntry("host-mode", "%s", "auto");
            }
            if (EngineModeIsIPS()) {
                host_mode = SURI_HOST_IS_ROUTER;
            } else {
                host_mode = SURI_HOST_IS_SNIFFER_ONLY;
            }
        }
    } else {
        if (EngineModeIsIPS()) {
            host_mode = SURI_HOST_IS_ROUTER;
            SCLogInfo("No 'host-mode': suricata is in IPS mode, using "
                      "default setting 'router'");
        } else {
            host_mode = SURI_HOST_IS_SNIFFER_ONLY;
            SCLogInfo("No 'host-mode': suricata is in IDS mode, using "
                      "default setting 'sniffer-only'");
        }
    }
}

static void SetupUserMode(SCInstance *suri)
{
    /* apply 'user mode' config updates here */
    if (suri->system == false) {
        if (suri->set_logdir == false) {
            /* override log dir to current work dir" */
            if (ConfigSetLogDirectory((char *)".") != TM_ECODE_OK) {
                FatalError("could not set USER mode logdir");
            }
        }
        if (suri->set_datadir == false) {
            /* override data dir to current work dir" */
            if (ConfigSetDataDirectory((char *)".") != TM_ECODE_OK) {
                FatalError("could not set USER mode datadir");
            }
        }
    }
}

/**
 * This function is meant to contain code that needs
 * to be run once the configuration has been loaded.
 */
int PostConfLoadedSetup(SCInstance *suri)
{
    /* do this as early as possible #1577 #1955 */
#ifdef HAVE_LUAJIT
    if (LuajitSetupStatesPool() != 0) {
        SCReturnInt(TM_ECODE_FAILED);
    }
#endif

    /* load the pattern matchers */
    MpmTableSetup();
    SpmTableSetup();

    int disable_offloading;
    if (ConfGetBool("capture.disable-offloading", &disable_offloading) == 0)
        disable_offloading = 1;
    if (disable_offloading) {
        LiveSetOffloadDisable();
    } else {
        LiveSetOffloadWarn();
    }

    if (suri->checksum_validation == -1) {
        const char *cv = NULL;
        if (ConfGet("capture.checksum-validation", &cv) == 1) {
            if (strcmp(cv, "none") == 0) {
                suri->checksum_validation = 0;
            } else if (strcmp(cv, "all") == 0) {
                suri->checksum_validation = 1;
            }
        }
    }
    switch (suri->checksum_validation) {
        case 0:
            ConfSet("stream.checksum-validation", "0");
            break;
        case 1:
            ConfSet("stream.checksum-validation", "1");
            break;
    }

    if (suri->runmode_custom_mode) {
        ConfSet("runmode", suri->runmode_custom_mode);
    }

    StorageInit();
#ifdef HAVE_PACKET_EBPF
    if (suri->run_mode == RUNMODE_AFP_DEV) {
        EBPFRegisterExtension();
        LiveDevRegisterExtension();
    }
#endif
    RegisterFlowBypassInfo();

    MacSetRegisterFlowStorage();

#ifdef HAVE_PLUGINS
    SCPluginsLoad(suri->capture_plugin_name, suri->capture_plugin_args);
#endif

    LiveDeviceFinalize(); // must be after EBPF extension registration

    RunModeEngineIsIPS(
            suricata.run_mode, suricata.runmode_custom_mode, suricata.capture_plugin_name);

    if (EngineModeIsUnknown()) { // if still uninitialized, set the default
        SCLogInfo("Setting engine mode to IDS mode by default");
        EngineModeSetIDS();
    }

    SetMasterExceptionPolicy();

    /* Must occur prior to output mod registration
       and app layer setup. */
    FeatureTrackingRegister();

    AppLayerSetup();

    /* Suricata will use this umask if provided. By default it will use the
       umask passed on from the shell. */
    const char *custom_umask;
    if (ConfGet("umask", &custom_umask) == 1) {
        uint16_t mask;
        if (StringParseUint16(&mask, 8, (uint16_t)strlen(custom_umask), custom_umask) > 0) {
            umask((mode_t)mask);
        }
    }


    if (ConfigGetCaptureValue(suri) != TM_ECODE_OK) {
        SCReturnInt(TM_ECODE_FAILED);
    }

#ifdef NFQ
    if (suri->run_mode == RUNMODE_NFQ)
        NFQInitConfig(false);
#endif

    /* Load the Host-OS lookup. */
    SCHInfoLoadFromConfig();

    if (suri->run_mode == RUNMODE_ENGINE_ANALYSIS) {
        SCLogInfo("== Carrying out Engine Analysis ==");
        const char *temp = NULL;
        if (ConfGet("engine-analysis", &temp) == 0) {
            SCLogInfo("no engine-analysis parameter(s) defined in conf file.  "
                      "Please define/enable them in the conf to use this "
                      "feature.");
            SCReturnInt(TM_ECODE_FAILED);
        }
    }

    /* hardcoded initialization code */
    SigTableSetup(); /* load the rule keywords */
    SigTableApplyStrictCommandLineOption(suri->strict_rule_parsing_string);
    TmqhSetup();

    TagInitCtx();
    PacketAlertTagInit();
    ThresholdInit();
    HostBitInitCtx();
    IPPairBitInitCtx();

    if (DetectAddressTestConfVars() < 0) {
        SCLogError(
                "basic address vars test failed. Please check %s for errors", suri->conf_filename);
        SCReturnInt(TM_ECODE_FAILED);
    }
    if (DetectPortTestConfVars() < 0) {
        SCLogError("basic port vars test failed. Please check %s for errors", suri->conf_filename);
        SCReturnInt(TM_ECODE_FAILED);
    }

    RegisterAllModules();
    AppLayerHtpNeedFileInspection();

    StorageFinalize();

    TmModuleRunInit();

    if (MayDaemonize(suri) != TM_ECODE_OK)
        SCReturnInt(TM_ECODE_FAILED);

    if (InitSignalHandler(suri) != TM_ECODE_OK)
        SCReturnInt(TM_ECODE_FAILED);

    /* Check for the existence of the default logging directory which we pick
     * from suricata.yaml.  If not found, shut the engine down */
    suri->log_dir = ConfigGetLogDirectory();

    if (ConfigCheckLogDirectoryExists(suri->log_dir) != TM_ECODE_OK) {
        SCLogError("The logging directory \"%s\" "
                   "supplied by %s (default-log-dir) doesn't exist. "
                   "Shutting down the engine",
                suri->log_dir, suri->conf_filename);
        SCReturnInt(TM_ECODE_FAILED);
    }
    if (!IsLogDirectoryWritable(suri->log_dir)) {
        SCLogError("The logging directory \"%s\" "
                   "supplied by %s (default-log-dir) is not writable. "
                   "Shutting down the engine",
                suri->log_dir, suri->conf_filename);
        SCReturnInt(TM_ECODE_FAILED);
    }

    if (suri->disabled_detect) {
        SCLogConfig("detection engine disabled");
        /* disable raw reassembly */
        (void)ConfSetFinal("stream.reassembly.raw", "false");
    }

    HostInitConfig(HOST_VERBOSE);

    CoredumpLoadConfig();

    DecodeGlobalConfig();

    /* hostmode depends on engine mode being set */
    PostConfLoadedSetupHostMode();

    PreRunInit(suri->run_mode);

    SCReturnInt(TM_ECODE_OK);
}

static void SuricataMainLoop(SCInstance *suri)
{
    while(1) {
        if (sigterm_count || sigint_count) {
            suricata_ctl_flags |= SURICATA_STOP;
        }

        if (suricata_ctl_flags & SURICATA_STOP) {
            SCLogNotice("Signal Received.  Stopping engine.");
            break;
        }

        TmThreadCheckThreadState();

        if (sighup_count > 0) {
            OutputNotifyFileRotation();
            sighup_count--;
        }

        if (sigusr2_count > 0) {
            if (!(DetectEngineReloadIsStart())) {
                DetectEngineReloadStart();
                DetectEngineReload(suri);
                DetectEngineReloadSetIdle();
                sigusr2_count--;
            }

        } else if (DetectEngineReloadIsStart()) {
            DetectEngineReload(suri);
            DetectEngineReloadSetIdle();
        }

        usleep(10* 1000);
    }
}

/**
 * \brief Global initialization common to all runmodes.
 *
 * This can be used by fuzz targets.
 */

int InitGlobal(void)
{
    rs_init(&suricata_context);

    SC_ATOMIC_INIT(engine_stage);

    /* initialize the logging subsys */
    SCLogInitLogModule(NULL);

    SCSetThreadName("Suricata-Main");

    /* Ignore SIGUSR2 as early as possible. We redeclare interest
     * once we're done launching threads. The goal is to either die
     * completely or handle any and all SIGUSR2s correctly.
     */
#ifndef OS_WIN32
    UtilSignalHandlerSetup(SIGUSR2, SIG_IGN);
    if (UtilSignalBlock(SIGUSR2)) {
        SCLogError("SIGUSR2 initialization error");
        return EXIT_FAILURE;
    }
#endif

    ParseSizeInit();
    RunModeRegisterRunModes();

    /* Initialize the configuration module. */
    ConfInit();

    VarNameStoreInit();

    // zero all module storage
    memset(tmm_modules, 0, TMM_SIZE * sizeof(TmModule));

    return 0;
}

int SuricataMain(int argc, char **argv)
{
    SCInstanceInit(&suricata, argv[0]);

    if (InitGlobal() != 0) {
        exit(EXIT_FAILURE);
    }

#ifdef OS_WIN32
    /* service initialization */
    if (WindowsInitService(argc, argv) != 0) {
        exit(EXIT_FAILURE);
    }
#endif /* OS_WIN32 */

    if (ParseCommandLine(argc, argv, &suricata) != TM_ECODE_OK) {
        exit(EXIT_FAILURE);
    }

    if (FinalizeRunMode(&suricata, argv) != TM_ECODE_OK) {
        exit(EXIT_FAILURE);
    }

    switch (StartInternalRunMode(&suricata, argc, argv)) {
        case TM_ECODE_DONE:
            exit(EXIT_SUCCESS);
        case TM_ECODE_FAILED:
            exit(EXIT_FAILURE);
    }

    /* Initializations for global vars, queues, etc (memsets, mutex init..) */
    GlobalsInitPreConfig();

    /* Load yaml configuration file if provided. */
    if (LoadYamlConfig(&suricata) != TM_ECODE_OK) {
        exit(EXIT_FAILURE);
    }

    if (suricata.run_mode == RUNMODE_DUMP_CONFIG) {
        ConfDump();
        exit(EXIT_SUCCESS);
    }

    int tracking = 1;
    if (ConfGetBool("vlan.use-for-tracking", &tracking) == 1 && !tracking) {
        /* Ignore vlan_ids when comparing flows. */
        g_vlan_mask = 0x0000;
    }
    SCLogDebug("vlan tracking is %s", tracking == 1 ? "enabled" : "disabled");
    if (ConfGetBool("livedev.use-for-tracking", &tracking) == 1 && !tracking) {
        /* Ignore livedev id when comparing flows. */
        g_livedev_mask = 0x0000;
    }
    SetupUserMode(&suricata);
    InitRunAs(&suricata);

    /* Since our config is now loaded we can finish configurating the
     * logging module. */
    SCLogLoadConfig(suricata.daemon, suricata.verbose, suricata.userid, suricata.groupid);

    LogVersion(&suricata);
    UtilCpuPrintSummary();
    RunModeInitializeThreadSettings();

    if (suricata.run_mode == RUNMODE_CONF_TEST)
        SCLogInfo("Running suricata under test mode");

    if (ParseInterfacesList(suricata.aux_run_mode, suricata.pcap_dev) != TM_ECODE_OK) {
        exit(EXIT_FAILURE);
    }

    if (PostConfLoadedSetup(&suricata) != TM_ECODE_OK) {
        exit(EXIT_FAILURE);
    }

    SCDropMainThreadCaps(suricata.userid, suricata.groupid);

    /* Re-enable coredumps after privileges are dropped. */
    CoredumpEnable();

    if (suricata.run_mode != RUNMODE_UNIX_SOCKET && !suricata.disabled_detect) {
        suricata.unix_socket_enabled = ConfUnixSocketIsEnable();
    }

    PreRunPostPrivsDropInit(suricata.run_mode);

    LandlockSandboxing(&suricata);

    PostConfLoadedDetectSetup(&suricata);
    if (suricata.run_mode == RUNMODE_ENGINE_ANALYSIS) {
        goto out;
    } else if (suricata.run_mode == RUNMODE_CONF_TEST){
        SCLogNotice("Configuration provided was successfully loaded. Exiting.");
        goto out;
    } else if (suricata.run_mode == RUNMODE_DUMP_FEATURES) {
        FeatureDump();
        goto out;
    }

    SystemHugepageSnapshot *prerun_snap = NULL;
    if (run_mode == RUNMODE_DPDK)
        prerun_snap = SystemHugepageSnapshotCreate();

    SCSetStartTime(&suricata);
    RunModeDispatch(suricata.run_mode, suricata.runmode_custom_mode,
            suricata.capture_plugin_name, suricata.capture_plugin_args);
    if (suricata.run_mode != RUNMODE_UNIX_SOCKET) {
        UnixManagerThreadSpawnNonRunmode(suricata.unix_socket_enabled);
    }

    /* Wait till all the threads have been initialized */
    if (TmThreadWaitOnThreadInit() == TM_ECODE_FAILED) {
        FatalError("Engine initialization failed, "
                   "aborting...");
    }

    int limit_nproc = 0;
    if (ConfGetBool("security.limit-noproc", &limit_nproc) == 0) {
        limit_nproc = 0;
    }

#if defined(SC_ADDRESS_SANITIZER)
    if (limit_nproc) {
        SCLogWarning(
                "\"security.limit-noproc\" (setrlimit()) not set when using address sanitizer");
        limit_nproc = 0;
    }
#endif

    if (limit_nproc) {
#if defined(HAVE_SYS_RESOURCE_H)
#ifdef linux
        if (geteuid() == 0) {
            SCLogWarning("setrlimit has no effet when running as root.");
        }
#endif
        struct rlimit r = { 0, 0 };
        if (setrlimit(RLIMIT_NPROC, &r) != 0) {
            SCLogWarning("setrlimit failed to prevent process creation.");
        }
#else
        SCLogWarning("setrlimit unavailable.");
#endif
    }

    SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);
    PacketPoolPostRunmodes();

    /* Un-pause all the paused threads */
    TmThreadContinueThreads();

    /* Must ensure all threads are fully operational before continuing with init process */
    if (TmThreadWaitOnThreadRunning() != TM_ECODE_OK) {
        exit(EXIT_FAILURE);
    }

    /* Print notice and send OS specific notification of threads in running state */
    OnNotifyRunning();

    PostRunStartedDetectSetup(&suricata);
    if (run_mode == RUNMODE_DPDK) { // only DPDK uses hpages at the moment
        SystemHugepageSnapshot *postrun_snap = SystemHugepageSnapshotCreate();
        SystemHugepageEvaluateHugepages(prerun_snap, postrun_snap);
        SystemHugepageSnapshotDestroy(prerun_snap);
        SystemHugepageSnapshotDestroy(postrun_snap);
    }
    SCPledge();
    SuricataMainLoop(&suricata);

    /* Update the engine stage/status flag */
    SC_ATOMIC_SET(engine_stage, SURICATA_DEINIT);

    UnixSocketKillSocketThread();
    PostRunDeinit(suricata.run_mode, &suricata.start_time);
    /* kill remaining threads */
    TmThreadKillThreads();

out:
    GlobalsDestroy(&suricata);

    exit(EXIT_SUCCESS);
}
