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