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