7955 libshare needs to initialize only those datasets being modified by the consumer
[unleashed.git] / usr / src / lib / libshare / common / libshare.c
blob5a58e0268eef14b38cd54050729a4e593f22d756
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 if (group != NULL)
1266 free(group);
1267 return ((sa_group_t)(node));
1271 * sa_get_next_group(group)
1272 * Return the "next" group after the specified group from
1273 * the internal group list. NULL if there are no more.
1275 sa_group_t
1276 sa_get_next_group(sa_group_t group)
1278 xmlNodePtr ngroup = NULL;
1279 if (group != NULL) {
1280 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
1281 ngroup = ngroup->next) {
1282 if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
1283 break;
1286 return ((sa_group_t)ngroup);
1290 * sa_get_share(group, sharepath)
1291 * Return the share object for the share specified. The share
1292 * must be in the specified group. Return NULL if not found.
1294 sa_share_t
1295 sa_get_share(sa_group_t group, char *sharepath)
1297 xmlNodePtr node = NULL;
1298 xmlChar *path;
1301 * For future scalability, this should end up building a cache
1302 * since it will get called regularly by the mountd and info
1303 * services.
1305 if (group != NULL) {
1306 for (node = ((xmlNodePtr)group)->children; node != NULL;
1307 node = node->next) {
1308 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1309 if (sharepath == NULL) {
1310 break;
1311 } else {
1312 /* is it the correct share? */
1313 path = xmlGetProp(node,
1314 (xmlChar *)"path");
1315 if (path != NULL &&
1316 xmlStrcmp(path,
1317 (xmlChar *)sharepath) == 0) {
1318 xmlFree(path);
1319 break;
1321 xmlFree(path);
1326 return ((sa_share_t)node);
1330 * sa_get_next_share(share)
1331 * Return the next share following the specified share
1332 * from the internal list of shares. Returns NULL if there
1333 * are no more shares. The list is relative to the same
1334 * group.
1336 sa_share_t
1337 sa_get_next_share(sa_share_t share)
1339 xmlNodePtr node = NULL;
1341 if (share != NULL) {
1342 for (node = ((xmlNodePtr)share)->next; node != NULL;
1343 node = node->next) {
1344 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1345 break;
1349 return ((sa_share_t)node);
1353 * _sa_get_child_node(node, type)
1355 * find the child node of the specified node that has "type". This is
1356 * used to implement several internal functions.
1359 static xmlNodePtr
1360 _sa_get_child_node(xmlNodePtr node, xmlChar *type)
1362 xmlNodePtr child;
1363 for (child = node->xmlChildrenNode; child != NULL;
1364 child = child->next)
1365 if (xmlStrcmp(child->name, type) == 0)
1366 return (child);
1367 return ((xmlNodePtr)NULL);
1371 * find_share(group, path)
1373 * Search all the shares in the specified group for one that has the
1374 * specified path.
1377 static sa_share_t
1378 find_share(sa_group_t group, char *sharepath)
1380 sa_share_t share;
1381 char *path;
1383 for (share = sa_get_share(group, NULL); share != NULL;
1384 share = sa_get_next_share(share)) {
1385 path = sa_get_share_attr(share, "path");
1386 if (path != NULL && strcmp(path, sharepath) == 0) {
1387 sa_free_attr_string(path);
1388 break;
1390 if (path != NULL)
1391 sa_free_attr_string(path);
1393 return (share);
1397 * sa_get_sub_group(group)
1399 * Get the first sub-group of group. The sa_get_next_group() function
1400 * can be used to get the rest. This is currently only used for ZFS
1401 * sub-groups but could be used to implement a more general mechanism.
1404 sa_group_t
1405 sa_get_sub_group(sa_group_t group)
1407 return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1408 (xmlChar *)"group"));
1412 * sa_find_share(sharepath)
1413 * Finds a share regardless of group. In the future, this
1414 * function should utilize a cache and hash table of some kind.
1415 * The current assumption is that a path will only be shared
1416 * once. In the future, this may change as implementation of
1417 * resource names comes into being.
1419 sa_share_t
1420 sa_find_share(sa_handle_t handle, char *sharepath)
1422 sa_group_t group;
1423 sa_group_t zgroup;
1424 sa_share_t share = NULL;
1425 int done = 0;
1427 for (group = sa_get_group(handle, NULL); group != NULL && !done;
1428 group = sa_get_next_group(group)) {
1429 if (is_zfs_group(group)) {
1430 for (zgroup =
1431 (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1432 (xmlChar *)"group");
1433 zgroup != NULL;
1434 zgroup = sa_get_next_group(zgroup)) {
1435 share = find_share(zgroup, sharepath);
1436 if (share != NULL)
1437 break;
1439 } else {
1440 share = find_share(group, sharepath);
1442 if (share != NULL)
1443 break;
1445 return (share);
1449 * sa_check_path(group, path, strictness)
1451 * Check that path is a valid path relative to the group. Currently,
1452 * we are ignoring the group and checking only the NFS rules. Later,
1453 * we may want to use the group to then check against the protocols
1454 * enabled on the group. The strictness values mean:
1455 * SA_CHECK_NORMAL == only check newpath against shares that are active
1456 * SA_CHECK_STRICT == check newpath against both active shares and those
1457 * stored in the repository
1461 sa_check_path(sa_group_t group, char *path, int strictness)
1463 sa_handle_t handle;
1465 handle = sa_find_group_handle(group);
1466 if (handle == NULL)
1467 return (SA_BAD_PATH);
1469 return (validpath(handle, path, strictness));
1473 * mark_excluded_protos(group, share, flags)
1475 * Walk through all the protocols enabled for the group and check to
1476 * see if the share has any of them should be in the exclude list
1477 * based on the featureset of the protocol. If there are any, add the
1478 * "exclude" property to the share.
1480 static void
1481 mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags)
1483 sa_optionset_t optionset;
1484 char exclude_list[SA_STRSIZE];
1485 char *sep = "";
1487 exclude_list[0] = '\0';
1488 for (optionset = sa_get_optionset(group, NULL);
1489 optionset != NULL;
1490 optionset = sa_get_next_optionset(optionset)) {
1491 char *value;
1492 uint64_t features;
1493 value = sa_get_optionset_attr(optionset, "type");
1494 if (value == NULL)
1495 continue;
1496 features = sa_proto_get_featureset(value);
1497 if (!(features & flags)) {
1498 (void) strlcat(exclude_list, sep,
1499 sizeof (exclude_list));
1500 (void) strlcat(exclude_list, value,
1501 sizeof (exclude_list));
1502 sep = ",";
1504 sa_free_attr_string(value);
1506 if (exclude_list[0] != '\0')
1507 (void) xmlSetProp(share, (xmlChar *)"exclude",
1508 (xmlChar *)exclude_list);
1512 * get_all_features(group)
1514 * Walk through all the protocols on the group and collect all
1515 * possible enabled features. This is the OR of all the featuresets.
1517 static uint64_t
1518 get_all_features(sa_group_t group)
1520 sa_optionset_t optionset;
1521 uint64_t features = 0;
1523 for (optionset = sa_get_optionset(group, NULL);
1524 optionset != NULL;
1525 optionset = sa_get_next_optionset(optionset)) {
1526 char *value;
1527 value = sa_get_optionset_attr(optionset, "type");
1528 if (value == NULL)
1529 continue;
1530 features |= sa_proto_get_featureset(value);
1531 sa_free_attr_string(value);
1533 return (features);
1538 * _sa_add_share(group, sharepath, persist, *error, flags)
1540 * Common code for all types of add_share. sa_add_share() is the
1541 * public API, we also need to be able to do this when parsing legacy
1542 * files and construction of the internal configuration while
1543 * extracting config info from SMF. "flags" indicates if some
1544 * protocols need relaxed rules while other don't. These values are
1545 * the featureset values defined in libshare.h.
1548 sa_share_t
1549 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error,
1550 uint64_t flags)
1552 xmlNodePtr node = NULL;
1553 int err;
1555 err = SA_OK; /* assume success */
1557 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL);
1558 if (node == NULL) {
1559 if (error != NULL)
1560 *error = SA_NO_MEMORY;
1561 return (node);
1564 (void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
1565 (void) xmlSetProp(node, (xmlChar *)"type",
1566 persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
1567 if (flags != 0)
1568 mark_excluded_protos(group, node, flags);
1569 if (persist != SA_SHARE_TRANSIENT) {
1571 * persistent shares come in two flavors: SMF and
1572 * ZFS. Sort this one out based on target group and
1573 * path type. Both NFS and SMB are supported. First,
1574 * check to see if the protocol is enabled on the
1575 * subgroup and then setup the share appropriately.
1577 if (sa_group_is_zfs(group) &&
1578 sa_path_is_zfs(sharepath)) {
1579 if (sa_get_optionset(group, "nfs") != NULL)
1580 err = sa_zfs_set_sharenfs(group, sharepath, 1);
1581 else if (sa_get_optionset(group, "smb") != NULL)
1582 err = sa_zfs_set_sharesmb(group, sharepath, 1);
1583 } else {
1584 sa_handle_impl_t impl_handle;
1585 impl_handle =
1586 (sa_handle_impl_t)sa_find_group_handle(group);
1587 if (impl_handle != NULL) {
1588 err = sa_commit_share(impl_handle->scfhandle,
1589 group, (sa_share_t)node);
1590 } else {
1591 err = SA_SYSTEM_ERR;
1595 if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER)
1596 /* called by the dfstab parser so could be a show */
1597 err = SA_OK;
1599 if (err != SA_OK) {
1601 * we couldn't commit to the repository so undo
1602 * our internal state to reflect reality.
1604 xmlUnlinkNode(node);
1605 xmlFreeNode(node);
1606 node = NULL;
1609 if (error != NULL)
1610 *error = err;
1612 return (node);
1616 * sa_add_share(group, sharepath, persist, *error)
1618 * Add a new share object to the specified group. The share will
1619 * have the specified sharepath and will only be constructed if
1620 * it is a valid path to be shared. NULL is returned on error
1621 * and a detailed error value will be returned via the error
1622 * pointer.
1624 sa_share_t
1625 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1627 xmlNodePtr node = NULL;
1628 int strictness = SA_CHECK_NORMAL;
1629 sa_handle_t handle;
1630 uint64_t special = 0;
1631 uint64_t features;
1634 * If the share is to be permanent, use strict checking so a
1635 * bad config doesn't get created. Transient shares only need
1636 * to check against the currently active
1637 * shares. SA_SHARE_PARSER is a modifier used internally to
1638 * indicate that we are being called by the dfstab parser and
1639 * that we need strict checking in all cases. Normally persist
1640 * is in integer value but SA_SHARE_PARSER may be or'd into
1641 * it as an override.
1643 if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT)
1644 strictness = SA_CHECK_STRICT;
1646 handle = sa_find_group_handle(group);
1649 * need to determine if the share is valid. The rules are:
1650 * - The path must not already exist
1651 * - The path must not be a subdir or parent dir of an
1652 * existing path unless at least one protocol allows it.
1653 * The sub/parent check is done in sa_check_path().
1656 if (sa_find_share(handle, sharepath) == NULL) {
1657 *error = sa_check_path(group, sharepath, strictness);
1658 features = get_all_features(group);
1659 switch (*error) {
1660 case SA_PATH_IS_SUBDIR:
1661 if (features & SA_FEATURE_ALLOWSUBDIRS)
1662 special |= SA_FEATURE_ALLOWSUBDIRS;
1663 break;
1664 case SA_PATH_IS_PARENTDIR:
1665 if (features & SA_FEATURE_ALLOWPARDIRS)
1666 special |= SA_FEATURE_ALLOWPARDIRS;
1667 break;
1669 if (*error == SA_OK || special != SA_FEATURE_NONE)
1670 node = _sa_add_share(group, sharepath, persist,
1671 error, special);
1672 } else {
1673 *error = SA_DUPLICATE_NAME;
1676 return ((sa_share_t)node);
1680 * sa_enable_share(share, protocol)
1681 * Enable the specified share to the specified protocol.
1682 * If protocol is NULL, then all protocols.
1685 sa_enable_share(sa_share_t share, char *protocol)
1687 char *sharepath;
1688 struct stat st;
1689 int err = SA_OK;
1690 int ret;
1692 sharepath = sa_get_share_attr(share, "path");
1693 if (sharepath == NULL)
1694 return (SA_NO_MEMORY);
1695 if (stat(sharepath, &st) < 0) {
1696 err = SA_NO_SUCH_PATH;
1697 } else {
1698 /* tell the server about the share */
1699 if (protocol != NULL) {
1700 if (excluded_protocol(share, protocol))
1701 goto done;
1703 /* lookup protocol specific handler */
1704 err = sa_proto_share(protocol, share);
1705 if (err == SA_OK)
1706 (void) sa_set_share_attr(share,
1707 "shared", "true");
1708 } else {
1709 /* Tell all protocols about the share */
1710 sa_group_t group;
1711 sa_optionset_t optionset;
1713 group = sa_get_parent_group(share);
1715 for (optionset = sa_get_optionset(group, NULL);
1716 optionset != NULL;
1717 optionset = sa_get_next_optionset(optionset)) {
1718 char *proto;
1719 proto = sa_get_optionset_attr(optionset,
1720 "type");
1721 if (proto != NULL) {
1722 if (!excluded_protocol(share, proto)) {
1723 ret = sa_proto_share(proto,
1724 share);
1725 if (ret != SA_OK)
1726 err = ret;
1728 sa_free_attr_string(proto);
1731 (void) sa_set_share_attr(share, "shared", "true");
1734 done:
1735 if (sharepath != NULL)
1736 sa_free_attr_string(sharepath);
1737 return (err);
1741 * sa_disable_share(share, protocol)
1742 * Disable the specified share to the specified protocol. If
1743 * protocol is NULL, then all protocols that are enabled for the
1744 * share should be disabled.
1747 sa_disable_share(sa_share_t share, char *protocol)
1749 char *path;
1750 int err = SA_OK;
1751 int ret = SA_OK;
1753 path = sa_get_share_attr(share, "path");
1755 if (protocol != NULL) {
1756 ret = sa_proto_unshare(share, protocol, path);
1757 } else {
1758 /* need to do all protocols */
1759 sa_group_t group;
1760 sa_optionset_t optionset;
1762 group = sa_get_parent_group(share);
1764 /* Tell all protocols about the share */
1765 for (optionset = sa_get_optionset(group, NULL);
1766 optionset != NULL;
1767 optionset = sa_get_next_optionset(optionset)) {
1768 char *proto;
1770 proto = sa_get_optionset_attr(optionset, "type");
1771 if (proto != NULL) {
1772 err = sa_proto_unshare(share, proto, path);
1773 if (err != SA_OK)
1774 ret = err;
1775 sa_free_attr_string(proto);
1779 if (ret == SA_OK)
1780 (void) sa_set_share_attr(share, "shared", NULL);
1781 if (path != NULL)
1782 sa_free_attr_string(path);
1783 return (ret);
1787 * sa_remove_share(share)
1789 * remove the specified share from its containing group.
1790 * Remove from the SMF or ZFS configuration space.
1794 sa_remove_share(sa_share_t share)
1796 sa_group_t group;
1797 int ret = SA_OK;
1798 char *type;
1799 int transient = 0;
1800 char *groupname;
1801 char *zfs;
1803 type = sa_get_share_attr(share, "type");
1804 group = sa_get_parent_group(share);
1805 zfs = sa_get_group_attr(group, "zfs");
1806 groupname = sa_get_group_attr(group, "name");
1807 if (type != NULL && strcmp(type, "persist") != 0)
1808 transient = 1;
1809 if (type != NULL)
1810 sa_free_attr_string(type);
1812 /* remove the node from its group then free the memory */
1815 * need to test if "busy"
1817 /* only do SMF action if permanent */
1818 if (!transient || zfs != NULL) {
1819 /* remove from legacy dfstab as well as possible SMF */
1820 ret = sa_delete_legacy(share, NULL);
1821 if (ret == SA_OK) {
1822 if (!sa_group_is_zfs(group)) {
1823 sa_handle_impl_t impl_handle;
1824 impl_handle = (sa_handle_impl_t)
1825 sa_find_group_handle(group);
1826 if (impl_handle != NULL) {
1827 ret = sa_delete_share(
1828 impl_handle->scfhandle, group,
1829 share);
1830 } else {
1831 ret = SA_SYSTEM_ERR;
1833 } else {
1834 char *sharepath = sa_get_share_attr(share,
1835 "path");
1836 if (sharepath != NULL) {
1837 ret = sa_zfs_set_sharenfs(group,
1838 sharepath, 0);
1839 sa_free_attr_string(sharepath);
1844 if (groupname != NULL)
1845 sa_free_attr_string(groupname);
1846 if (zfs != NULL)
1847 sa_free_attr_string(zfs);
1849 xmlUnlinkNode((xmlNodePtr)share);
1850 xmlFreeNode((xmlNodePtr)share);
1851 return (ret);
1855 * sa_move_share(group, share)
1857 * move the specified share to the specified group. Update SMF
1858 * appropriately.
1862 sa_move_share(sa_group_t group, sa_share_t share)
1864 sa_group_t oldgroup;
1865 int ret = SA_OK;
1867 /* remove the node from its group then free the memory */
1869 oldgroup = sa_get_parent_group(share);
1870 if (oldgroup != group) {
1871 sa_handle_impl_t impl_handle;
1872 xmlUnlinkNode((xmlNodePtr)share);
1874 * now that the share isn't in its old group, add to
1875 * the new one
1877 (void) xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
1878 /* need to deal with SMF */
1879 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1880 if (impl_handle != NULL) {
1882 * need to remove from old group first and then add to
1883 * new group. Ideally, we would do the other order but
1884 * need to avoid having the share in two groups at the
1885 * same time.
1887 ret = sa_delete_share(impl_handle->scfhandle, oldgroup,
1888 share);
1889 if (ret == SA_OK)
1890 ret = sa_commit_share(impl_handle->scfhandle,
1891 group, share);
1892 } else {
1893 ret = SA_SYSTEM_ERR;
1896 return (ret);
1900 * sa_get_parent_group(share)
1902 * Return the containing group for the share. If a group was actually
1903 * passed in, we don't want a parent so return NULL.
1906 sa_group_t
1907 sa_get_parent_group(sa_share_t share)
1909 xmlNodePtr node = NULL;
1910 if (share != NULL) {
1911 node = ((xmlNodePtr)share)->parent;
1913 * make sure parent is a group and not sharecfg since
1914 * we may be cheating and passing in a group.
1915 * Eventually, groups of groups might come into being.
1917 if (node == NULL ||
1918 xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
1919 node = NULL;
1921 return ((sa_group_t)node);
1925 * _sa_create_group(impl_handle, groupname)
1927 * Create a group in the document. The caller will need to deal with
1928 * configuration store and activation.
1931 sa_group_t
1932 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname)
1934 xmlNodePtr node = NULL;
1936 if (sa_valid_group_name(groupname)) {
1937 node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group",
1938 NULL);
1939 if (node != NULL) {
1940 (void) xmlSetProp(node, (xmlChar *)"name",
1941 (xmlChar *)groupname);
1942 (void) xmlSetProp(node, (xmlChar *)"state",
1943 (xmlChar *)"enabled");
1946 return ((sa_group_t)node);
1950 * _sa_create_zfs_group(group, groupname)
1952 * Create a ZFS subgroup under the specified group. This may
1953 * eventually form the basis of general sub-groups, but is currently
1954 * restricted to ZFS.
1956 sa_group_t
1957 _sa_create_zfs_group(sa_group_t group, char *groupname)
1959 xmlNodePtr node = NULL;
1961 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL);
1962 if (node != NULL) {
1963 (void) xmlSetProp(node, (xmlChar *)"name",
1964 (xmlChar *)groupname);
1965 (void) xmlSetProp(node, (xmlChar *)"state",
1966 (xmlChar *)"enabled");
1969 return ((sa_group_t)node);
1973 * sa_create_group(groupname, *error)
1975 * Create a new group with groupname. Need to validate that it is a
1976 * legal name for SMF and the construct the SMF service instance of
1977 * svc:/network/shares/group to implement the group. All necessary
1978 * operational properties must be added to the group at this point
1979 * (via the SMF transaction model).
1981 sa_group_t
1982 sa_create_group(sa_handle_t handle, char *groupname, int *error)
1984 xmlNodePtr node = NULL;
1985 sa_group_t group;
1986 int ret;
1987 char rbacstr[SA_STRSIZE];
1988 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1990 ret = SA_OK;
1992 if (impl_handle == NULL || impl_handle->scfhandle == NULL) {
1993 ret = SA_SYSTEM_ERR;
1994 goto err;
1997 group = sa_get_group(handle, groupname);
1998 if (group != NULL) {
1999 ret = SA_DUPLICATE_NAME;
2000 } else {
2001 if (sa_valid_group_name(groupname)) {
2002 node = xmlNewChild(impl_handle->tree, NULL,
2003 (xmlChar *)"group", NULL);
2004 if (node != NULL) {
2005 (void) xmlSetProp(node, (xmlChar *)"name",
2006 (xmlChar *)groupname);
2007 /* default to the group being enabled */
2008 (void) xmlSetProp(node, (xmlChar *)"state",
2009 (xmlChar *)"enabled");
2010 ret = sa_create_instance(impl_handle->scfhandle,
2011 groupname);
2012 if (ret == SA_OK) {
2013 ret = sa_start_transaction(
2014 impl_handle->scfhandle,
2015 "operation");
2017 if (ret == SA_OK) {
2018 ret = sa_set_property(
2019 impl_handle->scfhandle,
2020 "state", "enabled");
2021 if (ret == SA_OK) {
2022 ret = sa_end_transaction(
2023 impl_handle->scfhandle,
2024 impl_handle);
2025 } else {
2026 sa_abort_transaction(
2027 impl_handle->scfhandle);
2030 if (ret == SA_OK) {
2031 /* initialize the RBAC strings */
2032 ret = sa_start_transaction(
2033 impl_handle->scfhandle,
2034 "general");
2035 if (ret == SA_OK) {
2036 (void) snprintf(rbacstr,
2037 sizeof (rbacstr), "%s.%s",
2038 SA_RBAC_MANAGE, groupname);
2039 ret = sa_set_property(
2040 impl_handle->scfhandle,
2041 "action_authorization",
2042 rbacstr);
2044 if (ret == SA_OK) {
2045 (void) snprintf(rbacstr,
2046 sizeof (rbacstr), "%s.%s",
2047 SA_RBAC_VALUE, groupname);
2048 ret = sa_set_property(
2049 impl_handle->scfhandle,
2050 "value_authorization",
2051 rbacstr);
2053 if (ret == SA_OK) {
2054 ret = sa_end_transaction(
2055 impl_handle->scfhandle,
2056 impl_handle);
2057 } else {
2058 sa_abort_transaction(
2059 impl_handle->scfhandle);
2062 if (ret != SA_OK) {
2064 * Couldn't commit the group
2065 * so we need to undo
2066 * internally.
2068 xmlUnlinkNode(node);
2069 xmlFreeNode(node);
2070 node = NULL;
2072 } else {
2073 ret = SA_NO_MEMORY;
2075 } else {
2076 ret = SA_INVALID_NAME;
2079 err:
2080 if (error != NULL)
2081 *error = ret;
2082 return ((sa_group_t)node);
2086 * sa_remove_group(group)
2088 * Remove the specified group. This deletes from the SMF repository.
2089 * All property groups and properties are removed.
2093 sa_remove_group(sa_group_t group)
2095 char *name;
2096 int ret = SA_OK;
2097 sa_handle_impl_t impl_handle;
2099 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2100 if (impl_handle != NULL) {
2101 name = sa_get_group_attr(group, "name");
2102 if (name != NULL) {
2103 ret = sa_delete_instance(impl_handle->scfhandle, name);
2104 sa_free_attr_string(name);
2106 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
2107 xmlFreeNode((xmlNodePtr)group); /* now it is gone */
2108 } else {
2109 ret = SA_SYSTEM_ERR;
2111 return (ret);
2115 * sa_update_config()
2117 * Used to update legacy files that need to be updated in bulk
2118 * Currently, this is a placeholder and will go away in a future
2119 * release.
2123 sa_update_config(sa_handle_t handle)
2126 * do legacy files first so we can tell when they change.
2127 * This will go away when we start updating individual records
2128 * rather than the whole file.
2130 update_legacy_config(handle);
2131 return (SA_OK);
2135 * get_node_attr(node, tag)
2137 * Get the specified tag(attribute) if it exists on the node. This is
2138 * used internally by a number of attribute oriented functions.
2141 static char *
2142 get_node_attr(void *nodehdl, char *tag)
2144 xmlNodePtr node = (xmlNodePtr)nodehdl;
2145 xmlChar *name = NULL;
2147 if (node != NULL)
2148 name = xmlGetProp(node, (xmlChar *)tag);
2149 return ((char *)name);
2153 * set_node_attr(node, tag)
2155 * Set the specified tag(attribute) to the specified value This is
2156 * used internally by a number of attribute oriented functions. It
2157 * doesn't update the repository, only the internal document state.
2160 void
2161 set_node_attr(void *nodehdl, char *tag, char *value)
2163 xmlNodePtr node = (xmlNodePtr)nodehdl;
2164 if (node != NULL && tag != NULL) {
2165 if (value != NULL)
2166 (void) xmlSetProp(node, (xmlChar *)tag,
2167 (xmlChar *)value);
2168 else
2169 (void) xmlUnsetProp(node, (xmlChar *)tag);
2174 * sa_get_group_attr(group, tag)
2176 * Get the specied attribute, if defined, for the group.
2179 char *
2180 sa_get_group_attr(sa_group_t group, char *tag)
2182 return (get_node_attr((void *)group, tag));
2186 * sa_set_group_attr(group, tag, value)
2188 * set the specified tag/attribute on the group using value as its
2189 * value.
2191 * This will result in setting the property in the SMF repository as
2192 * well as in the internal document.
2196 sa_set_group_attr(sa_group_t group, char *tag, char *value)
2198 int ret;
2199 char *groupname;
2200 sa_handle_impl_t impl_handle;
2203 * ZFS group/subgroup doesn't need the handle so shortcut.
2205 if (sa_group_is_zfs(group)) {
2206 set_node_attr((void *)group, tag, value);
2207 return (SA_OK);
2210 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2211 if (impl_handle != NULL) {
2212 groupname = sa_get_group_attr(group, "name");
2213 ret = sa_get_instance(impl_handle->scfhandle, groupname);
2214 if (ret == SA_OK) {
2215 set_node_attr((void *)group, tag, value);
2216 ret = sa_start_transaction(impl_handle->scfhandle,
2217 "operation");
2218 if (ret == SA_OK) {
2219 ret = sa_set_property(impl_handle->scfhandle,
2220 tag, value);
2221 if (ret == SA_OK)
2222 ret = sa_end_transaction(
2223 impl_handle->scfhandle,
2224 impl_handle);
2225 else
2226 sa_abort_transaction(
2227 impl_handle->scfhandle);
2229 if (ret == SA_SYSTEM_ERR)
2230 ret = SA_NO_PERMISSION;
2232 if (groupname != NULL)
2233 sa_free_attr_string(groupname);
2234 } else {
2235 ret = SA_SYSTEM_ERR;
2237 return (ret);
2241 * sa_get_share_attr(share, tag)
2243 * Return the value of the tag/attribute set on the specified
2244 * share. Returns NULL if the tag doesn't exist.
2247 char *
2248 sa_get_share_attr(sa_share_t share, char *tag)
2250 return (get_node_attr((void *)share, tag));
2254 * _sa_set_share_description(share, description)
2256 * Add a description tag with text contents to the specified share. A
2257 * separate XML tag is used rather than a property. This can also be
2258 * used with resources.
2261 xmlNodePtr
2262 _sa_set_share_description(void *share, char *content)
2264 xmlNodePtr node;
2265 node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description",
2266 NULL);
2267 xmlNodeSetContent(node, (xmlChar *)content);
2268 return (node);
2272 * sa_set_share_attr(share, tag, value)
2274 * Set the share attribute specified by tag to the specified value. In
2275 * the case of "resource", enforce a no duplicates in a group rule. If
2276 * the share is not transient, commit the changes to the repository
2277 * else just update the share internally.
2281 sa_set_share_attr(sa_share_t share, char *tag, char *value)
2283 sa_group_t group;
2284 sa_share_t resource;
2285 int ret = SA_OK;
2287 group = sa_get_parent_group(share);
2290 * There are some attributes that may have specific
2291 * restrictions on them. Initially, only "resource" has
2292 * special meaning that needs to be checked. Only one instance
2293 * of a resource name may exist within a group.
2296 if (strcmp(tag, "resource") == 0) {
2297 resource = sa_get_resource(group, value);
2298 if (resource != share && resource != NULL)
2299 ret = SA_DUPLICATE_NAME;
2301 if (ret == SA_OK) {
2302 set_node_attr((void *)share, tag, value);
2303 if (group != NULL) {
2304 char *type;
2305 /* we can probably optimize this some */
2306 type = sa_get_share_attr(share, "type");
2307 if (type == NULL || strcmp(type, "transient") != 0) {
2308 sa_handle_impl_t impl_handle;
2309 impl_handle =
2310 (sa_handle_impl_t)sa_find_group_handle(
2311 group);
2312 if (impl_handle != NULL) {
2313 ret = sa_commit_share(
2314 impl_handle->scfhandle, group,
2315 share);
2316 } else {
2317 ret = SA_SYSTEM_ERR;
2320 if (type != NULL)
2321 sa_free_attr_string(type);
2324 return (ret);
2328 * sa_get_property_attr(prop, tag)
2330 * Get the value of the specified property attribute. Standard
2331 * attributes are "type" and "value".
2334 char *
2335 sa_get_property_attr(sa_property_t prop, char *tag)
2337 return (get_node_attr((void *)prop, tag));
2341 * sa_get_optionset_attr(prop, tag)
2343 * Get the value of the specified property attribute. Standard
2344 * attribute is "type".
2347 char *
2348 sa_get_optionset_attr(sa_property_t optionset, char *tag)
2350 return (get_node_attr((void *)optionset, tag));
2355 * sa_set_optionset_attr(optionset, tag, value)
2357 * Set the specified attribute(tag) to the specified value on the
2358 * optionset.
2361 void
2362 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
2364 set_node_attr((void *)optionset, tag, value);
2368 * sa_free_attr_string(string)
2370 * Free the string that was returned in one of the sa_get_*_attr()
2371 * functions.
2374 void
2375 sa_free_attr_string(char *string)
2377 xmlFree((xmlChar *)string);
2381 * sa_get_optionset(group, proto)
2383 * Return the optionset, if it exists, that is associated with the
2384 * specified protocol.
2387 sa_optionset_t
2388 sa_get_optionset(void *group, char *proto)
2390 xmlNodePtr node;
2391 xmlChar *value = NULL;
2393 for (node = ((xmlNodePtr)group)->children; node != NULL;
2394 node = node->next) {
2395 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2396 value = xmlGetProp(node, (xmlChar *)"type");
2397 if (proto != NULL) {
2398 if (value != NULL &&
2399 xmlStrcmp(value, (xmlChar *)proto) == 0) {
2400 break;
2402 if (value != NULL) {
2403 xmlFree(value);
2404 value = NULL;
2406 } else {
2407 break;
2411 if (value != NULL)
2412 xmlFree(value);
2413 return ((sa_optionset_t)node);
2417 * sa_get_next_optionset(optionset)
2419 * Return the next optionset in the group. NULL if this was the last.
2422 sa_optionset_t
2423 sa_get_next_optionset(sa_optionset_t optionset)
2425 xmlNodePtr node;
2427 for (node = ((xmlNodePtr)optionset)->next; node != NULL;
2428 node = node->next) {
2429 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2430 break;
2433 return ((sa_optionset_t)node);
2437 * sa_get_security(group, sectype, proto)
2439 * Return the security optionset. The internal name is a hold over
2440 * from the implementation and will be changed before the API is
2441 * finalized. This is really a named optionset that can be negotiated
2442 * as a group of properties (like NFS security options).
2445 sa_security_t
2446 sa_get_security(sa_group_t group, char *sectype, char *proto)
2448 xmlNodePtr node;
2449 xmlChar *value = NULL;
2451 for (node = ((xmlNodePtr)group)->children; node != NULL;
2452 node = node->next) {
2453 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2454 if (proto != NULL) {
2455 value = xmlGetProp(node, (xmlChar *)"type");
2456 if (value == NULL ||
2457 (value != NULL &&
2458 xmlStrcmp(value, (xmlChar *)proto) != 0)) {
2459 /* it doesn't match so continue */
2460 xmlFree(value);
2461 value = NULL;
2462 continue;
2465 if (value != NULL) {
2466 xmlFree(value);
2467 value = NULL;
2469 /* potential match */
2470 if (sectype != NULL) {
2471 value = xmlGetProp(node, (xmlChar *)"sectype");
2472 if (value != NULL &&
2473 xmlStrcmp(value, (xmlChar *)sectype) == 0) {
2474 break;
2476 } else {
2477 break;
2480 if (value != NULL) {
2481 xmlFree(value);
2482 value = NULL;
2485 if (value != NULL)
2486 xmlFree(value);
2487 return ((sa_security_t)node);
2491 * sa_get_next_security(security)
2493 * Get the next security optionset if one exists.
2496 sa_security_t
2497 sa_get_next_security(sa_security_t security)
2499 xmlNodePtr node;
2501 for (node = ((xmlNodePtr)security)->next; node != NULL;
2502 node = node->next) {
2503 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2504 break;
2507 return ((sa_security_t)node);
2511 * sa_get_property(optionset, prop)
2513 * Get the property object with the name specified in prop from the
2514 * optionset.
2517 sa_property_t
2518 sa_get_property(sa_optionset_t optionset, char *prop)
2520 xmlNodePtr node = (xmlNodePtr)optionset;
2521 xmlChar *value = NULL;
2523 if (optionset == NULL)
2524 return (NULL);
2526 for (node = node->children; node != NULL;
2527 node = node->next) {
2528 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2529 if (prop == NULL)
2530 break;
2531 value = xmlGetProp(node, (xmlChar *)"type");
2532 if (value != NULL &&
2533 xmlStrcmp(value, (xmlChar *)prop) == 0) {
2534 break;
2536 if (value != NULL) {
2537 xmlFree(value);
2538 value = NULL;
2542 if (value != NULL)
2543 xmlFree(value);
2544 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
2546 * avoid a non option node -- it is possible to be a
2547 * text node
2549 node = NULL;
2551 return ((sa_property_t)node);
2555 * sa_get_next_property(property)
2557 * Get the next property following the specified property. NULL if
2558 * this was the last.
2561 sa_property_t
2562 sa_get_next_property(sa_property_t property)
2564 xmlNodePtr node;
2566 for (node = ((xmlNodePtr)property)->next; node != NULL;
2567 node = node->next) {
2568 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2569 break;
2572 return ((sa_property_t)node);
2576 * sa_set_share_description(share, content)
2578 * Set the description of share to content.
2582 sa_set_share_description(sa_share_t share, char *content)
2584 xmlNodePtr node;
2585 sa_group_t group;
2586 int ret = SA_OK;
2588 for (node = ((xmlNodePtr)share)->children; node != NULL;
2589 node = node->next) {
2590 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2591 break;
2594 /* no existing description but want to add */
2595 if (node == NULL && content != NULL) {
2596 /* add a description */
2597 node = _sa_set_share_description(share, content);
2598 } else if (node != NULL && content != NULL) {
2599 /* update a description */
2600 xmlNodeSetContent(node, (xmlChar *)content);
2601 } else if (node != NULL && content == NULL) {
2602 /* remove an existing description */
2603 xmlUnlinkNode(node);
2604 xmlFreeNode(node);
2606 group = sa_get_parent_group(share);
2607 if (group != NULL &&
2608 sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
2609 sa_handle_impl_t impl_handle;
2610 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2611 if (impl_handle != NULL) {
2612 ret = sa_commit_share(impl_handle->scfhandle, group,
2613 share);
2614 } else {
2615 ret = SA_SYSTEM_ERR;
2618 return (ret);
2622 * fixproblemchars(string)
2624 * don't want any newline or tab characters in the text since these
2625 * could break display of data and legacy file formats.
2627 static void
2628 fixproblemchars(char *str)
2630 int c;
2631 for (c = *str; c != '\0'; c = *++str) {
2632 if (c == '\t' || c == '\n')
2633 *str = ' ';
2634 else if (c == '"')
2635 *str = '\'';
2640 * sa_get_share_description(share)
2642 * Return the description text for the specified share if it
2643 * exists. NULL if no description exists.
2646 char *
2647 sa_get_share_description(sa_share_t share)
2649 xmlChar *description = NULL;
2650 xmlNodePtr node;
2652 for (node = ((xmlNodePtr)share)->children; node != NULL;
2653 node = node->next) {
2654 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2655 break;
2658 if (node != NULL) {
2659 description = xmlNodeGetContent(node);
2660 fixproblemchars((char *)description);
2662 return ((char *)description);
2666 * sa_free(share_description(description)
2668 * Free the description string.
2671 void
2672 sa_free_share_description(char *description)
2674 xmlFree((xmlChar *)description);
2678 * sa_create_optionset(group, proto)
2680 * Create an optionset for the specified protocol in the specied
2681 * group. This is manifested as a property group within SMF.
2684 sa_optionset_t
2685 sa_create_optionset(sa_group_t group, char *proto)
2687 sa_optionset_t optionset;
2688 sa_group_t parent = group;
2689 sa_share_t share = NULL;
2690 int err = SA_OK;
2691 char *id = NULL;
2693 optionset = sa_get_optionset(group, proto);
2694 if (optionset != NULL) {
2695 /* can't have a duplicate protocol */
2696 optionset = NULL;
2697 } else {
2699 * Account for resource names being slightly
2700 * different.
2702 if (sa_is_share(group)) {
2704 * Transient shares do not have an "id" so not an
2705 * error to not find one.
2707 id = sa_get_share_attr((sa_share_t)group, "id");
2708 } else if (sa_is_resource(group)) {
2709 share = sa_get_resource_parent(
2710 (sa_resource_t)group);
2711 id = sa_get_resource_attr(share, "id");
2713 /* id can be NULL if the group is transient (ZFS) */
2714 if (id == NULL && sa_is_persistent(group))
2715 err = SA_NO_MEMORY;
2717 if (err == SA_NO_MEMORY) {
2719 * Couldn't get the id for the share or
2720 * resource. While this could be a
2721 * configuration issue, it is most likely an
2722 * out of memory. In any case, fail the create.
2724 return (NULL);
2727 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
2728 NULL, (xmlChar *)"optionset", NULL);
2730 * only put to repository if on a group and we were
2731 * able to create an optionset.
2733 if (optionset != NULL) {
2734 char oname[SA_STRSIZE];
2735 char *groupname;
2738 * Need to get parent group in all cases, but also get
2739 * the share if this is a resource.
2741 if (sa_is_share(group)) {
2742 parent = sa_get_parent_group((sa_share_t)group);
2743 } else if (sa_is_resource(group)) {
2744 share = sa_get_resource_parent(
2745 (sa_resource_t)group);
2746 parent = sa_get_parent_group(share);
2749 sa_set_optionset_attr(optionset, "type", proto);
2751 (void) sa_optionset_name(optionset, oname,
2752 sizeof (oname), id);
2753 groupname = sa_get_group_attr(parent, "name");
2754 if (groupname != NULL && sa_is_persistent(group)) {
2755 sa_handle_impl_t impl_handle;
2756 impl_handle =
2757 (sa_handle_impl_t)sa_find_group_handle(
2758 group);
2759 assert(impl_handle != NULL);
2760 if (impl_handle != NULL) {
2761 (void) sa_get_instance(
2762 impl_handle->scfhandle, groupname);
2763 (void) sa_create_pgroup(
2764 impl_handle->scfhandle, oname);
2767 if (groupname != NULL)
2768 sa_free_attr_string(groupname);
2772 if (id != NULL)
2773 sa_free_attr_string(id);
2774 return (optionset);
2778 * sa_get_property_parent(property)
2780 * Given a property, return the object it is a property of. This will
2781 * be an optionset of some type.
2784 static sa_optionset_t
2785 sa_get_property_parent(sa_property_t property)
2787 xmlNodePtr node = NULL;
2789 if (property != NULL)
2790 node = ((xmlNodePtr)property)->parent;
2791 return ((sa_optionset_t)node);
2795 * sa_get_optionset_parent(optionset)
2797 * Return the parent of the specified optionset. This could be a group
2798 * or a share.
2801 static sa_group_t
2802 sa_get_optionset_parent(sa_optionset_t optionset)
2804 xmlNodePtr node = NULL;
2806 if (optionset != NULL)
2807 node = ((xmlNodePtr)optionset)->parent;
2808 return ((sa_group_t)node);
2812 * zfs_needs_update(share)
2814 * In order to avoid making multiple updates to a ZFS share when
2815 * setting properties, the share attribute "changed" will be set to
2816 * true when a property is added or modified. When done adding
2817 * properties, we can then detect that an update is needed. We then
2818 * clear the state here to detect additional changes.
2821 static int
2822 zfs_needs_update(sa_share_t share)
2824 char *attr;
2825 int result = 0;
2827 attr = sa_get_share_attr(share, "changed");
2828 if (attr != NULL) {
2829 sa_free_attr_string(attr);
2830 result = 1;
2832 set_node_attr((void *)share, "changed", NULL);
2833 return (result);
2837 * zfs_set_update(share)
2839 * Set the changed attribute of the share to true.
2842 static void
2843 zfs_set_update(sa_share_t share)
2845 set_node_attr((void *)share, "changed", "true");
2849 * sa_commit_properties(optionset, clear)
2851 * Check if SMF or ZFS config and either update or abort the pending
2852 * changes.
2856 sa_commit_properties(sa_optionset_t optionset, int clear)
2858 sa_group_t group;
2859 sa_group_t parent;
2860 int zfs = 0;
2861 int needsupdate = 0;
2862 int ret = SA_OK;
2863 sa_handle_impl_t impl_handle;
2865 group = sa_get_optionset_parent(optionset);
2866 if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
2867 /* only update ZFS if on a share */
2868 parent = sa_get_parent_group(group);
2869 zfs++;
2870 if (parent != NULL && is_zfs_group(parent))
2871 needsupdate = zfs_needs_update(group);
2872 else
2873 zfs = 0;
2875 if (zfs) {
2876 if (!clear && needsupdate)
2877 ret = sa_zfs_update((sa_share_t)group);
2878 } else {
2879 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2880 if (impl_handle != NULL) {
2881 if (clear) {
2882 (void) sa_abort_transaction(
2883 impl_handle->scfhandle);
2884 } else {
2885 ret = sa_end_transaction(
2886 impl_handle->scfhandle, impl_handle);
2888 } else {
2889 ret = SA_SYSTEM_ERR;
2892 return (ret);
2896 * sa_destroy_optionset(optionset)
2898 * Remove the optionset from its group. Update the repository to
2899 * reflect this change.
2903 sa_destroy_optionset(sa_optionset_t optionset)
2905 char name[SA_STRSIZE];
2906 int len;
2907 int ret;
2908 char *id = NULL;
2909 sa_group_t group;
2910 int ispersist = 1;
2912 /* now delete the prop group */
2913 group = sa_get_optionset_parent(optionset);
2914 if (group != NULL) {
2915 if (sa_is_resource(group)) {
2916 sa_resource_t resource = group;
2917 sa_share_t share = sa_get_resource_parent(resource);
2918 group = sa_get_parent_group(share);
2919 id = sa_get_share_attr(share, "id");
2920 } else if (sa_is_share(group)) {
2921 id = sa_get_share_attr((sa_share_t)group, "id");
2923 ispersist = sa_is_persistent(group);
2925 if (ispersist) {
2926 sa_handle_impl_t impl_handle;
2927 len = sa_optionset_name(optionset, name, sizeof (name), id);
2928 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2929 if (impl_handle != NULL) {
2930 if (len > 0) {
2931 ret = sa_delete_pgroup(impl_handle->scfhandle,
2932 name);
2934 } else {
2935 ret = SA_SYSTEM_ERR;
2938 xmlUnlinkNode((xmlNodePtr)optionset);
2939 xmlFreeNode((xmlNodePtr)optionset);
2940 if (id != NULL)
2941 sa_free_attr_string(id);
2942 return (ret);
2945 /* private to the implementation */
2947 _sa_remove_optionset(sa_optionset_t optionset)
2949 int ret = SA_OK;
2951 xmlUnlinkNode((xmlNodePtr)optionset);
2952 xmlFreeNode((xmlNodePtr)optionset);
2953 return (ret);
2957 * sa_create_security(group, sectype, proto)
2959 * Create a security optionset (one that has a type name and a
2960 * proto). Security is left over from a pure NFS implementation. The
2961 * naming will change in the future when the API is released.
2963 sa_security_t
2964 sa_create_security(sa_group_t group, char *sectype, char *proto)
2966 sa_security_t security;
2967 char *id = NULL;
2968 sa_group_t parent;
2969 char *groupname = NULL;
2971 if (group != NULL && sa_is_share(group)) {
2972 id = sa_get_share_attr((sa_share_t)group, "id");
2973 parent = sa_get_parent_group(group);
2974 if (parent != NULL)
2975 groupname = sa_get_group_attr(parent, "name");
2976 } else if (group != NULL) {
2977 groupname = sa_get_group_attr(group, "name");
2980 security = sa_get_security(group, sectype, proto);
2981 if (security != NULL) {
2982 /* can't have a duplicate security option */
2983 security = NULL;
2984 } else {
2985 security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
2986 NULL, (xmlChar *)"security", NULL);
2987 if (security != NULL) {
2988 char oname[SA_STRSIZE];
2989 sa_set_security_attr(security, "type", proto);
2991 sa_set_security_attr(security, "sectype", sectype);
2992 (void) sa_security_name(security, oname,
2993 sizeof (oname), id);
2994 if (groupname != NULL && sa_is_persistent(group)) {
2995 sa_handle_impl_t impl_handle;
2996 impl_handle =
2997 (sa_handle_impl_t)sa_find_group_handle(
2998 group);
2999 if (impl_handle != NULL) {
3000 (void) sa_get_instance(
3001 impl_handle->scfhandle, groupname);
3002 (void) sa_create_pgroup(
3003 impl_handle->scfhandle, oname);
3008 if (id != NULL)
3009 sa_free_attr_string(id);
3010 if (groupname != NULL)
3011 sa_free_attr_string(groupname);
3012 return (security);
3016 * sa_destroy_security(security)
3018 * Remove the specified optionset from the document and the
3019 * configuration.
3023 sa_destroy_security(sa_security_t security)
3025 char name[SA_STRSIZE];
3026 int len;
3027 int ret = SA_OK;
3028 char *id = NULL;
3029 sa_group_t group;
3030 int iszfs = 0;
3031 int ispersist = 1;
3033 group = sa_get_optionset_parent(security);
3035 if (group != NULL)
3036 iszfs = sa_group_is_zfs(group);
3038 if (group != NULL && !iszfs) {
3039 if (sa_is_share(group))
3040 ispersist = sa_is_persistent(group);
3041 id = sa_get_share_attr((sa_share_t)group, "id");
3043 if (ispersist) {
3044 len = sa_security_name(security, name, sizeof (name), id);
3045 if (!iszfs && len > 0) {
3046 sa_handle_impl_t impl_handle;
3047 impl_handle =
3048 (sa_handle_impl_t)sa_find_group_handle(group);
3049 if (impl_handle != NULL) {
3050 ret = sa_delete_pgroup(impl_handle->scfhandle,
3051 name);
3052 } else {
3053 ret = SA_SYSTEM_ERR;
3057 xmlUnlinkNode((xmlNodePtr)security);
3058 xmlFreeNode((xmlNodePtr)security);
3059 if (iszfs)
3060 ret = sa_zfs_update(group);
3061 if (id != NULL)
3062 sa_free_attr_string(id);
3063 return (ret);
3067 * sa_get_security_attr(optionset, tag)
3069 * Return the specified attribute value from the optionset.
3072 char *
3073 sa_get_security_attr(sa_property_t optionset, char *tag)
3075 return (get_node_attr((void *)optionset, tag));
3080 * sa_set_security_attr(optionset, tag, value)
3082 * Set the optioset attribute specied by tag to the specified value.
3085 void
3086 sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
3088 set_node_attr((void *)optionset, tag, value);
3092 * is_nodetype(node, type)
3094 * Check to see if node is of the type specified.
3097 static int
3098 is_nodetype(void *node, char *type)
3100 return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
3104 * add_or_update()
3106 * Add or update a property. Pulled out of sa_set_prop_by_prop for
3107 * readability.
3109 static int
3110 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value,
3111 scf_transaction_entry_t *entry, char *name, char *valstr)
3113 int ret = SA_SYSTEM_ERR;
3115 if (value != NULL) {
3116 if (type == SA_PROP_OP_ADD)
3117 ret = scf_transaction_property_new(scf_handle->trans,
3118 entry, name, SCF_TYPE_ASTRING);
3119 else
3120 ret = scf_transaction_property_change(scf_handle->trans,
3121 entry, name, SCF_TYPE_ASTRING);
3122 if (ret == 0) {
3123 ret = scf_value_set_astring(value, valstr);
3124 if (ret == 0)
3125 ret = scf_entry_add_value(entry, value);
3126 if (ret == 0)
3127 return (ret);
3128 scf_value_destroy(value);
3129 } else {
3130 scf_entry_destroy(entry);
3133 return (SA_SYSTEM_ERR);
3137 * sa_set_prop_by_prop(optionset, group, prop, type)
3139 * Add/remove/update the specified property prop into the optionset or
3140 * share. If a share, sort out which property group based on GUID. In
3141 * all cases, the appropriate transaction is set (or ZFS share is
3142 * marked as needing an update)
3145 static int
3146 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
3147 sa_property_t prop, int type)
3149 char *name;
3150 char *valstr;
3151 int ret = SA_OK;
3152 scf_transaction_entry_t *entry;
3153 scf_value_t *value;
3154 int opttype; /* 1 == optionset, 0 == security */
3155 char *id = NULL;
3156 int iszfs = 0;
3157 sa_group_t parent = NULL;
3158 sa_share_t share = NULL;
3159 sa_handle_impl_t impl_handle;
3160 scfutilhandle_t *scf_handle;
3162 if (!sa_is_persistent(group)) {
3164 * if the group/share is not persistent we don't need
3165 * to do anything here
3167 return (SA_OK);
3169 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
3170 if (impl_handle == NULL || impl_handle->scfhandle == NULL)
3171 return (SA_SYSTEM_ERR);
3172 scf_handle = impl_handle->scfhandle;
3173 name = sa_get_property_attr(prop, "type");
3174 valstr = sa_get_property_attr(prop, "value");
3175 entry = scf_entry_create(scf_handle->handle);
3176 opttype = is_nodetype((void *)optionset, "optionset");
3179 * Check for share vs. resource since they need slightly
3180 * different treatment given the hierarchy.
3182 if (valstr != NULL && entry != NULL) {
3183 if (sa_is_share(group)) {
3184 parent = sa_get_parent_group(group);
3185 share = (sa_share_t)group;
3186 if (parent != NULL)
3187 iszfs = is_zfs_group(parent);
3188 } else if (sa_is_resource(group)) {
3189 share = sa_get_parent_group(group);
3190 if (share != NULL)
3191 parent = sa_get_parent_group(share);
3192 } else {
3193 iszfs = is_zfs_group(group);
3195 if (!iszfs) {
3196 if (scf_handle->trans == NULL) {
3197 char oname[SA_STRSIZE];
3198 char *groupname = NULL;
3199 if (share != NULL) {
3200 if (parent != NULL)
3201 groupname =
3202 sa_get_group_attr(parent,
3203 "name");
3204 id = sa_get_share_attr(
3205 (sa_share_t)share, "id");
3206 } else {
3207 groupname = sa_get_group_attr(group,
3208 "name");
3210 if (groupname != NULL) {
3211 ret = sa_get_instance(scf_handle,
3212 groupname);
3213 sa_free_attr_string(groupname);
3215 if (opttype)
3216 (void) sa_optionset_name(optionset,
3217 oname, sizeof (oname), id);
3218 else
3219 (void) sa_security_name(optionset,
3220 oname, sizeof (oname), id);
3221 ret = sa_start_transaction(scf_handle, oname);
3222 if (id != NULL)
3223 sa_free_attr_string(id);
3225 if (ret == SA_OK) {
3226 switch (type) {
3227 case SA_PROP_OP_REMOVE:
3228 ret = scf_transaction_property_delete(
3229 scf_handle->trans, entry, name);
3230 break;
3231 case SA_PROP_OP_ADD:
3232 case SA_PROP_OP_UPDATE:
3233 value = scf_value_create(
3234 scf_handle->handle);
3235 ret = add_or_update(scf_handle, type,
3236 value, entry, name, valstr);
3237 break;
3240 } else {
3242 * ZFS update. The calling function would have updated
3243 * the internal XML structure. Just need to flag it as
3244 * changed for ZFS.
3246 zfs_set_update((sa_share_t)group);
3250 if (name != NULL)
3251 sa_free_attr_string(name);
3252 if (valstr != NULL)
3253 sa_free_attr_string(valstr);
3254 else if (entry != NULL)
3255 scf_entry_destroy(entry);
3257 if (ret == -1)
3258 ret = SA_SYSTEM_ERR;
3260 return (ret);
3264 * sa_create_section(name, value)
3266 * Create a new section with the specified name and extra data.
3269 sa_property_t
3270 sa_create_section(char *name, char *extra)
3272 xmlNodePtr node;
3274 node = xmlNewNode(NULL, (xmlChar *)"section");
3275 if (node != NULL) {
3276 if (name != NULL)
3277 (void) xmlSetProp(node, (xmlChar *)"name",
3278 (xmlChar *)name);
3279 if (extra != NULL)
3280 (void) xmlSetProp(node, (xmlChar *)"extra",
3281 (xmlChar *)extra);
3283 return ((sa_property_t)node);
3286 void
3287 sa_set_section_attr(sa_property_t sect, char *name, char *value)
3289 (void) xmlSetProp(sect, (xmlChar *)name, (xmlChar *)value);
3293 * sa_create_property(section, name, value)
3295 * Create a new property with the specified name and value.
3298 sa_property_t
3299 sa_create_property(char *name, char *value)
3301 xmlNodePtr node;
3303 node = xmlNewNode(NULL, (xmlChar *)"option");
3304 if (node != NULL) {
3305 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
3306 (void) xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
3308 return ((sa_property_t)node);
3312 * sa_add_property(object, property)
3314 * Add the specified property to the object. Issue the appropriate
3315 * transaction or mark a ZFS object as needing an update.
3319 sa_add_property(void *object, sa_property_t property)
3321 int ret = SA_OK;
3322 sa_group_t parent;
3323 sa_group_t group;
3324 char *proto;
3326 if (property != NULL) {
3327 sa_handle_t handle;
3328 handle = sa_find_group_handle((sa_group_t)object);
3329 /* It is legitimate to not find a handle */
3330 proto = sa_get_optionset_attr(object, "type");
3331 if ((ret = sa_valid_property(handle, object, proto,
3332 property)) == SA_OK) {
3333 property = (sa_property_t)xmlAddChild(
3334 (xmlNodePtr)object, (xmlNodePtr)property);
3335 } else {
3336 if (proto != NULL)
3337 sa_free_attr_string(proto);
3338 return (ret);
3340 if (proto != NULL)
3341 sa_free_attr_string(proto);
3345 parent = sa_get_parent_group(object);
3346 if (!sa_is_persistent(parent))
3347 return (ret);
3349 if (sa_is_resource(parent)) {
3351 * Resources are children of share. Need to go up two
3352 * levels to find the group but the parent needs to be
3353 * the share at this point in order to get the "id".
3355 parent = sa_get_parent_group(parent);
3356 group = sa_get_parent_group(parent);
3357 } else if (sa_is_share(parent)) {
3358 group = sa_get_parent_group(parent);
3359 } else {
3360 group = parent;
3363 if (property == NULL) {
3364 ret = SA_NO_MEMORY;
3365 } else {
3366 char oname[SA_STRSIZE];
3368 if (!is_zfs_group(group)) {
3369 char *id = NULL;
3370 sa_handle_impl_t impl_handle;
3371 scfutilhandle_t *scf_handle;
3373 impl_handle = (sa_handle_impl_t)sa_find_group_handle(
3374 group);
3375 if (impl_handle == NULL ||
3376 impl_handle->scfhandle == NULL)
3377 ret = SA_SYSTEM_ERR;
3378 if (ret == SA_OK) {
3379 scf_handle = impl_handle->scfhandle;
3380 if (sa_is_share((sa_group_t)parent)) {
3381 id = sa_get_share_attr(
3382 (sa_share_t)parent, "id");
3384 if (scf_handle->trans == NULL) {
3385 if (is_nodetype(object, "optionset")) {
3386 (void) sa_optionset_name(
3387 (sa_optionset_t)object,
3388 oname, sizeof (oname), id);
3389 } else {
3390 (void) sa_security_name(
3391 (sa_optionset_t)object,
3392 oname, sizeof (oname), id);
3394 ret = sa_start_transaction(scf_handle,
3395 oname);
3397 if (ret == SA_OK) {
3398 char *name;
3399 char *value;
3400 name = sa_get_property_attr(property,
3401 "type");
3402 value = sa_get_property_attr(property,
3403 "value");
3404 if (name != NULL && value != NULL) {
3405 if (scf_handle->scf_state ==
3406 SCH_STATE_INIT) {
3407 ret = sa_set_property(
3408 scf_handle, name,
3409 value);
3411 } else {
3412 ret = SA_CONFIG_ERR;
3414 if (name != NULL)
3415 sa_free_attr_string(
3416 name);
3417 if (value != NULL)
3418 sa_free_attr_string(value);
3420 if (id != NULL)
3421 sa_free_attr_string(id);
3423 } else {
3425 * ZFS is a special case. We do want
3426 * to allow editing property/security
3427 * lists since we can have a better
3428 * syntax and we also want to keep
3429 * things consistent when possible.
3431 * Right now, we defer until the
3432 * sa_commit_properties so we can get
3433 * them all at once. We do need to
3434 * mark the share as "changed"
3436 zfs_set_update((sa_share_t)parent);
3439 return (ret);
3443 * sa_remove_property(property)
3445 * Remove the specied property from its containing object. Update the
3446 * repository as appropriate.
3450 sa_remove_property(sa_property_t property)
3452 int ret = SA_OK;
3454 if (property != NULL) {
3455 sa_optionset_t optionset;
3456 sa_group_t group;
3457 optionset = sa_get_property_parent(property);
3458 if (optionset != NULL) {
3459 group = sa_get_optionset_parent(optionset);
3460 if (group != NULL) {
3461 ret = sa_set_prop_by_prop(optionset, group,
3462 property, SA_PROP_OP_REMOVE);
3465 xmlUnlinkNode((xmlNodePtr)property);
3466 xmlFreeNode((xmlNodePtr)property);
3467 } else {
3468 ret = SA_NO_SUCH_PROP;
3470 return (ret);
3474 * sa_update_property(property, value)
3476 * Update the specified property to the new value. If value is NULL,
3477 * we currently treat this as a remove.
3481 sa_update_property(sa_property_t property, char *value)
3483 int ret = SA_OK;
3484 if (value == NULL) {
3485 return (sa_remove_property(property));
3486 } else {
3487 sa_optionset_t optionset;
3488 sa_group_t group;
3489 set_node_attr((void *)property, "value", value);
3490 optionset = sa_get_property_parent(property);
3491 if (optionset != NULL) {
3492 group = sa_get_optionset_parent(optionset);
3493 if (group != NULL) {
3494 ret = sa_set_prop_by_prop(optionset, group,
3495 property, SA_PROP_OP_UPDATE);
3497 } else {
3498 ret = SA_NO_SUCH_PROP;
3501 return (ret);
3505 * sa_get_protocol_section(propset, prop)
3507 * Get the specified protocol specific section. These are global to
3508 * the protocol and not specific to a group or share.
3511 sa_protocol_properties_t
3512 sa_get_protocol_section(sa_protocol_properties_t propset, char *section)
3514 xmlNodePtr node = (xmlNodePtr)propset;
3515 xmlChar *value = NULL;
3516 char *proto;
3518 proto = sa_get_optionset_attr(propset, "type");
3519 if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3520 if (proto != NULL)
3521 sa_free_attr_string(proto);
3522 return (propset);
3525 for (node = node->children; node != NULL;
3526 node = node->next) {
3527 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3528 if (section == NULL)
3529 break;
3530 value = xmlGetProp(node, (xmlChar *)"name");
3531 if (value != NULL &&
3532 xmlStrcasecmp(value, (xmlChar *)section) == 0) {
3533 break;
3535 if (value != NULL) {
3536 xmlFree(value);
3537 value = NULL;
3541 if (value != NULL)
3542 xmlFree(value);
3543 if (proto != NULL)
3544 sa_free_attr_string(proto);
3545 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"section") != 0) {
3547 * avoid a non option node -- it is possible to be a
3548 * text node
3550 node = NULL;
3552 return ((sa_protocol_properties_t)node);
3556 * sa_get_next_protocol_section(prop, find)
3558 * Get the next protocol specific section in the list.
3561 sa_property_t
3562 sa_get_next_protocol_section(sa_property_t prop, char *find)
3564 xmlNodePtr node;
3565 xmlChar *value = NULL;
3566 char *proto;
3568 proto = sa_get_optionset_attr(prop, "type");
3569 if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3570 if (proto != NULL)
3571 sa_free_attr_string(proto);
3572 return ((sa_property_t)NULL);
3575 for (node = ((xmlNodePtr)prop)->next; node != NULL;
3576 node = node->next) {
3577 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3578 if (find == NULL)
3579 break;
3580 value = xmlGetProp(node, (xmlChar *)"name");
3581 if (value != NULL &&
3582 xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3583 break;
3585 if (value != NULL) {
3586 xmlFree(value);
3587 value = NULL;
3592 if (value != NULL)
3593 xmlFree(value);
3594 if (proto != NULL)
3595 sa_free_attr_string(proto);
3596 return ((sa_property_t)node);
3600 * sa_get_protocol_property(propset, prop)
3602 * Get the specified protocol specific property. These are global to
3603 * the protocol and not specific to a group or share.
3606 sa_property_t
3607 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
3609 xmlNodePtr node = (xmlNodePtr)propset;
3610 xmlChar *value = NULL;
3612 if (propset == NULL)
3613 return (NULL);
3615 for (node = node->children; node != NULL;
3616 node = node->next) {
3617 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3618 if (prop == NULL)
3619 break;
3620 value = xmlGetProp(node, (xmlChar *)"type");
3621 if (value != NULL &&
3622 xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
3623 break;
3625 if (value != NULL) {
3626 xmlFree(value);
3627 value = NULL;
3631 if (value != NULL)
3632 xmlFree(value);
3633 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
3635 * avoid a non option node -- it is possible to be a
3636 * text node
3638 node = NULL;
3640 return ((sa_property_t)node);
3644 * sa_get_next_protocol_property(prop)
3646 * Get the next protocol specific property in the list.
3649 sa_property_t
3650 sa_get_next_protocol_property(sa_property_t prop, char *find)
3652 xmlNodePtr node;
3653 xmlChar *value = NULL;
3655 for (node = ((xmlNodePtr)prop)->next; node != NULL;
3656 node = node->next) {
3657 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3658 if (find == NULL)
3659 break;
3660 value = xmlGetProp(node, (xmlChar *)"type");
3661 if (value != NULL &&
3662 xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3663 break;
3665 if (value != NULL) {
3666 xmlFree(value);
3667 value = NULL;
3672 if (value != NULL)
3673 xmlFree(value);
3674 return ((sa_property_t)node);
3678 * sa_set_protocol_property(prop, value)
3680 * Set the specified property to have the new value. The protocol
3681 * specific plugin will then be called to update the property.
3685 sa_set_protocol_property(sa_property_t prop, char *section, char *value)
3687 sa_protocol_properties_t propset;
3688 char *proto;
3689 int ret = SA_INVALID_PROTOCOL;
3691 propset = ((xmlNodePtr)prop)->parent;
3692 if (propset != NULL) {
3693 proto = sa_get_optionset_attr(propset, "type");
3694 if (proto != NULL) {
3695 if (section != NULL)
3696 set_node_attr((xmlNodePtr)prop, "section",
3697 section);
3698 set_node_attr((xmlNodePtr)prop, "value", value);
3699 ret = sa_proto_set_property(proto, prop);
3700 sa_free_attr_string(proto);
3703 return (ret);
3707 * sa_add_protocol_property(propset, prop)
3709 * Add a new property to the protocol specific property set.
3713 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
3715 xmlNodePtr node;
3717 /* should check for legitimacy */
3718 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
3719 if (node != NULL)
3720 return (SA_OK);
3721 return (SA_NO_MEMORY);
3725 * sa_create_protocol_properties(proto)
3727 * Create a protocol specific property set.
3730 sa_protocol_properties_t
3731 sa_create_protocol_properties(char *proto)
3733 xmlNodePtr node;
3735 node = xmlNewNode(NULL, (xmlChar *)"propertyset");
3736 if (node != NULL)
3737 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
3738 return (node);
3742 * sa_get_share_resource(share, resource)
3744 * Get the named resource from the share, if it exists. If resource is
3745 * NULL, get the first resource.
3748 sa_resource_t
3749 sa_get_share_resource(sa_share_t share, char *resource)
3751 xmlNodePtr node = NULL;
3752 xmlChar *name;
3754 if (share != NULL) {
3755 for (node = ((xmlNodePtr)share)->children; node != NULL;
3756 node = node->next) {
3757 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) {
3758 if (resource == NULL) {
3760 * We are looking for the first
3761 * resource node and not a names
3762 * resource.
3764 break;
3765 } else {
3766 /* is it the correct share? */
3767 name = xmlGetProp(node,
3768 (xmlChar *)"name");
3769 if (name != NULL &&
3770 xmlStrcasecmp(name,
3771 (xmlChar *)resource) == 0) {
3772 xmlFree(name);
3773 break;
3775 xmlFree(name);
3780 return ((sa_resource_t)node);
3784 * sa_get_next_resource(resource)
3785 * Return the next share following the specified share
3786 * from the internal list of shares. Returns NULL if there
3787 * are no more shares. The list is relative to the same
3788 * group.
3790 sa_share_t
3791 sa_get_next_resource(sa_resource_t resource)
3793 xmlNodePtr node = NULL;
3795 if (resource != NULL) {
3796 for (node = ((xmlNodePtr)resource)->next; node != NULL;
3797 node = node->next) {
3798 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
3799 break;
3802 return ((sa_share_t)node);
3806 * _sa_get_next_resource_index(share)
3808 * get the next resource index number (one greater then current largest)
3811 static int
3812 _sa_get_next_resource_index(sa_share_t share)
3814 sa_resource_t resource;
3815 int index = 0;
3816 char *id;
3818 for (resource = sa_get_share_resource(share, NULL);
3819 resource != NULL;
3820 resource = sa_get_next_resource(resource)) {
3821 id = get_node_attr((void *)resource, "id");
3822 if (id != NULL) {
3823 int val;
3824 val = atoi(id);
3825 if (val > index)
3826 index = val;
3827 sa_free_attr_string(id);
3830 return (index + 1);
3835 * sa_add_resource(share, resource, persist, &err)
3837 * Adds a new resource name associated with share. The resource name
3838 * must be unique in the system and will be case insensitive (eventually).
3841 sa_resource_t
3842 sa_add_resource(sa_share_t share, char *resource, int persist, int *error)
3844 xmlNodePtr node;
3845 int err = SA_OK;
3846 sa_resource_t res;
3847 sa_group_t group;
3848 sa_handle_t handle;
3849 char istring[8]; /* just big enough for an integer value */
3850 int index;
3852 group = sa_get_parent_group(share);
3853 handle = sa_find_group_handle(group);
3854 res = sa_find_resource(handle, resource);
3855 if (res != NULL) {
3856 err = SA_DUPLICATE_NAME;
3857 res = NULL;
3858 } else {
3859 node = xmlNewChild((xmlNodePtr)share, NULL,
3860 (xmlChar *)"resource", NULL);
3861 if (node != NULL) {
3862 (void) xmlSetProp(node, (xmlChar *)"name",
3863 (xmlChar *)resource);
3864 (void) xmlSetProp(node, (xmlChar *)"type", persist ?
3865 (xmlChar *)"persist" : (xmlChar *)"transient");
3866 if (persist != SA_SHARE_TRANSIENT) {
3867 index = _sa_get_next_resource_index(share);
3868 (void) snprintf(istring, sizeof (istring), "%d",
3869 index);
3870 (void) xmlSetProp(node, (xmlChar *)"id",
3871 (xmlChar *)istring);
3873 if (!sa_is_persistent((sa_group_t)share))
3874 goto done;
3876 if (!sa_group_is_zfs(group)) {
3877 /* ZFS doesn't use resource names */
3878 sa_handle_impl_t ihandle;
3880 ihandle = (sa_handle_impl_t)
3881 sa_find_group_handle(
3882 group);
3883 if (ihandle != NULL)
3884 err = sa_commit_share(
3885 ihandle->scfhandle, group,
3886 share);
3887 else
3888 err = SA_SYSTEM_ERR;
3889 } else {
3890 err = sa_zfs_update((sa_share_t)group);
3895 done:
3896 if (error != NULL)
3897 *error = err;
3898 return ((sa_resource_t)node);
3902 * sa_remove_resource(resource)
3904 * Remove the resource name from the share (and the system)
3908 sa_remove_resource(sa_resource_t resource)
3910 sa_share_t share;
3911 sa_group_t group;
3912 char *type;
3913 int ret = SA_OK;
3914 boolean_t transient = B_FALSE;
3915 sa_optionset_t opt;
3917 share = sa_get_resource_parent(resource);
3918 type = sa_get_share_attr(share, "type");
3919 group = sa_get_parent_group(share);
3922 if (type != NULL) {
3923 if (strcmp(type, "persist") != 0)
3924 transient = B_TRUE;
3925 sa_free_attr_string(type);
3928 /* Disable the resource for all protocols. */
3929 (void) sa_disable_resource(resource, NULL);
3931 /* Remove any optionsets from the resource. */
3932 for (opt = sa_get_optionset(resource, NULL);
3933 opt != NULL;
3934 opt = sa_get_next_optionset(opt))
3935 (void) sa_destroy_optionset(opt);
3937 /* Remove from the share */
3938 xmlUnlinkNode((xmlNode *)resource);
3939 xmlFreeNode((xmlNode *)resource);
3941 /* only do SMF action if permanent and not ZFS */
3942 if (transient)
3943 return (ret);
3945 if (!sa_group_is_zfs(group)) {
3946 sa_handle_impl_t ihandle;
3947 ihandle = (sa_handle_impl_t)sa_find_group_handle(group);
3948 if (ihandle != NULL)
3949 ret = sa_commit_share(ihandle->scfhandle, group, share);
3950 else
3951 ret = SA_SYSTEM_ERR;
3952 } else {
3953 ret = sa_zfs_update((sa_share_t)group);
3956 return (ret);
3960 * proto_rename_resource(handle, group, resource, newname)
3962 * Helper function for sa_rename_resource that notifies the protocol
3963 * of a resource name change prior to a config repository update.
3965 static int
3966 proto_rename_resource(sa_handle_t handle, sa_group_t group,
3967 sa_resource_t resource, char *newname)
3969 sa_optionset_t optionset;
3970 int ret = SA_OK;
3971 int err;
3973 for (optionset = sa_get_optionset(group, NULL);
3974 optionset != NULL;
3975 optionset = sa_get_next_optionset(optionset)) {
3976 char *type;
3977 type = sa_get_optionset_attr(optionset, "type");
3978 if (type != NULL) {
3979 err = sa_proto_rename_resource(handle, type, resource,
3980 newname);
3981 if (err != SA_OK)
3982 ret = err;
3983 sa_free_attr_string(type);
3986 return (ret);
3990 * sa_rename_resource(resource, newname)
3992 * Rename the resource to the new name, if it is unique.
3996 sa_rename_resource(sa_resource_t resource, char *newname)
3998 sa_share_t share;
3999 sa_group_t group = NULL;
4000 sa_resource_t target;
4001 int ret = SA_CONFIG_ERR;
4002 sa_handle_t handle = NULL;
4004 share = sa_get_resource_parent(resource);
4005 if (share == NULL)
4006 return (ret);
4008 group = sa_get_parent_group(share);
4009 if (group == NULL)
4010 return (ret);
4012 handle = (sa_handle_impl_t)sa_find_group_handle(group);
4013 if (handle == NULL)
4014 return (ret);
4016 target = sa_find_resource(handle, newname);
4017 if (target != NULL) {
4018 ret = SA_DUPLICATE_NAME;
4019 } else {
4021 * Everything appears to be valid at this
4022 * point. Change the name of the active share and then
4023 * update the share in the appropriate repository.
4025 ret = proto_rename_resource(handle, group, resource, newname);
4026 set_node_attr(resource, "name", newname);
4028 if (!sa_is_persistent((sa_group_t)share))
4029 return (ret);
4031 if (!sa_group_is_zfs(group)) {
4032 sa_handle_impl_t ihandle = (sa_handle_impl_t)handle;
4033 ret = sa_commit_share(ihandle->scfhandle, group,
4034 share);
4035 } else {
4036 ret = sa_zfs_update((sa_share_t)group);
4039 return (ret);
4043 * sa_get_resource_attr(resource, tag)
4045 * Get the named attribute of the resource. "name" and "id" are
4046 * currently defined. NULL if tag not defined.
4049 char *
4050 sa_get_resource_attr(sa_resource_t resource, char *tag)
4052 return (get_node_attr((void *)resource, tag));
4056 * sa_set_resource_attr(resource, tag, value)
4058 * Get the named attribute of the resource. "name" and "id" are
4059 * currently defined. NULL if tag not defined. Currently we don't do
4060 * much, but additional checking may be needed in the future.
4064 sa_set_resource_attr(sa_resource_t resource, char *tag, char *value)
4066 set_node_attr((void *)resource, tag, value);
4067 return (SA_OK);
4071 * sa_get_resource_parent(resource_t)
4073 * Returns the share associated with the resource.
4076 sa_share_t
4077 sa_get_resource_parent(sa_resource_t resource)
4079 sa_share_t share = NULL;
4081 if (resource != NULL)
4082 share = (sa_share_t)((xmlNodePtr)resource)->parent;
4083 return (share);
4087 * find_resource(group, name)
4089 * Find the resource within the group.
4092 static sa_resource_t
4093 find_resource(sa_group_t group, char *resname)
4095 sa_share_t share;
4096 sa_resource_t resource = NULL;
4097 char *name;
4099 /* Iterate over all the shares and resources in the group. */
4100 for (share = sa_get_share(group, NULL);
4101 share != NULL && resource == NULL;
4102 share = sa_get_next_share(share)) {
4103 for (resource = sa_get_share_resource(share, NULL);
4104 resource != NULL;
4105 resource = sa_get_next_resource(resource)) {
4106 name = sa_get_resource_attr(resource, "name");
4107 if (name != NULL && xmlStrcasecmp((xmlChar*)name,
4108 (xmlChar*)resname) == 0) {
4109 sa_free_attr_string(name);
4110 break;
4112 if (name != NULL) {
4113 sa_free_attr_string(name);
4117 return (resource);
4121 * sa_find_resource(name)
4123 * Find the named resource in the system.
4126 sa_resource_t
4127 sa_find_resource(sa_handle_t handle, char *name)
4129 sa_group_t group;
4130 sa_group_t zgroup;
4131 sa_resource_t resource = NULL;
4134 * Iterate over all groups and zfs subgroups and check for
4135 * resource name in them.
4137 for (group = sa_get_group(handle, NULL); group != NULL;
4138 group = sa_get_next_group(group)) {
4140 if (is_zfs_group(group)) {
4141 for (zgroup =
4142 (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
4143 (xmlChar *)"group");
4144 zgroup != NULL && resource == NULL;
4145 zgroup = sa_get_next_group(zgroup)) {
4146 resource = find_resource(zgroup, name);
4148 } else {
4149 resource = find_resource(group, name);
4151 if (resource != NULL)
4152 break;
4154 return (resource);
4158 * sa_get_resource(group, resource)
4160 * Search all the shares in the specified group for a share with a
4161 * resource name matching the one specified.
4163 * In the future, it may be advantageous to allow group to be NULL and
4164 * search all groups but that isn't needed at present.
4167 sa_resource_t
4168 sa_get_resource(sa_group_t group, char *resource)
4170 sa_share_t share = NULL;
4171 sa_resource_t res = NULL;
4173 if (resource != NULL) {
4174 for (share = sa_get_share(group, NULL);
4175 share != NULL && res == NULL;
4176 share = sa_get_next_share(share)) {
4177 res = sa_get_share_resource(share, resource);
4180 return (res);
4184 * get_protocol_list(optionset, object)
4186 * Get the protocol optionset list for the object and add them as
4187 * properties to optionset.
4189 static int
4190 get_protocol_list(sa_optionset_t optionset, void *object)
4192 sa_property_t prop;
4193 sa_optionset_t opts;
4194 int ret = SA_OK;
4196 for (opts = sa_get_optionset(object, NULL);
4197 opts != NULL;
4198 opts = sa_get_next_optionset(opts)) {
4199 char *type;
4200 type = sa_get_optionset_attr(opts, "type");
4202 * It is possible to have a non-protocol optionset. We
4203 * skip any of those found.
4205 if (type == NULL)
4206 continue;
4207 prop = sa_create_property(type, "true");
4208 sa_free_attr_string(type);
4209 if (prop != NULL)
4210 prop = (sa_property_t)xmlAddChild((xmlNodePtr)optionset,
4211 (xmlNodePtr)prop);
4212 /* If prop is NULL, don't bother continuing */
4213 if (prop == NULL) {
4214 ret = SA_NO_MEMORY;
4215 break;
4218 return (ret);
4222 * sa_free_protoset(optionset)
4224 * Free the protocol property optionset.
4226 static void
4227 sa_free_protoset(sa_optionset_t optionset)
4229 if (optionset != NULL) {
4230 xmlUnlinkNode((xmlNodePtr) optionset);
4231 xmlFreeNode((xmlNodePtr) optionset);
4236 * sa_optionset_t sa_get_active_protocols(object)
4238 * Return a list of the protocols that are active for the object.
4239 * This is currently an internal helper function, but could be
4240 * made visible if there is enough demand for it.
4242 * The function finds the parent group and extracts the protocol
4243 * optionsets creating a new optionset with the protocols as properties.
4245 * The caller must free the returned optionset.
4248 static sa_optionset_t
4249 sa_get_active_protocols(void *object)
4251 sa_optionset_t options;
4252 sa_share_t share = NULL;
4253 sa_group_t group = NULL;
4254 sa_resource_t resource = NULL;
4255 int ret = SA_OK;
4257 if (object == NULL)
4258 return (NULL);
4259 options = (sa_optionset_t)xmlNewNode(NULL, (xmlChar *)"optionset");
4260 if (options == NULL)
4261 return (NULL);
4264 * Find the objects up the tree that might have protocols
4265 * enabled on them.
4267 if (sa_is_resource(object)) {
4268 resource = (sa_resource_t)object;
4269 share = sa_get_resource_parent(resource);
4270 group = sa_get_parent_group(share);
4271 } else if (sa_is_share(object)) {
4272 share = (sa_share_t)object;
4273 group = sa_get_parent_group(share);
4274 } else {
4275 group = (sa_group_t)group;
4277 if (resource != NULL)
4278 ret = get_protocol_list(options, resource);
4279 if (ret == SA_OK && share != NULL)
4280 ret = get_protocol_list(options, share);
4281 if (ret == SA_OK && group != NULL)
4282 ret = get_protocol_list(options, group);
4285 * If there was an error, we won't have a complete list so
4286 * abandon everything. The caller will have to deal with the
4287 * issue.
4289 if (ret != SA_OK) {
4290 sa_free_protoset(options);
4291 options = NULL;
4293 return (options);
4297 * sa_enable_resource, protocol)
4298 * Disable the specified share to the specified protocol.
4299 * If protocol is NULL, then all protocols.
4302 sa_enable_resource(sa_resource_t resource, char *protocol)
4304 int ret = SA_OK;
4306 if (protocol != NULL) {
4307 ret = sa_proto_share_resource(protocol, resource);
4308 } else {
4309 sa_optionset_t protoset;
4310 sa_property_t prop;
4311 char *proto;
4312 int err;
4314 /* need to do all protocols */
4315 protoset = sa_get_active_protocols(resource);
4316 if (protoset == NULL)
4317 return (SA_NO_MEMORY);
4318 for (prop = sa_get_property(protoset, NULL);
4319 prop != NULL;
4320 prop = sa_get_next_property(prop)) {
4321 proto = sa_get_property_attr(prop, "type");
4322 if (proto == NULL) {
4323 ret = SA_NO_MEMORY;
4324 continue;
4326 err = sa_proto_share_resource(proto, resource);
4327 if (err != SA_OK)
4328 ret = err;
4329 sa_free_attr_string(proto);
4331 sa_free_protoset(protoset);
4333 if (ret == SA_OK)
4334 (void) sa_set_resource_attr(resource, "shared", NULL);
4336 return (ret);
4340 * sa_disable_resource(resource, protocol)
4342 * Disable the specified share for the specified protocol. If
4343 * protocol is NULL, then all protocols. If the underlying
4344 * protocol doesn't implement disable at the resource level, we
4345 * disable at the share level.
4348 sa_disable_resource(sa_resource_t resource, char *protocol)
4350 int ret = SA_OK;
4352 if (protocol != NULL) {
4353 ret = sa_proto_unshare_resource(protocol, resource);
4354 if (ret == SA_NOT_IMPLEMENTED) {
4355 sa_share_t parent;
4357 * The protocol doesn't implement unshare
4358 * resource. That implies that resource names are
4359 * simple aliases for this protocol so we need to
4360 * unshare the share.
4362 parent = sa_get_resource_parent(resource);
4363 if (parent != NULL)
4364 ret = sa_disable_share(parent, protocol);
4365 else
4366 ret = SA_CONFIG_ERR;
4368 } else {
4369 sa_optionset_t protoset;
4370 sa_property_t prop;
4371 char *proto;
4372 int err;
4374 /* need to do all protocols */
4375 protoset = sa_get_active_protocols(resource);
4376 if (protoset == NULL)
4377 return (SA_NO_MEMORY);
4378 for (prop = sa_get_property(protoset, NULL);
4379 prop != NULL;
4380 prop = sa_get_next_property(prop)) {
4381 proto = sa_get_property_attr(prop, "type");
4382 if (proto == NULL) {
4383 ret = SA_NO_MEMORY;
4384 continue;
4386 err = sa_proto_unshare_resource(proto, resource);
4387 if (err == SA_NOT_SUPPORTED) {
4388 sa_share_t parent;
4389 parent = sa_get_resource_parent(resource);
4390 if (parent != NULL)
4391 err = sa_disable_share(parent, proto);
4392 else
4393 err = SA_CONFIG_ERR;
4395 if (err != SA_OK)
4396 ret = err;
4397 sa_free_attr_string(proto);
4399 sa_free_protoset(protoset);
4401 if (ret == SA_OK)
4402 (void) sa_set_resource_attr(resource, "shared", NULL);
4404 return (ret);
4408 * sa_set_resource_description(resource, content)
4410 * Set the description of share to content.
4414 sa_set_resource_description(sa_resource_t resource, char *content)
4416 xmlNodePtr node;
4417 sa_group_t group;
4418 sa_share_t share;
4419 int ret = SA_OK;
4421 for (node = ((xmlNodePtr)resource)->children;
4422 node != NULL;
4423 node = node->next) {
4424 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
4425 break;
4429 /* no existing description but want to add */
4430 if (node == NULL && content != NULL) {
4431 /* add a description */
4432 node = _sa_set_share_description(resource, content);
4433 } else if (node != NULL && content != NULL) {
4434 /* update a description */
4435 xmlNodeSetContent(node, (xmlChar *)content);
4436 } else if (node != NULL && content == NULL) {
4437 /* remove an existing description */
4438 xmlUnlinkNode(node);
4439 xmlFreeNode(node);
4442 share = sa_get_resource_parent(resource);
4443 group = sa_get_parent_group(share);
4444 if (group != NULL &&
4445 sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
4446 sa_handle_impl_t impl_handle;
4447 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
4448 if (impl_handle != NULL)
4449 ret = sa_commit_share(impl_handle->scfhandle,
4450 group, share);
4451 else
4452 ret = SA_SYSTEM_ERR;
4454 return (ret);
4458 * sa_get_resource_description(share)
4460 * Return the description text for the specified share if it
4461 * exists. NULL if no description exists.
4464 char *
4465 sa_get_resource_description(sa_resource_t resource)
4467 xmlChar *description = NULL;
4468 xmlNodePtr node;
4470 for (node = ((xmlNodePtr)resource)->children; node != NULL;
4471 node = node->next) {
4472 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0)
4473 break;
4475 if (node != NULL) {
4476 description = xmlNodeGetContent(node);
4477 fixproblemchars((char *)description);
4479 return ((char *)description);