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