/*
*****************************************************************************
*
*  File: knopwatchd.c
*
*  Purpose: knopwatchd checks on an interval of every five seconds to make
*           sure that both knopmd and fwknop are running on the box.  If
*           either daemon has died, knopwatchd will restart it and notify
*           each email address in EMAIL_ADDRESSES that the daemon has been
*           restarted.
*
*  Author: Michael Rash (mbr@cipherdyne.org)
*
*  Credits:  (see the CREDITS file)
*
*  Version: 1.9.7
*
*  Copyright (C) 2004-2008 Michael Rash (mbr@cipherdyne.org)
*
*  License (GNU Public License):
*
*     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
*     along with this program; if not, write to the Free Software
*     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
*     USA
*
*****************************************************************************
*
*  $Id: knopwatchd.c 1232 2008-08-24 03:48:56Z mbr $
*/

/* includes */
#include "fwknop.h"

/* defines */
#define FWKNOP_CONF "/etc/fwknop/fwknop.conf"

/* globals */
unsigned short int fwknopd_syscalls_ctr = 0;
unsigned short int knopmd_syscalls_ctr = 0;
unsigned short int no_email = 0;
unsigned short int check_knopmd = 0;
const char mail_redr[] = " < /dev/null > /dev/null 2>&1";
char hostname[MAX_GEN_LEN];
char mail_addrs[MAX_GEN_LEN];
char shCmd[MAX_GEN_LEN];
char mailCmd[MAX_GEN_LEN];
char config_file[MAX_PATH_LEN];
char fwknop_run_dir[MAX_PATH_LEN];
char alerting_methods[MAX_GEN_LEN];
char fwknopdCmd[MAX_PATH_LEN];
char fwknopd_pid_file[MAX_PATH_LEN];
char fwknopd_cmdline_file[MAX_PATH_LEN];
char knopmdCmd[MAX_PATH_LEN];
char knoptmCmd[MAX_PATH_LEN];
char knopmd_pid_file[MAX_PATH_LEN];
char knoptm_pid_file[MAX_PATH_LEN];
char knopwatchd_pid_file[MAX_PATH_LEN];
char char_knopwatchd_check_interval[MAX_NUM_LEN];
char char_knopwatchd_max_retries[MAX_NUM_LEN];
char enable_syslog_file[MAX_GEN_LEN];
unsigned int knopwatchd_check_interval = 5;  /* default to 5 seconds */
unsigned int knopwatchd_max_retries = 10; /* default to 10 tries */

static volatile sig_atomic_t received_sighup = 0;

/* prototypes */
static void parse_config(void);
static void expand_config_vars(void);
static void find_sub_var_value(
    char *value,
    char *sub_var,
    char *pre_str,
    char *post_str
);

static void check_process(
    const char *pid_name,
    const char *pid_file,
    const char *cmdline_file,
    const char *binary_path,
    unsigned int max_retries
);
static void check_auth_mode(void);
static void incr_syscall_ctr(const char *pid_name, unsigned int max_retries);
static void reset_syscall_ctr(const char *pid_name);
static void give_up(const char *pid_name);
static void exec_binary(const char *binary_path, const char *cmdline_file);
static void sighup_handler(int sig);

/* main */
int main(int argc, char *argv[]) {
    int cmdlopt;

#ifdef DEBUG
    fprintf(stderr, "[+] Entering DEBUG mode...\n");
    sleep(1);
#endif

    strlcpy(config_file, FWKNOP_CONF, MAX_PATH_LEN);

    /* handle command line arguments */
    while((cmdlopt = getopt(argc, argv, "c:")) != -1) {
        switch(cmdlopt) {
            case 'c':
                strlcpy(config_file, optarg, MAX_PATH_LEN);
                break;
            default:
                printf("[+] Usage: knopwatchd [-c <config file>] ");
                exit(EXIT_FAILURE);
        }
    }

#ifdef DEBUG
    fprintf(stderr, "[+] parsing config_file: %s\n", config_file);
#endif

    /* parse the config file */
    parse_config();

    /* see if we are supposed to disable all email alerts */
    if (strncmp("noemail", alerting_methods, MAX_GEN_LEN) == 0)
        no_email = 1;

    /* first make sure there isn't another knopwatchd already running */
    check_unique_pid(knopwatchd_pid_file, "knopwatchd");

#ifndef DEBUG
    /* become a daemon */
    daemonize_process(knopwatchd_pid_file);
#endif

    /* install signal handler for HUP signals */
    signal(SIGHUP, sighup_handler);

    /* start doing the real work now that the daemon is running and
     * the config file has been processed */

    /* MAIN LOOP */
    for (;;) {
        /* restart processes as necessary */
        check_process("fwknopd", fwknopd_pid_file, fwknopd_cmdline_file,
            fwknopdCmd, knopwatchd_max_retries);

        check_process("knoptm", knoptm_pid_file, NULL,
            knoptmCmd, knopwatchd_max_retries);

        if (check_knopmd)
            check_process("knopmd", knopmd_pid_file, NULL,
                knopmdCmd, knopwatchd_max_retries);

        /* sleep and then check to see if we received any signals */
        sleep(knopwatchd_check_interval);

        /* check for sighup */
        if (received_sighup) {
            received_sighup = 0;
#ifdef DEBUG
    fprintf(stderr, "[+] re-parsing config file: %s\n", config_file);
#endif
            /* reparse the config file since we received a
             * HUP signal */
            parse_config();

            slogr("fwknopd(knopwatchd)",
                    "received HUP signal, re-imported fwknop.conf");
        }
    }

    /* this statement doesn't get executed, but for completeness... */
    exit(EXIT_SUCCESS);
}
/******************** end main ********************/

