xref: /freebsd-src/usr.sbin/mfiutil/mfi_evt.c (revision c6ad7dcd9c44a49078841e9ec42bf8621ae312c7)
1763fae79SScott Long /*-
21de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
31de7b4b8SPedro F. Giffuni  *
4763fae79SScott Long  * Copyright (c) 2008, 2009 Yahoo!, Inc.
5763fae79SScott Long  * All rights reserved.
6763fae79SScott Long  *
7763fae79SScott Long  * Redistribution and use in source and binary forms, with or without
8763fae79SScott Long  * modification, are permitted provided that the following conditions
9763fae79SScott Long  * are met:
10763fae79SScott Long  * 1. Redistributions of source code must retain the above copyright
11763fae79SScott Long  *    notice, this list of conditions and the following disclaimer.
12763fae79SScott Long  * 2. Redistributions in binary form must reproduce the above copyright
13763fae79SScott Long  *    notice, this list of conditions and the following disclaimer in the
14763fae79SScott Long  *    documentation and/or other materials provided with the distribution.
15763fae79SScott Long  * 3. The names of the authors may not be used to endorse or promote
16763fae79SScott Long  *    products derived from this software without specific prior written
17763fae79SScott Long  *    permission.
18763fae79SScott Long  *
19763fae79SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20763fae79SScott Long  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21763fae79SScott Long  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22763fae79SScott Long  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23763fae79SScott Long  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24763fae79SScott Long  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25763fae79SScott Long  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26763fae79SScott Long  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27763fae79SScott Long  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28763fae79SScott Long  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29763fae79SScott Long  * SUCH DAMAGE.
30763fae79SScott Long  */
31763fae79SScott Long 
32763fae79SScott Long #include <sys/types.h>
33763fae79SScott Long #include <sys/errno.h>
34763fae79SScott Long #include <err.h>
35bf4ec4dfSEitan Adler #include <fcntl.h>
36186ddf96SJohn Baldwin #include <stdbool.h>
37763fae79SScott Long #include <stdio.h>
38763fae79SScott Long #include <stdlib.h>
39763fae79SScott Long #include <strings.h>
40763fae79SScott Long #include <time.h>
41763fae79SScott Long #include <unistd.h>
42763fae79SScott Long #include "mfiutil.h"
43763fae79SScott Long 
44763fae79SScott Long static int
45763fae79SScott Long mfi_event_get_info(int fd, struct mfi_evt_log_state *info, uint8_t *statusp)
46763fae79SScott Long {
47763fae79SScott Long 
48763fae79SScott Long 	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GETINFO, info,
49763fae79SScott Long 	    sizeof(struct mfi_evt_log_state), NULL, 0, statusp));
50763fae79SScott Long }
51763fae79SScott Long 
52763fae79SScott Long static int
53763fae79SScott Long mfi_get_events(int fd, struct mfi_evt_list *list, int num_events,
54763fae79SScott Long     union mfi_evt filter, uint32_t start_seq, uint8_t *statusp)
55763fae79SScott Long {
56763fae79SScott Long 	uint32_t mbox[2];
57763fae79SScott Long 	size_t size;
58763fae79SScott Long 
59763fae79SScott Long 	mbox[0] = start_seq;
60763fae79SScott Long 	mbox[1] = filter.word;
61763fae79SScott Long 	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
62763fae79SScott Long 	    (num_events - 1);
63763fae79SScott Long 	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size,
64763fae79SScott Long 	    (uint8_t *)&mbox, sizeof(mbox), statusp));
65763fae79SScott Long }
66763fae79SScott Long 
67763fae79SScott Long static int
6841b8cbdaSEitan Adler show_logstate(int ac, char **av __unused)
69763fae79SScott Long {
70763fae79SScott Long 	struct mfi_evt_log_state info;
71c02999d9SJohn Baldwin 	int error, fd;
72763fae79SScott Long 
73763fae79SScott Long 	if (ac != 1) {
74763fae79SScott Long 		warnx("show logstate: extra arguments");
75763fae79SScott Long 		return (EINVAL);
76763fae79SScott Long 	}
77763fae79SScott Long 
787e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDWR);
79763fae79SScott Long 	if (fd < 0) {
80c02999d9SJohn Baldwin 		error = errno;
81763fae79SScott Long 		warn("mfi_open");
82c02999d9SJohn Baldwin 		return (error);
83763fae79SScott Long 	}
84763fae79SScott Long 
85763fae79SScott Long 	if (mfi_event_get_info(fd, &info, NULL) < 0) {
86c02999d9SJohn Baldwin 		error = errno;
87763fae79SScott Long 		warn("Failed to get event log info");
88375c4656SBjoern A. Zeeb 		close(fd);
89c02999d9SJohn Baldwin 		return (error);
90763fae79SScott Long 	}
91763fae79SScott Long 
927e0f8b79SDoug Ambrisko 	printf("%s Event Log Sequence Numbers:\n", mfi_device);
93763fae79SScott Long 	printf("  Newest Seq #: %u\n", info.newest_seq_num);
94763fae79SScott Long 	printf("  Oldest Seq #: %u\n", info.oldest_seq_num);
95763fae79SScott Long 	printf("   Clear Seq #: %u\n", info.clear_seq_num);
96763fae79SScott Long 	printf("Shutdown Seq #: %u\n", info.shutdown_seq_num);
97763fae79SScott Long 	printf("    Boot Seq #: %u\n", info.boot_seq_num);
98763fae79SScott Long 
99763fae79SScott Long 	close(fd);
100763fae79SScott Long 
101763fae79SScott Long 	return (0);
102763fae79SScott Long }
103763fae79SScott Long MFI_COMMAND(show, logstate, show_logstate);
104763fae79SScott Long 
105763fae79SScott Long static int
106763fae79SScott Long parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq)
107763fae79SScott Long {
108763fae79SScott Long 	char *cp;
109763fae79SScott Long 	long val;
110763fae79SScott Long 
111763fae79SScott Long 	if (strcasecmp(arg, "newest") == 0) {
112763fae79SScott Long 		*seq = info->newest_seq_num;
113763fae79SScott Long 		return (0);
114763fae79SScott Long 	}
115763fae79SScott Long 	if (strcasecmp(arg, "oldest") == 0) {
116763fae79SScott Long 		*seq = info->oldest_seq_num;
117763fae79SScott Long 		return (0);
118763fae79SScott Long 	}
119763fae79SScott Long 	if (strcasecmp(arg, "clear") == 0) {
120763fae79SScott Long 		*seq = info->clear_seq_num;
121763fae79SScott Long 		return (0);
122763fae79SScott Long 	}
123763fae79SScott Long 	if (strcasecmp(arg, "shutdown") == 0) {
124763fae79SScott Long 		*seq = info->shutdown_seq_num;
125763fae79SScott Long 		return (0);
126763fae79SScott Long 	}
127763fae79SScott Long 	if (strcasecmp(arg, "boot") == 0) {
128763fae79SScott Long 		*seq = info->boot_seq_num;
129763fae79SScott Long 		return (0);
130763fae79SScott Long 	}
131763fae79SScott Long 	val = strtol(arg, &cp, 0);
132763fae79SScott Long 	if (*cp != '\0' || val < 0) {
133763fae79SScott Long 		errno = EINVAL;
134763fae79SScott Long 		return (-1);
135763fae79SScott Long 	}
136763fae79SScott Long 	*seq = val;
137763fae79SScott Long 	return (0);
138763fae79SScott Long }
139763fae79SScott Long 
140763fae79SScott Long static int
141763fae79SScott Long parse_locale(char *arg, uint16_t *locale)
142763fae79SScott Long {
143763fae79SScott Long 	char *cp;
144763fae79SScott Long 	long val;
145763fae79SScott Long 
146763fae79SScott Long 	if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) {
147763fae79SScott Long 		*locale = MFI_EVT_LOCALE_LD;
148763fae79SScott Long 		return (0);
149763fae79SScott Long 	}
150763fae79SScott Long 	if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) {
151763fae79SScott Long 		*locale = MFI_EVT_LOCALE_PD;
152763fae79SScott Long 		return (0);
153763fae79SScott Long 	}
154763fae79SScott Long 	if (strncasecmp(arg, "encl", 4) == 0) {
155763fae79SScott Long 		*locale = MFI_EVT_LOCALE_ENCL;
156763fae79SScott Long 		return (0);
157763fae79SScott Long 	}
158763fae79SScott Long 	if (strncasecmp(arg, "batt", 4) == 0 ||
159763fae79SScott Long 	    strncasecmp(arg, "bbu", 3) == 0) {
160763fae79SScott Long 		*locale = MFI_EVT_LOCALE_BBU;
161763fae79SScott Long 		return (0);
162763fae79SScott Long 	}
163763fae79SScott Long 	if (strcasecmp(arg, "sas") == 0) {
164763fae79SScott Long 		*locale = MFI_EVT_LOCALE_SAS;
165763fae79SScott Long 		return (0);
166763fae79SScott Long 	}
167763fae79SScott Long 	if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) {
168763fae79SScott Long 		*locale = MFI_EVT_LOCALE_CTRL;
169763fae79SScott Long 		return (0);
170763fae79SScott Long 	}
171763fae79SScott Long 	if (strcasecmp(arg, "config") == 0) {
172763fae79SScott Long 		*locale = MFI_EVT_LOCALE_CONFIG;
173763fae79SScott Long 		return (0);
174763fae79SScott Long 	}
175763fae79SScott Long 	if (strcasecmp(arg, "cluster") == 0) {
176763fae79SScott Long 		*locale = MFI_EVT_LOCALE_CLUSTER;
177763fae79SScott Long 		return (0);
178763fae79SScott Long 	}
179763fae79SScott Long 	if (strcasecmp(arg, "all") == 0) {
180763fae79SScott Long 		*locale = MFI_EVT_LOCALE_ALL;
181763fae79SScott Long 		return (0);
182763fae79SScott Long 	}
183763fae79SScott Long 	val = strtol(arg, &cp, 0);
184763fae79SScott Long 	if (*cp != '\0' || val < 0 || val > 0xffff) {
185763fae79SScott Long 		errno = EINVAL;
186763fae79SScott Long 		return (-1);
187763fae79SScott Long 	}
188763fae79SScott Long 	*locale = val;
189763fae79SScott Long 	return (0);
190763fae79SScott Long }
191763fae79SScott Long 
192763fae79SScott Long static int
193763fae79SScott Long parse_class(char *arg, int8_t *class)
194763fae79SScott Long {
195763fae79SScott Long 	char *cp;
196763fae79SScott Long 	long val;
197763fae79SScott Long 
198763fae79SScott Long 	if (strcasecmp(arg, "debug") == 0) {
199763fae79SScott Long 		*class = MFI_EVT_CLASS_DEBUG;
200763fae79SScott Long 		return (0);
201763fae79SScott Long 	}
202763fae79SScott Long 	if (strncasecmp(arg, "prog", 4) == 0) {
203763fae79SScott Long 		*class = MFI_EVT_CLASS_PROGRESS;
204763fae79SScott Long 		return (0);
205763fae79SScott Long 	}
206763fae79SScott Long 	if (strncasecmp(arg, "info", 4) == 0) {
207763fae79SScott Long 		*class = MFI_EVT_CLASS_INFO;
208763fae79SScott Long 		return (0);
209763fae79SScott Long 	}
210763fae79SScott Long 	if (strncasecmp(arg, "warn", 4) == 0) {
211763fae79SScott Long 		*class = MFI_EVT_CLASS_WARNING;
212763fae79SScott Long 		return (0);
213763fae79SScott Long 	}
214763fae79SScott Long 	if (strncasecmp(arg, "crit", 4) == 0) {
215763fae79SScott Long 		*class = MFI_EVT_CLASS_CRITICAL;
216763fae79SScott Long 		return (0);
217763fae79SScott Long 	}
218763fae79SScott Long 	if (strcasecmp(arg, "fatal") == 0) {
219763fae79SScott Long 		*class = MFI_EVT_CLASS_FATAL;
220763fae79SScott Long 		return (0);
221763fae79SScott Long 	}
222763fae79SScott Long 	if (strcasecmp(arg, "dead") == 0) {
223763fae79SScott Long 		*class = MFI_EVT_CLASS_DEAD;
224763fae79SScott Long 		return (0);
225763fae79SScott Long 	}
226763fae79SScott Long 	val = strtol(arg, &cp, 0);
227763fae79SScott Long 	if (*cp != '\0' || val < -128 || val > 127) {
228763fae79SScott Long 		errno = EINVAL;
229763fae79SScott Long 		return (-1);
230763fae79SScott Long 	}
231763fae79SScott Long 	*class = val;
232763fae79SScott Long 	return (0);
233763fae79SScott Long }
234763fae79SScott Long 
235763fae79SScott Long /*
236763fae79SScott Long  * The timestamp is the number of seconds since 00:00 Jan 1, 2000.  If
237763fae79SScott Long  * the bits in 24-31 are all set, then it is the number of seconds since
238763fae79SScott Long  * boot.
239763fae79SScott Long  */
240763fae79SScott Long static const char *
241763fae79SScott Long format_timestamp(uint32_t timestamp)
242763fae79SScott Long {
243763fae79SScott Long 	static char buffer[32];
244763fae79SScott Long 	static time_t base;
245763fae79SScott Long 	time_t t;
246763fae79SScott Long 	struct tm tm;
247763fae79SScott Long 
248763fae79SScott Long 	if ((timestamp & 0xff000000) == 0xff000000) {
249763fae79SScott Long 		snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
250763fae79SScott Long 		    0x00ffffff);
251763fae79SScott Long 		return (buffer);
252763fae79SScott Long 	}
253763fae79SScott Long 
254763fae79SScott Long 	if (base == 0) {
255763fae79SScott Long 		/* Compute 00:00 Jan 1, 2000 offset. */
256763fae79SScott Long 		bzero(&tm, sizeof(tm));
257763fae79SScott Long 		tm.tm_mday = 1;
258763fae79SScott Long 		tm.tm_year = (2000 - 1900);
259763fae79SScott Long 		base = mktime(&tm);
260763fae79SScott Long 	}
261763fae79SScott Long 	if (base == -1) {
262763fae79SScott Long 		snprintf(buffer, sizeof(buffer), "%us", timestamp);
263763fae79SScott Long 		return (buffer);
264763fae79SScott Long 	}
265763fae79SScott Long 	t = base + timestamp;
266763fae79SScott Long 	strftime(buffer, sizeof(buffer), "%+", localtime(&t));
267763fae79SScott Long 	return (buffer);
268763fae79SScott Long }
269763fae79SScott Long 
270763fae79SScott Long static const char *
271763fae79SScott Long format_locale(uint16_t locale)
272763fae79SScott Long {
273763fae79SScott Long 	static char buffer[8];
274763fae79SScott Long 
275763fae79SScott Long 	switch (locale) {
276763fae79SScott Long 	case MFI_EVT_LOCALE_LD:
277763fae79SScott Long 		return ("VOLUME");
278763fae79SScott Long 	case MFI_EVT_LOCALE_PD:
279763fae79SScott Long 		return ("DRIVE");
280763fae79SScott Long 	case MFI_EVT_LOCALE_ENCL:
281763fae79SScott Long 		return ("ENCL");
282763fae79SScott Long 	case MFI_EVT_LOCALE_BBU:
283763fae79SScott Long 		return ("BATTERY");
284763fae79SScott Long 	case MFI_EVT_LOCALE_SAS:
285763fae79SScott Long 		return ("SAS");
286763fae79SScott Long 	case MFI_EVT_LOCALE_CTRL:
287763fae79SScott Long 		return ("CTRL");
288763fae79SScott Long 	case MFI_EVT_LOCALE_CONFIG:
289763fae79SScott Long 		return ("CONFIG");
290763fae79SScott Long 	case MFI_EVT_LOCALE_CLUSTER:
291763fae79SScott Long 		return ("CLUSTER");
292763fae79SScott Long 	case MFI_EVT_LOCALE_ALL:
293763fae79SScott Long 		return ("ALL");
294763fae79SScott Long 	default:
295763fae79SScott Long 		snprintf(buffer, sizeof(buffer), "0x%04x", locale);
296763fae79SScott Long 		return (buffer);
297763fae79SScott Long 	}
298763fae79SScott Long }
299763fae79SScott Long 
300763fae79SScott Long static const char *
301763fae79SScott Long format_class(int8_t class)
302763fae79SScott Long {
303763fae79SScott Long 	static char buffer[6];
304763fae79SScott Long 
305763fae79SScott Long 	switch (class) {
306763fae79SScott Long 	case MFI_EVT_CLASS_DEBUG:
307763fae79SScott Long 		return ("debug");
308763fae79SScott Long 	case MFI_EVT_CLASS_PROGRESS:
309763fae79SScott Long 		return ("progress");
310763fae79SScott Long 	case MFI_EVT_CLASS_INFO:
311763fae79SScott Long 		return ("info");
312763fae79SScott Long 	case MFI_EVT_CLASS_WARNING:
313763fae79SScott Long 		return ("WARN");
314763fae79SScott Long 	case MFI_EVT_CLASS_CRITICAL:
315763fae79SScott Long 		return ("CRIT");
316763fae79SScott Long 	case MFI_EVT_CLASS_FATAL:
317763fae79SScott Long 		return ("FATAL");
318763fae79SScott Long 	case MFI_EVT_CLASS_DEAD:
319763fae79SScott Long 		return ("DEAD");
320763fae79SScott Long 	default:
321763fae79SScott Long 		snprintf(buffer, sizeof(buffer), "%d", class);
322763fae79SScott Long 		return (buffer);
323763fae79SScott Long 	}
324763fae79SScott Long }
325763fae79SScott Long 
326763fae79SScott Long /* Simulates %D from kernel printf(9). */
327763fae79SScott Long static void
328763fae79SScott Long simple_hex(void *ptr, size_t length, const char *separator)
329763fae79SScott Long {
330763fae79SScott Long 	unsigned char *cp;
331763fae79SScott Long 	u_int i;
332763fae79SScott Long 
333763fae79SScott Long 	if (length == 0)
334763fae79SScott Long 		return;
335763fae79SScott Long 	cp = ptr;
336763fae79SScott Long 	printf("%02x", cp[0]);
337763fae79SScott Long 	for (i = 1; i < length; i++)
338763fae79SScott Long 		printf("%s%02x", separator, cp[i]);
339763fae79SScott Long }
340763fae79SScott Long 
341763fae79SScott Long static const char *
342763fae79SScott Long pdrive_location(struct mfi_evt_pd *pd)
343763fae79SScott Long {
344763fae79SScott Long 	static char buffer[16];
345763fae79SScott Long 
346763fae79SScott Long 	if (pd->enclosure_index == 0)
347763fae79SScott Long 		snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id,
348763fae79SScott Long 		    pd->slot_number);
349763fae79SScott Long 	else
350763fae79SScott Long 		snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id,
351763fae79SScott Long 		    pd->enclosure_index, pd->slot_number);
352763fae79SScott Long 	return (buffer);
353763fae79SScott Long }
354763fae79SScott Long 
355763fae79SScott Long static const char *
356763fae79SScott Long volume_name(int fd, struct mfi_evt_ld *ld)
357763fae79SScott Long {
358763fae79SScott Long 
359763fae79SScott Long 	return (mfi_volume_name(fd, ld->target_id));
360763fae79SScott Long }
361763fae79SScott Long 
362763fae79SScott Long /* Ripped from sys/dev/mfi/mfi.c. */
363763fae79SScott Long static void
364763fae79SScott Long mfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose)
365763fae79SScott Long {
366763fae79SScott Long 
367763fae79SScott Long 	printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time),
368aacea6e2SEd Maste 	    format_locale(detail->evt_class.members.locale),
369aacea6e2SEd Maste 	    format_class(detail->evt_class.members.evt_class));
370763fae79SScott Long 	switch (detail->arg_type) {
371763fae79SScott Long 	case MR_EVT_ARGS_NONE:
372763fae79SScott Long 		break;
373763fae79SScott Long 	case MR_EVT_ARGS_CDB_SENSE:
374763fae79SScott Long 		if (verbose) {
375763fae79SScott Long 			printf("PD %s CDB ",
376763fae79SScott Long 			    pdrive_location(&detail->args.cdb_sense.pd)
377763fae79SScott Long 			    );
378763fae79SScott Long 			simple_hex(detail->args.cdb_sense.cdb,
379763fae79SScott Long 			    detail->args.cdb_sense.cdb_len, ":");
380763fae79SScott Long 			printf(" Sense ");
381763fae79SScott Long 			simple_hex(detail->args.cdb_sense.sense,
382763fae79SScott Long 			    detail->args.cdb_sense.sense_len, ":");
383763fae79SScott Long 			printf(":\n ");
384763fae79SScott Long 		}
385763fae79SScott Long 		break;
386763fae79SScott Long 	case MR_EVT_ARGS_LD:
387763fae79SScott Long 		printf("VOL %s event: ", volume_name(fd, &detail->args.ld));
388763fae79SScott Long 		break;
389763fae79SScott Long 	case MR_EVT_ARGS_LD_COUNT:
390763fae79SScott Long 		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
391763fae79SScott Long 		if (verbose) {
392763fae79SScott Long 			printf(" count %lld: ",
393763fae79SScott Long 			    (long long)detail->args.ld_count.count);
394763fae79SScott Long 		}
395763fae79SScott Long 		printf(": ");
396763fae79SScott Long 		break;
397763fae79SScott Long 	case MR_EVT_ARGS_LD_LBA:
398*c6ad7dcdSWHR 		printf("VOL %s", volume_name(fd, &detail->args.ld_lba.ld));
399763fae79SScott Long 		if (verbose) {
400763fae79SScott Long 			printf(" lba %lld",
401763fae79SScott Long 			    (long long)detail->args.ld_lba.lba);
402763fae79SScott Long 		}
403763fae79SScott Long 		printf(": ");
404763fae79SScott Long 		break;
405763fae79SScott Long 	case MR_EVT_ARGS_LD_OWNER:
406*c6ad7dcdSWHR 		printf("VOL %s", volume_name(fd, &detail->args.ld_owner.ld));
407763fae79SScott Long 		if (verbose) {
408763fae79SScott Long 			printf(" owner changed: prior %d, new %d",
409763fae79SScott Long 			    detail->args.ld_owner.pre_owner,
410763fae79SScott Long 			    detail->args.ld_owner.new_owner);
411763fae79SScott Long 		}
412763fae79SScott Long 		printf(": ");
413763fae79SScott Long 		break;
414763fae79SScott Long 	case MR_EVT_ARGS_LD_LBA_PD_LBA:
415*c6ad7dcdSWHR 		printf("VOL %s", volume_name(fd, &detail->args.ld_lba_pd_lba.ld));
416763fae79SScott Long 		if (verbose) {
417763fae79SScott Long 			printf(" lba %lld, physical drive PD %s lba %lld",
418763fae79SScott Long 			    (long long)detail->args.ld_lba_pd_lba.ld_lba,
419763fae79SScott Long 			    pdrive_location(&detail->args.ld_lba_pd_lba.pd),
420763fae79SScott Long 			    (long long)detail->args.ld_lba_pd_lba.pd_lba);
421763fae79SScott Long 		}
422763fae79SScott Long 		printf(": ");
423763fae79SScott Long 		break;
424763fae79SScott Long 	case MR_EVT_ARGS_LD_PROG:
425763fae79SScott Long 		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
426763fae79SScott Long 		if (verbose) {
427763fae79SScott Long 			printf(" progress %d%% in %ds",
428763fae79SScott Long 			    detail->args.ld_prog.prog.progress/655,
429763fae79SScott Long 			    detail->args.ld_prog.prog.elapsed_seconds);
430763fae79SScott Long 		}
431763fae79SScott Long 		printf(": ");
432763fae79SScott Long 		break;
433763fae79SScott Long 	case MR_EVT_ARGS_LD_STATE:
434*c6ad7dcdSWHR 		printf("VOL %s", volume_name(fd, &detail->args.ld_state.ld));
435763fae79SScott Long 		if (verbose) {
436763fae79SScott Long 			printf(" state prior %s new %s",
437763fae79SScott Long 			    mfi_ldstate(detail->args.ld_state.prev_state),
438763fae79SScott Long 			    mfi_ldstate(detail->args.ld_state.new_state));
439763fae79SScott Long 		}
440763fae79SScott Long 		printf(": ");
441763fae79SScott Long 		break;
442763fae79SScott Long 	case MR_EVT_ARGS_LD_STRIP:
4431b202316SJohn Baldwin 		printf("VOL %s", volume_name(fd, &detail->args.ld_strip.ld));
444763fae79SScott Long 		if (verbose) {
445763fae79SScott Long 			printf(" strip %lld",
446763fae79SScott Long 			    (long long)detail->args.ld_strip.strip);
447763fae79SScott Long 		}
448763fae79SScott Long 		printf(": ");
449763fae79SScott Long 		break;
450763fae79SScott Long 	case MR_EVT_ARGS_PD:
451763fae79SScott Long 		if (verbose) {
452763fae79SScott Long 			printf("PD %s event: ",
453763fae79SScott Long 			    pdrive_location(&detail->args.pd));
454763fae79SScott Long 		}
455763fae79SScott Long 		break;
456763fae79SScott Long 	case MR_EVT_ARGS_PD_ERR:
457763fae79SScott Long 		if (verbose) {
458763fae79SScott Long 			printf("PD %s err %d: ",
459763fae79SScott Long 			    pdrive_location(&detail->args.pd_err.pd),
460763fae79SScott Long 			    detail->args.pd_err.err);
461763fae79SScott Long 		}
462763fae79SScott Long 		break;
463763fae79SScott Long 	case MR_EVT_ARGS_PD_LBA:
464763fae79SScott Long 		if (verbose) {
465763fae79SScott Long 			printf("PD %s lba %lld: ",
466763fae79SScott Long 			    pdrive_location(&detail->args.pd_lba.pd),
467763fae79SScott Long 			    (long long)detail->args.pd_lba.lba);
468763fae79SScott Long 		}
469763fae79SScott Long 		break;
470763fae79SScott Long 	case MR_EVT_ARGS_PD_LBA_LD:
471763fae79SScott Long 		if (verbose) {
472763fae79SScott Long 			printf("PD %s lba %lld VOL %s: ",
473763fae79SScott Long 			    pdrive_location(&detail->args.pd_lba_ld.pd),
474763fae79SScott Long 			    (long long)detail->args.pd_lba.lba,
475763fae79SScott Long 			    volume_name(fd, &detail->args.pd_lba_ld.ld));
476763fae79SScott Long 		}
477763fae79SScott Long 		break;
478763fae79SScott Long 	case MR_EVT_ARGS_PD_PROG:
479763fae79SScott Long 		if (verbose) {
480763fae79SScott Long 			printf("PD %s progress %d%% seconds %ds: ",
481763fae79SScott Long 			    pdrive_location(&detail->args.pd_prog.pd),
482763fae79SScott Long 			    detail->args.pd_prog.prog.progress/655,
483763fae79SScott Long 			    detail->args.pd_prog.prog.elapsed_seconds);
484763fae79SScott Long 		}
485763fae79SScott Long 		break;
486763fae79SScott Long 	case MR_EVT_ARGS_PD_STATE:
487763fae79SScott Long 		if (verbose) {
488763fae79SScott Long 			printf("PD %s state prior %s new %s: ",
489*c6ad7dcdSWHR 			    pdrive_location(&detail->args.pd_state.pd),
490763fae79SScott Long 			    mfi_pdstate(detail->args.pd_state.prev_state),
491763fae79SScott Long 			    mfi_pdstate(detail->args.pd_state.new_state));
492763fae79SScott Long 		}
493763fae79SScott Long 		break;
494763fae79SScott Long 	case MR_EVT_ARGS_PCI:
495763fae79SScott Long 		if (verbose) {
496763fae79SScott Long 			printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ",
497763fae79SScott Long 			    detail->args.pci.venderId,
498763fae79SScott Long 			    detail->args.pci.deviceId,
499763fae79SScott Long 			    detail->args.pci.subVenderId,
500763fae79SScott Long 			    detail->args.pci.subDeviceId);
501763fae79SScott Long 		}
502763fae79SScott Long 		break;
503763fae79SScott Long 	case MR_EVT_ARGS_RATE:
504763fae79SScott Long 		if (verbose) {
505763fae79SScott Long 			printf("Rebuild rate %d: ", detail->args.rate);
506763fae79SScott Long 		}
507763fae79SScott Long 		break;
508763fae79SScott Long 	case MR_EVT_ARGS_TIME:
509763fae79SScott Long 		if (verbose) {
510763fae79SScott Long 			printf("Adapter time %s; %d seconds since power on: ",
511763fae79SScott Long 			    format_timestamp(detail->args.time.rtc),
512763fae79SScott Long 			    detail->args.time.elapsedSeconds);
513763fae79SScott Long 		}
514763fae79SScott Long 		break;
515763fae79SScott Long 	case MR_EVT_ARGS_ECC:
516763fae79SScott Long 		if (verbose) {
517763fae79SScott Long 			printf("Adapter ECC %x,%x: %s: ",
518763fae79SScott Long 			    detail->args.ecc.ecar,
519763fae79SScott Long 			    detail->args.ecc.elog,
520763fae79SScott Long 			    detail->args.ecc.str);
521763fae79SScott Long 		}
522763fae79SScott Long 		break;
523763fae79SScott Long 	default:
524763fae79SScott Long 		if (verbose) {
525763fae79SScott Long 			printf("Type %d: ", detail->arg_type);
526763fae79SScott Long 		}
527763fae79SScott Long 		break;
528763fae79SScott Long 	}
529763fae79SScott Long 	printf("%s\n", detail->description);
530763fae79SScott Long }
531763fae79SScott Long 
532763fae79SScott Long static int
533763fae79SScott Long show_events(int ac, char **av)
534763fae79SScott Long {
535763fae79SScott Long 	struct mfi_evt_log_state info;
536763fae79SScott Long 	struct mfi_evt_list *list;
537763fae79SScott Long 	union mfi_evt filter;
538186ddf96SJohn Baldwin 	bool first;
539763fae79SScott Long 	long val;
540763fae79SScott Long 	char *cp;
541763fae79SScott Long 	ssize_t size;
542763fae79SScott Long 	uint32_t seq, start, stop;
5433ac0e476SDimitry Andric 	uint16_t locale;
544763fae79SScott Long 	uint8_t status;
545c02999d9SJohn Baldwin 	int ch, error, fd, num_events, verbose;
546763fae79SScott Long 	u_int i;
547763fae79SScott Long 
5487e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDWR);
549763fae79SScott Long 	if (fd < 0) {
550c02999d9SJohn Baldwin 		error = errno;
551763fae79SScott Long 		warn("mfi_open");
552c02999d9SJohn Baldwin 		return (error);
553763fae79SScott Long 	}
554763fae79SScott Long 
555763fae79SScott Long 	if (mfi_event_get_info(fd, &info, NULL) < 0) {
556c02999d9SJohn Baldwin 		error = errno;
557763fae79SScott Long 		warn("Failed to get event log info");
558375c4656SBjoern A. Zeeb 		close(fd);
559c02999d9SJohn Baldwin 		return (error);
560763fae79SScott Long 	}
561763fae79SScott Long 
562763fae79SScott Long 	/* Default settings. */
563763fae79SScott Long 	num_events = 15;
564763fae79SScott Long 	filter.members.reserved = 0;
565763fae79SScott Long 	filter.members.locale = MFI_EVT_LOCALE_ALL;
566aacea6e2SEd Maste 	filter.members.evt_class = MFI_EVT_CLASS_WARNING;
567763fae79SScott Long 	start = info.boot_seq_num;
568763fae79SScott Long 	stop = info.newest_seq_num;
569763fae79SScott Long 	verbose = 0;
570763fae79SScott Long 
571763fae79SScott Long 	/* Parse any options. */
572763fae79SScott Long 	optind = 1;
573763fae79SScott Long 	while ((ch = getopt(ac, av, "c:l:n:v")) != -1) {
574763fae79SScott Long 		switch (ch) {
575763fae79SScott Long 		case 'c':
576aacea6e2SEd Maste 			if (parse_class(optarg, &filter.members.evt_class) < 0) {
577c02999d9SJohn Baldwin 				error = errno;
578763fae79SScott Long 				warn("Error parsing event class");
579375c4656SBjoern A. Zeeb 				close(fd);
580c02999d9SJohn Baldwin 				return (error);
581763fae79SScott Long 			}
582763fae79SScott Long 			break;
583763fae79SScott Long 		case 'l':
5843ac0e476SDimitry Andric 			if (parse_locale(optarg, &locale) < 0) {
585c02999d9SJohn Baldwin 				error = errno;
586763fae79SScott Long 				warn("Error parsing event locale");
587375c4656SBjoern A. Zeeb 				close(fd);
588c02999d9SJohn Baldwin 				return (error);
589763fae79SScott Long 			}
5903ac0e476SDimitry Andric 			filter.members.locale = locale;
591763fae79SScott Long 			break;
592763fae79SScott Long 		case 'n':
593763fae79SScott Long 			val = strtol(optarg, &cp, 0);
594763fae79SScott Long 			if (*cp != '\0' || val <= 0) {
595763fae79SScott Long 				warnx("Invalid event count");
596375c4656SBjoern A. Zeeb 				close(fd);
597763fae79SScott Long 				return (EINVAL);
598763fae79SScott Long 			}
599763fae79SScott Long 			num_events = val;
600763fae79SScott Long 			break;
601763fae79SScott Long 		case 'v':
602763fae79SScott Long 			verbose = 1;
603763fae79SScott Long 			break;
604763fae79SScott Long 		case '?':
605763fae79SScott Long 		default:
606375c4656SBjoern A. Zeeb 			close(fd);
607763fae79SScott Long 			return (EINVAL);
608763fae79SScott Long 		}
609763fae79SScott Long 	}
610763fae79SScott Long 	ac -= optind;
611763fae79SScott Long 	av += optind;
612763fae79SScott Long 
613763fae79SScott Long 	/* Determine buffer size and validate it. */
614763fae79SScott Long 	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
615763fae79SScott Long 	    (num_events - 1);
616763fae79SScott Long 	if (size > getpagesize()) {
617763fae79SScott Long 		warnx("Event count is too high");
618375c4656SBjoern A. Zeeb 		close(fd);
619763fae79SScott Long 		return (EINVAL);
620763fae79SScott Long 	}
621763fae79SScott Long 
622763fae79SScott Long 	/* Handle optional start and stop sequence numbers. */
623763fae79SScott Long 	if (ac > 2) {
624763fae79SScott Long 		warnx("show events: extra arguments");
625375c4656SBjoern A. Zeeb 		close(fd);
626763fae79SScott Long 		return (EINVAL);
627763fae79SScott Long 	}
628763fae79SScott Long 	if (ac > 0 && parse_seq(&info, av[0], &start) < 0) {
629c02999d9SJohn Baldwin 		error = errno;
630763fae79SScott Long 		warn("Error parsing starting sequence number");
631375c4656SBjoern A. Zeeb 		close(fd);
632c02999d9SJohn Baldwin 		return (error);
633763fae79SScott Long 	}
634763fae79SScott Long 	if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) {
635c02999d9SJohn Baldwin 		error = errno;
636763fae79SScott Long 		warn("Error parsing ending sequence number");
637375c4656SBjoern A. Zeeb 		close(fd);
638c02999d9SJohn Baldwin 		return (error);
639763fae79SScott Long 	}
640763fae79SScott Long 
641763fae79SScott Long 	list = malloc(size);
6423c22a809SJohn Baldwin 	if (list == NULL) {
6433c22a809SJohn Baldwin 		warnx("malloc failed");
644375c4656SBjoern A. Zeeb 		close(fd);
6453c22a809SJohn Baldwin 		return (ENOMEM);
6463c22a809SJohn Baldwin 	}
647186ddf96SJohn Baldwin 	first = true;
648186ddf96SJohn Baldwin 	seq = start;
649186ddf96SJohn Baldwin 	for (;;) {
650763fae79SScott Long 		if (mfi_get_events(fd, list, num_events, filter, seq,
651763fae79SScott Long 		    &status) < 0) {
652c02999d9SJohn Baldwin 			error = errno;
653763fae79SScott Long 			warn("Failed to fetch events");
654375c4656SBjoern A. Zeeb 			free(list);
655375c4656SBjoern A. Zeeb 			close(fd);
656c02999d9SJohn Baldwin 			return (error);
657763fae79SScott Long 		}
658763fae79SScott Long 		if (status == MFI_STAT_NOT_FOUND) {
659763fae79SScott Long 			break;
660763fae79SScott Long 		}
661763fae79SScott Long 		if (status != MFI_STAT_OK) {
662763fae79SScott Long 			warnx("Error fetching events: %s", mfi_status(status));
663375c4656SBjoern A. Zeeb 			free(list);
664375c4656SBjoern A. Zeeb 			close(fd);
665763fae79SScott Long 			return (EIO);
666763fae79SScott Long 		}
667763fae79SScott Long 
668763fae79SScott Long 		for (i = 0; i < list->count; i++) {
669763fae79SScott Long 			/*
670763fae79SScott Long 			 * If this event is newer than 'stop_seq' then
671763fae79SScott Long 			 * break out of the loop.  Note that the log
672763fae79SScott Long 			 * is a circular buffer so we have to handle
673763fae79SScott Long 			 * the case that our stop point is earlier in
674763fae79SScott Long 			 * the buffer than our start point.
675763fae79SScott Long 			 */
676186ddf96SJohn Baldwin 			if (list->event[i].seq > stop) {
677763fae79SScott Long 				if (start <= stop)
678186ddf96SJohn Baldwin 					goto finish;
679763fae79SScott Long 				else if (list->event[i].seq < start)
680186ddf96SJohn Baldwin 					goto finish;
681763fae79SScott Long 			}
682763fae79SScott Long 			mfi_decode_evt(fd, &list->event[i], verbose);
683186ddf96SJohn Baldwin 			first = false;
684763fae79SScott Long 		}
685763fae79SScott Long 
686763fae79SScott Long 		/*
687763fae79SScott Long 		 * XXX: If the event's seq # is the end of the buffer
688763fae79SScott Long 		 * then this probably won't do the right thing.  We
689763fae79SScott Long 		 * need to know the size of the buffer somehow.
690763fae79SScott Long 		 */
691763fae79SScott Long 		seq = list->event[list->count - 1].seq + 1;
692763fae79SScott Long 
693763fae79SScott Long 	}
694186ddf96SJohn Baldwin finish:
695186ddf96SJohn Baldwin 	if (first)
696186ddf96SJohn Baldwin 		warnx("No matching events found");
697763fae79SScott Long 
698763fae79SScott Long 	free(list);
699763fae79SScott Long 	close(fd);
700763fae79SScott Long 
701763fae79SScott Long 	return (0);
702763fae79SScott Long }
703763fae79SScott Long MFI_COMMAND(show, events, show_events);
704