/* glbl.c - this module holds global defintions and data items.
 * These are shared among the runtime library. Their use should be
 * limited to cases where it is actually needed. The main intension for
 * implementing them was support for the transistion from v2 to v4
 * (with fully modular design), but it turned out that there may also
 * be some other good use cases besides backwards-compatibility.
 *
 * Module begun 2008-04-16 by Rainer Gerhards
 *
 * Copyright 2008-2024 Rainer Gerhards and Adiscon GmbH.
 *
 * This file is part of the rsyslog runtime library.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *       -or-
 *       see COPYING.ASL20 in the source distribution
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "config.h"
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <ctype.h>
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <json.h>

#include "rsyslog.h"
#include "obj.h"
#include "unicode-helper.h"
#include "cfsysline.h"
#include "glbl.h"
#include "prop.h"
#include "atomic.h"
#include "errmsg.h"
#include "action.h"
#include "parserif.h"
#include "rainerscript.h"
#include "srUtils.h"
#include "operatingstate.h"
#include "net.h"
#include "rsconf.h"
#include "queue.h"
#include "dnscache.h"
#include "parser.h"
#include "timezones.h"

/* some defaults */
#ifndef DFLT_NETSTRM_DRVR
    #define DFLT_NETSTRM_DRVR ((uchar *)"ptcp")
#endif

/* static data */
DEFobjStaticHelpers;
DEFobjCurrIf(prop) DEFobjCurrIf(net)

    /* static data
     * For this object, these variables are obviously what makes the "meat" of the
     * class...
     */

    static struct cnfobj *mainqCnfObj = NULL; /* main queue object, to be used later in startup sequence */
static int bPreserveFQDN = 0; /* should FQDNs always be preserved? */
static prop_t *propLocalIPIF = NULL; /* IP address to report for the local host (default is 127.0.0.1) */
static int propLocalIPIF_set = 0; /* is propLocalIPIF already set? */
static prop_t *propLocalHostName = NULL; /* our hostname as FQDN - read-only after startup */
static prop_t *propLocalHostNameToDelete = NULL; /* see GenerateLocalHostName function hdr comment! */
static uchar *LocalHostName = NULL; /* our hostname  - read-only after startup, except HUP */
static uchar *LocalHostNameOverride = NULL; /* user-overridden hostname - read-only after startup */
static uchar *LocalFQDNName = NULL; /* our hostname as FQDN - read-only after startup, except HUP */
static uchar *LocalDomain = NULL; /* our local domain name  - read-only after startup, except HUP */
static int iMaxLine = 8096;
int bTerminateInputs = 0; /* global switch that inputs shall terminate ASAP (1=> terminate) */
volatile sig_atomic_t bTerminateInputsSigSafe = 0;
int glblUnloadModules = 1;
int glblAbortOnProgramError = 0;
char **glblDbgFiles = NULL;
size_t glblDbgFilesNum = 0;
int glblDbgWhitelist = 1;
int glblPermitCtlC = 0;
/* For global option CompactJsonString:
 *   Compact the JSON variable string, without extra space.
 *   Considering compatibility issues, the default options(CompactJsonString = "off")
 *   keep the same as before.
 */
int glblJsonFormatOpt = JSON_C_TO_STRING_SPACED;


pid_t glbl_ourpid;
#ifndef HAVE_ATOMIC_BUILTINS
DEF_ATOMIC_HELPER_MUT(mutTerminateInputs);
#endif
#ifdef USE_UNLIMITED_SELECT
static int iFdSetSize = howmany(FD_SETSIZE, __NFDBITS) * sizeof(fd_mask); /* size of select() bitmask in bytes */
#endif
static uchar *SourceIPofLocalClient = NULL; /* [ar] Source IP for local client to be used on multihomed host */
static uchar *GetDfltNetstrmDrvr(rsconf_t *cnf);

/* tables for interfacing with the v6 config system */
static struct cnfparamdescr cnfparamdescr[] = {
    {"workdirectory", eCmdHdlrString, 0},
    {"operatingstatefile", eCmdHdlrString, 0},
    {"dropmsgswithmaliciousdnsptrrecords", eCmdHdlrBinary, 0},
    {"localhostname", eCmdHdlrGetWord, 0},
    {"compactjsonstring", eCmdHdlrBinary, 0},
    {"preservefqdn", eCmdHdlrBinary, 0},
    {"debug.onshutdown", eCmdHdlrBinary, 0},
    {"debug.logfile", eCmdHdlrString, 0},
    {"debug.gnutls", eCmdHdlrNonNegInt, 0},
    {"debug.abortonprogramerror", eCmdHdlrBinary, 0},
    {"debug.unloadmodules", eCmdHdlrBinary, 0},
    {"defaultnetstreamdrivercafile", eCmdHdlrString, 0},
    {"defaultnetstreamdrivercrlfile", eCmdHdlrString, 0},
    {"defaultnetstreamdriverkeyfile", eCmdHdlrString, 0},
    {"defaultnetstreamdrivercertfile", eCmdHdlrString, 0},
    {"defaultnetstreamdriver", eCmdHdlrString, 0},
    {"defaultopensslengine", eCmdHdlrString, 0},
    {"netstreamdrivercaextrafiles", eCmdHdlrString, 0},
    {"maxmessagesize", eCmdHdlrSize, 0},
    {"oversizemsg.errorfile", eCmdHdlrGetWord, 0},
    {"oversizemsg.report", eCmdHdlrBinary, 0},
    {"oversizemsg.input.mode", eCmdHdlrGetWord, 0},
    {"reportchildprocessexits", eCmdHdlrGetWord, 0},
    {"action.reportsuspension", eCmdHdlrBinary, 0},
    {"action.reportsuspensioncontinuation", eCmdHdlrBinary, 0},
    {"parser.controlcharacterescapeprefix", eCmdHdlrGetChar, 0},
    {"parser.droptrailinglfonreception", eCmdHdlrBinary, 0},
    {"parser.droptrailingcronreception", eCmdHdlrBinary, 0},
    {"parser.escapecontrolcharactersonreceive", eCmdHdlrBinary, 0},
    {"parser.spacelfonreceive", eCmdHdlrBinary, 0},
    {"parser.escape8bitcharactersonreceive", eCmdHdlrBinary, 0},
    {"parser.escapecontrolcharactertab", eCmdHdlrBinary, 0},
    {"parser.escapecontrolcharacterscstyle", eCmdHdlrBinary, 0},
    {"parser.parsehostnameandtag", eCmdHdlrBinary, 0},
    {"parser.permitslashinprogramname", eCmdHdlrBinary, 0},
    {"stdlog.channelspec", eCmdHdlrString, 0},
    {"janitor.interval", eCmdHdlrPositiveInt, 0},
    {"senders.reportnew", eCmdHdlrBinary, 0},
    {"senders.reportgoneaway", eCmdHdlrBinary, 0},
    {"senders.timeoutafter", eCmdHdlrPositiveInt, 0},
    {"senders.keeptrack", eCmdHdlrBinary, 0},
    {"inputs.timeout.shutdown", eCmdHdlrPositiveInt, 0},
    {"privdrop.group.keepsupplemental", eCmdHdlrBinary, 0},
    {"privdrop.group.id", eCmdHdlrPositiveInt, 0},
    {"privdrop.group.name", eCmdHdlrGID, 0},
    {"privdrop.user.id", eCmdHdlrPositiveInt, 0},
    {"privdrop.user.name", eCmdHdlrUID, 0},
    {"net.ipprotocol", eCmdHdlrGetWord, 0},
    {"net.acladdhostnameonfail", eCmdHdlrBinary, 0},
    {"net.aclresolvehostname", eCmdHdlrBinary, 0},
    {"net.enabledns", eCmdHdlrBinary, 0},
    {"net.permitACLwarning", eCmdHdlrBinary, 0},
    {"maxopenfiles", eCmdHdlrPositiveInt, 0},
    {"compatibility.configformat.legacy", eCmdHdlrGetWord, 0},
    {"compatibility.configformat.syslogd", eCmdHdlrGetWord, 0},
    {"compatibility.configformat.property", eCmdHdlrGetWord, 0},
    {"compatibility.defaults.secure", eCmdHdlrGetWord, 0},
    {"systemd.notifyreadydelay", eCmdHdlrBinary, 0},
    {"abortonuncleanconfig", eCmdHdlrBinary, 0},
    {"abortonfailedqueuestartup", eCmdHdlrBinary, 0},
    {"variables.casesensitive", eCmdHdlrBinary, 0},
    {"environment", eCmdHdlrArray, 0},
    {"processinternalmessages", eCmdHdlrBinary, 0},
    {"umask", eCmdHdlrFileCreateMode, 0},
    {"security.abortonidresolutionfail", eCmdHdlrBinary, 0},
    {"internal.developeronly.options", eCmdHdlrInt, 0},
    {"internalmsg.ratelimit.interval", eCmdHdlrPositiveInt, 0},
    {"internalmsg.ratelimit.burst", eCmdHdlrPositiveInt, 0},
    {"internalmsg.severity", eCmdHdlrSeverity, 0},
    {"allmessagestostderr", eCmdHdlrBinary, 0},
    {"errormessagestostderr.maxnumber", eCmdHdlrPositiveInt, 0},
    {"shutdown.enable.ctlc", eCmdHdlrBinary, 0},
    {"default.action.queue.timeoutshutdown", eCmdHdlrInt, 0},
    {"default.action.queue.timeoutactioncompletion", eCmdHdlrInt, 0},
    {"default.action.queue.timeoutenqueue", eCmdHdlrInt, 0},
    {"default.action.queue.timeoutworkerthreadshutdown", eCmdHdlrInt, 0},
    {"default.ruleset.queue.timeoutshutdown", eCmdHdlrInt, 0},
    {"default.ruleset.queue.timeoutactioncompletion", eCmdHdlrInt, 0},
    {"default.ruleset.queue.timeoutenqueue", eCmdHdlrInt, 0},
    {"default.ruleset.queue.timeoutworkerthreadshutdown", eCmdHdlrInt, 0},
    {"reverselookup.cache.ttl.default", eCmdHdlrNonNegInt, 0},
    {"reverselookup.cache.ttl.enable", eCmdHdlrBinary, 0},
    {"parser.supportcompressionextension", eCmdHdlrBinary, 0},
    {"shutdown.queue.doublesize", eCmdHdlrBinary, 0},
    {"debug.files", eCmdHdlrArray, 0},
    {"debug.whitelist", eCmdHdlrBinary, 0},
    {"libcapng.default", eCmdHdlrBinary, 0},
    {"libcapng.enable", eCmdHdlrBinary, 0},
};
static struct cnfparamblk paramblk = {CNFPARAMBLK_VERSION, sizeof(cnfparamdescr) / sizeof(struct cnfparamdescr),
                                      cnfparamdescr};

