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