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
slt_post_ereport(fmd_hdl_t * hdl,fmd_xprt_t * xprt,const char * ereport_class,uint64_t ena,nvlist_t * detector,nvlist_t * payload)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
do_mkdir(const char * path,mode_t mode)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
mkpath(char * path,mode_t mode)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
check_file_size(ses_log_monitor_t * slmp,char * file,int byte_count)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 *
access_fmri(ses_log_monitor_t * slmp,nvlist_t * fmri,char * target,int operation,int * err)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
create_filename(char * fileName,expander_t * expander,ses_log_monitor_t * slmp,int byte_count)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 *
error_type(int severity)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
add_expander_record(ses_log_monitor_t * slmp,char * key)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
get_last_entry(ses_log_monitor_t * slmp,char * key,char ** expdata)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
check_code(ses_log_monitor_t * slmp,nvlist_t * fmri,char * pid,int code)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
platform_supported(char * pid)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
invert_fmri(ses_log_monitor_t * slmp,nvlist_t * fmri)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
handle_log_entry(ses_log_monitor_t * slmp,nvpair_t * entry,expander_t * expander,char * format_time,FILE * fp)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
free_expander(ses_log_monitor_t * slmp,expander_t * exp)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
get_log(ses_log_monitor_t * slmp,expander_t * expander,struct ses_log_call_struct * lib_param)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
process_log(ses_log_monitor_t * slmp,expander_t * expander,struct ses_log_call_struct * lib_param)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
slt_process_ses_log(topo_hdl_t * thp,tnode_t * node,void * arg)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
slt_timeout(fmd_hdl_t * hdl,id_t id,void * data)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
_fmd_init(fmd_hdl_t * hdl)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
_fmd_fini(fmd_hdl_t * hdl)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