static struct cnfparamvals *cnfparamvals = NULL;
/* we need to support multiple calls into our param block, so we need
 * to persist the current settings. Note that this must be re-set
 * each time a new config load begins. This is a hint if we will
 * ever implement multi-config support, which is just an idea right now.
 */

int glblGetMaxLine(rsconf_t *cnf) {
    /* glblGetMaxLine might be invoked before our configuration exists */
    return ((cnf != NULL) ? cnf->globals.iMaxLine : iMaxLine);
}


int GetGnuTLSLoglevel(rsconf_t *cnf) {
    /*
     * Loadable netstream drivers can be initialized while a configuration is
     * still being parsed, before runConf points at an active configuration.
     */
    if (cnf == NULL) {
        cnf = loadConf;
    }
    return ((cnf != NULL) ? cnf->globals.iGnuTLSLoglevel : 0);
}

/* define a macro for the simple properties' set and get functions
 * (which are always the same). This is only suitable for pretty
 * simple cases which require neither checks nor memory allocation.
 */
#define SIMP_PROP(nameFunc, nameVar, dataType) \
    SIMP_PROP_GET(nameFunc, nameVar, dataType) \
    SIMP_PROP_SET(nameFunc, nameVar, dataType)
#define SIMP_PROP_SET(nameFunc, nameVar, dataType)   \
    static rsRetVal Set##nameFunc(dataType newVal) { \
        nameVar = newVal;                            \
        return RS_RET_OK;                            \
    }
#define SIMP_PROP_GET(nameFunc, nameVar, dataType) \
    static dataType Get##nameFunc(void) {          \
        return (nameVar);                          \
    }

SIMP_PROP(PreserveFQDN, bPreserveFQDN, int)
SIMP_PROP(mainqCnfObj, mainqCnfObj, struct cnfobj *)
#ifdef USE_UNLIMITED_SELECT
SIMP_PROP(FdSetSize, iFdSetSize, int)
#endif

#undef SIMP_PROP
#undef SIMP_PROP_SET
#undef SIMP_PROP_GET

/* This is based on the previous SIMP_PROP but as a getter it uses
 * additional parameter specifying the configuration it belongs to.
 * The setter uses loadConf
 */
#define SIMP_PROP(nameFunc, nameVar, dataType) \
    SIMP_PROP_GET(nameFunc, nameVar, dataType) \
    SIMP_PROP_SET(nameFunc, nameVar, dataType)
#define SIMP_PROP_SET(nameFunc, nameVar, dataType)   \
    static rsRetVal Set##nameFunc(dataType newVal) { \
        loadConf->globals.nameVar = newVal;          \
        return RS_RET_OK;                            \
    }
#define SIMP_PROP_GET(nameFunc, nameVar, dataType) \
    static dataType Get##nameFunc(rsconf_t *cnf) { \
        return (cnf->globals.nameVar);             \
    }

SIMP_PROP(DropMalPTRMsgs, bDropMalPTRMsgs, int)
SIMP_PROP(DisableDNS, bDisableDNS, int)
SIMP_PROP(ParserEscapeControlCharactersCStyle, parser.bParserEscapeCCCStyle, int)
SIMP_PROP(ParseHOSTNAMEandTAG, parser.bParseHOSTNAMEandTAG, int)
SIMP_PROP(OptionDisallowWarning, optionDisallowWarning, int)
/* We omit setter on purpose, because we want to customize it */
SIMP_PROP_GET(DfltNetstrmDrvrCAF, pszDfltNetstrmDrvrCAF, uchar *)
SIMP_PROP_GET(DfltNetstrmDrvrCRLF, pszDfltNetstrmDrvrCRLF, uchar *)
SIMP_PROP_GET(DfltNetstrmDrvrCertFile, pszDfltNetstrmDrvrCertFile, uchar *)
SIMP_PROP_GET(DfltNetstrmDrvrKeyFile, pszDfltNetstrmDrvrKeyFile, uchar *)
SIMP_PROP_GET(NetstrmDrvrCAExtraFiles, pszNetstrmDrvrCAExtraFiles, uchar *)
SIMP_PROP_GET(ParserControlCharacterEscapePrefix, parser.cCCEscapeChar, uchar)
SIMP_PROP_GET(ParserDropTrailingLFOnReception, parser.bDropTrailingLF, int)
SIMP_PROP_GET(ParserDropTrailingCROnReception, parser.bDropTrailingCR, int)
SIMP_PROP_GET(ParserEscapeControlCharactersOnReceive, parser.bEscapeCCOnRcv, int)
SIMP_PROP_GET(ParserSpaceLFOnReceive, parser.bSpaceLFOnRcv, int)
SIMP_PROP_GET(ParserEscape8BitCharactersOnReceive, parser.bEscape8BitChars, int)
SIMP_PROP_GET(ParserEscapeControlCharacterTab, parser.bEscapeTab, int)

#undef SIMP_PROP
#undef SIMP_PROP_SET
#undef SIMP_PROP_GET

/* return global input termination status
 * rgerhards, 2009-07-20
 */
static int GetGlobalInputTermState(void) {
    return ATOMIC_LOAD_32BIT(&bTerminateInputs, &mutTerminateInputs);
}


/* set global termination state to "terminate". Note that this is a
 * "once in a lifetime" action which can not be undone. -- gerhards, 2009-07-20
 */
static void SetGlobalInputTermination(void) {
    /* Signal handlers must not take mutTerminateInputs in no-atomic builds.
     * Publish this one-way mirror first so a shutdown signal delivered during
     * termination setup may already break input-side blocking calls.
     */
    PREFER_STORE_INT(&bTerminateInputsSigSafe, 1);
    ATOMIC_STORE_32BIT(&bTerminateInputs, &mutTerminateInputs, 1);
}


/* set the local host IP address to a specific string. Helper to
 * small set of functions. No checks done, caller must ensure it is
 * ok to call. Most importantly, the IP address must not already have
 * been set. -- rgerhards, 2012-03-21
 */
static rsRetVal storeLocalHostIPIF(uchar *myIP) {
    DEFiRet;
    if (propLocalIPIF != NULL) {
        CHKiRet(prop.Destruct(&propLocalIPIF));
    }
    CHKiRet(prop.Construct(&propLocalIPIF));
    CHKiRet(prop.SetString(propLocalIPIF, myIP, ustrlen(myIP)));
    CHKiRet(prop.ConstructFinalize(propLocalIPIF));
    DBGPRINTF("rsyslog/glbl: using '%s' as localhost IP\n", myIP);
finalize_it:
    RETiRet;
}


/* This function is used to set the IP address that is to be
 * reported for the local host. Note that in order to ease things
 * for the v6 config interface, we do not allow this to be set more
 * than once.
 * rgerhards, 2012-03-21
 */
static rsRetVal setLocalHostIPIF(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    uchar myIP[128];
    rsRetVal localRet;
    DEFiRet;

    CHKiRet(objUse(net, CORE_COMPONENT));

    if (propLocalIPIF_set) {
        LogError(0, RS_RET_ERR,
                 "$LocalHostIPIF is already set "
                 "and cannot be reset; place it at TOP OF rsyslog.conf!");
        ABORT_FINALIZE(RS_RET_ERR);
    }

    localRet = net.GetIFIPAddr(pNewVal, AF_UNSPEC, myIP, (int)sizeof(myIP));
    if (localRet != RS_RET_OK) {
        LogError(0, RS_RET_ERR,
                 "$LocalHostIPIF: IP address for interface "
                 "'%s' cannnot be obtained - ignoring directive",
                 pNewVal);
    } else {
        storeLocalHostIPIF(myIP);
    }


finalize_it:
    free(pNewVal); /* no longer needed -> is in prop! */
    RETiRet;
}


/* This function is used to set the global work directory name.
 * It verifies that the provided directory actually exists and
 * emits an error message if not.
 * rgerhards, 2011-02-16
 */
