2 * Copyright (c) 2008, 2009 Yahoo!, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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
32 #include <sys/types.h>
33 #include <sys/errno.h>
35 //#include <libutil.h>
44 mfi_event_get_info(int fd, struct mfi_evt_log_state *info, uint8_t *statusp)
47 return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GETINFO, info,
48 sizeof(struct mfi_evt_log_state), NULL, 0, statusp));
52 mfi_get_events(int fd, struct mfi_evt_list *list, int num_events,
53 union mfi_evt filter, uint32_t start_seq, uint8_t *statusp)
59 mbox[1] = filter.word;
60 size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
62 return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size,
63 (uint8_t *)&mbox, sizeof(mbox), statusp));
67 show_logstate(int ac, char **av)
69 struct mfi_evt_log_state info;
73 warnx("show logstate: extra arguments");
77 fd = mfi_open(mfi_unit);
83 if (mfi_event_get_info(fd, &info, NULL) < 0) {
84 warn("Failed to get event log info");
88 printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit);
89 printf(" Newest Seq #: %u\n", info.newest_seq_num);
90 printf(" Oldest Seq #: %u\n", info.oldest_seq_num);
91 printf(" Clear Seq #: %u\n", info.clear_seq_num);
92 printf("Shutdown Seq #: %u\n", info.shutdown_seq_num);
93 printf(" Boot Seq #: %u\n", info.boot_seq_num);
99 MFI_COMMAND(show, logstate, show_logstate);
102 parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq)
107 if (strcasecmp(arg, "newest") == 0) {
108 *seq = info->newest_seq_num;
111 if (strcasecmp(arg, "oldest") == 0) {
112 *seq = info->oldest_seq_num;
115 if (strcasecmp(arg, "clear") == 0) {
116 *seq = info->clear_seq_num;
119 if (strcasecmp(arg, "shutdown") == 0) {
120 *seq = info->shutdown_seq_num;
123 if (strcasecmp(arg, "boot") == 0) {
124 *seq = info->boot_seq_num;
127 val = strtol(arg, &cp, 0);
128 if (*cp != '\0' || val < 0) {
137 parse_locale(char *arg, uint16_t *locale)
142 if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) {
143 *locale = MFI_EVT_LOCALE_LD;
146 if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) {
147 *locale = MFI_EVT_LOCALE_PD;
150 if (strncasecmp(arg, "encl", 4) == 0) {
151 *locale = MFI_EVT_LOCALE_ENCL;
154 if (strncasecmp(arg, "batt", 4) == 0 ||
155 strncasecmp(arg, "bbu", 3) == 0) {
156 *locale = MFI_EVT_LOCALE_BBU;
159 if (strcasecmp(arg, "sas") == 0) {
160 *locale = MFI_EVT_LOCALE_SAS;
163 if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) {
164 *locale = MFI_EVT_LOCALE_CTRL;
167 if (strcasecmp(arg, "config") == 0) {
168 *locale = MFI_EVT_LOCALE_CONFIG;
171 if (strcasecmp(arg, "cluster") == 0) {
172 *locale = MFI_EVT_LOCALE_CLUSTER;
175 if (strcasecmp(arg, "all") == 0) {
176 *locale = MFI_EVT_LOCALE_ALL;
179 val = strtol(arg, &cp, 0);
180 if (*cp != '\0' || val < 0 || val > 0xffff) {
189 parse_class(char *arg, int8_t *class)
194 if (strcasecmp(arg, "debug") == 0) {
195 *class = MFI_EVT_CLASS_DEBUG;
198 if (strncasecmp(arg, "prog", 4) == 0) {
199 *class = MFI_EVT_CLASS_PROGRESS;
202 if (strncasecmp(arg, "info", 4) == 0) {
203 *class = MFI_EVT_CLASS_INFO;
206 if (strncasecmp(arg, "warn", 4) == 0) {
207 *class = MFI_EVT_CLASS_WARNING;
210 if (strncasecmp(arg, "crit", 4) == 0) {
211 *class = MFI_EVT_CLASS_CRITICAL;
214 if (strcasecmp(arg, "fatal") == 0) {
215 *class = MFI_EVT_CLASS_FATAL;
218 if (strcasecmp(arg, "dead") == 0) {
219 *class = MFI_EVT_CLASS_DEAD;
222 val = strtol(arg, &cp, 0);
223 if (*cp != '\0' || val < -128 || val > 127) {
232 * The timestamp is the number of seconds since 00:00 Jan 1, 2000. If
233 * the bits in 24-31 are all set, then it is the number of seconds since
237 format_timestamp(uint32_t timestamp)
239 static char buffer[32];
244 if ((timestamp & 0xff000000) == 0xff000000) {
245 snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
251 /* Compute 00:00 Jan 1, 2000 offset. */
252 bzero(&tm, sizeof(tm));
254 tm.tm_year = (2000 - 1900);
258 snprintf(buffer, sizeof(buffer), "%us", timestamp);
261 t = base + timestamp;
262 strftime(buffer, sizeof(buffer), "%+", localtime(&t));
267 format_locale(uint16_t locale)
269 static char buffer[8];
272 case MFI_EVT_LOCALE_LD:
274 case MFI_EVT_LOCALE_PD:
276 case MFI_EVT_LOCALE_ENCL:
278 case MFI_EVT_LOCALE_BBU:
280 case MFI_EVT_LOCALE_SAS:
282 case MFI_EVT_LOCALE_CTRL:
284 case MFI_EVT_LOCALE_CONFIG:
286 case MFI_EVT_LOCALE_CLUSTER:
288 case MFI_EVT_LOCALE_ALL:
291 snprintf(buffer, sizeof(buffer), "0x%04x", locale);
297 format_class(int8_t class)
299 static char buffer[6];
302 case MFI_EVT_CLASS_DEBUG:
304 case MFI_EVT_CLASS_PROGRESS:
306 case MFI_EVT_CLASS_INFO:
308 case MFI_EVT_CLASS_WARNING:
310 case MFI_EVT_CLASS_CRITICAL:
312 case MFI_EVT_CLASS_FATAL:
314 case MFI_EVT_CLASS_DEAD:
317 snprintf(buffer, sizeof(buffer), "%d", class);
322 /* Simulates %D from kernel printf(9). */
324 simple_hex(void *ptr, size_t length, const char *separator)
332 printf("%02x", cp[0]);
333 for (i = 1; i < length; i++)
334 printf("%s%02x", separator, cp[i]);
338 pdrive_location(struct mfi_evt_pd *pd)
340 static char buffer[16];
342 if (pd->enclosure_index == 0)
343 snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id,
346 snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id,
347 pd->enclosure_index, pd->slot_number);
352 volume_name(int fd, struct mfi_evt_ld *ld)
355 return (mfi_volume_name(fd, ld->target_id));
358 /* Ripped from sys/dev/mfi/mfi.c. */
360 mfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose)
363 printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time),
364 format_locale(detail->class.members.locale),
365 format_class(detail->class.members.class));
366 switch (detail->arg_type) {
367 case MR_EVT_ARGS_NONE:
369 case MR_EVT_ARGS_CDB_SENSE:
372 pdrive_location(&detail->args.cdb_sense.pd)
374 simple_hex(detail->args.cdb_sense.cdb,
375 detail->args.cdb_sense.cdb_len, ":");
377 simple_hex(detail->args.cdb_sense.sense,
378 detail->args.cdb_sense.sense_len, ":");
383 printf("VOL %s event: ", volume_name(fd, &detail->args.ld));
385 case MR_EVT_ARGS_LD_COUNT:
386 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
388 printf(" count %lld: ",
389 (long long)detail->args.ld_count.count);
393 case MR_EVT_ARGS_LD_LBA:
394 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
397 (long long)detail->args.ld_lba.lba);
401 case MR_EVT_ARGS_LD_OWNER:
402 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
404 printf(" owner changed: prior %d, new %d",
405 detail->args.ld_owner.pre_owner,
406 detail->args.ld_owner.new_owner);
410 case MR_EVT_ARGS_LD_LBA_PD_LBA:
411 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
413 printf(" lba %lld, physical drive PD %s lba %lld",
414 (long long)detail->args.ld_lba_pd_lba.ld_lba,
415 pdrive_location(&detail->args.ld_lba_pd_lba.pd),
416 (long long)detail->args.ld_lba_pd_lba.pd_lba);
420 case MR_EVT_ARGS_LD_PROG:
421 printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
423 printf(" progress %d%% in %ds",
424 detail->args.ld_prog.prog.progress/655,
425 detail->args.ld_prog.prog.elapsed_seconds);
429 case MR_EVT_ARGS_LD_STATE:
430 printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
432 printf(" state prior %s new %s",
433 mfi_ldstate(detail->args.ld_state.prev_state),
434 mfi_ldstate(detail->args.ld_state.new_state));
438 case MR_EVT_ARGS_LD_STRIP:
439 printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
441 printf(" strip %lld",
442 (long long)detail->args.ld_strip.strip);
448 printf("PD %s event: ",
449 pdrive_location(&detail->args.pd));
452 case MR_EVT_ARGS_PD_ERR:
454 printf("PD %s err %d: ",
455 pdrive_location(&detail->args.pd_err.pd),
456 detail->args.pd_err.err);
459 case MR_EVT_ARGS_PD_LBA:
461 printf("PD %s lba %lld: ",
462 pdrive_location(&detail->args.pd_lba.pd),
463 (long long)detail->args.pd_lba.lba);
466 case MR_EVT_ARGS_PD_LBA_LD:
468 printf("PD %s lba %lld VOL %s: ",
469 pdrive_location(&detail->args.pd_lba_ld.pd),
470 (long long)detail->args.pd_lba.lba,
471 volume_name(fd, &detail->args.pd_lba_ld.ld));
474 case MR_EVT_ARGS_PD_PROG:
476 printf("PD %s progress %d%% seconds %ds: ",
477 pdrive_location(&detail->args.pd_prog.pd),
478 detail->args.pd_prog.prog.progress/655,
479 detail->args.pd_prog.prog.elapsed_seconds);
482 case MR_EVT_ARGS_PD_STATE:
484 printf("PD %s state prior %s new %s: ",
485 pdrive_location(&detail->args.pd_prog.pd),
486 mfi_pdstate(detail->args.pd_state.prev_state),
487 mfi_pdstate(detail->args.pd_state.new_state));
490 case MR_EVT_ARGS_PCI:
492 printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ",
493 detail->args.pci.venderId,
494 detail->args.pci.deviceId,
495 detail->args.pci.subVenderId,
496 detail->args.pci.subDeviceId);
499 case MR_EVT_ARGS_RATE:
501 printf("Rebuild rate %d: ", detail->args.rate);
504 case MR_EVT_ARGS_TIME:
506 printf("Adapter time %s; %d seconds since power on: ",
507 format_timestamp(detail->args.time.rtc),
508 detail->args.time.elapsedSeconds);
511 case MR_EVT_ARGS_ECC:
513 printf("Adapter ECC %x,%x: %s: ",
514 detail->args.ecc.ecar,
515 detail->args.ecc.elog,
516 detail->args.ecc.str);
521 printf("Type %d: ", detail->arg_type);
525 printf("%s\n", detail->description);
529 show_events(int ac, char **av)
531 struct mfi_evt_log_state info;
532 struct mfi_evt_list *list;
533 union mfi_evt filter;
537 uint32_t seq, start, stop;
539 int ch, fd, num_events, verbose;
542 fd = mfi_open(mfi_unit);
548 if (mfi_event_get_info(fd, &info, NULL) < 0) {
549 warn("Failed to get event log info");
553 /* Default settings. */
555 filter.members.reserved = 0;
556 filter.members.locale = MFI_EVT_LOCALE_ALL;
557 filter.members.class = MFI_EVT_CLASS_WARNING;
558 start = info.boot_seq_num;
559 stop = info.newest_seq_num;
562 /* Parse any options. */
564 while ((ch = getopt(ac, av, "c:l:n:v")) != -1) {
567 if (parse_class(optarg, &filter.members.class) < 0) {
568 warn("Error parsing event class");
573 if (parse_locale(optarg, &filter.members.locale) < 0) {
574 warn("Error parsing event locale");
579 val = strtol(optarg, &cp, 0);
580 if (*cp != '\0' || val <= 0) {
581 warnx("Invalid event count");
597 /* Determine buffer size and validate it. */
598 size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
600 if (size > getpagesize()) {
601 warnx("Event count is too high");
605 /* Handle optional start and stop sequence numbers. */
607 warnx("show events: extra arguments");
610 if (ac > 0 && parse_seq(&info, av[0], &start) < 0) {
611 warn("Error parsing starting sequence number");
614 if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) {
615 warn("Error parsing ending sequence number");
620 for (seq = start;;) {
621 if (mfi_get_events(fd, list, num_events, filter, seq,
623 warn("Failed to fetch events");
626 if (status == MFI_STAT_NOT_FOUND) {
628 warnx("No matching events found");
631 if (status != MFI_STAT_OK) {
632 warnx("Error fetching events: %s", mfi_status(status));
636 for (i = 0; i < list->count; i++) {
638 * If this event is newer than 'stop_seq' then
639 * break out of the loop. Note that the log
640 * is a circular buffer so we have to handle
641 * the case that our stop point is earlier in
642 * the buffer than our start point.
644 if (list->event[i].seq >= stop) {
647 else if (list->event[i].seq < start)
650 mfi_decode_evt(fd, &list->event[i], verbose);
654 * XXX: If the event's seq # is the end of the buffer
655 * then this probably won't do the right thing. We
656 * need to know the size of the buffer somehow.
658 seq = list->event[list->count - 1].seq + 1;
667 MFI_COMMAND(show, events, show_events);