1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * SCSI Enclosure Services Log Transport Module 27 * 28 * This transport module is responsible for accessing the ses devices seen 29 * from this host, reading their logs, generating ereports for targeted 30 * entries, and then writing the log contents to a well known location in 31 * the filesystem. 32 * 33 */ 34 35 #include <ctype.h> 36 #include <fm/fmd_api.h> 37 #include <fm/libtopo.h> 38 #include <fm/topo_hc.h> 39 #include <fm/topo_mod.h> 40 #include <limits.h> 41 #include <string.h> 42 #include <sys/fm/io/scsi.h> 43 #include <sys/fm/protocol.h> 44 #include <stdio.h> 45 #include <time.h> 46 #include <fm/libseslog.h> 47 #include <errno.h> 48 #include <sys/types.h> 49 #include <sys/stat.h> 50 51 /* 52 * This struct contains the default property values. These may 53 * be overridden by entries in a ses_log_transport.conf file. 54 * The severity is set to -1 here so that the _fmd_init routine will 55 * determine the default severity based on the constants in libseslog.h. 56 */ 57 static const fmd_prop_t fmd_props[] = { 58 { "interval", FMD_TYPE_TIME, "60s"}, 59 { "severity", FMD_TYPE_INT32, "-1"}, 60 { "path", FMD_TYPE_STRING, "/var/fm/fmd/ses_logs/"}, 61 { "logcount", FMD_TYPE_UINT32, "5"}, 62 { "maxlogsize", FMD_TYPE_UINT32, "1000000"}, 63 { NULL, 0, NULL} 64 }; 65 66 /* Maintains statistics on dropped ereports. */ 67 static struct slt_stat 68 { 69 fmd_stat_t dropped; 70 } slt_stats = { 71 { "dropped", FMD_TYPE_UINT64, "number of dropped ereports"} 72 }; 73 74 /* 75 * This structure maintains a reference to the input values, transport, and 76 * other data which is held by FMD and retrieved whenever an entry point 77 * is called. 78 */ 79 typedef struct ses_log_monitor 80 { 81 fmd_hdl_t *slt_hdl; /* opaque handle for this transport */ 82 fmd_xprt_t *slt_xprt; /* ereport transport */ 83 id_t slt_timer; /* Timer for FMD polling use */ 84 hrtime_t slt_interval; /* Polling interval */ 85 int32_t slt_severity; /* Min severity for logging ereports */ 86 char *slt_path; /* Output path for log files */ 87 int32_t slt_log_count; /* Max rolled logs to keep */ 88 int32_t slt_max_log_size; /* Max log size before rolling */ 89 nvlist_t *slt_expanders; /* List of expander log entries */ 90 } ses_log_monitor_t; 91 92 /* Contains expander log data retrieved from a topology node */ 93 typedef struct expander 94 { 95 char slt_label[MAXNAMELEN]; /* The expander name */ 96 char slt_pid[MAXNAMELEN]; /* The system product id */ 97 char slt_key[MAXNAMELEN]; /* The expander key (sas address) */ 98 char slt_path[MAXPATHLEN]; /* The ses path to the expander */ 99 nvlist_t *fmri; /* The fmri for this target */ 100 } expander_t; 101 102 #define DATA_FIELD "data" /* Label for the expander details */ 103 #define DEFAULT_DATA "0" /* Default expander details value */ 104 #define MIN_LOG_SIZE 100000 /* The minimum log file size. */ 105 #define MIN_LOG_COUNT 1 /* Num of rolled files to keep */ 106 #define EXAMINE_FMRI_VALUE 0 /* Extract fmri val */ 107 #define INVERT_FMRI_INSTANCE 1 /* Invert an FMRI instance value */ 108 #define FATAL_ERROR "fatal" /* ereport val for fatal errors */ 109 #define NON_FATAL_ERROR "non-fatal" /* val for non fatal errors */ 110 #define INVALID_OPERATION 0x01 /* Invalid access_fmri operation */ 111 #define NULL_LOG_DATA 0x02 /* Lib returned NULL log ref */ 112 #define INVALID_SEVERITY 0x03 /* Invalid severity value */ 113 #define DATE_STRING_SIZE 16 /* Size of date string prefix. */ 114 115 /* Prototype needed for use in declaring and populating tables */ 116 static int invert_fmri(ses_log_monitor_t *, nvlist_t *); 117 118 /* Holds a code-operation pair. Contains a log code an a function ptr */ 119 typedef struct code_operation { 120 int code; 121 int (*func_ptr)(ses_log_monitor_t *, nvlist_t *); 122 } code_operation_t; 123 124 /* Holds a platform type and a list of code-operation structures */ 125 typedef struct platform { 126 const char *pid; 127 int count; 128 code_operation_t *codes; 129 } platform_t; 130 131 /* Holds a reference to all of the platforms */ 132 typedef struct platforms { 133 int pcount; 134 platform_t *plist; 135 } platforms_t; 136 137 /* This is the genesis list of codes and functions. */ 138 static code_operation_t genesis_codes[] = { 139 { 684002, invert_fmri }, /* Alternate expander is down */ 140 { 685002, invert_fmri } /* Alternate expander is down */ 141 }; 142 143 /* This is the list of all platforms and their associated code op pairs. */ 144 static platform_t platform_list[] = { 145 { "SUN-GENESIS", 146 sizeof (genesis_codes) / sizeof (code_operation_t), 147 genesis_codes } 148 }; 149 150 /* This structure holds a reference to the platform list. */ 151 static const platforms_t platforms = { 152 sizeof (platform_list) / sizeof (platform_t), 153 platform_list 154 }; 155 156 /* 157 * Post ereports using this method. 158 */ 159 static void 160 slt_post_ereport(fmd_hdl_t *hdl, fmd_xprt_t *xprt, const char *ereport_class, 161 uint64_t ena, nvlist_t *detector, nvlist_t *payload) 162 { 163 nvlist_t *nvl; 164 int e = 0; 165 char fullclass[PATH_MAX]; 166 167 (void) snprintf(fullclass, sizeof (fullclass), "%s.io.sas.log.%s", 168 FM_EREPORT_CLASS, ereport_class); 169 170 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) == 0) { 171 172 e |= nvlist_add_string(nvl, FM_CLASS, fullclass); 173 e |= nvlist_add_uint8(nvl, FM_VERSION, FM_EREPORT_VERSION); 174 e |= nvlist_add_uint64(nvl, FM_EREPORT_ENA, ena); 175 e |= nvlist_add_nvlist(nvl, FM_EREPORT_DETECTOR, detector); 176 e |= nvlist_merge(nvl, payload, 0); 177 178 if (e == 0) { 179 fmd_xprt_post(hdl, xprt, nvl, 0); 180 } else { 181 nvlist_free(nvl); 182 fmd_hdl_debug(hdl, "Error adding fields to ereport"); 183 slt_stats.dropped.fmds_value.ui64++; 184 } 185 } else { 186 fmd_hdl_debug(hdl, "Could not allocate space for ereport"); 187 slt_stats.dropped.fmds_value.ui64++; 188 } 189 } 190 191 /* 192 * Create a directory if it doesn't exist. 193 * Parameters: 194 * path: The directory path to create. 195 * mode: The mode used when creating the directory. 196 */ 197 static int 198 do_mkdir(const char *path, mode_t mode) 199 { 200 struct stat st; 201 int status = 0; 202 203 if (stat(path, &st) != 0) { 204 /* Directory does not exist */ 205 if (mkdir(path, mode) != 0) 206 status = -1; 207 } else if (!S_ISDIR(st.st_mode)) { 208 errno = ENOTDIR; 209 status = -1; 210 } 211 212 return (status); 213 } 214 215 /* 216 * Validates that all directories in path exist 217 * path: The directory path to create. 218 * mode: The mode used when creating the directory. 219 */ 220 static int 221 mkpath(char *path, mode_t mode) 222 { 223 char *pp; 224 char *sp; 225 int status = 0; 226 227 pp = path; 228 while (status == 0 && (sp = strchr(pp, '/')) != 0) { 229 if (sp != pp) { 230 /* Neither root nor double slash in path */ 231 *sp = '\0'; 232 status = do_mkdir(path, mode); 233 *sp = '/'; 234 } 235 pp = sp + 1; 236 } 237 238 return (status); 239 } 240 241 /* 242 * Rotate the file from base.max-1->base.max, ... base.1->base.2, base->base.1 243 * Parameter: 244 * file: The name of the current log file. 245 */ 246 void 247 check_file_size(ses_log_monitor_t *slmp, char *file, int byte_count) 248 { 249 int i; 250 char newFile[MAXPATHLEN]; 251 char oldName[MAXPATHLEN]; 252 struct stat st; 253 int size; 254 255 stat(file, &st); 256 size = st.st_size; 257 /* 258 * If current file size plus what will be added is larger 259 * than max file size, rotate the logs 260 * For check to see if larger than configured max size. 261 */ 262 if (size + byte_count < slmp->slt_max_log_size) { 263 /* next log entries can fit */ 264 return; 265 } 266 /* next log entries could make log entries too large */ 267 for (i = slmp->slt_log_count; i > 1; i--) { 268 (void) snprintf(newFile, MAXPATHLEN, "%s.%x", file, i); 269 (void) snprintf(oldName, MAXPATHLEN, "%s.%x", file, i - 1); 270 (void) rename(oldName, newFile); 271 } 272 /* Finally move base to base.1 */ 273 (void) rename(file, oldName); 274 275 } 276 277 /* 278 * This method exists to give access into the fmri. One purpose is to flip the 279 * instance number on the FMRI for a given hc-list entry. It is also 280 * used to pull the value of an hc-list entry. In all cases, the function 281 * returns the value of the hc-list entry found, NULL if no value was found. 282 */ 283 static char * 284 access_fmri(ses_log_monitor_t *slmp, nvlist_t *fmri, char *target, 285 int operation, int *err) 286 { 287 int i; 288 nvpair_t *nvp; 289 nvpair_t *nvp2; 290 uint_t nelem; 291 nvlist_t **nvl_array; 292 char *name; 293 int ival; 294 char ivs[25]; 295 char *target_val = NULL; 296 297 if ((*err = nvlist_lookup_nvpair(fmri, "hc-list", &nvp)) != 0) { 298 fmd_hdl_debug(slmp->slt_hdl, "No hc-list in the fmri"); 299 return (NULL); 300 } 301 302 /* hc-list is an array of nvlists */ 303 (void) nvpair_value_nvlist_array(nvp, &nvl_array, &nelem); 304 305 /* 306 * Loop until you find the list that has hc-name that equals the 307 * passed in "target" value (such as controller) in it. 308 */ 309 for (i = 0; i < nelem; i++) { 310 311 /* Skip this pair if it is not labeled hc-name */ 312 if ((nvlist_lookup_nvpair(nvl_array[i], "hc-name", &nvp2)) 313 != 0) { 314 continue; 315 } 316 317 /* 318 * Extract the value of the name. Continue on an error because 319 * we want to check all of the hc-name entries. 320 */ 321 if (nvpair_value_string(nvp2, &name) != 0) { 322 continue; 323 } 324 325 /* If this isn't the target, go to the next pair. */ 326 if (strcmp(name, target) != 0) { 327 continue; 328 } 329 330 if ((*err = nvlist_lookup_nvpair(nvl_array[i], "hc-id", &nvp2)) 331 != 0) { 332 333 fmd_hdl_debug(slmp->slt_hdl, 334 "Could not find hc-id in the fmri for %s", target); 335 return (NULL); 336 } 337 338 /* 339 * This is the target pair. If we can't get the value then 340 * exit out and log an error. 341 */ 342 if ((*err = nvpair_value_string(nvp2, &target_val)) != 0) { 343 fmd_hdl_debug(slmp->slt_hdl, 344 "Target value not returned."); 345 return (NULL); 346 } 347 348 switch (operation) { 349 350 case INVERT_FMRI_INSTANCE: 351 352 ival = atoi(target_val); 353 ival = (ival + 1) % 2; 354 355 (void) snprintf(ivs, sizeof (ivs), "%d", ival); 356 357 if ((*err = nvlist_remove_nvpair(nvl_array[i], nvp2)) 358 == 0) { 359 360 if ((*err = nvlist_add_string(nvl_array[i], 361 "hc-id", ivs)) != 0) { 362 363 fmd_hdl_debug(slmp->slt_hdl, 364 "Error setting ivalue."); 365 } 366 } else { 367 fmd_hdl_debug(slmp->slt_hdl, 368 "Error removing original ivalue."); 369 } 370 371 break; 372 373 case EXAMINE_FMRI_VALUE: 374 /* 375 * target_val is already set. Return without modifying 376 * its value. 377 */ 378 break; 379 380 /* Can return target_val as is (NULL) */ 381 default: 382 *err = INVALID_OPERATION; 383 break; 384 385 } /* End switch on operation */ 386 387 388 /* Exit the loop. You have found the target */ 389 break; 390 } 391 392 return (target_val); 393 } 394 395 /* 396 * Generate a filename based on the target path 397 * Parameters: 398 * filename: The space for the generated output log file name. 399 * expander: An expander_t struct containing path, pid etc info from the node. 400 * slmp: A pointer to the transport data structure which contains the 401 * configurable file parameters. 402 * byte_count: The number of bytes that will be added to the target file for 403 * this expander. 404 */ 405 static int 406 create_filename(char *fileName, expander_t *expander, ses_log_monitor_t *slmp, 407 int byte_count) 408 { 409 char *ses_node; 410 int i; 411 int label_length; 412 int status = 0; 413 char *subchassis_val = NULL; 414 415 /* 416 * Add the file name with the path root 417 * and append a forward slash if one is not there. 418 */ 419 (void) snprintf(fileName, MAXPATHLEN, "%s", slmp->slt_path); 420 421 ses_node = strrchr(fileName, '/'); 422 423 if ((ses_node != NULL) && (ses_node[0] != '\0')) { 424 (void) strlcat(fileName, "/", MAXPATHLEN); 425 } 426 427 ses_node = strrchr(expander->slt_path, '/'); 428 429 (void) strlcat(fileName, ses_node + 1, MAXPATHLEN); 430 431 /* 432 * If a subchassis is defined, include it in the file name. 433 * Errors are logged in the function. There may legitimately be no 434 * subchassis, so simply continue if none is found. 435 */ 436 subchassis_val = access_fmri(slmp, expander->fmri, SUBCHASSIS, 437 EXAMINE_FMRI_VALUE, &status); 438 439 if (subchassis_val != NULL) { 440 (void) strlcat(fileName, "_", MAXPATHLEN); 441 (void) strlcat(fileName, SUBCHASSIS, MAXPATHLEN); 442 (void) strlcat(fileName, subchassis_val, MAXPATHLEN); 443 } 444 445 (void) strlcat(fileName, "_", MAXPATHLEN); 446 /* remove spaces and forward slashes from name */ 447 label_length = strlen(expander->slt_label); 448 for (i = 0; i < label_length; i++) { 449 if ((!isspace(expander->slt_label[i])) && 450 ('/' != expander->slt_label[i])) { 451 (void) strncat(fileName, &expander->slt_label[i], 1); 452 } 453 } 454 (void) strlcat(fileName, "/log", MAXPATHLEN); 455 456 /* 457 * Ensure directory structure exists for log file. 458 */ 459 status = mkpath(fileName, 0744); 460 461 /* 462 * Check size of file and rotate if necessary. 463 */ 464 check_file_size(slmp, fileName, byte_count); 465 466 return (status); 467 468 } 469 470 /* 471 * Determines the error class type based on the severity of the entry. 472 * Parameter 473 * severity: A severity level from a log entry. 474 */ 475 static char * 476 error_type(int severity) 477 { 478 char *rval; 479 480 switch (severity) { 481 case SES_LOG_LEVEL_FATAL: 482 rval = FATAL_ERROR; 483 break; 484 485 case SES_LOG_LEVEL_ERROR: 486 rval = NON_FATAL_ERROR; 487 break; 488 489 default: 490 rval = NULL; 491 break; 492 } 493 494 return (rval); 495 } 496 497 /* 498 * Allocates and adds an entry for a given expander to the expander list. 499 * Parameters 500 * slmp: A pointer to the ses_log_monitor_t struct for this transport. 501 * key: A unique identifier for this expander. 502 */ 503 static int 504 add_expander_record(ses_log_monitor_t *slmp, char *key) 505 { 506 nvlist_t *expanderDetails; 507 int status = 0; 508 509 510 if ((status = nvlist_alloc(&expanderDetails, NV_UNIQUE_NAME, 0)) != 0) { 511 fmd_hdl_debug(slmp->slt_hdl, 512 "Error allocating expander detail space (%d)", status); 513 return (status); 514 } 515 516 if ((status = nvlist_add_string(expanderDetails, DATA_FIELD, 517 DEFAULT_DATA)) != 0) { 518 519 fmd_hdl_debug(slmp->slt_hdl, 520 "Error adding default data to expander details (%d)", 521 status); 522 } else { 523 524 if ((status = nvlist_add_nvlist(slmp->slt_expanders, key, 525 expanderDetails)) != 0) { 526 527 fmd_hdl_debug(slmp->slt_hdl, 528 "Error storing the default expander details (%d)", 529 status); 530 } 531 } 532 533 nvlist_free(expanderDetails); 534 535 return (status); 536 537 } 538 539 /* 540 * Retrieves the expander record nvlist that is associated with the 541 * expander identified by the given key. If no match is found, an 542 * entry is created with default values. 543 * Parameters 544 * slmp: A pointer to the ses_log_monitor_t struct for this transport. 545 * key: A pointer to the key for an expander. 546 * expdata: A pointer to a pointer for the last log entry data for this 547 * expander. 548 */ 549 static int 550 get_last_entry(ses_log_monitor_t *slmp, char *key, char **expdata) 551 { 552 nvlist_t *expanderRecord; 553 int err = 0; 554 555 /* 556 * Retrieve the expander record that matches this expander. A default 557 * entry will be returned if no matching entry is found. 558 */ 559 if ((err = nvlist_lookup_nvlist(slmp->slt_expanders, key, 560 &expanderRecord)) != 0) { 561 562 if ((err = add_expander_record(slmp, key)) != 0) { 563 fmd_hdl_debug(slmp->slt_hdl, 564 "Expander add failed for %s", key); 565 return (err); 566 } 567 568 if ((err = nvlist_lookup_nvlist(slmp->slt_expanders, key, 569 &expanderRecord)) != 0) { 570 571 fmd_hdl_debug(slmp->slt_hdl, 572 "Could not retrieve the data after adding it", key); 573 return (err); 574 } 575 } 576 577 578 if ((err = nvlist_lookup_string(expanderRecord, DATA_FIELD, expdata)) 579 != 0) { 580 581 fmd_hdl_debug(slmp->slt_hdl, 582 "Could not retrieve the expander data field (%d)", err); 583 return (err); 584 } 585 586 return (err); 587 } 588 589 /* 590 * Searches the platform lists for target codes. If a match is found then 591 * it calls then indicated function. 592 */ 593 static int 594 check_code(ses_log_monitor_t *slmp, nvlist_t *fmri, char *pid, int code) 595 { 596 int status = 0; 597 int i, x; 598 599 for (i = 0; i < platforms.pcount; i++) { 600 if (strcmp(platforms.plist[i].pid, pid) == 0) { 601 602 for (x = 0; x < platforms.plist[i].count; x++) { 603 if (code == platforms.plist[i].codes[x].code) { 604 status = platforms.plist[i].codes[x]. 605 func_ptr(slmp, fmri); 606 607 break; 608 } 609 } 610 break; 611 } 612 } 613 614 if (status != 0) { 615 fmd_hdl_debug(slmp->slt_hdl, 616 "Error checking for a code action (%d)", status); 617 } 618 619 return (status); 620 } 621 622 /* 623 * Searches the platform lists for for a match on the supplied product id. 624 * Returns non zero if supported, zero otherwise. 625 */ 626 static int 627 platform_supported(char *pid) 628 { 629 int supported = 0; 630 int i; 631 632 for (i = 0; i < platforms.pcount; i++) { 633 if (strcmp(platforms.plist[i].pid, pid) == 0) { 634 supported = 1; 635 break; 636 } 637 } 638 639 return (supported); 640 } 641 642 /* 643 * Inverts the controller instance and the expander instance in the 644 * specified FMRI. 645 */ 646 static int 647 invert_fmri(ses_log_monitor_t *slmp, nvlist_t *fmri) 648 { 649 int err = 0; 650 651 (void) access_fmri(slmp, fmri, CONTROLLER, INVERT_FMRI_INSTANCE, &err); 652 if (err != 0) { 653 fmd_hdl_debug(slmp->slt_hdl, 654 "error inverting the controller instance: %d", err); 655 return (err); 656 } 657 658 (void) access_fmri(slmp, fmri, SASEXPANDER, INVERT_FMRI_INSTANCE, &err); 659 if (err != 0) { 660 fmd_hdl_debug(slmp->slt_hdl, 661 "error inverting sas-expander instance: %d", err); 662 } 663 664 return (err); 665 } 666 667 /* 668 * Checks the severity of the log entry against the configured boundary, 669 * generates and ereport, and writes the data out to the log file. 670 * Parameters 671 * slmp: A pointer to the ses_log_monitor_t struct for this transport. 672 * entry: The log entry 673 * ena: the ena for this transport. 674 * expander: Contains derived information for this expander. 675 * format_time: The formatted time to append to this entry. 676 * fp: A file pointer for the data to be written out to. 677 */ 678 static int 679 handle_log_entry(ses_log_monitor_t *slmp, nvpair_t *entry, 680 expander_t *expander, char *format_time, FILE *fp) 681 { 682 nvlist_t *entry_data; 683 char *log_entry; 684 char *severity; 685 int severityValue = 0; 686 char *code; 687 char *class_sev = NULL; 688 uint64_t ena; 689 int rval = 0; 690 691 if ((rval = nvpair_value_nvlist(entry, &entry_data)) != 0) { 692 fmd_hdl_debug(slmp->slt_hdl, "Unable to retrieve entry"); 693 return (rval); 694 } 695 696 if ((rval = nvlist_lookup_string(entry_data, ENTRY_SEVERITY, &severity)) 697 == 0) { 698 699 severityValue = atoi(severity); 700 701 if (severityValue >= slmp->slt_severity) { 702 /* 703 * Pull the code and check to see if there are any 704 * special operations to perform for it on the given 705 * platform. 706 */ 707 if ((rval = nvlist_lookup_string(entry_data, ENTRY_CODE, 708 &code)) != 0) { 709 710 fmd_hdl_debug(slmp->slt_hdl, 711 "Error retrieving code: %d", rval); 712 return (rval); 713 } 714 715 /* 716 * Check this code for any actions specific 717 * to this platform. 718 */ 719 (void) check_code(slmp, expander->fmri, 720 expander->slt_pid, atoi(code)); 721 722 class_sev = error_type(severityValue); 723 if (class_sev == NULL) { 724 fmd_hdl_debug(slmp->slt_hdl, 725 "log severity %d mapped to NULL", severity); 726 return (INVALID_SEVERITY); 727 } 728 729 /* Create the ENA for this ereport */ 730 ena = fmd_event_ena_create(slmp->slt_hdl); 731 732 slt_post_ereport(slmp->slt_hdl, slmp->slt_xprt, 733 class_sev, ena, expander->fmri, entry_data); 734 735 } 736 } else { 737 738 fmd_hdl_debug(slmp->slt_hdl, 739 "Unable to pull severity from the entry."); 740 return (rval); 741 } 742 743 /* 744 * Append the log entry to the log file. 745 */ 746 if (fp) { 747 748 if ((rval = nvlist_lookup_string(entry_data, ENTRY_LOG, 749 &log_entry)) == 0) { 750 751 (void) fprintf(fp, "%s %s\n", format_time, 752 log_entry); 753 } else { 754 755 fmd_hdl_debug(slmp->slt_hdl, 756 "Unable to pull log from the entry."); 757 } 758 } 759 760 return (rval); 761 762 } 763 764 /* 765 * The function performs the work of deallocating the space used for an 766 * expander_t structure. 767 * Parameters: 768 * slmp: A pointer to t ses_log_monitor_t struct for this transport. 769 * exp: A pointer to an expander_t structure that identifies an expander. 770 */ 771 static void 772 free_expander(ses_log_monitor_t *slmp, expander_t *exp) 773 { 774 if (exp != NULL) { 775 if (exp->fmri != NULL) { 776 nvlist_free(exp->fmri); 777 } 778 fmd_hdl_free(slmp->slt_hdl, exp, sizeof (expander_t)); 779 } 780 } 781 782 /* 783 * This function performs the log read on a target 784 * 785 * Parameters: 786 * slmp: A pointer to the ses log monitor structure. 787 * expander: A pointer to an expander object that contains info required 788 * for a call to the libseslog library. 789 * lib_param: The structure used to pass data to and from the library. This 790 * contains the target's information as well as a ponter to returned data. 791 */ 792 static int 793 get_log(ses_log_monitor_t *slmp, expander_t *expander, 794 struct ses_log_call_struct *lib_param) 795 { 796 char *expdata; 797 int err; 798 nvlist_t *expanderRecord; 799 800 /* Retrieve the last entry for this expander for the lib call */ 801 if ((err = get_last_entry(slmp, expander->slt_key, &expdata)) != 0) { 802 803 fmd_hdl_debug(slmp->slt_hdl, "Error collecting expander entry"); 804 return (err); 805 } 806 (void) strncpy(lib_param->target_path, expander->slt_path, MAXPATHLEN); 807 (void) strncpy(lib_param->product_id, expander->slt_pid, MAXNAMELEN); 808 (void) strncpy(lib_param->last_log_entry, expdata, MAXNAMELEN); 809 lib_param->poll_time = slmp->slt_interval; 810 811 /* 812 * If the library call returned non zero, log it, however, the call 813 * may still have returned valid log data. Check the log data. If it 814 * is NULL, return an error. Otherwise continue processing. 815 */ 816 if ((err = access_ses_log(lib_param)) != 0) { 817 fmd_hdl_debug(slmp->slt_hdl, "Library access error: %d", err); 818 } 819 820 /* Double check that log data actually exists. */ 821 if (lib_param->log_data == NULL) { 822 if (err != 0) { 823 return (err); 824 } 825 return (NULL_LOG_DATA); 826 } 827 828 /* 829 * If we can retrieve the expander details for this expander then store 830 * the last log entry returned from the library. Otherwise log it 831 * and continue processing. 832 */ 833 if ((err = nvlist_lookup_nvlist(slmp->slt_expanders, expander->slt_key, 834 &expanderRecord)) == 0) { 835 836 if (nvlist_add_string(expanderRecord, DATA_FIELD, 837 lib_param->last_log_entry) != 0) { 838 839 fmd_hdl_debug(slmp->slt_hdl, 840 "Error saving buffer data in expander details"); 841 } 842 } else { 843 fmd_hdl_debug(slmp->slt_hdl, 844 "Could not retrieve expander to store last entry: %d", err); 845 } 846 847 return (err); 848 849 } 850 851 /* 852 * This function processes the log data from a target. This includes 853 * writing the data to the filesystem and initiating generation of ereports 854 * as needed by calling slt_post_ereport. 855 * 856 * 857 * Parameters: 858 * slmp: A pointer to the ses log monitor structure. 859 * expander: A pointer to an expander object that contains info about the 860 * expander. 861 * lib_param: The structure used to pass data to and from the library. This 862 * contains the target's information as well as a ponter to returned data. 863 */ 864 static int 865 process_log(ses_log_monitor_t *slmp, expander_t *expander, 866 struct ses_log_call_struct *lib_param) 867 { 868 nvlist_t *result; 869 int err; 870 871 char *pairName; 872 nvpair_t *entry = NULL; 873 FILE *fp = NULL; 874 char fileName[MAXPATHLEN]; 875 time_t now; 876 char format_time[30]; 877 struct tm tim; 878 int output_count; 879 880 /* 881 * Determine how many bytes will be written out with this response, 882 * pass this count to a function that will determine whether or not 883 * to roll the logs, and will return the name of the file path to use. 884 */ 885 output_count = lib_param->number_log_entries * DATE_STRING_SIZE + 886 lib_param->size_of_log_entries; 887 888 err = create_filename(fileName, expander, slmp, output_count); 889 890 if (err == 0) { 891 fp = fopen(fileName, "a"); 892 if (fp == NULL) { 893 fmd_hdl_debug(slmp->slt_hdl, "File open failed"); 894 } 895 } 896 897 /* Format the time to prepend to the log entry */ 898 now = time(NULL); 899 tim = *(localtime(&now)); 900 (void) strftime(format_time, 30, "%b %d %H:%M:%S ", &tim); 901 902 /* 903 * For each entry returned, generate an ereport if the severity 904 * is at or above the target level, then append all entries to 905 * the appropriate log file. 906 */ 907 result = lib_param->log_data; 908 while ((entry = nvlist_next_nvpair(result, entry)) != NULL) { 909 910 pairName = nvpair_name(entry); 911 /* 912 * Process each entry in the result data returned from 913 * the library call. These are log entries and may 914 * warrant an ereport. 915 */ 916 if (strncmp(ENTRY_PREFIX, pairName, 5) == 0) { 917 918 err = handle_log_entry(slmp, entry, expander, 919 format_time, fp); 920 } 921 } 922 923 /* Close the log file */ 924 if (fp) { 925 (void) fclose(fp); 926 fp = NULL; 927 } 928 929 /* Free the space used for the result and the fmri. */ 930 nvlist_free(result); 931 932 return (0); 933 934 } 935 936 /* 937 * This function performs the log read and processing of the logs for a target 938 * as well as writing the data to the filesystem. Ereports are generated 939 * as needed by calling slt_post_ereport. 940 * 941 * Access the log data for a specific ses. 942 * If a log entry should generate an ereport, call slt_post_ereport 943 * Format and store the data at the appropriate location. 944 */ 945 static int 946 slt_process_ses_log(topo_hdl_t *thp, tnode_t *node, void *arg) 947 { 948 ses_log_monitor_t *slmp = arg; 949 nvlist_t *fmri; 950 expander_t *expander; 951 struct ses_log_call_struct lib_param; 952 953 int err = 0; 954 char *label = NULL; 955 char *target_path = NULL; 956 char *product_id = NULL; 957 char *sas_address = NULL; 958 959 if (strcmp(SASEXPANDER, topo_node_name(node)) != 0) { 960 /* Not the type of node we are looking for */ 961 return (TOPO_WALK_NEXT); 962 } 963 964 if (topo_prop_get_string(node, "authority", "product-id", 965 &product_id, &err) != 0) { 966 fmd_hdl_debug(slmp->slt_hdl, 967 "Error collecting product_id %d", err); 968 return (TOPO_WALK_NEXT); 969 } 970 971 /* If the current system type is unsupported stop processing the node */ 972 if (platform_supported(product_id) == 0) { 973 fmd_hdl_debug(slmp->slt_hdl, "Unsupported platform %d", 974 product_id); 975 topo_hdl_strfree(thp, product_id); 976 return (TOPO_WALK_NEXT); 977 } 978 979 /* Allocate space for the holder structure */ 980 expander = (expander_t *)fmd_hdl_zalloc(slmp->slt_hdl, 981 sizeof (expander_t), FMD_SLEEP); 982 983 (void) snprintf(expander->slt_pid, MAXNAMELEN, "%s", product_id); 984 topo_hdl_strfree(thp, product_id); 985 986 if (topo_prop_get_string(node, "protocol", "label", &label, &err) 987 != 0) { 988 fmd_hdl_debug(slmp->slt_hdl, "Error collecting label %d", err); 989 free_expander(slmp, expander); 990 return (TOPO_WALK_NEXT); 991 } 992 (void) snprintf(expander->slt_label, MAXNAMELEN, "%s", label); 993 topo_hdl_strfree(thp, label); 994 995 if (topo_prop_get_string(node, TOPO_PGROUP_SES, 996 TOPO_PROP_SES_DEV_PATH, &target_path, &err) != 0) { 997 fmd_hdl_debug(slmp->slt_hdl, 998 "Error collecting ses-devfs-path for %s: %d", 999 expander->slt_label, err); 1000 free_expander(slmp, expander); 1001 return (TOPO_WALK_NEXT); 1002 } 1003 (void) snprintf(expander->slt_path, MAXPATHLEN, "%s", target_path); 1004 topo_hdl_strfree(thp, target_path); 1005 1006 if (topo_prop_get_string(node, TOPO_PGROUP_STORAGE, 1007 TOPO_PROP_SAS_ADDR, &sas_address, &err) != 0) { 1008 fmd_hdl_debug(slmp->slt_hdl, 1009 "Error collecting sas_address for %s: %d", 1010 expander->slt_label, err); 1011 free_expander(slmp, expander); 1012 return (TOPO_WALK_NEXT); 1013 } 1014 if (strlen(sas_address) != 16) { 1015 fmd_hdl_debug(slmp->slt_hdl, 1016 "sas-address length is not 16: (%s)", sas_address); 1017 free_expander(slmp, expander); 1018 topo_hdl_strfree(thp, sas_address); 1019 return (TOPO_WALK_NEXT); 1020 } 1021 (void) snprintf(expander->slt_key, MAXNAMELEN, "%s", sas_address); 1022 topo_hdl_strfree(thp, sas_address); 1023 1024 /* Obtain the fmri for this node and save a reference to it. */ 1025 if (topo_node_resource(node, &fmri, &err) != 0) { 1026 fmd_hdl_debug(slmp->slt_hdl, "failed to get fmri for %s: %s", 1027 expander->slt_label, topo_strerror(err)); 1028 1029 free_expander(slmp, expander); 1030 return (TOPO_WALK_NEXT); 1031 } else { 1032 expander->fmri = fmri; 1033 } 1034 1035 if ((err = get_log(slmp, expander, &lib_param)) != 0) { 1036 /* 1037 * NULL_LOG_DATA means that no data was returned from the 1038 * library. (i.e. There were no log entries.) Just free memory 1039 * and return. 1040 */ 1041 if (err != NULL_LOG_DATA) { 1042 fmd_hdl_debug(slmp->slt_hdl, 1043 "Error retrieving logs from %s: %d", 1044 expander->slt_label, err); 1045 } 1046 free_expander(slmp, expander); 1047 return (TOPO_WALK_NEXT); 1048 } 1049 1050 if ((err = process_log(slmp, expander, &lib_param)) != 0) { 1051 fmd_hdl_debug(slmp->slt_hdl, 1052 "Error processing logs from %s: %d", 1053 expander->slt_label, err); 1054 } 1055 1056 /* Free the expander structure before exiting. */ 1057 free_expander(slmp, expander); 1058 1059 return (TOPO_WALK_NEXT); 1060 } 1061 1062 /* 1063 * Called by the FMD after the specified timeout has expired. 1064 * This initiates the processing of the SES device logs. 1065 * slt_process_ses_log() performs the actual log retrieval and analysis. 1066 * 1067 * The last action is to reset the timer so that this method is called again. 1068 */ 1069 /*ARGSUSED*/ 1070 static void 1071 slt_timeout(fmd_hdl_t *hdl, id_t id, void *data) 1072 { 1073 topo_hdl_t *thp; 1074 topo_walk_t *twp; 1075 int err; 1076 1077 /* Retrieve the SES log monitor structure. */ 1078 ses_log_monitor_t *slmp = fmd_hdl_getspecific(hdl); 1079 1080 if (slmp == NULL) { 1081 fmd_hdl_abort(hdl, "Unable to retrieve log monitor structure."); 1082 return; 1083 } 1084 slmp->slt_hdl = hdl; 1085 1086 thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION); 1087 1088 /* 1089 * This initializes a topology walk structure for stepping through 1090 * the snapshot associated with thp. Note that a callback function 1091 * is supplied (slt_process_ses_log in this case). 1092 */ 1093 if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, slt_process_ses_log, 1094 slmp, &err)) == NULL) { 1095 1096 fmd_hdl_topo_rele(hdl, thp); 1097 fmd_hdl_abort(hdl, "failed to get topology: %s\n", 1098 topo_strerror(err)); 1099 return; 1100 } 1101 1102 /* 1103 * This function walks through the snapshot and invokes the callback 1104 * function supplied when it was set up above. 1105 */ 1106 if (topo_walk_step(twp, TOPO_WALK_CHILD) == TOPO_WALK_ERR) { 1107 topo_walk_fini(twp); 1108 fmd_hdl_topo_rele(hdl, thp); 1109 fmd_hdl_abort(hdl, "failed to walk topology\n"); 1110 return; 1111 } 1112 1113 /* This releases the walk structure. */ 1114 topo_walk_fini(twp); 1115 fmd_hdl_topo_rele(hdl, thp); 1116 1117 /* Reset the timer for the next iteration. */ 1118 slmp->slt_timer = fmd_timer_install(hdl, NULL, NULL, 1119 slmp->slt_interval); 1120 1121 } 1122 1123 /* 1124 * Entry points for the FMD to access this transport. 1125 */ 1126 static const fmd_hdl_ops_t fmd_ops = { 1127 NULL, /* fmdo_recv */ 1128 slt_timeout, /* fmdo_timeout */ 1129 NULL, /* fmdo_close */ 1130 NULL, /* fmdo_stats */ 1131 NULL, /* fmdo_gc */ 1132 NULL, /* fmdo_send */ 1133 NULL, /* fmdo_topo_change */ 1134 }; 1135 1136 static const fmd_hdl_info_t fmd_info = { 1137 "SES Log Transport Agent", "1.0", &fmd_ops, fmd_props 1138 }; 1139 1140 /* 1141 * Initialize the transport. 1142 */ 1143 void 1144 _fmd_init(fmd_hdl_t *hdl) 1145 { 1146 ses_log_monitor_t *slmp; 1147 int error; 1148 nvlist_t *expanderList; 1149 1150 if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) 1151 return; 1152 1153 (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, 1154 sizeof (slt_stats) / sizeof (fmd_stat_t), 1155 (fmd_stat_t *)&slt_stats); 1156 1157 slmp = fmd_hdl_zalloc(hdl, sizeof (ses_log_monitor_t), FMD_SLEEP); 1158 fmd_hdl_setspecific(hdl, slmp); 1159 1160 slmp->slt_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL); 1161 if (slmp->slt_xprt == NULL) { 1162 fmd_hdl_error(hdl, 1163 "Unable to obtain a reference to the transport"); 1164 fmd_hdl_free(hdl, slmp, sizeof (*slmp)); 1165 fmd_hdl_unregister(hdl); 1166 return; 1167 } 1168 1169 /* 1170 * interval is validity checked by the framework since it is of type 1171 * FMD_TYPE_TIME. 1172 */ 1173 slmp->slt_interval = fmd_prop_get_int64(hdl, "interval"); 1174 1175 /* 1176 * Use default the severity if it is out of range. 1177 * Setting the severity too high is allowed as this has the effect 1178 * of preventing any ereports from being generated. 1179 */ 1180 slmp->slt_severity = fmd_prop_get_int32(hdl, "severity"); 1181 if (slmp->slt_severity < SES_LOG_LEVEL_NOTICE) { 1182 1183 slmp->slt_severity = SES_LOG_LEVEL_ERROR; 1184 } 1185 1186 slmp->slt_log_count = fmd_prop_get_int32(hdl, "logcount"); 1187 if (slmp->slt_log_count < MIN_LOG_COUNT) { 1188 slmp->slt_log_count = MIN_LOG_COUNT; 1189 } 1190 1191 slmp->slt_max_log_size = fmd_prop_get_int32(hdl, "maxlogsize"); 1192 if (slmp->slt_max_log_size < MIN_LOG_SIZE) { 1193 slmp->slt_max_log_size = MIN_LOG_SIZE; 1194 } 1195 1196 /* Invalid paths will be handled by logging and skipping log creation */ 1197 slmp->slt_path = fmd_prop_get_string(hdl, "path"); 1198 1199 /* Allocate space for the expander id holder */ 1200 if ((error = nvlist_alloc(&expanderList, NV_UNIQUE_NAME, 0)) != 0) { 1201 fmd_xprt_close(hdl, slmp->slt_xprt); 1202 fmd_hdl_strfree(hdl, slmp->slt_path); 1203 fmd_hdl_free(hdl, slmp, sizeof (*slmp)); 1204 1205 fmd_hdl_error(hdl, 1206 "Error allocating space for the expander list: %d", error); 1207 fmd_hdl_unregister(hdl); 1208 return; 1209 } 1210 1211 slmp->slt_expanders = expanderList; 1212 1213 /* 1214 * Call our initial timer routine, starting the periodic timeout. 1215 */ 1216 slmp->slt_timer = fmd_timer_install(hdl, NULL, NULL, 0); 1217 } 1218 1219 /* 1220 * Shut down the transport. The primary responsibility is to release any 1221 * allocated memory. 1222 */ 1223 void 1224 _fmd_fini(fmd_hdl_t *hdl) 1225 { 1226 ses_log_monitor_t *slmp; 1227 1228 slmp = fmd_hdl_getspecific(hdl); 1229 if (slmp) { 1230 fmd_timer_remove(hdl, slmp->slt_timer); 1231 fmd_xprt_close(hdl, slmp->slt_xprt); 1232 fmd_prop_free_string(hdl, slmp->slt_path); 1233 nvlist_free(slmp->slt_expanders); 1234 fmd_hdl_free(hdl, slmp, sizeof (*slmp)); 1235 } 1236 } 1237