xref: /illumos-gate/usr/src/cmd/svc/svccfg/svccfg_internal.c (revision 92a0208178405fef708b0283ffcaa02fbc3468ff)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.h>
30 #include <errno.h>
31 #include <libintl.h>
32 #include <libuutil.h>
33 #include <stdarg.h>
34 #include <stddef.h>
35 #include <string.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 
252 	return (b);
253 }
254 
255 void
256 internal_bundle_free(bundle_t *b)
257 {
258 	void *cookie = NULL;
259 	entity_t *service;
260 
261 	while ((service = uu_list_teardown(b->sc_bundle_services, &cookie)) !=
262 	    NULL)
263 		internal_service_free(service);
264 
265 	free(b);
266 }
267 
268 entity_t *
269 internal_service_new(const char *name)
270 {
271 	entity_t *s;
272 
273 	if ((s = uu_zalloc(sizeof (entity_t))) == NULL)
274 		uu_die(gettext("couldn't allocate memory"));
275 
276 	uu_list_node_init(s, &s->sc_node, entity_pool);
277 
278 	s->sc_name = name;
279 	s->sc_fmri = uu_msprintf("svc:/%s", name);
280 	if (s->sc_fmri == NULL)
281 		uu_die(gettext("couldn't allocate memory"));
282 
283 	s->sc_etype = SVCCFG_SERVICE_OBJECT;
284 	s->sc_pgroups = uu_list_create(pgroup_pool, s, 0);
285 	s->sc_dependents = uu_list_create(pgroup_pool, s, 0);
286 
287 	s->sc_u.sc_service.sc_service_type = SVCCFG_UNKNOWN_SERVICE;
288 	s->sc_u.sc_service.sc_service_instances = uu_list_create(entity_pool, s,
289 	    0);
290 
291 	return (s);
292 }
293 
294 void
295 internal_service_free(entity_t *s)
296 {
297 	entity_t *inst;
298 	pgroup_t *pg;
299 	void *cookie;
300 
301 	cookie = NULL;
302 	while ((pg = uu_list_teardown(s->sc_pgroups, &cookie)) != NULL)
303 		internal_pgroup_free(pg);
304 
305 	cookie = NULL;
306 	while ((pg = uu_list_teardown(s->sc_dependents, &cookie)) != NULL)
307 		internal_pgroup_free(pg);
308 
309 	cookie = NULL;
310 	while ((inst = uu_list_teardown(s->sc_u.sc_service.sc_service_instances,
311 	    &cookie)) != NULL)
312 		internal_instance_free(inst);
313 
314 	free(s);
315 }
316 
317 entity_t *
318 internal_instance_new(const char *name)
319 {
320 	entity_t *i;
321 
322 	if ((i = uu_zalloc(sizeof (entity_t))) == NULL)
323 		uu_die(gettext("couldn't allocate memory"));
324 
325 	uu_list_node_init(i, &i->sc_node, entity_pool);
326 
327 	i->sc_name = name;
328 	/* Can't set i->sc_fmri until we're attached to a service. */
329 	i->sc_etype = SVCCFG_INSTANCE_OBJECT;
330 	i->sc_pgroups = uu_list_create(pgroup_pool, i, 0);
331 	i->sc_dependents = uu_list_create(pgroup_pool, i, 0);
332 
333 	return (i);
334 }
335 
336 void
337 internal_instance_free(entity_t *i)
338 {
339 	pgroup_t *pg;
340 	void *cookie = NULL;
341 
342 	while ((pg = uu_list_teardown(i->sc_pgroups, &cookie)) != NULL)
343 		internal_pgroup_free(pg);
344 
345 	cookie = NULL;
346 	while ((pg = uu_list_teardown(i->sc_dependents, &cookie)) != NULL)
347 		internal_pgroup_free(pg);
348 
349 	free(i);
350 }
351 
352 entity_t *
353 internal_template_new()
354 {
355 	entity_t *t;
356 
357 	if ((t = uu_zalloc(sizeof (entity_t))) == NULL)
358 		uu_die(gettext("couldn't allocate memory"));
359 
360 	uu_list_node_init(t, &t->sc_node, entity_pool);
361 
362 	t->sc_etype = SVCCFG_TEMPLATE_OBJECT;
363 	t->sc_pgroups = uu_list_create(pgroup_pool, t, 0);
364 
365 	return (t);
366 }
367 
368 pgroup_t *
369 internal_pgroup_new()
370 {
371 	pgroup_t *p;
372 
373 	if ((p = uu_zalloc(sizeof (pgroup_t))) == NULL)
374 		uu_die(gettext("couldn't allocate memory"));
375 
376 	uu_list_node_init(p, &p->sc_node, pgroup_pool);
377 
378 	p->sc_pgroup_props = uu_list_create(property_pool, p, UU_LIST_SORTED);
379 	p->sc_pgroup_name = "<unset>";
380 	p->sc_pgroup_type = "<unset>";
381 
382 	return (p);
383 }
384 
385 void
386 internal_pgroup_free(pgroup_t *pg)
387 {
388 	property_t *prop;
389 	void *cookie = NULL;
390 
391 	while ((prop = uu_list_teardown(pg->sc_pgroup_props, &cookie)) != NULL)
392 		internal_property_free(prop);
393 
394 	uu_free(pg);
395 }
396 
397 static pgroup_t *
398 find_pgroup(uu_list_t *list, const char *name, const char *type)
399 {
400 	pgroup_t *pg;
401 
402 	for (pg = uu_list_first(list);
403 	    pg != NULL;
404 	    pg = uu_list_next(list, pg)) {
405 		if (strcmp(pg->sc_pgroup_name, name) != 0)
406 			continue;
407 
408 		if (type == NULL)
409 			return (pg);
410 
411 		if (strcmp(pg->sc_pgroup_type, type) == 0)
412 			return (pg);
413 	}
414 
415 	return (NULL);
416 }
417 
418 pgroup_t *
419 internal_dependent_find(entity_t *e, const char *name)
420 {
421 	return (find_pgroup(e->sc_dependents, name, NULL));
422 }
423 
424 pgroup_t *
425 internal_pgroup_find(entity_t *e, const char *name, const char *type)
426 {
427 	return (find_pgroup(e->sc_pgroups, name, type));
428 }
429 
430 pgroup_t *
431 internal_pgroup_find_or_create(entity_t *e, const char *name, const char *type)
432 {
433 	pgroup_t *pg;
434 
435 	pg = internal_pgroup_find(e, name, type);
436 	if (pg != NULL)
437 		return (pg);
438 
439 	pg = internal_pgroup_new();
440 	(void) internal_attach_pgroup(e, pg);
441 	pg->sc_pgroup_name = strdup(name);
442 	pg->sc_pgroup_type = strdup(type);
443 	pg->sc_pgroup_flags = 0;
444 
445 	if (pg->sc_pgroup_name == NULL || pg->sc_pgroup_type == NULL)
446 		uu_die(gettext("Could not duplicate string"));
447 
448 	return (pg);
449 }
450 
451 property_t *
452 internal_property_new()
453 {
454 	property_t *p;
455 
456 	if ((p = uu_zalloc(sizeof (property_t))) == NULL)
457 		uu_die(gettext("couldn't allocate memory"));
458 
459 	uu_list_node_init(p, &p->sc_node, property_pool);
460 
461 	p->sc_property_values = uu_list_create(value_pool, p, 0);
462 	p->sc_property_name = "<unset>";
463 
464 	return (p);
465 }
466 
467 void
468 internal_property_free(property_t *p)
469 {
470 	value_t *val;
471 	void *cookie = NULL;
472 
473 	while ((val = uu_list_teardown(p->sc_property_values, &cookie)) !=
474 	    NULL) {
475 		if (val->sc_free != NULL)
476 			val->sc_free(val);
477 		free(val);
478 	}
479 
480 	free(p);
481 }
482 
483 property_t *
484 internal_property_find(pgroup_t *pg, const char *name)
485 {
486 	property_t *p;
487 
488 	for (p = uu_list_first(pg->sc_pgroup_props);
489 	    p != NULL;
490 	    p = uu_list_next(pg->sc_pgroup_props, p))
491 		if (strcmp(p->sc_property_name, name) == 0)
492 			return (p);
493 
494 	return (NULL);
495 }
496 
497 value_t *
498 internal_value_new()
499 {
500 	value_t *v;
501 
502 	if ((v = uu_zalloc(sizeof (value_t))) == NULL)
503 		uu_die(gettext("couldn't allocate memory"));
504 
505 	uu_list_node_init(v, &v->sc_node, value_pool);
506 
507 	return (v);
508 }
509 
510 static void
511 internal_value_free_str(value_t *v)
512 {
513 	free(v->sc_u.sc_string);
514 }
515 
516 property_t *
517 internal_property_create(const char *name, scf_type_t vtype, uint_t nvals, ...)
518 {
519 	va_list args;
520 	property_t *p;
521 	value_t *v;
522 
523 	p = internal_property_new();
524 
525 	p->sc_property_name = (char *)name;
526 	p->sc_value_type = vtype;
527 
528 	va_start(args, nvals);
529 	for (; nvals > 0; nvals--) {
530 
531 		v = internal_value_new();
532 		v->sc_type = vtype;
533 
534 		switch (vtype) {
535 		case SCF_TYPE_BOOLEAN:
536 		case SCF_TYPE_COUNT:
537 			v->sc_u.sc_count = va_arg(args, uint64_t);
538 			break;
539 		case SCF_TYPE_INTEGER:
540 			v->sc_u.sc_integer = va_arg(args, int64_t);
541 			break;
542 		case SCF_TYPE_ASTRING:
543 		case SCF_TYPE_FMRI:
544 		case SCF_TYPE_HOST:
545 		case SCF_TYPE_HOSTNAME:
546 		case SCF_TYPE_NET_ADDR_V4:
547 		case SCF_TYPE_NET_ADDR_V6:
548 		case SCF_TYPE_OPAQUE:
549 		case SCF_TYPE_TIME:
550 		case SCF_TYPE_URI:
551 		case SCF_TYPE_USTRING:
552 			v->sc_u.sc_string = (char *)va_arg(args, uchar_t *);
553 			break;
554 		default:
555 			va_end(args);
556 			uu_die(gettext("unknown property type (%d)\n"), vtype);
557 			break;
558 		}
559 
560 		internal_attach_value(p, v);
561 	}
562 	va_end(args);
563 
564 	return (p);
565 }
566 
567 /*
568  * Some of these attach functions use uu_list_append() to maintain the
569  * same order across import/export, whereas others are always sorted
570  * anyway, or the order is irrelevant.
571  */
572 
573 int
574 internal_attach_service(bundle_t *bndl, entity_t *svc)
575 {
576 	if (uu_list_find(bndl->sc_bundle_services, svc, NULL, NULL) != NULL) {
577 		semerr(gettext("Multiple definitions for service %s in "
578 		    "bundle %s.\n"), svc->sc_name, bndl->sc_bundle_name);
579 		return (-1);
580 	}
581 
582 	(void) uu_list_append(bndl->sc_bundle_services, svc);
583 
584 	return (0);
585 }
586 
587 int
588 internal_attach_entity(entity_t *svc, entity_t *ent)
589 {
590 	if (ent->sc_etype == SVCCFG_TEMPLATE_OBJECT) {
591 		svc->sc_u.sc_service.sc_service_template = ent;
592 		return (0);
593 	}
594 
595 	if (svc->sc_etype != SVCCFG_SERVICE_OBJECT)
596 		uu_die(gettext("bad entity attach: %s is not a service\n"),
597 		    svc->sc_name);
598 
599 	if (uu_list_find(svc->sc_u.sc_service.sc_service_instances, ent, NULL,
600 	    NULL) != NULL) {
601 		semerr(gettext("Multiple definitions of entity %s in service "
602 		    "%s.\n"), ent->sc_name, svc->sc_name);
603 		return (-1);
604 	}
605 
606 	(void) uu_list_prepend(svc->sc_u.sc_service.sc_service_instances, ent);
607 	ent->sc_parent = svc;
608 	ent->sc_fmri = uu_msprintf("%s:%s", svc->sc_fmri, ent->sc_name);
609 	if (ent->sc_fmri == NULL)
610 		uu_die(gettext("couldn't allocate memory"));
611 
612 	return (0);
613 }
614 
615 int
616 internal_attach_pgroup(entity_t *ent, pgroup_t *pgrp)
617 {
618 	if (uu_list_find(ent->sc_pgroups, pgrp, NULL, NULL) != NULL) {
619 		semerr(gettext("Multiple definitions of property group %s in "
620 		    "entity %s.\n"), pgrp->sc_pgroup_name, ent->sc_name);
621 		return (-1);
622 	}
623 
624 	(void) uu_list_append(ent->sc_pgroups, pgrp);
625 
626 	pgrp->sc_parent = ent;
627 
628 	return (0);
629 }
630 
631 int
632 internal_attach_dependent(entity_t *ent, pgroup_t *pg)
633 {
634 	if (uu_list_find(ent->sc_dependents, pg, NULL, NULL) != NULL) {
635 		semerr(gettext("Multiple definitions of dependent %s in "
636 		    "entity %s.\n"), pg->sc_pgroup_name, ent->sc_name);
637 		return (-1);
638 	}
639 
640 	(void) uu_list_append(ent->sc_dependents, pg);
641 
642 	pg->sc_parent = ent;
643 
644 	return (0);
645 }
646 
647 /*
648  * Returns
649  *   0 - success
650  *   -1 - prop already exists in pgrp
651  */
652 int
653 internal_attach_property(pgroup_t *pgrp, property_t *prop)
654 {
655 	uu_list_index_t idx;
656 
657 	if (uu_list_find(pgrp->sc_pgroup_props, prop, NULL, &idx) != NULL) {
658 		semerr(gettext("Multiple definitions for property %s in "
659 		    "property group %s.\n"), prop->sc_property_name,
660 		    pgrp->sc_pgroup_name);
661 		return (-1);
662 	}
663 
664 	uu_list_insert(pgrp->sc_pgroup_props, prop, idx);
665 
666 	return (0);
667 }
668 
669 void
670 internal_attach_value(property_t *prop, value_t *val)
671 {
672 	(void) uu_list_append(prop->sc_property_values, val);
673 }
674 
675 /*
676  * These functions create an internal representation of a property group
677  * (pgroup_t) from the repository (scf_propertygroup_t).  They are used by the
678  * import functions in svccfg_libscf.c .
679  *
680  * load_init() must be called first to initialize these globals, and
681  * load_fini() should be called afterwards to destroy them.
682  */
683 
684 static char *loadbuf = NULL;
685 static size_t loadbuf_sz;
686 static scf_property_t *load_prop = NULL;
687 static scf_value_t *load_val = NULL;
688 static scf_iter_t *load_propiter = NULL, *load_valiter = NULL;
689 
690 /*
691  * Initialize the global state for the load_*() routines.
692  * Returns
693  *   0 - success
694  *   ENOMEM - out of memory
695  */
696 int
697 load_init(void)
698 {
699 	loadbuf_sz = ((max_scf_value_len > max_scf_pg_type_len) ?
700 	    max_scf_value_len : max_scf_pg_type_len) + 1;
701 
702 	loadbuf = malloc(loadbuf_sz);
703 	if (loadbuf == NULL)
704 		return (ENOMEM);
705 
706 	if ((load_prop = scf_property_create(g_hndl)) == NULL ||
707 	    (load_val = scf_value_create(g_hndl)) == NULL ||
708 	    (load_propiter = scf_iter_create(g_hndl)) == NULL ||
709 	    (load_valiter = scf_iter_create(g_hndl)) == NULL) {
710 		load_fini();
711 		return (ENOMEM);
712 	}
713 
714 	return (0);
715 }
716 
717 void
718 load_fini(void)
719 {
720 	scf_iter_destroy(load_propiter);
721 	load_propiter = NULL;
722 	scf_iter_destroy(load_valiter);
723 	load_valiter = NULL;
724 	scf_value_destroy(load_val);
725 	load_val = NULL;
726 	scf_property_destroy(load_prop);
727 	load_prop = NULL;
728 	free(loadbuf);
729 	loadbuf = NULL;
730 }
731 
732 /*
733  * Create a property_t which represents an scf_property_t.  Returns
734  *   0 - success
735  *   ECANCELED - prop's pg was deleted
736  *   ECONNABORTED - repository disconnected
737  *   ENOMEM - out of memory
738  *   EACCES - permission denied when reading property
739  */
740 static int
741 load_property(scf_property_t *prop, property_t **ipp)
742 {
743 	property_t *iprop;
744 	int r;
745 	ssize_t ssz;
746 
747 	/* get name */
748 	if (scf_property_get_name(prop, loadbuf, loadbuf_sz) < 0) {
749 		switch (scf_error()) {
750 		case SCF_ERROR_DELETED:
751 			return (ECANCELED);
752 
753 		case SCF_ERROR_CONNECTION_BROKEN:
754 			return (ECONNABORTED);
755 
756 		case SCF_ERROR_NOT_BOUND:
757 		case SCF_ERROR_NOT_SET:
758 		default:
759 			bad_error("scf_property_get_name", scf_error());
760 		}
761 	}
762 
763 	iprop = internal_property_new();
764 	iprop->sc_property_name = strdup(loadbuf);
765 	if (iprop->sc_property_name == NULL) {
766 		internal_property_free(iprop);
767 		return (ENOMEM);
768 	}
769 
770 	/* get type */
771 	if (scf_property_type(prop, &iprop->sc_value_type) != 0) {
772 		switch (scf_error()) {
773 		case SCF_ERROR_DELETED:
774 			r = ECANCELED;
775 			goto out;
776 
777 		case SCF_ERROR_CONNECTION_BROKEN:
778 			r = ECONNABORTED;
779 			goto out;
780 
781 		case SCF_ERROR_NOT_BOUND:
782 		case SCF_ERROR_NOT_SET:
783 		default:
784 			bad_error("scf_property_type", scf_error());
785 		}
786 	}
787 
788 	/* get values */
789 	if (scf_iter_property_values(load_valiter, prop) != 0) {
790 		switch (scf_error()) {
791 		case SCF_ERROR_DELETED:
792 			r = ECANCELED;
793 			goto out;
794 
795 		case SCF_ERROR_CONNECTION_BROKEN:
796 			r = ECONNABORTED;
797 			goto out;
798 
799 		case SCF_ERROR_HANDLE_MISMATCH:
800 		case SCF_ERROR_NOT_BOUND:
801 		case SCF_ERROR_NOT_SET:
802 		default:
803 			bad_error("scf_iter_property_values", scf_error());
804 		}
805 	}
806 
807 	for (;;) {
808 		value_t *ival;
809 
810 		r = scf_iter_next_value(load_valiter, load_val);
811 		if (r == 0)
812 			break;
813 		if (r != 1) {
814 			switch (scf_error()) {
815 			case SCF_ERROR_DELETED:
816 				r = ECANCELED;
817 				goto out;
818 
819 			case SCF_ERROR_CONNECTION_BROKEN:
820 				r = ECONNABORTED;
821 				goto out;
822 
823 			case SCF_ERROR_PERMISSION_DENIED:
824 				r = EACCES;
825 				goto out;
826 
827 			case SCF_ERROR_HANDLE_MISMATCH:
828 			case SCF_ERROR_NOT_BOUND:
829 			case SCF_ERROR_NOT_SET:
830 			case SCF_ERROR_INVALID_ARGUMENT:
831 			default:
832 				bad_error("scf_iter_next_value", scf_error());
833 			}
834 		}
835 
836 		ival = internal_value_new();
837 		ival->sc_type = scf_value_type(load_val);
838 		assert(ival->sc_type != SCF_TYPE_INVALID);
839 
840 		switch (ival->sc_type) {
841 		case SCF_TYPE_BOOLEAN: {
842 			uint8_t b;
843 
844 			r = scf_value_get_boolean(load_val, &b);
845 			if (r != 0)
846 				bad_error("scf_value_get_boolean", scf_error());
847 			ival->sc_u.sc_count = b;
848 			break;
849 		}
850 
851 		case SCF_TYPE_COUNT:
852 			r = scf_value_get_count(load_val, &ival->sc_u.sc_count);
853 			if (r != 0)
854 				bad_error("scf_value_get_count", scf_error());
855 			break;
856 
857 		case SCF_TYPE_INTEGER:
858 			r = scf_value_get_integer(load_val,
859 			    &ival->sc_u.sc_integer);
860 			if (r != 0)
861 				bad_error("scf_value_get_integer", scf_error());
862 			break;
863 
864 		default:
865 			ssz = scf_value_get_as_string(load_val, loadbuf,
866 			    loadbuf_sz);
867 			if (ssz < 0)
868 				bad_error("scf_value_get_as_string",
869 				    scf_error());
870 
871 			ival->sc_u.sc_string = strdup(loadbuf);
872 			if (ival->sc_u.sc_string == NULL) {
873 				r = ENOMEM;
874 				goto out;
875 			}
876 
877 			ival->sc_free = internal_value_free_str;
878 		}
879 
880 		internal_attach_value(iprop, ival);
881 	}
882 
883 	*ipp = iprop;
884 	return (0);
885 
886 out:
887 	free(iprop->sc_property_name);
888 	internal_property_free(iprop);
889 	return (r);
890 }
891 
892 /*
893  * Returns
894  *   0 - success
895  *   ECANCELED - pg was deleted
896  *   ECONNABORTED - repository disconnected
897  *   ENOMEM - out of memory
898  */
899 int
900 load_pg_attrs(const scf_propertygroup_t *pg, pgroup_t **ipgp)
901 {
902 	pgroup_t *ipg;
903 
904 	ipg = internal_pgroup_new();
905 
906 	if (scf_pg_get_flags(pg, &ipg->sc_pgroup_flags) != 0) {
907 		switch (scf_error()) {
908 		case SCF_ERROR_DELETED:
909 			internal_pgroup_free(ipg);
910 			return (ECANCELED);
911 
912 		case SCF_ERROR_CONNECTION_BROKEN:
913 			internal_pgroup_free(ipg);
914 			return (ECONNABORTED);
915 
916 		case SCF_ERROR_NOT_SET:
917 		case SCF_ERROR_NOT_BOUND:
918 		default:
919 			bad_error("scf_pg_get_name", scf_error());
920 		}
921 	}
922 
923 	if (scf_pg_get_name(pg, loadbuf, loadbuf_sz) < 0) {
924 		switch (scf_error()) {
925 		case SCF_ERROR_DELETED:
926 			internal_pgroup_free(ipg);
927 			return (ECANCELED);
928 
929 		case SCF_ERROR_CONNECTION_BROKEN:
930 			internal_pgroup_free(ipg);
931 			return (ECONNABORTED);
932 
933 		case SCF_ERROR_NOT_SET:
934 		case SCF_ERROR_NOT_BOUND:
935 		default:
936 			bad_error("scf_pg_get_name", scf_error());
937 		}
938 	}
939 
940 	ipg->sc_pgroup_name = strdup(loadbuf);
941 	if (ipg->sc_pgroup_name == NULL) {
942 		internal_pgroup_free(ipg);
943 		return (ENOMEM);
944 	}
945 
946 	if (scf_pg_get_type(pg, loadbuf, loadbuf_sz) < 0) {
947 		switch (scf_error()) {
948 		case SCF_ERROR_DELETED:
949 			free((char *)ipg->sc_pgroup_name);
950 			internal_pgroup_free(ipg);
951 			return (ECANCELED);
952 
953 		case SCF_ERROR_CONNECTION_BROKEN:
954 			free((char *)ipg->sc_pgroup_name);
955 			internal_pgroup_free(ipg);
956 			return (ECONNABORTED);
957 
958 		case SCF_ERROR_NOT_SET:
959 		case SCF_ERROR_NOT_BOUND:
960 		default:
961 			bad_error("scf_pg_get_name", scf_error());
962 		}
963 	}
964 
965 	ipg->sc_pgroup_type = strdup(loadbuf);
966 	if (ipg->sc_pgroup_type == NULL) {
967 		free((char *)ipg->sc_pgroup_name);
968 		internal_pgroup_free(ipg);
969 		return (ENOMEM);
970 	}
971 
972 	*ipgp = ipg;
973 	return (0);
974 }
975 
976 /*
977  * Load a property group into a pgroup_t.  Returns
978  *   0 - success
979  *   ECANCELED - pg was deleted
980  *   ECONNABORTED - repository disconnected
981  *   EBADF - pg is corrupt (error printed if fmri is given)
982  *   ENOMEM - out of memory
983  *   EACCES - permission denied when reading property
984  */
985 int
986 load_pg(const scf_propertygroup_t *pg, pgroup_t **ipgp, const char *fmri,
987     const char *snapname)
988 {
989 	pgroup_t *ipg;
990 	int r;
991 
992 	if (scf_iter_pg_properties(load_propiter, pg) != 0) {
993 		switch (scf_error()) {
994 		case SCF_ERROR_DELETED:
995 			return (ECANCELED);
996 
997 		case SCF_ERROR_CONNECTION_BROKEN:
998 			return (ECONNABORTED);
999 
1000 		case SCF_ERROR_HANDLE_MISMATCH:
1001 		case SCF_ERROR_NOT_SET:
1002 		case SCF_ERROR_NOT_BOUND:
1003 		default:
1004 			bad_error("scf_iter_pg_properties", scf_error());
1005 		}
1006 	}
1007 
1008 	r = load_pg_attrs(pg, &ipg);
1009 	switch (r) {
1010 	case 0:
1011 		break;
1012 
1013 	case ECANCELED:
1014 	case ECONNABORTED:
1015 	case ENOMEM:
1016 		return (r);
1017 
1018 	default:
1019 		bad_error("load_pg_attrs", r);
1020 	}
1021 
1022 	for (;;) {
1023 		property_t *iprop;
1024 
1025 		r = scf_iter_next_property(load_propiter, load_prop);
1026 		if (r == 0)
1027 			break;
1028 		if (r != 1) {
1029 			switch (scf_error()) {
1030 			case SCF_ERROR_DELETED:
1031 				r = ECANCELED;
1032 				goto out;
1033 
1034 			case SCF_ERROR_CONNECTION_BROKEN:
1035 				r = ECONNABORTED;
1036 				goto out;
1037 
1038 			case SCF_ERROR_HANDLE_MISMATCH:
1039 			case SCF_ERROR_NOT_BOUND:
1040 			case SCF_ERROR_NOT_SET:
1041 			case SCF_ERROR_INVALID_ARGUMENT:
1042 			default:
1043 				bad_error("scf_iter_next_property",
1044 				    scf_error());
1045 			}
1046 		}
1047 
1048 		r = load_property(load_prop, &iprop);
1049 		switch (r) {
1050 		case 0:
1051 			break;
1052 
1053 		case ECANCELED:
1054 		case ECONNABORTED:
1055 		case ENOMEM:
1056 		case EACCES:
1057 			goto out;
1058 
1059 		default:
1060 			bad_error("load_property", r);
1061 		}
1062 
1063 		r = internal_attach_property(ipg, iprop);
1064 		if (r != 0) {
1065 			if (fmri != NULL) {
1066 				if (snapname == NULL)
1067 					warn(gettext("Property group \"%s\" of "
1068 					    "%s has multiple definitions of "
1069 					    "property \"%s\".\n"),
1070 					    ipg->sc_pgroup_name, fmri,
1071 					    iprop->sc_property_name);
1072 				else
1073 					warn(gettext("Property group \"%s\" of "
1074 					    "the \"%s\" snapshot of %s has "
1075 					    "multiple definitions of property "
1076 					    "\"%s\".\n"),
1077 					    ipg->sc_pgroup_name, snapname, fmri,
1078 					    iprop->sc_property_name);
1079 			}
1080 			r = EBADF;
1081 			goto out;
1082 		}
1083 	}
1084 
1085 	*ipgp = ipg;
1086 	return (0);
1087 
1088 out:
1089 	internal_pgroup_free(ipg);
1090 	return (r);
1091 }
1092 
1093 /*
1094  * These functions compare internal property groups and properties (pgroup_t
1095  * & property_t).  They return 1 if the given structures are equal and
1096  * 0 otherwise.  Some will report the differences between the two structures.
1097  * They are used by the import functions in svccfg_libscf.c .
1098  */
1099 
1100 int
1101 prop_equal(property_t *p1, property_t *p2, const char *fmri, const char *pgname,
1102     int new)
1103 {
1104 	value_t *v1, *v2;
1105 
1106 	const char * const values_diff = gettext("Conflict upgrading %s "
1107 	    "(property \"%s/%s\" has different values).\n");
1108 	const char * const values_diff_new = gettext("Conflict upgrading %s "
1109 	    "(new property \"%s/%s\" has different values).\n");
1110 
1111 	assert((fmri == NULL) == (pgname == NULL));
1112 
1113 	if (fmri != NULL) {
1114 		/*
1115 		 * If we find any differences, we'll report conflicts.  But
1116 		 * conflict messages won't make any sense if the names don't
1117 		 * match.  If the caller supplied fmri, assert that the names
1118 		 * match.
1119 		 */
1120 		assert(strcmp(p1->sc_property_name, p2->sc_property_name) == 0);
1121 	} else {
1122 		if (strcmp(p1->sc_property_name, p2->sc_property_name) != 0)
1123 			return (0);
1124 	}
1125 
1126 	if (p1->sc_value_type != p2->sc_value_type) {
1127 		if (fmri != NULL) {
1128 			if (new)
1129 				warn(gettext("Conflict upgrading %s "
1130 				    "(new property \"%s/%s\" has different "
1131 				    "type).\n"), fmri, pgname,
1132 				    p1->sc_property_name);
1133 			else
1134 				warn(gettext("Conflict upgrading %s "
1135 				    "(property \"%s/%s\" has different "
1136 				    "type).\n"), fmri, pgname,
1137 				    p1->sc_property_name);
1138 		}
1139 		return (0);
1140 	}
1141 
1142 	if (uu_list_numnodes(p1->sc_property_values) !=
1143 	    uu_list_numnodes(p2->sc_property_values)) {
1144 		if (fmri != NULL)
1145 			warn(new ? values_diff_new : values_diff, fmri,
1146 			    pgname, p1->sc_property_name);
1147 		return (0);
1148 	}
1149 
1150 	v1 = uu_list_first(p1->sc_property_values);
1151 	v2 = uu_list_first(p2->sc_property_values);
1152 
1153 	while (v1 != NULL) {
1154 		assert(v2 != NULL);
1155 
1156 		if (value_cmp(v1, v2, NULL) != 0) {
1157 			if (fmri != NULL)
1158 				warn(new ? values_diff_new : values_diff,
1159 				    fmri, pgname, p1->sc_property_name);
1160 			return (0);
1161 		}
1162 
1163 		v1 = uu_list_next(p1->sc_property_values, v1);
1164 		v2 = uu_list_next(p2->sc_property_values, v2);
1165 	}
1166 
1167 	return (1);
1168 }
1169 
1170 int
1171 pg_attrs_equal(const pgroup_t *pg1, const pgroup_t *pg2, const char *fmri,
1172     int new)
1173 {
1174 	if (strcmp(pg1->sc_pgroup_name, pg2->sc_pgroup_name) != 0) {
1175 		assert(fmri == NULL);
1176 		return (0);
1177 	}
1178 
1179 	if (pg1->sc_pgroup_flags != pg2->sc_pgroup_flags) {
1180 		if (fmri) {
1181 			if (new)
1182 				warn(gettext("Conflict upgrading %s "
1183 				    "(new property group \"%s\" has different "
1184 				    "flags).\n"), fmri, pg1->sc_pgroup_name);
1185 			else
1186 				warn(gettext("Conflict upgrading %s "
1187 				    "(property group \"%s\" has different "
1188 				    "flags).\n"), fmri, pg1->sc_pgroup_name);
1189 		}
1190 		return (0);
1191 	}
1192 
1193 	if (strcmp(pg1->sc_pgroup_type, pg2->sc_pgroup_type) != 0) {
1194 		if (fmri) {
1195 			if (new)
1196 				warn(gettext("Conflict upgrading %s "
1197 				    "(new property group \"%s\" has different "
1198 				    "type).\n"), fmri, pg1->sc_pgroup_name);
1199 			else
1200 				warn(gettext("Conflict upgrading %s "
1201 				    "(property group \"%s\" has different "
1202 				    "type).\n"), fmri, pg1->sc_pgroup_name);
1203 		}
1204 		return (0);
1205 	}
1206 
1207 	return (1);
1208 }
1209 
1210 int
1211 pg_equal(pgroup_t *pg1, pgroup_t *pg2)
1212 {
1213 	property_t *p1, *p2;
1214 
1215 	if (!pg_attrs_equal(pg1, pg2, NULL, 0))
1216 		return (0);
1217 
1218 	if (uu_list_numnodes(pg1->sc_pgroup_props) !=
1219 	    uu_list_numnodes(pg2->sc_pgroup_props))
1220 		return (0);
1221 
1222 	p1 = uu_list_first(pg1->sc_pgroup_props);
1223 	p2 = uu_list_first(pg2->sc_pgroup_props);
1224 
1225 	while (p1 != NULL) {
1226 		assert(p2 != NULL);
1227 
1228 		if (!prop_equal(p1, p2, NULL, NULL, 0))
1229 			return (0);
1230 
1231 		p1 = uu_list_next(pg1->sc_pgroup_props, p1);
1232 		p2 = uu_list_next(pg2->sc_pgroup_props, p2);
1233 	}
1234 
1235 	return (1);
1236 }
1237