root/usr/src/lib/cfgadm_plugins/sbd/common/ap_msg.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <macros.h>
#include <errno.h>
#include <locale.h>
#include <libdevinfo.h>
#include <librcm.h>
#define CFGA_PLUGIN_LIB
#include <config_admin.h>
#include "ap.h"

#ifdef  SBD_DEBUG

static FILE *debug_fp;

int
debugging(void)
{
        char *ep;
        static int inited;

        if (inited)
                return (debug_fp != NULL);
        inited = 1;

        if ((ep = getenv("SBD_DEBUG")) == NULL)
                return (0);

        if (*ep == '\0')
                debug_fp = stderr;
        else {
                if ((debug_fp = fopen(ep, "a")) == NULL)
                        return (0);
        }
        (void) fprintf(debug_fp, "\nDebug started, pid=%d\n", (int)getpid());
        return (1);
}

/*PRINTFLIKE1*/
void
dbg(char *fmt, ...)
{
        va_list ap;

        if (!debugging())
                return;

        va_start(ap, fmt);
        (void) vfprintf(debug_fp, fmt, ap);
        va_end(ap);
}
#endif

static char *
ap_err_fmts[] = {
        "command invalid: %s",
        "%s %s: %s%s%s",                        /* command failed */
        "%s %s",                                /* command nacked */
        "command not supported: %s %s",
        "command aborted: %s %s",
        "option invalid: %s",
        "option requires value: %s",
        "option requires no value: %s",
        "option value invalid: %s %s",
        "attachment point invalid: %s",
        "component invalid: %s",
        "sequence invalid: %s (%s %s) %s",
        "change signal disposition failed",
        "cannot get RCM handle",
        "RCM %s failed for %s",
        "\n%-30s %-10s %s",
        "cannot open %s%s%s",
        "cannot find symbol %s in %s",
        "cannot stat %s: %s",
        "not enough memory",
        "%s plugin: %s",
        "unknown error",
        NULL
};

#define ap_err_fmt(i)           ap_err_fmts[min((uint_t)(i), ERR_NONE)]

static char *
ap_msg_fmts[] = {
        "%s %s\n",
        "%s %s skipped\n",
        "System may be temporarily suspended, proceed",
        "%s %s aborted\n",
        "%s %s done\n",
        "%s %s failed\n",
        "RCM library not found, feature will be disabled\n",
        "Unknown message\n",
        NULL
};

#define ap_msg_fmt(i)           ap_msg_fmts[min((uint_t)(i), MSG_NONE)]

#define STR_BD                  "board"
#define STR_SEP                 ": "
#define STR_NULL                "NULL"
#define STR_CMD_UNKNOWN         "unknown command"
#define STR_ERR_UNKNOWN         "unknown error"
#define STR_MSG_UNKNOWN         "unknown message\n"
#define STR_TGT_UNKNOWN         "unknown target"

#define get_cmd(c, ap, v) \
{ \
        (v) = va_arg((ap), int); \
        if (((c) = ap_cmd_name((v))) == NULL) \
                (c) = STR_CMD_UNKNOWN; \
}
#define get_tgt(t, ap) {\
        (t) = va_arg((ap), char *); \
        if (!str_valid((t))) \
                (t) = STR_TGT_UNKNOWN; \
}
#define check_tgt(tgt, t) {\
        if (str_valid((tgt))) \
                (t) = (tgt); \
        else \
                (t) = STR_TGT_UNKNOWN; \
}
#define get_str(v, ap, d) \
{ \
        (v) = va_arg((ap), char *); \
        if ((v) == NULL) \
                (v) = (d); \
}

static char *
ap_stnames[] = {
        "unknown state",
        "empty",
        "disconnected",
        "connected",
        "unconfigured",
        "configured"
};

/*
 * ap_err() accepts a variable number of message IDs and constructs
 * a corresponding error string.  ap_err() calls dgettext() to
 * internationalize the proper portions of a message.  If a system
 * error was encountered (errno set), ap_err() looks for the error
 * string corresponding to the returned error code if one is available.
 * If not, the standard libc error string is fetched.
 */
