/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
 * Copyright (c) 2004-2008 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2005 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, 
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * Copyright (c) 2015-2017 Los Alamos National Security, LLC. All rights
 *                         reserved.
 * Copyright (c) 2017      Intel, Inc.  All rights reserved.
 * $COPYRIGHT$
 * 
 * Additional copyrights may follow
 * 
 * $HEADER$
 *
 * These symbols are in a file by themselves to provide nice linker
 * semantics.  Since linkers generally pull in symbols by object
 * files, keeping these symbols as the only symbols in this file
 * prevents utility programs such as "ompi_info" from having to import
 * entire components just to query their version and parameters.
 */

#include "orte_config.h"
#include "orte/constants.h"

#include "orte/util/proc_info.h"
#include "orte/util/show_help.h"

#include "orte/mca/ess/ess.h"
#include "orte/mca/ess/hnp/ess_hnp.h"
#include "orte/runtime/orte_globals.h"

extern orte_ess_base_module_t orte_ess_hnp_module;
static int hnp_component_register (void);
static int hnp_component_open(void);
static int hnp_component_close(void);
static int hnp_component_query(mca_base_module_t **module, int *priority);

struct known_signal {
    /** signal number */
    int signal;
    /** signal name */
    char *signame;
    /** can this signal be forwarded */
    bool can_forward;
};

static struct known_signal known_signals[] = {
    {SIGTERM, "SIGTERM", false},
    {SIGHUP, "SIGHUP", false},
    {SIGINT, "SIGINT", false},
    {SIGKILL, "SIGKILL", false},
#ifdef SIGSYS
    {SIGSYS, "SIGSYS", true},
#endif
#ifdef SIGXCPU
    {SIGXCPU, "SIGXCPU", true},
#endif
    {SIGXFSZ, "SIGXFSZ", true},
#ifdef SIGVTALRM
    {SIGVTALRM, "SIGVTALRM", true},
#endif
#ifdef SIGPROF
    {SIGPROF, "SIGPROF", true},
#endif
#ifdef SIGINFO
    {SIGINFO, "SIGINFO", true},
#endif
#ifdef SIGPWR
    {SIGPWR, "SIGPWR", true},
#endif
    {0, NULL},
};

/*
 * Instantiate the public struct with all of our public information
 * and pointers to our public functions in it
 */
orte_ess_hnp_component_t mca_ess_hnp_component = {
    .base = {
        {
            ORTE_ESS_BASE_VERSION_3_0_0,

            /* Component name and version */
            "hnp",
            ORTE_MAJOR_VERSION,
            ORTE_MINOR_VERSION,
            ORTE_RELEASE_VERSION,

            /* Component open and close functions */
            hnp_component_open,
            hnp_component_close,
            hnp_component_query,
            hnp_component_register,
        },
        {
            /* The component is checkpoint ready */
            MCA_BASE_METADATA_PARAM_CHECKPOINT
        }
    }
};

static char *additional_signals;

static int hnp_component_register (void)
{
    additional_signals = NULL;
    (void) mca_base_component_var_register (&mca_ess_hnp_component.base.base_version,
                                            "forward_signals", "Comma-delimited list "
                                            "of additional signals (names or integers) to forward to "
                                            "application processes [\"none\" => forward nothing]", MCA_BASE_VAR_TYPE_STRING,
                                            NULL, 0, 0, OPAL_INFO_LVL_4, MCA_BASE_VAR_SCOPE_READONLY,
                                            &additional_signals);

    return ORTE_SUCCESS;
}

#define ESS_ADDSIGNAL(x, s)                                                 \
    do {                                                                    \
        ess_hnp_signal_t *_sig;                                             \
        _sig = OBJ_NEW(ess_hnp_signal_t);                                   \
        _sig->signal = (x);                                                 \
        _sig->signame = strdup((s));                                        \
        opal_list_append(&mca_ess_hnp_component.signals, &_sig->super);     \
    } while(0)

