Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /* helper functions for using libscf with sharemgr */
     28 
     29 #include <libscf.h>
     30 #include <libxml/parser.h>
     31 #include <libxml/tree.h>
     32 #include "libshare.h"
     33 #include "libshare_impl.h"
     34 #include "scfutil.h"
     35 #include <string.h>
     36 #include <ctype.h>
     37 #include <errno.h>
     38 #include <uuid/uuid.h>
     39 #include <sys/param.h>
     40 #include <signal.h>
     41 #include <sys/time.h>
     42 #include <libintl.h>
     43 
     44 ssize_t scf_max_name_len;
     45 extern struct sa_proto_plugin *sap_proto_list;
     46 extern sa_handle_impl_t get_handle_for_root(xmlNodePtr);
     47 static void set_transaction_tstamp(sa_handle_impl_t);
     48 /*
     49  * The SMF facility uses some properties that must exist. We want to
     50  * skip over these when processing protocol options.
     51  */
     52 static char *skip_props[] = {
     53 	"modify_authorization",
     54 	"action_authorization",
     55 	"value_authorization",
     56 	NULL
     57 };
     58 
     59 /*
     60  * sa_scf_fini(handle)
     61  *
     62  * Must be called when done. Called with the handle allocated in
     63  * sa_scf_init(), it cleans up the state and frees any SCF resources
     64  * still in use. Called by sa_fini().
     65  */
     66 
     67 void
     68 sa_scf_fini(scfutilhandle_t *handle)
     69 {
     70 	if (handle != NULL) {
     71 		int unbind = 0;
     72 		if (handle->scope != NULL) {
     73 			unbind = 1;
     74 			scf_scope_destroy(handle->scope);
     75 		}
     76 		if (handle->instance != NULL)
     77 			scf_instance_destroy(handle->instance);
     78 		if (handle->service != NULL)
     79 			scf_service_destroy(handle->service);
     80 		if (handle->pg != NULL)
     81 			scf_pg_destroy(handle->pg);
     82 		if (handle->handle != NULL) {
     83 			handle->scf_state = SCH_STATE_UNINIT;
     84 			if (unbind)
     85 				(void) scf_handle_unbind(handle->handle);
     86 			scf_handle_destroy(handle->handle);
     87 		}
     88 		free(handle);
     89 	}
     90 }
     91 
     92 /*
     93  * sa_scf_init()
     94  *
     95  * Must be called before using any of the SCF functions. Called by
     96  * sa_init() during the API setup.
     97  */
     98 
     99 scfutilhandle_t *
    100 sa_scf_init(sa_handle_impl_t ihandle)
    101 {
    102 	scfutilhandle_t *handle;
    103 
    104 	scf_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
    105 	if (scf_max_name_len <= 0)
    106 		scf_max_name_len = SA_MAX_NAME_LEN + 1;
    107 
    108 	handle = calloc(1, sizeof (scfutilhandle_t));
    109 	if (handle == NULL)
    110 		return (handle);
    111 
    112 	ihandle->scfhandle = handle;
    113 	handle->scf_state = SCH_STATE_INITIALIZING;
    114 	handle->handle = scf_handle_create(SCF_VERSION);
    115 	if (handle->handle == NULL) {
    116 		free(handle);
    117 		handle = NULL;
    118 		(void) printf("libshare could not access SMF repository: %s\n",
    119 		    scf_strerror(scf_error()));
    120 		return (handle);
    121 	}
    122 	if (scf_handle_bind(handle->handle) != 0)
    123 		goto err;
    124 
    125 	handle->scope = scf_scope_create(handle->handle);
    126 	handle->service = scf_service_create(handle->handle);
    127 	handle->pg = scf_pg_create(handle->handle);
    128 
    129 	/* Make sure we have sufficient SMF running */
    130 	handle->instance = scf_instance_create(handle->handle);
    131 	if (handle->scope == NULL || handle->service == NULL ||
    132 	    handle->pg == NULL || handle->instance == NULL)
    133 		goto err;
    134 	if (scf_handle_get_scope(handle->handle,
    135 	    SCF_SCOPE_LOCAL, handle->scope) != 0)
    136 		goto err;
    137 	if (scf_scope_get_service(handle->scope,
    138 	    SA_GROUP_SVC_NAME, handle->service) != 0)
    139 		goto err;
    140 
    141 	handle->scf_state = SCH_STATE_INIT;
    142 	if (sa_get_instance(handle, "default") != SA_OK) {
    143 		sa_group_t defgrp;
    144 		defgrp = sa_create_group((sa_handle_t)ihandle, "default", NULL);
    145 		/* Only NFS enabled for "default" group. */
    146 		if (defgrp != NULL)
    147 			(void) sa_create_optionset(defgrp, "nfs");
    148 	}
    149 
    150 	return (handle);
    151 
    152 	/* Error handling/unwinding */
    153 err:
    154 	(void) sa_scf_fini(handle);
    155 	(void) printf("libshare SMF initialization problem: %s\n",
    156 	    scf_strerror(scf_error()));
    157 	return (NULL);
    158 }
    159 
    160 /*
    161  * get_scf_limit(name)
    162  *
    163  * Since we use  scf_limit a lot and do the same  check and return the
    164  * same  value  if  it  fails,   implement  as  a  function  for  code
    165  * simplification.  Basically, if  name isn't found, return MAXPATHLEN
    166  * (1024) so we have a reasonable default buffer size.
    167  */
    168 static ssize_t
    169 get_scf_limit(uint32_t name)
    170 {
    171 	ssize_t vallen;
    172 
    173 	vallen = scf_limit(name);
    174 	if (vallen == (ssize_t)-1)
    175 		vallen = MAXPATHLEN;
    176 	return (vallen);
    177 }
    178 
    179 /*
    180  * skip_property(name)
    181  *
    182  * Internal function to check to see if a property is an SMF magic
    183  * property that needs to be skipped.
    184  */
    185 static int
    186 skip_property(char *name)
    187 {
    188 	int i;
    189 
    190 	for (i = 0; skip_props[i] != NULL; i++)
    191 		if (strcmp(name, skip_props[i]) == 0)
    192 		return (1);
    193 	return (0);
    194 }
    195 
    196 /*
    197  * generate_unique_sharename(sharename)
    198  *
    199  * Shares are represented in SMF as property groups. Due to share
    200  * paths containing characters that are not allowed in SMF names and
    201  * the need to be unique, we use UUIDs to construct a unique name.
    202  */
    203 
    204 static void
    205 generate_unique_sharename(char *sharename)
    206 {
    207 	uuid_t uuid;
    208 
    209 	uuid_generate(uuid);
    210 	(void) strcpy(sharename, "S-");
    211 	uuid_unparse(uuid, sharename + 2);
    212 }
    213 
    214 /*
    215  * valid_protocol(proto)
    216  *
    217  * Check to see if the specified protocol is a valid one for the
    218  * general sharemgr facility. We determine this by checking which
    219  * plugin protocols were found.
    220  */
    221 
    222 static int
    223 valid_protocol(char *proto)
    224 {
    225 	struct sa_proto_plugin *plugin;
    226 	for (plugin = sap_proto_list; plugin != NULL;
    227 	    plugin = plugin->plugin_next)
    228 		if (strcmp(proto, plugin->plugin_ops->sa_protocol) == 0)
    229 			return (1);
    230 	return (0);
    231 }
    232 
    233 /*
    234  * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype)
    235  *
    236  * Extract the name property group and create the specified type of
    237  * node on the provided group.  type will be optionset or security.
    238  */
    239 
    240 static int
    241 sa_extract_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
    242 			scf_propertygroup_t *pg,
    243 			char *nodetype, char *proto, char *sectype)
    244 {
    245 	xmlNodePtr node;
    246 	scf_iter_t *iter;
    247 	scf_property_t *prop;
    248 	scf_value_t *value;
    249 	char *name;
    250 	char *valuestr;
    251 	ssize_t vallen;
    252 	int ret = SA_OK;
    253 
    254 	vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
    255 
    256 	node = xmlNewChild(root, NULL, (xmlChar *)nodetype, NULL);
    257 	if (node == NULL)
    258 		return (ret);
    259 
    260 	if (proto != NULL)
    261 		(void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
    262 	if (sectype != NULL)
    263 		(void) xmlSetProp(node, (xmlChar *)"sectype",
    264 		    (xmlChar *)sectype);
    265 	/*
    266 	 * Have node to work with so iterate over the properties
    267 	 * in the pg and create option sub nodes.
    268 	 */
    269 	iter = scf_iter_create(handle->handle);
    270 	value = scf_value_create(handle->handle);
    271 	prop = scf_property_create(handle->handle);
    272 	name = malloc(scf_max_name_len);
    273 	valuestr = malloc(vallen);
    274 	/*
    275 	 * Want to iterate through the properties and add them
    276 	 * to the base optionset.
    277 	 */
    278 	if (iter == NULL || value == NULL || prop == NULL ||
    279 	    valuestr == NULL || name == NULL) {
    280 		ret = SA_NO_MEMORY;
    281 		goto out;
    282 	}
    283 	if (scf_iter_pg_properties(iter, pg) == 0) {
    284 		/* Now iterate the properties in the group */
    285 		while (scf_iter_next_property(iter, prop) > 0) {
    286 			/* have a property */
    287 			if (scf_property_get_name(prop, name,
    288 			    scf_max_name_len) > 0) {
    289 				sa_property_t saprop;
    290 				/* Some properties are part of the framework */
    291 				if (skip_property(name))
    292 					continue;
    293 				if (scf_property_get_value(prop, value) != 0)
    294 					continue;
    295 				if (scf_value_get_astring(value, valuestr,
    296 				    vallen) < 0)
    297 					continue;
    298 				saprop = sa_create_property(name, valuestr);
    299 				if (saprop != NULL) {
    300 					/*
    301 					 * Since in SMF, don't
    302 					 * recurse. Use xmlAddChild
    303 					 * directly, instead.
    304 					 */
    305 					(void) xmlAddChild(node,
    306 					    (xmlNodePtr) saprop);
    307 				}
    308 			}
    309 		}
    310 	}
    311 out:
    312 	/* cleanup to avoid memory leaks */
    313 	if (value != NULL)
    314 		scf_value_destroy(value);
    315 	if (iter != NULL)
    316 		scf_iter_destroy(iter);
    317 	if (prop != NULL)
    318 		scf_property_destroy(prop);
    319 	if (name != NULL)
    320 		free(name);
    321 	if (valuestr != NULL)
    322 		free(valuestr);
    323 
    324 	return (ret);
    325 }
    326 
    327 /*
    328  * sa_extract_attrs(root, handle, instance)
    329  *
    330  * Local function to extract the actual attributes/properties from the
    331  * property group of the service instance. These are the well known
    332  * attributes of "state" and "zfs". If additional attributes are
    333  * added, they should be added here.
    334  */
    335 
    336 static void
    337 sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle,
    338 		    scf_instance_t *instance)
    339 {
    340 	scf_property_t *prop;
    341 	scf_value_t *value;
    342 	char *valuestr;
    343 	ssize_t vallen;
    344 
    345 	vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
    346 	prop = scf_property_create(handle->handle);
    347 	value = scf_value_create(handle->handle);
    348 	valuestr = malloc(vallen);
    349 	if (prop == NULL || value == NULL || valuestr == NULL ||
    350 	    scf_instance_get_pg(instance, "operation", handle->pg) != 0) {
    351 		goto out;
    352 	}
    353 	/*
    354 	 * Have a property group with desired name so now get
    355 	 * the known attributes.
    356 	 */
    357 	if (scf_pg_get_property(handle->pg, "state", prop) == 0) {
    358 		/* Found the property so get the value */
    359 		if (scf_property_get_value(prop, value) == 0) {
    360 			if (scf_value_get_astring(value, valuestr,
    361 			    vallen) >= 0) {
    362 				(void) xmlSetProp(root, (xmlChar *)"state",
    363 				    (xmlChar *)valuestr);
    364 			}
    365 		}
    366 	}
    367 	if (scf_pg_get_property(handle->pg, "zfs", prop) == 0) {
    368 		/* Found the property so get the value */
    369 		if (scf_property_get_value(prop, value) == 0) {
    370 			if (scf_value_get_astring(value, valuestr,
    371 			    vallen) > 0) {
    372 				(void) xmlSetProp(root, (xmlChar *)"zfs",
    373 				    (xmlChar *)valuestr);
    374 			}
    375 		}
    376 	}
    377 out:
    378 	if (valuestr != NULL)
    379 		free(valuestr);
    380 	if (value != NULL)
    381 		scf_value_destroy(value);
    382 	if (prop != NULL)
    383 		scf_property_destroy(prop);
    384 }
    385 
    386 /*
    387  * List of known share attributes.
    388  */
    389 
    390 static char *share_attr[] = {
    391 	"path",
    392 	"id",
    393 	"drive-letter",
    394 	"exclude",
    395 	NULL,
    396 };
    397 
    398 static int
    399 is_share_attr(char *name)
    400 {
    401 	int i;
    402 	for (i = 0; share_attr[i] != NULL; i++)
    403 		if (strcmp(name, share_attr[i]) == 0)
    404 			return (1);
    405 	return (0);
    406 }
    407 
    408 /*
    409  * _sa_make_resource(node, valuestr)
    410  *
    411  * Make a resource node on the share node. The valusestr will either
    412  * be old format (SMF acceptable string) or new format (pretty much an
    413  * arbitrary string with "nnn:" prefixing in order to persist
    414  * mapping). The input valuestr will get modified in place. This is
    415  * only used in SMF repository parsing. A possible third field will be
    416  * a "description" string.
    417  */
    418 
    419 static void
    420 _sa_make_resource(xmlNodePtr node, char *valuestr)
    421 {
    422 	char *idx;
    423 	char *name;
    424 	char *description = NULL;
    425 
    426 	idx = valuestr;
    427 	name = strchr(valuestr, ':');
    428 	if (name == NULL) {
    429 		/* this is old form so give an index of "0" */
    430 		idx = "0";
    431 		name = valuestr;
    432 	} else {
    433 		/* NUL the ':' and move past it */
    434 		*name++ = '\0';
    435 		/* There could also be a description string */
    436 		description = strchr(name, ':');
    437 		if (description != NULL)
    438 			*description++ = '\0';
    439 	}
    440 	node = xmlNewChild(node, NULL, (xmlChar *)"resource", NULL);
    441 	if (node != NULL) {
    442 		(void) xmlSetProp(node, (xmlChar *)"name", (xmlChar *)name);
    443 		(void) xmlSetProp(node, (xmlChar *)"id", (xmlChar *)idx);
    444 		/* SMF values are always persistent */
    445 		(void) xmlSetProp(node, (xmlChar *)"type",
    446 		    (xmlChar *)"persist");
    447 		if (description != NULL && strlen(description) > 0) {
    448 			(void) xmlNewChild(node, NULL, (xmlChar *)"description",
    449 			    (xmlChar *)description);
    450 		}
    451 	}
    452 }
    453 
    454 
    455 /*
    456  * sa_share_from_pgroup
    457  *
    458  * Extract the share definition from the share property group. We do
    459  * some sanity checking to avoid bad data.
    460  *
    461  * Since this is only constructing the internal data structures, we
    462  * don't use the sa_* functions most of the time.
    463  */
    464 void
    465 sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
    466 			scf_propertygroup_t *pg, char *id)
    467 {
    468 	xmlNodePtr node;
    469 	char *name;
    470 	scf_iter_t *iter;
    471 	scf_property_t *prop;
    472 	scf_value_t *value;
    473 	ssize_t vallen;
    474 	char *valuestr;
    475 	int ret = SA_OK;
    476 	int have_path = 0;
    477 
    478 	/*
    479 	 * While preliminary check (starts with 'S') passed before
    480 	 * getting here. Need to make sure it is in ID syntax
    481 	 * (Snnnnnn). Note that shares with properties have similar
    482 	 * pgroups.
    483 	 */
    484 	vallen = strlen(id);
    485 	if (*id == SA_SHARE_PG_PREFIX[0] && vallen == SA_SHARE_PG_LEN) {
    486 		uuid_t uuid;
    487 		if (strncmp(id, SA_SHARE_PG_PREFIX,
    488 		    SA_SHARE_PG_PREFIXLEN) != 0 ||
    489 		    uuid_parse(id + 2, uuid) < 0)
    490 			return;
    491 	} else {
    492 		return;
    493 	}
    494 
    495 	vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
    496 
    497 	iter = scf_iter_create(handle->handle);
    498 	value = scf_value_create(handle->handle);
    499 	prop = scf_property_create(handle->handle);
    500 	name = malloc(scf_max_name_len);
    501 	valuestr = malloc(vallen);
    502 
    503 	/*
    504 	 * Construct the share XML node. It is similar to sa_add_share
    505 	 * but never changes the repository. Also, there won't be any
    506 	 * ZFS or transient shares.  Root will be the group it is
    507 	 * associated with.
    508 	 */
    509 	node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL);
    510 	if (node != NULL) {
    511 		/*
    512 		 * Make sure the UUID part of the property group is
    513 		 * stored in the share "id" property. We use this
    514 		 * later.
    515 		 */
    516 		(void) xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id);
    517 		(void) xmlSetProp(node, (xmlChar *)"type",
    518 		    (xmlChar *)"persist");
    519 	}
    520 
    521 	if (iter == NULL || value == NULL || prop == NULL || name == NULL)
    522 		goto out;
    523 
    524 	/* Iterate over the share pg properties */
    525 	if (scf_iter_pg_properties(iter, pg) != 0)
    526 		goto out;
    527 
    528 	while (scf_iter_next_property(iter, prop) > 0) {
    529 		ret = SA_SYSTEM_ERR; /* assume the worst */
    530 		if (scf_property_get_name(prop, name, scf_max_name_len) > 0) {
    531 			if (scf_property_get_value(prop, value) == 0) {
    532 				if (scf_value_get_astring(value, valuestr,
    533 				    vallen) >= 0) {
    534 					ret = SA_OK;
    535 				}
    536 			} else if (strcmp(name, "resource") == 0) {
    537 				ret = SA_OK;
    538 			}
    539 		}
    540 		if (ret != SA_OK)
    541 			continue;
    542 		/*
    543 		 * Check that we have the "path" property in
    544 		 * name. The string in name will always be nul
    545 		 * terminated if scf_property_get_name()
    546 		 * succeeded.
    547 		 */
    548 		if (strcmp(name, "path") == 0)
    549 			have_path = 1;
    550 		if (is_share_attr(name)) {
    551 			/*
    552 			 * If a share attr, then simple -
    553 			 * usually path and id name
    554 			 */
    555 			(void) xmlSetProp(node, (xmlChar *)name,
    556 			    (xmlChar *)valuestr);
    557 		} else if (strcmp(name, "resource") == 0) {
    558 			/*
    559 			 * Resource names handled differently since
    560 			 * there can be multiple on each share. The
    561 			 * "resource" id must be preserved since this
    562 			 * will be used by some protocols in mapping
    563 			 * "property spaces" to names and is always
    564 			 * used to create SMF property groups specific
    565 			 * to resources.  CIFS needs this.  The first
    566 			 * value is present so add and then loop for
    567 			 * any additional. Since this is new and
    568 			 * previous values may exist, handle
    569 			 * conversions.
    570 			 */
    571 			scf_iter_t *viter;
    572 			viter = scf_iter_create(handle->handle);
    573 			if (viter != NULL &&
    574 			    scf_iter_property_values(viter, prop) == 0) {
    575 				while (scf_iter_next_value(viter, value) > 0) {
    576 					/* Have a value so process it */
    577 					if (scf_value_get_ustring(value,
    578 					    valuestr, vallen) >= 0) {
    579 						/* have a ustring */
    580 						_sa_make_resource(node,
    581 						    valuestr);
    582 					} else if (scf_value_get_astring(value,
    583 					    valuestr, vallen) >= 0) {
    584 						/* have an astring */
    585 						_sa_make_resource(node,
    586 						    valuestr);
    587 					}
    588 				}
    589 				scf_iter_destroy(viter);
    590 			}
    591 		} else {
    592 			if (strcmp(name, "description") == 0) {
    593 				/* We have a description node */
    594 				xmlNodePtr desc;
    595 				desc = xmlNewChild(node, NULL,
    596 				    (xmlChar *)"description", NULL);
    597 				if (desc != NULL)
    598 					xmlNodeSetContent(desc,
    599 					    (xmlChar *)valuestr);
    600 			}
    601 		}
    602 	}
    603 out:
    604 	/*
    605 	 * A share without a path is broken so we want to not include
    606 	 * these.  They shouldn't happen but if you kill a sharemgr in
    607 	 * the process of creating a share, it could happen.  They
    608 	 * should be harmless.  It is also possible that another
    609 	 * sharemgr is running and in the process of creating a share.
    610 	 */
    611 	if (have_path == 0 && node != NULL) {
    612 		xmlUnlinkNode(node);
    613 		xmlFreeNode(node);
    614 	}
    615 	if (name != NULL)
    616 		free(name);
    617 	if (valuestr != NULL)
    618 		free(valuestr);
    619 	if (value != NULL)
    620 		scf_value_destroy(value);
    621 	if (iter != NULL)
    622 		scf_iter_destroy(iter);
    623 	if (prop != NULL)
    624 		scf_property_destroy(prop);
    625 }
    626 
    627 /*
    628  * find_share_by_id(shareid)
    629  *
    630  * Search all shares in all groups until we find the share represented
    631  * by "id".
    632  */
    633 
    634 static sa_share_t
    635 find_share_by_id(sa_handle_t handle, char *shareid)
    636 {
    637 	sa_group_t group;
    638 	sa_share_t share = NULL;
    639 	char *id = NULL;
    640 	int done = 0;
    641 
    642 	for (group = sa_get_group(handle, NULL);
    643 	    group != NULL && !done;
    644 	    group = sa_get_next_group(group)) {
    645 		for (share = sa_get_share(group, NULL);
    646 		    share != NULL;
    647 		    share = sa_get_next_share(share)) {
    648 			id = sa_get_share_attr(share, "id");
    649 			if (id != NULL && strcmp(id, shareid) == 0) {
    650 				sa_free_attr_string(id);
    651 				id = NULL;
    652 				done++;
    653 				break;
    654 			}
    655 			if (id != NULL) {
    656 				sa_free_attr_string(id);
    657 				id = NULL;
    658 			}
    659 		}
    660 	}
    661 	return (share);
    662 }
    663 
    664 /*
    665  * find_resource_by_index(share, index)
    666  *
    667  * Search the resource records on the share for the id index.
    668  */
    669 static sa_resource_t
    670 find_resource_by_index(sa_share_t share, char *index)
    671 {
    672 	sa_resource_t resource;
    673 	sa_resource_t found = NULL;
    674 	char *id;
    675 
    676 	for (resource = sa_get_share_resource(share, NULL);
    677 	    resource != NULL && found == NULL;
    678 	    resource = sa_get_next_resource(resource)) {
    679 		id = (char *)xmlGetProp((xmlNodePtr)resource, (xmlChar *)"id");
    680 		if (id != NULL) {
    681 			if (strcmp(id, index) == 0) {
    682 				/* found it so save in "found" */
    683 				found = resource;
    684 			}
    685 			sa_free_attr_string(id);
    686 		}
    687 	}
    688 	return (found);
    689 }
    690 
    691 /*
    692  * sa_share_props_from_pgroup(root, handle, pg, id, sahandle)
    693  *
    694  * Extract share properties from the SMF property group. More sanity
    695  * checks are done and the share object is created. We ignore some
    696  * errors that could exist in the repository and only worry about
    697  * property groups that validate in naming.
    698  */
    699 
    700 static int
    701 sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
    702 			scf_propertygroup_t *pg, char *id, sa_handle_t sahandle)
    703 {
    704 	xmlNodePtr node;
    705 	char *name = NULL;
    706 	scf_iter_t *iter = NULL;
    707 	scf_property_t *prop = NULL;
    708 	scf_value_t *value = NULL;
    709 	ssize_t vallen;
    710 	char *valuestr = NULL;
    711 	int ret = SA_OK;
    712 	char *sectype = NULL;
    713 	char *proto;
    714 	sa_share_t share;
    715 	uuid_t uuid;
    716 
    717 	/*
    718 	 * While preliminary check (starts with 'S') passed before
    719 	 * getting here. Need to make sure it is in ID syntax
    720 	 * (Snnnnnn). Note that shares with properties have similar
    721 	 * pgroups. If the pg name is more than SA_SHARE_PG_LEN
    722 	 * characters, it is likely one of the protocol/security
    723 	 * versions.
    724 	 */
    725 	vallen = strlen(id);
    726 	if (*id != SA_SHARE_PG_PREFIX[0] || vallen <= SA_SHARE_PG_LEN) {
    727 		/*
    728 		 * It is ok to not have what we thought since someone might
    729 		 * have added a name via SMF.
    730 		 */
    731 		return (ret);
    732 	}
    733 	if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) == 0) {
    734 		proto = strchr(id, '_');
    735 		if (proto == NULL)
    736 			return (ret);
    737 		*proto++ = '\0';
    738 		if (uuid_parse(id + SA_SHARE_PG_PREFIXLEN, uuid) < 0)
    739 			return (ret);
    740 		/*
    741 		 * probably a legal optionset so check a few more
    742 		 * syntax points below.
    743 		 */
    744 		if (*proto == '\0') {
    745 			/* not a valid proto (null) */
    746 			return (ret);
    747 		}
    748 
    749 		sectype = strchr(proto, '_');
    750 		if (sectype != NULL)
    751 			*sectype++ = '\0';
    752 		if (!valid_protocol(proto))
    753 			return (ret);
    754 	}
    755 
    756 	/*
    757 	 * To get here, we have a valid protocol and possibly a
    758 	 * security. We now have to find the share that it is really
    759 	 * associated with. The "id" portion of the pgroup name will
    760 	 * match.
    761 	 */
    762 
    763 	share = find_share_by_id(sahandle, id);
    764 	if (share == NULL)
    765 		return (SA_BAD_PATH);
    766 
    767 	root = (xmlNodePtr)share;
    768 
    769 	vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
    770 
    771 	if (sectype == NULL)
    772 		node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL);
    773 	else {
    774 		if (isdigit((int)*sectype)) {
    775 			sa_resource_t resource;
    776 			/*
    777 			 * If sectype[0] is a digit, then it is an index into
    778 			 * the resource names. We need to find a resource
    779 			 * record and then get the properties into an
    780 			 * optionset. The optionset becomes the "node" and the
    781 			 * rest is hung off of the share.
    782 			 */
    783 			resource = find_resource_by_index(share, sectype);
    784 			if (resource != NULL) {
    785 				node = xmlNewChild(resource, NULL,
    786 				    (xmlChar *)"optionset", NULL);
    787 			} else {
    788 				/* This shouldn't happen. */
    789 				ret = SA_SYSTEM_ERR;
    790 				goto out;
    791 			}
    792 		} else {
    793 			/*
    794 			 * If not a digit, then it is a security type
    795 			 * (alternate option space). Security types start with
    796 			 * an alphabetic.
    797 			 */
    798 			node = xmlNewChild(root, NULL, (xmlChar *)"security",
    799 			    NULL);
    800 			if (node != NULL)
    801 				(void) xmlSetProp(node, (xmlChar *)"sectype",
    802 				    (xmlChar *)sectype);
    803 		}
    804 	}
    805 	if (node == NULL) {
    806 		ret = SA_NO_MEMORY;
    807 		goto out;
    808 	}
    809 
    810 	(void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
    811 	/* now find the properties */
    812 	iter = scf_iter_create(handle->handle);
    813 	value = scf_value_create(handle->handle);
    814 	prop = scf_property_create(handle->handle);
    815 	name = malloc(scf_max_name_len);
    816 	valuestr = malloc(vallen);
    817 
    818 	if (iter == NULL || value == NULL || prop == NULL || name == NULL)
    819 		goto out;
    820 
    821 	/* iterate over the share pg properties */
    822 	if (scf_iter_pg_properties(iter, pg) == 0) {
    823 		while (scf_iter_next_property(iter, prop) > 0) {
    824 			ret = SA_SYSTEM_ERR; /* assume the worst */
    825 			if (scf_property_get_name(prop, name,
    826 			    scf_max_name_len) > 0) {
    827 				if (scf_property_get_value(prop, value) == 0) {
    828 					if (scf_value_get_astring(value,
    829 					    valuestr, vallen) >= 0) {
    830 						ret = SA_OK;
    831 					}
    832 				}
    833 			} else {
    834 				ret = SA_SYSTEM_ERR;
    835 			}
    836 			if (ret == SA_OK) {
    837 				sa_property_t prop;
    838 				prop = sa_create_property(name, valuestr);
    839 				if (prop != NULL)
    840 					prop = (sa_property_t)xmlAddChild(node,
    841 					    (xmlNodePtr)prop);
    842 				else
    843 					ret = SA_NO_MEMORY;
    844 			}
    845 		}
    846 	} else {
    847 		ret = SA_SYSTEM_ERR;
    848 	}
    849 out:
    850 	if (iter != NULL)
    851 		scf_iter_destroy(iter);
    852 	if (value != NULL)
    853 		scf_value_destroy(value);
    854 	if (prop != NULL)
    855 		scf_property_destroy(prop);
    856 	if (name != NULL)
    857 		free(name);
    858 	if (valuestr != NULL)
    859 		free(valuestr);
    860 	return (ret);
    861 }
    862 
    863 /*
    864  * sa_extract_group(root, handle, instance)
    865  *
    866  * Get the config info for this instance of a group and create the XML
    867  * subtree from it.
    868  */
    869 
    870 static int
    871 sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle,
    872     scf_instance_t *instance, sa_handle_t sahandle)
    873 {
    874 	char *buff;
    875 	xmlNodePtr node;
    876 	scf_iter_t *iter;
    877 	char *proto;
    878 	char *sectype;
    879 	boolean_t have_shares = B_FALSE;
    880 	boolean_t is_default = B_FALSE;
    881 	boolean_t is_nfs = B_FALSE;
    882 	int ret = SA_OK;
    883 	int err;
    884 
    885 	buff = malloc(scf_max_name_len);
    886 	if (buff == NULL)
    887 		return (SA_NO_MEMORY);
    888 
    889 	iter = scf_iter_create(handle->handle);
    890 	if (iter == NULL) {
    891 		ret = SA_NO_MEMORY;
    892 		goto out;
    893 	}
    894 
    895 	if (scf_instance_get_name(instance, buff, scf_max_name_len) > 0) {
    896 		node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL);
    897 		if (node == NULL) {
    898 			ret = SA_NO_MEMORY;
    899 			goto out;
    900 		}
    901 		(void) xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff);
    902 		if (strcmp(buff, "default") == 0)
    903 			is_default = B_TRUE;
    904 
    905 		sa_extract_attrs(node, handle, instance);
    906 		/*
    907 		 * Iterate through all the property groups
    908 		 * looking for those with security or
    909 		 * optionset prefixes. The names of the
    910 		 * matching pgroups are parsed to get the
    911 		 * protocol, and for security, the sectype.
    912 		 * Syntax is as follows:
    913 		 *    optionset | optionset_<proto>
    914 		 *    security_default | security_<proto>_<sectype>
    915 		 * "operation" is handled by
    916 		 * sa_extract_attrs().
    917 		 */
    918 		if (scf_iter_instance_pgs(iter, instance) != 0) {
    919 			ret = SA_NO_MEMORY;
    920 			goto out;
    921 		}
    922 		while (scf_iter_next_pg(iter, handle->pg) > 0) {
    923 			/* Have a pgroup so sort it out */
    924 			ret = scf_pg_get_name(handle->pg, buff,
    925 			    scf_max_name_len);
    926 			if (ret <= 0)
    927 				continue;
    928 			is_nfs = B_FALSE;
    929 
    930 			if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
    931 				sa_share_from_pgroup(node, handle,
    932 				    handle->pg, buff);
    933 				have_shares = B_TRUE;
    934 			} else if (strncmp(buff, "optionset", 9) == 0) {
    935 				char *nodetype = "optionset";
    936 				/* Have an optionset */
    937 				sectype = NULL;
    938 				proto = strchr(buff, '_');
    939 				if (proto != NULL) {
    940 					*proto++ = '\0';
    941 					sectype = strchr(proto, '_');
    942 					if (sectype != NULL) {
    943 						*sectype++ = '\0';
    944 						nodetype = "security";
    945 					}
    946 					is_nfs = strcmp(proto, "nfs") == 0;
    947 				} else if (strlen(buff) > 9) {
    948 					/*
    949 					 * This can only occur if
    950 					 * someone has made changes
    951 					 * via an SMF command. Since
    952 					 * this would be an unknown
    953 					 * syntax, we just ignore it.
    954 					 */
    955 					continue;
    956 				}
    957 				/*
    958 				 * If the group is not "default" or is
    959 				 * "default" and is_nfs, then extract the
    960 				 * pgroup.  If it is_default and !is_nfs,
    961 				 * then we have an error and should remove
    962 				 * the extraneous protocols.  We don't care
    963 				 * about errors on scf_pg_delete since we
    964 				 * might not have permission during an
    965 				 * extract only.
    966 				 */
    967 				if (!is_default || is_nfs) {
    968 					ret = sa_extract_pgroup(node, handle,
    969 					    handle->pg, nodetype, proto,
    970 					    sectype);
    971 				} else {
    972 					err = scf_pg_delete(handle->pg);
    973 					if (err == 0)
    974 						(void) fprintf(stderr,
    975 						    dgettext(TEXT_DOMAIN,
    976 						    "Removed protocol \"%s\" "
    977 						    "from group \"default\"\n"),
    978 						    proto);
    979 				}
    980 			} else if (strncmp(buff, "security", 8) == 0) {
    981 				/*
    982 				 * Have a security (note that
    983 				 * this should change in the
    984 				 * future)
    985 				 */
    986 				proto = strchr(buff, '_');
    987 				sectype = NULL;
    988 				if (proto != NULL) {
    989 					*proto++ = '\0';
    990 					sectype = strchr(proto, '_');
    991 					if (sectype != NULL)
    992 						*sectype++ = '\0';
    993 					if (strcmp(proto, "default") == 0)
    994 						proto = NULL;
    995 				}
    996 				ret = sa_extract_pgroup(node, handle,
    997 				    handle->pg, "security", proto, sectype);
    998 			}
    999 			/* Ignore everything else */
   1000 		}
   1001 		/*
   1002 		 * Make sure we have a valid default group.
   1003 		 * On first boot, default won't have any
   1004 		 * protocols defined and won't be enabled (but
   1005 		 * should be).  "default" only has NFS enabled on it.
   1006 		 */
   1007 		if (is_default) {
   1008 			char *state = sa_get_group_attr((sa_group_t)node,
   1009 			    "state");
   1010 
   1011 			if (state == NULL) {
   1012 				/* set attribute to enabled */
   1013 				(void) sa_set_group_attr((sa_group_t)node,
   1014 				    "state", "enabled");
   1015 				(void) sa_create_optionset((sa_group_t)node,
   1016 				    "nfs");
   1017 			} else {
   1018 				sa_free_attr_string(state);
   1019 			}
   1020 		}
   1021 		/* Do a second pass if shares were found */
   1022 		if (have_shares && scf_iter_instance_pgs(iter, instance) == 0) {
   1023 			while (scf_iter_next_pg(iter, handle->pg) > 0) {
   1024 				/*
   1025 				 * Have a pgroup so see if it is a
   1026 				 * share optionset
   1027 				 */
   1028 				err = scf_pg_get_name(handle->pg, buff,
   1029 				    scf_max_name_len);
   1030 				if (err  <= 0)
   1031 					continue;
   1032 				if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
   1033 					ret = sa_share_props_from_pgroup(node,
   1034 					    handle, handle->pg, buff,
   1035 					    sahandle);
   1036 				}
   1037 			}
   1038 		}
   1039 	}
   1040 out:
   1041 	if (iter != NULL)
   1042 		scf_iter_destroy(iter);
   1043 	if (buff != NULL)
   1044 		free(buff);
   1045 	return (ret);
   1046 }
   1047 
   1048 /*
   1049  * sa_extract_defaults(root, handle, instance)
   1050  *
   1051  * Local function to find the default properties that live in the
   1052  * default instance's "operation" property group.
   1053  */
   1054 
   1055 static void
   1056 sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle,
   1057 		    scf_instance_t *instance)
   1058 {
   1059 	xmlNodePtr node;
   1060 	scf_property_t *prop;
   1061 	scf_value_t *value;
   1062 	char *valuestr;
   1063 	ssize_t vallen;
   1064 
   1065 	vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
   1066 	prop = scf_property_create(handle->handle);
   1067 	value = scf_value_create(handle->handle);
   1068 	valuestr = malloc(vallen);
   1069 
   1070 	if (prop == NULL || value == NULL || vallen == 0 ||
   1071 	    scf_instance_get_pg(instance, "operation", handle->pg) != 0)
   1072 		goto out;
   1073 
   1074 	if (scf_pg_get_property(handle->pg, "legacy-timestamp", prop) != 0)
   1075 		goto out;
   1076 
   1077 	/* Found the property so get the value */
   1078 	if (scf_property_get_value(prop, value) == 0) {
   1079 		if (scf_value_get_astring(value, valuestr, vallen) > 0) {
   1080 			node = xmlNewChild(root, NULL, (xmlChar *)"legacy",
   1081 			    NULL);
   1082 			if (node != NULL) {
   1083 				(void) xmlSetProp(node, (xmlChar *)"timestamp",
   1084 				    (xmlChar *)valuestr);
   1085 				(void) xmlSetProp(node, (xmlChar *)"path",
   1086 				    (xmlChar *)SA_LEGACY_DFSTAB);
   1087 			}
   1088 		}
   1089 	}
   1090 out:
   1091 	if (valuestr != NULL)
   1092 		free(valuestr);
   1093 	if (value != NULL)
   1094 		scf_value_destroy(value);
   1095 	if (prop != NULL)
   1096 		scf_property_destroy(prop);
   1097 }
   1098 
   1099 
   1100 /*
   1101  * sa_get_config(handle, root, doc, sahandle)
   1102  *
   1103  * Walk the SMF repository for /network/shares/group and find all the
   1104  * instances. These become group names.  Then add the XML structure
   1105  * below the groups based on property groups and properties.
   1106  */
   1107 int
   1108 sa_get_config(scfutilhandle_t *handle, xmlNodePtr root, sa_handle_t sahandle)
   1109 {
   1110 	int ret = SA_OK;
   1111 	scf_instance_t *instance;
   1112 	scf_iter_t *iter;
   1113 	char buff[BUFSIZ * 2];
   1114 
   1115 	instance = scf_instance_create(handle->handle);
   1116 	iter = scf_iter_create(handle->handle);
   1117 	if (instance != NULL && iter != NULL) {
   1118 		if ((ret = scf_iter_service_instances(iter,
   1119 		    handle->service)) == 0) {
   1120 			while ((ret = scf_iter_next_instance(iter,
   1121 			    instance)) > 0) {
   1122 				if (scf_instance_get_name(instance, buff,
   1123 				    sizeof (buff)) > 0) {
   1124 					if (strcmp(buff, "default") == 0)
   1125 						sa_extract_defaults(root,
   1126 						    handle, instance);
   1127 					ret = sa_extract_group(root, handle,
   1128 					    instance, sahandle);
   1129 				}
   1130 			}
   1131 		}
   1132 	}
   1133 
   1134 	/* Always cleanup these */
   1135 	if (instance != NULL)
   1136 		scf_instance_destroy(instance);
   1137 	if (iter != NULL)
   1138 		scf_iter_destroy(iter);
   1139 	return (ret);
   1140 }
   1141 
   1142 /*
   1143  * sa_get_instance(handle, instance)
   1144  *
   1145  * Get the instance of the group service. This is actually the
   1146  * specific group name. The instance is needed for all property and
   1147  * control operations.
   1148  */
   1149 
   1150 int
   1151 sa_get_instance(scfutilhandle_t *handle, char *instname)
   1152 {
   1153 	if (scf_service_get_instance(handle->service, instname,
   1154 	    handle->instance) != 0) {
   1155 		return (SA_NO_SUCH_GROUP);
   1156 	}
   1157 	return (SA_OK);
   1158 }
   1159 
   1160 /*
   1161  * sa_create_instance(handle, instname)
   1162  *
   1163  * Create a new SMF service instance. There can only be one with a
   1164  * given name.
   1165  */
   1166 
   1167 int
   1168 sa_create_instance(scfutilhandle_t *handle, char *instname)
   1169 {
   1170 	int ret = SA_OK;
   1171 	char instance[SA_GROUP_INST_LEN];
   1172 	if (scf_service_add_instance(handle->service, instname,
   1173 	    handle->instance) != 0) {
   1174 	/* better error returns need to be added based on real error */
   1175 		if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
   1176 			ret = SA_NO_PERMISSION;
   1177 		else
   1178 			ret = SA_DUPLICATE_NAME;
   1179 	} else {
   1180 		/* have the service created, so enable it */
   1181 		(void) snprintf(instance, sizeof (instance), "%s:%s",
   1182 		    SA_SVC_FMRI_BASE, instname);
   1183 		(void) smf_enable_instance(instance, 0);
   1184 	}
   1185 	return (ret);
   1186 }
   1187 
   1188 /*
   1189  * sa_delete_instance(handle, instname)
   1190  *
   1191  * When a group goes away, we also remove the service instance.
   1192  */
   1193 
   1194 int
   1195 sa_delete_instance(scfutilhandle_t *handle, char *instname)
   1196 {
   1197 	int ret;
   1198 
   1199 	if (strcmp(instname, "default") == 0) {
   1200 		ret = SA_NO_PERMISSION;
   1201 	} else {
   1202 		if ((ret = sa_get_instance(handle, instname)) == SA_OK) {
   1203 			if (scf_instance_delete(handle->instance) != 0)
   1204 				/* need better analysis */
   1205 				ret = SA_NO_PERMISSION;
   1206 		}
   1207 	}
   1208 	return (ret);
   1209 }
   1210 
   1211 /*
   1212  * sa_create_pgroup(handle, pgroup)
   1213  *
   1214  * create a new property group
   1215  */
   1216 
   1217 int
   1218 sa_create_pgroup(scfutilhandle_t *handle, char *pgroup)
   1219 {
   1220 	int ret = SA_OK;
   1221 	int persist = 0;
   1222 
   1223 	/*
   1224 	 * Only create a handle if it doesn't exist. It is ok to exist
   1225 	 * since the pg handle will be set as a side effect.
   1226 	 */
   1227 	if (handle->pg == NULL)
   1228 		handle->pg = scf_pg_create(handle->handle);
   1229 
   1230 	/*
   1231 	 * Special case for a non-persistent property group. This is
   1232 	 * internal use only.
   1233 	 */
   1234 	if (*pgroup == '*') {
   1235 		persist = SCF_PG_FLAG_NONPERSISTENT;
   1236 		pgroup++;
   1237 	}
   1238 
   1239 	/*
   1240 	 * If the pgroup exists, we are done. If it doesn't, then we
   1241 	 * need to actually add one to the service instance.
   1242 	 */
   1243 	if (scf_instance_get_pg(handle->instance,
   1244 	    pgroup, handle->pg) != 0) {
   1245 
   1246 		/* Doesn't exist so create one */
   1247 		if (scf_instance_add_pg(handle->instance, pgroup,
   1248 		    SCF_GROUP_APPLICATION, persist, handle->pg) != 0) {
   1249 			switch (scf_error()) {
   1250 			case SCF_ERROR_PERMISSION_DENIED:
   1251 				ret = SA_NO_PERMISSION;
   1252 				break;
   1253 			default:
   1254 				ret = SA_SYSTEM_ERR;
   1255 				break;
   1256 			}
   1257 		}
   1258 	}
   1259 	return (ret);
   1260 }
   1261 
   1262 /*
   1263  * sa_delete_pgroup(handle, pgroup)
   1264  *
   1265  * Remove the property group from the current instance of the service,
   1266  * but only if it actually exists.
   1267  */
   1268 
   1269 int
   1270 sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup)
   1271 {
   1272 	int ret = SA_OK;
   1273 	/*
   1274 	 * Only delete if it does exist.
   1275 	 */
   1276 	if (scf_instance_get_pg(handle->instance, pgroup, handle->pg) == 0) {
   1277 		/* does exist so delete it */
   1278 		if (scf_pg_delete(handle->pg) != 0)
   1279 			ret = SA_SYSTEM_ERR;
   1280 	} else {
   1281 		ret = SA_SYSTEM_ERR;
   1282 	}
   1283 	if (ret == SA_SYSTEM_ERR &&
   1284 	    scf_error() == SCF_ERROR_PERMISSION_DENIED) {
   1285 		ret = SA_NO_PERMISSION;
   1286 	}
   1287 	return (ret);
   1288 }
   1289 
   1290 /*
   1291  * sa_start_transaction(handle, pgroup)
   1292  *
   1293  * Start an SMF transaction so we can deal with properties. it would
   1294  * be nice to not have to expose this, but we have to in order to
   1295  * optimize.
   1296  *
   1297  * Basic model is to hold the transaction in the handle and allow
   1298  * property adds/deletes/updates to be added then close the
   1299  * transaction (or abort).  There may eventually be a need to handle
   1300  * other types of transaction mechanisms but we don't do that now.
   1301  *
   1302  * An sa_start_transaction must be followed by either an
   1303  * sa_end_transaction or sa_abort_transaction before another
   1304  * sa_start_transaction can be done.
   1305  */
   1306 
   1307 int
   1308 sa_start_transaction(scfutilhandle_t *handle, char *propgroup)
   1309 {
   1310 	int ret = SA_OK;
   1311 	/*
   1312 	 * Lookup the property group and create it if it doesn't already
   1313 	 * exist.
   1314 	 */
   1315 	if (handle == NULL)
   1316 		return (SA_CONFIG_ERR);
   1317 
   1318 	if (handle->scf_state == SCH_STATE_INIT) {
   1319 		ret = sa_create_pgroup(handle, propgroup);
   1320 		if (ret == SA_OK) {
   1321 			handle->trans = scf_transaction_create(handle->handle);
   1322 			if (handle->trans != NULL) {
   1323 				if (scf_transaction_start(handle->trans,
   1324 				    handle->pg) != 0) {
   1325 					ret = SA_SYSTEM_ERR;
   1326 				}
   1327 				if (ret != SA_OK) {
   1328 					scf_transaction_destroy(handle->trans);
   1329 					handle->trans = NULL;
   1330 				}
   1331 			} else {
   1332 				ret = SA_SYSTEM_ERR;
   1333 			}
   1334 		}
   1335 	}
   1336 	if (ret == SA_SYSTEM_ERR &&
   1337 	    scf_error() == SCF_ERROR_PERMISSION_DENIED) {
   1338 		ret = SA_NO_PERMISSION;
   1339 	}
   1340 	return (ret);
   1341 }
   1342 
   1343 
   1344 /*
   1345  * sa_end_transaction(scfhandle, sahandle)
   1346  *
   1347  * Commit the changes that were added to the transaction in the
   1348  * handle. Do all necessary cleanup.
   1349  */
   1350 
   1351 int
   1352 sa_end_transaction(scfutilhandle_t *handle, sa_handle_impl_t sahandle)
   1353 {
   1354 	int ret = SA_OK;
   1355 
   1356 	if (handle == NULL || handle->trans == NULL || sahandle == NULL) {
   1357 		ret = SA_SYSTEM_ERR;
   1358 	} else {
   1359 		if (scf_transaction_commit(handle->trans) < 0)
   1360 			ret = SA_SYSTEM_ERR;
   1361 		scf_transaction_destroy_children(handle->trans);
   1362 		scf_transaction_destroy(handle->trans);
   1363 		if (ret == SA_OK)
   1364 			set_transaction_tstamp(sahandle);
   1365 		handle->trans = NULL;
   1366 	}
   1367 	return (ret);
   1368 }
   1369 
   1370 /*
   1371  * sa_abort_transaction(handle)
   1372  *
   1373  * Abort the changes that were added to the transaction in the
   1374  * handle. Do all necessary cleanup.
   1375  */
   1376 
   1377 void
   1378 sa_abort_transaction(scfutilhandle_t *handle)
   1379 {
   1380 	if (handle->trans != NULL) {
   1381 		scf_transaction_reset_all(handle->trans);
   1382 		scf_transaction_destroy_children(handle->trans);
   1383 		scf_transaction_destroy(handle->trans);
   1384 		handle->trans = NULL;
   1385 	}
   1386 }
   1387 
   1388 /*
   1389  * set_transaction_tstamp(sahandle)
   1390  *
   1391  * After a successful transaction commit, update the timestamp of the
   1392  * last transaction. This lets us detect changes from other processes.
   1393  */
   1394 static void
   1395 set_transaction_tstamp(sa_handle_impl_t sahandle)
   1396 {
   1397 	char tstring[32];
   1398 	struct timeval tv;
   1399 	scfutilhandle_t *scfhandle;
   1400 
   1401 	if (sahandle == NULL || sahandle->scfhandle == NULL)
   1402 		return;
   1403 
   1404 	scfhandle = sahandle->scfhandle;
   1405 
   1406 	if (sa_get_instance(scfhandle, "default") != SA_OK)
   1407 		return;
   1408 
   1409 	if (gettimeofday(&tv, NULL) != 0)
   1410 		return;
   1411 
   1412 	if (sa_start_transaction(scfhandle, "*state") != SA_OK)
   1413 		return;
   1414 
   1415 	sahandle->tstrans = TSTAMP((*(timestruc_t *)&tv));
   1416 	(void) snprintf(tstring, sizeof (tstring), "%lld", sahandle->tstrans);
   1417 	if (sa_set_property(sahandle->scfhandle, "lastupdate", tstring) ==
   1418 	    SA_OK) {
   1419 		/*
   1420 		 * While best if it succeeds, a failure doesn't cause
   1421 		 * problems and we will ignore it anyway.
   1422 		 */
   1423 		(void) scf_transaction_commit(scfhandle->trans);
   1424 		scf_transaction_destroy_children(scfhandle->trans);
   1425 		scf_transaction_destroy(scfhandle->trans);
   1426 	} else {
   1427 		sa_abort_transaction(scfhandle);
   1428 	}
   1429 }
   1430 
   1431 /*
   1432  * sa_set_property(handle, prop, value)
   1433  *
   1434  * Set a property transaction entry into the pending SMF transaction.
   1435  */
   1436 
   1437 int
   1438 sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr)
   1439 {
   1440 	int ret = SA_OK;
   1441 	scf_value_t *value;
   1442 	scf_transaction_entry_t *entry;
   1443 	/*
   1444 	 * Properties must be set in transactions and don't take
   1445 	 * effect until the transaction has been ended/committed.
   1446 	 */
   1447 	value = scf_value_create(handle->handle);
   1448 	entry = scf_entry_create(handle->handle);
   1449 	if (value != NULL && entry != NULL) {
   1450 		if (scf_transaction_property_change(handle->trans, entry,
   1451 		    propname, SCF_TYPE_ASTRING) == 0 ||
   1452 		    scf_transaction_property_new(handle->trans, entry,
   1453 		    propname, SCF_TYPE_ASTRING) == 0) {
   1454 			if (scf_value_set_astring(value, valstr) == 0) {
   1455 				if (scf_entry_add_value(entry, value) != 0) {
   1456 					ret = SA_SYSTEM_ERR;
   1457 					scf_value_destroy(value);
   1458 				}
   1459 				/* The value is in the transaction */
   1460 				value = NULL;
   1461 			} else {
   1462 				/* Value couldn't be constructed */
   1463 				ret = SA_SYSTEM_ERR;
   1464 			}
   1465 			/* The entry is in the transaction */
   1466 			entry = NULL;
   1467 		} else {
   1468 			ret = SA_SYSTEM_ERR;
   1469 		}
   1470 	} else {
   1471 		ret = SA_SYSTEM_ERR;
   1472 	}
   1473 	if (ret == SA_SYSTEM_ERR) {
   1474 		switch (scf_error()) {
   1475 		case SCF_ERROR_PERMISSION_DENIED:
   1476 			ret = SA_NO_PERMISSION;
   1477 			break;
   1478 		}
   1479 	}
   1480 	/*
   1481 	 * Cleanup if there were any errors that didn't leave these
   1482 	 * values where they would be cleaned up later.
   1483 	 */
   1484 	if (value != NULL)
   1485 		scf_value_destroy(value);
   1486 	if (entry != NULL)
   1487 		scf_entry_destroy(entry);
   1488 	return (ret);
   1489 }
   1490 
   1491 /*
   1492  * check_resource(share)
   1493  *
   1494  * Check to see if share has any persistent resources. We don't want
   1495  * to save if they are all transient.
   1496  */
   1497 static int
   1498 check_resource(sa_share_t share)
   1499 {
   1500 	sa_resource_t resource;
   1501 	int ret = B_FALSE;
   1502 
   1503 	for (resource = sa_get_share_resource(share, NULL);
   1504 	    resource != NULL && ret == B_FALSE;
   1505 	    resource = sa_get_next_resource(resource)) {
   1506 		char *type;
   1507 		type = sa_get_resource_attr(resource, "type");
   1508 		if (type != NULL) {
   1509 			if (strcmp(type, "transient") != 0) {
   1510 				ret = B_TRUE;
   1511 			}
   1512 			sa_free_attr_string(type);
   1513 		}
   1514 	}
   1515 	return (ret);
   1516 }
   1517 
   1518 /*
   1519  * sa_set_resource_property(handle, prop, value)
   1520  *
   1521  * set a property transaction entry into the pending SMF
   1522  * transaction. We don't want to include any transient resources
   1523  */
   1524 
   1525 static int
   1526 sa_set_resource_property(scfutilhandle_t *handle, sa_share_t share)
   1527 {
   1528 	int ret = SA_OK;
   1529 	scf_value_t *value;
   1530 	scf_transaction_entry_t *entry;
   1531 	sa_resource_t resource;
   1532 	char *valstr;
   1533 	char *idstr;
   1534 	char *description;
   1535 	char *propstr = NULL;
   1536 	size_t strsize;
   1537 
   1538 	/* don't bother if no persistent resources */
   1539 	if (check_resource(share) == B_FALSE)
   1540 		return (ret);
   1541 
   1542 	/*
   1543 	 * properties must be set in transactions and don't take
   1544 	 * effect until the transaction has been ended/committed.
   1545 	 */
   1546 	entry = scf_entry_create(handle->handle);
   1547 	if (entry == NULL)
   1548 		return (SA_SYSTEM_ERR);
   1549 
   1550 	if (scf_transaction_property_change(handle->trans, entry,
   1551 	    "resource",	SCF_TYPE_ASTRING) != 0 &&
   1552 	    scf_transaction_property_new(handle->trans, entry,
   1553 	    "resource", SCF_TYPE_ASTRING) != 0) {
   1554 		scf_entry_destroy(entry);
   1555 		return (SA_SYSTEM_ERR);
   1556 
   1557 	}
   1558 	for (resource = sa_get_share_resource(share, NULL);
   1559 	    resource != NULL;
   1560 	    resource = sa_get_next_resource(resource)) {
   1561 		value = scf_value_create(handle->handle);
   1562 		if (value == NULL) {
   1563 			ret = SA_NO_MEMORY;
   1564 			break;
   1565 		}
   1566 			/* Get size of complete string */
   1567 		valstr = sa_get_resource_attr(resource, "name");
   1568 		idstr = sa_get_resource_attr(resource, "id");
   1569 		description = sa_get_resource_description(resource);
   1570 		strsize = (valstr != NULL) ? strlen(valstr) : 0;
   1571 		strsize += (idstr != NULL) ? strlen(idstr) : 0;
   1572 		strsize += (description != NULL) ? strlen(description) : 0;
   1573 		if (strsize > 0) {
   1574 			strsize += 3; /* add nul and ':' */
   1575 			propstr = (char *)malloc(strsize);
   1576 			if (propstr == NULL) {
   1577 				scf_value_destroy(value);
   1578 				ret = SA_NO_MEMORY;
   1579 				goto err;
   1580 			}
   1581 			if (idstr == NULL)
   1582 				(void) snprintf(propstr, strsize, "%s",
   1583 				    valstr ? valstr : "");
   1584 			else
   1585 				(void) snprintf(propstr, strsize, "%s:%s:%s",
   1586 				    idstr, valstr ? valstr : "",
   1587 				    description ? description : "");
   1588 			if (scf_value_set_astring(value, propstr) != 0) {
   1589 				ret = SA_SYSTEM_ERR;
   1590 				free(propstr);
   1591 				scf_value_destroy(value);
   1592 				break;
   1593 			}
   1594 			if (scf_entry_add_value(entry, value) != 0) {
   1595 				ret = SA_SYSTEM_ERR;
   1596 				free(propstr);
   1597 				scf_value_destroy(value);
   1598 				break;
   1599 			}
   1600 			/* the value is in the transaction */
   1601 			value = NULL;
   1602 			free(propstr);
   1603 		}
   1604 err:
   1605 		if (valstr != NULL) {
   1606 			sa_free_attr_string(valstr);
   1607 			valstr = NULL;
   1608 		}
   1609 		if (idstr != NULL) {
   1610 			sa_free_attr_string(idstr);
   1611 			idstr = NULL;
   1612 		}
   1613 		if (description != NULL) {
   1614 			sa_free_share_description(description);
   1615 			description = NULL;
   1616 		}
   1617 	}
   1618 	/* the entry is in the transaction */
   1619 	entry = NULL;
   1620 
   1621 	if (valstr != NULL)
   1622 		sa_free_attr_string(valstr);
   1623 	if (idstr != NULL)
   1624 		sa_free_attr_string(idstr);
   1625 	if (description != NULL)
   1626 		sa_free_share_description(description);
   1627 
   1628 	if (ret == SA_SYSTEM_ERR) {
   1629 		switch (scf_error()) {
   1630 		case SCF_ERROR_PERMISSION_DENIED:
   1631 			ret = SA_NO_PERMISSION;
   1632 			break;
   1633 		}
   1634 	}
   1635 	/*
   1636 	 * cleanup if there were any errors that didn't leave
   1637 	 * these values where they would be cleaned up later.
   1638 	 */
   1639 	if (entry != NULL)
   1640 		scf_entry_destroy(entry);
   1641 
   1642 	return (ret);
   1643 }
   1644 
   1645 /*
   1646  * sa_commit_share(handle, group, share)
   1647  *
   1648  *	Commit this share to the repository.
   1649  *	properties are added if they exist but can be added later.
   1650  *	Need to add to dfstab and sharetab, if appropriate.
   1651  */
   1652 int
   1653 sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
   1654 {
   1655 	int ret = SA_OK;
   1656 	char *groupname;
   1657 	char *name;
   1658 	char *description;
   1659 	char *sharename;
   1660 	ssize_t proplen;
   1661 	char *propstring;
   1662 
   1663 	/*
   1664 	 * Don't commit in the zfs group. We do commit legacy
   1665 	 * (default) and all other groups/shares. ZFS is handled
   1666 	 * through the ZFS configuration rather than SMF.
   1667 	 */
   1668 
   1669 	groupname = sa_get_group_attr(group, "name");
   1670 	if (groupname != NULL) {
   1671 		if (strcmp(groupname, "zfs") == 0) {
   1672 			/*
   1673 			 * Adding to the ZFS group will result in the sharenfs
   1674 			 * property being set but we don't want to do anything
   1675 			 * SMF related at this point.
   1676 			 */
   1677 			sa_free_attr_string(groupname);
   1678 			return (ret);
   1679 		}
   1680 	}
   1681 
   1682 	proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
   1683 	propstring = malloc(proplen);
   1684 	if (propstring == NULL)
   1685 		ret = SA_NO_MEMORY;
   1686 
   1687 	if (groupname != NULL && ret == SA_OK) {
   1688 		ret = sa_get_instance(handle, groupname);
   1689 		sa_free_attr_string(groupname);
   1690 		groupname = NULL;
   1691 		sharename = sa_get_share_attr(share, "id");
   1692 		if (sharename == NULL) {
   1693 			/* slipped by */
   1694 			char shname[SA_SHARE_UUID_BUFLEN];
   1695 			generate_unique_sharename(shname);
   1696 			(void) xmlSetProp((xmlNodePtr)share, (xmlChar *)"id",
   1697 			    (xmlChar *)shname);
   1698 			sharename = strdup(shname);
   1699 		}
   1700 		if (sharename != NULL) {
   1701 			sigset_t old, new;
   1702 			/*
   1703 			 * Have a share name allocated so create a pgroup for
   1704 			 * it. It may already exist, but that is OK.  In order
   1705 			 * to avoid creating a share pgroup that doesn't have
   1706 			 * a path property, block signals around the critical
   1707 			 * region of creating the share pgroup and props.
   1708 			 */
   1709 			(void) sigprocmask(SIG_BLOCK, NULL, &new);
   1710 			(void) sigaddset(&new, SIGHUP);
   1711 			(void) sigaddset(&new, SIGINT);
   1712 			(void) sigaddset(&new, SIGQUIT);
   1713 			(void) sigaddset(&new, SIGTSTP);
   1714 			(void) sigprocmask(SIG_SETMASK, &new, &old);
   1715 
   1716 			ret = sa_create_pgroup(handle, sharename);
   1717 			if (ret == SA_OK) {
   1718 				/*
   1719 				 * Now start the transaction for the
   1720 				 * properties that define this share. They may
   1721 				 * exist so attempt to update before create.
   1722 				 */
   1723 				ret = sa_start_transaction(handle, sharename);
   1724 			}
   1725 			if (ret == SA_OK) {
   1726 				name = sa_get_share_attr(share, "path");
   1727 				if (name != NULL) {
   1728 					/*
   1729 					 * There needs to be a path
   1730 					 * for a share to exist.
   1731 					 */
   1732 					ret = sa_set_property(handle, "path",
   1733 					    name);
   1734 					sa_free_attr_string(name);
   1735 				} else {
   1736 					ret = SA_NO_MEMORY;
   1737 				}
   1738 			}
   1739 			if (ret == SA_OK) {
   1740 				name = sa_get_share_attr(share, "drive-letter");
   1741 				if (name != NULL) {
   1742 					/* A drive letter may exist for SMB */
   1743 					ret = sa_set_property(handle,
   1744 					    "drive-letter", name);
   1745 					sa_free_attr_string(name);
   1746 				}
   1747 			}
   1748 			if (ret == SA_OK) {
   1749 				name = sa_get_share_attr(share, "exclude");
   1750 				if (name != NULL) {
   1751 					/*
   1752 					 * In special cases need to
   1753 					 * exclude proto enable.
   1754 					 */
   1755 					ret = sa_set_property(handle,
   1756 					    "exclude", name);
   1757 					sa_free_attr_string(name);
   1758 				}
   1759 			}
   1760 			if (ret == SA_OK) {
   1761 				/*
   1762 				 * If there are resource names, bundle them up
   1763 				 * and save appropriately.
   1764 				 */
   1765 				ret = sa_set_resource_property(handle, share);
   1766 			}
   1767 
   1768 			if (ret == SA_OK) {
   1769 				description = sa_get_share_description(share);
   1770 				if (description != NULL) {
   1771 					ret = sa_set_property(handle,
   1772 					    "description",
   1773 					    description);
   1774 					sa_free_share_description(description);
   1775 				}
   1776 			}
   1777 			/* Make sure we cleanup the transaction */
   1778 			if (ret == SA_OK) {
   1779 				sa_handle_impl_t sahandle;
   1780 				sahandle = (sa_handle_impl_t)
   1781 				    sa_find_group_handle(group);
   1782 				if (sahandle != NULL)
   1783 					ret = sa_end_transaction(handle,
   1784 					    sahandle);
   1785 				else
   1786 					ret = SA_SYSTEM_ERR;
   1787 			} else {
   1788 				sa_abort_transaction(handle);
   1789 			}
   1790 
   1791 			(void) sigprocmask(SIG_SETMASK, &old, NULL);
   1792 
   1793 			free(sharename);
   1794 		}
   1795 	}
   1796 	if (ret == SA_SYSTEM_ERR) {
   1797 		int err = scf_error();
   1798 		if (err == SCF_ERROR_PERMISSION_DENIED)
   1799 			ret = SA_NO_PERMISSION;
   1800 	}
   1801 	if (propstring != NULL)
   1802 		free(propstring);
   1803 	if (groupname != NULL)
   1804 		sa_free_attr_string(groupname);
   1805 
   1806 	return (ret);
   1807 }
   1808 
   1809 /*
   1810  * remove_resources(handle, share, shareid)
   1811  *
   1812  * If the share has resources, remove all of them and their
   1813  * optionsets.
   1814  */
   1815 static int
   1816 remove_resources(scfutilhandle_t *handle, sa_share_t share, char *shareid)
   1817 {
   1818 	sa_resource_t resource;
   1819 	sa_optionset_t opt;
   1820 	char *proto;
   1821 	char *id;
   1822 	ssize_t proplen;
   1823 	char *propstring;
   1824 	int ret = SA_OK;
   1825 
   1826 	proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
   1827 	propstring = malloc(proplen);
   1828 	if (propstring == NULL)
   1829 		return (SA_NO_MEMORY);
   1830 
   1831 	for (resource = sa_get_share_resource(share, NULL);
   1832 	    resource != NULL; resource = sa_get_next_resource(resource)) {
   1833 		id = sa_get_resource_attr(resource, "id");
   1834 		if (id == NULL)
   1835 			continue;
   1836 		for (opt = sa_get_optionset(resource, NULL);
   1837 		    opt != NULL; opt = sa_get_next_optionset(resource)) {
   1838 			proto = sa_get_optionset_attr(opt, "type");
   1839 			if (proto != NULL) {
   1840 				(void) snprintf(propstring, proplen,
   1841 				    "%s_%s_%s", shareid, proto, id);
   1842 				ret = sa_delete_pgroup(handle, propstring);
   1843 				sa_free_attr_string(proto);
   1844 			}
   1845 		}
   1846 		sa_free_attr_string(id);
   1847 	}
   1848 	free(propstring);
   1849 	return (ret);
   1850 }
   1851 
   1852 /*
   1853  * sa_delete_share(handle, group, share)
   1854  *
   1855  * Remove the specified share from the group (and service instance).
   1856  */
   1857 
   1858 int
   1859 sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
   1860 {
   1861 	int ret = SA_OK;
   1862 	char *groupname = NULL;
   1863 	char *shareid = NULL;
   1864 	sa_optionset_t opt;
   1865 	sa_security_t sec;
   1866 	ssize_t proplen;
   1867 	char *propstring;
   1868 
   1869 	proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
   1870 	propstring = malloc(proplen);
   1871 	if (propstring == NULL)
   1872 		ret = SA_NO_MEMORY;
   1873 
   1874 	if (ret == SA_OK) {
   1875 		groupname = sa_get_group_attr(group, "name");
   1876 		shareid = sa_get_share_attr(share, "id");
   1877 		if (groupname == NULL || shareid == NULL) {
   1878 			ret = SA_CONFIG_ERR;
   1879 			goto out;
   1880 		}
   1881 		ret = sa_get_instance(handle, groupname);
   1882 		if (ret == SA_OK) {
   1883 			/* If a share has resources, remove them */
   1884 			ret = remove_resources(handle, share, shareid);
   1885 			/* If a share has properties, remove them */
   1886 			ret = sa_delete_pgroup(handle, shareid);
   1887 			for (opt = sa_get_optionset(share, NULL);
   1888 			    opt != NULL;
   1889 			    opt = sa_get_next_optionset(opt)) {
   1890 				char *proto;
   1891 				proto = sa_get_optionset_attr(opt, "type");
   1892 				if (proto != NULL) {
   1893 					(void) snprintf(propstring,
   1894 					    proplen, "%s_%s", shareid,
   1895 					    proto);
   1896 					ret = sa_delete_pgroup(handle,
   1897 					    propstring);
   1898 					sa_free_attr_string(proto);
   1899 				} else {
   1900 					ret = SA_NO_MEMORY;
   1901 				}
   1902 			}
   1903 			/*
   1904 			 * If a share has security/negotiable
   1905 			 * properties, remove them.
   1906 			 */
   1907 			for (sec = sa_get_security(share, NULL, NULL);
   1908 			    sec != NULL;
   1909 			    sec = sa_get_next_security(sec)) {
   1910 				char *proto;
   1911 				char *sectype;
   1912 				proto = sa_get_security_attr(sec, "type");
   1913 				sectype = sa_get_security_attr(sec, "sectype");
   1914 				if (proto != NULL && sectype != NULL) {
   1915 					(void) snprintf(propstring, proplen,
   1916 					    "%s_%s_%s", shareid,  proto,
   1917 					    sectype);
   1918 					ret = sa_delete_pgroup(handle,
   1919 					    propstring);
   1920 				} else {
   1921 					ret = SA_NO_MEMORY;
   1922 				}
   1923 				if (proto != NULL)
   1924 					sa_free_attr_string(proto);
   1925 				if (sectype != NULL)
   1926 					sa_free_attr_string(sectype);
   1927 			}
   1928 		}
   1929 	}
   1930 out:
   1931 	if (groupname != NULL)
   1932 		sa_free_attr_string(groupname);
   1933 	if (shareid != NULL)
   1934 		sa_free_attr_string(shareid);
   1935 	if (propstring != NULL)
   1936 		free(propstring);
   1937 
   1938 	return (ret);
   1939 }
   1940