1196200Sscottl/*-
2196200Sscottl * Copyright (c) 2008, 2009 Yahoo!, Inc.
3196200Sscottl * All rights reserved.
4196200Sscottl *
5196200Sscottl * Redistribution and use in source and binary forms, with or without
6196200Sscottl * modification, are permitted provided that the following conditions
7196200Sscottl * are met:
8196200Sscottl * 1. Redistributions of source code must retain the above copyright
9196200Sscottl *    notice, this list of conditions and the following disclaimer.
10196200Sscottl * 2. Redistributions in binary form must reproduce the above copyright
11196200Sscottl *    notice, this list of conditions and the following disclaimer in the
12196200Sscottl *    documentation and/or other materials provided with the distribution.
13196200Sscottl * 3. The names of the authors may not be used to endorse or promote
14196200Sscottl *    products derived from this software without specific prior written
15196200Sscottl *    permission.
16196200Sscottl *
17196200Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18196200Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19196200Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20196200Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21196200Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22196200Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23196200Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24196200Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25196200Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26196200Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27196200Sscottl * SUCH DAMAGE.
28196200Sscottl *
29196200Sscottl * $FreeBSD$
30196200Sscottl */
31196200Sscottl
32196200Sscottl#include <sys/types.h>
33196200Sscottl#include <sys/errno.h>
34196200Sscottl#include <err.h>
35237589Seadler#include <fcntl.h>
36266401Sjhb#include <stdbool.h>
37196200Sscottl#include <stdio.h>
38196200Sscottl#include <stdlib.h>
39196200Sscottl#include <strings.h>
40196200Sscottl#include <time.h>
41196200Sscottl#include <unistd.h>
42196200Sscottl#include "mfiutil.h"
43196200Sscottl
44196200Sscottlstatic int
45196200Sscottlmfi_event_get_info(int fd, struct mfi_evt_log_state *info, uint8_t *statusp)
46196200Sscottl{
47196200Sscottl
48196200Sscottl	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GETINFO, info,
49196200Sscottl	    sizeof(struct mfi_evt_log_state), NULL, 0, statusp));
50196200Sscottl}
51196200Sscottl
52196200Sscottlstatic int
53196200Sscottlmfi_get_events(int fd, struct mfi_evt_list *list, int num_events,
54196200Sscottl    union mfi_evt filter, uint32_t start_seq, uint8_t *statusp)
55196200Sscottl{
56196200Sscottl	uint32_t mbox[2];
57196200Sscottl	size_t size;
58196200Sscottl
59196200Sscottl	mbox[0] = start_seq;
60196200Sscottl	mbox[1] = filter.word;
61196200Sscottl	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
62196200Sscottl	    (num_events - 1);
63196200Sscottl	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size,
64196200Sscottl	    (uint8_t *)&mbox, sizeof(mbox), statusp));
65196200Sscottl}
66196200Sscottl
67196200Sscottlstatic int
68237589Seadlershow_logstate(int ac, char **av __unused)
69196200Sscottl{
70196200Sscottl	struct mfi_evt_log_state info;
71214396Sjhb	int error, fd;
72196200Sscottl
73196200Sscottl	if (ac != 1) {
74196200Sscottl		warnx("show logstate: extra arguments");
75196200Sscottl		return (EINVAL);
76196200Sscottl	}
77196200Sscottl
78237589Seadler	fd = mfi_open(mfi_unit, O_RDWR);
79196200Sscottl	if (fd < 0) {
80214396Sjhb		error = errno;
81196200Sscottl		warn("mfi_open");
82214396Sjhb		return (error);
83196200Sscottl	}
84196200Sscottl
85196200Sscottl	if (mfi_event_get_info(fd, &info, NULL) < 0) {
86214396Sjhb		error = errno;
87196200Sscottl		warn("Failed to get event log info");
88222899Sbz		close(fd);
89214396Sjhb		return (error);
90196200Sscottl	}
91196200Sscottl
92196200Sscottl	printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit);
93196200Sscottl	printf("  Newest Seq #: %u\n", info.newest_seq_num);
94196200Sscottl	printf("  Oldest Seq #: %u\n", info.oldest_seq_num);
95196200Sscottl	printf("   Clear Seq #: %u\n", info.clear_seq_num);
96196200Sscottl	printf("Shutdown Seq #: %u\n", info.shutdown_seq_num);
97196200Sscottl	printf("    Boot Seq #: %u\n", info.boot_seq_num);
98196200Sscottl
99196200Sscottl	close(fd);
100196200Sscottl
101196200Sscottl	return (0);
102196200Sscottl}
103196200SscottlMFI_COMMAND(show, logstate, show_logstate);
104196200Sscottl
105196200Sscottlstatic int
106196200Sscottlparse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq)
107196200Sscottl{
108196200Sscottl	char *cp;
109196200Sscottl	long val;
110196200Sscottl
111196200Sscottl	if (strcasecmp(arg, "newest") == 0) {
112196200Sscottl		*seq = info->newest_seq_num;
113196200Sscottl		return (0);
114196200Sscottl	}
115196200Sscottl	if (strcasecmp(arg, "oldest") == 0) {
116196200Sscottl		*seq = info->oldest_seq_num;
117196200Sscottl		return (0);
118196200Sscottl	}
119196200Sscottl	if (strcasecmp(arg, "clear") == 0) {
120196200Sscottl		*seq = info->clear_seq_num;
121196200Sscottl		return (0);
122196200Sscottl	}
123196200Sscottl	if (strcasecmp(arg, "shutdown") == 0) {
124196200Sscottl		*seq = info->shutdown_seq_num;
125196200Sscottl		return (0);
126196200Sscottl	}
127196200Sscottl	if (strcasecmp(arg, "boot") == 0) {
128196200Sscottl		*seq = info->boot_seq_num;
129196200Sscottl		return (0);
130196200Sscottl	}
131196200Sscottl	val = strtol(arg, &cp, 0);
132196200Sscottl	if (*cp != '\0' || val < 0) {
133196200Sscottl		errno = EINVAL;
134196200Sscottl		return (-1);
135196200Sscottl	}
136196200Sscottl	*seq = val;
137196200Sscottl	return (0);
138196200Sscottl}
139196200Sscottl
140196200Sscottlstatic int
141196200Sscottlparse_locale(char *arg, uint16_t *locale)
142196200Sscottl{
143196200Sscottl	char *cp;
144196200Sscottl	long val;
145196200Sscottl
146196200Sscottl	if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) {
147196200Sscottl		*locale = MFI_EVT_LOCALE_LD;
148196200Sscottl		return (0);
149196200Sscottl	}
150196200Sscottl	if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) {
151196200Sscottl		*locale = MFI_EVT_LOCALE_PD;
152196200Sscottl		return (0);
153196200Sscottl	}
154196200Sscottl	if (strncasecmp(arg, "encl", 4) == 0) {
155196200Sscottl		*locale = MFI_EVT_LOCALE_ENCL;
156196200Sscottl		return (0);
157196200Sscottl	}
158196200Sscottl	if (strncasecmp(arg, "batt", 4) == 0 ||
159196200Sscottl	    strncasecmp(arg, "bbu", 3) == 0) {
160196200Sscottl		*locale = MFI_EVT_LOCALE_BBU;
161196200Sscottl		return (0);
162196200Sscottl	}
163196200Sscottl	if (strcasecmp(arg, "sas") == 0) {
164196200Sscottl		*locale = MFI_EVT_LOCALE_SAS;
165196200Sscottl		return (0);
166196200Sscottl	}
167196200Sscottl	if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) {
168196200Sscottl		*locale = MFI_EVT_LOCALE_CTRL;
169196200Sscottl		return (0);
170196200Sscottl	}
171196200Sscottl	if (strcasecmp(arg, "config") == 0) {
172196200Sscottl		*locale = MFI_EVT_LOCALE_CONFIG;
173196200Sscottl		return (0);
174196200Sscottl	}
175196200Sscottl	if (strcasecmp(arg, "cluster") == 0) {
176196200Sscottl		*locale = MFI_EVT_LOCALE_CLUSTER;
177196200Sscottl		return (0);
178196200Sscottl	}
179196200Sscottl	if (strcasecmp(arg, "all") == 0) {
180196200Sscottl		*locale = MFI_EVT_LOCALE_ALL;
181196200Sscottl		return (0);
182196200Sscottl	}
183196200Sscottl	val = strtol(arg, &cp, 0);
184196200Sscottl	if (*cp != '\0' || val < 0 || val > 0xffff) {
185196200Sscottl		errno = EINVAL;
186196200Sscottl		return (-1);
187196200Sscottl	}
188196200Sscottl	*locale = val;
189196200Sscottl	return (0);
190196200Sscottl}
191196200Sscottl
192196200Sscottlstatic int
193196200Sscottlparse_class(char *arg, int8_t *class)
194196200Sscottl{
195196200Sscottl	char *cp;
196196200Sscottl	long val;
197196200Sscottl
198196200Sscottl	if (strcasecmp(arg, "debug") == 0) {
199196200Sscottl		*class = MFI_EVT_CLASS_DEBUG;
200196200Sscottl		return (0);
201196200Sscottl	}
202196200Sscottl	if (strncasecmp(arg, "prog", 4) == 0) {
203196200Sscottl		*class = MFI_EVT_CLASS_PROGRESS;
204196200Sscottl		return (0);
205196200Sscottl	}
206196200Sscottl	if (strncasecmp(arg, "info", 4) == 0) {
207196200Sscottl		*class = MFI_EVT_CLASS_INFO;
208196200Sscottl		return (0);
209196200Sscottl	}
210196200Sscottl	if (strncasecmp(arg, "warn", 4) == 0) {
211196200Sscottl		*class = MFI_EVT_CLASS_WARNING;
212196200Sscottl		return (0);
213196200Sscottl	}
214196200Sscottl	if (strncasecmp(arg, "crit", 4) == 0) {
215196200Sscottl		*class = MFI_EVT_CLASS_CRITICAL;
216196200Sscottl		return (0);
217196200Sscottl	}
218196200Sscottl	if (strcasecmp(arg, "fatal") == 0) {
219196200Sscottl		*class = MFI_EVT_CLASS_FATAL;
220196200Sscottl		return (0);
221196200Sscottl	}
222196200Sscottl	if (strcasecmp(arg, "dead") == 0) {
223196200Sscottl		*class = MFI_EVT_CLASS_DEAD;
224196200Sscottl		return (0);
225196200Sscottl	}
226196200Sscottl	val = strtol(arg, &cp, 0);
227196200Sscottl	if (*cp != '\0' || val < -128 || val > 127) {
228196200Sscottl		errno = EINVAL;
229196200Sscottl		return (-1);
230196200Sscottl	}
231196200Sscottl	*class = val;
232196200Sscottl	return (0);
233196200Sscottl}
234196200Sscottl
235196200Sscottl/*
236196200Sscottl * The timestamp is the number of seconds since 00:00 Jan 1, 2000.  If
237196200Sscottl * the bits in 24-31 are all set, then it is the number of seconds since
238196200Sscottl * boot.
239196200Sscottl */
240196200Sscottlstatic const char *
241196200Sscottlformat_timestamp(uint32_t timestamp)
242196200Sscottl{
243196200Sscottl	static char buffer[32];
244196200Sscottl	static time_t base;
245196200Sscottl	time_t t;
246196200Sscottl	struct tm tm;
247196200Sscottl
248196200Sscottl	if ((timestamp & 0xff000000) == 0xff000000) {
249196200Sscottl		snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
250196200Sscottl		    0x00ffffff);
251196200Sscottl		return (buffer);
252196200Sscottl	}
253196200Sscottl
254196200Sscottl	if (base == 0) {
255196200Sscottl		/* Compute 00:00 Jan 1, 2000 offset. */
256196200Sscottl		bzero(&tm, sizeof(tm));
257196200Sscottl		tm.tm_mday = 1;
258196200Sscottl		tm.tm_year = (2000 - 1900);
259196200Sscottl		base = mktime(&tm);
260196200Sscottl	}
261196200Sscottl	if (base == -1) {
262196200Sscottl		snprintf(buffer, sizeof(buffer), "%us", timestamp);
263196200Sscottl		return (buffer);
264196200Sscottl	}
265196200Sscottl	t = base + timestamp;
266196200Sscottl	strftime(buffer, sizeof(buffer), "%+", localtime(&t));
267196200Sscottl	return (buffer);
268196200Sscottl}
269196200Sscottl
270196200Sscottlstatic const char *
271196200Sscottlformat_locale(uint16_t locale)
272196200Sscottl{
273196200Sscottl	static char buffer[8];
274196200Sscottl
275196200Sscottl	switch (locale) {
276196200Sscottl	case MFI_EVT_LOCALE_LD:
277196200Sscottl		return ("VOLUME");
278196200Sscottl	case MFI_EVT_LOCALE_PD:
279196200Sscottl		return ("DRIVE");
280196200Sscottl	case MFI_EVT_LOCALE_ENCL:
281196200Sscottl		return ("ENCL");
282196200Sscottl	case MFI_EVT_LOCALE_BBU:
283196200Sscottl		return ("BATTERY");
284196200Sscottl	case MFI_EVT_LOCALE_SAS:
285196200Sscottl		return ("SAS");
286196200Sscottl	case MFI_EVT_LOCALE_CTRL:
287196200Sscottl		return ("CTRL");
288196200Sscottl	case MFI_EVT_LOCALE_CONFIG:
289196200Sscottl		return ("CONFIG");
290196200Sscottl	case MFI_EVT_LOCALE_CLUSTER:
291196200Sscottl		return ("CLUSTER");
292196200Sscottl	case MFI_EVT_LOCALE_ALL:
293196200Sscottl		return ("ALL");
294196200Sscottl	default:
295196200Sscottl		snprintf(buffer, sizeof(buffer), "0x%04x", locale);
296196200Sscottl		return (buffer);
297196200Sscottl	}
298196200Sscottl}
299196200Sscottl
300196200Sscottlstatic const char *
301196200Sscottlformat_class(int8_t class)
302196200Sscottl{
303196200Sscottl	static char buffer[6];
304196200Sscottl
305196200Sscottl	switch (class) {
306196200Sscottl	case MFI_EVT_CLASS_DEBUG:
307196200Sscottl		return ("debug");
308196200Sscottl	case MFI_EVT_CLASS_PROGRESS:
309196200Sscottl		return ("progress");
310196200Sscottl	case MFI_EVT_CLASS_INFO:
311196200Sscottl		return ("info");
312196200Sscottl	case MFI_EVT_CLASS_WARNING:
313196200Sscottl		return ("WARN");
314196200Sscottl	case MFI_EVT_CLASS_CRITICAL:
315196200Sscottl		return ("CRIT");
316196200Sscottl	case MFI_EVT_CLASS_FATAL:
317196200Sscottl		return ("FATAL");
318196200Sscottl	case MFI_EVT_CLASS_DEAD:
319196200Sscottl		return ("DEAD");
320196200Sscottl	default:
321196200Sscottl		snprintf(buffer, sizeof(buffer), "%d", class);
322196200Sscottl		return (buffer);
323196200Sscottl	}
324196200Sscottl}
325196200Sscottl
326196200Sscottl/* Simulates %D from kernel printf(9). */
327196200Sscottlstatic void
328196200Sscottlsimple_hex(void *ptr, size_t length, const char *separator)
329196200Sscottl{
330196200Sscottl	unsigned char *cp;
331196200Sscottl	u_int i;
332196200Sscottl
333196200Sscottl	if (length == 0)
334196200Sscottl		return;
335196200Sscottl	cp = ptr;
336196200Sscottl	printf("%02x", cp[0]);
337196200Sscottl	for (i = 1; i < length; i++)
338196200Sscottl		printf("%s%02x", separator, cp[i]);
339196200Sscottl}
340196200Sscottl
341196200Sscottlstatic const char *
342196200Sscottlpdrive_location(struct mfi_evt_pd *pd)
343196200Sscottl{
344196200Sscottl	static char buffer[16];
345196200Sscottl
346196200Sscottl	if (pd->enclosure_index == 0)
347196200Sscottl		snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id,
348196200Sscottl		    pd->slot_number);
349196200Sscottl	else
350196200Sscottl		snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id,
351196200Sscottl		    pd->enclosure_index, pd->slot_number);
352196200Sscottl	return (buffer);
353196200Sscottl}
354196200Sscottl
355196200Sscottlstatic const char *
356196200Sscottlvolume_name(int fd, struct mfi_evt_ld *ld)
357196200Sscottl{
358196200Sscottl
359196200Sscottl	return (mfi_volume_name(fd, ld->target_id));
360196200Sscottl}
361196200Sscottl
362196200Sscottl/* Ripped from sys/dev/mfi/mfi.c. */
363196200Sscottlstatic void
364196200Sscottlmfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose)
365196200Sscottl{
366196200Sscottl
367196200Sscottl	printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time),
368222589Semaste	    format_locale(detail->evt_class.members.locale),
369222589Semaste	    format_class(detail->evt_class.members.evt_class));
370196200Sscottl	switch (detail->arg_type) {
371196200Sscottl	case MR_EVT_ARGS_NONE:
372196200Sscottl		break;
373196200Sscottl	case MR_EVT_ARGS_CDB_SENSE:
374196200Sscottl		if (verbose) {
375196200Sscottl			printf("PD %s CDB ",
376196200Sscottl			    pdrive_location(&detail->args.cdb_sense.pd)
377196200Sscottl			    );
378196200Sscottl			simple_hex(detail->args.cdb_sense.cdb,
379196200Sscottl			    detail->args.cdb_sense.cdb_len, ":");
380196200Sscottl			printf(" Sense ");
381196200Sscottl			simple_hex(detail->args.cdb_sense.sense,
382196200Sscottl			    detail->args.cdb_sense.sense_len, ":");
383196200Sscottl			printf(":\n ");
384196200Sscottl		}
385196200Sscottl		break;
386196200Sscottl	case MR_EVT_ARGS_LD:
387196200Sscottl		printf("VOL %s event: ", volume_name(fd, &detail->args.ld));
388196200Sscottl		break;
389196200Sscottl	case MR_EVT_ARGS_LD_COUNT:
390196200Sscottl		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
391196200Sscottl		if (verbose) {
392196200Sscottl			printf(" count %lld: ",
393196200Sscottl			    (long long)detail->args.ld_count.count);
394196200Sscottl		}
395196200Sscottl		printf(": ");
396196200Sscottl		break;
397196200Sscottl	case MR_EVT_ARGS_LD_LBA:
398196200Sscottl		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
399196200Sscottl		if (verbose) {
400196200Sscottl			printf(" lba %lld",
401196200Sscottl			    (long long)detail->args.ld_lba.lba);
402196200Sscottl		}
403196200Sscottl		printf(": ");
404196200Sscottl		break;
405196200Sscottl	case MR_EVT_ARGS_LD_OWNER:
406196200Sscottl		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
407196200Sscottl		if (verbose) {
408196200Sscottl			printf(" owner changed: prior %d, new %d",
409196200Sscottl			    detail->args.ld_owner.pre_owner,
410196200Sscottl			    detail->args.ld_owner.new_owner);
411196200Sscottl		}
412196200Sscottl		printf(": ");
413196200Sscottl		break;
414196200Sscottl	case MR_EVT_ARGS_LD_LBA_PD_LBA:
415196200Sscottl		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
416196200Sscottl		if (verbose) {
417196200Sscottl			printf(" lba %lld, physical drive PD %s lba %lld",
418196200Sscottl			    (long long)detail->args.ld_lba_pd_lba.ld_lba,
419196200Sscottl			    pdrive_location(&detail->args.ld_lba_pd_lba.pd),
420196200Sscottl			    (long long)detail->args.ld_lba_pd_lba.pd_lba);
421196200Sscottl		}
422196200Sscottl		printf(": ");
423196200Sscottl		break;
424196200Sscottl	case MR_EVT_ARGS_LD_PROG:
425196200Sscottl		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
426196200Sscottl		if (verbose) {
427196200Sscottl			printf(" progress %d%% in %ds",
428196200Sscottl			    detail->args.ld_prog.prog.progress/655,
429196200Sscottl			    detail->args.ld_prog.prog.elapsed_seconds);
430196200Sscottl		}
431196200Sscottl		printf(": ");
432196200Sscottl		break;
433196200Sscottl	case MR_EVT_ARGS_LD_STATE:
434196200Sscottl		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
435196200Sscottl		if (verbose) {
436196200Sscottl			printf(" state prior %s new %s",
437196200Sscottl			    mfi_ldstate(detail->args.ld_state.prev_state),
438196200Sscottl			    mfi_ldstate(detail->args.ld_state.new_state));
439196200Sscottl		}
440196200Sscottl		printf(": ");
441196200Sscottl		break;
442196200Sscottl	case MR_EVT_ARGS_LD_STRIP:
443229623Sjhb		printf("VOL %s", volume_name(fd, &detail->args.ld_strip.ld));
444196200Sscottl		if (verbose) {
445196200Sscottl			printf(" strip %lld",
446196200Sscottl			    (long long)detail->args.ld_strip.strip);
447196200Sscottl		}
448196200Sscottl		printf(": ");
449196200Sscottl		break;
450196200Sscottl	case MR_EVT_ARGS_PD:
451196200Sscottl		if (verbose) {
452196200Sscottl			printf("PD %s event: ",
453196200Sscottl			    pdrive_location(&detail->args.pd));
454196200Sscottl		}
455196200Sscottl		break;
456196200Sscottl	case MR_EVT_ARGS_PD_ERR:
457196200Sscottl		if (verbose) {
458196200Sscottl			printf("PD %s err %d: ",
459196200Sscottl			    pdrive_location(&detail->args.pd_err.pd),
460196200Sscottl			    detail->args.pd_err.err);
461196200Sscottl		}
462196200Sscottl		break;
463196200Sscottl	case MR_EVT_ARGS_PD_LBA:
464196200Sscottl		if (verbose) {
465196200Sscottl			printf("PD %s lba %lld: ",
466196200Sscottl			    pdrive_location(&detail->args.pd_lba.pd),
467196200Sscottl			    (long long)detail->args.pd_lba.lba);
468196200Sscottl		}
469196200Sscottl		break;
470196200Sscottl	case MR_EVT_ARGS_PD_LBA_LD:
471196200Sscottl		if (verbose) {
472196200Sscottl			printf("PD %s lba %lld VOL %s: ",
473196200Sscottl			    pdrive_location(&detail->args.pd_lba_ld.pd),
474196200Sscottl			    (long long)detail->args.pd_lba.lba,
475196200Sscottl			    volume_name(fd, &detail->args.pd_lba_ld.ld));
476196200Sscottl		}
477196200Sscottl		break;
478196200Sscottl	case MR_EVT_ARGS_PD_PROG:
479196200Sscottl		if (verbose) {
480196200Sscottl			printf("PD %s progress %d%% seconds %ds: ",
481196200Sscottl			    pdrive_location(&detail->args.pd_prog.pd),
482196200Sscottl			    detail->args.pd_prog.prog.progress/655,
483196200Sscottl			    detail->args.pd_prog.prog.elapsed_seconds);
484196200Sscottl		}
485196200Sscottl		break;
486196200Sscottl	case MR_EVT_ARGS_PD_STATE:
487196200Sscottl		if (verbose) {
488196200Sscottl			printf("PD %s state prior %s new %s: ",
489196200Sscottl			    pdrive_location(&detail->args.pd_prog.pd),
490196200Sscottl			    mfi_pdstate(detail->args.pd_state.prev_state),
491196200Sscottl			    mfi_pdstate(detail->args.pd_state.new_state));
492196200Sscottl		}
493196200Sscottl		break;
494196200Sscottl	case MR_EVT_ARGS_PCI:
495196200Sscottl		if (verbose) {
496196200Sscottl			printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ",
497196200Sscottl			    detail->args.pci.venderId,
498196200Sscottl			    detail->args.pci.deviceId,
499196200Sscottl			    detail->args.pci.subVenderId,
500196200Sscottl			    detail->args.pci.subDeviceId);
501196200Sscottl		}
502196200Sscottl		break;
503196200Sscottl	case MR_EVT_ARGS_RATE:
504196200Sscottl		if (verbose) {
505196200Sscottl			printf("Rebuild rate %d: ", detail->args.rate);
506196200Sscottl		}
507196200Sscottl		break;
508196200Sscottl	case MR_EVT_ARGS_TIME:
509196200Sscottl		if (verbose) {
510196200Sscottl			printf("Adapter time %s; %d seconds since power on: ",
511196200Sscottl			    format_timestamp(detail->args.time.rtc),
512196200Sscottl			    detail->args.time.elapsedSeconds);
513196200Sscottl		}
514196200Sscottl		break;
515196200Sscottl	case MR_EVT_ARGS_ECC:
516196200Sscottl		if (verbose) {
517196200Sscottl			printf("Adapter ECC %x,%x: %s: ",
518196200Sscottl			    detail->args.ecc.ecar,
519196200Sscottl			    detail->args.ecc.elog,
520196200Sscottl			    detail->args.ecc.str);
521196200Sscottl		}
522196200Sscottl		break;
523196200Sscottl	default:
524196200Sscottl		if (verbose) {
525196200Sscottl			printf("Type %d: ", detail->arg_type);
526196200Sscottl		}
527196200Sscottl		break;
528196200Sscottl	}
529196200Sscottl	printf("%s\n", detail->description);
530196200Sscottl}
531196200Sscottl
532196200Sscottlstatic int
533196200Sscottlshow_events(int ac, char **av)
534196200Sscottl{
535196200Sscottl	struct mfi_evt_log_state info;
536196200Sscottl	struct mfi_evt_list *list;
537196200Sscottl	union mfi_evt filter;
538266401Sjhb	bool first;
539196200Sscottl	long val;
540196200Sscottl	char *cp;
541196200Sscottl	ssize_t size;
542196200Sscottl	uint32_t seq, start, stop;
543196200Sscottl	uint8_t status;
544214396Sjhb	int ch, error, fd, num_events, verbose;
545196200Sscottl	u_int i;
546196200Sscottl
547237589Seadler	fd = mfi_open(mfi_unit, O_RDWR);
548196200Sscottl	if (fd < 0) {
549214396Sjhb		error = errno;
550196200Sscottl		warn("mfi_open");
551214396Sjhb		return (error);
552196200Sscottl	}
553196200Sscottl
554196200Sscottl	if (mfi_event_get_info(fd, &info, NULL) < 0) {
555214396Sjhb		error = errno;
556196200Sscottl		warn("Failed to get event log info");
557222899Sbz		close(fd);
558214396Sjhb		return (error);
559196200Sscottl	}
560196200Sscottl
561196200Sscottl	/* Default settings. */
562196200Sscottl	num_events = 15;
563196200Sscottl	filter.members.reserved = 0;
564196200Sscottl	filter.members.locale = MFI_EVT_LOCALE_ALL;
565222589Semaste	filter.members.evt_class = MFI_EVT_CLASS_WARNING;
566196200Sscottl	start = info.boot_seq_num;
567196200Sscottl	stop = info.newest_seq_num;
568196200Sscottl	verbose = 0;
569196200Sscottl
570196200Sscottl	/* Parse any options. */
571196200Sscottl	optind = 1;
572196200Sscottl	while ((ch = getopt(ac, av, "c:l:n:v")) != -1) {
573196200Sscottl		switch (ch) {
574196200Sscottl		case 'c':
575222589Semaste			if (parse_class(optarg, &filter.members.evt_class) < 0) {
576214396Sjhb				error = errno;
577196200Sscottl				warn("Error parsing event class");
578222899Sbz				close(fd);
579214396Sjhb				return (error);
580196200Sscottl			}
581196200Sscottl			break;
582196200Sscottl		case 'l':
583196200Sscottl			if (parse_locale(optarg, &filter.members.locale) < 0) {
584214396Sjhb				error = errno;
585196200Sscottl				warn("Error parsing event locale");
586222899Sbz				close(fd);
587214396Sjhb				return (error);
588196200Sscottl			}
589196200Sscottl			break;
590196200Sscottl		case 'n':
591196200Sscottl			val = strtol(optarg, &cp, 0);
592196200Sscottl			if (*cp != '\0' || val <= 0) {
593196200Sscottl				warnx("Invalid event count");
594222899Sbz				close(fd);
595196200Sscottl				return (EINVAL);
596196200Sscottl			}
597196200Sscottl			num_events = val;
598196200Sscottl			break;
599196200Sscottl		case 'v':
600196200Sscottl			verbose = 1;
601196200Sscottl			break;
602196200Sscottl		case '?':
603196200Sscottl		default:
604222899Sbz			close(fd);
605196200Sscottl			return (EINVAL);
606196200Sscottl		}
607196200Sscottl	}
608196200Sscottl	ac -= optind;
609196200Sscottl	av += optind;
610196200Sscottl
611196200Sscottl	/* Determine buffer size and validate it. */
612196200Sscottl	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
613196200Sscottl	    (num_events - 1);
614196200Sscottl	if (size > getpagesize()) {
615196200Sscottl		warnx("Event count is too high");
616222899Sbz		close(fd);
617196200Sscottl		return (EINVAL);
618196200Sscottl	}
619196200Sscottl
620196200Sscottl	/* Handle optional start and stop sequence numbers. */
621196200Sscottl	if (ac > 2) {
622196200Sscottl		warnx("show events: extra arguments");
623222899Sbz		close(fd);
624196200Sscottl		return (EINVAL);
625196200Sscottl	}
626196200Sscottl	if (ac > 0 && parse_seq(&info, av[0], &start) < 0) {
627214396Sjhb		error = errno;
628196200Sscottl		warn("Error parsing starting sequence number");
629222899Sbz		close(fd);
630214396Sjhb		return (error);
631196200Sscottl	}
632196200Sscottl	if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) {
633214396Sjhb		error = errno;
634196200Sscottl		warn("Error parsing ending sequence number");
635222899Sbz		close(fd);
636214396Sjhb		return (error);
637196200Sscottl	}
638196200Sscottl
639196200Sscottl	list = malloc(size);
640215526Sjhb	if (list == NULL) {
641215526Sjhb		warnx("malloc failed");
642222899Sbz		close(fd);
643215526Sjhb		return (ENOMEM);
644215526Sjhb	}
645266401Sjhb	first = true;
646266401Sjhb	seq = start;
647266401Sjhb	for (;;) {
648196200Sscottl		if (mfi_get_events(fd, list, num_events, filter, seq,
649196200Sscottl		    &status) < 0) {
650214396Sjhb			error = errno;
651196200Sscottl			warn("Failed to fetch events");
652222899Sbz			free(list);
653222899Sbz			close(fd);
654214396Sjhb			return (error);
655196200Sscottl		}
656196200Sscottl		if (status == MFI_STAT_NOT_FOUND) {
657196200Sscottl			break;
658196200Sscottl		}
659196200Sscottl		if (status != MFI_STAT_OK) {
660196200Sscottl			warnx("Error fetching events: %s", mfi_status(status));
661222899Sbz			free(list);
662222899Sbz			close(fd);
663196200Sscottl			return (EIO);
664196200Sscottl		}
665196200Sscottl
666196200Sscottl		for (i = 0; i < list->count; i++) {
667196200Sscottl			/*
668196200Sscottl			 * If this event is newer than 'stop_seq' then
669196200Sscottl			 * break out of the loop.  Note that the log
670196200Sscottl			 * is a circular buffer so we have to handle
671196200Sscottl			 * the case that our stop point is earlier in
672196200Sscottl			 * the buffer than our start point.
673196200Sscottl			 */
674266401Sjhb			if (list->event[i].seq > stop) {
675196200Sscottl				if (start <= stop)
676266401Sjhb					goto finish;
677196200Sscottl				else if (list->event[i].seq < start)
678266401Sjhb					goto finish;
679196200Sscottl			}
680196200Sscottl			mfi_decode_evt(fd, &list->event[i], verbose);
681266401Sjhb			first = false;
682196200Sscottl		}
683196200Sscottl
684196200Sscottl		/*
685196200Sscottl		 * XXX: If the event's seq # is the end of the buffer
686196200Sscottl		 * then this probably won't do the right thing.  We
687196200Sscottl		 * need to know the size of the buffer somehow.
688196200Sscottl		 */
689196200Sscottl		seq = list->event[list->count - 1].seq + 1;
690196200Sscottl
691196200Sscottl	}
692266401Sjhbfinish:
693266401Sjhb	if (first)
694266401Sjhb		warnx("No matching events found");
695196200Sscottl
696196200Sscottl	free(list);
697196200Sscottl	close(fd);
698196200Sscottl
699196200Sscottl	return (0);
700196200Sscottl}
701196200SscottlMFI_COMMAND(show, events, show_events);
702