root/usr/src/cmd/scadm/sparc/mpxu/common/eventlog.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 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * eventlog.c: support for the scadm loghistory option (to display the
 * service processor log history)
 */

#include <libintl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>  /* required by librsc.h */

#include "librsc.h"
#include "adm.h"

#include "event_mess.h"
#define TAB '\t'
#define BACKSLASH_ESCAPE '\\'

/* #define DEBUG */

static char *
getEventLogMessage(int eventId)
{
        int     category;
        int     event;
        char    **alertCategory;
        char    *alertMessage;

        category = eventId >> 16;
        event = eventId &0x0000ffff;

        alertCategory = rsc_alerts[category];
        if (alertCategory) {
                alertMessage = alertCategory[event];
        } else {
                return (NULL);
        }

        if (alertMessage) {
                return (alertMessage);
        } else {
                return (NULL);
        }
}

/*
 * getNextEventLogParam
 *
 *      Return the next message from a TAB delimited message parameter list.
 *  Given a string message "mess1\tmess2\tmess3\t\t", this function will
 *  return a ponter to "mess2" the first time it is called.
 */
static char *
getNextEventLogParam(char *mess)
{
        char *p = mess;

        do {
                /* ESCAPE means interpret the next character literally */
                if ((p != mess) && (*(p-1) == BACKSLASH_ESCAPE)) {
                        p++;
                        continue;
                }

                if ((*p == TAB) && (*(p+1) == TAB)) {
                        /* Double tab means end of list */
                        return (NULL);
                }
                p++;

        } while (*p != TAB);

        /* return pointer to char after TAB */
        p++;
        return (p);

}

/*
 * expandEventLogMessage
 *
 *      This function will expand the base message for the category/event
 *  passed in with the TAB delimited parameters passed in via messParams.
 *      The expanded message will be returned in the buf character buffer.
 */

static int
expandEventLogMessage(int eventId, char *messParams, size_t messParamsLen,
    char *buf)
{

        char    *alertMessage;
        char    *s;
        char    *d;
        char    *param;

        /* Get Alert message from internal tables */
        alertMessage = getEventLogMessage(eventId);
        if (alertMessage == NULL) {
                (void) strcpy(buf, "Unknown alert");
                return (strlen("Unknown alert"));
        }

        /* No message parameters to copy */
        if (messParamsLen == 0) {
                (void) strcpy(buf, alertMessage);
                return (strlen(buf));
        }

        /* A %s in the base message means we expand with a parameter */
        if (strstr(alertMessage, "%s")) {
                s = alertMessage;
                d = buf;
                param = messParams;

                do {
                        if ((*s == '%') && (*(s+1) == 's')) {
                                if (param) {
                                        char *p = param;

                                        while ((*p) && (*p != TAB)) {
                                                *d++ = *p++;
                                        }
                                }
                                /* Get next parameter on list for next %s */
                                param = getNextEventLogParam(param);
                                s += 2;
                        }
                } while ((*d++ = *s++));

        } else {
                /* If no %s tokens to expand, just copy message */
                (void) strcpy(buf, alertMessage);
        }

        return (strlen(buf));

}

static void
ADM_Process_old_event_log()
{
        char                    timebuf[32];
        char                    messBuff[256];
        char                    eventMsgBuf[256];
        rscp_msg_t              Message;
        struct timespec         Timeout;
        dp_get_event_log_r_t    *rscReply;
        char                    *datap;
        dp_event_log_entry_t    entry;
        int                     i, len, entryhdrsize;

        ADM_Start();

        Message.type = DP_GET_EVENT_LOG;
        Message.len = 0;
        Message.data = NULL;
        ADM_Send(&Message);

        Timeout.tv_nsec = 0;
        Timeout.tv_sec  = ADM_TIMEOUT;
        ADM_Recv(&Message, &Timeout,
            DP_GET_EVENT_LOG_R, sizeof (*rscReply));

        /* Print the event log messages */
        rscReply = (dp_get_event_log_r_t *)Message.data;
        datap = (char *)rscReply->data;
        for (i = 0; i < rscReply->entry_count; i++) {
                entryhdrsize = sizeof (entry) - sizeof (entry.param);
                (void) memcpy(&entry, datap, entryhdrsize);
                datap += entryhdrsize;
                (void) memcpy(&entry.param, datap, entry.paramLen);
                (void) strftime(timebuf, sizeof (timebuf), "%b %d %H:%M:%S",
                    gmtime((time_t *)&entry.eventTime));
                (void) sprintf(messBuff, "%s : %08lx: \"", timebuf,
                    entry.eventId);
                len = expandEventLogMessage(entry.eventId, entry.param,
                    entry.paramLen, eventMsgBuf);
                (void) strncat(messBuff, eventMsgBuf, len);
                (void) strcat(messBuff, "\"\r\n");
                (void) printf(messBuff);
                datap += entry.paramLen;
        }

        ADM_Free(&Message);
}

