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