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