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