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