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