xref: /illumos-gate/usr/src/cmd/svc/svccfg/svccfg_internal.c (revision 89a7715a55deca73d03076f5c24463717f0aaa91)
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 2007 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  *   EACCES - permission denied when reading property
742  */
743 static int
744 load_property(scf_property_t *prop, property_t **ipp)
745 {
746 	property_t *iprop;
747 	int r;
748 	ssize_t ssz;
749 
750 	/* get name */
751 	if (scf_property_get_name(prop, loadbuf, loadbuf_sz) < 0) {
752 		switch (scf_error()) {
753 		case SCF_ERROR_DELETED:
754 			return (ECANCELED);
755 
756 		case SCF_ERROR_CONNECTION_BROKEN:
757 			return (ECONNABORTED);
758 
759 		case SCF_ERROR_NOT_BOUND:
760 		case SCF_ERROR_NOT_SET:
761 		default:
762 			bad_error("scf_property_get_name", scf_error());
763 		}
764 	}
765 
766 	iprop = internal_property_new();
767 	iprop->sc_property_name = strdup(loadbuf);
768 	if (iprop->sc_property_name == NULL) {
769 		internal_property_free(iprop);
770 		return (ENOMEM);
771 	}
772 
773 	/* get type */
774 	if (scf_property_type(prop, &iprop->sc_value_type) != 0) {
775 		switch (scf_error()) {
776 		case SCF_ERROR_DELETED:
777 			r = ECANCELED;
778 			goto out;
779 
780 		case SCF_ERROR_CONNECTION_BROKEN:
781 			r = ECONNABORTED;
782 			goto out;
783 
784 		case SCF_ERROR_NOT_BOUND:
785 		case SCF_ERROR_NOT_SET:
786 		default:
787 			bad_error("scf_property_type", scf_error());
788 		}
789 	}
790 
791 	/* get values */
792 	if (scf_iter_property_values(load_valiter, prop) != 0) {
793 		switch (scf_error()) {
794 		case SCF_ERROR_DELETED:
795 			r = ECANCELED;
796 			goto out;
797 
798 		case SCF_ERROR_CONNECTION_BROKEN:
799 			r = ECONNABORTED;
800 			goto out;
801 
802 		case SCF_ERROR_HANDLE_MISMATCH:
803 		case SCF_ERROR_NOT_BOUND:
804 		case SCF_ERROR_NOT_SET:
805 		default:
806 			bad_error("scf_iter_property_values", scf_error());
807 		}
808 	}
809 
810 	for (;;) {
811 		value_t *ival;
812 
813 		r = scf_iter_next_value(load_valiter, load_val);
814 		if (r == 0)
815 			break;
816 		if (r != 1) {
817 			switch (scf_error()) {
818 			case SCF_ERROR_DELETED:
819 				r = ECANCELED;
820 				goto out;
821 
822 			case SCF_ERROR_CONNECTION_BROKEN:
823 				r = ECONNABORTED;
824 				goto out;
825 
826 			case SCF_ERROR_PERMISSION_DENIED:
827 				r = EACCES;
828 				goto out;
829 
830 			case SCF_ERROR_HANDLE_MISMATCH:
831 			case SCF_ERROR_NOT_BOUND:
832 			case SCF_ERROR_NOT_SET:
833 			case SCF_ERROR_INVALID_ARGUMENT:
834 			default:
835 				bad_error("scf_iter_next_value", scf_error());
836 			}
837 		}
838 
839 		ival = internal_value_new();
840 		ival->sc_type = scf_value_type(load_val);
841 		assert(ival->sc_type != SCF_TYPE_INVALID);
842 
843 		switch (ival->sc_type) {
844 		case SCF_TYPE_BOOLEAN: {
845 			uint8_t b;
846 
847 			r = scf_value_get_boolean(load_val, &b);
848 			if (r != 0)
849 				bad_error("scf_value_get_boolean", scf_error());
850 			ival->sc_u.sc_count = b;
851 			break;
852 		}
853 
854 		case SCF_TYPE_COUNT:
855 			r = scf_value_get_count(load_val, &ival->sc_u.sc_count);
856 			if (r != 0)
857 				bad_error("scf_value_get_count", scf_error());
858 			break;
859 
860 		case SCF_TYPE_INTEGER:
861 			r = scf_value_get_integer(load_val,
862 			    &ival->sc_u.sc_integer);
863 			if (r != 0)
864 				bad_error("scf_value_get_integer", scf_error());
865 			break;
866 
867 		default:
868 			ssz = scf_value_get_as_string(load_val, loadbuf,
869 			    loadbuf_sz);
870 			if (ssz < 0)
871 				bad_error("scf_value_get_as_string",
872 				    scf_error());
873 
874 			ival->sc_u.sc_string = strdup(loadbuf);
875 			if (ival->sc_u.sc_string == NULL) {
876 				r = ENOMEM;
877 				goto out;
878 			}
879 
880 			ival->sc_free = internal_value_free_str;
881 		}
882 
883 		internal_attach_value(iprop, ival);
884 	}
885 
886 	*ipp = iprop;
887 	return (0);
888 
889 out:
890 	free(iprop->sc_property_name);
891 	internal_property_free(iprop);
892 	return (r);
893 }
894 
895 /*
896  * Returns
897  *   0 - success
898  *   ECANCELED - pg was deleted
899  *   ECONNABORTED - repository disconnected
900  *   ENOMEM - out of memory
901  */
902 int
903 load_pg_attrs(const scf_propertygroup_t *pg, pgroup_t **ipgp)
904 {
905 	pgroup_t *ipg;
906 
907 	ipg = internal_pgroup_new();
908 
909 	if (scf_pg_get_flags(pg, &ipg->sc_pgroup_flags) != 0) {
910 		switch (scf_error()) {
911 		case SCF_ERROR_DELETED:
912 			internal_pgroup_free(ipg);
913 			return (ECANCELED);
914 
915 		case SCF_ERROR_CONNECTION_BROKEN:
916 			internal_pgroup_free(ipg);
917 			return (ECONNABORTED);
918 
919 		case SCF_ERROR_NOT_SET:
920 		case SCF_ERROR_NOT_BOUND:
921 		default:
922 			bad_error("scf_pg_get_name", scf_error());
923 		}
924 	}
925 
926 	if (scf_pg_get_name(pg, loadbuf, loadbuf_sz) < 0) {
927 		switch (scf_error()) {
928 		case SCF_ERROR_DELETED:
929 			internal_pgroup_free(ipg);
930 			return (ECANCELED);
931 
932 		case SCF_ERROR_CONNECTION_BROKEN:
933 			internal_pgroup_free(ipg);
934 			return (ECONNABORTED);
935 
936 		case SCF_ERROR_NOT_SET:
937 		case SCF_ERROR_NOT_BOUND:
938 		default:
939 			bad_error("scf_pg_get_name", scf_error());
940 		}
941 	}
942 
943 	ipg->sc_pgroup_name = strdup(loadbuf);
944 	if (ipg->sc_pgroup_name == NULL) {
945 		internal_pgroup_free(ipg);
946 		return (ENOMEM);
947 	}
948 
949 	if (scf_pg_get_type(pg, loadbuf, loadbuf_sz) < 0) {
950 		switch (scf_error()) {
951 		case SCF_ERROR_DELETED:
952 			free((char *)ipg->sc_pgroup_name);
953 			internal_pgroup_free(ipg);
954 			return (ECANCELED);
955 
956 		case SCF_ERROR_CONNECTION_BROKEN:
957 			free((char *)ipg->sc_pgroup_name);
958 			internal_pgroup_free(ipg);
959 			return (ECONNABORTED);
960 
961 		case SCF_ERROR_NOT_SET:
962 		case SCF_ERROR_NOT_BOUND:
963 		default:
964 			bad_error("scf_pg_get_name", scf_error());
965 		}
966 	}
967 
968 	ipg->sc_pgroup_type = strdup(loadbuf);
969 	if (ipg->sc_pgroup_type == NULL) {
970 		free((char *)ipg->sc_pgroup_name);
971 		internal_pgroup_free(ipg);
972 		return (ENOMEM);
973 	}
974 
975 	*ipgp = ipg;
976 	return (0);
977 }
978 
979 /*
980  * Load a property group into a pgroup_t.  Returns
981  *   0 - success
982  *   ECANCELED - pg was deleted
983  *   ECONNABORTED - repository disconnected
984  *   EBADF - pg is corrupt (error printed if fmri is given)
985  *   ENOMEM - out of memory
986  *   EACCES - permission denied when reading property
987  */
988 int
989 load_pg(const scf_propertygroup_t *pg, pgroup_t **ipgp, const char *fmri,
990     const char *snapname)
991 {
992 	pgroup_t *ipg;
993 	int r;
994 
995 	if (scf_iter_pg_properties(load_propiter, pg) != 0) {
996 		switch (scf_error()) {
997 		case SCF_ERROR_DELETED:
998 			return (ECANCELED);
999 
1000 		case SCF_ERROR_CONNECTION_BROKEN:
1001 			return (ECONNABORTED);
1002 
1003 		case SCF_ERROR_HANDLE_MISMATCH:
1004 		case SCF_ERROR_NOT_SET:
1005 		case SCF_ERROR_NOT_BOUND:
1006 		default:
1007 			bad_error("scf_iter_pg_properties", scf_error());
1008 		}
1009 	}
1010 
1011 	r = load_pg_attrs(pg, &ipg);
1012 	switch (r) {
1013 	case 0:
1014 		break;
1015 
1016 	case ECANCELED:
1017 	case ECONNABORTED:
1018 	case ENOMEM:
1019 		return (r);
1020 
1021 	default:
1022 		bad_error("load_pg_attrs", r);
1023 	}
1024 
1025 	for (;;) {
1026 		property_t *iprop;
1027 
1028 		r = scf_iter_next_property(load_propiter, load_prop);
1029 		if (r == 0)
1030 			break;
1031 		if (r != 1) {
1032 			switch (scf_error()) {
1033 			case SCF_ERROR_DELETED:
1034 				r = ECANCELED;
1035 				goto out;
1036 
1037 			case SCF_ERROR_CONNECTION_BROKEN:
1038 				r = ECONNABORTED;
1039 				goto out;
1040 
1041 			case SCF_ERROR_HANDLE_MISMATCH:
1042 			case SCF_ERROR_NOT_BOUND:
1043 			case SCF_ERROR_NOT_SET:
1044 			case SCF_ERROR_INVALID_ARGUMENT:
1045 			default:
1046 				bad_error("scf_iter_next_property",
1047 				    scf_error());
1048 			}
1049 		}
1050 
1051 		r = load_property(load_prop, &iprop);
1052 		switch (r) {
1053 		case 0:
1054 			break;
1055 
1056 		case ECANCELED:
1057 		case ECONNABORTED:
1058 		case ENOMEM:
1059 		case EACCES:
1060 			goto out;
1061 
1062 		default:
1063 			bad_error("load_property", r);
1064 		}
1065 
1066 		r = internal_attach_property(ipg, iprop);
1067 		if (r != 0) {
1068 			if (fmri != NULL) {
1069 				if (snapname == NULL)
1070 					warn(gettext("Property group \"%s\" of "
1071 					    "%s has multiple definitions of "
1072 					    "property \"%s\".\n"),
1073 					    ipg->sc_pgroup_name, fmri,
1074 					    iprop->sc_property_name);
1075 				else
1076 					warn(gettext("Property group \"%s\" of "
1077 					    "the \"%s\" snapshot of %s has "
1078 					    "multiple definitions of property "
1079 					    "\"%s\".\n"),
1080 					    ipg->sc_pgroup_name, snapname, fmri,
1081 					    iprop->sc_property_name);
1082 			}
1083 			r = EBADF;
1084 			goto out;
1085 		}
1086 	}
1087 
1088 	*ipgp = ipg;
1089 	return (0);
1090 
1091 out:
1092 	internal_pgroup_free(ipg);
1093 	return (r);
1094 }
1095 
1096 /*
1097  * These functions compare internal property groups and properties (pgroup_t
1098  * & property_t).  They return 1 if the given structures are equal and
1099  * 0 otherwise.  Some will report the differences between the two structures.
1100  * They are used by the import functions in svccfg_libscf.c .
1101  */
1102 
1103 int
1104 prop_equal(property_t *p1, property_t *p2, const char *fmri, const char *pgname,
1105     int new)
1106 {
1107 	value_t *v1, *v2;
1108 
1109 	const char * const values_diff = gettext("Conflict upgrading %s "
1110 	    "(property \"%s/%s\" has different values).\n");
1111 	const char * const values_diff_new = gettext("Conflict upgrading %s "
1112 	    "(new property \"%s/%s\" has different values).\n");
1113 
1114 	assert((fmri == NULL) == (pgname == NULL));
1115 
1116 	if (fmri != NULL) {
1117 		/*
1118 		 * If we find any differences, we'll report conflicts.  But
1119 		 * conflict messages won't make any sense if the names don't
1120 		 * match.  If the caller supplied fmri, assert that the names
1121 		 * match.
1122 		 */
1123 		assert(strcmp(p1->sc_property_name, p2->sc_property_name) == 0);
1124 	} else {
1125 		if (strcmp(p1->sc_property_name, p2->sc_property_name) != 0)
1126 			return (0);
1127 	}
1128 
1129 	if (p1->sc_value_type != p2->sc_value_type) {
1130 		if (fmri != NULL) {
1131 			if (new)
1132 				warn(gettext("Conflict upgrading %s "
1133 				    "(new property \"%s/%s\" has different "
1134 				    "type).\n"), fmri, pgname,
1135 				    p1->sc_property_name);
1136 			else
1137 				warn(gettext("Conflict upgrading %s "
1138 				    "(property \"%s/%s\" has different "
1139 				    "type).\n"), fmri, pgname,
1140 				    p1->sc_property_name);
1141 		}
1142 		return (0);
1143 	}
1144 
1145 	if (uu_list_numnodes(p1->sc_property_values) !=
1146 	    uu_list_numnodes(p2->sc_property_values)) {
1147 		if (fmri != NULL)
1148 			warn(new ? values_diff_new : values_diff, fmri,
1149 			    pgname, p1->sc_property_name);
1150 		return (0);
1151 	}
1152 
1153 	v1 = uu_list_first(p1->sc_property_values);
1154 	v2 = uu_list_first(p2->sc_property_values);
1155 
1156 	while (v1 != NULL) {
1157 		assert(v2 != NULL);
1158 
1159 		if (value_cmp(v1, v2, NULL) != 0) {
1160 			if (fmri != NULL)
1161 				warn(new ? values_diff_new : values_diff,
1162 				    fmri, pgname, p1->sc_property_name);
1163 			return (0);
1164 		}
1165 
1166 		v1 = uu_list_next(p1->sc_property_values, v1);
1167 		v2 = uu_list_next(p2->sc_property_values, v2);
1168 	}
1169 
1170 	return (1);
1171 }
1172 
1173 int
1174 pg_attrs_equal(const pgroup_t *pg1, const pgroup_t *pg2, const char *fmri,
1175     int new)
1176 {
1177 	if (strcmp(pg1->sc_pgroup_name, pg2->sc_pgroup_name) != 0) {
1178 		assert(fmri == NULL);
1179 		return (0);
1180 	}
1181 
1182 	if (pg1->sc_pgroup_flags != pg2->sc_pgroup_flags) {
1183 		if (fmri) {
1184 			if (new)
1185 				warn(gettext("Conflict upgrading %s "
1186 				    "(new property group \"%s\" has different "
1187 				    "flags).\n"), fmri, pg1->sc_pgroup_name);
1188 			else
1189 				warn(gettext("Conflict upgrading %s "
1190 				    "(property group \"%s\" has different "
1191 				    "flags).\n"), fmri, pg1->sc_pgroup_name);
1192 		}
1193 		return (0);
1194 	}
1195 
1196 	if (strcmp(pg1->sc_pgroup_type, pg2->sc_pgroup_type) != 0) {
1197 		if (fmri) {
1198 			if (new)
1199 				warn(gettext("Conflict upgrading %s "
1200 				    "(new property group \"%s\" has different "
1201 				    "type).\n"), fmri, pg1->sc_pgroup_name);
1202 			else
1203 				warn(gettext("Conflict upgrading %s "
1204 				    "(property group \"%s\" has different "
1205 				    "type).\n"), fmri, pg1->sc_pgroup_name);
1206 		}
1207 		return (0);
1208 	}
1209 
1210 	return (1);
1211 }
1212 
1213 int
1214 pg_equal(pgroup_t *pg1, pgroup_t *pg2)
1215 {
1216 	property_t *p1, *p2;
1217 
1218 	if (!pg_attrs_equal(pg1, pg2, NULL, 0))
1219 		return (0);
1220 
1221 	if (uu_list_numnodes(pg1->sc_pgroup_props) !=
1222 	    uu_list_numnodes(pg2->sc_pgroup_props))
1223 		return (0);
1224 
1225 	p1 = uu_list_first(pg1->sc_pgroup_props);
1226 	p2 = uu_list_first(pg2->sc_pgroup_props);
1227 
1228 	while (p1 != NULL) {
1229 		assert(p2 != NULL);
1230 
1231 		if (!prop_equal(p1, p2, NULL, NULL, 0))
1232 			return (0);
1233 
1234 		p1 = uu_list_next(pg1->sc_pgroup_props, p1);
1235 		p2 = uu_list_next(pg2->sc_pgroup_props, p2);
1236 	}
1237 
1238 	return (1);
1239 }
1240