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