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