static void check_process(
    const char *pid_name,
    const char *pid_file,
    const char *cmdline_file,
    const char *binary_path,
    unsigned int max_retries)
{
    FILE *pidfile_ptr;
    pid_t pid;
    unsigned short int restart = 0;
    char mail_str[MAX_MSG_LEN] = "";
    char syslog_str[MAX_MSG_LEN] = "";
    char pid_line[MAX_PID_SIZE];

    if ((pidfile_ptr = fopen(pid_file, "r")) == NULL) {
#ifdef DEBUG
    fprintf(stderr, "[+] Could not open pid_file: %s\n", pid_file);
#endif
        /* the pid file must not exist (or we can't read it), so
         * setup to start the appropriate process */
        restart = 1;
    }

    /* read the first line of the pid_file, which will contain the
     * process id of any running pid_name process. */
    if (! restart) {
        if (fgets(pid_line, MAX_PID_SIZE, pidfile_ptr) == NULL) {
#ifdef DEBUG
            fprintf(stderr, "[+] Could not read the pid_file: %s\n", pid_file);
#endif
            /* see if we need to give up */
            incr_syscall_ctr(pid_name, max_retries);
            fclose(pidfile_ptr);
            return;
        }

        /* convert the pid_line into an integer */
        pid = atoi(pid_line);

        /* close the pid_file now that we have read it */
        fclose(pidfile_ptr);

        if (kill(pid, 0) != 0) {
            /* the process is not running so start it */
            restart = 1;
        }
    }

    if (restart) {
#ifdef DEBUG
        fprintf(stderr, "[+] executing exec_binary(%s)\n", binary_path);
#endif
        snprintf(mail_str, MAX_MSG_LEN,
                " -s \"[*] knopwatchd: Restarting %s on %s\" %s%s",
                pid_name, hostname, mail_addrs, mail_redr);
        mail_str[MAX_MSG_LEN-1] = '\0';

#ifdef DEBUG
        fprintf(stderr, "[+] sending mail: %s\n", mail_str);
#endif

        snprintf(syslog_str, MAX_MSG_LEN,
            "restarting %s on %s", pid_name, hostname);
        slogr("fwknopd(knopwatchd)", syslog_str);

        if (! no_email) {
            /* send the email */
            send_alert_email(shCmd, mailCmd, mail_str);
        }

        /* execute the binary_path fwknopd daemon */
        exec_binary(binary_path, cmdline_file);

        /* increment the number of times we have tried to restart the binary */
        incr_syscall_ctr(pid_name, max_retries);
    } else {
#ifdef DEBUG
        fprintf(stderr, "[+] %s is running.\n", pid_name);
#endif
        /* reset the syscall counter since the process is successfully
         * running. */
        reset_syscall_ctr(pid_name);
    }
    return;
}

static void incr_syscall_ctr(const char *pid_name, unsigned int max_retries)
{
    if (strcmp("fwknopd", pid_name) == 0) {
        fwknopd_syscalls_ctr++;
#ifdef DEBUG
        fprintf(stderr,
            "[+] %s not running.  Trying to restart (%d tries so far).\n",
            pid_name, fwknopd_syscalls_ctr);
#endif
        if (fwknopd_syscalls_ctr >= max_retries)
            give_up(pid_name);
    } else if (strcmp("knopmd", pid_name) == 0) {
        knopmd_syscalls_ctr++;
#ifdef DEBUG
        fprintf(stderr,
            "[+] %s not running.  Trying to restart (%d tries so far).\n",
            pid_name, knopmd_syscalls_ctr);
#endif
        if (knopmd_syscalls_ctr >= max_retries)
            give_up(pid_name);
    }
    return;
}

