1e5dcf7beSRobert Johnston /*
2e5dcf7beSRobert Johnston  * CDDL HEADER START
3e5dcf7beSRobert Johnston  *
4e5dcf7beSRobert Johnston  * The contents of this file are subject to the terms of the
5e5dcf7beSRobert Johnston  * Common Development and Distribution License (the "License").
6e5dcf7beSRobert Johnston  * You may not use this file except in compliance with the License.
7e5dcf7beSRobert Johnston  *
8e5dcf7beSRobert Johnston  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9e5dcf7beSRobert Johnston  * or http://www.opensolaris.org/os/licensing.
10e5dcf7beSRobert Johnston  * See the License for the specific language governing permissions
11e5dcf7beSRobert Johnston  * and limitations under the License.
12e5dcf7beSRobert Johnston  *
13e5dcf7beSRobert Johnston  * When distributing Covered Code, include this CDDL HEADER in each
14e5dcf7beSRobert Johnston  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15e5dcf7beSRobert Johnston  * If applicable, add the following below this CDDL HEADER, with the
16e5dcf7beSRobert Johnston  * fields enclosed by brackets "[]" replaced with your own identifying
17e5dcf7beSRobert Johnston  * information: Portions Copyright [yyyy] [name of copyright owner]
18e5dcf7beSRobert Johnston  *
19e5dcf7beSRobert Johnston  * CDDL HEADER END
20e5dcf7beSRobert Johnston  */
21e5dcf7beSRobert Johnston /*
22f6e214c7SGavin Maltby  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23ea30102cSRob Johnston  * Copyright (c) 2018, Joyent, Inc.
24e5dcf7beSRobert Johnston  */
25e5dcf7beSRobert Johnston 
26e5dcf7beSRobert Johnston #include <fm/fmd_api.h>
27e5dcf7beSRobert Johnston #include <fm/libtopo.h>
28e5dcf7beSRobert Johnston #include <fm/topo_hc.h>
29e5dcf7beSRobert Johnston #include <fm/topo_mod.h>
30e5dcf7beSRobert Johnston #include <fm/topo_method.h>
31e5dcf7beSRobert Johnston 
32e5dcf7beSRobert Johnston #include <sys/fm/protocol.h>
33e5dcf7beSRobert Johnston #include <sys/systeminfo.h>
34e5dcf7beSRobert Johnston 
35e5dcf7beSRobert Johnston #include <string.h>
36e5dcf7beSRobert Johnston 
37e5dcf7beSRobert Johnston #define	ST_EREPORT_CLASS	"ereport.sensor.failure"
38e5dcf7beSRobert Johnston 
39e5dcf7beSRobert Johnston typedef struct sensor_fault {
40e5dcf7beSRobert Johnston 	struct sensor_fault	*sf_next;
41e5dcf7beSRobert Johnston 	char			*sf_fru;
426c258465SRobert Johnston 	uint32_t		sf_num_fails;
43e5dcf7beSRobert Johnston 	boolean_t		sf_last_faulted;
44e5dcf7beSRobert Johnston 	boolean_t		sf_faulted;
45e5dcf7beSRobert Johnston 	boolean_t		sf_unknown;
46e5dcf7beSRobert Johnston } sensor_fault_t;
47e5dcf7beSRobert Johnston 
48e5dcf7beSRobert Johnston typedef struct sensor_transport {
49e5dcf7beSRobert Johnston 	fmd_hdl_t	*st_hdl;
50e5dcf7beSRobert Johnston 	fmd_xprt_t	*st_xprt;
51e5dcf7beSRobert Johnston 	hrtime_t	st_interval;
52e5dcf7beSRobert Johnston 	id_t		st_timer;
53e5dcf7beSRobert Johnston 	sensor_fault_t	*st_faults;
54e5dcf7beSRobert Johnston 	boolean_t	st_first;
556c258465SRobert Johnston 	/*
566c258465SRobert Johnston 	 * The number of consecutive sensor readings indicating failure that
576c258465SRobert Johnston 	 * we'll tolerate before sending an ereport.
586c258465SRobert Johnston 	 */
596c258465SRobert Johnston 	uint32_t	st_tolerance;
60ea30102cSRob Johnston 	nvlist_t	*st_spoofs;
61e5dcf7beSRobert Johnston } sensor_transport_t;
62e5dcf7beSRobert Johnston 
63e5dcf7beSRobert Johnston typedef struct st_stats {
64e5dcf7beSRobert Johnston 	fmd_stat_t st_bad_fmri;
65e5dcf7beSRobert Johnston 	fmd_stat_t st_topo_errs;
66e5dcf7beSRobert Johnston 	fmd_stat_t st_repairs;
67e5dcf7beSRobert Johnston } st_stats_t;
68e5dcf7beSRobert Johnston 
69e5dcf7beSRobert Johnston st_stats_t st_stats = {
70e5dcf7beSRobert Johnston 	{ "bad_fmri", FMD_TYPE_UINT64, "bad or missing resource/FRU FMRI" },
71e5dcf7beSRobert Johnston 	{ "topo_errors", FMD_TYPE_UINT64, "errors walking topology" },
72e5dcf7beSRobert Johnston 	{ "repairs", FMD_TYPE_UINT64, "auto repairs" }
73e5dcf7beSRobert Johnston };
74e5dcf7beSRobert Johnston 
75f6e214c7SGavin Maltby static int st_check_component_complaints;
76f6e214c7SGavin Maltby static int have_complained;
77ea30102cSRob Johnston static char *spoof_prop = NULL;
78f6e214c7SGavin Maltby 
79e5dcf7beSRobert Johnston static int
st_check_component(topo_hdl_t * thp,tnode_t * node,void * arg)80e5dcf7beSRobert Johnston st_check_component(topo_hdl_t *thp, tnode_t *node, void *arg)
81e5dcf7beSRobert Johnston {
82e5dcf7beSRobert Johnston 	sensor_transport_t *stp = arg;
83e5dcf7beSRobert Johnston 	fmd_hdl_t *hdl = stp->st_hdl;
84e5dcf7beSRobert Johnston 	const char *name = topo_node_name(node);
85e5dcf7beSRobert Johnston 	nvlist_t *nvl, *props, *rsrc, *fru;
86e5dcf7beSRobert Johnston 	char *fmri;
876c258465SRobert Johnston 	int err, ret;
88e5dcf7beSRobert Johnston 	int32_t last_source, source = -1;
89ea30102cSRob Johnston 	boolean_t nonrecov, faulted, predictive, source_diff, injected;
90e5dcf7beSRobert Johnston 	nvpair_t *nvp;
91e5dcf7beSRobert Johnston 	uint64_t ena;
92e5dcf7beSRobert Johnston 	nvlist_t *event;
93e5dcf7beSRobert Johnston 	sensor_fault_t *sfp, **current;
94e5dcf7beSRobert Johnston 
95e5dcf7beSRobert Johnston 	if (strcmp(name, FAN) != 0 && strcmp(name, PSU) != 0)
96e5dcf7beSRobert Johnston 		return (0);
97e5dcf7beSRobert Johnston 
986c258465SRobert Johnston 	if (topo_node_resource(node, &rsrc, NULL) != 0) {
996c258465SRobert Johnston 		st_stats.st_bad_fmri.fmds_value.ui64++;
1006c258465SRobert Johnston 		return (0);
1016c258465SRobert Johnston 	}
1026c258465SRobert Johnston 
1036c258465SRobert Johnston 	/*
1046c258465SRobert Johnston 	 * If the resource isn't present, don't bother invoking the sensor
1056c258465SRobert Johnston 	 * failure method.  It may be that the sensors aren't part of the same
1066c258465SRobert Johnston 	 * physical FRU and will report failure if the FRU is no longer there.
1076c258465SRobert Johnston 	 */
1086c258465SRobert Johnston 	if ((ret = topo_fmri_present(thp, rsrc, &err)) < 0) {
1096c258465SRobert Johnston 		fmd_hdl_debug(hdl, "topo_fmri_present() failed for %s=%d",
1106c258465SRobert Johnston 		    name, topo_node_instance(node));
1116c258465SRobert Johnston 		nvlist_free(rsrc);
1126c258465SRobert Johnston 		return (0);
1136c258465SRobert Johnston 	}
1146c258465SRobert Johnston 
1156c258465SRobert Johnston 	if (!ret) {
1166c258465SRobert Johnston 		fmd_hdl_debug(hdl, "%s=%d is not present, ignoring",
1176c258465SRobert Johnston 		    name, topo_node_instance(node));
1186c258465SRobert Johnston 		nvlist_free(rsrc);
1196c258465SRobert Johnston 		return (0);
1206c258465SRobert Johnston 	}
1216c258465SRobert Johnston 
122e5dcf7beSRobert Johnston 	if (topo_method_invoke(node, TOPO_METH_SENSOR_FAILURE,
123ea30102cSRob Johnston 	    TOPO_METH_SENSOR_FAILURE_VERSION, stp->st_spoofs, &nvl, &err) !=
124ea30102cSRob Johnston 	    0) {
125e5dcf7beSRobert Johnston 		if (err == ETOPO_METHOD_NOTSUP) {
126f6e214c7SGavin Maltby 			st_check_component_complaints++;
127f6e214c7SGavin Maltby 			if (!have_complained) {
128f6e214c7SGavin Maltby 				fmd_hdl_debug(hdl, "Method %s not supported "
129f6e214c7SGavin Maltby 				    "on %s=%d", TOPO_METH_SENSOR_FAILURE, name,
130f6e214c7SGavin Maltby 				    topo_node_instance(node));
131f6e214c7SGavin Maltby 			}
1326c258465SRobert Johnston 			nvlist_free(rsrc);
133e5dcf7beSRobert Johnston 			return (0);
134e5dcf7beSRobert Johnston 		}
135e5dcf7beSRobert Johnston 		nvl = NULL;
136e5dcf7beSRobert Johnston 	}
137e5dcf7beSRobert Johnston 
138ea30102cSRob Johnston 	if (topo_node_fru(node, &fru, NULL, &err) != 0) {
139e5dcf7beSRobert Johnston 		st_stats.st_bad_fmri.fmds_value.ui64++;
140e5dcf7beSRobert Johnston 		nvlist_free(nvl);
141e5dcf7beSRobert Johnston 		nvlist_free(rsrc);
142e5dcf7beSRobert Johnston 		return (0);
143e5dcf7beSRobert Johnston 	}
144e5dcf7beSRobert Johnston 
145e5dcf7beSRobert Johnston 	if (topo_fmri_nvl2str(thp, fru, &fmri, &err) != 0) {
146e5dcf7beSRobert Johnston 		st_stats.st_bad_fmri.fmds_value.ui64++;
147e5dcf7beSRobert Johnston 		nvlist_free(nvl);
148e5dcf7beSRobert Johnston 		nvlist_free(fru);
149e5dcf7beSRobert Johnston 		nvlist_free(rsrc);
150e5dcf7beSRobert Johnston 		return (0);
151e5dcf7beSRobert Johnston 	}
152e5dcf7beSRobert Johnston 
153e5dcf7beSRobert Johnston 	nvlist_free(fru);
154e5dcf7beSRobert Johnston 
155ea30102cSRob Johnston 	faulted = nonrecov = source_diff = injected = B_FALSE;
156e5dcf7beSRobert Johnston 	predictive = B_TRUE;
157e5dcf7beSRobert Johnston 	if (nvl != NULL)  {
158e5dcf7beSRobert Johnston 		nvp = NULL;
159e5dcf7beSRobert Johnston 		while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
160e5dcf7beSRobert Johnston 			if (nvpair_value_nvlist(nvp, &props) != 0)
161e5dcf7beSRobert Johnston 				continue;
162e5dcf7beSRobert Johnston 
163e5dcf7beSRobert Johnston 			faulted = B_TRUE;
164e5dcf7beSRobert Johnston 
165e5dcf7beSRobert Johnston 			/*
166e5dcf7beSRobert Johnston 			 * We need some simple rules to handle the case where
167e5dcf7beSRobert Johnston 			 * there are multiple facility nodes that indicate
168e5dcf7beSRobert Johnston 			 * a problem with this FRU, but disagree on the values
169e5dcf7beSRobert Johnston 			 * of nonrecov, predictive or source:
170e5dcf7beSRobert Johnston 			 *
171e5dcf7beSRobert Johnston 			 * 1) nonrecov will be set to true if one or more
172e5dcf7beSRobert Johnston 			 *   facility nodes indicates true.  Otherwise it will
173e5dcf7beSRobert Johnston 			 *   default to false
174e5dcf7beSRobert Johnston 			 *
175e5dcf7beSRobert Johnston 			 * 2) predictive will default to false and remain false
176e5dcf7beSRobert Johnston 			 *    if one or more facility nodes indicate false.
177e5dcf7beSRobert Johnston 			 *
178e5dcf7beSRobert Johnston 			 * 3) source will be set to unknown unless all facility
179e5dcf7beSRobert Johnston 			 *    nodes agree on the source
180ea30102cSRob Johnston 			 *
181ea30102cSRob Johnston 			 * 4) injected defaults to false, but will be set to
182ea30102cSRob Johnston 			 *    true if any of the sensor states were injected.
183e5dcf7beSRobert Johnston 			 */
184e5dcf7beSRobert Johnston 			if (nonrecov == B_FALSE)
185e5dcf7beSRobert Johnston 				if (nvlist_lookup_boolean_value(props,
186e5dcf7beSRobert Johnston 				    "nonrecov", &nonrecov) != 0)
187e5dcf7beSRobert Johnston 					nonrecov = B_FALSE;
188e5dcf7beSRobert Johnston 			if (predictive == B_TRUE)
189e5dcf7beSRobert Johnston 				if (nvlist_lookup_boolean_value(props,
190e5dcf7beSRobert Johnston 				    "predictive", &predictive) != 0)
191e5dcf7beSRobert Johnston 					predictive = B_FALSE;
192ea30102cSRob Johnston 			(void) nvlist_lookup_boolean_value(props,
193ea30102cSRob Johnston 			    "injected", &injected);
194e5dcf7beSRobert Johnston 
195e5dcf7beSRobert Johnston 			last_source = source;
196e5dcf7beSRobert Johnston 			if (nvlist_lookup_uint32(props, "source",
197e5dcf7beSRobert Johnston 			    (uint32_t *)&source) != 0)
198e5dcf7beSRobert Johnston 				source = TOPO_SENSOR_ERRSRC_UNKNOWN;
199e5dcf7beSRobert Johnston 			if (last_source != -1 && last_source != source)
200e5dcf7beSRobert Johnston 				source_diff = B_TRUE;
201e5dcf7beSRobert Johnston 		}
202e5dcf7beSRobert Johnston 		if (source_diff)
203e5dcf7beSRobert Johnston 			source = TOPO_SENSOR_ERRSRC_UNKNOWN;
204e5dcf7beSRobert Johnston 	}
205e5dcf7beSRobert Johnston 
206e5dcf7beSRobert Johnston 	/*
207e5dcf7beSRobert Johnston 	 * See if we know about this fru.
208e5dcf7beSRobert Johnston 	 */
209e5dcf7beSRobert Johnston 	for (current = &stp->st_faults; *current != NULL;
210e5dcf7beSRobert Johnston 	    current = &(*current)->sf_next) {
211e5dcf7beSRobert Johnston 		if (topo_fmri_strcmp(thp, fmri,
212e5dcf7beSRobert Johnston 		    (*current)->sf_fru))
213e5dcf7beSRobert Johnston 			break;
214e5dcf7beSRobert Johnston 	}
215e5dcf7beSRobert Johnston 
216e5dcf7beSRobert Johnston 	sfp = *current;
217e5dcf7beSRobert Johnston 	if (sfp == NULL) {
218e5dcf7beSRobert Johnston 		/*
219e5dcf7beSRobert Johnston 		 * We add this FRU to our list under two circumstances:
220e5dcf7beSRobert Johnston 		 *
221ea30102cSRob Johnston 		 *	1. This FRU is faulted and needs to be remembered to
222e5dcf7beSRobert Johnston 		 *	   avoid duplicate ereports.
223e5dcf7beSRobert Johnston 		 *
224ea30102cSRob Johnston 		 *	2. This is the initial pass, and we want to repair the
225e5dcf7beSRobert Johnston 		 *	   FRU in case it was repaired while we were offline.
226e5dcf7beSRobert Johnston 		 */
227e5dcf7beSRobert Johnston 		if (stp->st_first || faulted) {
228e5dcf7beSRobert Johnston 			sfp = fmd_hdl_zalloc(hdl, sizeof (sensor_fault_t),
229e5dcf7beSRobert Johnston 			    FMD_SLEEP);
230e5dcf7beSRobert Johnston 			sfp->sf_fru = fmd_hdl_strdup(hdl, fmri, FMD_SLEEP);
231e5dcf7beSRobert Johnston 			sfp->sf_next = stp->st_faults;
232e5dcf7beSRobert Johnston 			stp->st_faults = sfp;
233e5dcf7beSRobert Johnston 		} else {
234e5dcf7beSRobert Johnston 			goto out;
235e5dcf7beSRobert Johnston 		}
236e5dcf7beSRobert Johnston 	}
237e5dcf7beSRobert Johnston 
2386c258465SRobert Johnston 	if (faulted)
2396c258465SRobert Johnston 		sfp->sf_num_fails++;
2406c258465SRobert Johnston 
241e5dcf7beSRobert Johnston 	if (nvl == NULL)
242e5dcf7beSRobert Johnston 		sfp->sf_unknown = B_TRUE;
243e5dcf7beSRobert Johnston 
244e5dcf7beSRobert Johnston 	if (faulted) {
245e5dcf7beSRobert Johnston 		/*
246e5dcf7beSRobert Johnston 		 * Construct and post the ereport.
247e5dcf7beSRobert Johnston 		 *
248e5dcf7beSRobert Johnston 		 * XXFM we only post one ereport per fru.  It should be possible
249e5dcf7beSRobert Johnston 		 * to uniquely identify faulty resources instead and post one
250e5dcf7beSRobert Johnston 		 * per resource, even if they share the same FRU.
251e5dcf7beSRobert Johnston 		 */
2526c258465SRobert Johnston 		if (!sfp->sf_last_faulted &&
2536c258465SRobert Johnston 		    (sfp->sf_num_fails > stp->st_tolerance)) {
254e5dcf7beSRobert Johnston 			ena = fmd_event_ena_create(hdl);
255e5dcf7beSRobert Johnston 			event = fmd_nvl_alloc(hdl, FMD_SLEEP);
256e5dcf7beSRobert Johnston 
257e5dcf7beSRobert Johnston 			(void) nvlist_add_string(event, "type", name);
258e5dcf7beSRobert Johnston 			(void) nvlist_add_boolean_value(event, "nonrecov",
259e5dcf7beSRobert Johnston 			    nonrecov);
260e5dcf7beSRobert Johnston 			(void) nvlist_add_boolean_value(event, "predictive",
261e5dcf7beSRobert Johnston 			    predictive);
262e5dcf7beSRobert Johnston 			(void) nvlist_add_uint32(event, "source",
263e5dcf7beSRobert Johnston 			    (uint32_t)source);
264e5dcf7beSRobert Johnston 			(void) nvlist_add_nvlist(event, "details", nvl);
265e5dcf7beSRobert Johnston 			(void) nvlist_add_string(event, FM_CLASS,
266e5dcf7beSRobert Johnston 			    ST_EREPORT_CLASS);
267e5dcf7beSRobert Johnston 			(void) nvlist_add_uint8(event, FM_VERSION,
268e5dcf7beSRobert Johnston 			    FM_EREPORT_VERSION);
269e5dcf7beSRobert Johnston 			(void) nvlist_add_uint64(event, FM_EREPORT_ENA, ena);
270e5dcf7beSRobert Johnston 			(void) nvlist_add_nvlist(event, FM_EREPORT_DETECTOR,
271e5dcf7beSRobert Johnston 			    rsrc);
272ea30102cSRob Johnston 			(void) nvlist_add_boolean_value(event, "__injected",
273ea30102cSRob Johnston 			    injected);
274e5dcf7beSRobert Johnston 			fmd_xprt_post(hdl, stp->st_xprt, event, 0);
275e5dcf7beSRobert Johnston 			fmd_hdl_debug(hdl, "posted ereport: %s",
276e5dcf7beSRobert Johnston 			    ST_EREPORT_CLASS);
277e5dcf7beSRobert Johnston 		}
278e5dcf7beSRobert Johnston 
279e5dcf7beSRobert Johnston 		sfp->sf_faulted = B_TRUE;
280e5dcf7beSRobert Johnston 	}
281e5dcf7beSRobert Johnston 
282e5dcf7beSRobert Johnston out:
283e5dcf7beSRobert Johnston 	topo_hdl_strfree(thp, fmri);
284e5dcf7beSRobert Johnston 	nvlist_free(rsrc);
285e5dcf7beSRobert Johnston 	nvlist_free(nvl);
286e5dcf7beSRobert Johnston 	return (0);
287e5dcf7beSRobert Johnston }
288e5dcf7beSRobert Johnston 
289f6e214c7SGavin Maltby int st_timeout_verbose = 0;
290f6e214c7SGavin Maltby 
291e5dcf7beSRobert Johnston /*ARGSUSED*/
292e5dcf7beSRobert Johnston static void
st_timeout(fmd_hdl_t * hdl,id_t id,void * data)293e5dcf7beSRobert Johnston st_timeout(fmd_hdl_t *hdl, id_t id, void *data)
294e5dcf7beSRobert Johnston {
295e5dcf7beSRobert Johnston 	sensor_transport_t *stp;
296e5dcf7beSRobert Johnston 	sensor_fault_t *sfp, **current;
297e5dcf7beSRobert Johnston 	topo_hdl_t *thp;
298e5dcf7beSRobert Johnston 	topo_walk_t *twp;
299e5dcf7beSRobert Johnston 	int err;
300e5dcf7beSRobert Johnston 
301f6e214c7SGavin Maltby 	if (st_timeout_verbose)
302f6e214c7SGavin Maltby 		fmd_hdl_debug(hdl, "timeout: checking topology");
303e5dcf7beSRobert Johnston 
304e5dcf7beSRobert Johnston 	stp = fmd_hdl_getspecific(hdl);
305e5dcf7beSRobert Johnston 	thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
306e5dcf7beSRobert Johnston 
307e5dcf7beSRobert Johnston 	if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, st_check_component,
308e5dcf7beSRobert Johnston 	    stp, &err)) == NULL) {
309e5dcf7beSRobert Johnston 		fmd_hdl_topo_rele(hdl, thp);
310e5dcf7beSRobert Johnston 		fmd_hdl_error(hdl, "failed to walk topology: %s\n",
311e5dcf7beSRobert Johnston 		    topo_strerror(err));
312e5dcf7beSRobert Johnston 		st_stats.st_topo_errs.fmds_value.ui64++;
313e5dcf7beSRobert Johnston 		return;
314e5dcf7beSRobert Johnston 	}
315e5dcf7beSRobert Johnston 
316f6e214c7SGavin Maltby 	if (st_check_component_complaints)
317f6e214c7SGavin Maltby 		have_complained++;
318f6e214c7SGavin Maltby 
319e5dcf7beSRobert Johnston 	/*
320e5dcf7beSRobert Johnston 	 * Initialize values in our internal FRU list for this iteration of
321e5dcf7beSRobert Johnston 	 * sensor reads.  Keep track of whether the FRU was faulted in the
322e5dcf7beSRobert Johnston 	 * previous pass so we don't send multiple ereports for the same
323e5dcf7beSRobert Johnston 	 * problem.
324e5dcf7beSRobert Johnston 	 */
325e5dcf7beSRobert Johnston 	for (sfp = stp->st_faults; sfp != NULL; sfp = sfp->sf_next) {
326e5dcf7beSRobert Johnston 		sfp->sf_unknown = B_FALSE;
3276c258465SRobert Johnston 		if (sfp->sf_num_fails > stp->st_tolerance)
3286c258465SRobert Johnston 			sfp->sf_last_faulted = sfp->sf_faulted;
329e5dcf7beSRobert Johnston 		sfp->sf_faulted = B_FALSE;
330e5dcf7beSRobert Johnston 	}
331e5dcf7beSRobert Johnston 
332e5dcf7beSRobert Johnston 	if (topo_walk_step(twp, TOPO_WALK_CHILD) == TOPO_WALK_ERR) {
333e5dcf7beSRobert Johnston 		topo_walk_fini(twp);
334e5dcf7beSRobert Johnston 		fmd_hdl_topo_rele(hdl, thp);
335e5dcf7beSRobert Johnston 		fmd_hdl_error(hdl, "failed to walk topology\n");
336e5dcf7beSRobert Johnston 		st_stats.st_topo_errs.fmds_value.ui64++;
337e5dcf7beSRobert Johnston 		return;
338e5dcf7beSRobert Johnston 	}
339e5dcf7beSRobert Johnston 
340e5dcf7beSRobert Johnston 	/*
341e5dcf7beSRobert Johnston 	 * Remove any faults that weren't seen in the last pass.
342e5dcf7beSRobert Johnston 	 */
343e5dcf7beSRobert Johnston 	for (current = &stp->st_faults; *current != NULL; ) {
344e5dcf7beSRobert Johnston 		sfp = *current;
345e5dcf7beSRobert Johnston 		if (!sfp->sf_faulted && !sfp->sf_unknown) {
346e5dcf7beSRobert Johnston 			fmd_hdl_debug(hdl, "repairing %s", sfp->sf_fru);
347e5dcf7beSRobert Johnston 			fmd_repair_fru(hdl, sfp->sf_fru);
348e5dcf7beSRobert Johnston 			st_stats.st_repairs.fmds_value.ui64++;
349e5dcf7beSRobert Johnston 			*current = sfp->sf_next;
350e5dcf7beSRobert Johnston 			fmd_hdl_strfree(hdl, sfp->sf_fru);
351e5dcf7beSRobert Johnston 			fmd_hdl_free(hdl, sfp, sizeof (sensor_fault_t));
352e5dcf7beSRobert Johnston 		} else {
353e5dcf7beSRobert Johnston 			current = &sfp->sf_next;
354e5dcf7beSRobert Johnston 		}
355e5dcf7beSRobert Johnston 	}
356e5dcf7beSRobert Johnston 
357e5dcf7beSRobert Johnston 	stp->st_first = B_FALSE;
358e5dcf7beSRobert Johnston 	topo_walk_fini(twp);
359e5dcf7beSRobert Johnston 	fmd_hdl_topo_rele(hdl, thp);
360e5dcf7beSRobert Johnston 
361e5dcf7beSRobert Johnston 	stp->st_timer = fmd_timer_install(hdl, NULL, NULL, stp->st_interval);
362e5dcf7beSRobert Johnston }
363e5dcf7beSRobert Johnston 
364ea30102cSRob Johnston /*
365ea30102cSRob Johnston  * Parse the value of the spoof-sensor-state module property and store the
366ea30102cSRob Johnston  * result in an nvlist of nvlists.  The format of the value is 3-tuple,
367ea30102cSRob Johnston  * delimited by colons, as follows:
368ea30102cSRob Johnston  *
369ea30102cSRob Johnston  * FMRIPATTERN:SENSORNAME:SENSORSTATE;...
370ea30102cSRob Johnston  *
371ea30102cSRob Johnston  * where FMRIPATTERN can be a string with wildcards that matches the FMRI
372ea30102cSRob Johnston  * of a node associated with the target sensor facility.
373ea30102cSRob Johnston  *
374ea30102cSRob Johnston  * where SENSORNAME is the node name of the target sensor facility
375ea30102cSRob Johnston  *
376ea30102cSRob Johnston  * where SENSORSTATE is the desired sensor state value to spoof.
377ea30102cSRob Johnston  *
378ea30102cSRob Johnston  * Multiple tuples can be specifed, delimited by semicolons.
379ea30102cSRob Johnston  *
380ea30102cSRob Johnston  * If any errors are encountered while parsing the value, all parsing is
381ea30102cSRob Johnston  * ceased and an ereport will be generated indicating a failure to parse
382ea30102cSRob Johnston  * the value.
383ea30102cSRob Johnston  */
384ea30102cSRob Johnston /*ARGSUSED*/
385ea30102cSRob Johnston static int
parse_spoof_param(fmd_hdl_t * hdl,char * param,sensor_transport_t * stp)386ea30102cSRob Johnston parse_spoof_param(fmd_hdl_t *hdl, char *param, sensor_transport_t *stp)
387ea30102cSRob Johnston {
388ea30102cSRob Johnston 	char *sensor, *last_sensor, *field, *last_field;
389ea30102cSRob Johnston 	nvlist_t *spoof;
390ea30102cSRob Johnston 
391*940f2f58SToomas Soome 	if (nvlist_alloc(&stp->st_spoofs, NV_UNIQUE_NAME, 0) != 0) {
392ea30102cSRob Johnston 		return (-1);
393ea30102cSRob Johnston 	}
394ea30102cSRob Johnston 
395ea30102cSRob Johnston 	sensor = strtok_r(param, ";", &last_sensor);
396ea30102cSRob Johnston 	while (sensor != NULL) {
397ea30102cSRob Johnston 		if (nvlist_alloc(&spoof, NV_UNIQUE_NAME, 0) != 0)
398ea30102cSRob Johnston 			goto err;
399ea30102cSRob Johnston 
400ea30102cSRob Johnston 		if ((field = strtok_r(sensor, ":", &last_field)) == NULL ||
401ea30102cSRob Johnston 		    nvlist_add_string(spoof, ST_SPOOF_FMRI, field) != 0)
402ea30102cSRob Johnston 			goto err;
403ea30102cSRob Johnston 
404ea30102cSRob Johnston 		if ((field = strtok_r(NULL, ":", &last_field)) == NULL ||
405ea30102cSRob Johnston 		    nvlist_add_string(spoof, ST_SPOOF_SENSOR, field) != 0)
406ea30102cSRob Johnston 			goto err;
407ea30102cSRob Johnston 
408ea30102cSRob Johnston 		if ((field = strtok_r(NULL, ":", &last_field)) == NULL ||
409ea30102cSRob Johnston 		    nvlist_add_uint32(spoof, ST_SPOOF_STATE,
410ea30102cSRob Johnston 		    strtol(field, NULL, 0)) != 0)
411ea30102cSRob Johnston 			goto err;
412ea30102cSRob Johnston 
413ea30102cSRob Johnston 		if (nvlist_add_nvlist(stp->st_spoofs, sensor, spoof) != 0)
414ea30102cSRob Johnston 			goto err;
415ea30102cSRob Johnston 
416ea30102cSRob Johnston 		spoof = NULL;
417ea30102cSRob Johnston 		sensor = strtok_r(NULL, ";", &last_sensor);
418ea30102cSRob Johnston 	}
419ea30102cSRob Johnston 
420ea30102cSRob Johnston 	return (0);
421ea30102cSRob Johnston err:
422ea30102cSRob Johnston 	nvlist_free(spoof);
423ea30102cSRob Johnston 	nvlist_free(stp->st_spoofs);
424ea30102cSRob Johnston 	stp->st_spoofs = NULL;
425ea30102cSRob Johnston 	return (-1);
426ea30102cSRob Johnston }
427ea30102cSRob Johnston 
428e5dcf7beSRobert Johnston static const fmd_prop_t fmd_props[] = {
429e5dcf7beSRobert Johnston 	{ "interval", FMD_TYPE_TIME, "1min" },
4306c258465SRobert Johnston 	{ "tolerance", FMD_TYPE_UINT32, "1" },
431ea30102cSRob Johnston 	{ "spoof_sensor_state", FMD_TYPE_STRING, NULL },
432e5dcf7beSRobert Johnston 	{ NULL, 0, NULL }
433e5dcf7beSRobert Johnston };
434e5dcf7beSRobert Johnston 
435e5dcf7beSRobert Johnston static const fmd_hdl_ops_t fmd_ops = {
436e5dcf7beSRobert Johnston 	NULL,			/* fmdo_recv */
437e5dcf7beSRobert Johnston 	st_timeout,		/* fmdo_timeout */
438ea30102cSRob Johnston 	NULL,			/* fmdo_close */
439e5dcf7beSRobert Johnston 	NULL,			/* fmdo_stats */
440e5dcf7beSRobert Johnston 	NULL,			/* fmdo_gc */
441e5dcf7beSRobert Johnston 	NULL,			/* fmdo_send */
442e5dcf7beSRobert Johnston 	NULL			/* fmdo_topo */
443e5dcf7beSRobert Johnston };
444e5dcf7beSRobert Johnston 
445e5dcf7beSRobert Johnston static const fmd_hdl_info_t fmd_info = {
4466c258465SRobert Johnston 	"Sensor Transport Agent", "1.1", &fmd_ops, fmd_props
447e5dcf7beSRobert Johnston };
448e5dcf7beSRobert Johnston 
449e5dcf7beSRobert Johnston void
_fmd_init(fmd_hdl_t * hdl)450e5dcf7beSRobert Johnston _fmd_init(fmd_hdl_t *hdl)
451e5dcf7beSRobert Johnston {
452e5dcf7beSRobert Johnston 	sensor_transport_t *stp;
453e5dcf7beSRobert Johnston 	char buf[SYS_NMLN];
454e5dcf7beSRobert Johnston 
455e5dcf7beSRobert Johnston 	/*
456e5dcf7beSRobert Johnston 	 * The sensor-transport module is currently only supported on x86
457e5dcf7beSRobert Johnston 	 * platforms.  So to avoid unnecessarily wasting cpu cycles on sparc
458e5dcf7beSRobert Johnston 	 * walking the hc scheme tree every 60 seconds, we'll bail out before
459e5dcf7beSRobert Johnston 	 * registering the handle.
460e5dcf7beSRobert Johnston 	 */
461e5dcf7beSRobert Johnston 	if ((sysinfo(SI_ARCHITECTURE, buf, sizeof (buf)) == -1) ||
462e5dcf7beSRobert Johnston 	    (strcmp(buf, "i386") != 0))
463e5dcf7beSRobert Johnston 		return;
464e5dcf7beSRobert Johnston 
465e5dcf7beSRobert Johnston 	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
466e5dcf7beSRobert Johnston 		return;
467e5dcf7beSRobert Johnston 
468e5dcf7beSRobert Johnston 	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
469e5dcf7beSRobert Johnston 	    sizeof (st_stats) / sizeof (fmd_stat_t),
470e5dcf7beSRobert Johnston 	    (fmd_stat_t *)&st_stats);
471e5dcf7beSRobert Johnston 
472e5dcf7beSRobert Johnston 	stp = fmd_hdl_zalloc(hdl, sizeof (sensor_transport_t), FMD_SLEEP);
473e5dcf7beSRobert Johnston 	stp->st_interval = fmd_prop_get_int64(hdl, "interval");
4746c258465SRobert Johnston 	stp->st_tolerance = fmd_prop_get_int32(hdl, "tolerance");
475ea30102cSRob Johnston 	spoof_prop = fmd_prop_get_string(hdl, "spoof_sensor_state");
476ea30102cSRob Johnston 
477ea30102cSRob Johnston 	if (spoof_prop != NULL && parse_spoof_param(hdl, spoof_prop, stp) != 0)
478ea30102cSRob Johnston 		fmd_hdl_error(hdl, "Error parsing config file");
479e5dcf7beSRobert Johnston 
480e5dcf7beSRobert Johnston 	fmd_hdl_setspecific(hdl, stp);
481e5dcf7beSRobert Johnston 
482e5dcf7beSRobert Johnston 	stp->st_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL);
483e5dcf7beSRobert Johnston 	stp->st_hdl = hdl;
484e5dcf7beSRobert Johnston 	stp->st_first = B_TRUE;
485e5dcf7beSRobert Johnston 
486e5dcf7beSRobert Johnston 	/* kick off the first asynchronous discovery */
487e5dcf7beSRobert Johnston 	stp->st_timer = fmd_timer_install(hdl, NULL, NULL, 0);
488e5dcf7beSRobert Johnston }
489e5dcf7beSRobert Johnston 
490e5dcf7beSRobert Johnston void
_fmd_fini(fmd_hdl_t * hdl)491e5dcf7beSRobert Johnston _fmd_fini(fmd_hdl_t *hdl)
492e5dcf7beSRobert Johnston {
493e5dcf7beSRobert Johnston 	sensor_transport_t *stp;
494e5dcf7beSRobert Johnston 	sensor_fault_t *sfp;
495e5dcf7beSRobert Johnston 
496e5dcf7beSRobert Johnston 	stp = fmd_hdl_getspecific(hdl);
497e5dcf7beSRobert Johnston 	if (stp != NULL) {
498e5dcf7beSRobert Johnston 		fmd_xprt_close(hdl, stp->st_xprt);
499e5dcf7beSRobert Johnston 
500e5dcf7beSRobert Johnston 		while ((sfp = stp->st_faults) != NULL) {
501e5dcf7beSRobert Johnston 			stp->st_faults = sfp->sf_next;
502e5dcf7beSRobert Johnston 
503e5dcf7beSRobert Johnston 			fmd_hdl_strfree(hdl, sfp->sf_fru);
504e5dcf7beSRobert Johnston 			fmd_hdl_free(hdl, sfp, sizeof (sensor_fault_t));
505e5dcf7beSRobert Johnston 		}
506ea30102cSRob Johnston 		nvlist_free(stp->st_spoofs);
507e5dcf7beSRobert Johnston 		fmd_hdl_free(hdl, stp, sizeof (sensor_transport_t));
508e5dcf7beSRobert Johnston 	}
509ea30102cSRob Johnston 	fmd_prop_free_string(hdl, spoof_prop);
510e5dcf7beSRobert Johnston }
511