xref: /titanic_41/usr/src/cmd/picl/plugins/sun4u/envmon/piclenvmon.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This plugin creates PICL nodes and properties for objects handled through
31  * the enhanced LOMV system-processor interface.
32  *
33  * All the nodes which may be accessible through the system-processor are
34  * included below the service-processor node  in the /platform tree.
35  * This plugin interrogates the system-processor to determine which of
36  * those nodes are actually available. Properties are added to such nodes and
37  * in the case of volatile properties like temperature, a call-back function
38  * is established for on-demand access to the current value.
39  * LEDs for which the system-processor provides write access are associated
40  * with read/write volatile properties.
41  *
42  * NOTE:
43  * Depends on PICL devtree plugin.
44  */
45 
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50 #include <alloca.h>
51 #include <syslog.h>
52 #include <string.h>
53 #include <libintl.h>
54 #include <picl.h>
55 #include <picltree.h>
56 #include <libnvpair.h>
57 #include <errno.h>
58 #include <limits.h>
59 #include <ctype.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <sys/obpdefs.h>
63 #include <sys/envmon.h>
64 #include <sys/systeminfo.h>
65 #include <dirent.h>
66 #include <time.h>
67 #include <picldefs.h>
68 #include <picld_pluginutil.h>
69 #include <libdevinfo.h>
70 #include "piclenvmon.h"
71 
72 static void	piclenvmon_register(void);
73 static void	piclenvmon_init(void);
74 static void	piclenvmon_fini(void);
75 static node_el_t	*create_node_el(picl_nodehdl_t nodeh);
76 static void	delete_node_el(node_el_t *pel);
77 static node_list_t	*create_node_list();
78 static void	delete_node_list(node_list_t *pnl);
79 static void	add_node_to_list(picl_nodehdl_t nodeh, node_list_t *listp);
80 static void	get_node_list_by_class(picl_nodehdl_t nodeh,
81     const char *classname, node_list_t *listp);
82 static int	get_envmon_limits(int envmon_fd, envmon_sysinfo_t *limits_p);
83 static void	create_arrays();
84 static int	get_envmon_node(picl_nodehdl_t *envmoninfh);
85 static char	*create_envmon_pathname(picl_nodehdl_t envmoninfh);
86 static int	get_child_by_name(picl_nodehdl_t nodeh, const char *name,
87     picl_nodehdl_t *childh);
88 static int	add_regular_prop(picl_nodehdl_t nodeh, const char *name,
89     int type, int access, int size, const void *valbuf, picl_prophdl_t *prophp);
90 static int	add_volatile_prop(picl_nodehdl_t nodeh, const char *name,
91     int type, int access, int size, ptree_vol_rdfunc_t rdfunc,
92     ptree_vol_wrfunc_t wrfunc, picl_prophdl_t *prophp);
93 static int	get_sensor_data(int envmon_fd, envmon_handle_t *id, int cmd,
94     envmon_thresholds_t *lows, envmon_thresholds_t *highs, int16_t *value);
95 static int	get_indicator_data(int envmon_fd, envmon_handle_t *id, int cmd,
96     int16_t *condition);
97 static int	get_fan_data(int envmon_fd, envmon_handle_t *id, int cmd,
98     envmon_thresholds_t *lows, uint16_t *speed, char *units);
99 static int	get_led_data(int envmon_fd, envmon_handle_t *id, int cmd,
100     int8_t *state, int8_t *colour);
101 static int	get_keyswitch_data(int envmon_fd, envmon_handle_t *id, int cmd,
102     envmon_keysw_pos_t *key_state);
103 static void	convert_node_name(char *ptr);
104 static void	convert_label_name(char *ptr);
105 static int	add_value_prop(picl_nodehdl_t node_hdl, const char *prop_name,
106     int fru_type, int16_t value);
107 static int	find_picl_handle(picl_prophdl_t proph);
108 static int	lookup_led_status(int8_t state, const char **string);
109 static int	lookup_key_posn(envmon_keysw_pos_t pos, const char **string);
110 static int	get_config_file(char *filename);
111 static int	read_vol_data(ptree_rarg_t *r_arg, void *buf);
112 static int	write_led_data(ptree_warg_t *w_arg, const void *buf);
113 static int	add_env_nodes(int envmon_fd, uint8_t fru_type,
114     picl_nodehdl_t envmonh);
115 static void	fixstate(uint8_t state, const char *string, int *max_len);
116 static void	fixkeyposn(envmon_keysw_pos_t keyposn, const char *string,
117     int *max_len);
118 static void	setup_strings();
119 static void	free_vol_prop(picl_prophdl_t proph);
120 static void	envmon_evhandler(const char *ename, const void *earg,
121     size_t size, void *cookie);
122 
123 #pragma	init(piclenvmon_register)
124 
125 static picld_plugin_reg_t  my_reg_info = {
126 	PICLD_PLUGIN_VERSION_1,
127 	PICLD_PLUGIN_NON_CRITICAL,
128 	"SUNW_piclenvmon",
129 	piclenvmon_init,
130 	piclenvmon_fini
131 };
132 
133 static	const char str_On[] = "on";
134 static	const char str_Off[] = "off";
135 static	const char str_SC[] = "SC";
136 static	char *envmon_device_name = NULL;
137 static	envmon_sysinfo_t	env_limits;
138 static	handle_array_t	handle_arr;
139 static	struct {
140 	int		size;
141 	char		*str_colour;
142 } colour_lkup[1 + ENVMON_LED_CLR_RED];
143 
144 static	struct {
145 	int8_t		state;
146 	char		*str_ledstate;
147 } ledstate_lkup[] = {
148 	{	ENVMON_LED_OFF			},
149 	{	ENVMON_LED_ON			},
150 	{	ENVMON_LED_BLINKING		},
151 	{	ENVMON_LED_FLASHING		}
152 };
153 
154 static	struct {
155 	envmon_keysw_pos_t	pos;
156 	char			*str_keyposn;
157 } keyposn_lkup[] = {
158 	{	ENVMON_KEYSW_POS_UNKNOWN	},
159 	{	ENVMON_KEYSW_POS_NORMAL		},
160 	{	ENVMON_KEYSW_POS_DIAG		},
161 	{	ENVMON_KEYSW_POS_LOCKED		},
162 	{	ENVMON_KEYSW_POS_OFF		}
163 };
164 
165 /*
166  * fru-type to ioctl cmd lookup
167  */
168 int	fru_to_cmd[] = {
169 	ENVMONIOCVOLTSENSOR,
170 	ENVMONIOCVOLTIND,
171 	ENVMONIOCAMPSENSOR,
172 	ENVMONIOCAMPIND,
173 	ENVMONIOCTEMPSENSOR,
174 	ENVMONIOCTEMPIND,
175 	ENVMONIOCFAN,
176 	ENVMONIOCFANIND,
177 	ENVMONIOCGETLED,
178 	ENVMONIOCGETKEYSW
179 };
180 
181 /*
182  * fru-type to PICL CLASS
183  */
184 const char *fru_to_class[] = {
185 	PICL_CLASS_VOLTAGE_SENSOR,
186 	PICL_CLASS_VOLTAGE_INDICATOR,
187 	PICL_CLASS_CURRENT_SENSOR,
188 	PICL_CLASS_CURRENT_INDICATOR,
189 	PICL_CLASS_TEMPERATURE_SENSOR,
190 	PICL_CLASS_TEMPERATURE_INDICATOR,
191 	PICL_CLASS_FAN,
192 	PICL_CLASS_FAN,
193 	PICL_CLASS_LED,
194 	PICL_CLASS_KEYSWITCH
195 };
196 
197 /*
198  * fru-type to PICL PROPERTY for volatile data
199  */
200 const char *fru_to_prop[] = {
201 	PICL_PROP_VOLTAGE,
202 	PICL_PROP_CONDITION,
203 	PICL_PROP_CURRENT,
204 	PICL_PROP_CONDITION,
205 	PICL_PROP_TEMPERATURE,
206 	PICL_PROP_CONDITION,
207 	PICL_PROP_FAN_SPEED,
208 	PICL_PROP_FAN_SPEED_UNIT,
209 	PICL_PROP_STATE,
210 	PICL_PROP_STATE
211 };
212 
213 /*
214  * fru-type to PICL PTYPE
215  */
216 int	fru_to_ptype[] = {
217 	PICL_PTYPE_FLOAT,
218 	PICL_PTYPE_CHARSTRING,
219 	PICL_PTYPE_FLOAT,
220 	PICL_PTYPE_CHARSTRING,
221 	PICL_PTYPE_INT,
222 	PICL_PTYPE_CHARSTRING,
223 	PICL_PTYPE_UNSIGNED_INT,
224 	PICL_PTYPE_CHARSTRING,
225 	PICL_PTYPE_CHARSTRING,
226 	PICL_PTYPE_CHARSTRING
227 };
228 
229 /*
230  * condition strings
231  */
232 static char *cond_okay;
233 static char *cond_failed;
234 
235 /*
236  * fru-type to size of volatile property
237  * the -1's are replaced by the max size of a condition string
238  */
239 int	fru_to_size[] = {
240 	4, -1, 4, -1, 2, -1, 2, -1, -1, -1
241 };
242 
243 static node_el_t *
244 create_node_el(picl_nodehdl_t nodeh)
245 {
246 	node_el_t *ptr = malloc(sizeof (node_el_t));
247 
248 	if (ptr != NULL) {
249 		ptr->nodeh = nodeh;
250 		ptr->next = NULL;
251 	}
252 
253 	return (ptr);
254 }
255 
256 static void
257 delete_node_el(node_el_t *pel)
258 {
259 	free(pel);
260 }
261 
262 static node_list_t *
263 create_node_list()
264 {
265 	node_list_t *ptr = malloc(sizeof (node_list_t));
266 
267 	if (ptr != NULL) {
268 		ptr->head = NULL;
269 		ptr->tail = NULL;
270 	}
271 
272 	return (ptr);
273 }
274 
275 static void
276 delete_node_list(node_list_t *pnl)
277 {
278 	node_el_t	*pel;
279 
280 	if (pnl == NULL)
281 		return;
282 
283 	while ((pel = pnl->head) != NULL) {
284 		pnl->head = pel->next;
285 		delete_node_el(pel);
286 	}
287 
288 	/*
289 	 * normally pnl->tail would be to NULL next,
290 	 * but as it is about to be freed, this step can be skipped.
291 	 */
292 	free(pnl);
293 }
294 
295 /*
296  * Get a linking element and add handle to end of chain
297  */
298 static void
299 add_node_to_list(picl_nodehdl_t nodeh, node_list_t *listp)
300 {
301 	node_el_t	*pel = create_node_el(nodeh);
302 
303 	if (pel != NULL) {
304 		if (listp->tail == NULL)
305 			listp->head = pel;
306 		else
307 			listp->tail->next = pel;
308 
309 		listp->tail = pel;
310 	}
311 }
312 
313 /*
314  * Get a list of nodes of the specified classname under nodeh.
315  * Once a node of the specified class is found, its children are not
316  * searched.
317  */
318 static void
319 get_node_list_by_class(picl_nodehdl_t nodeh, const char *classname,
320     node_list_t *listp)
321 {
322 	int		err;
323 	char		clname[PICL_CLASSNAMELEN_MAX+1];
324 	picl_nodehdl_t	chdh;
325 
326 	/*
327 	 * go through the children
328 	 */
329 	err = ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD, &chdh,
330 	    sizeof (picl_nodehdl_t));
331 
332 	while (err == PICL_SUCCESS) {
333 		err = ptree_get_propval_by_name(chdh, PICL_PROP_CLASSNAME,
334 		    clname, strlen(classname) + 1);
335 
336 		if ((err == PICL_SUCCESS) && (strcmp(clname, classname) == 0))
337 			add_node_to_list(chdh, listp);
338 		else
339 			get_node_list_by_class(chdh, classname, listp);
340 
341 		err = ptree_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh,
342 		    sizeof (picl_nodehdl_t));
343 	}
344 }
345 
346 static int
347 get_envmon_limits(int envmon_fd, envmon_sysinfo_t *limits_p)
348 {
349 	return (ioctl(envmon_fd, ENVMONIOCSYSINFO, limits_p));
350 }
351 
352 static int
353 re_create_arrays(int envmon_fd)
354 {
355 	envmon_sysinfo_t	new_limits;
356 	int			res;
357 	int			maxnum;
358 	uchar_t			*fru_types;
359 	envmon_handle_t		*envhandles;
360 	picl_prophdl_t		*piclprhdls;
361 
362 	res = get_envmon_limits(envmon_fd, &new_limits);
363 	if (res != 0)
364 		return (res);
365 
366 	maxnum = new_limits.maxVoltSens + new_limits.maxVoltInd +
367 	    new_limits.maxAmpSens + new_limits.maxAmpInd +
368 	    new_limits.maxTempSens + new_limits.maxTempInd +
369 	    new_limits.maxFanSens + new_limits.maxFanInd +
370 	    new_limits.maxLED + N_KEY_SWITCHES;
371 
372 	if (maxnum != handle_arr.maxnum) {
373 		/*
374 		 * space requirements have changed
375 		 */
376 		fru_types = calloc(maxnum, sizeof (uchar_t));
377 		envhandles = calloc(maxnum, sizeof (envmon_handle_t));
378 		piclprhdls = calloc(maxnum, sizeof (picl_prophdl_t));
379 		if ((fru_types == NULL) || (envhandles == NULL) ||
380 		    (piclprhdls == NULL)) {
381 			free(fru_types);
382 			free(envhandles);
383 			free(piclprhdls);
384 			return (-1);
385 		}
386 		free(handle_arr.fru_types);
387 		handle_arr.fru_types = fru_types;
388 		free(handle_arr.envhandles);
389 		handle_arr.envhandles = envhandles;
390 		free(handle_arr.piclprhdls);
391 		handle_arr.piclprhdls = piclprhdls;
392 	} else {
393 		(void) memset(handle_arr.fru_types, 0,
394 		    maxnum * sizeof (uchar_t));
395 		(void) memset(handle_arr.envhandles, 0,
396 		    maxnum * sizeof (envmon_handle_t));
397 		(void) memset(handle_arr.piclprhdls, 0,
398 		    maxnum * sizeof (picl_prophdl_t));
399 	}
400 
401 	handle_arr.num = 0;
402 	handle_arr.maxnum = maxnum;
403 	env_limits = new_limits;
404 	return (0);
405 }
406 
407 static void
408 create_arrays()
409 {
410 	int maxnum = env_limits.maxVoltSens + env_limits.maxVoltInd +
411 	    env_limits.maxAmpSens + env_limits.maxAmpInd +
412 	    env_limits.maxTempSens + env_limits.maxTempInd +
413 	    env_limits.maxFanSens + env_limits.maxFanInd +
414 	    env_limits.maxLED + N_KEY_SWITCHES;
415 	handle_arr.maxnum = maxnum;
416 	handle_arr.num = 0;
417 	handle_arr.fru_types = calloc(maxnum, sizeof (uchar_t));
418 	handle_arr.envhandles = calloc(maxnum, sizeof (envmon_handle_t));
419 	handle_arr.piclprhdls = calloc(maxnum, sizeof (picl_prophdl_t));
420 }
421 
422 static int
423 get_envmon_node(picl_nodehdl_t *envmoninfh)
424 {
425 	int			err = PICL_SUCCESS;
426 	node_list_t		*listp;
427 
428 	listp = create_node_list();
429 
430 	if ((err = ptree_get_node_by_path(PICL_NODE_ROOT PICL_NODE_PLATFORM,
431 	    envmoninfh)) != PICL_SUCCESS) {
432 		syslog(LOG_ERR, EM_MISSING_NODE,
433 		    PICL_NODE_ROOT PICL_NODE_PLATFORM);
434 		return (err);	/* no /platform ! */
435 	}
436 
437 	get_node_list_by_class(*envmoninfh, PICL_CLASS_SERVICE_PROCESSOR,
438 	    listp);
439 
440 	if (listp->head == NULL) {
441 		*envmoninfh = 0;
442 		syslog(LOG_ERR, EM_MISSING_NODE, PICL_CLASS_SERVICE_PROCESSOR);
443 		err = PICL_NODENOTFOUND;
444 	} else {
445 		*envmoninfh = listp->head->nodeh;
446 	}
447 
448 	delete_node_list(listp);
449 	return (err);
450 }
451 
452 static char *
453 create_envmon_pathname(picl_nodehdl_t envmoninfh)
454 {
455 	char		*ptr;
456 	char		namebuf[PATH_MAX];
457 	size_t		len;
458 	DIR		*dirp;
459 	struct dirent	*dp;
460 	struct stat	statbuf;
461 
462 	/* prefix devfs-path name with /devices */
463 	(void) strlcpy(namebuf, "/devices", PATH_MAX);
464 
465 	/*
466 	 * append devfs-path property
467 	 */
468 	len = strlen(namebuf);
469 	if (ptree_get_propval_by_name(envmoninfh, PICL_PROP_DEVFS_PATH,
470 	    namebuf + len, sizeof (namebuf) - len) != PICL_SUCCESS) {
471 		syslog(LOG_ERR, EM_SC_NODE_INCOMPLETE);
472 		return (NULL);
473 	}
474 
475 	/* locate final component of name */
476 	ptr = strrchr(namebuf, '/');
477 	if (ptr == NULL)
478 		return (NULL);
479 	*ptr = '\0';		/* terminate at end of directory path */
480 	len = strlen(ptr + 1);	/* length of terminal name */
481 	dirp = opendir(namebuf);
482 	if (dirp == NULL) {
483 		syslog(LOG_ERR, EM_SC_NODE_MISSING);
484 		return (NULL);
485 	}
486 	*ptr++ = '/';		/* restore '/' and advance to final name */
487 
488 	while ((dp = readdir(dirp)) != NULL) {
489 		/*
490 		 * look for a name which starts with the string at *ptr
491 		 */
492 		if (strlen(dp->d_name) < len)
493 			continue;	/* skip short names */
494 		if (strncmp(dp->d_name, ptr, len) == 0) {
495 			/*
496 			 * Got a match, restore full pathname and stat the
497 			 * entry. Reject if not a char device
498 			 */
499 			(void) strlcpy(ptr, dp->d_name,
500 			    sizeof (namebuf) - (ptr - namebuf));
501 			if (stat(namebuf, &statbuf) < 0)
502 				continue;	/* reject if can't stat it */
503 			if (!S_ISCHR(statbuf.st_mode))
504 				continue;	/* not a character device */
505 			/*
506 			 * go with this entry
507 			 */
508 			(void) closedir(dirp);
509 			return (strdup(namebuf));
510 		}
511 	}
512 	syslog(LOG_ERR, EM_SC_NODE_MISSING);
513 	(void) closedir(dirp);
514 	return (NULL);
515 }
516 
517 /*
518  * look for named node as child of supplied handle
519  */
520 static int
521 get_child_by_name(picl_nodehdl_t nodeh, const char *name,
522     picl_nodehdl_t *childh)
523 {
524 	int		err;
525 	char		node_name[ENVMON_MAXNAMELEN];
526 
527 	if (strlen(name) >= ENVMON_MAXNAMELEN)
528 		return (PICL_NODENOTFOUND);
529 	err = ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD, childh,
530 	    sizeof (*childh));
531 	while (err == PICL_SUCCESS) {
532 		err = ptree_get_propval_by_name(*childh, PICL_PROP_NAME,
533 		    node_name, sizeof (node_name));
534 		if ((err == PICL_SUCCESS) &&
535 		    (strncmp(name, node_name, ENVMON_MAXNAMELEN) == 0))
536 			return (PICL_SUCCESS);
537 		err = ptree_get_propval_by_name(*childh, PICL_PROP_PEER,
538 		    childh, sizeof (*childh));
539 	}
540 	return (err);
541 }
542 
543 /*
544  * Create and add the specified regular property
545  */
546 static int
547 add_regular_prop(picl_nodehdl_t nodeh, const char *name, int type, int access,
548     int size, const void *valbuf, picl_prophdl_t *prophp)
549 {
550 	int			err;
551 	ptree_propinfo_t	propinfo;
552 	picl_prophdl_t		proph;
553 
554 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
555 	    type, access, size, (char *)name, NULL, NULL);
556 	if (err != PICL_SUCCESS)
557 		return (err);
558 
559 	err = ptree_create_and_add_prop(nodeh, &propinfo, (void *)valbuf,
560 	    &proph);
561 	if (err == PICL_SUCCESS && prophp)
562 		*prophp = proph;
563 	return (err);
564 }
565 
566 
567 /*
568  * Create and add the specified volatile property
569  */
570 static int
571 add_volatile_prop(picl_nodehdl_t nodeh, const char *name, int type, int access,
572     int size, ptree_vol_rdfunc_t rdfunc, ptree_vol_wrfunc_t wrfunc,
573     picl_prophdl_t *prophp)
574 {
575 	int			err;
576 	ptree_propinfo_t	propinfo;
577 	picl_prophdl_t		proph;
578 
579 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
580 	    type, (access|PICL_VOLATILE), size, (char *)name, rdfunc, wrfunc);
581 	if (err != PICL_SUCCESS)
582 		return (err);
583 
584 	err = ptree_create_and_add_prop(nodeh, &propinfo, NULL, &proph);
585 	if (err == PICL_SUCCESS && prophp)
586 		*prophp = proph;
587 	return (err);
588 }
589 
590 /*
591  * There are 5 different structures used for reading environmental data
592  * from the service-processor. A different function is used for each one.
593  * Some functions cover several ioctls, so the desired ioctl is part of
594  * the interface. In each case the id parameter is read/write, the
595  * returned value being the next id for this fru type.
596  */
597 
598 /*
599  * Function to read sensor data.
600  */
601 static int
602 get_sensor_data(int envmon_fd, envmon_handle_t *id, int cmd,
603     envmon_thresholds_t *lows, envmon_thresholds_t *highs, int16_t *value)
604 {
605 	int		res;
606 	envmon_sensor_t	data;
607 
608 	(void) memset(&data, 0, sizeof (data));
609 	data.id = *id;
610 	res = ioctl(envmon_fd, cmd, &data);
611 	if (res < 0) {
612 		return (PICL_NOTREADABLE);
613 	}
614 
615 	*id = data.next_id;
616 
617 	if ((data.sensor_status & ENVMON_NOT_PRESENT) != 0)
618 		return (PICL_INVALIDHANDLE);
619 
620 	/*
621 	 * it is assumed that threshold data will be available,
622 	 * even though the current sensor value may be inaccessible
623 	 */
624 	if (lows != NULL)
625 		*lows = data.lowthresholds;
626 	if (highs != NULL)
627 		*highs = data.highthresholds;
628 
629 	if ((data.sensor_status & ENVMON_INACCESSIBLE) != 0) {
630 		if (value != NULL)
631 			*value = ENVMON_VAL_UNAVAILABLE;
632 		return (PICL_PROPVALUNAVAILABLE);
633 	}
634 	if (value != NULL)
635 		*value = data.value;
636 	return (PICL_SUCCESS);
637 }
638 
639 /*
640  * Function to read indicator data.
641  */
642 static int
643 get_indicator_data(int envmon_fd, envmon_handle_t *id, int cmd,
644     int16_t *condition)
645 {
646 	int			res;
647 	envmon_indicator_t	data;
648 
649 	data.id = *id;
650 	res = ioctl(envmon_fd, cmd, &data);
651 	if (res < 0)
652 		return (PICL_NOTREADABLE);
653 	*id = data.next_id;
654 	if ((data.sensor_status & ENVMON_NOT_PRESENT) != 0)
655 		return (PICL_INVALIDHANDLE);
656 	if (condition != NULL)
657 		*condition = data.condition;
658 	if ((data.sensor_status & ENVMON_INACCESSIBLE) != 0) {
659 		return (PICL_PROPVALUNAVAILABLE);
660 	}
661 	return (PICL_SUCCESS);
662 }
663 
664 /*
665  * Function to read fan data.
666  */
667 static int
668 get_fan_data(int envmon_fd, envmon_handle_t *id, int cmd,
669     envmon_thresholds_t *lows, uint16_t *speed, char *units)
670 {
671 	int		res;
672 	envmon_fan_t	data;
673 
674 	data.id = *id;
675 	res = ioctl(envmon_fd, cmd, &data);
676 	if (res < 0)
677 		return (PICL_NOTREADABLE);
678 	*id = data.next_id;
679 	if ((data.sensor_status & ENVMON_NOT_PRESENT) != 0)
680 		return (PICL_INVALIDHANDLE);
681 	if (lows != NULL)
682 		*lows = data.lowthresholds;
683 	if (units != NULL)
684 		(void) strlcpy(units, data.units, sizeof (data.units));
685 
686 	if ((data.sensor_status & ENVMON_INACCESSIBLE) != 0) {
687 		if (speed != NULL)
688 			*speed = ENVMON_VAL_UNAVAILABLE;
689 		return (PICL_PROPVALUNAVAILABLE);
690 	}
691 	if (speed != NULL)
692 		*speed = data.speed;
693 	return (PICL_SUCCESS);
694 }
695 
696 /*
697  * Function to read LED data.
698  */
699 static int
700 get_led_data(int envmon_fd, envmon_handle_t *id, int cmd,
701     int8_t *state, int8_t *colour)
702 {
703 	int			res;
704 	envmon_led_info_t	data;
705 
706 	data.id = *id;
707 	res = ioctl(envmon_fd, cmd, &data);
708 	if (res < 0)
709 		return (PICL_NOTREADABLE);
710 	*id = data.next_id;
711 	if ((data.sensor_status & ENVMON_NOT_PRESENT) != 0)
712 		return (PICL_INVALIDHANDLE);
713 	if (colour != NULL)
714 		*colour = data.led_color;
715 	if ((data.sensor_status & ENVMON_INACCESSIBLE) != 0) {
716 		return (PICL_PROPVALUNAVAILABLE);
717 	}
718 	if (state != NULL)
719 		*state = data.led_state;
720 	return (PICL_SUCCESS);
721 }
722 
723 /*
724  * Function to read key-switch position
725  * Returns PICL_INVALIDHANDLE if ioctl not supported (or fails)
726  */
727 static int
728 get_keyswitch_data(int envmon_fd, envmon_handle_t *id, int cmd,
729     envmon_keysw_pos_t *key_state)
730 {
731 	int			res;
732 
733 	if (id->name[0] == '\0') {
734 		(void) strlcpy(id->name, KEYSWITCH_NAME, sizeof (id->name));
735 		return (PICL_INVALIDHANDLE);
736 	} else if (strncmp(id->name, KEYSWITCH_NAME, sizeof (id->name)) != 0) {
737 		id->name[0] = '\0';
738 		return (PICL_INVALIDHANDLE);
739 	} else {
740 		res = ioctl(envmon_fd, cmd, key_state);
741 		id->name[0] = '\0';
742 
743 		if (res < 0)
744 			return (PICL_INVALIDHANDLE);
745 		return (PICL_SUCCESS);
746 	}
747 }
748 
749 /*
750  * change to lower case and convert any spaces into hyphens,
751  * and any dots or colons symbols into underscores
752  */
753 static void
754 convert_node_name(char *ptr)
755 {
756 	char ch;
757 
758 	for (ch = *ptr; ch != '\0'; ch = *++ptr) {
759 		if (isupper(ch)) {
760 			*ptr = tolower(ch);
761 		} else if (isspace(ch)) {
762 			*ptr = '-';
763 		} else if ((ch == '.') || (ch == ':')) {
764 			*ptr = '_';
765 		}
766 	}
767 }
768 
769 /*
770  * strip to the last '.' separator and keep the rest
771  * change ':' to '/' within the last component
772  */
773 static void
774 convert_label_name(char *name)
775 {
776 	const char	*cptr;
777 	char		ch;
778 
779 	cptr = strrchr(name, '.');
780 
781 	if (cptr == NULL)
782 		cptr = name;
783 	else
784 		cptr++;			/* skip the '.' */
785 
786 	do {
787 		ch = *cptr++;
788 
789 		if (ch == ':')
790 			ch = '/';
791 
792 		*name++ = ch;
793 	} while (ch != '\0');
794 }
795 
796 /*
797  * add a value property
798  */
799 static int
800 add_value_prop(picl_nodehdl_t node_hdl, const char *prop_name, int fru_type,
801     int16_t value)
802 {
803 	int err;
804 	union {
805 		float		u_f;
806 		int16_t		u_i16;
807 	} val_buf;
808 
809 	if (fru_to_ptype[fru_type] == PICL_PTYPE_FLOAT)
810 		val_buf.u_f = (float)((float)value / (float)1000.0);
811 	else
812 		val_buf.u_i16 = value;
813 
814 	err = add_regular_prop(node_hdl, prop_name, fru_to_ptype[fru_type],
815 	    PICL_READ, fru_to_size[fru_type], &val_buf, NULL);
816 	return (err);
817 }
818 
819 static int
820 find_picl_handle(picl_prophdl_t proph)
821 {
822 	int	index;
823 
824 	for (index = 0; index < handle_arr.num; index++) {
825 		if (handle_arr.piclprhdls[index] == proph)
826 			return (index);
827 	}
828 
829 	return (-1);
830 }
831 
832 /*
833  * look up function to convert led status into string
834  */
835 static int
836 lookup_led_status(int8_t state, const char **string)
837 {
838 	int	i;
839 	int	lim = sizeof (ledstate_lkup) / sizeof (ledstate_lkup[0]);
840 
841 	for (i = 0; i < lim; i++) {
842 		if (ledstate_lkup[i].state == state) {
843 			*string = ledstate_lkup[i].str_ledstate;
844 			return (PICL_SUCCESS);
845 		}
846 	}
847 
848 	*string = "";
849 	return (PICL_PROPVALUNAVAILABLE);
850 }
851 
852 static int
853 lookup_key_posn(envmon_keysw_pos_t pos, const char **string)
854 {
855 	int	i;
856 	int	lim = sizeof (keyposn_lkup) / sizeof (keyposn_lkup[0]);
857 
858 	for (i = 0; i < lim; i++) {
859 		if (keyposn_lkup[i].pos == pos) {
860 			*string = keyposn_lkup[i].str_keyposn;
861 			return (PICL_SUCCESS);
862 		}
863 	}
864 
865 	*string = "";
866 	return (PICL_PROPVALUNAVAILABLE);
867 }
868 
869 /*
870  * function to read volatile data associated with a PICL property handle
871  */
872 static int
873 read_vol_data(ptree_rarg_t *r_arg, void *buf)
874 {
875 	picl_prophdl_t		proph;
876 	int			index;
877 	uint8_t			fru_type;
878 	envmon_handle_t		id;
879 	int16_t			sensor_data;
880 	int8_t			led_state;
881 	envmon_keysw_pos_t	key_posn;
882 	float			float_data;
883 	int			cmd;
884 	int			err;
885 	int			envmon_fd;
886 	const char		*cptr;
887 
888 	proph = r_arg->proph;
889 	index = find_picl_handle(proph);
890 	if (index < 0)
891 		return (PICL_INVALIDHANDLE);
892 	fru_type = handle_arr.fru_types[index];
893 	id = handle_arr.envhandles[index];
894 	cmd = fru_to_cmd[fru_type];
895 	envmon_fd = open(envmon_device_name, O_RDONLY);
896 	if (envmon_fd < 0)
897 		return (PICL_NOTREADABLE);
898 
899 	/*
900 	 * read environmental data according to type
901 	 */
902 	switch (fru_type) {
903 	case ENVMON_VOLT_SENS:
904 		/*FALLTHROUGH*/
905 	case ENVMON_AMP_SENS:
906 		/*FALLTHROUGH*/
907 	case ENVMON_TEMP_SENS:
908 		err = get_sensor_data(envmon_fd, &id, cmd, NULL, NULL,
909 		    &sensor_data);
910 		break;
911 	case ENVMON_VOLT_IND:
912 		/*FALLTHROUGH*/
913 	case ENVMON_AMP_IND:
914 		/*FALLTHROUGH*/
915 	case ENVMON_TEMP_IND:
916 		/*FALLTHROUGH*/
917 	case ENVMON_FAN_IND:
918 		err = get_indicator_data(envmon_fd, &id, cmd, &sensor_data);
919 		break;
920 	case ENVMON_FAN_SENS:
921 		err = get_fan_data(envmon_fd, &id, cmd, NULL,
922 		    (uint16_t *)&sensor_data, NULL);
923 		break;
924 	case ENVMON_LED_IND:
925 		err = get_led_data(envmon_fd, &id, cmd, &led_state, NULL);
926 		break;
927 	case ENVMON_KEY_SWITCH:
928 		err = get_keyswitch_data(envmon_fd, &id, cmd, &key_posn);
929 		break;
930 	default:
931 		err = PICL_FAILURE;
932 		break;
933 	}
934 
935 	(void) close(envmon_fd);
936 	if (err != PICL_SUCCESS) {
937 		/*
938 		 * PICL_INVALIDHANDLE is used internally, but it upsets
939 		 * prtpicl; change it to PICL_PROPVALUNAVAILABLE
940 		 */
941 		if (err == PICL_INVALIDHANDLE)
942 			err = PICL_PROPVALUNAVAILABLE;
943 		return (err);
944 	}
945 
946 	/*
947 	 * convert data and copy out
948 	 */
949 	switch (fru_type) {
950 	case ENVMON_VOLT_SENS:
951 		/*FALLTHROUGH*/
952 	case ENVMON_AMP_SENS:
953 		float_data = (float)((float)sensor_data / (float)1000.0);
954 		(void) memcpy(buf, &float_data, sizeof (float_data));
955 		break;
956 
957 	case ENVMON_TEMP_SENS:
958 		/*FALLTHROUGH*/
959 	case ENVMON_FAN_SENS:
960 		(void) memcpy(buf, &sensor_data, sizeof (sensor_data));
961 		break;
962 
963 	case ENVMON_VOLT_IND:
964 		/*FALLTHROUGH*/
965 	case ENVMON_AMP_IND:
966 		/*FALLTHROUGH*/
967 	case ENVMON_TEMP_IND:
968 		/*FALLTHROUGH*/
969 	case ENVMON_FAN_IND:
970 		(void) strlcpy(buf, sensor_data == 0 ? cond_okay : cond_failed,
971 		    fru_to_size[fru_type]);
972 		break;
973 
974 	case ENVMON_LED_IND:
975 		err = lookup_led_status(led_state, &cptr);
976 		if (err != PICL_SUCCESS)
977 			return (err);
978 		(void) strlcpy(buf, cptr, fru_to_size[fru_type]);
979 		break;
980 
981 	case ENVMON_KEY_SWITCH:
982 		err = lookup_key_posn(key_posn, &cptr);
983 		if (err != PICL_SUCCESS)
984 			return (err);
985 		(void) strlcpy(buf, cptr, fru_to_size[fru_type]);
986 		break;
987 
988 	default:
989 		return (PICL_FAILURE);
990 	}
991 
992 	return (PICL_SUCCESS);
993 }
994 
995 static int
996 write_led_data(ptree_warg_t *w_arg, const void *buf)
997 {
998 	picl_prophdl_t		proph;
999 	int			index;
1000 	uint8_t			fru_type;
1001 	int			err;
1002 	int			envmon_fd;
1003 	envmon_led_ctl_t	led_ctl;
1004 
1005 	proph = w_arg->proph;
1006 	index = find_picl_handle(proph);
1007 	if (index < 0)
1008 		return (PICL_INVALIDHANDLE);
1009 	fru_type = handle_arr.fru_types[index];
1010 	if (fru_type != ENVMON_LED_IND)
1011 		return (PICL_INVALIDARG);
1012 	if (w_arg->cred.dc_euid != SUPER_USER)
1013 		return (PICL_PERMDENIED);
1014 
1015 	/* see if the requested state is recognized */
1016 	if (strcasecmp(str_Off, buf) == 0)
1017 		led_ctl.led_state = ENVMON_LED_OFF;
1018 	else if (strcasecmp(str_On, buf) == 0)
1019 		led_ctl.led_state = ENVMON_LED_ON;
1020 	else
1021 		return (PICL_INVALIDARG);
1022 
1023 	envmon_fd = open(envmon_device_name, O_RDWR);
1024 	if (envmon_fd < 0)
1025 		return (PICL_FAILURE);
1026 	led_ctl.id = handle_arr.envhandles[index];
1027 	err = ioctl(envmon_fd, ENVMONIOCSETLED, &led_ctl);
1028 	(void) close(envmon_fd);
1029 	if (err < 0)
1030 		return (PICL_FAILURE);
1031 	return (PICL_SUCCESS);
1032 }
1033 
1034 /*
1035  * if colour information is not supplied by the service processor,
1036  * try to determine led colour from the handle name.
1037  */
1038 static void
1039 fix_led_colour(int8_t *colour_p, const char *id)
1040 {
1041 	const char	*cptr = strrchr(id, '.');
1042 
1043 	if ((*colour_p < ENVMON_LED_CLR_NONE) ||
1044 	    (*colour_p > ENVMON_LED_CLR_RED))
1045 		syslog(LOG_ERR, EM_INVALID_COLOR, *colour_p, id);
1046 	if (cptr == NULL) {
1047 		*colour_p = ENVMON_LED_CLR_NONE;
1048 		return;
1049 	}
1050 
1051 	cptr++;		/* step over '.' */
1052 
1053 	if (strcmp(cptr, LED_ACT) == 0)
1054 		    *colour_p = ENVMON_LED_CLR_GREEN;
1055 	else if (strcmp(cptr, LED_SERVICE) == 0)
1056 		*colour_p = ENVMON_LED_CLR_AMBER;
1057 	else if (strcmp(cptr, LED_LOCATE) == 0)
1058 		*colour_p = ENVMON_LED_CLR_WHITE;
1059 	else if (strcmp(cptr, LED_OK2RM) == 0)
1060 		*colour_p = ENVMON_LED_CLR_BLUE;
1061 	else
1062 		*colour_p = ENVMON_LED_CLR_NONE;
1063 }
1064 
1065 /*
1066  * Add nodes for environmental devices of type fru_type
1067  * below the supplied node.
1068  */
1069 static int
1070 add_env_nodes(int envmon_fd, uint8_t fru_type, picl_nodehdl_t envmonh)
1071 {
1072 	envmon_handle_t		id;
1073 	envmon_thresholds_t	lows;
1074 	envmon_thresholds_t	highs;
1075 	char			units[ENVMON_MAXNAMELEN];
1076 	char			platform_tree_name[ENVMON_MAXNAMELEN];
1077 	char			label_name[ENVMON_MAXNAMELEN];
1078 	int16_t			sensor_data;
1079 	int8_t			led_state;
1080 	int8_t			colour;
1081 	envmon_keysw_pos_t	key_state;
1082 	int			cmd;
1083 	int			err;
1084 	int			index = handle_arr.num;
1085 	picl_nodehdl_t		node_hdl;
1086 
1087 	/*
1088 	 * catch table is full at start
1089 	 */
1090 	if (index >= handle_arr.maxnum)
1091 		return (PICL_FAILURE);
1092 
1093 	cmd = fru_to_cmd[fru_type];
1094 	id.name[0] = '\0';
1095 
1096 	do {
1097 		lows.warning = lows.shutdown = lows.poweroff =
1098 		    ENVMON_VAL_UNAVAILABLE;
1099 		highs.warning = highs.shutdown = highs.poweroff =
1100 		    ENVMON_VAL_UNAVAILABLE;
1101 		handle_arr.fru_types[index] = fru_type;
1102 		/* must store id before reading data as it is then updated */
1103 		handle_arr.envhandles[index] = id;
1104 		/*
1105 		 * read environmental data according to type
1106 		 */
1107 		switch (fru_type) {
1108 		case ENVMON_VOLT_SENS:
1109 			/*FALLTHROUGH*/
1110 		case ENVMON_AMP_SENS:
1111 			/*FALLTHROUGH*/
1112 		case ENVMON_TEMP_SENS:
1113 			err = get_sensor_data(envmon_fd, &id, cmd, &lows,
1114 			    &highs, &sensor_data);
1115 			break;
1116 		case ENVMON_VOLT_IND:
1117 			/*FALLTHROUGH*/
1118 		case ENVMON_AMP_IND:
1119 			/*FALLTHROUGH*/
1120 		case ENVMON_TEMP_IND:
1121 			/*FALLTHROUGH*/
1122 		case ENVMON_FAN_IND:
1123 			err = get_indicator_data(envmon_fd, &id, cmd,
1124 			    &sensor_data);
1125 			break;
1126 		case ENVMON_FAN_SENS:
1127 			err = get_fan_data(envmon_fd, &id, cmd, &lows,
1128 			    (uint16_t *)&sensor_data, units);
1129 			break;
1130 		case ENVMON_LED_IND:
1131 			err = get_led_data(envmon_fd, &id, cmd, &led_state,
1132 			    &colour);
1133 			break;
1134 		case ENVMON_KEY_SWITCH:
1135 			err = get_keyswitch_data(envmon_fd, &id, cmd,
1136 			    &key_state);
1137 			break;
1138 		default:
1139 			return (PICL_FAILURE);
1140 		}
1141 
1142 		if (err == PICL_INVALIDHANDLE)
1143 			continue;
1144 		if ((err != PICL_SUCCESS) && (err != PICL_PROPVALUNAVAILABLE)) {
1145 			syslog(LOG_ERR, EM_NODE_ACCESS, id, fru_type, err);
1146 			continue;
1147 		}
1148 
1149 		/*
1150 		 * successfully read environmental data, add to PICL
1151 		 */
1152 		(void) strlcpy(platform_tree_name,
1153 		    handle_arr.envhandles[index].name,
1154 		    sizeof (platform_tree_name));
1155 
1156 		(void) strlcpy(label_name, platform_tree_name,
1157 		    ENVMON_MAXNAMELEN);
1158 		convert_label_name(label_name);
1159 		convert_node_name(platform_tree_name);
1160 		/*
1161 		 * does this node already exist?
1162 		 */
1163 		err = get_child_by_name(envmonh, platform_tree_name, &node_hdl);
1164 		if (err == PICL_SUCCESS) {
1165 			/*
1166 			 * skip over existing node
1167 			 */
1168 			continue;
1169 		}
1170 		err = ptree_create_node(platform_tree_name,
1171 		    fru_to_class[fru_type], &node_hdl);
1172 		if (err != PICL_SUCCESS) {
1173 			break;
1174 		}
1175 		err = add_volatile_prop(node_hdl, fru_to_prop[fru_type],
1176 		    fru_to_ptype[fru_type],
1177 		    PICL_READ | (fru_type == ENVMON_LED_IND ? PICL_WRITE : 0),
1178 		    fru_to_size[fru_type], read_vol_data,
1179 		    fru_type == ENVMON_LED_IND ? write_led_data : NULL,
1180 		    &handle_arr.piclprhdls[index]);
1181 		if (err != PICL_SUCCESS) {
1182 			break;
1183 		}
1184 		/*
1185 		 * if any thresholds are defined add a property
1186 		 */
1187 		if (lows.warning != ENVMON_VAL_UNAVAILABLE) {
1188 			err = add_value_prop(node_hdl, PICL_PROP_LOW_WARNING,
1189 			    fru_type, lows.warning);
1190 			if (err != PICL_SUCCESS) {
1191 				break;
1192 			}
1193 		}
1194 		if (lows.shutdown != ENVMON_VAL_UNAVAILABLE) {
1195 			err = add_value_prop(node_hdl, PICL_PROP_LOW_SHUTDOWN,
1196 			    fru_type, lows.shutdown);
1197 			if (err != PICL_SUCCESS) {
1198 				break;
1199 			}
1200 		}
1201 		if (lows.poweroff != ENVMON_VAL_UNAVAILABLE) {
1202 			err = add_value_prop(node_hdl, PICL_PROP_LOW_POWER_OFF,
1203 			    fru_type, lows.poweroff);
1204 			if (err != PICL_SUCCESS) {
1205 				break;
1206 			}
1207 		}
1208 		if (highs.warning != ENVMON_VAL_UNAVAILABLE) {
1209 			err = add_value_prop(node_hdl, PICL_PROP_HIGH_WARNING,
1210 			    fru_type, highs.warning);
1211 			if (err != PICL_SUCCESS) {
1212 				break;
1213 			}
1214 		}
1215 		if (highs.shutdown != ENVMON_VAL_UNAVAILABLE) {
1216 			err = add_value_prop(node_hdl, PICL_PROP_HIGH_SHUTDOWN,
1217 			    fru_type, highs.shutdown);
1218 			if (err != PICL_SUCCESS) {
1219 				break;
1220 			}
1221 		}
1222 		if (highs.poweroff != ENVMON_VAL_UNAVAILABLE) {
1223 			err = add_value_prop(node_hdl, PICL_PROP_HIGH_POWER_OFF,
1224 			    fru_type, highs.poweroff);
1225 			if (err != PICL_SUCCESS) {
1226 				break;
1227 			}
1228 		}
1229 
1230 		/*
1231 		 * if device is a fan sensor, add a speedunit property
1232 		 */
1233 		if (fru_type == ENVMON_FAN_SENS) {
1234 			err = add_regular_prop(node_hdl,
1235 			    PICL_PROP_FAN_SPEED_UNIT, PICL_PTYPE_CHARSTRING,
1236 			    PICL_READ, 1 + strlen(units), units, NULL);
1237 			if (err != PICL_SUCCESS) {
1238 				break;
1239 			}
1240 		}
1241 		/*
1242 		 * If device is a LED indicator and returns a colour,
1243 		 * add a colour property.
1244 		 */
1245 		if (fru_type == ENVMON_LED_IND) {
1246 			if (colour < 0 || colour == ENVMON_LED_CLR_ANY ||
1247 			    colour > ENVMON_LED_CLR_RED)
1248 				fix_led_colour(&colour,
1249 				    handle_arr.envhandles[index].name);
1250 			if (colour != ENVMON_LED_CLR_NONE) {
1251 				err = add_regular_prop(node_hdl,
1252 				    PICL_PROP_COLOR, PICL_PTYPE_CHARSTRING,
1253 				    PICL_READ, colour_lkup[colour].size,
1254 				    colour_lkup[colour].str_colour, NULL);
1255 				if (err != PICL_SUCCESS) {
1256 					break;
1257 				}
1258 			}
1259 		}
1260 		/*
1261 		 * add a label property unless it's a keyswitch
1262 		 * (keyswitch is labelled from a config file because the
1263 		 * ALOM interface doesn't supply a name for it)
1264 		 */
1265 		if (fru_type != ENVMON_KEY_SWITCH) {
1266 			err = add_regular_prop(node_hdl, PICL_PROP_LABEL,
1267 			    PICL_PTYPE_CHARSTRING, PICL_READ,
1268 			    1 + strlen(label_name), label_name, NULL);
1269 
1270 			if (err != PICL_SUCCESS) {
1271 				break;
1272 			}
1273 		}
1274 		/*
1275 		 * all properties added to this node, add the node below
1276 		 * the supplied anchor point
1277 		 */
1278 		err = ptree_add_node(envmonh, node_hdl);
1279 
1280 		if (err != PICL_SUCCESS) {
1281 			break;
1282 		}
1283 
1284 		/*
1285 		 * that node went in OK, advance index
1286 		 */
1287 		index++;
1288 
1289 	} while ((id.name[0] != '\0') && (index < handle_arr.maxnum));
1290 
1291 	handle_arr.num = index;
1292 	return (err);
1293 }
1294 
1295 static void
1296 fixstate(uint8_t state, const char *string, int *max_len)
1297 {
1298 	int		i;
1299 	int		len;
1300 
1301 	for (i = 0; i < (sizeof (ledstate_lkup) / sizeof (ledstate_lkup[0]));
1302 	    i++) {
1303 		if (ledstate_lkup[i].state == state) {
1304 			if (ledstate_lkup[i].str_ledstate != NULL)
1305 				free(ledstate_lkup[i].str_ledstate);
1306 			ledstate_lkup[i].str_ledstate = strdup(string);
1307 			len = strlen(string);
1308 			if (len >= *max_len)
1309 				*max_len = len + 1;
1310 			break;
1311 		}
1312 	}
1313 }
1314 
1315 static void
1316 fixkeyposn(envmon_keysw_pos_t keyposn, const char *string, int *max_len)
1317 {
1318 	int		i;
1319 	int		len;
1320 
1321 	for (i = 0; i < (sizeof (keyposn_lkup) / sizeof (keyposn_lkup[0]));
1322 	    i++) {
1323 		if (keyposn_lkup[i].pos == keyposn) {
1324 			if (keyposn_lkup[i].str_keyposn != NULL)
1325 				free(keyposn_lkup[i].str_keyposn);
1326 			keyposn_lkup[i].str_keyposn = strdup(string);
1327 			len = strlen(string);
1328 			if (len >= *max_len)
1329 				*max_len = len + 1;
1330 			break;
1331 		}
1332 	}
1333 }
1334 
1335 static void
1336 setup_strings()
1337 {
1338 	int string_size;
1339 	int i;
1340 	int lim = sizeof (colour_lkup) / sizeof (colour_lkup[0]);
1341 
1342 	/*
1343 	 * initialise led colours lookup
1344 	 */
1345 	for (i = 0; i < lim; i++) {
1346 		if (colour_lkup[i].str_colour != NULL)
1347 			free(colour_lkup[i].str_colour);
1348 	}
1349 
1350 	colour_lkup[ENVMON_LED_CLR_ANY].str_colour = strdup(gettext("any"));
1351 	colour_lkup[ENVMON_LED_CLR_WHITE].str_colour =
1352 	    strdup(gettext("white"));
1353 	colour_lkup[ENVMON_LED_CLR_BLUE].str_colour = strdup(gettext("blue"));
1354 	colour_lkup[ENVMON_LED_CLR_GREEN].str_colour =
1355 	    strdup(gettext("green"));
1356 	colour_lkup[ENVMON_LED_CLR_AMBER].str_colour =
1357 	    strdup(gettext("amber"));
1358 	colour_lkup[ENVMON_LED_CLR_RED].str_colour =
1359 	    strdup(gettext("red"));
1360 
1361 	for (i = 0; i < lim; i++) {
1362 		if (colour_lkup[i].str_colour != NULL)
1363 			colour_lkup[i].size =
1364 			    1 + strlen(colour_lkup[i].str_colour);
1365 	}
1366 
1367 	/*
1368 	 * initialise condition strings and note longest
1369 	 */
1370 	string_size = 0;
1371 	cond_okay = strdup(gettext("okay"));
1372 	if (strlen(cond_okay) >= string_size)
1373 		string_size = 1 + strlen(cond_okay);
1374 	cond_failed = strdup(gettext("failed"));
1375 	if (strlen(cond_failed) >= string_size)
1376 		string_size = 1 + strlen(cond_failed);
1377 
1378 	for (i = 0; i < sizeof (fru_to_size) / sizeof (fru_to_size[0]); i++)
1379 		if (fru_to_size[i] == -1)
1380 			fru_to_size[i] = string_size;
1381 
1382 	/*
1383 	 * initialise led state lookup strings
1384 	 */
1385 	string_size = 0;
1386 	fixstate(ENVMON_LED_OFF, gettext("off"), &string_size);
1387 	fixstate(ENVMON_LED_ON, gettext("on"), &string_size);
1388 	fixstate(ENVMON_LED_BLINKING, gettext("blinking"), &string_size);
1389 	fixstate(ENVMON_LED_FLASHING, gettext("flashing"), &string_size);
1390 	fru_to_size[ENVMON_LED_IND] = string_size;
1391 
1392 	/*
1393 	 * initialise key position lookup strings
1394 	 */
1395 	string_size = 0;
1396 	fixkeyposn(ENVMON_KEYSW_POS_UNKNOWN, gettext("UNKNOWN"), &string_size);
1397 	fixkeyposn(ENVMON_KEYSW_POS_NORMAL, gettext("NORMAL"), &string_size);
1398 	fixkeyposn(ENVMON_KEYSW_POS_DIAG, gettext("DIAG"), &string_size);
1399 	fixkeyposn(ENVMON_KEYSW_POS_LOCKED, gettext("LOCKED"), &string_size);
1400 	fixkeyposn(ENVMON_KEYSW_POS_OFF, gettext("STBY"), &string_size);
1401 	fru_to_size[ENVMON_KEY_SWITCH] = string_size;
1402 }
1403 
1404 /*
1405  * The size of outfilename must be PATH_MAX
1406  */
1407 static int
1408 get_config_file(char *filename)
1409 {
1410 	char	nmbuf[SYS_NMLN];
1411 	char	pname[PATH_MAX];
1412 
1413 	if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) != -1) {
1414 		(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
1415 		(void) strlcat(pname, ENVMON_CONFFILE_NAME, PATH_MAX);
1416 		if (access(pname, R_OK) == 0) {
1417 			(void) strlcpy(filename, pname, PATH_MAX);
1418 			return (0);
1419 		}
1420 	}
1421 
1422 	if (sysinfo(SI_MACHINE, nmbuf, sizeof (nmbuf)) != -1) {
1423 		(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
1424 		(void) strlcat(pname, ENVMON_CONFFILE_NAME, PATH_MAX);
1425 		if (access(pname, R_OK) == 0) {
1426 			(void) strlcpy(filename, pname, PATH_MAX);
1427 			return (0);
1428 		}
1429 	}
1430 
1431 	(void) snprintf(pname, PATH_MAX, "%s/%s",
1432 	    PICLD_COMMON_PLUGIN_DIR, ENVMON_CONFFILE_NAME);
1433 
1434 	if (access(pname, R_OK) == 0) {
1435 		(void) strlcpy(filename, pname, PATH_MAX);
1436 		return (0);
1437 	}
1438 
1439 	return (-1);
1440 }
1441 
1442 static void
1443 free_vol_prop(picl_prophdl_t proph)
1444 {
1445 	int	index;
1446 
1447 	index = find_picl_handle(proph);
1448 	if (index >= 0) {
1449 		handle_arr.num--;
1450 		if (index != handle_arr.num) {
1451 			/* relocate last entry into hole just created */
1452 			handle_arr.fru_types[index] =
1453 			    handle_arr.fru_types[handle_arr.num];
1454 			handle_arr.envhandles[index] =
1455 			    handle_arr.envhandles[handle_arr.num];
1456 			handle_arr.piclprhdls[index] =
1457 			    handle_arr.piclprhdls[handle_arr.num];
1458 		}
1459 	}
1460 }
1461 
1462 /*
1463  * handle PICL FRU ADDED and FRU REMOVED events
1464  */
1465 /*ARGSUSED*/
1466 static void
1467 envmon_evhandler(const char *ename, const void *earg, size_t size,
1468     void *cookie)
1469 {
1470 	char			path[MAXPATHLEN];
1471 	picl_nodehdl_t		locnodeh;
1472 	int			retval;
1473 	picl_nodehdl_t		childh;
1474 	picl_nodehdl_t		nodeh;
1475 	picl_prophdl_t		tableh;
1476 	picl_prophdl_t		tblh;
1477 	picl_prophdl_t		proph;
1478 	ptree_propinfo_t	pi;
1479 
1480 	if (strcmp(ename, PICL_FRU_ADDED) == 0) {
1481 		retval = nvlist_lookup_uint64((nvlist_t *)earg,
1482 		    PICLEVENTARG_PARENTHANDLE, &locnodeh);
1483 
1484 		if (retval != 0) {
1485 			syslog(LOG_ERR, EM_EV_MISSING_ARG,
1486 			    PICLEVENTARG_PARENTHANDLE);
1487 			return;
1488 		}
1489 		retval = ptree_get_propval_by_name(locnodeh, PICL_PROP_NAME,
1490 		    path, sizeof (path));
1491 		if (retval == PICL_SUCCESS) {
1492 			/*
1493 			 * Open envmon device and interrogate
1494 			 */
1495 			int		envmon_fd;
1496 			int		fru_type;
1497 			picl_nodehdl_t	envmoninfh;
1498 
1499 			if (get_envmon_node(&envmoninfh) != PICL_SUCCESS) {
1500 				syslog(LOG_ERR, EM_SC_NODE_MISSING);
1501 				return;
1502 			}
1503 
1504 			if ((envmon_fd = open(envmon_device_name, O_RDONLY)) <
1505 			    0) {
1506 				syslog(LOG_ERR, EM_SYS_ERR, envmon_device_name,
1507 				    strerror(errno));
1508 				return;
1509 			}
1510 
1511 			if (strcmp(str_SC, path) == 0) {
1512 				/*
1513 				 * SC state change - re-assess platform tree
1514 				 */
1515 				if (re_create_arrays(envmon_fd) != 0) {
1516 					/*
1517 					 * out of memory - make no changes
1518 					 */
1519 					return;
1520 				}
1521 				/*
1522 				 * dropped memory of volatile prop handles
1523 				 * so drop the nodes also, then rebuild for
1524 				 * the newly loaded SC
1525 				 */
1526 				retval = ptree_get_propval_by_name(envmoninfh,
1527 				    PICL_PROP_PARENT, &nodeh, sizeof (nodeh));
1528 				if (retval != PICL_SUCCESS) {
1529 					(void) close(envmon_fd);
1530 					return;
1531 				}
1532 				retval = ptree_get_propval_by_name(envmoninfh,
1533 				    PICL_PROP_NAME, path, sizeof (path));
1534 				if (retval != PICL_SUCCESS) {
1535 					(void) close(envmon_fd);
1536 					return;
1537 				}
1538 
1539 				retval = ptree_delete_node(envmoninfh);
1540 				if (retval == PICL_SUCCESS)
1541 				    (void) ptree_destroy_node(envmoninfh);
1542 				retval = ptree_create_node(path,
1543 				    PICL_CLASS_SERVICE_PROCESSOR, &envmoninfh);
1544 				if (retval != PICL_SUCCESS) {
1545 					(void) close(envmon_fd);
1546 					return;
1547 				}
1548 				retval = ptree_add_node(nodeh, envmoninfh);
1549 				if (retval != PICL_SUCCESS) {
1550 					(void) close(envmon_fd);
1551 					return;
1552 				}
1553 			}
1554 
1555 			for (fru_type = 0; fru_type < ENVMONTYPES;
1556 			    fru_type++) {
1557 				(void) add_env_nodes(envmon_fd, fru_type,
1558 				    envmoninfh);
1559 			}
1560 
1561 			(void) close(envmon_fd);
1562 		}
1563 	} else if (strcmp(ename, PICL_FRU_REMOVED) == 0) {
1564 		retval = nvlist_lookup_uint64((nvlist_t *)earg,
1565 		    PICLEVENTARG_FRUHANDLE, &childh);
1566 
1567 		if (retval != 0) {
1568 			syslog(LOG_ERR, EM_EV_MISSING_ARG,
1569 			    PICLEVENTARG_FRUHANDLE);
1570 			return;
1571 		}
1572 		retval = ptree_get_propval_by_name(childh, PICL_PROP_NAME,
1573 		    path, sizeof (path));
1574 		if (retval == PICL_SUCCESS) {
1575 			retval = ptree_get_prop_by_name(childh,
1576 			    PICL_PROP_DEVICES, &tableh);
1577 
1578 			if (retval != PICL_SUCCESS) {
1579 				/* no Devices table, nothing to do */
1580 				return;
1581 			}
1582 
1583 			/*
1584 			 * follow all reference properties in the second
1585 			 * column of the table and delete the referenced node
1586 			 */
1587 			retval = ptree_get_propval(tableh, &tblh,
1588 			    sizeof (tblh));
1589 			if (retval != PICL_SUCCESS) {
1590 				/*
1591 				 * can't get value of table property
1592 				 */
1593 				return;
1594 			}
1595 			/* get first col, first row */
1596 			retval = ptree_get_next_by_col(tblh, &tblh);
1597 			if (retval != PICL_SUCCESS) {
1598 				/*
1599 				 * no rows?
1600 				 */
1601 				return;
1602 			}
1603 			/*
1604 			 * starting at next col, get every entry in the column
1605 			 */
1606 			for (retval = ptree_get_next_by_row(tblh, &tblh);
1607 			    retval == PICL_SUCCESS;
1608 			    retval = ptree_get_next_by_col(tblh, &tblh)) {
1609 				/*
1610 				 * should be a ref prop in our hands,
1611 				 * get the target node handle
1612 				 */
1613 				retval = ptree_get_propval(tblh, &nodeh,
1614 				    sizeof (nodeh));
1615 				if (retval != PICL_SUCCESS) {
1616 					continue;
1617 				}
1618 				/*
1619 				 * got the referenced node, has it got a
1620 				 * volatile property to clean up?
1621 				 */
1622 				retval = ptree_get_first_prop(nodeh, &proph);
1623 				while (retval == PICL_SUCCESS) {
1624 					retval = ptree_get_propinfo(proph, &pi);
1625 					if ((retval == PICL_SUCCESS) &&
1626 					    (pi.piclinfo.accessmode &
1627 					    PICL_VOLATILE))
1628 						free_vol_prop(proph);
1629 					retval = ptree_get_next_prop(proph,
1630 					    &proph);
1631 				}
1632 				/*
1633 				 * all volatile properties gone, remove node
1634 				 */
1635 				retval = ptree_delete_node(nodeh);
1636 				if (retval == PICL_SUCCESS)
1637 				    (void) ptree_destroy_node(nodeh);
1638 			}
1639 		}
1640 	}
1641 }
1642 
1643 /*
1644  * executed as part of .init when the plugin is dlopen()ed
1645  */
1646 static void
1647 piclenvmon_register(void)
1648 {
1649 	(void) picld_plugin_register(&my_reg_info);
1650 }
1651 
1652 /*
1653  * Init entry point of the plugin
1654  * Creates the PICL nodes and properties in the physical and logical aspects.
1655  */
1656 static void
1657 piclenvmon_init(void)
1658 {
1659 	picl_nodehdl_t		rooth;
1660 	picl_nodehdl_t		plfh;
1661 	picl_nodehdl_t		envmoninfh;
1662 	int			res;
1663 	int			envmon_fd;
1664 	int			fru_type;
1665 	char			pathname[PATH_MAX];
1666 
1667 	/*
1668 	 * locate and parse config file
1669 	 */
1670 	if (get_config_file(pathname) < 0)
1671 		return;
1672 
1673 	if ((ptree_get_root(&rooth) != PICL_SUCCESS) ||
1674 	    (picld_pluginutil_parse_config_file(rooth, pathname) !=
1675 	    PICL_SUCCESS)) {
1676 		syslog(LOG_ERR, EM_INIT_FAILED);
1677 	}
1678 
1679 	/*
1680 	 * Get platform node
1681 	 */
1682 	if (ptree_get_node_by_path(PICL_NODE_ROOT PICL_NODE_PLATFORM, &plfh)
1683 	    != PICL_SUCCESS) {
1684 		syslog(LOG_ERR, EM_MISSING_NODE, PICL_NODE_PLATFORM);
1685 		syslog(LOG_ERR, EM_INIT_FAILED);
1686 		return;
1687 	}
1688 
1689 	/*
1690 	 * Get service-processor node
1691 	 */
1692 	if (get_envmon_node(&envmoninfh) != PICL_SUCCESS)
1693 		return;
1694 
1695 	/*
1696 	 * We may have been restarted, make sure we don't leak
1697 	 */
1698 	if (envmon_device_name != NULL) {
1699 		free(envmon_device_name);
1700 	}
1701 
1702 	if ((envmon_device_name = create_envmon_pathname(envmoninfh)) == NULL)
1703 		return;
1704 
1705 	/*
1706 	 * Open envmon device and interrogate for devices it monitors
1707 	 */
1708 	if ((envmon_fd = open(envmon_device_name, O_RDONLY)) < 0) {
1709 		syslog(LOG_ERR, EM_SYS_ERR, envmon_device_name,
1710 		    strerror(errno));
1711 		return;
1712 	}
1713 
1714 	if (get_envmon_limits(envmon_fd, &env_limits) < 0)
1715 		return;
1716 
1717 	/*
1718 	 * A set of arrays are used whose bounds are determined by the
1719 	 * response to get_envmon_limits. Establish these arrays now.
1720 	 */
1721 	create_arrays();
1722 	setup_strings();
1723 
1724 	for (fru_type = 0; fru_type < ENVMONTYPES; fru_type++) {
1725 		(void) add_env_nodes(envmon_fd, fru_type, envmoninfh);
1726 	}
1727 
1728 	(void) close(envmon_fd);
1729 
1730 	res = ptree_register_handler(PICL_FRU_ADDED, envmon_evhandler, NULL);
1731 	if (res != PICL_SUCCESS) {
1732 		syslog(LOG_ERR, EM_EVREG_FAILED, res);
1733 	}
1734 	res = ptree_register_handler(PICL_FRU_REMOVED, envmon_evhandler, NULL);
1735 	if (res != PICL_SUCCESS) {
1736 		syslog(LOG_ERR, EM_EVREG_FAILED, res);
1737 	}
1738 }
1739 
1740 /*
1741  * fini entry point of the plugin
1742  */
1743 static void
1744 piclenvmon_fini(void)
1745 {
1746 	if (envmon_device_name != NULL) {
1747 		free(envmon_device_name);
1748 		envmon_device_name = NULL;
1749 	}
1750 	(void) ptree_unregister_handler(PICL_FRU_ADDED,
1751 	    envmon_evhandler, NULL);
1752 	(void) ptree_unregister_handler(PICL_FRU_REMOVED,
1753 	    envmon_evhandler, NULL);
1754 }
1755