static void reset_syscall_ctr(const char *pid_name)
{
    if (strcmp("fwknopd", pid_name) == 0) {
        fwknopd_syscalls_ctr = 0;
    } else if (strcmp("knopmd", pid_name) == 0) {
        knopmd_syscalls_ctr = 0;
    }
    return;
}

static void give_up(const char *pid_name)
{
    char mail_str[MAX_MSG_LEN] = "";
#ifdef DEBUG
    fprintf(stderr, "[*] Could not restart %s process.  Exiting.\n", pid_name);
#endif
    snprintf(mail_str, MAX_MSG_LEN,
            " -s \"[*] knopwatchd: Could not restart %s on %s. Exiting.\" %s%s",
            pid_name, hostname, mail_addrs, mail_redr);
    mail_str[MAX_MSG_LEN-1] = '\0';

    if (! no_email) {
        /* Send the email */
        send_alert_email(shCmd, mailCmd, mail_str);
    }
    exit(EXIT_FAILURE);
}

static void exec_binary(const char *binary, const char *cmdlinefile)
{
    FILE *cmdline_ptr;
    char *prog_argv[MAX_ARG_LEN];
    char cmdline_buf[MAX_LINE_BUF];
    char *index;
    pid_t child_pid;
    int arg_num=0, non_ws, i;

    prog_argv[arg_num] = (char *) safe_malloc(strlen(binary)+1);
    if (prog_argv[arg_num] == NULL) {
        exit(EXIT_FAILURE);
    }
    strlcpy(prog_argv[arg_num], binary, strlen(binary)+1);
    arg_num++;

    if (cmdlinefile != NULL) {
        /* restart binary with its command line arguments intact */
        if ((cmdline_ptr = fopen(cmdlinefile, "r")) == NULL) {
            exit(EXIT_FAILURE);
        }
        if ((fgets(cmdline_buf, MAX_LINE_BUF, cmdline_ptr)) == NULL) {
            exit(EXIT_FAILURE);
        }
        fclose(cmdline_ptr);

        /* initialize index to the beginning of the line */
        index = cmdline_buf;

        /* advance the index pointer through any whitespace
         * at the beginning of the line */
        while (*index == ' ' || *index == '\t') index++;

        while (*index != '\n' && *index != '\0') {
            non_ws = 0;
            while (*index != ' ' && *index != '\t'
                    && index != '\0' && *index != '\n') {
                index++;
                non_ws++;
            }
            prog_argv[arg_num] = (char *) safe_malloc(non_ws+1);
            if (prog_argv[arg_num] == NULL) {
                exit(EXIT_FAILURE);
            }
            for (i=0; i<non_ws; i++)
                prog_argv[arg_num][i] = *(index - (non_ws - i));
            prog_argv[arg_num][i] = '\0';

            arg_num++;

            /* get past any whitespace */
            while (*index == ' ' || *index == '\t') index++;
        }
    }

    if (arg_num >= MAX_ARG_LEN)
        exit(EXIT_FAILURE);
    prog_argv[arg_num] = NULL;

    if ((child_pid = fork()) < 0)
        /* could not fork */
        exit(EXIT_FAILURE);
    else if (child_pid > 0) {
        wait(NULL);
        for (i=0; i<=arg_num; i++) {
            free(prog_argv[i]);
        }
    } else {
#ifdef DEBUG
        fprintf(stderr, "[+] restarting %s\n", binary);
#endif
        execve(binary, prog_argv, NULL);  /* don't use environment */
    }
    return;
}

