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