1*3034Sdougm /*
2*3034Sdougm  * CDDL HEADER START
3*3034Sdougm  *
4*3034Sdougm  * The contents of this file are subject to the terms of the
5*3034Sdougm  * Common Development and Distribution License (the "License").
6*3034Sdougm  * You may not use this file except in compliance with the License.
7*3034Sdougm  *
8*3034Sdougm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*3034Sdougm  * or http://www.opensolaris.org/os/licensing.
10*3034Sdougm  * See the License for the specific language governing permissions
11*3034Sdougm  * and limitations under the License.
12*3034Sdougm  *
13*3034Sdougm  * When distributing Covered Code, include this CDDL HEADER in each
14*3034Sdougm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*3034Sdougm  * If applicable, add the following below this CDDL HEADER, with the
16*3034Sdougm  * fields enclosed by brackets "[]" replaced with your own identifying
17*3034Sdougm  * information: Portions Copyright [yyyy] [name of copyright owner]
18*3034Sdougm  *
19*3034Sdougm  * CDDL HEADER END
20*3034Sdougm  */
21*3034Sdougm 
22*3034Sdougm /*
23*3034Sdougm  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*3034Sdougm  * Use is subject to license terms.
25*3034Sdougm  */
26*3034Sdougm 
27*3034Sdougm #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*3034Sdougm 
29*3034Sdougm /*
30*3034Sdougm  * Share control API
31*3034Sdougm  */
32*3034Sdougm #include <stdio.h>
33*3034Sdougm #include <string.h>
34*3034Sdougm #include <ctype.h>
35*3034Sdougm #include <sys/types.h>
36*3034Sdougm #include <sys/stat.h>
37*3034Sdougm #include <unistd.h>
38*3034Sdougm #include <libxml/parser.h>
39*3034Sdougm #include <libxml/tree.h>
40*3034Sdougm #include "libshare.h"
41*3034Sdougm #include "libshare_impl.h"
42*3034Sdougm #include <libscf.h>
43*3034Sdougm #include "scfutil.h"
44*3034Sdougm #include <ctype.h>
45*3034Sdougm #include <libintl.h>
46*3034Sdougm 
47*3034Sdougm #if _NOT_SMF
48*3034Sdougm #define	CONFIG_FILE	"/var/tmp/share.cfg"
49*3034Sdougm #define	CONFIG_FILE_TMP	"/var/tmp/share.cfg.tmp"
50*3034Sdougm #endif
51*3034Sdougm #define	TSTAMP(tm)	(uint64_t)(((uint64_t)tm.tv_sec << 32) | \
52*3034Sdougm 					(tm.tv_nsec & 0xffffffff))
53*3034Sdougm 
54*3034Sdougm /*
55*3034Sdougm  * internal data structures
56*3034Sdougm  */
57*3034Sdougm 
58*3034Sdougm static xmlNodePtr sa_config_tree;	/* the current config */
59*3034Sdougm static xmlDocPtr  sa_config_doc = NULL;	/* current config document */
60*3034Sdougm extern struct sa_proto_plugin *sap_proto_list;
61*3034Sdougm 
62*3034Sdougm /* current SMF/SVC repository handle */
63*3034Sdougm static scfutilhandle_t *scf_handle = NULL;
64*3034Sdougm extern void getlegacyconfig(char *, xmlNodePtr *);
65*3034Sdougm extern int gettransients(xmlNodePtr *);
66*3034Sdougm extern int sa_valid_property(void *, char *, sa_property_t);
67*3034Sdougm extern char *sa_fstype(char *);
68*3034Sdougm extern int sa_is_share(void *);
69*3034Sdougm extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
70*3034Sdougm extern int sa_group_is_zfs(sa_group_t);
71*3034Sdougm extern int sa_path_is_zfs(char *);
72*3034Sdougm extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
73*3034Sdougm extern void update_legacy_config(void);
74*3034Sdougm extern int issubdir(char *, char *);
75*3034Sdougm 
76*3034Sdougm static int sa_initialized = 0;
77*3034Sdougm 
78*3034Sdougm /* helper functions */
79*3034Sdougm 
80*3034Sdougm char *
81*3034Sdougm sa_errorstr(int err)
82*3034Sdougm {
83*3034Sdougm 	static char errstr[32];
84*3034Sdougm 	char *ret = NULL;
85*3034Sdougm 
86*3034Sdougm 	switch (err) {
87*3034Sdougm 	case SA_OK:
88*3034Sdougm 	    ret = gettext("ok");
89*3034Sdougm 	    break;
90*3034Sdougm 	case SA_NO_SUCH_PATH:
91*3034Sdougm 	    ret = gettext("path doesn't exist");
92*3034Sdougm 	    break;
93*3034Sdougm 	case SA_NO_MEMORY:
94*3034Sdougm 	    ret = gettext("no memory");
95*3034Sdougm 	    break;
96*3034Sdougm 	case SA_DUPLICATE_NAME:
97*3034Sdougm 	    ret = gettext("name in use");
98*3034Sdougm 	    break;
99*3034Sdougm 	case SA_BAD_PATH:
100*3034Sdougm 	    ret = gettext("bad path");
101*3034Sdougm 	    break;
102*3034Sdougm 	case SA_NO_SUCH_GROUP:
103*3034Sdougm 	    ret = gettext("no such group");
104*3034Sdougm 	    break;
105*3034Sdougm 	case SA_CONFIG_ERR:
106*3034Sdougm 	    ret = gettext("configuration error");
107*3034Sdougm 	    break;
108*3034Sdougm 	case SA_SYSTEM_ERR:
109*3034Sdougm 	    ret = gettext("system error");
110*3034Sdougm 	    break;
111*3034Sdougm 	case SA_SYNTAX_ERR:
112*3034Sdougm 	    ret = gettext("syntax error");
113*3034Sdougm 	    break;
114*3034Sdougm 	case SA_NO_PERMISSION:
115*3034Sdougm 	    ret = gettext("no permission");
116*3034Sdougm 	    break;
117*3034Sdougm 	case SA_BUSY:
118*3034Sdougm 	    ret = gettext("busy");
119*3034Sdougm 	    break;
120*3034Sdougm 	case SA_NO_SUCH_PROP:
121*3034Sdougm 	    ret = gettext("no such property");
122*3034Sdougm 	    break;
123*3034Sdougm 	case SA_INVALID_NAME:
124*3034Sdougm 	    ret = gettext("invalid name");
125*3034Sdougm 	    break;
126*3034Sdougm 	case SA_INVALID_PROTOCOL:
127*3034Sdougm 	    ret = gettext("invalid protocol");
128*3034Sdougm 	    break;
129*3034Sdougm 	case SA_NOT_ALLOWED:
130*3034Sdougm 	    ret = gettext("operation not allowed");
131*3034Sdougm 	    break;
132*3034Sdougm 	case SA_BAD_VALUE:
133*3034Sdougm 	    ret = gettext("bad property value");
134*3034Sdougm 	    break;
135*3034Sdougm 	case SA_INVALID_SECURITY:
136*3034Sdougm 	    ret = gettext("invalid security type");
137*3034Sdougm 	    break;
138*3034Sdougm 	case SA_NO_SUCH_SECURITY:
139*3034Sdougm 	    ret = gettext("security type not found");
140*3034Sdougm 	    break;
141*3034Sdougm 	case SA_VALUE_CONFLICT:
142*3034Sdougm 	    ret = gettext("property value conflict");
143*3034Sdougm 	    break;
144*3034Sdougm 	case SA_NOT_IMPLEMENTED:
145*3034Sdougm 	    ret = gettext("not implemented");
146*3034Sdougm 	    break;
147*3034Sdougm 	case SA_INVALID_PATH:
148*3034Sdougm 	    ret = gettext("invalid path");
149*3034Sdougm 	    break;
150*3034Sdougm 	case SA_NOT_SUPPORTED:
151*3034Sdougm 	    ret = gettext("operation not supported");
152*3034Sdougm 	    break;
153*3034Sdougm 	case SA_PROP_SHARE_ONLY:
154*3034Sdougm 	    ret = gettext("property not valid for group");
155*3034Sdougm 	    break;
156*3034Sdougm 	case SA_NOT_SHARED:
157*3034Sdougm 	    ret = gettext("not shared");
158*3034Sdougm 	    break;
159*3034Sdougm 	default:
160*3034Sdougm 	    (void) snprintf(errstr, sizeof (errstr),
161*3034Sdougm 				gettext("unknown %d"), err);
162*3034Sdougm 	    ret = errstr;
163*3034Sdougm 	}
164*3034Sdougm 	return (ret);
165*3034Sdougm }
166*3034Sdougm 
167*3034Sdougm /*
168*3034Sdougm  * get_legacy_timestamp(root, path)
169*3034Sdougm  *	gets the timestamp of the last time sharemgr updated the legacy
170*3034Sdougm  *	files. This is used to determine if someone has modified them by
171*3034Sdougm  *	hand.
172*3034Sdougm  */
173*3034Sdougm static uint64_t
174*3034Sdougm get_legacy_timestamp(xmlNodePtr root, char *path)
175*3034Sdougm {
176*3034Sdougm 	uint64_t tval = 0;
177*3034Sdougm 	xmlNodePtr node;
178*3034Sdougm 	xmlChar *lpath = NULL;
179*3034Sdougm 	xmlChar *timestamp = NULL;
180*3034Sdougm 
181*3034Sdougm 	for (node = root->xmlChildrenNode; node != NULL;
182*3034Sdougm 		node = node->next) {
183*3034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
184*3034Sdougm 		/* a possible legacy node for this path */
185*3034Sdougm 		lpath = xmlGetProp(node, (xmlChar *)"path");
186*3034Sdougm 		if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) {
187*3034Sdougm 		    /* now have the node, extract the data */
188*3034Sdougm 		    timestamp = xmlGetProp(node, (xmlChar *)"timestamp");
189*3034Sdougm 		    if (timestamp != NULL) {
190*3034Sdougm 			tval = strtoull((char *)timestamp, NULL, 0);
191*3034Sdougm 			break;
192*3034Sdougm 		    }
193*3034Sdougm 		}
194*3034Sdougm 		if (lpath != NULL) {
195*3034Sdougm 		    xmlFree(lpath);
196*3034Sdougm 		    lpath = NULL;
197*3034Sdougm 		}
198*3034Sdougm 	    }
199*3034Sdougm 	}
200*3034Sdougm 	if (lpath != NULL)
201*3034Sdougm 	    xmlFree(lpath);
202*3034Sdougm 	if (timestamp != NULL)
203*3034Sdougm 	    xmlFree(timestamp);
204*3034Sdougm 	return (tval);
205*3034Sdougm }
206*3034Sdougm 
207*3034Sdougm /*
208*3034Sdougm  * set_legacy_timestamp(root, path, timevalue)
209*3034Sdougm  *
210*3034Sdougm  * add the current timestamp value to the configuration for use in
211*3034Sdougm  * determining when to update the legacy files.  For SMF, this
212*3034Sdougm  * property is kept in default/operation/legacy_timestamp
213*3034Sdougm  */
214*3034Sdougm 
215*3034Sdougm static void
216*3034Sdougm set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval)
217*3034Sdougm {
218*3034Sdougm 	xmlNodePtr node;
219*3034Sdougm 	xmlChar *lpath = NULL;
220*3034Sdougm 
221*3034Sdougm 	for (node = root->xmlChildrenNode; node != NULL;
222*3034Sdougm 		node = node->next) {
223*3034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
224*3034Sdougm 		/* a possible legacy node for this path */
225*3034Sdougm 		lpath = xmlGetProp(node, (xmlChar *)"path");
226*3034Sdougm 		if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) {
227*3034Sdougm 		    xmlFree(lpath);
228*3034Sdougm 		    break;
229*3034Sdougm 		}
230*3034Sdougm 		if (lpath != NULL)
231*3034Sdougm 		    xmlFree(lpath);
232*3034Sdougm 	    }
233*3034Sdougm 	}
234*3034Sdougm 	if (node == NULL) {
235*3034Sdougm 	    /* need to create the first legacy timestamp node */
236*3034Sdougm 	    node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL);
237*3034Sdougm 	}
238*3034Sdougm 	if (node != NULL) {
239*3034Sdougm 	    char tstring[32];
240*3034Sdougm 	    int ret;
241*3034Sdougm 
242*3034Sdougm 	    (void) snprintf(tstring, sizeof (tstring), "%lld", tval);
243*3034Sdougm 	    xmlSetProp(node, (xmlChar *)"timestamp", (xmlChar *)tstring);
244*3034Sdougm 	    xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path);
245*3034Sdougm 	    /* now commit to SMF */
246*3034Sdougm 	    ret = sa_get_instance(scf_handle, "default");
247*3034Sdougm 	    if (ret == SA_OK) {
248*3034Sdougm 		ret = sa_start_transaction(scf_handle, "operation");
249*3034Sdougm 		if (ret == SA_OK) {
250*3034Sdougm 		    ret = sa_set_property(scf_handle, "legacy-timestamp",
251*3034Sdougm 					    tstring);
252*3034Sdougm 		    if (ret == SA_OK) {
253*3034Sdougm 			(void) sa_end_transaction(scf_handle);
254*3034Sdougm 		    } else {
255*3034Sdougm 			sa_abort_transaction(scf_handle);
256*3034Sdougm 		    }
257*3034Sdougm 		}
258*3034Sdougm 	    }
259*3034Sdougm 	}
260*3034Sdougm }
261*3034Sdougm 
262*3034Sdougm /*
263*3034Sdougm  * is_shared(share)
264*3034Sdougm  *
265*3034Sdougm  * determine if the specified share is currently shared or not.
266*3034Sdougm  */
267*3034Sdougm static int
268*3034Sdougm is_shared(sa_share_t share)
269*3034Sdougm {
270*3034Sdougm 	char *shared;
271*3034Sdougm 	int result = 0; /* assume not */
272*3034Sdougm 
273*3034Sdougm 	shared = sa_get_share_attr(share, "shared");
274*3034Sdougm 	if (shared != NULL) {
275*3034Sdougm 	    if (strcmp(shared, "true") == 0)
276*3034Sdougm 		result = 1;
277*3034Sdougm 	    sa_free_attr_string(shared);
278*3034Sdougm 	}
279*3034Sdougm 	return (result);
280*3034Sdougm }
281*3034Sdougm 
282*3034Sdougm /*
283*3034Sdougm  * checksubdir determines if the specified path is a subdirectory of
284*3034Sdougm  * another share. It calls issubdir() from the old share
285*3034Sdougm  * implementation to do the complicated work.
286*3034Sdougm  */
287*3034Sdougm static int
288*3034Sdougm checksubdir(char *newpath)
289*3034Sdougm {
290*3034Sdougm 	sa_group_t group;
291*3034Sdougm 	sa_share_t share;
292*3034Sdougm 	int issub;
293*3034Sdougm 	char *path = NULL;
294*3034Sdougm 
295*3034Sdougm 	for (issub = 0, group = sa_get_group(NULL);
296*3034Sdougm 		group != NULL && !issub;
297*3034Sdougm 		group = sa_get_next_group(group)) {
298*3034Sdougm 	    for (share = sa_get_share(group, NULL); share != NULL;
299*3034Sdougm 		    share = sa_get_next_share(share)) {
300*3034Sdougm 		/*
301*3034Sdougm 		 * The original behavior of share never checked
302*3034Sdougm 		 * against the permanent configuration
303*3034Sdougm 		 * (/etc/dfs/dfstab).  PIT has a number of cases where
304*3034Sdougm 		 * it depends on this older behavior even though it
305*3034Sdougm 		 * could be considered incorrect.  We may tighten this
306*3034Sdougm 		 * up in the future.
307*3034Sdougm 		 */
308*3034Sdougm 		if (!is_shared(share))
309*3034Sdougm 		    continue;
310*3034Sdougm 
311*3034Sdougm 		path = sa_get_share_attr(share, "path");
312*3034Sdougm 		if (newpath != NULL &&
313*3034Sdougm 		    (strcmp(path, newpath) == 0 || issubdir(newpath, path) ||
314*3034Sdougm 			issubdir(path, newpath))) {
315*3034Sdougm 		    sa_free_attr_string(path);
316*3034Sdougm 		    path = NULL;
317*3034Sdougm 		    issub = SA_INVALID_PATH;
318*3034Sdougm 		    break;
319*3034Sdougm 		}
320*3034Sdougm 		sa_free_attr_string(path);
321*3034Sdougm 		path = NULL;
322*3034Sdougm 	    }
323*3034Sdougm 	}
324*3034Sdougm 	if (path != NULL)
325*3034Sdougm 	    sa_free_attr_string(path);
326*3034Sdougm 	return (issub);
327*3034Sdougm }
328*3034Sdougm 
329*3034Sdougm /*
330*3034Sdougm  * validpath(path)
331*3034Sdougm  * determine if the provided path is valid for a share. It shouldn't
332*3034Sdougm  * be a sub-dir of an already shared path or the parent directory of a
333*3034Sdougm  * share path.
334*3034Sdougm  */
335*3034Sdougm static int
336*3034Sdougm validpath(char *path)
337*3034Sdougm {
338*3034Sdougm 	int error = SA_OK;
339*3034Sdougm 	struct stat st;
340*3034Sdougm 	sa_share_t share;
341*3034Sdougm 	char *fstype;
342*3034Sdougm 
343*3034Sdougm 	if (*path != '/') {
344*3034Sdougm 	    return (SA_BAD_PATH);
345*3034Sdougm 	}
346*3034Sdougm 	if (stat(path, &st) < 0) {
347*3034Sdougm 	    error = SA_NO_SUCH_PATH;
348*3034Sdougm 	} else {
349*3034Sdougm 	    share = sa_find_share(path);
350*3034Sdougm 	    if (share != NULL) {
351*3034Sdougm 		error = SA_DUPLICATE_NAME;
352*3034Sdougm 	    }
353*3034Sdougm 	    if (error == SA_OK) {
354*3034Sdougm 		/*
355*3034Sdougm 		 * check for special case with file system that might
356*3034Sdougm 		 * have restrictions.  For now, ZFS is the only case
357*3034Sdougm 		 * since it has its own idea of how to configure
358*3034Sdougm 		 * shares. We do this before subdir checking since
359*3034Sdougm 		 * things like ZFS will do that for us. This should
360*3034Sdougm 		 * also be done via plugin interface.
361*3034Sdougm 		 */
362*3034Sdougm 		fstype = sa_fstype(path);
363*3034Sdougm 		if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
364*3034Sdougm 		    if (sa_zfs_is_shared(path))
365*3034Sdougm 			error = SA_DUPLICATE_NAME;
366*3034Sdougm 		}
367*3034Sdougm 		if (fstype != NULL)
368*3034Sdougm 		    sa_free_fstype(fstype);
369*3034Sdougm 	    }
370*3034Sdougm 	    if (error == SA_OK) {
371*3034Sdougm 		error = checksubdir(path);
372*3034Sdougm 	    }
373*3034Sdougm 	}
374*3034Sdougm 	return (error);
375*3034Sdougm }
376*3034Sdougm 
377*3034Sdougm /*
378*3034Sdougm  * check to see if group/share is persistent.
379*3034Sdougm  */
380*3034Sdougm static int
381*3034Sdougm is_persistent(sa_group_t group)
382*3034Sdougm {
383*3034Sdougm 	char *type;
384*3034Sdougm 	int persist = 1;
385*3034Sdougm 
386*3034Sdougm 	type = sa_get_group_attr(group, "type");
387*3034Sdougm 	if (type != NULL && strcmp(type, "transient") == 0)
388*3034Sdougm 	    persist = 0;
389*3034Sdougm 	if (type != NULL)
390*3034Sdougm 	    sa_free_attr_string(type);
391*3034Sdougm 	return (persist);
392*3034Sdougm }
393*3034Sdougm 
394*3034Sdougm /*
395*3034Sdougm  * sa_valid_group_name(name)
396*3034Sdougm  *
397*3034Sdougm  * check that the "name" contains only valid characters and otherwise
398*3034Sdougm  * fits the required naming conventions. Valid names must start with
399*3034Sdougm  * an alphabetic and the remainder may consist of only alphanumeric
400*3034Sdougm  * plus the '-' and '_' characters. This name limitation comes from
401*3034Sdougm  * inherent limitations in SMF.
402*3034Sdougm  */
403*3034Sdougm 
404*3034Sdougm int
405*3034Sdougm sa_valid_group_name(char *name)
406*3034Sdougm {
407*3034Sdougm 	int ret = 1;
408*3034Sdougm 	ssize_t len;
409*3034Sdougm 
410*3034Sdougm 	if (name != NULL && isalpha(*name)) {
411*3034Sdougm 	    char c;
412*3034Sdougm 	    len = strlen(name);
413*3034Sdougm 	    if (len < (scf_max_name_len - sizeof ("group:"))) {
414*3034Sdougm 		for (c = *name++; c != '\0' && ret != 0; c = *name++) {
415*3034Sdougm 		    if (!isalnum(c) && c != '-' && c != '_')
416*3034Sdougm 			ret = 0;
417*3034Sdougm 		}
418*3034Sdougm 	    } else {
419*3034Sdougm 		ret = 0;
420*3034Sdougm 	    }
421*3034Sdougm 	} else {
422*3034Sdougm 	    ret = 0;
423*3034Sdougm 	}
424*3034Sdougm 	return (ret);
425*3034Sdougm }
426*3034Sdougm 
427*3034Sdougm 
428*3034Sdougm /*
429*3034Sdougm  * is_zfs_group(group)
430*3034Sdougm  *	Determine if the specified group is a ZFS sharenfs group
431*3034Sdougm  */
432*3034Sdougm static int
433*3034Sdougm is_zfs_group(sa_group_t group)
434*3034Sdougm {
435*3034Sdougm 	int ret = 0;
436*3034Sdougm 	xmlNodePtr parent;
437*3034Sdougm 	xmlChar *zfs;
438*3034Sdougm 
439*3034Sdougm 	if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0) {
440*3034Sdougm 	    parent = (xmlNodePtr)sa_get_parent_group(group);
441*3034Sdougm 	} else {
442*3034Sdougm 	    parent = (xmlNodePtr)group;
443*3034Sdougm 	}
444*3034Sdougm 	zfs = xmlGetProp(parent, (xmlChar *)"zfs");
445*3034Sdougm 	if (zfs != NULL) {
446*3034Sdougm 	    xmlFree(zfs);
447*3034Sdougm 	    ret = 1;
448*3034Sdougm 	}
449*3034Sdougm 	return (ret);
450*3034Sdougm }
451*3034Sdougm 
452*3034Sdougm /*
453*3034Sdougm  * sa_optionset_name(optionset, oname, len, id)
454*3034Sdougm  *	return the SMF name for the optionset. If id is not NULL, it
455*3034Sdougm  *	will have the GUID value for a share and should be used
456*3034Sdougm  *	instead of the keyword "optionset" which is used for
457*3034Sdougm  *	groups. If the optionset doesn't have a protocol type
458*3034Sdougm  *	associated with it, "default" is used. This shouldn't happen
459*3034Sdougm  *	at this point but may be desirable in the future if there are
460*3034Sdougm  *	protocol independent properties added. The name is returned in
461*3034Sdougm  *	oname.
462*3034Sdougm  */
463*3034Sdougm 
464*3034Sdougm static int
465*3034Sdougm sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id)
466*3034Sdougm {
467*3034Sdougm 	char *proto;
468*3034Sdougm 
469*3034Sdougm 	if (id == NULL)
470*3034Sdougm 	    id = "optionset";
471*3034Sdougm 
472*3034Sdougm 	proto = sa_get_optionset_attr(optionset, "type");
473*3034Sdougm 	len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default");
474*3034Sdougm 
475*3034Sdougm 	if (proto != NULL)
476*3034Sdougm 	    sa_free_attr_string(proto);
477*3034Sdougm 	return (len);
478*3034Sdougm }
479*3034Sdougm 
480*3034Sdougm /*
481*3034Sdougm  * sa_security_name(optionset, oname, len, id)
482*3034Sdougm  *
483*3034Sdougm  * return the SMF name for the security. If id is not NULL, it will
484*3034Sdougm  * have the GUID value for a share and should be used instead of the
485*3034Sdougm  * keyword "optionset" which is used for groups. If the optionset
486*3034Sdougm  * doesn't have a protocol type associated with it, "default" is
487*3034Sdougm  * used. This shouldn't happen at this point but may be desirable in
488*3034Sdougm  * the future if there are protocol independent properties added. The
489*3034Sdougm  * name is returned in oname. The security type is also encoded into
490*3034Sdougm  * the name. In the future, this wil *be handled a bit differently.
491*3034Sdougm  */
492*3034Sdougm 
493*3034Sdougm static int
494*3034Sdougm sa_security_name(sa_security_t security, char *oname, size_t len, char *id)
495*3034Sdougm {
496*3034Sdougm 	char *proto;
497*3034Sdougm 	char *sectype;
498*3034Sdougm 
499*3034Sdougm 	if (id == NULL)
500*3034Sdougm 	    id = "optionset";
501*3034Sdougm 
502*3034Sdougm 	proto = sa_get_security_attr(security, "type");
503*3034Sdougm 	sectype = sa_get_security_attr(security, "sectype");
504*3034Sdougm 	len = snprintf(oname, len, "%s_%s_%s", id,
505*3034Sdougm 			    proto ? proto : "default",
506*3034Sdougm 			    sectype ? sectype : "default");
507*3034Sdougm 	if (proto != NULL)
508*3034Sdougm 	    sa_free_attr_string(proto);
509*3034Sdougm 	if (sectype != NULL)
510*3034Sdougm 	    sa_free_attr_string(sectype);
511*3034Sdougm 	return (len);
512*3034Sdougm }
513*3034Sdougm 
514*3034Sdougm /*
515*3034Sdougm  * sa_init()
516*3034Sdougm  *	Initialize the API
517*3034Sdougm  *	find all the shared objects
518*3034Sdougm  *	init the tables with all objects
519*3034Sdougm  *	read in the current configuration
520*3034Sdougm  */
521*3034Sdougm 
522*3034Sdougm void
523*3034Sdougm sa_init(int init_service)
524*3034Sdougm {
525*3034Sdougm 	struct stat st;
526*3034Sdougm 	int legacy = 0;
527*3034Sdougm 	uint64_t tval = 0;
528*3034Sdougm 
529*3034Sdougm 	if (!sa_initialized) {
530*3034Sdougm 	    /* get protocol specific structures */
531*3034Sdougm 	    (void) proto_plugin_init();
532*3034Sdougm 	    if (init_service & SA_INIT_SHARE_API) {
533*3034Sdougm 		/*
534*3034Sdougm 		 * since we want to use SMF, initialize an svc handle
535*3034Sdougm 		 * and find out what is there.
536*3034Sdougm 		 */
537*3034Sdougm 		scf_handle = sa_scf_init();
538*3034Sdougm 		if (scf_handle != NULL) {
539*3034Sdougm 		    (void) sa_get_config(scf_handle, &sa_config_tree,
540*3034Sdougm 				    &sa_config_doc);
541*3034Sdougm 		    tval = get_legacy_timestamp(sa_config_tree,
542*3034Sdougm 						SA_LEGACY_DFSTAB);
543*3034Sdougm 		    if (tval == 0) {
544*3034Sdougm 			/* first time so make sure default is setup */
545*3034Sdougm 			sa_group_t defgrp;
546*3034Sdougm 			sa_optionset_t opt;
547*3034Sdougm 			defgrp = sa_get_group("default");
548*3034Sdougm 			if (defgrp != NULL) {
549*3034Sdougm 			    opt = sa_get_optionset(defgrp, NULL);
550*3034Sdougm 			    if (opt == NULL)
551*3034Sdougm 				/* NFS is the default for default */
552*3034Sdougm 				opt = sa_create_optionset(defgrp, "nfs");
553*3034Sdougm 			}
554*3034Sdougm 		    }
555*3034Sdougm 		    if (stat(SA_LEGACY_DFSTAB, &st) >= 0 &&
556*3034Sdougm 			tval != TSTAMP(st.st_ctim)) {
557*3034Sdougm 			getlegacyconfig(SA_LEGACY_DFSTAB, &sa_config_tree);
558*3034Sdougm 			if (stat(SA_LEGACY_DFSTAB, &st) >= 0)
559*3034Sdougm 			    set_legacy_timestamp(sa_config_tree,
560*3034Sdougm 						SA_LEGACY_DFSTAB,
561*3034Sdougm 						TSTAMP(st.st_ctim));
562*3034Sdougm 		    }
563*3034Sdougm 		    legacy |= sa_get_zfs_shares("zfs");
564*3034Sdougm 		    legacy |= gettransients(&sa_config_tree);
565*3034Sdougm 		}
566*3034Sdougm 	    }
567*3034Sdougm 	}
568*3034Sdougm }
569*3034Sdougm 
570*3034Sdougm /*
571*3034Sdougm  * sa_fini()
572*3034Sdougm  *	Uninitialize the API structures including the configuration
573*3034Sdougm  *	data structures
574*3034Sdougm  */
575*3034Sdougm 
576*3034Sdougm void
577*3034Sdougm sa_fini()
578*3034Sdougm {
579*3034Sdougm 	if (sa_initialized) {
580*3034Sdougm 		/* free the config trees */
581*3034Sdougm 		sa_initialized = 0;
582*3034Sdougm 		if (sa_config_doc != NULL)
583*3034Sdougm 			xmlFreeDoc(sa_config_doc);
584*3034Sdougm 		sa_config_tree = NULL;
585*3034Sdougm 		sa_config_doc = NULL;
586*3034Sdougm 		sa_scf_fini(scf_handle);
587*3034Sdougm 		(void) proto_plugin_init();
588*3034Sdougm 	}
589*3034Sdougm }
590*3034Sdougm 
591*3034Sdougm /*
592*3034Sdougm  * sa_get_protocols(char **protocol)
593*3034Sdougm  *	Get array of protocols that are supported
594*3034Sdougm  *	Returns pointer to an allocated and NULL terminated
595*3034Sdougm  *	array of strings.  Caller must free.
596*3034Sdougm  *	This really should be determined dynamically.
597*3034Sdougm  *	If there aren't any defined, return -1.
598*3034Sdougm  *	Use free() to return memory.
599*3034Sdougm  */
600*3034Sdougm 
601*3034Sdougm int
602*3034Sdougm sa_get_protocols(char ***protocols)
603*3034Sdougm {
604*3034Sdougm 	int numproto = -1;
605*3034Sdougm 
606*3034Sdougm 	if (protocols != NULL) {
607*3034Sdougm 	    struct sa_proto_plugin *plug;
608*3034Sdougm 	    for (numproto = 0, plug = sap_proto_list; plug != NULL;
609*3034Sdougm 		plug = plug->plugin_next) {
610*3034Sdougm 		numproto++;
611*3034Sdougm 	    }
612*3034Sdougm 
613*3034Sdougm 	    *protocols = calloc(numproto + 1,  sizeof (char *));
614*3034Sdougm 	    if (*protocols != NULL) {
615*3034Sdougm 		int ret = 0;
616*3034Sdougm 		for (plug = sap_proto_list; plug != NULL;
617*3034Sdougm 		    plug = plug->plugin_next) {
618*3034Sdougm 		    /* faking for now */
619*3034Sdougm 		    (*protocols)[ret++] = plug->plugin_ops->sa_protocol;
620*3034Sdougm 		}
621*3034Sdougm 	    } else {
622*3034Sdougm 		numproto = -1;
623*3034Sdougm 	    }
624*3034Sdougm 	}
625*3034Sdougm 	return (numproto);
626*3034Sdougm }
627*3034Sdougm 
628*3034Sdougm /*
629*3034Sdougm  * find_group_by_name(node, group)
630*3034Sdougm  *
631*3034Sdougm  * search the XML document subtree specified by node to find the group
632*3034Sdougm  * specified by group. Searching subtree allows subgroups to be
633*3034Sdougm  * searched for.
634*3034Sdougm  */
635*3034Sdougm 
636*3034Sdougm static xmlNodePtr
637*3034Sdougm find_group_by_name(xmlNodePtr node, xmlChar *group)
638*3034Sdougm {
639*3034Sdougm 	xmlChar *name = NULL;
640*3034Sdougm 
641*3034Sdougm 	for (node = node->xmlChildrenNode; node != NULL;
642*3034Sdougm 	    node = node->next) {
643*3034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
644*3034Sdougm 		/* if no groupname, return the first found */
645*3034Sdougm 		if (group == NULL)
646*3034Sdougm 		    break;
647*3034Sdougm 		name = xmlGetProp(node, (xmlChar *)"name");
648*3034Sdougm 		if (name != NULL &&
649*3034Sdougm 		    xmlStrcmp(name, group) == 0) {
650*3034Sdougm 		    break;
651*3034Sdougm 		}
652*3034Sdougm 		if (name != NULL) {
653*3034Sdougm 		    xmlFree(name);
654*3034Sdougm 		    name = NULL;
655*3034Sdougm 		}
656*3034Sdougm 	    }
657*3034Sdougm 	}
658*3034Sdougm 	if (name != NULL)
659*3034Sdougm 	    xmlFree(name);
660*3034Sdougm 	return (node);
661*3034Sdougm }
662*3034Sdougm 
663*3034Sdougm /*
664*3034Sdougm  * sa_get_group(groupname)
665*3034Sdougm  *	Return the "group" specified.  If groupname is NULL,
666*3034Sdougm  *	return the first group of the list of groups.
667*3034Sdougm  */
668*3034Sdougm sa_group_t
669*3034Sdougm sa_get_group(char *groupname)
670*3034Sdougm {
671*3034Sdougm 	xmlNodePtr node = NULL;
672*3034Sdougm 	char *subgroup = NULL;
673*3034Sdougm 	char *group = NULL;
674*3034Sdougm 
675*3034Sdougm 	if (sa_config_tree != NULL) {
676*3034Sdougm 	    if (groupname != NULL) {
677*3034Sdougm 		group = strdup(groupname);
678*3034Sdougm 		subgroup = strchr(group, '/');
679*3034Sdougm 		if (subgroup != NULL)
680*3034Sdougm 		    *subgroup++ = '\0';
681*3034Sdougm 	    }
682*3034Sdougm 	    node = find_group_by_name(sa_config_tree, (xmlChar *)group);
683*3034Sdougm 	    /* if a subgroup, find it before returning */
684*3034Sdougm 	    if (subgroup != NULL && node != NULL) {
685*3034Sdougm 		node = find_group_by_name(node, (xmlChar *)subgroup);
686*3034Sdougm 	    }
687*3034Sdougm 	}
688*3034Sdougm 	if (node != NULL && (char *)group != NULL)
689*3034Sdougm 	    (void) sa_get_instance(scf_handle, (char *)group);
690*3034Sdougm 	if (group != NULL)
691*3034Sdougm 	    free(group);
692*3034Sdougm 	return ((sa_group_t)(node));
693*3034Sdougm }
694*3034Sdougm 
695*3034Sdougm /*
696*3034Sdougm  * sa_get_next_group(group)
697*3034Sdougm  *	Return the "next" group after the specified group from
698*3034Sdougm  *	the internal group list.  NULL if there are no more.
699*3034Sdougm  */
700*3034Sdougm sa_group_t
701*3034Sdougm sa_get_next_group(sa_group_t group)
702*3034Sdougm {
703*3034Sdougm 	xmlNodePtr ngroup = NULL;
704*3034Sdougm 	if (group != NULL) {
705*3034Sdougm 	    for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
706*3034Sdougm 		    ngroup = ngroup->next) {
707*3034Sdougm 		if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
708*3034Sdougm 		    break;
709*3034Sdougm 	    }
710*3034Sdougm 	}
711*3034Sdougm 	return ((sa_group_t)ngroup);
712*3034Sdougm }
713*3034Sdougm 
714*3034Sdougm /*
715*3034Sdougm  * sa_get_share(group, sharepath)
716*3034Sdougm  *	Return the share object for the share specified. The share
717*3034Sdougm  *	must be in the specified group.  Return NULL if not found.
718*3034Sdougm  */
719*3034Sdougm sa_share_t
720*3034Sdougm sa_get_share(sa_group_t group, char *sharepath)
721*3034Sdougm {
722*3034Sdougm 	xmlNodePtr node = NULL;
723*3034Sdougm 	xmlChar *path;
724*3034Sdougm 
725*3034Sdougm 	/*
726*3034Sdougm 	 * For future scalability, this should end up building a cache
727*3034Sdougm 	 * since it will get called regularly by the mountd and info
728*3034Sdougm 	 * services.
729*3034Sdougm 	 */
730*3034Sdougm 	if (group != NULL) {
731*3034Sdougm 	    for (node = ((xmlNodePtr)group)->children; node != NULL;
732*3034Sdougm 		    node = node->next) {
733*3034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
734*3034Sdougm 			if (sharepath == NULL) {
735*3034Sdougm 				break;
736*3034Sdougm 			} else {
737*3034Sdougm 				/* is it the correct share? */
738*3034Sdougm 			    path = xmlGetProp(node, (xmlChar *)"path");
739*3034Sdougm 			    if (path != NULL &&
740*3034Sdougm 				xmlStrcmp(path, (xmlChar *)sharepath) == 0) {
741*3034Sdougm 				xmlFree(path);
742*3034Sdougm 				break;
743*3034Sdougm 			    }
744*3034Sdougm 			    xmlFree(path);
745*3034Sdougm 			}
746*3034Sdougm 		}
747*3034Sdougm 	    }
748*3034Sdougm 	}
749*3034Sdougm 	return ((sa_share_t)node);
750*3034Sdougm }
751*3034Sdougm 
752*3034Sdougm /*
753*3034Sdougm  * sa_get_next_share(share)
754*3034Sdougm  *	Return the next share following the specified share
755*3034Sdougm  *	from the internal list of shares. Returns NULL if there
756*3034Sdougm  *	are no more shares.  The list is relative to the same
757*3034Sdougm  *	group.
758*3034Sdougm  */
759*3034Sdougm sa_share_t
760*3034Sdougm sa_get_next_share(sa_share_t share)
761*3034Sdougm {
762*3034Sdougm 	xmlNodePtr node = NULL;
763*3034Sdougm 
764*3034Sdougm 	if (share != NULL) {
765*3034Sdougm 	    for (node = ((xmlNodePtr)share)->next; node != NULL;
766*3034Sdougm 		    node = node->next) {
767*3034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
768*3034Sdougm 			break;
769*3034Sdougm 		}
770*3034Sdougm 	    }
771*3034Sdougm 	}
772*3034Sdougm 	return ((sa_share_t)node);
773*3034Sdougm }
774*3034Sdougm 
775*3034Sdougm /*
776*3034Sdougm  * _sa_get_child_node(node, type)
777*3034Sdougm  *
778*3034Sdougm  * find the child node of the specified node that has "type". This is
779*3034Sdougm  * used to implement several internal functions.
780*3034Sdougm  */
781*3034Sdougm 
782*3034Sdougm static xmlNodePtr
783*3034Sdougm _sa_get_child_node(xmlNodePtr node, xmlChar *type)
784*3034Sdougm {
785*3034Sdougm 	xmlNodePtr child;
786*3034Sdougm 	for (child = node->xmlChildrenNode; child != NULL;
787*3034Sdougm 	    child = child->next)
788*3034Sdougm 	    if (xmlStrcmp(child->name, type) == 0)
789*3034Sdougm 		return (child);
790*3034Sdougm 	return ((xmlNodePtr)NULL);
791*3034Sdougm }
792*3034Sdougm 
793*3034Sdougm /*
794*3034Sdougm  *  find_share(group, path)
795*3034Sdougm  *
796*3034Sdougm  * Search all the shares in the specified group for one that has the
797*3034Sdougm  * specified path.
798*3034Sdougm  */
799*3034Sdougm 
800*3034Sdougm static sa_share_t
801*3034Sdougm find_share(sa_group_t group, char *sharepath)
802*3034Sdougm {
803*3034Sdougm 	sa_share_t share;
804*3034Sdougm 	char *path;
805*3034Sdougm 
806*3034Sdougm 	for (share = sa_get_share(group, NULL); share != NULL;
807*3034Sdougm 	    share = sa_get_next_share(share)) {
808*3034Sdougm 	    path = sa_get_share_attr(share, "path");
809*3034Sdougm 	    if (path != NULL && strcmp(path, sharepath) == 0) {
810*3034Sdougm 		sa_free_attr_string(path);
811*3034Sdougm 		break;
812*3034Sdougm 	    }
813*3034Sdougm 	    if (path != NULL)
814*3034Sdougm 		sa_free_attr_string(path);
815*3034Sdougm 	}
816*3034Sdougm 	return (share);
817*3034Sdougm }
818*3034Sdougm 
819*3034Sdougm /*
820*3034Sdougm  * sa_get_sub_group(group)
821*3034Sdougm  *
822*3034Sdougm  * Get the first sub-group of group. The sa_get_next_group() function
823*3034Sdougm  * can be used to get the rest. This is currently only used for ZFS
824*3034Sdougm  * sub-groups but could be used to implement a more general mechanism.
825*3034Sdougm  */
826*3034Sdougm 
827*3034Sdougm sa_group_t
828*3034Sdougm sa_get_sub_group(sa_group_t group)
829*3034Sdougm {
830*3034Sdougm 	return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
831*3034Sdougm 					    (xmlChar *)"group"));
832*3034Sdougm }
833*3034Sdougm 
834*3034Sdougm /*
835*3034Sdougm  * sa_find_share(sharepath)
836*3034Sdougm  *	Finds a share regardless of group.  In the future, this
837*3034Sdougm  *	function should utilize a cache and hash table of some kind.
838*3034Sdougm  *	The current assumption is that a path will only be shared
839*3034Sdougm  *	once.  In the future, this may change as implementation of
840*3034Sdougm  *	resource names comes into being.
841*3034Sdougm  */
842*3034Sdougm sa_share_t
843*3034Sdougm sa_find_share(char *sharepath)
844*3034Sdougm {
845*3034Sdougm 	sa_group_t group;
846*3034Sdougm 	sa_group_t zgroup;
847*3034Sdougm 	sa_share_t share = NULL;
848*3034Sdougm 	int done = 0;
849*3034Sdougm 
850*3034Sdougm 	for (group = sa_get_group(NULL); group != NULL && !done;
851*3034Sdougm 		group = sa_get_next_group(group)) {
852*3034Sdougm 	    if (is_zfs_group(group)) {
853*3034Sdougm 		for (zgroup = (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
854*3034Sdougm 							(xmlChar *)"group");
855*3034Sdougm 		    zgroup != NULL; zgroup = sa_get_next_group(zgroup)) {
856*3034Sdougm 		    share = find_share(zgroup, sharepath);
857*3034Sdougm 		    if (share != NULL)
858*3034Sdougm 			break;
859*3034Sdougm 		}
860*3034Sdougm 	    } else {
861*3034Sdougm 		share = find_share(group, sharepath);
862*3034Sdougm 	    }
863*3034Sdougm 	    if (share != NULL)
864*3034Sdougm 		break;
865*3034Sdougm 	}
866*3034Sdougm 	return (share);
867*3034Sdougm }
868*3034Sdougm 
869*3034Sdougm /*
870*3034Sdougm  *  sa_check_path(group, path)
871*3034Sdougm  *
872*3034Sdougm  * check that path is a valid path relative to the group.  Currently,
873*3034Sdougm  * we are ignoring the group and checking only the NFS rules. Later,
874*3034Sdougm  * we may want to use the group to then check against the protocols
875*3034Sdougm  * enabled on the group.
876*3034Sdougm  */
877*3034Sdougm 
878*3034Sdougm int
879*3034Sdougm sa_check_path(sa_group_t group, char *path)
880*3034Sdougm {
881*3034Sdougm #ifdef lint
882*3034Sdougm 	group = group;
883*3034Sdougm #endif
884*3034Sdougm 	return (validpath(path));
885*3034Sdougm }
886*3034Sdougm 
887*3034Sdougm /*
888*3034Sdougm  * _sa_add_share(group, sharepath, persist, *error)
889*3034Sdougm  *
890*3034Sdougm  * common code for all types of add_share. sa_add_share() is the
891*3034Sdougm  * public API, we also need to be able to do this when parsing legacy
892*3034Sdougm  * files and construction of the internal configuration while
893*3034Sdougm  * extracting config info from SMF.
894*3034Sdougm  */
895*3034Sdougm 
896*3034Sdougm sa_share_t
897*3034Sdougm _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
898*3034Sdougm {
899*3034Sdougm 	xmlNodePtr node = NULL;
900*3034Sdougm 	int err;
901*3034Sdougm 
902*3034Sdougm 	err  = SA_OK; /* assume success */
903*3034Sdougm 
904*3034Sdougm 	node = xmlNewChild((xmlNodePtr)group, NULL,
905*3034Sdougm 				(xmlChar *)"share", NULL);
906*3034Sdougm 	if (node != NULL) {
907*3034Sdougm 	    xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
908*3034Sdougm 	    xmlSetProp(node, (xmlChar *)"type", persist ?
909*3034Sdougm 			(xmlChar *)"persist" : (xmlChar *)"transient");
910*3034Sdougm 	    if (persist != SA_SHARE_TRANSIENT) {
911*3034Sdougm 		/*
912*3034Sdougm 		 * persistent shares come in two flavors: SMF and
913*3034Sdougm 		 * ZFS. Sort this one out based on target group and
914*3034Sdougm 		 * path type. Currently, only NFS is supported in the
915*3034Sdougm 		 * ZFS group and it is always on.
916*3034Sdougm 		 */
917*3034Sdougm 		if (sa_group_is_zfs(group) && sa_path_is_zfs(sharepath)) {
918*3034Sdougm 		    err = sa_zfs_set_sharenfs(group, sharepath, 1);
919*3034Sdougm 		} else {
920*3034Sdougm 		    err = sa_commit_share(scf_handle, group,
921*3034Sdougm 						(sa_share_t)node);
922*3034Sdougm 		}
923*3034Sdougm 	    }
924*3034Sdougm 	    if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) {
925*3034Sdougm 		/* called by the dfstab parser so could be a show */
926*3034Sdougm 		err = SA_OK;
927*3034Sdougm 	    }
928*3034Sdougm 	    if (err != SA_OK) {
929*3034Sdougm 		/*
930*3034Sdougm 		 * we couldn't commit to the repository so undo
931*3034Sdougm 		 * our internal state to reflect reality.
932*3034Sdougm 		 */
933*3034Sdougm 		xmlUnlinkNode(node);
934*3034Sdougm 		xmlFreeNode(node);
935*3034Sdougm 		node = NULL;
936*3034Sdougm 	    }
937*3034Sdougm 	} else {
938*3034Sdougm 	    err = SA_NO_MEMORY;
939*3034Sdougm 	}
940*3034Sdougm 	if (error != NULL)
941*3034Sdougm 	    *error = err;
942*3034Sdougm 	return (node);
943*3034Sdougm }
944*3034Sdougm 
945*3034Sdougm /*
946*3034Sdougm  * sa_add_share(group, sharepath, persist, *error)
947*3034Sdougm  *
948*3034Sdougm  *	Add a new share object to the specified group.  The share will
949*3034Sdougm  *	have the specified sharepath and will only be constructed if
950*3034Sdougm  *	it is a valid path to be shared.  NULL is returned on error
951*3034Sdougm  *	and a detailed error value will be returned via the error
952*3034Sdougm  *	pointer.
953*3034Sdougm  */
954*3034Sdougm sa_share_t
955*3034Sdougm sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
956*3034Sdougm {
957*3034Sdougm 	xmlNodePtr node = NULL;
958*3034Sdougm 	sa_share_t dup;
959*3034Sdougm 
960*3034Sdougm 	if ((dup = sa_find_share(sharepath)) == NULL &&
961*3034Sdougm 		(*error = sa_check_path(group, sharepath)) == SA_OK) {
962*3034Sdougm 	    node = _sa_add_share(group, sharepath, persist, error);
963*3034Sdougm 	}
964*3034Sdougm 	if (dup != NULL)
965*3034Sdougm 	    *error = SA_DUPLICATE_NAME;
966*3034Sdougm 
967*3034Sdougm 	return ((sa_share_t)node);
968*3034Sdougm }
969*3034Sdougm 
970*3034Sdougm /*
971*3034Sdougm  * sa_enable_share(share, protocol)
972*3034Sdougm  *	Enable the specified share to the specified protocol.
973*3034Sdougm  *	If protocol is NULL, then all protocols.
974*3034Sdougm  */
975*3034Sdougm int
976*3034Sdougm sa_enable_share(sa_share_t share, char *protocol)
977*3034Sdougm {
978*3034Sdougm 	char *sharepath;
979*3034Sdougm 	struct stat st;
980*3034Sdougm 	int err = 0;
981*3034Sdougm 
982*3034Sdougm 	sharepath = sa_get_share_attr(share, "path");
983*3034Sdougm 	if (stat(sharepath, &st) < 0) {
984*3034Sdougm 	    err = SA_NO_SUCH_PATH;
985*3034Sdougm 	} else {
986*3034Sdougm 	    /* tell the server about the share */
987*3034Sdougm 	    if (protocol != NULL) {
988*3034Sdougm 		/* lookup protocol specific handler */
989*3034Sdougm 		err = sa_proto_share(protocol, share);
990*3034Sdougm 		if (err == SA_OK)
991*3034Sdougm 		    (void) sa_set_share_attr(share, "shared", "true");
992*3034Sdougm 	    } else {
993*3034Sdougm 		/* tell all protocols */
994*3034Sdougm 		err = sa_proto_share("nfs", share); /* only NFS for now */
995*3034Sdougm 		(void) sa_set_share_attr(share, "shared", "true");
996*3034Sdougm 	    }
997*3034Sdougm 	}
998*3034Sdougm 	if (sharepath != NULL)
999*3034Sdougm 	    sa_free_attr_string(sharepath);
1000*3034Sdougm 	return (err);
1001*3034Sdougm }
1002*3034Sdougm 
1003*3034Sdougm /*
1004*3034Sdougm  * sa_disable_share(share, protocol)
1005*3034Sdougm  *	Disable the specified share to the specified protocol.
1006*3034Sdougm  *	If protocol is NULL, then all protocols.
1007*3034Sdougm  */
1008*3034Sdougm int
1009*3034Sdougm sa_disable_share(sa_share_t share, char *protocol)
1010*3034Sdougm {
1011*3034Sdougm 	char *path;
1012*3034Sdougm 	char *shared;
1013*3034Sdougm 	int ret = SA_OK;
1014*3034Sdougm 
1015*3034Sdougm 	path = sa_get_share_attr(share, "path");
1016*3034Sdougm 	shared = sa_get_share_attr(share, "shared");
1017*3034Sdougm 
1018*3034Sdougm 	if (protocol != NULL) {
1019*3034Sdougm 	    ret = sa_proto_unshare(protocol, path);
1020*3034Sdougm 	} else {
1021*3034Sdougm 	    /* need to do all protocols */
1022*3034Sdougm 	    ret = sa_proto_unshare("nfs", path);
1023*3034Sdougm 	}
1024*3034Sdougm 	if (ret == SA_OK)
1025*3034Sdougm 		(void) sa_set_share_attr(share, "shared", NULL);
1026*3034Sdougm 	if (path != NULL)
1027*3034Sdougm 	    sa_free_attr_string(path);
1028*3034Sdougm 	if (shared != NULL)
1029*3034Sdougm 	    sa_free_attr_string(shared);
1030*3034Sdougm 	return (ret);
1031*3034Sdougm }
1032*3034Sdougm 
1033*3034Sdougm /*
1034*3034Sdougm  * sa_remove_share(share)
1035*3034Sdougm  *
1036*3034Sdougm  * remove the specified share from its containing group.
1037*3034Sdougm  * Remove from the SMF or ZFS configuration space.
1038*3034Sdougm  */
1039*3034Sdougm 
1040*3034Sdougm int
1041*3034Sdougm sa_remove_share(sa_share_t share)
1042*3034Sdougm {
1043*3034Sdougm 	sa_group_t group;
1044*3034Sdougm 	int ret = SA_OK;
1045*3034Sdougm 	char *type;
1046*3034Sdougm 	int transient = 0;
1047*3034Sdougm 	char *groupname;
1048*3034Sdougm 	char *zfs;
1049*3034Sdougm 
1050*3034Sdougm 	type = sa_get_share_attr(share, "type");
1051*3034Sdougm 	group = sa_get_parent_group(share);
1052*3034Sdougm 	zfs = sa_get_group_attr(group, "zfs");
1053*3034Sdougm 	groupname = sa_get_group_attr(group, "name");
1054*3034Sdougm 	if (type != NULL && strcmp(type, "persist") != 0)
1055*3034Sdougm 	    transient = 1;
1056*3034Sdougm 	if (type != NULL)
1057*3034Sdougm 	    sa_free_attr_string(type);
1058*3034Sdougm 
1059*3034Sdougm 	/* remove the node from its group then free the memory */
1060*3034Sdougm 
1061*3034Sdougm 	/*
1062*3034Sdougm 	 * need to test if "busy"
1063*3034Sdougm 	 */
1064*3034Sdougm 	/* only do SMF action if permanent */
1065*3034Sdougm 	if (!transient || zfs != NULL) {
1066*3034Sdougm 	    /* remove from legacy dfstab as well as possible SMF */
1067*3034Sdougm 	    ret = sa_delete_legacy(share);
1068*3034Sdougm 	    if (ret == SA_OK) {
1069*3034Sdougm 		if (!sa_group_is_zfs(group)) {
1070*3034Sdougm 		    ret = sa_delete_share(scf_handle, group, share);
1071*3034Sdougm 		} else {
1072*3034Sdougm 		    char *sharepath = sa_get_share_attr(share, "path");
1073*3034Sdougm 		    if (sharepath != NULL) {
1074*3034Sdougm 			ret = sa_zfs_set_sharenfs(group, sharepath, 0);
1075*3034Sdougm 			sa_free_attr_string(sharepath);
1076*3034Sdougm 		    }
1077*3034Sdougm 		}
1078*3034Sdougm 	    }
1079*3034Sdougm 	}
1080*3034Sdougm 	if (groupname != NULL)
1081*3034Sdougm 	    sa_free_attr_string(groupname);
1082*3034Sdougm 	if (zfs != NULL)
1083*3034Sdougm 	    sa_free_attr_string(zfs);
1084*3034Sdougm 
1085*3034Sdougm 	xmlUnlinkNode((xmlNodePtr)share);
1086*3034Sdougm 	xmlFreeNode((xmlNodePtr)share);
1087*3034Sdougm 	return (ret);
1088*3034Sdougm }
1089*3034Sdougm 
1090*3034Sdougm /*
1091*3034Sdougm  * sa_move_share(group, share)
1092*3034Sdougm  *
1093*3034Sdougm  * move the specified share to the specified group.  Update SMF
1094*3034Sdougm  * appropriately.
1095*3034Sdougm  */
1096*3034Sdougm 
1097*3034Sdougm int
1098*3034Sdougm sa_move_share(sa_group_t group, sa_share_t share)
1099*3034Sdougm {
1100*3034Sdougm 	sa_group_t oldgroup;
1101*3034Sdougm 	int ret = SA_OK;
1102*3034Sdougm 
1103*3034Sdougm 	/* remove the node from its group then free the memory */
1104*3034Sdougm 
1105*3034Sdougm 	oldgroup = sa_get_parent_group(share);
1106*3034Sdougm 	if (oldgroup != group) {
1107*3034Sdougm 	    xmlUnlinkNode((xmlNodePtr)share);
1108*3034Sdougm 	    /* now that the share isn't in its old group, add to the new one */
1109*3034Sdougm 	    xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
1110*3034Sdougm 	    /* need to deal with SMF */
1111*3034Sdougm 	    if (ret == SA_OK) {
1112*3034Sdougm 		/*
1113*3034Sdougm 		 * need to remove from old group first and then add to
1114*3034Sdougm 		 * new group. Ideally, we would do the other order but
1115*3034Sdougm 		 * need to avoid having the share in two groups at the
1116*3034Sdougm 		 * same time.
1117*3034Sdougm 		 */
1118*3034Sdougm 		ret = sa_delete_share(scf_handle, oldgroup, share);
1119*3034Sdougm 	    }
1120*3034Sdougm 	    ret = sa_commit_share(scf_handle, group, share);
1121*3034Sdougm 	}
1122*3034Sdougm 	return (ret);
1123*3034Sdougm }
1124*3034Sdougm 
1125*3034Sdougm /*
1126*3034Sdougm  * sa_get_parent_group(share)
1127*3034Sdougm  *
1128*3034Sdougm  * Return the containg group for the share. If a group was actually
1129*3034Sdougm  * passed in, we don't want a parent so return NULL.
1130*3034Sdougm  */
1131*3034Sdougm 
1132*3034Sdougm sa_group_t
1133*3034Sdougm sa_get_parent_group(sa_share_t share)
1134*3034Sdougm {
1135*3034Sdougm 	xmlNodePtr node = NULL;
1136*3034Sdougm 	if (share != NULL) {
1137*3034Sdougm 	    node = ((xmlNodePtr)share)->parent;
1138*3034Sdougm 		/*
1139*3034Sdougm 		 * make sure parent is a group and not sharecfg since
1140*3034Sdougm 		 * we may be cheating and passing in a group.
1141*3034Sdougm 		 * Eventually, groups of groups might come into being.
1142*3034Sdougm 		 */
1143*3034Sdougm 	    if (node == NULL ||
1144*3034Sdougm 		xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
1145*3034Sdougm 		node = NULL;
1146*3034Sdougm 	}
1147*3034Sdougm 	return ((sa_group_t)node);
1148*3034Sdougm }
1149*3034Sdougm 
1150*3034Sdougm /*
1151*3034Sdougm  * _sa_create_group(groupname)
1152*3034Sdougm  *
1153*3034Sdougm  * Create a group in the document. The caller will need to deal with
1154*3034Sdougm  * configuration store and activation.
1155*3034Sdougm  */
1156*3034Sdougm 
1157*3034Sdougm sa_group_t
1158*3034Sdougm _sa_create_group(char *groupname)
1159*3034Sdougm {
1160*3034Sdougm 	xmlNodePtr node = NULL;
1161*3034Sdougm 
1162*3034Sdougm 	if (sa_valid_group_name(groupname)) {
1163*3034Sdougm 	    node = xmlNewChild(sa_config_tree, NULL,
1164*3034Sdougm 				(xmlChar *)"group", NULL);
1165*3034Sdougm 	    if (node != NULL) {
1166*3034Sdougm 		xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
1167*3034Sdougm 		xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
1168*3034Sdougm 	    }
1169*3034Sdougm 	}
1170*3034Sdougm 	return ((sa_group_t)node);
1171*3034Sdougm }
1172*3034Sdougm 
1173*3034Sdougm /*
1174*3034Sdougm  * _sa_create_zfs_group(group, groupname)
1175*3034Sdougm  *
1176*3034Sdougm  * Create a ZFS subgroup under the specified group. This may
1177*3034Sdougm  * eventually form the basis of general sub-groups, but is currently
1178*3034Sdougm  * restricted to ZFS.
1179*3034Sdougm  */
1180*3034Sdougm sa_group_t
1181*3034Sdougm _sa_create_zfs_group(sa_group_t group, char *groupname)
1182*3034Sdougm {
1183*3034Sdougm 	xmlNodePtr node = NULL;
1184*3034Sdougm 
1185*3034Sdougm 	node = xmlNewChild((xmlNodePtr)group, NULL,
1186*3034Sdougm 				(xmlChar *)"group", NULL);
1187*3034Sdougm 	if (node != NULL) {
1188*3034Sdougm 		xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
1189*3034Sdougm 		xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
1190*3034Sdougm 	}
1191*3034Sdougm 
1192*3034Sdougm 	return ((sa_group_t)node);
1193*3034Sdougm }
1194*3034Sdougm 
1195*3034Sdougm /*
1196*3034Sdougm  * sa_create_group(groupname, *error)
1197*3034Sdougm  *
1198*3034Sdougm  * Create a new group with groupname.  Need to validate that it is a
1199*3034Sdougm  * legal name for SMF and the construct the SMF service instance of
1200*3034Sdougm  * svc:/network/shares/group to implement the group. All necessary
1201*3034Sdougm  * operational properties must be added to the group at this point
1202*3034Sdougm  * (via the SMF transaction model).
1203*3034Sdougm  */
1204*3034Sdougm sa_group_t
1205*3034Sdougm sa_create_group(char *groupname, int *error)
1206*3034Sdougm {
1207*3034Sdougm 	xmlNodePtr node = NULL;
1208*3034Sdougm 	sa_group_t group;
1209*3034Sdougm 	int ret;
1210*3034Sdougm 	char rbacstr[256];
1211*3034Sdougm 
1212*3034Sdougm 	ret = SA_OK;
1213*3034Sdougm 
1214*3034Sdougm 	if (scf_handle == NULL) {
1215*3034Sdougm 	    ret = SA_SYSTEM_ERR;
1216*3034Sdougm 	    goto err;
1217*3034Sdougm 	}
1218*3034Sdougm 
1219*3034Sdougm 	group = sa_get_group(groupname);
1220*3034Sdougm 	if (group != NULL) {
1221*3034Sdougm 	    ret = SA_DUPLICATE_NAME;
1222*3034Sdougm 	} else {
1223*3034Sdougm 	    if (sa_valid_group_name(groupname)) {
1224*3034Sdougm 		node = xmlNewChild(sa_config_tree, NULL,
1225*3034Sdougm 				    (xmlChar *)"group", NULL);
1226*3034Sdougm 		if (node != NULL) {
1227*3034Sdougm 		    xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
1228*3034Sdougm 		    /* default to the group being enabled */
1229*3034Sdougm 		    xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
1230*3034Sdougm 		    ret = sa_create_instance(scf_handle, groupname);
1231*3034Sdougm 		    if (ret == SA_OK) {
1232*3034Sdougm 			ret = sa_start_transaction(scf_handle, "operation");
1233*3034Sdougm 		    }
1234*3034Sdougm 		    if (ret == SA_OK) {
1235*3034Sdougm 			ret = sa_set_property(scf_handle, "state", "enabled");
1236*3034Sdougm 			if (ret == SA_OK) {
1237*3034Sdougm 			    ret = sa_end_transaction(scf_handle);
1238*3034Sdougm 			} else {
1239*3034Sdougm 			    sa_abort_transaction(scf_handle);
1240*3034Sdougm 			}
1241*3034Sdougm 		    }
1242*3034Sdougm 		    if (ret == SA_OK) {
1243*3034Sdougm 			/* initialize the RBAC strings */
1244*3034Sdougm 			ret = sa_start_transaction(scf_handle, "general");
1245*3034Sdougm 			if (ret == SA_OK) {
1246*3034Sdougm 			    (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s",
1247*3034Sdougm 					SA_RBAC_MANAGE, groupname);
1248*3034Sdougm 			    ret = sa_set_property(scf_handle,
1249*3034Sdougm 						    "action_authorization",
1250*3034Sdougm 						    rbacstr);
1251*3034Sdougm 			}
1252*3034Sdougm 			if (ret == SA_OK) {
1253*3034Sdougm 			    (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s",
1254*3034Sdougm 					SA_RBAC_VALUE, groupname);
1255*3034Sdougm 			    ret = sa_set_property(scf_handle,
1256*3034Sdougm 						    "value_authorization",
1257*3034Sdougm 						    rbacstr);
1258*3034Sdougm 			}
1259*3034Sdougm 			if (ret == SA_OK) {
1260*3034Sdougm 			    ret = sa_end_transaction(scf_handle);
1261*3034Sdougm 			} else {
1262*3034Sdougm 			    sa_abort_transaction(scf_handle);
1263*3034Sdougm 			}
1264*3034Sdougm 		    }
1265*3034Sdougm 		    if (ret != SA_OK) {
1266*3034Sdougm 			/*
1267*3034Sdougm 			 * Couldn't commit the group so we need to
1268*3034Sdougm 			 * undo internally.
1269*3034Sdougm 			 */
1270*3034Sdougm 			xmlUnlinkNode(node);
1271*3034Sdougm 			xmlFreeNode(node);
1272*3034Sdougm 			node = NULL;
1273*3034Sdougm 		    }
1274*3034Sdougm 		} else {
1275*3034Sdougm 		    ret = SA_NO_MEMORY;
1276*3034Sdougm 		}
1277*3034Sdougm 	    } else {
1278*3034Sdougm 		ret = SA_INVALID_NAME;
1279*3034Sdougm 	    }
1280*3034Sdougm 	}
1281*3034Sdougm err:
1282*3034Sdougm 	if (error != NULL)
1283*3034Sdougm 	    *error = ret;
1284*3034Sdougm 	return ((sa_group_t)node);
1285*3034Sdougm }
1286*3034Sdougm 
1287*3034Sdougm /*
1288*3034Sdougm  * sa_remove_group(group)
1289*3034Sdougm  *
1290*3034Sdougm  * Remove the specified group. This deletes from the SMF repository.
1291*3034Sdougm  * All property groups and properties are removed.
1292*3034Sdougm  */
1293*3034Sdougm 
1294*3034Sdougm int
1295*3034Sdougm sa_remove_group(sa_group_t group)
1296*3034Sdougm {
1297*3034Sdougm 	char *name;
1298*3034Sdougm 	int ret = SA_OK;
1299*3034Sdougm 
1300*3034Sdougm 	name = sa_get_group_attr(group, "name");
1301*3034Sdougm 	if (name != NULL) {
1302*3034Sdougm 	    ret = sa_delete_instance(scf_handle, name);
1303*3034Sdougm 	    sa_free_attr_string(name);
1304*3034Sdougm 	}
1305*3034Sdougm 	xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
1306*3034Sdougm 	xmlFreeNode((xmlNodePtr)group);   /* now it is gone */
1307*3034Sdougm 	return (ret);
1308*3034Sdougm }
1309*3034Sdougm 
1310*3034Sdougm /*
1311*3034Sdougm  * sa_update_config()
1312*3034Sdougm  *
1313*3034Sdougm  * Used to update legacy files that need to be updated in bulk
1314*3034Sdougm  * Currently, this is a placeholder and will go away in a future
1315*3034Sdougm  * release.
1316*3034Sdougm  */
1317*3034Sdougm 
1318*3034Sdougm int
1319*3034Sdougm sa_update_config()
1320*3034Sdougm {
1321*3034Sdougm 	struct stat st;
1322*3034Sdougm 
1323*3034Sdougm 	/*
1324*3034Sdougm 	 * do legacy files first so we can tell when they change.
1325*3034Sdougm 	 * This will go away when we start updating individual records
1326*3034Sdougm 	 * rather than the whole file.
1327*3034Sdougm 	 */
1328*3034Sdougm 	update_legacy_config();
1329*3034Sdougm 	/* update legacy timestamp */
1330*3034Sdougm 	if (stat(SA_LEGACY_DFSTAB, &st) >= 0) {
1331*3034Sdougm 	    set_legacy_timestamp(sa_config_tree, SA_LEGACY_DFSTAB,
1332*3034Sdougm 					TSTAMP(st.st_ctim));
1333*3034Sdougm 	}
1334*3034Sdougm 	return (SA_OK);
1335*3034Sdougm }
1336*3034Sdougm 
1337*3034Sdougm /*
1338*3034Sdougm  * get_node_attr(node, tag)
1339*3034Sdougm  *
1340*3034Sdougm  * Get the speficied tag(attribute) if it exists on the node.  This is
1341*3034Sdougm  * used internally by a number of attribute oriented functions.
1342*3034Sdougm  */
1343*3034Sdougm 
1344*3034Sdougm static char *
1345*3034Sdougm get_node_attr(void *nodehdl, char *tag)
1346*3034Sdougm {
1347*3034Sdougm 	xmlNodePtr node = (xmlNodePtr)nodehdl;
1348*3034Sdougm 	xmlChar *name = NULL;
1349*3034Sdougm 
1350*3034Sdougm 	if (node != NULL) {
1351*3034Sdougm 		name = xmlGetProp(node, (xmlChar *)tag);
1352*3034Sdougm 	}
1353*3034Sdougm 	return ((char *)name);
1354*3034Sdougm }
1355*3034Sdougm 
1356*3034Sdougm /*
1357*3034Sdougm  * get_node_attr(node, tag)
1358*3034Sdougm  *
1359*3034Sdougm  * Set the speficied tag(attribute) to the specified value This is
1360*3034Sdougm  * used internally by a number of attribute oriented functions. It
1361*3034Sdougm  * doesn't update the repository, only the internal document state.
1362*3034Sdougm  */
1363*3034Sdougm 
1364*3034Sdougm void
1365*3034Sdougm set_node_attr(void *nodehdl, char *tag, char *value)
1366*3034Sdougm {
1367*3034Sdougm 	xmlNodePtr node = (xmlNodePtr)nodehdl;
1368*3034Sdougm 	if (node != NULL && tag != NULL) {
1369*3034Sdougm 		if (value != NULL) {
1370*3034Sdougm 			xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value);
1371*3034Sdougm 		} else {
1372*3034Sdougm 			xmlUnsetProp(node, (xmlChar *)tag);
1373*3034Sdougm 		}
1374*3034Sdougm 	}
1375*3034Sdougm }
1376*3034Sdougm 
1377*3034Sdougm /*
1378*3034Sdougm  * sa_get_group_attr(group, tag)
1379*3034Sdougm  *
1380*3034Sdougm  * Get the specied attribute, if defined, for the group.
1381*3034Sdougm  */
1382*3034Sdougm 
1383*3034Sdougm char *
1384*3034Sdougm sa_get_group_attr(sa_group_t group, char *tag)
1385*3034Sdougm {
1386*3034Sdougm 	return (get_node_attr((void *)group, tag));
1387*3034Sdougm }
1388*3034Sdougm 
1389*3034Sdougm /*
1390*3034Sdougm  * sa_set_group_attr(group, tag, value)
1391*3034Sdougm  *
1392*3034Sdougm  * set the specified tag/attribute on the group using value as its
1393*3034Sdougm  * value.
1394*3034Sdougm  *
1395*3034Sdougm  * This will result in setting the property in the SMF repository as
1396*3034Sdougm  * well as in the internal document.
1397*3034Sdougm  */
1398*3034Sdougm 
1399*3034Sdougm int
1400*3034Sdougm sa_set_group_attr(sa_group_t group, char *tag, char *value)
1401*3034Sdougm {
1402*3034Sdougm 	int ret;
1403*3034Sdougm 	char *groupname;
1404*3034Sdougm 
1405*3034Sdougm 	groupname = sa_get_group_attr(group, "name");
1406*3034Sdougm 	ret = sa_get_instance(scf_handle, groupname);
1407*3034Sdougm 	if (ret == SA_OK) {
1408*3034Sdougm 	    set_node_attr((void *)group, tag, value);
1409*3034Sdougm 	    ret = sa_start_transaction(scf_handle, "operation");
1410*3034Sdougm 	    if (ret == SA_OK) {
1411*3034Sdougm 		ret = sa_set_property(scf_handle, tag, value);
1412*3034Sdougm 		if (ret == SA_OK)
1413*3034Sdougm 		    (void) sa_end_transaction(scf_handle);
1414*3034Sdougm 		else {
1415*3034Sdougm 		    sa_abort_transaction(scf_handle);
1416*3034Sdougm 		}
1417*3034Sdougm 	    }
1418*3034Sdougm 	}
1419*3034Sdougm 	if (groupname != NULL)
1420*3034Sdougm 	    sa_free_attr_string(groupname);
1421*3034Sdougm 	return (ret);
1422*3034Sdougm }
1423*3034Sdougm 
1424*3034Sdougm /*
1425*3034Sdougm  * sa_get_share_attr(share, tag)
1426*3034Sdougm  *
1427*3034Sdougm  * Return the value of the tag/attribute set on the specified
1428*3034Sdougm  * share. Returns NULL if the tag doesn't exist.
1429*3034Sdougm  */
1430*3034Sdougm 
1431*3034Sdougm char *
1432*3034Sdougm sa_get_share_attr(sa_share_t share, char *tag)
1433*3034Sdougm {
1434*3034Sdougm 	return (get_node_attr((void *)share, tag));
1435*3034Sdougm }
1436*3034Sdougm 
1437*3034Sdougm /*
1438*3034Sdougm  * sa_get_resource(group, resource)
1439*3034Sdougm  *
1440*3034Sdougm  * Search all the shares in the speified group for a share with a
1441*3034Sdougm  * resource name matching the one specified.
1442*3034Sdougm  *
1443*3034Sdougm  * In the future, it may be advantageous to allow group to be NULL and
1444*3034Sdougm  * search all groups but that isn't needed at present.
1445*3034Sdougm  */
1446*3034Sdougm 
1447*3034Sdougm sa_share_t
1448*3034Sdougm sa_get_resource(sa_group_t group, char *resource)
1449*3034Sdougm {
1450*3034Sdougm 	sa_share_t share = NULL;
1451*3034Sdougm 	char *name = NULL;
1452*3034Sdougm 
1453*3034Sdougm 	if (resource != NULL) {
1454*3034Sdougm 	    for (share = sa_get_share(group, NULL); share != NULL;
1455*3034Sdougm 		share = sa_get_next_share(share)) {
1456*3034Sdougm 		name = sa_get_share_attr(share, "resource");
1457*3034Sdougm 		if (name != NULL) {
1458*3034Sdougm 		    if (strcmp(name, resource) == 0)
1459*3034Sdougm 			break;
1460*3034Sdougm 		    sa_free_attr_string(name);
1461*3034Sdougm 		    name = NULL;
1462*3034Sdougm 		}
1463*3034Sdougm 	    }
1464*3034Sdougm 	    if (name != NULL)
1465*3034Sdougm 		sa_free_attr_string(name);
1466*3034Sdougm 	}
1467*3034Sdougm 	return ((sa_share_t)share);
1468*3034Sdougm }
1469*3034Sdougm 
1470*3034Sdougm /*
1471*3034Sdougm  * _sa_set_share_description(share, description)
1472*3034Sdougm  *
1473*3034Sdougm  * Add a description tag with text contents to the specified share.
1474*3034Sdougm  * A separate XML tag is used rather than a property.
1475*3034Sdougm  */
1476*3034Sdougm 
1477*3034Sdougm xmlNodePtr
1478*3034Sdougm _sa_set_share_description(sa_share_t share, char *content)
1479*3034Sdougm {
1480*3034Sdougm 	xmlNodePtr node;
1481*3034Sdougm 	node = xmlNewChild((xmlNodePtr)share,
1482*3034Sdougm 			    NULL, (xmlChar *)"description", NULL);
1483*3034Sdougm 	xmlNodeSetContent(node, (xmlChar *)content);
1484*3034Sdougm 	return (node);
1485*3034Sdougm }
1486*3034Sdougm 
1487*3034Sdougm /*
1488*3034Sdougm  * sa_set_share_attr(share, tag, value)
1489*3034Sdougm  *
1490*3034Sdougm  * Set the share attribute specified by tag to the specified value. In
1491*3034Sdougm  * the case of "resource", enforce a no duplicates in a group rule. If
1492*3034Sdougm  * the share is not transient, commit the changes to the repository
1493*3034Sdougm  * else just update the share internally.
1494*3034Sdougm  */
1495*3034Sdougm 
1496*3034Sdougm int
1497*3034Sdougm sa_set_share_attr(sa_share_t share, char *tag, char *value)
1498*3034Sdougm {
1499*3034Sdougm 	sa_group_t group;
1500*3034Sdougm 	sa_share_t resource;
1501*3034Sdougm 	int ret = SA_OK;
1502*3034Sdougm 
1503*3034Sdougm 	group = sa_get_parent_group(share);
1504*3034Sdougm 
1505*3034Sdougm 	/*
1506*3034Sdougm 	 * There are some attributes that may have specific
1507*3034Sdougm 	 * restrictions on them. Initially, only "resource" has
1508*3034Sdougm 	 * special meaning that needs to be checked. Only one instance
1509*3034Sdougm 	 * of a resource name may exist within a group.
1510*3034Sdougm 	 */
1511*3034Sdougm 
1512*3034Sdougm 	if (strcmp(tag, "resource") == 0) {
1513*3034Sdougm 	    resource = sa_get_resource(group, value);
1514*3034Sdougm 	    if (resource != share && resource != NULL)
1515*3034Sdougm 		ret = SA_DUPLICATE_NAME;
1516*3034Sdougm 	}
1517*3034Sdougm 	if (ret == SA_OK) {
1518*3034Sdougm 	    set_node_attr((void *)share, tag, value);
1519*3034Sdougm 	    if (group != NULL) {
1520*3034Sdougm 		char *type;
1521*3034Sdougm 		/* we can probably optimize this some */
1522*3034Sdougm 		type = sa_get_share_attr(share, "type");
1523*3034Sdougm 		if (type == NULL || strcmp(type, "transient") != 0)
1524*3034Sdougm 		    ret = sa_commit_share(scf_handle, group, share);
1525*3034Sdougm 		if (type != NULL)
1526*3034Sdougm 		    sa_free_attr_string(type);
1527*3034Sdougm 	    }
1528*3034Sdougm 	}
1529*3034Sdougm 	return (ret);
1530*3034Sdougm }
1531*3034Sdougm 
1532*3034Sdougm /*
1533*3034Sdougm  * sa_get_property_attr(prop, tag)
1534*3034Sdougm  *
1535*3034Sdougm  * Get the value of the specified property attribute. Standard
1536*3034Sdougm  * attributes are "type" and "value".
1537*3034Sdougm  */
1538*3034Sdougm 
1539*3034Sdougm char *
1540*3034Sdougm sa_get_property_attr(sa_property_t prop, char *tag)
1541*3034Sdougm {
1542*3034Sdougm 	return (get_node_attr((void *)prop, tag));
1543*3034Sdougm }
1544*3034Sdougm 
1545*3034Sdougm /*
1546*3034Sdougm  * sa_get_optionset_attr(prop, tag)
1547*3034Sdougm  *
1548*3034Sdougm  * Get the value of the specified property attribute. Standard
1549*3034Sdougm  * attribute is "type".
1550*3034Sdougm  */
1551*3034Sdougm 
1552*3034Sdougm char *
1553*3034Sdougm sa_get_optionset_attr(sa_property_t optionset, char *tag)
1554*3034Sdougm {
1555*3034Sdougm 	return (get_node_attr((void *)optionset, tag));
1556*3034Sdougm 
1557*3034Sdougm }
1558*3034Sdougm 
1559*3034Sdougm /*
1560*3034Sdougm  * sa_set_optionset_attr(optionset, tag, value)
1561*3034Sdougm  *
1562*3034Sdougm  * Set the specified attribute(tag) to the specified value on the
1563*3034Sdougm  * optionset.
1564*3034Sdougm  */
1565*3034Sdougm 
1566*3034Sdougm void
1567*3034Sdougm sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
1568*3034Sdougm {
1569*3034Sdougm 	set_node_attr((void *)optionset, tag, value);
1570*3034Sdougm }
1571*3034Sdougm 
1572*3034Sdougm /*
1573*3034Sdougm  * sa_free_attr_string(string)
1574*3034Sdougm  *
1575*3034Sdougm  * Free the string that was returned in one of the sa_get_*_attr()
1576*3034Sdougm  * functions.
1577*3034Sdougm  */
1578*3034Sdougm 
1579*3034Sdougm void
1580*3034Sdougm sa_free_attr_string(char *string)
1581*3034Sdougm {
1582*3034Sdougm 	xmlFree((xmlChar *)string);
1583*3034Sdougm }
1584*3034Sdougm 
1585*3034Sdougm /*
1586*3034Sdougm  * sa_get_optionset(group, proto)
1587*3034Sdougm  *
1588*3034Sdougm  * Return the optionset, if it exists, that is associated with the
1589*3034Sdougm  * specified protocol.
1590*3034Sdougm  */
1591*3034Sdougm 
1592*3034Sdougm sa_optionset_t
1593*3034Sdougm sa_get_optionset(void *group, char *proto)
1594*3034Sdougm {
1595*3034Sdougm 	xmlNodePtr node;
1596*3034Sdougm 	xmlChar *value = NULL;
1597*3034Sdougm 
1598*3034Sdougm 	for (node = ((xmlNodePtr)group)->children; node != NULL;
1599*3034Sdougm 		node = node->next) {
1600*3034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
1601*3034Sdougm 		    value = xmlGetProp(node, (xmlChar *)"type");
1602*3034Sdougm 		    if (proto != NULL) {
1603*3034Sdougm 			if (value != NULL &&
1604*3034Sdougm 			    xmlStrcmp(value, (xmlChar *)proto) == 0) {
1605*3034Sdougm 			    break;
1606*3034Sdougm 			}
1607*3034Sdougm 			if (value != NULL) {
1608*3034Sdougm 			    xmlFree(value);
1609*3034Sdougm 			    value = NULL;
1610*3034Sdougm 			}
1611*3034Sdougm 		    } else {
1612*3034Sdougm 			break;
1613*3034Sdougm 		    }
1614*3034Sdougm 		}
1615*3034Sdougm 	}
1616*3034Sdougm 	if (value != NULL)
1617*3034Sdougm 	    xmlFree(value);
1618*3034Sdougm 	return ((sa_optionset_t)node);
1619*3034Sdougm }
1620*3034Sdougm 
1621*3034Sdougm /*
1622*3034Sdougm  * sa_get_next_optionset(optionset)
1623*3034Sdougm  *
1624*3034Sdougm  * Return the next optionset in the group. NULL if this was the last.
1625*3034Sdougm  */
1626*3034Sdougm 
1627*3034Sdougm sa_optionset_t
1628*3034Sdougm sa_get_next_optionset(sa_optionset_t optionset)
1629*3034Sdougm {
1630*3034Sdougm 	xmlNodePtr node;
1631*3034Sdougm 
1632*3034Sdougm 	for (node = ((xmlNodePtr)optionset)->next; node != NULL;
1633*3034Sdougm 		node = node->next) {
1634*3034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
1635*3034Sdougm 			break;
1636*3034Sdougm 		}
1637*3034Sdougm 	}
1638*3034Sdougm 	return ((sa_optionset_t)node);
1639*3034Sdougm }
1640*3034Sdougm 
1641*3034Sdougm /*
1642*3034Sdougm  * sa_get_security(group, sectype, proto)
1643*3034Sdougm  *
1644*3034Sdougm  * Return the security optionset. The internal name is a hold over
1645*3034Sdougm  * from the implementation and will be changed before the API is
1646*3034Sdougm  * finalized. This is really a named optionset that can be negotiated
1647*3034Sdougm  * as a group of properties (like NFS security options).
1648*3034Sdougm  */
1649*3034Sdougm 
1650*3034Sdougm sa_security_t
1651*3034Sdougm sa_get_security(sa_group_t group, char *sectype, char *proto)
1652*3034Sdougm {
1653*3034Sdougm 	xmlNodePtr node;
1654*3034Sdougm 	xmlChar *value = NULL;
1655*3034Sdougm 
1656*3034Sdougm 	for (node = ((xmlNodePtr)group)->children; node != NULL;
1657*3034Sdougm 		node = node->next) {
1658*3034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
1659*3034Sdougm 		if (proto != NULL) {
1660*3034Sdougm 		    value = xmlGetProp(node, (xmlChar *)"type");
1661*3034Sdougm 		    if (value == NULL ||
1662*3034Sdougm 			(value != NULL &&
1663*3034Sdougm 			xmlStrcmp(value, (xmlChar *)proto) != 0)) {
1664*3034Sdougm 			/* it doesn't match so continue */
1665*3034Sdougm 			xmlFree(value);
1666*3034Sdougm 			value = NULL;
1667*3034Sdougm 			continue;
1668*3034Sdougm 		    }
1669*3034Sdougm 		}
1670*3034Sdougm 		if (value != NULL) {
1671*3034Sdougm 		    xmlFree(value);
1672*3034Sdougm 		    value = NULL;
1673*3034Sdougm 		}
1674*3034Sdougm 		/* potential match */
1675*3034Sdougm 		if (sectype != NULL) {
1676*3034Sdougm 		    value = xmlGetProp(node, (xmlChar *)"sectype");
1677*3034Sdougm 		    if (value != NULL &&
1678*3034Sdougm 			xmlStrcmp(value, (xmlChar *)sectype) == 0) {
1679*3034Sdougm 			break;
1680*3034Sdougm 		    }
1681*3034Sdougm 		} else {
1682*3034Sdougm 		    break;
1683*3034Sdougm 		}
1684*3034Sdougm 	    }
1685*3034Sdougm 	    if (value != NULL) {
1686*3034Sdougm 		xmlFree(value);
1687*3034Sdougm 		value = NULL;
1688*3034Sdougm 	    }
1689*3034Sdougm 	}
1690*3034Sdougm 	if (value != NULL)
1691*3034Sdougm 	    xmlFree(value);
1692*3034Sdougm 	return ((sa_security_t)node);
1693*3034Sdougm }
1694*3034Sdougm 
1695*3034Sdougm /*
1696*3034Sdougm  * sa_get_next_security(security)
1697*3034Sdougm  *
1698*3034Sdougm  * Get the next security optionset if one exists.
1699*3034Sdougm  */
1700*3034Sdougm 
1701*3034Sdougm sa_security_t
1702*3034Sdougm sa_get_next_security(sa_security_t security)
1703*3034Sdougm {
1704*3034Sdougm 	xmlNodePtr node;
1705*3034Sdougm 
1706*3034Sdougm 	for (node = ((xmlNodePtr)security)->next; node != NULL;
1707*3034Sdougm 		node = node->next) {
1708*3034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
1709*3034Sdougm 			break;
1710*3034Sdougm 		}
1711*3034Sdougm 	}
1712*3034Sdougm 	return ((sa_security_t)node);
1713*3034Sdougm }
1714*3034Sdougm 
1715*3034Sdougm /*
1716*3034Sdougm  * sa_get_property(optionset, prop)
1717*3034Sdougm  *
1718*3034Sdougm  * Get the property object with the name specified in prop from the
1719*3034Sdougm  * optionset.
1720*3034Sdougm  */
1721*3034Sdougm 
1722*3034Sdougm sa_property_t
1723*3034Sdougm sa_get_property(sa_optionset_t optionset, char *prop)
1724*3034Sdougm {
1725*3034Sdougm 	xmlNodePtr node = (xmlNodePtr)optionset;
1726*3034Sdougm 	xmlChar *value = NULL;
1727*3034Sdougm 
1728*3034Sdougm 	if (optionset == NULL)
1729*3034Sdougm 	    return (NULL);
1730*3034Sdougm 
1731*3034Sdougm 	for (node = node->children; node != NULL;
1732*3034Sdougm 		node = node->next) {
1733*3034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
1734*3034Sdougm 		if (prop == NULL)
1735*3034Sdougm 		    break;
1736*3034Sdougm 		value = xmlGetProp(node, (xmlChar *)"type");
1737*3034Sdougm 		if (value != NULL && xmlStrcmp(value, (xmlChar *)prop) == 0) {
1738*3034Sdougm 		    break;
1739*3034Sdougm 		}
1740*3034Sdougm 		if (value != NULL) {
1741*3034Sdougm 		    xmlFree(value);
1742*3034Sdougm 		    value = NULL;
1743*3034Sdougm 		}
1744*3034Sdougm 	    }
1745*3034Sdougm 	}
1746*3034Sdougm 	if (value != NULL)
1747*3034Sdougm 		xmlFree(value);
1748*3034Sdougm 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
1749*3034Sdougm 	    /* avoid a non option node -- it is possible to be a text node */
1750*3034Sdougm 	    node = NULL;
1751*3034Sdougm 	}
1752*3034Sdougm 	return ((sa_property_t)node);
1753*3034Sdougm }
1754*3034Sdougm 
1755*3034Sdougm /*
1756*3034Sdougm  * sa_get_next_property(property)
1757*3034Sdougm  *
1758*3034Sdougm  * Get the next property following the specified property. NULL if
1759*3034Sdougm  * this was the last.
1760*3034Sdougm  */
1761*3034Sdougm 
1762*3034Sdougm sa_property_t
1763*3034Sdougm sa_get_next_property(sa_property_t property)
1764*3034Sdougm {
1765*3034Sdougm 	xmlNodePtr node;
1766*3034Sdougm 
1767*3034Sdougm 	for (node = ((xmlNodePtr)property)->next; node != NULL;
1768*3034Sdougm 		node = node->next) {
1769*3034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
1770*3034Sdougm 			break;
1771*3034Sdougm 		}
1772*3034Sdougm 	}
1773*3034Sdougm 	return ((sa_property_t)node);
1774*3034Sdougm }
1775*3034Sdougm 
1776*3034Sdougm /*
1777*3034Sdougm  * sa_set_share_description(share, content)
1778*3034Sdougm  *
1779*3034Sdougm  * Set the description of share to content.
1780*3034Sdougm  */
1781*3034Sdougm 
1782*3034Sdougm int
1783*3034Sdougm sa_set_share_description(sa_share_t share, char *content)
1784*3034Sdougm {
1785*3034Sdougm 	xmlNodePtr node;
1786*3034Sdougm 	sa_group_t group;
1787*3034Sdougm 	int ret = SA_OK;
1788*3034Sdougm 
1789*3034Sdougm 	for (node = ((xmlNodePtr)share)->children; node != NULL;
1790*3034Sdougm 		node = node->next) {
1791*3034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
1792*3034Sdougm 			break;
1793*3034Sdougm 		}
1794*3034Sdougm 	}
1795*3034Sdougm 	group = sa_get_parent_group(share);
1796*3034Sdougm 	/* no existing description but want to add */
1797*3034Sdougm 	if (node == NULL && content != NULL) {
1798*3034Sdougm 		/* add a description */
1799*3034Sdougm 	    node = _sa_set_share_description(share, content);
1800*3034Sdougm 	} else if (node != NULL && content != NULL) {
1801*3034Sdougm 		/* update a description */
1802*3034Sdougm 		xmlNodeSetContent(node, (xmlChar *)content);
1803*3034Sdougm 	} else if (node != NULL && content == NULL) {
1804*3034Sdougm 		/* remove an existing description */
1805*3034Sdougm 		xmlUnlinkNode(node);
1806*3034Sdougm 		xmlFreeNode(node);
1807*3034Sdougm 	}
1808*3034Sdougm 	if (group != NULL && is_persistent((sa_group_t)share))
1809*3034Sdougm 	    ret = sa_commit_share(scf_handle, group, share);
1810*3034Sdougm 	return (ret);
1811*3034Sdougm }
1812*3034Sdougm 
1813*3034Sdougm /*
1814*3034Sdougm  * fixproblemchars(string)
1815*3034Sdougm  *
1816*3034Sdougm  * don't want any newline or tab characters in the text since these
1817*3034Sdougm  * could break display of data and legacy file formats.
1818*3034Sdougm  */
1819*3034Sdougm static void
1820*3034Sdougm fixproblemchars(char *str)
1821*3034Sdougm {
1822*3034Sdougm 	int c;
1823*3034Sdougm 	for (c = *str; c != '\0'; c = *++str) {
1824*3034Sdougm 	    if (c == '\t' || c == '\n')
1825*3034Sdougm 		*str = ' ';
1826*3034Sdougm 	    else if (c == '"')
1827*3034Sdougm 		*str = '\'';
1828*3034Sdougm 	}
1829*3034Sdougm }
1830*3034Sdougm 
1831*3034Sdougm /*
1832*3034Sdougm  * sa_get_share_description(share)
1833*3034Sdougm  *
1834*3034Sdougm  * Return the description text for the specified share if it
1835*3034Sdougm  * exists. NULL if no description exists.
1836*3034Sdougm  */
1837*3034Sdougm 
1838*3034Sdougm char *
1839*3034Sdougm sa_get_share_description(sa_share_t share)
1840*3034Sdougm {
1841*3034Sdougm 	xmlChar *description = NULL;
1842*3034Sdougm 	xmlNodePtr node;
1843*3034Sdougm 
1844*3034Sdougm 	for (node = ((xmlNodePtr)share)->children; node != NULL;
1845*3034Sdougm 		node = node->next) {
1846*3034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
1847*3034Sdougm 		break;
1848*3034Sdougm 	    }
1849*3034Sdougm 	}
1850*3034Sdougm 	if (node != NULL) {
1851*3034Sdougm 	    description = xmlNodeGetContent((xmlNodePtr)share);
1852*3034Sdougm 	    fixproblemchars((char *)description);
1853*3034Sdougm 	}
1854*3034Sdougm 	return ((char *)description);
1855*3034Sdougm }
1856*3034Sdougm 
1857*3034Sdougm /*
1858*3034Sdougm  * sa_free(share_description(description)
1859*3034Sdougm  *
1860*3034Sdougm  * Free the description string.
1861*3034Sdougm  */
1862*3034Sdougm 
1863*3034Sdougm void
1864*3034Sdougm sa_free_share_description(char *description)
1865*3034Sdougm {
1866*3034Sdougm 	xmlFree((xmlChar *)description);
1867*3034Sdougm }
1868*3034Sdougm 
1869*3034Sdougm /*
1870*3034Sdougm  * sa_create_optionset(group, proto)
1871*3034Sdougm  *
1872*3034Sdougm  * Create an optionset for the specified protocol in the specied
1873*3034Sdougm  * group. This is manifested as a property group within SMF.
1874*3034Sdougm  */
1875*3034Sdougm 
1876*3034Sdougm sa_optionset_t
1877*3034Sdougm sa_create_optionset(sa_group_t group, char *proto)
1878*3034Sdougm {
1879*3034Sdougm 	sa_optionset_t optionset;
1880*3034Sdougm 	sa_group_t parent = group;
1881*3034Sdougm 
1882*3034Sdougm 	optionset = sa_get_optionset(group, proto);
1883*3034Sdougm 	if (optionset != NULL) {
1884*3034Sdougm 		/* can't have a duplicate protocol */
1885*3034Sdougm 	    optionset = NULL;
1886*3034Sdougm 	} else {
1887*3034Sdougm 	    optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
1888*3034Sdougm 						    NULL,
1889*3034Sdougm 						    (xmlChar *)"optionset",
1890*3034Sdougm 						    NULL);
1891*3034Sdougm 		/*
1892*3034Sdougm 		 * only put to repository if on a group and we were
1893*3034Sdougm 		 * able to create an optionset.
1894*3034Sdougm 		 */
1895*3034Sdougm 	    if (optionset != NULL) {
1896*3034Sdougm 		char oname[256];
1897*3034Sdougm 		char *groupname;
1898*3034Sdougm 		char *id = NULL;
1899*3034Sdougm 
1900*3034Sdougm 		if (sa_is_share(group))
1901*3034Sdougm 		    parent = sa_get_parent_group((sa_share_t)group);
1902*3034Sdougm 
1903*3034Sdougm 		sa_set_optionset_attr(optionset, "type", proto);
1904*3034Sdougm 
1905*3034Sdougm 		if (sa_is_share(group)) {
1906*3034Sdougm 			id = sa_get_share_attr((sa_share_t)group, "id");
1907*3034Sdougm 		}
1908*3034Sdougm 		(void) sa_optionset_name(optionset, oname,
1909*3034Sdougm 					sizeof (oname), id);
1910*3034Sdougm 		groupname = sa_get_group_attr(parent, "name");
1911*3034Sdougm 		if (groupname != NULL && is_persistent(group)) {
1912*3034Sdougm 			(void) sa_get_instance(scf_handle, groupname);
1913*3034Sdougm 			sa_free_attr_string(groupname);
1914*3034Sdougm 			(void) sa_create_pgroup(scf_handle, oname);
1915*3034Sdougm 		}
1916*3034Sdougm 		if (id != NULL)
1917*3034Sdougm 		    sa_free_attr_string(id);
1918*3034Sdougm 	    }
1919*3034Sdougm 	}
1920*3034Sdougm 	return (optionset);
1921*3034Sdougm }
1922*3034Sdougm 
1923*3034Sdougm /*
1924*3034Sdougm  * sa_get_property_parent(property)
1925*3034Sdougm  *
1926*3034Sdougm  * Given a property, return the object it is a property of. This will
1927*3034Sdougm  * be an optionset of some type.
1928*3034Sdougm  */
1929*3034Sdougm 
1930*3034Sdougm static sa_optionset_t
1931*3034Sdougm sa_get_property_parent(sa_property_t property)
1932*3034Sdougm {
1933*3034Sdougm 	xmlNodePtr node = NULL;
1934*3034Sdougm 
1935*3034Sdougm 	if (property != NULL) {
1936*3034Sdougm 	    node = ((xmlNodePtr)property)->parent;
1937*3034Sdougm 	}
1938*3034Sdougm 	return ((sa_optionset_t)node);
1939*3034Sdougm }
1940*3034Sdougm 
1941*3034Sdougm /*
1942*3034Sdougm  * sa_get_optionset_parent(optionset)
1943*3034Sdougm  *
1944*3034Sdougm  * Return the parent of the specified optionset. This could be a group
1945*3034Sdougm  * or a share.
1946*3034Sdougm  */
1947*3034Sdougm 
1948*3034Sdougm static sa_group_t
1949*3034Sdougm sa_get_optionset_parent(sa_optionset_t optionset)
1950*3034Sdougm {
1951*3034Sdougm 	xmlNodePtr node = NULL;
1952*3034Sdougm 
1953*3034Sdougm 	if (optionset != NULL) {
1954*3034Sdougm 	    node = ((xmlNodePtr)optionset)->parent;
1955*3034Sdougm 	}
1956*3034Sdougm 	return ((sa_group_t)node);
1957*3034Sdougm }
1958*3034Sdougm 
1959*3034Sdougm /*
1960*3034Sdougm  * zfs_needs_update(share)
1961*3034Sdougm  *
1962*3034Sdougm  * In order to avoid making multiple updates to a ZFS share when
1963*3034Sdougm  * setting properties, the share attribute "changed" will be set to
1964*3034Sdougm  * true when a property is added or modifed.  When done adding
1965*3034Sdougm  * properties, we can then detect that an update is needed.  We then
1966*3034Sdougm  * clear the state here to detect additional changes.
1967*3034Sdougm  */
1968*3034Sdougm 
1969*3034Sdougm static int
1970*3034Sdougm zfs_needs_update(sa_share_t share)
1971*3034Sdougm {
1972*3034Sdougm 	char *attr;
1973*3034Sdougm 	int result = 0;
1974*3034Sdougm 
1975*3034Sdougm 	attr = sa_get_share_attr(share, "changed");
1976*3034Sdougm 	if (attr != NULL) {
1977*3034Sdougm 	    sa_free_attr_string(attr);
1978*3034Sdougm 		result = 1;
1979*3034Sdougm 	}
1980*3034Sdougm 	set_node_attr((void *)share, "changed", NULL);
1981*3034Sdougm 	return (result);
1982*3034Sdougm }
1983*3034Sdougm 
1984*3034Sdougm /*
1985*3034Sdougm  * zfs_set_update(share)
1986*3034Sdougm  *
1987*3034Sdougm  * Set the changed attribute of the share to true.
1988*3034Sdougm  */
1989*3034Sdougm 
1990*3034Sdougm static void
1991*3034Sdougm zfs_set_update(sa_share_t share)
1992*3034Sdougm {
1993*3034Sdougm 	set_node_attr((void *)share, "changed", "true");
1994*3034Sdougm }
1995*3034Sdougm 
1996*3034Sdougm /*
1997*3034Sdougm  * sa_commit_properties(optionset, clear)
1998*3034Sdougm  *
1999*3034Sdougm  * Check if SMF or ZFS config and either update or abort the pending
2000*3034Sdougm  * changes.
2001*3034Sdougm  */
2002*3034Sdougm 
2003*3034Sdougm int
2004*3034Sdougm sa_commit_properties(sa_optionset_t optionset, int clear)
2005*3034Sdougm {
2006*3034Sdougm 	sa_group_t group;
2007*3034Sdougm 	sa_group_t parent;
2008*3034Sdougm 	int zfs = 0;
2009*3034Sdougm 	int needsupdate = 0;
2010*3034Sdougm 	int ret = SA_OK;
2011*3034Sdougm 
2012*3034Sdougm 	group = sa_get_optionset_parent(optionset);
2013*3034Sdougm 	if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
2014*3034Sdougm 	    /* only update ZFS if on a share */
2015*3034Sdougm 	    parent = sa_get_parent_group(group);
2016*3034Sdougm 	    zfs++;
2017*3034Sdougm 	    if (parent != NULL && is_zfs_group(parent)) {
2018*3034Sdougm 		needsupdate = zfs_needs_update(group);
2019*3034Sdougm 	    } else {
2020*3034Sdougm 		zfs = 0;
2021*3034Sdougm 	    }
2022*3034Sdougm 	}
2023*3034Sdougm 	if (zfs) {
2024*3034Sdougm 	    if (!clear && needsupdate)
2025*3034Sdougm 		ret = sa_zfs_update((sa_share_t)group);
2026*3034Sdougm 	} else {
2027*3034Sdougm 	    if (clear)
2028*3034Sdougm 		(void) sa_abort_transaction(scf_handle);
2029*3034Sdougm 	    else
2030*3034Sdougm 		ret = sa_end_transaction(scf_handle);
2031*3034Sdougm 	}
2032*3034Sdougm 	return (ret);
2033*3034Sdougm }
2034*3034Sdougm 
2035*3034Sdougm /*
2036*3034Sdougm  * sa_destroy_optionset(optionset)
2037*3034Sdougm  *
2038*3034Sdougm  * Remove the optionset from its group. Update the repostory to
2039*3034Sdougm  * reflect this change.
2040*3034Sdougm  */
2041*3034Sdougm 
2042*3034Sdougm int
2043*3034Sdougm sa_destroy_optionset(sa_optionset_t optionset)
2044*3034Sdougm {
2045*3034Sdougm 	char name[256];
2046*3034Sdougm 	int len;
2047*3034Sdougm 	int ret;
2048*3034Sdougm 	char *id = NULL;
2049*3034Sdougm 	sa_group_t group;
2050*3034Sdougm 	int ispersist = 1;
2051*3034Sdougm 
2052*3034Sdougm 	/* now delete the prop group */
2053*3034Sdougm 	group = sa_get_optionset_parent(optionset);
2054*3034Sdougm 	if (group != NULL && sa_is_share(group)) {
2055*3034Sdougm 	    ispersist = is_persistent(group);
2056*3034Sdougm 	    id = sa_get_share_attr((sa_share_t)group, "id");
2057*3034Sdougm 	}
2058*3034Sdougm 	if (ispersist) {
2059*3034Sdougm 	    len = sa_optionset_name(optionset, name, sizeof (name), id);
2060*3034Sdougm 	    if (len > 0) {
2061*3034Sdougm 		ret = sa_delete_pgroup(scf_handle, name);
2062*3034Sdougm 	    }
2063*3034Sdougm 	}
2064*3034Sdougm 	xmlUnlinkNode((xmlNodePtr)optionset);
2065*3034Sdougm 	xmlFreeNode((xmlNodePtr)optionset);
2066*3034Sdougm 	if (id != NULL)
2067*3034Sdougm 	    sa_free_attr_string(id);
2068*3034Sdougm 	return (ret);
2069*3034Sdougm }
2070*3034Sdougm 
2071*3034Sdougm /* private to the implementation */
2072*3034Sdougm int
2073*3034Sdougm _sa_remove_optionset(sa_optionset_t optionset)
2074*3034Sdougm {
2075*3034Sdougm 	int ret = SA_OK;
2076*3034Sdougm 
2077*3034Sdougm 	xmlUnlinkNode((xmlNodePtr)optionset);
2078*3034Sdougm 	xmlFreeNode((xmlNodePtr)optionset);
2079*3034Sdougm 	return (ret);
2080*3034Sdougm }
2081*3034Sdougm 
2082*3034Sdougm /*
2083*3034Sdougm  * sa_create_security(group, sectype, proto)
2084*3034Sdougm  *
2085*3034Sdougm  * Create a security optionset (one that has a type name and a
2086*3034Sdougm  * proto). Security is left over from a pure NFS implementation. The
2087*3034Sdougm  * naming will change in the future when the API is released.
2088*3034Sdougm  */
2089*3034Sdougm sa_security_t
2090*3034Sdougm sa_create_security(sa_group_t group, char *sectype, char *proto)
2091*3034Sdougm {
2092*3034Sdougm 	sa_security_t security;
2093*3034Sdougm 	char *id = NULL;
2094*3034Sdougm 	sa_group_t parent;
2095*3034Sdougm 	char *groupname = NULL;
2096*3034Sdougm 
2097*3034Sdougm 	if (group != NULL && sa_is_share(group)) {
2098*3034Sdougm 	    id = sa_get_share_attr((sa_share_t)group, "id");
2099*3034Sdougm 	    parent = sa_get_parent_group(group);
2100*3034Sdougm 	    if (parent != NULL)
2101*3034Sdougm 		groupname = sa_get_group_attr(parent, "name");
2102*3034Sdougm 	} else if (group != NULL) {
2103*3034Sdougm 	    groupname = sa_get_group_attr(group, "name");
2104*3034Sdougm 	}
2105*3034Sdougm 
2106*3034Sdougm 	security = sa_get_security(group, sectype, proto);
2107*3034Sdougm 	if (security != NULL) {
2108*3034Sdougm 		/* can't have a duplicate security option */
2109*3034Sdougm 		security = NULL;
2110*3034Sdougm 	} else {
2111*3034Sdougm 		security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
2112*3034Sdougm 							NULL,
2113*3034Sdougm 							(xmlChar *)"security",
2114*3034Sdougm 							NULL);
2115*3034Sdougm 		if (security != NULL) {
2116*3034Sdougm 			char oname[256];
2117*3034Sdougm 			sa_set_security_attr(security, "type", proto);
2118*3034Sdougm 
2119*3034Sdougm 			sa_set_security_attr(security, "sectype", sectype);
2120*3034Sdougm 			(void) sa_security_name(security, oname,
2121*3034Sdougm 						sizeof (oname), id);
2122*3034Sdougm 			if (groupname != NULL && is_persistent(group)) {
2123*3034Sdougm 			    (void) sa_get_instance(scf_handle, groupname);
2124*3034Sdougm 			    (void) sa_create_pgroup(scf_handle, oname);
2125*3034Sdougm 			}
2126*3034Sdougm 		}
2127*3034Sdougm 	}
2128*3034Sdougm 	if (groupname != NULL)
2129*3034Sdougm 	    sa_free_attr_string(groupname);
2130*3034Sdougm 	return (security);
2131*3034Sdougm }
2132*3034Sdougm 
2133*3034Sdougm /*
2134*3034Sdougm  * sa_destroy_security(security)
2135*3034Sdougm  *
2136*3034Sdougm  * Remove the specified optionset from the document and the
2137*3034Sdougm  * configuration.
2138*3034Sdougm  */
2139*3034Sdougm 
2140*3034Sdougm int
2141*3034Sdougm sa_destroy_security(sa_security_t security)
2142*3034Sdougm {
2143*3034Sdougm 	char name[256];
2144*3034Sdougm 	int len;
2145*3034Sdougm 	int ret = SA_OK;
2146*3034Sdougm 	char *id = NULL;
2147*3034Sdougm 	sa_group_t group;
2148*3034Sdougm 	int iszfs = 0;
2149*3034Sdougm 	int ispersist = 1;
2150*3034Sdougm 
2151*3034Sdougm 	group = sa_get_optionset_parent(security);
2152*3034Sdougm 
2153*3034Sdougm 	if (group != NULL)
2154*3034Sdougm 	    iszfs = sa_group_is_zfs(group);
2155*3034Sdougm 
2156*3034Sdougm 	if (group != NULL && !iszfs) {
2157*3034Sdougm 	    if (sa_is_share(group))
2158*3034Sdougm 		ispersist = is_persistent(group);
2159*3034Sdougm 	    id = sa_get_share_attr((sa_share_t)group, "id");
2160*3034Sdougm 	}
2161*3034Sdougm 	if (ispersist) {
2162*3034Sdougm 	    len = sa_security_name(security, name, sizeof (name), id);
2163*3034Sdougm 	    if (!iszfs && len > 0) {
2164*3034Sdougm 		ret = sa_delete_pgroup(scf_handle, name);
2165*3034Sdougm 	    }
2166*3034Sdougm 	}
2167*3034Sdougm 	xmlUnlinkNode((xmlNodePtr)security);
2168*3034Sdougm 	xmlFreeNode((xmlNodePtr)security);
2169*3034Sdougm 	if (iszfs) {
2170*3034Sdougm 	    ret = sa_zfs_update(group);
2171*3034Sdougm 	}
2172*3034Sdougm 	if (id != NULL)
2173*3034Sdougm 	    sa_free_attr_string(id);
2174*3034Sdougm 	return (ret);
2175*3034Sdougm }
2176*3034Sdougm 
2177*3034Sdougm /*
2178*3034Sdougm  * sa_get_security_attr(optionset, tag)
2179*3034Sdougm  *
2180*3034Sdougm  * Return the specified attribute value from the optionset.
2181*3034Sdougm  */
2182*3034Sdougm 
2183*3034Sdougm char *
2184*3034Sdougm sa_get_security_attr(sa_property_t optionset, char *tag)
2185*3034Sdougm {
2186*3034Sdougm 	return (get_node_attr((void *)optionset, tag));
2187*3034Sdougm 
2188*3034Sdougm }
2189*3034Sdougm 
2190*3034Sdougm /*
2191*3034Sdougm  * sa_set_security_attr(optionset, tag, value)
2192*3034Sdougm  *
2193*3034Sdougm  * Set the optioset attribute specied by tag to the specified value.
2194*3034Sdougm  */
2195*3034Sdougm 
2196*3034Sdougm void
2197*3034Sdougm sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
2198*3034Sdougm {
2199*3034Sdougm 	set_node_attr((void *)optionset, tag, value);
2200*3034Sdougm }
2201*3034Sdougm 
2202*3034Sdougm /*
2203*3034Sdougm  * is_nodetype(node, type)
2204*3034Sdougm  *
2205*3034Sdougm  * Check to see if node is of the type specified.
2206*3034Sdougm  */
2207*3034Sdougm 
2208*3034Sdougm static int
2209*3034Sdougm is_nodetype(void *node, char *type)
2210*3034Sdougm {
2211*3034Sdougm 	return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
2212*3034Sdougm }
2213*3034Sdougm 
2214*3034Sdougm /*
2215*3034Sdougm  * sa_set_prop_by_prop(optionset, group, prop, type)
2216*3034Sdougm  *
2217*3034Sdougm  * Add/remove/update the specified property prop into the optionset or
2218*3034Sdougm  * share. If a share, sort out which property group based on GUID. In
2219*3034Sdougm  * all cases, the appropriate transaction is set (or ZFS share is
2220*3034Sdougm  * marked as needing an update)
2221*3034Sdougm  */
2222*3034Sdougm 
2223*3034Sdougm #define	SA_PROP_OP_REMOVE	1
2224*3034Sdougm #define	SA_PROP_OP_ADD		2
2225*3034Sdougm #define	SA_PROP_OP_UPDATE	3
2226*3034Sdougm static int
2227*3034Sdougm sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
2228*3034Sdougm 			sa_property_t prop, int type)
2229*3034Sdougm {
2230*3034Sdougm 	char *name;
2231*3034Sdougm 	char *valstr;
2232*3034Sdougm 	int ret = SA_OK;
2233*3034Sdougm 	scf_transaction_entry_t *entry;
2234*3034Sdougm 	scf_value_t *value;
2235*3034Sdougm 	int opttype; /* 1 == optionset, 0 == security */
2236*3034Sdougm 	char *id = NULL;
2237*3034Sdougm 	int iszfs = 0;
2238*3034Sdougm 	int isshare = 0;
2239*3034Sdougm 	sa_group_t parent = NULL;
2240*3034Sdougm 
2241*3034Sdougm 	if (!is_persistent(group)) {
2242*3034Sdougm 		/*
2243*3034Sdougm 		 * if the group/share is not persistent we don't need
2244*3034Sdougm 		 * to do anything here
2245*3034Sdougm 		 */
2246*3034Sdougm 	    return (SA_OK);
2247*3034Sdougm 	}
2248*3034Sdougm 	name = sa_get_property_attr(prop, "type");
2249*3034Sdougm 	valstr = sa_get_property_attr(prop, "value");
2250*3034Sdougm 	entry = scf_entry_create(scf_handle->handle);
2251*3034Sdougm 	opttype = is_nodetype((void *)optionset, "optionset");
2252*3034Sdougm 
2253*3034Sdougm 	if (valstr != NULL && entry != NULL) {
2254*3034Sdougm 	    if (sa_is_share(group)) {
2255*3034Sdougm 		isshare = 1;
2256*3034Sdougm 		parent = sa_get_parent_group(group);
2257*3034Sdougm 		if (parent != NULL) {
2258*3034Sdougm 		    iszfs = is_zfs_group(parent);
2259*3034Sdougm 		}
2260*3034Sdougm 	    } else {
2261*3034Sdougm 		iszfs = is_zfs_group(group);
2262*3034Sdougm 	    }
2263*3034Sdougm 	    if (!iszfs) {
2264*3034Sdougm 		if (scf_handle->trans == NULL) {
2265*3034Sdougm 		    char oname[256];
2266*3034Sdougm 		    char *groupname = NULL;
2267*3034Sdougm 		    if (isshare) {
2268*3034Sdougm 			if (parent != NULL) {
2269*3034Sdougm 			    groupname = sa_get_group_attr(parent, "name");
2270*3034Sdougm 			}
2271*3034Sdougm 			id = sa_get_share_attr((sa_share_t)group, "id");
2272*3034Sdougm 		    } else {
2273*3034Sdougm 			groupname = sa_get_group_attr(group, "name");
2274*3034Sdougm 		    }
2275*3034Sdougm 		    if (groupname != NULL) {
2276*3034Sdougm 			ret = sa_get_instance(scf_handle, groupname);
2277*3034Sdougm 			sa_free_attr_string(groupname);
2278*3034Sdougm 		    }
2279*3034Sdougm 		    if (opttype)
2280*3034Sdougm 			(void) sa_optionset_name(optionset, oname,
2281*3034Sdougm 							sizeof (oname), id);
2282*3034Sdougm 		    else
2283*3034Sdougm 			(void) sa_security_name(optionset, oname,
2284*3034Sdougm 							sizeof (oname), id);
2285*3034Sdougm 		    ret = sa_start_transaction(scf_handle, oname);
2286*3034Sdougm 		}
2287*3034Sdougm 		if (ret == SA_OK) {
2288*3034Sdougm 		    switch (type) {
2289*3034Sdougm 		    case SA_PROP_OP_REMOVE:
2290*3034Sdougm 			ret = scf_transaction_property_delete(scf_handle->trans,
2291*3034Sdougm 								entry,
2292*3034Sdougm 								name);
2293*3034Sdougm 			break;
2294*3034Sdougm 		    case SA_PROP_OP_ADD:
2295*3034Sdougm 		    case SA_PROP_OP_UPDATE:
2296*3034Sdougm 			value = scf_value_create(scf_handle->handle);
2297*3034Sdougm 			if (value != NULL) {
2298*3034Sdougm 			    if (type == SA_PROP_OP_ADD)
2299*3034Sdougm 				ret = scf_transaction_property_new(
2300*3034Sdougm 							    scf_handle->trans,
2301*3034Sdougm 							    entry,
2302*3034Sdougm 							    name,
2303*3034Sdougm 							    SCF_TYPE_ASTRING);
2304*3034Sdougm 			    else
2305*3034Sdougm 				ret = scf_transaction_property_change(
2306*3034Sdougm 							    scf_handle->trans,
2307*3034Sdougm 							    entry,
2308*3034Sdougm 							    name,
2309*3034Sdougm 							    SCF_TYPE_ASTRING);
2310*3034Sdougm 			    if (ret == 0) {
2311*3034Sdougm 				ret = scf_value_set_astring(value, valstr);
2312*3034Sdougm 				if (ret == 0)
2313*3034Sdougm 				    ret = scf_entry_add_value(entry, value);
2314*3034Sdougm 				if (ret != 0) {
2315*3034Sdougm 				    scf_value_destroy(value);
2316*3034Sdougm 				    ret = SA_SYSTEM_ERR;
2317*3034Sdougm 				}
2318*3034Sdougm 			    } else {
2319*3034Sdougm 				scf_entry_destroy(entry);
2320*3034Sdougm 				ret = SA_SYSTEM_ERR;
2321*3034Sdougm 			    }
2322*3034Sdougm 			    break;
2323*3034Sdougm 			}
2324*3034Sdougm 		    }
2325*3034Sdougm 		}
2326*3034Sdougm 	    } else {
2327*3034Sdougm 		/*
2328*3034Sdougm 		 * ZFS update. The calling function would have updated
2329*3034Sdougm 		 * the internal XML structure. Just need to flag it as
2330*3034Sdougm 		 * changed for ZFS.
2331*3034Sdougm 		 */
2332*3034Sdougm 		zfs_set_update((sa_share_t)group);
2333*3034Sdougm 	    }
2334*3034Sdougm 	}
2335*3034Sdougm 
2336*3034Sdougm 	if (name != NULL)
2337*3034Sdougm 	    sa_free_attr_string(name);
2338*3034Sdougm 	if (valstr != NULL)
2339*3034Sdougm 	    sa_free_attr_string(valstr);
2340*3034Sdougm 	else if (entry != NULL)
2341*3034Sdougm 	    scf_entry_destroy(entry);
2342*3034Sdougm 
2343*3034Sdougm 	if (ret == -1)
2344*3034Sdougm 	    ret = SA_SYSTEM_ERR;
2345*3034Sdougm 
2346*3034Sdougm 	return (ret);
2347*3034Sdougm }
2348*3034Sdougm 
2349*3034Sdougm /*
2350*3034Sdougm  * sa_create_property(name, value)
2351*3034Sdougm  *
2352*3034Sdougm  * Create a new property with the specified name and value.
2353*3034Sdougm  */
2354*3034Sdougm 
2355*3034Sdougm sa_property_t
2356*3034Sdougm sa_create_property(char *name, char *value)
2357*3034Sdougm {
2358*3034Sdougm 	xmlNodePtr node;
2359*3034Sdougm 
2360*3034Sdougm 	node = xmlNewNode(NULL, (xmlChar *)"option");
2361*3034Sdougm 	if (node != NULL) {
2362*3034Sdougm 		xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
2363*3034Sdougm 		xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
2364*3034Sdougm 	}
2365*3034Sdougm 	return ((sa_property_t)node);
2366*3034Sdougm }
2367*3034Sdougm 
2368*3034Sdougm /*
2369*3034Sdougm  * sa_add_property(object, property)
2370*3034Sdougm  *
2371*3034Sdougm  * Add the specified property to the object. Issue the appropriate
2372*3034Sdougm  * transaction or mark a ZFS object as needing an update.
2373*3034Sdougm  */
2374*3034Sdougm 
2375*3034Sdougm int
2376*3034Sdougm sa_add_property(void *object, sa_property_t property)
2377*3034Sdougm {
2378*3034Sdougm 	int ret = SA_OK;
2379*3034Sdougm 	sa_group_t parent;
2380*3034Sdougm 	sa_group_t group;
2381*3034Sdougm 	char *proto;
2382*3034Sdougm 
2383*3034Sdougm 	proto = sa_get_optionset_attr(object, "type");
2384*3034Sdougm 	if (property != NULL) {
2385*3034Sdougm 	    if ((ret = sa_valid_property(object, proto, property)) == SA_OK) {
2386*3034Sdougm 		property = (sa_property_t)xmlAddChild((xmlNodePtr)object,
2387*3034Sdougm 							(xmlNodePtr)property);
2388*3034Sdougm 	    } else {
2389*3034Sdougm 		if (proto != NULL)
2390*3034Sdougm 		    sa_free_attr_string(proto);
2391*3034Sdougm 		return (ret);
2392*3034Sdougm 	    }
2393*3034Sdougm 	}
2394*3034Sdougm 
2395*3034Sdougm 	if (proto != NULL)
2396*3034Sdougm 	    sa_free_attr_string(proto);
2397*3034Sdougm 
2398*3034Sdougm 	parent = sa_get_parent_group(object);
2399*3034Sdougm 	if (!is_persistent(parent)) {
2400*3034Sdougm 	    return (ret);
2401*3034Sdougm 	}
2402*3034Sdougm 
2403*3034Sdougm 	if (sa_is_share(parent))
2404*3034Sdougm 	    group = sa_get_parent_group(parent);
2405*3034Sdougm 	else
2406*3034Sdougm 	    group = parent;
2407*3034Sdougm 
2408*3034Sdougm 	if (property == NULL)
2409*3034Sdougm 	    ret = SA_NO_MEMORY;
2410*3034Sdougm 	else {
2411*3034Sdougm 	    char oname[256];
2412*3034Sdougm 
2413*3034Sdougm 	    if (!is_zfs_group(group)) {
2414*3034Sdougm 		char *id = NULL;
2415*3034Sdougm 		if (sa_is_share((sa_group_t)parent)) {
2416*3034Sdougm 		    id = sa_get_share_attr((sa_share_t)parent, "id");
2417*3034Sdougm 		}
2418*3034Sdougm 		if (scf_handle->trans == NULL) {
2419*3034Sdougm 		    if (is_nodetype(object, "optionset"))
2420*3034Sdougm 			(void) sa_optionset_name((sa_optionset_t)object,
2421*3034Sdougm 					    oname, sizeof (oname), id);
2422*3034Sdougm 		    else
2423*3034Sdougm 			(void) sa_security_name((sa_optionset_t)object,
2424*3034Sdougm 					    oname, sizeof (oname), id);
2425*3034Sdougm 		    ret = sa_start_transaction(scf_handle, oname);
2426*3034Sdougm 		}
2427*3034Sdougm 		if (ret == SA_OK) {
2428*3034Sdougm 		    char *name;
2429*3034Sdougm 		    char *value;
2430*3034Sdougm 		    name = sa_get_property_attr(property, "type");
2431*3034Sdougm 		    value = sa_get_property_attr(property, "value");
2432*3034Sdougm 		    if (name != NULL && value != NULL) {
2433*3034Sdougm 			if (scf_handle->scf_state == SCH_STATE_INIT)
2434*3034Sdougm 			    ret = sa_set_property(scf_handle, name, value);
2435*3034Sdougm 		    } else
2436*3034Sdougm 			ret = SA_CONFIG_ERR;
2437*3034Sdougm 		    if (name != NULL)
2438*3034Sdougm 			sa_free_attr_string(name);
2439*3034Sdougm 		    if (value != NULL)
2440*3034Sdougm 			sa_free_attr_string(value);
2441*3034Sdougm 		}
2442*3034Sdougm 		if (id != NULL)
2443*3034Sdougm 		    sa_free_attr_string(id);
2444*3034Sdougm 	    } else {
2445*3034Sdougm 		/*
2446*3034Sdougm 		 * ZFS is a special case. We do want to allow editing
2447*3034Sdougm 		 * property/security lists since we can have a better
2448*3034Sdougm 		 * syntax and we also want to keep things consistent
2449*3034Sdougm 		 * when possible.
2450*3034Sdougm 		 *
2451*3034Sdougm 		 * Right now, we defer until the sa_commit_properties
2452*3034Sdougm 		 * so we can get them all at once. We do need to mark
2453*3034Sdougm 		 * the share as "changed"
2454*3034Sdougm 		 */
2455*3034Sdougm 		zfs_set_update((sa_share_t)parent);
2456*3034Sdougm 	    }
2457*3034Sdougm 	}
2458*3034Sdougm 	return (ret);
2459*3034Sdougm }
2460*3034Sdougm 
2461*3034Sdougm /*
2462*3034Sdougm  * sa_remove_property(property)
2463*3034Sdougm  *
2464*3034Sdougm  * Remove the specied property from its containing object. Update the
2465*3034Sdougm  * repository as appropriate.
2466*3034Sdougm  */
2467*3034Sdougm 
2468*3034Sdougm int
2469*3034Sdougm sa_remove_property(sa_property_t property)
2470*3034Sdougm {
2471*3034Sdougm 	int ret = SA_OK;
2472*3034Sdougm 
2473*3034Sdougm 	if (property != NULL) {
2474*3034Sdougm 		sa_optionset_t optionset;
2475*3034Sdougm 		sa_group_t group;
2476*3034Sdougm 		optionset = sa_get_property_parent(property);
2477*3034Sdougm 		if (optionset != NULL) {
2478*3034Sdougm 		    group = sa_get_optionset_parent(optionset);
2479*3034Sdougm 		    if (group != NULL) {
2480*3034Sdougm 			ret = sa_set_prop_by_prop(optionset, group, property,
2481*3034Sdougm 					    SA_PROP_OP_REMOVE);
2482*3034Sdougm 		    }
2483*3034Sdougm 		}
2484*3034Sdougm 		xmlUnlinkNode((xmlNodePtr)property);
2485*3034Sdougm 		xmlFreeNode((xmlNodePtr)property);
2486*3034Sdougm 	} else {
2487*3034Sdougm 	    ret = SA_NO_SUCH_PROP;
2488*3034Sdougm 	}
2489*3034Sdougm 	return (ret);
2490*3034Sdougm }
2491*3034Sdougm 
2492*3034Sdougm /*
2493*3034Sdougm  * sa_update_property(property, value)
2494*3034Sdougm  *
2495*3034Sdougm  * Update the specified property to the new value.  If value is NULL,
2496*3034Sdougm  * we currently treat this as a remove.
2497*3034Sdougm  */
2498*3034Sdougm 
2499*3034Sdougm int
2500*3034Sdougm sa_update_property(sa_property_t property, char *value)
2501*3034Sdougm {
2502*3034Sdougm 	int ret = SA_OK;
2503*3034Sdougm 	if (value == NULL) {
2504*3034Sdougm 		return (sa_remove_property(property));
2505*3034Sdougm 	} else {
2506*3034Sdougm 		sa_optionset_t optionset;
2507*3034Sdougm 		sa_group_t group;
2508*3034Sdougm 		set_node_attr((void *)property, "value", value);
2509*3034Sdougm 		optionset = sa_get_property_parent(property);
2510*3034Sdougm 		if (optionset != NULL) {
2511*3034Sdougm 		    group = sa_get_optionset_parent(optionset);
2512*3034Sdougm 		    if (group != NULL) {
2513*3034Sdougm 			ret = sa_set_prop_by_prop(optionset, group, property,
2514*3034Sdougm 					    SA_PROP_OP_UPDATE);
2515*3034Sdougm 		    }
2516*3034Sdougm 		} else {
2517*3034Sdougm 		    ret = SA_NO_SUCH_PROP;
2518*3034Sdougm 		}
2519*3034Sdougm 	}
2520*3034Sdougm 	return (ret);
2521*3034Sdougm }
2522*3034Sdougm 
2523*3034Sdougm /*
2524*3034Sdougm  *  _sa_get_next_error(node)
2525*3034Sdougm  *
2526*3034Sdougm  * Get the next (first if node==NULL) error node in the
2527*3034Sdougm  * document. "error" nodes are added if there were syntax errors
2528*3034Sdougm  * during parsing of the /etc/dfs/dfstab file. They are preserved in
2529*3034Sdougm  * comments and recreated in the doc on the next parse.
2530*3034Sdougm  */
2531*3034Sdougm 
2532*3034Sdougm xmlNodePtr
2533*3034Sdougm _sa_get_next_error(xmlNodePtr node)
2534*3034Sdougm {
2535*3034Sdougm 	if (node == NULL) {
2536*3034Sdougm 	    for (node = sa_config_tree->xmlChildrenNode;
2537*3034Sdougm 		node != NULL; node = node->next)
2538*3034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"error") == 0)
2539*3034Sdougm 		    return (node);
2540*3034Sdougm 	} else {
2541*3034Sdougm 	    for (node = node->next; node != NULL; node = node->next)
2542*3034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"error") == 0)
2543*3034Sdougm 		    return (node);
2544*3034Sdougm 	}
2545*3034Sdougm 	return (node);
2546*3034Sdougm }
2547*3034Sdougm 
2548*3034Sdougm /*
2549*3034Sdougm  * sa_get_protocol_property(propset, prop)
2550*3034Sdougm  *
2551*3034Sdougm  * Get the specified protocol specific property. These are global to
2552*3034Sdougm  * the protocol and not specific to a group or share.
2553*3034Sdougm  */
2554*3034Sdougm 
2555*3034Sdougm sa_property_t
2556*3034Sdougm sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
2557*3034Sdougm {
2558*3034Sdougm 	xmlNodePtr node = (xmlNodePtr)propset;
2559*3034Sdougm 	xmlChar *value = NULL;
2560*3034Sdougm 
2561*3034Sdougm 	for (node = node->children; node != NULL;
2562*3034Sdougm 		node = node->next) {
2563*3034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2564*3034Sdougm 		if (prop == NULL)
2565*3034Sdougm 		    break;
2566*3034Sdougm 		value = xmlGetProp(node, (xmlChar *)"type");
2567*3034Sdougm 		if (value != NULL &&
2568*3034Sdougm 		    xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
2569*3034Sdougm 		    break;
2570*3034Sdougm 		}
2571*3034Sdougm 		if (value != NULL) {
2572*3034Sdougm 		    xmlFree(value);
2573*3034Sdougm 		    value = NULL;
2574*3034Sdougm 		}
2575*3034Sdougm 	    }
2576*3034Sdougm 	}
2577*3034Sdougm 	if (value != NULL)
2578*3034Sdougm 		xmlFree(value);
2579*3034Sdougm 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
2580*3034Sdougm 	    /* avoid a non option node -- it is possible to be a text node */
2581*3034Sdougm 	    node = NULL;
2582*3034Sdougm 	}
2583*3034Sdougm 	return ((sa_property_t)node);
2584*3034Sdougm }
2585*3034Sdougm 
2586*3034Sdougm /*
2587*3034Sdougm  * sa_get_next_protocol_property(prop)
2588*3034Sdougm  *
2589*3034Sdougm  * Get the next protocol specific property in the list.
2590*3034Sdougm  */
2591*3034Sdougm 
2592*3034Sdougm sa_property_t
2593*3034Sdougm sa_get_next_protocol_property(sa_property_t prop)
2594*3034Sdougm {
2595*3034Sdougm 	xmlNodePtr node;
2596*3034Sdougm 
2597*3034Sdougm 	for (node = ((xmlNodePtr)prop)->next; node != NULL;
2598*3034Sdougm 		node = node->next) {
2599*3034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2600*3034Sdougm 			break;
2601*3034Sdougm 		}
2602*3034Sdougm 	}
2603*3034Sdougm 	return ((sa_property_t)node);
2604*3034Sdougm }
2605*3034Sdougm 
2606*3034Sdougm /*
2607*3034Sdougm  * sa_set_protocol_property(prop, value)
2608*3034Sdougm  *
2609*3034Sdougm  * Set the specified property to have the new value.  The protocol
2610*3034Sdougm  * specific plugin will then be called to update the property.
2611*3034Sdougm  */
2612*3034Sdougm 
2613*3034Sdougm int
2614*3034Sdougm sa_set_protocol_property(sa_property_t prop, char *value)
2615*3034Sdougm {
2616*3034Sdougm 	sa_protocol_properties_t propset;
2617*3034Sdougm 	char *proto;
2618*3034Sdougm 	int ret = SA_INVALID_PROTOCOL;
2619*3034Sdougm 
2620*3034Sdougm 	propset = ((xmlNodePtr)prop)->parent;
2621*3034Sdougm 	if (propset != NULL) {
2622*3034Sdougm 	    proto = sa_get_optionset_attr(propset, "type");
2623*3034Sdougm 	    if (proto != NULL) {
2624*3034Sdougm 		set_node_attr((xmlNodePtr)prop, "value", value);
2625*3034Sdougm 		ret = sa_proto_set_property(proto, prop);
2626*3034Sdougm 		sa_free_attr_string(prop);
2627*3034Sdougm 	    }
2628*3034Sdougm 	}
2629*3034Sdougm 	return (ret);
2630*3034Sdougm }
2631*3034Sdougm 
2632*3034Sdougm /*
2633*3034Sdougm  * sa_add_protocol_property(propset, prop)
2634*3034Sdougm  *
2635*3034Sdougm  * Add a new property to the protocol sepcific property set.
2636*3034Sdougm  */
2637*3034Sdougm 
2638*3034Sdougm int
2639*3034Sdougm sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
2640*3034Sdougm {
2641*3034Sdougm 	xmlNodePtr node;
2642*3034Sdougm 
2643*3034Sdougm 	/* should check for legitimacy */
2644*3034Sdougm 	node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
2645*3034Sdougm 	if (node != NULL)
2646*3034Sdougm 	    return (SA_OK);
2647*3034Sdougm 	return (SA_NO_MEMORY);
2648*3034Sdougm }
2649*3034Sdougm 
2650*3034Sdougm /*
2651*3034Sdougm  * sa_create_protocol_properties(proto)
2652*3034Sdougm  *
2653*3034Sdougm  * Create a protocol specifity property set.
2654*3034Sdougm  */
2655*3034Sdougm 
2656*3034Sdougm sa_protocol_properties_t
2657*3034Sdougm sa_create_protocol_properties(char *proto)
2658*3034Sdougm {
2659*3034Sdougm 	xmlNodePtr node;
2660*3034Sdougm 	node = xmlNewNode(NULL, (xmlChar *)"propertyset");
2661*3034Sdougm 	if (node != NULL) {
2662*3034Sdougm 	    xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
2663*3034Sdougm 	}
2664*3034Sdougm 	return (node);
2665*3034Sdougm }
2666