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
);
321 if (valuestr
!= NULL
)
328 * sa_extract_attrs(root, handle, instance)
330 * Local function to extract the actual attributes/properties from the
331 * property group of the service instance. These are the well known
332 * attributes of "state" and "zfs". If additional attributes are
333 * added, they should be added here.
337 sa_extract_attrs(xmlNodePtr root
, scfutilhandle_t
*handle
,
338 scf_instance_t
*instance
)
340 scf_property_t
*prop
;
345 vallen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
346 prop
= scf_property_create(handle
->handle
);
347 value
= scf_value_create(handle
->handle
);
348 valuestr
= malloc(vallen
);
349 if (prop
== NULL
|| value
== NULL
|| valuestr
== NULL
||
350 scf_instance_get_pg(instance
, "operation", handle
->pg
) != 0) {
354 * Have a property group with desired name so now get
355 * the known attributes.
357 if (scf_pg_get_property(handle
->pg
, "state", prop
) == 0) {
358 /* Found the property so get the value */
359 if (scf_property_get_value(prop
, value
) == 0) {
360 if (scf_value_get_astring(value
, valuestr
,
362 (void) xmlSetProp(root
, (xmlChar
*)"state",
363 (xmlChar
*)valuestr
);
367 if (scf_pg_get_property(handle
->pg
, "zfs", prop
) == 0) {
368 /* Found the property so get the value */
369 if (scf_property_get_value(prop
, value
) == 0) {
370 if (scf_value_get_astring(value
, valuestr
,
372 (void) xmlSetProp(root
, (xmlChar
*)"zfs",
373 (xmlChar
*)valuestr
);
378 if (valuestr
!= NULL
)
381 scf_value_destroy(value
);
383 scf_property_destroy(prop
);
387 * List of known share attributes.
390 static char *share_attr
[] = {
399 is_share_attr(char *name
)
402 for (i
= 0; share_attr
[i
] != NULL
; i
++)
403 if (strcmp(name
, share_attr
[i
]) == 0)
409 * _sa_make_resource(node, valuestr)
411 * Make a resource node on the share node. The valusestr will either
412 * be old format (SMF acceptable string) or new format (pretty much an
413 * arbitrary string with "nnn:" prefixing in order to persist
414 * mapping). The input valuestr will get modified in place. This is
415 * only used in SMF repository parsing. A possible third field will be
416 * a "description" string.
420 _sa_make_resource(xmlNodePtr node
, char *valuestr
)
424 char *description
= NULL
;
427 name
= strchr(valuestr
, ':');
429 /* this is old form so give an index of "0" */
433 /* NUL the ':' and move past it */
435 /* There could also be a description string */
436 description
= strchr(name
, ':');
437 if (description
!= NULL
)
438 *description
++ = '\0';
440 node
= xmlNewChild(node
, NULL
, (xmlChar
*)"resource", NULL
);
442 (void) xmlSetProp(node
, (xmlChar
*)"name", (xmlChar
*)name
);
443 (void) xmlSetProp(node
, (xmlChar
*)"id", (xmlChar
*)idx
);
444 /* SMF values are always persistent */
445 (void) xmlSetProp(node
, (xmlChar
*)"type",
446 (xmlChar
*)"persist");
447 if (description
!= NULL
&& strlen(description
) > 0) {
448 (void) xmlNewChild(node
, NULL
, (xmlChar
*)"description",
449 (xmlChar
*)description
);
456 * sa_share_from_pgroup
458 * Extract the share definition from the share property group. We do
459 * some sanity checking to avoid bad data.
461 * Since this is only constructing the internal data structures, we
462 * don't use the sa_* functions most of the time.
465 sa_share_from_pgroup(xmlNodePtr root
, scfutilhandle_t
*handle
,
466 scf_propertygroup_t
*pg
, char *id
)
471 scf_property_t
*prop
;
479 * While preliminary check (starts with 'S') passed before
480 * getting here. Need to make sure it is in ID syntax
481 * (Snnnnnn). Note that shares with properties have similar
485 if (*id
== SA_SHARE_PG_PREFIX
[0] && vallen
== SA_SHARE_PG_LEN
) {
487 if (strncmp(id
, SA_SHARE_PG_PREFIX
,
488 SA_SHARE_PG_PREFIXLEN
) != 0 ||
489 uuid_parse(id
+ 2, uuid
) < 0)
495 vallen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
497 iter
= scf_iter_create(handle
->handle
);
498 value
= scf_value_create(handle
->handle
);
499 prop
= scf_property_create(handle
->handle
);
500 name
= malloc(scf_max_name_len
);
501 valuestr
= malloc(vallen
);
504 * Construct the share XML node. It is similar to sa_add_share
505 * but never changes the repository. Also, there won't be any
506 * ZFS or transient shares. Root will be the group it is
509 node
= xmlNewChild(root
, NULL
, (xmlChar
*)"share", NULL
);
512 * Make sure the UUID part of the property group is
513 * stored in the share "id" property. We use this
516 (void) xmlSetProp(node
, (xmlChar
*)"id", (xmlChar
*)id
);
517 (void) xmlSetProp(node
, (xmlChar
*)"type",
518 (xmlChar
*)"persist");
521 if (iter
== NULL
|| value
== NULL
|| prop
== NULL
|| name
== NULL
)
524 /* Iterate over the share pg properties */
525 if (scf_iter_pg_properties(iter
, pg
) != 0)
528 while (scf_iter_next_property(iter
, prop
) > 0) {
529 ret
= SA_SYSTEM_ERR
; /* assume the worst */
530 if (scf_property_get_name(prop
, name
, scf_max_name_len
) > 0) {
531 if (scf_property_get_value(prop
, value
) == 0) {
532 if (scf_value_get_astring(value
, valuestr
,
536 } else if (strcmp(name
, "resource") == 0) {
543 * Check that we have the "path" property in
544 * name. The string in name will always be nul
545 * terminated if scf_property_get_name()
548 if (strcmp(name
, "path") == 0)
550 if (is_share_attr(name
)) {
552 * If a share attr, then simple -
553 * usually path and id name
555 (void) xmlSetProp(node
, (xmlChar
*)name
,
556 (xmlChar
*)valuestr
);
557 } else if (strcmp(name
, "resource") == 0) {
559 * Resource names handled differently since
560 * there can be multiple on each share. The
561 * "resource" id must be preserved since this
562 * will be used by some protocols in mapping
563 * "property spaces" to names and is always
564 * used to create SMF property groups specific
565 * to resources. CIFS needs this. The first
566 * value is present so add and then loop for
567 * any additional. Since this is new and
568 * previous values may exist, handle
572 viter
= scf_iter_create(handle
->handle
);
574 scf_iter_property_values(viter
, prop
) == 0) {
575 while (scf_iter_next_value(viter
, value
) > 0) {
576 /* Have a value so process it */
577 if (scf_value_get_ustring(value
,
578 valuestr
, vallen
) >= 0) {
580 _sa_make_resource(node
,
582 } else if (scf_value_get_astring(value
,
583 valuestr
, vallen
) >= 0) {
584 /* have an astring */
585 _sa_make_resource(node
,
589 scf_iter_destroy(viter
);
592 if (strcmp(name
, "description") == 0) {
593 /* We have a description node */
595 desc
= xmlNewChild(node
, NULL
,
596 (xmlChar
*)"description", NULL
);
598 xmlNodeSetContent(desc
,
599 (xmlChar
*)valuestr
);
605 * A share without a path is broken so we want to not include
606 * these. They shouldn't happen but if you kill a sharemgr in
607 * the process of creating a share, it could happen. They
608 * should be harmless. It is also possible that another
609 * sharemgr is running and in the process of creating a share.
611 if (have_path
== 0 && node
!= NULL
) {
617 if (valuestr
!= NULL
)
620 scf_value_destroy(value
);
622 scf_iter_destroy(iter
);
624 scf_property_destroy(prop
);
628 * find_share_by_id(shareid)
630 * Search all shares in all groups until we find the share represented
635 find_share_by_id(sa_handle_t handle
, char *shareid
)
638 sa_share_t share
= NULL
;
642 for (group
= sa_get_group(handle
, NULL
);
643 group
!= NULL
&& !done
;
644 group
= sa_get_next_group(group
)) {
645 for (share
= sa_get_share(group
, NULL
);
647 share
= sa_get_next_share(share
)) {
648 id
= sa_get_share_attr(share
, "id");
649 if (id
!= NULL
&& strcmp(id
, shareid
) == 0) {
650 sa_free_attr_string(id
);
656 sa_free_attr_string(id
);
665 * find_resource_by_index(share, index)
667 * Search the resource records on the share for the id index.
670 find_resource_by_index(sa_share_t share
, char *index
)
672 sa_resource_t resource
;
673 sa_resource_t found
= NULL
;
676 for (resource
= sa_get_share_resource(share
, NULL
);
677 resource
!= NULL
&& found
== NULL
;
678 resource
= sa_get_next_resource(resource
)) {
679 id
= (char *)xmlGetProp((xmlNodePtr
)resource
, (xmlChar
*)"id");
681 if (strcmp(id
, index
) == 0) {
682 /* found it so save in "found" */
685 sa_free_attr_string(id
);
692 * sa_share_props_from_pgroup(root, handle, pg, id, sahandle)
694 * Extract share properties from the SMF property group. More sanity
695 * checks are done and the share object is created. We ignore some
696 * errors that could exist in the repository and only worry about
697 * property groups that validate in naming.
701 sa_share_props_from_pgroup(xmlNodePtr root
, scfutilhandle_t
*handle
,
702 scf_propertygroup_t
*pg
, char *id
, sa_handle_t sahandle
)
706 scf_iter_t
*iter
= NULL
;
707 scf_property_t
*prop
= NULL
;
708 scf_value_t
*value
= NULL
;
710 char *valuestr
= NULL
;
712 char *sectype
= NULL
;
718 * While preliminary check (starts with 'S') passed before
719 * getting here. Need to make sure it is in ID syntax
720 * (Snnnnnn). Note that shares with properties have similar
721 * pgroups. If the pg name is more than SA_SHARE_PG_LEN
722 * characters, it is likely one of the protocol/security
726 if (*id
!= SA_SHARE_PG_PREFIX
[0] || vallen
<= SA_SHARE_PG_LEN
) {
728 * It is ok to not have what we thought since someone might
729 * have added a name via SMF.
733 if (strncmp(id
, SA_SHARE_PG_PREFIX
, SA_SHARE_PG_PREFIXLEN
) == 0) {
734 proto
= strchr(id
, '_');
738 if (uuid_parse(id
+ SA_SHARE_PG_PREFIXLEN
, uuid
) < 0)
741 * probably a legal optionset so check a few more
742 * syntax points below.
744 if (*proto
== '\0') {
745 /* not a valid proto (null) */
749 sectype
= strchr(proto
, '_');
752 if (!valid_protocol(proto
))
757 * To get here, we have a valid protocol and possibly a
758 * security. We now have to find the share that it is really
759 * associated with. The "id" portion of the pgroup name will
763 share
= find_share_by_id(sahandle
, id
);
765 return (SA_BAD_PATH
);
767 root
= (xmlNodePtr
)share
;
769 vallen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
772 node
= xmlNewChild(root
, NULL
, (xmlChar
*)"optionset", NULL
);
774 if (isdigit((int)*sectype
)) {
775 sa_resource_t resource
;
777 * If sectype[0] is a digit, then it is an index into
778 * the resource names. We need to find a resource
779 * record and then get the properties into an
780 * optionset. The optionset becomes the "node" and the
781 * rest is hung off of the share.
783 resource
= find_resource_by_index(share
, sectype
);
784 if (resource
!= NULL
) {
785 node
= xmlNewChild(resource
, NULL
,
786 (xmlChar
*)"optionset", NULL
);
788 /* This shouldn't happen. */
794 * If not a digit, then it is a security type
795 * (alternate option space). Security types start with
798 node
= xmlNewChild(root
, NULL
, (xmlChar
*)"security",
801 (void) xmlSetProp(node
, (xmlChar
*)"sectype",
810 (void) xmlSetProp(node
, (xmlChar
*)"type", (xmlChar
*)proto
);
811 /* now find the properties */
812 iter
= scf_iter_create(handle
->handle
);
813 value
= scf_value_create(handle
->handle
);
814 prop
= scf_property_create(handle
->handle
);
815 name
= malloc(scf_max_name_len
);
816 valuestr
= malloc(vallen
);
818 if (iter
== NULL
|| value
== NULL
|| prop
== NULL
|| name
== NULL
)
821 /* iterate over the share pg properties */
822 if (scf_iter_pg_properties(iter
, pg
) == 0) {
823 while (scf_iter_next_property(iter
, prop
) > 0) {
824 ret
= SA_SYSTEM_ERR
; /* assume the worst */
825 if (scf_property_get_name(prop
, name
,
826 scf_max_name_len
) > 0) {
827 if (scf_property_get_value(prop
, value
) == 0) {
828 if (scf_value_get_astring(value
,
829 valuestr
, vallen
) >= 0) {
838 prop
= sa_create_property(name
, valuestr
);
840 prop
= (sa_property_t
)xmlAddChild(node
,
851 scf_iter_destroy(iter
);
853 scf_value_destroy(value
);
855 scf_property_destroy(prop
);
858 if (valuestr
!= NULL
)
864 * sa_extract_group(root, handle, instance)
866 * Get the config info for this instance of a group and create the XML
871 sa_extract_group(xmlNodePtr root
, scfutilhandle_t
*handle
,
872 scf_instance_t
*instance
, sa_handle_t sahandle
)
879 boolean_t have_shares
= B_FALSE
;
880 boolean_t is_default
= B_FALSE
;
881 boolean_t is_nfs
= B_FALSE
;
885 buff
= malloc(scf_max_name_len
);
887 return (SA_NO_MEMORY
);
889 iter
= scf_iter_create(handle
->handle
);
895 if (scf_instance_get_name(instance
, buff
, scf_max_name_len
) > 0) {
896 node
= xmlNewChild(root
, NULL
, (xmlChar
*)"group", NULL
);
901 (void) xmlSetProp(node
, (xmlChar
*)"name", (xmlChar
*)buff
);
902 if (strcmp(buff
, "default") == 0)
905 sa_extract_attrs(node
, handle
, instance
);
907 * Iterate through all the property groups
908 * looking for those with security or
909 * optionset prefixes. The names of the
910 * matching pgroups are parsed to get the
911 * protocol, and for security, the sectype.
912 * Syntax is as follows:
913 * optionset | optionset_<proto>
914 * security_default | security_<proto>_<sectype>
915 * "operation" is handled by
916 * sa_extract_attrs().
918 if (scf_iter_instance_pgs(iter
, instance
) != 0) {
922 while (scf_iter_next_pg(iter
, handle
->pg
) > 0) {
923 /* Have a pgroup so sort it out */
924 ret
= scf_pg_get_name(handle
->pg
, buff
,
930 if (buff
[0] == SA_SHARE_PG_PREFIX
[0]) {
931 sa_share_from_pgroup(node
, handle
,
933 have_shares
= B_TRUE
;
934 } else if (strncmp(buff
, "optionset", 9) == 0) {
935 char *nodetype
= "optionset";
936 /* Have an optionset */
938 proto
= strchr(buff
, '_');
941 sectype
= strchr(proto
, '_');
942 if (sectype
!= NULL
) {
944 nodetype
= "security";
946 is_nfs
= strcmp(proto
, "nfs") == 0;
947 } else if (strlen(buff
) > 9) {
949 * This can only occur if
950 * someone has made changes
951 * via an SMF command. Since
952 * this would be an unknown
953 * syntax, we just ignore it.
958 * If the group is not "default" or is
959 * "default" and is_nfs, then extract the
960 * pgroup. If it is_default and !is_nfs,
961 * then we have an error and should remove
962 * the extraneous protocols. We don't care
963 * about errors on scf_pg_delete since we
964 * might not have permission during an
967 if (!is_default
|| is_nfs
) {
968 ret
= sa_extract_pgroup(node
, handle
,
969 handle
->pg
, nodetype
, proto
,
972 err
= scf_pg_delete(handle
->pg
);
974 (void) fprintf(stderr
,
975 dgettext(TEXT_DOMAIN
,
976 "Removed protocol \"%s\" "
977 "from group \"default\"\n"),
980 } else if (strncmp(buff
, "security", 8) == 0) {
982 * Have a security (note that
983 * this should change in the
986 proto
= strchr(buff
, '_');
990 sectype
= strchr(proto
, '_');
993 if (strcmp(proto
, "default") == 0)
996 ret
= sa_extract_pgroup(node
, handle
,
997 handle
->pg
, "security", proto
, sectype
);
999 /* Ignore everything else */
1002 * Make sure we have a valid default group.
1003 * On first boot, default won't have any
1004 * protocols defined and won't be enabled (but
1005 * should be). "default" only has NFS enabled on it.
1008 char *state
= sa_get_group_attr((sa_group_t
)node
,
1011 if (state
== NULL
) {
1012 /* set attribute to enabled */
1013 (void) sa_set_group_attr((sa_group_t
)node
,
1014 "state", "enabled");
1015 (void) sa_create_optionset((sa_group_t
)node
,
1018 sa_free_attr_string(state
);
1021 /* Do a second pass if shares were found */
1022 if (have_shares
&& scf_iter_instance_pgs(iter
, instance
) == 0) {
1023 while (scf_iter_next_pg(iter
, handle
->pg
) > 0) {
1025 * Have a pgroup so see if it is a
1028 err
= scf_pg_get_name(handle
->pg
, buff
,
1032 if (buff
[0] == SA_SHARE_PG_PREFIX
[0]) {
1033 ret
= sa_share_props_from_pgroup(node
,
1034 handle
, handle
->pg
, buff
,
1042 scf_iter_destroy(iter
);
1049 * sa_extract_defaults(root, handle, instance)
1051 * Local function to find the default properties that live in the
1052 * default instance's "operation" property group.
1056 sa_extract_defaults(xmlNodePtr root
, scfutilhandle_t
*handle
,
1057 scf_instance_t
*instance
)
1060 scf_property_t
*prop
;
1065 vallen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
1066 prop
= scf_property_create(handle
->handle
);
1067 value
= scf_value_create(handle
->handle
);
1068 valuestr
= malloc(vallen
);
1070 if (prop
== NULL
|| value
== NULL
|| vallen
== 0 ||
1071 scf_instance_get_pg(instance
, "operation", handle
->pg
) != 0)
1074 if (scf_pg_get_property(handle
->pg
, "legacy-timestamp", prop
) != 0)
1077 /* Found the property so get the value */
1078 if (scf_property_get_value(prop
, value
) == 0) {
1079 if (scf_value_get_astring(value
, valuestr
, vallen
) > 0) {
1080 node
= xmlNewChild(root
, NULL
, (xmlChar
*)"legacy",
1083 (void) xmlSetProp(node
, (xmlChar
*)"timestamp",
1084 (xmlChar
*)valuestr
);
1085 (void) xmlSetProp(node
, (xmlChar
*)"path",
1086 (xmlChar
*)SA_LEGACY_DFSTAB
);
1091 if (valuestr
!= NULL
)
1094 scf_value_destroy(value
);
1096 scf_property_destroy(prop
);
1101 * sa_get_config(handle, root, doc, sahandle)
1103 * Walk the SMF repository for /network/shares/group and find all the
1104 * instances. These become group names. Then add the XML structure
1105 * below the groups based on property groups and properties.
1108 sa_get_config(scfutilhandle_t
*handle
, xmlNodePtr root
, sa_handle_t sahandle
)
1111 scf_instance_t
*instance
;
1113 char buff
[BUFSIZ
* 2];
1115 instance
= scf_instance_create(handle
->handle
);
1116 iter
= scf_iter_create(handle
->handle
);
1117 if (instance
!= NULL
&& iter
!= NULL
) {
1118 if ((ret
= scf_iter_service_instances(iter
,
1119 handle
->service
)) == 0) {
1120 while ((ret
= scf_iter_next_instance(iter
,
1122 if (scf_instance_get_name(instance
, buff
,
1123 sizeof (buff
)) > 0) {
1124 if (strcmp(buff
, "default") == 0)
1125 sa_extract_defaults(root
,
1127 ret
= sa_extract_group(root
, handle
,
1128 instance
, sahandle
);
1134 /* Always cleanup these */
1135 if (instance
!= NULL
)
1136 scf_instance_destroy(instance
);
1138 scf_iter_destroy(iter
);
1143 * sa_get_instance(handle, instance)
1145 * Get the instance of the group service. This is actually the
1146 * specific group name. The instance is needed for all property and
1147 * control operations.
1151 sa_get_instance(scfutilhandle_t
*handle
, char *instname
)
1153 if (scf_service_get_instance(handle
->service
, instname
,
1154 handle
->instance
) != 0) {
1155 return (SA_NO_SUCH_GROUP
);
1161 * sa_create_instance(handle, instname)
1163 * Create a new SMF service instance. There can only be one with a
1168 sa_create_instance(scfutilhandle_t
*handle
, char *instname
)
1171 char instance
[SA_GROUP_INST_LEN
];
1172 if (scf_service_add_instance(handle
->service
, instname
,
1173 handle
->instance
) != 0) {
1174 /* better error returns need to be added based on real error */
1175 if (scf_error() == SCF_ERROR_PERMISSION_DENIED
)
1176 ret
= SA_NO_PERMISSION
;
1178 ret
= SA_DUPLICATE_NAME
;
1180 /* have the service created, so enable it */
1181 (void) snprintf(instance
, sizeof (instance
), "%s:%s",
1182 SA_SVC_FMRI_BASE
, instname
);
1183 (void) smf_enable_instance(instance
, 0);
1189 * sa_delete_instance(handle, instname)
1191 * When a group goes away, we also remove the service instance.
1195 sa_delete_instance(scfutilhandle_t
*handle
, char *instname
)
1199 if (strcmp(instname
, "default") == 0) {
1200 ret
= SA_NO_PERMISSION
;
1202 if ((ret
= sa_get_instance(handle
, instname
)) == SA_OK
) {
1203 if (scf_instance_delete(handle
->instance
) != 0)
1204 /* need better analysis */
1205 ret
= SA_NO_PERMISSION
;
1212 * sa_create_pgroup(handle, pgroup)
1214 * create a new property group
1218 sa_create_pgroup(scfutilhandle_t
*handle
, char *pgroup
)
1224 * Only create a handle if it doesn't exist. It is ok to exist
1225 * since the pg handle will be set as a side effect.
1227 if (handle
->pg
== NULL
)
1228 handle
->pg
= scf_pg_create(handle
->handle
);
1231 * Special case for a non-persistent property group. This is
1232 * internal use only.
1234 if (*pgroup
== '*') {
1235 persist
= SCF_PG_FLAG_NONPERSISTENT
;
1240 * If the pgroup exists, we are done. If it doesn't, then we
1241 * need to actually add one to the service instance.
1243 if (scf_instance_get_pg(handle
->instance
,
1244 pgroup
, handle
->pg
) != 0) {
1246 /* Doesn't exist so create one */
1247 if (scf_instance_add_pg(handle
->instance
, pgroup
,
1248 SCF_GROUP_APPLICATION
, persist
, handle
->pg
) != 0) {
1249 switch (scf_error()) {
1250 case SCF_ERROR_PERMISSION_DENIED
:
1251 ret
= SA_NO_PERMISSION
;
1254 ret
= SA_SYSTEM_ERR
;
1263 * sa_delete_pgroup(handle, pgroup)
1265 * Remove the property group from the current instance of the service,
1266 * but only if it actually exists.
1270 sa_delete_pgroup(scfutilhandle_t
*handle
, char *pgroup
)
1274 * Only delete if it does exist.
1276 if (scf_instance_get_pg(handle
->instance
, pgroup
, handle
->pg
) == 0) {
1277 /* does exist so delete it */
1278 if (scf_pg_delete(handle
->pg
) != 0)
1279 ret
= SA_SYSTEM_ERR
;
1281 ret
= SA_SYSTEM_ERR
;
1283 if (ret
== SA_SYSTEM_ERR
&&
1284 scf_error() == SCF_ERROR_PERMISSION_DENIED
) {
1285 ret
= SA_NO_PERMISSION
;
1291 * sa_start_transaction(handle, pgroup)
1293 * Start an SMF transaction so we can deal with properties. it would
1294 * be nice to not have to expose this, but we have to in order to
1297 * Basic model is to hold the transaction in the handle and allow
1298 * property adds/deletes/updates to be added then close the
1299 * transaction (or abort). There may eventually be a need to handle
1300 * other types of transaction mechanisms but we don't do that now.
1302 * An sa_start_transaction must be followed by either an
1303 * sa_end_transaction or sa_abort_transaction before another
1304 * sa_start_transaction can be done.
1308 sa_start_transaction(scfutilhandle_t
*handle
, char *propgroup
)
1312 * Lookup the property group and create it if it doesn't already
1316 return (SA_CONFIG_ERR
);
1318 if (handle
->scf_state
== SCH_STATE_INIT
) {
1319 ret
= sa_create_pgroup(handle
, propgroup
);
1321 handle
->trans
= scf_transaction_create(handle
->handle
);
1322 if (handle
->trans
!= NULL
) {
1323 if (scf_transaction_start(handle
->trans
,
1325 ret
= SA_SYSTEM_ERR
;
1328 scf_transaction_destroy(handle
->trans
);
1329 handle
->trans
= NULL
;
1332 ret
= SA_SYSTEM_ERR
;
1336 if (ret
== SA_SYSTEM_ERR
&&
1337 scf_error() == SCF_ERROR_PERMISSION_DENIED
) {
1338 ret
= SA_NO_PERMISSION
;
1345 * sa_end_transaction(scfhandle, sahandle)
1347 * Commit the changes that were added to the transaction in the
1348 * handle. Do all necessary cleanup.
1352 sa_end_transaction(scfutilhandle_t
*handle
, sa_handle_impl_t sahandle
)
1356 if (handle
== NULL
|| handle
->trans
== NULL
|| sahandle
== NULL
) {
1357 ret
= SA_SYSTEM_ERR
;
1359 if (scf_transaction_commit(handle
->trans
) < 0)
1360 ret
= SA_SYSTEM_ERR
;
1361 scf_transaction_destroy_children(handle
->trans
);
1362 scf_transaction_destroy(handle
->trans
);
1364 set_transaction_tstamp(sahandle
);
1365 handle
->trans
= NULL
;
1371 * sa_abort_transaction(handle)
1373 * Abort the changes that were added to the transaction in the
1374 * handle. Do all necessary cleanup.
1378 sa_abort_transaction(scfutilhandle_t
*handle
)
1380 if (handle
->trans
!= NULL
) {
1381 scf_transaction_reset_all(handle
->trans
);
1382 scf_transaction_destroy_children(handle
->trans
);
1383 scf_transaction_destroy(handle
->trans
);
1384 handle
->trans
= NULL
;
1389 * set_transaction_tstamp(sahandle)
1391 * After a successful transaction commit, update the timestamp of the
1392 * last transaction. This lets us detect changes from other processes.
1395 set_transaction_tstamp(sa_handle_impl_t sahandle
)
1399 scfutilhandle_t
*scfhandle
;
1401 if (sahandle
== NULL
|| sahandle
->scfhandle
== NULL
)
1404 scfhandle
= sahandle
->scfhandle
;
1406 if (sa_get_instance(scfhandle
, "default") != SA_OK
)
1409 if (gettimeofday(&tv
, NULL
) != 0)
1412 if (sa_start_transaction(scfhandle
, "*state") != SA_OK
)
1415 sahandle
->tstrans
= TSTAMP((*(timestruc_t
*)&tv
));
1416 (void) snprintf(tstring
, sizeof (tstring
), "%lld", sahandle
->tstrans
);
1417 if (sa_set_property(sahandle
->scfhandle
, "lastupdate", tstring
) ==
1420 * While best if it succeeds, a failure doesn't cause
1421 * problems and we will ignore it anyway.
1423 (void) scf_transaction_commit(scfhandle
->trans
);
1424 scf_transaction_destroy_children(scfhandle
->trans
);
1425 scf_transaction_destroy(scfhandle
->trans
);
1427 sa_abort_transaction(scfhandle
);
1432 * sa_set_property(handle, prop, value)
1434 * Set a property transaction entry into the pending SMF transaction.
1438 sa_set_property(scfutilhandle_t
*handle
, char *propname
, char *valstr
)
1442 scf_transaction_entry_t
*entry
;
1444 * Properties must be set in transactions and don't take
1445 * effect until the transaction has been ended/committed.
1447 value
= scf_value_create(handle
->handle
);
1448 entry
= scf_entry_create(handle
->handle
);
1449 if (value
!= NULL
&& entry
!= NULL
) {
1450 if (scf_transaction_property_change(handle
->trans
, entry
,
1451 propname
, SCF_TYPE_ASTRING
) == 0 ||
1452 scf_transaction_property_new(handle
->trans
, entry
,
1453 propname
, SCF_TYPE_ASTRING
) == 0) {
1454 if (scf_value_set_astring(value
, valstr
) == 0) {
1455 if (scf_entry_add_value(entry
, value
) != 0) {
1456 ret
= SA_SYSTEM_ERR
;
1457 scf_value_destroy(value
);
1459 /* The value is in the transaction */
1462 /* Value couldn't be constructed */
1463 ret
= SA_SYSTEM_ERR
;
1465 /* The entry is in the transaction */
1468 ret
= SA_SYSTEM_ERR
;
1471 ret
= SA_SYSTEM_ERR
;
1473 if (ret
== SA_SYSTEM_ERR
) {
1474 switch (scf_error()) {
1475 case SCF_ERROR_PERMISSION_DENIED
:
1476 ret
= SA_NO_PERMISSION
;
1481 * Cleanup if there were any errors that didn't leave these
1482 * values where they would be cleaned up later.
1485 scf_value_destroy(value
);
1487 scf_entry_destroy(entry
);
1492 * check_resource(share)
1494 * Check to see if share has any persistent resources. We don't want
1495 * to save if they are all transient.
1498 check_resource(sa_share_t share
)
1500 sa_resource_t resource
;
1503 for (resource
= sa_get_share_resource(share
, NULL
);
1504 resource
!= NULL
&& ret
== B_FALSE
;
1505 resource
= sa_get_next_resource(resource
)) {
1507 type
= sa_get_resource_attr(resource
, "type");
1509 if (strcmp(type
, "transient") != 0) {
1512 sa_free_attr_string(type
);
1519 * sa_set_resource_property(handle, prop, value)
1521 * set a property transaction entry into the pending SMF
1522 * transaction. We don't want to include any transient resources
1526 sa_set_resource_property(scfutilhandle_t
*handle
, sa_share_t share
)
1530 scf_transaction_entry_t
*entry
;
1531 sa_resource_t resource
;
1535 char *propstr
= NULL
;
1538 /* don't bother if no persistent resources */
1539 if (check_resource(share
) == B_FALSE
)
1543 * properties must be set in transactions and don't take
1544 * effect until the transaction has been ended/committed.
1546 entry
= scf_entry_create(handle
->handle
);
1548 return (SA_SYSTEM_ERR
);
1550 if (scf_transaction_property_change(handle
->trans
, entry
,
1551 "resource", SCF_TYPE_ASTRING
) != 0 &&
1552 scf_transaction_property_new(handle
->trans
, entry
,
1553 "resource", SCF_TYPE_ASTRING
) != 0) {
1554 scf_entry_destroy(entry
);
1555 return (SA_SYSTEM_ERR
);
1558 for (resource
= sa_get_share_resource(share
, NULL
);
1560 resource
= sa_get_next_resource(resource
)) {
1561 value
= scf_value_create(handle
->handle
);
1562 if (value
== NULL
) {
1566 /* Get size of complete string */
1567 valstr
= sa_get_resource_attr(resource
, "name");
1568 idstr
= sa_get_resource_attr(resource
, "id");
1569 description
= sa_get_resource_description(resource
);
1570 strsize
= (valstr
!= NULL
) ? strlen(valstr
) : 0;
1571 strsize
+= (idstr
!= NULL
) ? strlen(idstr
) : 0;
1572 strsize
+= (description
!= NULL
) ? strlen(description
) : 0;
1574 strsize
+= 3; /* add nul and ':' */
1575 propstr
= (char *)malloc(strsize
);
1576 if (propstr
== NULL
) {
1577 scf_value_destroy(value
);
1582 (void) snprintf(propstr
, strsize
, "%s",
1583 valstr
? valstr
: "");
1585 (void) snprintf(propstr
, strsize
, "%s:%s:%s",
1586 idstr
, valstr
? valstr
: "",
1587 description
? description
: "");
1588 if (scf_value_set_astring(value
, propstr
) != 0) {
1589 ret
= SA_SYSTEM_ERR
;
1591 scf_value_destroy(value
);
1594 if (scf_entry_add_value(entry
, value
) != 0) {
1595 ret
= SA_SYSTEM_ERR
;
1597 scf_value_destroy(value
);
1600 /* the value is in the transaction */
1605 if (valstr
!= NULL
) {
1606 sa_free_attr_string(valstr
);
1609 if (idstr
!= NULL
) {
1610 sa_free_attr_string(idstr
);
1613 if (description
!= NULL
) {
1614 sa_free_share_description(description
);
1618 /* the entry is in the transaction */
1622 sa_free_attr_string(valstr
);
1624 sa_free_attr_string(idstr
);
1625 if (description
!= NULL
)
1626 sa_free_share_description(description
);
1628 if (ret
== SA_SYSTEM_ERR
) {
1629 switch (scf_error()) {
1630 case SCF_ERROR_PERMISSION_DENIED
:
1631 ret
= SA_NO_PERMISSION
;
1636 * cleanup if there were any errors that didn't leave
1637 * these values where they would be cleaned up later.
1640 scf_entry_destroy(entry
);
1646 * sa_commit_share(handle, group, share)
1648 * Commit this share to the repository.
1649 * properties are added if they exist but can be added later.
1650 * Need to add to dfstab and sharetab, if appropriate.
1653 sa_commit_share(scfutilhandle_t
*handle
, sa_group_t group
, sa_share_t share
)
1664 * Don't commit in the zfs group. We do commit legacy
1665 * (default) and all other groups/shares. ZFS is handled
1666 * through the ZFS configuration rather than SMF.
1669 groupname
= sa_get_group_attr(group
, "name");
1670 if (groupname
!= NULL
) {
1671 if (strcmp(groupname
, "zfs") == 0) {
1673 * Adding to the ZFS group will result in the sharenfs
1674 * property being set but we don't want to do anything
1675 * SMF related at this point.
1677 sa_free_attr_string(groupname
);
1682 proplen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
1683 propstring
= malloc(proplen
);
1684 if (propstring
== NULL
)
1687 if (groupname
!= NULL
&& ret
== SA_OK
) {
1688 ret
= sa_get_instance(handle
, groupname
);
1689 sa_free_attr_string(groupname
);
1691 sharename
= sa_get_share_attr(share
, "id");
1692 if (sharename
== NULL
) {
1694 char shname
[SA_SHARE_UUID_BUFLEN
];
1695 generate_unique_sharename(shname
);
1696 (void) xmlSetProp((xmlNodePtr
)share
, (xmlChar
*)"id",
1698 sharename
= strdup(shname
);
1700 if (sharename
!= NULL
) {
1703 * Have a share name allocated so create a pgroup for
1704 * it. It may already exist, but that is OK. In order
1705 * to avoid creating a share pgroup that doesn't have
1706 * a path property, block signals around the critical
1707 * region of creating the share pgroup and props.
1709 (void) sigprocmask(SIG_BLOCK
, NULL
, &new);
1710 (void) sigaddset(&new, SIGHUP
);
1711 (void) sigaddset(&new, SIGINT
);
1712 (void) sigaddset(&new, SIGQUIT
);
1713 (void) sigaddset(&new, SIGTSTP
);
1714 (void) sigprocmask(SIG_SETMASK
, &new, &old
);
1716 ret
= sa_create_pgroup(handle
, sharename
);
1719 * Now start the transaction for the
1720 * properties that define this share. They may
1721 * exist so attempt to update before create.
1723 ret
= sa_start_transaction(handle
, sharename
);
1726 name
= sa_get_share_attr(share
, "path");
1729 * There needs to be a path
1730 * for a share to exist.
1732 ret
= sa_set_property(handle
, "path",
1734 sa_free_attr_string(name
);
1740 name
= sa_get_share_attr(share
, "drive-letter");
1742 /* A drive letter may exist for SMB */
1743 ret
= sa_set_property(handle
,
1744 "drive-letter", name
);
1745 sa_free_attr_string(name
);
1749 name
= sa_get_share_attr(share
, "exclude");
1752 * In special cases need to
1753 * exclude proto enable.
1755 ret
= sa_set_property(handle
,
1757 sa_free_attr_string(name
);
1762 * If there are resource names, bundle them up
1763 * and save appropriately.
1765 ret
= sa_set_resource_property(handle
, share
);
1769 description
= sa_get_share_description(share
);
1770 if (description
!= NULL
) {
1771 ret
= sa_set_property(handle
,
1774 sa_free_share_description(description
);
1777 /* Make sure we cleanup the transaction */
1779 sa_handle_impl_t sahandle
;
1780 sahandle
= (sa_handle_impl_t
)
1781 sa_find_group_handle(group
);
1782 if (sahandle
!= NULL
)
1783 ret
= sa_end_transaction(handle
,
1786 ret
= SA_SYSTEM_ERR
;
1788 sa_abort_transaction(handle
);
1791 (void) sigprocmask(SIG_SETMASK
, &old
, NULL
);
1796 if (ret
== SA_SYSTEM_ERR
) {
1797 int err
= scf_error();
1798 if (err
== SCF_ERROR_PERMISSION_DENIED
)
1799 ret
= SA_NO_PERMISSION
;
1801 if (propstring
!= NULL
)
1803 if (groupname
!= NULL
)
1804 sa_free_attr_string(groupname
);
1810 * remove_resources(handle, share, shareid)
1812 * If the share has resources, remove all of them and their
1816 remove_resources(scfutilhandle_t
*handle
, sa_share_t share
, char *shareid
)
1818 sa_resource_t resource
;
1826 proplen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
1827 propstring
= malloc(proplen
);
1828 if (propstring
== NULL
)
1829 return (SA_NO_MEMORY
);
1831 for (resource
= sa_get_share_resource(share
, NULL
);
1832 resource
!= NULL
; resource
= sa_get_next_resource(resource
)) {
1833 id
= sa_get_resource_attr(resource
, "id");
1836 for (opt
= sa_get_optionset(resource
, NULL
);
1837 opt
!= NULL
; opt
= sa_get_next_optionset(resource
)) {
1838 proto
= sa_get_optionset_attr(opt
, "type");
1839 if (proto
!= NULL
) {
1840 (void) snprintf(propstring
, proplen
,
1841 "%s_%s_%s", shareid
, proto
, id
);
1842 ret
= sa_delete_pgroup(handle
, propstring
);
1843 sa_free_attr_string(proto
);
1846 sa_free_attr_string(id
);
1853 * sa_delete_share(handle, group, share)
1855 * Remove the specified share from the group (and service instance).
1859 sa_delete_share(scfutilhandle_t
*handle
, sa_group_t group
, sa_share_t share
)
1862 char *groupname
= NULL
;
1863 char *shareid
= NULL
;
1869 proplen
= get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
1870 propstring
= malloc(proplen
);
1871 if (propstring
== NULL
)
1875 groupname
= sa_get_group_attr(group
, "name");
1876 shareid
= sa_get_share_attr(share
, "id");
1877 if (groupname
== NULL
|| shareid
== NULL
) {
1878 ret
= SA_CONFIG_ERR
;
1881 ret
= sa_get_instance(handle
, groupname
);
1883 /* If a share has resources, remove them */
1884 ret
= remove_resources(handle
, share
, shareid
);
1885 /* If a share has properties, remove them */
1886 ret
= sa_delete_pgroup(handle
, shareid
);
1887 for (opt
= sa_get_optionset(share
, NULL
);
1889 opt
= sa_get_next_optionset(opt
)) {
1891 proto
= sa_get_optionset_attr(opt
, "type");
1892 if (proto
!= NULL
) {
1893 (void) snprintf(propstring
,
1894 proplen
, "%s_%s", shareid
,
1896 ret
= sa_delete_pgroup(handle
,
1898 sa_free_attr_string(proto
);
1904 * If a share has security/negotiable
1905 * properties, remove them.
1907 for (sec
= sa_get_security(share
, NULL
, NULL
);
1909 sec
= sa_get_next_security(sec
)) {
1912 proto
= sa_get_security_attr(sec
, "type");
1913 sectype
= sa_get_security_attr(sec
, "sectype");
1914 if (proto
!= NULL
&& sectype
!= NULL
) {
1915 (void) snprintf(propstring
, proplen
,
1916 "%s_%s_%s", shareid
, proto
,
1918 ret
= sa_delete_pgroup(handle
,
1924 sa_free_attr_string(proto
);
1925 if (sectype
!= NULL
)
1926 sa_free_attr_string(sectype
);
1931 if (groupname
!= NULL
)
1932 sa_free_attr_string(groupname
);
1933 if (shareid
!= NULL
)
1934 sa_free_attr_string(shareid
);
1935 if (propstring
!= NULL
)