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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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, UU_LIST_SORTED);
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 	uu_list_index_t idx;
673 
674 	(void) uu_list_find(prop->sc_property_values, val, NULL, &idx);
675 	uu_list_insert(prop->sc_property_values, val, idx);
676 }
677 
678 /*
679  * These functions create an internal representation of a property group
680  * (pgroup_t) from the repository (scf_propertygroup_t).  They are used by the
681  * import functions in svccfg_libscf.c .
682  *
683  * load_init() must be called first to initialize these globals, and
684  * load_fini() should be called afterwards to destroy them.
685  */
686 
687 static char *loadbuf = NULL;
688 static size_t loadbuf_sz;
689 static scf_property_t *load_prop = NULL;
690 static scf_value_t *load_val = NULL;
691 static scf_iter_t *load_propiter = NULL, *load_valiter = NULL;
692 
693 /*
694  * Initialize the global state for the load_*() routines.
695  * Returns
696  *   0 - success
697  *   ENOMEM - out of memory
698  */
699 int
700 load_init(void)
701 {
702 	loadbuf_sz = ((max_scf_value_len > max_scf_pg_type_len) ?
703 	    max_scf_value_len : max_scf_pg_type_len) + 1;
704 
705 	loadbuf = malloc(loadbuf_sz);
706 	if (loadbuf == NULL)
707 		return (ENOMEM);
708 
709 	if ((load_prop = scf_property_create(g_hndl)) == NULL ||
710 	    (load_val = scf_value_create(g_hndl)) == NULL ||
711 	    (load_propiter = scf_iter_create(g_hndl)) == NULL ||
712 	    (load_valiter = scf_iter_create(g_hndl)) == NULL) {
713 		load_fini();
714 		return (ENOMEM);
715 	}
716 
717 	return (0);
718 }
719 
720 void
721 load_fini(void)
722 {
723 	scf_iter_destroy(load_propiter);
724 	load_propiter = NULL;
725 	scf_iter_destroy(load_valiter);
726 	load_valiter = NULL;
727 	scf_value_destroy(load_val);
728 	load_val = NULL;
729 	scf_property_destroy(load_prop);
730 	load_prop = NULL;
731 	free(loadbuf);
732 	loadbuf = NULL;
733 }
734 
735 /*
736  * Create a property_t which represents an scf_property_t.  Returns
737  *   0 - success
738  *   ECANCELED - prop's pg was deleted
739  *   ECONNABORTED - repository disconnected
740  *   ENOMEM - out of memory
741  */
742 static int
743 load_property(scf_property_t *prop, property_t **ipp)
744 {
745 	property_t *iprop;
746 	int r;
747 	ssize_t ssz;
748 
749 	/* get name */
750 	if (scf_property_get_name(prop, loadbuf, loadbuf_sz) < 0) {
751 		switch (scf_error()) {
752 		case SCF_ERROR_DELETED:
753 			return (ECANCELED);
754 
755 		case SCF_ERROR_CONNECTION_BROKEN:
756 			return (ECONNABORTED);
757 
758 		case SCF_ERROR_NOT_BOUND:
759 		case SCF_ERROR_NOT_SET:
760 		default:
761 			bad_error("scf_property_get_name", scf_error());
762 		}
763 	}
764 
765 	iprop = internal_property_new();
766 	iprop->sc_property_name = strdup(loadbuf);
767 	if (iprop->sc_property_name == NULL) {
768 		internal_property_free(iprop);
769 		return (ENOMEM);
770 	}
771 
772 	/* get type */
773 	if (scf_property_type(prop, &iprop->sc_value_type) != 0) {
774 		switch (scf_error()) {
775 		case SCF_ERROR_DELETED:
776 			r = ECANCELED;
777 			goto out;
778 
779 		case SCF_ERROR_CONNECTION_BROKEN:
780 			r = ECONNABORTED;
781 			goto out;
782 
783 		case SCF_ERROR_NOT_BOUND:
784 		case SCF_ERROR_NOT_SET:
785 		default:
786 			bad_error("scf_property_type", scf_error());
787 		}
788 	}
789 
790 	/* get values */
791 	if (scf_iter_property_values(load_valiter, prop) != 0) {
792 		switch (scf_error()) {
793 		case SCF_ERROR_DELETED:
794 			r = ECANCELED;
795 			goto out;
796 
797 		case SCF_ERROR_CONNECTION_BROKEN:
798 			r = ECONNABORTED;
799 			goto out;
800 
801 		case SCF_ERROR_HANDLE_MISMATCH:
802 		case SCF_ERROR_NOT_BOUND:
803 		case SCF_ERROR_NOT_SET:
804 		default:
805 			bad_error("scf_iter_property_values", scf_error());
806 		}
807 	}
808 
809 	for (;;) {
810 		value_t *ival;
811 
812 		r = scf_iter_next_value(load_valiter, load_val);
813 		if (r == 0)
814 			break;
815 		if (r != 1) {
816 			switch (scf_error()) {
817 			case SCF_ERROR_DELETED:
818 				r = ECANCELED;
819 				goto out;
820 
821 			case SCF_ERROR_CONNECTION_BROKEN:
822 				r = ECONNABORTED;
823 				goto out;
824 
825 			case SCF_ERROR_HANDLE_MISMATCH:
826 			case SCF_ERROR_NOT_BOUND:
827 			case SCF_ERROR_NOT_SET:
828 			case SCF_ERROR_INVALID_ARGUMENT:
829 			default:
830 				bad_error("scf_iter_next_value", scf_error());
831 			}
832 		}
833 
834 		ival = internal_value_new();
835 		ival->sc_type = scf_value_type(load_val);
836 		assert(ival->sc_type != SCF_TYPE_INVALID);
837 
838 		switch (ival->sc_type) {
839 		case SCF_TYPE_BOOLEAN: {
840 			uint8_t b;
841 
842 			r = scf_value_get_boolean(load_val, &b);
843 			if (r != 0)
844 				bad_error("scf_value_get_boolean", scf_error());
845 			ival->sc_u.sc_count = b;
846 			break;
847 		}
848 
849 		case SCF_TYPE_COUNT:
850 			r = scf_value_get_count(load_val, &ival->sc_u.sc_count);
851 			if (r != 0)
852 				bad_error("scf_value_get_count", scf_error());
853 			break;
854 
855 		case SCF_TYPE_INTEGER:
856 			r = scf_value_get_integer(load_val,
857 			    &ival->sc_u.sc_integer);
858 			if (r != 0)
859 				bad_error("scf_value_get_integer", scf_error());
860 			break;
861 
862 		default:
863 			ssz = scf_value_get_as_string(load_val, loadbuf,
864 			    loadbuf_sz);
865 			if (ssz < 0)
866 				bad_error("scf_value_get_as_string",
867 				    scf_error());
868 
869 			ival->sc_u.sc_string = strdup(loadbuf);
870 			if (ival->sc_u.sc_string == NULL) {
871 				r = ENOMEM;
872 				goto out;
873 			}
874 
875 			ival->sc_free = internal_value_free_str;
876 		}
877 
878 		internal_attach_value(iprop, ival);
879 	}
880 
881 	*ipp = iprop;
882 	return (0);
883 
884 out:
885 	free(iprop->sc_property_name);
886 	internal_property_free(iprop);
887 	return (r);
888 }
889 
890 /*
891  * Returns
892  *   0 - success
893  *   ECANCELED - pg was deleted
894  *   ECONNABORTED - repository disconnected
895  *   ENOMEM - out of memory
896  */
897 int
898 load_pg_attrs(const scf_propertygroup_t *pg, pgroup_t **ipgp)
899 {
900 	pgroup_t *ipg;
901 
902 	ipg = internal_pgroup_new();
903 
904 	if (scf_pg_get_flags(pg, &ipg->sc_pgroup_flags) != 0) {
905 		switch (scf_error()) {
906 		case SCF_ERROR_DELETED:
907 			internal_pgroup_free(ipg);
908 			return (ECANCELED);
909 
910 		case SCF_ERROR_CONNECTION_BROKEN:
911 			internal_pgroup_free(ipg);
912 			return (ECONNABORTED);
913 
914 		case SCF_ERROR_NOT_SET:
915 		case SCF_ERROR_NOT_BOUND:
916 		default:
917 			bad_error("scf_pg_get_name", scf_error());
918 		}
919 	}
920 
921 	if (scf_pg_get_name(pg, loadbuf, loadbuf_sz) < 0) {
922 		switch (scf_error()) {
923 		case SCF_ERROR_DELETED:
924 			internal_pgroup_free(ipg);
925 			return (ECANCELED);
926 
927 		case SCF_ERROR_CONNECTION_BROKEN:
928 			internal_pgroup_free(ipg);
929 			return (ECONNABORTED);
930 
931 		case SCF_ERROR_NOT_SET:
932 		case SCF_ERROR_NOT_BOUND:
933 		default:
934 			bad_error("scf_pg_get_name", scf_error());
935 		}
936 	}
937 
938 	ipg->sc_pgroup_name = strdup(loadbuf);
939 	if (ipg->sc_pgroup_name == NULL) {
940 		internal_pgroup_free(ipg);
941 		return (ENOMEM);
942 	}
943 
944 	if (scf_pg_get_type(pg, loadbuf, loadbuf_sz) < 0) {
945 		switch (scf_error()) {
946 		case SCF_ERROR_DELETED:
947 			free((char *)ipg->sc_pgroup_name);
948 			internal_pgroup_free(ipg);
949 			return (ECANCELED);
950 
951 		case SCF_ERROR_CONNECTION_BROKEN:
952 			free((char *)ipg->sc_pgroup_name);
953 			internal_pgroup_free(ipg);
954 			return (ECONNABORTED);
955 
956 		case SCF_ERROR_NOT_SET:
957 		case SCF_ERROR_NOT_BOUND:
958 		default:
959 			bad_error("scf_pg_get_name", scf_error());
960 		}
961 	}
962 
963 	ipg->sc_pgroup_type = strdup(loadbuf);
964 	if (ipg->sc_pgroup_type == NULL) {
965 		free((char *)ipg->sc_pgroup_name);
966 		internal_pgroup_free(ipg);
967 		return (ENOMEM);
968 	}
969 
970 	*ipgp = ipg;
971 	return (0);
972 }
973 
974 /*
975  * Load a property group into a pgroup_t.  Returns
976  *   0 - success
977  *   ECANCELED - pg was deleted
978  *   ECONNABORTED - repository disconnected
979  *   EBADF - pg is corrupt (error printed if fmri is given)
980  *   ENOMEM - out of memory
981  */
982 int
983 load_pg(const scf_propertygroup_t *pg, pgroup_t **ipgp, const char *fmri,
984     const char *snapname)
985 {
986 	pgroup_t *ipg;
987 	int r;
988 
989 	if (scf_iter_pg_properties(load_propiter, pg) != 0) {
990 		switch (scf_error()) {
991 		case SCF_ERROR_DELETED:
992 			return (ECANCELED);
993 
994 		case SCF_ERROR_CONNECTION_BROKEN:
995 			return (ECONNABORTED);
996 
997 		case SCF_ERROR_HANDLE_MISMATCH:
998 		case SCF_ERROR_NOT_SET:
999 		case SCF_ERROR_NOT_BOUND:
1000 		default:
1001 			bad_error("scf_iter_pg_properties", scf_error());
1002 		}
1003 	}
1004 
1005 	r = load_pg_attrs(pg, &ipg);
1006 	switch (r) {
1007 	case 0:
1008 		break;
1009 
1010 	case ECANCELED:
1011 	case ECONNABORTED:
1012 	case ENOMEM:
1013 		return (r);
1014 
1015 	default:
1016 		bad_error("load_pg_attrs", r);
1017 	}
1018 
1019 	for (;;) {
1020 		property_t *iprop;
1021 
1022 		r = scf_iter_next_property(load_propiter, load_prop);
1023 		if (r == 0)
1024 			break;
1025 		if (r != 1) {
1026 			switch (scf_error()) {
1027 			case SCF_ERROR_DELETED:
1028 				r = ECANCELED;
1029 				goto out;
1030 
1031 			case SCF_ERROR_CONNECTION_BROKEN:
1032 				r = ECONNABORTED;
1033 				goto out;
1034 
1035 			case SCF_ERROR_HANDLE_MISMATCH:
1036 			case SCF_ERROR_NOT_BOUND:
1037 			case SCF_ERROR_NOT_SET:
1038 			case SCF_ERROR_INVALID_ARGUMENT:
1039 			default:
1040 				bad_error("scf_iter_next_property",
1041 				    scf_error());
1042 			}
1043 		}
1044 
1045 		r = load_property(load_prop, &iprop);
1046 		switch (r) {
1047 		case 0:
1048 			break;
1049 
1050 		case ECANCELED:
1051 		case ECONNABORTED:
1052 		case ENOMEM:
1053 			goto out;
1054 
1055 		default:
1056 			bad_error("load_property", r);
1057 		}
1058 
1059 		r = internal_attach_property(ipg, iprop);
1060 		if (r != 0) {
1061 			if (fmri != NULL) {
1062 				if (snapname == NULL)
1063 					warn(gettext("Property group \"%s\" of "
1064 					    "%s has multiple definitions of "
1065 					    "property \"%s\".\n"),
1066 					    ipg->sc_pgroup_name, fmri,
1067 					    iprop->sc_property_name);
1068 				else
1069 					warn(gettext("Property group \"%s\" of "
1070 					    "the \"%s\" snapshot of %s has "
1071 					    "multiple definitions of property "
1072 					    "\"%s\".\n"),
1073 					    ipg->sc_pgroup_name, snapname, fmri,
1074 					    iprop->sc_property_name);
1075 			}
1076 			r = EBADF;
1077 			goto out;
1078 		}
1079 	}
1080 
1081 	*ipgp = ipg;
1082 	return (0);
1083 
1084 out:
1085 	internal_pgroup_free(ipg);
1086 	return (r);
1087 }
1088 
1089 /*
1090  * These functions compare internal property groups and properties (pgroup_t
1091  * & property_t).  They return 1 if the given structures are equal and
1092  * 0 otherwise.  Some will report the differences between the two structures.
1093  * They are used by the import functions in svccfg_libscf.c .
1094  */
1095 
1096 int
1097 prop_equal(property_t *p1, property_t *p2, const char *fmri, const char *pgname,
1098     int new)
1099 {
1100 	value_t *v1, *v2;
1101 
1102 	const char * const values_diff = gettext("Conflict upgrading %s "
1103 	    "(property \"%s/%s\" has different values).\n");
1104 	const char * const values_diff_new = gettext("Conflict upgrading %s "
1105 	    "(new property \"%s/%s\" has different values).\n");
1106 
1107 	assert((fmri == NULL) == (pgname == NULL));
1108 
1109 	if (fmri != NULL) {
1110 		/*
1111 		 * If we find any differences, we'll report conflicts.  But
1112 		 * conflict messages won't make any sense if the names don't
1113 		 * match.  If the caller supplied fmri, assert that the names
1114 		 * match.
1115 		 */
1116 		assert(strcmp(p1->sc_property_name, p2->sc_property_name) == 0);
1117 	} else {
1118 		if (strcmp(p1->sc_property_name, p2->sc_property_name) != 0)
1119 			return (0);
1120 	}
1121 
1122 	if (p1->sc_value_type != p2->sc_value_type) {
1123 		if (fmri != NULL) {
1124 			if (new)
1125 				warn(gettext("Conflict upgrading %s "
1126 				    "(new property \"%s/%s\" has different "
1127 				    "type).\n"), fmri, pgname,
1128 				    p1->sc_property_name);
1129 			else
1130 				warn(gettext("Conflict upgrading %s "
1131 				    "(property \"%s/%s\" has different "
1132 				    "type).\n"), fmri, pgname,
1133 				    p1->sc_property_name);
1134 		}
1135 		return (0);
1136 	}
1137 
1138 	if (uu_list_numnodes(p1->sc_property_values) !=
1139 	    uu_list_numnodes(p2->sc_property_values)) {
1140 		if (fmri != NULL)
1141 			warn(new ? values_diff_new : values_diff, fmri,
1142 			    pgname, p1->sc_property_name);
1143 		return (0);
1144 	}
1145 
1146 	v1 = uu_list_first(p1->sc_property_values);
1147 	v2 = uu_list_first(p2->sc_property_values);
1148 
1149 	while (v1 != NULL) {
1150 		assert(v2 != NULL);
1151 
1152 		if (value_cmp(v1, v2, NULL) != 0) {
1153 			if (fmri != NULL)
1154 				warn(new ? values_diff_new : values_diff,
1155 				    fmri, pgname, p1->sc_property_name);
1156 			return (0);
1157 		}
1158 
1159 		v1 = uu_list_next(p1->sc_property_values, v1);
1160 		v2 = uu_list_next(p2->sc_property_values, v2);
1161 	}
1162 
1163 	return (1);
1164 }
1165 
1166 int
1167 pg_attrs_equal(const pgroup_t *pg1, const pgroup_t *pg2, const char *fmri,
1168     int new)
1169 {
1170 	if (strcmp(pg1->sc_pgroup_name, pg2->sc_pgroup_name) != 0) {
1171 		assert(fmri == NULL);
1172 		return (0);
1173 	}
1174 
1175 	if (pg1->sc_pgroup_flags != pg2->sc_pgroup_flags) {
1176 		if (fmri) {
1177 			if (new)
1178 				warn(gettext("Conflict upgrading %s "
1179 				    "(new property group \"%s\" has different "
1180 				    "flags).\n"), fmri, pg1->sc_pgroup_name);
1181 			else
1182 				warn(gettext("Conflict upgrading %s "
1183 				    "(property group \"%s\" has different "
1184 				    "flags).\n"), fmri, pg1->sc_pgroup_name);
1185 		}
1186 		return (0);
1187 	}
1188 
1189 	if (strcmp(pg1->sc_pgroup_type, pg2->sc_pgroup_type) != 0) {
1190 		if (fmri) {
1191 			if (new)
1192 				warn(gettext("Conflict upgrading %s "
1193 				    "(new property group \"%s\" has different "
1194 				    "type).\n"), fmri, pg1->sc_pgroup_name);
1195 			else
1196 				warn(gettext("Conflict upgrading %s "
1197 				    "(property group \"%s\" has different "
1198 				    "type).\n"), fmri, pg1->sc_pgroup_name);
1199 		}
1200 		return (0);
1201 	}
1202 
1203 	return (1);
1204 }
1205 
1206 int
1207 pg_equal(pgroup_t *pg1, pgroup_t *pg2)
1208 {
1209 	property_t *p1, *p2;
1210 
1211 	if (!pg_attrs_equal(pg1, pg2, NULL, 0))
1212 		return (0);
1213 
1214 	if (uu_list_numnodes(pg1->sc_pgroup_props) !=
1215 	    uu_list_numnodes(pg2->sc_pgroup_props))
1216 		return (0);
1217 
1218 	p1 = uu_list_first(pg1->sc_pgroup_props);
1219 	p2 = uu_list_first(pg2->sc_pgroup_props);
1220 
1221 	while (p1 != NULL) {
1222 		assert(p2 != NULL);
1223 
1224 		if (!prop_equal(p1, p2, NULL, NULL, 0))
1225 			return (0);
1226 
1227 		p1 = uu_list_next(pg1->sc_pgroup_props, p1);
1228 		p2 = uu_list_next(pg2->sc_pgroup_props, p2);
1229 	}
1230 
1231 	return (1);
1232 }
1233