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