/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include "svccfg.h" /* * Internal representation manipulation routines for svccfg(1) */ static uu_list_pool_t *entity_pool; static uu_list_pool_t *pgroup_pool; static uu_list_pool_t *property_pool; static uu_list_pool_t *value_pool; /* ARGSUSED */ static int entity_cmp(const void *a, const void *b, void *p) { entity_t *A = (entity_t *)a; entity_t *B = (entity_t *)b; return (strcmp(A->sc_name, B->sc_name)); } /*ARGSUSED*/ static int pgroup_cmp(const void *a, const void *b, void *p) { pgroup_t *A = (pgroup_t *)a; pgroup_t *B = (pgroup_t *)b; return (strcmp(A->sc_pgroup_name, B->sc_pgroup_name)); } /* ARGSUSED */ static int property_cmp(const void *a, const void *b, void *p) { property_t *A = (property_t *)a; property_t *B = (property_t *)b; return (strcmp(A->sc_property_name, B->sc_property_name)); } /* ARGSUSED */ int value_cmp(const void *a, const void *b, void *p) { const value_t *A = a; const value_t *B = b; if (A->sc_type != B->sc_type) return (B->sc_type - A->sc_type); switch (A->sc_type) { case SCF_TYPE_BOOLEAN: case SCF_TYPE_COUNT: return (B->sc_u.sc_count - A->sc_u.sc_count); case SCF_TYPE_INTEGER: return (B->sc_u.sc_integer - A->sc_u.sc_integer); default: return (strcmp(A->sc_u.sc_string, B->sc_u.sc_string)); } } void internal_init() { if ((entity_pool = uu_list_pool_create("entities", sizeof (entity_t), offsetof(entity_t, sc_node), entity_cmp, 0)) == NULL) uu_die(gettext("entity list pool creation failed: %s\n"), uu_strerror(uu_error())); if ((pgroup_pool = uu_list_pool_create("property_groups", sizeof (pgroup_t), offsetof(pgroup_t, sc_node), pgroup_cmp, 0)) == NULL) uu_die( gettext("property group list pool creation failed: %s\n"), uu_strerror(uu_error())); if ((property_pool = uu_list_pool_create("properties", sizeof (property_t), offsetof(property_t, sc_node), property_cmp, 0)) == NULL) uu_die(gettext("property list pool creation failed: %s\n"), uu_strerror(uu_error())); if ((value_pool = uu_list_pool_create("property_values", sizeof (value_t), offsetof(value_t, sc_node), value_cmp, 0)) == NULL) uu_die( gettext("property value list pool creation failed: %s\n"), uu_strerror(uu_error())); } /*ARGSUSED*/ static int internal_value_dump(void *v, void *pvt) { value_t *val = v; switch (val->sc_type) { case SCF_TYPE_BOOLEAN: (void) printf(" value = %s\n", val->sc_u.sc_count ? "true" : "false"); break; case SCF_TYPE_COUNT: (void) printf(" value = %llu\n", val->sc_u.sc_count); break; case SCF_TYPE_INTEGER: (void) printf(" value = %lld\n", val->sc_u.sc_integer); break; case SCF_TYPE_ASTRING: case SCF_TYPE_FMRI: case SCF_TYPE_HOST: case SCF_TYPE_HOSTNAME: case SCF_TYPE_NET_ADDR_V4: case SCF_TYPE_NET_ADDR_V6: case SCF_TYPE_OPAQUE: case SCF_TYPE_TIME: case SCF_TYPE_URI: case SCF_TYPE_USTRING: (void) printf(" value = %s\n", val->sc_u.sc_string ? val->sc_u.sc_string : "(nil)"); break; default: uu_die(gettext("unknown value type (%d)\n"), val->sc_type); break; } return (UU_WALK_NEXT); } /*ARGSUSED*/ static int internal_property_dump(void *v, void *pvt) { property_t *p = v; (void) printf("property\n name = %s\n", p->sc_property_name); (void) printf(" type = %d\n", p->sc_value_type); (void) uu_list_walk(p->sc_property_values, internal_value_dump, NULL, UU_DEFAULT); return (UU_WALK_NEXT); } /*ARGSUSED*/ static int internal_pgroup_dump(void *v, void *pvt) { pgroup_t *pg = v; (void) printf("pgroup name = %s\n", pg->sc_pgroup_name); (void) printf(" type = %s\n", pg->sc_pgroup_type); (void) uu_list_walk(pg->sc_pgroup_props, internal_property_dump, NULL, UU_DEFAULT); return (UU_WALK_NEXT); } /*ARGSUSED*/ static int internal_instance_dump(void *v, void *pvt) { entity_t *i = v; (void) printf("instance name = %s\n", i->sc_name); (void) uu_list_walk(i->sc_pgroups, internal_pgroup_dump, NULL, UU_DEFAULT); return (UU_WALK_NEXT); } /*ARGSUSED*/ static int internal_service_dump(void *v, void *pvt) { entity_t *s = v; (void) printf("service name = %s\n", s->sc_name); (void) printf(" type = %x\n", s->sc_u.sc_service.sc_service_type); (void) printf(" version = %u\n", s->sc_u.sc_service.sc_service_version); (void) uu_list_walk(s->sc_pgroups, internal_pgroup_dump, NULL, UU_DEFAULT); (void) uu_list_walk(s->sc_u.sc_service.sc_service_instances, internal_instance_dump, NULL, UU_DEFAULT); return (UU_WALK_NEXT); } void internal_dump(bundle_t *b) { (void) printf("bundle name = %s\n", b->sc_bundle_name); (void) printf(" type = %x\n", b->sc_bundle_type); (void) uu_list_walk(b->sc_bundle_services, internal_service_dump, NULL, UU_DEFAULT); } bundle_t * internal_bundle_new() { bundle_t *b; if ((b = uu_zalloc(sizeof (bundle_t))) == NULL) uu_die(gettext("couldn't allocate memory")); b->sc_bundle_type = SVCCFG_UNKNOWN_BUNDLE; b->sc_bundle_services = uu_list_create(entity_pool, b, 0); if (b->sc_bundle_services == NULL) { uu_die(gettext("Unable to create list for bundle services. " "%s\n"), uu_strerror(uu_error())); } return (b); } void internal_bundle_free(bundle_t *b) { void *cookie = NULL; entity_t *service; while ((service = uu_list_teardown(b->sc_bundle_services, &cookie)) != NULL) internal_service_free(service); free(b); } entity_t * internal_entity_new(entity_type_t entity) { entity_t *e; if ((e = uu_zalloc(sizeof (entity_t))) == NULL) uu_die(gettext("couldn't allocate memory")); uu_list_node_init(e, &e->sc_node, entity_pool); e->sc_etype = entity; e->sc_pgroups = uu_list_create(pgroup_pool, e, 0); e->sc_op = SVCCFG_OP_NONE; if (e->sc_pgroups == NULL) { uu_die(gettext("Unable to create list for entity property " "groups. %s\n"), uu_strerror(uu_error())); } return (e); } entity_t * internal_service_new(const char *name) { entity_t *s; s = internal_entity_new(SVCCFG_SERVICE_OBJECT); s->sc_name = name; s->sc_fmri = uu_msprintf("svc:/%s", name); if (s->sc_fmri == NULL) uu_die(gettext("couldn't allocate memory")); s->sc_dependents = uu_list_create(pgroup_pool, s, 0); if (s->sc_dependents == NULL) { uu_die(gettext("Unable to create list for service dependents. " "%s\n"), uu_strerror(uu_error())); } s->sc_u.sc_service.sc_service_type = SVCCFG_UNKNOWN_SERVICE; s->sc_u.sc_service.sc_service_instances = uu_list_create(entity_pool, s, 0); if (s->sc_u.sc_service.sc_service_instances == NULL) { uu_die(gettext("Unable to create list for service instances. " "%s\n"), uu_strerror(uu_error())); } return (s); } void internal_service_free(entity_t *s) { entity_t *inst; pgroup_t *pg; void *cookie; if (s->sc_u.sc_service.sc_restarter != NULL) internal_instance_free(s->sc_u.sc_service.sc_restarter); if (s->sc_u.sc_service.sc_global != NULL) internal_instance_free(s->sc_u.sc_service.sc_global); cookie = NULL; while ((pg = uu_list_teardown(s->sc_pgroups, &cookie)) != NULL) internal_pgroup_free(pg); cookie = NULL; while ((pg = uu_list_teardown(s->sc_dependents, &cookie)) != NULL) internal_pgroup_free(pg); cookie = NULL; while ((inst = uu_list_teardown(s->sc_u.sc_service.sc_service_instances, &cookie)) != NULL) internal_instance_free(inst); uu_free((void *)s->sc_fmri); free(s); } entity_t * internal_instance_new(const char *name) { entity_t *i; i = internal_entity_new(SVCCFG_INSTANCE_OBJECT); i->sc_name = name; /* Can't set i->sc_fmri until we're attached to a service. */ i->sc_dependents = uu_list_create(pgroup_pool, i, 0); if (i->sc_dependents == NULL) { uu_die(gettext("Unable to create list for instance " "dependents. %s\n"), uu_strerror(uu_error())); } return (i); } void internal_instance_free(entity_t *i) { pgroup_t *pg; void *cookie = NULL; entity_t *rs; rs = i->sc_u.sc_instance.sc_instance_restarter; if (rs != NULL) internal_instance_free(rs); while ((pg = uu_list_teardown(i->sc_pgroups, &cookie)) != NULL) internal_pgroup_free(pg); cookie = NULL; while ((pg = uu_list_teardown(i->sc_dependents, &cookie)) != NULL) internal_pgroup_free(pg); uu_free((void *)i->sc_fmri); free(i); } pgroup_t * internal_pgroup_new() { pgroup_t *p; if ((p = uu_zalloc(sizeof (pgroup_t))) == NULL) uu_die(gettext("couldn't allocate memory")); uu_list_node_init(p, &p->sc_node, pgroup_pool); p->sc_pgroup_props = uu_list_create(property_pool, p, UU_LIST_SORTED); if (p->sc_pgroup_props == NULL) { uu_die(gettext("Unable to create list for properties. %s\n"), uu_strerror(uu_error())); } p->sc_pgroup_name = ""; p->sc_pgroup_type = ""; return (p); } void internal_pgroup_free(pgroup_t *pg) { property_t *prop; void *cookie = NULL; /* * Templates validation code should clean up this reference when * the validation is finished. */ assert(pg->sc_pgroup_composed == NULL); while ((prop = uu_list_teardown(pg->sc_pgroup_props, &cookie)) != NULL) internal_property_free(prop); uu_free(pg); } static pgroup_t * find_pgroup(uu_list_t *list, const char *name, const char *type) { pgroup_t *pg; for (pg = uu_list_first(list); pg != NULL; pg = uu_list_next(list, pg)) { if (strcmp(pg->sc_pgroup_name, name) != 0) continue; if (type == NULL) return (pg); if (strcmp(pg->sc_pgroup_type, type) == 0) return (pg); } return (NULL); } pgroup_t * internal_dependent_find(entity_t *e, const char *name) { return (find_pgroup(e->sc_dependents, name, NULL)); } pgroup_t * internal_pgroup_find(entity_t *e, const char *name, const char *type) { return (find_pgroup(e->sc_pgroups, name, type)); } static pgroup_t * internal_pgroup_create_common(entity_t *e, const char *name, const char *type, boolean_t unique) { pgroup_t *pg; pg = internal_pgroup_find(e, name, type); if (pg != NULL) { if (unique == B_TRUE) { return (NULL); } else { return (pg); } } pg = internal_pgroup_new(); (void) internal_attach_pgroup(e, pg); pg->sc_pgroup_name = strdup(name); pg->sc_pgroup_flags = 0; if (type != NULL) { pg->sc_pgroup_type = strdup(type); } else { est->sc_miss_type = B_TRUE; pg->sc_pgroup_type = NULL; } if (pg->sc_pgroup_name == NULL || (e->sc_op != SVCCFG_OP_APPLY && pg->sc_pgroup_type == NULL)) uu_die(gettext("Could not duplicate string")); return (pg); } pgroup_t * internal_pgroup_find_or_create(entity_t *e, const char *name, const char *type) { return (internal_pgroup_create_common(e, name, type, B_FALSE)); } pgroup_t * internal_pgroup_create_strict(entity_t *e, const char *name, const char *type) { return (internal_pgroup_create_common(e, name, type, B_TRUE)); } property_t * internal_property_new() { property_t *p; if ((p = uu_zalloc(sizeof (property_t))) == NULL) uu_die(gettext("couldn't allocate memory")); uu_list_node_init(p, &p->sc_node, property_pool); p->sc_property_values = uu_list_create(value_pool, p, 0); if (p->sc_property_values == NULL) { uu_die(gettext("Unable to create list for property values. " "%s\n"), uu_strerror(uu_error())); } p->sc_property_name = ""; tmpl_property_init(p); return (p); } void internal_property_free(property_t *p) { value_t *val; void *cookie = NULL; tmpl_property_fini(p); while ((val = uu_list_teardown(p->sc_property_values, &cookie)) != NULL) { if (val->sc_free != NULL) val->sc_free(val); free(val); } free(p); } property_t * internal_property_find(pgroup_t *pg, const char *name) { property_t *p; for (p = uu_list_first(pg->sc_pgroup_props); p != NULL; p = uu_list_next(pg->sc_pgroup_props, p)) if (strcmp(p->sc_property_name, name) == 0) return (p); return (NULL); } value_t * internal_value_new() { value_t *v; if ((v = uu_zalloc(sizeof (value_t))) == NULL) uu_die(gettext("couldn't allocate memory")); uu_list_node_init(v, &v->sc_node, value_pool); return (v); } static void internal_value_free_str(value_t *v) { free(v->sc_u.sc_string); } property_t * internal_property_create(const char *name, scf_type_t vtype, uint_t nvals, ...) { va_list args; property_t *p; value_t *v; p = internal_property_new(); p->sc_property_name = (char *)name; p->sc_value_type = vtype; va_start(args, nvals); for (; nvals > 0; nvals--) { v = internal_value_new(); v->sc_type = vtype; switch (vtype) { case SCF_TYPE_BOOLEAN: case SCF_TYPE_COUNT: v->sc_u.sc_count = va_arg(args, uint64_t); break; case SCF_TYPE_INTEGER: v->sc_u.sc_integer = va_arg(args, int64_t); break; case SCF_TYPE_ASTRING: case SCF_TYPE_FMRI: case SCF_TYPE_HOST: case SCF_TYPE_HOSTNAME: case SCF_TYPE_NET_ADDR_V4: case SCF_TYPE_NET_ADDR_V6: case SCF_TYPE_OPAQUE: case SCF_TYPE_TIME: case SCF_TYPE_URI: case SCF_TYPE_USTRING: v->sc_u.sc_string = (char *)va_arg(args, uchar_t *); break; default: va_end(args); uu_die(gettext("unknown property type (%d)\n"), vtype); break; } internal_attach_value(p, v); } va_end(args); return (p); } /* * Some of these attach functions use uu_list_append() to maintain the * same order across import/export, whereas others are always sorted * anyway, or the order is irrelevant. */ int internal_attach_service(bundle_t *bndl, entity_t *svc) { if (uu_list_find(bndl->sc_bundle_services, svc, NULL, NULL) != NULL) { semerr(gettext("Multiple definitions for service %s in " "bundle %s.\n"), svc->sc_name, bndl->sc_bundle_name); return (-1); } (void) uu_list_append(bndl->sc_bundle_services, svc); return (0); } int internal_attach_entity(entity_t *svc, entity_t *ent) { if (svc->sc_etype != SVCCFG_SERVICE_OBJECT) uu_die(gettext("bad entity attach: %s is not a service\n"), svc->sc_name); if (uu_list_find(svc->sc_u.sc_service.sc_service_instances, ent, NULL, NULL) != NULL) { semerr(gettext("Multiple definitions of entity %s in service " "%s.\n"), ent->sc_name, svc->sc_name); return (-1); } (void) uu_list_prepend(svc->sc_u.sc_service.sc_service_instances, ent); ent->sc_parent = svc; ent->sc_op = svc->sc_op; ent->sc_fmri = uu_msprintf("%s:%s", svc->sc_fmri, ent->sc_name); if (ent->sc_fmri == NULL) uu_die(gettext("couldn't allocate memory")); return (0); } int internal_attach_pgroup(entity_t *ent, pgroup_t *pgrp) { if (uu_list_find(ent->sc_pgroups, pgrp, NULL, NULL) != NULL) { semerr(gettext("Multiple definitions of property group %s in " "entity %s.\n"), pgrp->sc_pgroup_name, ent->sc_name); return (-1); } (void) uu_list_append(ent->sc_pgroups, pgrp); pgrp->sc_parent = ent; return (0); } void internal_detach_pgroup(entity_t *ent, pgroup_t *pgrp) { uu_list_remove(ent->sc_pgroups, pgrp); } int internal_attach_dependent(entity_t *ent, pgroup_t *pg) { if (uu_list_find(ent->sc_dependents, pg, NULL, NULL) != NULL) { semerr(gettext("Multiple definitions of dependent %s in " "entity %s.\n"), pg->sc_pgroup_name, ent->sc_name); return (-1); } (void) uu_list_append(ent->sc_dependents, pg); pg->sc_parent = ent; return (0); } /* * Returns * 0 - success * -1 - prop already exists in pgrp */ int internal_attach_property(pgroup_t *pgrp, property_t *prop) { uu_list_index_t idx; if (uu_list_find(pgrp->sc_pgroup_props, prop, NULL, &idx) != NULL) { semerr(gettext("Multiple definitions for property %s in " "property group %s.\n"), prop->sc_property_name, pgrp->sc_pgroup_name); return (-1); } uu_list_insert(pgrp->sc_pgroup_props, prop, idx); return (0); } void internal_detach_property(pgroup_t *pgrp, property_t *prop) { uu_list_remove(pgrp->sc_pgroup_props, prop); } void internal_attach_value(property_t *prop, value_t *val) { (void) uu_list_append(prop->sc_property_values, val); } /* * These functions create an internal representation of a property group * (pgroup_t) from the repository (scf_propertygroup_t). They are used by the * import functions in svccfg_libscf.c . * * load_init() must be called first to initialize these globals, and * load_fini() should be called afterwards to destroy them. */ static char *loadbuf = NULL; static size_t loadbuf_sz; static scf_propertygroup_t *load_pgroup = NULL; static scf_property_t *load_prop = NULL; static scf_value_t *load_val = NULL; static scf_iter_t *load_propiter = NULL, *load_valiter = NULL; static scf_iter_t *load_pgiter = NULL; /* * Initialize the global state for the load_*() routines. * Returns * 0 - success * ENOMEM - out of memory */ int load_init(void) { loadbuf_sz = ((max_scf_value_len > max_scf_pg_type_len) ? max_scf_value_len : max_scf_pg_type_len) + 1; loadbuf = malloc(loadbuf_sz); if (loadbuf == NULL) return (ENOMEM); if ((load_prop = scf_property_create(g_hndl)) == NULL || (load_val = scf_value_create(g_hndl)) == NULL || (load_pgroup = scf_pg_create(g_hndl)) == NULL || (load_pgiter = scf_iter_create(g_hndl)) == NULL || (load_propiter = scf_iter_create(g_hndl)) == NULL || (load_valiter = scf_iter_create(g_hndl)) == NULL) { load_fini(); return (ENOMEM); } return (0); } void load_fini(void) { scf_iter_destroy(load_propiter); load_propiter = NULL; scf_iter_destroy(load_valiter); load_valiter = NULL; scf_iter_destroy(load_pgiter); load_pgiter = NULL; scf_pg_destroy(load_pgroup); load_pgroup = NULL; scf_value_destroy(load_val); load_val = NULL; scf_property_destroy(load_prop); load_prop = NULL; free(loadbuf); loadbuf = NULL; } /* * Create a property_t which represents an scf_property_t. Returns * 0 - success * ECANCELED - prop's pg was deleted * ECONNABORTED - repository disconnected * ENOMEM - out of memory * EACCES - permission denied when reading property */ static int load_property(scf_property_t *prop, property_t **ipp) { property_t *iprop; int r; ssize_t ssz; /* get name */ if (scf_property_get_name(prop, loadbuf, loadbuf_sz) < 0) { switch (scf_error()) { case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_CONNECTION_BROKEN: return (ECONNABORTED); case SCF_ERROR_NOT_BOUND: case SCF_ERROR_NOT_SET: default: bad_error("scf_property_get_name", scf_error()); } } iprop = internal_property_new(); iprop->sc_property_name = strdup(loadbuf); if (iprop->sc_property_name == NULL) { internal_property_free(iprop); return (ENOMEM); } /* get type */ if (scf_property_type(prop, &iprop->sc_value_type) != 0) { switch (scf_error()) { case SCF_ERROR_DELETED: r = ECANCELED; goto out; case SCF_ERROR_CONNECTION_BROKEN: r = ECONNABORTED; goto out; case SCF_ERROR_NOT_BOUND: case SCF_ERROR_NOT_SET: default: bad_error("scf_property_type", scf_error()); } } /* get values */ if (scf_iter_property_values(load_valiter, prop) != 0) { switch (scf_error()) { case SCF_ERROR_DELETED: r = ECANCELED; goto out; case SCF_ERROR_CONNECTION_BROKEN: r = ECONNABORTED; goto out; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_BOUND: case SCF_ERROR_NOT_SET: default: bad_error("scf_iter_property_values", scf_error()); } } for (;;) { value_t *ival; r = scf_iter_next_value(load_valiter, load_val); if (r == 0) break; if (r != 1) { switch (scf_error()) { case SCF_ERROR_DELETED: r = ECANCELED; goto out; case SCF_ERROR_CONNECTION_BROKEN: r = ECONNABORTED; goto out; case SCF_ERROR_PERMISSION_DENIED: r = EACCES; goto out; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_BOUND: case SCF_ERROR_NOT_SET: case SCF_ERROR_INVALID_ARGUMENT: default: bad_error("scf_iter_next_value", scf_error()); } } ival = internal_value_new(); ival->sc_type = scf_value_type(load_val); assert(ival->sc_type != SCF_TYPE_INVALID); switch (ival->sc_type) { case SCF_TYPE_BOOLEAN: { uint8_t b; r = scf_value_get_boolean(load_val, &b); if (r != 0) bad_error("scf_value_get_boolean", scf_error()); ival->sc_u.sc_count = b; break; } case SCF_TYPE_COUNT: r = scf_value_get_count(load_val, &ival->sc_u.sc_count); if (r != 0) bad_error("scf_value_get_count", scf_error()); break; case SCF_TYPE_INTEGER: r = scf_value_get_integer(load_val, &ival->sc_u.sc_integer); if (r != 0) bad_error("scf_value_get_integer", scf_error()); break; default: ssz = scf_value_get_as_string(load_val, loadbuf, loadbuf_sz); if (ssz < 0) bad_error("scf_value_get_as_string", scf_error()); ival->sc_u.sc_string = strdup(loadbuf); if (ival->sc_u.sc_string == NULL) { r = ENOMEM; goto out; } ival->sc_free = internal_value_free_str; } internal_attach_value(iprop, ival); } *ipp = iprop; return (0); out: free(iprop->sc_property_name); internal_property_free(iprop); return (r); } /* * Returns * 0 - success * ECANCELED - pg was deleted * ECONNABORTED - repository disconnected * ENOMEM - out of memory */ int load_pg_attrs(const scf_propertygroup_t *pg, pgroup_t **ipgp) { pgroup_t *ipg; ipg = internal_pgroup_new(); if (scf_pg_get_flags(pg, &ipg->sc_pgroup_flags) != 0) { switch (scf_error()) { case SCF_ERROR_DELETED: internal_pgroup_free(ipg); return (ECANCELED); case SCF_ERROR_CONNECTION_BROKEN: internal_pgroup_free(ipg); return (ECONNABORTED); case SCF_ERROR_NOT_SET: case SCF_ERROR_NOT_BOUND: default: bad_error("scf_pg_get_name", scf_error()); } } if (scf_pg_get_name(pg, loadbuf, loadbuf_sz) < 0) { switch (scf_error()) { case SCF_ERROR_DELETED: internal_pgroup_free(ipg); return (ECANCELED); case SCF_ERROR_CONNECTION_BROKEN: internal_pgroup_free(ipg); return (ECONNABORTED); case SCF_ERROR_NOT_SET: case SCF_ERROR_NOT_BOUND: default: bad_error("scf_pg_get_name", scf_error()); } } ipg->sc_pgroup_name = strdup(loadbuf); if (ipg->sc_pgroup_name == NULL) { internal_pgroup_free(ipg); return (ENOMEM); } if (scf_pg_get_type(pg, loadbuf, loadbuf_sz) < 0) { switch (scf_error()) { case SCF_ERROR_DELETED: free((char *)ipg->sc_pgroup_name); internal_pgroup_free(ipg); return (ECANCELED); case SCF_ERROR_CONNECTION_BROKEN: free((char *)ipg->sc_pgroup_name); internal_pgroup_free(ipg); return (ECONNABORTED); case SCF_ERROR_NOT_SET: case SCF_ERROR_NOT_BOUND: default: bad_error("scf_pg_get_name", scf_error()); } } ipg->sc_pgroup_type = strdup(loadbuf); if (ipg->sc_pgroup_type == NULL) { free((char *)ipg->sc_pgroup_name); internal_pgroup_free(ipg); return (ENOMEM); } *ipgp = ipg; return (0); } /* * Load a property group into a pgroup_t. Returns * 0 - success * ECANCELED - pg was deleted * ECONNABORTED - repository disconnected * EBADF - pg is corrupt (error printed if fmri is given) * ENOMEM - out of memory * EACCES - permission denied when reading property */ int load_pg(const scf_propertygroup_t *pg, pgroup_t **ipgp, const char *fmri, const char *snapname) { pgroup_t *ipg; int r; if (scf_iter_pg_properties(load_propiter, pg) != 0) { switch (scf_error()) { case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_CONNECTION_BROKEN: return (ECONNABORTED); case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: case SCF_ERROR_NOT_BOUND: default: bad_error("scf_iter_pg_properties", scf_error()); } } r = load_pg_attrs(pg, &ipg); switch (r) { case 0: break; case ECANCELED: case ECONNABORTED: case ENOMEM: return (r); default: bad_error("load_pg_attrs", r); } for (;;) { property_t *iprop; r = scf_iter_next_property(load_propiter, load_prop); if (r == 0) break; if (r != 1) { switch (scf_error()) { case SCF_ERROR_DELETED: r = ECANCELED; goto out; case SCF_ERROR_CONNECTION_BROKEN: r = ECONNABORTED; goto out; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_BOUND: case SCF_ERROR_NOT_SET: case SCF_ERROR_INVALID_ARGUMENT: default: bad_error("scf_iter_next_property", scf_error()); } } r = load_property(load_prop, &iprop); switch (r) { case 0: break; case ECANCELED: case ECONNABORTED: case ENOMEM: case EACCES: goto out; default: bad_error("load_property", r); } r = internal_attach_property(ipg, iprop); if (r != 0) { if (fmri != NULL) { if (snapname == NULL) warn(gettext("Property group \"%s\" of " "%s has multiple definitions of " "property \"%s\".\n"), ipg->sc_pgroup_name, fmri, iprop->sc_property_name); else warn(gettext("Property group \"%s\" of " "the \"%s\" snapshot of %s has " "multiple definitions of property " "\"%s\".\n"), ipg->sc_pgroup_name, snapname, fmri, iprop->sc_property_name); } r = EBADF; goto out; } } *ipgp = ipg; return (0); out: internal_pgroup_free(ipg); return (r); } /* * Load the instance for fmri from the repository into memory. The * property groups that define the instances pg_patterns and prop_patterns * are also loaded. * * Returns 0 on success and non-zero on failure. */ int load_instance(const char *fmri, const char *name, entity_t **inst_ptr) { entity_t *e = NULL; scf_instance_t *inst; pgroup_t *ipg; int rc; char *type = NULL; ssize_t tsize; assert(inst_ptr != NULL); if ((inst = scf_instance_create(g_hndl)) == NULL) { switch (scf_error()) { case SCF_ERROR_NO_MEMORY: case SCF_ERROR_NO_RESOURCES: rc = EAGAIN; goto errout; default: bad_error("scf_instance_create", scf_error()); } } if (scf_handle_decode_fmri(g_hndl, fmri, NULL, NULL, inst, NULL, NULL, SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: rc = ECONNABORTED; goto errout; case SCF_ERROR_DELETED: case SCF_ERROR_NOT_FOUND: rc = ENOENT; goto errout; case SCF_ERROR_INVALID_ARGUMENT: rc = EINVAL; goto errout; case SCF_ERROR_CONSTRAINT_VIOLATED: rc = ENOTSUP; goto errout; default: bad_error("scf_handle_decode_fmri", scf_error()); } } if (scf_iter_instance_pgs_composed(load_pgiter, inst, NULL) != 0) { switch (scf_error()) { case SCF_ERROR_DELETED: rc = ECANCELED; goto errout; case SCF_ERROR_CONNECTION_BROKEN: rc = ECONNABORTED; goto errout; default: bad_error("scf_iter_instance_pgs_composed", scf_error()); } } tsize = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH); type = uu_zalloc(tsize); if (type == NULL) { rc = ENOMEM; goto errout; } /* * Initialize our entity structure. */ e = internal_instance_new(name); if (e == NULL) { rc = ENOMEM; goto errout; } e->sc_fmri = uu_strdup(fmri); if (e->sc_fmri == NULL) { rc = ENOMEM; goto errout; } /* * Walk through the property group's of the instance and capture * the property groups that are of type * SCF_GROUP_TEMPLATE_PG_PATTERN and * SCF_GROUP_TEMPLATE_PROP_PATTERN. In other words grab the * pg_pattern and prop_pattern property groups. */ while ((rc = scf_iter_next_pg(load_pgiter, load_pgroup)) == 1) { if (scf_pg_get_type(load_pgroup, type, tsize) <= 0) { switch (scf_error()) { case SCF_ERROR_DELETED: rc = ENOENT; break; case SCF_ERROR_CONNECTION_BROKEN: rc = ECONNABORTED; break; default: bad_error("scf_pg_get_type", scf_error()); } goto errout; } if ((strcmp(type, SCF_GROUP_TEMPLATE_PG_PATTERN) != 0) && (strcmp(type, SCF_GROUP_TEMPLATE_PROP_PATTERN) != 0)) { continue; } if ((rc = load_pg(load_pgroup, &ipg, fmri, NULL)) != 0) { switch (rc) { case ECANCELED: case ECONNABORTED: case EACCES: case ENOMEM: break; default: bad_error("load_pg", rc); } goto errout; } if (internal_attach_pgroup(e, ipg) != 0) { rc = EBADF; goto errout; } } if (rc == -1) { /* Error in iteration. */ switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: rc = ECONNABORTED; break; case SCF_ERROR_DELETED: rc = ENOENT; break; case SCF_ERROR_NO_RESOURCES: rc = EAGAIN; break; default: bad_error("scf_iter_next_pg", scf_error()); } goto errout; } *inst_ptr = e; scf_instance_destroy(inst); return (0); errout: if (type != NULL) uu_free(type); if (inst != NULL) scf_instance_destroy(inst); if (e != NULL) internal_instance_free(e); return (rc); } /* * These functions compare internal property groups and properties (pgroup_t * & property_t). They return 1 if the given structures are equal and * 0 otherwise. Some will report the differences between the two structures. * They are used by the import functions in svccfg_libscf.c . */ int prop_equal(property_t *p1, property_t *p2, const char *fmri, const char *pgname, int new) { value_t *v1, *v2; const char * const values_diff = gettext("Conflict upgrading %s " "(property \"%s/%s\" has different values).\n"); const char * const values_diff_new = gettext("Conflict upgrading %s " "(new property \"%s/%s\" has different values).\n"); assert((fmri == NULL) == (pgname == NULL)); if (fmri != NULL) { /* * If we find any differences, we'll report conflicts. But * conflict messages won't make any sense if the names don't * match. If the caller supplied fmri, assert that the names * match. */ assert(strcmp(p1->sc_property_name, p2->sc_property_name) == 0); } else { if (strcmp(p1->sc_property_name, p2->sc_property_name) != 0) return (0); } if (p1->sc_value_type != p2->sc_value_type) { if (fmri != NULL) { if (new) warn(gettext("Conflict upgrading %s " "(new property \"%s/%s\" has different " "type).\n"), fmri, pgname, p1->sc_property_name); else warn(gettext("Conflict upgrading %s " "(property \"%s/%s\" has different " "type).\n"), fmri, pgname, p1->sc_property_name); } return (0); } if (uu_list_numnodes(p1->sc_property_values) != uu_list_numnodes(p2->sc_property_values)) { if (fmri != NULL) warn(new ? values_diff_new : values_diff, fmri, pgname, p1->sc_property_name); return (0); } v1 = uu_list_first(p1->sc_property_values); v2 = uu_list_first(p2->sc_property_values); while (v1 != NULL) { assert(v2 != NULL); if (value_cmp(v1, v2, NULL) != 0) { if (fmri != NULL) warn(new ? values_diff_new : values_diff, fmri, pgname, p1->sc_property_name); return (0); } v1 = uu_list_next(p1->sc_property_values, v1); v2 = uu_list_next(p2->sc_property_values, v2); } return (1); } int pg_attrs_equal(const pgroup_t *pg1, const pgroup_t *pg2, const char *fmri, int new) { if (strcmp(pg1->sc_pgroup_name, pg2->sc_pgroup_name) != 0) { assert(fmri == NULL); return (0); } if (pg1->sc_pgroup_flags != pg2->sc_pgroup_flags) { if (fmri) { if (new) warn(gettext("Conflict upgrading %s " "(new property group \"%s\" has different " "flags).\n"), fmri, pg1->sc_pgroup_name); else warn(gettext("Conflict upgrading %s " "(property group \"%s\" has different " "flags).\n"), fmri, pg1->sc_pgroup_name); } return (0); } if (strcmp(pg1->sc_pgroup_type, pg2->sc_pgroup_type) != 0) { if (fmri) { if (new) warn(gettext("Conflict upgrading %s " "(new property group \"%s\" has different " "type).\n"), fmri, pg1->sc_pgroup_name); else warn(gettext("Conflict upgrading %s " "(property group \"%s\" has different " "type).\n"), fmri, pg1->sc_pgroup_name); } return (0); } return (1); } int pg_equal(pgroup_t *pg1, pgroup_t *pg2) { property_t *p1, *p2; if (!pg_attrs_equal(pg1, pg2, NULL, 0)) return (0); if (uu_list_numnodes(pg1->sc_pgroup_props) != uu_list_numnodes(pg2->sc_pgroup_props)) return (0); p1 = uu_list_first(pg1->sc_pgroup_props); p2 = uu_list_first(pg2->sc_pgroup_props); while (p1 != NULL) { assert(p2 != NULL); if (!prop_equal(p1, p2, NULL, NULL, 0)) return (0); p1 = uu_list_next(pg1->sc_pgroup_props, p1); p2 = uu_list_next(pg2->sc_pgroup_props, p2); } return (1); }