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