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