void
ap_err(apd_t *a, ...)
{
        int v;
        int err;
        int len;
        char *p;
        char *sep;
        char *rsep;
        const char *fmt;
        char *cmd;
        char *value;
        char *target;
        char *serr;
        char *syserr;
        char *rstate;
        char *ostate;
        char *srsrc;
        char *sysrsrc;
        char *option;
        char *path;
        char *sym;
        char *msg;
        const char *error;
        char **errstring;
        char *rinfostr = NULL;
        va_list ap;

        DBG("ap_err(%p)\n", (void *)a);

        /*
         * If there is no descriptor or string pointer or if
         * there is an outstanding error, just return.
         */
        if (a == NULL || (errstring = a->errstring) == NULL ||
            *errstring != NULL)
                return;

        va_start(ap, a);

        err = va_arg(ap, int);

        if ((fmt = ap_err_fmt(err)) == NULL)
                fmt = STR_ERR_UNKNOWN;
        fmt = dgettext(TEXT_DOMAIN, fmt);
        len = strlen(fmt);

        sep = "";
        serr = NULL;
        srsrc = NULL;
        error = NULL;

        /*
         * Get the proper arguments for the error.
         */
        switch (err) {
        case ERR_CMD_ABORT:
        case ERR_CMD_FAIL:
        case ERR_CMD_NACK:
                get_cmd(cmd, ap, v);
                check_tgt(a->target, target);
                len += strlen(cmd) + strlen(target);
                DBG("<%s><%s>", cmd, target);
                break;
        case ERR_CMD_NOTSUPP:
                get_cmd(cmd, ap, v);
                if (a->tgt == AP_BOARD)
                        target = STR_BD;
                else
                        check_tgt(a->cname, target);
                len += strlen(cmd) + strlen(target);
                DBG("<%s><%s>", cmd, target);
                break;
        case ERR_AP_INVAL:
                check_tgt((char *)a->apid, target);
                len += strlen(target);
                DBG("<%s>", target);
                break;
        case ERR_CMD_INVAL:
        case ERR_CM_INVAL:
        case ERR_OPT_INVAL:
        case ERR_OPT_NOVAL:
        case ERR_OPT_VAL:
        case ERR_OPT_BADVAL:
                get_str(option, ap, STR_NULL);
                len += strlen(option);
                DBG("<%s>", option);
                if (err != ERR_OPT_BADVAL)
                        break;
                get_str(value, ap, STR_NULL);
                len += strlen(value);
                DBG("<%s>", value);
                break;
        case ERR_TRANS_INVAL: {
                cfga_stat_t rs, os;

                get_cmd(cmd, ap, v);
                check_tgt(a->target, target);
                len += strlen(cmd) + strlen(target);
                ap_state(a, &rs, &os);
                rstate = ap_stnames[rs];
                ostate = ap_stnames[os];
                len += strlen(rstate) + strlen(ostate);
                DBG("<%s><%s><%s><%s>", cmd, target, rstate, ostate);
                break;
        }
        case ERR_RCM_CMD: {

                get_cmd(cmd, ap, v);
                check_tgt(a->target, target);
                len += strlen(cmd) + strlen(target);
                DBG("<%s><%s>", cmd, target);

                if ((ap_rcm_info(a, &rinfostr) == 0) && (rinfostr != NULL)) {
                        len += strlen(rinfostr);
                }

                break;
        }
        case ERR_LIB_OPEN:
                get_str(path, ap, STR_NULL);
                get_str(error, ap, "");
                if (str_valid(error))
                        sep = STR_SEP;
                DBG("<%s><%s>", path, error);
                break;
        case ERR_LIB_SYM:
                get_str(path, ap, STR_NULL);
                get_str(sym, ap, STR_NULL);
                DBG("<%s><%s>", path, sym);
                break;
        case ERR_STAT:
                get_str(path, ap, STR_NULL);
                break;
        case ERR_PLUGIN:
                get_str(msg, ap, STR_NULL);
                break;
        default:
                DBG("<NOARGS>");
                break;
        }

        va_end(ap);

        /*
         * In case of a system error, get the reason for
         * the failure as well as the resource if availbale.
         * If we already got some error info (e.g. from RCM)
         * don't bother looking.
         */
        if (!str_valid(error) && errno) {
                sep = STR_SEP;
                sysrsrc = NULL;
                if ((syserr = ap_sys_err(a, &sysrsrc)) == NULL)
                        syserr = STR_ERR_UNKNOWN;
                else
                        serr = syserr;

                syserr = dgettext(TEXT_DOMAIN, syserr);

                if (sysrsrc == NULL)
                        sysrsrc = "";
                else
                        srsrc = sysrsrc;

                len += strlen(syserr) + strlen(sysrsrc);

                if (str_valid(sysrsrc)) {
                        rsep = STR_SEP;
                        len += strlen(rsep);
                } else
                        rsep = "";

                DBG("<%s><%s><%s>", syserr, rsep, sysrsrc);

        } else
                syserr = rsep = sysrsrc = "";

        DBG("\n");

        if ((p = (char *)calloc(len, 1)) != NULL)
                *errstring = p;

        /*
         * Print the string with appropriate arguments.
         */
        switch (err) {
        case ERR_CMD_FAIL:
                (void) snprintf(p, len, fmt, cmd, target,
                    syserr, rsep, sysrsrc);
                break;
        case ERR_CMD_ABORT:
        case ERR_CMD_NACK:
        case ERR_CMD_NOTSUPP:
                (void) snprintf(p, len, fmt, cmd, target);
                break;
        case ERR_AP_INVAL:
                (void) snprintf(p, len, fmt, target);
                break;
        case ERR_CMD_INVAL:
        case ERR_CM_INVAL:
        case ERR_OPT_INVAL:
        case ERR_OPT_NOVAL:
        case ERR_OPT_VAL:
                (void) snprintf(p, len, fmt, option);
                break;
        case ERR_OPT_BADVAL:
                (void) snprintf(p, len, fmt, option, value);
                break;
        case ERR_TRANS_INVAL:
                (void) snprintf(p, len, fmt, cmd, rstate, ostate, target);
                break;
        case ERR_SIG_CHANGE:
        case ERR_RCM_HANDLE:
                (void) snprintf(p, len, fmt);
                break;
        case ERR_RCM_CMD:
                /*
                 * If the rinfostr has a string, then the librcm has returned
                 * us a text field of its reasons why the command failed.
                 *
                 * If the rinfostr is not returning data, we will use
                 * the standard ap_err_fmts[] for the rcm error.
                 */
                if (rinfostr != NULL)
                        (void) snprintf(p, len, "%s", rinfostr);
                else
                        (void) snprintf(p, len, fmt, cmd, target);
                break;
        case ERR_LIB_OPEN:
                (void) snprintf(p, len, fmt, path, sep, error);
                break;
        case ERR_LIB_SYM:
                (void) snprintf(p, len, fmt, sym, path);
                break;
        case ERR_STAT:
                (void) snprintf(p, len, fmt, path, syserr);
                break;
        case ERR_NOMEM:
                (void) snprintf(p, len, fmt);
                break;
        case ERR_PLUGIN:
                (void) snprintf(p, len, fmt, a->class, msg);
                break;
        default:
                break;
        }

        if (serr)
                free(serr);
        if (srsrc)
                free(srsrc);
}

