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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* helper functions for using libscf with sharemgr */
30 #include <libxml/parser.h>
31 #include <libxml/tree.h>
33 #include "libshare_impl.h"
38 #include <uuid/uuid.h>
39 #include <sys/param.h>
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
);
49 * The SMF facility uses some properties that must exist. We want to
50 * skip over these when processing protocol options.
52 static char *skip_props
[] = {
53 "modify_authorization",
54 "action_authorization",
55 "value_authorization",
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().
68 sa_scf_fini(scfutilhandle_t
*handle
)
72 if (handle
->scope
!= NULL
) {
74 scf_scope_destroy(handle
->scope
);
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
;
85 (void) scf_handle_unbind(handle
->handle
);
86 scf_handle_destroy(handle
->handle
);
95 * Must be called before using any of the SCF functions. Called by
96 * sa_init() during the API setup.
100 sa_scf_init(sa_handle_impl_t ihandle
)
102 scfutilhandle_t
*handle
;
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;
108 handle
= calloc(1, sizeof (scfutilhandle_t
));
112 ihandle
->scfhandle
= handle
;
113 handle
->scf_state
= SCH_STATE_INITIALIZING
;
114 handle
->handle
= scf_handle_create(SCF_VERSION
);
115 if (handle
->handle
== NULL
) {
118 (void) printf("libshare could not access SMF repository: %s\n",
119 scf_strerror(scf_error()));
122 if (scf_handle_bind(handle
->handle
) != 0)
125 handle
->scope
= scf_scope_create(handle
->handle
);
126 handle
->service
= scf_service_create(handle
->handle
);
127 handle
->pg
= scf_pg_create(handle
->handle
);
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
)
134 if (scf_handle_get_scope(handle
->handle
,
135 SCF_SCOPE_LOCAL
, handle
->scope
) != 0)
137 if (scf_scope_get_service(handle
->scope
,
138 SA_GROUP_SVC_NAME
, handle
->service
) != 0)
141 handle
->scf_state
= SCH_STATE_INIT
;
142 if (sa_get_instance(handle
, "default") != SA_OK
) {
144 defgrp
= sa_create_group((sa_handle_t
)ihandle
, "default", NULL
);
145 /* Only NFS enabled for "default" group. */
147 (void) sa_create_optionset(defgrp
, "nfs");
152 /* Error handling/unwinding */
154 (void) sa_scf_fini(handle
);
155 (void) printf("libshare SMF initialization problem: %s\n",
156 scf_strerror(scf_error()));
161 * get_scf_limit(name)
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.
169 get_scf_limit(uint32_t name
)
173 vallen
= scf_limit(name
);
174 if (vallen
== (ssize_t
)-1)
180 * skip_property(name)
182 * Internal function to check to see if a property is an SMF magic
183 * property that needs to be skipped.
186 skip_property(char *name
)
190 for (i
= 0; skip_props
[i
] != NULL
; i
++)
191 if (strcmp(name
, skip_props
[i
]) == 0)
197 * generate_unique_sharename(sharename)
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.
205 generate_unique_sharename(char *sharename
)
210 (void) strcpy(sharename
, "S-");
211 uuid_unparse(uuid
, sharename
+ 2);
215 * valid_protocol(proto)
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.
223 valid_protocol(char *proto
)
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)
234 * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype)
236 * Extract the name property group and create the specified type of
237 * node on the provided group. type will be optionset or security.
241 sa_extract_pgroup(xmlNodePtr root
, scfutilhandle_t
*handle
,
242 scf_propertygroup_t
*pg
,
243 char *nodetype
, char *proto
, char *sectype
)
247 scf_property_t
*prop
;
254 vallen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
256 node
= xmlNewChild(root
, NULL
, (xmlChar
*)nodetype
, NULL
);
261 (void) xmlSetProp(node
, (xmlChar
*)"type", (xmlChar
*)proto
);
263 (void) xmlSetProp(node
, (xmlChar
*)"sectype",
266 * Have node to work with so iterate over the properties
267 * in the pg and create option sub nodes.
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
);
275 * Want to iterate through the properties and add them
276 * to the base optionset.
278 if (iter
== NULL
|| value
== NULL
|| prop
== NULL
||
279 valuestr
== NULL
|| name
== NULL
) {
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
))
293 if (scf_property_get_value(prop
, value
) != 0)
295 if (scf_value_get_astring(value
, valuestr
,
298 saprop
= sa_create_property(name
, valuestr
);
299 if (saprop
!= NULL
) {
301 * Since in SMF, don't
302 * recurse. Use xmlAddChild
305 (void) xmlAddChild(node
,
306 (xmlNodePtr
) saprop
);
312 /* cleanup to avoid memory leaks */
314 scf_value_destroy(value
);
316 scf_iter_destroy(iter
);
318 scf_property_destroy(prop
);
326 * sa_extract_attrs(root, handle, instance)
328 * Local function to extract the actual attributes/properties from the
329 * property group of the service instance. These are the well known
330 * attributes of "state" and "zfs". If additional attributes are
331 * added, they should be added here.
335 sa_extract_attrs(xmlNodePtr root
, scfutilhandle_t
*handle
,
336 scf_instance_t
*instance
)
338 scf_property_t
*prop
;
343 vallen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
344 prop
= scf_property_create(handle
->handle
);
345 value
= scf_value_create(handle
->handle
);
346 valuestr
= malloc(vallen
);
347 if (prop
== NULL
|| value
== NULL
|| valuestr
== NULL
||
348 scf_instance_get_pg(instance
, "operation", handle
->pg
) != 0) {
352 * Have a property group with desired name so now get
353 * the known attributes.
355 if (scf_pg_get_property(handle
->pg
, "state", prop
) == 0) {
356 /* Found the property so get the value */
357 if (scf_property_get_value(prop
, value
) == 0) {
358 if (scf_value_get_astring(value
, valuestr
,
360 (void) xmlSetProp(root
, (xmlChar
*)"state",
361 (xmlChar
*)valuestr
);
365 if (scf_pg_get_property(handle
->pg
, "zfs", prop
) == 0) {
366 /* Found the property so get the value */
367 if (scf_property_get_value(prop
, value
) == 0) {
368 if (scf_value_get_astring(value
, valuestr
,
370 (void) xmlSetProp(root
, (xmlChar
*)"zfs",
371 (xmlChar
*)valuestr
);
378 scf_value_destroy(value
);
380 scf_property_destroy(prop
);
384 * List of known share attributes.
387 static char *share_attr
[] = {
396 is_share_attr(char *name
)
399 for (i
= 0; share_attr
[i
] != NULL
; i
++)
400 if (strcmp(name
, share_attr
[i
]) == 0)
406 * _sa_make_resource(node, valuestr)
408 * Make a resource node on the share node. The valusestr will either
409 * be old format (SMF acceptable string) or new format (pretty much an
410 * arbitrary string with "nnn:" prefixing in order to persist
411 * mapping). The input valuestr will get modified in place. This is
412 * only used in SMF repository parsing. A possible third field will be
413 * a "description" string.
417 _sa_make_resource(xmlNodePtr node
, char *valuestr
)
421 char *description
= NULL
;
424 name
= strchr(valuestr
, ':');
426 /* this is old form so give an index of "0" */
430 /* NUL the ':' and move past it */
432 /* There could also be a description string */
433 description
= strchr(name
, ':');
434 if (description
!= NULL
)
435 *description
++ = '\0';
437 node
= xmlNewChild(node
, NULL
, (xmlChar
*)"resource", NULL
);
439 (void) xmlSetProp(node
, (xmlChar
*)"name", (xmlChar
*)name
);
440 (void) xmlSetProp(node
, (xmlChar
*)"id", (xmlChar
*)idx
);
441 /* SMF values are always persistent */
442 (void) xmlSetProp(node
, (xmlChar
*)"type",
443 (xmlChar
*)"persist");
444 if (description
!= NULL
&& strlen(description
) > 0) {
445 (void) xmlNewChild(node
, NULL
, (xmlChar
*)"description",
446 (xmlChar
*)description
);
453 * sa_share_from_pgroup
455 * Extract the share definition from the share property group. We do
456 * some sanity checking to avoid bad data.
458 * Since this is only constructing the internal data structures, we
459 * don't use the sa_* functions most of the time.
462 sa_share_from_pgroup(xmlNodePtr root
, scfutilhandle_t
*handle
,
463 scf_propertygroup_t
*pg
, char *id
)
468 scf_property_t
*prop
;
476 * While preliminary check (starts with 'S') passed before
477 * getting here. Need to make sure it is in ID syntax
478 * (Snnnnnn). Note that shares with properties have similar
482 if (*id
== SA_SHARE_PG_PREFIX
[0] && vallen
== SA_SHARE_PG_LEN
) {
484 if (strncmp(id
, SA_SHARE_PG_PREFIX
,
485 SA_SHARE_PG_PREFIXLEN
) != 0 ||
486 uuid_parse(id
+ 2, uuid
) < 0)
492 vallen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
494 iter
= scf_iter_create(handle
->handle
);
495 value
= scf_value_create(handle
->handle
);
496 prop
= scf_property_create(handle
->handle
);
497 name
= malloc(scf_max_name_len
);
498 valuestr
= malloc(vallen
);
501 * Construct the share XML node. It is similar to sa_add_share
502 * but never changes the repository. Also, there won't be any
503 * ZFS or transient shares. Root will be the group it is
506 node
= xmlNewChild(root
, NULL
, (xmlChar
*)"share", NULL
);
509 * Make sure the UUID part of the property group is
510 * stored in the share "id" property. We use this
513 (void) xmlSetProp(node
, (xmlChar
*)"id", (xmlChar
*)id
);
514 (void) xmlSetProp(node
, (xmlChar
*)"type",
515 (xmlChar
*)"persist");
518 if (iter
== NULL
|| value
== NULL
|| prop
== NULL
|| name
== NULL
)
521 /* Iterate over the share pg properties */
522 if (scf_iter_pg_properties(iter
, pg
) != 0)
525 while (scf_iter_next_property(iter
, prop
) > 0) {
526 ret
= SA_SYSTEM_ERR
; /* assume the worst */
527 if (scf_property_get_name(prop
, name
, scf_max_name_len
) > 0) {
528 if (scf_property_get_value(prop
, value
) == 0) {
529 if (scf_value_get_astring(value
, valuestr
,
533 } else if (strcmp(name
, "resource") == 0) {
540 * Check that we have the "path" property in
541 * name. The string in name will always be nul
542 * terminated if scf_property_get_name()
545 if (strcmp(name
, "path") == 0)
547 if (is_share_attr(name
)) {
549 * If a share attr, then simple -
550 * usually path and id name
552 (void) xmlSetProp(node
, (xmlChar
*)name
,
553 (xmlChar
*)valuestr
);
554 } else if (strcmp(name
, "resource") == 0) {
556 * Resource names handled differently since
557 * there can be multiple on each share. The
558 * "resource" id must be preserved since this
559 * will be used by some protocols in mapping
560 * "property spaces" to names and is always
561 * used to create SMF property groups specific
562 * to resources. CIFS needs this. The first
563 * value is present so add and then loop for
564 * any additional. Since this is new and
565 * previous values may exist, handle
569 viter
= scf_iter_create(handle
->handle
);
571 scf_iter_property_values(viter
, prop
) == 0) {
572 while (scf_iter_next_value(viter
, value
) > 0) {
573 /* Have a value so process it */
574 if (scf_value_get_ustring(value
,
575 valuestr
, vallen
) >= 0) {
577 _sa_make_resource(node
,
579 } else if (scf_value_get_astring(value
,
580 valuestr
, vallen
) >= 0) {
581 /* have an astring */
582 _sa_make_resource(node
,
586 scf_iter_destroy(viter
);
589 if (strcmp(name
, "description") == 0) {
590 /* We have a description node */
592 desc
= xmlNewChild(node
, NULL
,
593 (xmlChar
*)"description", NULL
);
595 xmlNodeSetContent(desc
,
596 (xmlChar
*)valuestr
);
602 * A share without a path is broken so we want to not include
603 * these. They shouldn't happen but if you kill a sharemgr in
604 * the process of creating a share, it could happen. They
605 * should be harmless. It is also possible that another
606 * sharemgr is running and in the process of creating a share.
608 if (have_path
== 0 && node
!= NULL
) {
615 scf_value_destroy(value
);
617 scf_iter_destroy(iter
);
619 scf_property_destroy(prop
);
623 * find_share_by_id(shareid)
625 * Search all shares in all groups until we find the share represented
630 find_share_by_id(sa_handle_t handle
, char *shareid
)
633 sa_share_t share
= NULL
;
637 for (group
= sa_get_group(handle
, NULL
);
638 group
!= NULL
&& !done
;
639 group
= sa_get_next_group(group
)) {
640 for (share
= sa_get_share(group
, NULL
);
642 share
= sa_get_next_share(share
)) {
643 id
= sa_get_share_attr(share
, "id");
644 if (id
!= NULL
&& strcmp(id
, shareid
) == 0) {
645 sa_free_attr_string(id
);
651 sa_free_attr_string(id
);
660 * find_resource_by_index(share, index)
662 * Search the resource records on the share for the id index.
665 find_resource_by_index(sa_share_t share
, char *index
)
667 sa_resource_t resource
;
668 sa_resource_t found
= NULL
;
671 for (resource
= sa_get_share_resource(share
, NULL
);
672 resource
!= NULL
&& found
== NULL
;
673 resource
= sa_get_next_resource(resource
)) {
674 id
= (char *)xmlGetProp((xmlNodePtr
)resource
, (xmlChar
*)"id");
676 if (strcmp(id
, index
) == 0) {
677 /* found it so save in "found" */
680 sa_free_attr_string(id
);
687 * sa_share_props_from_pgroup(root, handle, pg, id, sahandle)
689 * Extract share properties from the SMF property group. More sanity
690 * checks are done and the share object is created. We ignore some
691 * errors that could exist in the repository and only worry about
692 * property groups that validate in naming.
696 sa_share_props_from_pgroup(xmlNodePtr root
, scfutilhandle_t
*handle
,
697 scf_propertygroup_t
*pg
, char *id
, sa_handle_t sahandle
)
701 scf_iter_t
*iter
= NULL
;
702 scf_property_t
*prop
= NULL
;
703 scf_value_t
*value
= NULL
;
705 char *valuestr
= NULL
;
707 char *sectype
= NULL
;
713 * While preliminary check (starts with 'S') passed before
714 * getting here. Need to make sure it is in ID syntax
715 * (Snnnnnn). Note that shares with properties have similar
716 * pgroups. If the pg name is more than SA_SHARE_PG_LEN
717 * characters, it is likely one of the protocol/security
721 if (*id
!= SA_SHARE_PG_PREFIX
[0] || vallen
<= SA_SHARE_PG_LEN
) {
723 * It is ok to not have what we thought since someone might
724 * have added a name via SMF.
728 if (strncmp(id
, SA_SHARE_PG_PREFIX
, SA_SHARE_PG_PREFIXLEN
) == 0) {
729 proto
= strchr(id
, '_');
733 if (uuid_parse(id
+ SA_SHARE_PG_PREFIXLEN
, uuid
) < 0)
736 * probably a legal optionset so check a few more
737 * syntax points below.
739 if (*proto
== '\0') {
740 /* not a valid proto (null) */
744 sectype
= strchr(proto
, '_');
747 if (!valid_protocol(proto
))
752 * To get here, we have a valid protocol and possibly a
753 * security. We now have to find the share that it is really
754 * associated with. The "id" portion of the pgroup name will
758 share
= find_share_by_id(sahandle
, id
);
760 return (SA_BAD_PATH
);
762 root
= (xmlNodePtr
)share
;
764 vallen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
767 node
= xmlNewChild(root
, NULL
, (xmlChar
*)"optionset", NULL
);
769 if (isdigit((int)*sectype
)) {
770 sa_resource_t resource
;
772 * If sectype[0] is a digit, then it is an index into
773 * the resource names. We need to find a resource
774 * record and then get the properties into an
775 * optionset. The optionset becomes the "node" and the
776 * rest is hung off of the share.
778 resource
= find_resource_by_index(share
, sectype
);
779 if (resource
!= NULL
) {
780 node
= xmlNewChild(resource
, NULL
,
781 (xmlChar
*)"optionset", NULL
);
783 /* This shouldn't happen. */
789 * If not a digit, then it is a security type
790 * (alternate option space). Security types start with
793 node
= xmlNewChild(root
, NULL
, (xmlChar
*)"security",
796 (void) xmlSetProp(node
, (xmlChar
*)"sectype",
805 (void) xmlSetProp(node
, (xmlChar
*)"type", (xmlChar
*)proto
);
806 /* now find the properties */
807 iter
= scf_iter_create(handle
->handle
);
808 value
= scf_value_create(handle
->handle
);
809 prop
= scf_property_create(handle
->handle
);
810 name
= malloc(scf_max_name_len
);
811 valuestr
= malloc(vallen
);
813 if (iter
== NULL
|| value
== NULL
|| prop
== NULL
|| name
== NULL
)
816 /* iterate over the share pg properties */
817 if (scf_iter_pg_properties(iter
, pg
) == 0) {
818 while (scf_iter_next_property(iter
, prop
) > 0) {
819 ret
= SA_SYSTEM_ERR
; /* assume the worst */
820 if (scf_property_get_name(prop
, name
,
821 scf_max_name_len
) > 0) {
822 if (scf_property_get_value(prop
, value
) == 0) {
823 if (scf_value_get_astring(value
,
824 valuestr
, vallen
) >= 0) {
833 prop
= sa_create_property(name
, valuestr
);
835 prop
= (sa_property_t
)xmlAddChild(node
,
846 scf_iter_destroy(iter
);
848 scf_value_destroy(value
);
850 scf_property_destroy(prop
);
857 * sa_extract_group(root, handle, instance)
859 * Get the config info for this instance of a group and create the XML
864 sa_extract_group(xmlNodePtr root
, scfutilhandle_t
*handle
,
865 scf_instance_t
*instance
, sa_handle_t sahandle
)
872 boolean_t have_shares
= B_FALSE
;
873 boolean_t is_default
= B_FALSE
;
874 boolean_t is_nfs
= B_FALSE
;
878 buff
= malloc(scf_max_name_len
);
880 return (SA_NO_MEMORY
);
882 iter
= scf_iter_create(handle
->handle
);
888 if (scf_instance_get_name(instance
, buff
, scf_max_name_len
) > 0) {
889 node
= xmlNewChild(root
, NULL
, (xmlChar
*)"group", NULL
);
894 (void) xmlSetProp(node
, (xmlChar
*)"name", (xmlChar
*)buff
);
895 if (strcmp(buff
, "default") == 0)
898 sa_extract_attrs(node
, handle
, instance
);
900 * Iterate through all the property groups
901 * looking for those with security or
902 * optionset prefixes. The names of the
903 * matching pgroups are parsed to get the
904 * protocol, and for security, the sectype.
905 * Syntax is as follows:
906 * optionset | optionset_<proto>
907 * security_default | security_<proto>_<sectype>
908 * "operation" is handled by
909 * sa_extract_attrs().
911 if (scf_iter_instance_pgs(iter
, instance
) != 0) {
915 while (scf_iter_next_pg(iter
, handle
->pg
) > 0) {
916 /* Have a pgroup so sort it out */
917 ret
= scf_pg_get_name(handle
->pg
, buff
,
923 if (buff
[0] == SA_SHARE_PG_PREFIX
[0]) {
924 sa_share_from_pgroup(node
, handle
,
926 have_shares
= B_TRUE
;
927 } else if (strncmp(buff
, "optionset", 9) == 0) {
928 char *nodetype
= "optionset";
929 /* Have an optionset */
931 proto
= strchr(buff
, '_');
934 sectype
= strchr(proto
, '_');
935 if (sectype
!= NULL
) {
937 nodetype
= "security";
939 is_nfs
= strcmp(proto
, "nfs") == 0;
940 } else if (strlen(buff
) > 9) {
942 * This can only occur if
943 * someone has made changes
944 * via an SMF command. Since
945 * this would be an unknown
946 * syntax, we just ignore it.
951 * If the group is not "default" or is
952 * "default" and is_nfs, then extract the
953 * pgroup. If it is_default and !is_nfs,
954 * then we have an error and should remove
955 * the extraneous protocols. We don't care
956 * about errors on scf_pg_delete since we
957 * might not have permission during an
960 if (!is_default
|| is_nfs
) {
961 ret
= sa_extract_pgroup(node
, handle
,
962 handle
->pg
, nodetype
, proto
,
965 err
= scf_pg_delete(handle
->pg
);
967 (void) fprintf(stderr
,
968 dgettext(TEXT_DOMAIN
,
969 "Removed protocol \"%s\" "
970 "from group \"default\"\n"),
973 } else if (strncmp(buff
, "security", 8) == 0) {
975 * Have a security (note that
976 * this should change in the
979 proto
= strchr(buff
, '_');
983 sectype
= strchr(proto
, '_');
986 if (strcmp(proto
, "default") == 0)
989 ret
= sa_extract_pgroup(node
, handle
,
990 handle
->pg
, "security", proto
, sectype
);
992 /* Ignore everything else */
995 * Make sure we have a valid default group.
996 * On first boot, default won't have any
997 * protocols defined and won't be enabled (but
998 * should be). "default" only has NFS enabled on it.
1001 char *state
= sa_get_group_attr((sa_group_t
)node
,
1004 if (state
== NULL
) {
1005 /* set attribute to enabled */
1006 (void) sa_set_group_attr((sa_group_t
)node
,
1007 "state", "enabled");
1008 (void) sa_create_optionset((sa_group_t
)node
,
1011 sa_free_attr_string(state
);
1014 /* Do a second pass if shares were found */
1015 if (have_shares
&& scf_iter_instance_pgs(iter
, instance
) == 0) {
1016 while (scf_iter_next_pg(iter
, handle
->pg
) > 0) {
1018 * Have a pgroup so see if it is a
1021 err
= scf_pg_get_name(handle
->pg
, buff
,
1025 if (buff
[0] == SA_SHARE_PG_PREFIX
[0]) {
1026 ret
= sa_share_props_from_pgroup(node
,
1027 handle
, handle
->pg
, buff
,
1035 scf_iter_destroy(iter
);
1041 * sa_extract_defaults(root, handle, instance)
1043 * Local function to find the default properties that live in the
1044 * default instance's "operation" property group.
1048 sa_extract_defaults(xmlNodePtr root
, scfutilhandle_t
*handle
,
1049 scf_instance_t
*instance
)
1052 scf_property_t
*prop
;
1057 vallen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
1058 prop
= scf_property_create(handle
->handle
);
1059 value
= scf_value_create(handle
->handle
);
1060 valuestr
= malloc(vallen
);
1062 if (prop
== NULL
|| value
== NULL
|| vallen
== 0 ||
1063 scf_instance_get_pg(instance
, "operation", handle
->pg
) != 0)
1066 if (scf_pg_get_property(handle
->pg
, "legacy-timestamp", prop
) != 0)
1069 /* Found the property so get the value */
1070 if (scf_property_get_value(prop
, value
) == 0) {
1071 if (scf_value_get_astring(value
, valuestr
, vallen
) > 0) {
1072 node
= xmlNewChild(root
, NULL
, (xmlChar
*)"legacy",
1075 (void) xmlSetProp(node
, (xmlChar
*)"timestamp",
1076 (xmlChar
*)valuestr
);
1077 (void) xmlSetProp(node
, (xmlChar
*)"path",
1078 (xmlChar
*)SA_LEGACY_DFSTAB
);
1085 scf_value_destroy(value
);
1087 scf_property_destroy(prop
);
1092 * sa_get_config(handle, root, doc, sahandle)
1094 * Walk the SMF repository for /network/shares/group and find all the
1095 * instances. These become group names. Then add the XML structure
1096 * below the groups based on property groups and properties.
1099 sa_get_config(scfutilhandle_t
*handle
, xmlNodePtr root
, sa_handle_t sahandle
)
1102 scf_instance_t
*instance
;
1104 char buff
[BUFSIZ
* 2];
1106 instance
= scf_instance_create(handle
->handle
);
1107 iter
= scf_iter_create(handle
->handle
);
1108 if (instance
!= NULL
&& iter
!= NULL
) {
1109 if ((ret
= scf_iter_service_instances(iter
,
1110 handle
->service
)) == 0) {
1111 while ((ret
= scf_iter_next_instance(iter
,
1113 if (scf_instance_get_name(instance
, buff
,
1114 sizeof (buff
)) > 0) {
1115 if (strcmp(buff
, "default") == 0)
1116 sa_extract_defaults(root
,
1118 ret
= sa_extract_group(root
, handle
,
1119 instance
, sahandle
);
1125 /* Always cleanup these */
1126 if (instance
!= NULL
)
1127 scf_instance_destroy(instance
);
1129 scf_iter_destroy(iter
);
1134 * sa_get_instance(handle, instance)
1136 * Get the instance of the group service. This is actually the
1137 * specific group name. The instance is needed for all property and
1138 * control operations.
1142 sa_get_instance(scfutilhandle_t
*handle
, char *instname
)
1144 if (scf_service_get_instance(handle
->service
, instname
,
1145 handle
->instance
) != 0) {
1146 return (SA_NO_SUCH_GROUP
);
1152 * sa_create_instance(handle, instname)
1154 * Create a new SMF service instance. There can only be one with a
1159 sa_create_instance(scfutilhandle_t
*handle
, char *instname
)
1162 char instance
[SA_GROUP_INST_LEN
];
1163 if (scf_service_add_instance(handle
->service
, instname
,
1164 handle
->instance
) != 0) {
1165 /* better error returns need to be added based on real error */
1166 if (scf_error() == SCF_ERROR_PERMISSION_DENIED
)
1167 ret
= SA_NO_PERMISSION
;
1169 ret
= SA_DUPLICATE_NAME
;
1171 /* have the service created, so enable it */
1172 (void) snprintf(instance
, sizeof (instance
), "%s:%s",
1173 SA_SVC_FMRI_BASE
, instname
);
1174 (void) smf_enable_instance(instance
, 0);
1180 * sa_delete_instance(handle, instname)
1182 * When a group goes away, we also remove the service instance.
1186 sa_delete_instance(scfutilhandle_t
*handle
, char *instname
)
1190 if (strcmp(instname
, "default") == 0) {
1191 ret
= SA_NO_PERMISSION
;
1193 if ((ret
= sa_get_instance(handle
, instname
)) == SA_OK
) {
1194 if (scf_instance_delete(handle
->instance
) != 0)
1195 /* need better analysis */
1196 ret
= SA_NO_PERMISSION
;
1203 * sa_create_pgroup(handle, pgroup)
1205 * create a new property group
1209 sa_create_pgroup(scfutilhandle_t
*handle
, char *pgroup
)
1215 * Only create a handle if it doesn't exist. It is ok to exist
1216 * since the pg handle will be set as a side effect.
1218 if (handle
->pg
== NULL
)
1219 handle
->pg
= scf_pg_create(handle
->handle
);
1222 * Special case for a non-persistent property group. This is
1223 * internal use only.
1225 if (*pgroup
== '*') {
1226 persist
= SCF_PG_FLAG_NONPERSISTENT
;
1231 * If the pgroup exists, we are done. If it doesn't, then we
1232 * need to actually add one to the service instance.
1234 if (scf_instance_get_pg(handle
->instance
,
1235 pgroup
, handle
->pg
) != 0) {
1237 /* Doesn't exist so create one */
1238 if (scf_instance_add_pg(handle
->instance
, pgroup
,
1239 SCF_GROUP_APPLICATION
, persist
, handle
->pg
) != 0) {
1240 switch (scf_error()) {
1241 case SCF_ERROR_PERMISSION_DENIED
:
1242 ret
= SA_NO_PERMISSION
;
1245 ret
= SA_SYSTEM_ERR
;
1254 * sa_delete_pgroup(handle, pgroup)
1256 * Remove the property group from the current instance of the service,
1257 * but only if it actually exists.
1261 sa_delete_pgroup(scfutilhandle_t
*handle
, char *pgroup
)
1265 * Only delete if it does exist.
1267 if (scf_instance_get_pg(handle
->instance
, pgroup
, handle
->pg
) == 0) {
1268 /* does exist so delete it */
1269 if (scf_pg_delete(handle
->pg
) != 0)
1270 ret
= SA_SYSTEM_ERR
;
1272 ret
= SA_SYSTEM_ERR
;
1274 if (ret
== SA_SYSTEM_ERR
&&
1275 scf_error() == SCF_ERROR_PERMISSION_DENIED
) {
1276 ret
= SA_NO_PERMISSION
;
1282 * sa_start_transaction(handle, pgroup)
1284 * Start an SMF transaction so we can deal with properties. it would
1285 * be nice to not have to expose this, but we have to in order to
1288 * Basic model is to hold the transaction in the handle and allow
1289 * property adds/deletes/updates to be added then close the
1290 * transaction (or abort). There may eventually be a need to handle
1291 * other types of transaction mechanisms but we don't do that now.
1293 * An sa_start_transaction must be followed by either an
1294 * sa_end_transaction or sa_abort_transaction before another
1295 * sa_start_transaction can be done.
1299 sa_start_transaction(scfutilhandle_t
*handle
, char *propgroup
)
1303 * Lookup the property group and create it if it doesn't already
1307 return (SA_CONFIG_ERR
);
1309 if (handle
->scf_state
== SCH_STATE_INIT
) {
1310 ret
= sa_create_pgroup(handle
, propgroup
);
1312 handle
->trans
= scf_transaction_create(handle
->handle
);
1313 if (handle
->trans
!= NULL
) {
1314 if (scf_transaction_start(handle
->trans
,
1316 ret
= SA_SYSTEM_ERR
;
1319 scf_transaction_destroy(handle
->trans
);
1320 handle
->trans
= NULL
;
1323 ret
= SA_SYSTEM_ERR
;
1327 if (ret
== SA_SYSTEM_ERR
&&
1328 scf_error() == SCF_ERROR_PERMISSION_DENIED
) {
1329 ret
= SA_NO_PERMISSION
;
1336 * sa_end_transaction(scfhandle, sahandle)
1338 * Commit the changes that were added to the transaction in the
1339 * handle. Do all necessary cleanup.
1343 sa_end_transaction(scfutilhandle_t
*handle
, sa_handle_impl_t sahandle
)
1347 if (handle
== NULL
|| handle
->trans
== NULL
|| sahandle
== NULL
) {
1348 ret
= SA_SYSTEM_ERR
;
1350 if (scf_transaction_commit(handle
->trans
) < 0)
1351 ret
= SA_SYSTEM_ERR
;
1352 scf_transaction_destroy_children(handle
->trans
);
1353 scf_transaction_destroy(handle
->trans
);
1355 set_transaction_tstamp(sahandle
);
1356 handle
->trans
= NULL
;
1362 * sa_abort_transaction(handle)
1364 * Abort the changes that were added to the transaction in the
1365 * handle. Do all necessary cleanup.
1369 sa_abort_transaction(scfutilhandle_t
*handle
)
1371 if (handle
->trans
!= NULL
) {
1372 scf_transaction_reset_all(handle
->trans
);
1373 scf_transaction_destroy_children(handle
->trans
);
1374 scf_transaction_destroy(handle
->trans
);
1375 handle
->trans
= NULL
;
1380 * set_transaction_tstamp(sahandle)
1382 * After a successful transaction commit, update the timestamp of the
1383 * last transaction. This lets us detect changes from other processes.
1386 set_transaction_tstamp(sa_handle_impl_t sahandle
)
1390 scfutilhandle_t
*scfhandle
;
1392 if (sahandle
== NULL
|| sahandle
->scfhandle
== NULL
)
1395 scfhandle
= sahandle
->scfhandle
;
1397 if (sa_get_instance(scfhandle
, "default") != SA_OK
)
1400 if (gettimeofday(&tv
, NULL
) != 0)
1403 if (sa_start_transaction(scfhandle
, "*state") != SA_OK
)
1406 sahandle
->tstrans
= TSTAMP((*(timestruc_t
*)&tv
));
1407 (void) snprintf(tstring
, sizeof (tstring
), "%lld", sahandle
->tstrans
);
1408 if (sa_set_property(sahandle
->scfhandle
, "lastupdate", tstring
) ==
1411 * While best if it succeeds, a failure doesn't cause
1412 * problems and we will ignore it anyway.
1414 (void) scf_transaction_commit(scfhandle
->trans
);
1415 scf_transaction_destroy_children(scfhandle
->trans
);
1416 scf_transaction_destroy(scfhandle
->trans
);
1418 sa_abort_transaction(scfhandle
);
1423 * sa_set_property(handle, prop, value)
1425 * Set a property transaction entry into the pending SMF transaction.
1429 sa_set_property(scfutilhandle_t
*handle
, char *propname
, char *valstr
)
1433 scf_transaction_entry_t
*entry
;
1435 * Properties must be set in transactions and don't take
1436 * effect until the transaction has been ended/committed.
1438 value
= scf_value_create(handle
->handle
);
1439 entry
= scf_entry_create(handle
->handle
);
1440 if (value
!= NULL
&& entry
!= NULL
) {
1441 if (scf_transaction_property_change(handle
->trans
, entry
,
1442 propname
, SCF_TYPE_ASTRING
) == 0 ||
1443 scf_transaction_property_new(handle
->trans
, entry
,
1444 propname
, SCF_TYPE_ASTRING
) == 0) {
1445 if (scf_value_set_astring(value
, valstr
) == 0) {
1446 if (scf_entry_add_value(entry
, value
) != 0) {
1447 ret
= SA_SYSTEM_ERR
;
1448 scf_value_destroy(value
);
1450 /* The value is in the transaction */
1453 /* Value couldn't be constructed */
1454 ret
= SA_SYSTEM_ERR
;
1456 /* The entry is in the transaction */
1459 ret
= SA_SYSTEM_ERR
;
1462 ret
= SA_SYSTEM_ERR
;
1464 if (ret
== SA_SYSTEM_ERR
) {
1465 switch (scf_error()) {
1466 case SCF_ERROR_PERMISSION_DENIED
:
1467 ret
= SA_NO_PERMISSION
;
1472 * Cleanup if there were any errors that didn't leave these
1473 * values where they would be cleaned up later.
1476 scf_value_destroy(value
);
1478 scf_entry_destroy(entry
);
1483 * check_resource(share)
1485 * Check to see if share has any persistent resources. We don't want
1486 * to save if they are all transient.
1489 check_resource(sa_share_t share
)
1491 sa_resource_t resource
;
1494 for (resource
= sa_get_share_resource(share
, NULL
);
1495 resource
!= NULL
&& ret
== B_FALSE
;
1496 resource
= sa_get_next_resource(resource
)) {
1498 type
= sa_get_resource_attr(resource
, "type");
1500 if (strcmp(type
, "transient") != 0) {
1503 sa_free_attr_string(type
);
1510 * sa_set_resource_property(handle, prop, value)
1512 * set a property transaction entry into the pending SMF
1513 * transaction. We don't want to include any transient resources
1517 sa_set_resource_property(scfutilhandle_t
*handle
, sa_share_t share
)
1521 scf_transaction_entry_t
*entry
;
1522 sa_resource_t resource
;
1526 char *propstr
= NULL
;
1529 /* don't bother if no persistent resources */
1530 if (check_resource(share
) == B_FALSE
)
1534 * properties must be set in transactions and don't take
1535 * effect until the transaction has been ended/committed.
1537 entry
= scf_entry_create(handle
->handle
);
1539 return (SA_SYSTEM_ERR
);
1541 if (scf_transaction_property_change(handle
->trans
, entry
,
1542 "resource", SCF_TYPE_ASTRING
) != 0 &&
1543 scf_transaction_property_new(handle
->trans
, entry
,
1544 "resource", SCF_TYPE_ASTRING
) != 0) {
1545 scf_entry_destroy(entry
);
1546 return (SA_SYSTEM_ERR
);
1549 for (resource
= sa_get_share_resource(share
, NULL
);
1551 resource
= sa_get_next_resource(resource
)) {
1552 value
= scf_value_create(handle
->handle
);
1553 if (value
== NULL
) {
1557 /* Get size of complete string */
1558 valstr
= sa_get_resource_attr(resource
, "name");
1559 idstr
= sa_get_resource_attr(resource
, "id");
1560 description
= sa_get_resource_description(resource
);
1561 strsize
= (valstr
!= NULL
) ? strlen(valstr
) : 0;
1562 strsize
+= (idstr
!= NULL
) ? strlen(idstr
) : 0;
1563 strsize
+= (description
!= NULL
) ? strlen(description
) : 0;
1565 strsize
+= 3; /* add nul and ':' */
1566 propstr
= (char *)malloc(strsize
);
1567 if (propstr
== NULL
) {
1568 scf_value_destroy(value
);
1573 (void) snprintf(propstr
, strsize
, "%s",
1574 valstr
? valstr
: "");
1576 (void) snprintf(propstr
, strsize
, "%s:%s:%s",
1577 idstr
, valstr
? valstr
: "",
1578 description
? description
: "");
1579 if (scf_value_set_astring(value
, propstr
) != 0) {
1580 ret
= SA_SYSTEM_ERR
;
1582 scf_value_destroy(value
);
1585 if (scf_entry_add_value(entry
, value
) != 0) {
1586 ret
= SA_SYSTEM_ERR
;
1588 scf_value_destroy(value
);
1591 /* the value is in the transaction */
1596 if (valstr
!= NULL
) {
1597 sa_free_attr_string(valstr
);
1600 if (idstr
!= NULL
) {
1601 sa_free_attr_string(idstr
);
1604 if (description
!= NULL
) {
1605 sa_free_share_description(description
);
1609 /* the entry is in the transaction */
1613 sa_free_attr_string(valstr
);
1615 sa_free_attr_string(idstr
);
1616 if (description
!= NULL
)
1617 sa_free_share_description(description
);
1619 if (ret
== SA_SYSTEM_ERR
) {
1620 switch (scf_error()) {
1621 case SCF_ERROR_PERMISSION_DENIED
:
1622 ret
= SA_NO_PERMISSION
;
1627 * cleanup if there were any errors that didn't leave
1628 * these values where they would be cleaned up later.
1631 scf_entry_destroy(entry
);
1637 * sa_commit_share(handle, group, share)
1639 * Commit this share to the repository.
1640 * properties are added if they exist but can be added later.
1641 * Need to add to dfstab and sharetab, if appropriate.
1644 sa_commit_share(scfutilhandle_t
*handle
, sa_group_t group
, sa_share_t share
)
1655 * Don't commit in the zfs group. We do commit legacy
1656 * (default) and all other groups/shares. ZFS is handled
1657 * through the ZFS configuration rather than SMF.
1660 groupname
= sa_get_group_attr(group
, "name");
1661 if (groupname
!= NULL
) {
1662 if (strcmp(groupname
, "zfs") == 0) {
1664 * Adding to the ZFS group will result in the sharenfs
1665 * property being set but we don't want to do anything
1666 * SMF related at this point.
1668 sa_free_attr_string(groupname
);
1673 proplen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
1674 propstring
= malloc(proplen
);
1675 if (propstring
== NULL
)
1678 if (groupname
!= NULL
&& ret
== SA_OK
) {
1679 ret
= sa_get_instance(handle
, groupname
);
1680 sa_free_attr_string(groupname
);
1682 sharename
= sa_get_share_attr(share
, "id");
1683 if (sharename
== NULL
) {
1685 char shname
[SA_SHARE_UUID_BUFLEN
];
1686 generate_unique_sharename(shname
);
1687 (void) xmlSetProp((xmlNodePtr
)share
, (xmlChar
*)"id",
1689 sharename
= strdup(shname
);
1691 if (sharename
!= NULL
) {
1694 * Have a share name allocated so create a pgroup for
1695 * it. It may already exist, but that is OK. In order
1696 * to avoid creating a share pgroup that doesn't have
1697 * a path property, block signals around the critical
1698 * region of creating the share pgroup and props.
1700 (void) sigprocmask(SIG_BLOCK
, NULL
, &new);
1701 (void) sigaddset(&new, SIGHUP
);
1702 (void) sigaddset(&new, SIGINT
);
1703 (void) sigaddset(&new, SIGQUIT
);
1704 (void) sigaddset(&new, SIGTSTP
);
1705 (void) sigprocmask(SIG_SETMASK
, &new, &old
);
1707 ret
= sa_create_pgroup(handle
, sharename
);
1710 * Now start the transaction for the
1711 * properties that define this share. They may
1712 * exist so attempt to update before create.
1714 ret
= sa_start_transaction(handle
, sharename
);
1717 name
= sa_get_share_attr(share
, "path");
1720 * There needs to be a path
1721 * for a share to exist.
1723 ret
= sa_set_property(handle
, "path",
1725 sa_free_attr_string(name
);
1731 name
= sa_get_share_attr(share
, "drive-letter");
1733 /* A drive letter may exist for SMB */
1734 ret
= sa_set_property(handle
,
1735 "drive-letter", name
);
1736 sa_free_attr_string(name
);
1740 name
= sa_get_share_attr(share
, "exclude");
1743 * In special cases need to
1744 * exclude proto enable.
1746 ret
= sa_set_property(handle
,
1748 sa_free_attr_string(name
);
1753 * If there are resource names, bundle them up
1754 * and save appropriately.
1756 ret
= sa_set_resource_property(handle
, share
);
1760 description
= sa_get_share_description(share
);
1761 if (description
!= NULL
) {
1762 ret
= sa_set_property(handle
,
1765 sa_free_share_description(description
);
1768 /* Make sure we cleanup the transaction */
1770 sa_handle_impl_t sahandle
;
1771 sahandle
= (sa_handle_impl_t
)
1772 sa_find_group_handle(group
);
1773 if (sahandle
!= NULL
)
1774 ret
= sa_end_transaction(handle
,
1777 ret
= SA_SYSTEM_ERR
;
1779 sa_abort_transaction(handle
);
1782 (void) sigprocmask(SIG_SETMASK
, &old
, NULL
);
1787 if (ret
== SA_SYSTEM_ERR
) {
1788 int err
= scf_error();
1789 if (err
== SCF_ERROR_PERMISSION_DENIED
)
1790 ret
= SA_NO_PERMISSION
;
1793 if (groupname
!= NULL
)
1794 sa_free_attr_string(groupname
);
1800 * remove_resources(handle, share, shareid)
1802 * If the share has resources, remove all of them and their
1806 remove_resources(scfutilhandle_t
*handle
, sa_share_t share
, char *shareid
)
1808 sa_resource_t resource
;
1816 proplen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
1817 propstring
= malloc(proplen
);
1818 if (propstring
== NULL
)
1819 return (SA_NO_MEMORY
);
1821 for (resource
= sa_get_share_resource(share
, NULL
);
1822 resource
!= NULL
; resource
= sa_get_next_resource(resource
)) {
1823 id
= sa_get_resource_attr(resource
, "id");
1826 for (opt
= sa_get_optionset(resource
, NULL
);
1827 opt
!= NULL
; opt
= sa_get_next_optionset(resource
)) {
1828 proto
= sa_get_optionset_attr(opt
, "type");
1829 if (proto
!= NULL
) {
1830 (void) snprintf(propstring
, proplen
,
1831 "%s_%s_%s", shareid
, proto
, id
);
1832 ret
= sa_delete_pgroup(handle
, propstring
);
1833 sa_free_attr_string(proto
);
1836 sa_free_attr_string(id
);
1843 * sa_delete_share(handle, group, share)
1845 * Remove the specified share from the group (and service instance).
1849 sa_delete_share(scfutilhandle_t
*handle
, sa_group_t group
, sa_share_t share
)
1852 char *groupname
= NULL
;
1853 char *shareid
= NULL
;
1859 proplen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
1860 propstring
= malloc(proplen
);
1861 if (propstring
== NULL
)
1865 groupname
= sa_get_group_attr(group
, "name");
1866 shareid
= sa_get_share_attr(share
, "id");
1867 if (groupname
== NULL
|| shareid
== NULL
) {
1868 ret
= SA_CONFIG_ERR
;
1871 ret
= sa_get_instance(handle
, groupname
);
1873 /* If a share has resources, remove them */
1874 ret
= remove_resources(handle
, share
, shareid
);
1875 /* If a share has properties, remove them */
1876 ret
= sa_delete_pgroup(handle
, shareid
);
1877 for (opt
= sa_get_optionset(share
, NULL
);
1879 opt
= sa_get_next_optionset(opt
)) {
1881 proto
= sa_get_optionset_attr(opt
, "type");
1882 if (proto
!= NULL
) {
1883 (void) snprintf(propstring
,
1884 proplen
, "%s_%s", shareid
,
1886 ret
= sa_delete_pgroup(handle
,
1888 sa_free_attr_string(proto
);
1894 * If a share has security/negotiable
1895 * properties, remove them.
1897 for (sec
= sa_get_security(share
, NULL
, NULL
);
1899 sec
= sa_get_next_security(sec
)) {
1902 proto
= sa_get_security_attr(sec
, "type");
1903 sectype
= sa_get_security_attr(sec
, "sectype");
1904 if (proto
!= NULL
&& sectype
!= NULL
) {
1905 (void) snprintf(propstring
, proplen
,
1906 "%s_%s_%s", shareid
, proto
,
1908 ret
= sa_delete_pgroup(handle
,
1914 sa_free_attr_string(proto
);
1915 if (sectype
!= NULL
)
1916 sa_free_attr_string(sectype
);
1921 if (groupname
!= NULL
)
1922 sa_free_attr_string(groupname
);
1923 if (shareid
!= NULL
)
1924 sa_free_attr_string(shareid
);