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.6 2011/12/05 15:11:35 jhb 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, __unused 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 static void 325 simple_hex(void *ptr, size_t length, const char *separator) 326 { 327 unsigned char *cp; 328 u_int i; 329 330 if (length == 0) 331 return; 332 cp = ptr; 333 printf("%02x", cp[0]); 334 for (i = 1; i < length; i++) 335 printf("%s%02x", separator, cp[i]); 336 } 337 338 static const char * 339 pdrive_location(struct mfi_evt_pd *pd) 340 { 341 static char buffer[16]; 342 343 if (pd->enclosure_index == 0) 344 snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id, 345 pd->slot_number); 346 else 347 snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id, 348 pd->enclosure_index, pd->slot_number); 349 return (buffer); 350 } 351 352 static const char * 353 volume_name(int fd, struct mfi_evt_ld *ld) 354 { 355 356 return (mfi_volume_name(fd, ld->target_id)); 357 } 358 359 /* Ripped from sys/dev/mfi/mfi.c. */ 360 static void 361 mfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose) 362 { 363 364 printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time), 365 format_locale(detail->evt_class.members.locale), 366 format_class(detail->evt_class.members.evt_class)); 367 switch (detail->arg_type) { 368 case MR_EVT_ARGS_NONE: 369 break; 370 case MR_EVT_ARGS_CDB_SENSE: 371 if (verbose) { 372 printf("PD %s CDB ", 373 pdrive_location(&detail->args.cdb_sense.pd) 374 ); 375 simple_hex(detail->args.cdb_sense.cdb, 376 detail->args.cdb_sense.cdb_len, ":"); 377 printf(" Sense "); 378 simple_hex(detail->args.cdb_sense.sense, 379 detail->args.cdb_sense.sense_len, ":"); 380 printf(":\n "); 381 } 382 break; 383 case MR_EVT_ARGS_LD: 384 printf("VOL %s event: ", volume_name(fd, &detail->args.ld)); 385 break; 386 case MR_EVT_ARGS_LD_COUNT: 387 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld)); 388 if (verbose) { 389 printf(" count %lld: ", 390 (long long)detail->args.ld_count.count); 391 } 392 printf(": "); 393 break; 394 case MR_EVT_ARGS_LD_LBA: 395 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld)); 396 if (verbose) { 397 printf(" lba %lld", 398 (long long)detail->args.ld_lba.lba); 399 } 400 printf(": "); 401 break; 402 case MR_EVT_ARGS_LD_OWNER: 403 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld)); 404 if (verbose) { 405 printf(" owner changed: prior %d, new %d", 406 detail->args.ld_owner.pre_owner, 407 detail->args.ld_owner.new_owner); 408 } 409 printf(": "); 410 break; 411 case MR_EVT_ARGS_LD_LBA_PD_LBA: 412 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld)); 413 if (verbose) { 414 printf(" lba %lld, physical drive PD %s lba %lld", 415 (long long)detail->args.ld_lba_pd_lba.ld_lba, 416 pdrive_location(&detail->args.ld_lba_pd_lba.pd), 417 (long long)detail->args.ld_lba_pd_lba.pd_lba); 418 } 419 printf(": "); 420 break; 421 case MR_EVT_ARGS_LD_PROG: 422 printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld)); 423 if (verbose) { 424 printf(" progress %d%% in %ds", 425 detail->args.ld_prog.prog.progress/655, 426 detail->args.ld_prog.prog.elapsed_seconds); 427 } 428 printf(": "); 429 break; 430 case MR_EVT_ARGS_LD_STATE: 431 printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld)); 432 if (verbose) { 433 printf(" state prior %s new %s", 434 mfi_ldstate(detail->args.ld_state.prev_state), 435 mfi_ldstate(detail->args.ld_state.new_state)); 436 } 437 printf(": "); 438 break; 439 case MR_EVT_ARGS_LD_STRIP: 440 printf("VOL %s", volume_name(fd, &detail->args.ld_strip.ld)); 441 if (verbose) { 442 printf(" strip %lld", 443 (long long)detail->args.ld_strip.strip); 444 } 445 printf(": "); 446 break; 447 case MR_EVT_ARGS_PD: 448 if (verbose) { 449 printf("PD %s event: ", 450 pdrive_location(&detail->args.pd)); 451 } 452 break; 453 case MR_EVT_ARGS_PD_ERR: 454 if (verbose) { 455 printf("PD %s err %d: ", 456 pdrive_location(&detail->args.pd_err.pd), 457 detail->args.pd_err.err); 458 } 459 break; 460 case MR_EVT_ARGS_PD_LBA: 461 if (verbose) { 462 printf("PD %s lba %lld: ", 463 pdrive_location(&detail->args.pd_lba.pd), 464 (long long)detail->args.pd_lba.lba); 465 } 466 break; 467 case MR_EVT_ARGS_PD_LBA_LD: 468 if (verbose) { 469 printf("PD %s lba %lld VOL %s: ", 470 pdrive_location(&detail->args.pd_lba_ld.pd), 471 (long long)detail->args.pd_lba.lba, 472 volume_name(fd, &detail->args.pd_lba_ld.ld)); 473 } 474 break; 475 case MR_EVT_ARGS_PD_PROG: 476 if (verbose) { 477 printf("PD %s progress %d%% seconds %ds: ", 478 pdrive_location(&detail->args.pd_prog.pd), 479 detail->args.pd_prog.prog.progress/655, 480 detail->args.pd_prog.prog.elapsed_seconds); 481 } 482 break; 483 case MR_EVT_ARGS_PD_STATE: 484 if (verbose) { 485 printf("PD %s state prior %s new %s: ", 486 pdrive_location(&detail->args.pd_prog.pd), 487 mfi_pdstate(detail->args.pd_state.prev_state), 488 mfi_pdstate(detail->args.pd_state.new_state)); 489 } 490 break; 491 case MR_EVT_ARGS_PCI: 492 if (verbose) { 493 printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ", 494 detail->args.pci.venderId, 495 detail->args.pci.deviceId, 496 detail->args.pci.subVenderId, 497 detail->args.pci.subDeviceId); 498 } 499 break; 500 case MR_EVT_ARGS_RATE: 501 if (verbose) { 502 printf("Rebuild rate %d: ", detail->args.rate); 503 } 504 break; 505 case MR_EVT_ARGS_TIME: 506 if (verbose) { 507 printf("Adapter time %s; %d seconds since power on: ", 508 format_timestamp(detail->args.time.rtc), 509 detail->args.time.elapsedSeconds); 510 } 511 break; 512 case MR_EVT_ARGS_ECC: 513 if (verbose) { 514 printf("Adapter ECC %x,%x: %s: ", 515 detail->args.ecc.ecar, 516 detail->args.ecc.elog, 517 detail->args.ecc.str); 518 } 519 break; 520 default: 521 if (verbose) { 522 printf("Type %d: ", detail->arg_type); 523 } 524 break; 525 } 526 printf("%s\n", detail->description); 527 } 528 529 static int 530 show_events(int ac, char **av) 531 { 532 struct mfi_evt_log_state info; 533 struct mfi_evt_list *list; 534 union mfi_evt filter; 535 long val; 536 char *cp; 537 ssize_t size; 538 uint32_t seq, start, stop; 539 uint8_t status; 540 int ch, error, fd, num_events, verbose; 541 u_int i; 542 543 fd = mfi_open(mfi_unit); 544 if (fd < 0) { 545 error = errno; 546 warn("mfi_open"); 547 return (error); 548 } 549 550 if (mfi_event_get_info(fd, &info, NULL) < 0) { 551 error = errno; 552 warn("Failed to get event log info"); 553 close(fd); 554 return (error); 555 } 556 557 /* Default settings. */ 558 num_events = 15; 559 filter.members.reserved = 0; 560 filter.members.locale = MFI_EVT_LOCALE_ALL; 561 filter.members.evt_class = MFI_EVT_CLASS_WARNING; 562 start = info.boot_seq_num; 563 stop = info.newest_seq_num; 564 verbose = 0; 565 566 /* Parse any options. */ 567 optind = 1; 568 while ((ch = getopt(ac, av, "c:l:n:v")) != -1) { 569 switch (ch) { 570 case 'c': 571 if (parse_class(optarg, &filter.members.evt_class) < 0) { 572 error = errno; 573 warn("Error parsing event class"); 574 close(fd); 575 return (error); 576 } 577 break; 578 case 'l': 579 if (parse_locale(optarg, &filter.members.locale) < 0) { 580 error = errno; 581 warn("Error parsing event locale"); 582 close(fd); 583 return (error); 584 } 585 break; 586 case 'n': 587 val = strtol(optarg, &cp, 0); 588 if (*cp != '\0' || val <= 0) { 589 warnx("Invalid event count"); 590 close(fd); 591 return (EINVAL); 592 } 593 num_events = val; 594 break; 595 case 'v': 596 verbose = 1; 597 break; 598 case '?': 599 default: 600 close(fd); 601 return (EINVAL); 602 } 603 } 604 ac -= optind; 605 av += optind; 606 607 /* Determine buffer size and validate it. */ 608 size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) * 609 (num_events - 1); 610 if (size > getpagesize()) { 611 warnx("Event count is too high"); 612 close(fd); 613 return (EINVAL); 614 } 615 616 /* Handle optional start and stop sequence numbers. */ 617 if (ac > 2) { 618 warnx("show events: extra arguments"); 619 close(fd); 620 return (EINVAL); 621 } 622 if (ac > 0 && parse_seq(&info, av[0], &start) < 0) { 623 error = errno; 624 warn("Error parsing starting sequence number"); 625 close(fd); 626 return (error); 627 } 628 if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) { 629 error = errno; 630 warn("Error parsing ending sequence number"); 631 close(fd); 632 return (error); 633 } 634 635 list = malloc(size); 636 if (list == NULL) { 637 warnx("malloc failed"); 638 close(fd); 639 return (ENOMEM); 640 } 641 for (seq = start;;) { 642 if (mfi_get_events(fd, list, num_events, filter, seq, 643 &status) < 0) { 644 error = errno; 645 warn("Failed to fetch events"); 646 free(list); 647 close(fd); 648 return (error); 649 } 650 if (status == MFI_STAT_NOT_FOUND) { 651 if (seq == start) 652 warnx("No matching events found"); 653 break; 654 } 655 if (status != MFI_STAT_OK) { 656 warnx("Error fetching events: %s", mfi_status(status)); 657 free(list); 658 close(fd); 659 return (EIO); 660 } 661 662 for (i = 0; i < list->count; i++) { 663 /* 664 * If this event is newer than 'stop_seq' then 665 * break out of the loop. Note that the log 666 * is a circular buffer so we have to handle 667 * the case that our stop point is earlier in 668 * the buffer than our start point. 669 */ 670 if (list->event[i].seq >= stop) { 671 if (start <= stop) 672 break; 673 else if (list->event[i].seq < start) 674 break; 675 } 676 mfi_decode_evt(fd, &list->event[i], verbose); 677 } 678 679 /* 680 * XXX: If the event's seq # is the end of the buffer 681 * then this probably won't do the right thing. We 682 * need to know the size of the buffer somehow. 683 */ 684 seq = list->event[list->count - 1].seq + 1; 685 686 } 687 688 free(list); 689 close(fd); 690 691 return (0); 692 } 693 MFI_COMMAND(show, events, show_events); 694