xref: /illumos-gate/usr/src/lib/libshare/common/scfutil.c (revision 1d1813a7a7c570174c2b6adc372045307b266117)
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 2007 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 <errno.h>
39 #include <uuid/uuid.h>
40 #include <sys/param.h>
41 #include <signal.h>
42 
43 ssize_t scf_max_name_len;
44 extern struct sa_proto_plugin *sap_proto_list;
45 extern sa_handle_impl_t get_handle_for_root(xmlNodePtr);
46 
47 /*
48  * The SMF facility uses some properties that must exist. We want to
49  * skip over these when processing protocol options.
50  */
51 static char *skip_props[] = {
52 	"modify_authorization",
53 	"action_authorization",
54 	"value_authorization",
55 	NULL
56 };
57 
58 /*
59  * sa_scf_fini(handle)
60  *
61  * must be called when done. Called with the handle allocated in
62  * sa_scf_init(), it cleans up the state and frees any SCF resources
63  * still in use. Called by sa_fini().
64  */
65 
66 void
67 sa_scf_fini(scfutilhandle_t *handle)
68 {
69 	if (handle != NULL) {
70 	    int unbind = 0;
71 	    if (handle->scope != NULL) {
72 		unbind = 1;
73 		scf_scope_destroy(handle->scope);
74 	    }
75 	    if (handle->service != NULL)
76 		    scf_service_destroy(handle->service);
77 	    if (handle->pg != NULL)
78 		scf_pg_destroy(handle->pg);
79 	    if (handle->handle != NULL) {
80 		handle->scf_state = SCH_STATE_UNINIT;
81 		if (unbind)
82 		    (void) scf_handle_unbind(handle->handle);
83 		scf_handle_destroy(handle->handle);
84 	    }
85 	    free(handle);
86 	}
87 }
88 
89 /*
90  * sa_scf_init()
91  *
92  * must be called before using any of the SCF functions. Called by
93  * sa_init() during the API setup.
94  */
95 
96 scfutilhandle_t *
97 sa_scf_init(sa_handle_impl_t ihandle)
98 {
99 	scfutilhandle_t *handle;
100 
101 	scf_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
102 	if (scf_max_name_len <= 0)
103 	    scf_max_name_len = SA_MAX_NAME_LEN + 1;
104 
105 	handle = calloc(1, sizeof (scfutilhandle_t));
106 	if (handle != NULL) {
107 	    ihandle->scfhandle = handle;
108 	    handle->scf_state = SCH_STATE_INITIALIZING;
109 	    handle->handle = scf_handle_create(SCF_VERSION);
110 	    if (handle->handle != NULL) {
111 		if (scf_handle_bind(handle->handle) == 0) {
112 		    handle->scope = scf_scope_create(handle->handle);
113 		    handle->service = scf_service_create(handle->handle);
114 		    handle->pg = scf_pg_create(handle->handle);
115 		    handle->instance = scf_instance_create(handle->handle);
116 		    if (scf_handle_get_scope(handle->handle,
117 					SCF_SCOPE_LOCAL, handle->scope) == 0) {
118 			if (scf_scope_get_service(handle->scope,
119 						    SA_GROUP_SVC_NAME,
120 						    handle->service) != 0) {
121 			    goto err;
122 			}
123 			handle->scf_state = SCH_STATE_INIT;
124 			if (sa_get_instance(handle, "default") != SA_OK) {
125 			    char **protolist;
126 			    int numprotos, i;
127 			    sa_group_t defgrp;
128 			    defgrp = sa_create_group((sa_handle_t)ihandle,
129 							"default", NULL);
130 			    if (defgrp != NULL) {
131 				numprotos = sa_get_protocols(&protolist);
132 				for (i = 0; i < numprotos; i++) {
133 				    (void) sa_create_optionset(defgrp,
134 								protolist[i]);
135 				}
136 				if (protolist != NULL)
137 				    free(protolist);
138 			    }
139 			}
140 		    } else {
141 			goto err;
142 		    }
143 		} else {
144 		    goto err;
145 		}
146 	    } else {
147 		free(handle);
148 		handle = NULL;
149 		(void) printf("libshare could not access SMF repository: %s\n",
150 				scf_strerror(scf_error()));
151 	    }
152 	}
153 	return (handle);
154 
155 	/* error handling/unwinding */
156 err:
157 	(void) sa_scf_fini(handle);
158 	(void) printf("libshare SMF initialization problem: %s\n",
159 			scf_strerror(scf_error()));
160 	return (NULL);
161 }
162 
163 /*
164  * get_scf_limit(name)
165  *
166  * Since we use  scf_limit a lot and do the same  check and return the
167  * same  value  if  it  fails,   implement  as  a  function  for  code
168  * simplification.  Basically, if  name isn't found, return MAXPATHLEN
169  * (1024) so we have a reasonable default buffer size.
170  */
171 static ssize_t
172 get_scf_limit(uint32_t name)
173 {
174 	ssize_t vallen;
175 
176 	vallen = scf_limit(name);
177 	if (vallen == (ssize_t)-1)
178 	    vallen = MAXPATHLEN;
179 	return (vallen);
180 }
181 
182 /*
183  * skip_property(name)
184  *
185  * internal function to check to see if a property is an SMF magic
186  * property that needs to be skipped.
187  */
188 static int
189 skip_property(char *name)
190 {
191 	int i;
192 
193 	for (i = 0; skip_props[i] != NULL; i++)
194 	    if (strcmp(name, skip_props[i]) == 0)
195 		return (1);
196 	return (0);
197 }
198 
199 /*
200  * generate_unique_sharename(sharename)
201  *
202  * Shares are represented in SMF as property groups. Due to share
203  * paths containing characters that are not allowed in SMF names and
204  * the need to be unique, we use UUIDs to construct a unique name.
205  */
206 
207 static void
208 generate_unique_sharename(char *sharename)
209 {
210 	uuid_t uuid;
211 
212 	uuid_generate(uuid);
213 	(void) strcpy(sharename, "S-");
214 	uuid_unparse(uuid, sharename + 2);
215 }
216 
217 /*
218  * valid_protocol(proto)
219  *
220  * check to see if the specified protocol is a valid one for the
221  * general sharemgr facility. We determine this by checking which
222  * plugin protocols were found.
223  */
224 
225 static int
226 valid_protocol(char *proto)
227 {
228 	struct sa_proto_plugin *plugin;
229 	for (plugin = sap_proto_list; plugin != NULL;
230 	    plugin = plugin->plugin_next)
231 	    if (strcmp(proto, plugin->plugin_ops->sa_protocol) == 0)
232 		return (1);
233 	return (0);
234 }
235 
236 /*
237  * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype)
238  *
239  * extract the name property group and create the specified type of
240  * node on the provided group.  type will be optionset or security.
241  */
242 
243 static int
244 sa_extract_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
245 			scf_propertygroup_t *pg,
246 			char *nodetype, char *proto, char *sectype)
247 {
248 	xmlNodePtr node;
249 	scf_iter_t *iter;
250 	scf_property_t *prop;
251 	scf_value_t *value;
252 	char *name;
253 	char *valuestr;
254 	ssize_t vallen;
255 	int ret = SA_OK;
256 
257 	vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
258 
259 	node = xmlNewChild(root, NULL, (xmlChar *)nodetype, NULL);
260 	if (node != NULL) {
261 	    if (proto != NULL)
262 		xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
263 	    if (sectype != NULL)
264 		xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype);
265 		/*
266 		 * have node to work with so iterate over the properties
267 		 * in the pg and create option sub nodes.
268 		 */
269 		iter = scf_iter_create(handle->handle);
270 		value = scf_value_create(handle->handle);
271 		prop = scf_property_create(handle->handle);
272 		name = malloc(scf_max_name_len);
273 		valuestr = malloc(vallen);
274 		/*
275 		 * want to iterate through the properties and add them
276 		 * to the base optionset.
277 		 */
278 		if (iter != NULL && value != NULL && prop != NULL &&
279 		    valuestr != NULL && name != NULL) {
280 		    if (scf_iter_pg_properties(iter, pg) == 0) {
281 			/* now iterate the properties in the group */
282 			while (scf_iter_next_property(iter, prop) > 0) {
283 			    /* have a property */
284 			    if (scf_property_get_name(prop, name,
285 							scf_max_name_len) > 0) {
286 				/* some properties are part of the framework */
287 				if (skip_property(name))
288 				    continue;
289 				if (scf_property_get_value(prop, value) == 0) {
290 				    if (scf_value_get_astring(value, valuestr,
291 								vallen) >= 0) {
292 					sa_property_t saprop;
293 					saprop = sa_create_property(name,
294 								    valuestr);
295 					if (saprop != NULL) {
296 					/*
297 					 * since in SMF, don't
298 					 * recurse. Use xmlAddChild
299 					 * directly, instead.
300 					 */
301 					    xmlAddChild(node,
302 							(xmlNodePtr) saprop);
303 					}
304 				    }
305 				}
306 			    }
307 			}
308 		    }
309 		} else {
310 		    ret = SA_NO_MEMORY;
311 		}
312 		/* cleanup to avoid memory leaks */
313 		if (value != NULL)
314 		    scf_value_destroy(value);
315 		if (iter != NULL)
316 		    scf_iter_destroy(iter);
317 		if (prop != NULL)
318 		    scf_property_destroy(prop);
319 		if (name != NULL)
320 		    free(name);
321 		if (valuestr != NULL)
322 		    free(valuestr);
323 	}
324 	return (ret);
325 }
326 
327 /*
328  * sa_extract_attrs(root, handle, instance)
329  *
330  * local function to extract the actual attributes/properties from the
331  * property group of the service instance. These are the well known
332  * attributes of "state" and "zfs". If additional attributes are
333  * added, they should be added here.
334  */
335 
336 static void
337 sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle,
338 		    scf_instance_t *instance)
339 {
340 	scf_property_t *prop;
341 	scf_value_t *value;
342 	char *valuestr;
343 	ssize_t vallen;
344 
345 	vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
346 	prop = scf_property_create(handle->handle);
347 	value = scf_value_create(handle->handle);
348 	valuestr = malloc(vallen);
349 	if (prop != NULL && value != NULL && valuestr != NULL &&
350 	    scf_instance_get_pg(instance, "operation",
351 				handle->pg) == 0) {
352 		/*
353 		 * have a property group with desired name so now get
354 		 * the known attributes.
355 		 */
356 	    if (scf_pg_get_property(handle->pg, "state", prop) == 0) {
357 		/* found the property so get the value */
358 		if (scf_property_get_value(prop, value) == 0) {
359 		    if (scf_value_get_astring(value, valuestr, vallen) >= 0) {
360 			xmlSetProp(root, (xmlChar *)"state",
361 				    (xmlChar *)valuestr);
362 		    }
363 		}
364 	    }
365 	    if (scf_pg_get_property(handle->pg, "zfs", 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, vallen) > 0) {
369 			xmlSetProp(root, (xmlChar *)"zfs",
370 				    (xmlChar *)valuestr);
371 		    }
372 		}
373 	    }
374 	}
375 	if (valuestr != NULL)
376 	    free(valuestr);
377 	if (value != NULL)
378 	    scf_value_destroy(value);
379 	if (prop != NULL)
380 	    scf_property_destroy(prop);
381 }
382 
383 /*
384  * list of known share attributes.
385  */
386 
387 static char *share_attr[] = {
388 	"path",
389 	"id",
390 	"resource",
391 	NULL,
392 };
393 
394 static int
395 is_share_attr(char *name)
396 {
397 	int i;
398 	for (i = 0; share_attr[i] != NULL; i++)
399 	    if (strcmp(name, share_attr[i]) == 0)
400 		return (1);
401 	return (0);
402 }
403 
404 /*
405  * sa_share_from_pgroup
406  *
407  * extract the share definition from the share property group. We do
408  * some sanity checking to avoid bad data.
409  *
410  * Since this is only constructing the internal data structures, we
411  * don't use the sa_* functions most of the time.
412  */
413 void
414 sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
415 			scf_propertygroup_t *pg, char *id)
416 {
417 	xmlNodePtr node;
418 	char *name;
419 	scf_iter_t *iter;
420 	scf_property_t *prop;
421 	scf_value_t *value;
422 	ssize_t vallen;
423 	char *valuestr;
424 	int ret = SA_OK;
425 	int have_path = 0;
426 
427 	/*
428 	 * While preliminary check (starts with 'S') passed before
429 	 * getting here. Need to make sure it is in ID syntax
430 	 * (Snnnnnn). Note that shares with properties have similar
431 	 * pgroups.
432 	 */
433 	vallen = strlen(id);
434 	if (*id == SA_SHARE_PG_PREFIX[0] && vallen == SA_SHARE_PG_LEN) {
435 	    uuid_t uuid;
436 	    if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) != 0 ||
437 		uuid_parse(id + 2, uuid) < 0)
438 		return;
439 	} else {
440 	    return;
441 	}
442 
443 	vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
444 
445 	iter = scf_iter_create(handle->handle);
446 	value = scf_value_create(handle->handle);
447 	prop = scf_property_create(handle->handle);
448 	name = malloc(scf_max_name_len);
449 	valuestr = malloc(vallen);
450 
451 	/*
452 	 * construct the share XML node. It is similar to sa_add_share
453 	 * but never changes the repository. Also, there won't be any
454 	 * ZFS or transient shares.  Root will be the group it is
455 	 * associated with.
456 	 */
457 	node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL);
458 	if (node != NULL) {
459 		/*
460 		 * make sure the UUID part of the property group is
461 		 * stored in the share "id" property. We use this
462 		 * later.
463 		 */
464 	    xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id);
465 	    xmlSetProp(node, (xmlChar *)"type", (xmlChar *)"persist");
466 	}
467 
468 	if (iter != NULL && value != NULL && prop != NULL && name != NULL) {
469 		/* iterate over the share pg properties */
470 	    if (scf_iter_pg_properties(iter, pg) == 0) {
471 		while (scf_iter_next_property(iter, prop) > 0) {
472 		    ret = SA_SYSTEM_ERR; /* assume the worst */
473 		    if (scf_property_get_name(prop, name,
474 						scf_max_name_len) > 0) {
475 			if (scf_property_get_value(prop, value) == 0) {
476 			    if (scf_value_get_astring(value, valuestr,
477 							vallen) >= 0) {
478 				ret = SA_OK;
479 			    }
480 			}
481 		    }
482 		    if (ret == SA_OK) {
483 			/*
484 			 * check that we have the "path" property in
485 			 * name. The string in name will always be nul
486 			 * terminated if scf_property_get_name()
487 			 * succeeded.
488 			 */
489 			if (strcmp(name, "path") == 0)
490 			    have_path = 1;
491 			if (is_share_attr(name)) {
492 				/*
493 				 * if a share attr, then simple -
494 				 * usually path and resource name
495 				 */
496 			    xmlSetProp(node, (xmlChar *)name,
497 					(xmlChar *)valuestr);
498 			} else {
499 			    if (strcmp(name, "description") == 0) {
500 				/* we have a description node */
501 				xmlNodePtr desc;
502 				desc = xmlNewChild(node, NULL,
503 						    (xmlChar *)"description",
504 						    NULL);
505 				if (desc != NULL)
506 				    xmlNodeSetContent(desc,
507 							(xmlChar *)valuestr);
508 			    }
509 			}
510 		    }
511 		}
512 	    }
513 	}
514 	/*
515 	 * a share without a path is broken so we want to not include
516 	 * these.  They shouldn't happen but if you kill a sharemgr in
517 	 * the process of creating a share, it could happen.  They
518 	 * should be harmless.  It is also possible that another
519 	 * sharemgr is running and in the process of creating a share.
520 	 */
521 	if (have_path == 0 && node != NULL) {
522 	    xmlUnlinkNode(node);
523 	    xmlFreeNode(node);
524 	}
525 	if (name != NULL)
526 	    free(name);
527 	if (valuestr != NULL)
528 	    free(valuestr);
529 	if (value != NULL)
530 	    scf_value_destroy(value);
531 	if (iter != NULL)
532 	    scf_iter_destroy(iter);
533 	if (prop != NULL)
534 	    scf_property_destroy(prop);
535 }
536 
537 /*
538  * find_share_by_id(shareid)
539  *
540  * Search all shares in all groups until we find the share represented
541  * by "id".
542  */
543 
544 static sa_share_t
545 find_share_by_id(sa_handle_t handle, char *shareid)
546 {
547 	sa_group_t group;
548 	sa_share_t share = NULL;
549 	char *id = NULL;
550 	int done = 0;
551 
552 	for (group = sa_get_group(handle, NULL); group != NULL && !done;
553 		group = sa_get_next_group(group)) {
554 		for (share = sa_get_share(group, NULL); share != NULL;
555 			share = sa_get_next_share(share)) {
556 			id = sa_get_share_attr(share, "id");
557 			if (id != NULL && strcmp(id, shareid) == 0) {
558 				sa_free_attr_string(id);
559 				id = NULL;
560 				done++;
561 				break;
562 			}
563 			if (id != NULL) {
564 			    sa_free_attr_string(id);
565 			    id = NULL;
566 			}
567 		}
568 	}
569 	return (share);
570 }
571 
572 /*
573  * sa_share_props_from_pgroup(root, handle, pg, id)
574  *
575  * extract share properties from the SMF property group. More sanity
576  * checks are done and the share object is created. We ignore some
577  * errors that could exist in the repository and only worry about
578  * property groups that validate in naming.
579  */
580 
581 static int
582 sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
583 			scf_propertygroup_t *pg, char *id, sa_handle_t sahandle)
584 {
585 	xmlNodePtr node;
586 	char *name;
587 	scf_iter_t *iter;
588 	scf_property_t *prop;
589 	scf_value_t *value;
590 	ssize_t vallen;
591 	char *valuestr;
592 	int ret = SA_OK;
593 	char *sectype = NULL;
594 	char *proto;
595 	sa_share_t share;
596 
597 	/*
598 	 * While preliminary check (starts with 'S') passed before
599 	 * getting here. Need to make sure it is in ID syntax
600 	 * (Snnnnnn). Note that shares with properties have similar
601 	 * pgroups. If the pg name is more than SA_SHARE_PG_LEN
602 	 * characters, it is likely one of the protocol/security
603 	 * versions.
604 	 */
605 	vallen = strlen(id);
606 	if (*id == SA_SHARE_PG_PREFIX[0] && vallen > SA_SHARE_PG_LEN) {
607 	    uuid_t uuid;
608 	    if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) == 0) {
609 		proto = strchr(id, '_');
610 		if (proto == NULL)
611 		    return (ret);
612 		*proto++ = '\0';
613 		if (uuid_parse(id + SA_SHARE_PG_PREFIXLEN, uuid) < 0)
614 		    return (ret);
615 		/*
616 		 * probably a legal optionset so check a few more
617 		 * syntax points below.
618 		 */
619 		if (*proto == '\0') {
620 		    /* not a valid proto (null) */
621 		    return (ret);
622 		}
623 		sectype = strchr(proto, '_');
624 		if (sectype != NULL)
625 		    *sectype++ = '\0';
626 		if (!valid_protocol(proto))
627 		    return (ret);
628 	    }
629 	} else {
630 	/*
631 	 * it is ok to not have what we thought since someone might
632 	 * have added a name via SMF.
633 	 */
634 	    return (ret);
635 	}
636 
637 	/*
638 	 * to get here, we have a valid protocol and possibly a
639 	 * security. We now have to find the share that it is really
640 	 * associated with. The "id" portion of the pgroup name will
641 	 * match.
642 	 */
643 
644 	share = find_share_by_id(sahandle, id);
645 	if (share == NULL)
646 	    return (SA_BAD_PATH);
647 
648 	root = (xmlNodePtr)share;
649 
650 	vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
651 
652 	iter = scf_iter_create(handle->handle);
653 	value = scf_value_create(handle->handle);
654 	prop = scf_property_create(handle->handle);
655 	name = malloc(scf_max_name_len);
656 	valuestr = malloc(vallen);
657 
658 	if (sectype == NULL)
659 	    node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL);
660 	else {
661 	    node = xmlNewChild(root, NULL, (xmlChar *)"security", NULL);
662 	    if (node != NULL)
663 		xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype);
664 	}
665 	if (node != NULL) {
666 	    xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
667 	    /* now find the properties */
668 	    if (iter != NULL && value != NULL && prop != NULL && name != NULL) {
669 		/* iterate over the share pg properties */
670 		if (scf_iter_pg_properties(iter, pg) == 0) {
671 		    while (scf_iter_next_property(iter, prop) > 0) {
672 			ret = SA_SYSTEM_ERR; /* assume the worst */
673 			if (scf_property_get_name(prop, name,
674 						    scf_max_name_len) > 0) {
675 			    if (scf_property_get_value(prop, value) == 0) {
676 				if (scf_value_get_astring(value, valuestr,
677 							    vallen) >= 0) {
678 				    ret = SA_OK;
679 				}
680 			    }
681 			} else {
682 			    ret = SA_SYSTEM_ERR;
683 			}
684 			if (ret == SA_OK) {
685 			    sa_property_t prop;
686 			    prop = sa_create_property(name, valuestr);
687 			    if (prop != NULL)
688 				prop = (sa_property_t)xmlAddChild(node,
689 							(xmlNodePtr)prop);
690 			    else
691 				ret = SA_NO_MEMORY;
692 			}
693 		    }
694 		} else {
695 		    ret = SA_SYSTEM_ERR;
696 		}
697 	    }
698 	} else {
699 	    ret = SA_NO_MEMORY;
700 	}
701 	if (iter != NULL)
702 	    scf_iter_destroy(iter);
703 	if (value != NULL)
704 	    scf_value_destroy(value);
705 	if (prop != NULL)
706 	    scf_property_destroy(prop);
707 	if (name != NULL)
708 	    free(name);
709 	if (valuestr != NULL)
710 	    free(valuestr);
711 	return (ret);
712 }
713 
714 /*
715  * sa_extract_group(root, handle, instance)
716  *
717  * get the config info for this instance of a group and create the XML
718  * subtree from it.
719  */
720 
721 static int
722 sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle,
723 			scf_instance_t *instance, sa_handle_t sahandle)
724 {
725 	char *buff;
726 	xmlNodePtr node;
727 	scf_iter_t *iter;
728 	char *proto;
729 	char *sectype;
730 	int have_shares = 0;
731 	int has_proto = 0;
732 	int is_default = 0;
733 	int ret = SA_OK;
734 	int err;
735 
736 	buff = malloc(scf_max_name_len);
737 	iter = scf_iter_create(handle->handle);
738 	if (buff != NULL) {
739 	    if (scf_instance_get_name(instance, buff,
740 						scf_max_name_len) > 0) {
741 		node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL);
742 		if (node != NULL) {
743 		    xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff);
744 		    if (strcmp(buff, "default") == 0)
745 			is_default++;
746 		    sa_extract_attrs(node, handle, instance);
747 			/*
748 			 * Iterate through all the property groups
749 			 * looking for those with security or
750 			 * optionset prefixes. The names of the
751 			 * matching pgroups are parsed to get the
752 			 * protocol, and for security, the sectype.
753 			 * Syntax is as follows:
754 			 *    optionset | optionset_<proto>
755 			 *    security_default | security_<proto>_<sectype>
756 			 * "operation" is handled by
757 			 * sa_extract_attrs().
758 			 */
759 		    if (iter != NULL) {
760 			if (scf_iter_instance_pgs(iter, instance) == 0) {
761 			    while (scf_iter_next_pg(iter, handle->pg) > 0) {
762 				/* have a pgroup so sort it out */
763 				ret = scf_pg_get_name(handle->pg, buff,
764 							scf_max_name_len);
765 				if (ret  > 0) {
766 				    if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
767 					sa_share_from_pgroup(node, handle,
768 								handle->pg,
769 								buff);
770 					have_shares++;
771 				    } else if (strncmp(buff, "optionset", 9) ==
772 						0) {
773 					char *nodetype = "optionset";
774 					/* have an optionset */
775 					sectype = NULL;
776 					proto = strchr(buff, '_');
777 					if (proto != NULL) {
778 					    *proto++ = '\0';
779 					    sectype = strchr(proto, '_');
780 					    if (sectype != NULL) {
781 						*sectype++ = '\0';
782 						nodetype = "security";
783 					    }
784 					}
785 					ret = sa_extract_pgroup(node, handle,
786 							    handle->pg,
787 							    nodetype,
788 							    proto, sectype);
789 					has_proto++;
790 				    } else if (strncmp(buff,
791 							"security", 8) == 0) {
792 					/*
793 					 * have a security (note that
794 					 * this should change in the
795 					 * future)
796 					 */
797 					proto = strchr(buff, '_');
798 					sectype = NULL;
799 					if (proto != NULL) {
800 					    *proto++ = '\0';
801 					    sectype = strchr(proto, '_');
802 					    if (sectype != NULL)
803 						*sectype++ = '\0';
804 					    if (strcmp(proto, "default") == 0)
805 						proto = NULL;
806 					}
807 					ret = sa_extract_pgroup(node, handle,
808 							    handle->pg,
809 							    "security", proto,
810 							    sectype);
811 					has_proto++;
812 				    }
813 				    /* ignore everything else */
814 				}
815 			    }
816 			} else {
817 			    ret = SA_NO_MEMORY;
818 			}
819 			/*
820 			 * Make sure we have a valid default group.
821 			 * On first boot, default won't have any
822 			 * protocols defined and won't be enabled (but
823 			 * should be).
824 			 */
825 			if (is_default) {
826 			    char *state = sa_get_group_attr((sa_group_t)node,
827 							    "state");
828 			    char **protos;
829 			    int numprotos;
830 			    int i;
831 
832 			    if (state == NULL) {
833 				/* set attribute to enabled */
834 				(void) sa_set_group_attr((sa_group_t)node,
835 							    "state",
836 							    "enabled");
837 				/* we can assume no protocols */
838 				numprotos = sa_get_protocols(&protos);
839 				for (i = 0; i < numprotos; i++)
840 				    (void) sa_create_optionset((sa_group_t)node,
841 								protos[i]);
842 				if (numprotos > 0)
843 				    free(protos);
844 			    } else {
845 				sa_free_attr_string(state);
846 			    }
847 			}
848 			/* do a second pass if shares were found */
849 			if (have_shares &&
850 				scf_iter_instance_pgs(iter, instance) == 0) {
851 			    while (scf_iter_next_pg(iter, handle->pg) > 0) {
852 				/*
853 				 * have a pgroup so see if it is a
854 				 * share optionset
855 				 */
856 				err = scf_pg_get_name(handle->pg, buff,
857 							scf_max_name_len);
858 				if (err  > 0) {
859 				    if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
860 					ret = sa_share_props_from_pgroup(node,
861 								handle,
862 								handle->pg,
863 								buff, sahandle);
864 				    }
865 				}
866 			    }
867 			}
868 		    }
869 		}
870 	    }
871 	}
872 	if (iter != NULL)
873 	    scf_iter_destroy(iter);
874 	if (buff != NULL)
875 	    free(buff);
876 	return (ret);
877 }
878 
879 /*
880  * sa_extract_defaults(root, handle, instance)
881  *
882  * local function to find the default properties that live in the
883  * default instance's "operation" proprerty group.
884  */
885 
886 static void
887 sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle,
888 		    scf_instance_t *instance)
889 {
890 	xmlNodePtr node;
891 	scf_property_t *prop;
892 	scf_value_t *value;
893 	char *valuestr;
894 	ssize_t vallen;
895 
896 	vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
897 	prop = scf_property_create(handle->handle);
898 	value = scf_value_create(handle->handle);
899 	valuestr = malloc(vallen);
900 	if (prop != NULL && value != NULL && vallen != NULL &&
901 	    scf_instance_get_pg(instance, "operation",
902 				handle->pg) == 0) {
903 	    if (scf_pg_get_property(handle->pg,
904 				    "legacy-timestamp", prop) == 0) {
905 		/* found the property so get the value */
906 		if (scf_property_get_value(prop, value) == 0) {
907 		    if (scf_value_get_astring(value, valuestr, vallen) > 0) {
908 			node = xmlNewChild(root, NULL, (xmlChar *)"legacy",
909 					    NULL);
910 			if (node != NULL) {
911 			    xmlSetProp(node, (xmlChar *)"timestamp",
912 					(xmlChar *)valuestr);
913 			    xmlSetProp(node, (xmlChar *)"path",
914 					(xmlChar *)SA_LEGACY_DFSTAB);
915 			}
916 		    }
917 		}
918 	    }
919 	}
920 	if (valuestr != NULL)
921 	    free(valuestr);
922 	if (value != NULL)
923 	    scf_value_destroy(value);
924 	if (prop != NULL)
925 	    scf_property_destroy(prop);
926 }
927 
928 
929 /*
930  * sa_get_config(handle, root, doc, sahandlec)
931  *
932  * walk the SMF repository for /network/shares/group and find all the
933  * instances. These become group names.  Then add the XML structure
934  * below the groups based on property groups and properties.
935  */
936 int
937 sa_get_config(scfutilhandle_t *handle, xmlNodePtr root, sa_handle_t sahandle)
938 {
939 	int ret = SA_OK;
940 	scf_instance_t *instance;
941 	scf_iter_t *iter;
942 	char buff[BUFSIZ * 2];
943 
944 	instance = scf_instance_create(handle->handle);
945 	iter = scf_iter_create(handle->handle);
946 	if (instance != NULL && iter != NULL) {
947 	    if ((ret = scf_iter_service_instances(iter,
948 						    handle->service)) == 0) {
949 		while ((ret = scf_iter_next_instance(iter,
950 							instance)) > 0) {
951 		    if (scf_instance_get_name(instance, buff,
952 						sizeof (buff)) > 0) {
953 			if (strcmp(buff, "default") == 0)
954 			    sa_extract_defaults(root, handle, instance);
955 			ret = sa_extract_group(root, handle, instance,
956 						sahandle);
957 		    }
958 		}
959 	    }
960 	}
961 
962 	/* always cleanup these */
963 	if (instance != NULL)
964 	    scf_instance_destroy(instance);
965 	if (iter != NULL)
966 	    scf_iter_destroy(iter);
967 	return (ret);
968 }
969 
970 /*
971  * sa_get_instance(handle, instance)
972  *
973  * get the instance of the group service. This is actually the
974  * specific group name. The instance is needed for all property and
975  * control operations.
976  */
977 
978 int
979 sa_get_instance(scfutilhandle_t *handle, char *instname)
980 {
981 	if (scf_service_get_instance(handle->service, instname,
982 					handle->instance) != 0) {
983 	    return (SA_NO_SUCH_GROUP);
984 	}
985 	return (SA_OK);
986 }
987 
988 /*
989  * sa_create_instance(handle, instname)
990  *
991  * Create a new SMF service instance. There can only be one with a
992  * given name.
993  */
994 
995 int
996 sa_create_instance(scfutilhandle_t *handle, char *instname)
997 {
998 	int ret = SA_OK;
999 	char instance[SA_GROUP_INST_LEN];
1000 	if (scf_service_add_instance(handle->service, instname,
1001 					handle->instance) != 0) {
1002 	/* better error returns need to be added based on real error */
1003 	    if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
1004 		ret = SA_NO_PERMISSION;
1005 	    else
1006 		ret = SA_DUPLICATE_NAME;
1007 	} else {
1008 	    /* have the service created, so enable it */
1009 	    (void) snprintf(instance, sizeof (instance), "%s:%s",
1010 				SA_SVC_FMRI_BASE, instname);
1011 	    (void) smf_enable_instance(instance, 0);
1012 	}
1013 	return (ret);
1014 }
1015 
1016 /*
1017  * sa_delete_instance(handle, instname)
1018  *
1019  * When a group goes away, we also remove the service instance.
1020  */
1021 
1022 int
1023 sa_delete_instance(scfutilhandle_t *handle, char *instname)
1024 {
1025 	int ret;
1026 
1027 	if (strcmp(instname, "default") == 0) {
1028 	    ret = SA_NO_PERMISSION;
1029 	} else {
1030 	    if ((ret = sa_get_instance(handle, instname)) == SA_OK) {
1031 		if (scf_instance_delete(handle->instance) != 0)
1032 			/* need better analysis */
1033 		    ret = SA_NO_PERMISSION;
1034 	    }
1035 	}
1036 	return (ret);
1037 }
1038 
1039 /*
1040  * sa_create_pgroup(handle, pgroup)
1041  *
1042  * create a new property group
1043  */
1044 
1045 int
1046 sa_create_pgroup(scfutilhandle_t *handle, char *pgroup)
1047 {
1048 	int ret = SA_OK;
1049 	/*
1050 	 * only create a handle if it doesn't exist. It is ok to exist
1051 	 * since the pg handle will be set as a side effect.
1052 	 */
1053 	if (handle->pg == NULL) {
1054 	    handle->pg = scf_pg_create(handle->handle);
1055 	}
1056 	/*
1057 	 * if the pgroup exists, we are done. If it doesn't, then we
1058 	 * need to actually add one to the service instance.
1059 	 */
1060 	if (scf_instance_get_pg(handle->instance,
1061 				pgroup, handle->pg) != 0) {
1062 	    /* doesn't exist so create one */
1063 	    if (scf_instance_add_pg(handle->instance, pgroup,
1064 				    SCF_GROUP_APPLICATION, 0,
1065 				    handle->pg) != 0) {
1066 		switch (scf_error()) {
1067 		case SCF_ERROR_PERMISSION_DENIED:
1068 		    ret = SA_NO_PERMISSION;
1069 		    break;
1070 		default:
1071 		    ret = SA_SYSTEM_ERR;
1072 		    break;
1073 		}
1074 	    }
1075 	}
1076 	return (ret);
1077 }
1078 
1079 /*
1080  * sa_delete_pgroup(handle, pgroup)
1081  *
1082  * remove the property group from the current instance of the service,
1083  * but only if it actually exists.
1084  */
1085 
1086 int
1087 sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup)
1088 {
1089 	int ret = SA_OK;
1090 	/*
1091 	 * only delete if it does exist.
1092 	 */
1093 	if (scf_instance_get_pg(handle->instance,
1094 				pgroup, handle->pg) == 0) {
1095 	    /* does exist so delete it */
1096 	    if (scf_pg_delete(handle->pg) != 0) {
1097 		ret = SA_SYSTEM_ERR;
1098 	    }
1099 	} else {
1100 	    ret = SA_SYSTEM_ERR;
1101 	}
1102 	if (ret == SA_SYSTEM_ERR &&
1103 	    scf_error() == SCF_ERROR_PERMISSION_DENIED) {
1104 		ret = SA_NO_PERMISSION;
1105 	}
1106 	return (ret);
1107 }
1108 
1109 /*
1110  * sa_start_transaction(handle, pgroup)
1111  *
1112  * Start an SMF transaction so we can deal with properties. it would
1113  * be nice to not have to expose this, but we have to in order to
1114  * optimize.
1115  *
1116  * Basic model is to hold the transaction in the handle and allow
1117  * property adds/deletes/updates to be added then close the
1118  * transaction (or abort).  There may eventually be a need to handle
1119  * other types of transaction mechanisms but we don't do that now.
1120  *
1121  * An sa_start_transaction must be followed by either an
1122  * sa_end_transaction or sa_abort_transaction before another
1123  * sa_start_transaction can be done.
1124  */
1125 
1126 int
1127 sa_start_transaction(scfutilhandle_t *handle, char *propgroup)
1128 {
1129 	int ret = SA_OK;
1130 	/*
1131 	 * lookup the property group and create it if it doesn't already
1132 	 * exist.
1133 	 */
1134 	if (handle->scf_state == SCH_STATE_INIT) {
1135 	    ret = sa_create_pgroup(handle, propgroup);
1136 	    if (ret == SA_OK) {
1137 		handle->trans = scf_transaction_create(handle->handle);
1138 		if (handle->trans != NULL) {
1139 		    if (scf_transaction_start(handle->trans, handle->pg) != 0) {
1140 			ret = SA_SYSTEM_ERR;
1141 		    }
1142 		    if (ret != SA_OK) {
1143 			scf_transaction_destroy(handle->trans);
1144 			handle->trans = NULL;
1145 		    }
1146 		} else {
1147 		    ret = SA_SYSTEM_ERR;
1148 		}
1149 	    }
1150 	}
1151 	if (ret == SA_SYSTEM_ERR &&
1152 	    scf_error() == SCF_ERROR_PERMISSION_DENIED) {
1153 		ret = SA_NO_PERMISSION;
1154 	}
1155 	return (ret);
1156 }
1157 
1158 /*
1159  * sa_end_transaction(handle)
1160  *
1161  * Commit the changes that were added to the transaction in the
1162  * handle. Do all necessary cleanup.
1163  */
1164 
1165 int
1166 sa_end_transaction(scfutilhandle_t *handle)
1167 {
1168 	int ret = SA_OK;
1169 
1170 	if (handle->trans == NULL) {
1171 	    ret = SA_SYSTEM_ERR;
1172 	} else {
1173 	    if (scf_transaction_commit(handle->trans) < 0)
1174 		ret = SA_SYSTEM_ERR;
1175 	    scf_transaction_destroy_children(handle->trans);
1176 	    scf_transaction_destroy(handle->trans);
1177 	    handle->trans = NULL;
1178 	}
1179 	return (ret);
1180 }
1181 
1182 /*
1183  * sa_abort_transaction(handle)
1184  *
1185  * Abort the changes that were added to the transaction in the
1186  * handle. Do all necessary cleanup.
1187  */
1188 
1189 void
1190 sa_abort_transaction(scfutilhandle_t *handle)
1191 {
1192 	if (handle->trans != NULL) {
1193 	    scf_transaction_reset_all(handle->trans);
1194 	    scf_transaction_destroy_children(handle->trans);
1195 	    scf_transaction_destroy(handle->trans);
1196 	    handle->trans = NULL;
1197 	}
1198 }
1199 
1200 /*
1201  * sa_set_property(handle, prop, value)
1202  *
1203  * set a property transaction entry into the pending SMF transaction.
1204  */
1205 
1206 int
1207 sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr)
1208 {
1209 	int ret = SA_OK;
1210 	scf_value_t *value;
1211 	scf_transaction_entry_t *entry;
1212 	/*
1213 	 * properties must be set in transactions and don't take
1214 	 * effect until the transaction has been ended/committed.
1215 	 */
1216 	value = scf_value_create(handle->handle);
1217 	entry = scf_entry_create(handle->handle);
1218 	if (value != NULL && entry != NULL) {
1219 	    if (scf_transaction_property_change(handle->trans, entry,
1220 						propname,
1221 						SCF_TYPE_ASTRING) == 0 ||
1222 		scf_transaction_property_new(handle->trans, entry,
1223 						propname,
1224 						SCF_TYPE_ASTRING) == 0) {
1225 		if (scf_value_set_astring(value, valstr) == 0) {
1226 		    if (scf_entry_add_value(entry, value) != 0) {
1227 			ret = SA_SYSTEM_ERR;
1228 			scf_value_destroy(value);
1229 		    }
1230 		    /* the value is in the transaction */
1231 		    value = NULL;
1232 		} else {
1233 		    /* value couldn't be constructed */
1234 		    ret = SA_SYSTEM_ERR;
1235 		}
1236 		/* the entry is in the transaction */
1237 		entry = NULL;
1238 	    } else {
1239 		ret = SA_SYSTEM_ERR;
1240 	    }
1241 	} else {
1242 	    ret = SA_SYSTEM_ERR;
1243 	}
1244 	if (ret == SA_SYSTEM_ERR) {
1245 	    switch (scf_error()) {
1246 	    case SCF_ERROR_PERMISSION_DENIED:
1247 		ret = SA_NO_PERMISSION;
1248 		break;
1249 	    }
1250 	}
1251 	/*
1252 	 * cleanup if there were any errors that didn't leave these
1253 	 * values where they would be cleaned up later.
1254 	 */
1255 	if (value != NULL)
1256 	    scf_value_destroy(value);
1257 	if (entry != NULL)
1258 	    scf_entry_destroy(entry);
1259 	return (ret);
1260 }
1261 
1262 /*
1263  * sa_commit_share(handle, group, share)
1264  *
1265  *	commit this share to the repository.
1266  *	properties are added if they exist but can be added later.
1267  *	Need to add to dfstab and sharetab, if appropriate.
1268  */
1269 int
1270 sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
1271 {
1272 	int ret = SA_OK;
1273 	char *groupname;
1274 	char *name;
1275 	char *resource;
1276 	char *description;
1277 	char *sharename;
1278 	ssize_t proplen;
1279 	char *propstring;
1280 
1281 	/*
1282 	 * don't commit in the zfs group. We do commit legacy
1283 	 * (default) and all other groups/shares. ZFS is handled
1284 	 * through the ZFS configuration rather than SMF.
1285 	 */
1286 
1287 	groupname = sa_get_group_attr(group, "name");
1288 	if (groupname != NULL) {
1289 	    if (strcmp(groupname, "zfs") == 0) {
1290 		/*
1291 		 * adding to the ZFS group will result in the sharenfs
1292 		 * property being set but we don't want to do anything
1293 		 * SMF related at this point.
1294 		 */
1295 		sa_free_attr_string(groupname);
1296 		return (ret);
1297 	    }
1298 	}
1299 
1300 	proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1301 	propstring = malloc(proplen);
1302 	if (propstring == NULL)
1303 	    ret = SA_NO_MEMORY;
1304 
1305 	if (groupname != NULL && ret == SA_OK) {
1306 	    ret = sa_get_instance(handle, groupname);
1307 	    sa_free_attr_string(groupname);
1308 	    groupname = NULL;
1309 	    sharename = sa_get_share_attr(share, "id");
1310 	    if (sharename == NULL) {
1311 		/* slipped by */
1312 		char shname[SA_SHARE_UUID_BUFLEN];
1313 		generate_unique_sharename(shname);
1314 		xmlSetProp((xmlNodePtr)share, (xmlChar *)"id",
1315 			    (xmlChar *)shname);
1316 		sharename = strdup(shname);
1317 	    }
1318 	    if (sharename != NULL) {
1319 		sigset_t old, new;
1320 		/*
1321 		 * have a share name allocated so create a pgroup for
1322 		 * it. It may already exist, but that is OK.  In order
1323 		 * to avoid creating a share pgroup that doesn't have
1324 		 * a path property, block signals around the critical
1325 		 * region of creating the share pgroup and props.
1326 		 */
1327 		(void) sigprocmask(SIG_BLOCK, NULL, &new);
1328 		(void) sigaddset(&new, SIGHUP);
1329 		(void) sigaddset(&new, SIGINT);
1330 		(void) sigaddset(&new, SIGQUIT);
1331 		(void) sigaddset(&new, SIGTSTP);
1332 		(void) sigprocmask(SIG_SETMASK, &new, &old);
1333 
1334 		ret = sa_create_pgroup(handle, sharename);
1335 		if (ret == SA_OK) {
1336 			/*
1337 			 * now start the transaction for the
1338 			 * properties that define this share. They may
1339 			 * exist so attempt to update before create.
1340 			 */
1341 		    ret = sa_start_transaction(handle, sharename);
1342 		}
1343 		if (ret == SA_OK) {
1344 		    name = sa_get_share_attr(share, "path");
1345 		    if (name != NULL) {
1346 			/* there needs to be a path for a share to exist */
1347 			ret = sa_set_property(handle, "path", name);
1348 			sa_free_attr_string(name);
1349 		    } else {
1350 			ret = SA_NO_MEMORY;
1351 		    }
1352 		}
1353 		if (ret == SA_OK) {
1354 		    resource = sa_get_share_attr(share, "resource");
1355 		    if (resource != NULL) {
1356 			ret = sa_set_property(handle, "resource", resource);
1357 			sa_free_attr_string(resource);
1358 		    }
1359 		}
1360 		if (ret == SA_OK) {
1361 		    description = sa_get_share_description(share);
1362 		    if (description != NULL) {
1363 			ret = sa_set_property(handle, "description",
1364 						description);
1365 			sa_free_share_description(description);
1366 		    }
1367 		}
1368 		/* make sure we cleanup the transaction */
1369 		if (ret == SA_OK) {
1370 		    ret = sa_end_transaction(handle);
1371 		} else {
1372 		    sa_abort_transaction(handle);
1373 		}
1374 
1375 		(void) sigprocmask(SIG_SETMASK, &old, NULL);
1376 
1377 		free(sharename);
1378 	    }
1379 	}
1380 	if (ret == SA_SYSTEM_ERR) {
1381 	    int err = scf_error();
1382 	    if (err == SCF_ERROR_PERMISSION_DENIED)
1383 		ret = SA_NO_PERMISSION;
1384 	}
1385 	if (propstring != NULL)
1386 	    free(propstring);
1387 	if (groupname != NULL)
1388 	    sa_free_attr_string(groupname);
1389 
1390 	return (ret);
1391 }
1392 
1393 /*
1394  * sa_delete_share(handle, group, share)
1395  *
1396  * remove the specified share from the group (and service instance).
1397  */
1398 
1399 int
1400 sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
1401 {
1402 	int ret = SA_OK;
1403 	char *groupname = NULL;
1404 	char *shareid = NULL;
1405 	sa_optionset_t opt;
1406 	sa_security_t sec;
1407 	ssize_t proplen;
1408 	char *propstring;
1409 
1410 	proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1411 	propstring = malloc(proplen);
1412 	if (propstring == NULL)
1413 	    ret = SA_NO_MEMORY;
1414 
1415 	if (ret == SA_OK) {
1416 	    groupname = sa_get_group_attr(group, "name");
1417 	    shareid = sa_get_share_attr(share, "id");
1418 	    if (groupname != NULL && shareid != NULL) {
1419 		ret = sa_get_instance(handle, groupname);
1420 		if (ret == SA_OK) {
1421 		    /* if a share has properties, remove them */
1422 		    ret = sa_delete_pgroup(handle, shareid);
1423 		    for (opt = sa_get_optionset(share, NULL); opt != NULL;
1424 			opt = sa_get_next_optionset(opt)) {
1425 			char *proto;
1426 			proto = sa_get_optionset_attr(opt, "type");
1427 			if (proto != NULL) {
1428 			    (void) snprintf(propstring, proplen, "%s_%s",
1429 						shareid, proto);
1430 			    ret = sa_delete_pgroup(handle, propstring);
1431 			    sa_free_attr_string(proto);
1432 			} else {
1433 			    ret = SA_NO_MEMORY;
1434 			}
1435 		    }
1436 			/*
1437 			 * if a share has security/negotiable
1438 			 * properties, remove them.
1439 			 */
1440 		    for (sec = sa_get_security(share, NULL, NULL); sec != NULL;
1441 			sec = sa_get_next_security(sec)) {
1442 			char *proto;
1443 			char *sectype;
1444 			proto = sa_get_security_attr(sec, "type");
1445 			sectype = sa_get_security_attr(sec, "sectype");
1446 			if (proto != NULL && sectype != NULL) {
1447 			    (void) snprintf(propstring, proplen, "%s_%s_%s",
1448 					shareid,
1449 					proto, sectype);
1450 			    ret = sa_delete_pgroup(handle, propstring);
1451 			} else {
1452 			    ret = SA_NO_MEMORY;
1453 			}
1454 			if (proto != NULL)
1455 			    sa_free_attr_string(proto);
1456 			if (sectype != NULL)
1457 			    sa_free_attr_string(sectype);
1458 		    }
1459 		}
1460 	    } else {
1461 		ret = SA_CONFIG_ERR;
1462 	    }
1463 	}
1464 	if (groupname != NULL)
1465 	    sa_free_attr_string(groupname);
1466 	if (shareid != NULL)
1467 	    sa_free_attr_string(shareid);
1468 	if (propstring != NULL)
1469 	    free(propstring);
1470 
1471 	return (ret);
1472 }
1473