dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libshare / common / libshare.c
blob7623daecd0236e36bb33ea68e0c87eb68731a5ae
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
29 * Share control API
31 #include <stdio.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <libxml/parser.h>
39 #include <libxml/tree.h>
40 #include "libshare.h"
41 #include "libshare_impl.h"
42 #include <libscf.h>
43 #include "scfutil.h"
44 #include <ctype.h>
45 #include <libintl.h>
46 #include <thread.h>
47 #include <synch.h>
48 #include <errno.h>
50 #define DFS_LOCK_FILE "/etc/dfs/fstypes"
51 #define SA_STRSIZE 256 /* max string size for names */
54 * internal object type values returned by sa_get_object_type()
56 #define SA_TYPE_UNKNOWN 0
57 #define SA_TYPE_GROUP 1
58 #define SA_TYPE_SHARE 2
59 #define SA_TYPE_RESOURCE 3
60 #define SA_TYPE_OPTIONSET 4
61 #define SA_TYPE_ALTSPACE 5
64 * internal data structures
67 extern struct sa_proto_plugin *sap_proto_list;
69 /* current SMF/SVC repository handle */
70 extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
71 extern int gettransients(sa_handle_impl_t, xmlNodePtr *);
72 extern int get_one_transient(sa_handle_impl_t, xmlNodePtr *, char **, size_t);
73 extern char *sa_fstype(char *);
74 extern int sa_is_share(void *);
75 extern int sa_is_resource(void *);
76 extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
77 extern int sa_group_is_zfs(sa_group_t);
78 extern int sa_path_is_zfs(char *);
79 extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
80 extern int sa_zfs_set_sharesmb(sa_group_t, char *, int);
81 extern void update_legacy_config(sa_handle_t);
82 extern int issubdir(char *, char *);
83 extern int sa_zfs_init(sa_handle_impl_t);
84 extern void sa_zfs_fini(sa_handle_impl_t);
85 extern void sablocksigs(sigset_t *);
86 extern void saunblocksigs(sigset_t *);
87 static sa_group_t sa_get_optionset_parent(sa_optionset_t);
88 static char *get_node_attr(void *, char *);
89 extern void sa_update_sharetab_ts(sa_handle_t);
92 * Data structures for finding/managing the document root to access
93 * handle mapping. The list isn't expected to grow very large so a
94 * simple list is acceptable. The purpose is to provide a way to start
95 * with a group or share and find the library handle needed for
96 * various operations.
98 mutex_t sa_global_lock;
99 struct doc2handle {
100 struct doc2handle *next;
101 xmlNodePtr root;
102 sa_handle_impl_t handle;
105 mutex_t sa_dfstab_lock;
107 /* definitions used in a couple of property functions */
108 #define SA_PROP_OP_REMOVE 1
109 #define SA_PROP_OP_ADD 2
110 #define SA_PROP_OP_UPDATE 3
112 static struct doc2handle *sa_global_handles = NULL;
114 /* helper functions */
117 * sa_errorstr(err)
119 * convert an error value to an error string
122 char *
123 sa_errorstr(int err)
125 static char errstr[32];
126 char *ret = NULL;
128 switch (err) {
129 case SA_OK:
130 ret = dgettext(TEXT_DOMAIN, "ok");
131 break;
132 case SA_NO_SUCH_PATH:
133 ret = dgettext(TEXT_DOMAIN, "path doesn't exist");
134 break;
135 case SA_NO_MEMORY:
136 ret = dgettext(TEXT_DOMAIN, "no memory");
137 break;
138 case SA_DUPLICATE_NAME:
139 ret = dgettext(TEXT_DOMAIN, "name in use");
140 break;
141 case SA_BAD_PATH:
142 ret = dgettext(TEXT_DOMAIN, "bad path");
143 break;
144 case SA_NO_SUCH_GROUP:
145 ret = dgettext(TEXT_DOMAIN, "no such group");
146 break;
147 case SA_CONFIG_ERR:
148 ret = dgettext(TEXT_DOMAIN, "configuration error");
149 break;
150 case SA_SYSTEM_ERR:
151 ret = dgettext(TEXT_DOMAIN, "system error");
152 break;
153 case SA_SYNTAX_ERR:
154 ret = dgettext(TEXT_DOMAIN, "syntax error");
155 break;
156 case SA_NO_PERMISSION:
157 ret = dgettext(TEXT_DOMAIN, "no permission");
158 break;
159 case SA_BUSY:
160 ret = dgettext(TEXT_DOMAIN, "busy");
161 break;
162 case SA_NO_SUCH_PROP:
163 ret = dgettext(TEXT_DOMAIN, "no such property");
164 break;
165 case SA_INVALID_NAME:
166 ret = dgettext(TEXT_DOMAIN, "invalid name");
167 break;
168 case SA_INVALID_PROTOCOL:
169 ret = dgettext(TEXT_DOMAIN, "invalid protocol");
170 break;
171 case SA_NOT_ALLOWED:
172 ret = dgettext(TEXT_DOMAIN, "operation not allowed");
173 break;
174 case SA_BAD_VALUE:
175 ret = dgettext(TEXT_DOMAIN, "bad property value");
176 break;
177 case SA_INVALID_SECURITY:
178 ret = dgettext(TEXT_DOMAIN, "invalid security type");
179 break;
180 case SA_NO_SUCH_SECURITY:
181 ret = dgettext(TEXT_DOMAIN, "security type not found");
182 break;
183 case SA_VALUE_CONFLICT:
184 ret = dgettext(TEXT_DOMAIN, "property value conflict");
185 break;
186 case SA_NOT_IMPLEMENTED:
187 ret = dgettext(TEXT_DOMAIN, "not implemented");
188 break;
189 case SA_INVALID_PATH:
190 ret = dgettext(TEXT_DOMAIN, "invalid path");
191 break;
192 case SA_NOT_SUPPORTED:
193 ret = dgettext(TEXT_DOMAIN, "operation not supported");
194 break;
195 case SA_PROP_SHARE_ONLY:
196 ret = dgettext(TEXT_DOMAIN, "property not valid for group");
197 break;
198 case SA_NOT_SHARED:
199 ret = dgettext(TEXT_DOMAIN, "not shared");
200 break;
201 case SA_NO_SUCH_RESOURCE:
202 ret = dgettext(TEXT_DOMAIN, "no such resource");
203 break;
204 case SA_RESOURCE_REQUIRED:
205 ret = dgettext(TEXT_DOMAIN, "resource name required");
206 break;
207 case SA_MULTIPLE_ERROR:
208 ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols");
209 break;
210 case SA_PATH_IS_SUBDIR:
211 ret = dgettext(TEXT_DOMAIN, "path is a subpath of share");
212 break;
213 case SA_PATH_IS_PARENTDIR:
214 ret = dgettext(TEXT_DOMAIN, "path is parent of a share");
215 break;
216 case SA_NO_SECTION:
217 ret = dgettext(TEXT_DOMAIN, "protocol requires a section");
218 break;
219 case SA_NO_PROPERTIES:
220 ret = dgettext(TEXT_DOMAIN, "properties not found");
221 break;
222 case SA_NO_SUCH_SECTION:
223 ret = dgettext(TEXT_DOMAIN, "section not found");
224 break;
225 case SA_PASSWORD_ENC:
226 ret = dgettext(TEXT_DOMAIN, "passwords must be encrypted");
227 break;
228 case SA_SHARE_EXISTS:
229 ret = dgettext(TEXT_DOMAIN, "path or file is already shared");
230 break;
231 default:
232 (void) snprintf(errstr, sizeof (errstr),
233 dgettext(TEXT_DOMAIN, "unknown %d"), err);
234 ret = errstr;
236 return (ret);
240 * Document root to active handle mapping functions. These are only
241 * used internally. A mutex is used to prevent access while the list
242 * is changing. In general, the list will be relatively short - one
243 * item per thread that has called sa_init().
246 sa_handle_impl_t
247 get_handle_for_root(xmlNodePtr root)
249 struct doc2handle *item;
251 (void) mutex_lock(&sa_global_lock);
252 for (item = sa_global_handles; item != NULL; item = item->next) {
253 if (item->root == root)
254 break;
256 (void) mutex_unlock(&sa_global_lock);
257 if (item != NULL)
258 return (item->handle);
259 return (NULL);
262 static int
263 add_handle_for_root(xmlNodePtr root, sa_handle_impl_t handle)
265 struct doc2handle *item;
266 int ret = SA_NO_MEMORY;
268 item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1);
269 if (item != NULL) {
270 item->root = root;
271 item->handle = handle;
272 (void) mutex_lock(&sa_global_lock);
273 item->next = sa_global_handles;
274 sa_global_handles = item;
275 (void) mutex_unlock(&sa_global_lock);
276 ret = SA_OK;
278 return (ret);
282 * remove_handle_for_root(root)
284 * Walks the list of handles and removes the one for this "root" from
285 * the list. It is up to the caller to free the data.
288 static void
289 remove_handle_for_root(xmlNodePtr root)
291 struct doc2handle *item, *prev;
293 (void) mutex_lock(&sa_global_lock);
294 for (prev = NULL, item = sa_global_handles; item != NULL;
295 item = item->next) {
296 if (item->root == root) {
297 /* first in the list */
298 if (prev == NULL)
299 sa_global_handles = sa_global_handles->next;
300 else
301 prev->next = item->next;
302 /* Item is out of the list so free the list structure */
303 free(item);
304 break;
306 prev = item;
308 (void) mutex_unlock(&sa_global_lock);
312 * sa_find_group_handle(sa_group_t group)
314 * Find the sa_handle_t for the configuration associated with this
315 * group.
317 sa_handle_t
318 sa_find_group_handle(sa_group_t group)
320 xmlNodePtr node = (xmlNodePtr)group;
321 sa_handle_t handle;
323 while (node != NULL) {
324 if (strcmp((char *)(node->name), "sharecfg") == 0) {
325 /* have the root so get the handle */
326 handle = (sa_handle_t)get_handle_for_root(node);
327 return (handle);
329 node = node->parent;
331 return (NULL);
335 * set_legacy_timestamp(root, path, timevalue)
337 * add the current timestamp value to the configuration for use in
338 * determining when to update the legacy files. For SMF, this
339 * property is kept in default/operation/legacy_timestamp
342 static void
343 set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval)
345 xmlNodePtr node;
346 xmlChar *lpath = NULL;
347 sa_handle_impl_t handle;
349 /* Have to have a handle or else we weren't initialized. */
350 handle = get_handle_for_root(root);
351 if (handle == NULL)
352 return;
354 for (node = root->xmlChildrenNode; node != NULL;
355 node = node->next) {
356 if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
357 /* a possible legacy node for this path */
358 lpath = xmlGetProp(node, (xmlChar *)"path");
359 if (lpath != NULL &&
360 xmlStrcmp(lpath, (xmlChar *)path) == 0) {
361 xmlFree(lpath);
362 break;
364 if (lpath != NULL)
365 xmlFree(lpath);
368 if (node == NULL) {
369 /* need to create the first legacy timestamp node */
370 node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL);
372 if (node != NULL) {
373 char tstring[32];
374 int ret;
376 (void) snprintf(tstring, sizeof (tstring), "%lld", tval);
377 (void) xmlSetProp(node, (xmlChar *)"timestamp",
378 (xmlChar *)tstring);
379 (void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path);
380 /* now commit to SMF */
381 ret = sa_get_instance(handle->scfhandle, "default");
382 if (ret == SA_OK) {
383 ret = sa_start_transaction(handle->scfhandle,
384 "operation");
385 if (ret == SA_OK) {
386 ret = sa_set_property(handle->scfhandle,
387 "legacy-timestamp", tstring);
388 if (ret == SA_OK) {
389 (void) sa_end_transaction(
390 handle->scfhandle, handle);
391 } else {
392 sa_abort_transaction(handle->scfhandle);
400 * is_shared(share)
402 * determine if the specified share is currently shared or not.
404 static int
405 is_shared(sa_share_t share)
407 char *shared;
408 int result = 0; /* assume not */
410 shared = sa_get_share_attr(share, "shared");
411 if (shared != NULL) {
412 if (strcmp(shared, "true") == 0)
413 result = 1;
414 sa_free_attr_string(shared);
416 return (result);
420 * excluded_protocol(share, proto)
422 * Returns B_TRUE if the specified protocol appears in the "exclude"
423 * property. This is used to prevent sharing special case shares
424 * (e.g. subdirs when SMB wants a subdir and NFS doesn't. B_FALSE is
425 * returned if the protocol isn't in the list.
427 static boolean_t
428 excluded_protocol(sa_share_t share, char *proto)
430 char *protolist;
431 char *str;
432 char *token;
434 protolist = sa_get_share_attr(share, "exclude");
435 if (protolist != NULL) {
436 str = protolist;
437 while ((token = strtok(str, ",")) != NULL) {
438 if (strcmp(token, proto) == 0) {
439 sa_free_attr_string(protolist);
440 return (B_TRUE);
442 str = NULL;
444 sa_free_attr_string(protolist);
446 return (B_FALSE);
450 * checksubdirgroup(group, newpath, strictness)
452 * check all the specified newpath against all the paths in the
453 * group. This is a helper function for checksubdir to make it easier
454 * to also check ZFS subgroups.
455 * The strictness values mean:
456 * SA_CHECK_NORMAL == only check newpath against shares that are active
457 * SA_CHECK_STRICT == check newpath against both active shares and those
458 * stored in the repository
460 static int
461 checksubdirgroup(sa_group_t group, char *newpath, int strictness)
463 sa_share_t share;
464 char *path;
465 int issub = SA_OK;
466 int subdir;
467 int parent;
469 if (newpath == NULL)
470 return (SA_INVALID_PATH);
472 for (share = sa_get_share(group, NULL); share != NULL;
473 share = sa_get_next_share(share)) {
475 * The original behavior of share never checked
476 * against the permanent configuration
477 * (/etc/dfs/dfstab). PIT has a number of cases where
478 * it depends on this older behavior even though it
479 * could be considered incorrect. We may tighten this
480 * up in the future.
482 if (strictness == SA_CHECK_NORMAL && !is_shared(share))
483 continue;
485 path = sa_get_share_attr(share, "path");
487 * If path is NULL, then a share is in the process of
488 * construction or someone has modified the property
489 * group inappropriately. It should be
490 * ignored. issubdir() comes from the original share
491 * implementation and does the difficult part of
492 * checking subdirectories.
494 if (path == NULL)
495 continue;
497 if (strcmp(path, newpath) == 0) {
498 issub = SA_INVALID_PATH;
499 } else {
500 subdir = issubdir(newpath, path);
501 parent = issubdir(path, newpath);
502 if (subdir || parent) {
503 sa_free_attr_string(path);
504 path = NULL;
505 return (subdir ?
506 SA_PATH_IS_SUBDIR : SA_PATH_IS_PARENTDIR);
509 sa_free_attr_string(path);
510 path = NULL;
512 return (issub);
516 * checksubdir(newpath, strictness)
518 * checksubdir determines if the specified path (newpath) is a
519 * subdirectory of another share. It calls checksubdirgroup() to do
520 * the complicated work. The strictness parameter determines how
521 * strict a check to make against the path. The strictness values
522 * mean: SA_CHECK_NORMAL == only check newpath against shares that are
523 * active SA_CHECK_STRICT == check newpath against both active shares
524 * and those * stored in the repository
526 static int
527 checksubdir(sa_handle_t handle, char *newpath, int strictness)
529 sa_group_t group;
530 int issub = SA_OK;
531 char *path = NULL;
533 for (group = sa_get_group(handle, NULL);
534 group != NULL && issub == SA_OK;
535 group = sa_get_next_group(group)) {
536 if (sa_group_is_zfs(group)) {
537 sa_group_t subgroup;
538 for (subgroup = sa_get_sub_group(group);
539 subgroup != NULL && issub == SA_OK;
540 subgroup = sa_get_next_group(subgroup))
541 issub = checksubdirgroup(subgroup, newpath,
542 strictness);
543 } else {
544 issub = checksubdirgroup(group, newpath, strictness);
547 if (path != NULL)
548 sa_free_attr_string(path);
549 return (issub);
553 * validpath(path, strictness)
554 * determine if the provided path is valid for a share. It shouldn't
555 * be a sub-dir of an already shared path or the parent directory of a
556 * share path.
558 static int
559 validpath(sa_handle_t handle, char *path, int strictness)
561 int error = SA_OK;
562 struct stat st;
563 sa_share_t share;
564 char *fstype;
566 if (*path != '/')
567 return (SA_BAD_PATH);
569 if (stat(path, &st) < 0) {
570 error = SA_NO_SUCH_PATH;
571 } else {
572 share = sa_find_share(handle, path);
573 if (share != NULL)
574 error = SA_DUPLICATE_NAME;
576 if (error == SA_OK) {
578 * check for special case with file system
579 * that might have restrictions. For now, ZFS
580 * is the only case since it has its own idea
581 * of how to configure shares. We do this
582 * before subdir checking since things like
583 * ZFS will do that for us. This should also
584 * be done via plugin interface.
586 fstype = sa_fstype(path);
587 if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
588 if (sa_zfs_is_shared(handle, path))
589 error = SA_INVALID_NAME;
591 if (fstype != NULL)
592 sa_free_fstype(fstype);
594 if (error == SA_OK)
595 error = checksubdir(handle, path, strictness);
597 return (error);
601 * check to see if group/share is persistent.
603 * "group" can be either an sa_group_t or an sa_share_t. (void *)
604 * works since both these types are also void *.
605 * If the share is a ZFS share, mark it as persistent.
608 sa_is_persistent(void *group)
610 char *type;
611 int persist = 1;
612 sa_group_t grp;
614 type = sa_get_group_attr((sa_group_t)group, "type");
615 if (type != NULL) {
616 if (strcmp(type, "transient") == 0)
617 persist = 0;
618 sa_free_attr_string(type);
621 grp = (sa_is_share(group)) ? sa_get_parent_group(group) : group;
622 if (sa_group_is_zfs(grp))
623 persist = 1;
625 return (persist);
629 * sa_valid_group_name(name)
631 * check that the "name" contains only valid characters and otherwise
632 * fits the required naming conventions. Valid names must start with
633 * an alphabetic and the remainder may consist of only alphanumeric
634 * plus the '-' and '_' characters. This name limitation comes from
635 * inherent limitations in SMF.
639 sa_valid_group_name(char *name)
641 int ret = 1;
642 ssize_t len;
644 if (name != NULL && isalpha(*name)) {
645 char c;
646 len = strlen(name);
647 if (len < (scf_max_name_len - sizeof ("group:"))) {
648 for (c = *name++; c != '\0' && ret != 0; c = *name++) {
649 if (!isalnum(c) && c != '-' && c != '_')
650 ret = 0;
652 } else {
653 ret = 0;
655 } else {
656 ret = 0;
658 return (ret);
663 * is_zfs_group(group)
664 * Determine if the specified group is a ZFS sharenfs group
666 static int
667 is_zfs_group(sa_group_t group)
669 int ret = 0;
670 xmlNodePtr parent;
671 xmlChar *zfs;
673 if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0)
674 parent = (xmlNodePtr)sa_get_parent_group(group);
675 else
676 parent = (xmlNodePtr)group;
677 zfs = xmlGetProp(parent, (xmlChar *)"zfs");
678 if (zfs != NULL) {
679 xmlFree(zfs);
680 ret = 1;
682 return (ret);
686 * sa_get_object_type(object)
688 * This function returns a numeric value representing the object
689 * type. This allows using simpler checks when doing type specific
690 * operations.
693 static int
694 sa_get_object_type(void *object)
696 xmlNodePtr node = (xmlNodePtr)object;
697 int type;
699 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0)
700 type = SA_TYPE_GROUP;
701 else if (xmlStrcmp(node->name, (xmlChar *)"share") == 0)
702 type = SA_TYPE_SHARE;
703 else if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
704 type = SA_TYPE_RESOURCE;
705 else if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0)
706 type = SA_TYPE_OPTIONSET;
707 else if (xmlStrcmp(node->name, (xmlChar *)"security") == 0)
708 type = SA_TYPE_ALTSPACE;
709 else
710 assert(0);
711 return (type);
715 * sa_optionset_name(optionset, oname, len, id)
716 * return the SMF name for the optionset. If id is not NULL, it
717 * will have the GUID value for a share and should be used
718 * instead of the keyword "optionset" which is used for
719 * groups. If the optionset doesn't have a protocol type
720 * associated with it, "default" is used. This shouldn't happen
721 * at this point but may be desirable in the future if there are
722 * protocol independent properties added. The name is returned in
723 * oname.
726 static int
727 sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id)
729 char *proto;
730 void *parent;
731 int ptype;
733 if (id == NULL)
734 id = "optionset";
736 parent = sa_get_optionset_parent(optionset);
737 if (parent != NULL) {
738 ptype = sa_get_object_type(parent);
739 proto = sa_get_optionset_attr(optionset, "type");
740 if (ptype != SA_TYPE_RESOURCE) {
741 len = snprintf(oname, len, "%s_%s", id,
742 proto ? proto : "default");
743 } else {
744 char *index;
745 index = get_node_attr((void *)parent, "id");
746 if (index != NULL) {
747 len = snprintf(oname, len, "%s_%s_%s", id,
748 proto ? proto : "default", index);
749 sa_free_attr_string(index);
750 } else {
751 len = 0;
755 if (proto != NULL)
756 sa_free_attr_string(proto);
757 } else {
758 len = 0;
760 return (len);
764 * sa_security_name(optionset, oname, len, id)
766 * return the SMF name for the security. If id is not NULL, it will
767 * have the GUID value for a share and should be used instead of the
768 * keyword "optionset" which is used for groups. If the optionset
769 * doesn't have a protocol type associated with it, "default" is
770 * used. This shouldn't happen at this point but may be desirable in
771 * the future if there are protocol independent properties added. The
772 * name is returned in oname. The security type is also encoded into
773 * the name. In the future, this wil *be handled a bit differently.
776 static int
777 sa_security_name(sa_security_t security, char *oname, size_t len, char *id)
779 char *proto;
780 char *sectype;
782 if (id == NULL)
783 id = "optionset";
785 proto = sa_get_security_attr(security, "type");
786 sectype = sa_get_security_attr(security, "sectype");
787 len = snprintf(oname, len, "%s_%s_%s", id, proto ? proto : "default",
788 sectype ? sectype : "default");
789 if (proto != NULL)
790 sa_free_attr_string(proto);
791 if (sectype != NULL)
792 sa_free_attr_string(sectype);
793 return (len);
797 * verifydefgroupopts(handle)
799 * Make sure a "default" group exists and has default protocols enabled.
801 static void
802 verifydefgroupopts(sa_handle_t handle)
804 sa_group_t defgrp;
805 sa_optionset_t opt;
807 defgrp = sa_get_group(handle, "default");
808 if (defgrp != NULL) {
809 opt = sa_get_optionset(defgrp, NULL);
811 * NFS is the default for default group
813 if (opt == NULL)
814 opt = sa_create_optionset(defgrp, "nfs");
819 * sa_init_impl(init_service, arg)
820 * Initialize the API
821 * find all the shared objects
822 * init the tables with all objects
823 * read in the current configuration
825 * arg is a parameter passed in whose meaning is based on the init_service.
826 * See libshare.h under API initialization.
828 #define GETPROP(prop) scf_simple_prop_next_astring(prop)
829 #define CHECKTSTAMP(st, tval) stat(SA_LEGACY_DFSTAB, &st) >= 0 && \
830 tval != TSTAMP(st.st_ctim)
831 static sa_handle_t
832 sa_init_impl(int init_service, void *arg)
834 struct stat st;
835 /* legacy is used for debugging only as far as I can tell */
836 int legacy = 0;
837 uint64_t tval = 0;
838 int lockfd;
839 sigset_t old;
840 int updatelegacy = B_FALSE;
841 scf_simple_prop_t *prop;
842 sa_handle_impl_t handle;
843 int err;
845 handle = calloc(sizeof (struct sa_handle_impl), 1);
847 if (handle != NULL) {
848 handle->sa_service = init_service;
850 * Get protocol specific structures, but only if this
851 * is the only handle.
853 (void) mutex_lock(&sa_global_lock);
854 if (sa_global_handles == NULL)
855 (void) proto_plugin_init();
856 (void) mutex_unlock(&sa_global_lock);
857 if (init_service & (SA_INIT_SHARE_API |
858 SA_INIT_SHARE_API_SELECTIVE | SA_INIT_ONE_SHARE_FROM_NAME |
859 SA_INIT_ONE_SHARE_FROM_HANDLE)) {
861 * initialize access into libzfs. We use this
862 * when collecting info about ZFS datasets and
863 * shares.
865 if (sa_zfs_init(handle) == B_FALSE) {
866 free(handle);
867 (void) mutex_lock(&sa_global_lock);
868 (void) proto_plugin_fini();
869 (void) mutex_unlock(&sa_global_lock);
870 return (NULL);
873 * since we want to use SMF, initialize an svc handle
874 * and find out what is there.
876 handle->scfhandle = sa_scf_init(handle);
877 if (handle->scfhandle != NULL) {
879 * Need to lock the extraction of the
880 * configuration if the dfstab file has
881 * changed. Lock everything now and release if
882 * not needed. Use a file that isn't being
883 * manipulated by other parts of the system in
884 * order to not interfere with locking. Using
885 * dfstab doesn't work.
887 sablocksigs(&old);
888 lockfd = open(DFS_LOCK_FILE, O_RDWR);
889 if (lockfd >= 0) {
890 errno = 0;
891 (void) lockf(lockfd, F_LOCK, 0);
892 (void) mutex_lock(&sa_dfstab_lock);
894 * Check whether we are going to need
895 * to merge any dfstab changes. This
896 * is done by comparing the value of
897 * legacy-timestamp with the current
898 * st_ctim of the file. If they are
899 * different, an update is needed and
900 * the file must remain locked until
901 * the merge is done in order to
902 * prevent multiple startups from
903 * changing the SMF repository at the
904 * same time. The first to get the
905 * lock will make any changes before
906 * the others can read the repository.
908 prop = scf_simple_prop_get
909 (handle->scfhandle->handle,
910 (const char *)SA_SVC_FMRI_BASE
911 ":default", "operation",
912 "legacy-timestamp");
913 if (prop != NULL) {
914 char *i64;
915 i64 = GETPROP(prop);
916 if (i64 != NULL)
917 tval = strtoull(i64,
918 NULL, 0);
919 if (CHECKTSTAMP(st, tval))
920 updatelegacy = B_TRUE;
921 scf_simple_prop_free(prop);
922 } else {
924 * We haven't set the
925 * timestamp before so do it.
927 updatelegacy = B_TRUE;
929 if (updatelegacy == B_FALSE) {
930 (void) mutex_unlock(
931 &sa_dfstab_lock);
932 (void) lockf(lockfd, F_ULOCK,
934 (void) close(lockfd);
939 * It is essential that the document tree and
940 * the internal list of roots to handles be
941 * setup before anything that might try to
942 * create a new object is called. The document
943 * tree is the combination of handle->doc and
944 * handle->tree. This allows searches,
945 * etc. when all you have is an object in the
946 * tree.
948 handle->doc = xmlNewDoc((xmlChar *)"1.0");
949 handle->tree = xmlNewNode(NULL,
950 (xmlChar *)"sharecfg");
951 if (handle->doc != NULL &&
952 handle->tree != NULL) {
953 (void) xmlDocSetRootElement(handle->doc,
954 handle->tree);
955 err = add_handle_for_root(handle->tree,
956 handle);
957 if (err == SA_OK)
958 err = sa_get_config(
959 handle->scfhandle,
960 handle->tree, handle);
961 } else {
962 if (handle->doc != NULL)
963 xmlFreeDoc(handle->doc);
964 if (handle->tree != NULL)
965 xmlFreeNode(handle->tree);
966 err = SA_NO_MEMORY;
969 saunblocksigs(&old);
971 if (err != SA_OK) {
973 * If we couldn't add the tree handle
974 * to the list, then things are going
975 * to fail badly. Might as well undo
976 * everything now and fail the
977 * sa_init().
979 sa_fini(handle);
980 if (updatelegacy == B_TRUE) {
981 (void) mutex_unlock(
982 &sa_dfstab_lock);
983 (void) lockf(lockfd,
984 F_ULOCK, 0);
985 (void) close(lockfd);
987 return (NULL);
990 if (tval == 0) {
992 * first time so make sure
993 * default is setup
995 verifydefgroupopts(handle);
998 if (updatelegacy == B_TRUE) {
999 sablocksigs(&old);
1000 getlegacyconfig((sa_handle_t)handle,
1001 SA_LEGACY_DFSTAB, &handle->tree);
1002 if (stat(SA_LEGACY_DFSTAB, &st) >= 0)
1003 set_legacy_timestamp(
1004 handle->tree,
1005 SA_LEGACY_DFSTAB,
1006 TSTAMP(st.st_ctim));
1007 saunblocksigs(&old);
1009 * Safe to unlock now to allow
1010 * others to run
1012 (void) mutex_unlock(&sa_dfstab_lock);
1013 (void) lockf(lockfd, F_ULOCK, 0);
1014 (void) close(lockfd);
1016 /* Get sharetab timestamp */
1017 sa_update_sharetab_ts((sa_handle_t)handle);
1019 /* Get lastupdate (transaction) timestamp */
1020 prop = scf_simple_prop_get(
1021 handle->scfhandle->handle,
1022 (const char *)SA_SVC_FMRI_BASE ":default",
1023 "state", "lastupdate");
1024 if (prop != NULL) {
1025 char *str;
1026 str =
1027 scf_simple_prop_next_astring(prop);
1028 if (str != NULL)
1029 handle->tstrans =
1030 strtoull(str, NULL, 0);
1031 else
1032 handle->tstrans = 0;
1033 scf_simple_prop_free(prop);
1036 * In this conditional the library reads from
1037 * zfs and /etc/dfs/sharetab to find datasets
1038 * that must be shared. The result is a tree of
1039 * groups that are stored in the handle for
1040 * libshare to utilize later when asked to share
1041 * or unshare datasets.
1043 if (init_service &
1044 SA_INIT_SHARE_API_SELECTIVE) {
1045 char **paths;
1046 size_t paths_len, i;
1048 legacy |= sa_get_one_zfs_share(handle,
1049 "zfs",
1050 (sa_init_selective_arg_t *)arg,
1051 &paths, &paths_len);
1052 legacy |= get_one_transient(handle,
1053 &handle->tree, paths, paths_len);
1054 for (i = 0; i < paths_len; ++i) {
1055 free(paths[i]);
1057 free(paths);
1058 } else if (init_service &
1059 SA_INIT_ONE_SHARE_FROM_NAME) {
1060 char path[ZFS_MAXPROPLEN];
1061 char *ptr = path;
1062 char **ptr_to_path = &ptr;
1064 legacy |=
1065 sa_get_zfs_share_for_name(handle,
1066 "zfs", (char *)arg, path);
1067 legacy |= get_one_transient(handle,
1068 &handle->tree, ptr_to_path, 1);
1069 } else if (init_service &
1070 SA_INIT_ONE_SHARE_FROM_HANDLE) {
1071 char path[ZFS_MAXPROPLEN];
1072 char *ptr = path;
1073 char **ptr_to_path = &ptr;
1075 legacy |=
1076 sa_get_zfs_share_for_name(handle,
1077 "zfs",
1078 zfs_get_name(
1079 (zfs_handle_t *)arg),
1080 path);
1081 legacy |= get_one_transient(handle,
1082 &handle->tree, ptr_to_path, 1);
1083 } else {
1084 legacy |= sa_get_zfs_shares(handle,
1085 "zfs");
1086 legacy |= gettransients(handle,
1087 &handle->tree);
1092 return ((sa_handle_t)handle);
1096 * sa_init exists as a legacy interface, new consumers should use sa_init_arg.
1098 sa_handle_t
1099 sa_init(int init_service)
1101 return (sa_init_impl(init_service, NULL));
1105 * See libshare.h "API Initialization" section for valid values of init_service
1106 * as well as the appropriate argument type for a given init_service.
1108 sa_handle_t
1109 sa_init_arg(int init_service, void *arg)
1111 return (sa_init_impl(init_service, arg));
1115 * sa_fini(handle)
1116 * Uninitialize the API structures including the configuration
1117 * data structures and ZFS related data.
1120 void
1121 sa_fini(sa_handle_t handle)
1123 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1125 if (impl_handle != NULL) {
1127 * Free the config trees and any other data structures
1128 * used in the handle.
1130 if (impl_handle->doc != NULL)
1131 xmlFreeDoc(impl_handle->doc);
1133 /* Remove and free the entry in the global list. */
1134 remove_handle_for_root(impl_handle->tree);
1137 * If this was the last handle to release, unload the
1138 * plugins that were loaded. Use a mutex in case
1139 * another thread is reinitializing.
1141 (void) mutex_lock(&sa_global_lock);
1142 if (sa_global_handles == NULL)
1143 (void) proto_plugin_fini();
1144 (void) mutex_unlock(&sa_global_lock);
1146 sa_scf_fini(impl_handle->scfhandle);
1147 sa_zfs_fini(impl_handle);
1149 /* Make sure we free the handle */
1150 free(impl_handle);
1156 * sa_get_protocols(char **protocol)
1157 * Get array of protocols that are supported
1158 * Returns pointer to an allocated and NULL terminated
1159 * array of strings. Caller must free.
1160 * This really should be determined dynamically.
1161 * If there aren't any defined, return -1.
1162 * Use free() to return memory.
1166 sa_get_protocols(char ***protocols)
1168 int numproto = -1;
1170 if (protocols != NULL) {
1171 struct sa_proto_plugin *plug;
1172 for (numproto = 0, plug = sap_proto_list; plug != NULL;
1173 plug = plug->plugin_next) {
1174 numproto++;
1177 *protocols = calloc(numproto + 1, sizeof (char *));
1178 if (*protocols != NULL) {
1179 int ret = 0;
1180 for (plug = sap_proto_list; plug != NULL;
1181 plug = plug->plugin_next) {
1182 /* faking for now */
1183 (*protocols)[ret++] =
1184 plug->plugin_ops->sa_protocol;
1186 } else {
1187 numproto = -1;
1190 return (numproto);
1194 * find_group_by_name(node, group)
1196 * search the XML document subtree specified by node to find the group
1197 * specified by group. Searching subtree allows subgroups to be
1198 * searched for.
1201 static xmlNodePtr
1202 find_group_by_name(xmlNodePtr node, xmlChar *group)
1204 xmlChar *name = NULL;
1206 for (node = node->xmlChildrenNode; node != NULL;
1207 node = node->next) {
1208 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
1209 /* if no groupname, return the first found */
1210 if (group == NULL)
1211 break;
1212 name = xmlGetProp(node, (xmlChar *)"name");
1213 if (name != NULL && xmlStrcmp(name, group) == 0)
1214 break;
1215 if (name != NULL) {
1216 xmlFree(name);
1217 name = NULL;
1221 if (name != NULL)
1222 xmlFree(name);
1223 return (node);
1227 * sa_get_group(groupname)
1228 * Return the "group" specified. If groupname is NULL,
1229 * return the first group of the list of groups.
1231 sa_group_t
1232 sa_get_group(sa_handle_t handle, char *groupname)
1234 xmlNodePtr node = NULL;
1235 char *subgroup = NULL;
1236 char *group = NULL;
1237 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1239 if (impl_handle != NULL && impl_handle->tree != NULL) {
1240 if (groupname != NULL) {
1241 group = strdup(groupname);
1242 if (group != NULL) {
1243 subgroup = strchr(group, '/');
1244 if (subgroup != NULL)
1245 *subgroup++ = '\0';
1249 * We want to find the, possibly, named group. If
1250 * group is not NULL, then lookup the name. If it is
1251 * NULL, we only do the find if groupname is also
1252 * NULL. This allows lookup of the "first" group in
1253 * the internal list.
1255 if (group != NULL || groupname == NULL)
1256 node = find_group_by_name(impl_handle->tree,
1257 (xmlChar *)group);
1259 /* if a subgroup, find it before returning */
1260 if (subgroup != NULL && node != NULL)
1261 node = find_group_by_name(node, (xmlChar *)subgroup);
1263 if (node != NULL && (char *)group != NULL)
1264 (void) sa_get_instance(impl_handle->scfhandle, (char *)group);
1265 free(group);
1266 return ((sa_group_t)(node));
1270 * sa_get_next_group(group)
1271 * Return the "next" group after the specified group from
1272 * the internal group list. NULL if there are no more.
1274 sa_group_t
1275 sa_get_next_group(sa_group_t group)
1277 xmlNodePtr ngroup = NULL;
1278 if (group != NULL) {
1279 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
1280 ngroup = ngroup->next) {
1281 if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
1282 break;
1285 return ((sa_group_t)ngroup);
1289 * sa_get_share(group, sharepath)
1290 * Return the share object for the share specified. The share
1291 * must be in the specified group. Return NULL if not found.
1293 sa_share_t
1294 sa_get_share(sa_group_t group, char *sharepath)
1296 xmlNodePtr node = NULL;
1297 xmlChar *path;
1300 * For future scalability, this should end up building a cache
1301 * since it will get called regularly by the mountd and info
1302 * services.
1304 if (group != NULL) {
1305 for (node = ((xmlNodePtr)group)->children; node != NULL;
1306 node = node->next) {
1307 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1308 if (sharepath == NULL) {
1309 break;
1310 } else {
1311 /* is it the correct share? */
1312 path = xmlGetProp(node,
1313 (xmlChar *)"path");
1314 if (path != NULL &&
1315 xmlStrcmp(path,
1316 (xmlChar *)sharepath) == 0) {
1317 xmlFree(path);
1318 break;
1320 xmlFree(path);
1325 return ((sa_share_t)node);
1329 * sa_get_next_share(share)
1330 * Return the next share following the specified share
1331 * from the internal list of shares. Returns NULL if there
1332 * are no more shares. The list is relative to the same
1333 * group.
1335 sa_share_t
1336 sa_get_next_share(sa_share_t share)
1338 xmlNodePtr node = NULL;
1340 if (share != NULL) {
1341 for (node = ((xmlNodePtr)share)->next; node != NULL;
1342 node = node->next) {
1343 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1344 break;
1348 return ((sa_share_t)node);
1352 * _sa_get_child_node(node, type)
1354 * find the child node of the specified node that has "type". This is
1355 * used to implement several internal functions.
1358 static xmlNodePtr
1359 _sa_get_child_node(xmlNodePtr node, xmlChar *type)
1361 xmlNodePtr child;
1362 for (child = node->xmlChildrenNode; child != NULL;
1363 child = child->next)
1364 if (xmlStrcmp(child->name, type) == 0)
1365 return (child);
1366 return ((xmlNodePtr)NULL);
1370 * find_share(group, path)
1372 * Search all the shares in the specified group for one that has the
1373 * specified path.
1376 static sa_share_t
1377 find_share(sa_group_t group, char *sharepath)
1379 sa_share_t share;
1380 char *path;
1382 for (share = sa_get_share(group, NULL); share != NULL;
1383 share = sa_get_next_share(share)) {
1384 path = sa_get_share_attr(share, "path");
1385 if (path != NULL && strcmp(path, sharepath) == 0) {
1386 sa_free_attr_string(path);
1387 break;
1389 if (path != NULL)
1390 sa_free_attr_string(path);
1392 return (share);
1396 * sa_get_sub_group(group)
1398 * Get the first sub-group of group. The sa_get_next_group() function
1399 * can be used to get the rest. This is currently only used for ZFS
1400 * sub-groups but could be used to implement a more general mechanism.
1403 sa_group_t
1404 sa_get_sub_group(sa_group_t group)
1406 return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1407 (xmlChar *)"group"));
1411 * sa_find_share(sharepath)
1412 * Finds a share regardless of group. In the future, this
1413 * function should utilize a cache and hash table of some kind.
1414 * The current assumption is that a path will only be shared
1415 * once. In the future, this may change as implementation of
1416 * resource names comes into being.
1418 sa_share_t
1419 sa_find_share(sa_handle_t handle, char *sharepath)
1421 sa_group_t group;
1422 sa_group_t zgroup;
1423 sa_share_t share = NULL;
1424 int done = 0;
1426 for (group = sa_get_group(handle, NULL); group != NULL && !done;
1427 group = sa_get_next_group(group)) {
1428 if (is_zfs_group(group)) {
1429 for (zgroup =
1430 (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1431 (xmlChar *)"group");
1432 zgroup != NULL;
1433 zgroup = sa_get_next_group(zgroup)) {
1434 share = find_share(zgroup, sharepath);
1435 if (share != NULL)
1436 break;
1438 } else {
1439 share = find_share(group, sharepath);
1441 if (share != NULL)
1442 break;
1444 return (share);
1448 * sa_check_path(group, path, strictness)
1450 * Check that path is a valid path relative to the group. Currently,
1451 * we are ignoring the group and checking only the NFS rules. Later,
1452 * we may want to use the group to then check against the protocols
1453 * enabled on the group. The strictness values mean:
1454 * SA_CHECK_NORMAL == only check newpath against shares that are active
1455 * SA_CHECK_STRICT == check newpath against both active shares and those
1456 * stored in the repository
1460 sa_check_path(sa_group_t group, char *path, int strictness)
1462 sa_handle_t handle;
1464 handle = sa_find_group_handle(group);
1465 if (handle == NULL)
1466 return (SA_BAD_PATH);
1468 return (validpath(handle, path, strictness));
1472 * mark_excluded_protos(group, share, flags)
1474 * Walk through all the protocols enabled for the group and check to
1475 * see if the share has any of them should be in the exclude list
1476 * based on the featureset of the protocol. If there are any, add the
1477 * "exclude" property to the share.
1479 static void
1480 mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags)
1482 sa_optionset_t optionset;
1483 char exclude_list[SA_STRSIZE];
1484 char *sep = "";
1486 exclude_list[0] = '\0';
1487 for (optionset = sa_get_optionset(group, NULL);
1488 optionset != NULL;
1489 optionset = sa_get_next_optionset(optionset)) {
1490 char *value;
1491 uint64_t features;
1492 value = sa_get_optionset_attr(optionset, "type");
1493 if (value == NULL)
1494 continue;
1495 features = sa_proto_get_featureset(value);
1496 if (!(features & flags)) {
1497 (void) strlcat(exclude_list, sep,
1498 sizeof (exclude_list));
1499 (void) strlcat(exclude_list, value,
1500 sizeof (exclude_list));
1501 sep = ",";
1503 sa_free_attr_string(value);
1505 if (exclude_list[0] != '\0')
1506 (void) xmlSetProp(share, (xmlChar *)"exclude",
1507 (xmlChar *)exclude_list);
1511 * get_all_features(group)
1513 * Walk through all the protocols on the group and collect all
1514 * possible enabled features. This is the OR of all the featuresets.
1516 static uint64_t
1517 get_all_features(sa_group_t group)
1519 sa_optionset_t optionset;
1520 uint64_t features = 0;
1522 for (optionset = sa_get_optionset(group, NULL);
1523 optionset != NULL;
1524 optionset = sa_get_next_optionset(optionset)) {
1525 char *value;
1526 value = sa_get_optionset_attr(optionset, "type");
1527 if (value == NULL)
1528 continue;
1529 features |= sa_proto_get_featureset(value);
1530 sa_free_attr_string(value);
1532 return (features);
1537 * _sa_add_share(group, sharepath, persist, *error, flags)
1539 * Common code for all types of add_share. sa_add_share() is the
1540 * public API, we also need to be able to do this when parsing legacy
1541 * files and construction of the internal configuration while
1542 * extracting config info from SMF. "flags" indicates if some
1543 * protocols need relaxed rules while other don't. These values are
1544 * the featureset values defined in libshare.h.
1547 sa_share_t
1548 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error,
1549 uint64_t flags)
1551 xmlNodePtr node = NULL;
1552 int err;
1554 err = SA_OK; /* assume success */
1556 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL);
1557 if (node == NULL) {
1558 if (error != NULL)
1559 *error = SA_NO_MEMORY;
1560 return (node);
1563 (void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
1564 (void) xmlSetProp(node, (xmlChar *)"type",
1565 persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
1566 if (flags != 0)
1567 mark_excluded_protos(group, node, flags);
1568 if (persist != SA_SHARE_TRANSIENT) {
1570 * persistent shares come in two flavors: SMF and
1571 * ZFS. Sort this one out based on target group and
1572 * path type. Both NFS and SMB are supported. First,
1573 * check to see if the protocol is enabled on the
1574 * subgroup and then setup the share appropriately.
1576 if (sa_group_is_zfs(group) &&
1577 sa_path_is_zfs(sharepath)) {
1578 if (sa_get_optionset(group, "nfs") != NULL)
1579 err = sa_zfs_set_sharenfs(group, sharepath, 1);
1580 else if (sa_get_optionset(group, "smb") != NULL)
1581 err = sa_zfs_set_sharesmb(group, sharepath, 1);
1582 } else {
1583 sa_handle_impl_t impl_handle;
1584 impl_handle =
1585 (sa_handle_impl_t)sa_find_group_handle(group);
1586 if (impl_handle != NULL) {
1587 err = sa_commit_share(impl_handle->scfhandle,
1588 group, (sa_share_t)node);
1589 } else {
1590 err = SA_SYSTEM_ERR;
1594 if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER)
1595 /* called by the dfstab parser so could be a show */
1596 err = SA_OK;
1598 if (err != SA_OK) {
1600 * we couldn't commit to the repository so undo
1601 * our internal state to reflect reality.
1603 xmlUnlinkNode(node);
1604 xmlFreeNode(node);
1605 node = NULL;
1608 if (error != NULL)
1609 *error = err;
1611 return (node);
1615 * sa_add_share(group, sharepath, persist, *error)
1617 * Add a new share object to the specified group. The share will
1618 * have the specified sharepath and will only be constructed if
1619 * it is a valid path to be shared. NULL is returned on error
1620 * and a detailed error value will be returned via the error
1621 * pointer.
1623 sa_share_t
1624 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1626 xmlNodePtr node = NULL;
1627 int strictness = SA_CHECK_NORMAL;
1628 sa_handle_t handle;
1629 uint64_t special = 0;
1630 uint64_t features;
1633 * If the share is to be permanent, use strict checking so a
1634 * bad config doesn't get created. Transient shares only need
1635 * to check against the currently active
1636 * shares. SA_SHARE_PARSER is a modifier used internally to
1637 * indicate that we are being called by the dfstab parser and
1638 * that we need strict checking in all cases. Normally persist
1639 * is in integer value but SA_SHARE_PARSER may be or'd into
1640 * it as an override.
1642 if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT)
1643 strictness = SA_CHECK_STRICT;
1645 handle = sa_find_group_handle(group);
1648 * need to determine if the share is valid. The rules are:
1649 * - The path must not already exist
1650 * - The path must not be a subdir or parent dir of an
1651 * existing path unless at least one protocol allows it.
1652 * The sub/parent check is done in sa_check_path().
1655 if (sa_find_share(handle, sharepath) == NULL) {
1656 *error = sa_check_path(group, sharepath, strictness);
1657 features = get_all_features(group);
1658 switch (*error) {
1659 case SA_PATH_IS_SUBDIR:
1660 if (features & SA_FEATURE_ALLOWSUBDIRS)
1661 special |= SA_FEATURE_ALLOWSUBDIRS;
1662 break;
1663 case SA_PATH_IS_PARENTDIR:
1664 if (features & SA_FEATURE_ALLOWPARDIRS)
1665 special |= SA_FEATURE_ALLOWPARDIRS;
1666 break;
1668 if (*error == SA_OK || special != SA_FEATURE_NONE)
1669 node = _sa_add_share(group, sharepath, persist,
1670 error, special);
1671 } else {
1672 *error = SA_DUPLICATE_NAME;
1675 return ((sa_share_t)node);
1679 * sa_enable_share(share, protocol)
1680 * Enable the specified share to the specified protocol.
1681 * If protocol is NULL, then all protocols.
1684 sa_enable_share(sa_share_t share, char *protocol)
1686 char *sharepath;
1687 struct stat st;
1688 int err = SA_OK;
1689 int ret;
1691 sharepath = sa_get_share_attr(share, "path");
1692 if (sharepath == NULL)
1693 return (SA_NO_MEMORY);
1694 if (stat(sharepath, &st) < 0) {
1695 err = SA_NO_SUCH_PATH;
1696 } else {
1697 /* tell the server about the share */
1698 if (protocol != NULL) {
1699 if (excluded_protocol(share, protocol))
1700 goto done;
1702 /* lookup protocol specific handler */
1703 err = sa_proto_share(protocol, share);
1704 if (err == SA_OK)
1705 (void) sa_set_share_attr(share,
1706 "shared", "true");
1707 } else {
1708 /* Tell all protocols about the share */
1709 sa_group_t group;
1710 sa_optionset_t optionset;
1712 group = sa_get_parent_group(share);
1714 for (optionset = sa_get_optionset(group, NULL);
1715 optionset != NULL;
1716 optionset = sa_get_next_optionset(optionset)) {
1717 char *proto;
1718 proto = sa_get_optionset_attr(optionset,
1719 "type");
1720 if (proto != NULL) {
1721 if (!excluded_protocol(share, proto)) {
1722 ret = sa_proto_share(proto,
1723 share);
1724 if (ret != SA_OK)
1725 err = ret;
1727 sa_free_attr_string(proto);
1730 (void) sa_set_share_attr(share, "shared", "true");
1733 done:
1734 if (sharepath != NULL)
1735 sa_free_attr_string(sharepath);
1736 return (err);
1740 * sa_disable_share(share, protocol)
1741 * Disable the specified share to the specified protocol. If
1742 * protocol is NULL, then all protocols that are enabled for the
1743 * share should be disabled.
1746 sa_disable_share(sa_share_t share, char *protocol)
1748 char *path;
1749 int err = SA_OK;
1750 int ret = SA_OK;
1752 path = sa_get_share_attr(share, "path");
1754 if (protocol != NULL) {
1755 ret = sa_proto_unshare(share, protocol, path);
1756 } else {
1757 /* need to do all protocols */
1758 sa_group_t group;
1759 sa_optionset_t optionset;
1761 group = sa_get_parent_group(share);
1763 /* Tell all protocols about the share */
1764 for (optionset = sa_get_optionset(group, NULL);
1765 optionset != NULL;
1766 optionset = sa_get_next_optionset(optionset)) {
1767 char *proto;
1769 proto = sa_get_optionset_attr(optionset, "type");
1770 if (proto != NULL) {
1771 err = sa_proto_unshare(share, proto, path);
1772 if (err != SA_OK)
1773 ret = err;
1774 sa_free_attr_string(proto);
1778 if (ret == SA_OK)
1779 (void) sa_set_share_attr(share, "shared", NULL);
1780 if (path != NULL)
1781 sa_free_attr_string(path);
1782 return (ret);
1786 * sa_remove_share(share)
1788 * remove the specified share from its containing group.
1789 * Remove from the SMF or ZFS configuration space.
1793 sa_remove_share(sa_share_t share)
1795 sa_group_t group;
1796 int ret = SA_OK;
1797 char *type;
1798 int transient = 0;
1799 char *groupname;
1800 char *zfs;
1802 type = sa_get_share_attr(share, "type");
1803 group = sa_get_parent_group(share);
1804 zfs = sa_get_group_attr(group, "zfs");
1805 groupname = sa_get_group_attr(group, "name");
1806 if (type != NULL && strcmp(type, "persist") != 0)
1807 transient = 1;
1808 if (type != NULL)
1809 sa_free_attr_string(type);
1811 /* remove the node from its group then free the memory */
1814 * need to test if "busy"
1816 /* only do SMF action if permanent */
1817 if (!transient || zfs != NULL) {
1818 /* remove from legacy dfstab as well as possible SMF */
1819 ret = sa_delete_legacy(share, NULL);
1820 if (ret == SA_OK) {
1821 if (!sa_group_is_zfs(group)) {
1822 sa_handle_impl_t impl_handle;
1823 impl_handle = (sa_handle_impl_t)
1824 sa_find_group_handle(group);
1825 if (impl_handle != NULL) {
1826 ret = sa_delete_share(
1827 impl_handle->scfhandle, group,
1828 share);
1829 } else {
1830 ret = SA_SYSTEM_ERR;
1832 } else {
1833 char *sharepath = sa_get_share_attr(share,
1834 "path");
1835 if (sharepath != NULL) {
1836 ret = sa_zfs_set_sharenfs(group,
1837 sharepath, 0);
1838 sa_free_attr_string(sharepath);
1843 if (groupname != NULL)
1844 sa_free_attr_string(groupname);
1845 if (zfs != NULL)
1846 sa_free_attr_string(zfs);
1848 xmlUnlinkNode((xmlNodePtr)share);
1849 xmlFreeNode((xmlNodePtr)share);
1850 return (ret);
1854 * sa_move_share(group, share)
1856 * move the specified share to the specified group. Update SMF
1857 * appropriately.
1861 sa_move_share(sa_group_t group, sa_share_t share)
1863 sa_group_t oldgroup;
1864 int ret = SA_OK;
1866 /* remove the node from its group then free the memory */
1868 oldgroup = sa_get_parent_group(share);
1869 if (oldgroup != group) {
1870 sa_handle_impl_t impl_handle;
1871 xmlUnlinkNode((xmlNodePtr)share);
1873 * now that the share isn't in its old group, add to
1874 * the new one
1876 (void) xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
1877 /* need to deal with SMF */
1878 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1879 if (impl_handle != NULL) {
1881 * need to remove from old group first and then add to
1882 * new group. Ideally, we would do the other order but
1883 * need to avoid having the share in two groups at the
1884 * same time.
1886 ret = sa_delete_share(impl_handle->scfhandle, oldgroup,
1887 share);
1888 if (ret == SA_OK)
1889 ret = sa_commit_share(impl_handle->scfhandle,
1890 group, share);
1891 } else {
1892 ret = SA_SYSTEM_ERR;
1895 return (ret);
1899 * sa_get_parent_group(share)
1901 * Return the containing group for the share. If a group was actually
1902 * passed in, we don't want a parent so return NULL.
1905 sa_group_t
1906 sa_get_parent_group(sa_share_t share)
1908 xmlNodePtr node = NULL;
1909 if (share != NULL) {
1910 node = ((xmlNodePtr)share)->parent;
1912 * make sure parent is a group and not sharecfg since
1913 * we may be cheating and passing in a group.
1914 * Eventually, groups of groups might come into being.
1916 if (node == NULL ||
1917 xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
1918 node = NULL;
1920 return ((sa_group_t)node);
1924 * _sa_create_group(impl_handle, groupname)
1926 * Create a group in the document. The caller will need to deal with
1927 * configuration store and activation.
1930 sa_group_t
1931 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname)
1933 xmlNodePtr node = NULL;
1935 if (sa_valid_group_name(groupname)) {
1936 node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group",
1937 NULL);
1938 if (node != NULL) {
1939 (void) xmlSetProp(node, (xmlChar *)"name",
1940 (xmlChar *)groupname);
1941 (void) xmlSetProp(node, (xmlChar *)"state",
1942 (xmlChar *)"enabled");
1945 return ((sa_group_t)node);
1949 * _sa_create_zfs_group(group, groupname)
1951 * Create a ZFS subgroup under the specified group. This may
1952 * eventually form the basis of general sub-groups, but is currently
1953 * restricted to ZFS.
1955 sa_group_t
1956 _sa_create_zfs_group(sa_group_t group, char *groupname)
1958 xmlNodePtr node = NULL;
1960 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL);
1961 if (node != NULL) {
1962 (void) xmlSetProp(node, (xmlChar *)"name",
1963 (xmlChar *)groupname);
1964 (void) xmlSetProp(node, (xmlChar *)"state",
1965 (xmlChar *)"enabled");
1968 return ((sa_group_t)node);
1972 * sa_create_group(groupname, *error)
1974 * Create a new group with groupname. Need to validate that it is a
1975 * legal name for SMF and the construct the SMF service instance of
1976 * svc:/network/shares/group to implement the group. All necessary
1977 * operational properties must be added to the group at this point
1978 * (via the SMF transaction model).
1980 sa_group_t
1981 sa_create_group(sa_handle_t handle, char *groupname, int *error)
1983 xmlNodePtr node = NULL;
1984 sa_group_t group;
1985 int ret;
1986 char rbacstr[SA_STRSIZE];
1987 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1989 ret = SA_OK;
1991 if (impl_handle == NULL || impl_handle->scfhandle == NULL) {
1992 ret = SA_SYSTEM_ERR;
1993 goto err;
1996 group = sa_get_group(handle, groupname);
1997 if (group != NULL) {
1998 ret = SA_DUPLICATE_NAME;
1999 } else {
2000 if (sa_valid_group_name(groupname)) {
2001 node = xmlNewChild(impl_handle->tree, NULL,
2002 (xmlChar *)"group", NULL);
2003 if (node != NULL) {
2004 (void) xmlSetProp(node, (xmlChar *)"name",
2005 (xmlChar *)groupname);
2006 /* default to the group being enabled */
2007 (void) xmlSetProp(node, (xmlChar *)"state",
2008 (xmlChar *)"enabled");
2009 ret = sa_create_instance(impl_handle->scfhandle,
2010 groupname);
2011 if (ret == SA_OK) {
2012 ret = sa_start_transaction(
2013 impl_handle->scfhandle,
2014 "operation");
2016 if (ret == SA_OK) {
2017 ret = sa_set_property(
2018 impl_handle->scfhandle,
2019 "state", "enabled");
2020 if (ret == SA_OK) {
2021 ret = sa_end_transaction(
2022 impl_handle->scfhandle,
2023 impl_handle);
2024 } else {
2025 sa_abort_transaction(
2026 impl_handle->scfhandle);
2029 if (ret == SA_OK) {
2030 /* initialize the RBAC strings */
2031 ret = sa_start_transaction(
2032 impl_handle->scfhandle,
2033 "general");
2034 if (ret == SA_OK) {
2035 (void) snprintf(rbacstr,
2036 sizeof (rbacstr), "%s.%s",
2037 SA_RBAC_MANAGE, groupname);
2038 ret = sa_set_property(
2039 impl_handle->scfhandle,
2040 "action_authorization",
2041 rbacstr);
2043 if (ret == SA_OK) {
2044 (void) snprintf(rbacstr,
2045 sizeof (rbacstr), "%s.%s",
2046 SA_RBAC_VALUE, groupname);
2047 ret = sa_set_property(
2048 impl_handle->scfhandle,
2049 "value_authorization",
2050 rbacstr);
2052 if (ret == SA_OK) {
2053 ret = sa_end_transaction(
2054 impl_handle->scfhandle,
2055 impl_handle);
2056 } else {
2057 sa_abort_transaction(
2058 impl_handle->scfhandle);
2061 if (ret != SA_OK) {
2063 * Couldn't commit the group
2064 * so we need to undo
2065 * internally.
2067 xmlUnlinkNode(node);
2068 xmlFreeNode(node);
2069 node = NULL;
2071 } else {
2072 ret = SA_NO_MEMORY;
2074 } else {
2075 ret = SA_INVALID_NAME;
2078 err:
2079 if (error != NULL)
2080 *error = ret;
2081 return ((sa_group_t)node);
2085 * sa_remove_group(group)
2087 * Remove the specified group. This deletes from the SMF repository.
2088 * All property groups and properties are removed.
2092 sa_remove_group(sa_group_t group)
2094 char *name;
2095 int ret = SA_OK;
2096 sa_handle_impl_t impl_handle;
2098 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2099 if (impl_handle != NULL) {
2100 name = sa_get_group_attr(group, "name");
2101 if (name != NULL) {
2102 ret = sa_delete_instance(impl_handle->scfhandle, name);
2103 sa_free_attr_string(name);
2105 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
2106 xmlFreeNode((xmlNodePtr)group); /* now it is gone */
2107 } else {
2108 ret = SA_SYSTEM_ERR;
2110 return (ret);
2114 * sa_update_config()
2116 * Used to update legacy files that need to be updated in bulk
2117 * Currently, this is a placeholder and will go away in a future
2118 * release.
2122 sa_update_config(sa_handle_t handle)
2125 * do legacy files first so we can tell when they change.
2126 * This will go away when we start updating individual records
2127 * rather than the whole file.
2129 update_legacy_config(handle);
2130 return (SA_OK);
2134 * get_node_attr(node, tag)
2136 * Get the specified tag(attribute) if it exists on the node. This is
2137 * used internally by a number of attribute oriented functions.
2140 static char *
2141 get_node_attr(void *nodehdl, char *tag)
2143 xmlNodePtr node = (xmlNodePtr)nodehdl;
2144 xmlChar *name = NULL;
2146 if (node != NULL)
2147 name = xmlGetProp(node, (xmlChar *)tag);
2148 return ((char *)name);
2152 * set_node_attr(node, tag)
2154 * Set the specified tag(attribute) to the specified value This is
2155 * used internally by a number of attribute oriented functions. It
2156 * doesn't update the repository, only the internal document state.
2159 void
2160 set_node_attr(void *nodehdl, char *tag, char *value)
2162 xmlNodePtr node = (xmlNodePtr)nodehdl;
2163 if (node != NULL && tag != NULL) {
2164 if (value != NULL)
2165 (void) xmlSetProp(node, (xmlChar *)tag,
2166 (xmlChar *)value);
2167 else
2168 (void) xmlUnsetProp(node, (xmlChar *)tag);
2173 * sa_get_group_attr(group, tag)
2175 * Get the specied attribute, if defined, for the group.
2178 char *
2179 sa_get_group_attr(sa_group_t group, char *tag)
2181 return (get_node_attr((void *)group, tag));
2185 * sa_set_group_attr(group, tag, value)
2187 * set the specified tag/attribute on the group using value as its
2188 * value.
2190 * This will result in setting the property in the SMF repository as
2191 * well as in the internal document.
2195 sa_set_group_attr(sa_group_t group, char *tag, char *value)
2197 int ret;
2198 char *groupname;
2199 sa_handle_impl_t impl_handle;
2202 * ZFS group/subgroup doesn't need the handle so shortcut.
2204 if (sa_group_is_zfs(group)) {
2205 set_node_attr((void *)group, tag, value);
2206 return (SA_OK);
2209 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2210 if (impl_handle != NULL) {
2211 groupname = sa_get_group_attr(group, "name");
2212 ret = sa_get_instance(impl_handle->scfhandle, groupname);
2213 if (ret == SA_OK) {
2214 set_node_attr((void *)group, tag, value);
2215 ret = sa_start_transaction(impl_handle->scfhandle,
2216 "operation");
2217 if (ret == SA_OK) {
2218 ret = sa_set_property(impl_handle->scfhandle,
2219 tag, value);
2220 if (ret == SA_OK)
2221 ret = sa_end_transaction(
2222 impl_handle->scfhandle,
2223 impl_handle);
2224 else
2225 sa_abort_transaction(
2226 impl_handle->scfhandle);
2228 if (ret == SA_SYSTEM_ERR)
2229 ret = SA_NO_PERMISSION;
2231 if (groupname != NULL)
2232 sa_free_attr_string(groupname);
2233 } else {
2234 ret = SA_SYSTEM_ERR;
2236 return (ret);
2240 * sa_get_share_attr(share, tag)
2242 * Return the value of the tag/attribute set on the specified
2243 * share. Returns NULL if the tag doesn't exist.
2246 char *
2247 sa_get_share_attr(sa_share_t share, char *tag)
2249 return (get_node_attr((void *)share, tag));
2253 * _sa_set_share_description(share, description)
2255 * Add a description tag with text contents to the specified share. A
2256 * separate XML tag is used rather than a property. This can also be
2257 * used with resources.
2260 xmlNodePtr
2261 _sa_set_share_description(void *share, char *content)
2263 xmlNodePtr node;
2264 node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description",
2265 NULL);
2266 xmlNodeSetContent(node, (xmlChar *)content);
2267 return (node);
2271 * sa_set_share_attr(share, tag, value)
2273 * Set the share attribute specified by tag to the specified value. In
2274 * the case of "resource", enforce a no duplicates in a group rule. If
2275 * the share is not transient, commit the changes to the repository
2276 * else just update the share internally.
2280 sa_set_share_attr(sa_share_t share, char *tag, char *value)
2282 sa_group_t group;
2283 sa_share_t resource;
2284 int ret = SA_OK;
2286 group = sa_get_parent_group(share);
2289 * There are some attributes that may have specific
2290 * restrictions on them. Initially, only "resource" has
2291 * special meaning that needs to be checked. Only one instance
2292 * of a resource name may exist within a group.
2295 if (strcmp(tag, "resource") == 0) {
2296 resource = sa_get_resource(group, value);
2297 if (resource != share && resource != NULL)
2298 ret = SA_DUPLICATE_NAME;
2300 if (ret == SA_OK) {
2301 set_node_attr((void *)share, tag, value);
2302 if (group != NULL) {
2303 char *type;
2304 /* we can probably optimize this some */
2305 type = sa_get_share_attr(share, "type");
2306 if (type == NULL || strcmp(type, "transient") != 0) {
2307 sa_handle_impl_t impl_handle;
2308 impl_handle =
2309 (sa_handle_impl_t)sa_find_group_handle(
2310 group);
2311 if (impl_handle != NULL) {
2312 ret = sa_commit_share(
2313 impl_handle->scfhandle, group,
2314 share);
2315 } else {
2316 ret = SA_SYSTEM_ERR;
2319 if (type != NULL)
2320 sa_free_attr_string(type);
2323 return (ret);
2327 * sa_get_property_attr(prop, tag)
2329 * Get the value of the specified property attribute. Standard
2330 * attributes are "type" and "value".
2333 char *
2334 sa_get_property_attr(sa_property_t prop, char *tag)
2336 return (get_node_attr((void *)prop, tag));
2340 * sa_get_optionset_attr(prop, tag)
2342 * Get the value of the specified property attribute. Standard
2343 * attribute is "type".
2346 char *
2347 sa_get_optionset_attr(sa_property_t optionset, char *tag)
2349 return (get_node_attr((void *)optionset, tag));
2354 * sa_set_optionset_attr(optionset, tag, value)
2356 * Set the specified attribute(tag) to the specified value on the
2357 * optionset.
2360 void
2361 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
2363 set_node_attr((void *)optionset, tag, value);
2367 * sa_free_attr_string(string)
2369 * Free the string that was returned in one of the sa_get_*_attr()
2370 * functions.
2373 void
2374 sa_free_attr_string(char *string)
2376 xmlFree((xmlChar *)string);
2380 * sa_get_optionset(group, proto)
2382 * Return the optionset, if it exists, that is associated with the
2383 * specified protocol.
2386 sa_optionset_t
2387 sa_get_optionset(void *group, char *proto)
2389 xmlNodePtr node;
2390 xmlChar *value = NULL;
2392 for (node = ((xmlNodePtr)group)->children; node != NULL;
2393 node = node->next) {
2394 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2395 value = xmlGetProp(node, (xmlChar *)"type");
2396 if (proto != NULL) {
2397 if (value != NULL &&
2398 xmlStrcmp(value, (xmlChar *)proto) == 0) {
2399 break;
2401 if (value != NULL) {
2402 xmlFree(value);
2403 value = NULL;
2405 } else {
2406 break;
2410 if (value != NULL)
2411 xmlFree(value);
2412 return ((sa_optionset_t)node);
2416 * sa_get_next_optionset(optionset)
2418 * Return the next optionset in the group. NULL if this was the last.
2421 sa_optionset_t
2422 sa_get_next_optionset(sa_optionset_t optionset)
2424 xmlNodePtr node;
2426 for (node = ((xmlNodePtr)optionset)->next; node != NULL;
2427 node = node->next) {
2428 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2429 break;
2432 return ((sa_optionset_t)node);
2436 * sa_get_security(group, sectype, proto)
2438 * Return the security optionset. The internal name is a hold over
2439 * from the implementation and will be changed before the API is
2440 * finalized. This is really a named optionset that can be negotiated
2441 * as a group of properties (like NFS security options).
2444 sa_security_t
2445 sa_get_security(sa_group_t group, char *sectype, char *proto)
2447 xmlNodePtr node;
2448 xmlChar *value = NULL;
2450 for (node = ((xmlNodePtr)group)->children; node != NULL;
2451 node = node->next) {
2452 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2453 if (proto != NULL) {
2454 value = xmlGetProp(node, (xmlChar *)"type");
2455 if (value == NULL ||
2456 (value != NULL &&
2457 xmlStrcmp(value, (xmlChar *)proto) != 0)) {
2458 /* it doesn't match so continue */
2459 xmlFree(value);
2460 value = NULL;
2461 continue;
2464 if (value != NULL) {
2465 xmlFree(value);
2466 value = NULL;
2468 /* potential match */
2469 if (sectype != NULL) {
2470 value = xmlGetProp(node, (xmlChar *)"sectype");
2471 if (value != NULL &&
2472 xmlStrcmp(value, (xmlChar *)sectype) == 0) {
2473 break;
2475 } else {
2476 break;
2479 if (value != NULL) {
2480 xmlFree(value);
2481 value = NULL;
2484 if (value != NULL)
2485 xmlFree(value);
2486 return ((sa_security_t)node);
2490 * sa_get_next_security(security)
2492 * Get the next security optionset if one exists.
2495 sa_security_t
2496 sa_get_next_security(sa_security_t security)
2498 xmlNodePtr node;
2500 for (node = ((xmlNodePtr)security)->next; node != NULL;
2501 node = node->next) {
2502 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2503 break;
2506 return ((sa_security_t)node);
2510 * sa_get_property(optionset, prop)
2512 * Get the property object with the name specified in prop from the
2513 * optionset.
2516 sa_property_t
2517 sa_get_property(sa_optionset_t optionset, char *prop)
2519 xmlNodePtr node = (xmlNodePtr)optionset;
2520 xmlChar *value = NULL;
2522 if (optionset == NULL)
2523 return (NULL);
2525 for (node = node->children; node != NULL;
2526 node = node->next) {
2527 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2528 if (prop == NULL)
2529 break;
2530 value = xmlGetProp(node, (xmlChar *)"type");
2531 if (value != NULL &&
2532 xmlStrcmp(value, (xmlChar *)prop) == 0) {
2533 break;
2535 if (value != NULL) {
2536 xmlFree(value);
2537 value = NULL;
2541 if (value != NULL)
2542 xmlFree(value);
2543 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
2545 * avoid a non option node -- it is possible to be a
2546 * text node
2548 node = NULL;
2550 return ((sa_property_t)node);
2554 * sa_get_next_property(property)
2556 * Get the next property following the specified property. NULL if
2557 * this was the last.
2560 sa_property_t
2561 sa_get_next_property(sa_property_t property)
2563 xmlNodePtr node;
2565 for (node = ((xmlNodePtr)property)->next; node != NULL;
2566 node = node->next) {
2567 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2568 break;
2571 return ((sa_property_t)node);
2575 * sa_set_share_description(share, content)
2577 * Set the description of share to content.
2581 sa_set_share_description(sa_share_t share, char *content)
2583 xmlNodePtr node;
2584 sa_group_t group;
2585 int ret = SA_OK;
2587 for (node = ((xmlNodePtr)share)->children; node != NULL;
2588 node = node->next) {
2589 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2590 break;
2593 /* no existing description but want to add */
2594 if (node == NULL && content != NULL) {
2595 /* add a description */
2596 node = _sa_set_share_description(share, content);
2597 } else if (node != NULL && content != NULL) {
2598 /* update a description */
2599 xmlNodeSetContent(node, (xmlChar *)content);
2600 } else if (node != NULL && content == NULL) {
2601 /* remove an existing description */
2602 xmlUnlinkNode(node);
2603 xmlFreeNode(node);
2605 group = sa_get_parent_group(share);
2606 if (group != NULL &&
2607 sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
2608 sa_handle_impl_t impl_handle;
2609 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2610 if (impl_handle != NULL) {
2611 ret = sa_commit_share(impl_handle->scfhandle, group,
2612 share);
2613 } else {
2614 ret = SA_SYSTEM_ERR;
2617 return (ret);
2621 * fixproblemchars(string)
2623 * don't want any newline or tab characters in the text since these
2624 * could break display of data and legacy file formats.
2626 static void
2627 fixproblemchars(char *str)
2629 int c;
2630 for (c = *str; c != '\0'; c = *++str) {
2631 if (c == '\t' || c == '\n')
2632 *str = ' ';
2633 else if (c == '"')
2634 *str = '\'';
2639 * sa_get_share_description(share)
2641 * Return the description text for the specified share if it
2642 * exists. NULL if no description exists.
2645 char *
2646 sa_get_share_description(sa_share_t share)
2648 xmlChar *description = NULL;
2649 xmlNodePtr node;
2651 for (node = ((xmlNodePtr)share)->children; node != NULL;
2652 node = node->next) {
2653 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2654 break;
2657 if (node != NULL) {
2658 description = xmlNodeGetContent(node);
2659 fixproblemchars((char *)description);
2661 return ((char *)description);
2665 * sa_free(share_description(description)
2667 * Free the description string.
2670 void
2671 sa_free_share_description(char *description)
2673 xmlFree((xmlChar *)description);
2677 * sa_create_optionset(group, proto)
2679 * Create an optionset for the specified protocol in the specied
2680 * group. This is manifested as a property group within SMF.
2683 sa_optionset_t
2684 sa_create_optionset(sa_group_t group, char *proto)
2686 sa_optionset_t optionset;
2687 sa_group_t parent = group;
2688 sa_share_t share = NULL;
2689 int err = SA_OK;
2690 char *id = NULL;
2692 optionset = sa_get_optionset(group, proto);
2693 if (optionset != NULL) {
2694 /* can't have a duplicate protocol */
2695 optionset = NULL;
2696 } else {
2698 * Account for resource names being slightly
2699 * different.
2701 if (sa_is_share(group)) {
2703 * Transient shares do not have an "id" so not an
2704 * error to not find one.
2706 id = sa_get_share_attr((sa_share_t)group, "id");
2707 } else if (sa_is_resource(group)) {
2708 share = sa_get_resource_parent(
2709 (sa_resource_t)group);
2710 id = sa_get_resource_attr(share, "id");
2712 /* id can be NULL if the group is transient (ZFS) */
2713 if (id == NULL && sa_is_persistent(group))
2714 err = SA_NO_MEMORY;
2716 if (err == SA_NO_MEMORY) {
2718 * Couldn't get the id for the share or
2719 * resource. While this could be a
2720 * configuration issue, it is most likely an
2721 * out of memory. In any case, fail the create.
2723 return (NULL);
2726 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
2727 NULL, (xmlChar *)"optionset", NULL);
2729 * only put to repository if on a group and we were
2730 * able to create an optionset.
2732 if (optionset != NULL) {
2733 char oname[SA_STRSIZE];
2734 char *groupname;
2737 * Need to get parent group in all cases, but also get
2738 * the share if this is a resource.
2740 if (sa_is_share(group)) {
2741 parent = sa_get_parent_group((sa_share_t)group);
2742 } else if (sa_is_resource(group)) {
2743 share = sa_get_resource_parent(
2744 (sa_resource_t)group);
2745 parent = sa_get_parent_group(share);
2748 sa_set_optionset_attr(optionset, "type", proto);
2750 (void) sa_optionset_name(optionset, oname,
2751 sizeof (oname), id);
2752 groupname = sa_get_group_attr(parent, "name");
2753 if (groupname != NULL && sa_is_persistent(group)) {
2754 sa_handle_impl_t impl_handle;
2755 impl_handle =
2756 (sa_handle_impl_t)sa_find_group_handle(
2757 group);
2758 assert(impl_handle != NULL);
2759 if (impl_handle != NULL) {
2760 (void) sa_get_instance(
2761 impl_handle->scfhandle, groupname);
2762 (void) sa_create_pgroup(
2763 impl_handle->scfhandle, oname);
2766 if (groupname != NULL)
2767 sa_free_attr_string(groupname);
2771 if (id != NULL)
2772 sa_free_attr_string(id);
2773 return (optionset);
2777 * sa_get_property_parent(property)
2779 * Given a property, return the object it is a property of. This will
2780 * be an optionset of some type.
2783 static sa_optionset_t
2784 sa_get_property_parent(sa_property_t property)
2786 xmlNodePtr node = NULL;
2788 if (property != NULL)
2789 node = ((xmlNodePtr)property)->parent;
2790 return ((sa_optionset_t)node);
2794 * sa_get_optionset_parent(optionset)
2796 * Return the parent of the specified optionset. This could be a group
2797 * or a share.
2800 static sa_group_t
2801 sa_get_optionset_parent(sa_optionset_t optionset)
2803 xmlNodePtr node = NULL;
2805 if (optionset != NULL)
2806 node = ((xmlNodePtr)optionset)->parent;
2807 return ((sa_group_t)node);
2811 * zfs_needs_update(share)
2813 * In order to avoid making multiple updates to a ZFS share when
2814 * setting properties, the share attribute "changed" will be set to
2815 * true when a property is added or modified. When done adding
2816 * properties, we can then detect that an update is needed. We then
2817 * clear the state here to detect additional changes.
2820 static int
2821 zfs_needs_update(sa_share_t share)
2823 char *attr;
2824 int result = 0;
2826 attr = sa_get_share_attr(share, "changed");
2827 if (attr != NULL) {
2828 sa_free_attr_string(attr);
2829 result = 1;
2831 set_node_attr((void *)share, "changed", NULL);
2832 return (result);
2836 * zfs_set_update(share)
2838 * Set the changed attribute of the share to true.
2841 static void
2842 zfs_set_update(sa_share_t share)
2844 set_node_attr((void *)share, "changed", "true");
2848 * sa_commit_properties(optionset, clear)
2850 * Check if SMF or ZFS config and either update or abort the pending
2851 * changes.
2855 sa_commit_properties(sa_optionset_t optionset, int clear)
2857 sa_group_t group;
2858 sa_group_t parent;
2859 int zfs = 0;
2860 int needsupdate = 0;
2861 int ret = SA_OK;
2862 sa_handle_impl_t impl_handle;
2864 group = sa_get_optionset_parent(optionset);
2865 if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
2866 /* only update ZFS if on a share */
2867 parent = sa_get_parent_group(group);
2868 zfs++;
2869 if (parent != NULL && is_zfs_group(parent))
2870 needsupdate = zfs_needs_update(group);
2871 else
2872 zfs = 0;
2874 if (zfs) {
2875 if (!clear && needsupdate)
2876 ret = sa_zfs_update((sa_share_t)group);
2877 } else {
2878 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2879 if (impl_handle != NULL) {
2880 if (clear) {
2881 (void) sa_abort_transaction(
2882 impl_handle->scfhandle);
2883 } else {
2884 ret = sa_end_transaction(
2885 impl_handle->scfhandle, impl_handle);
2887 } else {
2888 ret = SA_SYSTEM_ERR;
2891 return (ret);
2895 * sa_destroy_optionset(optionset)
2897 * Remove the optionset from its group. Update the repository to
2898 * reflect this change.
2902 sa_destroy_optionset(sa_optionset_t optionset)
2904 char name[SA_STRSIZE];
2905 int len;
2906 int ret;
2907 char *id = NULL;
2908 sa_group_t group;
2909 int ispersist = 1;
2911 /* now delete the prop group */
2912 group = sa_get_optionset_parent(optionset);
2913 if (group != NULL) {
2914 if (sa_is_resource(group)) {
2915 sa_resource_t resource = group;
2916 sa_share_t share = sa_get_resource_parent(resource);
2917 group = sa_get_parent_group(share);
2918 id = sa_get_share_attr(share, "id");
2919 } else if (sa_is_share(group)) {
2920 id = sa_get_share_attr((sa_share_t)group, "id");
2922 ispersist = sa_is_persistent(group);
2924 if (ispersist) {
2925 sa_handle_impl_t impl_handle;
2926 len = sa_optionset_name(optionset, name, sizeof (name), id);
2927 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2928 if (impl_handle != NULL) {
2929 if (len > 0) {
2930 ret = sa_delete_pgroup(impl_handle->scfhandle,
2931 name);
2933 } else {
2934 ret = SA_SYSTEM_ERR;
2937 xmlUnlinkNode((xmlNodePtr)optionset);
2938 xmlFreeNode((xmlNodePtr)optionset);
2939 if (id != NULL)
2940 sa_free_attr_string(id);
2941 return (ret);
2944 /* private to the implementation */
2946 _sa_remove_optionset(sa_optionset_t optionset)
2948 int ret = SA_OK;
2950 xmlUnlinkNode((xmlNodePtr)optionset);
2951 xmlFreeNode((xmlNodePtr)optionset);
2952 return (ret);
2956 * sa_create_security(group, sectype, proto)
2958 * Create a security optionset (one that has a type name and a
2959 * proto). Security is left over from a pure NFS implementation. The
2960 * naming will change in the future when the API is released.
2962 sa_security_t
2963 sa_create_security(sa_group_t group, char *sectype, char *proto)
2965 sa_security_t security;
2966 char *id = NULL;
2967 sa_group_t parent;
2968 char *groupname = NULL;
2970 if (group != NULL && sa_is_share(group)) {
2971 id = sa_get_share_attr((sa_share_t)group, "id");
2972 parent = sa_get_parent_group(group);
2973 if (parent != NULL)
2974 groupname = sa_get_group_attr(parent, "name");
2975 } else if (group != NULL) {
2976 groupname = sa_get_group_attr(group, "name");
2979 security = sa_get_security(group, sectype, proto);
2980 if (security != NULL) {
2981 /* can't have a duplicate security option */
2982 security = NULL;
2983 } else {
2984 security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
2985 NULL, (xmlChar *)"security", NULL);
2986 if (security != NULL) {
2987 char oname[SA_STRSIZE];
2988 sa_set_security_attr(security, "type", proto);
2990 sa_set_security_attr(security, "sectype", sectype);
2991 (void) sa_security_name(security, oname,
2992 sizeof (oname), id);
2993 if (groupname != NULL && sa_is_persistent(group)) {
2994 sa_handle_impl_t impl_handle;
2995 impl_handle =
2996 (sa_handle_impl_t)sa_find_group_handle(
2997 group);
2998 if (impl_handle != NULL) {
2999 (void) sa_get_instance(
3000 impl_handle->scfhandle, groupname);
3001 (void) sa_create_pgroup(
3002 impl_handle->scfhandle, oname);
3007 if (id != NULL)
3008 sa_free_attr_string(id);
3009 if (groupname != NULL)
3010 sa_free_attr_string(groupname);
3011 return (security);
3015 * sa_destroy_security(security)
3017 * Remove the specified optionset from the document and the
3018 * configuration.
3022 sa_destroy_security(sa_security_t security)
3024 char name[SA_STRSIZE];
3025 int len;
3026 int ret = SA_OK;
3027 char *id = NULL;
3028 sa_group_t group;
3029 int iszfs = 0;
3030 int ispersist = 1;
3032 group = sa_get_optionset_parent(security);
3034 if (group != NULL)
3035 iszfs = sa_group_is_zfs(group);
3037 if (group != NULL && !iszfs) {
3038 if (sa_is_share(group))
3039 ispersist = sa_is_persistent(group);
3040 id = sa_get_share_attr((sa_share_t)group, "id");
3042 if (ispersist) {
3043 len = sa_security_name(security, name, sizeof (name), id);
3044 if (!iszfs && len > 0) {
3045 sa_handle_impl_t impl_handle;
3046 impl_handle =
3047 (sa_handle_impl_t)sa_find_group_handle(group);
3048 if (impl_handle != NULL) {
3049 ret = sa_delete_pgroup(impl_handle->scfhandle,
3050 name);
3051 } else {
3052 ret = SA_SYSTEM_ERR;
3056 xmlUnlinkNode((xmlNodePtr)security);
3057 xmlFreeNode((xmlNodePtr)security);
3058 if (iszfs)
3059 ret = sa_zfs_update(group);
3060 if (id != NULL)
3061 sa_free_attr_string(id);
3062 return (ret);
3066 * sa_get_security_attr(optionset, tag)
3068 * Return the specified attribute value from the optionset.
3071 char *
3072 sa_get_security_attr(sa_property_t optionset, char *tag)
3074 return (get_node_attr((void *)optionset, tag));
3079 * sa_set_security_attr(optionset, tag, value)
3081 * Set the optioset attribute specied by tag to the specified value.
3084 void
3085 sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
3087 set_node_attr((void *)optionset, tag, value);
3091 * is_nodetype(node, type)
3093 * Check to see if node is of the type specified.
3096 static int
3097 is_nodetype(void *node, char *type)
3099 return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
3103 * add_or_update()
3105 * Add or update a property. Pulled out of sa_set_prop_by_prop for
3106 * readability.
3108 static int
3109 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value,
3110 scf_transaction_entry_t *entry, char *name, char *valstr)
3112 int ret = SA_SYSTEM_ERR;
3114 if (value != NULL) {
3115 if (type == SA_PROP_OP_ADD)
3116 ret = scf_transaction_property_new(scf_handle->trans,
3117 entry, name, SCF_TYPE_ASTRING);
3118 else
3119 ret = scf_transaction_property_change(scf_handle->trans,
3120 entry, name, SCF_TYPE_ASTRING);
3121 if (ret == 0) {
3122 ret = scf_value_set_astring(value, valstr);
3123 if (ret == 0)
3124 ret = scf_entry_add_value(entry, value);
3125 if (ret == 0)
3126 return (ret);
3127 scf_value_destroy(value);
3128 } else {
3129 scf_entry_destroy(entry);
3132 return (SA_SYSTEM_ERR);
3136 * sa_set_prop_by_prop(optionset, group, prop, type)
3138 * Add/remove/update the specified property prop into the optionset or
3139 * share. If a share, sort out which property group based on GUID. In
3140 * all cases, the appropriate transaction is set (or ZFS share is
3141 * marked as needing an update)
3144 static int
3145 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
3146 sa_property_t prop, int type)
3148 char *name;
3149 char *valstr;
3150 int ret = SA_OK;
3151 scf_transaction_entry_t *entry;
3152 scf_value_t *value;
3153 int opttype; /* 1 == optionset, 0 == security */
3154 char *id = NULL;
3155 int iszfs = 0;
3156 sa_group_t parent = NULL;
3157 sa_share_t share = NULL;
3158 sa_handle_impl_t impl_handle;
3159 scfutilhandle_t *scf_handle;
3161 if (!sa_is_persistent(group)) {
3163 * if the group/share is not persistent we don't need
3164 * to do anything here
3166 return (SA_OK);
3168 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
3169 if (impl_handle == NULL || impl_handle->scfhandle == NULL)
3170 return (SA_SYSTEM_ERR);
3171 scf_handle = impl_handle->scfhandle;
3172 name = sa_get_property_attr(prop, "type");
3173 valstr = sa_get_property_attr(prop, "value");
3174 entry = scf_entry_create(scf_handle->handle);
3175 opttype = is_nodetype((void *)optionset, "optionset");
3178 * Check for share vs. resource since they need slightly
3179 * different treatment given the hierarchy.
3181 if (valstr != NULL && entry != NULL) {
3182 if (sa_is_share(group)) {
3183 parent = sa_get_parent_group(group);
3184 share = (sa_share_t)group;
3185 if (parent != NULL)
3186 iszfs = is_zfs_group(parent);
3187 } else if (sa_is_resource(group)) {
3188 share = sa_get_parent_group(group);
3189 if (share != NULL)
3190 parent = sa_get_parent_group(share);
3191 } else {
3192 iszfs = is_zfs_group(group);
3194 if (!iszfs) {
3195 if (scf_handle->trans == NULL) {
3196 char oname[SA_STRSIZE];
3197 char *groupname = NULL;
3198 if (share != NULL) {
3199 if (parent != NULL)
3200 groupname =
3201 sa_get_group_attr(parent,
3202 "name");
3203 id = sa_get_share_attr(
3204 (sa_share_t)share, "id");
3205 } else {
3206 groupname = sa_get_group_attr(group,
3207 "name");
3209 if (groupname != NULL) {
3210 ret = sa_get_instance(scf_handle,
3211 groupname);
3212 sa_free_attr_string(groupname);
3214 if (opttype)
3215 (void) sa_optionset_name(optionset,
3216 oname, sizeof (oname), id);
3217 else
3218 (void) sa_security_name(optionset,
3219 oname, sizeof (oname), id);
3220 ret = sa_start_transaction(scf_handle, oname);
3221 if (id != NULL)
3222 sa_free_attr_string(id);
3224 if (ret == SA_OK) {
3225 switch (type) {
3226 case SA_PROP_OP_REMOVE:
3227 ret = scf_transaction_property_delete(
3228 scf_handle->trans, entry, name);
3229 break;
3230 case SA_PROP_OP_ADD:
3231 case SA_PROP_OP_UPDATE:
3232 value = scf_value_create(
3233 scf_handle->handle);
3234 ret = add_or_update(scf_handle, type,
3235 value, entry, name, valstr);
3236 break;
3239 } else {
3241 * ZFS update. The calling function would have updated
3242 * the internal XML structure. Just need to flag it as
3243 * changed for ZFS.
3245 zfs_set_update((sa_share_t)group);
3249 if (name != NULL)
3250 sa_free_attr_string(name);
3251 if (valstr != NULL)
3252 sa_free_attr_string(valstr);
3253 else if (entry != NULL)
3254 scf_entry_destroy(entry);
3256 if (ret == -1)
3257 ret = SA_SYSTEM_ERR;
3259 return (ret);
3263 * sa_create_section(name, value)
3265 * Create a new section with the specified name and extra data.
3268 sa_property_t
3269 sa_create_section(char *name, char *extra)
3271 xmlNodePtr node;
3273 node = xmlNewNode(NULL, (xmlChar *)"section");
3274 if (node != NULL) {
3275 if (name != NULL)
3276 (void) xmlSetProp(node, (xmlChar *)"name",
3277 (xmlChar *)name);
3278 if (extra != NULL)
3279 (void) xmlSetProp(node, (xmlChar *)"extra",
3280 (xmlChar *)extra);
3282 return ((sa_property_t)node);
3285 void
3286 sa_set_section_attr(sa_property_t sect, char *name, char *value)
3288 (void) xmlSetProp(sect, (xmlChar *)name, (xmlChar *)value);
3292 * sa_create_property(section, name, value)
3294 * Create a new property with the specified name and value.
3297 sa_property_t
3298 sa_create_property(char *name, char *value)
3300 xmlNodePtr node;
3302 node = xmlNewNode(NULL, (xmlChar *)"option");
3303 if (node != NULL) {
3304 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
3305 (void) xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
3307 return ((sa_property_t)node);
3311 * sa_add_property(object, property)
3313 * Add the specified property to the object. Issue the appropriate
3314 * transaction or mark a ZFS object as needing an update.
3318 sa_add_property(void *object, sa_property_t property)
3320 int ret = SA_OK;
3321 sa_group_t parent;
3322 sa_group_t group;
3323 char *proto;
3325 if (property != NULL) {
3326 sa_handle_t handle;
3327 handle = sa_find_group_handle((sa_group_t)object);
3328 /* It is legitimate to not find a handle */
3329 proto = sa_get_optionset_attr(object, "type");
3330 if ((ret = sa_valid_property(handle, object, proto,
3331 property)) == SA_OK) {
3332 property = (sa_property_t)xmlAddChild(
3333 (xmlNodePtr)object, (xmlNodePtr)property);
3334 } else {
3335 if (proto != NULL)
3336 sa_free_attr_string(proto);
3337 return (ret);
3339 if (proto != NULL)
3340 sa_free_attr_string(proto);
3344 parent = sa_get_parent_group(object);
3345 if (!sa_is_persistent(parent))
3346 return (ret);
3348 if (sa_is_resource(parent)) {
3350 * Resources are children of share. Need to go up two
3351 * levels to find the group but the parent needs to be
3352 * the share at this point in order to get the "id".
3354 parent = sa_get_parent_group(parent);
3355 group = sa_get_parent_group(parent);
3356 } else if (sa_is_share(parent)) {
3357 group = sa_get_parent_group(parent);
3358 } else {
3359 group = parent;
3362 if (property == NULL) {
3363 ret = SA_NO_MEMORY;
3364 } else {
3365 char oname[SA_STRSIZE];
3367 if (!is_zfs_group(group)) {
3368 char *id = NULL;
3369 sa_handle_impl_t impl_handle;
3370 scfutilhandle_t *scf_handle;
3372 impl_handle = (sa_handle_impl_t)sa_find_group_handle(
3373 group);
3374 if (impl_handle == NULL ||
3375 impl_handle->scfhandle == NULL)
3376 ret = SA_SYSTEM_ERR;
3377 if (ret == SA_OK) {
3378 scf_handle = impl_handle->scfhandle;
3379 if (sa_is_share((sa_group_t)parent)) {
3380 id = sa_get_share_attr(
3381 (sa_share_t)parent, "id");
3383 if (scf_handle->trans == NULL) {
3384 if (is_nodetype(object, "optionset")) {
3385 (void) sa_optionset_name(
3386 (sa_optionset_t)object,
3387 oname, sizeof (oname), id);
3388 } else {
3389 (void) sa_security_name(
3390 (sa_optionset_t)object,
3391 oname, sizeof (oname), id);
3393 ret = sa_start_transaction(scf_handle,
3394 oname);
3396 if (ret == SA_OK) {
3397 char *name;
3398 char *value;
3399 name = sa_get_property_attr(property,
3400 "type");
3401 value = sa_get_property_attr(property,
3402 "value");
3403 if (name != NULL && value != NULL) {
3404 if (scf_handle->scf_state ==
3405 SCH_STATE_INIT) {
3406 ret = sa_set_property(
3407 scf_handle, name,
3408 value);
3410 } else {
3411 ret = SA_CONFIG_ERR;
3413 if (name != NULL)
3414 sa_free_attr_string(
3415 name);
3416 if (value != NULL)
3417 sa_free_attr_string(value);
3419 if (id != NULL)
3420 sa_free_attr_string(id);
3422 } else {
3424 * ZFS is a special case. We do want
3425 * to allow editing property/security
3426 * lists since we can have a better
3427 * syntax and we also want to keep
3428 * things consistent when possible.
3430 * Right now, we defer until the
3431 * sa_commit_properties so we can get
3432 * them all at once. We do need to
3433 * mark the share as "changed"
3435 zfs_set_update((sa_share_t)parent);
3438 return (ret);
3442 * sa_remove_property(property)
3444 * Remove the specied property from its containing object. Update the
3445 * repository as appropriate.
3449 sa_remove_property(sa_property_t property)
3451 int ret = SA_OK;
3453 if (property != NULL) {
3454 sa_optionset_t optionset;
3455 sa_group_t group;
3456 optionset = sa_get_property_parent(property);
3457 if (optionset != NULL) {
3458 group = sa_get_optionset_parent(optionset);
3459 if (group != NULL) {
3460 ret = sa_set_prop_by_prop(optionset, group,
3461 property, SA_PROP_OP_REMOVE);
3464 xmlUnlinkNode((xmlNodePtr)property);
3465 xmlFreeNode((xmlNodePtr)property);
3466 } else {
3467 ret = SA_NO_SUCH_PROP;
3469 return (ret);
3473 * sa_update_property(property, value)
3475 * Update the specified property to the new value. If value is NULL,
3476 * we currently treat this as a remove.
3480 sa_update_property(sa_property_t property, char *value)
3482 int ret = SA_OK;
3483 if (value == NULL) {
3484 return (sa_remove_property(property));
3485 } else {
3486 sa_optionset_t optionset;
3487 sa_group_t group;
3488 set_node_attr((void *)property, "value", value);
3489 optionset = sa_get_property_parent(property);
3490 if (optionset != NULL) {
3491 group = sa_get_optionset_parent(optionset);
3492 if (group != NULL) {
3493 ret = sa_set_prop_by_prop(optionset, group,
3494 property, SA_PROP_OP_UPDATE);
3496 } else {
3497 ret = SA_NO_SUCH_PROP;
3500 return (ret);
3504 * sa_get_protocol_section(propset, prop)
3506 * Get the specified protocol specific section. These are global to
3507 * the protocol and not specific to a group or share.
3510 sa_protocol_properties_t
3511 sa_get_protocol_section(sa_protocol_properties_t propset, char *section)
3513 xmlNodePtr node = (xmlNodePtr)propset;
3514 xmlChar *value = NULL;
3515 char *proto;
3517 proto = sa_get_optionset_attr(propset, "type");
3518 if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3519 if (proto != NULL)
3520 sa_free_attr_string(proto);
3521 return (propset);
3524 for (node = node->children; node != NULL;
3525 node = node->next) {
3526 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3527 if (section == NULL)
3528 break;
3529 value = xmlGetProp(node, (xmlChar *)"name");
3530 if (value != NULL &&
3531 xmlStrcasecmp(value, (xmlChar *)section) == 0) {
3532 break;
3534 if (value != NULL) {
3535 xmlFree(value);
3536 value = NULL;
3540 if (value != NULL)
3541 xmlFree(value);
3542 if (proto != NULL)
3543 sa_free_attr_string(proto);
3544 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"section") != 0) {
3546 * avoid a non option node -- it is possible to be a
3547 * text node
3549 node = NULL;
3551 return ((sa_protocol_properties_t)node);
3555 * sa_get_next_protocol_section(prop, find)
3557 * Get the next protocol specific section in the list.
3560 sa_property_t
3561 sa_get_next_protocol_section(sa_property_t prop, char *find)
3563 xmlNodePtr node;
3564 xmlChar *value = NULL;
3565 char *proto;
3567 proto = sa_get_optionset_attr(prop, "type");
3568 if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3569 if (proto != NULL)
3570 sa_free_attr_string(proto);
3571 return ((sa_property_t)NULL);
3574 for (node = ((xmlNodePtr)prop)->next; node != NULL;
3575 node = node->next) {
3576 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3577 if (find == NULL)
3578 break;
3579 value = xmlGetProp(node, (xmlChar *)"name");
3580 if (value != NULL &&
3581 xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3582 break;
3584 if (value != NULL) {
3585 xmlFree(value);
3586 value = NULL;
3591 if (value != NULL)
3592 xmlFree(value);
3593 if (proto != NULL)
3594 sa_free_attr_string(proto);
3595 return ((sa_property_t)node);
3599 * sa_get_protocol_property(propset, prop)
3601 * Get the specified protocol specific property. These are global to
3602 * the protocol and not specific to a group or share.
3605 sa_property_t
3606 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
3608 xmlNodePtr node = (xmlNodePtr)propset;
3609 xmlChar *value = NULL;
3611 if (propset == NULL)
3612 return (NULL);
3614 for (node = node->children; node != NULL;
3615 node = node->next) {
3616 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3617 if (prop == NULL)
3618 break;
3619 value = xmlGetProp(node, (xmlChar *)"type");
3620 if (value != NULL &&
3621 xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
3622 break;
3624 if (value != NULL) {
3625 xmlFree(value);
3626 value = NULL;
3630 if (value != NULL)
3631 xmlFree(value);
3632 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
3634 * avoid a non option node -- it is possible to be a
3635 * text node
3637 node = NULL;
3639 return ((sa_property_t)node);
3643 * sa_get_next_protocol_property(prop)
3645 * Get the next protocol specific property in the list.
3648 sa_property_t
3649 sa_get_next_protocol_property(sa_property_t prop, char *find)
3651 xmlNodePtr node;
3652 xmlChar *value = NULL;
3654 for (node = ((xmlNodePtr)prop)->next; node != NULL;
3655 node = node->next) {
3656 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3657 if (find == NULL)
3658 break;
3659 value = xmlGetProp(node, (xmlChar *)"type");
3660 if (value != NULL &&
3661 xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3662 break;
3664 if (value != NULL) {
3665 xmlFree(value);
3666 value = NULL;
3671 if (value != NULL)
3672 xmlFree(value);
3673 return ((sa_property_t)node);
3677 * sa_set_protocol_property(prop, value)
3679 * Set the specified property to have the new value. The protocol
3680 * specific plugin will then be called to update the property.
3684 sa_set_protocol_property(sa_property_t prop, char *section, char *value)
3686 sa_protocol_properties_t propset;
3687 char *proto;
3688 int ret = SA_INVALID_PROTOCOL;
3690 propset = ((xmlNodePtr)prop)->parent;
3691 if (propset != NULL) {
3692 proto = sa_get_optionset_attr(propset, "type");
3693 if (proto != NULL) {
3694 if (section != NULL)
3695 set_node_attr((xmlNodePtr)prop, "section",
3696 section);
3697 set_node_attr((xmlNodePtr)prop, "value", value);
3698 ret = sa_proto_set_property(proto, prop);
3699 sa_free_attr_string(proto);
3702 return (ret);
3706 * sa_add_protocol_property(propset, prop)
3708 * Add a new property to the protocol specific property set.
3712 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
3714 xmlNodePtr node;
3716 /* should check for legitimacy */
3717 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
3718 if (node != NULL)
3719 return (SA_OK);
3720 return (SA_NO_MEMORY);
3724 * sa_create_protocol_properties(proto)
3726 * Create a protocol specific property set.
3729 sa_protocol_properties_t
3730 sa_create_protocol_properties(char *proto)
3732 xmlNodePtr node;
3734 node = xmlNewNode(NULL, (xmlChar *)"propertyset");
3735 if (node != NULL)
3736 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
3737 return (node);
3741 * sa_get_share_resource(share, resource)
3743 * Get the named resource from the share, if it exists. If resource is
3744 * NULL, get the first resource.
3747 sa_resource_t
3748 sa_get_share_resource(sa_share_t share, char *resource)
3750 xmlNodePtr node = NULL;
3751 xmlChar *name;
3753 if (share != NULL) {
3754 for (node = ((xmlNodePtr)share)->children; node != NULL;
3755 node = node->next) {
3756 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) {
3757 if (resource == NULL) {
3759 * We are looking for the first
3760 * resource node and not a names
3761 * resource.
3763 break;
3764 } else {
3765 /* is it the correct share? */
3766 name = xmlGetProp(node,
3767 (xmlChar *)"name");
3768 if (name != NULL &&
3769 xmlStrcasecmp(name,
3770 (xmlChar *)resource) == 0) {
3771 xmlFree(name);
3772 break;
3774 xmlFree(name);
3779 return ((sa_resource_t)node);
3783 * sa_get_next_resource(resource)
3784 * Return the next share following the specified share
3785 * from the internal list of shares. Returns NULL if there
3786 * are no more shares. The list is relative to the same
3787 * group.
3789 sa_share_t
3790 sa_get_next_resource(sa_resource_t resource)
3792 xmlNodePtr node = NULL;
3794 if (resource != NULL) {
3795 for (node = ((xmlNodePtr)resource)->next; node != NULL;
3796 node = node->next) {
3797 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
3798 break;
3801 return ((sa_share_t)node);
3805 * _sa_get_next_resource_index(share)
3807 * get the next resource index number (one greater then current largest)
3810 static int
3811 _sa_get_next_resource_index(sa_share_t share)
3813 sa_resource_t resource;
3814 int index = 0;
3815 char *id;
3817 for (resource = sa_get_share_resource(share, NULL);
3818 resource != NULL;
3819 resource = sa_get_next_resource(resource)) {
3820 id = get_node_attr((void *)resource, "id");
3821 if (id != NULL) {
3822 int val;
3823 val = atoi(id);
3824 if (val > index)
3825 index = val;
3826 sa_free_attr_string(id);
3829 return (index + 1);
3834 * sa_add_resource(share, resource, persist, &err)
3836 * Adds a new resource name associated with share. The resource name
3837 * must be unique in the system and will be case insensitive (eventually).
3840 sa_resource_t
3841 sa_add_resource(sa_share_t share, char *resource, int persist, int *error)
3843 xmlNodePtr node;
3844 int err = SA_OK;
3845 sa_resource_t res;
3846 sa_group_t group;
3847 sa_handle_t handle;
3848 char istring[8]; /* just big enough for an integer value */
3849 int index;
3851 group = sa_get_parent_group(share);
3852 handle = sa_find_group_handle(group);
3853 res = sa_find_resource(handle, resource);
3854 if (res != NULL) {
3855 err = SA_DUPLICATE_NAME;
3856 res = NULL;
3857 } else {
3858 node = xmlNewChild((xmlNodePtr)share, NULL,
3859 (xmlChar *)"resource", NULL);
3860 if (node != NULL) {
3861 (void) xmlSetProp(node, (xmlChar *)"name",
3862 (xmlChar *)resource);
3863 (void) xmlSetProp(node, (xmlChar *)"type", persist ?
3864 (xmlChar *)"persist" : (xmlChar *)"transient");
3865 if (persist != SA_SHARE_TRANSIENT) {
3866 index = _sa_get_next_resource_index(share);
3867 (void) snprintf(istring, sizeof (istring), "%d",
3868 index);
3869 (void) xmlSetProp(node, (xmlChar *)"id",
3870 (xmlChar *)istring);
3872 if (!sa_is_persistent((sa_group_t)share))
3873 goto done;
3875 if (!sa_group_is_zfs(group)) {
3876 /* ZFS doesn't use resource names */
3877 sa_handle_impl_t ihandle;
3879 ihandle = (sa_handle_impl_t)
3880 sa_find_group_handle(
3881 group);
3882 if (ihandle != NULL)
3883 err = sa_commit_share(
3884 ihandle->scfhandle, group,
3885 share);
3886 else
3887 err = SA_SYSTEM_ERR;
3888 } else {
3889 err = sa_zfs_update((sa_share_t)group);
3894 done:
3895 if (error != NULL)
3896 *error = err;
3897 return ((sa_resource_t)node);
3901 * sa_remove_resource(resource)
3903 * Remove the resource name from the share (and the system)
3907 sa_remove_resource(sa_resource_t resource)
3909 sa_share_t share;
3910 sa_group_t group;
3911 char *type;
3912 int ret = SA_OK;
3913 boolean_t transient = B_FALSE;
3914 sa_optionset_t opt;
3916 share = sa_get_resource_parent(resource);
3917 type = sa_get_share_attr(share, "type");
3918 group = sa_get_parent_group(share);
3921 if (type != NULL) {
3922 if (strcmp(type, "persist") != 0)
3923 transient = B_TRUE;
3924 sa_free_attr_string(type);
3927 /* Disable the resource for all protocols. */
3928 (void) sa_disable_resource(resource, NULL);
3930 /* Remove any optionsets from the resource. */
3931 for (opt = sa_get_optionset(resource, NULL);
3932 opt != NULL;
3933 opt = sa_get_next_optionset(opt))
3934 (void) sa_destroy_optionset(opt);
3936 /* Remove from the share */
3937 xmlUnlinkNode((xmlNode *)resource);
3938 xmlFreeNode((xmlNode *)resource);
3940 /* only do SMF action if permanent and not ZFS */
3941 if (transient)
3942 return (ret);
3944 if (!sa_group_is_zfs(group)) {
3945 sa_handle_impl_t ihandle;
3946 ihandle = (sa_handle_impl_t)sa_find_group_handle(group);
3947 if (ihandle != NULL)
3948 ret = sa_commit_share(ihandle->scfhandle, group, share);
3949 else
3950 ret = SA_SYSTEM_ERR;
3951 } else {
3952 ret = sa_zfs_update((sa_share_t)group);
3955 return (ret);
3959 * proto_rename_resource(handle, group, resource, newname)
3961 * Helper function for sa_rename_resource that notifies the protocol
3962 * of a resource name change prior to a config repository update.
3964 static int
3965 proto_rename_resource(sa_handle_t handle, sa_group_t group,
3966 sa_resource_t resource, char *newname)
3968 sa_optionset_t optionset;
3969 int ret = SA_OK;
3970 int err;
3972 for (optionset = sa_get_optionset(group, NULL);
3973 optionset != NULL;
3974 optionset = sa_get_next_optionset(optionset)) {
3975 char *type;
3976 type = sa_get_optionset_attr(optionset, "type");
3977 if (type != NULL) {
3978 err = sa_proto_rename_resource(handle, type, resource,
3979 newname);
3980 if (err != SA_OK)
3981 ret = err;
3982 sa_free_attr_string(type);
3985 return (ret);
3989 * sa_rename_resource(resource, newname)
3991 * Rename the resource to the new name, if it is unique.
3995 sa_rename_resource(sa_resource_t resource, char *newname)
3997 sa_share_t share;
3998 sa_group_t group = NULL;
3999 sa_resource_t target;
4000 int ret = SA_CONFIG_ERR;
4001 sa_handle_t handle = NULL;
4003 share = sa_get_resource_parent(resource);
4004 if (share == NULL)
4005 return (ret);
4007 group = sa_get_parent_group(share);
4008 if (group == NULL)
4009 return (ret);
4011 handle = (sa_handle_impl_t)sa_find_group_handle(group);
4012 if (handle == NULL)
4013 return (ret);
4015 target = sa_find_resource(handle, newname);
4016 if (target != NULL) {
4017 ret = SA_DUPLICATE_NAME;
4018 } else {
4020 * Everything appears to be valid at this
4021 * point. Change the name of the active share and then
4022 * update the share in the appropriate repository.
4024 ret = proto_rename_resource(handle, group, resource, newname);
4025 set_node_attr(resource, "name", newname);
4027 if (!sa_is_persistent((sa_group_t)share))
4028 return (ret);
4030 if (!sa_group_is_zfs(group)) {
4031 sa_handle_impl_t ihandle = (sa_handle_impl_t)handle;
4032 ret = sa_commit_share(ihandle->scfhandle, group,
4033 share);
4034 } else {
4035 ret = sa_zfs_update((sa_share_t)group);
4038 return (ret);
4042 * sa_get_resource_attr(resource, tag)
4044 * Get the named attribute of the resource. "name" and "id" are
4045 * currently defined. NULL if tag not defined.
4048 char *
4049 sa_get_resource_attr(sa_resource_t resource, char *tag)
4051 return (get_node_attr((void *)resource, tag));
4055 * sa_set_resource_attr(resource, tag, value)
4057 * Get the named attribute of the resource. "name" and "id" are
4058 * currently defined. NULL if tag not defined. Currently we don't do
4059 * much, but additional checking may be needed in the future.
4063 sa_set_resource_attr(sa_resource_t resource, char *tag, char *value)
4065 set_node_attr((void *)resource, tag, value);
4066 return (SA_OK);
4070 * sa_get_resource_parent(resource_t)
4072 * Returns the share associated with the resource.
4075 sa_share_t
4076 sa_get_resource_parent(sa_resource_t resource)
4078 sa_share_t share = NULL;
4080 if (resource != NULL)
4081 share = (sa_share_t)((xmlNodePtr)resource)->parent;
4082 return (share);
4086 * find_resource(group, name)
4088 * Find the resource within the group.
4091 static sa_resource_t
4092 find_resource(sa_group_t group, char *resname)
4094 sa_share_t share;
4095 sa_resource_t resource = NULL;
4096 char *name;
4098 /* Iterate over all the shares and resources in the group. */
4099 for (share = sa_get_share(group, NULL);
4100 share != NULL && resource == NULL;
4101 share = sa_get_next_share(share)) {
4102 for (resource = sa_get_share_resource(share, NULL);
4103 resource != NULL;
4104 resource = sa_get_next_resource(resource)) {
4105 name = sa_get_resource_attr(resource, "name");
4106 if (name != NULL && xmlStrcasecmp((xmlChar*)name,
4107 (xmlChar*)resname) == 0) {
4108 sa_free_attr_string(name);
4109 break;
4111 if (name != NULL) {
4112 sa_free_attr_string(name);
4116 return (resource);
4120 * sa_find_resource(name)
4122 * Find the named resource in the system.
4125 sa_resource_t
4126 sa_find_resource(sa_handle_t handle, char *name)
4128 sa_group_t group;
4129 sa_group_t zgroup;
4130 sa_resource_t resource = NULL;
4133 * Iterate over all groups and zfs subgroups and check for
4134 * resource name in them.
4136 for (group = sa_get_group(handle, NULL); group != NULL;
4137 group = sa_get_next_group(group)) {
4139 if (is_zfs_group(group)) {
4140 for (zgroup =
4141 (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
4142 (xmlChar *)"group");
4143 zgroup != NULL && resource == NULL;
4144 zgroup = sa_get_next_group(zgroup)) {
4145 resource = find_resource(zgroup, name);
4147 } else {
4148 resource = find_resource(group, name);
4150 if (resource != NULL)
4151 break;
4153 return (resource);
4157 * sa_get_resource(group, resource)
4159 * Search all the shares in the specified group for a share with a
4160 * resource name matching the one specified.
4162 * In the future, it may be advantageous to allow group to be NULL and
4163 * search all groups but that isn't needed at present.
4166 sa_resource_t
4167 sa_get_resource(sa_group_t group, char *resource)
4169 sa_share_t share = NULL;
4170 sa_resource_t res = NULL;
4172 if (resource != NULL) {
4173 for (share = sa_get_share(group, NULL);
4174 share != NULL && res == NULL;
4175 share = sa_get_next_share(share)) {
4176 res = sa_get_share_resource(share, resource);
4179 return (res);
4183 * get_protocol_list(optionset, object)
4185 * Get the protocol optionset list for the object and add them as
4186 * properties to optionset.
4188 static int
4189 get_protocol_list(sa_optionset_t optionset, void *object)
4191 sa_property_t prop;
4192 sa_optionset_t opts;
4193 int ret = SA_OK;
4195 for (opts = sa_get_optionset(object, NULL);
4196 opts != NULL;
4197 opts = sa_get_next_optionset(opts)) {
4198 char *type;
4199 type = sa_get_optionset_attr(opts, "type");
4201 * It is possible to have a non-protocol optionset. We
4202 * skip any of those found.
4204 if (type == NULL)
4205 continue;
4206 prop = sa_create_property(type, "true");
4207 sa_free_attr_string(type);
4208 if (prop != NULL)
4209 prop = (sa_property_t)xmlAddChild((xmlNodePtr)optionset,
4210 (xmlNodePtr)prop);
4211 /* If prop is NULL, don't bother continuing */
4212 if (prop == NULL) {
4213 ret = SA_NO_MEMORY;
4214 break;
4217 return (ret);
4221 * sa_free_protoset(optionset)
4223 * Free the protocol property optionset.
4225 static void
4226 sa_free_protoset(sa_optionset_t optionset)
4228 if (optionset != NULL) {
4229 xmlUnlinkNode((xmlNodePtr) optionset);
4230 xmlFreeNode((xmlNodePtr) optionset);
4235 * sa_optionset_t sa_get_active_protocols(object)
4237 * Return a list of the protocols that are active for the object.
4238 * This is currently an internal helper function, but could be
4239 * made visible if there is enough demand for it.
4241 * The function finds the parent group and extracts the protocol
4242 * optionsets creating a new optionset with the protocols as properties.
4244 * The caller must free the returned optionset.
4247 static sa_optionset_t
4248 sa_get_active_protocols(void *object)
4250 sa_optionset_t options;
4251 sa_share_t share = NULL;
4252 sa_group_t group = NULL;
4253 sa_resource_t resource = NULL;
4254 int ret = SA_OK;
4256 if (object == NULL)
4257 return (NULL);
4258 options = (sa_optionset_t)xmlNewNode(NULL, (xmlChar *)"optionset");
4259 if (options == NULL)
4260 return (NULL);
4263 * Find the objects up the tree that might have protocols
4264 * enabled on them.
4266 if (sa_is_resource(object)) {
4267 resource = (sa_resource_t)object;
4268 share = sa_get_resource_parent(resource);
4269 group = sa_get_parent_group(share);
4270 } else if (sa_is_share(object)) {
4271 share = (sa_share_t)object;
4272 group = sa_get_parent_group(share);
4273 } else {
4274 group = (sa_group_t)group;
4276 if (resource != NULL)
4277 ret = get_protocol_list(options, resource);
4278 if (ret == SA_OK && share != NULL)
4279 ret = get_protocol_list(options, share);
4280 if (ret == SA_OK && group != NULL)
4281 ret = get_protocol_list(options, group);
4284 * If there was an error, we won't have a complete list so
4285 * abandon everything. The caller will have to deal with the
4286 * issue.
4288 if (ret != SA_OK) {
4289 sa_free_protoset(options);
4290 options = NULL;
4292 return (options);
4296 * sa_enable_resource, protocol)
4297 * Disable the specified share to the specified protocol.
4298 * If protocol is NULL, then all protocols.
4301 sa_enable_resource(sa_resource_t resource, char *protocol)
4303 int ret = SA_OK;
4305 if (protocol != NULL) {
4306 ret = sa_proto_share_resource(protocol, resource);
4307 } else {
4308 sa_optionset_t protoset;
4309 sa_property_t prop;
4310 char *proto;
4311 int err;
4313 /* need to do all protocols */
4314 protoset = sa_get_active_protocols(resource);
4315 if (protoset == NULL)
4316 return (SA_NO_MEMORY);
4317 for (prop = sa_get_property(protoset, NULL);
4318 prop != NULL;
4319 prop = sa_get_next_property(prop)) {
4320 proto = sa_get_property_attr(prop, "type");
4321 if (proto == NULL) {
4322 ret = SA_NO_MEMORY;
4323 continue;
4325 err = sa_proto_share_resource(proto, resource);
4326 if (err != SA_OK)
4327 ret = err;
4328 sa_free_attr_string(proto);
4330 sa_free_protoset(protoset);
4332 if (ret == SA_OK)
4333 (void) sa_set_resource_attr(resource, "shared", NULL);
4335 return (ret);
4339 * sa_disable_resource(resource, protocol)
4341 * Disable the specified share for the specified protocol. If
4342 * protocol is NULL, then all protocols. If the underlying
4343 * protocol doesn't implement disable at the resource level, we
4344 * disable at the share level.
4347 sa_disable_resource(sa_resource_t resource, char *protocol)
4349 int ret = SA_OK;
4351 if (protocol != NULL) {
4352 ret = sa_proto_unshare_resource(protocol, resource);
4353 if (ret == SA_NOT_IMPLEMENTED) {
4354 sa_share_t parent;
4356 * The protocol doesn't implement unshare
4357 * resource. That implies that resource names are
4358 * simple aliases for this protocol so we need to
4359 * unshare the share.
4361 parent = sa_get_resource_parent(resource);
4362 if (parent != NULL)
4363 ret = sa_disable_share(parent, protocol);
4364 else
4365 ret = SA_CONFIG_ERR;
4367 } else {
4368 sa_optionset_t protoset;
4369 sa_property_t prop;
4370 char *proto;
4371 int err;
4373 /* need to do all protocols */
4374 protoset = sa_get_active_protocols(resource);
4375 if (protoset == NULL)
4376 return (SA_NO_MEMORY);
4377 for (prop = sa_get_property(protoset, NULL);
4378 prop != NULL;
4379 prop = sa_get_next_property(prop)) {
4380 proto = sa_get_property_attr(prop, "type");
4381 if (proto == NULL) {
4382 ret = SA_NO_MEMORY;
4383 continue;
4385 err = sa_proto_unshare_resource(proto, resource);
4386 if (err == SA_NOT_SUPPORTED) {
4387 sa_share_t parent;
4388 parent = sa_get_resource_parent(resource);
4389 if (parent != NULL)
4390 err = sa_disable_share(parent, proto);
4391 else
4392 err = SA_CONFIG_ERR;
4394 if (err != SA_OK)
4395 ret = err;
4396 sa_free_attr_string(proto);
4398 sa_free_protoset(protoset);
4400 if (ret == SA_OK)
4401 (void) sa_set_resource_attr(resource, "shared", NULL);
4403 return (ret);
4407 * sa_set_resource_description(resource, content)
4409 * Set the description of share to content.
4413 sa_set_resource_description(sa_resource_t resource, char *content)
4415 xmlNodePtr node;
4416 sa_group_t group;
4417 sa_share_t share;
4418 int ret = SA_OK;
4420 for (node = ((xmlNodePtr)resource)->children;
4421 node != NULL;
4422 node = node->next) {
4423 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
4424 break;
4428 /* no existing description but want to add */
4429 if (node == NULL && content != NULL) {
4430 /* add a description */
4431 node = _sa_set_share_description(resource, content);
4432 } else if (node != NULL && content != NULL) {
4433 /* update a description */
4434 xmlNodeSetContent(node, (xmlChar *)content);
4435 } else if (node != NULL && content == NULL) {
4436 /* remove an existing description */
4437 xmlUnlinkNode(node);
4438 xmlFreeNode(node);
4441 share = sa_get_resource_parent(resource);
4442 group = sa_get_parent_group(share);
4443 if (group != NULL &&
4444 sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
4445 sa_handle_impl_t impl_handle;
4446 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
4447 if (impl_handle != NULL)
4448 ret = sa_commit_share(impl_handle->scfhandle,
4449 group, share);
4450 else
4451 ret = SA_SYSTEM_ERR;
4453 return (ret);
4457 * sa_get_resource_description(share)
4459 * Return the description text for the specified share if it
4460 * exists. NULL if no description exists.
4463 char *
4464 sa_get_resource_description(sa_resource_t resource)
4466 xmlChar *description = NULL;
4467 xmlNodePtr node;
4469 for (node = ((xmlNodePtr)resource)->children; node != NULL;
4470 node = node->next) {
4471 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0)
4472 break;
4474 if (node != NULL) {
4475 description = xmlNodeGetContent(node);
4476 fixproblemchars((char *)description);
4478 return ((char *)description);