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