static int hnp_component_open(void)
{
    int i, sval;
    char **signals, *tmp;
    ess_hnp_signal_t *sig;
    bool ignore, found;

    OBJ_CONSTRUCT(&mca_ess_hnp_component.signals, opal_list_t);

    /* we know that some signals are (nearly) always defined, regardless
     * of environment, so add them here */
    ESS_ADDSIGNAL(SIGTSTP, "SIGTSTP");
    ESS_ADDSIGNAL(SIGUSR1, "SIGUSR1");
    ESS_ADDSIGNAL(SIGUSR2, "SIGUSR2");
    ESS_ADDSIGNAL(SIGABRT, "SIGABRT");
    ESS_ADDSIGNAL(SIGALRM, "SIGALRM");
    ESS_ADDSIGNAL(SIGCONT, "SIGCONT");
#ifdef SIGURG
    ESS_ADDSIGNAL(SIGURG, "SIGURG");
#endif

    /* see if they asked for anything beyond those - note that they may
     * have asked for some we already cover, and so we ignore any duplicates */
    if (NULL != additional_signals) {
        /* if they told us "none", then dump the list */
        if (0 == strcmp(additional_signals, "none")) {
            OPAL_LIST_DESTRUCT(&mca_ess_hnp_component.signals);
            /* need to reconstruct it for when we close */
            OBJ_CONSTRUCT(&mca_ess_hnp_component.signals, opal_list_t);
            return ORTE_SUCCESS;
        }
        signals = opal_argv_split(additional_signals, ',');
        for (i=0; NULL != signals[i]; i++) {
            sval = 0;
            if (0 != strncmp(signals[i], "SIG", 3)) {
                /* treat it like a number */
                errno = 0;
                sval = strtoul(signals[i], &tmp, 10);
                if (0 != errno || '\0' != *tmp) {
                    orte_show_help("help-ess-hnp.txt", "ess-hnp:unknown-signal",
                                   true, signals[i], additional_signals);
                    opal_argv_free(signals);
                    return OPAL_ERR_SILENT;
                }
            }

            /* see if it is one we already covered */
            ignore = false;
            OPAL_LIST_FOREACH(sig, &mca_ess_hnp_component.signals, ess_hnp_signal_t) {
                if (0 == strcasecmp(signals[i], sig->signame) || sval == sig->signal) {
                    /* got it - we will ignore */
                    ignore = true;
                    break;
                }
            }

            if (ignore) {
                continue;
            }

            /* see if they gave us a signal name */
            found = false;
            for (int j = 0 ; known_signals[j].signame ; ++j) {
                if (0 == strcasecmp (signals[i], known_signals[j].signame) || sval == known_signals[j].signal) {
                    if (!known_signals[j].can_forward) {
                        orte_show_help("help-ess-hnp.txt", "ess-hnp:cannot-forward",
                                       true, known_signals[j].signame, additional_signals);
                        opal_argv_free(signals);
                        return OPAL_ERR_SILENT;
                    }
                    found = true;
                    ESS_ADDSIGNAL(known_signals[j].signal, known_signals[j].signame);
                    break;
                }
            }

            if (!found) {
                if (0 == strncmp(signals[i], "SIG", 3)) {
                    orte_show_help("help-ess-hnp.txt", "ess-hnp:unknown-signal",
                                   true, signals[i], additional_signals);
                    opal_argv_free(signals);
                    return OPAL_ERR_SILENT;
                }

                ESS_ADDSIGNAL(sval, signals[i]);
            }
        }
        opal_argv_free (signals);
    }

    return ORTE_SUCCESS;
}

int hnp_component_query(mca_base_module_t **module, int *priority)
{

    /* we are the hnp module - we need to be selected
     * IFF we are designated as the hnp
     */
    if (ORTE_PROC_IS_HNP) {
        *priority = 100;
        *module = (mca_base_module_t *)&orte_ess_hnp_module;
        return ORTE_SUCCESS;
    }
    
    /* else, we are not */
    *priority = -1;
    *module = NULL;
    return ORTE_ERROR;
}


static int hnp_component_close(void)
{
    return ORTE_SUCCESS;
}

/* instantiate the class */
static void scon(ess_hnp_signal_t *t)
{
    t->signame = NULL;
}
static void sdes(ess_hnp_signal_t *t)
{
    if (NULL != t->signame) {
        free(t->signame);
    }
}
OBJ_CLASS_INSTANCE(ess_hnp_signal_t,
                   opal_list_item_t,
                   scon, sdes);