static void parse_config(void)
{
    FILE *config_ptr;         /* FILE pointer to the config file */
    int linectr = 0;
    char config_buf[MAX_LINE_BUF];
    char char_knopwatchd_check_interval[MAX_NUM_LEN];
    char char_knopwatchd_max_retries[MAX_NUM_LEN];
    char *index;

    /* first check to see if knopmd should not be running (i.e.
     * AUTH_MODE in the fwknop.conf file is set to KNOCK).
     * This will set check_knopmd appropriately */
    check_auth_mode();

    if ((config_ptr = fopen(config_file, "r")) == NULL) {
        perror("[*] Could not open config file");
        exit(EXIT_FAILURE);
    }

    /* increment through each line of the config file */
    while ((fgets(config_buf, MAX_LINE_BUF, config_ptr)) != NULL) {
        linectr++;
        index = config_buf;  /* set the index pointer to the
                                beginning of the line */

        /* advance the index pointer through any whitespace
         * at the beginning of the line */
        while (*index == ' ' || *index == '\t') index++;

        /* skip comments and blank lines, etc. */
        if ((*index != '#') && (*index != '\n') &&
                (*index != ';') && (index != NULL)) {

            find_char_var("fwknopdCmd ", fwknopdCmd, index);
            find_char_var("HOSTNAME ", hostname, index);
            find_char_var("FWKNOP_RUN_DIR", fwknop_run_dir, index);
            find_char_var("FWKNOP_PID_FILE ", fwknopd_pid_file, index);
            find_char_var("FWKNOP_CMDLINE_FILE ", fwknopd_cmdline_file, index);
            find_char_var("knopmdCmd ", knopmdCmd, index);
            find_char_var("knoptmCmd ", knoptmCmd, index);
            find_char_var("KNOPMD_PID_FILE ", knopmd_pid_file, index);
            find_char_var("KNOPTM_PID_FILE ", knoptm_pid_file, index);
            find_char_var("shCmd ", shCmd, index);
            find_char_var("mailCmd ", mailCmd, index);
            find_char_var("EMAIL_ADDRESSES ", mail_addrs, index);
            find_char_var("KNOPWATCHD_CHECK_INTERVAL ",
                char_knopwatchd_check_interval, index);
            find_char_var("KNOPWATCHD_MAX_RETRIES ",
                char_knopwatchd_max_retries, index);
            find_char_var("ENABLE_SYSLOG_FILE", enable_syslog_file, index);
            find_char_var("KNOPWATCHD_PID_FILE ", knopwatchd_pid_file, index);
            find_char_var("ALERTING_METHODS ", alerting_methods, index);
        }
    }
    fclose(config_ptr);

    if (fwknopdCmd[0] == '\0') {
        fprintf(stderr, "[*] Could not get fwknopdCmd from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (hostname[0] == '\0') {
        fprintf(stderr, "[*] Could not get HOSTNAME from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (fwknopd_pid_file[0] == '\0') {
        fprintf(stderr, "[*] Could not get FWKNOP_PID_FILE from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (fwknopd_cmdline_file[0] == '\0') {
        fprintf(stderr, "[*] Could not get FWKNOP_CMDLINE_FILE from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (knopmdCmd[0] == '\0') {
        fprintf(stderr, "[*] Could not get knopmdCmd from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (knoptmCmd[0] == '\0') {
        fprintf(stderr, "[*] Could not get knoptmCmd from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (knopmd_pid_file[0] == '\0') {
        fprintf(stderr, "[*] Could not get KNOPMD_PID_FILE from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (knoptm_pid_file[0] == '\0') {
        fprintf(stderr, "[*] Could not get KNOPTM_PID_FILE from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (shCmd[0] == '\0') {
        fprintf(stderr, "[*] Could not get shCmd from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (mailCmd[0] == '\0') {
        fprintf(stderr, "[*] Could not get mailCmd from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (mail_addrs[0] == '\0') {
        fprintf(stderr, "[*] Could not get EMAIL_ADDRESSES from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (char_knopwatchd_check_interval[0] == '\0') {
        fprintf(stderr, "[*] Could not get KNOPWATCHD_CHECK_INTERVAL from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (char_knopwatchd_max_retries[0] == '\0') {
        fprintf(stderr, "[*] Could not get KNOPWATCHD_MAX_RETRIES from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }
    if (knopwatchd_pid_file[0] == '\0') {
        fprintf(stderr, "[*] Could not get KNOPWATCHD_PID_FILE from %s\n",
                config_file);
        exit(EXIT_FAILURE);
    }

    /* resolve any embedded variables */
    expand_config_vars();

    knopwatchd_check_interval = atoi(char_knopwatchd_check_interval);
    knopwatchd_max_retries    = atoi(char_knopwatchd_max_retries);

    return;
}

static void expand_config_vars(void)
{
    char sub_var[MAX_GEN_LEN]  = "";
    char pre_str[MAX_GEN_LEN]  = "";
    char post_str[MAX_GEN_LEN] = "";
    int found_sub_var = 1, resolve_ctr = 0;

    while (found_sub_var) {
        resolve_ctr++;
        if (resolve_ctr >= 20) {
            fprintf(stderr, "[*] Exceeded maximum variable resolution attempts.\n");
            exit(EXIT_FAILURE);
        }
        found_sub_var = 0;
        if (has_sub_var("EMAIL_ADDRESSES", mail_addrs, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(mail_addrs, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("HOSTNAME", hostname, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(hostname, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("FWKNOP_RUN_DIR", fwknop_run_dir, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(fwknop_run_dir, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("FWKNOP_PID_FILE", fwknopd_pid_file, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(fwknopd_pid_file, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("FWKNOP_CMDLINE_FILE", fwknopd_cmdline_file, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(fwknopd_cmdline_file, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("KNOPMD_PID_FILE", knopmd_pid_file, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(knopmd_pid_file, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("KNOPTM_PID_FILE", knoptm_pid_file, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(knoptm_pid_file, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("KNOPWATCHD_PID_FILE", knopwatchd_pid_file, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(knopwatchd_pid_file, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("KNOPWATCHD_CHECK_INTERVAL",
                char_knopwatchd_check_interval, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(char_knopwatchd_check_interval,
                sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("KNOPWATCHD_MAX_RETRIES", char_knopwatchd_max_retries,
                sub_var, pre_str, post_str)) {
            find_sub_var_value(char_knopwatchd_max_retries,
                sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("mailCmd", mailCmd, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(mailCmd, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("shCmd", shCmd, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(shCmd, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("knopmdCmd", knopmdCmd, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(knopmdCmd, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }

        if (has_sub_var("fwknopdCmd", fwknopdCmd, sub_var,
                pre_str, post_str)) {
            find_sub_var_value(fwknopdCmd, sub_var, pre_str, post_str);
            found_sub_var = 1;
        }
    }

    return;
}

static void find_sub_var_value(char *value, char *sub_var, char *pre_str,
    char *post_str)
{
    int found_var = 0;
    if (strncmp(sub_var, "EMAIL_ADDRESSES", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, mail_addrs, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "HOSTNAME", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, hostname, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "FWKNOP_RUN_DIR", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, fwknop_run_dir, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "FWKNOP_PID_FILE", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, fwknopd_pid_file, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "FWKNOP_CMDLINE_FILE", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, fwknopd_cmdline_file, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "KNOPMD_PID_FILE", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, knopmd_pid_file, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "KNOPTM_PID_FILE", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, knoptm_pid_file, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "KNOPWATCHD_PID_FILE", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, knopwatchd_pid_file, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "KNOPWATCDHD_CHECK_INTERVAL", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, char_knopwatchd_check_interval, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "KNOPWATCDHD_MAX_RETRIES", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, char_knopwatchd_max_retries, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "mailCmd", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, mailCmd, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "shCmd", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, shCmd, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "knopmdCmd", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, knopmdCmd, MAX_GEN_LEN);
        found_var = 1;
    } else if (strncmp(sub_var, "fwknopdCmd", MAX_GEN_LEN) == 0) {
        strlcpy(sub_var, fwknopdCmd, MAX_GEN_LEN);
        found_var = 1;
    }

    if (found_var)

        /* substitute the variable value */
        expand_sub_var_value(value, sub_var, pre_str, post_str);

    else {
        fprintf(stderr, "[*] Could not resolve sub-var: %s to a value.\n",
            sub_var);
        exit(EXIT_FAILURE);
    }
    return;
}

static void check_auth_mode(void)
{
    FILE *config_ptr;   /* FILE pointer to the config file */
    char config_buf[MAX_LINE_BUF];
    char auth_mode[MAX_GEN_LEN];
    char *index;

    if ((config_ptr = fopen(FWKNOP_CONF, "r")) == NULL) {
        fprintf(stderr, "[-] Could not open %s for reading.\n",
            FWKNOP_CONF);
        exit(EXIT_FAILURE);
    }

    auth_mode[0] = '\0';

    /* increment through each line of the config file */
    while ((fgets(config_buf, MAX_LINE_BUF, config_ptr)) != NULL) {
        /* set the index pointer to the beginning of the line */
        index = config_buf;

        /* advance the index pointer through any whitespace
         * at the beginning of the line */
        while (*index == ' ' || *index == '\t') index++;

        /* skip comments and blank lines, etc. */
        if ((*index != '#') && (*index != '\n') &&
                (*index != ';') && (index != NULL)) {

            find_char_var("AUTH_MODE ", auth_mode, index);
        }
    }
    fclose(config_ptr);

    /* see if we are using the KNOCK mode */
    if (strncmp(auth_mode, "KNOCK", MAX_GEN_LEN) == 0)
        check_knopmd = 1;

    if (check_knopmd) {  /* PK mode */
        /* see if ENABLE_SYSLOG_FILE is enabled, so fwknopd is just parsing
         * a file written to by syslog directly */
        if (strncmp(enable_syslog_file, "Y", 1) == 0)
            check_knopmd = 0;
    }

    return;
}

static void sighup_handler(int sig)
{
    received_sighup = 1;
}
