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