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