static rsRetVal setWorkDir(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    size_t lenDir;
    int i;
    struct stat sb;
    DEFiRet;

    /* remove trailing slashes */
    lenDir = ustrlen(pNewVal);
    i = lenDir - 1;
    while (i > 0 && pNewVal[i] == '/') {
        --i;
    }

    if (i < 0) {
        LogError(0, RS_RET_ERR_WRKDIR,
                 "$WorkDirectory: empty value "
                 "- directive ignored");
        ABORT_FINALIZE(RS_RET_ERR_WRKDIR);
    }

    if (i != (int)lenDir - 1) {
        pNewVal[i + 1] = '\0';
        LogError(0, RS_RET_WRN_WRKDIR,
                 "$WorkDirectory: trailing slashes "
                 "removed, new value is '%s'",
                 pNewVal);
    }

    if (stat((char *)pNewVal, &sb) != 0) {
        LogError(0, RS_RET_ERR_WRKDIR,
                 "$WorkDirectory: %s can not be "
                 "accessed, probably does not exist - directive ignored",
                 pNewVal);
        ABORT_FINALIZE(RS_RET_ERR_WRKDIR);
    }

    if (!S_ISDIR(sb.st_mode)) {
        LogError(0, RS_RET_ERR_WRKDIR, "$WorkDirectory: %s not a directory - directive ignored", pNewVal);
        ABORT_FINALIZE(RS_RET_ERR_WRKDIR);
    }

    free(loadConf->globals.pszWorkDir);
    loadConf->globals.pszWorkDir = pNewVal;

finalize_it:
    RETiRet;
}


static rsRetVal setDfltNetstrmDrvrCAF(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    DEFiRet;
    FILE *fp;
    free(loadConf->globals.pszDfltNetstrmDrvrCAF);
    loadConf->globals.pszDfltNetstrmDrvrCAF = pNewVal;
    fp = fopen((const char *)pNewVal, "r");
    if (fp == NULL) {
        LogError(errno, RS_RET_NO_FILE_ACCESS,
                 "error: defaultnetstreamdrivercafile file '%s' "
                 "could not be accessed",
                 pNewVal);
    } else {
        fclose(fp);
    }

    RETiRet;
}

static rsRetVal setNetstrmDrvrCAExtraFiles(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    DEFiRet;
    FILE *fp;
    char *token;
    char *saveptr;
    int error = 0;
    char *valCopy = NULL;

    free(loadConf->globals.pszNetstrmDrvrCAExtraFiles);

    /* Validate files without modifying the original comma-separated string */
    if (pNewVal != NULL) {
        valCopy = strdup((const char *)pNewVal);
        if (valCopy == NULL) {
            LogError(errno, RS_RET_OUT_OF_MEMORY, "error: strdup failed in setNetstrmDrvrCAExtraFiles");
            free(pNewVal);
            ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
        }
        for (token = strtok_r(valCopy, ",", &saveptr); token != NULL; token = strtok_r(NULL, ",", &saveptr)) {
            while (isspace((unsigned char)*token)) {
                token++;
            }
            char *end = token + strlen(token) - 1;
            while (end > token && isspace((unsigned char)*end)) {
                end--;
            }
            *(end + 1) = '\0';

            if (*token == '\0') {
                continue;
            }

            fp = fopen((const char *)token, "r");
            if (fp == NULL) {
                LogError(errno, RS_RET_NO_FILE_ACCESS,
                         "error: netstreamdrivercaextrafiles file '%s' "
                         "could not be accessed",
                         token);
                error = 1;
            } else {
                fclose(fp);
            }
        }
    }

    if (!error) {
        loadConf->globals.pszNetstrmDrvrCAExtraFiles = pNewVal;
    } else {
        /* Validation failed: prevent leak of newly allocated pNewVal */
        free(pNewVal);
        loadConf->globals.pszNetstrmDrvrCAExtraFiles = NULL;
    }

finalize_it:
    if (valCopy != NULL) {
        free(valCopy);
    }
    RETiRet;
}

static rsRetVal setDfltNetstrmDrvrCRLF(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    DEFiRet;
    FILE *fp;
    free(loadConf->globals.pszDfltNetstrmDrvrCRLF);
    loadConf->globals.pszDfltNetstrmDrvrCRLF = pNewVal;
    fp = fopen((const char *)pNewVal, "r");
    if (fp == NULL) {
        LogError(errno, RS_RET_NO_FILE_ACCESS,
                 "error: defaultnetstreamdrivercrlfile file '%s' "
                 "could not be accessed",
                 pNewVal);
    } else {
        fclose(fp);
    }

    RETiRet;
}


static rsRetVal setDfltNetstrmDrvrCertFile(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    DEFiRet;
    FILE *fp;

    free(loadConf->globals.pszDfltNetstrmDrvrCertFile);
    loadConf->globals.pszDfltNetstrmDrvrCertFile = pNewVal;
    fp = fopen((const char *)pNewVal, "r");
    if (fp == NULL) {
        LogError(errno, RS_RET_NO_FILE_ACCESS,
                 "error: defaultnetstreamdrivercertfile '%s' "
                 "could not be accessed",
                 pNewVal);
    } else {
        fclose(fp);
    }

    RETiRet;
}

static rsRetVal setDfltNetstrmDrvrKeyFile(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    DEFiRet;
    FILE *fp;

    free(loadConf->globals.pszDfltNetstrmDrvrKeyFile);
    loadConf->globals.pszDfltNetstrmDrvrKeyFile = pNewVal;
    fp = fopen((const char *)pNewVal, "r");
    if (fp == NULL) {
        LogError(errno, RS_RET_NO_FILE_ACCESS,
                 "error: defaultnetstreamdriverkeyfile '%s' "
                 "could not be accessed",
                 pNewVal);
    } else {
        fclose(fp);
    }

    RETiRet;
}

static rsRetVal setDfltNetstrmDrvr(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    DEFiRet;
    free(loadConf->globals.pszDfltNetstrmDrvr);
    loadConf->globals.pszDfltNetstrmDrvr = pNewVal;
    RETiRet;
}

static rsRetVal setDfltOpensslEngine(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    DEFiRet;
    free(loadConf->globals.pszDfltOpensslEngine);
    loadConf->globals.pszDfltOpensslEngine = pNewVal;
    RETiRet;
}

/* Set the maximum number of files the rsyslog process may open. */
rsRetVal glblSetMaxOpenFiles(void __attribute__((unused)) * pVal, int iFiles) {
    struct rlimit maxFiles;
    struct rlimit currentLimit;

    DEFiRet;

    if (getrlimit(RLIMIT_NOFILE, &currentLimit) < 0) {
        currentLimit.rlim_max = 0;
    }

    maxFiles.rlim_cur = iFiles;
    maxFiles.rlim_max = iFiles;

    if (setrlimit(RLIMIT_NOFILE, &maxFiles) < 0) {
        /* NOTE: under valgrind, we seem to be unable to extend the size! */
        LogError(errno, RS_RET_ERR_RLIM_NOFILE,
                 "could not set process file limit to %d "
                 "[current hard limit %llu]",
                 iFiles, (unsigned long long)currentLimit.rlim_max);
        ABORT_FINALIZE(RS_RET_ERR_RLIM_NOFILE);
    }
#ifdef USE_UNLIMITED_SELECT
    SetFdSetSize(howmany(iFiles, __NFDBITS) * sizeof(fd_mask));
#endif
    DBGPRINTF("Max number of files set to %d [current hard limit %llu].\n", iFiles,
              (unsigned long long)currentLimit.rlim_max);

finalize_it:
    RETiRet;
}


static rsRetVal setParserControlCharacterEscapePrefix(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    DEFiRet;
    loadConf->globals.parser.cCCEscapeChar = *pNewVal;
    RETiRet;
}

static rsRetVal setParserDropTrailingLFOnReception(void __attribute__((unused)) * pVal, int pNewVal) {
    DEFiRet;
    loadConf->globals.parser.bDropTrailingLF = pNewVal;
    RETiRet;
}

static rsRetVal setParserDropTrailingCROnReception(void __attribute__((unused)) * pVal, int pNewVal) {
    DEFiRet;
    loadConf->globals.parser.bDropTrailingCR = pNewVal;
    RETiRet;
}

static rsRetVal setParserEscapeControlCharactersOnReceive(void __attribute__((unused)) * pVal, int pNewVal) {
    DEFiRet;
    loadConf->globals.parser.bEscapeCCOnRcv = pNewVal;
    RETiRet;
}

static rsRetVal setParserSpaceLFOnReceive(void __attribute__((unused)) * pVal, int pNewVal) {
    DEFiRet;
    loadConf->globals.parser.bSpaceLFOnRcv = pNewVal;
    RETiRet;
}

static rsRetVal setParserEscape8BitCharactersOnReceive(void __attribute__((unused)) * pVal, int pNewVal) {
    DEFiRet;
    loadConf->globals.parser.bEscape8BitChars = pNewVal;
    RETiRet;
}

static rsRetVal setParserEscapeControlCharacterTab(void __attribute__((unused)) * pVal, int pNewVal) {
    DEFiRet;
    loadConf->globals.parser.bEscapeTab = pNewVal;
    RETiRet;
}

/* This function is used both by legacy and RainerScript conf. It is a real setter. */
static void setMaxLine(const int64_t iNew) {
    if (iNew < 128) {
        LogError(0, RS_RET_INVALID_VALUE,
                 "maxMessageSize tried to set "
                 "to %lld, but cannot be less than 128 - set to 128 "
                 "instead",
                 (long long)iNew);
        loadConf->globals.iMaxLine = 128;
    } else if (iNew > (int64_t)INT_MAX) {
        LogError(0, RS_RET_INVALID_VALUE,
                 "maxMessageSize larger than "
                 "INT_MAX (%d) - reduced to INT_MAX",
                 INT_MAX);
        loadConf->globals.iMaxLine = INT_MAX;
    } else {
        loadConf->globals.iMaxLine = (int)iNew;
    }
}


static rsRetVal legacySetMaxMessageSize(void __attribute__((unused)) * pVal, int64_t iNew) {
    setMaxLine(iNew);
    return RS_RET_OK;
}

static rsRetVal setDebugFile(void __attribute__((unused)) * pVal, uchar *pNewVal) {
    DEFiRet;
    dbgSetDebugFile(pNewVal);
    free(pNewVal);
    RETiRet;
}

