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