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