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