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 return (error); 87 } 88 89 printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit); 90 printf(" Newest Seq #: %u\n", info.newest_seq_num); 91 printf(" Oldest Seq #: %u\n", info.oldest_seq_num); 92 printf(" Clear Seq #: %u\n", info.clear_seq_num); 93 printf("Shutdown Seq #: %u\n", info.shutdown_seq_num); 94 printf(" Boot Seq #: %u\n", info.boot_seq_num); 95 96 close(fd); 97 98 return (0); 99 } 100 MFI_COMMAND(show, logstate, show_logstate); 101 102 static int 103 parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq) 104 { 105 char *cp; 106 long val; 107 108 if (strcasecmp(arg, "newest") == 0) { 109 *seq = info->newest_seq_num; 110 return (0); 111 } 112 if (strcasecmp(arg, "oldest") == 0) { 113 *seq = info->oldest_seq_num; 114 return (0); 115 } 116 if (strcasecmp(arg, "clear") == 0) { 117 *seq = info->clear_seq_num; 118 return (0); 119 } 120 if (strcasecmp(arg, "shutdown") == 0) { 121 *seq = info->shutdown_seq_num; 122 return (0); 123 } 124 if (strcasecmp(arg, "boot") == 0) { 125 *seq = info->boot_seq_num; 126 return (0); 127 } 128 val = strtol(arg, &cp, 0); 129 if (*cp != '\0' || val < 0) { 130 errno = EINVAL; 131 return (-1); 132 } 133 *seq = val; 134 return (0); 135 } 136 137 static int 138 parse_locale(char *arg, uint16_t *locale) 139 { 140 char *cp; 141 long val; 142 143 if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) { 144 *locale = MFI_EVT_LOCALE_LD; 145 return (0); 146 } 147 if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) { 148 *locale = MFI_EVT_LOCALE_PD; 149 return (0); 150 } 151 if (strncasecmp(arg, "encl", 4) == 0) { 152 *locale = MFI_EVT_LOCALE_ENCL; 153 return (0); 154 } 155 if (strncasecmp(arg, "batt", 4) == 0 || 156 strncasecmp(arg, "bbu", 3) == 0) { 157 *locale = MFI_EVT_LOCALE_BBU; 158 return (0); 159 } 160 if (strcasecmp(arg, "sas") == 0) { 161 *locale = MFI_EVT_LOCALE_SAS; 162 return (0); 163 } 164 if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) { 165 *locale = MFI_EVT_LOCALE_CTRL; 166 return (0); 167 } 168 if (strcasecmp(arg, "config") == 0) { 169 *locale = MFI_EVT_LOCALE_CONFIG; 170 return (0); 171 } 172 if (strcasecmp(arg, "cluster") == 0) { 173 *locale = MFI_EVT_LOCALE_CLUSTER; 174 return (0); 175 } 176 if (strcasecmp(arg, "all") == 0) { 177 *locale = MFI_EVT_LOCALE_ALL; 178 return (0); 179 } 180 val = strtol(arg, &cp, 0); 181 if (*cp != '\0' || val < 0 || val > 0xffff) { 182 errno = EINVAL; 183 return (-1); 184 } 185 *locale = val; 186 return (0); 187 } 188 189 static int 190 parse_class(char *arg, int8_t *class) 191 { 192 char *cp; 193 long val; 194 195 if (strcasecmp(arg, "debug") == 0) { 196 *class = MFI_EVT_CLASS_DEBUG; 197 return (0); 198 } 199 if (strncasecmp(arg, "prog", 4) == 0) { 200 *class = MFI_EVT_CLASS_PROGRESS; 201 return (0); 202 } 203 if (strncasecmp(arg, "info", 4) == 0) { 204 *class = MFI_EVT_CLASS_INFO; 205 return (0); 206 } 207 if (strncasecmp(arg, "warn", 4) == 0) { 208 *class = MFI_EVT_CLASS_WARNING; 209 return (0); 210 } 211 if (strncasecmp(arg, "crit", 4) == 0) { 212 *class = MFI_EVT_CLASS_CRITICAL; 213 return (0); 214 } 215 if (strcasecmp(arg, "fatal") == 0) { 216 *class = MFI_EVT_CLASS_FATAL; 217 return (0); 218 } 219 if (strcasecmp(arg, "dead") == 0) { 220 *class = MFI_EVT_CLASS_DEAD; 221 return (0); 222 } 223 val = strtol(arg, &cp, 0); 224 if (*cp != '\0' || val < -128 || val > 127) { 225 errno = EINVAL; 226 return (-1); 227 } 228 *class = val; 229 return (0); 230 } 231 232 /* 233 * The timestamp is the number of seconds since 00:00 Jan 1, 2000. If 234 * the bits in 24-31 are all set, then it is the number of seconds since 235 * boot. 236 */ 237 static const char * 238 format_timestamp(uint32_t timestamp) 239 { 240 static char buffer[32]; 241 static time_t base; 242 time_t t; 243 struct tm tm; 244 245 if ((timestamp & 0xff000000) == 0xff000000) { 246 snprintf(buffer, sizeof(buffer), "boot + %us", timestamp & 247 0x00ffffff); 248 return (buffer); 249 } 250 251 if (base == 0) { 252 /* Compute 00:00 Jan 1, 2000 offset. */ 253 bzero(&tm, sizeof(tm)); 254 tm.tm_mday = 1; 255 tm.tm_year = (2000 - 1900); 256 base = mktime(&tm); 257 } 258 if (base == -1) { 259 snprintf(buffer, sizeof(buffer), "%us", timestamp); 260 return (buffer); 261 } 262 t = base + timestamp; 263 strftime(buffer, sizeof(buffer), "%+", localtime(&t)); 264 return (buffer); 265 } 266 267 static const char * 268 format_locale(uint16_t locale) 269 { 270 static char buffer[8]; 271 272 switch (locale) { 273 case MFI_EVT_LOCALE_LD: 274 return ("VOLUME"); 275 case MFI_EVT_LOCALE_PD: 276 return ("DRIVE"); 277 case MFI_EVT_LOCALE_ENCL: 278 return ("ENCL"); 279 case MFI_EVT_LOCALE_BBU: 280 return ("BATTERY"); 281 case MFI_EVT_LOCALE_SAS: 282 return ("SAS"); 283 case MFI_EVT_LOCALE_CTRL: 284 return ("CTRL"); 285 case MFI_EVT_LOCALE_CONFIG: 286 return ("CONFIG"); 287 case MFI_EVT_LOCALE_CLUSTER: 288 return ("CLUSTER"); 289 case MFI_EVT_LOCALE_ALL: 290 return ("ALL"); 291 default: 292 snprintf(buffer, sizeof(buffer), "0x%04x", locale); 293 return (buffer); 294 } 295 } 296 297 static const char * 298 format_class(int8_t class) 299 { 300 static char buffer[6]; 301 302 switch (class) { 303 case MFI_EVT_CLASS_DEBUG: 304 return ("debug"); 305 case MFI_EVT_CLASS_PROGRESS: 306 return ("progress"); 307 case MFI_EVT_CLASS_INFO: 308 return ("info"); 309 case MFI_EVT_CLASS_WARNING: 310 return ("WARN"); 311 case MFI_EVT_CLASS_CRITICAL: 312 return ("CRIT"); 313 case MFI_EVT_CLASS_FATAL: 314 return ("FATAL"); 315 case MFI_EVT_CLASS_DEAD: 316 return ("DEAD"); 317 default: 318 snprintf(buffer, sizeof(buffer), "%d", class); 319 return (buffer); 320 } 321 } 322 323 /* Simulates %D from kernel printf(9). */ 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_prog.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 return (error); 554 } 555 556 /* Default settings. */ 557 num_events = 15; 558 filter.members.reserved = 0; 559 filter.members.locale = MFI_EVT_LOCALE_ALL; 560 filter.members.evt_class = MFI_EVT_CLASS_WARNING; 561 start = info.boot_seq_num; 562 stop = info.newest_seq_num; 563 verbose = 0; 564 565 /* Parse any options. */ 566 optind = 1; 567 while ((ch = getopt(ac, av, "c:l:n:v")) != -1) { 568 switch (ch) { 569 case 'c': 570 if (parse_class(optarg, &filter.members.evt_class) < 0) { 571 error = errno; 572 warn("Error parsing event class"); 573 return (error); 574 } 575 break; 576 case 'l': 577 if (parse_locale(optarg, &filter.members.locale) < 0) { 578 error = errno; 579 warn("Error parsing event locale"); 580 return (error); 581 } 582 break; 583 case 'n': 584 val = strtol(optarg, &cp, 0); 585 if (*cp != '\0' || val <= 0) { 586 warnx("Invalid event count"); 587 return (EINVAL); 588 } 589 num_events = val; 590 break; 591 case 'v': 592 verbose = 1; 593 break; 594 case '?': 595 default: 596 return (EINVAL); 597 } 598 } 599 ac -= optind; 600 av += optind; 601 602 /* Determine buffer size and validate it. */ 603 size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) * 604 (num_events - 1); 605 if (size > getpagesize()) { 606 warnx("Event count is too high"); 607 return (EINVAL); 608 } 609 610 /* Handle optional start and stop sequence numbers. */ 611 if (ac > 2) { 612 warnx("show events: extra arguments"); 613 return (EINVAL); 614 } 615 if (ac > 0 && parse_seq(&info, av[0], &start) < 0) { 616 error = errno; 617 warn("Error parsing starting sequence number"); 618 return (error); 619 } 620 if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) { 621 error = errno; 622 warn("Error parsing ending sequence number"); 623 return (error); 624 } 625 626 list = malloc(size); 627 if (list == NULL) { 628 warnx("malloc failed"); 629 return (ENOMEM); 630 } 631 for (seq = start;;) { 632 if (mfi_get_events(fd, list, num_events, filter, seq, 633 &status) < 0) { 634 error = errno; 635 warn("Failed to fetch events"); 636 return (error); 637 } 638 if (status == MFI_STAT_NOT_FOUND) { 639 if (seq == start) 640 warnx("No matching events found"); 641 break; 642 } 643 if (status != MFI_STAT_OK) { 644 warnx("Error fetching events: %s", mfi_status(status)); 645 return (EIO); 646 } 647 648 for (i = 0; i < list->count; i++) { 649 /* 650 * If this event is newer than 'stop_seq' then 651 * break out of the loop. Note that the log 652 * is a circular buffer so we have to handle 653 * the case that our stop point is earlier in 654 * the buffer than our start point. 655 */ 656 if (list->event[i].seq >= stop) { 657 if (start <= stop) 658 break; 659 else if (list->event[i].seq < start) 660 break; 661 } 662 mfi_decode_evt(fd, &list->event[i], verbose); 663 } 664 665 /* 666 * XXX: If the event's seq # is the end of the buffer 667 * then this probably won't do the right thing. We 668 * need to know the size of the buffer somehow. 669 */ 670 seq = list->event[list->count - 1].seq + 1; 671 672 } 673 674 free(list); 675 close(fd); 676 677 return (0); 678 } 679 MFI_COMMAND(show, events, show_events); 680