xref: /titanic_41/usr/src/lib/libshare/common/scfutil.c (revision 4aac33d31b41cc7e3ac6fb66747ff2cae63d08cf)
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, xmlDocPtr *doc,
938 		sa_handle_t sahandle)
939 {
940 	int ret = SA_OK;
941 	scf_instance_t *instance;
942 	scf_iter_t *iter;
943 	char buff[BUFSIZ * 2];
944 
945 	*doc = xmlNewDoc((xmlChar *)"1.0");
946 	*root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
947 	instance = scf_instance_create(handle->handle);
948 	iter = scf_iter_create(handle->handle);
949 	if (*doc != NULL && *root != NULL && instance != NULL && iter != NULL) {
950 	    xmlDocSetRootElement(*doc, *root);
951 	    if ((ret = scf_iter_service_instances(iter,
952 						    handle->service)) == 0) {
953 		while ((ret = scf_iter_next_instance(iter,
954 							instance)) > 0) {
955 		    if (scf_instance_get_name(instance, buff,
956 						sizeof (buff)) > 0) {
957 			if (strcmp(buff, "default") == 0)
958 			    sa_extract_defaults(*root, handle, instance);
959 			ret = sa_extract_group(*root, handle, instance,
960 						sahandle);
961 		    }
962 		}
963 	    }
964 	} else {
965 	    /* if we can't create the document, cleanup */
966 	    if (*doc != NULL)
967 		xmlFreeDoc(*doc);
968 	    if (*root != NULL)
969 		xmlFreeNode(*root);
970 	    *doc = NULL;
971 	    *root = NULL;
972 	}
973 	/* always cleanup these */
974 	if (instance != NULL)
975 	    scf_instance_destroy(instance);
976 	if (iter != NULL)
977 	    scf_iter_destroy(iter);
978 	return (ret);
979 }
980 
981 /*
982  * sa_get_instance(handle, instance)
983  *
984  * get the instance of the group service. This is actually the
985  * specific group name. The instance is needed for all property and
986  * control operations.
987  */
988 
989 int
990 sa_get_instance(scfutilhandle_t *handle, char *instname)
991 {
992 	if (scf_service_get_instance(handle->service, instname,
993 					handle->instance) != 0) {
994 	    return (SA_NO_SUCH_GROUP);
995 	}
996 	return (SA_OK);
997 }
998 
999 /*
1000  * sa_create_instance(handle, instname)
1001  *
1002  * Create a new SMF service instance. There can only be one with a
1003  * given name.
1004  */
1005 
1006 int
1007 sa_create_instance(scfutilhandle_t *handle, char *instname)
1008 {
1009 	int ret = SA_OK;
1010 	char instance[SA_GROUP_INST_LEN];
1011 	if (scf_service_add_instance(handle->service, instname,
1012 					handle->instance) != 0) {
1013 	/* better error returns need to be added based on real error */
1014 	    if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
1015 		ret = SA_NO_PERMISSION;
1016 	    else
1017 		ret = SA_DUPLICATE_NAME;
1018 	} else {
1019 	    /* have the service created, so enable it */
1020 	    (void) snprintf(instance, sizeof (instance), "%s:%s",
1021 				SA_SVC_FMRI_BASE, instname);
1022 	    (void) smf_enable_instance(instance, 0);
1023 	}
1024 	return (ret);
1025 }
1026 
1027 /*
1028  * sa_delete_instance(handle, instname)
1029  *
1030  * When a group goes away, we also remove the service instance.
1031  */
1032 
1033 int
1034 sa_delete_instance(scfutilhandle_t *handle, char *instname)
1035 {
1036 	int ret;
1037 
1038 	if (strcmp(instname, "default") == 0) {
1039 	    ret = SA_NO_PERMISSION;
1040 	} else {
1041 	    if ((ret = sa_get_instance(handle, instname)) == SA_OK) {
1042 		if (scf_instance_delete(handle->instance) != 0)
1043 			/* need better analysis */
1044 		    ret = SA_NO_PERMISSION;
1045 	    }
1046 	}
1047 	return (ret);
1048 }
1049 
1050 /*
1051  * sa_create_pgroup(handle, pgroup)
1052  *
1053  * create a new property group
1054  */
1055 
1056 int
1057 sa_create_pgroup(scfutilhandle_t *handle, char *pgroup)
1058 {
1059 	int ret = SA_OK;
1060 	/*
1061 	 * only create a handle if it doesn't exist. It is ok to exist
1062 	 * since the pg handle will be set as a side effect.
1063 	 */
1064 	if (handle->pg == NULL) {
1065 	    handle->pg = scf_pg_create(handle->handle);
1066 	}
1067 	/*
1068 	 * if the pgroup exists, we are done. If it doesn't, then we
1069 	 * need to actually add one to the service instance.
1070 	 */
1071 	if (scf_instance_get_pg(handle->instance,
1072 				pgroup, handle->pg) != 0) {
1073 	    /* doesn't exist so create one */
1074 	    if (scf_instance_add_pg(handle->instance, pgroup,
1075 				    SCF_GROUP_APPLICATION, 0,
1076 				    handle->pg) != 0) {
1077 		switch (scf_error()) {
1078 		case SCF_ERROR_PERMISSION_DENIED:
1079 		    ret = SA_NO_PERMISSION;
1080 		    break;
1081 		default:
1082 		    ret = SA_SYSTEM_ERR;
1083 		    break;
1084 		}
1085 	    }
1086 	}
1087 	return (ret);
1088 }
1089 
1090 /*
1091  * sa_delete_pgroup(handle, pgroup)
1092  *
1093  * remove the property group from the current instance of the service,
1094  * but only if it actually exists.
1095  */
1096 
1097 int
1098 sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup)
1099 {
1100 	int ret = SA_OK;
1101 	/*
1102 	 * only delete if it does exist.
1103 	 */
1104 	if (scf_instance_get_pg(handle->instance,
1105 				pgroup, handle->pg) == 0) {
1106 	    /* does exist so delete it */
1107 	    if (scf_pg_delete(handle->pg) != 0) {
1108 		ret = SA_SYSTEM_ERR;
1109 	    }
1110 	} else {
1111 	    ret = SA_SYSTEM_ERR;
1112 	}
1113 	if (ret == SA_SYSTEM_ERR &&
1114 	    scf_error() == SCF_ERROR_PERMISSION_DENIED) {
1115 		ret = SA_NO_PERMISSION;
1116 	}
1117 	return (ret);
1118 }
1119 
1120 /*
1121  * sa_start_transaction(handle, pgroup)
1122  *
1123  * Start an SMF transaction so we can deal with properties. it would
1124  * be nice to not have to expose this, but we have to in order to
1125  * optimize.
1126  *
1127  * Basic model is to hold the transaction in the handle and allow
1128  * property adds/deletes/updates to be added then close the
1129  * transaction (or abort).  There may eventually be a need to handle
1130  * other types of transaction mechanisms but we don't do that now.
1131  *
1132  * An sa_start_transaction must be followed by either an
1133  * sa_end_transaction or sa_abort_transaction before another
1134  * sa_start_transaction can be done.
1135  */
1136 
1137 int
1138 sa_start_transaction(scfutilhandle_t *handle, char *propgroup)
1139 {
1140 	int ret = SA_OK;
1141 	/*
1142 	 * lookup the property group and create it if it doesn't already
1143 	 * exist.
1144 	 */
1145 	if (handle->scf_state == SCH_STATE_INIT) {
1146 	    ret = sa_create_pgroup(handle, propgroup);
1147 	    if (ret == SA_OK) {
1148 		handle->trans = scf_transaction_create(handle->handle);
1149 		if (handle->trans != NULL) {
1150 		    if (scf_transaction_start(handle->trans, handle->pg) != 0) {
1151 			ret = SA_SYSTEM_ERR;
1152 		    }
1153 		    if (ret != SA_OK) {
1154 			scf_transaction_destroy(handle->trans);
1155 			handle->trans = NULL;
1156 		    }
1157 		} else {
1158 		    ret = SA_SYSTEM_ERR;
1159 		}
1160 	    }
1161 	}
1162 	if (ret == SA_SYSTEM_ERR &&
1163 	    scf_error() == SCF_ERROR_PERMISSION_DENIED) {
1164 		ret = SA_NO_PERMISSION;
1165 	}
1166 	return (ret);
1167 }
1168 
1169 /*
1170  * sa_end_transaction(handle)
1171  *
1172  * Commit the changes that were added to the transaction in the
1173  * handle. Do all necessary cleanup.
1174  */
1175 
1176 int
1177 sa_end_transaction(scfutilhandle_t *handle)
1178 {
1179 	int ret = SA_OK;
1180 
1181 	if (handle->trans == NULL) {
1182 	    ret = SA_SYSTEM_ERR;
1183 	} else {
1184 	    if (scf_transaction_commit(handle->trans) < 0)
1185 		ret = SA_SYSTEM_ERR;
1186 	    scf_transaction_destroy_children(handle->trans);
1187 	    scf_transaction_destroy(handle->trans);
1188 	    handle->trans = NULL;
1189 	}
1190 	return (ret);
1191 }
1192 
1193 /*
1194  * sa_abort_transaction(handle)
1195  *
1196  * Abort the changes that were added to the transaction in the
1197  * handle. Do all necessary cleanup.
1198  */
1199 
1200 void
1201 sa_abort_transaction(scfutilhandle_t *handle)
1202 {
1203 	if (handle->trans != NULL) {
1204 	    scf_transaction_reset_all(handle->trans);
1205 	    scf_transaction_destroy_children(handle->trans);
1206 	    scf_transaction_destroy(handle->trans);
1207 	    handle->trans = NULL;
1208 	}
1209 }
1210 
1211 /*
1212  * sa_set_property(handle, prop, value)
1213  *
1214  * set a property transaction entry into the pending SMF transaction.
1215  */
1216 
1217 int
1218 sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr)
1219 {
1220 	int ret = SA_OK;
1221 	scf_value_t *value;
1222 	scf_transaction_entry_t *entry;
1223 	/*
1224 	 * properties must be set in transactions and don't take
1225 	 * effect until the transaction has been ended/committed.
1226 	 */
1227 	value = scf_value_create(handle->handle);
1228 	entry = scf_entry_create(handle->handle);
1229 	if (value != NULL && entry != NULL) {
1230 	    if (scf_transaction_property_change(handle->trans, entry,
1231 						propname,
1232 						SCF_TYPE_ASTRING) == 0 ||
1233 		scf_transaction_property_new(handle->trans, entry,
1234 						propname,
1235 						SCF_TYPE_ASTRING) == 0) {
1236 		if (scf_value_set_astring(value, valstr) == 0) {
1237 		    if (scf_entry_add_value(entry, value) != 0) {
1238 			ret = SA_SYSTEM_ERR;
1239 			scf_value_destroy(value);
1240 		    }
1241 		    /* the value is in the transaction */
1242 		    value = NULL;
1243 		} else {
1244 		    /* value couldn't be constructed */
1245 		    ret = SA_SYSTEM_ERR;
1246 		}
1247 		/* the entry is in the transaction */
1248 		entry = NULL;
1249 	    } else {
1250 		ret = SA_SYSTEM_ERR;
1251 	    }
1252 	} else {
1253 	    ret = SA_SYSTEM_ERR;
1254 	}
1255 	if (ret == SA_SYSTEM_ERR) {
1256 	    switch (scf_error()) {
1257 	    case SCF_ERROR_PERMISSION_DENIED:
1258 		ret = SA_NO_PERMISSION;
1259 		break;
1260 	    }
1261 	}
1262 	/*
1263 	 * cleanup if there were any errors that didn't leave these
1264 	 * values where they would be cleaned up later.
1265 	 */
1266 	if (value != NULL)
1267 	    scf_value_destroy(value);
1268 	if (entry != NULL)
1269 	    scf_entry_destroy(entry);
1270 	return (ret);
1271 }
1272 
1273 /*
1274  * sa_commit_share(handle, group, share)
1275  *
1276  *	commit this share to the repository.
1277  *	properties are added if they exist but can be added later.
1278  *	Need to add to dfstab and sharetab, if appropriate.
1279  */
1280 int
1281 sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
1282 {
1283 	int ret = SA_OK;
1284 	char *groupname;
1285 	char *name;
1286 	char *resource;
1287 	char *description;
1288 	char *sharename;
1289 	ssize_t proplen;
1290 	char *propstring;
1291 
1292 	/*
1293 	 * don't commit in the zfs group. We do commit legacy
1294 	 * (default) and all other groups/shares. ZFS is handled
1295 	 * through the ZFS configuration rather than SMF.
1296 	 */
1297 
1298 	groupname = sa_get_group_attr(group, "name");
1299 	if (groupname != NULL) {
1300 	    if (strcmp(groupname, "zfs") == 0) {
1301 		/*
1302 		 * adding to the ZFS group will result in the sharenfs
1303 		 * property being set but we don't want to do anything
1304 		 * SMF related at this point.
1305 		 */
1306 		sa_free_attr_string(groupname);
1307 		return (ret);
1308 	    }
1309 	}
1310 
1311 	proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1312 	propstring = malloc(proplen);
1313 	if (propstring == NULL)
1314 	    ret = SA_NO_MEMORY;
1315 
1316 	if (groupname != NULL && ret == SA_OK) {
1317 	    ret = sa_get_instance(handle, groupname);
1318 	    sa_free_attr_string(groupname);
1319 	    groupname = NULL;
1320 	    sharename = sa_get_share_attr(share, "id");
1321 	    if (sharename == NULL) {
1322 		/* slipped by */
1323 		char shname[SA_SHARE_UUID_BUFLEN];
1324 		generate_unique_sharename(shname);
1325 		xmlSetProp((xmlNodePtr)share, (xmlChar *)"id",
1326 			    (xmlChar *)shname);
1327 		sharename = strdup(shname);
1328 	    }
1329 	    if (sharename != NULL) {
1330 		sigset_t old, new;
1331 		/*
1332 		 * have a share name allocated so create a pgroup for
1333 		 * it. It may already exist, but that is OK.  In order
1334 		 * to avoid creating a share pgroup that doesn't have
1335 		 * a path property, block signals around the critical
1336 		 * region of creating the share pgroup and props.
1337 		 */
1338 		(void) sigprocmask(SIG_BLOCK, NULL, &new);
1339 		(void) sigaddset(&new, SIGHUP);
1340 		(void) sigaddset(&new, SIGINT);
1341 		(void) sigaddset(&new, SIGQUIT);
1342 		(void) sigaddset(&new, SIGTSTP);
1343 		(void) sigprocmask(SIG_SETMASK, &new, &old);
1344 
1345 		ret = sa_create_pgroup(handle, sharename);
1346 		if (ret == SA_OK) {
1347 			/*
1348 			 * now start the transaction for the
1349 			 * properties that define this share. They may
1350 			 * exist so attempt to update before create.
1351 			 */
1352 		    ret = sa_start_transaction(handle, sharename);
1353 		}
1354 		if (ret == SA_OK) {
1355 		    name = sa_get_share_attr(share, "path");
1356 		    if (name != NULL) {
1357 			/* there needs to be a path for a share to exist */
1358 			ret = sa_set_property(handle, "path", name);
1359 			sa_free_attr_string(name);
1360 		    } else {
1361 			ret = SA_NO_MEMORY;
1362 		    }
1363 		}
1364 		if (ret == SA_OK) {
1365 		    resource = sa_get_share_attr(share, "resource");
1366 		    if (resource != NULL) {
1367 			ret = sa_set_property(handle, "resource", resource);
1368 			sa_free_attr_string(resource);
1369 		    }
1370 		}
1371 		if (ret == SA_OK) {
1372 		    description = sa_get_share_description(share);
1373 		    if (description != NULL) {
1374 			ret = sa_set_property(handle, "description",
1375 						description);
1376 			sa_free_share_description(description);
1377 		    }
1378 		}
1379 		/* make sure we cleanup the transaction */
1380 		if (ret == SA_OK) {
1381 		    ret = sa_end_transaction(handle);
1382 		} else {
1383 		    sa_abort_transaction(handle);
1384 		}
1385 
1386 		(void) sigprocmask(SIG_SETMASK, &old, NULL);
1387 
1388 		free(sharename);
1389 	    }
1390 	}
1391 	if (ret == SA_SYSTEM_ERR) {
1392 	    int err = scf_error();
1393 	    if (err == SCF_ERROR_PERMISSION_DENIED)
1394 		ret = SA_NO_PERMISSION;
1395 	}
1396 	if (propstring != NULL)
1397 	    free(propstring);
1398 	if (groupname != NULL)
1399 	    sa_free_attr_string(groupname);
1400 
1401 	return (ret);
1402 }
1403 
1404 /*
1405  * sa_delete_share(handle, group, share)
1406  *
1407  * remove the specified share from the group (and service instance).
1408  */
1409 
1410 int
1411 sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
1412 {
1413 	int ret = SA_OK;
1414 	char *groupname = NULL;
1415 	char *shareid = NULL;
1416 	sa_optionset_t opt;
1417 	sa_security_t sec;
1418 	ssize_t proplen;
1419 	char *propstring;
1420 
1421 	proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1422 	propstring = malloc(proplen);
1423 	if (propstring == NULL)
1424 	    ret = SA_NO_MEMORY;
1425 
1426 	if (ret == SA_OK) {
1427 	    groupname = sa_get_group_attr(group, "name");
1428 	    shareid = sa_get_share_attr(share, "id");
1429 	    if (groupname != NULL && shareid != NULL) {
1430 		ret = sa_get_instance(handle, groupname);
1431 		if (ret == SA_OK) {
1432 		    /* if a share has properties, remove them */
1433 		    ret = sa_delete_pgroup(handle, shareid);
1434 		    for (opt = sa_get_optionset(share, NULL); opt != NULL;
1435 			opt = sa_get_next_optionset(opt)) {
1436 			char *proto;
1437 			proto = sa_get_optionset_attr(opt, "type");
1438 			if (proto != NULL) {
1439 			    (void) snprintf(propstring, proplen, "%s_%s",
1440 						shareid, proto);
1441 			    ret = sa_delete_pgroup(handle, propstring);
1442 			    sa_free_attr_string(proto);
1443 			} else {
1444 			    ret = SA_NO_MEMORY;
1445 			}
1446 		    }
1447 			/*
1448 			 * if a share has security/negotiable
1449 			 * properties, remove them.
1450 			 */
1451 		    for (sec = sa_get_security(share, NULL, NULL); sec != NULL;
1452 			sec = sa_get_next_security(sec)) {
1453 			char *proto;
1454 			char *sectype;
1455 			proto = sa_get_security_attr(sec, "type");
1456 			sectype = sa_get_security_attr(sec, "sectype");
1457 			if (proto != NULL && sectype != NULL) {
1458 			    (void) snprintf(propstring, proplen, "%s_%s_%s",
1459 					shareid,
1460 					proto, sectype);
1461 			    ret = sa_delete_pgroup(handle, propstring);
1462 			} else {
1463 			    ret = SA_NO_MEMORY;
1464 			}
1465 			if (proto != NULL)
1466 			    sa_free_attr_string(proto);
1467 			if (sectype != NULL)
1468 			    sa_free_attr_string(sectype);
1469 		    }
1470 		}
1471 	    } else {
1472 		ret = SA_CONFIG_ERR;
1473 	    }
1474 	}
1475 	if (groupname != NULL)
1476 	    sa_free_attr_string(groupname);
1477 	if (shareid != NULL)
1478 	    sa_free_attr_string(shareid);
1479 	if (propstring != NULL)
1480 	    free(propstring);
1481 
1482 	return (ret);
1483 }
1484