static rsRetVal setDebugLevel(void __attribute__((unused)) * pVal, int level) {
    DEFiRet;
    dbgSetDebugLevel(level);
    dbgprintf("debug level %d set via config file\n", level);
    dbgprintf("This is rsyslog version " VERSION "\n");
    RETiRet;
}

static rsRetVal ATTR_NONNULL() setOversizeMsgInputMode(const uchar *const mode) {
    DEFiRet;
    if (!strcmp((char *)mode, "truncate")) {
        loadConf->globals.oversizeMsgInputMode = glblOversizeMsgInputMode_Truncate;
    } else if (!strcmp((char *)mode, "split")) {
        loadConf->globals.oversizeMsgInputMode = glblOversizeMsgInputMode_Split;
    } else if (!strcmp((char *)mode, "accept")) {
        loadConf->globals.oversizeMsgInputMode = glblOversizeMsgInputMode_Accept;
    } else {
        loadConf->globals.oversizeMsgInputMode = glblOversizeMsgInputMode_Truncate;
    }
    RETiRet;
}

static rsRetVal ATTR_NONNULL() setReportChildProcessExits(const uchar *const mode) {
    DEFiRet;
    if (!strcmp((char *)mode, "none")) {
        loadConf->globals.reportChildProcessExits = REPORT_CHILD_PROCESS_EXITS_NONE;
    } else if (!strcmp((char *)mode, "errors")) {
        loadConf->globals.reportChildProcessExits = REPORT_CHILD_PROCESS_EXITS_ERRORS;
    } else if (!strcmp((char *)mode, "all")) {
        loadConf->globals.reportChildProcessExits = REPORT_CHILD_PROCESS_EXITS_ALL;
    } else {
        LogError(0, RS_RET_CONF_PARAM_INVLD,
                 "invalid value '%s' for global parameter reportChildProcessExits -- ignored", mode);
        iRet = RS_RET_CONF_PARAM_INVLD;
    }
    RETiRet;
}

/** Convert enable/warn/disable config-format policy text to its internal enum. */
static rsRetVal ATTR_NONNULL()
    setCompatConfigFormatMode(const char *const param, const uchar *const mode, int *const dst) {
    DEFiRet;
    if (!strcmp((const char *)mode, "enable")) {
        *dst = COMPAT_CONFIGFORMAT_ENABLE;
    } else if (!strcmp((const char *)mode, "warn")) {
        *dst = COMPAT_CONFIGFORMAT_WARN;
    } else if (!strcmp((const char *)mode, "disable")) {
        *dst = COMPAT_CONFIGFORMAT_DISABLE;
    } else {
        parser_errmsg("invalid value '%s' for global parameter %s", mode, param);
        iRet = RS_RET_CONF_PARAM_INVLD;
    }
    RETiRet;
}

/** Convert strict/backward-compatible/warn secure-defaults policy text to its internal enum. */
static rsRetVal ATTR_NONNULL() setCompatDefaultsSecure(const uchar *const mode) {
    DEFiRet;
    if (!strcmp((const char *)mode, "strict")) {
        loadConf->globals.compatDefaultsSecure = COMPAT_DEFAULTS_SECURE_STRICT;
        loadConf->globals.bAbortOnUncleanConfig = 1;
    } else if (!strcmp((const char *)mode, "backward-compatible")) {
        loadConf->globals.compatDefaultsSecure = COMPAT_DEFAULTS_SECURE_BACKWARD_COMPATIBLE;
    } else if (!strcmp((const char *)mode, "warn")) {
        loadConf->globals.compatDefaultsSecure = COMPAT_DEFAULTS_SECURE_WARN;
    } else {
        parser_errmsg("invalid value '%s' for global parameter compatibility.defaults.secure", mode);
        iRet = RS_RET_CONF_PARAM_INVLD;
    }
    RETiRet;
}

/** Apply a global compatibility enum string during config parsing. */
static rsRetVal ATTR_NONNULL() processCompatGlobalParam(const char *const name, struct cnfparamvals *const pval) {
    char *mode;
    DEFiRet;

    mode = es_str2cstr(pval->val.d.estr, NULL);
    if (mode == NULL) {
        parser_errmsg("out of memory processing global parameter %s", name);
        ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
    }
    if (!strcmp(name, "compatibility.configformat.legacy")) {
        CHKiRet(setCompatConfigFormatMode(name, (uchar *)mode, &loadConf->globals.compatConfigFormatLegacy));
    } else if (!strcmp(name, "compatibility.configformat.syslogd")) {
        CHKiRet(setCompatConfigFormatMode(name, (uchar *)mode, &loadConf->globals.compatConfigFormatSyslogd));
    } else if (!strcmp(name, "compatibility.configformat.property")) {
        CHKiRet(setCompatConfigFormatMode(name, (uchar *)mode, &loadConf->globals.compatConfigFormatProperty));
    } else if (!strcmp(name, "compatibility.defaults.secure")) {
        CHKiRet(setCompatDefaultsSecure((uchar *)mode));
    }

finalize_it:
    free(mode);
    RETiRet;
}

/** Emit a compatibility warning through the parser diagnostics path. */
static void ATTR_NONNULL() compatParserWarnmsg(const char *const msg) {
    parser_warnmsg("%s", msg);
}

/** Emit warning/error for compatibility-controlled legacy syntax and say how to re-enable it. */
static int ATTR_NONNULL()
    permitCompatSyntax(const int mode, const char *const kind, const char *const text, const char *const option) {
    int permitted = 1;

    if (mode == COMPAT_CONFIGFORMAT_WARN) {
        char msg[1024];
        snprintf(msg, sizeof(msg), "obsolete %s syntax encountered: '%s'", kind, text);
        msg[sizeof(msg) - 1] = '\0';
        compatParserWarnmsg(msg);
    } else if (mode == COMPAT_CONFIGFORMAT_DISABLE) {
        parser_errmsg("obsolete %s syntax disabled: '%s'; use global(%s=\"enable\") to enable it", kind, text, option);
        permitted = 0;
    }

    return permitted;
}

int glblPermitLegacyConfigDirective(rsconf_t *cnf, const char *directive) {
    return permitCompatSyntax(cnf->globals.compatConfigFormatLegacy, "$-directive", directive,
                              "compatibility.configformat.legacy");
}

int glblPermitSyslogdConfigFilter(rsconf_t *cnf, const char *filter) {
    return permitCompatSyntax(cnf->globals.compatConfigFormatSyslogd, "classic syslogd filter", filter,
                              "compatibility.configformat.syslogd");
}

int glblPermitPropertyConfigFilter(rsconf_t *cnf, const char *filter) {
    return permitCompatSyntax(cnf->globals.compatConfigFormatProperty, "classic property-based filter", filter,
                              "compatibility.configformat.property");
}

void glblWarnIfInsecureDefault(rsconf_t *cnf, const char *detail) {
    if (cnf != NULL && cnf->globals.compatDefaultsSecure == COMPAT_DEFAULTS_SECURE_WARN) {
        parser_warnmsg(
            "backward-compatible insecure default in use: %s; "
            "use global(compatibility.defaults.secure=\"strict\") to enable the secure default",
            detail);
    }
}

const uchar *glblGetEffectiveNetstrmDrvr(rsconf_t *cnf, const uchar *localDrvr) {
    if (localDrvr != NULL) {
        return localDrvr;
    }
    if (cnf == NULL) {
        return DFLT_NETSTRM_DRVR;
    }
    return GetDfltNetstrmDrvr(cnf);
}

int glblIsTlsCapableNetstrmDrvr(const uchar *drvr) {
    return drvr != NULL && (!strcasecmp((const char *)drvr, "ossl") || !strcasecmp((const char *)drvr, "gtls") ||
                            !strcasecmp((const char *)drvr, "mbedtls"));
}

static int getDefPFFamily(rsconf_t *cnf) {
    return cnf->globals.iDefPFFamily;
}

/* return our local IP.
 * If no local IP is set, "127.0.0.1" is selected *and* set. This
 * is an intensional side effect that we do in order to keep things
 * consistent and avoid config errors (this will make us not accept
 * setting the local IP address once a module has obtained it - so
 * it forces the $LocalHostIPIF directive high up in rsyslog.conf)
 * rgerhards, 2012-03-21
 */
static prop_t *GetLocalHostIP(void) {
    assert(propLocalIPIF != NULL);
    return (propLocalIPIF);
}


/* set our local hostname. Free previous hostname, if it was already set.
 * Note that we do now do this in a thread. As such, the method here
 * is NOT 100% clean. If we run into issues, we need to think about
 * refactoring the whole way we handle the local host name processing.
 * Possibly using a PROP might be the right solution then.
 */
static rsRetVal SetLocalHostName(uchar *const newname) {
    uchar *toFree;
    if (LocalHostName == NULL || strcmp((const char *)LocalHostName, (const char *)newname)) {
        toFree = LocalHostName;
        LocalHostName = newname;
    } else {
        toFree = newname;
    }
    free(toFree);
    return RS_RET_OK;
}


/* return our local hostname. if it is not set, "[localhost]" is returned
 */
uchar *glblGetLocalHostName(void) {
    uchar *pszRet;

    if (LocalHostNameOverride != NULL) {
        pszRet = LocalHostNameOverride;
        goto done;
    }

    if (LocalHostName == NULL)
        pszRet = (uchar *)"[localhost]";
    else {
        if (GetPreserveFQDN() == 1)
            pszRet = LocalFQDNName;
        else
            pszRet = LocalHostName;
    }
done:
    return (pszRet);
}


/* return the name of the file where oversize messages are written to
 */
uchar *glblGetOversizeMsgErrorFile(rsconf_t *cnf) {
    return cnf->globals.oversizeMsgErrorFile;
}

const uchar *glblGetOperatingStateFile(rsconf_t *cnf) {
    return cnf->globals.operatingStateFile;
}

