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