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