/* return the mode with which oversize messages will be put forward
 */
int glblGetOversizeMsgInputMode(rsconf_t *cnf) {
    return cnf->globals.oversizeMsgInputMode;
}

int glblReportOversizeMessage(rsconf_t *cnf) {
    return cnf->globals.reportOversizeMsg;
}


/* logs a message indicating that a child process has terminated.
 * If name != NULL, prints it as the program name.
 */
void glblReportChildProcessExit(rsconf_t *cnf, const uchar *name, pid_t pid, int status) {
    DBGPRINTF("waitpid for child %ld returned status: %2.2x\n", (long)pid, status);

    if (cnf->globals.reportChildProcessExits == REPORT_CHILD_PROCESS_EXITS_NONE ||
        (cnf->globals.reportChildProcessExits == REPORT_CHILD_PROCESS_EXITS_ERRORS && WIFEXITED(status) &&
         WEXITSTATUS(status) == 0)) {
        return;
    }

    if (WIFEXITED(status)) {
        int severity = WEXITSTATUS(status) == 0 ? LOG_INFO : LOG_WARNING;
        if (name != NULL) {
            LogMsg(0, NO_ERRCODE, severity, "program '%s' (pid %ld) exited with status %d", name, (long)pid,
                   WEXITSTATUS(status));
        } else {
            LogMsg(0, NO_ERRCODE, severity, "child process (pid %ld) exited with status %d", (long)pid,
                   WEXITSTATUS(status));
        }
    } else if (WIFSIGNALED(status)) {
        if (name != NULL) {
            LogMsg(0, NO_ERRCODE, LOG_WARNING, "program '%s' (pid %ld) terminated by signal %d", name, (long)pid,
                   WTERMSIG(status));
        } else {
            LogMsg(0, NO_ERRCODE, LOG_WARNING, "child process (pid %ld) terminated by signal %d", (long)pid,
                   WTERMSIG(status));
        }
    }
}


/* set our local domain name. Free previous domain, if it was already set.
 */
static rsRetVal SetLocalDomain(uchar *newname) {
    free(LocalDomain);
    LocalDomain = newname;
    return RS_RET_OK;
}


/* return our local hostname. if it is not set, "[localhost]" is returned
 */
static uchar *GetLocalDomain(void) {
    return LocalDomain;
}


/* generate the local hostname property. This must be done after the hostname info
 * has been set as well as PreserveFQDN.
 * rgerhards, 2009-06-30
 * NOTE: This function tries to avoid locking by not destructing the previous value
 * immediately. This is so that current readers can  continue to use the previous name.
 * Otherwise, we would need to use read/write locks to protect the update process.
 * In order to do so, we save the previous value and delete it when we are called again
 * the next time. Note that this in theory is racy and can lead to a double-free.
 * In practice, however, the window of exposure to trigger this is extremely short
 * and as this functions is very infrequently being called (on HUP), the trigger
 * condition for this bug is so highly unlikely that it never occurs in practice.
 * Probably if you HUP rsyslog every few milliseconds, but who does that...
 * To further reduce risk potential, we do only update the property when there
 * actually is a hostname change, which makes it even less likely.
 * rgerhards, 2013-10-28
 */
static rsRetVal GenerateLocalHostNameProperty(void) {
    uchar *pszPrev;
    int lenPrev;
    prop_t *hostnameNew;
    uchar *pszName;
    DEFiRet;

    if (propLocalHostNameToDelete != NULL) prop.Destruct(&propLocalHostNameToDelete);

    if (LocalHostNameOverride == NULL) {
        if (LocalHostName == NULL)
            pszName = (uchar *)"[localhost]";
        else {
            if (GetPreserveFQDN() == 1)
                pszName = LocalFQDNName;
            else
                pszName = LocalHostName;
        }
    } else { /* local hostname is overriden via config */
        pszName = LocalHostNameOverride;
    }
    DBGPRINTF("GenerateLocalHostName uses '%s'\n", pszName);

    if (propLocalHostName == NULL)
        pszPrev = (uchar *)""; /* make sure strcmp() below does not match */
    else
        prop.GetString(propLocalHostName, &pszPrev, &lenPrev);

    if (ustrcmp(pszPrev, pszName)) {
        /* we need to update */
        CHKiRet(prop.Construct(&hostnameNew));
        CHKiRet(prop.SetString(hostnameNew, pszName, ustrlen(pszName)));
        CHKiRet(prop.ConstructFinalize(hostnameNew));
        propLocalHostNameToDelete = propLocalHostName;
        propLocalHostName = hostnameNew;
    }

finalize_it:
    RETiRet;
}


/* return our local hostname as a string property
 */
static prop_t *GetLocalHostNameProp(void) {
    return (propLocalHostName);
}


static rsRetVal SetLocalFQDNName(uchar *newname) {
    free(LocalFQDNName);
    LocalFQDNName = newname;
    return RS_RET_OK;
}

/* return the current localhost name as FQDN (requires FQDN to be set)
 */
static uchar *GetLocalFQDNName(void) {
    return (LocalFQDNName == NULL ? (uchar *)"[localhost]" : LocalFQDNName);
}


/* return the current working directory */
static uchar *GetWorkDir(rsconf_t *cnf) {
    return (cnf->globals.pszWorkDir == NULL ? (uchar *)"" : cnf->globals.pszWorkDir);
}

/* return the "raw" working directory, which means
 * NULL if unset.
 */
const uchar *glblGetWorkDirRaw(rsconf_t *cnf) {
    return cnf->globals.pszWorkDir;
}

/* return the current default netstream driver */
static uchar *GetDfltNetstrmDrvr(rsconf_t *cnf) {
    return (cnf->globals.pszDfltNetstrmDrvr == NULL ? DFLT_NETSTRM_DRVR : cnf->globals.pszDfltNetstrmDrvr);
}

/* return the current default openssl engine name */
static uchar *GetDfltOpensslEngine(rsconf_t *cnf) {
    return (cnf->globals.pszDfltOpensslEngine);
}

/* [ar] Source IP for local client to be used on multihomed host */
static rsRetVal SetSourceIPofLocalClient(uchar *newname) {
    if (SourceIPofLocalClient != NULL) {
        free(SourceIPofLocalClient);
    }
    SourceIPofLocalClient = newname;
    return RS_RET_OK;
}

static uchar *GetSourceIPofLocalClient(void) {
    return (SourceIPofLocalClient);
}


/* queryInterface function
 * rgerhards, 2008-02-21
 */
BEGINobjQueryInterface(glbl)
    CODESTARTobjQueryInterface(glbl);
    if (pIf->ifVersion != glblCURR_IF_VERSION) { /* check for current version, increment on each change */
        ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
    }

    /* ok, we have the right interface, so let's fill it
     * Please note that we may also do some backwards-compatibility
     * work here (if we can support an older interface version - that,
     * of course, also affects the "if" above).
     */
    pIf->GetWorkDir = GetWorkDir;
    pIf->GenerateLocalHostNameProperty = GenerateLocalHostNameProperty;
    pIf->GetLocalHostNameProp = GetLocalHostNameProp;
    pIf->GetLocalHostIP = GetLocalHostIP;
    pIf->SetGlobalInputTermination = SetGlobalInputTermination;
    pIf->GetGlobalInputTermState = GetGlobalInputTermState;
    pIf->GetSourceIPofLocalClient = GetSourceIPofLocalClient; /* [ar] */
    pIf->SetSourceIPofLocalClient = SetSourceIPofLocalClient; /* [ar] */
    pIf->GetDefPFFamily = getDefPFFamily;
    pIf->GetDisableDNS = GetDisableDNS;
    pIf->GetMaxLine = glblGetMaxLine;
    pIf->GetOptionDisallowWarning = GetOptionDisallowWarning;
    pIf->GetDfltNetstrmDrvrCAF = GetDfltNetstrmDrvrCAF;
    pIf->GetDfltNetstrmDrvrCRLF = GetDfltNetstrmDrvrCRLF;
    pIf->GetDfltNetstrmDrvrCertFile = GetDfltNetstrmDrvrCertFile;
    pIf->GetDfltNetstrmDrvrKeyFile = GetDfltNetstrmDrvrKeyFile;
    pIf->GetDfltNetstrmDrvr = GetDfltNetstrmDrvr;
    pIf->GetDfltOpensslEngine = GetDfltOpensslEngine;
    pIf->GetNetstrmDrvrCAExtraFiles = GetNetstrmDrvrCAExtraFiles;
    pIf->GetParserControlCharacterEscapePrefix = GetParserControlCharacterEscapePrefix;
    pIf->GetParserDropTrailingLFOnReception = GetParserDropTrailingLFOnReception;
    pIf->GetParserDropTrailingCROnReception = GetParserDropTrailingCROnReception;
    pIf->GetParserEscapeControlCharactersOnReceive = GetParserEscapeControlCharactersOnReceive;
    pIf->GetParserSpaceLFOnReceive = GetParserSpaceLFOnReceive;
    pIf->GetParserEscape8BitCharactersOnReceive = GetParserEscape8BitCharactersOnReceive;
    pIf->GetParserEscapeControlCharacterTab = GetParserEscapeControlCharacterTab;
    pIf->GetLocalHostName = glblGetLocalHostName;
    pIf->SetLocalHostName = SetLocalHostName;
#define SIMP_PROP(name)         \
    pIf->Get##name = Get##name; \
    pIf->Set##name = Set##name;
    SIMP_PROP(PreserveFQDN);
    SIMP_PROP(DropMalPTRMsgs);
    SIMP_PROP(mainqCnfObj);
    SIMP_PROP(LocalFQDNName)
    SIMP_PROP(LocalDomain)
    SIMP_PROP(ParserEscapeControlCharactersCStyle)
    SIMP_PROP(ParseHOSTNAMEandTAG)
