xref: /freebsd/usr.sbin/mfiutil/mfi_evt.c (revision d4eeb02986980bf33dd56c41ceb9fc5f180c0d47)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2008, 2009 Yahoo!, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The names of the authors may not be used to endorse or promote
16  *    products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 
34 #include <sys/types.h>
35 #include <sys/errno.h>
36 #include <err.h>
37 #include <fcntl.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <strings.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include "mfiutil.h"
45 
46 static int
47 mfi_event_get_info(int fd, struct mfi_evt_log_state *info, uint8_t *statusp)
48 {
49 
50 	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GETINFO, info,
51 	    sizeof(struct mfi_evt_log_state), NULL, 0, statusp));
52 }
53 
54 static int
55 mfi_get_events(int fd, struct mfi_evt_list *list, int num_events,
56     union mfi_evt filter, uint32_t start_seq, uint8_t *statusp)
57 {
58 	uint32_t mbox[2];
59 	size_t size;
60 
61 	mbox[0] = start_seq;
62 	mbox[1] = filter.word;
63 	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
64 	    (num_events - 1);
65 	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size,
66 	    (uint8_t *)&mbox, sizeof(mbox), statusp));
67 }
68 
69 static int
70 show_logstate(int ac, char **av __unused)
71 {
72 	struct mfi_evt_log_state info;
73 	int error, fd;
74 
75 	if (ac != 1) {
76 		warnx("show logstate: extra arguments");
77 		return (EINVAL);
78 	}
79 
80 	fd = mfi_open(mfi_unit, O_RDWR);
81 	if (fd < 0) {
82 		error = errno;
83 		warn("mfi_open");
84 		return (error);
85 	}
86 
87 	if (mfi_event_get_info(fd, &info, NULL) < 0) {
88 		error = errno;
89 		warn("Failed to get event log info");
90 		close(fd);
91 		return (error);
92 	}
93 
94 	printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit);
95 	printf("  Newest Seq #: %u\n", info.newest_seq_num);
96 	printf("  Oldest Seq #: %u\n", info.oldest_seq_num);
97 	printf("   Clear Seq #: %u\n", info.clear_seq_num);
98 	printf("Shutdown Seq #: %u\n", info.shutdown_seq_num);
99 	printf("    Boot Seq #: %u\n", info.boot_seq_num);
100 
101 	close(fd);
102 
103 	return (0);
104 }
105 MFI_COMMAND(show, logstate, show_logstate);
106 
107 static int
108 parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq)
109 {
110 	char *cp;
111 	long val;
112 
113 	if (strcasecmp(arg, "newest") == 0) {
114 		*seq = info->newest_seq_num;
115 		return (0);
116 	}
117 	if (strcasecmp(arg, "oldest") == 0) {
118 		*seq = info->oldest_seq_num;
119 		return (0);
120 	}
121 	if (strcasecmp(arg, "clear") == 0) {
122 		*seq = info->clear_seq_num;
123 		return (0);
124 	}
125 	if (strcasecmp(arg, "shutdown") == 0) {
126 		*seq = info->shutdown_seq_num;
127 		return (0);
128 	}
129 	if (strcasecmp(arg, "boot") == 0) {
130 		*seq = info->boot_seq_num;
131 		return (0);
132 	}
133 	val = strtol(arg, &cp, 0);
134 	if (*cp != '\0' || val < 0) {
135 		errno = EINVAL;
136 		return (-1);
137 	}
138 	*seq = val;
139 	return (0);
140 }
141 
142 static int
143 parse_locale(char *arg, uint16_t *locale)
144 {
145 	char *cp;
146 	long val;
147 
148 	if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) {
149 		*locale = MFI_EVT_LOCALE_LD;
150 		return (0);
151 	}
152 	if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) {
153 		*locale = MFI_EVT_LOCALE_PD;
154 		return (0);
155 	}
156 	if (strncasecmp(arg, "encl", 4) == 0) {
157 		*locale = MFI_EVT_LOCALE_ENCL;
158 		return (0);
159 	}
160 	if (strncasecmp(arg, "batt", 4) == 0 ||
161 	    strncasecmp(arg, "bbu", 3) == 0) {
162 		*locale = MFI_EVT_LOCALE_BBU;
163 		return (0);
164 	}
165 	if (strcasecmp(arg, "sas") == 0) {
166 		*locale = MFI_EVT_LOCALE_SAS;
167 		return (0);
168 	}
169 	if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) {
170 		*locale = MFI_EVT_LOCALE_CTRL;
171 		return (0);
172 	}
173 	if (strcasecmp(arg, "config") == 0) {
174 		*locale = MFI_EVT_LOCALE_CONFIG;
175 		return (0);
176 	}
177 	if (strcasecmp(arg, "cluster") == 0) {
178 		*locale = MFI_EVT_LOCALE_CLUSTER;
179 		return (0);
180 	}
181 	if (strcasecmp(arg, "all") == 0) {
182 		*locale = MFI_EVT_LOCALE_ALL;
183 		return (0);
184 	}
185 	val = strtol(arg, &cp, 0);
186 	if (*cp != '\0' || val < 0 || val > 0xffff) {
187 		errno = EINVAL;
188 		return (-1);
189 	}
190 	*locale = val;
191 	return (0);
192 }
193 
194 static int
195 parse_class(char *arg, int8_t *class)
196 {
197 	char *cp;
198 	long val;
199 
200 	if (strcasecmp(arg, "debug") == 0) {
201 		*class = MFI_EVT_CLASS_DEBUG;
202 		return (0);
203 	}
204 	if (strncasecmp(arg, "prog", 4) == 0) {
205 		*class = MFI_EVT_CLASS_PROGRESS;
206 		return (0);
207 	}
208 	if (strncasecmp(arg, "info", 4) == 0) {
209 		*class = MFI_EVT_CLASS_INFO;
210 		return (0);
211 	}
212 	if (strncasecmp(arg, "warn", 4) == 0) {
213 		*class = MFI_EVT_CLASS_WARNING;
214 		return (0);
215 	}
216 	if (strncasecmp(arg, "crit", 4) == 0) {
217 		*class = MFI_EVT_CLASS_CRITICAL;
218 		return (0);
219 	}
220 	if (strcasecmp(arg, "fatal") == 0) {
221 		*class = MFI_EVT_CLASS_FATAL;
222 		return (0);
223 	}
224 	if (strcasecmp(arg, "dead") == 0) {
225 		*class = MFI_EVT_CLASS_DEAD;
226 		return (0);
227 	}
228 	val = strtol(arg, &cp, 0);
229 	if (*cp != '\0' || val < -128 || val > 127) {
230 		errno = EINVAL;
231 		return (-1);
232 	}
233 	*class = val;
234 	return (0);
235 }
236 
237 /*
238  * The timestamp is the number of seconds since 00:00 Jan 1, 2000.  If
239  * the bits in 24-31 are all set, then it is the number of seconds since
240  * boot.
241  */
242 static const char *
243 format_timestamp(uint32_t timestamp)
244 {
245 	static char buffer[32];
246 	static time_t base;
247 	time_t t;
248 	struct tm tm;
249 
250 	if ((timestamp & 0xff000000) == 0xff000000) {
251 		snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
252 		    0x00ffffff);
253 		return (buffer);
254 	}
255 
256 	if (base == 0) {
257 		/* Compute 00:00 Jan 1, 2000 offset. */
258 		bzero(&tm, sizeof(tm));
259 		tm.tm_mday = 1;
260 		tm.tm_year = (2000 - 1900);
261 		base = mktime(&tm);
262 	}
263 	if (base == -1) {
264 		snprintf(buffer, sizeof(buffer), "%us", timestamp);
265 		return (buffer);
266 	}
267 	t = base + timestamp;
268 	strftime(buffer, sizeof(buffer), "%+", localtime(&t));
269 	return (buffer);
270 }
271 
272 static const char *
273 format_locale(uint16_t locale)
274 {
275 	static char buffer[8];
276 
277 	switch (locale) {
278 	case MFI_EVT_LOCALE_LD:
279 		return ("VOLUME");
280 	case MFI_EVT_LOCALE_PD:
281 		return ("DRIVE");
282 	case MFI_EVT_LOCALE_ENCL:
283 		return ("ENCL");
284 	case MFI_EVT_LOCALE_BBU:
285 		return ("BATTERY");
286 	case MFI_EVT_LOCALE_SAS:
287 		return ("SAS");
288 	case MFI_EVT_LOCALE_CTRL:
289 		return ("CTRL");
290 	case MFI_EVT_LOCALE_CONFIG:
291 		return ("CONFIG");
292 	case MFI_EVT_LOCALE_CLUSTER:
293 		return ("CLUSTER");
294 	case MFI_EVT_LOCALE_ALL:
295 		return ("ALL");
296 	default:
297 		snprintf(buffer, sizeof(buffer), "0x%04x", locale);
298 		return (buffer);
299 	}
300 }
301 
302 static const char *
303 format_class(int8_t class)
304 {
305 	static char buffer[6];
306 
307 	switch (class) {
308 	case MFI_EVT_CLASS_DEBUG:
309 		return ("debug");
310 	case MFI_EVT_CLASS_PROGRESS:
311 		return ("progress");
312 	case MFI_EVT_CLASS_INFO:
313 		return ("info");
314 	case MFI_EVT_CLASS_WARNING:
315 		return ("WARN");
316 	case MFI_EVT_CLASS_CRITICAL:
317 		return ("CRIT");
318 	case MFI_EVT_CLASS_FATAL:
319 		return ("FATAL");
320 	case MFI_EVT_CLASS_DEAD:
321 		return ("DEAD");
322 	default:
323 		snprintf(buffer, sizeof(buffer), "%d", class);
324 		return (buffer);
325 	}
326 }
327 
328 /* Simulates %D from kernel printf(9). */
329 static void
330 simple_hex(void *ptr, size_t length, const char *separator)
331 {
332 	unsigned char *cp;
333 	u_int i;
334 
335 	if (length == 0)
336 		return;
337 	cp = ptr;
338 	printf("%02x", cp[0]);
339 	for (i = 1; i < length; i++)
340 		printf("%s%02x", separator, cp[i]);
341 }
342 
343 static const char *
344 pdrive_location(struct mfi_evt_pd *pd)
345 {
346 	static char buffer[16];
347 
348 	if (pd->enclosure_index == 0)
349 		snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id,
350 		    pd->slot_number);
351 	else
352 		snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id,
353 		    pd->enclosure_index, pd->slot_number);
354 	return (buffer);
355 }
356 
357 static const char *
358 volume_name(int fd, struct mfi_evt_ld *ld)
359 {
360 
361 	return (mfi_volume_name(fd, ld->target_id));
362 }
363 
364 /* Ripped from sys/dev/mfi/mfi.c. */
365 static void
366 mfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose)
367 {
368 
369 	printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time),
370 	    format_locale(detail->evt_class.members.locale),
371 	    format_class(detail->evt_class.members.evt_class));
372 	switch (detail->arg_type) {
373 	case MR_EVT_ARGS_NONE:
374 		break;
375 	case MR_EVT_ARGS_CDB_SENSE:
376 		if (verbose) {
377 			printf("PD %s CDB ",
378 			    pdrive_location(&detail->args.cdb_sense.pd)
379 			    );
380 			simple_hex(detail->args.cdb_sense.cdb,
381 			    detail->args.cdb_sense.cdb_len, ":");
382 			printf(" Sense ");
383 			simple_hex(detail->args.cdb_sense.sense,
384 			    detail->args.cdb_sense.sense_len, ":");
385 			printf(":\n ");
386 		}
387 		break;
388 	case MR_EVT_ARGS_LD:
389 		printf("VOL %s event: ", volume_name(fd, &detail->args.ld));
390 		break;
391 	case MR_EVT_ARGS_LD_COUNT:
392 		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
393 		if (verbose) {
394 			printf(" count %lld: ",
395 			    (long long)detail->args.ld_count.count);
396 		}
397 		printf(": ");
398 		break;
399 	case MR_EVT_ARGS_LD_LBA:
400 		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
401 		if (verbose) {
402 			printf(" lba %lld",
403 			    (long long)detail->args.ld_lba.lba);
404 		}
405 		printf(": ");
406 		break;
407 	case MR_EVT_ARGS_LD_OWNER:
408 		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
409 		if (verbose) {
410 			printf(" owner changed: prior %d, new %d",
411 			    detail->args.ld_owner.pre_owner,
412 			    detail->args.ld_owner.new_owner);
413 		}
414 		printf(": ");
415 		break;
416 	case MR_EVT_ARGS_LD_LBA_PD_LBA:
417 		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
418 		if (verbose) {
419 			printf(" lba %lld, physical drive PD %s lba %lld",
420 			    (long long)detail->args.ld_lba_pd_lba.ld_lba,
421 			    pdrive_location(&detail->args.ld_lba_pd_lba.pd),
422 			    (long long)detail->args.ld_lba_pd_lba.pd_lba);
423 		}
424 		printf(": ");
425 		break;
426 	case MR_EVT_ARGS_LD_PROG:
427 		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
428 		if (verbose) {
429 			printf(" progress %d%% in %ds",
430 			    detail->args.ld_prog.prog.progress/655,
431 			    detail->args.ld_prog.prog.elapsed_seconds);
432 		}
433 		printf(": ");
434 		break;
435 	case MR_EVT_ARGS_LD_STATE:
436 		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
437 		if (verbose) {
438 			printf(" state prior %s new %s",
439 			    mfi_ldstate(detail->args.ld_state.prev_state),
440 			    mfi_ldstate(detail->args.ld_state.new_state));
441 		}
442 		printf(": ");
443 		break;
444 	case MR_EVT_ARGS_LD_STRIP:
445 		printf("VOL %s", volume_name(fd, &detail->args.ld_strip.ld));
446 		if (verbose) {
447 			printf(" strip %lld",
448 			    (long long)detail->args.ld_strip.strip);
449 		}
450 		printf(": ");
451 		break;
452 	case MR_EVT_ARGS_PD:
453 		if (verbose) {
454 			printf("PD %s event: ",
455 			    pdrive_location(&detail->args.pd));
456 		}
457 		break;
458 	case MR_EVT_ARGS_PD_ERR:
459 		if (verbose) {
460 			printf("PD %s err %d: ",
461 			    pdrive_location(&detail->args.pd_err.pd),
462 			    detail->args.pd_err.err);
463 		}
464 		break;
465 	case MR_EVT_ARGS_PD_LBA:
466 		if (verbose) {
467 			printf("PD %s lba %lld: ",
468 			    pdrive_location(&detail->args.pd_lba.pd),
469 			    (long long)detail->args.pd_lba.lba);
470 		}
471 		break;
472 	case MR_EVT_ARGS_PD_LBA_LD:
473 		if (verbose) {
474 			printf("PD %s lba %lld VOL %s: ",
475 			    pdrive_location(&detail->args.pd_lba_ld.pd),
476 			    (long long)detail->args.pd_lba.lba,
477 			    volume_name(fd, &detail->args.pd_lba_ld.ld));
478 		}
479 		break;
480 	case MR_EVT_ARGS_PD_PROG:
481 		if (verbose) {
482 			printf("PD %s progress %d%% seconds %ds: ",
483 			    pdrive_location(&detail->args.pd_prog.pd),
484 			    detail->args.pd_prog.prog.progress/655,
485 			    detail->args.pd_prog.prog.elapsed_seconds);
486 		}
487 		break;
488 	case MR_EVT_ARGS_PD_STATE:
489 		if (verbose) {
490 			printf("PD %s state prior %s new %s: ",
491 			    pdrive_location(&detail->args.pd_prog.pd),
492 			    mfi_pdstate(detail->args.pd_state.prev_state),
493 			    mfi_pdstate(detail->args.pd_state.new_state));
494 		}
495 		break;
496 	case MR_EVT_ARGS_PCI:
497 		if (verbose) {
498 			printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ",
499 			    detail->args.pci.venderId,
500 			    detail->args.pci.deviceId,
501 			    detail->args.pci.subVenderId,
502 			    detail->args.pci.subDeviceId);
503 		}
504 		break;
505 	case MR_EVT_ARGS_RATE:
506 		if (verbose) {
507 			printf("Rebuild rate %d: ", detail->args.rate);
508 		}
509 		break;
510 	case MR_EVT_ARGS_TIME:
511 		if (verbose) {
512 			printf("Adapter time %s; %d seconds since power on: ",
513 			    format_timestamp(detail->args.time.rtc),
514 			    detail->args.time.elapsedSeconds);
515 		}
516 		break;
517 	case MR_EVT_ARGS_ECC:
518 		if (verbose) {
519 			printf("Adapter ECC %x,%x: %s: ",
520 			    detail->args.ecc.ecar,
521 			    detail->args.ecc.elog,
522 			    detail->args.ecc.str);
523 		}
524 		break;
525 	default:
526 		if (verbose) {
527 			printf("Type %d: ", detail->arg_type);
528 		}
529 		break;
530 	}
531 	printf("%s\n", detail->description);
532 }
533 
534 static int
535 show_events(int ac, char **av)
536 {
537 	struct mfi_evt_log_state info;
538 	struct mfi_evt_list *list;
539 	union mfi_evt filter;
540 	bool first;
541 	long val;
542 	char *cp;
543 	ssize_t size;
544 	uint32_t seq, start, stop;
545 	uint16_t locale;
546 	uint8_t status;
547 	int ch, error, fd, num_events, verbose;
548 	u_int i;
549 
550 	fd = mfi_open(mfi_unit, O_RDWR);
551 	if (fd < 0) {
552 		error = errno;
553 		warn("mfi_open");
554 		return (error);
555 	}
556 
557 	if (mfi_event_get_info(fd, &info, NULL) < 0) {
558 		error = errno;
559 		warn("Failed to get event log info");
560 		close(fd);
561 		return (error);
562 	}
563 
564 	/* Default settings. */
565 	num_events = 15;
566 	filter.members.reserved = 0;
567 	filter.members.locale = MFI_EVT_LOCALE_ALL;
568 	filter.members.evt_class = MFI_EVT_CLASS_WARNING;
569 	start = info.boot_seq_num;
570 	stop = info.newest_seq_num;
571 	verbose = 0;
572 
573 	/* Parse any options. */
574 	optind = 1;
575 	while ((ch = getopt(ac, av, "c:l:n:v")) != -1) {
576 		switch (ch) {
577 		case 'c':
578 			if (parse_class(optarg, &filter.members.evt_class) < 0) {
579 				error = errno;
580 				warn("Error parsing event class");
581 				close(fd);
582 				return (error);
583 			}
584 			break;
585 		case 'l':
586 			if (parse_locale(optarg, &locale) < 0) {
587 				error = errno;
588 				warn("Error parsing event locale");
589 				close(fd);
590 				return (error);
591 			}
592 			filter.members.locale = locale;
593 			break;
594 		case 'n':
595 			val = strtol(optarg, &cp, 0);
596 			if (*cp != '\0' || val <= 0) {
597 				warnx("Invalid event count");
598 				close(fd);
599 				return (EINVAL);
600 			}
601 			num_events = val;
602 			break;
603 		case 'v':
604 			verbose = 1;
605 			break;
606 		case '?':
607 		default:
608 			close(fd);
609 			return (EINVAL);
610 		}
611 	}
612 	ac -= optind;
613 	av += optind;
614 
615 	/* Determine buffer size and validate it. */
616 	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
617 	    (num_events - 1);
618 	if (size > getpagesize()) {
619 		warnx("Event count is too high");
620 		close(fd);
621 		return (EINVAL);
622 	}
623 
624 	/* Handle optional start and stop sequence numbers. */
625 	if (ac > 2) {
626 		warnx("show events: extra arguments");
627 		close(fd);
628 		return (EINVAL);
629 	}
630 	if (ac > 0 && parse_seq(&info, av[0], &start) < 0) {
631 		error = errno;
632 		warn("Error parsing starting sequence number");
633 		close(fd);
634 		return (error);
635 	}
636 	if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) {
637 		error = errno;
638 		warn("Error parsing ending sequence number");
639 		close(fd);
640 		return (error);
641 	}
642 
643 	list = malloc(size);
644 	if (list == NULL) {
645 		warnx("malloc failed");
646 		close(fd);
647 		return (ENOMEM);
648 	}
649 	first = true;
650 	seq = start;
651 	for (;;) {
652 		if (mfi_get_events(fd, list, num_events, filter, seq,
653 		    &status) < 0) {
654 			error = errno;
655 			warn("Failed to fetch events");
656 			free(list);
657 			close(fd);
658 			return (error);
659 		}
660 		if (status == MFI_STAT_NOT_FOUND) {
661 			break;
662 		}
663 		if (status != MFI_STAT_OK) {
664 			warnx("Error fetching events: %s", mfi_status(status));
665 			free(list);
666 			close(fd);
667 			return (EIO);
668 		}
669 
670 		for (i = 0; i < list->count; i++) {
671 			/*
672 			 * If this event is newer than 'stop_seq' then
673 			 * break out of the loop.  Note that the log
674 			 * is a circular buffer so we have to handle
675 			 * the case that our stop point is earlier in
676 			 * the buffer than our start point.
677 			 */
678 			if (list->event[i].seq > stop) {
679 				if (start <= stop)
680 					goto finish;
681 				else if (list->event[i].seq < start)
682 					goto finish;
683 			}
684 			mfi_decode_evt(fd, &list->event[i], verbose);
685 			first = false;
686 		}
687 
688 		/*
689 		 * XXX: If the event's seq # is the end of the buffer
690 		 * then this probably won't do the right thing.  We
691 		 * need to know the size of the buffer somehow.
692 		 */
693 		seq = list->event[list->count - 1].seq + 1;
694 
695 	}
696 finish:
697 	if (first)
698 		warnx("No matching events found");
699 
700 	free(list);
701 	close(fd);
702 
703 	return (0);
704 }
705 MFI_COMMAND(show, events, show_events);
706