Super User's BSD Cross Reference: /FreeBSD/usr.sbin/mfiutil/mfi_evt.c

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 

AltStyle によって変換されたページ (->オリジナル) /