#ifdef USE_UNLIMITED_SELECT
    SIMP_PROP(FdSetSize)
#endif
#undef SIMP_PROP
finalize_it:
ENDobjQueryInterface(glbl)

/* Reset config variables to default values.
 * rgerhards, 2008-04-17
 */
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) * pp, void __attribute__((unused)) * pVal) {
    free(loadConf->globals.pszDfltNetstrmDrvr);
    loadConf->globals.pszDfltNetstrmDrvr = NULL;
    free(loadConf->globals.pszDfltNetstrmDrvrCAF);
    loadConf->globals.pszDfltNetstrmDrvrCAF = NULL;
    free(loadConf->globals.pszDfltNetstrmDrvrCRLF);
    loadConf->globals.pszDfltNetstrmDrvrCRLF = NULL;
    free(loadConf->globals.pszDfltNetstrmDrvrKeyFile);
    loadConf->globals.pszDfltNetstrmDrvrKeyFile = NULL;
    free(loadConf->globals.pszDfltNetstrmDrvrCertFile);
    loadConf->globals.pszDfltNetstrmDrvrCertFile = NULL;
    free(loadConf->globals.pszDfltOpensslEngine);
    loadConf->globals.pszDfltOpensslEngine = NULL;
    free(LocalHostNameOverride);
    LocalHostNameOverride = NULL;
    free(loadConf->globals.oversizeMsgErrorFile);
    loadConf->globals.oversizeMsgErrorFile = NULL;
    loadConf->globals.oversizeMsgInputMode = glblOversizeMsgInputMode_Accept;
    loadConf->globals.reportChildProcessExits = REPORT_CHILD_PROCESS_EXITS_ERRORS;
    free(loadConf->globals.pszWorkDir);
    loadConf->globals.pszWorkDir = NULL;
    free((void *)loadConf->globals.operatingStateFile);
    loadConf->globals.operatingStateFile = NULL;
    loadConf->globals.bDropMalPTRMsgs = 0;
    loadConf->globals.systemdNotifyReadyDelay = 0;
    bPreserveFQDN = 0;
    loadConf->globals.iMaxLine = 8192;
    loadConf->globals.reportOversizeMsg = 1;
    loadConf->globals.parser.cCCEscapeChar = '#';
    loadConf->globals.parser.bDropTrailingLF = 1;
    loadConf->globals.parser.bDropTrailingCR = 0;
    loadConf->globals.parser.bEscapeCCOnRcv = 1; /* default is to escape control characters */
    loadConf->globals.parser.bSpaceLFOnRcv = 0;
    loadConf->globals.parser.bEscape8BitChars = 0; /* default is not to escape control characters */
    loadConf->globals.parser.bEscapeTab = 1; /* default is to escape tab characters */
    loadConf->globals.parser.bParserEscapeCCCStyle = 0;
#ifdef USE_UNLIMITED_SELECT
    iFdSetSize = howmany(FD_SETSIZE, __NFDBITS) * sizeof(fd_mask);
#endif
    return RS_RET_OK;
}


/* Prepare for new config
 */
void glblPrepCnf(void) {
    free(mainqCnfObj);
    mainqCnfObj = NULL;
    free(cnfparamvals);
    cnfparamvals = NULL;
}

/* handle the timezone() object. Each incarnation adds one additional
 * zone info to the global table of time zones.
 */

int bs_arrcmp_glblDbgFiles(const void *s1, const void *s2) {
    return strcmp((char *)s1, *(char **)s2);
}


/* handle a global config object. Note that multiple global config statements
 * are permitted (because of plugin support), so once we got a param block,
 * we need to hold to it.
 * rgerhards, 2011-07-19
 */
void glblProcessCnf(struct cnfobj *o) {
    int i;

    cnfparamvals = nvlstGetParams(o->nvlst, &paramblk, cnfparamvals);
    if (cnfparamvals == NULL) {
        LogError(0, RS_RET_MISSING_CNFPARAMS,
                 "error processing global "
                 "config parameters [global(...)]");
        goto done;
    }
    if (Debug) {
        dbgprintf("glbl param blk after glblProcessCnf:\n");
        cnfparamsPrint(&paramblk, cnfparamvals);
    }

    /* The next thing is a bit hackish and should be changed in higher
     * versions. There are a select few parameters which we need to
     * act on immediately. These are processed here.
     */
    for (i = 0; i < paramblk.nParams; ++i) {
        if (!cnfparamvals[i].bUsed) continue;
        if (!strcmp(paramblk.descr[i].name, "processinternalmessages")) {
            loadConf->globals.bProcessInternalMessages = (int)cnfparamvals[i].val.d.n;
            cnfparamvals[i].bUsed = TRUE;
        } else if (!strcmp(paramblk.descr[i].name, "internal.developeronly.options")) {
            loadConf->globals.glblDevOptions = (uint64_t)cnfparamvals[i].val.d.n;
            cnfparamvals[i].bUsed = TRUE;
        } else if (!strcmp(paramblk.descr[i].name, "stdlog.channelspec")) {
#ifndef ENABLE_LIBLOGGING_STDLOG
            LogError(0, RS_RET_ERR,
                     "rsyslog wasn't "
                     "compiled with liblogging-stdlog support. "
                     "The 'stdlog.channelspec' parameter "
                     "is ignored. Note: the syslog API is used instead.\n");
#else
            loadConf->globals.stdlog_chanspec = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            /* we need to re-open with the new channel */
            stdlog_close(loadConf->globals.stdlog_hdl);
            loadConf->globals.stdlog_hdl =
                stdlog_open("rsyslogd", 0, STDLOG_SYSLOG, (char *)loadConf->globals.stdlog_chanspec);
            cnfparamvals[i].bUsed = TRUE;
#endif
        } else if (!strcmp(paramblk.descr[i].name, "operatingstatefile")) {
            if (loadConf->globals.operatingStateFile != NULL) {
                LogError(errno, RS_RET_PARAM_ERROR,
                         "error: operatingStateFile already set to '%s' - "
                         "new value ignored",
                         loadConf->globals.operatingStateFile);
            } else {
                loadConf->globals.operatingStateFile = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
                osf_open();
            }
        } else if (!strcmp(paramblk.descr[i].name, "security.abortonidresolutionfail")) {
            loadConf->globals.abortOnIDResolutionFail = (int)cnfparamvals[i].val.d.n;
            cnfparamvals[i].bUsed = TRUE;
        } else if (!strcmp(paramblk.descr[i].name, "defaultnetstreamdriver")) {
            uchar *const cstr = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            if (cstr == NULL) {
                LogError(0, RS_RET_OUT_OF_MEMORY, "out of memory processing defaultnetstreamdriver");
            } else {
                setDfltNetstrmDrvr(NULL, cstr);
            }
            cnfparamvals[i].bUsed = TRUE;
        } else if (!strncmp(paramblk.descr[i].name, "compatibility.", 14)) {
            if (processCompatGlobalParam(paramblk.descr[i].name, &cnfparamvals[i]) == RS_RET_OK) {
                cnfparamvals[i].bUsed = 1;
            } else {
                dbgprintf("glblProcessCnf: failed to process early global parameter '%s'\n", paramblk.descr[i].name);
            }
        }
    }
done:
    return;
}

/* Set mainq parameters. Note that when this is not called, we'll use the
 * legacy parameter config. mainq parameters can only be set once.
 */
void glblProcessMainQCnf(struct cnfobj *o) {
    if (mainqCnfObj == NULL) {
        mainqCnfObj = o;
    } else {
        LogError(0, RS_RET_ERR,
                 "main_queue() object can only be specified "
                 "once - all but first ignored\n");
    }
}

/* destruct the main q cnf object after it is no longer needed. This is
 * also used to do some final checks.
 */
void glblDestructMainqCnfObj(void) {
    /* Only destruct if not NULL! */
    if (mainqCnfObj != NULL) {
        nvlstChkUnused(mainqCnfObj->nvlst);
        cnfobjDestruct(mainqCnfObj);
        mainqCnfObj = NULL;
    }
}

static int qs_arrcmp_glblDbgFiles(const void *s1, const void *s2) {
    return strcmp(*((char **)s1), *((char **)s2));
}

/* set an environment variable */
static rsRetVal do_setenv(const char *const var) {
    char varname[128];
    const char *val = var;
    size_t i;
    DEFiRet;

    for (i = 0; *val != '='; ++i, ++val) {
        if (i == sizeof(varname) - i) {
            parser_errmsg(
                "environment variable name too long "
                "[max %zu chars] or malformed entry: '%s'",
                sizeof(varname) - 1, var);
            ABORT_FINALIZE(RS_RET_ERR_SETENV);
        }
        if (*val == '\0') {
            parser_errmsg(
                "environment variable entry is missing "
                "equal sign (for value): '%s'",
                var);
            ABORT_FINALIZE(RS_RET_ERR_SETENV);
        }
        varname[i] = *val;
    }
    varname[i] = '\0';
    ++val;
    DBGPRINTF("do_setenv, var '%s', val '%s'\n", varname, val);

    if (setenv(varname, val, 1) != 0) {
        char errStr[1024];
        rs_strerror_r(errno, errStr, sizeof(errStr));
        parser_errmsg(
            "error setting environment variable "
            "'%s' to '%s': %s",
            varname, val, errStr);
        ABORT_FINALIZE(RS_RET_ERR_SETENV);
    }


finalize_it:
    RETiRet;
}


/* This processes the "regular" parameters which are to be set after the
 * config has been fully loaded.
 */