/*
 * ap_msg() accepts a variable number of message IDs and constructs
 * a corresponding message string which is printed via the message print
 * routine argument.  ap_msg() internationalizes the appropriate portion
 * of the message.
 */
void
ap_msg(apd_t *a, ...)
{
        int v;
        int len;
        char *p;
        const char *fmt;
        char *cmd;
        char *target;
        struct cfga_msg *msgp;
        va_list ap;

        DBG("ap_msg(%p)\n", (void *)a);

        if (a == NULL || ap_getopt(a, OPT_VERBOSE) == 0)
                return;

        msgp = a->msgp;

        if (msgp == NULL || msgp->message_routine == NULL)
                return;

        va_start(ap, a);

        v = va_arg(ap, int);

        if ((fmt = ap_msg_fmt(v)) == NULL)
                fmt = STR_MSG_UNKNOWN;
        fmt = dgettext(TEXT_DOMAIN, fmt);
        len = strlen(fmt) + 128;        /* slop */

        DBG("<%d>", v);

        switch (v) {
        case MSG_ISSUE:
        case MSG_SKIP:
        case MSG_ABORT:
        case MSG_FAIL:
        case MSG_DONE:
                get_cmd(cmd, ap, v);
                get_tgt(target, ap);
                DBG("<%s><%s>\n", cmd, target);
                len += strlen(cmd) + strlen(target);
                break;
        default:
                break;
        }

        va_end(ap);

        if ((p = (char *)calloc(len, 1)) == NULL)
                return;

        (void) snprintf(p, len, fmt, cmd, target);

        (*msgp->message_routine)(msgp->appdata_ptr, p);
        free(p);
}

int
ap_confirm(apd_t *a)
{
        int rc;
        char *msg;
        struct cfga_confirm *confp;

        if (a == NULL)
                return (0);

        confp = a->confp;

        if (confp == NULL || confp->confirm == NULL)
                return (0);

        msg = dgettext(TEXT_DOMAIN, ap_msg_fmt(MSG_SUSPEND));

        rc = (*confp->confirm)(confp->appdata_ptr, msg);

        return (rc);
}