16185db85Sdougm /* 26185db85Sdougm * CDDL HEADER START 36185db85Sdougm * 46185db85Sdougm * The contents of this file are subject to the terms of the 56185db85Sdougm * Common Development and Distribution License (the "License"). 66185db85Sdougm * You may not use this file except in compliance with the License. 76185db85Sdougm * 86185db85Sdougm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 96185db85Sdougm * or http://www.opensolaris.org/os/licensing. 106185db85Sdougm * See the License for the specific language governing permissions 116185db85Sdougm * and limitations under the License. 126185db85Sdougm * 136185db85Sdougm * When distributing Covered Code, include this CDDL HEADER in each 146185db85Sdougm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 156185db85Sdougm * If applicable, add the following below this CDDL HEADER, with the 166185db85Sdougm * fields enclosed by brackets "[]" replaced with your own identifying 176185db85Sdougm * information: Portions Copyright [yyyy] [name of copyright owner] 186185db85Sdougm * 196185db85Sdougm * CDDL HEADER END 206185db85Sdougm */ 216185db85Sdougm 226185db85Sdougm /* 23*5b6e0c46Sdougm * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 246185db85Sdougm * Use is subject to license terms. 256185db85Sdougm */ 266185db85Sdougm 276185db85Sdougm #pragma ident "%Z%%M% %I% %E% SMI" 286185db85Sdougm 296185db85Sdougm /* helper functions for using libscf with sharemgr */ 306185db85Sdougm 316185db85Sdougm #include <libscf.h> 326185db85Sdougm #include <libxml/parser.h> 336185db85Sdougm #include <libxml/tree.h> 346185db85Sdougm #include "libshare.h" 356185db85Sdougm #include "libshare_impl.h" 366185db85Sdougm #include "scfutil.h" 376185db85Sdougm #include <string.h> 38da6c28aaSamw #include <ctype.h> 396185db85Sdougm #include <errno.h> 406185db85Sdougm #include <uuid/uuid.h> 416185db85Sdougm #include <sys/param.h> 42f345c0beSdougm #include <signal.h> 43*5b6e0c46Sdougm #include <sys/time.h> 446185db85Sdougm 456185db85Sdougm ssize_t scf_max_name_len; 466185db85Sdougm extern struct sa_proto_plugin *sap_proto_list; 47549ec3ffSdougm extern sa_handle_impl_t get_handle_for_root(xmlNodePtr); 48*5b6e0c46Sdougm static void set_transaction_tstamp(sa_handle_impl_t); 496185db85Sdougm /* 506185db85Sdougm * The SMF facility uses some properties that must exist. We want to 516185db85Sdougm * skip over these when processing protocol options. 526185db85Sdougm */ 536185db85Sdougm static char *skip_props[] = { 546185db85Sdougm "modify_authorization", 556185db85Sdougm "action_authorization", 566185db85Sdougm "value_authorization", 576185db85Sdougm NULL 586185db85Sdougm }; 596185db85Sdougm 606185db85Sdougm /* 616185db85Sdougm * sa_scf_fini(handle) 626185db85Sdougm * 6325a68471Sdougm * Must be called when done. Called with the handle allocated in 646185db85Sdougm * sa_scf_init(), it cleans up the state and frees any SCF resources 656185db85Sdougm * still in use. Called by sa_fini(). 666185db85Sdougm */ 676185db85Sdougm 686185db85Sdougm void 696185db85Sdougm sa_scf_fini(scfutilhandle_t *handle) 706185db85Sdougm { 716185db85Sdougm if (handle != NULL) { 726185db85Sdougm int unbind = 0; 736185db85Sdougm if (handle->scope != NULL) { 746185db85Sdougm unbind = 1; 756185db85Sdougm scf_scope_destroy(handle->scope); 766185db85Sdougm } 77a3351425Sdougm if (handle->instance != NULL) 78a3351425Sdougm scf_instance_destroy(handle->instance); 796185db85Sdougm if (handle->service != NULL) 806185db85Sdougm scf_service_destroy(handle->service); 816185db85Sdougm if (handle->pg != NULL) 826185db85Sdougm scf_pg_destroy(handle->pg); 836185db85Sdougm if (handle->handle != NULL) { 846185db85Sdougm handle->scf_state = SCH_STATE_UNINIT; 856185db85Sdougm if (unbind) 866185db85Sdougm (void) scf_handle_unbind(handle->handle); 876185db85Sdougm scf_handle_destroy(handle->handle); 886185db85Sdougm } 896185db85Sdougm free(handle); 906185db85Sdougm } 916185db85Sdougm } 926185db85Sdougm 936185db85Sdougm /* 946185db85Sdougm * sa_scf_init() 956185db85Sdougm * 9625a68471Sdougm * Must be called before using any of the SCF functions. Called by 976185db85Sdougm * sa_init() during the API setup. 986185db85Sdougm */ 996185db85Sdougm 1006185db85Sdougm scfutilhandle_t * 101549ec3ffSdougm sa_scf_init(sa_handle_impl_t ihandle) 1026185db85Sdougm { 1036185db85Sdougm scfutilhandle_t *handle; 1046185db85Sdougm 1056185db85Sdougm scf_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH); 1066185db85Sdougm if (scf_max_name_len <= 0) 1076185db85Sdougm scf_max_name_len = SA_MAX_NAME_LEN + 1; 1086185db85Sdougm 1096185db85Sdougm handle = calloc(1, sizeof (scfutilhandle_t)); 11025a68471Sdougm if (handle == NULL) 11125a68471Sdougm return (handle); 11225a68471Sdougm 113549ec3ffSdougm ihandle->scfhandle = handle; 1146185db85Sdougm handle->scf_state = SCH_STATE_INITIALIZING; 1156185db85Sdougm handle->handle = scf_handle_create(SCF_VERSION); 11625a68471Sdougm if (handle->handle == NULL) { 11725a68471Sdougm free(handle); 11825a68471Sdougm handle = NULL; 11925a68471Sdougm (void) printf("libshare could not access SMF repository: %s\n", 12025a68471Sdougm scf_strerror(scf_error())); 12125a68471Sdougm return (handle); 12225a68471Sdougm } 12325a68471Sdougm if (scf_handle_bind(handle->handle) != 0) 12425a68471Sdougm goto err; 12525a68471Sdougm 1266185db85Sdougm handle->scope = scf_scope_create(handle->handle); 1276185db85Sdougm handle->service = scf_service_create(handle->handle); 1286185db85Sdougm handle->pg = scf_pg_create(handle->handle); 129a3351425Sdougm 13025a68471Sdougm /* Make sure we have sufficient SMF running */ 1316185db85Sdougm handle->instance = scf_instance_create(handle->handle); 132a3351425Sdougm if (handle->scope == NULL || handle->service == NULL || 133a3351425Sdougm handle->pg == NULL || handle->instance == NULL) 134a3351425Sdougm goto err; 1356185db85Sdougm if (scf_handle_get_scope(handle->handle, 13625a68471Sdougm SCF_SCOPE_LOCAL, handle->scope) != 0) 1376185db85Sdougm goto err; 13825a68471Sdougm if (scf_scope_get_service(handle->scope, 13925a68471Sdougm SA_GROUP_SVC_NAME, handle->service) != 0) 14025a68471Sdougm goto err; 14125a68471Sdougm 1426185db85Sdougm handle->scf_state = SCH_STATE_INIT; 1436185db85Sdougm if (sa_get_instance(handle, "default") != SA_OK) { 1446185db85Sdougm char **protolist; 1456185db85Sdougm int numprotos, i; 1466185db85Sdougm sa_group_t defgrp; 14725a68471Sdougm defgrp = sa_create_group((sa_handle_t)ihandle, "default", NULL); 1486185db85Sdougm if (defgrp != NULL) { 14925a68471Sdougm numprotos = sa_get_protocols( 15025a68471Sdougm &protolist); 15125a68471Sdougm for (i = 0; i < numprotos; i++) 1526185db85Sdougm (void) sa_create_optionset(defgrp, 1536185db85Sdougm protolist[i]); 1546185db85Sdougm if (protolist != NULL) 1556185db85Sdougm free(protolist); 1566185db85Sdougm } 1576185db85Sdougm } 15825a68471Sdougm 1596185db85Sdougm return (handle); 1606185db85Sdougm 16125a68471Sdougm /* Error handling/unwinding */ 1626185db85Sdougm err: 1636185db85Sdougm (void) sa_scf_fini(handle); 1646185db85Sdougm (void) printf("libshare SMF initialization problem: %s\n", 1656185db85Sdougm scf_strerror(scf_error())); 1666185db85Sdougm return (NULL); 1676185db85Sdougm } 1686185db85Sdougm 1696185db85Sdougm /* 1706185db85Sdougm * get_scf_limit(name) 1716185db85Sdougm * 1726185db85Sdougm * Since we use scf_limit a lot and do the same check and return the 1736185db85Sdougm * same value if it fails, implement as a function for code 1746185db85Sdougm * simplification. Basically, if name isn't found, return MAXPATHLEN 1756185db85Sdougm * (1024) so we have a reasonable default buffer size. 1766185db85Sdougm */ 1776185db85Sdougm static ssize_t 1786185db85Sdougm get_scf_limit(uint32_t name) 1796185db85Sdougm { 1806185db85Sdougm ssize_t vallen; 1816185db85Sdougm 1826185db85Sdougm vallen = scf_limit(name); 1836185db85Sdougm if (vallen == (ssize_t)-1) 1846185db85Sdougm vallen = MAXPATHLEN; 1856185db85Sdougm return (vallen); 1866185db85Sdougm } 1876185db85Sdougm 1886185db85Sdougm /* 1896185db85Sdougm * skip_property(name) 1906185db85Sdougm * 19125a68471Sdougm * Internal function to check to see if a property is an SMF magic 1926185db85Sdougm * property that needs to be skipped. 1936185db85Sdougm */ 1946185db85Sdougm static int 1956185db85Sdougm skip_property(char *name) 1966185db85Sdougm { 1976185db85Sdougm int i; 1986185db85Sdougm 1996185db85Sdougm for (i = 0; skip_props[i] != NULL; i++) 2006185db85Sdougm if (strcmp(name, skip_props[i]) == 0) 2016185db85Sdougm return (1); 2026185db85Sdougm return (0); 2036185db85Sdougm } 2046185db85Sdougm 2056185db85Sdougm /* 2066185db85Sdougm * generate_unique_sharename(sharename) 2076185db85Sdougm * 2086185db85Sdougm * Shares are represented in SMF as property groups. Due to share 2096185db85Sdougm * paths containing characters that are not allowed in SMF names and 2106185db85Sdougm * the need to be unique, we use UUIDs to construct a unique name. 2116185db85Sdougm */ 2126185db85Sdougm 2136185db85Sdougm static void 2146185db85Sdougm generate_unique_sharename(char *sharename) 2156185db85Sdougm { 2166185db85Sdougm uuid_t uuid; 2176185db85Sdougm 2186185db85Sdougm uuid_generate(uuid); 2196185db85Sdougm (void) strcpy(sharename, "S-"); 2206185db85Sdougm uuid_unparse(uuid, sharename + 2); 2216185db85Sdougm } 2226185db85Sdougm 2236185db85Sdougm /* 2246185db85Sdougm * valid_protocol(proto) 2256185db85Sdougm * 22625a68471Sdougm * Check to see if the specified protocol is a valid one for the 2276185db85Sdougm * general sharemgr facility. We determine this by checking which 2286185db85Sdougm * plugin protocols were found. 2296185db85Sdougm */ 2306185db85Sdougm 2316185db85Sdougm static int 2326185db85Sdougm valid_protocol(char *proto) 2336185db85Sdougm { 2346185db85Sdougm struct sa_proto_plugin *plugin; 2356185db85Sdougm for (plugin = sap_proto_list; plugin != NULL; 2366185db85Sdougm plugin = plugin->plugin_next) 2376185db85Sdougm if (strcmp(proto, plugin->plugin_ops->sa_protocol) == 0) 2386185db85Sdougm return (1); 2396185db85Sdougm return (0); 2406185db85Sdougm } 2416185db85Sdougm 2426185db85Sdougm /* 2436185db85Sdougm * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype) 2446185db85Sdougm * 24525a68471Sdougm * Extract the name property group and create the specified type of 2466185db85Sdougm * node on the provided group. type will be optionset or security. 2476185db85Sdougm */ 2486185db85Sdougm 2496185db85Sdougm static int 2506185db85Sdougm sa_extract_pgroup(xmlNodePtr root, scfutilhandle_t *handle, 2516185db85Sdougm scf_propertygroup_t *pg, 2526185db85Sdougm char *nodetype, char *proto, char *sectype) 2536185db85Sdougm { 2546185db85Sdougm xmlNodePtr node; 2556185db85Sdougm scf_iter_t *iter; 2566185db85Sdougm scf_property_t *prop; 2576185db85Sdougm scf_value_t *value; 2586185db85Sdougm char *name; 2596185db85Sdougm char *valuestr; 2606185db85Sdougm ssize_t vallen; 2616185db85Sdougm int ret = SA_OK; 2626185db85Sdougm 2636185db85Sdougm vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 2646185db85Sdougm 2656185db85Sdougm node = xmlNewChild(root, NULL, (xmlChar *)nodetype, NULL); 26625a68471Sdougm if (node == NULL) 26725a68471Sdougm return (ret); 26825a68471Sdougm 2696185db85Sdougm if (proto != NULL) 2706185db85Sdougm xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 2716185db85Sdougm if (sectype != NULL) 2726185db85Sdougm xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype); 2736185db85Sdougm /* 27425a68471Sdougm * Have node to work with so iterate over the properties 2756185db85Sdougm * in the pg and create option sub nodes. 2766185db85Sdougm */ 2776185db85Sdougm iter = scf_iter_create(handle->handle); 2786185db85Sdougm value = scf_value_create(handle->handle); 2796185db85Sdougm prop = scf_property_create(handle->handle); 2806185db85Sdougm name = malloc(scf_max_name_len); 2816185db85Sdougm valuestr = malloc(vallen); 2826185db85Sdougm /* 28325a68471Sdougm * Want to iterate through the properties and add them 2846185db85Sdougm * to the base optionset. 2856185db85Sdougm */ 28625a68471Sdougm if (iter == NULL || value == NULL || prop == NULL || 28725a68471Sdougm valuestr == NULL || name == NULL) { 28825a68471Sdougm ret = SA_NO_MEMORY; 28925a68471Sdougm goto out; 29025a68471Sdougm } 2916185db85Sdougm if (scf_iter_pg_properties(iter, pg) == 0) { 29225a68471Sdougm /* Now iterate the properties in the group */ 2936185db85Sdougm while (scf_iter_next_property(iter, prop) > 0) { 2946185db85Sdougm /* have a property */ 2956185db85Sdougm if (scf_property_get_name(prop, name, 2966185db85Sdougm scf_max_name_len) > 0) { 29725a68471Sdougm sa_property_t saprop; 29825a68471Sdougm /* Some properties are part of the framework */ 2996185db85Sdougm if (skip_property(name)) 3006185db85Sdougm continue; 30125a68471Sdougm if (scf_property_get_value(prop, value) != 0) 30225a68471Sdougm continue; 3036185db85Sdougm if (scf_value_get_astring(value, valuestr, 30425a68471Sdougm vallen) < 0) 30525a68471Sdougm continue; 30625a68471Sdougm saprop = sa_create_property(name, valuestr); 3076185db85Sdougm if (saprop != NULL) { 3086185db85Sdougm /* 30925a68471Sdougm * Since in SMF, don't 3106185db85Sdougm * recurse. Use xmlAddChild 3116185db85Sdougm * directly, instead. 3126185db85Sdougm */ 3136185db85Sdougm xmlAddChild(node, 3146185db85Sdougm (xmlNodePtr) saprop); 3156185db85Sdougm } 3166185db85Sdougm } 3176185db85Sdougm } 3186185db85Sdougm } 31925a68471Sdougm out: 3206185db85Sdougm /* cleanup to avoid memory leaks */ 3216185db85Sdougm if (value != NULL) 3226185db85Sdougm scf_value_destroy(value); 3236185db85Sdougm if (iter != NULL) 3246185db85Sdougm scf_iter_destroy(iter); 3256185db85Sdougm if (prop != NULL) 3266185db85Sdougm scf_property_destroy(prop); 3276185db85Sdougm if (name != NULL) 3286185db85Sdougm free(name); 3296185db85Sdougm if (valuestr != NULL) 3306185db85Sdougm free(valuestr); 33125a68471Sdougm 3326185db85Sdougm return (ret); 3336185db85Sdougm } 3346185db85Sdougm 3356185db85Sdougm /* 3366185db85Sdougm * sa_extract_attrs(root, handle, instance) 3376185db85Sdougm * 33825a68471Sdougm * Local function to extract the actual attributes/properties from the 3396185db85Sdougm * property group of the service instance. These are the well known 3406185db85Sdougm * attributes of "state" and "zfs". If additional attributes are 3416185db85Sdougm * added, they should be added here. 3426185db85Sdougm */ 3436185db85Sdougm 3446185db85Sdougm static void 3456185db85Sdougm sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle, 3466185db85Sdougm scf_instance_t *instance) 3476185db85Sdougm { 3486185db85Sdougm scf_property_t *prop; 3496185db85Sdougm scf_value_t *value; 3506185db85Sdougm char *valuestr; 3516185db85Sdougm ssize_t vallen; 3526185db85Sdougm 3536185db85Sdougm vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 3546185db85Sdougm prop = scf_property_create(handle->handle); 3556185db85Sdougm value = scf_value_create(handle->handle); 3566185db85Sdougm valuestr = malloc(vallen); 35725a68471Sdougm if (prop == NULL || value == NULL || valuestr == NULL || 35825a68471Sdougm scf_instance_get_pg(instance, "operation", handle->pg) != 0) { 35925a68471Sdougm goto out; 36025a68471Sdougm } 3616185db85Sdougm /* 36225a68471Sdougm * Have a property group with desired name so now get 3636185db85Sdougm * the known attributes. 3646185db85Sdougm */ 3656185db85Sdougm if (scf_pg_get_property(handle->pg, "state", prop) == 0) { 36625a68471Sdougm /* Found the property so get the value */ 3676185db85Sdougm if (scf_property_get_value(prop, value) == 0) { 36825a68471Sdougm if (scf_value_get_astring(value, valuestr, 36925a68471Sdougm vallen) >= 0) { 3706185db85Sdougm xmlSetProp(root, (xmlChar *)"state", 3716185db85Sdougm (xmlChar *)valuestr); 3726185db85Sdougm } 3736185db85Sdougm } 3746185db85Sdougm } 3756185db85Sdougm if (scf_pg_get_property(handle->pg, "zfs", prop) == 0) { 37625a68471Sdougm /* Found the property so get the value */ 3776185db85Sdougm if (scf_property_get_value(prop, value) == 0) { 37825a68471Sdougm if (scf_value_get_astring(value, valuestr, 37925a68471Sdougm vallen) > 0) { 3806185db85Sdougm xmlSetProp(root, (xmlChar *)"zfs", 3816185db85Sdougm (xmlChar *)valuestr); 3826185db85Sdougm } 3836185db85Sdougm } 3846185db85Sdougm } 38525a68471Sdougm out: 3866185db85Sdougm if (valuestr != NULL) 3876185db85Sdougm free(valuestr); 3886185db85Sdougm if (value != NULL) 3896185db85Sdougm scf_value_destroy(value); 3906185db85Sdougm if (prop != NULL) 3916185db85Sdougm scf_property_destroy(prop); 3926185db85Sdougm } 3936185db85Sdougm 3946185db85Sdougm /* 39525a68471Sdougm * List of known share attributes. 3966185db85Sdougm */ 3976185db85Sdougm 3986185db85Sdougm static char *share_attr[] = { 3996185db85Sdougm "path", 4006185db85Sdougm "id", 401da6c28aaSamw "drive-letter", 402da6c28aaSamw "exclude", 4036185db85Sdougm NULL, 4046185db85Sdougm }; 4056185db85Sdougm 4066185db85Sdougm static int 4076185db85Sdougm is_share_attr(char *name) 4086185db85Sdougm { 4096185db85Sdougm int i; 4106185db85Sdougm for (i = 0; share_attr[i] != NULL; i++) 4116185db85Sdougm if (strcmp(name, share_attr[i]) == 0) 4126185db85Sdougm return (1); 4136185db85Sdougm return (0); 4146185db85Sdougm } 4156185db85Sdougm 4166185db85Sdougm /* 417da6c28aaSamw * _sa_make_resource(node, valuestr) 418da6c28aaSamw * 419da6c28aaSamw * Make a resource node on the share node. The valusestr will either 420da6c28aaSamw * be old format (SMF acceptable string) or new format (pretty much an 421da6c28aaSamw * arbitrary string with "nnn:" prefixing in order to persist 422da6c28aaSamw * mapping). The input valuestr will get modified in place. This is 423da6c28aaSamw * only used in SMF repository parsing. A possible third field will be 424da6c28aaSamw * a "description" string. 425da6c28aaSamw */ 426da6c28aaSamw 427da6c28aaSamw static void 428da6c28aaSamw _sa_make_resource(xmlNodePtr node, char *valuestr) 429da6c28aaSamw { 430da6c28aaSamw char *idx; 431da6c28aaSamw char *name; 432da6c28aaSamw char *description = NULL; 433da6c28aaSamw 434da6c28aaSamw idx = valuestr; 435da6c28aaSamw name = strchr(valuestr, ':'); 436da6c28aaSamw if (name == NULL) { 437da6c28aaSamw /* this is old form so give an index of "0" */ 438da6c28aaSamw idx = "0"; 439da6c28aaSamw name = valuestr; 440da6c28aaSamw } else { 441da6c28aaSamw /* NUL the ':' and move past it */ 442da6c28aaSamw *name++ = '\0'; 443da6c28aaSamw /* There could also be a description string */ 444da6c28aaSamw description = strchr(name, ':'); 445da6c28aaSamw if (description != NULL) 446da6c28aaSamw *description++ = '\0'; 447da6c28aaSamw } 448da6c28aaSamw node = xmlNewChild(node, NULL, (xmlChar *)"resource", NULL); 449da6c28aaSamw if (node != NULL) { 450da6c28aaSamw xmlSetProp(node, (xmlChar *)"name", (xmlChar *)name); 451da6c28aaSamw xmlSetProp(node, (xmlChar *)"id", (xmlChar *)idx); 452da6c28aaSamw /* SMF values are always persistent */ 453da6c28aaSamw xmlSetProp(node, (xmlChar *)"type", (xmlChar *)"persist"); 454da6c28aaSamw if (description != NULL && strlen(description) > 0) { 455da6c28aaSamw (void) xmlNewChild(node, NULL, (xmlChar *)"description", 456da6c28aaSamw (xmlChar *)description); 457da6c28aaSamw } 458da6c28aaSamw } 459da6c28aaSamw } 460da6c28aaSamw 461da6c28aaSamw 462da6c28aaSamw /* 4636185db85Sdougm * sa_share_from_pgroup 4646185db85Sdougm * 46525a68471Sdougm * Extract the share definition from the share property group. We do 4666185db85Sdougm * some sanity checking to avoid bad data. 4676185db85Sdougm * 4686185db85Sdougm * Since this is only constructing the internal data structures, we 4696185db85Sdougm * don't use the sa_* functions most of the time. 4706185db85Sdougm */ 4716185db85Sdougm void 4726185db85Sdougm sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle, 4736185db85Sdougm scf_propertygroup_t *pg, char *id) 4746185db85Sdougm { 4756185db85Sdougm xmlNodePtr node; 4766185db85Sdougm char *name; 4776185db85Sdougm scf_iter_t *iter; 4786185db85Sdougm scf_property_t *prop; 4796185db85Sdougm scf_value_t *value; 4806185db85Sdougm ssize_t vallen; 4816185db85Sdougm char *valuestr; 4826185db85Sdougm int ret = SA_OK; 483f345c0beSdougm int have_path = 0; 4846185db85Sdougm 4856185db85Sdougm /* 4866185db85Sdougm * While preliminary check (starts with 'S') passed before 4876185db85Sdougm * getting here. Need to make sure it is in ID syntax 4886185db85Sdougm * (Snnnnnn). Note that shares with properties have similar 4896185db85Sdougm * pgroups. 4906185db85Sdougm */ 4916185db85Sdougm vallen = strlen(id); 4926185db85Sdougm if (*id == SA_SHARE_PG_PREFIX[0] && vallen == SA_SHARE_PG_LEN) { 4936185db85Sdougm uuid_t uuid; 49425a68471Sdougm if (strncmp(id, SA_SHARE_PG_PREFIX, 49525a68471Sdougm SA_SHARE_PG_PREFIXLEN) != 0 || 4966185db85Sdougm uuid_parse(id + 2, uuid) < 0) 4976185db85Sdougm return; 4986185db85Sdougm } else { 4996185db85Sdougm return; 5006185db85Sdougm } 5016185db85Sdougm 5026185db85Sdougm vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 5036185db85Sdougm 5046185db85Sdougm iter = scf_iter_create(handle->handle); 5056185db85Sdougm value = scf_value_create(handle->handle); 5066185db85Sdougm prop = scf_property_create(handle->handle); 5076185db85Sdougm name = malloc(scf_max_name_len); 5086185db85Sdougm valuestr = malloc(vallen); 5096185db85Sdougm 5106185db85Sdougm /* 51125a68471Sdougm * Construct the share XML node. It is similar to sa_add_share 5126185db85Sdougm * but never changes the repository. Also, there won't be any 5136185db85Sdougm * ZFS or transient shares. Root will be the group it is 5146185db85Sdougm * associated with. 5156185db85Sdougm */ 5166185db85Sdougm node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL); 5176185db85Sdougm if (node != NULL) { 5186185db85Sdougm /* 51925a68471Sdougm * Make sure the UUID part of the property group is 5206185db85Sdougm * stored in the share "id" property. We use this 5216185db85Sdougm * later. 5226185db85Sdougm */ 5236185db85Sdougm xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id); 5246185db85Sdougm xmlSetProp(node, (xmlChar *)"type", (xmlChar *)"persist"); 5256185db85Sdougm } 5266185db85Sdougm 52725a68471Sdougm if (iter == NULL || value == NULL || prop == NULL || name == NULL) 52825a68471Sdougm goto out; 52925a68471Sdougm 53025a68471Sdougm /* Iterate over the share pg properties */ 53125a68471Sdougm if (scf_iter_pg_properties(iter, pg) != 0) 53225a68471Sdougm goto out; 53325a68471Sdougm 5346185db85Sdougm while (scf_iter_next_property(iter, prop) > 0) { 5356185db85Sdougm ret = SA_SYSTEM_ERR; /* assume the worst */ 53625a68471Sdougm if (scf_property_get_name(prop, name, scf_max_name_len) > 0) { 5376185db85Sdougm if (scf_property_get_value(prop, value) == 0) { 5386185db85Sdougm if (scf_value_get_astring(value, valuestr, 5396185db85Sdougm vallen) >= 0) { 5406185db85Sdougm ret = SA_OK; 5416185db85Sdougm } 542da6c28aaSamw } else if (strcmp(name, "resource") == 0) { 543da6c28aaSamw ret = SA_OK; 5446185db85Sdougm } 5456185db85Sdougm } 546da6c28aaSamw if (ret != SA_OK) 547da6c28aaSamw continue; 548f345c0beSdougm /* 54925a68471Sdougm * Check that we have the "path" property in 550f345c0beSdougm * name. The string in name will always be nul 551f345c0beSdougm * terminated if scf_property_get_name() 552f345c0beSdougm * succeeded. 553f345c0beSdougm */ 554f345c0beSdougm if (strcmp(name, "path") == 0) 555f345c0beSdougm have_path = 1; 5566185db85Sdougm if (is_share_attr(name)) { 5576185db85Sdougm /* 55825a68471Sdougm * If a share attr, then simple - 559da6c28aaSamw * usually path and id name 5606185db85Sdougm */ 5616185db85Sdougm xmlSetProp(node, (xmlChar *)name, 5626185db85Sdougm (xmlChar *)valuestr); 563da6c28aaSamw } else if (strcmp(name, "resource") == 0) { 564da6c28aaSamw /* 565da6c28aaSamw * Resource names handled differently since 566da6c28aaSamw * there can be multiple on each share. The 567da6c28aaSamw * "resource" id must be preserved since this 568da6c28aaSamw * will be used by some protocols in mapping 569da6c28aaSamw * "property spaces" to names and is always 570da6c28aaSamw * used to create SMF property groups specific 571da6c28aaSamw * to resources. CIFS needs this. The first 572da6c28aaSamw * value is present so add and then loop for 573da6c28aaSamw * any additional. Since this is new and 574da6c28aaSamw * previous values may exist, handle 575da6c28aaSamw * conversions. 576da6c28aaSamw */ 577da6c28aaSamw scf_iter_t *viter; 578da6c28aaSamw viter = scf_iter_create(handle->handle); 579da6c28aaSamw if (viter != NULL && 580da6c28aaSamw scf_iter_property_values(viter, prop) == 0) { 581da6c28aaSamw while (scf_iter_next_value(viter, value) > 0) { 582da6c28aaSamw /* Have a value so process it */ 583da6c28aaSamw if (scf_value_get_ustring(value, 584da6c28aaSamw valuestr, vallen) >= 0) { 585da6c28aaSamw /* have a ustring */ 586da6c28aaSamw _sa_make_resource(node, 587da6c28aaSamw valuestr); 588da6c28aaSamw } else if (scf_value_get_astring(value, 589da6c28aaSamw valuestr, vallen) >= 0) { 590da6c28aaSamw /* have an astring */ 591da6c28aaSamw _sa_make_resource(node, 592da6c28aaSamw valuestr); 593da6c28aaSamw } 594da6c28aaSamw } 595da6c28aaSamw scf_iter_destroy(viter); 596da6c28aaSamw } 5976185db85Sdougm } else { 5986185db85Sdougm if (strcmp(name, "description") == 0) { 59925a68471Sdougm /* We have a description node */ 6006185db85Sdougm xmlNodePtr desc; 6016185db85Sdougm desc = xmlNewChild(node, NULL, 60225a68471Sdougm (xmlChar *)"description", NULL); 6036185db85Sdougm if (desc != NULL) 6046185db85Sdougm xmlNodeSetContent(desc, 6056185db85Sdougm (xmlChar *)valuestr); 6066185db85Sdougm } 6076185db85Sdougm } 6086185db85Sdougm } 60925a68471Sdougm out: 610f345c0beSdougm /* 61125a68471Sdougm * A share without a path is broken so we want to not include 612f345c0beSdougm * these. They shouldn't happen but if you kill a sharemgr in 613f345c0beSdougm * the process of creating a share, it could happen. They 614f345c0beSdougm * should be harmless. It is also possible that another 615f345c0beSdougm * sharemgr is running and in the process of creating a share. 616f345c0beSdougm */ 617f345c0beSdougm if (have_path == 0 && node != NULL) { 618f345c0beSdougm xmlUnlinkNode(node); 619f345c0beSdougm xmlFreeNode(node); 620f345c0beSdougm } 6216185db85Sdougm if (name != NULL) 6226185db85Sdougm free(name); 6236185db85Sdougm if (valuestr != NULL) 6246185db85Sdougm free(valuestr); 6256185db85Sdougm if (value != NULL) 6266185db85Sdougm scf_value_destroy(value); 6276185db85Sdougm if (iter != NULL) 6286185db85Sdougm scf_iter_destroy(iter); 6296185db85Sdougm if (prop != NULL) 6306185db85Sdougm scf_property_destroy(prop); 6316185db85Sdougm } 6326185db85Sdougm 6336185db85Sdougm /* 6346185db85Sdougm * find_share_by_id(shareid) 6356185db85Sdougm * 6366185db85Sdougm * Search all shares in all groups until we find the share represented 6376185db85Sdougm * by "id". 6386185db85Sdougm */ 6396185db85Sdougm 6406185db85Sdougm static sa_share_t 641549ec3ffSdougm find_share_by_id(sa_handle_t handle, char *shareid) 6426185db85Sdougm { 6436185db85Sdougm sa_group_t group; 6446185db85Sdougm sa_share_t share = NULL; 6456185db85Sdougm char *id = NULL; 6466185db85Sdougm int done = 0; 6476185db85Sdougm 64825a68471Sdougm for (group = sa_get_group(handle, NULL); 64925a68471Sdougm group != NULL && !done; 6506185db85Sdougm group = sa_get_next_group(group)) { 65125a68471Sdougm for (share = sa_get_share(group, NULL); 65225a68471Sdougm share != NULL; 6536185db85Sdougm share = sa_get_next_share(share)) { 6546185db85Sdougm id = sa_get_share_attr(share, "id"); 6556185db85Sdougm if (id != NULL && strcmp(id, shareid) == 0) { 6566185db85Sdougm sa_free_attr_string(id); 6576185db85Sdougm id = NULL; 6586185db85Sdougm done++; 6596185db85Sdougm break; 6606185db85Sdougm } 6616185db85Sdougm if (id != NULL) { 6626185db85Sdougm sa_free_attr_string(id); 6636185db85Sdougm id = NULL; 6646185db85Sdougm } 6656185db85Sdougm } 6666185db85Sdougm } 6676185db85Sdougm return (share); 6686185db85Sdougm } 6696185db85Sdougm 6706185db85Sdougm /* 671da6c28aaSamw * find_resource_by_index(share, index) 672da6c28aaSamw * 673da6c28aaSamw * Search the resource records on the share for the id index. 674da6c28aaSamw */ 675da6c28aaSamw static sa_resource_t 676da6c28aaSamw find_resource_by_index(sa_share_t share, char *index) 677da6c28aaSamw { 678da6c28aaSamw sa_resource_t resource; 679da6c28aaSamw sa_resource_t found = NULL; 680da6c28aaSamw char *id; 681da6c28aaSamw 682da6c28aaSamw for (resource = sa_get_share_resource(share, NULL); 683da6c28aaSamw resource != NULL && found == NULL; 684da6c28aaSamw resource = sa_get_next_resource(resource)) { 685da6c28aaSamw id = (char *)xmlGetProp((xmlNodePtr)resource, (xmlChar *)"id"); 686da6c28aaSamw if (id != NULL) { 687da6c28aaSamw if (strcmp(id, index) == 0) { 688da6c28aaSamw /* found it so save in "found" */ 689da6c28aaSamw found = resource; 690da6c28aaSamw } 691da6c28aaSamw sa_free_attr_string(id); 692da6c28aaSamw } 693da6c28aaSamw } 694da6c28aaSamw return (found); 695da6c28aaSamw } 696da6c28aaSamw 697da6c28aaSamw /* 698da6c28aaSamw * sa_share_props_from_pgroup(root, handle, pg, id, sahandle) 6996185db85Sdougm * 70025a68471Sdougm * Extract share properties from the SMF property group. More sanity 7016185db85Sdougm * checks are done and the share object is created. We ignore some 7026185db85Sdougm * errors that could exist in the repository and only worry about 7036185db85Sdougm * property groups that validate in naming. 7046185db85Sdougm */ 7056185db85Sdougm 7066185db85Sdougm static int 7076185db85Sdougm sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle, 708549ec3ffSdougm scf_propertygroup_t *pg, char *id, sa_handle_t sahandle) 7096185db85Sdougm { 7106185db85Sdougm xmlNodePtr node; 71125a68471Sdougm char *name = NULL; 71225a68471Sdougm scf_iter_t *iter = NULL; 71325a68471Sdougm scf_property_t *prop = NULL; 71425a68471Sdougm scf_value_t *value = NULL; 7156185db85Sdougm ssize_t vallen; 71625a68471Sdougm char *valuestr = NULL; 7176185db85Sdougm int ret = SA_OK; 7186185db85Sdougm char *sectype = NULL; 7196185db85Sdougm char *proto; 7206185db85Sdougm sa_share_t share; 72125a68471Sdougm uuid_t uuid; 7226185db85Sdougm 7236185db85Sdougm /* 7246185db85Sdougm * While preliminary check (starts with 'S') passed before 7256185db85Sdougm * getting here. Need to make sure it is in ID syntax 7266185db85Sdougm * (Snnnnnn). Note that shares with properties have similar 7276185db85Sdougm * pgroups. If the pg name is more than SA_SHARE_PG_LEN 7286185db85Sdougm * characters, it is likely one of the protocol/security 7296185db85Sdougm * versions. 7306185db85Sdougm */ 7316185db85Sdougm vallen = strlen(id); 73225a68471Sdougm if (*id != SA_SHARE_PG_PREFIX[0] || vallen <= SA_SHARE_PG_LEN) { 73325a68471Sdougm /* 73425a68471Sdougm * It is ok to not have what we thought since someone might 73525a68471Sdougm * have added a name via SMF. 73625a68471Sdougm */ 73725a68471Sdougm return (ret); 73825a68471Sdougm } 7396185db85Sdougm if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) == 0) { 7406185db85Sdougm proto = strchr(id, '_'); 7416185db85Sdougm if (proto == NULL) 7426185db85Sdougm return (ret); 7436185db85Sdougm *proto++ = '\0'; 7446185db85Sdougm if (uuid_parse(id + SA_SHARE_PG_PREFIXLEN, uuid) < 0) 7456185db85Sdougm return (ret); 7466185db85Sdougm /* 7476185db85Sdougm * probably a legal optionset so check a few more 7486185db85Sdougm * syntax points below. 7496185db85Sdougm */ 7506185db85Sdougm if (*proto == '\0') { 7516185db85Sdougm /* not a valid proto (null) */ 7526185db85Sdougm return (ret); 7536185db85Sdougm } 754da6c28aaSamw 7556185db85Sdougm sectype = strchr(proto, '_'); 7566185db85Sdougm if (sectype != NULL) 7576185db85Sdougm *sectype++ = '\0'; 7586185db85Sdougm if (!valid_protocol(proto)) 7596185db85Sdougm return (ret); 7606185db85Sdougm } 7616185db85Sdougm 7626185db85Sdougm /* 76325a68471Sdougm * To get here, we have a valid protocol and possibly a 7646185db85Sdougm * security. We now have to find the share that it is really 7656185db85Sdougm * associated with. The "id" portion of the pgroup name will 7666185db85Sdougm * match. 7676185db85Sdougm */ 7686185db85Sdougm 769549ec3ffSdougm share = find_share_by_id(sahandle, id); 7706185db85Sdougm if (share == NULL) 7716185db85Sdougm return (SA_BAD_PATH); 7726185db85Sdougm 7736185db85Sdougm root = (xmlNodePtr)share; 7746185db85Sdougm 7756185db85Sdougm vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 7766185db85Sdougm 77725a68471Sdougm if (sectype == NULL) 77825a68471Sdougm node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL); 77925a68471Sdougm else { 780da6c28aaSamw if (isdigit((int)*sectype)) { 781da6c28aaSamw sa_resource_t resource; 782da6c28aaSamw /* 783da6c28aaSamw * If sectype[0] is a digit, then it is an index into 784da6c28aaSamw * the resource names. We need to find a resource 785da6c28aaSamw * record and then get the properties into an 786da6c28aaSamw * optionset. The optionset becomes the "node" and the 787da6c28aaSamw * rest is hung off of the share. 788da6c28aaSamw */ 789da6c28aaSamw resource = find_resource_by_index(share, sectype); 790da6c28aaSamw if (resource != NULL) { 791da6c28aaSamw node = xmlNewChild(resource, NULL, 792da6c28aaSamw (xmlChar *)"optionset", NULL); 793da6c28aaSamw } else { 79455bf511dSas200622 /* This shouldn't happen. */ 795da6c28aaSamw ret = SA_SYSTEM_ERR; 79655bf511dSas200622 goto out; 797da6c28aaSamw } 798da6c28aaSamw } else { 799da6c28aaSamw /* 800da6c28aaSamw * If not a digit, then it is a security type 801da6c28aaSamw * (alternate option space). Security types start with 802da6c28aaSamw * an alphabetic. 803da6c28aaSamw */ 804da6c28aaSamw node = xmlNewChild(root, NULL, (xmlChar *)"security", 805da6c28aaSamw NULL); 80625a68471Sdougm if (node != NULL) 80725a68471Sdougm xmlSetProp(node, (xmlChar *)"sectype", 80825a68471Sdougm (xmlChar *)sectype); 80925a68471Sdougm } 810da6c28aaSamw } 81125a68471Sdougm if (node == NULL) { 81225a68471Sdougm ret = SA_NO_MEMORY; 81325a68471Sdougm goto out; 81425a68471Sdougm } 81525a68471Sdougm 81625a68471Sdougm xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 81725a68471Sdougm /* now find the properties */ 8186185db85Sdougm iter = scf_iter_create(handle->handle); 8196185db85Sdougm value = scf_value_create(handle->handle); 8206185db85Sdougm prop = scf_property_create(handle->handle); 8216185db85Sdougm name = malloc(scf_max_name_len); 8226185db85Sdougm valuestr = malloc(vallen); 8236185db85Sdougm 82425a68471Sdougm if (iter == NULL || value == NULL || prop == NULL || name == NULL) 82525a68471Sdougm goto out; 82625a68471Sdougm 827da6c28aaSamw /* iterate over the share pg properties */ 8286185db85Sdougm if (scf_iter_pg_properties(iter, pg) == 0) { 8296185db85Sdougm while (scf_iter_next_property(iter, prop) > 0) { 8306185db85Sdougm ret = SA_SYSTEM_ERR; /* assume the worst */ 8316185db85Sdougm if (scf_property_get_name(prop, name, 8326185db85Sdougm scf_max_name_len) > 0) { 8336185db85Sdougm if (scf_property_get_value(prop, value) == 0) { 83425a68471Sdougm if (scf_value_get_astring(value, 83525a68471Sdougm valuestr, vallen) >= 0) { 8366185db85Sdougm ret = SA_OK; 8376185db85Sdougm } 8386185db85Sdougm } 8396185db85Sdougm } else { 8406185db85Sdougm ret = SA_SYSTEM_ERR; 8416185db85Sdougm } 8426185db85Sdougm if (ret == SA_OK) { 8436185db85Sdougm sa_property_t prop; 8446185db85Sdougm prop = sa_create_property(name, valuestr); 8456185db85Sdougm if (prop != NULL) 8466185db85Sdougm prop = (sa_property_t)xmlAddChild(node, 8476185db85Sdougm (xmlNodePtr)prop); 8486185db85Sdougm else 8496185db85Sdougm ret = SA_NO_MEMORY; 8506185db85Sdougm } 8516185db85Sdougm } 8526185db85Sdougm } else { 8536185db85Sdougm ret = SA_SYSTEM_ERR; 8546185db85Sdougm } 85525a68471Sdougm out: 8566185db85Sdougm if (iter != NULL) 8576185db85Sdougm scf_iter_destroy(iter); 8586185db85Sdougm if (value != NULL) 8596185db85Sdougm scf_value_destroy(value); 8606185db85Sdougm if (prop != NULL) 8616185db85Sdougm scf_property_destroy(prop); 8626185db85Sdougm if (name != NULL) 8636185db85Sdougm free(name); 8646185db85Sdougm if (valuestr != NULL) 8656185db85Sdougm free(valuestr); 8666185db85Sdougm return (ret); 8676185db85Sdougm } 8686185db85Sdougm 8696185db85Sdougm /* 8706185db85Sdougm * sa_extract_group(root, handle, instance) 8716185db85Sdougm * 87225a68471Sdougm * Get the config info for this instance of a group and create the XML 8736185db85Sdougm * subtree from it. 8746185db85Sdougm */ 8756185db85Sdougm 8766185db85Sdougm static int 8776185db85Sdougm sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle, 878549ec3ffSdougm scf_instance_t *instance, sa_handle_t sahandle) 8796185db85Sdougm { 8806185db85Sdougm char *buff; 8816185db85Sdougm xmlNodePtr node; 8826185db85Sdougm scf_iter_t *iter; 8836185db85Sdougm char *proto; 8846185db85Sdougm char *sectype; 8856185db85Sdougm int have_shares = 0; 8866185db85Sdougm int has_proto = 0; 8876185db85Sdougm int is_default = 0; 8886185db85Sdougm int ret = SA_OK; 8896185db85Sdougm int err; 8906185db85Sdougm 8916185db85Sdougm buff = malloc(scf_max_name_len); 89225a68471Sdougm if (buff == NULL) 89325a68471Sdougm return (SA_NO_MEMORY); 89425a68471Sdougm 8956185db85Sdougm iter = scf_iter_create(handle->handle); 89625a68471Sdougm if (iter == NULL) { 89725a68471Sdougm ret = SA_NO_MEMORY; 89825a68471Sdougm goto out; 89925a68471Sdougm } 90025a68471Sdougm 90125a68471Sdougm if (scf_instance_get_name(instance, buff, scf_max_name_len) > 0) { 9026185db85Sdougm node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL); 90325a68471Sdougm if (node == NULL) { 90425a68471Sdougm ret = SA_NO_MEMORY; 90525a68471Sdougm goto out; 90625a68471Sdougm } 9076185db85Sdougm xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff); 9086185db85Sdougm if (strcmp(buff, "default") == 0) 9096185db85Sdougm is_default++; 91025a68471Sdougm 9116185db85Sdougm sa_extract_attrs(node, handle, instance); 9126185db85Sdougm /* 9136185db85Sdougm * Iterate through all the property groups 9146185db85Sdougm * looking for those with security or 9156185db85Sdougm * optionset prefixes. The names of the 9166185db85Sdougm * matching pgroups are parsed to get the 9176185db85Sdougm * protocol, and for security, the sectype. 9186185db85Sdougm * Syntax is as follows: 9196185db85Sdougm * optionset | optionset_<proto> 9206185db85Sdougm * security_default | security_<proto>_<sectype> 9216185db85Sdougm * "operation" is handled by 9226185db85Sdougm * sa_extract_attrs(). 9236185db85Sdougm */ 92425a68471Sdougm if (scf_iter_instance_pgs(iter, instance) != 0) { 92525a68471Sdougm ret = SA_NO_MEMORY; 92625a68471Sdougm goto out; 92725a68471Sdougm } 9286185db85Sdougm while (scf_iter_next_pg(iter, handle->pg) > 0) { 92925a68471Sdougm /* Have a pgroup so sort it out */ 9306185db85Sdougm ret = scf_pg_get_name(handle->pg, buff, 9316185db85Sdougm scf_max_name_len); 9326185db85Sdougm if (ret > 0) { 9336185db85Sdougm if (buff[0] == SA_SHARE_PG_PREFIX[0]) { 9346185db85Sdougm sa_share_from_pgroup(node, handle, 93525a68471Sdougm handle->pg, buff); 9366185db85Sdougm have_shares++; 9376185db85Sdougm } else if (strncmp(buff, "optionset", 9) == 9386185db85Sdougm 0) { 9396185db85Sdougm char *nodetype = "optionset"; 94025a68471Sdougm /* Have an optionset */ 9416185db85Sdougm sectype = NULL; 9426185db85Sdougm proto = strchr(buff, '_'); 9436185db85Sdougm if (proto != NULL) { 9446185db85Sdougm *proto++ = '\0'; 9456185db85Sdougm sectype = strchr(proto, '_'); 9466185db85Sdougm if (sectype != NULL) { 9476185db85Sdougm *sectype++ = '\0'; 9486185db85Sdougm nodetype = "security"; 9496185db85Sdougm } 9506185db85Sdougm } 9516185db85Sdougm ret = sa_extract_pgroup(node, handle, 95225a68471Sdougm handle->pg, nodetype, proto, 95325a68471Sdougm sectype); 9546185db85Sdougm has_proto++; 95525a68471Sdougm } else if (strncmp(buff, "security", 8) == 0) { 9566185db85Sdougm /* 95725a68471Sdougm * Have a security (note that 9586185db85Sdougm * this should change in the 9596185db85Sdougm * future) 9606185db85Sdougm */ 9616185db85Sdougm proto = strchr(buff, '_'); 9626185db85Sdougm sectype = NULL; 9636185db85Sdougm if (proto != NULL) { 9646185db85Sdougm *proto++ = '\0'; 9656185db85Sdougm sectype = strchr(proto, '_'); 9666185db85Sdougm if (sectype != NULL) 9676185db85Sdougm *sectype++ = '\0'; 96825a68471Sdougm if (strcmp(proto, "default") == 96925a68471Sdougm 0) 9706185db85Sdougm proto = NULL; 9716185db85Sdougm } 9726185db85Sdougm ret = sa_extract_pgroup(node, handle, 97325a68471Sdougm handle->pg, "security", proto, 9746185db85Sdougm sectype); 9756185db85Sdougm has_proto++; 9766185db85Sdougm } 97725a68471Sdougm /* Ignore everything else */ 9786185db85Sdougm } 9796185db85Sdougm } 9806185db85Sdougm /* 9816185db85Sdougm * Make sure we have a valid default group. 9826185db85Sdougm * On first boot, default won't have any 9836185db85Sdougm * protocols defined and won't be enabled (but 9846185db85Sdougm * should be). 9856185db85Sdougm */ 9866185db85Sdougm if (is_default) { 9876185db85Sdougm char *state = sa_get_group_attr((sa_group_t)node, 9886185db85Sdougm "state"); 9896185db85Sdougm char **protos; 9906185db85Sdougm int numprotos; 9916185db85Sdougm int i; 9926185db85Sdougm 9936185db85Sdougm if (state == NULL) { 9946185db85Sdougm /* set attribute to enabled */ 9956185db85Sdougm (void) sa_set_group_attr((sa_group_t)node, 99625a68471Sdougm "state", "enabled"); 99725a68471Sdougm /* We can assume no protocols */ 9986185db85Sdougm numprotos = sa_get_protocols(&protos); 9996185db85Sdougm for (i = 0; i < numprotos; i++) 100025a68471Sdougm (void) sa_create_optionset( 100125a68471Sdougm (sa_group_t)node, protos[i]); 10026185db85Sdougm if (numprotos > 0) 10036185db85Sdougm free(protos); 10046185db85Sdougm } else { 10056185db85Sdougm sa_free_attr_string(state); 10066185db85Sdougm } 10076185db85Sdougm } 100825a68471Sdougm /* Do a second pass if shares were found */ 100925a68471Sdougm if (have_shares && scf_iter_instance_pgs(iter, instance) == 0) { 10106185db85Sdougm while (scf_iter_next_pg(iter, handle->pg) > 0) { 10116185db85Sdougm /* 101225a68471Sdougm * Have a pgroup so see if it is a 10136185db85Sdougm * share optionset 10146185db85Sdougm */ 10156185db85Sdougm err = scf_pg_get_name(handle->pg, buff, 10166185db85Sdougm scf_max_name_len); 101725a68471Sdougm if (err <= 0) 101825a68471Sdougm continue; 10196185db85Sdougm if (buff[0] == SA_SHARE_PG_PREFIX[0]) { 10206185db85Sdougm ret = sa_share_props_from_pgroup(node, 102125a68471Sdougm handle, handle->pg, buff, 102225a68471Sdougm sahandle); 10236185db85Sdougm } 10246185db85Sdougm } 10256185db85Sdougm } 10266185db85Sdougm } 102725a68471Sdougm out: 10286185db85Sdougm if (iter != NULL) 10296185db85Sdougm scf_iter_destroy(iter); 10306185db85Sdougm if (buff != NULL) 10316185db85Sdougm free(buff); 10326185db85Sdougm return (ret); 10336185db85Sdougm } 10346185db85Sdougm 10356185db85Sdougm /* 10366185db85Sdougm * sa_extract_defaults(root, handle, instance) 10376185db85Sdougm * 103825a68471Sdougm * Local function to find the default properties that live in the 1039da6c28aaSamw * default instance's "operation" property group. 10406185db85Sdougm */ 10416185db85Sdougm 10426185db85Sdougm static void 10436185db85Sdougm sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle, 10446185db85Sdougm scf_instance_t *instance) 10456185db85Sdougm { 10466185db85Sdougm xmlNodePtr node; 10476185db85Sdougm scf_property_t *prop; 10486185db85Sdougm scf_value_t *value; 10496185db85Sdougm char *valuestr; 10506185db85Sdougm ssize_t vallen; 10516185db85Sdougm 10526185db85Sdougm vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 10536185db85Sdougm prop = scf_property_create(handle->handle); 10546185db85Sdougm value = scf_value_create(handle->handle); 10556185db85Sdougm valuestr = malloc(vallen); 105625a68471Sdougm 105725a68471Sdougm if (prop == NULL || value == NULL || vallen == 0 || 105825a68471Sdougm scf_instance_get_pg(instance, "operation", handle->pg) != 0) 105925a68471Sdougm goto out; 106025a68471Sdougm 106125a68471Sdougm if (scf_pg_get_property(handle->pg, "legacy-timestamp", prop) != 0) 106225a68471Sdougm goto out; 106325a68471Sdougm 106425a68471Sdougm /* Found the property so get the value */ 10656185db85Sdougm if (scf_property_get_value(prop, value) == 0) { 10666185db85Sdougm if (scf_value_get_astring(value, valuestr, vallen) > 0) { 10676185db85Sdougm node = xmlNewChild(root, NULL, (xmlChar *)"legacy", 10686185db85Sdougm NULL); 10696185db85Sdougm if (node != NULL) { 10706185db85Sdougm xmlSetProp(node, (xmlChar *)"timestamp", 10716185db85Sdougm (xmlChar *)valuestr); 10726185db85Sdougm xmlSetProp(node, (xmlChar *)"path", 10736185db85Sdougm (xmlChar *)SA_LEGACY_DFSTAB); 10746185db85Sdougm } 10756185db85Sdougm } 10766185db85Sdougm } 107725a68471Sdougm out: 10786185db85Sdougm if (valuestr != NULL) 10796185db85Sdougm free(valuestr); 10806185db85Sdougm if (value != NULL) 10816185db85Sdougm scf_value_destroy(value); 10826185db85Sdougm if (prop != NULL) 10836185db85Sdougm scf_property_destroy(prop); 10846185db85Sdougm } 10856185db85Sdougm 10866185db85Sdougm 10876185db85Sdougm /* 1088da6c28aaSamw * sa_get_config(handle, root, doc, sahandle) 10896185db85Sdougm * 109025a68471Sdougm * Walk the SMF repository for /network/shares/group and find all the 10916185db85Sdougm * instances. These become group names. Then add the XML structure 10926185db85Sdougm * below the groups based on property groups and properties. 10936185db85Sdougm */ 10946185db85Sdougm int 10951d1813a7Sdougm sa_get_config(scfutilhandle_t *handle, xmlNodePtr root, sa_handle_t sahandle) 10966185db85Sdougm { 10976185db85Sdougm int ret = SA_OK; 10986185db85Sdougm scf_instance_t *instance; 10996185db85Sdougm scf_iter_t *iter; 11006185db85Sdougm char buff[BUFSIZ * 2]; 11016185db85Sdougm 11026185db85Sdougm instance = scf_instance_create(handle->handle); 11036185db85Sdougm iter = scf_iter_create(handle->handle); 11041d1813a7Sdougm if (instance != NULL && iter != NULL) { 11056185db85Sdougm if ((ret = scf_iter_service_instances(iter, 11066185db85Sdougm handle->service)) == 0) { 11076185db85Sdougm while ((ret = scf_iter_next_instance(iter, 11086185db85Sdougm instance)) > 0) { 11096185db85Sdougm if (scf_instance_get_name(instance, buff, 11106185db85Sdougm sizeof (buff)) > 0) { 11116185db85Sdougm if (strcmp(buff, "default") == 0) 111225a68471Sdougm sa_extract_defaults(root, 111325a68471Sdougm handle, instance); 111425a68471Sdougm ret = sa_extract_group(root, handle, 111525a68471Sdougm instance, sahandle); 11166185db85Sdougm } 11176185db85Sdougm } 11186185db85Sdougm } 11196185db85Sdougm } 11201d1813a7Sdougm 112125a68471Sdougm /* Always cleanup these */ 11226185db85Sdougm if (instance != NULL) 11236185db85Sdougm scf_instance_destroy(instance); 11246185db85Sdougm if (iter != NULL) 11256185db85Sdougm scf_iter_destroy(iter); 11266185db85Sdougm return (ret); 11276185db85Sdougm } 11286185db85Sdougm 11296185db85Sdougm /* 11306185db85Sdougm * sa_get_instance(handle, instance) 11316185db85Sdougm * 113225a68471Sdougm * Get the instance of the group service. This is actually the 11336185db85Sdougm * specific group name. The instance is needed for all property and 11346185db85Sdougm * control operations. 11356185db85Sdougm */ 11366185db85Sdougm 11376185db85Sdougm int 11386185db85Sdougm sa_get_instance(scfutilhandle_t *handle, char *instname) 11396185db85Sdougm { 11406185db85Sdougm if (scf_service_get_instance(handle->service, instname, 11416185db85Sdougm handle->instance) != 0) { 11426185db85Sdougm return (SA_NO_SUCH_GROUP); 11436185db85Sdougm } 11446185db85Sdougm return (SA_OK); 11456185db85Sdougm } 11466185db85Sdougm 11476185db85Sdougm /* 11486185db85Sdougm * sa_create_instance(handle, instname) 11496185db85Sdougm * 11506185db85Sdougm * Create a new SMF service instance. There can only be one with a 11516185db85Sdougm * given name. 11526185db85Sdougm */ 11536185db85Sdougm 11546185db85Sdougm int 11556185db85Sdougm sa_create_instance(scfutilhandle_t *handle, char *instname) 11566185db85Sdougm { 11576185db85Sdougm int ret = SA_OK; 11586185db85Sdougm char instance[SA_GROUP_INST_LEN]; 11596185db85Sdougm if (scf_service_add_instance(handle->service, instname, 11606185db85Sdougm handle->instance) != 0) { 11616185db85Sdougm /* better error returns need to be added based on real error */ 11626185db85Sdougm if (scf_error() == SCF_ERROR_PERMISSION_DENIED) 11636185db85Sdougm ret = SA_NO_PERMISSION; 11646185db85Sdougm else 11656185db85Sdougm ret = SA_DUPLICATE_NAME; 11666185db85Sdougm } else { 11676185db85Sdougm /* have the service created, so enable it */ 11686185db85Sdougm (void) snprintf(instance, sizeof (instance), "%s:%s", 11696185db85Sdougm SA_SVC_FMRI_BASE, instname); 11706185db85Sdougm (void) smf_enable_instance(instance, 0); 11716185db85Sdougm } 11726185db85Sdougm return (ret); 11736185db85Sdougm } 11746185db85Sdougm 11756185db85Sdougm /* 11766185db85Sdougm * sa_delete_instance(handle, instname) 11776185db85Sdougm * 11786185db85Sdougm * When a group goes away, we also remove the service instance. 11796185db85Sdougm */ 11806185db85Sdougm 11816185db85Sdougm int 11826185db85Sdougm sa_delete_instance(scfutilhandle_t *handle, char *instname) 11836185db85Sdougm { 11846185db85Sdougm int ret; 11856185db85Sdougm 11866185db85Sdougm if (strcmp(instname, "default") == 0) { 11876185db85Sdougm ret = SA_NO_PERMISSION; 11886185db85Sdougm } else { 11896185db85Sdougm if ((ret = sa_get_instance(handle, instname)) == SA_OK) { 11906185db85Sdougm if (scf_instance_delete(handle->instance) != 0) 11916185db85Sdougm /* need better analysis */ 11926185db85Sdougm ret = SA_NO_PERMISSION; 11936185db85Sdougm } 11946185db85Sdougm } 11956185db85Sdougm return (ret); 11966185db85Sdougm } 11976185db85Sdougm 11986185db85Sdougm /* 11996185db85Sdougm * sa_create_pgroup(handle, pgroup) 12006185db85Sdougm * 12016185db85Sdougm * create a new property group 12026185db85Sdougm */ 12036185db85Sdougm 12046185db85Sdougm int 12056185db85Sdougm sa_create_pgroup(scfutilhandle_t *handle, char *pgroup) 12066185db85Sdougm { 12076185db85Sdougm int ret = SA_OK; 1208*5b6e0c46Sdougm int persist = 0; 1209*5b6e0c46Sdougm 12106185db85Sdougm /* 121125a68471Sdougm * Only create a handle if it doesn't exist. It is ok to exist 12126185db85Sdougm * since the pg handle will be set as a side effect. 12136185db85Sdougm */ 121425a68471Sdougm if (handle->pg == NULL) 12156185db85Sdougm handle->pg = scf_pg_create(handle->handle); 121625a68471Sdougm 12176185db85Sdougm /* 1218*5b6e0c46Sdougm * Special case for a non-persistent property group. This is 1219*5b6e0c46Sdougm * internal use only. 1220*5b6e0c46Sdougm */ 1221*5b6e0c46Sdougm if (*pgroup == '*') { 1222*5b6e0c46Sdougm persist = SCF_PG_FLAG_NONPERSISTENT; 1223*5b6e0c46Sdougm pgroup++; 1224*5b6e0c46Sdougm } 1225*5b6e0c46Sdougm 1226*5b6e0c46Sdougm /* 122725a68471Sdougm * If the pgroup exists, we are done. If it doesn't, then we 12286185db85Sdougm * need to actually add one to the service instance. 12296185db85Sdougm */ 12306185db85Sdougm if (scf_instance_get_pg(handle->instance, 12316185db85Sdougm pgroup, handle->pg) != 0) { 1232*5b6e0c46Sdougm 123325a68471Sdougm /* Doesn't exist so create one */ 12346185db85Sdougm if (scf_instance_add_pg(handle->instance, pgroup, 1235*5b6e0c46Sdougm SCF_GROUP_APPLICATION, persist, handle->pg) != 0) { 12366185db85Sdougm switch (scf_error()) { 12376185db85Sdougm case SCF_ERROR_PERMISSION_DENIED: 12386185db85Sdougm ret = SA_NO_PERMISSION; 12396185db85Sdougm break; 12406185db85Sdougm default: 12416185db85Sdougm ret = SA_SYSTEM_ERR; 12426185db85Sdougm break; 12436185db85Sdougm } 12446185db85Sdougm } 12456185db85Sdougm } 12466185db85Sdougm return (ret); 12476185db85Sdougm } 12486185db85Sdougm 12496185db85Sdougm /* 12506185db85Sdougm * sa_delete_pgroup(handle, pgroup) 12516185db85Sdougm * 125225a68471Sdougm * Remove the property group from the current instance of the service, 12536185db85Sdougm * but only if it actually exists. 12546185db85Sdougm */ 12556185db85Sdougm 12566185db85Sdougm int 12576185db85Sdougm sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup) 12586185db85Sdougm { 12596185db85Sdougm int ret = SA_OK; 12606185db85Sdougm /* 126125a68471Sdougm * Only delete if it does exist. 12626185db85Sdougm */ 126325a68471Sdougm if (scf_instance_get_pg(handle->instance, pgroup, handle->pg) == 0) { 12646185db85Sdougm /* does exist so delete it */ 126525a68471Sdougm if (scf_pg_delete(handle->pg) != 0) 12666185db85Sdougm ret = SA_SYSTEM_ERR; 12676185db85Sdougm } else { 12686185db85Sdougm ret = SA_SYSTEM_ERR; 12696185db85Sdougm } 12706185db85Sdougm if (ret == SA_SYSTEM_ERR && 12716185db85Sdougm scf_error() == SCF_ERROR_PERMISSION_DENIED) { 12726185db85Sdougm ret = SA_NO_PERMISSION; 12736185db85Sdougm } 12746185db85Sdougm return (ret); 12756185db85Sdougm } 12766185db85Sdougm 12776185db85Sdougm /* 12786185db85Sdougm * sa_start_transaction(handle, pgroup) 12796185db85Sdougm * 12806185db85Sdougm * Start an SMF transaction so we can deal with properties. it would 12816185db85Sdougm * be nice to not have to expose this, but we have to in order to 12826185db85Sdougm * optimize. 12836185db85Sdougm * 12846185db85Sdougm * Basic model is to hold the transaction in the handle and allow 12856185db85Sdougm * property adds/deletes/updates to be added then close the 12866185db85Sdougm * transaction (or abort). There may eventually be a need to handle 12876185db85Sdougm * other types of transaction mechanisms but we don't do that now. 12886185db85Sdougm * 12896185db85Sdougm * An sa_start_transaction must be followed by either an 12906185db85Sdougm * sa_end_transaction or sa_abort_transaction before another 12916185db85Sdougm * sa_start_transaction can be done. 12926185db85Sdougm */ 12936185db85Sdougm 12946185db85Sdougm int 12956185db85Sdougm sa_start_transaction(scfutilhandle_t *handle, char *propgroup) 12966185db85Sdougm { 12976185db85Sdougm int ret = SA_OK; 12986185db85Sdougm /* 129925a68471Sdougm * Lookup the property group and create it if it doesn't already 13006185db85Sdougm * exist. 13016185db85Sdougm */ 1302*5b6e0c46Sdougm if (handle == NULL) 1303*5b6e0c46Sdougm return (SA_CONFIG_ERR); 1304*5b6e0c46Sdougm 13056185db85Sdougm if (handle->scf_state == SCH_STATE_INIT) { 13066185db85Sdougm ret = sa_create_pgroup(handle, propgroup); 13076185db85Sdougm if (ret == SA_OK) { 13086185db85Sdougm handle->trans = scf_transaction_create(handle->handle); 13096185db85Sdougm if (handle->trans != NULL) { 131025a68471Sdougm if (scf_transaction_start(handle->trans, 131125a68471Sdougm handle->pg) != 0) { 13126185db85Sdougm ret = SA_SYSTEM_ERR; 13136185db85Sdougm } 13146185db85Sdougm if (ret != SA_OK) { 13156185db85Sdougm scf_transaction_destroy(handle->trans); 13166185db85Sdougm handle->trans = NULL; 13176185db85Sdougm } 13186185db85Sdougm } else { 13196185db85Sdougm ret = SA_SYSTEM_ERR; 13206185db85Sdougm } 13216185db85Sdougm } 13226185db85Sdougm } 13236185db85Sdougm if (ret == SA_SYSTEM_ERR && 13246185db85Sdougm scf_error() == SCF_ERROR_PERMISSION_DENIED) { 13256185db85Sdougm ret = SA_NO_PERMISSION; 13266185db85Sdougm } 13276185db85Sdougm return (ret); 13286185db85Sdougm } 13296185db85Sdougm 1330*5b6e0c46Sdougm 13316185db85Sdougm /* 1332*5b6e0c46Sdougm * sa_end_transaction(scfhandle, sahandle) 13336185db85Sdougm * 13346185db85Sdougm * Commit the changes that were added to the transaction in the 13356185db85Sdougm * handle. Do all necessary cleanup. 13366185db85Sdougm */ 13376185db85Sdougm 13386185db85Sdougm int 1339*5b6e0c46Sdougm sa_end_transaction(scfutilhandle_t *handle, sa_handle_impl_t sahandle) 13406185db85Sdougm { 13416185db85Sdougm int ret = SA_OK; 13426185db85Sdougm 1343*5b6e0c46Sdougm if (handle == NULL || handle->trans == NULL || sahandle == NULL) { 13446185db85Sdougm ret = SA_SYSTEM_ERR; 13456185db85Sdougm } else { 13466185db85Sdougm if (scf_transaction_commit(handle->trans) < 0) 13476185db85Sdougm ret = SA_SYSTEM_ERR; 13486185db85Sdougm scf_transaction_destroy_children(handle->trans); 13496185db85Sdougm scf_transaction_destroy(handle->trans); 1350*5b6e0c46Sdougm if (ret == SA_OK) 1351*5b6e0c46Sdougm set_transaction_tstamp(sahandle); 13526185db85Sdougm handle->trans = NULL; 13536185db85Sdougm } 13546185db85Sdougm return (ret); 13556185db85Sdougm } 13566185db85Sdougm 13576185db85Sdougm /* 13586185db85Sdougm * sa_abort_transaction(handle) 13596185db85Sdougm * 13606185db85Sdougm * Abort the changes that were added to the transaction in the 13616185db85Sdougm * handle. Do all necessary cleanup. 13626185db85Sdougm */ 13636185db85Sdougm 13646185db85Sdougm void 13656185db85Sdougm sa_abort_transaction(scfutilhandle_t *handle) 13666185db85Sdougm { 13676185db85Sdougm if (handle->trans != NULL) { 13686185db85Sdougm scf_transaction_reset_all(handle->trans); 13696185db85Sdougm scf_transaction_destroy_children(handle->trans); 13706185db85Sdougm scf_transaction_destroy(handle->trans); 13716185db85Sdougm handle->trans = NULL; 13726185db85Sdougm } 13736185db85Sdougm } 13746185db85Sdougm 13756185db85Sdougm /* 1376*5b6e0c46Sdougm * set_transaction_tstamp(sahandle) 1377*5b6e0c46Sdougm * 1378*5b6e0c46Sdougm * After a successful transaction commit, update the timestamp of the 1379*5b6e0c46Sdougm * last transaction. This lets us detect changes from other processes. 1380*5b6e0c46Sdougm */ 1381*5b6e0c46Sdougm static void 1382*5b6e0c46Sdougm set_transaction_tstamp(sa_handle_impl_t sahandle) 1383*5b6e0c46Sdougm { 1384*5b6e0c46Sdougm char tstring[32]; 1385*5b6e0c46Sdougm struct timeval tv; 1386*5b6e0c46Sdougm scfutilhandle_t *scfhandle; 1387*5b6e0c46Sdougm 1388*5b6e0c46Sdougm if (sahandle == NULL || sahandle->scfhandle == NULL) 1389*5b6e0c46Sdougm return; 1390*5b6e0c46Sdougm 1391*5b6e0c46Sdougm scfhandle = sahandle->scfhandle; 1392*5b6e0c46Sdougm 1393*5b6e0c46Sdougm if (sa_get_instance(scfhandle, "default") != SA_OK) 1394*5b6e0c46Sdougm return; 1395*5b6e0c46Sdougm 1396*5b6e0c46Sdougm if (gettimeofday(&tv, NULL) != 0) 1397*5b6e0c46Sdougm return; 1398*5b6e0c46Sdougm 1399*5b6e0c46Sdougm if (sa_start_transaction(scfhandle, "*state") != SA_OK) 1400*5b6e0c46Sdougm return; 1401*5b6e0c46Sdougm 1402*5b6e0c46Sdougm sahandle->tstrans = TSTAMP((*(timestruc_t *)&tv)); 1403*5b6e0c46Sdougm (void) snprintf(tstring, sizeof (tstring), "%lld", sahandle->tstrans); 1404*5b6e0c46Sdougm if (sa_set_property(sahandle->scfhandle, "lastupdate", tstring) == 1405*5b6e0c46Sdougm SA_OK) { 1406*5b6e0c46Sdougm /* 1407*5b6e0c46Sdougm * While best if it succeeds, a failure doesn't cause 1408*5b6e0c46Sdougm * problems and we will ignore it anyway. 1409*5b6e0c46Sdougm */ 1410*5b6e0c46Sdougm (void) scf_transaction_commit(scfhandle->trans); 1411*5b6e0c46Sdougm scf_transaction_destroy_children(scfhandle->trans); 1412*5b6e0c46Sdougm scf_transaction_destroy(scfhandle->trans); 1413*5b6e0c46Sdougm } else { 1414*5b6e0c46Sdougm sa_abort_transaction(scfhandle); 1415*5b6e0c46Sdougm } 1416*5b6e0c46Sdougm } 1417*5b6e0c46Sdougm 1418*5b6e0c46Sdougm /* 14196185db85Sdougm * sa_set_property(handle, prop, value) 14206185db85Sdougm * 142125a68471Sdougm * Set a property transaction entry into the pending SMF transaction. 14226185db85Sdougm */ 14236185db85Sdougm 14246185db85Sdougm int 14256185db85Sdougm sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr) 14266185db85Sdougm { 14276185db85Sdougm int ret = SA_OK; 14286185db85Sdougm scf_value_t *value; 14296185db85Sdougm scf_transaction_entry_t *entry; 14306185db85Sdougm /* 143125a68471Sdougm * Properties must be set in transactions and don't take 14326185db85Sdougm * effect until the transaction has been ended/committed. 14336185db85Sdougm */ 14346185db85Sdougm value = scf_value_create(handle->handle); 14356185db85Sdougm entry = scf_entry_create(handle->handle); 14366185db85Sdougm if (value != NULL && entry != NULL) { 14376185db85Sdougm if (scf_transaction_property_change(handle->trans, entry, 143825a68471Sdougm propname, SCF_TYPE_ASTRING) == 0 || 14396185db85Sdougm scf_transaction_property_new(handle->trans, entry, 144025a68471Sdougm propname, SCF_TYPE_ASTRING) == 0) { 14416185db85Sdougm if (scf_value_set_astring(value, valstr) == 0) { 14426185db85Sdougm if (scf_entry_add_value(entry, value) != 0) { 14436185db85Sdougm ret = SA_SYSTEM_ERR; 14446185db85Sdougm scf_value_destroy(value); 14456185db85Sdougm } 144625a68471Sdougm /* The value is in the transaction */ 14476185db85Sdougm value = NULL; 14486185db85Sdougm } else { 144925a68471Sdougm /* Value couldn't be constructed */ 14506185db85Sdougm ret = SA_SYSTEM_ERR; 14516185db85Sdougm } 145225a68471Sdougm /* The entry is in the transaction */ 14536185db85Sdougm entry = NULL; 14546185db85Sdougm } else { 14556185db85Sdougm ret = SA_SYSTEM_ERR; 14566185db85Sdougm } 14576185db85Sdougm } else { 14586185db85Sdougm ret = SA_SYSTEM_ERR; 14596185db85Sdougm } 14606185db85Sdougm if (ret == SA_SYSTEM_ERR) { 14616185db85Sdougm switch (scf_error()) { 14626185db85Sdougm case SCF_ERROR_PERMISSION_DENIED: 14636185db85Sdougm ret = SA_NO_PERMISSION; 14646185db85Sdougm break; 14656185db85Sdougm } 14666185db85Sdougm } 14676185db85Sdougm /* 146825a68471Sdougm * Cleanup if there were any errors that didn't leave these 14696185db85Sdougm * values where they would be cleaned up later. 14706185db85Sdougm */ 14716185db85Sdougm if (value != NULL) 14726185db85Sdougm scf_value_destroy(value); 14736185db85Sdougm if (entry != NULL) 14746185db85Sdougm scf_entry_destroy(entry); 14756185db85Sdougm return (ret); 14766185db85Sdougm } 14776185db85Sdougm 14786185db85Sdougm /* 1479da6c28aaSamw * check_resource(share) 1480da6c28aaSamw * 1481da6c28aaSamw * Check to see if share has any persistent resources. We don't want 1482da6c28aaSamw * to save if they are all transient. 1483da6c28aaSamw */ 1484da6c28aaSamw static int 1485da6c28aaSamw check_resource(sa_share_t share) 1486da6c28aaSamw { 1487da6c28aaSamw sa_resource_t resource; 1488da6c28aaSamw int ret = B_FALSE; 1489da6c28aaSamw 1490da6c28aaSamw for (resource = sa_get_share_resource(share, NULL); 1491da6c28aaSamw resource != NULL && ret == B_FALSE; 1492da6c28aaSamw resource = sa_get_next_resource(resource)) { 1493da6c28aaSamw char *type; 1494da6c28aaSamw type = sa_get_resource_attr(resource, "type"); 1495da6c28aaSamw if (type != NULL) { 1496da6c28aaSamw if (strcmp(type, "transient") != 0) { 1497da6c28aaSamw ret = B_TRUE; 1498da6c28aaSamw } 1499da6c28aaSamw sa_free_attr_string(type); 1500da6c28aaSamw } 1501da6c28aaSamw } 1502da6c28aaSamw return (ret); 1503da6c28aaSamw } 1504da6c28aaSamw 1505da6c28aaSamw /* 1506da6c28aaSamw * sa_set_resource_property(handle, prop, value) 1507da6c28aaSamw * 1508da6c28aaSamw * set a property transaction entry into the pending SMF 1509da6c28aaSamw * transaction. We don't want to include any transient resources 1510da6c28aaSamw */ 1511da6c28aaSamw 1512da6c28aaSamw static int 1513da6c28aaSamw sa_set_resource_property(scfutilhandle_t *handle, sa_share_t share) 1514da6c28aaSamw { 1515da6c28aaSamw int ret = SA_OK; 1516da6c28aaSamw scf_value_t *value; 1517da6c28aaSamw scf_transaction_entry_t *entry; 1518da6c28aaSamw sa_resource_t resource; 1519da6c28aaSamw char *valstr; 1520da6c28aaSamw char *idstr; 1521da6c28aaSamw char *description; 1522da6c28aaSamw char *propstr = NULL; 1523da6c28aaSamw size_t strsize; 1524da6c28aaSamw 1525da6c28aaSamw /* don't bother if no persistent resources */ 1526da6c28aaSamw if (check_resource(share) == B_FALSE) 1527da6c28aaSamw return (ret); 1528da6c28aaSamw 1529da6c28aaSamw /* 1530da6c28aaSamw * properties must be set in transactions and don't take 1531da6c28aaSamw * effect until the transaction has been ended/committed. 1532da6c28aaSamw */ 1533da6c28aaSamw entry = scf_entry_create(handle->handle); 1534da6c28aaSamw if (entry == NULL) 1535da6c28aaSamw return (SA_SYSTEM_ERR); 1536da6c28aaSamw 1537da6c28aaSamw if (scf_transaction_property_change(handle->trans, entry, 1538da6c28aaSamw "resource", SCF_TYPE_ASTRING) != 0 && 1539da6c28aaSamw scf_transaction_property_new(handle->trans, entry, 1540da6c28aaSamw "resource", SCF_TYPE_ASTRING) != 0) { 1541da6c28aaSamw scf_entry_destroy(entry); 1542da6c28aaSamw return (SA_SYSTEM_ERR); 1543da6c28aaSamw 1544da6c28aaSamw } 1545da6c28aaSamw for (resource = sa_get_share_resource(share, NULL); 1546da6c28aaSamw resource != NULL; 1547da6c28aaSamw resource = sa_get_next_resource(resource)) { 1548da6c28aaSamw value = scf_value_create(handle->handle); 1549da6c28aaSamw if (value == NULL) { 1550da6c28aaSamw ret = SA_NO_MEMORY; 1551da6c28aaSamw break; 1552da6c28aaSamw } 1553da6c28aaSamw /* Get size of complete string */ 1554da6c28aaSamw valstr = sa_get_resource_attr(resource, "name"); 1555da6c28aaSamw idstr = sa_get_resource_attr(resource, "id"); 1556da6c28aaSamw description = sa_get_resource_description(resource); 1557da6c28aaSamw strsize = (valstr != NULL) ? strlen(valstr) : 0; 1558da6c28aaSamw strsize += (idstr != NULL) ? strlen(idstr) : 0; 1559da6c28aaSamw strsize += (description != NULL) ? strlen(description) : 0; 1560da6c28aaSamw if (strsize > 0) { 1561da6c28aaSamw strsize += 3; /* add nul and ':' */ 1562da6c28aaSamw propstr = (char *)malloc(strsize); 1563da6c28aaSamw if (propstr == NULL) { 1564da6c28aaSamw scf_value_destroy(value); 1565da6c28aaSamw ret = SA_NO_MEMORY; 1566da6c28aaSamw goto err; 1567da6c28aaSamw } 1568da6c28aaSamw if (idstr == NULL) 1569da6c28aaSamw (void) snprintf(propstr, strsize, "%s", 1570da6c28aaSamw valstr ? valstr : ""); 1571da6c28aaSamw else 1572da6c28aaSamw (void) snprintf(propstr, strsize, "%s:%s:%s", 1573da6c28aaSamw idstr ? idstr : "", valstr ? valstr : "", 1574da6c28aaSamw description ? description : ""); 1575da6c28aaSamw if (scf_value_set_astring(value, propstr) != 0) { 1576da6c28aaSamw ret = SA_SYSTEM_ERR; 1577da6c28aaSamw free(propstr); 1578da6c28aaSamw scf_value_destroy(value); 1579da6c28aaSamw break; 1580da6c28aaSamw } 1581da6c28aaSamw if (scf_entry_add_value(entry, value) != 0) { 1582da6c28aaSamw ret = SA_SYSTEM_ERR; 1583da6c28aaSamw free(propstr); 1584da6c28aaSamw scf_value_destroy(value); 1585da6c28aaSamw break; 1586da6c28aaSamw } 1587da6c28aaSamw /* the value is in the transaction */ 1588da6c28aaSamw value = NULL; 1589da6c28aaSamw free(propstr); 1590da6c28aaSamw } 1591da6c28aaSamw err: 1592da6c28aaSamw if (valstr != NULL) 1593da6c28aaSamw sa_free_attr_string(valstr); 1594da6c28aaSamw if (idstr != NULL) 1595da6c28aaSamw sa_free_attr_string(idstr); 1596da6c28aaSamw if (description != NULL) 1597da6c28aaSamw sa_free_share_description(description); 1598da6c28aaSamw } 1599da6c28aaSamw /* the entry is in the transaction */ 1600da6c28aaSamw entry = NULL; 1601da6c28aaSamw 1602da6c28aaSamw if (ret == SA_SYSTEM_ERR) { 1603da6c28aaSamw switch (scf_error()) { 1604da6c28aaSamw case SCF_ERROR_PERMISSION_DENIED: 1605da6c28aaSamw ret = SA_NO_PERMISSION; 1606da6c28aaSamw break; 1607da6c28aaSamw } 1608da6c28aaSamw } 1609da6c28aaSamw /* 1610da6c28aaSamw * cleanup if there were any errors that didn't leave 1611da6c28aaSamw * these values where they would be cleaned up later. 1612da6c28aaSamw */ 1613da6c28aaSamw if (entry != NULL) 1614da6c28aaSamw scf_entry_destroy(entry); 1615da6c28aaSamw 1616da6c28aaSamw return (ret); 1617da6c28aaSamw } 1618da6c28aaSamw 1619da6c28aaSamw /* 16206185db85Sdougm * sa_commit_share(handle, group, share) 16216185db85Sdougm * 162225a68471Sdougm * Commit this share to the repository. 16236185db85Sdougm * properties are added if they exist but can be added later. 16246185db85Sdougm * Need to add to dfstab and sharetab, if appropriate. 16256185db85Sdougm */ 16266185db85Sdougm int 16276185db85Sdougm sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share) 16286185db85Sdougm { 16296185db85Sdougm int ret = SA_OK; 16306185db85Sdougm char *groupname; 16316185db85Sdougm char *name; 16326185db85Sdougm char *description; 16336185db85Sdougm char *sharename; 16346185db85Sdougm ssize_t proplen; 16356185db85Sdougm char *propstring; 16366185db85Sdougm 16376185db85Sdougm /* 163825a68471Sdougm * Don't commit in the zfs group. We do commit legacy 16396185db85Sdougm * (default) and all other groups/shares. ZFS is handled 16406185db85Sdougm * through the ZFS configuration rather than SMF. 16416185db85Sdougm */ 16426185db85Sdougm 16436185db85Sdougm groupname = sa_get_group_attr(group, "name"); 16446185db85Sdougm if (groupname != NULL) { 16456185db85Sdougm if (strcmp(groupname, "zfs") == 0) { 16466185db85Sdougm /* 164725a68471Sdougm * Adding to the ZFS group will result in the sharenfs 16486185db85Sdougm * property being set but we don't want to do anything 16496185db85Sdougm * SMF related at this point. 16506185db85Sdougm */ 16516185db85Sdougm sa_free_attr_string(groupname); 16526185db85Sdougm return (ret); 16536185db85Sdougm } 16546185db85Sdougm } 16556185db85Sdougm 16566185db85Sdougm proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 16576185db85Sdougm propstring = malloc(proplen); 16586185db85Sdougm if (propstring == NULL) 16596185db85Sdougm ret = SA_NO_MEMORY; 16606185db85Sdougm 16616185db85Sdougm if (groupname != NULL && ret == SA_OK) { 16626185db85Sdougm ret = sa_get_instance(handle, groupname); 16636185db85Sdougm sa_free_attr_string(groupname); 16646185db85Sdougm groupname = NULL; 16656185db85Sdougm sharename = sa_get_share_attr(share, "id"); 16666185db85Sdougm if (sharename == NULL) { 16676185db85Sdougm /* slipped by */ 16686185db85Sdougm char shname[SA_SHARE_UUID_BUFLEN]; 16696185db85Sdougm generate_unique_sharename(shname); 16706185db85Sdougm xmlSetProp((xmlNodePtr)share, (xmlChar *)"id", 16716185db85Sdougm (xmlChar *)shname); 16726185db85Sdougm sharename = strdup(shname); 16736185db85Sdougm } 16746185db85Sdougm if (sharename != NULL) { 1675f345c0beSdougm sigset_t old, new; 16766185db85Sdougm /* 167725a68471Sdougm * Have a share name allocated so create a pgroup for 1678f345c0beSdougm * it. It may already exist, but that is OK. In order 1679f345c0beSdougm * to avoid creating a share pgroup that doesn't have 1680f345c0beSdougm * a path property, block signals around the critical 1681f345c0beSdougm * region of creating the share pgroup and props. 16826185db85Sdougm */ 1683f345c0beSdougm (void) sigprocmask(SIG_BLOCK, NULL, &new); 1684f345c0beSdougm (void) sigaddset(&new, SIGHUP); 1685f345c0beSdougm (void) sigaddset(&new, SIGINT); 1686f345c0beSdougm (void) sigaddset(&new, SIGQUIT); 1687f345c0beSdougm (void) sigaddset(&new, SIGTSTP); 1688f345c0beSdougm (void) sigprocmask(SIG_SETMASK, &new, &old); 1689f345c0beSdougm 16906185db85Sdougm ret = sa_create_pgroup(handle, sharename); 16916185db85Sdougm if (ret == SA_OK) { 16926185db85Sdougm /* 169325a68471Sdougm * Now start the transaction for the 16946185db85Sdougm * properties that define this share. They may 16956185db85Sdougm * exist so attempt to update before create. 16966185db85Sdougm */ 16976185db85Sdougm ret = sa_start_transaction(handle, sharename); 16986185db85Sdougm } 16996185db85Sdougm if (ret == SA_OK) { 17006185db85Sdougm name = sa_get_share_attr(share, "path"); 17016185db85Sdougm if (name != NULL) { 170225a68471Sdougm /* 170325a68471Sdougm * There needs to be a path 170425a68471Sdougm * for a share to exist. 170525a68471Sdougm */ 170625a68471Sdougm ret = sa_set_property(handle, "path", 170725a68471Sdougm name); 17086185db85Sdougm sa_free_attr_string(name); 17096185db85Sdougm } else { 17106185db85Sdougm ret = SA_NO_MEMORY; 17116185db85Sdougm } 17126185db85Sdougm } 17136185db85Sdougm if (ret == SA_OK) { 1714da6c28aaSamw name = sa_get_share_attr(share, "drive-letter"); 1715da6c28aaSamw if (name != NULL) { 1716da6c28aaSamw /* A drive letter may exist for SMB */ 171725a68471Sdougm ret = sa_set_property(handle, 1718da6c28aaSamw "drive-letter", name); 1719da6c28aaSamw sa_free_attr_string(name); 17206185db85Sdougm } 17216185db85Sdougm } 17226185db85Sdougm if (ret == SA_OK) { 1723da6c28aaSamw name = sa_get_share_attr(share, "exclude"); 1724da6c28aaSamw if (name != NULL) { 1725da6c28aaSamw /* 1726da6c28aaSamw * In special cases need to 1727da6c28aaSamw * exclude proto enable. 1728da6c28aaSamw */ 1729da6c28aaSamw ret = sa_set_property(handle, 1730da6c28aaSamw "exclude", name); 1731da6c28aaSamw sa_free_attr_string(name); 1732da6c28aaSamw } 1733da6c28aaSamw } 1734da6c28aaSamw if (ret == SA_OK) { 1735da6c28aaSamw /* 1736da6c28aaSamw * If there are resource names, bundle them up 1737da6c28aaSamw * and save appropriately. 1738da6c28aaSamw */ 1739da6c28aaSamw ret = sa_set_resource_property(handle, share); 1740da6c28aaSamw } 1741da6c28aaSamw 1742da6c28aaSamw if (ret == SA_OK) { 17436185db85Sdougm description = sa_get_share_description(share); 17446185db85Sdougm if (description != NULL) { 174525a68471Sdougm ret = sa_set_property(handle, 174625a68471Sdougm "description", 17476185db85Sdougm description); 17486185db85Sdougm sa_free_share_description(description); 17496185db85Sdougm } 17506185db85Sdougm } 175125a68471Sdougm /* Make sure we cleanup the transaction */ 17526185db85Sdougm if (ret == SA_OK) { 1753*5b6e0c46Sdougm sa_handle_impl_t sahandle; 1754*5b6e0c46Sdougm sahandle = (sa_handle_impl_t) 1755*5b6e0c46Sdougm sa_find_group_handle(group); 1756*5b6e0c46Sdougm if (sahandle != NULL) 1757*5b6e0c46Sdougm ret = sa_end_transaction(handle, 1758*5b6e0c46Sdougm sahandle); 1759*5b6e0c46Sdougm else 1760*5b6e0c46Sdougm ret = SA_SYSTEM_ERR; 17616185db85Sdougm } else { 17626185db85Sdougm sa_abort_transaction(handle); 17636185db85Sdougm } 1764f345c0beSdougm 1765f345c0beSdougm (void) sigprocmask(SIG_SETMASK, &old, NULL); 1766f345c0beSdougm 17676185db85Sdougm free(sharename); 17686185db85Sdougm } 17696185db85Sdougm } 17706185db85Sdougm if (ret == SA_SYSTEM_ERR) { 17716185db85Sdougm int err = scf_error(); 17726185db85Sdougm if (err == SCF_ERROR_PERMISSION_DENIED) 17736185db85Sdougm ret = SA_NO_PERMISSION; 17746185db85Sdougm } 17756185db85Sdougm if (propstring != NULL) 17766185db85Sdougm free(propstring); 17776185db85Sdougm if (groupname != NULL) 17786185db85Sdougm sa_free_attr_string(groupname); 17796185db85Sdougm 17806185db85Sdougm return (ret); 17816185db85Sdougm } 17826185db85Sdougm 17836185db85Sdougm /* 1784da6c28aaSamw * remove_resources(handle, share, shareid) 1785da6c28aaSamw * 1786da6c28aaSamw * If the share has resources, remove all of them and their 1787da6c28aaSamw * optionsets. 1788da6c28aaSamw */ 1789da6c28aaSamw static int 1790da6c28aaSamw remove_resources(scfutilhandle_t *handle, sa_share_t share, char *shareid) 1791da6c28aaSamw { 1792da6c28aaSamw sa_resource_t resource; 1793da6c28aaSamw sa_optionset_t opt; 1794da6c28aaSamw char *proto; 1795da6c28aaSamw char *id; 1796da6c28aaSamw ssize_t proplen; 1797da6c28aaSamw char *propstring; 1798da6c28aaSamw int ret = SA_OK; 1799da6c28aaSamw 1800da6c28aaSamw proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 1801da6c28aaSamw propstring = malloc(proplen); 1802da6c28aaSamw if (propstring == NULL) 1803da6c28aaSamw return (SA_NO_MEMORY); 1804da6c28aaSamw 1805da6c28aaSamw for (resource = sa_get_share_resource(share, NULL); 1806da6c28aaSamw resource != NULL; resource = sa_get_next_resource(resource)) { 1807da6c28aaSamw id = sa_get_resource_attr(resource, "id"); 1808da6c28aaSamw if (id == NULL) 1809da6c28aaSamw continue; 1810da6c28aaSamw for (opt = sa_get_optionset(resource, NULL); 1811da6c28aaSamw opt != NULL; opt = sa_get_next_optionset(resource)) { 1812da6c28aaSamw proto = sa_get_optionset_attr(opt, "type"); 1813da6c28aaSamw if (proto != NULL) { 1814da6c28aaSamw (void) snprintf(propstring, proplen, 1815da6c28aaSamw "%s_%s_%s", shareid, proto, id); 1816da6c28aaSamw ret = sa_delete_pgroup(handle, propstring); 1817da6c28aaSamw sa_free_attr_string(proto); 1818da6c28aaSamw } 1819da6c28aaSamw } 1820da6c28aaSamw sa_free_attr_string(id); 1821da6c28aaSamw } 1822da6c28aaSamw free(propstring); 1823da6c28aaSamw return (ret); 1824da6c28aaSamw } 1825da6c28aaSamw 1826da6c28aaSamw /* 18276185db85Sdougm * sa_delete_share(handle, group, share) 18286185db85Sdougm * 182925a68471Sdougm * Remove the specified share from the group (and service instance). 18306185db85Sdougm */ 18316185db85Sdougm 18326185db85Sdougm int 18336185db85Sdougm sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share) 18346185db85Sdougm { 18356185db85Sdougm int ret = SA_OK; 18366185db85Sdougm char *groupname = NULL; 18376185db85Sdougm char *shareid = NULL; 18386185db85Sdougm sa_optionset_t opt; 18396185db85Sdougm sa_security_t sec; 18406185db85Sdougm ssize_t proplen; 18416185db85Sdougm char *propstring; 18426185db85Sdougm 18436185db85Sdougm proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 18446185db85Sdougm propstring = malloc(proplen); 18456185db85Sdougm if (propstring == NULL) 18466185db85Sdougm ret = SA_NO_MEMORY; 18476185db85Sdougm 18486185db85Sdougm if (ret == SA_OK) { 18496185db85Sdougm groupname = sa_get_group_attr(group, "name"); 18506185db85Sdougm shareid = sa_get_share_attr(share, "id"); 185125a68471Sdougm if (groupname == NULL || shareid == NULL) { 185225a68471Sdougm ret = SA_CONFIG_ERR; 185325a68471Sdougm goto out; 185425a68471Sdougm } 18556185db85Sdougm ret = sa_get_instance(handle, groupname); 18566185db85Sdougm if (ret == SA_OK) { 1857da6c28aaSamw /* If a share has resources, remove them */ 1858da6c28aaSamw ret = remove_resources(handle, share, shareid); 185925a68471Sdougm /* If a share has properties, remove them */ 18606185db85Sdougm ret = sa_delete_pgroup(handle, shareid); 186125a68471Sdougm for (opt = sa_get_optionset(share, NULL); 186225a68471Sdougm opt != NULL; 18636185db85Sdougm opt = sa_get_next_optionset(opt)) { 18646185db85Sdougm char *proto; 18656185db85Sdougm proto = sa_get_optionset_attr(opt, "type"); 18666185db85Sdougm if (proto != NULL) { 186725a68471Sdougm (void) snprintf(propstring, 186825a68471Sdougm proplen, "%s_%s", shareid, 186925a68471Sdougm proto); 187025a68471Sdougm ret = sa_delete_pgroup(handle, 187125a68471Sdougm propstring); 18726185db85Sdougm sa_free_attr_string(proto); 18736185db85Sdougm } else { 18746185db85Sdougm ret = SA_NO_MEMORY; 18756185db85Sdougm } 18766185db85Sdougm } 18776185db85Sdougm /* 187825a68471Sdougm * If a share has security/negotiable 18796185db85Sdougm * properties, remove them. 18806185db85Sdougm */ 188125a68471Sdougm for (sec = sa_get_security(share, NULL, NULL); 188225a68471Sdougm sec != NULL; 18836185db85Sdougm sec = sa_get_next_security(sec)) { 18846185db85Sdougm char *proto; 18856185db85Sdougm char *sectype; 18866185db85Sdougm proto = sa_get_security_attr(sec, "type"); 18876185db85Sdougm sectype = sa_get_security_attr(sec, "sectype"); 18886185db85Sdougm if (proto != NULL && sectype != NULL) { 188925a68471Sdougm (void) snprintf(propstring, proplen, 189025a68471Sdougm "%s_%s_%s", shareid, proto, 189125a68471Sdougm sectype); 189225a68471Sdougm ret = sa_delete_pgroup(handle, 189325a68471Sdougm propstring); 18946185db85Sdougm } else { 18956185db85Sdougm ret = SA_NO_MEMORY; 18966185db85Sdougm } 18976185db85Sdougm if (proto != NULL) 18986185db85Sdougm sa_free_attr_string(proto); 18996185db85Sdougm if (sectype != NULL) 19006185db85Sdougm sa_free_attr_string(sectype); 19016185db85Sdougm } 19026185db85Sdougm } 19036185db85Sdougm } 190425a68471Sdougm out: 19056185db85Sdougm if (groupname != NULL) 19066185db85Sdougm sa_free_attr_string(groupname); 19076185db85Sdougm if (shareid != NULL) 19086185db85Sdougm sa_free_attr_string(shareid); 19096185db85Sdougm if (propstring != NULL) 19106185db85Sdougm free(propstring); 19116185db85Sdougm 19126185db85Sdougm return (ret); 19136185db85Sdougm } 1914