xref: /illumos-gate/usr/src/cmd/svc/svccfg/svccfg_internal.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <assert.h>
28 #include <errno.h>
29 #include <libintl.h>
30 #include <libuutil.h>
31 #include <stdarg.h>
32 #include <stddef.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <libscf_priv.h>
36 
37 #include "svccfg.h"
38 
39 /*
40  * Internal representation manipulation routines for svccfg(1)
41  */
42 
43 static uu_list_pool_t	*entity_pool;
44 static uu_list_pool_t	*pgroup_pool;
45 static uu_list_pool_t	*property_pool;
46 static uu_list_pool_t	*value_pool;
47 
48 /* ARGSUSED */
49 static int
50 entity_cmp(const void *a, const void *b, void *p)
51 {
52 	entity_t *A = (entity_t *)a;
53 	entity_t *B = (entity_t *)b;
54 
55 	return (strcmp(A->sc_name, B->sc_name));
56 }
57 
58 /*ARGSUSED*/
59 static int
60 pgroup_cmp(const void *a, const void *b, void *p)
61 {
62 	pgroup_t *A = (pgroup_t *)a;
63 	pgroup_t *B = (pgroup_t *)b;
64 
65 	return (strcmp(A->sc_pgroup_name, B->sc_pgroup_name));
66 }
67 
68 /* ARGSUSED */
69 static int
70 property_cmp(const void *a, const void *b, void *p)
71 {
72 	property_t *A = (property_t *)a;
73 	property_t *B = (property_t *)b;
74 
75 	return (strcmp(A->sc_property_name, B->sc_property_name));
76 }
77 
78 /* ARGSUSED */
79 int
80 value_cmp(const void *a, const void *b, void *p)
81 {
82 	const value_t *A = a;
83 	const value_t *B = b;
84 
85 	if (A->sc_type != B->sc_type)
86 		return (B->sc_type - A->sc_type);
87 
88 	switch (A->sc_type) {
89 	case SCF_TYPE_BOOLEAN:
90 	case SCF_TYPE_COUNT:
91 		return (B->sc_u.sc_count - A->sc_u.sc_count);
92 
93 	case SCF_TYPE_INTEGER:
94 		return (B->sc_u.sc_integer - A->sc_u.sc_integer);
95 
96 	default:
97 		return (strcmp(A->sc_u.sc_string, B->sc_u.sc_string));
98 	}
99 }
100 
101 void
102 internal_init()
103 {
104 	if ((entity_pool = uu_list_pool_create("entities", sizeof (entity_t),
105 	    offsetof(entity_t, sc_node), entity_cmp, 0)) == NULL)
106 		uu_die(gettext("entity list pool creation failed: %s\n"),
107 		    uu_strerror(uu_error()));
108 
109 	if ((pgroup_pool = uu_list_pool_create("property_groups",
110 	    sizeof (pgroup_t), offsetof(pgroup_t, sc_node), pgroup_cmp, 0)) ==
111 	    NULL)
112 		uu_die(
113 		    gettext("property group list pool creation failed: %s\n"),
114 		    uu_strerror(uu_error()));
115 
116 	if ((property_pool = uu_list_pool_create("properties",
117 	    sizeof (property_t), offsetof(property_t, sc_node), property_cmp,
118 	    0)) == NULL)
119 		uu_die(gettext("property list pool creation failed: %s\n"),
120 		    uu_strerror(uu_error()));
121 
122 	if ((value_pool = uu_list_pool_create("property_values",
123 	    sizeof (value_t), offsetof(value_t, sc_node), value_cmp, 0)) ==
124 	    NULL)
125 		uu_die(
126 		    gettext("property value list pool creation failed: %s\n"),
127 		    uu_strerror(uu_error()));
128 }
129 
130 /*ARGSUSED*/
131 static int
132 internal_value_dump(void *v, void *pvt)
133 {
134 	value_t *val = v;
135 
136 	switch (val->sc_type) {
137 	case SCF_TYPE_BOOLEAN:
138 		(void) printf("	value = %s\n",
139 		    val->sc_u.sc_count ? "true" : "false");
140 		break;
141 	case SCF_TYPE_COUNT:
142 		(void) printf("	value = %llu\n", val->sc_u.sc_count);
143 		break;
144 	case SCF_TYPE_INTEGER:
145 		(void) printf("	value = %lld\n", val->sc_u.sc_integer);
146 		break;
147 	case SCF_TYPE_ASTRING:
148 	case SCF_TYPE_FMRI:
149 	case SCF_TYPE_HOST:
150 	case SCF_TYPE_HOSTNAME:
151 	case SCF_TYPE_NET_ADDR_V4:
152 	case SCF_TYPE_NET_ADDR_V6:
153 	case SCF_TYPE_OPAQUE:
154 	case SCF_TYPE_TIME:
155 	case SCF_TYPE_URI:
156 	case SCF_TYPE_USTRING:
157 		(void) printf("	value = %s\n",
158 		    val->sc_u.sc_string ? val->sc_u.sc_string : "(nil)");
159 		break;
160 	default:
161 		uu_die(gettext("unknown value type (%d)\n"), val->sc_type);
162 		break;
163 	}
164 
165 	return (UU_WALK_NEXT);
166 }
167 
168 /*ARGSUSED*/
169 static int
170 internal_property_dump(void *v, void *pvt)
171 {
172 	property_t *p = v;
173 
174 	(void) printf("property\n	name = %s\n", p->sc_property_name);
175 	(void) printf("	type = %d\n", p->sc_value_type);
176 
177 	(void) uu_list_walk(p->sc_property_values, internal_value_dump,
178 	    NULL, UU_DEFAULT);
179 
180 	return (UU_WALK_NEXT);
181 }
182 
183 /*ARGSUSED*/
184 static int
185 internal_pgroup_dump(void *v, void *pvt)
186 {
187 	pgroup_t *pg = v;
188 
189 	(void) printf("pgroup	name = %s\n", pg->sc_pgroup_name);
190 	(void) printf("	type = %s\n", pg->sc_pgroup_type);
191 
192 	(void) uu_list_walk(pg->sc_pgroup_props, internal_property_dump,
193 	    NULL, UU_DEFAULT);
194 
195 	return (UU_WALK_NEXT);
196 }
197 
198 /*ARGSUSED*/
199 static int
200 internal_instance_dump(void *v, void *pvt)
201 {
202 	entity_t *i = v;
203 
204 	(void) printf("instance	name = %s\n", i->sc_name);
205 
206 	(void) uu_list_walk(i->sc_pgroups, internal_pgroup_dump, NULL,
207 	    UU_DEFAULT);
208 
209 	return (UU_WALK_NEXT);
210 }
211 
212 /*ARGSUSED*/
213 static int
214 internal_service_dump(void *v, void *pvt)
215 {
216 	entity_t *s = v;
217 
218 	(void) printf("service	name = %s\n", s->sc_name);
219 	(void) printf("	type = %x\n", s->sc_u.sc_service.sc_service_type);
220 	(void) printf("	version = %u\n", s->sc_u.sc_service.sc_service_version);
221 
222 	(void) uu_list_walk(s->sc_pgroups, internal_pgroup_dump, NULL,
223 	    UU_DEFAULT);
224 
225 	(void) uu_list_walk(s->sc_u.sc_service.sc_service_instances,
226 	    internal_instance_dump, NULL, UU_DEFAULT);
227 
228 	return (UU_WALK_NEXT);
229 }
230 
231 void
232 internal_dump(bundle_t *b)
233 {
234 	(void) printf("bundle	name = %s\n", b->sc_bundle_name);
235 	(void) printf("	type = %x\n", b->sc_bundle_type);
236 
237 	(void) uu_list_walk(b->sc_bundle_services, internal_service_dump,
238 	    NULL, UU_DEFAULT);
239 }
240 
241 bundle_t *
242 internal_bundle_new()
243 {
244 	bundle_t	*b;
245 
246 	if ((b = uu_zalloc(sizeof (bundle_t))) == NULL)
247 		uu_die(gettext("couldn't allocate memory"));
248 
249 	b->sc_bundle_type = SVCCFG_UNKNOWN_BUNDLE;
250 	b->sc_bundle_services = uu_list_create(entity_pool, b, 0);
251 	if (b->sc_bundle_services == NULL) {
252 		uu_die(gettext("Unable to create list for bundle services.  "
253 		    "%s\n"), uu_strerror(uu_error()));
254 	}
255 
256 	return (b);
257 }
258 
259 void
260 internal_bundle_free(bundle_t *b)
261 {
262 	void *cookie = NULL;
263 	entity_t *service;
264 
265 	while ((service = uu_list_teardown(b->sc_bundle_services, &cookie)) !=
266 	    NULL)
267 		internal_service_free(service);
268 
269 	free(b);
270 }
271 
272 entity_t *
273 internal_entity_new(entity_type_t entity)
274 {
275 	entity_t *e;
276 
277 	if ((e = uu_zalloc(sizeof (entity_t))) == NULL)
278 		uu_die(gettext("couldn't allocate memory"));
279 
280 	uu_list_node_init(e, &e->sc_node, entity_pool);
281 
282 	e->sc_etype = entity;
283 	e->sc_pgroups = uu_list_create(pgroup_pool, e, 0);
284 	if (e->sc_pgroups == NULL) {
285 		uu_die(gettext("Unable to create list for entity property "
286 		    "groups.  %s\n"), uu_strerror(uu_error()));
287 	}
288 
289 	return (e);
290 }
291 
292 entity_t *
293 internal_service_new(const char *name)
294 {
295 	entity_t *s;
296 
297 	s = internal_entity_new(SVCCFG_SERVICE_OBJECT);
298 
299 	s->sc_name = name;
300 	s->sc_fmri = uu_msprintf("svc:/%s", name);
301 	if (s->sc_fmri == NULL)
302 		uu_die(gettext("couldn't allocate memory"));
303 
304 	s->sc_dependents = uu_list_create(pgroup_pool, s, 0);
305 	if (s->sc_dependents == NULL) {
306 		uu_die(gettext("Unable to create list for service dependents.  "
307 		    "%s\n"), uu_strerror(uu_error()));
308 	}
309 
310 	s->sc_u.sc_service.sc_service_type = SVCCFG_UNKNOWN_SERVICE;
311 	s->sc_u.sc_service.sc_service_instances = uu_list_create(entity_pool, s,
312 	    0);
313 	if (s->sc_u.sc_service.sc_service_instances == NULL) {
314 		uu_die(gettext("Unable to create list for service instances.  "
315 		    "%s\n"), uu_strerror(uu_error()));
316 	}
317 
318 	return (s);
319 }
320 
321 void
322 internal_service_free(entity_t *s)
323 {
324 	entity_t *inst;
325 	pgroup_t *pg;
326 	void *cookie;
327 
328 	if (s->sc_u.sc_service.sc_restarter != NULL)
329 		internal_instance_free(s->sc_u.sc_service.sc_restarter);
330 	if (s->sc_u.sc_service.sc_global != NULL)
331 		internal_instance_free(s->sc_u.sc_service.sc_global);
332 
333 	cookie = NULL;
334 	while ((pg = uu_list_teardown(s->sc_pgroups, &cookie)) != NULL)
335 		internal_pgroup_free(pg);
336 
337 	cookie = NULL;
338 	while ((pg = uu_list_teardown(s->sc_dependents, &cookie)) != NULL)
339 		internal_pgroup_free(pg);
340 
341 	cookie = NULL;
342 	while ((inst = uu_list_teardown(s->sc_u.sc_service.sc_service_instances,
343 	    &cookie)) != NULL)
344 		internal_instance_free(inst);
345 	uu_free((void *)s->sc_fmri);
346 
347 	free(s);
348 }
349 
350 entity_t *
351 internal_instance_new(const char *name)
352 {
353 	entity_t *i;
354 
355 	i = internal_entity_new(SVCCFG_INSTANCE_OBJECT);
356 	i->sc_name = name;
357 	/* Can't set i->sc_fmri until we're attached to a service. */
358 	i->sc_dependents = uu_list_create(pgroup_pool, i, 0);
359 	if (i->sc_dependents == NULL) {
360 		uu_die(gettext("Unable to create list for instance "
361 		    "dependents.  %s\n"), uu_strerror(uu_error()));
362 	}
363 
364 	return (i);
365 }
366 
367 void
368 internal_instance_free(entity_t *i)
369 {
370 	pgroup_t *pg;
371 	void *cookie = NULL;
372 	entity_t *rs;
373 
374 	rs = i->sc_u.sc_instance.sc_instance_restarter;
375 	if (rs != NULL)
376 		internal_instance_free(rs);
377 	while ((pg = uu_list_teardown(i->sc_pgroups, &cookie)) != NULL)
378 		internal_pgroup_free(pg);
379 
380 	cookie = NULL;
381 	while ((pg = uu_list_teardown(i->sc_dependents, &cookie)) != NULL)
382 		internal_pgroup_free(pg);
383 	uu_free((void *)i->sc_fmri);
384 
385 	free(i);
386 }
387 
388 pgroup_t *
389 internal_pgroup_new()
390 {
391 	pgroup_t *p;
392 
393 	if ((p = uu_zalloc(sizeof (pgroup_t))) == NULL)
394 		uu_die(gettext("couldn't allocate memory"));
395 
396 	uu_list_node_init(p, &p->sc_node, pgroup_pool);
397 
398 	p->sc_pgroup_props = uu_list_create(property_pool, p, UU_LIST_SORTED);
399 	if (p->sc_pgroup_props == NULL) {
400 		uu_die(gettext("Unable to create list for properties.  %s\n"),
401 		    uu_strerror(uu_error()));
402 	}
403 	p->sc_pgroup_name = "<unset>";
404 	p->sc_pgroup_type = "<unset>";
405 
406 	return (p);
407 }
408 
409 void
410 internal_pgroup_free(pgroup_t *pg)
411 {
412 	property_t *prop;
413 	void *cookie = NULL;
414 
415 	/*
416 	 * Templates validation code should clean up this reference when
417 	 * the validation is finished.
418 	 */
419 	assert(pg->sc_pgroup_composed == NULL);
420 
421 	while ((prop = uu_list_teardown(pg->sc_pgroup_props, &cookie)) != NULL)
422 		internal_property_free(prop);
423 
424 	uu_free(pg);
425 }
426 
427 static pgroup_t *
428 find_pgroup(uu_list_t *list, const char *name, const char *type)
429 {
430 	pgroup_t *pg;
431 
432 	for (pg = uu_list_first(list);
433 	    pg != NULL;
434 	    pg = uu_list_next(list, pg)) {
435 		if (strcmp(pg->sc_pgroup_name, name) != 0)
436 			continue;
437 
438 		if (type == NULL)
439 			return (pg);
440 
441 		if (strcmp(pg->sc_pgroup_type, type) == 0)
442 			return (pg);
443 	}
444 
445 	return (NULL);
446 }
447 
448 pgroup_t *
449 internal_dependent_find(entity_t *e, const char *name)
450 {
451 	return (find_pgroup(e->sc_dependents, name, NULL));
452 }
453 
454 pgroup_t *
455 internal_pgroup_find(entity_t *e, const char *name, const char *type)
456 {
457 	return (find_pgroup(e->sc_pgroups, name, type));
458 }
459 
460 static pgroup_t *
461 internal_pgroup_create_common(entity_t *e, const char *name, const char *type,
462 	boolean_t unique)
463 {
464 	pgroup_t *pg;
465 
466 	pg = internal_pgroup_find(e, name, type);
467 	if (pg != NULL) {
468 		if (unique == B_TRUE) {
469 			return (NULL);
470 		} else {
471 			return (pg);
472 		}
473 	}
474 
475 	pg = internal_pgroup_new();
476 	(void) internal_attach_pgroup(e, pg);
477 	pg->sc_pgroup_name = strdup(name);
478 	pg->sc_pgroup_type = strdup(type);
479 	pg->sc_pgroup_flags = 0;
480 
481 	if (pg->sc_pgroup_name == NULL || pg->sc_pgroup_type == NULL)
482 		uu_die(gettext("Could not duplicate string"));
483 
484 	return (pg);
485 }
486 
487 pgroup_t *
488 internal_pgroup_find_or_create(entity_t *e, const char *name, const char *type)
489 {
490 	return (internal_pgroup_create_common(e, name, type, B_FALSE));
491 }
492 
493 pgroup_t *
494 internal_pgroup_create_strict(entity_t *e, const char *name, const char *type)
495 {
496 	return (internal_pgroup_create_common(e, name, type, B_TRUE));
497 }
498 
499 property_t *
500 internal_property_new()
501 {
502 	property_t *p;
503 
504 	if ((p = uu_zalloc(sizeof (property_t))) == NULL)
505 		uu_die(gettext("couldn't allocate memory"));
506 
507 	uu_list_node_init(p, &p->sc_node, property_pool);
508 
509 	p->sc_property_values = uu_list_create(value_pool, p, 0);
510 	if (p->sc_property_values == NULL) {
511 		uu_die(gettext("Unable to create list for property values.  "
512 		    "%s\n"), uu_strerror(uu_error()));
513 	}
514 	p->sc_property_name = "<unset>";
515 
516 	tmpl_property_init(p);
517 
518 	return (p);
519 }
520 
521 void
522 internal_property_free(property_t *p)
523 {
524 	value_t *val;
525 	void *cookie = NULL;
526 
527 	tmpl_property_fini(p);
528 
529 	while ((val = uu_list_teardown(p->sc_property_values, &cookie)) !=
530 	    NULL) {
531 		if (val->sc_free != NULL)
532 			val->sc_free(val);
533 		free(val);
534 	}
535 
536 	free(p);
537 }
538 
539 property_t *
540 internal_property_find(pgroup_t *pg, const char *name)
541 {
542 	property_t *p;
543 
544 	for (p = uu_list_first(pg->sc_pgroup_props);
545 	    p != NULL;
546 	    p = uu_list_next(pg->sc_pgroup_props, p))
547 		if (strcmp(p->sc_property_name, name) == 0)
548 			return (p);
549 
550 	return (NULL);
551 }
552 
553 value_t *
554 internal_value_new()
555 {
556 	value_t *v;
557 
558 	if ((v = uu_zalloc(sizeof (value_t))) == NULL)
559 		uu_die(gettext("couldn't allocate memory"));
560 
561 	uu_list_node_init(v, &v->sc_node, value_pool);
562 
563 	return (v);
564 }
565 
566 static void
567 internal_value_free_str(value_t *v)
568 {
569 	free(v->sc_u.sc_string);
570 }
571 
572 property_t *
573 internal_property_create(const char *name, scf_type_t vtype, uint_t nvals, ...)
574 {
575 	va_list args;
576 	property_t *p;
577 	value_t *v;
578 
579 	p = internal_property_new();
580 
581 	p->sc_property_name = (char *)name;
582 	p->sc_value_type = vtype;
583 
584 	va_start(args, nvals);
585 	for (; nvals > 0; nvals--) {
586 
587 		v = internal_value_new();
588 		v->sc_type = vtype;
589 
590 		switch (vtype) {
591 		case SCF_TYPE_BOOLEAN:
592 		case SCF_TYPE_COUNT:
593 			v->sc_u.sc_count = va_arg(args, uint64_t);
594 			break;
595 		case SCF_TYPE_INTEGER:
596 			v->sc_u.sc_integer = va_arg(args, int64_t);
597 			break;
598 		case SCF_TYPE_ASTRING:
599 		case SCF_TYPE_FMRI:
600 		case SCF_TYPE_HOST:
601 		case SCF_TYPE_HOSTNAME:
602 		case SCF_TYPE_NET_ADDR_V4:
603 		case SCF_TYPE_NET_ADDR_V6:
604 		case SCF_TYPE_OPAQUE:
605 		case SCF_TYPE_TIME:
606 		case SCF_TYPE_URI:
607 		case SCF_TYPE_USTRING:
608 			v->sc_u.sc_string = (char *)va_arg(args, uchar_t *);
609 			break;
610 		default:
611 			va_end(args);
612 			uu_die(gettext("unknown property type (%d)\n"), vtype);
613 			break;
614 		}
615 
616 		internal_attach_value(p, v);
617 	}
618 	va_end(args);
619 
620 	return (p);
621 }
622 
623 /*
624  * Some of these attach functions use uu_list_append() to maintain the
625  * same order across import/export, whereas others are always sorted
626  * anyway, or the order is irrelevant.
627  */
628 
629 int
630 internal_attach_service(bundle_t *bndl, entity_t *svc)
631 {
632 	if (uu_list_find(bndl->sc_bundle_services, svc, NULL, NULL) != NULL) {
633 		semerr(gettext("Multiple definitions for service %s in "
634 		    "bundle %s.\n"), svc->sc_name, bndl->sc_bundle_name);
635 		return (-1);
636 	}
637 
638 	(void) uu_list_append(bndl->sc_bundle_services, svc);
639 
640 	return (0);
641 }
642 
643 int
644 internal_attach_entity(entity_t *svc, entity_t *ent)
645 {
646 	if (svc->sc_etype != SVCCFG_SERVICE_OBJECT)
647 		uu_die(gettext("bad entity attach: %s is not a service\n"),
648 		    svc->sc_name);
649 
650 	if (uu_list_find(svc->sc_u.sc_service.sc_service_instances, ent, NULL,
651 	    NULL) != NULL) {
652 		semerr(gettext("Multiple definitions of entity %s in service "
653 		    "%s.\n"), ent->sc_name, svc->sc_name);
654 		return (-1);
655 	}
656 
657 	(void) uu_list_prepend(svc->sc_u.sc_service.sc_service_instances, ent);
658 	ent->sc_parent = svc;
659 	ent->sc_fmri = uu_msprintf("%s:%s", svc->sc_fmri, ent->sc_name);
660 	if (ent->sc_fmri == NULL)
661 		uu_die(gettext("couldn't allocate memory"));
662 
663 	return (0);
664 }
665 
666 int
667 internal_attach_pgroup(entity_t *ent, pgroup_t *pgrp)
668 {
669 	if (uu_list_find(ent->sc_pgroups, pgrp, NULL, NULL) != NULL) {
670 		semerr(gettext("Multiple definitions of property group %s in "
671 		    "entity %s.\n"), pgrp->sc_pgroup_name, ent->sc_name);
672 		return (-1);
673 	}
674 
675 	(void) uu_list_append(ent->sc_pgroups, pgrp);
676 
677 	pgrp->sc_parent = ent;
678 
679 	return (0);
680 }
681 
682 void
683 internal_detach_pgroup(entity_t *ent, pgroup_t *pgrp)
684 {
685 	uu_list_remove(ent->sc_pgroups, pgrp);
686 }
687 
688 int
689 internal_attach_dependent(entity_t *ent, pgroup_t *pg)
690 {
691 	if (uu_list_find(ent->sc_dependents, pg, NULL, NULL) != NULL) {
692 		semerr(gettext("Multiple definitions of dependent %s in "
693 		    "entity %s.\n"), pg->sc_pgroup_name, ent->sc_name);
694 		return (-1);
695 	}
696 
697 	(void) uu_list_append(ent->sc_dependents, pg);
698 
699 	pg->sc_parent = ent;
700 
701 	return (0);
702 }
703 
704 /*
705  * Returns
706  *   0 - success
707  *   -1 - prop already exists in pgrp
708  */
709 int
710 internal_attach_property(pgroup_t *pgrp, property_t *prop)
711 {
712 	uu_list_index_t idx;
713 
714 	if (uu_list_find(pgrp->sc_pgroup_props, prop, NULL, &idx) != NULL) {
715 		semerr(gettext("Multiple definitions for property %s in "
716 		    "property group %s.\n"), prop->sc_property_name,
717 		    pgrp->sc_pgroup_name);
718 		return (-1);
719 	}
720 
721 	uu_list_insert(pgrp->sc_pgroup_props, prop, idx);
722 
723 	return (0);
724 }
725 
726 void
727 internal_detach_property(pgroup_t *pgrp, property_t *prop)
728 {
729 	uu_list_remove(pgrp->sc_pgroup_props, prop);
730 }
731 
732 void
733 internal_attach_value(property_t *prop, value_t *val)
734 {
735 	(void) uu_list_append(prop->sc_property_values, val);
736 }
737 
738 /*
739  * These functions create an internal representation of a property group
740  * (pgroup_t) from the repository (scf_propertygroup_t).  They are used by the
741  * import functions in svccfg_libscf.c .
742  *
743  * load_init() must be called first to initialize these globals, and
744  * load_fini() should be called afterwards to destroy them.
745  */
746 
747 static char *loadbuf = NULL;
748 static size_t loadbuf_sz;
749 static scf_propertygroup_t *load_pgroup = NULL;
750 static scf_property_t *load_prop = NULL;
751 static scf_value_t *load_val = NULL;
752 static scf_iter_t *load_propiter = NULL, *load_valiter = NULL;
753 static scf_iter_t *load_pgiter = NULL;
754 
755 /*
756  * Initialize the global state for the load_*() routines.
757  * Returns
758  *   0 - success
759  *   ENOMEM - out of memory
760  */
761 int
762 load_init(void)
763 {
764 	loadbuf_sz = ((max_scf_value_len > max_scf_pg_type_len) ?
765 	    max_scf_value_len : max_scf_pg_type_len) + 1;
766 
767 	loadbuf = malloc(loadbuf_sz);
768 	if (loadbuf == NULL)
769 		return (ENOMEM);
770 
771 	if ((load_prop = scf_property_create(g_hndl)) == NULL ||
772 	    (load_val = scf_value_create(g_hndl)) == NULL ||
773 	    (load_pgroup = scf_pg_create(g_hndl)) == NULL ||
774 	    (load_pgiter = scf_iter_create(g_hndl)) == NULL ||
775 	    (load_propiter = scf_iter_create(g_hndl)) == NULL ||
776 	    (load_valiter = scf_iter_create(g_hndl)) == NULL) {
777 		load_fini();
778 		return (ENOMEM);
779 	}
780 
781 	return (0);
782 }
783 
784 void
785 load_fini(void)
786 {
787 	scf_iter_destroy(load_propiter);
788 	load_propiter = NULL;
789 	scf_iter_destroy(load_valiter);
790 	load_valiter = NULL;
791 	scf_iter_destroy(load_pgiter);
792 	load_pgiter = NULL;
793 	scf_pg_destroy(load_pgroup);
794 	load_pgroup = NULL;
795 	scf_value_destroy(load_val);
796 	load_val = NULL;
797 	scf_property_destroy(load_prop);
798 	load_prop = NULL;
799 	free(loadbuf);
800 	loadbuf = NULL;
801 }
802 
803 /*
804  * Create a property_t which represents an scf_property_t.  Returns
805  *   0 - success
806  *   ECANCELED - prop's pg was deleted
807  *   ECONNABORTED - repository disconnected
808  *   ENOMEM - out of memory
809  *   EACCES - permission denied when reading property
810  */
811 static int
812 load_property(scf_property_t *prop, property_t **ipp)
813 {
814 	property_t *iprop;
815 	int r;
816 	ssize_t ssz;
817 
818 	/* get name */
819 	if (scf_property_get_name(prop, loadbuf, loadbuf_sz) < 0) {
820 		switch (scf_error()) {
821 		case SCF_ERROR_DELETED:
822 			return (ECANCELED);
823 
824 		case SCF_ERROR_CONNECTION_BROKEN:
825 			return (ECONNABORTED);
826 
827 		case SCF_ERROR_NOT_BOUND:
828 		case SCF_ERROR_NOT_SET:
829 		default:
830 			bad_error("scf_property_get_name", scf_error());
831 		}
832 	}
833 
834 	iprop = internal_property_new();
835 	iprop->sc_property_name = strdup(loadbuf);
836 	if (iprop->sc_property_name == NULL) {
837 		internal_property_free(iprop);
838 		return (ENOMEM);
839 	}
840 
841 	/* get type */
842 	if (scf_property_type(prop, &iprop->sc_value_type) != 0) {
843 		switch (scf_error()) {
844 		case SCF_ERROR_DELETED:
845 			r = ECANCELED;
846 			goto out;
847 
848 		case SCF_ERROR_CONNECTION_BROKEN:
849 			r = ECONNABORTED;
850 			goto out;
851 
852 		case SCF_ERROR_NOT_BOUND:
853 		case SCF_ERROR_NOT_SET:
854 		default:
855 			bad_error("scf_property_type", scf_error());
856 		}
857 	}
858 
859 	/* get values */
860 	if (scf_iter_property_values(load_valiter, prop) != 0) {
861 		switch (scf_error()) {
862 		case SCF_ERROR_DELETED:
863 			r = ECANCELED;
864 			goto out;
865 
866 		case SCF_ERROR_CONNECTION_BROKEN:
867 			r = ECONNABORTED;
868 			goto out;
869 
870 		case SCF_ERROR_HANDLE_MISMATCH:
871 		case SCF_ERROR_NOT_BOUND:
872 		case SCF_ERROR_NOT_SET:
873 		default:
874 			bad_error("scf_iter_property_values", scf_error());
875 		}
876 	}
877 
878 	for (;;) {
879 		value_t *ival;
880 
881 		r = scf_iter_next_value(load_valiter, load_val);
882 		if (r == 0)
883 			break;
884 		if (r != 1) {
885 			switch (scf_error()) {
886 			case SCF_ERROR_DELETED:
887 				r = ECANCELED;
888 				goto out;
889 
890 			case SCF_ERROR_CONNECTION_BROKEN:
891 				r = ECONNABORTED;
892 				goto out;
893 
894 			case SCF_ERROR_PERMISSION_DENIED:
895 				r = EACCES;
896 				goto out;
897 
898 			case SCF_ERROR_HANDLE_MISMATCH:
899 			case SCF_ERROR_NOT_BOUND:
900 			case SCF_ERROR_NOT_SET:
901 			case SCF_ERROR_INVALID_ARGUMENT:
902 			default:
903 				bad_error("scf_iter_next_value", scf_error());
904 			}
905 		}
906 
907 		ival = internal_value_new();
908 		ival->sc_type = scf_value_type(load_val);
909 		assert(ival->sc_type != SCF_TYPE_INVALID);
910 
911 		switch (ival->sc_type) {
912 		case SCF_TYPE_BOOLEAN: {
913 			uint8_t b;
914 
915 			r = scf_value_get_boolean(load_val, &b);
916 			if (r != 0)
917 				bad_error("scf_value_get_boolean", scf_error());
918 			ival->sc_u.sc_count = b;
919 			break;
920 		}
921 
922 		case SCF_TYPE_COUNT:
923 			r = scf_value_get_count(load_val, &ival->sc_u.sc_count);
924 			if (r != 0)
925 				bad_error("scf_value_get_count", scf_error());
926 			break;
927 
928 		case SCF_TYPE_INTEGER:
929 			r = scf_value_get_integer(load_val,
930 			    &ival->sc_u.sc_integer);
931 			if (r != 0)
932 				bad_error("scf_value_get_integer", scf_error());
933 			break;
934 
935 		default:
936 			ssz = scf_value_get_as_string(load_val, loadbuf,
937 			    loadbuf_sz);
938 			if (ssz < 0)
939 				bad_error("scf_value_get_as_string",
940 				    scf_error());
941 
942 			ival->sc_u.sc_string = strdup(loadbuf);
943 			if (ival->sc_u.sc_string == NULL) {
944 				r = ENOMEM;
945 				goto out;
946 			}
947 
948 			ival->sc_free = internal_value_free_str;
949 		}
950 
951 		internal_attach_value(iprop, ival);
952 	}
953 
954 	*ipp = iprop;
955 	return (0);
956 
957 out:
958 	free(iprop->sc_property_name);
959 	internal_property_free(iprop);
960 	return (r);
961 }
962 
963 /*
964  * Returns
965  *   0 - success
966  *   ECANCELED - pg was deleted
967  *   ECONNABORTED - repository disconnected
968  *   ENOMEM - out of memory
969  */
970 int
971 load_pg_attrs(const scf_propertygroup_t *pg, pgroup_t **ipgp)
972 {
973 	pgroup_t *ipg;
974 
975 	ipg = internal_pgroup_new();
976 
977 	if (scf_pg_get_flags(pg, &ipg->sc_pgroup_flags) != 0) {
978 		switch (scf_error()) {
979 		case SCF_ERROR_DELETED:
980 			internal_pgroup_free(ipg);
981 			return (ECANCELED);
982 
983 		case SCF_ERROR_CONNECTION_BROKEN:
984 			internal_pgroup_free(ipg);
985 			return (ECONNABORTED);
986 
987 		case SCF_ERROR_NOT_SET:
988 		case SCF_ERROR_NOT_BOUND:
989 		default:
990 			bad_error("scf_pg_get_name", scf_error());
991 		}
992 	}
993 
994 	if (scf_pg_get_name(pg, loadbuf, loadbuf_sz) < 0) {
995 		switch (scf_error()) {
996 		case SCF_ERROR_DELETED:
997 			internal_pgroup_free(ipg);
998 			return (ECANCELED);
999 
1000 		case SCF_ERROR_CONNECTION_BROKEN:
1001 			internal_pgroup_free(ipg);
1002 			return (ECONNABORTED);
1003 
1004 		case SCF_ERROR_NOT_SET:
1005 		case SCF_ERROR_NOT_BOUND:
1006 		default:
1007 			bad_error("scf_pg_get_name", scf_error());
1008 		}
1009 	}
1010 
1011 	ipg->sc_pgroup_name = strdup(loadbuf);
1012 	if (ipg->sc_pgroup_name == NULL) {
1013 		internal_pgroup_free(ipg);
1014 		return (ENOMEM);
1015 	}
1016 
1017 	if (scf_pg_get_type(pg, loadbuf, loadbuf_sz) < 0) {
1018 		switch (scf_error()) {
1019 		case SCF_ERROR_DELETED:
1020 			free((char *)ipg->sc_pgroup_name);
1021 			internal_pgroup_free(ipg);
1022 			return (ECANCELED);
1023 
1024 		case SCF_ERROR_CONNECTION_BROKEN:
1025 			free((char *)ipg->sc_pgroup_name);
1026 			internal_pgroup_free(ipg);
1027 			return (ECONNABORTED);
1028 
1029 		case SCF_ERROR_NOT_SET:
1030 		case SCF_ERROR_NOT_BOUND:
1031 		default:
1032 			bad_error("scf_pg_get_name", scf_error());
1033 		}
1034 	}
1035 
1036 	ipg->sc_pgroup_type = strdup(loadbuf);
1037 	if (ipg->sc_pgroup_type == NULL) {
1038 		free((char *)ipg->sc_pgroup_name);
1039 		internal_pgroup_free(ipg);
1040 		return (ENOMEM);
1041 	}
1042 
1043 	*ipgp = ipg;
1044 	return (0);
1045 }
1046 
1047 /*
1048  * Load a property group into a pgroup_t.  Returns
1049  *   0 - success
1050  *   ECANCELED - pg was deleted
1051  *   ECONNABORTED - repository disconnected
1052  *   EBADF - pg is corrupt (error printed if fmri is given)
1053  *   ENOMEM - out of memory
1054  *   EACCES - permission denied when reading property
1055  */
1056 int
1057 load_pg(const scf_propertygroup_t *pg, pgroup_t **ipgp, const char *fmri,
1058     const char *snapname)
1059 {
1060 	pgroup_t *ipg;
1061 	int r;
1062 
1063 	if (scf_iter_pg_properties(load_propiter, pg) != 0) {
1064 		switch (scf_error()) {
1065 		case SCF_ERROR_DELETED:
1066 			return (ECANCELED);
1067 
1068 		case SCF_ERROR_CONNECTION_BROKEN:
1069 			return (ECONNABORTED);
1070 
1071 		case SCF_ERROR_HANDLE_MISMATCH:
1072 		case SCF_ERROR_NOT_SET:
1073 		case SCF_ERROR_NOT_BOUND:
1074 		default:
1075 			bad_error("scf_iter_pg_properties", scf_error());
1076 		}
1077 	}
1078 
1079 	r = load_pg_attrs(pg, &ipg);
1080 	switch (r) {
1081 	case 0:
1082 		break;
1083 
1084 	case ECANCELED:
1085 	case ECONNABORTED:
1086 	case ENOMEM:
1087 		return (r);
1088 
1089 	default:
1090 		bad_error("load_pg_attrs", r);
1091 	}
1092 
1093 	for (;;) {
1094 		property_t *iprop;
1095 
1096 		r = scf_iter_next_property(load_propiter, load_prop);
1097 		if (r == 0)
1098 			break;
1099 		if (r != 1) {
1100 			switch (scf_error()) {
1101 			case SCF_ERROR_DELETED:
1102 				r = ECANCELED;
1103 				goto out;
1104 
1105 			case SCF_ERROR_CONNECTION_BROKEN:
1106 				r = ECONNABORTED;
1107 				goto out;
1108 
1109 			case SCF_ERROR_HANDLE_MISMATCH:
1110 			case SCF_ERROR_NOT_BOUND:
1111 			case SCF_ERROR_NOT_SET:
1112 			case SCF_ERROR_INVALID_ARGUMENT:
1113 			default:
1114 				bad_error("scf_iter_next_property",
1115 				    scf_error());
1116 			}
1117 		}
1118 
1119 		r = load_property(load_prop, &iprop);
1120 		switch (r) {
1121 		case 0:
1122 			break;
1123 
1124 		case ECANCELED:
1125 		case ECONNABORTED:
1126 		case ENOMEM:
1127 		case EACCES:
1128 			goto out;
1129 
1130 		default:
1131 			bad_error("load_property", r);
1132 		}
1133 
1134 		r = internal_attach_property(ipg, iprop);
1135 		if (r != 0) {
1136 			if (fmri != NULL) {
1137 				if (snapname == NULL)
1138 					warn(gettext("Property group \"%s\" of "
1139 					    "%s has multiple definitions of "
1140 					    "property \"%s\".\n"),
1141 					    ipg->sc_pgroup_name, fmri,
1142 					    iprop->sc_property_name);
1143 				else
1144 					warn(gettext("Property group \"%s\" of "
1145 					    "the \"%s\" snapshot of %s has "
1146 					    "multiple definitions of property "
1147 					    "\"%s\".\n"),
1148 					    ipg->sc_pgroup_name, snapname, fmri,
1149 					    iprop->sc_property_name);
1150 			}
1151 			r = EBADF;
1152 			goto out;
1153 		}
1154 	}
1155 
1156 	*ipgp = ipg;
1157 	return (0);
1158 
1159 out:
1160 	internal_pgroup_free(ipg);
1161 	return (r);
1162 }
1163 
1164 /*
1165  * Load the instance for fmri from the repository into memory.  The
1166  * property groups that define the instances pg_patterns and prop_patterns
1167  * are also loaded.
1168  *
1169  * Returns 0 on success and non-zero on failure.
1170  */
1171 int
1172 load_instance(const char *fmri, const char *name, entity_t **inst_ptr)
1173 {
1174 	entity_t *e = NULL;
1175 	scf_instance_t *inst;
1176 	pgroup_t *ipg;
1177 	int rc;
1178 	char *type = NULL;
1179 	ssize_t tsize;
1180 
1181 	assert(inst_ptr != NULL);
1182 
1183 	if ((inst = scf_instance_create(g_hndl)) == NULL) {
1184 		switch (scf_error()) {
1185 		case SCF_ERROR_NO_MEMORY:
1186 		case SCF_ERROR_NO_RESOURCES:
1187 			rc = EAGAIN;
1188 			goto errout;
1189 		default:
1190 			bad_error("scf_instance_create", scf_error());
1191 		}
1192 	}
1193 	if (scf_handle_decode_fmri(g_hndl, fmri, NULL, NULL, inst, NULL, NULL,
1194 	    SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1195 		switch (scf_error()) {
1196 		case SCF_ERROR_CONNECTION_BROKEN:
1197 			rc = ECONNABORTED;
1198 			goto errout;
1199 		case SCF_ERROR_DELETED:
1200 		case SCF_ERROR_NOT_FOUND:
1201 			rc = ENOENT;
1202 			goto errout;
1203 		default:
1204 			bad_error("scf_handle_decode_fmri", scf_error());
1205 		}
1206 	}
1207 	if (scf_iter_instance_pgs_composed(load_pgiter, inst, NULL) != 0) {
1208 		switch (scf_error()) {
1209 		case SCF_ERROR_DELETED:
1210 			rc = ECANCELED;
1211 			goto errout;
1212 		case SCF_ERROR_CONNECTION_BROKEN:
1213 			rc = ECONNABORTED;
1214 			goto errout;
1215 		default:
1216 			bad_error("scf_iter_instance_pgs_composed",
1217 			    scf_error());
1218 		}
1219 	}
1220 
1221 	tsize = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH);
1222 	type = uu_zalloc(tsize);
1223 	if (type == NULL) {
1224 		rc = ENOMEM;
1225 		goto errout;
1226 	}
1227 
1228 	/*
1229 	 * Initialize our entity structure.
1230 	 */
1231 	e = internal_instance_new(name);
1232 	if (e == NULL) {
1233 		rc = ENOMEM;
1234 		goto errout;
1235 	}
1236 	e->sc_fmri = uu_strdup(fmri);
1237 	if (e->sc_fmri == NULL) {
1238 		rc = ENOMEM;
1239 		goto errout;
1240 	}
1241 
1242 	/*
1243 	 * Walk through the property group's of the instance and capture
1244 	 * the property groups that are of type
1245 	 * SCF_GROUP_TEMPLATE_PG_PATTERN and
1246 	 * SCF_GROUP_TEMPLATE_PROP_PATTERN.  In other words grab the
1247 	 * pg_pattern and prop_pattern property groups.
1248 	 */
1249 	while ((rc = scf_iter_next_pg(load_pgiter, load_pgroup)) == 1) {
1250 		if (scf_pg_get_type(load_pgroup, type, tsize) <= 0) {
1251 			switch (scf_error()) {
1252 			case SCF_ERROR_DELETED:
1253 				rc = ENOENT;
1254 				break;
1255 			case SCF_ERROR_CONNECTION_BROKEN:
1256 				rc = ECONNABORTED;
1257 				break;
1258 			default:
1259 				bad_error("scf_pg_get_type", scf_error());
1260 			}
1261 			goto errout;
1262 		}
1263 		if ((strcmp(type, SCF_GROUP_TEMPLATE_PG_PATTERN) != 0) &&
1264 		    (strcmp(type, SCF_GROUP_TEMPLATE_PROP_PATTERN) != 0)) {
1265 			continue;
1266 		}
1267 		if ((rc = load_pg(load_pgroup, &ipg, fmri, NULL)) != 0) {
1268 			switch (rc) {
1269 			case ECANCELED:
1270 			case ECONNABORTED:
1271 			case EACCES:
1272 			case ENOMEM:
1273 				break;
1274 			default:
1275 				bad_error("load_pg", rc);
1276 			}
1277 			goto errout;
1278 		}
1279 		if (internal_attach_pgroup(e, ipg) != 0) {
1280 			rc = EBADF;
1281 			goto errout;
1282 		}
1283 	}
1284 	if (rc == -1) {
1285 		/* Error in iteration. */
1286 		switch (scf_error()) {
1287 		case SCF_ERROR_CONNECTION_BROKEN:
1288 			rc = ECONNABORTED;
1289 			break;
1290 		case SCF_ERROR_DELETED:
1291 			rc = ENOENT;
1292 			break;
1293 		case SCF_ERROR_NO_RESOURCES:
1294 			rc = EAGAIN;
1295 			break;
1296 		default:
1297 			bad_error("scf_iter_next_pg", scf_error());
1298 		}
1299 		goto errout;
1300 	}
1301 
1302 	*inst_ptr = e;
1303 	scf_instance_destroy(inst);
1304 	return (0);
1305 
1306 errout:
1307 	if (type != NULL)
1308 		uu_free(type);
1309 	if (inst != NULL)
1310 		scf_instance_destroy(inst);
1311 	if (e != NULL)
1312 		internal_instance_free(e);
1313 	return (rc);
1314 }
1315 
1316 /*
1317  * These functions compare internal property groups and properties (pgroup_t
1318  * & property_t).  They return 1 if the given structures are equal and
1319  * 0 otherwise.  Some will report the differences between the two structures.
1320  * They are used by the import functions in svccfg_libscf.c .
1321  */
1322 
1323 int
1324 prop_equal(property_t *p1, property_t *p2, const char *fmri, const char *pgname,
1325     int new)
1326 {
1327 	value_t *v1, *v2;
1328 
1329 	const char * const values_diff = gettext("Conflict upgrading %s "
1330 	    "(property \"%s/%s\" has different values).\n");
1331 	const char * const values_diff_new = gettext("Conflict upgrading %s "
1332 	    "(new property \"%s/%s\" has different values).\n");
1333 
1334 	assert((fmri == NULL) == (pgname == NULL));
1335 
1336 	if (fmri != NULL) {
1337 		/*
1338 		 * If we find any differences, we'll report conflicts.  But
1339 		 * conflict messages won't make any sense if the names don't
1340 		 * match.  If the caller supplied fmri, assert that the names
1341 		 * match.
1342 		 */
1343 		assert(strcmp(p1->sc_property_name, p2->sc_property_name) == 0);
1344 	} else {
1345 		if (strcmp(p1->sc_property_name, p2->sc_property_name) != 0)
1346 			return (0);
1347 	}
1348 
1349 	if (p1->sc_value_type != p2->sc_value_type) {
1350 		if (fmri != NULL) {
1351 			if (new)
1352 				warn(gettext("Conflict upgrading %s "
1353 				    "(new property \"%s/%s\" has different "
1354 				    "type).\n"), fmri, pgname,
1355 				    p1->sc_property_name);
1356 			else
1357 				warn(gettext("Conflict upgrading %s "
1358 				    "(property \"%s/%s\" has different "
1359 				    "type).\n"), fmri, pgname,
1360 				    p1->sc_property_name);
1361 		}
1362 		return (0);
1363 	}
1364 
1365 	if (uu_list_numnodes(p1->sc_property_values) !=
1366 	    uu_list_numnodes(p2->sc_property_values)) {
1367 		if (fmri != NULL)
1368 			warn(new ? values_diff_new : values_diff, fmri,
1369 			    pgname, p1->sc_property_name);
1370 		return (0);
1371 	}
1372 
1373 	v1 = uu_list_first(p1->sc_property_values);
1374 	v2 = uu_list_first(p2->sc_property_values);
1375 
1376 	while (v1 != NULL) {
1377 		assert(v2 != NULL);
1378 
1379 		if (value_cmp(v1, v2, NULL) != 0) {
1380 			if (fmri != NULL)
1381 				warn(new ? values_diff_new : values_diff,
1382 				    fmri, pgname, p1->sc_property_name);
1383 			return (0);
1384 		}
1385 
1386 		v1 = uu_list_next(p1->sc_property_values, v1);
1387 		v2 = uu_list_next(p2->sc_property_values, v2);
1388 	}
1389 
1390 	return (1);
1391 }
1392 
1393 int
1394 pg_attrs_equal(const pgroup_t *pg1, const pgroup_t *pg2, const char *fmri,
1395     int new)
1396 {
1397 	if (strcmp(pg1->sc_pgroup_name, pg2->sc_pgroup_name) != 0) {
1398 		assert(fmri == NULL);
1399 		return (0);
1400 	}
1401 
1402 	if (pg1->sc_pgroup_flags != pg2->sc_pgroup_flags) {
1403 		if (fmri) {
1404 			if (new)
1405 				warn(gettext("Conflict upgrading %s "
1406 				    "(new property group \"%s\" has different "
1407 				    "flags).\n"), fmri, pg1->sc_pgroup_name);
1408 			else
1409 				warn(gettext("Conflict upgrading %s "
1410 				    "(property group \"%s\" has different "
1411 				    "flags).\n"), fmri, pg1->sc_pgroup_name);
1412 		}
1413 		return (0);
1414 	}
1415 
1416 	if (strcmp(pg1->sc_pgroup_type, pg2->sc_pgroup_type) != 0) {
1417 		if (fmri) {
1418 			if (new)
1419 				warn(gettext("Conflict upgrading %s "
1420 				    "(new property group \"%s\" has different "
1421 				    "type).\n"), fmri, pg1->sc_pgroup_name);
1422 			else
1423 				warn(gettext("Conflict upgrading %s "
1424 				    "(property group \"%s\" has different "
1425 				    "type).\n"), fmri, pg1->sc_pgroup_name);
1426 		}
1427 		return (0);
1428 	}
1429 
1430 	return (1);
1431 }
1432 
1433 int
1434 pg_equal(pgroup_t *pg1, pgroup_t *pg2)
1435 {
1436 	property_t *p1, *p2;
1437 
1438 	if (!pg_attrs_equal(pg1, pg2, NULL, 0))
1439 		return (0);
1440 
1441 	if (uu_list_numnodes(pg1->sc_pgroup_props) !=
1442 	    uu_list_numnodes(pg2->sc_pgroup_props))
1443 		return (0);
1444 
1445 	p1 = uu_list_first(pg1->sc_pgroup_props);
1446 	p2 = uu_list_first(pg2->sc_pgroup_props);
1447 
1448 	while (p1 != NULL) {
1449 		assert(p2 != NULL);
1450 
1451 		if (!prop_equal(p1, p2, NULL, NULL, 0))
1452 			return (0);
1453 
1454 		p1 = uu_list_next(pg1->sc_pgroup_props, p1);
1455 		p2 = uu_list_next(pg2->sc_pgroup_props, p2);
1456 	}
1457 
1458 	return (1);
1459 }
1460