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 <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_strip.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); 695