1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * eventlog.c: support for the scadm loghistory option (to display the
31 * service processor log history)
32 */
33
34#include <libintl.h>
35#include <stdio.h>
36#include <string.h>
37#include <time.h>  /* required by librsc.h */
38
39#include "librsc.h"
40#include "adm.h"
41
42#include "event_mess.h"
43#define	TAB '\t'
44#define	BACKSLASH_ESCAPE '\\'
45
46/* #define DEBUG */
47
48static char *
49getEventLogMessage(int eventId)
50{
51	int	category;
52	int	event;
53	char	**alertCategory;
54	char	*alertMessage;
55
56	category = eventId >> 16;
57	event = eventId &0x0000ffff;
58
59	alertCategory = rsc_alerts[category];
60	if (alertCategory) {
61		alertMessage = alertCategory[event];
62	} else {
63		return (NULL);
64	}
65
66	if (alertMessage) {
67		return (alertMessage);
68	} else {
69		return (NULL);
70	}
71}
72
73/*
74 * getNextEventLogParam
75 *
76 *	Return the next message from a TAB delimited message parameter list.
77 *  Given a string message "mess1\tmess2\tmess3\t\t", this function will
78 *  return a ponter to "mess2" the first time it is called.
79 */
80static char *
81getNextEventLogParam(char *mess)
82{
83	char *p = mess;
84
85	do {
86		/* ESCAPE means interpret the next character literally */
87		if ((p != mess) && (*(p-1) == BACKSLASH_ESCAPE)) {
88			p++;
89			continue;
90		}
91
92		if ((*p == TAB) && (*(p+1) == TAB)) {
93			/* Double tab means end of list */
94			return (NULL);
95		}
96		p++;
97
98	} while (*p != TAB);
99
100	/* return pointer to char after TAB */
101	p++;
102	return (p);
103
104}
105
106/*
107 * expandEventLogMessage
108 *
109 *	This function will expand the base message for the category/event
110 *  passed in with the TAB delimited parameters passed in via messParams.
111 * 	The expanded message will be returned in the buf character buffer.
112 */
113
114static int
115expandEventLogMessage(int eventId, char *messParams, size_t messParamsLen,
116    char *buf)
117{
118
119	char	*alertMessage;
120	char	*s;
121	char	*d;
122	char	*param;
123
124	/* Get Alert message from internal tables */
125	alertMessage = getEventLogMessage(eventId);
126	if (alertMessage == NULL) {
127		(void) strcpy(buf, "Unknown alert");
128		return (strlen("Unknown alert"));
129	}
130
131	/* No message parameters to copy */
132	if (messParamsLen == 0) {
133		(void) strcpy(buf, alertMessage);
134		return (strlen(buf));
135	}
136
137	/* A %s in the base message means we expand with a parameter */
138	if (strstr(alertMessage, "%s")) {
139		s = alertMessage;
140		d = buf;
141		param = messParams;
142
143		do {
144			if ((*s == '%') && (*(s+1) == 's')) {
145				if (param) {
146					char *p = param;
147
148					while ((*p) && (*p != TAB)) {
149						*d++ = *p++;
150					}
151				}
152				/* Get next parameter on list for next %s */
153				param = getNextEventLogParam(param);
154				s += 2;
155			}
156		} while ((*d++ = *s++));
157
158	} else {
159		/* If no %s tokens to expand, just copy message */
160		(void) strcpy(buf, alertMessage);
161	}
162
163	return (strlen(buf));
164
165}
166
167static void
168ADM_Process_old_event_log()
169{
170	char			timebuf[32];
171	char			messBuff[256];
172	char			eventMsgBuf[256];
173	rscp_msg_t		Message;
174	struct timespec		Timeout;
175	dp_get_event_log_r_t	*rscReply;
176	char			*datap;
177	dp_event_log_entry_t	entry;
178	int			i, len, entryhdrsize;
179
180	ADM_Start();
181
182	Message.type = DP_GET_EVENT_LOG;
183	Message.len = 0;
184	Message.data = NULL;
185	ADM_Send(&Message);
186
187	Timeout.tv_nsec = 0;
188	Timeout.tv_sec  = ADM_TIMEOUT;
189	ADM_Recv(&Message, &Timeout,
190	    DP_GET_EVENT_LOG_R, sizeof (*rscReply));
191
192	/* Print the event log messages */
193	rscReply = (dp_get_event_log_r_t *)Message.data;
194	datap = (char *)rscReply->data;
195	for (i = 0; i < rscReply->entry_count; i++) {
196		entryhdrsize = sizeof (entry) - sizeof (entry.param);
197		(void) memcpy(&entry, datap, entryhdrsize);
198		datap += entryhdrsize;
199		(void) memcpy(&entry.param, datap, entry.paramLen);
200		(void) strftime(timebuf, sizeof (timebuf), "%b %d %H:%M:%S",
201		    gmtime((time_t *)&entry.eventTime));
202		(void) sprintf(messBuff, "%s : %08lx: \"", timebuf,
203		    entry.eventId);
204		len = expandEventLogMessage(entry.eventId, entry.param,
205		    entry.paramLen, eventMsgBuf);
206		(void) strncat(messBuff, eventMsgBuf, len);
207		(void) strcat(messBuff, "\"\r\n");
208		(void) printf(messBuff);
209		datap += entry.paramLen;
210	}
211
212	ADM_Free(&Message);
213}
214
215static int
216ADM_Process_new_event_log(int all)
217{
218	char			timebuf[32];
219	char			messBuff[256];
220	char			eventMsgBuf[256];
221	rscp_msg_t		Message;
222	struct timespec		Timeout;
223	dp_get_event_log2_r_t	*rscReply;
224	char			*datap;
225	dp_event_log_entry_t	entry;
226	int			i, len, entryhdrsize, sent_ok;
227	rsci64			events_remaining, seqno;
228	rsci16			request_size, returned_events;
229	dp_get_event_log2_t	rscCmd;
230
231	ADM_Start();
232
233	/*
234	 * Start by sending a zero-length request to ALOM, so that
235	 * we can learn the length of the console log.  We expect
236	 * ALOM to return the length of the entire log.  We get
237	 * a snapshot of the length of the log here - it may however
238	 * continue to grow as we're reading it.  We read only as
239	 * much of the log as we get in this snapshot.
240	 *
241	 * If the command fails, we quietly return failure here so
242	 * that the caller can re-try with the old/legacy command.
243	 */
244	rscCmd.start_seq = 0;
245	rscCmd.length = 0;
246	Message.type = DP_GET_EVENT_LOG2;
247	Message.len = sizeof (rscCmd);
248	Message.data = (char *)&rscCmd;
249	if (ADM_Send_ret(&Message) != 0) {
250		return (1);
251	}
252
253	Timeout.tv_nsec = 0;
254	Timeout.tv_sec  = ADM_TIMEOUT;
255	ADM_Recv(&Message, &Timeout,
256	    DP_GET_EVENT_LOG2_R, sizeof (*rscReply));
257
258	rscReply = (dp_get_event_log2_r_t *)Message.data;
259
260	/*
261	 * Fetch an fixed number of events from the end of
262	 * the log if at least that many exist, and we were not
263	 * asked to fetch all the events.
264	 */
265	if ((all == 0) &&
266	    (rscReply->remaining_log_events > DEFAULT_NUM_EVENTS)) {
267		events_remaining = DEFAULT_NUM_EVENTS;
268		seqno = (rscReply->remaining_log_events +
269		    rscReply->next_seq) - events_remaining;
270	} else {
271		events_remaining = rscReply->remaining_log_events;
272		seqno = rscReply->next_seq;
273	}
274	request_size = sizeof (rscReply->buffer);
275	ADM_Free(&Message);
276
277	/*
278	 * This loop runs as long as there is data in the log, or until
279	 * we hit the default limit (above).  It's possible that ALOM may
280	 * shrink the log - we need to account for this.  If ALOM returns
281	 * no data, we bail out.
282	 */
283	while (events_remaining) {
284		rscCmd.start_seq = seqno;
285		rscCmd.length = request_size;
286		Message.type = DP_GET_EVENT_LOG2;
287		Message.len = sizeof (rscCmd);
288		Message.data = (char *)&rscCmd;
289		ADM_Send(&Message);
290
291		Timeout.tv_nsec = 0;
292		Timeout.tv_sec  = ADM_TIMEOUT;
293		ADM_Recv(&Message, &Timeout,
294		    DP_GET_EVENT_LOG2_R, sizeof (*rscReply));
295
296		rscReply = (dp_get_event_log2_r_t *)Message.data;
297
298		/* If ALOM returns zero events, we're done. */
299		returned_events = rscReply->num_events;
300		if (returned_events == 0) {
301			ADM_Free(&Message);
302			break;
303		}
304
305		/*
306		 * if the event at the original sequence number is no
307		 * longer in the log, print a message
308		 */
309		if (seqno + returned_events < rscReply->next_seq) {
310			printf(gettext("\nscadm: lost %d events\n"),
311			    rscReply->next_seq - (seqno + returned_events));
312		}
313
314		/*
315		 * get ready for next main loop iteration
316		 */
317		seqno = rscReply->next_seq;
318		events_remaining -= returned_events;
319
320		/* Print the event log messages */
321		datap = rscReply->buffer;
322
323		for (i = 0; i < returned_events; i++) {
324			entryhdrsize = sizeof (entry) - sizeof (entry.param);
325			(void) memcpy(&entry, datap, entryhdrsize);
326			datap += entryhdrsize;
327			(void) memcpy(&entry.param, datap, entry.paramLen);
328			(void) strftime(timebuf, sizeof (timebuf),
329			    "%b %d %H:%M:%S",
330			    gmtime((time_t *)&entry.eventTime));
331			(void) sprintf(messBuff, "%s : %08lx: \"", timebuf,
332			    entry.eventId);
333			len = expandEventLogMessage(entry.eventId, entry.param,
334			    entry.paramLen, eventMsgBuf);
335			(void) strncat(messBuff, eventMsgBuf, len);
336			(void) strcat(messBuff, "\"\r\n");
337			(void) printf(messBuff);
338			datap += entry.paramLen;
339		}
340
341		ADM_Free(&Message);
342	}
343	return (0);
344}
345
346void
347ADM_Process_event_log(int all)
348{
349	if (ADM_Process_new_event_log(all) != 0) {
350		ADM_Process_old_event_log();
351	}
352}
353