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