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 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <strings.h>
27#include <assert.h>
28#include <fm/libtopo.h>
29#include <topo_prop.h>
30#include <topo_string.h>
31#include <topo_alloc.h>
32#include <topo_error.h>
33#include <topo_method.h>
34
35/*
36 * Topology nodes are permitted to contain property information.
37 * Property information is organized according to property grouping.
38 * Each property group defines a name, a stability level for that name,
39 * a stability level for all underlying property data (name, type, values),
40 * a version for the property group definition and and a list of uniquely
41 * defined properties.  Property group versions are incremented when one of
42 * the following changes occurs:
43 *	- a property name changes
44 *	- a property type changes
45 *	- a property definition is removed from the group
46 * Compatible changes such as new property definitions in the group do
47 * not require version changes.
48 *
49 * Each property defines a unique (within the group) name, a type and
50 * a value.  Properties may be statically defined as int32, uint32, int64,
51 * uint64, fmri, string or arrays of each type.  Properties may also be
52 * dynamically exported via module registered methods.  For example, a module
53 * may register a method to export an ASRU property that is dynamically
54 * contructed when a call to topo_node_fmri() is invoked for a particular
55 * topology node.
56 *
57 * Static properties are persistently attached to topology nodes during
58 * enumeration by an enumeration module or as part of XML statements in a
59 * toplogy map file using the topo_prop_set* family of routines.  Similarly,
60 * property methods are registered during enumeration or as part of
61 * statements in topololgy map files.  Set-up of property methods is performed
62 * by calling topo_prop_method_register().
63 *
64 * All properties, whether statically persisted in a snapshot or dynamically
65 * obtained, may be read via the topo_prop_get* family of interfaces.
66 * Callers wishing to receive all property groups and properties for a given
67 * node may use topo_prop_getall().  This routine returns a nested nvlist
68 * of all groupings and property (name, type, value) sets.  Groupings
69 * are defined by TOPO_PROP_GROUP (name, data stability, name stability and
70 * version) and a nested nvlist of properties (TOPO_PROP_VAL).  Each property
71 * value is defined by its name, type and value.
72 */
73static void topo_propval_destroy(topo_propval_t *);
74
75static topo_pgroup_t *
76pgroup_get(tnode_t *node, const char *pgname)
77{
78	topo_pgroup_t *pg;
79	/*
80	 * Check for an existing pgroup
81	 */
82	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
83	    pg = topo_list_next(pg)) {
84		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
85			return (pg);
86		}
87	}
88
89	return (NULL);
90}
91
92static topo_propval_t *
93propval_get(topo_pgroup_t *pg, const char *pname)
94{
95	topo_proplist_t *pvl;
96
97	if (pg == NULL)
98		return (NULL);
99
100	for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
101	    pvl = topo_list_next(pvl)) {
102		if (strcmp(pvl->tp_pval->tp_name, pname) == 0)
103			return (pvl->tp_pval);
104	}
105
106	return (NULL);
107}
108
109static int
110method_geterror(nvlist_t *nvl, int err, int *errp)
111{
112	if (nvl != NULL)
113		nvlist_free(nvl);
114
115	*errp = err;
116
117	return (-1);
118}
119
120static int
121prop_method_get(tnode_t *node, topo_propval_t *pv, topo_propmethod_t *pm,
122    nvlist_t *pargs, int *err)
123{
124	int ret;
125	nvlist_t *args, *nvl;
126	char *name;
127	topo_type_t type;
128
129	if (topo_hdl_nvalloc(pv->tp_hdl, &args, NV_UNIQUE_NAME) < 0 ||
130	    nvlist_add_nvlist(args, TOPO_PROP_ARGS, pm->tpm_args) != 0)
131		return (method_geterror(NULL, ETOPO_PROP_NVL, err));
132
133	if (pargs != NULL)
134		if (nvlist_add_nvlist(args, TOPO_PROP_PARGS, pargs) != 0)
135			return (method_geterror(args, ETOPO_PROP_NVL, err));
136
137	/*
138	 * Now, get the latest value
139	 *
140	 * Grab a reference to the property and then unlock the node.  This will
141	 * allow property methods to safely re-enter the prop_get codepath,
142	 * making it possible for property methods to access other property
143	 * values on the same node w\o causing a deadlock.
144	 */
145	topo_prop_hold(pv);
146	topo_node_unlock(node);
147	if (topo_method_call(node, pm->tpm_name, pm->tpm_version,
148	    args, &nvl, err) < 0) {
149		topo_node_lock(node);
150		topo_prop_rele(pv);
151		return (method_geterror(args, *err, err));
152	}
153	topo_node_lock(node);
154	topo_prop_rele(pv);
155
156	nvlist_free(args);
157
158	/* Verify the property contents */
159	ret = nvlist_lookup_string(nvl, TOPO_PROP_VAL_NAME, &name);
160	if (ret != 0 || strcmp(name, pv->tp_name) != 0)
161		return (method_geterror(nvl, ETOPO_PROP_NAME, err));
162
163	ret = nvlist_lookup_uint32(nvl, TOPO_PROP_VAL_TYPE, (uint32_t *)&type);
164	if (ret != 0 || type != pv->tp_type)
165		return (method_geterror(nvl, ETOPO_PROP_TYPE, err));
166
167	/* Release the last value and re-assign to the new value */
168	if (pv->tp_val != NULL)
169		nvlist_free(pv->tp_val);
170	pv->tp_val = nvl;
171
172	return (0);
173}
174
175static topo_propval_t *
176prop_get(tnode_t *node, const char *pgname, const char *pname, nvlist_t *pargs,
177    int *err)
178{
179	topo_propval_t *pv = NULL;
180
181	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
182		*err = ETOPO_PROP_NOENT;
183		return (NULL);
184	}
185
186	if (pv->tp_flag & TOPO_PROP_NONVOLATILE && pv->tp_val != NULL)
187		return (pv);
188
189	if (pv->tp_method != NULL) {
190		if (prop_method_get(node, pv, pv->tp_method, pargs, err) < 0)
191			return (NULL);
192	}
193
194	return (pv);
195}
196
197static int
198get_properror(tnode_t *node, int *errp, int err)
199{
200	topo_node_unlock(node);
201	*errp = err;
202	return (-1);
203}
204
205static int
206prop_getval(tnode_t *node, const char *pgname, const char *pname, void *val,
207    topo_type_t type, uint_t *nelems, int *err)
208{
209	int i, j, ret = 0;
210	topo_hdl_t *thp = node->tn_hdl;
211	topo_propval_t *pv;
212
213	topo_node_lock(node);
214	if ((pv = prop_get(node, pgname, pname, NULL, err))
215	    == NULL)
216		return (get_properror(node, err, *err));
217
218	if (pv->tp_type != type)
219		return (get_properror(node, err, ETOPO_PROP_TYPE));
220
221	switch (type) {
222		case TOPO_TYPE_INT32:
223			ret = nvlist_lookup_int32(pv->tp_val, TOPO_PROP_VAL_VAL,
224			    (int32_t *)val);
225			break;
226		case TOPO_TYPE_UINT32:
227			ret = nvlist_lookup_uint32(pv->tp_val,
228			    TOPO_PROP_VAL_VAL, (uint32_t *)val);
229			break;
230		case TOPO_TYPE_INT64:
231			ret = nvlist_lookup_int64(pv->tp_val, TOPO_PROP_VAL_VAL,
232			    (int64_t *)val);
233			break;
234		case TOPO_TYPE_UINT64:
235			ret = nvlist_lookup_uint64(pv->tp_val,
236			    TOPO_PROP_VAL_VAL, (uint64_t *)val);
237			break;
238		case TOPO_TYPE_DOUBLE:
239			ret = nvlist_lookup_double(pv->tp_val,
240			    TOPO_PROP_VAL_VAL, (double *)val);
241			break;
242		case TOPO_TYPE_STRING: {
243			char *str;
244
245			ret = nvlist_lookup_string(pv->tp_val,
246			    TOPO_PROP_VAL_VAL, &str);
247			if (ret == 0) {
248				char *s2;
249				if ((s2 = topo_hdl_strdup(thp, str)) == NULL)
250					ret = -1;
251				else
252					*(char **)val = s2;
253			}
254			break;
255		}
256		case TOPO_TYPE_FMRI: {
257			nvlist_t *nvl;
258
259			ret = nvlist_lookup_nvlist(pv->tp_val,
260			    TOPO_PROP_VAL_VAL, &nvl);
261			if (ret == 0)
262				ret = topo_hdl_nvdup(thp, nvl,
263				    (nvlist_t **)val);
264			break;
265		}
266		case TOPO_TYPE_INT32_ARRAY: {
267			int32_t *a1, *a2;
268
269			if ((ret = nvlist_lookup_int32_array(pv->tp_val,
270			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
271				break;
272			if ((a1 = topo_hdl_alloc(thp, sizeof (int32_t) *
273			    *nelems)) == NULL) {
274				ret = ETOPO_NOMEM;
275				break;
276			}
277			for (i = 0; i < *nelems; ++i)
278				a1[i] = a2[i];
279			*(int32_t **)val = a1;
280			break;
281		}
282		case TOPO_TYPE_UINT32_ARRAY: {
283			uint32_t *a1, *a2;
284
285			if ((ret = nvlist_lookup_uint32_array(pv->tp_val,
286			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
287				break;
288			if ((a1 = topo_hdl_alloc(thp, sizeof (uint32_t) *
289			    *nelems)) == NULL) {
290				ret = ETOPO_NOMEM;
291				break;
292			}
293			for (i = 0; i < *nelems; ++i)
294				a1[i] = a2[i];
295			*(uint32_t **)val = a1;
296			break;
297		}
298		case TOPO_TYPE_INT64_ARRAY: {
299			int64_t *a1, *a2;
300
301			if ((ret = nvlist_lookup_int64_array(pv->tp_val,
302			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
303				break;
304			if ((a1 = topo_hdl_alloc(thp, sizeof (int64_t) *
305			    *nelems)) == NULL) {
306				ret = ETOPO_NOMEM;
307				break;
308			}
309			for (i = 0; i < *nelems; ++i)
310				a1[i] = a2[i];
311			*(int64_t **)val = a1;
312			break;
313		}
314		case TOPO_TYPE_UINT64_ARRAY: {
315			uint64_t *a1, *a2;
316
317			if ((ret = nvlist_lookup_uint64_array(pv->tp_val,
318			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
319				break;
320			if ((a1 = topo_hdl_alloc(thp, sizeof (uint64_t) *
321			    *nelems)) == NULL) {
322				ret = ETOPO_NOMEM;
323				break;
324			}
325			for (i = 0; i < *nelems; ++i)
326				a1[i] = a2[i];
327			*(uint64_t **)val = a1;
328			break;
329		}
330		case TOPO_TYPE_STRING_ARRAY: {
331			char **a1, **a2;
332
333			if ((ret = nvlist_lookup_string_array(pv->tp_val,
334			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
335				break;
336			if ((a1 = topo_hdl_alloc(thp, sizeof (char *) *
337			    *nelems)) == NULL) {
338				ret = ETOPO_NOMEM;
339				break;
340			}
341			for (i = 0; i < *nelems; ++i) {
342				if ((a1[i] = topo_hdl_strdup(thp, a2[i]))
343				    == NULL) {
344					for (j = 0; j < i; ++j)
345						topo_hdl_free(thp, a1[j],
346						    sizeof (char *));
347					topo_hdl_free(thp, a1,
348					    sizeof (char *) * *nelems);
349					break;
350				}
351			}
352			*(char ***)val = a1;
353			break;
354		}
355		case TOPO_TYPE_FMRI_ARRAY: {
356			nvlist_t **a1, **a2;
357
358			if ((ret = nvlist_lookup_nvlist_array(pv->tp_val,
359			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
360				break;
361			if ((a1 = topo_hdl_alloc(thp, sizeof (nvlist_t *) *
362			    *nelems)) == NULL) {
363				ret = ETOPO_NOMEM;
364				break;
365			}
366			for (i = 0; i < *nelems; ++i) {
367				if (topo_hdl_nvdup(thp, a2[i], &a1[i]) < 0) {
368					for (j = 0; j < i; ++j)
369						nvlist_free(a1[j]);
370					topo_hdl_free(thp, a1,
371					    sizeof (nvlist_t *) * *nelems);
372					break;
373				}
374			}
375			*(nvlist_t ***)val = a1;
376			break;
377		}
378		default:
379			ret = ETOPO_PROP_NOENT;
380	}
381
382	if (ret != 0)
383		if (ret == ENOENT)
384			return (get_properror(node, err, ETOPO_PROP_NOENT));
385		else if (ret < ETOPO_UNKNOWN)
386			return (get_properror(node, err, ETOPO_PROP_NVL));
387		else
388			return (get_properror(node, err, ret));
389
390	topo_node_unlock(node);
391	return (0);
392}
393
394int
395topo_prop_get_int32(tnode_t *node, const char *pgname, const char *pname,
396    int32_t *val, int *err)
397{
398	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_INT32,
399	    NULL, err));
400}
401
402int
403topo_prop_get_uint32(tnode_t *node, const char *pgname, const char *pname,
404    uint32_t *val, int *err)
405{
406	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_UINT32,
407	    NULL, err));
408}
409
410int
411topo_prop_get_int64(tnode_t *node, const char *pgname, const char *pname,
412    int64_t *val, int *err)
413{
414	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_INT64,
415	    NULL, err));
416}
417
418int
419topo_prop_get_uint64(tnode_t *node, const char *pgname, const char *pname,
420    uint64_t *val, int *err)
421{
422	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_UINT64,
423	    NULL, err));
424}
425
426int
427topo_prop_get_double(tnode_t *node, const char *pgname, const char *pname,
428    double *val, int *err)
429{
430	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_DOUBLE,
431	    NULL, err));
432}
433
434int
435topo_prop_get_string(tnode_t *node, const char *pgname, const char *pname,
436    char **val, int *err)
437{
438	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_STRING,
439	    NULL, err));
440}
441
442int
443topo_prop_get_fmri(tnode_t *node, const char *pgname, const char *pname,
444    nvlist_t **val, int *err)
445{
446	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_FMRI,
447	    NULL, err));
448}
449
450int
451topo_prop_get_int32_array(tnode_t *node, const char *pgname, const char *pname,
452    int32_t **val, uint_t *nelem, int *err)
453{
454	return (prop_getval(node, pgname, pname, (void *)val,
455	    TOPO_TYPE_INT32_ARRAY, nelem, err));
456}
457
458int
459topo_prop_get_uint32_array(tnode_t *node, const char *pgname, const char *pname,
460    uint32_t **val, uint_t *nelem, int *err)
461{
462	return (prop_getval(node, pgname, pname, (void *)val,
463	    TOPO_TYPE_UINT32_ARRAY, nelem, err));
464}
465
466int
467topo_prop_get_int64_array(tnode_t *node, const char *pgname, const char *pname,
468    int64_t **val, uint_t *nelem, int *err)
469{
470	return (prop_getval(node, pgname, pname, (void *)val,
471	    TOPO_TYPE_INT64_ARRAY, nelem, err));
472}
473
474int
475topo_prop_get_uint64_array(tnode_t *node, const char *pgname, const char *pname,
476    uint64_t **val, uint_t *nelem, int *err)
477{
478	return (prop_getval(node, pgname, pname, (void *)val,
479	    TOPO_TYPE_UINT64_ARRAY, nelem, err));
480}
481
482int
483topo_prop_get_string_array(tnode_t *node, const char *pgname, const char *pname,
484    char ***val, uint_t *nelem, int *err)
485{
486	return (prop_getval(node, pgname, pname, (void *)val,
487	    TOPO_TYPE_STRING_ARRAY, nelem, err));
488}
489
490int
491topo_prop_get_fmri_array(tnode_t *node, const char *pgname, const char *pname,
492    nvlist_t ***val, uint_t *nelem, int *err)
493{
494	return (prop_getval(node, pgname, pname, (void *)val,
495	    TOPO_TYPE_FMRI_ARRAY, nelem, err));
496}
497
498static topo_propval_t *
499set_seterror(tnode_t *node, topo_proplist_t *pvl, int *errp, int err)
500{
501	topo_hdl_t *thp = node->tn_hdl;
502	topo_propval_t *pv;
503
504	if (pvl != NULL) {
505		pv = pvl->tp_pval;
506		topo_propval_destroy(pv);
507		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
508	}
509
510	topo_node_unlock(node);
511	*errp = err;
512
513	return (NULL);
514}
515
516static topo_propval_t *
517prop_create(tnode_t *node, const char *pgname, const char *pname,
518    topo_type_t type, int flag, int *err)
519{
520	topo_hdl_t *thp = node->tn_hdl;
521	topo_pgroup_t *pg;
522	topo_propval_t *pv;
523	topo_proplist_t *pvl;
524
525	/*
526	 * Replace existing prop value with new one
527	 */
528	if ((pg = pgroup_get(node, pgname)) == NULL) {
529		topo_node_unlock(node);
530		*err = ETOPO_PROP_NOENT;
531		return (NULL);
532	}
533
534	if ((pv = propval_get(pg, pname)) != NULL) {
535		if (pv->tp_type != type)
536			return (set_seterror(node, NULL, err, ETOPO_PROP_TYPE));
537		else if (! (pv->tp_flag & TOPO_PROP_MUTABLE))
538			return (set_seterror(node, NULL, err, ETOPO_PROP_DEFD));
539
540		nvlist_free(pv->tp_val);
541		pv->tp_val = NULL;
542	} else {
543		if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
544		    == NULL)
545			return (set_seterror(node, NULL, err, ETOPO_NOMEM));
546
547		if ((pv = topo_hdl_zalloc(thp, sizeof (topo_propval_t)))
548		    == NULL)
549			return (set_seterror(node, pvl, err, ETOPO_NOMEM));
550
551		pv->tp_hdl = thp;
552		pvl->tp_pval = pv;
553
554		if ((pv->tp_name = topo_hdl_strdup(thp, pname))
555		    == NULL)
556			return (set_seterror(node, pvl, err, ETOPO_NOMEM));
557		pv->tp_flag = flag;
558		pv->tp_type = type;
559		topo_prop_hold(pv);
560		topo_list_append(&pg->tpg_pvals, pvl);
561	}
562
563	return (pv);
564}
565
566static int
567topo_prop_set(tnode_t *node, const char *pgname, const char *pname,
568    topo_type_t type, int flag, void *val, int nelems, int *err)
569{
570	int ret;
571	topo_hdl_t *thp = node->tn_hdl;
572	nvlist_t *nvl;
573
574	if (topo_hdl_nvalloc(thp, &nvl, NV_UNIQUE_NAME) < 0) {
575		*err = ETOPO_PROP_NVL;
576		return (-1);
577	}
578
579	ret = nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, pname);
580	ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, type);
581	switch (type) {
582		case TOPO_TYPE_INT32:
583			ret |= nvlist_add_int32(nvl, TOPO_PROP_VAL_VAL,
584			    *(int32_t *)val);
585			break;
586		case TOPO_TYPE_UINT32:
587			ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL,
588			    *(uint32_t *)val);
589			break;
590		case TOPO_TYPE_INT64:
591			ret |= nvlist_add_int64(nvl, TOPO_PROP_VAL_VAL,
592			    *(int64_t *)val);
593			break;
594		case TOPO_TYPE_UINT64:
595			ret |= nvlist_add_uint64(nvl, TOPO_PROP_VAL_VAL,
596			    *(uint64_t *)val);
597			break;
598		case TOPO_TYPE_DOUBLE:
599			ret |= nvlist_add_double(nvl, TOPO_PROP_VAL_VAL,
600			    *(double *)val);
601			break;
602		case TOPO_TYPE_STRING:
603			ret |= nvlist_add_string(nvl, TOPO_PROP_VAL_VAL,
604			    (char *)val);
605			break;
606		case TOPO_TYPE_FMRI:
607			ret |= nvlist_add_nvlist(nvl, TOPO_PROP_VAL_VAL,
608			    (nvlist_t *)val);
609			break;
610		case TOPO_TYPE_INT32_ARRAY:
611			ret |= nvlist_add_int32_array(nvl,
612			    TOPO_PROP_VAL_VAL, (int32_t *)val, nelems);
613			break;
614		case TOPO_TYPE_UINT32_ARRAY:
615			ret |= nvlist_add_uint32_array(nvl,
616			    TOPO_PROP_VAL_VAL, (uint32_t *)val, nelems);
617			break;
618		case TOPO_TYPE_INT64_ARRAY:
619			ret |= nvlist_add_int64_array(nvl,
620			    TOPO_PROP_VAL_VAL, (int64_t *)val, nelems);
621			break;
622		case TOPO_TYPE_UINT64_ARRAY:
623			ret |= nvlist_add_uint64_array(nvl,
624			    TOPO_PROP_VAL_VAL, (uint64_t *)val, nelems);
625			break;
626		case TOPO_TYPE_STRING_ARRAY:
627			ret |= nvlist_add_string_array(nvl,
628			    TOPO_PROP_VAL_VAL, (char **)val, nelems);
629			break;
630		case TOPO_TYPE_FMRI_ARRAY:
631			ret |= nvlist_add_nvlist_array(nvl,
632			    TOPO_PROP_VAL_VAL, (nvlist_t **)val, nelems);
633			break;
634		default:
635			*err = ETOPO_PROP_TYPE;
636			return (-1);
637	}
638
639	if (ret != 0) {
640		nvlist_free(nvl);
641		if (ret == ENOMEM) {
642			*err = ETOPO_PROP_NOMEM;
643			return (-1);
644		} else {
645			*err = ETOPO_PROP_NVL;
646			return (-1);
647		}
648	}
649
650	if (topo_prop_setprop(node, pgname, nvl, flag, nvl, err) != 0) {
651		nvlist_free(nvl);
652		return (-1); /* err set */
653	}
654	nvlist_free(nvl);
655	return (ret);
656}
657
658int
659topo_prop_set_int32(tnode_t *node, const char *pgname, const char *pname,
660    int flag, int32_t val, int *err)
661{
662	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32, flag,
663	    &val, 1, err));
664}
665
666int
667topo_prop_set_uint32(tnode_t *node, const char *pgname, const char *pname,
668    int flag, uint32_t val, int *err)
669{
670	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32, flag,
671	    &val, 1, err));
672}
673
674int
675topo_prop_set_int64(tnode_t *node, const char *pgname, const char *pname,
676    int flag, int64_t val, int *err)
677{
678	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64, flag,
679	    &val, 1, err));
680}
681
682int
683topo_prop_set_uint64(tnode_t *node, const char *pgname, const char *pname,
684    int flag, uint64_t val, int *err)
685{
686	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64, flag,
687	    &val, 1, err));
688}
689
690int
691topo_prop_set_double(tnode_t *node, const char *pgname, const char *pname,
692    int flag, double val, int *err)
693{
694	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_DOUBLE, flag,
695	    &val, 1, err));
696}
697
698int
699topo_prop_set_string(tnode_t *node, const char *pgname, const char *pname,
700    int flag, const char *val, int *err)
701{
702	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING, flag,
703	    (void *)val, 1, err));
704}
705
706int
707topo_prop_set_fmri(tnode_t *node, const char *pgname, const char *pname,
708    int flag, const nvlist_t *fmri, int *err)
709{
710	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI, flag,
711	    (void *)fmri, 1, err));
712}
713
714int
715topo_prop_set_int32_array(tnode_t *node, const char *pgname, const char *pname,
716    int flag, int32_t *val, uint_t nelems, int *err)
717{
718	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32_ARRAY, flag,
719	    val, nelems, err));
720}
721
722int
723topo_prop_set_uint32_array(tnode_t *node, const char *pgname, const char *pname,
724    int flag, uint32_t *val, uint_t nelems, int *err)
725{
726	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32_ARRAY, flag,
727	    val, nelems, err));
728}
729
730int
731topo_prop_set_int64_array(tnode_t *node, const char *pgname, const char *pname,
732    int flag, int64_t *val, uint_t nelems, int *err)
733{
734	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64_ARRAY, flag,
735	    val, nelems, err));
736}
737
738int
739topo_prop_set_uint64_array(tnode_t *node, const char *pgname, const char *pname,
740    int flag, uint64_t *val, uint_t nelems, int *err)
741{
742	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64_ARRAY, flag,
743	    val, nelems, err));
744}
745
746int
747topo_prop_set_string_array(tnode_t *node, const char *pgname, const char *pname,
748    int flag, const char **val, uint_t nelems, int *err)
749{
750	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING_ARRAY, flag,
751	    (void *)val, nelems, err));
752}
753
754int
755topo_prop_set_fmri_array(tnode_t *node, const char *pgname, const char *pname,
756    int flag, const nvlist_t **fmri, uint_t nelems, int *err)
757{
758	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI_ARRAY, flag,
759	    (void *)fmri, nelems, err));
760}
761
762/*
763 * topo_prop_setprop() is a private project function for fmtopo
764 */
765int
766topo_prop_setprop(tnode_t *node, const char *pgname, nvlist_t *prop,
767    int flag, nvlist_t *pargs, int *err)
768{
769	int ret;
770	topo_hdl_t *thp = node->tn_hdl;
771	topo_propval_t *pv;
772	nvlist_t *nvl, *args;
773	char *name;
774	topo_type_t type;
775
776	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_NAME, &name) != 0) {
777		*err = ETOPO_PROP_NAME;
778		return (-1);
779	}
780	if (nvlist_lookup_uint32(prop, TOPO_PROP_VAL_TYPE, (uint32_t *)&type)
781	    != 0) {
782		*err = ETOPO_PROP_TYPE;
783		return (-1);
784	}
785
786	topo_node_lock(node);
787	if ((pv = prop_create(node, pgname, name, type, flag, err)) == NULL)
788		return (-1); /* unlocked and err set */
789
790	/*
791	 * Set by method or set to new prop value.  If we fail, leave
792	 * property in list with old value.
793	 */
794	if (pv->tp_method != NULL) {
795		topo_propmethod_t *pm = pv->tp_method;
796
797		if (topo_hdl_nvalloc(pv->tp_hdl, &args, NV_UNIQUE_NAME) < 0) {
798			topo_node_unlock(node);
799			*err = ETOPO_PROP_NOMEM;
800			return (-1);
801		}
802		ret = nvlist_add_nvlist(args, TOPO_PROP_ARGS, pm->tpm_args);
803		if (pargs != NULL)
804			ret |= nvlist_add_nvlist(args, TOPO_PROP_PARGS, pargs);
805
806		if (ret != 0) {
807			topo_node_unlock(node);
808			nvlist_free(args);
809			*err = ETOPO_PROP_NVL;
810			return (-1);
811		}
812
813		/*
814		 *
815		 * Grab a reference to the property and then unlock the node.
816		 * This will allow property methods to safely re-enter the
817		 * prop_get codepath, making it possible for property methods
818		 * to access other property values on the same node w\o causing
819		 * a deadlock.
820		 *
821		 * We don't technically need this now, since this interface is
822		 * currently only used by fmtopo (which is single-threaded), but
823		 * we may make this interface available to other parts of
824		 * libtopo in the future, so best to make it MT-safe now.
825		 */
826		topo_prop_hold(pv);
827		topo_node_unlock(node);
828		ret = topo_method_call(node, pm->tpm_name, pm->tpm_version,
829		    args, &nvl, err);
830		topo_node_lock(node);
831		topo_prop_rele(pv);
832
833		nvlist_free(args);
834	} else {
835		if ((ret = topo_hdl_nvdup(thp, prop, &nvl)) != 0)
836			*err = ETOPO_PROP_NOMEM;
837	}
838
839	if (ret != 0) {
840		topo_node_unlock(node);
841		return (-1);
842	}
843
844	pv->tp_val = nvl;
845	topo_node_unlock(node);
846	return (0);
847}
848
849static int
850register_methoderror(tnode_t *node, topo_propmethod_t *pm, int *errp, int l,
851    int err)
852{
853	topo_hdl_t *thp = node->tn_hdl;
854
855	if (pm != NULL) {
856		if (pm->tpm_name != NULL)
857			topo_hdl_strfree(thp, pm->tpm_name);
858		if (pm->tpm_args != NULL)
859			nvlist_free(pm->tpm_args);
860		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
861	}
862
863	*errp = err;
864
865	if (l != 0)
866		topo_node_unlock(node);
867
868	return (-1);
869}
870
871int
872prop_method_register(tnode_t *node, const char *pgname, const char *pname,
873    topo_type_t ptype, const char *mname, topo_version_t version,
874    const nvlist_t *args, int *err)
875{
876	topo_hdl_t *thp = node->tn_hdl;
877	topo_propmethod_t *pm = NULL;
878	topo_propval_t *pv = NULL;
879
880	if ((pm = topo_hdl_zalloc(thp, sizeof (topo_propmethod_t))) == NULL)
881		return (register_methoderror(node, pm, err, 1,
882		    ETOPO_PROP_NOMEM));
883
884	if ((pm->tpm_name = topo_hdl_strdup(thp, mname)) == NULL)
885		return (register_methoderror(node, pm, err, 1,
886		    ETOPO_PROP_NOMEM));
887
888	pm->tpm_version = version;
889
890	if (topo_hdl_nvdup(thp, (nvlist_t *)args, &pm->tpm_args) != 0)
891		return (register_methoderror(node, pm, err, 1,
892		    ETOPO_PROP_NOMEM));
893
894	/*
895	 * It's possible the property may already exist.  However we still want
896	 * to allow the method to be registered.  This is to handle the case
897	 * where we specify a prop method in an xml map to override the value
898	 * that was set by the enumerator.
899	 *
900	 * By default, propmethod-backed properties are not MUTABLE.  This is
901	 * done to simplify the programming model for modules that implement
902	 * property methods as most propmethods tend to only support get
903	 * operations.  Enumerator modules can override this by calling
904	 * topo_prop_setmutable().  Propmethods that are registered via XML can
905	 * be set as mutable via the optional "mutable" attribute, which will
906	 * result in the xml parser calling topo_prop_setflags() after
907	 * registering the propmethod.
908	 */
909	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL)
910		if ((pv = prop_create(node, pgname, pname, ptype,
911		    TOPO_PROP_IMMUTABLE, err)) == NULL) {
912			/* node unlocked */
913			return (register_methoderror(node, pm, err, 0, *err));
914		}
915
916	if (pv->tp_method != NULL)
917		return (register_methoderror(node, pm, err, 1,
918		    ETOPO_METHOD_DEFD));
919
920	if (pv->tp_val != NULL) {
921		nvlist_free(pv->tp_val);
922		pv->tp_val = NULL;
923	}
924	pv->tp_method = pm;
925
926	topo_node_unlock(node);
927
928	return (0);
929}
930
931int
932topo_prop_method_register(tnode_t *node, const char *pgname, const char *pname,
933    topo_type_t ptype, const char *mname, const nvlist_t *args, int *err)
934{
935	topo_imethod_t *mp;
936
937	topo_node_lock(node);
938
939	if ((mp = topo_method_lookup(node, mname)) == NULL)
940		return (register_methoderror(node, NULL, err, 1,
941		    ETOPO_METHOD_NOTSUP)); /* node unlocked */
942
943	topo_node_lock(node);
944
945	return (prop_method_register(node, pgname, pname, ptype, mname,
946	    mp->tim_version, args, err)); /* err set and node unlocked */
947}
948
949int
950topo_prop_method_version_register(tnode_t *node, const char *pgname,
951    const char *pname, topo_type_t ptype, const char *mname,
952    topo_version_t version, const nvlist_t *args, int *err)
953{
954	topo_imethod_t *mp;
955
956	topo_node_lock(node);
957
958	if ((mp = topo_method_lookup(node, mname)) == NULL)
959		return (register_methoderror(node, NULL, err, 1,
960		    ETOPO_METHOD_NOTSUP)); /* node unlocked */
961
962	topo_node_lock(node);
963
964	if (version < mp->tim_version)
965		return (register_methoderror(node, NULL, err, 1,
966		    ETOPO_METHOD_VEROLD));
967	if (version > mp->tim_version)
968		return (register_methoderror(node, NULL, err, 1,
969		    ETOPO_METHOD_VERNEW));
970
971	return (prop_method_register(node, pgname, pname, ptype, mname,
972	    version, args, err)); /* err set and node unlocked */
973}
974
975void
976topo_prop_method_unregister(tnode_t *node, const char *pgname,
977    const char *pname)
978{
979	topo_propval_t *pv;
980	topo_pgroup_t *pg;
981	topo_proplist_t *pvl;
982	topo_hdl_t *thp = node->tn_hdl;
983
984	topo_node_lock(node);
985
986	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
987	    pg = topo_list_next(pg)) {
988		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
989			break;
990		}
991	}
992
993	if (pg == NULL) {
994		topo_node_unlock(node);
995		return;
996	}
997
998	for (pvl = topo_list_next(&pg->tpg_list); pvl != NULL;
999	    pvl = topo_list_next(pvl)) {
1000		pv = pvl->tp_pval;
1001		if (strcmp(pv->tp_name, pname) == 0) {
1002			topo_list_delete(&pg->tpg_pvals, pvl);
1003			assert(pv->tp_refs == 1);
1004			topo_prop_rele(pv);
1005			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1006			break;
1007		}
1008	}
1009
1010	topo_node_unlock(node);
1011}
1012
1013int
1014topo_prop_setmutable(tnode_t *node, const char *pgname, const char *pname,
1015    int *err)
1016{
1017	topo_propval_t *pv = NULL;
1018
1019	topo_node_lock(node);
1020	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
1021		topo_node_unlock(node);
1022		*err = ETOPO_PROP_NOENT;
1023		return (-1);
1024	}
1025
1026	/*
1027	 * If the property is being inherited then we don't want to allow a
1028	 * change from IMMUTABLE to MUTABLE.
1029	 */
1030	if (pv->tp_refs > 1) {
1031		topo_node_unlock(node);
1032		*err = ETOPO_PROP_DEFD;
1033		return (-1);
1034	}
1035	pv->tp_flag |= TOPO_PROP_MUTABLE;
1036
1037	topo_node_unlock(node);
1038
1039	return (0);
1040}
1041int
1042topo_prop_setnonvolatile(tnode_t *node, const char *pgname, const char *pname,
1043    int *err)
1044{
1045	topo_propval_t *pv = NULL;
1046
1047	topo_node_lock(node);
1048	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
1049		topo_node_unlock(node);
1050		*err = ETOPO_PROP_NOENT;
1051		return (-1);
1052	}
1053
1054	pv->tp_flag |= TOPO_PROP_NONVOLATILE;
1055
1056	topo_node_unlock(node);
1057
1058	return (0);
1059}
1060
1061static int
1062inherit_seterror(tnode_t *node, int *errp, int err)
1063{
1064	topo_node_unlock(node);
1065	topo_node_unlock(node->tn_parent);
1066
1067	*errp = err;
1068
1069	return (-1);
1070}
1071
1072int
1073topo_prop_inherit(tnode_t *node, const char *pgname, const char *name, int *err)
1074{
1075	topo_hdl_t *thp = node->tn_hdl;
1076	tnode_t *pnode = node->tn_parent;
1077	topo_pgroup_t *pg;
1078	topo_propval_t *pv;
1079	topo_proplist_t *pvl;
1080
1081	topo_node_lock(pnode);
1082	topo_node_lock(node);
1083
1084	/*
1085	 * Check if the requested property group and prop val are already set
1086	 * on the node.
1087	 */
1088	if (propval_get(pgroup_get(node, pgname), name) != NULL)
1089		return (inherit_seterror(node, err, ETOPO_PROP_DEFD));
1090
1091	/*
1092	 * Check if the requested property group and prop val exists on the
1093	 * parent node
1094	 */
1095	if ((pv = propval_get(pgroup_get(pnode, pgname), name)) == NULL)
1096		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
1097
1098	/*
1099	 * Can this propval be inherited?
1100	 */
1101	if (pv->tp_flag & TOPO_PROP_MUTABLE)
1102		return (inherit_seterror(node, err, ETOPO_PROP_NOINHERIT));
1103
1104	/*
1105	 * Property group should already exist: bump the ref count for this
1106	 * propval and add it to the node's property group
1107	 */
1108	if ((pg = pgroup_get(node, pgname)) == NULL)
1109		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
1110
1111	if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
1112	    == NULL)
1113		return (inherit_seterror(node, err, ETOPO_NOMEM));
1114
1115	topo_prop_hold(pv);
1116	pvl->tp_pval = pv;
1117	topo_list_append(&pg->tpg_pvals, pvl);
1118
1119	topo_node_unlock(node);
1120	topo_node_unlock(pnode);
1121
1122	return (0);
1123}
1124
1125topo_pgroup_info_t *
1126topo_pgroup_info(tnode_t *node, const char *pgname, int *err)
1127{
1128	topo_hdl_t *thp = node->tn_hdl;
1129	topo_pgroup_t *pg;
1130	topo_ipgroup_info_t *pip;
1131	topo_pgroup_info_t *info;
1132
1133	topo_node_lock(node);
1134	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1135	    pg = topo_list_next(pg)) {
1136		if (strcmp(pgname, pg->tpg_info->tpi_name) == 0) {
1137			if ((info = topo_hdl_alloc(thp,
1138			    sizeof (topo_pgroup_info_t))) == NULL)
1139				return (NULL);
1140
1141			pip = pg->tpg_info;
1142			if ((info->tpi_name =
1143			    topo_hdl_strdup(thp, pip->tpi_name)) == NULL) {
1144				*err = ETOPO_PROP_NOMEM;
1145				topo_hdl_free(thp, info,
1146				    sizeof (topo_pgroup_info_t));
1147				topo_node_unlock(node);
1148				return (NULL);
1149			}
1150			info->tpi_namestab = pip->tpi_namestab;
1151			info->tpi_datastab = pip->tpi_datastab;
1152			info->tpi_version = pip->tpi_version;
1153			topo_node_unlock(node);
1154			return (info);
1155		}
1156	}
1157
1158	*err = ETOPO_PROP_NOENT;
1159	topo_node_unlock(node);
1160	return (NULL);
1161}
1162
1163static int
1164pgroup_seterr(tnode_t *node, topo_pgroup_t *pg, topo_ipgroup_info_t *pip,
1165    int *err)
1166{
1167	topo_hdl_t *thp = node->tn_hdl;
1168
1169	if (pip != NULL) {
1170		if (pip->tpi_name != NULL)
1171			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1172		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1173	}
1174
1175	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1176	*err = ETOPO_NOMEM;
1177
1178	topo_node_unlock(node);
1179
1180	return (-1);
1181}
1182
1183int
1184topo_pgroup_create(tnode_t *node, const topo_pgroup_info_t *pinfo, int *err)
1185{
1186	topo_pgroup_t *pg;
1187	topo_ipgroup_info_t *pip;
1188	topo_hdl_t *thp = node->tn_hdl;
1189
1190	*err = 0;
1191
1192	topo_node_lock(node);
1193	/*
1194	 * Check for an existing pgroup
1195	 */
1196	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1197	    pg = topo_list_next(pg)) {
1198		if (strcmp(pg->tpg_info->tpi_name, pinfo->tpi_name) == 0) {
1199			*err = ETOPO_PROP_DEFD;
1200			topo_node_unlock(node);
1201			return (-1);
1202		}
1203	}
1204
1205	if ((pg = topo_hdl_zalloc(thp, sizeof (topo_pgroup_t))) == NULL) {
1206		*err = ETOPO_NOMEM;
1207		topo_node_unlock(node);
1208		return (-1);
1209	}
1210
1211	if ((pip = topo_hdl_zalloc(thp, sizeof (topo_ipgroup_info_t)))
1212	    == NULL)
1213		return (pgroup_seterr(node, pg, pip, err));
1214
1215	if ((pip->tpi_name = topo_hdl_strdup(thp, pinfo->tpi_name))
1216	    == NULL)
1217		return (pgroup_seterr(node, pg, pip, err));
1218
1219	pip->tpi_namestab = pinfo->tpi_namestab;
1220	pip->tpi_datastab = pinfo->tpi_datastab;
1221	pip->tpi_version = pinfo->tpi_version;
1222
1223	pg->tpg_info = pip;
1224
1225	topo_list_append(&node->tn_pgroups, pg);
1226	topo_node_unlock(node);
1227
1228	return (0);
1229}
1230
1231void
1232topo_pgroup_destroy(tnode_t *node, const char *pname)
1233{
1234	topo_hdl_t *thp = node->tn_hdl;
1235	topo_pgroup_t *pg;
1236	topo_proplist_t *pvl;
1237	topo_ipgroup_info_t *pip;
1238
1239	topo_node_lock(node);
1240	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1241	    pg = topo_list_next(pg)) {
1242		if (strcmp(pg->tpg_info->tpi_name, pname) == 0) {
1243			break;
1244		}
1245	}
1246
1247	if (pg == NULL) {
1248		topo_node_unlock(node);
1249		return;
1250	}
1251
1252	while ((pvl = topo_list_next(&pg->tpg_list)) != NULL) {
1253		topo_list_delete(&pg->tpg_pvals, pvl);
1254		topo_prop_rele(pvl->tp_pval);
1255		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1256	}
1257
1258	topo_list_delete(&node->tn_pgroups, pg);
1259	topo_node_unlock(node);
1260
1261	pip = pg->tpg_info;
1262	if (pip != NULL) {
1263		if (pip->tpi_name != NULL)
1264			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1265		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1266	}
1267
1268	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1269}
1270
1271void
1272topo_pgroup_destroy_all(tnode_t *node)
1273{
1274	topo_hdl_t *thp = node->tn_hdl;
1275	topo_pgroup_t *pg;
1276	topo_proplist_t *pvl;
1277	topo_ipgroup_info_t *pip;
1278
1279	topo_node_lock(node);
1280	while ((pg = topo_list_next(&node->tn_pgroups)) != NULL) {
1281		while ((pvl = topo_list_next(&pg->tpg_pvals)) != NULL) {
1282			topo_list_delete(&pg->tpg_pvals, pvl);
1283			topo_prop_rele(pvl->tp_pval);
1284			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1285		}
1286
1287		topo_list_delete(&node->tn_pgroups, pg);
1288
1289		pip = pg->tpg_info;
1290		if (pip != NULL) {
1291			if (pip->tpi_name != NULL)
1292				topo_hdl_strfree(thp, (char *)pip->tpi_name);
1293			topo_hdl_free(thp, pip, sizeof (topo_pgroup_info_t));
1294		}
1295
1296		topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1297	}
1298	topo_node_unlock(node);
1299}
1300
1301static void
1302propmethod_destroy(topo_hdl_t *thp, topo_propval_t *pv)
1303{
1304	topo_propmethod_t *pm;
1305
1306	pm = pv->tp_method;
1307	if (pm != NULL) {
1308		if (pm->tpm_name != NULL)
1309			topo_hdl_strfree(thp, pm->tpm_name);
1310		if (pm->tpm_args != NULL)
1311			nvlist_free(pm->tpm_args);
1312		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
1313		pv->tp_method = NULL;
1314	}
1315}
1316
1317static void
1318topo_propval_destroy(topo_propval_t *pv)
1319{
1320	topo_hdl_t *thp;
1321
1322	if (pv == NULL)
1323		return;
1324
1325	thp = pv->tp_hdl;
1326
1327	if (pv->tp_name != NULL)
1328		topo_hdl_strfree(thp, pv->tp_name);
1329
1330	if (pv->tp_val != NULL)
1331		nvlist_free(pv->tp_val);
1332
1333	propmethod_destroy(thp, pv);
1334
1335	topo_hdl_free(thp, pv, sizeof (topo_propval_t));
1336}
1337
1338void
1339topo_prop_hold(topo_propval_t *pv)
1340{
1341	pv->tp_refs++;
1342}
1343
1344void
1345topo_prop_rele(topo_propval_t *pv)
1346{
1347	pv->tp_refs--;
1348
1349	assert(pv->tp_refs >= 0);
1350
1351	if (pv->tp_refs == 0)
1352		topo_propval_destroy(pv);
1353}
1354
1355/*
1356 * topo_prop_getprop() and topo_prop_getprops() are private project functions
1357 * for fmtopo
1358 */
1359int
1360topo_prop_getprop(tnode_t *node, const char *pgname, const char *pname,
1361    nvlist_t *args, nvlist_t **prop, int *err)
1362{
1363	topo_hdl_t *thp = node->tn_hdl;
1364	topo_propval_t *pv;
1365
1366	topo_node_lock(node);
1367	if ((pv = prop_get(node, pgname, pname, args, err)) == NULL) {
1368		(void) get_properror(node, err, *err);
1369		return (-1);
1370	}
1371
1372	if (topo_hdl_nvdup(thp, pv->tp_val, prop) != 0) {
1373		(void) get_properror(node, err, ETOPO_NOMEM);
1374		return (-1);
1375	}
1376	topo_node_unlock(node);
1377
1378	return (0);
1379}
1380
1381static int
1382prop_val_add(tnode_t *node, nvlist_t **nvl, topo_propval_t *pv, int *err)
1383{
1384	if (pv->tp_method != NULL)
1385		if (prop_method_get(node, pv, pv->tp_method, NULL, err) < 0)
1386			return (-1);
1387
1388	if (pv->tp_val == NULL) {
1389		*err = ETOPO_PROP_NOENT;
1390		return (-1);
1391	}
1392
1393	if (topo_hdl_nvdup(pv->tp_hdl, pv->tp_val, nvl) != 0) {
1394		*err = ETOPO_PROP_NOMEM;
1395		return (-1);
1396	}
1397
1398	return (0);
1399}
1400
1401static int
1402get_pgrp_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1403{
1404	topo_node_unlock(node);
1405
1406	if (nvl != NULL)
1407		nvlist_free(nvl);
1408
1409	*errp = err;
1410
1411	return (-1);
1412}
1413
1414int
1415topo_prop_getpgrp(tnode_t *node, const char *pgname, nvlist_t **pgrp,
1416    int *err)
1417{
1418	int ret;
1419	topo_hdl_t *thp = node->tn_hdl;
1420	nvlist_t *nvl, *pvnvl;
1421	topo_pgroup_t *pg;
1422	topo_propval_t *pv;
1423	topo_proplist_t *pvl;
1424
1425	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1426		*err = ETOPO_NOMEM;
1427		return (-1);
1428	}
1429
1430	topo_node_lock(node);
1431	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1432	    pg = topo_list_next(pg)) {
1433
1434		if (strcmp(pgname, pg->tpg_info->tpi_name) != 0)
1435			continue;
1436
1437		if (nvlist_add_string(nvl, TOPO_PROP_GROUP_NAME,
1438		    pg->tpg_info->tpi_name) != 0 ||
1439		    nvlist_add_string(nvl, TOPO_PROP_GROUP_NSTAB,
1440		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1441		    nvlist_add_string(nvl, TOPO_PROP_GROUP_DSTAB,
1442		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1443		    nvlist_add_int32(nvl, TOPO_PROP_GROUP_VERSION,
1444		    pg->tpg_info->tpi_version) != 0)
1445			return (get_pgrp_seterror(node, nvl, err,
1446			    ETOPO_PROP_NVL));
1447
1448		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1449		    pvl = topo_list_next(pvl)) {
1450
1451			pv = pvl->tp_pval;
1452			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1453				return (get_pgrp_seterror(node, nvl, err,
1454				    *err));
1455			}
1456			if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_VAL,
1457			    pvnvl)) != 0) {
1458				nvlist_free(pvnvl);
1459				return (get_pgrp_seterror(node, nvl, err, ret));
1460			}
1461
1462			nvlist_free(pvnvl);
1463		}
1464		topo_node_unlock(node);
1465		*pgrp = nvl;
1466		return (0);
1467	}
1468
1469	topo_node_unlock(node);
1470	*err = ETOPO_PROP_NOENT;
1471	return (-1);
1472}
1473
1474static nvlist_t *
1475get_all_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1476{
1477	topo_node_unlock(node);
1478
1479	if (nvl != NULL)
1480		nvlist_free(nvl);
1481
1482	*errp = err;
1483
1484	return (NULL);
1485}
1486
1487nvlist_t *
1488topo_prop_getprops(tnode_t *node, int *err)
1489{
1490	int ret;
1491	topo_hdl_t *thp = node->tn_hdl;
1492	nvlist_t *nvl, *pgnvl, *pvnvl;
1493	topo_pgroup_t *pg;
1494	topo_propval_t *pv;
1495	topo_proplist_t *pvl;
1496
1497	topo_node_lock(node);
1498	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1499		return (get_all_seterror(node, NULL, err, ETOPO_NOMEM));
1500	}
1501
1502	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1503	    pg = topo_list_next(pg)) {
1504		if (topo_hdl_nvalloc(thp, &pgnvl, 0) != 0)
1505			return (get_all_seterror(node, nvl, err, ETOPO_NOMEM));
1506
1507		if (nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NAME,
1508		    pg->tpg_info->tpi_name) != 0 ||
1509		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NSTAB,
1510		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1511		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_DSTAB,
1512		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1513		    nvlist_add_int32(pgnvl, TOPO_PROP_GROUP_VERSION,
1514		    pg->tpg_info->tpi_version) != 0)
1515			return (get_all_seterror(node, nvl, err,
1516			    ETOPO_PROP_NVL));
1517
1518		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1519		    pvl = topo_list_next(pvl)) {
1520
1521			pv = pvl->tp_pval;
1522			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1523				nvlist_free(pgnvl);
1524				return (get_all_seterror(node, nvl, err, *err));
1525			}
1526			if ((ret = nvlist_add_nvlist(pgnvl, TOPO_PROP_VAL,
1527			    pvnvl)) != 0) {
1528				nvlist_free(pgnvl);
1529				nvlist_free(pvnvl);
1530				return (get_all_seterror(node, nvl, err, ret));
1531			}
1532
1533			nvlist_free(pvnvl);
1534		}
1535		if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_GROUP, pgnvl))
1536		    != 0) {
1537			nvlist_free(pgnvl);
1538			return (get_all_seterror(node, nvl, err, ret));
1539		}
1540
1541		nvlist_free(pgnvl);
1542	}
1543
1544	topo_node_unlock(node);
1545
1546	return (nvl);
1547}
1548