static int
ADM_Process_new_event_log(int all)
{
        char                    timebuf[32];
        char                    messBuff[256];
        char                    eventMsgBuf[256];
        rscp_msg_t              Message;
        struct timespec         Timeout;
        dp_get_event_log2_r_t   *rscReply;
        char                    *datap;
        dp_event_log_entry_t    entry;
        int                     i, len, entryhdrsize, sent_ok;
        rsci64                  events_remaining, seqno;
        rsci16                  request_size, returned_events;
        dp_get_event_log2_t     rscCmd;

        ADM_Start();

        /*
         * Start by sending a zero-length request to ALOM, so that
         * we can learn the length of the console log.  We expect
         * ALOM to return the length of the entire log.  We get
         * a snapshot of the length of the log here - it may however
         * continue to grow as we're reading it.  We read only as
         * much of the log as we get in this snapshot.
         *
         * If the command fails, we quietly return failure here so
         * that the caller can re-try with the old/legacy command.
         */
        rscCmd.start_seq = 0;
        rscCmd.length = 0;
        Message.type = DP_GET_EVENT_LOG2;
        Message.len = sizeof (rscCmd);
        Message.data = (char *)&rscCmd;
        if (ADM_Send_ret(&Message) != 0) {
                return (1);
        }

        Timeout.tv_nsec = 0;
        Timeout.tv_sec  = ADM_TIMEOUT;
        ADM_Recv(&Message, &Timeout,
            DP_GET_EVENT_LOG2_R, sizeof (*rscReply));

        rscReply = (dp_get_event_log2_r_t *)Message.data;

        /*
         * Fetch an fixed number of events from the end of
         * the log if at least that many exist, and we were not
         * asked to fetch all the events.
         */
        if ((all == 0) &&
            (rscReply->remaining_log_events > DEFAULT_NUM_EVENTS)) {
                events_remaining = DEFAULT_NUM_EVENTS;
                seqno = (rscReply->remaining_log_events +
                    rscReply->next_seq) - events_remaining;
        } else {
                events_remaining = rscReply->remaining_log_events;
                seqno = rscReply->next_seq;
        }
        request_size = sizeof (rscReply->buffer);
        ADM_Free(&Message);

        /*
         * This loop runs as long as there is data in the log, or until
         * we hit the default limit (above).  It's possible that ALOM may
         * shrink the log - we need to account for this.  If ALOM returns
         * no data, we bail out.
         */
        while (events_remaining) {
                rscCmd.start_seq = seqno;
                rscCmd.length = request_size;
                Message.type = DP_GET_EVENT_LOG2;
                Message.len = sizeof (rscCmd);
                Message.data = (char *)&rscCmd;
                ADM_Send(&Message);

                Timeout.tv_nsec = 0;
                Timeout.tv_sec  = ADM_TIMEOUT;
                ADM_Recv(&Message, &Timeout,
                    DP_GET_EVENT_LOG2_R, sizeof (*rscReply));

                rscReply = (dp_get_event_log2_r_t *)Message.data;

                /* If ALOM returns zero events, we're done. */
                returned_events = rscReply->num_events;
                if (returned_events == 0) {
                        ADM_Free(&Message);
                        break;
                }

                /*
                 * if the event at the original sequence number is no
                 * longer in the log, print a message
                 */
                if (seqno + returned_events < rscReply->next_seq) {
                        printf(gettext("\nscadm: lost %d events\n"),
                            rscReply->next_seq - (seqno + returned_events));
                }

                /*
                 * get ready for next main loop iteration
                 */
                seqno = rscReply->next_seq;
                events_remaining -= returned_events;

                /* Print the event log messages */
                datap = rscReply->buffer;

                for (i = 0; i < returned_events; i++) {
                        entryhdrsize = sizeof (entry) - sizeof (entry.param);
                        (void) memcpy(&entry, datap, entryhdrsize);
                        datap += entryhdrsize;
                        (void) memcpy(&entry.param, datap, entry.paramLen);
                        (void) strftime(timebuf, sizeof (timebuf),
                            "%b %d %H:%M:%S",
                            gmtime((time_t *)&entry.eventTime));
                        (void) sprintf(messBuff, "%s : %08lx: \"", timebuf,
                            entry.eventId);
                        len = expandEventLogMessage(entry.eventId, entry.param,
                            entry.paramLen, eventMsgBuf);
                        (void) strncat(messBuff, eventMsgBuf, len);
                        (void) strcat(messBuff, "\"\r\n");
                        (void) printf(messBuff);
                        datap += entry.paramLen;
                }

                ADM_Free(&Message);
        }
        return (0);
}

void
ADM_Process_event_log(int all)
{
        if (ADM_Process_new_event_log(all) != 0) {
                ADM_Process_old_event_log();
        }
}