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
72static void	piclenvmon_register(void);
73static void	piclenvmon_init(void);
74static void	piclenvmon_fini(void);
75static node_el_t	*create_node_el(picl_nodehdl_t nodeh);
76static void	delete_node_el(node_el_t *pel);
77static node_list_t	*create_node_list();
78static void	delete_node_list(node_list_t *pnl);
79static void	add_node_to_list(picl_nodehdl_t nodeh, node_list_t *listp);
80static void	get_node_list_by_class(picl_nodehdl_t nodeh,
81    const char *classname, node_list_t *listp);
82static int	get_envmon_limits(int envmon_fd, envmon_sysinfo_t *limits_p);
83static void	create_arrays();
84static int	get_envmon_node(picl_nodehdl_t *envmoninfh);
85static char	*create_envmon_pathname(picl_nodehdl_t envmoninfh);
86static int	get_child_by_name(picl_nodehdl_t nodeh, const char *name,
87    picl_nodehdl_t *childh);
88static 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);
90static 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);
93static 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);
95static int	get_indicator_data(int envmon_fd, envmon_handle_t *id, int cmd,
96    int16_t *condition);
97static int	get_fan_data(int envmon_fd, envmon_handle_t *id, int cmd,
98    envmon_thresholds_t *lows, uint16_t *speed, char *units);
99static int	get_led_data(int envmon_fd, envmon_handle_t *id, int cmd,
100    int8_t *state, int8_t *colour);
101static int	get_keyswitch_data(int envmon_fd, envmon_handle_t *id, int cmd,
102    envmon_keysw_pos_t *key_state);
103static void	convert_node_name(char *ptr);
104static void	convert_label_name(char *ptr);
105static int	add_value_prop(picl_nodehdl_t node_hdl, const char *prop_name,
106    int fru_type, int16_t value);
107static int	find_picl_handle(picl_prophdl_t proph);
108static int	lookup_led_status(int8_t state, const char **string);
109static int	lookup_key_posn(envmon_keysw_pos_t pos, const char **string);
110static int	get_config_file(char *filename);
111static int	read_vol_data(ptree_rarg_t *r_arg, void *buf);
112static int	write_led_data(ptree_warg_t *w_arg, const void *buf);
113static int	add_env_nodes(int envmon_fd, uint8_t fru_type,
114    picl_nodehdl_t envmonh);
115static void	fixstate(uint8_t state, const char *string, int *max_len);
116static void	fixkeyposn(envmon_keysw_pos_t keyposn, const char *string,
117    int *max_len);
118static void	setup_strings();
119static void	free_vol_prop(picl_prophdl_t proph);
120static void	envmon_evhandler(const char *ename, const void *earg,
121    size_t size, void *cookie);
122static 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
127static 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
135static	const char str_On[] = "on";
136static	const char str_Off[] = "off";
137static  const char str_Blinking[] = "blinking";
138static  const char str_Flashing[] = "flashing";
139static	const char str_SC[] = "SC";
140static	char *envmon_device_name = NULL;
141static	envmon_sysinfo_t	env_limits;
142static	handle_array_t	handle_arr;
143static	struct {
144	int		size;
145	char		*str_colour;
146} colour_lkup[1 + ENVMON_LED_CLR_RED];
147
148static	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
158static	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 */
172int	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 */
189const 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 */
206const 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 */
223int	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 */
240static char *cond_okay;
241static 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 */
247int	fru_to_size[] = {
248	4, -1, 4, -1, 2, -1, 2, -1, -1, -1, -1
249};
250
251static node_el_t *
252create_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
264static void
265delete_node_el(node_el_t *pel)
266{
267	free(pel);
268}
269
270static node_list_t *
271create_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
283static void
284delete_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 */
306static void
307add_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 */
326static void
327get_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
354static int
355get_envmon_limits(int envmon_fd, envmon_sysinfo_t *limits_p)
356{
357	return (ioctl(envmon_fd, ENVMONIOCSYSINFO, limits_p));
358}
359
360static int
361re_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
415static void
416create_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
430static int
431get_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
460static char *
461create_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 */
528static int
529get_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 */
554static int
555add_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 */
578static int
579add_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 */
609static int
610get_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 */
650static int
651get_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 */
675static int
676get_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 */
707static int
708get_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 */
735static int
736get_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 */
761static int
762get_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 */
789static void
790convert_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 */
809static void
810convert_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 */
835static int
836add_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
855static int
856find_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 */
871static int
872lookup_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
888static int
889lookup_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 */
908static int
909read_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
1039static int
1040write_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 */
1086static void
1087fix_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 */
1117static int
1118add_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
1351static void
1352fixstate(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
1371static void
1372fixkeyposn(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
1391static void
1392setup_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 */
1468static int
1469get_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
1503static void
1504free_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*/
1527static void
1528envmon_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 */
1707static void
1708piclenvmon_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 */
1717static void
1718piclenvmon_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 */
1804static void
1805piclenvmon_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