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