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