rsRetVal glblDoneLoadCnf(void) {
    int i;
    unsigned char *cstr;
    DEFiRet;
    CHKiRet(objUse(net, CORE_COMPONENT));

    sortTimezones(loadConf);
    DBGPRINTF("Timezone information table (%d entries):\n", loadConf->timezones.ntzinfos);
    displayTimezones(loadConf);

    if (cnfparamvals == NULL) goto finalize_it;

    for (i = 0; i < paramblk.nParams; ++i) {
        if (!cnfparamvals[i].bUsed) continue;
        if (!strcmp(paramblk.descr[i].name, "workdirectory")) {
            cstr = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            setWorkDir(NULL, cstr);
        } else if (!strcmp(paramblk.descr[i].name, "libcapng.default")) {
#ifdef ENABLE_LIBCAPNG
            loadConf->globals.bAbortOnFailedLibcapngSetup = (int)cnfparamvals[i].val.d.n;
#else
            LogError(0, RS_RET_ERR,
                     "rsyslog wasn't "
                     "compiled with libcap-ng support.");
#endif
        } else if (!strcmp(paramblk.descr[i].name, "libcapng.enable")) {
#ifdef ENABLE_LIBCAPNG
            loadConf->globals.bCapabilityDropEnabled = (int)cnfparamvals[i].val.d.n;
#else
            LogError(0, RS_RET_ERR,
                     "rsyslog wasn't "
                     "compiled with libcap-ng support.");
#endif
        } else if (!strcmp(paramblk.descr[i].name, "variables.casesensitive")) {
            const int val = (int)cnfparamvals[i].val.d.n;
            fjson_global_do_case_sensitive_comparison(val);
            DBGPRINTF("global/config: set case sensitive variables to %d\n", val);
        } else if (!strcmp(paramblk.descr[i].name, "localhostname")) {
            free(LocalHostNameOverride);
            LocalHostNameOverride = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
        } else if (!strcmp(paramblk.descr[i].name, "defaultnetstreamdriverkeyfile")) {
            cstr = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            setDfltNetstrmDrvrKeyFile(NULL, cstr);
        } else if (!strcmp(paramblk.descr[i].name, "defaultnetstreamdrivercertfile")) {
            cstr = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            setDfltNetstrmDrvrCertFile(NULL, cstr);
        } else if (!strcmp(paramblk.descr[i].name, "defaultnetstreamdrivercafile")) {
            cstr = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            setDfltNetstrmDrvrCAF(NULL, cstr);
        } else if (!strcmp(paramblk.descr[i].name, "defaultnetstreamdrivercrlfile")) {
            cstr = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            setDfltNetstrmDrvrCRLF(NULL, cstr);
        } else if (!strcmp(paramblk.descr[i].name, "defaultopensslengine")) {
            cstr = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            setDfltOpensslEngine(NULL, cstr);
        } else if (!strcmp(paramblk.descr[i].name, "netstreamdrivercaextrafiles")) {
            cstr = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            setNetstrmDrvrCAExtraFiles(NULL, cstr);
        } else if (!strcmp(paramblk.descr[i].name, "preservefqdn")) {
            bPreserveFQDN = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "compactjsonstring")) {
            glblJsonFormatOpt = cnfparamvals[i].val.d.n ? JSON_C_TO_STRING_PLAIN : JSON_C_TO_STRING_SPACED;
        } else if (!strcmp(paramblk.descr[i].name, "dropmsgswithmaliciousdnsptrrecords")) {
            loadConf->globals.bDropMalPTRMsgs = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "action.reportsuspension")) {
            loadConf->globals.bActionReportSuspension = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "action.reportsuspensioncontinuation")) {
            loadConf->globals.bActionReportSuspensionCont = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "maxmessagesize")) {
            setMaxLine(cnfparamvals[i].val.d.n);
        } else if (!strcmp(paramblk.descr[i].name, "maxopenfiles")) {
            loadConf->globals.iMaxOpenFiles = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "oversizemsg.errorfile")) {
            free(loadConf->globals.oversizeMsgErrorFile);
            loadConf->globals.oversizeMsgErrorFile = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
        } else if (!strcmp(paramblk.descr[i].name, "oversizemsg.report")) {
            loadConf->globals.reportOversizeMsg = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "oversizemsg.input.mode")) {
            const char *const tmp = es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            setOversizeMsgInputMode((uchar *)tmp);
            free((void *)tmp);
        } else if (!strcmp(paramblk.descr[i].name, "reportchildprocessexits")) {
            const char *const tmp = es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            setReportChildProcessExits((uchar *)tmp);
            free((void *)tmp);
        } else if (!strcmp(paramblk.descr[i].name, "debug.onshutdown")) {
            loadConf->globals.debugOnShutdown = (int)cnfparamvals[i].val.d.n;
            LogMsg(0, RS_RET_OK, LOG_INFO, "debug: onShutdown set to %d", loadConf->globals.debugOnShutdown);
        } else if (!strcmp(paramblk.descr[i].name, "debug.gnutls")) {
            loadConf->globals.iGnuTLSLoglevel = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "debug.abortonprogramerror")) {
            glblAbortOnProgramError = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "debug.unloadmodules")) {
            glblUnloadModules = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "parser.controlcharacterescapeprefix")) {
            uchar *tmp = (uchar *)es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            setParserControlCharacterEscapePrefix(NULL, tmp);
            free(tmp);
        } else if (!strcmp(paramblk.descr[i].name, "parser.droptrailinglfonreception")) {
            const int tmp = (int)cnfparamvals[i].val.d.n;
            setParserDropTrailingLFOnReception(NULL, tmp);
        } else if (!strcmp(paramblk.descr[i].name, "parser.droptrailingcronreception")) {
            const int tmp = (int)cnfparamvals[i].val.d.n;
            setParserDropTrailingCROnReception(NULL, tmp);
        } else if (!strcmp(paramblk.descr[i].name, "parser.escapecontrolcharactersonreceive")) {
            const int tmp = (int)cnfparamvals[i].val.d.n;
            setParserEscapeControlCharactersOnReceive(NULL, tmp);
        } else if (!strcmp(paramblk.descr[i].name, "parser.spacelfonreceive")) {
            const int tmp = (int)cnfparamvals[i].val.d.n;
            setParserSpaceLFOnReceive(NULL, tmp);
        } else if (!strcmp(paramblk.descr[i].name, "parser.escape8bitcharactersonreceive")) {
            const int tmp = (int)cnfparamvals[i].val.d.n;
            setParserEscape8BitCharactersOnReceive(NULL, tmp);
        } else if (!strcmp(paramblk.descr[i].name, "parser.escapecontrolcharactertab")) {
            const int tmp = (int)cnfparamvals[i].val.d.n;
            setParserEscapeControlCharacterTab(NULL, tmp);
        } else if (!strcmp(paramblk.descr[i].name, "parser.escapecontrolcharacterscstyle")) {
            const int tmp = (int)cnfparamvals[i].val.d.n;
            SetParserEscapeControlCharactersCStyle(tmp);
        } else if (!strcmp(paramblk.descr[i].name, "parser.parsehostnameandtag")) {
            const int tmp = (int)cnfparamvals[i].val.d.n;
            SetParseHOSTNAMEandTAG(tmp);
        } else if (!strcmp(paramblk.descr[i].name, "parser.permitslashinprogramname")) {
            loadConf->globals.parser.bPermitSlashInProgramname = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "debug.logfile")) {
            if (pszAltDbgFileName == NULL) {
                pszAltDbgFileName = es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
                /* can actually happen if debug system also opened altdbg */
                if (altdbg != -1) {
                    close(altdbg);
                }
                if ((altdbg = open(pszAltDbgFileName, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY | O_CLOEXEC,
                                   S_IRUSR | S_IWUSR)) == -1) {
                    LogError(0, RS_RET_ERR, "debug log file '%s' could not be opened", pszAltDbgFileName);
                }
            }
            LogMsg(0, RS_RET_OK, LOG_INFO, "debug log file is '%s', fd %d", pszAltDbgFileName, altdbg);
        } else if (!strcmp(paramblk.descr[i].name, "janitor.interval")) {
            loadConf->globals.janitorInterval = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "net.ipprotocol")) {
            char *proto = es_str2cstr(cnfparamvals[i].val.d.estr, NULL);
            if (!strcmp(proto, "unspecified")) {
                loadConf->globals.iDefPFFamily = PF_UNSPEC;
            } else if (!strcmp(proto, "ipv4-only")) {
                loadConf->globals.iDefPFFamily = PF_INET;
            } else if (!strcmp(proto, "ipv6-only")) {
                loadConf->globals.iDefPFFamily = PF_INET6;
            } else {
                LogError(0, RS_RET_ERR,
                         "invalid net.ipprotocol "
                         "parameter '%s' -- ignored",
                         proto);
            }
            free(proto);
        } else if (!strcmp(paramblk.descr[i].name, "senders.reportnew")) {
            loadConf->globals.reportNewSenders = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "senders.reportgoneaway")) {
            loadConf->globals.reportGoneAwaySenders = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "senders.timeoutafter")) {
            loadConf->globals.senderStatsTimeout = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "senders.keeptrack")) {
            loadConf->globals.senderKeepTrack = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "inputs.timeout.shutdown")) {
            loadConf->globals.inputTimeoutShutdown = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "privdrop.group.keepsupplemental")) {
            loadConf->globals.gidDropPrivKeepSupplemental = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "privdrop.group.id")) {
            loadConf->globals.gidDropPriv = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "privdrop.group.name")) {
            loadConf->globals.gidDropPriv = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "privdrop.user.id")) {
            loadConf->globals.uidDropPriv = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "privdrop.user.name")) {
            loadConf->globals.uidDropPriv = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "security.abortonidresolutionfail")) {
            loadConf->globals.abortOnIDResolutionFail = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "net.acladdhostnameonfail")) {
            loadConf->globals.ACLAddHostnameOnFail = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "net.aclresolvehostname")) {
            loadConf->globals.ACLDontResolve = !((int)cnfparamvals[i].val.d.n);
        } else if (!strcmp(paramblk.descr[i].name, "net.enabledns")) {
            SetDisableDNS(!((int)cnfparamvals[i].val.d.n));
        } else if (!strcmp(paramblk.descr[i].name, "net.permitwarning")) {
            SetOptionDisallowWarning(!((int)cnfparamvals[i].val.d.n));
        } else if (!strncmp(paramblk.descr[i].name, "compatibility.", 14)) {
            /* processed immediately by glblProcessCnf(), as later syntax depends on it */
        } else if (!strcmp(paramblk.descr[i].name, "systemd.notifyreadydelay")) {
            loadConf->globals.systemdNotifyReadyDelay = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "abortonuncleanconfig")) {
            loadConf->globals.bAbortOnUncleanConfig = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "abortonfailedqueuestartup")) {
            loadConf->globals.bAbortOnFailedQueueStartup = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "internalmsg.ratelimit.burst")) {
            loadConf->globals.intMsgRateLimitBurst = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "internalmsg.ratelimit.interval")) {
            loadConf->globals.intMsgRateLimitItv = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "internalmsg.severity")) {
            loadConf->globals.intMsgsSeverityFilter = (int)cnfparamvals[i].val.d.n;
            if ((loadConf->globals.intMsgsSeverityFilter < 0) || (loadConf->globals.intMsgsSeverityFilter > 7)) {
                parser_errmsg("invalid internalmsg.severity value");
                loadConf->globals.intMsgsSeverityFilter = DFLT_INT_MSGS_SEV_FILTER;
            }
        } else if (!strcmp(paramblk.descr[i].name, "environment")) {
            for (int j = 0; j < cnfparamvals[i].val.d.ar->nmemb; ++j) {
                char *const var = es_str2cstr(cnfparamvals[i].val.d.ar->arr[j], NULL);
                do_setenv(var);
                free(var);
            }
        } else if (!strcmp(paramblk.descr[i].name, "errormessagestostderr.maxnumber")) {
            loadConf->globals.maxErrMsgToStderr = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "allmessagestostderr")) {
            loadConf->globals.bAllMsgToStderr = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "debug.files")) {
            free(glblDbgFiles); /* "fix" Coverity false positive */
            glblDbgFilesNum = cnfparamvals[i].val.d.ar->nmemb;
            glblDbgFiles = (char **)malloc(cnfparamvals[i].val.d.ar->nmemb * sizeof(char *));
            for (int j = 0; j < cnfparamvals[i].val.d.ar->nmemb; ++j) {
                glblDbgFiles[j] = es_str2cstr(cnfparamvals[i].val.d.ar->arr[j], NULL);
            }
            qsort(glblDbgFiles, glblDbgFilesNum, sizeof(char *), qs_arrcmp_glblDbgFiles);
        } else if (!strcmp(paramblk.descr[i].name, "debug.whitelist")) {
            glblDbgWhitelist = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "shutdown.queue.doublesize")) {
            loadConf->globals.shutdownQueueDoubleSize = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "umask")) {
            loadConf->globals.umask = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "shutdown.enable.ctlc")) {
            loadConf->globals.permitCtlC = (int)cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "default.action.queue.timeoutshutdown")) {
            loadConf->globals.actq_dflt_toQShutdown = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "default.action.queue.timeoutactioncompletion")) {
            loadConf->globals.actq_dflt_toActShutdown = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "default.action.queue.timeoutenqueue")) {
            loadConf->globals.actq_dflt_toEnq = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "default.action.queue.timeoutworkerthreadshutdown")) {
            loadConf->globals.actq_dflt_toWrkShutdown = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "default.ruleset.queue.timeoutshutdown")) {
            loadConf->globals.ruleset_dflt_toQShutdown = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "default.ruleset.queue.timeoutactioncompletion")) {
            loadConf->globals.ruleset_dflt_toActShutdown = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "default.ruleset.queue.timeoutenqueue")) {
            loadConf->globals.ruleset_dflt_toEnq = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "default.ruleset.queue.timeoutworkerthreadshutdown")) {
            loadConf->globals.ruleset_dflt_toWrkShutdown = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "reverselookup.cache.ttl.default")) {
            loadConf->globals.dnscacheDefaultTTL = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "reverselookup.cache.ttl.enable")) {
            loadConf->globals.dnscacheEnableTTL = cnfparamvals[i].val.d.n;
        } else if (!strcmp(paramblk.descr[i].name, "parser.supportcompressionextension")) {
            loadConf->globals.bSupportCompressionExtension = cnfparamvals[i].val.d.n;
        } else {
            dbgprintf(
                "glblDoneLoadCnf: program error, non-handled "
                "param '%s'\n",
                paramblk.descr[i].name);
        }
    }

    if (loadConf->globals.debugOnShutdown && Debug != DEBUG_FULL) {
        Debug = DEBUG_ONDEMAND;
        stddbg = -1;
    }

    if (loadConf->globals.compatDefaultsSecure == COMPAT_DEFAULTS_SECURE_STRICT) {
        loadConf->globals.bAbortOnUncleanConfig = 1;
    } else if (!loadConf->globals.bAbortOnUncleanConfig) {
        glblWarnIfInsecureDefault(loadConf, "global(abortOnUncleanConfig=\"off\")");
    }

