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.6 2011/12/05 15:11:35 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
mfi_event_get_info(int fd,struct mfi_evt_log_state * info,uint8_t * statusp)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
mfi_get_events(int fd,struct mfi_evt_list * list,int num_events,union mfi_evt filter,uint32_t start_seq,uint8_t * statusp)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
show_logstate(int ac,__unused char ** av)66 show_logstate(int ac, __unused 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 close(fd);
87 return (error);
88 }
89
90 printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit);
91 printf(" Newest Seq #: %u\n", info.newest_seq_num);
92 printf(" Oldest Seq #: %u\n", info.oldest_seq_num);
93 printf(" Clear Seq #: %u\n", info.clear_seq_num);
94 printf("Shutdown Seq #: %u\n", info.shutdown_seq_num);
95 printf(" Boot Seq #: %u\n", info.boot_seq_num);
96
97 close(fd);
98
99 return (0);
100 }
101 MFI_COMMAND(show, logstate, show_logstate);
102
103 static int
parse_seq(struct mfi_evt_log_state * info,char * arg,uint32_t * seq)104 parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq)
105 {
106 char *cp;
107 long val;
108
109 if (strcasecmp(arg, "newest") == 0) {
110 *seq = info->newest_seq_num;
111 return (0);
112 }
113 if (strcasecmp(arg, "oldest") == 0) {
114 *seq = info->oldest_seq_num;
115 return (0);
116 }
117 if (strcasecmp(arg, "clear") == 0) {
118 *seq = info->clear_seq_num;
119 return (0);
120 }
121 if (strcasecmp(arg, "shutdown") == 0) {
122 *seq = info->shutdown_seq_num;
123 return (0);
124 }
125 if (strcasecmp(arg, "boot") == 0) {
126 *seq = info->boot_seq_num;
127 return (0);
128 }
129 val = strtol(arg, &cp, 0);
130 if (*cp != '\0' || val < 0) {
131 errno = EINVAL;
132 return (-1);
133 }
134 *seq = val;
135 return (0);
136 }
137
138 static int
parse_locale(char * arg,uint16_t * locale)139 parse_locale(char *arg, uint16_t *locale)
140 {
141 char *cp;
142 long val;
143
144 if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) {
145 *locale = MFI_EVT_LOCALE_LD;
146 return (0);
147 }
148 if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) {
149 *locale = MFI_EVT_LOCALE_PD;
150 return (0);
151 }
152 if (strncasecmp(arg, "encl", 4) == 0) {
153 *locale = MFI_EVT_LOCALE_ENCL;
154 return (0);
155 }
156 if (strncasecmp(arg, "batt", 4) == 0 ||
157 strncasecmp(arg, "bbu", 3) == 0) {
158 *locale = MFI_EVT_LOCALE_BBU;
159 return (0);
160 }
161 if (strcasecmp(arg, "sas") == 0) {
162 *locale = MFI_EVT_LOCALE_SAS;
163 return (0);
164 }
165 if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) {
166 *locale = MFI_EVT_LOCALE_CTRL;
167 return (0);
168 }
169 if (strcasecmp(arg, "config") == 0) {
170 *locale = MFI_EVT_LOCALE_CONFIG;
171 return (0);
172 }
173 if (strcasecmp(arg, "cluster") == 0) {
174 *locale = MFI_EVT_LOCALE_CLUSTER;
175 return (0);
176 }
177 if (strcasecmp(arg, "all") == 0) {
178 *locale = MFI_EVT_LOCALE_ALL;
179 return (0);
180 }
181 val = strtol(arg, &cp, 0);
182 if (*cp != '\0' || val < 0 || val > 0xffff) {
183 errno = EINVAL;
184 return (-1);
185 }
186 *locale = val;
187 return (0);
188 }
189
190 static int
parse_class(char * arg,int8_t * class)191 parse_class(char *arg, int8_t *class)
192 {
193 char *cp;
194 long val;
195
196 if (strcasecmp(arg, "debug") == 0) {
197 *class = MFI_EVT_CLASS_DEBUG;
198 return (0);
199 }
200 if (strncasecmp(arg, "prog", 4) == 0) {
201 *class = MFI_EVT_CLASS_PROGRESS;
202 return (0);
203 }
204 if (strncasecmp(arg, "info", 4) == 0) {
205 *class = MFI_EVT_CLASS_INFO;
206 return (0);
207 }
208 if (strncasecmp(arg, "warn", 4) == 0) {
209 *class = MFI_EVT_CLASS_WARNING;
210 return (0);
211 }
212 if (strncasecmp(arg, "crit", 4) == 0) {
213 *class = MFI_EVT_CLASS_CRITICAL;
214 return (0);
215 }
216 if (strcasecmp(arg, "fatal") == 0) {
217 *class = MFI_EVT_CLASS_FATAL;
218 return (0);
219 }
220 if (strcasecmp(arg, "dead") == 0) {
221 *class = MFI_EVT_CLASS_DEAD;
222 return (0);
223 }
224 val = strtol(arg, &cp, 0);
225 if (*cp != '\0' || val < -128 || val > 127) {
226 errno = EINVAL;
227 return (-1);
228 }
229 *class = val;
230 return (0);
231 }
232
233 /*
234 * The timestamp is the number of seconds since 00:00 Jan 1, 2000. If
235 * the bits in 24-31 are all set, then it is the number of seconds since
236 * boot.
237 */
238 static const char *
format_timestamp(uint32_t timestamp)239 format_timestamp(uint32_t timestamp)
240 {
241 static char buffer[32];
242 static time_t base;
243 time_t t;
244 struct tm tm;
245
246 if ((timestamp & 0xff000000) == 0xff000000) {
247 snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
248 0x00ffffff);
249 return (buffer);
250 }
251
252 if (base == 0) {
253 /* Compute 00:00 Jan 1, 2000 offset. */
254 bzero(&tm, sizeof(tm));
255 tm.tm_mday = 1;
256 tm.tm_year = (2000 - 1900);
257 base = mktime(&tm);
258 }
259 if (base == -1) {
260 snprintf(buffer, sizeof(buffer), "%us", timestamp);
261 return (buffer);
262 }
263 t = base + timestamp;
264 strftime(buffer, sizeof(buffer), "%+", localtime(&t));
265 return (buffer);
266 }
267
268 static const char *
format_locale(uint16_t locale)269 format_locale(uint16_t locale)
270 {
271 static char buffer[8];
272
273 switch (locale) {
274 case MFI_EVT_LOCALE_LD:
275 return ("VOLUME");
276 case MFI_EVT_LOCALE_PD:
277 return ("DRIVE");
278 case MFI_EVT_LOCALE_ENCL:
279 return ("ENCL");
280 case MFI_EVT_LOCALE_BBU:
281 return ("BATTERY");
282 case MFI_EVT_LOCALE_SAS:
283 return ("SAS");
284 case MFI_EVT_LOCALE_CTRL:
285 return ("CTRL");
286 case MFI_EVT_LOCALE_CONFIG:
287 return ("CONFIG");
288 case MFI_EVT_LOCALE_CLUSTER:
289 return ("CLUSTER");
290 case MFI_EVT_LOCALE_ALL:
291 return ("ALL");
292 default:
293 snprintf(buffer, sizeof(buffer), "0x%04x", locale);
294 return (buffer);
295 }
296 }
297
298 static const char *
format_class(int8_t class)299 format_class(int8_t class)
300 {
301 static char buffer[6];
302
303 switch (class) {
304 case MFI_EVT_CLASS_DEBUG:
305 return ("debug");
306 case MFI_EVT_CLASS_PROGRESS:
307 return ("progress");
308 case MFI_EVT_CLASS_INFO:
309 return ("info");
310 case MFI_EVT_CLASS_WARNING:
311 return ("WARN");
312 case MFI_EVT_CLASS_CRITICAL:
313 return ("CRIT");
314 case MFI_EVT_CLASS_FATAL:
315 return ("FATAL");
316 case MFI_EVT_CLASS_DEAD:
317 return ("DEAD");
318 default:
319 snprintf(buffer, sizeof(buffer), "%d", class);
320 return (buffer);
321 }
322 }
323
324 static void
simple_hex(void * ptr,size_t length,const char * separator)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 *
pdrive_location(struct mfi_evt_pd * pd)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 *
volume_name(int fd,struct mfi_evt_ld * ld)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
mfi_decode_evt(int fd,struct mfi_evt_detail * detail,int verbose)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->evt_class.members.locale),
366 format_class(detail->evt_class.members.evt_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_strip.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
show_events(int ac,char ** av)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 close(fd);
554 return (error);
555 }
556
557 /* Default settings. */
558 num_events = 15;
559 filter.members.reserved = 0;
560 filter.members.locale = MFI_EVT_LOCALE_ALL;
561 filter.members.evt_class = MFI_EVT_CLASS_WARNING;
562 start = info.boot_seq_num;
563 stop = info.newest_seq_num;
564 verbose = 0;
565
566 /* Parse any options. */
567 optind = 1;
568 while ((ch = getopt(ac, av, "c:l:n:v")) != -1) {
569 switch (ch) {
570 case 'c':
571 if (parse_class(optarg, &filter.members.evt_class) < 0) {
572 error = errno;
573 warn("Error parsing event class");
574 close(fd);
575 return (error);
576 }
577 break;
578 case 'l':
579 if (parse_locale(optarg, &filter.members.locale) < 0) {
580 error = errno;
581 warn("Error parsing event locale");
582 close(fd);
583 return (error);
584 }
585 break;
586 case 'n':
587 val = strtol(optarg, &cp, 0);
588 if (*cp != '\0' || val <= 0) {
589 warnx("Invalid event count");
590 close(fd);
591 return (EINVAL);
592 }
593 num_events = val;
594 break;
595 case 'v':
596 verbose = 1;
597 break;
598 case '?':
599 default:
600 close(fd);
601 return (EINVAL);
602 }
603 }
604 ac -= optind;
605 av += optind;
606
607 /* Determine buffer size and validate it. */
608 size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
609 (num_events - 1);
610 if (size > getpagesize()) {
611 warnx("Event count is too high");
612 close(fd);
613 return (EINVAL);
614 }
615
616 /* Handle optional start and stop sequence numbers. */
617 if (ac > 2) {
618 warnx("show events: extra arguments");
619 close(fd);
620 return (EINVAL);
621 }
622 if (ac > 0 && parse_seq(&info, av[0], &start) < 0) {
623 error = errno;
624 warn("Error parsing starting sequence number");
625 close(fd);
626 return (error);
627 }
628 if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) {
629 error = errno;
630 warn("Error parsing ending sequence number");
631 close(fd);
632 return (error);
633 }
634
635 list = malloc(size);
636 if (list == NULL) {
637 warnx("malloc failed");
638 close(fd);
639 return (ENOMEM);
640 }
641 for (seq = start;;) {
642 if (mfi_get_events(fd, list, num_events, filter, seq,
643 &status) < 0) {
644 error = errno;
645 warn("Failed to fetch events");
646 free(list);
647 close(fd);
648 return (error);
649 }
650 if (status == MFI_STAT_NOT_FOUND) {
651 if (seq == start)
652 warnx("No matching events found");
653 break;
654 }
655 if (status != MFI_STAT_OK) {
656 warnx("Error fetching events: %s", mfi_status(status));
657 free(list);
658 close(fd);
659 return (EIO);
660 }
661
662 for (i = 0; i < list->count; i++) {
663 /*
664 * If this event is newer than 'stop_seq' then
665 * break out of the loop. Note that the log
666 * is a circular buffer so we have to handle
667 * the case that our stop point is earlier in
668 * the buffer than our start point.
669 */
670 if (list->event[i].seq >= stop) {
671 if (start <= stop)
672 break;
673 else if (list->event[i].seq < start)
674 break;
675 }
676 mfi_decode_evt(fd, &list->event[i], verbose);
677 }
678
679 /*
680 * XXX: If the event's seq # is the end of the buffer
681 * then this probably won't do the right thing. We
682 * need to know the size of the buffer somehow.
683 */
684 seq = list->event[list->count - 1].seq + 1;
685
686 }
687
688 free(list);
689 close(fd);
690
691 return (0);
692 }
693 MFI_COMMAND(show, events, show_events);
694