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