finalize_it:
    /* we have now read the config. We need to query the local host name now
     * as it was set by the config.
     *
     * Note: early messages are already emited, and have "[localhost]" as
     * hostname. These messages are currently in iminternal queue. Once they
     * are taken from that queue, the hostname will be adapted.
     */
    queryLocalHostname(loadConf);
    RETiRet;
}


/* Initialize the glbl class. Must be called as the very first method
 * before anything else is called inside this class.
 * rgerhards, 2008-02-19
 */
BEGINAbstractObjClassInit(glbl, 1, OBJ_IS_CORE_MODULE) /* class, version */
    /* request objects we use */
    CHKiRet(objUse(prop, CORE_COMPONENT));

    /* initialize properties */
    storeLocalHostIPIF((uchar *)"127.0.0.1");

    /* config handlers are never unregistered and need not be - we are always loaded ;) */
    CHKiRet(regCfSysLineHdlr((uchar *)"debugfile", 0, eCmdHdlrGetWord, setDebugFile, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"debuglevel", 0, eCmdHdlrInt, setDebugLevel, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"workdirectory", 0, eCmdHdlrGetWord, setWorkDir, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"dropmsgswithmaliciousdnsptrrecords", 0, eCmdHdlrBinary, SetDropMalPTRMsgs, NULL,
                             NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdriver", 0, eCmdHdlrGetWord, setDfltNetstrmDrvr, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"defaultopensslengine", 0, eCmdHdlrGetWord, setDfltOpensslEngine, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdrivercafile", 0, eCmdHdlrGetWord, setDfltNetstrmDrvrCAF, NULL,
                             NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdrivercrlfile", 0, eCmdHdlrGetWord, setDfltNetstrmDrvrCRLF, NULL,
                             NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdriverkeyfile", 0, eCmdHdlrGetWord, setDfltNetstrmDrvrKeyFile,
                             NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdrivercertfile", 0, eCmdHdlrGetWord, setDfltNetstrmDrvrCertFile,
                             NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"localhostname", 0, eCmdHdlrGetWord, NULL, &LocalHostNameOverride, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"localhostipif", 0, eCmdHdlrGetWord, setLocalHostIPIF, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"netstreamdrivercaextrafiles", 0, eCmdHdlrGetWord, setNetstrmDrvrCAExtraFiles,
                             NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"optimizeforuniprocessor", 0, eCmdHdlrGoneAway, NULL, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"preservefqdn", 0, eCmdHdlrBinary, NULL, &bPreserveFQDN, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"maxmessagesize", 0, eCmdHdlrSize, legacySetMaxMessageSize, NULL, NULL));

    /* Deprecated parser config options */
    CHKiRet(regCfSysLineHdlr((uchar *)"controlcharacterescapeprefix", 0, eCmdHdlrGetChar,
                             setParserControlCharacterEscapePrefix, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"droptrailinglfonreception", 0, eCmdHdlrBinary,
                             setParserDropTrailingLFOnReception, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"droptrailingcronreception", 0, eCmdHdlrBinary,
                             setParserDropTrailingCROnReception, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"escapecontrolcharactersonreceive", 0, eCmdHdlrBinary,
                             setParserEscapeControlCharactersOnReceive, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"spacelfonreceive", 0, eCmdHdlrBinary, setParserSpaceLFOnReceive, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"escape8bitcharactersonreceive", 0, eCmdHdlrBinary,
                             setParserEscape8BitCharactersOnReceive, NULL, NULL));
    CHKiRet(regCfSysLineHdlr((uchar *)"escapecontrolcharactertab", 0, eCmdHdlrBinary,
                             setParserEscapeControlCharacterTab, NULL, NULL));

    CHKiRet(
        regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL));

    INIT_ATOMIC_HELPER_MUT(mutTerminateInputs);
ENDObjClassInit(glbl)


/* Exit the glbl class.
 * rgerhards, 2008-04-17
 */
BEGINObjClassExit(glbl, OBJ_IS_CORE_MODULE) /* class, version */
    free(LocalDomain);
    free(LocalHostName);
    free(LocalHostNameOverride);
    free(LocalFQDNName);
    objRelease(prop, CORE_COMPONENT);
    if (propLocalHostNameToDelete != NULL) prop.Destruct(&propLocalHostNameToDelete);
    DESTROY_ATOMIC_HELPER_MUT(mutTerminateInputs);
ENDObjClassExit(glbl)
