summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
Diffstat (limited to 'usr')
-rw-r--r--usr/src/Makefile.lint1
-rw-r--r--usr/src/Targetdirs1
-rw-r--r--usr/src/cmd/Makefile1
-rw-r--r--usr/src/cmd/Makefile.cmd4
-rw-r--r--usr/src/cmd/dfs.cmds/Makefile16
-rw-r--r--usr/src/cmd/dfs.cmds/share/share.c311
-rw-r--r--usr/src/cmd/dfs.cmds/shareall/shareall.sh39
-rw-r--r--usr/src/cmd/dfs.cmds/sharectl/Makefile (renamed from usr/src/cmd/fs.d/nfs/unshare/Makefile)44
-rw-r--r--usr/src/cmd/dfs.cmds/sharectl/Makefile.com92
-rw-r--r--usr/src/cmd/dfs.cmds/sharectl/i386/Makefile (renamed from usr/src/cmd/dfs.cmds/share/Makefile)35
-rw-r--r--usr/src/cmd/dfs.cmds/sharectl/sharectl.c425
-rw-r--r--usr/src/cmd/dfs.cmds/sharectl/sparc/Makefile30
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/Makefile57
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/Makefile.com104
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/commands.c4011
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/group.xml98
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/i386/Makefile30
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/plugins/Makefile52
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/plugins/Makefile.com129
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/plugins/i386/Makefile30
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.c2487
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.h131
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/plugins/sparc/Makefile30
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/sharemgr.h117
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/sharemgr_main.c152
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/shareutil.c84
-rw-r--r--usr/src/cmd/dfs.cmds/sharemgr/sparc/Makefile30
-rw-r--r--usr/src/cmd/dfs.cmds/unshareall/unshareall.sh23
-rw-r--r--usr/src/cmd/fs.d/nfs/Makefile6
-rw-r--r--usr/src/cmd/fs.d/nfs/share/Makefile57
-rw-r--r--usr/src/cmd/fs.d/nfs/share/share.c1257
-rw-r--r--usr/src/cmd/fs.d/nfs/svc/nfs-server46
-rw-r--r--usr/src/cmd/fs.d/nfs/svc/server.xml37
-rw-r--r--usr/src/cmd/fs.d/nfs/unshare/unshare.c196
-rw-r--r--usr/src/cmd/initpkg/dfstab.sh18
-rw-r--r--usr/src/lib/Makefile4
-rw-r--r--usr/src/lib/libsecdb/exec_attr.txt1
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt2
-rw-r--r--usr/src/lib/libshare/Makefile68
-rw-r--r--usr/src/lib/libshare/Makefile.com81
-rw-r--r--usr/src/lib/libshare/common/issubdir.c (renamed from usr/src/cmd/fs.d/nfs/share/issubdir.c)0
-rw-r--r--usr/src/lib/libshare/common/libshare.c2665
-rw-r--r--usr/src/lib/libshare/common/libshare.h217
-rw-r--r--usr/src/lib/libshare/common/libshare_impl.h143
-rw-r--r--usr/src/lib/libshare/common/libshare_zfs.c496
-rw-r--r--usr/src/lib/libshare/common/libsharecore.c1801
-rw-r--r--usr/src/lib/libshare/common/llib-lshare32
-rw-r--r--usr/src/lib/libshare/common/mapfile-vers110
-rw-r--r--usr/src/lib/libshare/common/parser.c121
-rw-r--r--usr/src/lib/libshare/common/plugin.c458
-rw-r--r--usr/src/lib/libshare/common/scfutil.c1443
-rw-r--r--usr/src/lib/libshare/common/scfutil.h81
-rw-r--r--usr/src/lib/libshare/i386/Makefile30
-rw-r--r--usr/src/lib/libshare/sparc/Makefile30
-rw-r--r--usr/src/pkgdefs/SUNWarc/prototype_com2
-rw-r--r--usr/src/pkgdefs/SUNWcsr/prototype_com2
-rw-r--r--usr/src/pkgdefs/SUNWcsu/prototype_com8
-rw-r--r--usr/src/pkgdefs/SUNWhea/prototype_com1
-rw-r--r--usr/src/pkgdefs/SUNWnfssu/prototype_com11
59 files changed, 16009 insertions, 1979 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index e11d7f0c1c..95d7251dd1 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -361,6 +361,7 @@ COMMON_SUBDIRS = \
lib/libsecdb \
lib/libsendfile \
lib/libsip \
+ lib/libshare \
lib/libsldap \
lib/libslp \
lib/libsmbios \
diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs
index 3e48489d6b..7061d9c6d7 100644
--- a/usr/src/Targetdirs
+++ b/usr/src/Targetdirs
@@ -165,6 +165,7 @@ ROOT.SYS= \
/var/svc/manifest/network/nis \
/var/svc/manifest/network/rpc \
/var/svc/manifest/network/security \
+ /var/svc/manifest/network/shares \
/var/svc/manifest/network/ssl \
/var/svc/manifest/application \
/var/svc/manifest/application/management \
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index c6c05aadb2..78c8e2083b 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -531,6 +531,7 @@ MSGSUBDIRS= \
dd \
deroff \
devfsadm \
+ dfs.cmds \
diff \
diffmk \
dladm \
diff --git a/usr/src/cmd/Makefile.cmd b/usr/src/cmd/Makefile.cmd
index 6ea1b083b5..f8e70bedeb 100644
--- a/usr/src/cmd/Makefile.cmd
+++ b/usr/src/cmd/Makefile.cmd
@@ -205,6 +205,7 @@ ROOTSVCNETWORKNIS= $(ROOTSVCNETWORK)/nis
ROOTSVCNETWORKRPC= $(ROOTSVCNETWORK)/rpc
ROOTSVCNETWORKSECURITY= $(ROOTSVCNETWORK)/security
ROOTSVCNETWORKSSL= $(ROOTSVCNETWORK)/ssl
+ROOTSVCNETWORKSHARES= $(ROOTSVCNETWORK)/shares
ROOTSVCPLATFORM= $(ROOTVARSVCMANIFEST)/platform
ROOTSVCPLATFORMI86PC= $(ROOTSVCPLATFORM)/i86pc
ROOTSVCPLATFORMSUN4U= $(ROOTSVCPLATFORM)/sun4u
@@ -367,6 +368,9 @@ $(ROOTSVCNETWORKSECURITY)/%: %
$(ROOTSVCNETWORKSSL)/%: %
$(INS.file)
+$(ROOTSVCNETWORKSHARES)/%: %
+ $(INS.file)
+
$(ROOTSVCAPPLICATION)/%: %
$(INS.file)
diff --git a/usr/src/cmd/dfs.cmds/Makefile b/usr/src/cmd/dfs.cmds/Makefile
index 70b14abbc0..01dc8aabc7 100644
--- a/usr/src/cmd/dfs.cmds/Makefile
+++ b/usr/src/cmd/dfs.cmds/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -20,13 +19,15 @@
# CDDL HEADER END
#
#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+##
#ident "%Z%%M% %I% %E% SMI"
#
-# Copyright (c) 1989 by Sun Microsystems, Inc.
-#
# cmd/dfs.cmds/Makefile
-SUBDIRS= general dfshares share shareall unshareall
+SUBDIRS= general dfshares shareall unshareall sharemgr sharectl
+MSGSUBDIRS= sharectl sharemgr
include ../Makefile.cmd
@@ -35,6 +36,7 @@ install := TARGET= install
clean := TARGET= clean
clobber := TARGET= clobber
lint := TARGET= lint
+_msg := TARGET = _msg
.KEEP_STATE:
@@ -43,4 +45,6 @@ all install clean clobber lint: $(SUBDIRS)
$(SUBDIRS): FRC
@cd $@; pwd; $(MAKE) $(TARGET)
+_msg: $(MSGSUBDIRS)
+
FRC:
diff --git a/usr/src/cmd/dfs.cmds/share/share.c b/usr/src/cmd/dfs.cmds/share/share.c
deleted file mode 100644
index 6a6fecfc1c..0000000000
--- a/usr/src/cmd/dfs.cmds/share/share.c
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
-/* All Rights Reserved */
-
-
-/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-/*
- * generic interface to share
- *
- * usage: share [-F fstype] [-o fs_options] [-d desc] [ args ]
- *
- * exec's /usr/lib/fs/<fstype>/<cmd>
- * <cmd> is the basename of the command.
- *
- * if -F is missing, fstype is the first entry in /etc/dfs/fstypes
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <utmpx.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <dirent.h>
-#include <string.h>
-
-#define DFSTYPES "/etc/dfs/fstypes" /* dfs list */
-#define FSCMD "/usr/lib/fs/%s/%s"
-#define SHAREFILE "/etc/dfs/sharetab"
-#define MAXFIELD 5
-#define BUFSIZE 65536
-
-#define ARGVPAD 6 /* non-[arg...] elements in new argv list: */
- /* cmd name, -o, opts, -d, desc, (char *)0 */
- /* terminator */
-
-static char *fieldv[MAXFIELD];
-static struct stat stbuf;
-static char *cmd; /* basename of this command */
-static char *getfs();
-static void list_res(char *fsname);
-static void get_data(char *s);
-void perror();
-
-int
-main(argc, argv)
-int argc;
-char **argv;
-{
- static int invalid();
- extern char *optarg;
- extern int optind;
- FILE *dfp; /* fp for dfs list */
- int c, err = 0;
- char subcmd[BUFSIZE]; /* fs specific command */
- char *fsname = NULL; /* file system name */
- char *desc = NULL; /* for -d */
- char *opts = NULL; /* -o options */
- char **nargv; /* new argv list */
- int nargc = 0; /* new argc */
- int optnum;
- struct utmpx *utmpxp;
- struct stat st;
- int showall = (argc <= 1); /* show all resources */
- static char usage[] =
- "usage: %s [-F fstype] [-o fs_options ]"
- " [-d description] [pathname [resourcename]]\n";
-
- cmd = strrchr(argv[0], '/'); /* find the basename */
- if (cmd)
- ++cmd;
- else
- cmd = argv[0];
-
- while ((c = getopt(argc, argv, "F:d:o:")) != -1)
- switch (c) {
- case 'F':
- err |= (fsname != NULL); /* at most one -F */
- fsname = optarg;
- break;
- case 'd': /* description */
- err |= (desc != NULL); /* at most one -d */
- desc = optarg;
- break;
- case 'o': /* fs specific options */
- err |= (opts != NULL); /* at most one -o */
- opts = optarg;
- break;
- case '?':
- err = 1;
- break;
- }
- if (err) {
- (void) fprintf(stderr, usage, cmd);
- exit(1);
- }
-
- if ((dfp = fopen(DFSTYPES, "r")) == NULL) {
- (void) fprintf(stderr, "%s: cannot open %s\n", cmd, DFSTYPES);
- exit(1);
- }
-
- /* allocate a block for the new argv list */
- if (!(nargv = (char **)malloc(sizeof (char *)*(argc-optind+ARGVPAD)))) {
- (void) fprintf(stderr, "%s: malloc failed.\n", cmd);
- exit(1);
- }
- optnum = optind;
- nargv[nargc++] = cmd;
- if (opts) {
- nargv[nargc++] = "-o";
- nargv[nargc++] = opts;
- }
- if (desc) {
- nargv[nargc++] = "-d";
- nargv[nargc++] = desc;
- }
- for (; optind <= argc; ++optind) /* this copies the last NULL */
- nargv[nargc++] = argv[optind];
-
- if (showall) { /* share with no args -- show all dfs's */
- while (fsname = getfs(dfp)) {
- list_res(fsname);
- }
- (void) fclose(dfp);
- exit(0);
- }
-
- if (fsname) { /* generate fs specific command name */
- if (invalid(fsname, dfp)) { /* valid ? */
- (void) fprintf(stderr,
- "%s: invalid file system name\n", cmd);
- (void) fprintf(stderr, usage, cmd);
- exit(1);
- }
- if (argc <= 3 && argc == optnum) {
- /* list shared resources for share -F fstype */
- list_res(fsname);
- exit(0);
- }
- else
- (void) snprintf(subcmd, sizeof (subcmd),
- FSCMD, fsname, cmd);
- } else if (fsname = getfs(dfp)) /* use 1st line in dfstypes */
- (void) snprintf(subcmd, sizeof (subcmd), FSCMD, fsname, cmd);
- else {
- (void) fprintf(stderr, "%s: no file systems in %s\n",
- cmd, DFSTYPES);
- (void) fprintf(stderr, usage, cmd);
- exit(1);
- }
- /*
- * if sharetab is older than boot time then remove it.
- * This will happen only for the first time share is
- * called after boot.
- */
- if ((stat(SHAREFILE, &st) == 0) && /* does sharetab exist? */
- (utmpxp = getutxent()) != NULL && /* does utmpx exist? */
- (utmpxp->ut_xtime > st.st_mtime)) /* sharetab older? */
- (void) truncate(SHAREFILE, 0);
-
- (void) execvp(subcmd, nargv);
- perror(subcmd);
- return (1);
- /*NOTREACHED*/
-}
-
-
-/*
- * invalid(name, f) - return non-zero if name is not in
- * the list of fs names in file f
- */
-
-static int
-invalid(name, f)
-char *name; /* file system name */
-FILE *f; /* file of list of systems */
-{
- char *s;
-
- while (s = getfs(f)) /* while there's still hope ... */
- if (strcmp(s, name) == 0)
- return (0); /* we got it! */
- return (1);
-}
-
-
-/*
- * getfs(fp) - get the next file system name from fp
- * ignoring lines starting with a #.
- * All leading whitespace is discarded.
- */
-
-static char buf[BUFSIZE];
-
-static char *
-getfs(fp)
-FILE *fp;
-{
- register char *s;
-
- while (s = fgets(buf, BUFSIZE, fp)) {
- while (isspace(*s)) /* leading whitespace doesn't count */
- ++s;
- if (*s != '#') { /* not a comment */
- char *t = s;
-
- while (!isspace(*t)) /* get the token */
- ++t;
- *t = '\0'; /* ignore rest of line */
- return (s);
- }
- }
- return (NULL); /* that's all, folks! */
-}
-
-/* list data in /etc/dfs/sharetab in adv(1) format */
-
-static void
-list_res(fsname)
-char *fsname;
-{
- char advbuf[BUFSIZE];
- FILE *fp;
-
-
- if (stat(SHAREFILE, &stbuf) != -1) {
- if ((fp = fopen(SHAREFILE, "r")) == NULL) {
- (void) fprintf(stderr, "%s: cannot open <%s>\n",
- cmd, SHAREFILE);
- exit(1);
- }
- while (fgets(advbuf, BUFSIZE, fp)) {
- get_data(advbuf);
- if (strcmp(fieldv[2], fsname) == 0) {
- (void) printf("%-14.14s", fieldv[1]);
- (void) printf(" %s ", fieldv[0]);
- (void) printf(" %s ", fieldv[3]);
- if (*fieldv[4]) /* description */
- (void) printf(" \"%s\" ", fieldv[4]);
- else
- (void) printf(" \"\" ");
- (void) printf("\n");
- }
- }
- (void) fclose(fp);
- }
-}
-
-static char empty[] = "";
-
-static void
-get_data(s)
-char *s;
-{
- register int fieldc = 0;
-
- /*
- * This function parses an advertise entry from
- * /etc/dfs/sharetab and sets the pointers appropriately.
- * fieldv[0] : pathname
- * fieldv[1] : resource
- * fieldv[2] : fstype
- * fieldv[3] : options
- * fieldv[4] : description
- */
-
- while ((*s != '\n') && (*s != '\0') && (fieldc < 5)) {
- while (isspace(*s))
- s++;
- fieldv[fieldc++] = s;
-
- if (fieldc == 5) { /* get the description field */
- if (fieldv[4][strlen(fieldv[4])-1] == '\n')
- fieldv[4][strlen(fieldv[4])-1] = '\0';
- break;
- }
- while (*s && !isspace(*s)) ++s;
- if (*s)
- *s++ = '\0';
- }
- while (fieldc < 5)
- fieldv[fieldc++] = empty;
-}
diff --git a/usr/src/cmd/dfs.cmds/shareall/shareall.sh b/usr/src/cmd/dfs.cmds/shareall/shareall.sh
index 0e6534e41b..96facecced 100644
--- a/usr/src/cmd/dfs.cmds/shareall/shareall.sh
+++ b/usr/src/cmd/dfs.cmds/shareall/shareall.sh
@@ -3,9 +3,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -20,8 +19,8 @@
#
# CDDL HEADER END
#
-# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
-# All Rights Reserved
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
#ident "%Z%%M% %I% %E% SMI"
@@ -60,17 +59,21 @@ fi
if [ "$fsys" ] # for each file system ...
then
- while read line # get complete lines
- do
+ if [ "$infile" = "/etc/dfs/dfstab" ]
+ then
+ /usr/sbin/sharemgr start -P $fsys -a
+ else
+ while read line # get complete lines
+ do
echo $line
- done < $infile |
+ done < $infile |
- `egrep "^[^#]*[ ][ ]*-F[ ]*(\`echo $fsys|tr ',' '|'\`)" |
- /sbin/sh`
+ `egrep "^[^#]*[ ][ ]*-F[ ]*(\`echo $fsys|tr ',' '|'\`)" |
+ /sbin/sh`
- fsys_file=/etc/dfs/fstypes
- if [ -f $fsys_file ] # get default file system type
- then
+ fsys_file=/etc/dfs/fstypes
+ if [ -f $fsys_file ] # get default file system type
+ then
def_fs=`egrep '^[^#]' $fsys_file | awk '{print $1; exit}'`
if [ "$def_fs" = "$fsys" ] # if default is what we want ...
then # for every file system ...
@@ -82,9 +85,15 @@ then
# not a comment and no -F option
`egrep -v "(^[#]|-F)" | /sbin/sh`
fi
- else
+ else
echo "shareall: can't open $fsys_file"
+ fi
fi
else # for every file system ...
- cat $infile|/sbin/sh
+ if [ "$infile" = "/etc/dfs/dfstab" ]
+ then
+ /usr/sbin/sharemgr start -a
+ else
+ cat $infile|/sbin/sh
+ fi
fi
diff --git a/usr/src/cmd/fs.d/nfs/unshare/Makefile b/usr/src/cmd/dfs.cmds/sharectl/Makefile
index 4ff50408b5..45e60ccb1b 100644
--- a/usr/src/cmd/fs.d/nfs/unshare/Makefile
+++ b/usr/src/cmd/dfs.cmds/sharectl/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -20,33 +19,34 @@
# CDDL HEADER END
#
#
-#ident "%Z%%M% %I% %E% SMI"
+# ident "%Z%%M% %I% %E% SMI"
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+DEFAULTFILES =
-FSTYPE= nfs
-LIBPROG= unshare
-ATTMK= $(LIBPROG)
+include ../../Makefile.cmd
-include ../../Makefile.fstype
+#
+# One for each ISA.
+#
+SUBDIRS = $(MACH)
-COMMON= sharetab.o nfslogtab.o
-OBJS= $(LIBPROG).o $(COMMON)
-SRCS= $(LIBPROG).c ../lib/sharetab.c ../lib/nfslogtab.c
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+_msg := TARGET= _msg
+lint := TARGET= lint
-$(LIBPROG): $(OBJS)
- $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
- $(POST_PROCESS)
+.KEEP_STATE:
-sharetab.o: ../lib/sharetab.c
- $(COMPILE.c) ../lib/sharetab.c
+all clean clobber lint _msg: $(SUBDIRS)
-nfslogtab.o: ../lib/nfslogtab.c
- $(COMPILE.c) ../lib/nfslogtab.c
+install: $(SUBDIRS) $(ROOTETCDEFAULTFILES)
-lint: lint_SRCS
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
-clean:
- $(RM) $(OBJS)
+FRC:
diff --git a/usr/src/cmd/dfs.cmds/sharectl/Makefile.com b/usr/src/cmd/dfs.cmds/sharectl/Makefile.com
new file mode 100644
index 0000000000..75e31110cc
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharectl/Makefile.com
@@ -0,0 +1,92 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../../../Makefile.cmd
+
+COMMON = ..
+
+PROG= sharectl
+
+SHARECTL_MOD = sharectl
+
+SHARECTL_SRC = $(SHARECTL_MOD:%=$(COMMON)/%.c) shareutil.c
+
+SHARECTL_OBJ = $(SHARECTL_MOD:%=%.o) shareutil.o
+
+
+MYCPPFLAGS = -I.. -I../../sharemgr
+CPPFLAGS += $(MYCPPFLAGS)
+LDLIBS += -lshare -lumem
+
+SRCS = $(SHARECTL_SRC)
+OBJS = $(SHARECTL_OBJ)
+MODS = $(SHARECTL_MOD)
+
+CLOBBERFILES = $(MODS)
+
+POFILES = $(SHARECTL_SRC:.c=.po)
+POFILE = sharectl.po
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg:= TARGET= catalog
+
+.KEEP_STATE:
+
+all: $(MODS)
+
+catalog: $(POFILE)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG)
+
+lint: $(SHARECTL_MOD).ln $(SHARECTL_SRC:.c=.ln)
+
+clean:
+ $(RM) $(OBJS)
+
+%.ln: FRC
+ $(LINT.c) $(SHARECTL_SRC) $(LDLIBS)
+
+include ../../../Makefile.targ
+
+$(POFILE): $(POFILES)
+ $(RM) $@; cat $(POFILES) > $@
+
+%.o: $(COMMON)/%.c
+ $(COMPILE.c) -o $@ $<
+
+shareutil.c: ../../sharemgr/shareutil.c
+ $(CP) -f ../../sharemgr/shareutil.c shareutil.c
+
+FRC:
diff --git a/usr/src/cmd/dfs.cmds/share/Makefile b/usr/src/cmd/dfs.cmds/sharectl/i386/Makefile
index e720feed94..9201a4391d 100644
--- a/usr/src/cmd/dfs.cmds/share/Makefile
+++ b/usr/src/cmd/dfs.cmds/sharectl/i386/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -22,32 +21,10 @@
#
# ident "%Z%%M% %I% %E% SMI"
#
-# Copyright (c) 1989 by Sun Microsystems, Inc.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
#
-# cmd/dfs.cmds/share/Makefile
-#
-
-PROG= share
-
-OBJS= share.o
-
-CFLAG += -s
-
-include ../../Makefile.cmd
-
-.KEEP_STATE:
-
-all: $(PROG)
-
-$(PROG): share.o
- $(LINK.c) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
- $(POST_PROCESS)
-
-install: all $(ROOTUSRSBINPROG)
-
-clean:
- $(RM) $(OBJS)
-lint: lint_PROG
+include ../Makefile.com
-include ../../Makefile.targ
+install: all
diff --git a/usr/src/cmd/dfs.cmds/sharectl/sharectl.c b/usr/src/cmd/dfs.cmds/sharectl/sharectl.c
new file mode 100644
index 0000000000..475da4842f
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharectl/sharectl.c
@@ -0,0 +1,425 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include "libshare.h"
+#include <sharemgr.h>
+
+#include <libintl.h>
+#include <locale.h>
+
+static int run_command(char *, int, char **);
+static void sub_command_help(char *proto);
+
+static void
+global_help()
+{
+ (void) printf(gettext("usage: sharectl <command> [options]\n"));
+ sub_command_help(NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int help = 0;
+ int rval;
+ char *command;
+
+ /*
+ * make sure locale and gettext domain is setup
+ */
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ sa_init(SA_INIT_CONTROL_API);
+
+ while ((c = getopt(argc, argv, "h?")) != EOF) {
+ switch (c) {
+ case '?':
+ case 'h':
+ help = 1;
+ break;
+ default:
+ (void) printf(gettext("Invalid option: %c\n"), c);
+ }
+ }
+ if (optind == argc || help) {
+ /* no subcommand */
+ global_help();
+ exit(0);
+ }
+ optind = 1;
+
+ /*
+ * now have enough to parse rest of command line
+ */
+ command = argv[optind];
+ rval = run_command(command, argc - optind, argv + optind);
+
+ sa_fini();
+ return (rval);
+}
+
+char *
+sc_get_usage(sc_usage_t index)
+{
+ char *ret = NULL;
+
+ switch (index) {
+ case USAGE_CTL_GET:
+ ret = gettext("get [-h] -p property ... proto");
+ break;
+ case USAGE_CTL_SET:
+ ret = gettext("set [-h] -p property=value ... proto");
+ break;
+ case USAGE_CTL_STATUS:
+ ret = gettext("status -h | proto...");
+ break;
+ }
+ return (ret);
+}
+
+static int
+sc_get(int flags, int argc, char *argv[])
+{
+ char *proto = NULL;
+ struct options *optlist = NULL;
+ int ret = SA_OK;
+ int c;
+#ifdef lint
+ flags = flags;
+#endif
+
+ while ((c = getopt(argc, argv, "?hp:")) != EOF) {
+ switch (c) {
+ case 'p':
+ ret = add_opt(&optlist, optarg, 1);
+ if (ret != SA_OK) {
+ (void) printf(gettext("Problem with property: %s\n"),
+ optarg);
+ return (SA_NO_MEMORY);
+ }
+ break;
+ default:
+ (void) printf(gettext("usage: %s\n"),
+ sc_get_usage(USAGE_CTL_GET));
+ return (SA_SYNTAX_ERR);
+ case '?':
+ case 'h':
+ (void) printf(gettext("usage: %s\n"),
+ sc_get_usage(USAGE_CTL_GET));
+ return (SA_OK);
+ break;
+ }
+ }
+
+ if (optind >= argc) {
+ (void) printf(gettext("usage: %s\n"), sc_get_usage(USAGE_CTL_GET));
+ (void) printf(gettext("\tprotocol must be specified.\n"));
+ return (SA_INVALID_PROTOCOL);
+ }
+
+ proto = argv[optind];
+ if (sa_valid_protocol(proto)) {
+ sa_protocol_properties_t propset;
+ propset = sa_proto_get_properties(proto);
+ if (propset != NULL) {
+ sa_property_t prop;
+ char *value;
+ char *name;
+ if (optlist == NULL) {
+ /* display all known properties for this protocol */
+ for (prop = sa_get_protocol_property(propset, NULL);
+ prop != NULL;
+ prop = sa_get_next_protocol_property(prop)) {
+
+ /* get and display the property and value */
+ name = sa_get_property_attr(prop, "type");
+ if (name != NULL) {
+ value = sa_get_property_attr(prop, "value");
+ (void) printf(gettext("%s=%s\n"), name,
+ value != NULL ? value : "");
+ }
+ if (value != NULL)
+ sa_free_attr_string(value);
+ if (name != NULL)
+ sa_free_attr_string(name);
+ }
+ } else {
+ struct options *opt;
+ /* list the specified option(s) */
+ for (opt = optlist; opt != NULL; opt = opt->next) {
+ prop = sa_get_protocol_property(propset, opt->optname);
+ if (prop != NULL) {
+ value = sa_get_property_attr(prop, "value");
+ (void) printf(gettext("%s=%s\n"), opt->optname,
+ value != NULL ? value : "");
+ sa_free_attr_string(value);
+ } else {
+ (void) printf(gettext("%s: not defined\n"),
+ opt->optname);
+ }
+ }
+ }
+ }
+ } else {
+ (void) printf(gettext("Invalid protocol specified: %s\n"), proto);
+ ret = SA_INVALID_PROTOCOL;
+ }
+ return (ret);
+}
+
+static int
+sc_set(int flags, int argc, char *argv[])
+{
+ char *proto = NULL;
+ struct options *optlist = NULL;
+ int ret = SA_OK;
+ int c;
+#ifdef lint
+ flags = flags;
+#endif
+
+ while ((c = getopt(argc, argv, "?hp:")) != EOF) {
+ switch (c) {
+ case 'p':
+ ret = add_opt(&optlist, optarg, 0);
+ if (ret != SA_OK) {
+ (void) printf(gettext("Problem with property: %s\n"),
+ optarg);
+ return (SA_NO_MEMORY);
+ }
+ break;
+ default:
+ (void) printf(gettext("usage: %s\n"),
+ sc_get_usage(USAGE_CTL_SET));
+ return (SA_SYNTAX_ERR);
+ case '?':
+ case 'h':
+ (void) printf(gettext("usage: %s\n"),
+ sc_get_usage(USAGE_CTL_SET));
+ return (SA_OK);
+ break;
+ }
+ }
+
+ if (optind >= argc) {
+ (void) printf(gettext("usage: %s\n"),
+ sc_get_usage(USAGE_CTL_SET));
+ (void) printf(gettext("\tprotocol must be specified.\n"));
+ return (SA_INVALID_PROTOCOL);
+ }
+
+ proto = argv[optind];
+ if (sa_valid_protocol(proto)) {
+ sa_protocol_properties_t propset;
+ propset = sa_proto_get_properties(proto);
+ if (propset != NULL) {
+ sa_property_t prop;
+
+ if (optlist == NULL) {
+ (void) printf(gettext("usage: %s\n"),
+ sc_get_usage(USAGE_CTL_SET));
+ (void) printf(gettext("\tat least one property and value "
+ "must be specified\n"));
+ } else {
+ struct options *opt;
+ /* list the specified option(s) */
+ for (opt = optlist; opt != NULL; opt = opt->next) {
+ prop = sa_get_protocol_property(propset, opt->optname);
+ if (prop != NULL) {
+ ret = sa_set_protocol_property(prop, opt->optvalue);
+ if (ret != SA_OK) {
+ (void) printf(gettext("Could not set property"
+ " %s: %s\n"),
+ opt->optname, sa_errorstr(ret));
+ }
+ } else {
+ (void) printf(gettext("%s: not defined\n"),
+ opt->optname);
+ }
+ }
+ }
+ }
+ } else {
+ (void) printf(gettext("Invalid protocol specified: %s\n"), proto);
+ ret = SA_INVALID_PROTOCOL;
+ }
+ return (ret);
+}
+
+static void
+show_status(char *proto)
+{
+ char *status;
+ status = sa_get_protocol_status(proto);
+ (void) printf("%s\t%s\n", proto, status ? gettext(status) : "-");
+ if (status != NULL)
+ free(status);
+}
+
+static int
+valid_proto(char **protos, int num, char *proto)
+{
+ int i;
+ for (i = 0; i < num; i++)
+ if (strcmp(protos[i], proto) == 0)
+ return (1);
+ return (0);
+}
+
+static int
+sc_status(int flags, int argc, char *argv[])
+{
+ char **protos;
+ int ret = SA_OK;
+ int c;
+ int i;
+ int num_proto;
+ int verbose = 0;
+#ifdef lint
+ flags = flags;
+#endif
+
+ while ((c = getopt(argc, argv, "?hv")) != EOF) {
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ case 'h':
+ (void) printf(gettext("usage: %s\n"),
+ sc_get_usage(USAGE_CTL_SET));
+ return (SA_OK);
+ default:
+ (void) printf(gettext("usage: %s\n"),
+ sc_get_usage(USAGE_CTL_SET));
+ return (SA_SYNTAX_ERR);
+ }
+ }
+
+ num_proto = sa_get_protocols(&protos);
+ if (optind == argc) {
+ /* status for all protocols */
+ for (i = 0; i < num_proto; i++) {
+ show_status(protos[i]);
+ }
+ } else {
+ for (i = optind; i < argc; i++) {
+ if (valid_proto(protos, num_proto, argv[i])) {
+ show_status(argv[i]);
+ } else {
+ (void) printf(gettext("Invalid protocol: %s\n"), argv[i]);
+ ret = SA_INVALID_PROTOCOL;
+ }
+ }
+ }
+ if (protos != NULL)
+ free(protos);
+ return (ret);
+}
+
+static sa_command_t commands[] = {
+ {"get", 0, sc_get, USAGE_CTL_GET},
+ {"set", 0, sc_set, USAGE_CTL_SET},
+ {"status", 0, sc_status, USAGE_CTL_STATUS},
+ {NULL, 0, NULL, 0},
+};
+
+void
+sub_command_help(char *proto)
+{
+ int i;
+#ifdef lint
+ proto = proto;
+#endif
+
+ (void) printf("\tsub-commands:\n");
+ for (i = 0; commands[i].cmdname != NULL; i++) {
+ if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
+ (void) printf("\t%s\n",
+ sc_get_usage((sc_usage_t)commands[i].cmdidx));
+ }
+}
+
+sa_command_t *
+sa_lookup(char *cmd)
+{
+ int i;
+ size_t len;
+
+ len = strlen(cmd);
+ for (i = 0; commands[i].cmdname != NULL; i++) {
+ if (strncmp(cmd, commands[i].cmdname, len) == 0)
+ return (&commands[i]);
+ }
+ return (NULL);
+}
+
+static int
+run_command(char *command, int argc, char *argv[])
+{
+ sa_command_t *cmdvec;
+ int ret;
+
+ /*
+ * To get here, we know there should be a command due to the
+ * preprocessing done earlier. Need to find the protocol
+ * that is being affected. If no protocol, then it is ALL
+ * protocols.
+ *
+ * ??? do we really need the protocol at this level? it may be
+ * sufficient to let the commands look it up if needed since
+ * not all commands do proto specific things
+ *
+ * Known sub-commands are handled at this level. An unknown
+ * command will be passed down to the shared object that
+ * actually implements it. We can do this since the semantics
+ * of the common sub-commands is well defined.
+ */
+
+ cmdvec = sa_lookup(command);
+ if (cmdvec == NULL) {
+ (void) printf(gettext("command %s not found\n"), command);
+ exit(1);
+ }
+ /*
+ * need to check priviledges and restrict what can be done
+ * based on least priviledge and sub-command.
+ */
+ ret = cmdvec->cmdfunc(NULL, argc, argv);
+ return (ret);
+}
diff --git a/usr/src/cmd/dfs.cmds/sharectl/sparc/Makefile b/usr/src/cmd/dfs.cmds/sharectl/sparc/Makefile
new file mode 100644
index 0000000000..9201a4391d
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharectl/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/Makefile b/usr/src/cmd/dfs.cmds/sharemgr/Makefile
new file mode 100644
index 0000000000..e7a9a7f920
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/Makefile
@@ -0,0 +1,57 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+DEFAULTFILES =
+
+MANIFEST = group.xml
+
+include ../../Makefile.cmd
+
+#
+# One for each ISA.
+#
+SUBDIRS = $(MACH) plugins
+
+ROOTMANIFESTDIR = $(ROOTSVCNETWORKSHARES)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+_msg := TARGET= _msg
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+all clean clobber lint _msg: $(SUBDIRS)
+
+install: $(SUBDIRS) $(ROOTETCDEFAULTFILES) $(ROOTMANIFEST)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/Makefile.com b/usr/src/cmd/dfs.cmds/sharemgr/Makefile.com
new file mode 100644
index 0000000000..3b62040ae3
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/Makefile.com
@@ -0,0 +1,104 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../../../Makefile.cmd
+
+COMMON = ..
+
+PROG= sharemgr
+
+LINK_SRCS = libshare_nfs.c
+LINK_OBJS = libshare_nfs.o
+LINK_MODS = libshare_nfs.so
+
+SHAREMGR_MOD = sharemgr
+
+COMMONSRC = sharemgr_main.c commands.c shareutil.c
+
+SHAREMGR_SRC = $(COMMONSRC:%=$(COMMON)/%)
+
+SHAREMGR_OBJ = $(COMMONSRC:%.c=%.o)
+
+ROOTLINKS = $(ROOTUSRSBIN)/share $(ROOTUSRSBIN)/unshare
+
+MYCPPFLAGS = -I../../../../lib/libfsmgt/common -I/usr/include/libxml2 \
+ -I../..
+CPPFLAGS += $(MYCPPFLAGS)
+LDLIBS += -lshare -lxml2 -lscf -lsecdb -lumem
+LINTFLAGS += -u
+
+SRCS = $(SHAREMGR_SRC)
+OBJS = $(SHAREMGR_OBJ)
+MODS = $(SHAREMGR_MOD)
+
+CLOBBERFILES = $(MODS)
+
+POFILES = $(SHAREMGR_SRC:.c=.po)
+POFILE = sharemgr.po
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg:= TARGET= catalog
+
+.KEEP_STATE:
+
+all: $(MODS)
+
+catalog: $(POFILE)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG) $(ROOTLINKS)
+
+$(ROOTLINKS): $(ROOTUSRSBINPROG)
+ $(RM) $@
+ $(LN) $(ROOTUSRSBINPROG) $@
+
+lint: $(SHAREMGR_MOD).ln $(SHAREMGR_SRC:.c=.ln)
+
+clean:
+ $(RM) $(OBJS)
+
+check: $(CHKMANIFEST)
+
+%.ln: FRC
+ $(LINT.c) $(SHAREMGR_SRC) $(LDLIBS)
+
+include ../../../Makefile.targ
+
+$(POFILE): $(POFILES)
+ $(RM) $@; cat $(POFILES) > $@
+
+%.o: $(COMMON)/%.c
+ $(COMPILE.c) -o $@ $<
+
+FRC:
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/commands.c b/usr/src/cmd/dfs.cmds/sharemgr/commands.c
new file mode 100644
index 0000000000..33a4a8eb25
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/commands.c
@@ -0,0 +1,4011 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <utmpx.h>
+#include <pwd.h>
+#include <auth_attr.h>
+#include <secdb.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <libshare.h>
+#include "sharemgr.h"
+#include <libscf.h>
+#include <libxml/tree.h>
+#include <libintl.h>
+
+static char *sa_get_usage(sa_usage_t);
+
+/*
+ * Implementation of the common sub-commands supported by sharemgr.
+ * A number of helper functions are also included.
+ */
+
+/*
+ * has_protocol(group, proto)
+ * If the group has an optionset with the specified protocol,
+ * return true (1) otherwise false (0).
+ */
+static int
+has_protocol(sa_group_t group, char *protocol)
+{
+ sa_optionset_t optionset;
+ int result = 0;
+
+ optionset = sa_get_optionset(group, protocol);
+ if (optionset != NULL) {
+ result++;
+ }
+ return (result);
+}
+
+/*
+ * add_list(list, item)
+ * Adds a new list member that points to item to the list.
+ * If list is NULL, it starts a new list. The function returns
+ * the first member of the list.
+ */
+struct list *
+add_list(struct list *listp, void *item, void *data)
+{
+ struct list *new, *tmp;
+
+ new = malloc(sizeof (struct list));
+ if (new != NULL) {
+ new->next = NULL;
+ new->item = item;
+ new->itemdata = data;
+ } else {
+ return (listp);
+ }
+
+ if (listp == NULL)
+ return (new);
+
+ for (tmp = listp; tmp->next != NULL; tmp = tmp->next) {
+ /* get to end of list */
+ }
+ tmp->next = new;
+ return (listp);
+}
+
+/*
+ * free_list(list)
+ * Given a list, free all the members of the list;
+ */
+static void
+free_list(struct list *listp)
+{
+ struct list *tmp;
+ while (listp != NULL) {
+ tmp = listp;
+ listp = listp->next;
+ free(tmp);
+ }
+}
+
+/*
+ * check_authorization(instname, which)
+ *
+ * Checks to see if the specific type of authorization in which is
+ * enabled for the user in this SMF service instance.
+ */
+
+static int
+check_authorization(char *instname, int which)
+{
+ scf_handle_t *handle = NULL;
+ scf_simple_prop_t *prop = NULL;
+ char svcstring[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
+ char *authstr = NULL;
+ ssize_t numauths;
+ int ret = 1;
+ uid_t uid;
+ struct passwd *pw = NULL;
+
+ uid = getuid();
+ pw = getpwuid(uid);
+ if (pw == NULL)
+ ret = 0;
+
+ if (ret == 1) {
+ /* since names are restricted to SA_MAX_NAME_LEN won't overflow */
+ (void) snprintf(svcstring, sizeof (svcstring),
+ "%s:%s", SA_SVC_FMRI_BASE, instname);
+ handle = scf_handle_create(SCF_VERSION);
+ if (handle != NULL) {
+ if (scf_handle_bind(handle) == 0) {
+ switch (which) {
+ case SVC_SET:
+ prop = scf_simple_prop_get(handle, svcstring,
+ "general",
+ SVC_AUTH_VALUE);
+ break;
+ case SVC_ACTION:
+ prop = scf_simple_prop_get(handle, svcstring,
+ "general",
+ SVC_AUTH_ACTION);
+ break;
+ }
+ }
+ }
+ }
+ /* make sure we have an authorization string property */
+ if (prop != NULL) {
+ int i;
+ numauths = scf_simple_prop_numvalues(prop);
+ for (ret = 0, i = 0; i < numauths; i++) {
+ authstr = scf_simple_prop_next_astring(prop);
+ if (authstr != NULL) {
+ /* check if this user has one of the strings */
+ if (chkauthattr(authstr, pw->pw_name)) {
+ ret = 1;
+ break;
+ }
+ }
+ }
+ endauthattr();
+ scf_simple_prop_free(prop);
+ } else {
+ /* no authorization string defined */
+ ret = 0;
+ }
+ if (handle != NULL)
+ scf_handle_destroy(handle);
+ return (ret);
+}
+
+/*
+ * check_authorizations(instname, flags)
+ *
+ * check all the needed authorizations for the user in this service
+ * instance. Return value of 1(true) or 0(false) indicates whether
+ * there are authorizations for the user or not.
+ */
+
+static int
+check_authorizations(char *instname, int flags)
+{
+ int ret1 = 0;
+ int ret2 = 0;
+ int ret;
+
+ if (flags & SVC_SET)
+ ret1 = check_authorization(instname, SVC_SET);
+ if (flags & SVC_ACTION)
+ ret2 = check_authorization(instname, SVC_ACTION);
+ switch (flags) {
+ case SVC_ACTION:
+ ret = ret2;
+ break;
+ case SVC_SET:
+ ret = ret1;
+ break;
+ case SVC_ACTION|SVC_SET:
+ ret = ret1 & ret2;
+ break;
+ default:
+ /* if not flags set, we assume we don't need authorizations */
+ ret = 1;
+ }
+ return (ret);
+}
+
+/*
+ * enable_all_groups(list, setstate, online, update)
+ * Given a list of groups, enable each one found. If update is
+ * not NULL, then update all the shares for the protocol that was
+ * passed in.
+ */
+static int
+enable_all_groups(struct list *work, int setstate, int online, char *update)
+{
+ sa_share_t share;
+ int ret = SA_OK;
+ char instance[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
+ char *state;
+ char *name;
+ char *zfs = NULL;
+ int dozfs = 0;
+ sa_group_t group;
+
+ while (work != NULL && ret == SA_OK) {
+ group = (sa_group_t)work->item;
+ /* if itemdata != NULL then a single share */
+ if (work->itemdata != NULL) {
+ ret = sa_enable_share((sa_share_t)work->itemdata, NULL);
+ }
+ if (setstate)
+ ret = sa_set_group_attr(group, "state",
+ "enabled");
+ if (ret == SA_OK) {
+ /* if itemdata == NULL then the whole group */
+ if (work->itemdata == NULL) {
+ for (share = sa_get_share(group, NULL);
+ share != NULL; share = sa_get_next_share(share)) {
+ if (update != NULL)
+ (void) sa_update_legacy(share, update);
+ ret = sa_enable_share(share, NULL);
+ }
+ }
+ if (online) {
+ name = sa_get_group_attr(group, "name");
+ zfs = sa_get_group_attr(group, "zfs");
+ if (name != NULL) {
+ if (zfs == NULL) {
+ (void) snprintf(instance, sizeof (instance),
+ "%s:%s",
+ SA_SVC_FMRI_BASE, name);
+ state = smf_get_state(instance);
+ if (state == NULL ||
+ strcmp(state, "online") != 0) {
+ (void) smf_enable_instance(instance, 0);
+ free(state);
+ }
+ } else {
+ dozfs++;
+ sa_free_attr_string(zfs);
+ zfs = NULL;
+ }
+ if (name != NULL)
+ sa_free_attr_string(name);
+ }
+ } else {
+ zfs = sa_get_group_attr(group, "zfs");
+ if (zfs != NULL) {
+ dozfs++;
+ sa_free_attr_string(zfs);
+ zfs = NULL;
+ }
+ }
+ work = work->next;
+ }
+ }
+ if (ret == SA_OK) {
+ ret = sa_update_config();
+ }
+ /* do ZFS last to allow everything to get updated */
+ if (ret == SA_OK && dozfs) {
+ FILE *sys;
+ int err;
+ sys = popen(ZFS_SHAREALL, "r");
+ if (sys != NULL) {
+ err = pclose(sys);
+ if (err != 0)
+ ret = SA_SYSTEM_ERR;
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * chk_opt(optlistp, security, proto)
+ *
+ * Do a sanity check on the optlist provided for the protocol. This
+ * is a syntax check and verification that the property is either a
+ * general or specific to a names optionset.
+ */
+
+static int
+chk_opt(struct options *optlistp, int security, char *proto)
+{
+ struct options *optlist;
+ char *sep = "";
+ int notfirst = 0;
+ int ret;
+
+ for (optlist = optlistp; optlist != NULL; optlist = optlist->next) {
+ char *optname;
+
+ optname = optlist->optname;
+ ret = OPT_ADD_OK;
+ /* extract property/value pair */
+ if (sa_is_security(optname, proto)) {
+ if (!security)
+ ret = OPT_ADD_SECURITY;
+ } else {
+ if (security)
+ ret = OPT_ADD_PROPERTY;
+ }
+ if (ret != OPT_ADD_OK) {
+ if (notfirst == 0)
+ (void) printf(gettext("Property syntax error: "));
+ switch (ret) {
+ case OPT_ADD_SYNTAX:
+ (void) printf(gettext("%ssyntax error: %s"),
+ sep, optname);
+ sep = ", ";
+ break;
+ case OPT_ADD_SECURITY:
+ (void) printf(gettext("%s%s requires -S"),
+ optname, sep);
+ sep = ", ";
+ break;
+ case OPT_ADD_PROPERTY:
+ (void) printf(gettext("%s%s not supported with -S"),
+ optname, sep);
+ sep = ", ";
+ break;
+ }
+ notfirst++;
+ }
+ }
+ if (notfirst) {
+ (void) printf("\n");
+ ret = SA_SYNTAX_ERR;
+ }
+ return (ret);
+}
+
+/*
+ * free_opt(optlist)
+ * Free the specified option list.
+ */
+static void
+free_opt(struct options *optlist)
+{
+ struct options *nextopt;
+ while (optlist != NULL) {
+ nextopt = optlist->next;
+ free(optlist);
+ optlist = nextopt;
+ }
+}
+
+/*
+ * check property list for valid properties
+ * A null value is a remove which is always valid.
+ */
+static int
+valid_options(struct options *optlist, char *proto, void *object, char *sec)
+{
+ int ret = SA_OK;
+ struct options *cur;
+ sa_property_t prop;
+ sa_optionset_t parent = NULL;
+
+ if (object != NULL) {
+ if (sec == NULL)
+ parent = sa_get_optionset(object, proto);
+ else
+ parent = sa_get_security(object, sec, proto);
+ }
+
+ for (cur = optlist; cur != NULL; cur = cur->next) {
+ if (cur->optvalue != NULL) {
+ prop = sa_create_property(cur->optname, cur->optvalue);
+ if (prop == NULL)
+ ret = SA_NO_MEMORY;
+ if (ret != SA_OK ||
+ (ret = sa_valid_property(parent, proto, prop)) != SA_OK) {
+ (void) printf(gettext("Could not add property %s: %s\n"),
+ cur->optname,
+ sa_errorstr(ret));
+ }
+ (void) sa_remove_property(prop);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * add_optionset(group, optlist, protocol, *err)
+ * Add the options in optlist to an optionset and then add the optionset
+ * to the group.
+ *
+ * The return value indicates if there was a "change" while errors are
+ * returned via the *err parameters.
+ */
+static int
+add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err)
+{
+ sa_optionset_t optionset;
+ int ret = SA_OK;
+ int result = 0;
+
+ optionset = sa_get_optionset(group, proto);
+ if (optionset == NULL) {
+ optionset = sa_create_optionset(group, proto);
+ result = 1; /* adding a protocol is a change */
+ }
+ if (optionset != NULL) {
+ while (optlist != NULL) {
+ sa_property_t prop;
+ prop = sa_get_property(optionset, optlist->optname);
+ if (prop == NULL) {
+ /*
+ * add the property, but only if it is
+ * a non-NULL or non-zero length value
+ */
+ if (optlist->optvalue != NULL) {
+ prop = sa_create_property(optlist->optname,
+ optlist->optvalue);
+ if (prop != NULL) {
+ ret = sa_valid_property(optionset, proto, prop);
+ if (ret != SA_OK) {
+ (void) sa_remove_property(prop);
+ (void) printf(gettext("Could not add property "
+ "%s: %s\n"),
+ optlist->optname,
+ sa_errorstr(ret));
+ }
+ }
+ if (ret == SA_OK) {
+ ret = sa_add_property(optionset, prop);
+ if (ret != SA_OK) {
+ (void) printf(gettext("Could not add property"
+ " %s: %s\n"),
+ optlist->optname,
+ sa_errorstr(ret));
+ } else {
+ /* there was a change */
+ result = 1;
+ }
+ }
+ }
+ } else {
+ ret = sa_update_property(prop, optlist->optvalue);
+ /* should check to see if value changed */
+ if (ret != SA_OK) {
+ (void) printf(gettext("Could not update "
+ "property %s: %s\n"),
+ optlist->optname,
+ sa_errorstr(ret));
+ } else {
+ result = 1;
+ }
+ }
+ optlist = optlist->next;
+ }
+ ret = sa_commit_properties(optionset, 0);
+ }
+ if (err != NULL)
+ *err = ret;
+ return (result);
+}
+
+/*
+ * sa_create(flags, argc, argv)
+ * create a new group
+ * this may or may not have a protocol associated with it.
+ * No protocol means "all" protocols in this case.
+ */
+static int
+sa_create(int flags, int argc, char *argv[])
+{
+ char *groupname;
+
+ sa_group_t group;
+ int verbose = 0;
+ int dryrun = 0;
+ int c;
+ char *protocol = NULL;
+ int ret = SA_OK;
+ struct options *optlist = NULL;
+ int err = 0;
+ int auth;
+
+ while ((c = getopt(argc, argv, "?hvnP:p:")) != EOF) {
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+ case 'n':
+ dryrun++;
+ break;
+ case 'P':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ (void) printf(gettext("Invalid protocol specified: %s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ break;
+ case 'p':
+ ret = add_opt(&optlist, optarg, 0);
+ switch (ret) {
+ case OPT_ADD_SYNTAX:
+ (void) printf(gettext("Property syntax error for "
+ "property: %s\n"),
+ optarg);
+ return (SA_SYNTAX_ERR);
+ case OPT_ADD_SECURITY:
+ (void) printf(gettext("Security properties need "
+ "to be set with set-security: %s\n"),
+ optarg);
+ return (SA_SYNTAX_ERR);
+ default:
+ break;
+ }
+
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_CREATE));
+ return (0);
+ }
+ }
+
+ if (optind >= argc) {
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_CREATE));
+ (void) printf(gettext("\tgroup must be specified.\n"));
+ return (SA_BAD_PATH);
+ }
+
+ if ((optind + 1) < argc) {
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_CREATE));
+ (void) printf(gettext("\textraneous group(s) at end\n"));
+ return (SA_SYNTAX_ERR);
+ }
+
+ if (protocol == NULL && optlist != NULL) {
+ /* lookup default protocol */
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_CREATE));
+ (void) printf(gettext("\tprotocol must be specified "
+ "with properties\n"));
+ return (SA_INVALID_PROTOCOL);
+ }
+
+ if (optlist != NULL)
+ ret = chk_opt(optlist, 0, protocol);
+ if (ret == OPT_ADD_SECURITY) {
+ (void) printf(gettext("Security properties not "
+ "supported with create\n"));
+ return (SA_SYNTAX_ERR);
+ }
+
+ /*
+ * if a group already exists, we can only add a new protocol
+ * to it and not create a new one or add the same protocol
+ * again.
+ */
+
+ groupname = argv[optind];
+
+ auth = check_authorizations(groupname, flags);
+
+ group = sa_get_group(groupname);
+ if (group != NULL) {
+ /* group exists so must be a protocol add */
+ if (protocol != NULL) {
+ if (has_protocol(group, protocol)) {
+ (void) printf(gettext("Group \"%s\" already exists"
+ " with protocol %s\n"),
+ groupname, protocol);
+ ret = SA_DUPLICATE_NAME;
+ }
+ } else {
+ /* must add new protocol */
+ (void) printf(gettext("Group already exists and no protocol"
+ " specified.\n"));
+ ret = SA_DUPLICATE_NAME;
+ }
+ } else {
+ /*
+ * is it a valid name? Must comply with SMF instance
+ * name restrictions.
+ */
+ if (!sa_valid_group_name(groupname)) {
+ ret = SA_INVALID_NAME;
+ (void) printf(gettext("Invalid group name: %s\n"), groupname);
+ }
+ }
+ if (ret == SA_OK) {
+ /* check protocol vs optlist */
+ if (optlist != NULL) {
+ /* check options, if any, for validity */
+ ret = valid_options(optlist, protocol, group, NULL);
+ }
+ }
+ if (ret == SA_OK && !dryrun) {
+ if (group == NULL) {
+ group = sa_create_group((char *)groupname, &err);
+ }
+ if (group != NULL) {
+ sa_optionset_t optionset;
+ if (optlist != NULL) {
+ (void) add_optionset(group, optlist, protocol, &ret);
+ } else if (protocol != NULL) {
+ optionset = sa_create_optionset(group, protocol);
+ if (optionset == NULL)
+ ret = SA_NO_MEMORY;
+ } else if (protocol == NULL) {
+ char **protolist;
+ int numprotos, i;
+ numprotos = sa_get_protocols(&protolist);
+ for (i = 0; i < numprotos; i++) {
+ optionset = sa_create_optionset(group, protolist[i]);
+ }
+ if (protolist != NULL)
+ free(protolist);
+ }
+ /*
+ * we have a group and legal additions
+ */
+ if (ret == SA_OK) {
+ /*
+ * commit to configuration for protocols that
+ * need to do block updates. For NFS, this
+ * doesn't do anything but it will be run for
+ * all protocols that implement the
+ * appropriate plugin.
+ */
+ ret = sa_update_config();
+ } else {
+ if (group != NULL)
+ (void) sa_remove_group(group);
+ }
+ } else {
+ ret = err;
+ (void) printf(gettext("Could not create group: %s\n"),
+ sa_errorstr(ret));
+ }
+ }
+ if (dryrun && ret == SA_OK && !auth && verbose) {
+ (void) printf(gettext("Command would fail: %s\n"),
+ sa_errorstr(SA_NO_PERMISSION));
+ ret = SA_NO_PERMISSION;
+ }
+ free_opt(optlist);
+ return (ret);
+}
+
+/*
+ * group_status(group)
+ *
+ * return the current status (enabled/disabled) of the group.
+ */
+
+static char *
+group_status(sa_group_t group)
+{
+ char *state;
+ int enabled = 0;
+
+ state = sa_get_group_attr(group, "state");
+ if (state != NULL) {
+ if (strcmp(state, "enabled") == 0) {
+ enabled = 1;
+ }
+ sa_free_attr_string(state);
+ }
+ return (enabled ? gettext("enabled") : gettext("disabled"));
+}
+
+/*
+ * sa_delete(flags, argc, argv)
+ *
+ * Delete a group.
+ */
+
+static int
+sa_delete(int flags, int argc, char *argv[])
+{
+ char *groupname;
+ sa_group_t group;
+ sa_share_t share;
+ int verbose = 0;
+ int dryrun = 0;
+ int force = 0;
+ int c;
+ char *protocol = NULL;
+ char *sectype = NULL;
+ int ret = SA_OK;
+ int auth;
+
+ while ((c = getopt(argc, argv, "?hvnP:fS:")) != EOF) {
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+ case 'n':
+ dryrun++;
+ break;
+ case 'P':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ (void) printf(gettext("Invalid protocol specified: %s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ break;
+ case 'S':
+ sectype = optarg;
+ break;
+ case 'f':
+ force++;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_DELETE));
+ return (0);
+ }
+ }
+
+ if (optind >= argc) {
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_DELETE));
+ (void) printf(gettext("\tgroup must be specified.\n"));
+ return (SA_SYNTAX_ERR);
+ }
+
+ if ((optind + 1) < argc) {
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_DELETE));
+ (void) printf(gettext("\textraneous group(s) at end\n"));
+ return (SA_SYNTAX_ERR);
+ }
+
+ if (sectype != NULL && protocol == NULL) {
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_DELETE));
+ (void) printf(gettext("\tsecurity requires protocol to be "
+ "specified.\n"));
+ return (SA_SYNTAX_ERR);
+ }
+
+ /*
+ * Determine if the group already exists since it must in
+ * order to be removed.
+ *
+ * We can delete when:
+ *
+ * - group is empty
+ * - force flag is set
+ * - if protocol specified, only delete the protocol
+ */
+
+ groupname = argv[optind];
+ group = sa_get_group(groupname);
+ if (group == NULL) {
+ ret = SA_NO_SUCH_GROUP;
+ } else {
+ auth = check_authorizations(groupname, flags);
+ if (protocol == NULL) {
+ share = sa_get_share(group, NULL);
+ if (share != NULL)
+ ret = SA_BUSY;
+ if (share == NULL || (share != NULL && force == 1)) {
+ ret = SA_OK;
+ if (!dryrun) {
+ while (share != NULL) {
+ sa_share_t next_share;
+ next_share = sa_get_next_share(share);
+ /*
+ * need to do the disable of each
+ * share, but don't actually do
+ * anything on a dryrun.
+ */
+ ret = sa_disable_share(share, NULL);
+ ret = sa_remove_share(share);
+ share = next_share;
+ }
+ ret = sa_remove_group(group);
+ }
+ }
+ /* commit to configuration if not a dryrun */
+ if (!dryrun && ret == SA_OK) {
+ ret = sa_update_config();
+ }
+ } else {
+ /* a protocol delete */
+ sa_optionset_t optionset;
+ sa_security_t security;
+ if (sectype != NULL) {
+ /* only delete specified security */
+ security = sa_get_security(group, sectype, protocol);
+ if (security != NULL && !dryrun) {
+ ret = sa_destroy_security(security);
+ } else {
+ ret = SA_INVALID_PROTOCOL;
+ }
+ } else {
+ optionset = sa_get_optionset(group, protocol);
+ if (optionset != NULL && !dryrun) {
+ /* have an optionset with protocol to delete */
+ ret = sa_destroy_optionset(optionset);
+ /*
+ * now find all security sets for the protocol
+ * and remove them. Don't remove other
+ * protocols.
+ */
+ for (security = sa_get_security(group, NULL, NULL);
+ ret == SA_OK && security != NULL;
+ security = sa_get_next_security(security)) {
+ char *secprot;
+
+ secprot = sa_get_security_attr(security, "type");
+ if (secprot != NULL &&
+ strcmp(secprot, protocol) == 0)
+ ret = sa_destroy_security(security);
+ if (secprot != NULL)
+ sa_free_attr_string(secprot);
+ }
+ } else {
+ if (!dryrun)
+ ret = SA_INVALID_PROTOCOL;
+ }
+ }
+ }
+ }
+ if (ret != SA_OK) {
+ (void) printf(gettext("Could not delete group: %s\n"),
+ sa_errorstr(ret));
+ } else if (dryrun && !auth && verbose) {
+ (void) printf(gettext("Command would fail: %s\n"),
+ sa_errorstr(SA_NO_PERMISSION));
+ }
+ return (ret);
+}
+
+/*
+ * strndupr(*buff, str, buffsize)
+ *
+ * used with small strings to duplicate and possibly increase the
+ * buffer size of a string.
+ */
+static char *
+strndupr(char *buff, char *str, int *buffsize)
+{
+ int limit;
+ char *orig_buff = buff;
+
+ if (buff == NULL) {
+ buff = (char *)malloc(64);
+ if (buff == NULL)
+ return (NULL);
+ *buffsize = 64;
+ buff[0] = '\0';
+ }
+ limit = strlen(buff) + strlen(str) + 1;
+ if (limit > *buffsize) {
+ limit = *buffsize = *buffsize + ((limit / 64) + 64);
+ buff = realloc(buff, limit);
+ }
+ if (buff != NULL) {
+ (void) strcat(buff, str);
+ } else {
+ /* if it fails, fail it hard */
+ if (orig_buff != NULL)
+ free(orig_buff);
+ }
+ return (buff);
+}
+
+/*
+ * group_proto(group)
+ *
+ * return a string of all the protocols (space separated) associated
+ * with this group.
+ */
+
+static char *
+group_proto(sa_group_t group)
+{
+ sa_optionset_t optionset;
+ char *proto;
+ char *buff = NULL;
+ int buffsize = 0;
+ int addspace = 0;
+ /*
+ * get the protocol list by finding the optionsets on this
+ * group and extracting the type value. The initial call to
+ * strndupr() initailizes buff.
+ */
+ buff = strndupr(buff, "", &buffsize);
+ if (buff != NULL) {
+ for (optionset = sa_get_optionset(group, NULL);
+ optionset != NULL && buff != NULL;
+ optionset = sa_get_next_optionset(optionset)) {
+ /*
+ * extract out the protocol type from this optionset
+ * and append it to the buffer "buff". strndupr() will
+ * reallocate space as necessay.
+ */
+ proto = sa_get_optionset_attr(optionset, "type");
+ if (proto != NULL) {
+ if (addspace++)
+ buff = strndupr(buff, " ", &buffsize);
+ buff = strndupr(buff, proto, &buffsize);
+ sa_free_attr_string(proto);
+ }
+ }
+ }
+ return (buff);
+}
+
+/*
+ * sa_list(flags, argc, argv)
+ *
+ * implements the "list" subcommand to list groups and optionally
+ * their state and protocols.
+ */
+
+static int
+sa_list(int flags, int argc, char *argv[])
+{
+ sa_group_t group;
+ int verbose = 0;
+ int c;
+ char *protocol = NULL;
+#ifdef lint
+ flags = flags;
+#endif
+
+ while ((c = getopt(argc, argv, "?hvP:")) != EOF) {
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+ case 'P':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ (void) printf(gettext("Invalid protocol specified:"
+ "%s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_LIST));
+ return (0);
+ }
+ }
+
+ for (group = sa_get_group(NULL); group != NULL;
+ group = sa_get_next_group(group)) {
+ char *name;
+ char *proto;
+ if (protocol == NULL || has_protocol(group, protocol)) {
+ name = sa_get_group_attr(group, "name");
+ if (name != NULL && (verbose > 1 || name[0] != '#')) {
+ (void) printf("%s", (char *)name);
+ if (verbose) {
+ /*
+ * need the list of protocols
+ * and current status once
+ * available.
+ */
+ (void) printf("\t%s", group_status(group));
+ proto = group_proto(group);
+ if (proto != NULL) {
+ (void) printf("\t%s", (char *)proto);
+ free(proto);
+ }
+ }
+ (void) printf("\n");
+ }
+ if (name != NULL)
+ sa_free_attr_string(name);
+ }
+ }
+ return (0);
+}
+
+/*
+ * out_properties(optionset, proto, sec)
+ *
+ * Format the properties and encode the protocol and optional named
+ * optionset into the string.
+ *
+ * format is protocol[:name]=(property-list)
+ */
+
+static void
+out_properties(sa_optionset_t optionset, char *proto, char *sec)
+{
+ char *type;
+ char *value;
+ int spacer;
+ sa_property_t prop;
+
+ if (sec == NULL) {
+ (void) printf(" %s=(", proto ? proto : gettext("all"));
+ } else {
+ (void) printf(" %s:%s=(", proto ? proto : gettext("all"), sec);
+ }
+
+ for (spacer = 0, prop = sa_get_property(optionset, NULL);
+ prop != NULL; prop = sa_get_next_property(prop)) {
+
+ /*
+ * extract the property name/value and output with
+ * appropriate spacing. I.e. no prefixed space the
+ * first time through but a space on subsequent
+ * properties.
+ */
+ type = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (type != NULL) {
+ (void) printf("%s%s=", spacer ? " " : "", type);
+ spacer = 1;
+ if (value != NULL)
+ (void) printf("\"%s\"", value);
+ else
+ (void) printf("\"\"");
+ }
+ if (type != NULL)
+ sa_free_attr_string(type);
+ if (value != NULL)
+ sa_free_attr_string(value);
+ }
+ (void) printf(")");
+}
+
+/*
+ * show_properties(group, protocol, prefix)
+ *
+ * print the properties for a group. If protocol is NULL, do all
+ * protocols otherwise only the specified protocol. All security
+ * (named groups specific to the protocol) are included.
+ *
+ * The "prefix" is always applied. The caller knows whether it wants
+ * some type of prefix string (white space) or not. Once the prefix
+ * has been output, it is reduced to the zero length string for the
+ * remainder of the property output.
+ */
+
+static void
+show_properties(sa_group_t group, char *protocol, char *prefix)
+{
+ sa_optionset_t optionset;
+ sa_security_t security;
+ char *value;
+ char *secvalue;
+
+ if (protocol != NULL) {
+ optionset = sa_get_optionset(group, protocol);
+ if (optionset != NULL) {
+ (void) printf("%s", prefix);
+ prefix = "";
+ out_properties(optionset, protocol, NULL);
+ }
+ security = sa_get_security(group, protocol, NULL);
+ if (security != NULL) {
+ (void) printf("%s", prefix);
+ prefix = "";
+ out_properties(security, protocol, NULL);
+ }
+ } else {
+ for (optionset = sa_get_optionset(group, protocol);
+ optionset != NULL;
+ optionset = sa_get_next_optionset(optionset)) {
+
+ value = sa_get_optionset_attr(optionset, "type");
+ (void) printf("%s", prefix);
+ prefix = "";
+ out_properties(optionset, value, 0);
+ if (value != NULL)
+ sa_free_attr_string(value);
+ }
+ for (security = sa_get_security(group, NULL, protocol);
+ security != NULL;
+ security = sa_get_next_security(security)) {
+
+ value = sa_get_security_attr(security, "type");
+ secvalue = sa_get_security_attr(security, "sectype");
+ (void) printf("%s", prefix);
+ prefix = "";
+ out_properties(security, value, secvalue);
+ if (value != NULL)
+ sa_free_attr_string(value);
+ if (secvalue != NULL)
+ sa_free_attr_string(secvalue);
+ }
+ }
+}
+
+/*
+ * show_group(group, verbose, properties, proto, subgroup)
+ *
+ * helper function to show the contents of a group.
+ */
+
+static void
+show_group(sa_group_t group, int verbose, int properties, char *proto,
+ char *subgroup)
+{
+ sa_share_t share;
+ char *groupname;
+ char *sharepath;
+ char *resource;
+ char *description;
+ char *type;
+ char *zfs = NULL;
+ int iszfs = 0;
+
+ groupname = sa_get_group_attr(group, "name");
+ if (groupname != NULL) {
+ if (proto != NULL && !has_protocol(group, proto)) {
+ sa_free_attr_string(groupname);
+ return;
+ }
+ /*
+ * check to see if the group is managed by ZFS. If
+ * there is an attribute, then it is. A non-NULL zfs
+ * variable will trigger the different way to display
+ * and will remove the transient property indicator
+ * from the output.
+ */
+ zfs = sa_get_group_attr(group, "zfs");
+ if (zfs != NULL) {
+ iszfs = 1;
+ sa_free_attr_string(zfs);
+ }
+ share = sa_get_share(group, NULL);
+ if (subgroup == NULL)
+ (void) printf("%s", groupname);
+ else
+ (void) printf(" %s/%s", subgroup, groupname);
+ if (properties) {
+ show_properties(group, proto, "");
+ }
+ (void) printf("\n");
+ if (strcmp(groupname, "zfs") == 0) {
+ sa_group_t zgroup;
+
+ for (zgroup = sa_get_sub_group(group); zgroup != NULL;
+ zgroup = sa_get_next_group(zgroup)) {
+ show_group(zgroup, verbose, properties, proto, "zfs");
+ }
+ sa_free_attr_string(groupname);
+ return;
+ }
+ /*
+ * have a group, so list the contents. Resource and
+ * description are only listed if verbose is set.
+ */
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ sharepath = sa_get_share_attr(share, "path");
+ if (sharepath != NULL) {
+ if (verbose) {
+ resource = sa_get_share_attr(share, "resource");
+ description = sa_get_share_description(share);
+ type = sa_get_share_attr(share, "type");
+ if (type != NULL && !iszfs &&
+ strcmp(type, "transient") == 0)
+ (void) printf("\t* ");
+ else
+ (void) printf("\t ");
+ if (resource != NULL && strlen(resource) > 0) {
+ (void) printf("%s=%s", resource, sharepath);
+ } else {
+ (void) printf("%s", sharepath);
+ }
+ if (resource != NULL)
+ sa_free_attr_string(resource);
+ if (properties)
+ show_properties(share, NULL, "\t");
+ if (description != NULL) {
+ if (strlen(description) > 0) {
+ (void) printf("\t\"%s\"", description);
+ }
+ sa_free_share_description(description);
+ }
+ if (type != NULL)
+ sa_free_attr_string(type);
+ } else {
+ (void) printf("\t%s", sharepath);
+ if (properties)
+ show_properties(share, NULL, "\t");
+ }
+ (void) printf("\n");
+ sa_free_attr_string(sharepath);
+ }
+ }
+ }
+ if (groupname != NULL) {
+ sa_free_attr_string(groupname);
+ }
+}
+
+/*
+ * show_group_xml_init()
+ *
+ * Create an XML document that will be used to display config info via
+ * XML format.
+ */
+
+xmlDocPtr
+show_group_xml_init()
+{
+ xmlDocPtr doc;
+ xmlNodePtr root;
+
+ doc = xmlNewDoc((xmlChar *)"1.0");
+ if (doc != NULL) {
+ root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
+ if (root != NULL)
+ xmlDocSetRootElement(doc, root);
+ }
+ return (doc);
+}
+
+/*
+ * show_group_xml(doc, group)
+ *
+ * Copy the group info into the XML doc.
+ */
+
+static void
+show_group_xml(xmlDocPtr doc, sa_group_t group)
+{
+ xmlNodePtr node;
+ xmlNodePtr root;
+
+ root = xmlDocGetRootElement(doc);
+ node = xmlCopyNode((xmlNodePtr)group, 1);
+ if (node != NULL && root != NULL) {
+ xmlAddChild(root, node);
+ /*
+ * In the future, we may have interally used tags that
+ * should not appear in the XML output. Remove
+ * anything we don't want to show here.
+ */
+ }
+}
+
+/*
+ * sa_show(flags, argc, argv)
+ *
+ * Implements the show subcommand.
+ */
+
+int
+sa_show(int flags, int argc, char *argv[])
+{
+ sa_group_t group;
+ int verbose = 0;
+ int properties = 0;
+ int c;
+ int ret = SA_OK;
+ char *protocol = NULL;
+ int xml = 0;
+ xmlDocPtr doc;
+#ifdef lint
+ flags = flags;
+#endif
+
+ while ((c = getopt(argc, argv, "?hvP:px")) != EOF) {
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+ case 'p':
+ properties++;
+ break;
+ case 'P':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ (void) printf(gettext("Invalid protocol specified: %s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ break;
+ case 'x':
+ xml++;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SHOW));
+ return (0);
+ }
+ }
+
+ if (xml) {
+ doc = show_group_xml_init();
+ if (doc == NULL)
+ ret = SA_NO_MEMORY;
+ }
+
+ if (optind == argc) {
+ /* no group specified so go through them all */
+ for (group = sa_get_group(NULL); group != NULL;
+ group = sa_get_next_group(group)) {
+ /*
+ * have a group so check if one we want and then list
+ * contents with appropriate options.
+ */
+ if (xml)
+ show_group_xml(doc, group);
+ else
+ show_group(group, verbose, properties, protocol, NULL);
+ }
+ } else {
+ /* have a specified list of groups */
+ for (; optind < argc; optind++) {
+ group = sa_get_group(argv[optind]);
+ if (group != NULL) {
+ if (xml)
+ show_group_xml(doc, group);
+ else
+ show_group(group, verbose, properties, protocol, NULL);
+ } else {
+ (void) printf(gettext("%s: not found\n"), argv[optind]);
+ ret = SA_NO_SUCH_GROUP;
+ }
+ }
+ }
+ if (xml && ret == SA_OK) {
+ xmlDocFormatDump(stdout, doc, 1);
+ xmlFreeDoc(doc);
+ }
+ return (ret);
+
+}
+
+/*
+ * enable_share(group, share, update_legacy)
+ *
+ * helper function to enable a share if the group is enabled.
+ */
+
+static int
+enable_share(sa_group_t group, sa_share_t share, int update_legacy)
+{
+ char *value;
+ int enabled;
+ sa_optionset_t optionset;
+ int ret = SA_OK;
+ char *zfs = NULL;
+ int iszfs = 0;
+
+ /*
+ * need to enable this share if the group is enabled but not
+ * otherwise. The enable is also done on each protocol
+ * represented in the group.
+ */
+ value = sa_get_group_attr(group, "state");
+ enabled = value != NULL && strcmp(value, "enabled") == 0;
+ if (value != NULL)
+ sa_free_attr_string(value);
+ /* remove legacy config if necessary */
+ if (update_legacy)
+ ret = sa_delete_legacy(share);
+ zfs = sa_get_group_attr(group, "zfs");
+ if (zfs != NULL) {
+ iszfs++;
+ sa_free_attr_string(zfs);
+ }
+
+ /*
+ * Step through each optionset at the group level and
+ * enable the share based on the protocol type. This
+ * works because protocols must be set on the group
+ * for the protocol to be enabled.
+ */
+ for (optionset = sa_get_optionset(group, NULL);
+ optionset != NULL && ret == SA_OK;
+ optionset = sa_get_next_optionset(optionset)) {
+ value = sa_get_optionset_attr(optionset, "type");
+ if (value != NULL) {
+ if (enabled)
+ ret = sa_enable_share(share, value);
+ if (update_legacy && !iszfs)
+ (void) sa_update_legacy(share, value);
+ sa_free_attr_string(value);
+ }
+ }
+ if (ret == SA_OK)
+ (void) sa_update_config();
+ return (ret);
+}
+
+/*
+ * sa_addshare(flags, argc, argv)
+ *
+ * implements add-share subcommand.
+ */
+
+int
+sa_addshare(int flags, int argc, char *argv[])
+{
+ int verbose = 0;
+ int dryrun = 0;
+ int c;
+ int ret = SA_OK;
+ sa_group_t group;
+ sa_share_t share;
+ char *sharepath = NULL;
+ char *description = NULL;
+ char *resource = NULL;
+ int persist = SA_SHARE_PERMANENT; /* default to persist */
+ int auth;
+ char dir[MAXPATHLEN];
+
+ while ((c = getopt(argc, argv, "?hvns:d:r:t")) != EOF) {
+ switch (c) {
+ case 'n':
+ dryrun++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'd':
+ description = optarg;
+ break;
+ case 'r':
+ resource = optarg;
+ break;
+ case 's':
+ /*
+ * save share path into group. Currently limit
+ * to one share per command.
+ */
+ if (sharepath != NULL) {
+ (void) printf(gettext("Adding multiple shares not"
+ "supported\n"));
+ return (1);
+ }
+ sharepath = optarg;
+ break;
+ case 't':
+ persist = SA_SHARE_TRANSIENT;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_ADD_SHARE));
+ return (0);
+ }
+ }
+
+ if (optind >= argc) {
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_ADD_SHARE));
+ if (dryrun || sharepath != NULL || description != NULL ||
+ resource != NULL || verbose || persist) {
+ (void) printf(gettext("\tgroup must be specified\n"));
+ ret = SA_NO_SUCH_GROUP;
+ } else {
+ ret = SA_OK;
+ }
+ } else {
+ if (sharepath == NULL) {
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_ADD_SHARE));
+ (void) printf(gettext("\t-s sharepath must be specified\n"));
+ ret = SA_BAD_PATH;
+ }
+ if (ret == SA_OK) {
+ if (realpath(sharepath, dir) == NULL) {
+ ret = SA_BAD_PATH;
+ (void) printf(gettext("Path is not valid: %s\n"),
+ sharepath);
+ } else {
+ sharepath = dir;
+ }
+ }
+ if (ret == SA_OK && resource != NULL) {
+ /* check for valid syntax */
+ if (strpbrk(resource, " \t/") != NULL) {
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_ADD_SHARE));
+ (void) printf(gettext("\tresource must not contain white"
+ "space or '/' characters\n"));
+ ret = SA_BAD_PATH;
+ }
+ }
+ if (ret == SA_OK) {
+ group = sa_get_group(argv[optind]);
+ if (group != NULL) {
+ auth = check_authorizations(argv[optind], flags);
+ share = sa_find_share(sharepath);
+ if (share != NULL) {
+ group = sa_get_parent_group(share);
+ if (group != NULL) {
+ char *groupname;
+ groupname = sa_get_group_attr(group, "name");
+ if (groupname != NULL) {
+ (void) printf(gettext("Share path already "
+ "shared in group "
+ "\"%s\": %s\n"),
+ groupname, sharepath);
+ sa_free_attr_string(groupname);
+ } else {
+ (void) printf(gettext("Share path already"
+ "shared: %s\n"),
+ groupname, sharepath);
+ }
+ } else {
+ (void) printf(gettext("Share path %s already "
+ "shared\n"),
+ sharepath);
+ }
+ ret = SA_DUPLICATE_NAME;
+ } else {
+ /*
+ * need to check that resource name is unique
+ * at some point.
+ */
+ if (dryrun)
+ ret = sa_check_path(group, sharepath);
+ else
+ share = sa_add_share(group, sharepath,
+ persist, &ret);
+ if (!dryrun && share == NULL) {
+ (void) printf(gettext("Could not add share: "
+ "%s\n"),
+ sa_errorstr(ret));
+ } else {
+ if (!dryrun && ret == SA_OK) {
+ if (resource != NULL) {
+ if (strpbrk(resource, " \t/") == NULL) {
+ ret = sa_set_share_attr(share,
+ "resource",
+ resource);
+ }
+ }
+ if (ret == SA_OK && description != NULL) {
+ ret = sa_set_share_description(share,
+ description);
+ }
+ if (ret == SA_OK) {
+ /* now enable the share(s) */
+ ret = enable_share(group, share, 1);
+ ret = sa_update_config();
+ }
+ switch (ret) {
+ case SA_DUPLICATE_NAME:
+ (void) printf(gettext("Resource name in"
+ "use: %s\n"),
+ resource);
+ break;
+ default:
+ (void) printf(gettext("Could not set "
+ "attribute: %s\n"),
+ sa_errorstr(ret));
+ break;
+ case SA_OK:
+ break;
+ }
+ } else if (dryrun && ret == SA_OK &&
+ !auth && verbose) {
+ (void) printf(gettext("Command would fail: "
+ "%s\n"),
+ sa_errorstr(SA_NO_PERMISSION));
+ ret = SA_NO_PERMISSION;
+ }
+ }
+ }
+ } else {
+ (void) printf(gettext("Group \"%s\" not found\n"),
+ argv[optind]);
+ ret = SA_NO_SUCH_GROUP;
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_moveshare(flags, argc, argv)
+ *
+ * implements move-share subcommand.
+ */
+
+int
+sa_moveshare(int flags, int argc, char *argv[])
+{
+ int verbose = 0;
+ int dryrun = 0;
+ int c;
+ int ret = SA_OK;
+ sa_group_t group;
+ sa_share_t share;
+ char *sharepath = NULL;
+ int authsrc = 0, authdst = 0;
+
+ while ((c = getopt(argc, argv, "?hvns:")) != EOF) {
+ switch (c) {
+ case 'n':
+ dryrun++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 's':
+ /*
+ * remove share path from group. Currently limit
+ * to one share per command.
+ */
+ if (sharepath != NULL) {
+ (void) printf(gettext("Moving multiple shares not"
+ "supported\n"));
+ return (SA_BAD_PATH);
+ }
+ sharepath = optarg;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_MOVE_SHARE));
+ return (0);
+ }
+ }
+
+ if (optind >= argc || sharepath == NULL) {
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_MOVE_SHARE));
+ if (dryrun || verbose || sharepath != NULL) {
+ (void) printf(gettext("\tgroup must be specified\n"));
+ ret = SA_NO_SUCH_GROUP;
+ } else {
+ if (sharepath == NULL) {
+ ret = SA_SYNTAX_ERR;
+ (void) printf(gettext("\tsharepath must be specified\n"));
+ } else
+ ret = SA_OK;
+ }
+ } else {
+ if (sharepath == NULL) {
+ (void) printf(gettext("sharepath must be specified with "
+ "the -s option\n"));
+ ret = SA_BAD_PATH;
+ } else {
+ group = sa_get_group(argv[optind]);
+ if (group != NULL) {
+ share = sa_find_share(sharepath);
+ authdst = check_authorizations(argv[optind], flags);
+ if (share == NULL) {
+ (void) printf(gettext("Share not found: %s\n"),
+ sharepath);
+ ret = SA_NO_SUCH_PATH;
+ } else {
+ sa_group_t parent;
+ char *zfsold;
+ char *zfsnew;
+
+ parent = sa_get_parent_group(share);
+ if (parent != NULL) {
+ char *pname;
+ pname = sa_get_group_attr(parent, "name");
+ if (pname != NULL) {
+ authsrc = check_authorizations(pname, flags);
+ sa_free_attr_string(pname);
+ }
+ zfsold = sa_get_group_attr(parent, "zfs");
+ zfsnew = sa_get_group_attr(group, "zfs");
+ if ((zfsold != NULL && zfsnew == NULL) ||
+ (zfsold == NULL && zfsnew != NULL)) {
+ ret = SA_NOT_ALLOWED;
+ }
+ if (zfsold != NULL)
+ sa_free_attr_string(zfsold);
+ if (zfsnew != NULL)
+ sa_free_attr_string(zfsnew);
+ }
+ if (!dryrun && ret == SA_OK) {
+ ret = sa_move_share(group, share);
+ }
+ if (ret == SA_OK && parent != group && !dryrun) {
+ char *oldstate;
+ ret = sa_update_config();
+ /*
+ * note that the share may need to be
+ * "unshared" if the new group is
+ * disabled and the old was enabled or
+ * it may need to be share to update
+ * if the new group is enabled.
+ */
+ oldstate = sa_get_group_attr(parent, "state");
+ /* enable_share determines what to do */
+ if (strcmp(oldstate, "enabled") == 0) {
+ (void) sa_disable_share(share, NULL);
+ }
+ (void) enable_share(group, share, 1);
+ if (oldstate != NULL)
+ sa_free_attr_string(oldstate);
+ }
+ if (ret != SA_OK) {
+ (void) printf(gettext("Could not move share: %s\n"),
+ sa_errorstr(ret));
+ }
+ if (dryrun && ret == SA_OK && !(authsrc & authdst) &&
+ verbose) {
+ (void) printf(gettext("Command would fail: %s\n"),
+ sa_errorstr(SA_NO_PERMISSION));
+ }
+ }
+ } else {
+ (void) printf(gettext("Group \"%s\" not found\n"),
+ argv[optind]);
+ ret = SA_NO_SUCH_GROUP;
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_removeshare(flags, argc, argv)
+ *
+ * implements remove-share subcommand.
+ */
+
+int
+sa_removeshare(int flags, int argc, char *argv[])
+{
+ int verbose = 0;
+ int dryrun = 0;
+ int force = 0;
+ int c;
+ int ret = SA_OK;
+ sa_group_t group;
+ sa_share_t share;
+ char *sharepath = NULL;
+ char dir[MAXPATHLEN];
+ int auth;
+
+ while ((c = getopt(argc, argv, "?hfns:v")) != EOF) {
+ switch (c) {
+ case 'n':
+ dryrun++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'f':
+ force++;
+ break;
+ case 's':
+ /*
+ * remove share path from group. Currently limit
+ * to one share per command.
+ */
+ if (sharepath != NULL) {
+ (void) printf(gettext("Removing multiple shares not"
+ "supported\n"));
+ return (SA_SYNTAX_ERR);
+ }
+ sharepath = optarg;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_REMOVE_SHARE));
+ return (0);
+ }
+ }
+
+ if (optind >= argc || sharepath == NULL) {
+ if (sharepath == NULL) {
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_REMOVE_SHARE));
+ (void) printf(gettext("\t-s sharepath must be specified\n"));
+ ret = SA_BAD_PATH;
+ } else {
+ ret = SA_OK;
+ }
+ }
+ if (ret == SA_OK) {
+ if (optind < argc) {
+ if ((optind + 1) < argc) {
+ (void) printf(gettext("Extraneous group(s) at end of "
+ "command\n"));
+ ret = SA_SYNTAX_ERR;
+ } else {
+ group = sa_get_group(argv[optind]);
+ if (group == NULL) {
+ (void) printf(gettext("Group \"%s\" not found\n"),
+ argv[optind]);
+ ret = SA_NO_SUCH_GROUP;
+ }
+ }
+ } else {
+ group = NULL;
+ }
+ if (ret == SA_OK) {
+ if (realpath(sharepath, dir) == NULL) {
+ ret = SA_BAD_PATH;
+ (void) printf(gettext("Path is not valid: %s\n"),
+ sharepath);
+ } else {
+ sharepath = dir;
+ }
+ }
+ if (ret == SA_OK) {
+ if (group != NULL)
+ share = sa_get_share(group, sharepath);
+ else
+ share = sa_find_share(sharepath);
+ if (share == NULL) {
+ if (group != NULL)
+ (void) printf(gettext("Share not found in group %s:"
+ "%s\n"),
+ argv[optind], sharepath);
+ else
+ (void) printf(gettext("Share not found: %s\n"),
+ sharepath);
+ ret = SA_NO_SUCH_PATH;
+ } else {
+ if (group == NULL)
+ group = sa_get_parent_group(share);
+ if (!dryrun) {
+ if (ret == SA_OK) {
+ ret = sa_disable_share(share, NULL);
+ /*
+ * we don't care if it fails since it
+ * could be disabled already.
+ */
+ if (ret == SA_OK || ret == SA_NO_SUCH_PATH ||
+ ret == SA_NOT_SUPPORTED) {
+ ret = sa_remove_share(share);
+ }
+ if (ret == SA_OK)
+ ret = sa_update_config();
+ }
+ if (ret != SA_OK) {
+ (void) printf(gettext("Could not remove share:"
+ " %s\n"),
+ sa_errorstr(ret));
+ }
+ } else if (ret == SA_OK) {
+ char *pname;
+ pname = sa_get_group_attr(group, "name");
+ if (pname != NULL) {
+ auth = check_authorizations(pname, flags);
+ sa_free_attr_string(pname);
+ }
+ if (!auth && verbose) {
+ (void) printf(gettext("Command would fail: %s\n"),
+ sa_errorstr(SA_NO_PERMISSION));
+ }
+ }
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_set_share(flags, argc, argv)
+ *
+ * implements set-share subcommand.
+ */
+
+int
+sa_set_share(int flags, int argc, char *argv[])
+{
+ int dryrun = 0;
+ int c;
+ int ret = SA_OK;
+ sa_group_t group, sharegroup;
+ sa_share_t share;
+ char *sharepath = NULL;
+ char *description = NULL;
+ char *resource = NULL;
+ int auth;
+ int verbose = 0;
+
+ while ((c = getopt(argc, argv, "?hnd:r:s:")) != EOF) {
+ switch (c) {
+ case 'n':
+ dryrun++;
+ break;
+ case 'd':
+ description = optarg;
+ break;
+ case 'r':
+ resource = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 's':
+ /*
+ * save share path into group. Currently limit
+ * to one share per command.
+ */
+ if (sharepath != NULL) {
+ (void) printf(gettext("Updating multiple shares not"
+ "supported\n"));
+ return (SA_BAD_PATH);
+ }
+ sharepath = optarg;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_SET_SHARE));
+ return (SA_OK);
+ }
+ }
+ if (optind >= argc || sharepath == NULL) {
+ if (sharepath == NULL) {
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_SET_SHARE));
+ (void) printf(gettext("\tgroup must be specified\n"));
+ ret = SA_BAD_PATH;
+ } else {
+ ret = SA_OK;
+ }
+ }
+ if ((optind + 1) < argc) {
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_SET_SHARE));
+ (void) printf(gettext("\tExtraneous group(s) at end\n"));
+ ret = SA_SYNTAX_ERR;
+ }
+ if (ret == SA_OK) {
+ char *groupname;
+ if (optind < argc) {
+ groupname = argv[optind];
+ group = sa_get_group(groupname);
+ } else {
+ group = NULL;
+ groupname = NULL;
+ }
+ share = sa_find_share(sharepath);
+ if (share != NULL) {
+ sharegroup = sa_get_parent_group(share);
+ if (group != NULL && group != sharegroup) {
+ (void) printf(gettext("Group \"%s\" does not contain "
+ "share %s\n"),
+ argv[optind], sharepath);
+ ret = SA_BAD_PATH;
+ } else {
+ int delgroupname = 0;
+ if (groupname == NULL) {
+ groupname = sa_get_group_attr(sharegroup, "name");
+ delgroupname = 1;
+ }
+ if (groupname != NULL) {
+ auth = check_authorizations(groupname, flags);
+ if (delgroupname) {
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ if (resource != NULL) {
+ if (strpbrk(resource, " \t/") == NULL) {
+ if (!dryrun) {
+ ret = sa_set_share_attr(share, "resource",
+ resource);
+ } else {
+ sa_share_t resshare;
+ resshare = sa_get_resource(sharegroup,
+ resource);
+ if (resshare != NULL && resshare != share)
+ ret = SA_DUPLICATE_NAME;
+ }
+ } else {
+ ret = SA_BAD_PATH;
+ (void) printf(gettext("Resource must not contain "
+ "white space or '/'\n"));
+ }
+ }
+ if (ret == SA_OK && description != NULL) {
+ ret = sa_set_share_description(share, description);
+ }
+ }
+ if (!dryrun && ret == SA_OK) {
+ ret = sa_update_config();
+ }
+ switch (ret) {
+ case SA_DUPLICATE_NAME:
+ (void) printf(gettext("Resource name in use: %s\n"),
+ resource);
+ break;
+ default:
+ (void) printf(gettext("Could not set attribute: %s\n"),
+ sa_errorstr(ret));
+ break;
+ case SA_OK:
+ if (dryrun && !auth && verbose) {
+ (void) printf(gettext("Command would fail: %s\n"),
+ sa_errorstr(SA_NO_PERMISSION));
+ }
+ break;
+ }
+ } else {
+ (void) printf(gettext("Share path \"%s\" not found\n"),
+ sharepath);
+ ret = SA_NO_SUCH_PATH;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * add_security(group, sectype, optlist, proto, *err)
+ *
+ * Helper function to add a security option (named optionset) to the
+ * group.
+ */
+
+static int
+add_security(sa_group_t group, char *sectype,
+ struct options *optlist, char *proto, int *err)
+{
+ sa_security_t security;
+ int ret = SA_OK;
+ int result = 0;
+
+ sectype = sa_proto_space_alias(proto, sectype);
+ security = sa_get_security(group, sectype, proto);
+ if (security == NULL) {
+ security = sa_create_security(group, sectype, proto);
+ }
+ if (sectype != NULL)
+ sa_free_attr_string(sectype);
+ if (security != NULL) {
+ while (optlist != NULL) {
+ sa_property_t prop;
+ prop = sa_get_property(security, optlist->optname);
+ if (prop == NULL) {
+ /*
+ * add the property, but only if it is
+ * a non-NULL or non-zero length value
+ */
+ if (optlist->optvalue != NULL) {
+ prop = sa_create_property(optlist->optname,
+ optlist->optvalue);
+ if (prop != NULL) {
+ ret = sa_valid_property(security, proto, prop);
+ if (ret != SA_OK) {
+ (void) sa_remove_property(prop);
+ (void) printf(gettext("Could not add "
+ "property %s: %s\n"),
+ optlist->optname,
+ sa_errorstr(ret));
+ }
+ if (ret == SA_OK) {
+ ret = sa_add_property(security, prop);
+ if (ret != SA_OK) {
+ (void) printf(gettext("Could not add "
+ "property (%s=%s): %s\n"),
+ optlist->optname,
+ optlist->optvalue,
+ sa_errorstr(ret));
+ } else {
+ result = 1;
+ }
+ }
+ }
+ }
+ } else {
+ ret = sa_update_property(prop, optlist->optvalue);
+ result = 1; /* should check if really changed */
+ }
+ optlist = optlist->next;
+ }
+ /*
+ * when done, properties may have all been removed but
+ * we need to keep the security type itself until
+ * explicitly removed.
+ */
+ if (result)
+ ret = sa_commit_properties(security, 0);
+ }
+ *err = ret;
+ return (result);
+}
+
+/*
+ * basic_set(groupname, optlist, protocol, sharepath, dryrun)
+ *
+ * This function implements "set" when a name space (-S) is not
+ * specified. It is a basic set. Options and other CLI parsing has
+ * already been done.
+ */
+
+static int
+basic_set(char *groupname, struct options *optlist, char *protocol,
+ char *sharepath, int dryrun)
+{
+ sa_group_t group;
+ int ret = SA_OK;
+ int change = 0;
+ struct list *worklist = NULL;
+
+ group = sa_get_group(groupname);
+ if (group != NULL) {
+ sa_share_t share = NULL;
+ if (sharepath != NULL) {
+ share = sa_get_share(group, sharepath);
+ if (share == NULL) {
+ (void) printf(gettext("Share does not exist in group %s\n"),
+ groupname, sharepath);
+ ret = SA_NO_SUCH_PATH;
+ }
+ }
+ if (ret == SA_OK) {
+ /* group must exist */
+ ret = valid_options(optlist, protocol,
+ share == NULL ? group : share, NULL);
+ if (ret == SA_OK && !dryrun) {
+ if (share != NULL)
+ change |= add_optionset(share, optlist, protocol,
+ &ret);
+ else
+ change |= add_optionset(group, optlist, protocol,
+ &ret);
+ if (ret == SA_OK && change) {
+ worklist = add_list(worklist, group, share);
+ }
+ }
+ }
+ free_opt(optlist);
+ } else {
+ (void) printf(gettext("Group \"%s\" not found\n"), groupname);
+ ret = SA_NO_SUCH_GROUP;
+ }
+ /*
+ * we have a group and potentially legal additions
+ */
+
+ /* commit to configuration if not a dryrun */
+ if (!dryrun && ret == SA_OK) {
+ if (change && worklist != NULL) {
+ /* properties changed, so update all shares */
+ (void) enable_all_groups(worklist, 0, 0, protocol);
+ }
+ }
+ if (worklist != NULL)
+ free_list(worklist);
+ return (ret);
+}
+
+/*
+ * space_set(groupname, optlist, protocol, sharepath, dryrun)
+ *
+ * This function implements "set" when a name space (-S) is
+ * specified. It is a namespace set. Options and other CLI parsing has
+ * already been done.
+ */
+
+static int
+space_set(char *groupname, struct options *optlist, char *protocol,
+ char *sharepath, int dryrun, char *sectype)
+{
+ sa_group_t group;
+ int ret = SA_OK;
+ int change = 0;
+ struct list *worklist = NULL;
+
+ /*
+ * make sure protcol and sectype are valid
+ */
+
+ if (sa_proto_valid_space(protocol, sectype) == 0) {
+ (void) printf(gettext("Option space \"%s\" not valid "
+ "for protocol.\n"),
+ sectype);
+ return (SA_INVALID_SECURITY);
+ }
+
+ group = sa_get_group(groupname);
+ if (group != NULL) {
+ sa_share_t share = NULL;
+ if (sharepath != NULL) {
+ share = sa_get_share(group, sharepath);
+ if (share == NULL) {
+ (void) printf(gettext("Share does not exist in group %s\n"),
+ groupname, sharepath);
+ ret = SA_NO_SUCH_PATH;
+ }
+ }
+ if (ret == SA_OK) {
+ /* group must exist */
+ ret = valid_options(optlist, protocol,
+ share == NULL ? group : share, sectype);
+ if (ret == SA_OK && !dryrun) {
+ if (share != NULL)
+ change = add_security(share, sectype, optlist,
+ protocol,
+ &ret);
+ else
+ change = add_security(group, sectype, optlist,
+ protocol,
+ &ret);
+ if (ret != SA_OK)
+ (void) printf(gettext("Could not set property: %s\n"),
+ sa_errorstr(ret));
+ }
+ if (ret == SA_OK && change)
+ worklist = add_list(worklist, group, share);
+ }
+ free_opt(optlist);
+ } else {
+ (void) printf(gettext("Group \"%s\" not found\n"), groupname);
+ ret = SA_NO_SUCH_GROUP;
+ }
+ /*
+ * we have a group and potentially legal additions
+ */
+
+ /* commit to configuration if not a dryrun */
+ if (!dryrun && ret == 0) {
+ if (change && worklist != NULL) {
+ /* properties changed, so update all shares */
+ (void) enable_all_groups(worklist, 0, 0, protocol);
+ }
+ ret = sa_update_config();
+ }
+ if (worklist != NULL)
+ free_list(worklist);
+ return (ret);
+}
+
+/*
+ * sa_set(flags, argc, argv)
+ *
+ * Implements the set subcommand. It keys off of -S to determine which
+ * set of operations to actually do.
+ */
+
+int
+sa_set(int flags, int argc, char *argv[])
+{
+ char *groupname;
+ int verbose = 0;
+ int dryrun = 0;
+ int c;
+ char *protocol = NULL;
+ int ret = SA_OK;
+ struct options *optlist = NULL;
+ char *sharepath = NULL;
+ char *optset = NULL;
+ int auth;
+
+ while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+ case 'n':
+ dryrun++;
+ break;
+ case 'P':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ (void) printf(gettext("Invalid protocol specified:"
+ "%s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ break;
+ case 'p':
+ ret = add_opt(&optlist, optarg, 0);
+ switch (ret) {
+ case OPT_ADD_SYNTAX:
+ (void) printf(gettext("Property syntax error: %s\n"),
+ optarg);
+ return (SA_SYNTAX_ERR);
+ case OPT_ADD_MEMORY:
+ (void) printf(gettext("No memory to set property: %s\n"),
+ optarg);
+ return (SA_NO_MEMORY);
+ default:
+ break;
+ }
+ break;
+ case 's':
+ sharepath = optarg;
+ break;
+ case 'S':
+ optset = optarg;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_SET));
+ return (SA_OK);
+ }
+ }
+
+ if (optlist != NULL)
+ ret = chk_opt(optlist, optset != NULL, protocol);
+
+ if (optind >= argc || (optlist == NULL && optset == NULL) ||
+ protocol == NULL ||
+ ret != OPT_ADD_OK) {
+ char *sep = "\t";
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET));
+ if (optind >= argc) {
+ (void) printf(gettext("%sgroup must be specified"), sep);
+ sep = ", ";
+ }
+ if (optlist == NULL) {
+ (void) printf(gettext("%sat least one property must be"
+ " specified"), sep);
+ sep = ", ";
+ }
+ if (protocol == NULL) {
+ (void) printf(gettext("%sprotocol must be specified"), sep);
+ sep = ", ";
+ }
+ (void) printf("\n");
+ ret = SA_SYNTAX_ERR;
+ } else {
+ /*
+ * if a group already exists, we can only add a new
+ * protocol to it and not create a new one or add the
+ * same protocol again.
+ */
+
+ groupname = argv[optind];
+ auth = check_authorizations(groupname, flags);
+ if (optset == NULL)
+ ret = basic_set(groupname, optlist, protocol,
+ sharepath, dryrun);
+ else
+ ret = space_set(groupname, optlist, protocol,
+ sharepath, dryrun, optset);
+ if (dryrun && ret == SA_OK && !auth && verbose) {
+ (void) printf(gettext("Command would fail: %s\n"),
+ sa_errorstr(SA_NO_PERMISSION));
+ }
+ }
+ return (ret);
+}
+
+/*
+ * remove_options(group, optlist, proto, *err)
+ *
+ * helper function to actually remove options from a group after all
+ * preprocessing is done.
+ */
+
+static int
+remove_options(sa_group_t group, struct options *optlist,
+ char *proto, int *err)
+{
+ struct options *cur;
+ sa_optionset_t optionset;
+ sa_property_t prop;
+ int change = 0;
+ int ret = SA_OK;
+
+ optionset = sa_get_optionset(group, proto);
+ if (optionset != NULL) {
+ for (cur = optlist; cur != NULL; cur = cur->next) {
+ prop = sa_get_property(optionset, cur->optname);
+ if (prop != NULL) {
+ ret = sa_remove_property(prop);
+ if (ret != SA_OK)
+ break;
+ change = 1;
+ }
+ }
+ }
+ if (ret == SA_OK && change)
+ ret = sa_commit_properties(optionset, 0);
+
+ if (err != NULL)
+ *err = ret;
+ return (change);
+}
+
+/*
+ * valid_unset(group, optlist, proto)
+ *
+ * Sanity check the optlist to make sure they can be removed. Issue an
+ * error if a property doesn't exist.
+ */
+
+static int
+valid_unset(sa_group_t group, struct options *optlist, char *proto)
+{
+ struct options *cur;
+ sa_optionset_t optionset;
+ sa_property_t prop;
+ int ret = SA_OK;
+
+ optionset = sa_get_optionset(group, proto);
+ if (optionset != NULL) {
+ for (cur = optlist; cur != NULL; cur = cur->next) {
+ prop = sa_get_property(optionset, cur->optname);
+ if (prop == NULL) {
+ (void) printf(gettext("Could not unset property %s:"
+ " not set\n"),
+ cur->optname);
+ ret = SA_NO_SUCH_PROP;
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * valid_unset_security(group, optlist, proto)
+ *
+ * Sanity check the optlist to make sure they can be removed. Issue an
+ * error if a property doesn't exist.
+ */
+
+static int
+valid_unset_security(sa_group_t group, struct options *optlist, char *proto,
+ char *sectype)
+{
+ struct options *cur;
+ sa_security_t security;
+ sa_property_t prop;
+ int ret = SA_OK;
+ char *sec;
+
+ sec = sa_proto_space_alias(proto, sectype);
+ security = sa_get_security(group, sec, proto);
+ if (security != NULL) {
+ for (cur = optlist; cur != NULL; cur = cur->next) {
+ prop = sa_get_property(security, cur->optname);
+ if (prop == NULL) {
+ (void) printf(gettext("Could not unset property %s:"
+ " not set\n"),
+ cur->optname);
+ ret = SA_NO_SUCH_PROP;
+ }
+ }
+ } else {
+ (void) printf(gettext("Could not unset %s: space not defined\n"),
+ sectype);
+ ret = SA_NO_SUCH_SECURITY;
+ }
+ if (sec != NULL)
+ sa_free_attr_string(sec);
+ return (ret);
+}
+
+/*
+ * remove_security(group, optlist, proto)
+ *
+ * Remove the properties since they were checked as valid.
+ */
+
+static int
+remove_security(sa_group_t group, char *sectype,
+ struct options *optlist, char *proto, int *err)
+{
+ sa_security_t security;
+ int ret = SA_OK;
+ int change = 0;
+
+ sectype = sa_proto_space_alias(proto, sectype);
+ security = sa_get_security(group, sectype, proto);
+ if (sectype != NULL)
+ sa_free_attr_string(sectype);
+
+ if (security != NULL) {
+ while (optlist != NULL) {
+ sa_property_t prop;
+ prop = sa_get_property(security, optlist->optname);
+ if (prop != NULL) {
+ ret = sa_remove_property(prop);
+ if (ret != SA_OK)
+ break;
+ change = 1;
+ }
+ optlist = optlist->next;
+ }
+ /*
+ * when done, properties may have all been removed but
+ * we need to keep the security type itself until
+ * explicitly removed.
+ */
+ if (ret == SA_OK && change)
+ ret = sa_commit_properties(security, 0);
+ } else {
+ ret = SA_NO_SUCH_PROP;
+ }
+ if (err != NULL)
+ *err = ret;
+ return (change);
+}
+
+/*
+ * basic_unset(groupname, optlist, protocol, sharepath, dryrun)
+ *
+ * unset non-named optionset properties.
+ */
+
+static int
+basic_unset(char *groupname, struct options *optlist, char *protocol,
+ char *sharepath, int dryrun)
+{
+ sa_group_t group;
+ int ret = SA_OK;
+ int change = 0;
+ struct list *worklist = NULL;
+
+ group = sa_get_group(groupname);
+ if (group != NULL) {
+ sa_share_t share = NULL;
+ if (sharepath != NULL) {
+ share = sa_get_share(group, sharepath);
+ if (share == NULL) {
+ (void) printf(gettext("Share does not exist in group %s\n"),
+ groupname, sharepath);
+ ret = SA_NO_SUCH_PATH;
+ }
+ }
+ if (ret == SA_OK) {
+ /* group must exist */
+ ret = valid_unset(share != NULL ? share : group,
+ optlist, protocol);
+ if (ret == SA_OK && !dryrun) {
+ if (share != NULL) {
+ sa_optionset_t optionset;
+ sa_property_t prop;
+ change |= remove_options(share, optlist, protocol,
+ &ret);
+ /* if a share optionset is empty, remove it */
+ optionset = sa_get_optionset((sa_share_t)share,
+ protocol);
+ if (optionset != NULL) {
+ prop = sa_get_property(optionset, NULL);
+ if (prop == NULL)
+ (void) sa_destroy_optionset(optionset);
+ }
+ } else {
+ change |= remove_options(group, optlist, protocol,
+ &ret);
+ }
+ if (ret == SA_OK && change)
+ worklist = add_list(worklist, group, share);
+ if (ret != SA_OK)
+ (void) printf(gettext("Could not remove properties:"
+ "%s\n"),
+ sa_errorstr(ret));
+ }
+ } else {
+ (void) printf(gettext("Group \"%s\" not found\n"), groupname);
+ ret = SA_NO_SUCH_GROUP;
+ }
+ free_opt(optlist);
+ }
+
+ /*
+ * we have a group and potentially legal additions
+ */
+ /* commit to configuration if not a dryrun */
+ if (!dryrun && ret == SA_OK) {
+ if (change && worklist != NULL) {
+ /* properties changed, so update all shares */
+ (void) enable_all_groups(worklist, 0, 0, protocol);
+ }
+ }
+ if (worklist != NULL)
+ free_list(worklist);
+ return (ret);
+}
+
+/*
+ * space_unset(groupname, optlist, protocol, sharepath, dryrun)
+ *
+ * unset named optionset properties.
+ */
+static int
+space_unset(char *groupname, struct options *optlist, char *protocol,
+ char *sharepath, int dryrun, char *sectype)
+{
+ sa_group_t group;
+ int ret = SA_OK;
+ int change = 0;
+ struct list *worklist = NULL;
+
+ group = sa_get_group(groupname);
+ if (group != NULL) {
+ sa_share_t share = NULL;
+ if (sharepath != NULL) {
+ share = sa_get_share(group, sharepath);
+ if (share == NULL) {
+ (void) printf(gettext("Share does not exist in group %s\n"),
+ groupname, sharepath);
+ ret = SA_NO_SUCH_PATH;
+ }
+ }
+ if (ret == SA_OK) {
+ ret = valid_unset_security(share != NULL ? share : group,
+ optlist, protocol, sectype);
+ if (ret == SA_OK && !dryrun) {
+ if (optlist != NULL) {
+ if (share != NULL) {
+ sa_security_t optionset;
+ sa_property_t prop;
+ change = remove_security(share, sectype,
+ optlist, protocol,
+ &ret);
+ /* if a share security is empty, remove it */
+ optionset = sa_get_security((sa_group_t)share,
+ sectype,
+ protocol);
+ if (optionset != NULL) {
+ prop = sa_get_property(optionset, NULL);
+ if (prop == NULL)
+ ret = sa_destroy_security(optionset);
+ }
+ } else {
+ change = remove_security(group, sectype,
+ optlist, protocol,
+ &ret);
+ }
+ } else {
+ sa_security_t security;
+ char *sec;
+ sec = sa_proto_space_alias(protocol, sectype);
+ security = sa_get_security(group, sec, protocol);
+ if (sec != NULL)
+ sa_free_attr_string(sec);
+ if (security != NULL) {
+ ret = sa_destroy_security(security);
+ if (ret == SA_OK)
+ change = 1;
+ } else {
+ ret = SA_NO_SUCH_PROP;
+ }
+ }
+ if (ret != SA_OK)
+ (void) printf(gettext("Could not unset property: %s\n"),
+ sa_errorstr(ret));
+ }
+
+ if (ret == SA_OK && change)
+ worklist = add_list(worklist, group, 0);
+ }
+ } else {
+ (void) printf(gettext("Group \"%s\" not found\n"), groupname);
+ ret = SA_NO_SUCH_GROUP;
+ }
+ free_opt(optlist);
+ /*
+ * we have a group and potentially legal additions
+ */
+
+ /* commit to configuration if not a dryrun */
+ if (!dryrun && ret == 0) {
+ if (change && worklist != NULL) {
+ /* properties changed, so update all shares */
+ (void) enable_all_groups(worklist, 0, 0, protocol);
+ }
+ ret = sa_update_config();
+ }
+ if (worklist != NULL)
+ free_list(worklist);
+ return (ret);
+}
+
+/*
+ * sa_unset(flags, argc, argv)
+ *
+ * implements the unset subcommand. Parsing done here and then basic
+ * or space versions of the real code are called.
+ */
+
+int
+sa_unset(int flags, int argc, char *argv[])
+{
+ char *groupname;
+ int verbose = 0;
+ int dryrun = 0;
+ int c;
+ char *protocol = NULL;
+ int ret = SA_OK;
+ struct options *optlist = NULL;
+ char *sharepath = NULL;
+ char *optset = NULL;
+ int auth;
+
+ while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+ case 'n':
+ dryrun++;
+ break;
+ case 'P':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ (void) printf(gettext("Invalid protocol specified: %s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ break;
+ case 'p':
+ ret = add_opt(&optlist, optarg, 1);
+ switch (ret) {
+ case OPT_ADD_SYNTAX:
+ (void) printf(gettext("Property syntax error for "
+ "property %s\n"),
+ optarg);
+ return (SA_SYNTAX_ERR);
+ case OPT_ADD_PROPERTY:
+ (void) printf(gettext("Properties need to be set"
+ " with set command: %s\n"),
+ optarg);
+ return (SA_SYNTAX_ERR);
+ default:
+ break;
+ }
+ break;
+ case 's':
+ sharepath = optarg;
+ break;
+ case 'S':
+ optset = optarg;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_UNSET));
+ return (SA_OK);
+ }
+ }
+
+ if (optlist != NULL)
+ ret = chk_opt(optlist, optset != NULL, protocol);
+
+ if (optind >= argc || (optlist == NULL && optset == NULL) ||
+ protocol == NULL) {
+ char *sep = "\t";
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_UNSET));
+ if (optind >= argc) {
+ (void) printf(gettext("%sgroup must be specified"), sep);
+ sep = ", ";
+ }
+ if (optlist == NULL) {
+ (void) printf(gettext("%sat least one property must be "
+ "specified"),
+ sep);
+ sep = ", ";
+ }
+ if (protocol == NULL) {
+ (void) printf(gettext("%sprotocol must be specified"), sep);
+ sep = ", ";
+ }
+ (void) printf("\n");
+ ret = SA_SYNTAX_ERR;
+ } else {
+
+ /*
+ * if a group already exists, we can only add a new
+ * protocol to it and not create a new one or add the
+ * same protocol again.
+ */
+
+ groupname = argv[optind];
+ auth = check_authorizations(groupname, flags);
+ if (optset == NULL)
+ ret = basic_unset(groupname, optlist, protocol,
+ sharepath, dryrun);
+ else
+ ret = space_unset(groupname, optlist, protocol,
+ sharepath, dryrun, optset);
+
+ if (dryrun && ret == SA_OK && !auth && verbose) {
+ (void) printf(gettext("Command would fail: %s\n"),
+ sa_errorstr(SA_NO_PERMISSION));
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_enable_group(flags, argc, argv)
+ *
+ * Implements the enable subcommand
+ */
+
+int
+sa_enable_group(int flags, int argc, char *argv[])
+{
+ int verbose = 0;
+ int dryrun = 0;
+ int all = 0;
+ int c;
+ int ret = SA_OK;
+ char *protocol = NULL;
+ char *state;
+ struct list *worklist = NULL;
+ int auth = 1;
+
+ while ((c = getopt(argc, argv, "?havnP:")) != EOF) {
+ switch (c) {
+ case 'a':
+ all = 1;
+ break;
+ case 'n':
+ dryrun++;
+ break;
+ case 'P':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ (void) printf(gettext("Invalid protocol specified: %s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_ENABLE));
+ return (0);
+ }
+ }
+
+ if (optind == argc && !all) {
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_ENABLE));
+ (void) printf(gettext("\tmust specify group\n"));
+ ret = SA_NO_SUCH_PATH;
+ } else {
+ sa_group_t group;
+ if (!all) {
+ while (optind < argc) {
+ group = sa_get_group(argv[optind]);
+ if (group != NULL) {
+ auth &= check_authorizations(argv[optind], flags);
+ state = sa_get_group_attr(group, "state");
+ if (state != NULL &&
+ strcmp(state, "enabled") == 0) {
+ /* already enabled */
+ if (verbose)
+ (void) printf(gettext("Group \"%s\" is already "
+ "enabled\n"),
+ argv[optind]);
+ ret = SA_BUSY; /* already enabled */
+ } else {
+ worklist = add_list(worklist, group, 0);
+ if (verbose)
+ (void) printf(gettext("Enabling group "
+ "\"%s\"\n"),
+ argv[optind]);
+ }
+ if (state != NULL)
+ sa_free_attr_string(state);
+ } else {
+ ret = SA_NO_SUCH_GROUP;
+ }
+ optind++;
+ }
+ } else {
+ for (group = sa_get_group(NULL); group != NULL;
+ group = sa_get_next_group(group)) {
+ worklist = add_list(worklist, group, 0);
+ }
+ }
+ if (!dryrun && ret == SA_OK) {
+ ret = enable_all_groups(worklist, 1, 0, NULL);
+ }
+ if (ret != SA_OK && ret != SA_BUSY)
+ (void) printf(gettext("Could not enable group: %s\n"),
+ sa_errorstr(ret));
+ if (ret == SA_BUSY)
+ ret = SA_OK;
+ }
+ if (worklist != NULL)
+ free_list(worklist);
+ if (dryrun && ret == SA_OK && !auth && verbose) {
+ (void) printf(gettext("Command would fail: %s\n"),
+ sa_errorstr(SA_NO_PERMISSION));
+ }
+ return (ret);
+}
+
+/*
+ * disable_group(group, setstate)
+ *
+ * disable all the shares in the specified group honoring the setstate
+ * argument. This is a helper for disable_all_groups in order to
+ * simplify regular and subgroup (zfs) disabling. Group has already
+ * been checked for non-NULL.
+ */
+
+static int
+disable_group(sa_group_t group)
+{
+ sa_share_t share;
+ int ret = SA_OK;
+
+ for (share = sa_get_share(group, NULL);
+ share != NULL && ret == SA_OK;
+ share = sa_get_next_share(share)) {
+ ret = sa_disable_share(share, NULL);
+ if (ret == SA_NO_SUCH_PATH) {
+ /*
+ * this is OK since the path is gone. we can't
+ * re-share it anyway so no error.
+ */
+ ret = SA_OK;
+ }
+ }
+ return (ret);
+}
+
+
+/*
+ * disable_all_groups(work, setstate)
+ *
+ * helper function that disables the shares in the list of groups
+ * provided. It optionally marks the group as disabled. Used by both
+ * enable and start subcommands.
+ */
+
+static int
+disable_all_groups(struct list *work, int setstate)
+{
+ int ret = SA_OK;
+ sa_group_t subgroup, group;
+
+ while (work != NULL && ret == SA_OK) {
+ group = (sa_group_t)work->item;
+ if (setstate)
+ ret = sa_set_group_attr(group, "state", "disabled");
+ if (ret == SA_OK) {
+ char *name;
+ name = sa_get_group_attr(group, "name");
+ if (name != NULL && strcmp(name, "zfs") == 0) {
+ /* need to get the sub-groups for stopping */
+ for (subgroup = sa_get_sub_group(group); subgroup != NULL;
+ subgroup = sa_get_next_group(subgroup)) {
+ ret = disable_group(subgroup);
+ }
+ } else {
+ ret = disable_group(group);
+ }
+ /*
+ * we don't want to "disable" since it won't come
+ * up after a reboot. The SMF framework should do
+ * the right thing. On enable we do want to do
+ * something.
+ */
+ }
+ work = work->next;
+ }
+ if (ret == SA_OK)
+ ret = sa_update_config();
+ return (ret);
+}
+
+/*
+ * sa_disable_group(flags, argc, argv)
+ *
+ * Implements the disable subcommand
+ */
+
+int
+sa_disable_group(int flags, int argc, char *argv[])
+{
+ int verbose = 0;
+ int dryrun = 0;
+ int all = 0;
+ int c;
+ int ret = SA_OK;
+ char *protocol;
+ char *state;
+ struct list *worklist = NULL;
+ int auth = 1;
+
+ while ((c = getopt(argc, argv, "?havn")) != EOF) {
+ switch (c) {
+ case 'a':
+ all = 1;
+ break;
+ case 'n':
+ dryrun++;
+ break;
+ case 'P':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ (void) printf(gettext("Invalid protocol specified: %s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_DISABLE));
+ return (0);
+ }
+ }
+
+ if (optind == argc && !all) {
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_DISABLE));
+ (void) printf(gettext("\tmust specify group\n"));
+ ret = SA_NO_SUCH_PATH;
+ } else {
+ sa_group_t group;
+ if (!all) {
+ while (optind < argc) {
+ group = sa_get_group(argv[optind]);
+ if (group != NULL) {
+ auth &= check_authorizations(argv[optind], flags);
+ state = sa_get_group_attr(group, "state");
+ if (state == NULL ||
+ strcmp(state, "disabled") == 0) {
+ /* already disabled */
+ if (verbose)
+ (void) printf(gettext("Group \"%s\" is "
+ "already disabled\n"),
+ argv[optind]);
+ ret = SA_BUSY; /* already disable */
+ } else {
+ worklist = add_list(worklist, group, 0);
+ if (verbose)
+ (void) printf(gettext("Disabling group "
+ "\"%s\"\n"),
+ argv[optind]);
+ }
+ if (state != NULL)
+ sa_free_attr_string(state);
+ } else {
+ ret = SA_NO_SUCH_GROUP;
+ }
+ optind++;
+ }
+ } else {
+ for (group = sa_get_group(NULL); group != NULL;
+ group = sa_get_next_group(group)) {
+ worklist = add_list(worklist, group, 0);
+ }
+ }
+ if (ret == SA_OK && !dryrun) {
+ ret = disable_all_groups(worklist, 1);
+ }
+ if (ret != SA_OK && ret != SA_BUSY)
+ (void) printf(gettext("Could not disable group: %s\n"),
+ sa_errorstr(ret));
+ if (ret == SA_BUSY)
+ ret = SA_OK;
+ }
+ if (worklist != NULL)
+ free_list(worklist);
+ if (dryrun && ret == SA_OK && !auth && verbose) {
+ (void) printf(gettext("Command would fail: %s\n"),
+ sa_errorstr(SA_NO_PERMISSION));
+ }
+ return (ret);
+}
+
+/*
+ * check_sharetab()
+ *
+ * Checks to see if the /etc/dfs/sharetab file is stale (exists from
+ * before the current boot). If it is, truncate it since nothing is
+ * really shared.
+ */
+
+static void
+check_sharetab()
+{
+ int fd;
+ struct utmpx *utmpxp;
+ struct stat st;
+
+ fd = open(SA_LEGACY_SHARETAB, O_RDWR);
+ if (fd >= 0) {
+ /*
+ * Attempt to get a lock on the file. Whgen we get
+ * one, then check to see if it is older than the boot
+ * time. Truncate if older than boot.
+ */
+ (void) lockf(fd, F_LOCK, 0);
+ if ((fstat(fd, &st) == 0) && /* does sharetab exist? */
+ (utmpxp = getutxent()) != NULL && /* does utmpx exist? */
+ (utmpxp->ut_xtime > st.st_mtime)) /* sharetab older? */
+ (void) ftruncate(fd, 0);
+
+ (void) lockf(fd, F_ULOCK, 0);
+ (void) close(fd);
+ endutxent();
+ }
+}
+
+/*
+ * sa_start_group(flags, argc, argv)
+ *
+ * Implements the start command.
+ * This is similar to enable except it doesn't change the state
+ * of the group(s) and only enables shares if the group is already
+ * enabled.
+ */
+
+int
+sa_start_group(int flags, int argc, char *argv[])
+{
+ int verbose = 0;
+ int all = 0;
+ int c;
+ int ret = SMF_EXIT_OK;
+ char *protocol = NULL;
+ char *state;
+ struct list *worklist = NULL;
+#ifdef lint
+ flags = flags;
+#endif
+
+ while ((c = getopt(argc, argv, "?havP:")) != EOF) {
+ switch (c) {
+ case 'a':
+ all = 1;
+ break;
+ case 'P':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ (void) printf(gettext("Invalid protocol specified: %s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_START));
+ return (SA_OK);
+ }
+ }
+
+ if (optind == argc && !all) {
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_START));
+ ret = SMF_EXIT_ERR_FATAL;
+ } else {
+ sa_group_t group;
+
+ check_sharetab();
+
+ if (!all) {
+ while (optind < argc) {
+ group = sa_get_group(argv[optind]);
+ if (group != NULL) {
+ state = sa_get_group_attr(group, "state");
+ if (state == NULL ||
+ strcmp(state, "enabled") == 0) {
+ worklist = add_list(worklist, group, 0);
+ if (verbose)
+ (void) printf(gettext("Starting group "
+ "\"%s\"\n"),
+ argv[optind]);
+ } else {
+ /*
+ * determine if there are any
+ * protocols. if there aren't any,
+ * then there isn't anything to do in
+ * any case so no error.
+ */
+ if (sa_get_optionset(group, protocol) != NULL) {
+ ret = SMF_EXIT_OK;
+ }
+ }
+ if (state != NULL)
+ sa_free_attr_string(state);
+ }
+ optind++;
+ }
+ } else {
+ for (group = sa_get_group(NULL); group != NULL;
+ group = sa_get_next_group(group)) {
+ state = sa_get_group_attr(group, "state");
+ if (state == NULL || strcmp(state, "enabled") == 0)
+ worklist = add_list(worklist, group, 0);
+ if (state != NULL)
+ sa_free_attr_string(state);
+ }
+ }
+ (void) enable_all_groups(worklist, 0, 1, NULL);
+ }
+ if (worklist != NULL)
+ free_list(worklist);
+ return (ret);
+}
+
+/*
+ * sa_stop_group(flags, argc, argv)
+ *
+ * Implements the stop command.
+ * This is similar to disable except it doesn't change the state
+ * of the group(s) and only disables shares if the group is already
+ * enabled.
+ */
+
+int
+sa_stop_group(int flags, int argc, char *argv[])
+{
+ int verbose = 0;
+ int all = 0;
+ int c;
+ int ret = SMF_EXIT_OK;
+ char *protocol = NULL;
+ char *state;
+ struct list *worklist = NULL;
+#ifdef lint
+ flags = flags;
+#endif
+
+ while ((c = getopt(argc, argv, "?havP:")) != EOF) {
+ switch (c) {
+ case 'a':
+ all = 1;
+ break;
+ case 'P':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ (void) printf(gettext("Invalid protocol specified: %s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ case 'h':
+ case '?':
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_STOP));
+ return (0);
+ }
+ }
+
+ if (optind == argc && !all) {
+ (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_STOP));
+ ret = SMF_EXIT_ERR_FATAL;
+ } else {
+ sa_group_t group;
+ if (!all) {
+ while (optind < argc) {
+ group = sa_get_group(argv[optind]);
+ if (group != NULL) {
+ state = sa_get_group_attr(group, "state");
+ if (state == NULL ||
+ strcmp(state, "enabled") == 0) {
+ worklist = add_list(worklist, group, 0);
+ if (verbose)
+ (void) printf(gettext("Stopping group "
+ "\"%s\"\n"),
+ argv[optind]);
+ } else {
+ ret = SMF_EXIT_OK;
+ }
+ if (state != NULL)
+ sa_free_attr_string(state);
+ }
+ optind++;
+ }
+ } else {
+ for (group = sa_get_group(NULL); group != NULL;
+ group = sa_get_next_group(group)) {
+ state = sa_get_group_attr(group, "state");
+ if (state == NULL || strcmp(state, "enabled") == 0)
+ worklist = add_list(worklist, group, 0);
+ if (state != NULL)
+ sa_free_attr_string(state);
+ }
+ }
+ (void) disable_all_groups(worklist, 0);
+ ret = sa_update_config();
+ }
+ if (worklist != NULL)
+ free_list(worklist);
+ return (ret);
+}
+
+/*
+ * remove_all_options(share, proto)
+ *
+ * Removes all options on a share.
+ */
+
+static void
+remove_all_options(sa_share_t share, char *proto)
+{
+ sa_optionset_t optionset;
+ sa_security_t security;
+ sa_security_t prevsec = NULL;
+
+ optionset = sa_get_optionset(share, proto);
+ if (optionset != NULL)
+ (void) sa_destroy_optionset(optionset);
+ for (security = sa_get_security(share, NULL, NULL);
+ security != NULL;
+ security = sa_get_next_security(security)) {
+ char *type;
+ /*
+ * we walk through the list. prevsec keeps the
+ * previous security so we can delete it without
+ * destroying the list.
+ */
+ if (prevsec != NULL) {
+ /* remove the previously seen security */
+ (void) sa_destroy_security(prevsec);
+ /* set to NULL so we don't try multiple times */
+ prevsec = NULL;
+ }
+ type = sa_get_security_attr(security, "type");
+ if (type != NULL) {
+ /*
+ * if the security matches the specified protocol, we
+ * want to remove it. prevsec holds it until either
+ * the next pass or we fall out of the loop.
+ */
+ if (strcmp(type, proto) == 0)
+ prevsec = security;
+ sa_free_attr_string(type);
+ }
+ }
+ /* in case there is one left */
+ if (prevsec != NULL)
+ (void) sa_destroy_security(prevsec);
+}
+
+
+/*
+ * for legacy support, we need to handle the old syntax. This is what
+ * we get if sharemgr is called with the name "share" rather than
+ * sharemgr.
+ */
+
+static int
+format_legacy_path(char *buff, int buffsize, char *proto, char *cmd)
+{
+ int err;
+
+ err = snprintf(buff, buffsize, "/usr/lib/fs/%s/%s", proto, cmd);
+ if (err > buffsize)
+ return (-1);
+ return (0);
+}
+
+
+/*
+ * check_legacy_cmd(proto, cmd)
+ *
+ * Check to see if the cmd exists in /usr/lib/fs/<proto>/<cmd> and is
+ * executable.
+ */
+
+static int
+check_legacy_cmd(char *path)
+{
+ struct stat st;
+ int ret = 0;
+
+ if (stat(path, &st) == 0) {
+ if (S_ISREG(st.st_mode) && st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
+ ret = 1;
+ }
+ return (ret);
+}
+
+/*
+ * run_legacy_command(proto, cmd, argv)
+ *
+ * we know the command exists, so attempt to execute it with all the
+ * arguments. This implements full legacy share support for those
+ * protocols that don't have plugin providers.
+ */
+
+static int
+run_legacy_command(char *path, char *argv[])
+{
+ int ret;
+
+ ret = execv(path, argv);
+ if (ret < 0) {
+ switch (errno) {
+ case EACCES:
+ ret = SA_NO_PERMISSION;
+ break;
+ default:
+ ret = SA_SYSTEM_ERR;
+ break;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * out_share(out, group, proto, options)
+ *
+ * Display the share information in the format that the "share"
+ * command has traditionally used.
+ */
+
+static void
+out_share(FILE *out, sa_group_t group, char *proto, char *options)
+{
+ sa_share_t share;
+ char resfmt[128];
+
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ char *path;
+ char *type;
+ char *resource;
+ char *description;
+ char *groupname;
+ char *sharedstate;
+ int shared = 1;
+ char *soptions;
+
+ sharedstate = sa_get_share_attr(share, "shared");
+ path = sa_get_share_attr(share, "path");
+ type = sa_get_share_attr(share, "type");
+ resource = sa_get_share_attr(share, "resource");
+ groupname = sa_get_group_attr(group, "name");
+
+ if (groupname != NULL && strcmp(groupname, "default") == 0) {
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ }
+ description = sa_get_share_description(share);
+ soptions = options;
+
+ if (sharedstate == NULL)
+ shared = 0;
+
+ soptions = sa_proto_legacy_format(proto, share, 1);
+
+ if (shared) {
+ /* only persisting share go here */
+ (void) snprintf(resfmt, sizeof (resfmt), "%s%s%s",
+ resource != NULL ? resource : "-",
+ groupname != NULL ? "@" : "",
+ groupname != NULL ? groupname : "");
+ (void) fprintf(out, "%-14.14s %s %s \"%s\" \n",
+ resfmt,
+ path,
+ (soptions != NULL && strlen(soptions) > 0) ?
+ soptions : "rw",
+ (description != NULL) ? description : "");
+ }
+
+ if (path != NULL)
+ sa_free_attr_string(path);
+ if (type != NULL)
+ sa_free_attr_string(type);
+ if (resource != NULL)
+ sa_free_attr_string(resource);
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+ if (description != NULL)
+ sa_free_share_description(description);
+ if (sharedstate != NULL)
+ sa_free_attr_string(sharedstate);
+ if (soptions != NULL && soptions != options)
+ sa_format_free(soptions);
+ }
+}
+
+/*
+ * output_legacy_file(out, proto)
+ *
+ * Walk all of the groups for the specified protocol and call
+ * out_share() to format and write in the format displayed by the
+ * "share" command with no arguments.
+ */
+
+static void
+output_legacy_file(FILE *out, char *proto)
+{
+ sa_group_t group;
+
+ for (group = sa_get_group(NULL); group != NULL;
+ group = sa_get_next_group(group)) {
+ char *options;
+ char *zfs;
+
+ /*
+ * get default options preformated, being careful to
+ * handle legacy shares differently from new style
+ * shares. Legacy share have options on the share.
+ */
+
+ zfs = sa_get_group_attr(group, "zfs");
+ if (zfs != NULL) {
+ sa_group_t zgroup;
+ sa_free_attr_string(zfs);
+ options = sa_proto_legacy_format(proto, group, 1);
+ for (zgroup = sa_get_sub_group(group); zgroup != NULL;
+ zgroup = sa_get_next_group(zgroup)) {
+
+ /* got a group, so display it */
+ out_share(out, zgroup, proto, options);
+ }
+ } else {
+ options = sa_proto_legacy_format(proto, group, 1);
+ out_share(out, group, proto, options);
+ }
+ if (options != NULL)
+ free(options);
+ }
+}
+
+int
+sa_legacy_share(int flags, int argc, char *argv[])
+{
+ char *protocol = "nfs";
+ char *options = NULL;
+ char *description = NULL;
+ char *groupname = NULL;
+ char *sharepath = NULL;
+ char *resource = NULL;
+ char *groupstatus = NULL;
+ int persist = SA_SHARE_TRANSIENT;
+ int argsused = 0;
+ int c;
+ int ret = SA_OK;
+ int zfs = 0;
+ int true_legacy = 0;
+ int curtype = SA_SHARE_TRANSIENT;
+ char cmd[MAXPATHLEN];
+#ifdef lint
+ flags = flags;
+#endif
+
+ while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) {
+ switch (c) {
+ case 'd':
+ description = optarg;
+ argsused++;
+ break;
+ case 'F':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ if (format_legacy_path(cmd, MAXPATHLEN,
+ protocol, "share") == 0 && check_legacy_cmd(cmd)) {
+ true_legacy++;
+ } else {
+ (void) fprintf(stderr,
+ gettext("Invalid protocol specified:"
+ "%s\n"),
+ protocol);
+ return (SA_INVALID_PROTOCOL);
+ }
+ }
+ break;
+ case 'o':
+ options = optarg;
+ argsused++;
+ break;
+ case 'p':
+ persist = SA_SHARE_PERMANENT;
+ argsused++;
+ break;
+ case 'h':
+ case '?':
+ default:
+ (void) fprintf(stderr, gettext("usage: %s\n"),
+ sa_get_usage(USAGE_SHARE));
+ return (SA_OK);
+ }
+ }
+
+ /* have the info so construct what is needed */
+ if (!argsused && optind == argc) {
+ /* display current info in share format */
+ (void) output_legacy_file(stdout, "nfs");
+ } else {
+ sa_group_t group = NULL;
+ sa_share_t share;
+ char dir[MAXPATHLEN];
+
+ /* we are modifying the configuration */
+ if (optind == argc) {
+ (void) fprintf(stderr, gettext("usage: %s\n"),
+ sa_get_usage(USAGE_SHARE));
+ return (SA_LEGACY_ERR);
+ }
+
+ if (true_legacy) {
+ /* if still using legacy share/unshare, exec it */
+ ret = run_legacy_command(cmd, argv);
+ return (ret);
+ }
+
+ sharepath = argv[optind++];
+ if (optind < argc) {
+ resource = argv[optind];
+ groupname = strchr(resource, '@');
+ if (groupname != NULL)
+ *groupname++ = '\0';
+ }
+ if (realpath(sharepath, dir) == NULL)
+ ret = SA_BAD_PATH;
+ else
+ sharepath = dir;
+ if (ret == SA_OK) {
+ share = sa_find_share(sharepath);
+ } else {
+ share = NULL;
+ }
+ if (groupname != NULL) {
+ ret = SA_NOT_ALLOWED;
+ } else if (ret == SA_OK) {
+ char *legacygroup = "default";
+ /*
+ * the legacy group is always present and zfs groups
+ * come and go. zfs shares may be in sub-groups and
+ * the zfs share will already be in that group so it
+ * isn't an error.
+ */
+ if (share != NULL) {
+ /*
+ * if the share exists, then make sure it is one we
+ * want to handle.
+ */
+ group = sa_get_parent_group(share);
+ } else {
+ group = sa_get_group(legacygroup);
+ }
+ if (group != NULL) {
+ groupstatus = group_status(group);
+ if (share == NULL) {
+ share = sa_add_share(group, sharepath, persist, &ret);
+ if (share == NULL && ret == SA_DUPLICATE_NAME) {
+ /* could be a ZFS path being started */
+ if (sa_zfs_is_shared(sharepath)) {
+ ret = SA_OK;
+ group = sa_get_group("zfs");
+ if (group == NULL) {
+ /* this shouldn't happen */
+ ret = SA_CONFIG_ERR;
+ }
+ if (group != NULL) {
+ share = sa_add_share(group, sharepath,
+ persist, &ret);
+ }
+ }
+ }
+ } else {
+ /*
+ * may want to change persist state, but the
+ * important thing is to change options unless
+ * this is ZFS where we just want to do the
+ * enable since everything is current.
+ */
+ if (!sa_zfs_is_shared(sharepath)) {
+ char *type;
+ remove_all_options(share, protocol);
+ type = sa_get_share_attr(share, "type");
+ if (type != NULL &&
+ strcmp(type, "transient") != 0) {
+ curtype = SA_SHARE_PERMANENT;
+ }
+ if (type != NULL)
+ sa_free_attr_string(type);
+ if (curtype != persist) {
+ (void) sa_set_share_attr(share, "type",
+ persist == SA_SHARE_PERMANENT ?
+ "persist" : "transient");
+ }
+ } else {
+ zfs++;
+ }
+ }
+ if (!zfs) {
+ /* have a group to hold this share path */
+ if (ret == SA_OK && options != NULL &&
+ strlen(options) > 0) {
+ ret = sa_parse_legacy_options(share,
+ options,
+ protocol);
+ }
+ if (ret == SA_OK && description != NULL)
+ ret = sa_set_share_description(share, description);
+ if (ret == SA_OK && resource != NULL)
+ ret = sa_set_share_attr(share, "resource",
+ resource);
+ }
+ if (ret == SA_OK) {
+ if (strcmp(groupstatus, "enabled") == 0)
+ ret = sa_enable_share(share, protocol);
+ if (ret == SA_OK && persist == SA_SHARE_PERMANENT) {
+ (void) sa_update_legacy(share, protocol);
+ }
+ if (ret == SA_OK)
+ ret = sa_update_config();
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ }
+ if (ret != SA_OK) {
+ (void) fprintf(stderr, gettext("Could not share: %s: %s\n"),
+ sharepath, sa_errorstr(ret));
+ ret = SA_LEGACY_ERR;
+
+ }
+ return (ret);
+}
+
+/*
+ * sa_legacy_unshare(flags, argc, argv)
+ *
+ * Implements the original unshare command.
+ */
+
+int
+sa_legacy_unshare(int flags, int argc, char *argv[])
+{
+ char *protocol = "nfs"; /* for now */
+ char *options = NULL;
+ char *sharepath = NULL;
+ int persist = SA_SHARE_TRANSIENT;
+ int argsused = 0;
+ int c;
+ int ret = SA_OK;
+ int true_legacy = 0;
+ char cmd[MAXPATHLEN];
+#ifdef lint
+ flags = flags;
+ options = options;
+#endif
+
+ while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) {
+ switch (c) {
+ case 'h':
+ case '?':
+ break;
+ case 'F':
+ protocol = optarg;
+ if (!sa_valid_protocol(protocol)) {
+ if (format_legacy_path(cmd, MAXPATHLEN,
+ protocol, "unshare") == 0 &&
+ check_legacy_cmd(cmd)) {
+ true_legacy++;
+ } else {
+ (void) printf(gettext("Invalid file system name\n"));
+ return (SA_INVALID_PROTOCOL);
+ }
+ }
+ break;
+ case 'o':
+ options = optarg;
+ argsused++;
+ break;
+ case 'p':
+ persist = SA_SHARE_PERMANENT;
+ argsused++;
+ break;
+ default:
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_UNSHARE));
+ return (SA_OK);
+ }
+ }
+
+ /* have the info so construct what is needed */
+ if (optind == argc || (optind + 1) < argc) {
+ ret = SA_SYNTAX_ERR;
+ } else {
+ sa_share_t share;
+ char dir[MAXPATHLEN];
+ if (true_legacy) {
+ /* if still using legacy share/unshare, exec it */
+ ret = run_legacy_command(cmd, argv);
+ return (ret);
+ }
+ sharepath = argv[optind++];
+ if (realpath(sharepath, dir) == NULL) {
+ ret = SA_NO_SUCH_PATH;
+ } else {
+ sharepath = dir;
+ share = sa_find_share(sharepath);
+ if (share != NULL) {
+ ret = sa_disable_share(share, protocol);
+ if (ret == SA_OK) {
+ if (persist == SA_SHARE_PERMANENT)
+ ret = sa_remove_share(share);
+ ret = sa_update_config();
+ }
+ } else {
+ ret = SA_NOT_SHARED;
+ }
+ }
+ }
+ switch (ret) {
+ default:
+ (void) printf("%s: %s\n", sharepath, sa_errorstr(ret));
+ ret = SA_LEGACY_ERR;
+ break;
+ case SA_SYNTAX_ERR:
+ (void) printf(gettext("usage: %s\n"),
+ sa_get_usage(USAGE_UNSHARE));
+ break;
+ case SA_OK:
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * common commands that implement the sub-commands used by all
+ * protcols. The entries are found via the lookup command
+ */
+
+static sa_command_t commands[] = {
+ {"add-share", 0, sa_addshare, USAGE_ADD_SHARE, SVC_SET},
+ {"create", 0, sa_create, USAGE_CREATE, SVC_SET|SVC_ACTION},
+ {"delete", 0, sa_delete, USAGE_DELETE, SVC_SET|SVC_ACTION},
+ {"disable", 0, sa_disable_group, USAGE_DISABLE, SVC_SET|SVC_ACTION},
+ {"enable", 0, sa_enable_group, USAGE_ENABLE, SVC_SET|SVC_ACTION},
+ {"list", 0, sa_list, USAGE_LIST},
+ {"move-share", 0, sa_moveshare, USAGE_MOVE_SHARE, SVC_SET},
+ {"remove-share", 0, sa_removeshare, USAGE_REMOVE_SHARE, SVC_SET},
+ {"set", 0, sa_set, USAGE_SET, SVC_SET},
+ {"set-share", 0, sa_set_share, USAGE_SET_SHARE, SVC_SET},
+ {"show", 0, sa_show, USAGE_SHOW},
+ {"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION},
+ {"start", CMD_NODISPLAY, sa_start_group, USAGE_START,
+ SVC_SET|SVC_ACTION},
+ {"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION},
+ {"unset", 0, sa_unset, USAGE_UNSET, SVC_SET},
+ {"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION},
+ {NULL, 0, NULL, NULL}
+};
+
+static char *
+sa_get_usage(sa_usage_t index)
+{
+ char *ret = NULL;
+ switch (index) {
+ case USAGE_ADD_SHARE:
+ ret = gettext("add-share [-nth] [-r resource-name] "
+ "[-d \"description text\"] -s sharepath group");
+ break;
+ case USAGE_CREATE:
+ ret = gettext("create [-nvh] [-P proto [-p property=value]] group");
+ break;
+ case USAGE_DELETE:
+ ret = gettext("delete [-nvh] [-P proto] [-f] group");
+ break;
+ case USAGE_DISABLE:
+ ret = gettext("disable [-nvh] {-a | group ...}");
+ break;
+ case USAGE_ENABLE:
+ ret = gettext("enable [-nvh] {-a | group ...}");
+ break;
+ case USAGE_LIST:
+ ret = gettext("list [-vh] [-P proto]");
+ break;
+ case USAGE_MOVE_SHARE:
+ ret = gettext("move-share [-nvh] -s sharepath destination-group");
+ break;
+ case USAGE_REMOVE_SHARE:
+ ret = gettext("remove-share [-fnvh] -s sharepath group");
+ break;
+ case USAGE_SET:
+ ret = gettext("set [-nvh] -P proto [-S optspace] "
+ "[-p property=value]* [-s sharepath] group");
+ break;
+ case USAGE_SET_SECURITY:
+ ret = gettext("set-security [-nvh] -P proto -S security-type "
+ "[-p property=value]* group");
+ break;
+ case USAGE_SET_SHARE:
+ ret = gettext("set-share [-nh] [-r resource] "
+ "[-d \"description text\"] -s sharepath group");
+ break;
+ case USAGE_SHOW:
+ ret = gettext("show [-pvxh] [-P proto] [group ...]");
+ break;
+ case USAGE_SHARE:
+ ret = gettext("share [-F fstype] [-p] [-o optionlist]"
+ "[-d description] [pathname [resourcename]]");
+ break;
+ case USAGE_START:
+ ret = gettext("start [-vh] [-P proto] {-a | group ...}");
+ break;
+ case USAGE_STOP:
+ ret = gettext("stop [-vh] [-P proto] {-a | group ...}");
+ break;
+ case USAGE_UNSET:
+ ret = gettext("unset [-nvh] -P proto [-S optspace] "
+ "[-p property]* group");
+ break;
+ case USAGE_UNSET_SECURITY:
+ ret = gettext("unset-security [-nvh] -P proto -S security-type "
+ "[-p property]* group");
+ break;
+ case USAGE_UNSHARE:
+ ret = gettext("unshare [-F fstype] [-p] [-o optionlist] sharepath");
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * sa_lookup(cmd, proto)
+ *
+ * Lookup the sub-command. proto isn't currently used, but it may
+ * eventually provide a way to provide protocol specific sub-commands.
+ */
+
+sa_command_t *
+sa_lookup(char *cmd, char *proto)
+{
+ int i;
+ size_t len;
+#ifdef lint
+ proto = proto;
+#endif
+
+ len = strlen(cmd);
+ for (i = 0; commands[i].cmdname != NULL; i++) {
+ if (strncmp(cmd, commands[i].cmdname, len) == 0)
+ return (&commands[i]);
+ }
+ return (NULL);
+}
+
+void
+sub_command_help(char *proto)
+{
+ int i;
+#ifdef lint
+ proto = proto;
+#endif
+
+ (void) printf(gettext("\tsub-commands:\n"));
+ for (i = 0; commands[i].cmdname != NULL; i++) {
+ if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
+ (void) printf("\t%s\n",
+ sa_get_usage((sa_usage_t)commands[i].cmdidx));
+ }
+}
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/group.xml b/usr/src/cmd/dfs.cmds/sharemgr/group.xml
new file mode 100644
index 0000000000..7f1140486d
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/group.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+-->
+
+<service_bundle type='manifest' name='SUNWshmr:group'>
+
+<service
+ name='network/shares/group'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <!-- Must have all local filesystems mounted before we share them -->
+ <dependency name='filesystem-local'
+ grouping='require_all'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/local' />
+ </dependency>
+
+ <!--
+ The start method will cause each share group to be
+ activated. since these are done in parallel and have been
+ pre-checked for sanity, the shareall equivalent should not
+ take overly long, but be prepared.
+ -->
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/sbin/sharemgr start %i'
+ timeout_seconds='3600' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec='/usr/sbin/sharemgr stop %i'
+ timeout_seconds='3600' />
+
+ <exec_method
+ type='method'
+ name='restart'
+ exec='/usr/sbin/sharemgr start %i'
+ timeout_seconds='3600' />
+
+ <exec_method
+ type='method'
+ name='refresh'
+ exec='/usr/sbin/sharemgr start %i'
+ timeout_seconds='3600' />
+
+ <property_group name='general' type='framework'>
+ <!-- to start/stop service -->
+ <propval name='action_authorization' type='astring'
+ value='solaris.smf.manage.group' />
+ <propval name='value_authorization' type='astring'
+ value='solaris.smf.manage.group' />
+ </property_group>
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring'
+ value='transient' />
+ </property_group>
+
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/i386/Makefile b/usr/src/cmd/dfs.cmds/sharemgr/i386/Makefile
new file mode 100644
index 0000000000..9201a4391d
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/plugins/Makefile b/usr/src/cmd/dfs.cmds/sharemgr/plugins/Makefile
new file mode 100644
index 0000000000..29601b6443
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/plugins/Makefile
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+DEFAULTFILES =
+
+include ../../../Makefile.cmd
+
+#
+# One for each ISA.
+#
+SUBDIRS = $(MACH)
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+_msg := TARGET= _msg
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+all clean clobber lint _msg: $(SUBDIRS)
+
+install: $(SUBDIRS) $(ROOTETCDEFAULTFILES)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/plugins/Makefile.com b/usr/src/cmd/dfs.cmds/sharemgr/plugins/Makefile.com
new file mode 100644
index 0000000000..4c93f157b8
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/plugins/Makefile.com
@@ -0,0 +1,129 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../../../../Makefile.cmd
+
+COMMON = ..
+
+NFS_MOD = libshare_nfs.so
+NFS_SRC = $(NFS_MOD:%.so=$(COMMON)/%.c)
+NFSLIB_DIR = $(SRC)/cmd/fs.d/nfs/lib
+
+SHAREDSRC1 = nfs_sec.c
+SHAREDSRC2 = nfslog_config.c
+SHAREDSRC3 = nfslogtab.c
+NFS_SRCS = $(COMMON)/$(SHAREDSRC1) \
+ $(COMMON)/$(SHAREDSRC2) $(COMMON)/$(SHAREDSRC3) $(NFS_SRC)
+NFS_OBJS = libshare_nfs.o nfs_sec.o nfslog_config.o nfslogtab.o
+
+LINK_SRCS = $(NFS_SRC)
+
+LINT_MODULES = $(LINK_SRCS:.c=.ln)
+# -u to eliminate XML warnings.
+LINTFLAGS += -u
+
+LINK_OBJS = $(NFS_OBJS)
+
+LINK_MODS = $(NFS_MOD)
+
+SRCS = $(LINK_SRCS)
+OBJS = $(LINK_OBJS)
+MODS = $(LINK_MODS)
+
+NFS_DIR = fs/nfs
+ROOTLIB_NFS_LINKMOD = $(ROOTLIB)/$(NFS_DIR)
+ROOTLIB_NFS_LINK_MODS = $(LINK_MODS:%=$(ROOTLIB_NFS_LINKMOD)/%)
+
+CLOBBERFILES = $(MODS) $(SHAREDSRC)
+
+MYCPPFLAGS = -I$(SRC)/lib/libshare/common -I$(SRC)/lib/libfsmgt/common \
+ -I/usr/include/libxml2 -I.. -I../.. \
+ -I../../../../fs.d/nfs/lib
+CPPFLAGS += $(MYCPPFLAGS)
+LDLIBS += -lshare -lxml2 -lnsl -lscf -lumem -lc
+LDFLAGS += -zdefs -zcombreloc
+CFLAGS += -Kpic
+
+POFILES = $(LINK_SRCS:.c=.po)
+POFILE = libshare_nfs.po
+
+OWNER= root
+GROUP= sys
+FILEMODE= 555
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+
+
+.KEEP_STATE:
+
+all: $(MODS)
+
+install: all \
+ $(ROOTLIB_NFS_LINKMOD) \
+ $(ROOTLIB_NFS_LINK_MODS)
+
+%.so: %.o
+ $(LINK.c) -o $@ $(GSHARED) -h $@ $<
+
+%.o: $(COMMON)/%.c
+ $(COMPILE.c) -o $@ $<
+
+$(NFS_MOD): $(NFS_OBJS)
+ $(LINK.c) -o $@ $(GSHARED) $(LDLIBS) -h $@ $(NFS_OBJS)
+ $(POST_PROCESS)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: $(LINT_MODULES)
+
+%.ln: FRC
+ $(LINT.c) $(@:.ln=.c) $(LDLIBS)
+
+FRC:
+
+include ../../../../Makefile.targ
+
+$(POFILE): $(POFILES)
+ $(RM) $@; cat $(POFILES) > $@
+
+$(ROOTLIB_NFS):
+ $(INS.dir)
+
+$(ROOTLIB_NFS_LINKMOD):
+ $(INS.dir)
+
+$(ROOTLIB_NFS_LINKMOD)/%.so: %.so
+ $(INS.file)
+
+%.o: $(NFSLIB_DIR)/%.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $< $(CTFCONVERT_HOOK)
+ $(POST_PROCESS_O)
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/plugins/i386/Makefile b/usr/src/cmd/dfs.cmds/sharemgr/plugins/i386/Makefile
new file mode 100644
index 0000000000..9201a4391d
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/plugins/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.c b/usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.c
new file mode 100644
index 0000000000..d2e012eb08
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.c
@@ -0,0 +1,2487 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * NFS specific functions
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <zone.h>
+#include <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include "libshare.h"
+#include "libshare_impl.h"
+#include <nfs/export.h>
+#include <pwd.h>
+#include <limits.h>
+#include <libscf.h>
+#include "nfslog_config.h"
+#include "nfslogtab.h"
+#include "libshare_nfs.h"
+#include <rpcsvc/daemon_utils.h>
+#include <nfs/nfs.h>
+
+/* should really be in some global place */
+#define DEF_WIN 30000
+#define OPT_CHUNK 1024
+
+int debug = 0;
+
+
+/* internal functions */
+static int nfs_init();
+static void nfs_fini();
+static int nfs_enable_share(sa_share_t);
+static int nfs_disable_share(char *);
+static int nfs_validate_property(sa_property_t, sa_optionset_t);
+static int nfs_validate_security_mode(char *);
+static int nfs_is_security_opt(char *);
+static int nfs_parse_legacy_options(sa_group_t, char *);
+static char *nfs_format_options(sa_group_t, int);
+static int nfs_set_proto_prop(sa_property_t);
+static sa_protocol_properties_t nfs_get_proto_set();
+static char *nfs_get_status();
+static char *nfs_space_alias(char *);
+
+/*
+ * ops vector that provides the protocol specific info and operations
+ * for share management.
+ */
+
+struct sa_plugin_ops sa_plugin_ops = {
+ SA_PLUGIN_VERSION,
+ "nfs",
+ nfs_init,
+ nfs_fini,
+ nfs_enable_share,
+ nfs_disable_share,
+ nfs_validate_property,
+ nfs_validate_security_mode,
+ nfs_is_security_opt,
+ nfs_parse_legacy_options,
+ nfs_format_options,
+ nfs_set_proto_prop,
+ nfs_get_proto_set,
+ nfs_get_status,
+ nfs_space_alias,
+ NULL,
+ NULL
+};
+
+/*
+ * list of support services needed
+ * defines should come from head/rpcsvc/daemon_utils.h
+ */
+
+static char *service_list_default[] =
+ { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NULL };
+static char *service_list_logging[] =
+ { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, NULL };
+
+/*
+ * option definitions. Make sure to keep the #define for the option
+ * index just before the entry it is the index for. Changing the order
+ * can cause breakage. E.g OPT_RW is index 1 and must precede the
+ * line that includes the SHOPT_RW and OPT_RW entries.
+ */
+
+struct option_defs optdefs[] = {
+#define OPT_RO 0
+ {SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST},
+#define OPT_RW 1
+ {SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST},
+#define OPT_ROOT 2
+ {SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST},
+#define OPT_SECURE 3
+ {SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED},
+#define OPT_ANON 4
+ {SHOPT_ANON, OPT_ANON, OPT_TYPE_USER},
+#define OPT_WINDOW 5
+ {SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER},
+#define OPT_NOSUID 6
+ {SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN},
+#define OPT_ACLOK 7
+ {SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN},
+#define OPT_NOSUB 8
+ {SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN},
+#define OPT_SEC 9
+ {SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY},
+#define OPT_PUBLIC 10
+ {SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY},
+#define OPT_INDEX 11
+ {SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE},
+#define OPT_LOG 12
+ {SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG},
+#define OPT_CKSUM 13
+ {SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET},
+#ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */
+#define OPT_VOLFH 14
+ {SHOPT_VOLFH, OPT_VOLFH},
+#endif /* VOLATILE_FH_TEST */
+ NULL
+};
+
+/*
+ * list of propertye that are related to security flavors.
+ */
+static char *seclist[] = {
+ SHOPT_RO,
+ SHOPT_RW,
+ SHOPT_ROOT,
+ SHOPT_WINDOW,
+ NULL
+};
+
+/* structure for list of securities */
+struct securities {
+ sa_security_t security;
+ struct securities *next;
+};
+
+/*
+ * findopt(name)
+ *
+ * Lookup option "name" in the option table and return the table
+ * index.
+ */
+
+static int
+findopt(char *name)
+{
+ int i;
+ if (name != NULL) {
+ for (i = 0; optdefs[i].tag != NULL; i++) {
+ if (strcmp(optdefs[i].tag, name) == 0)
+ return (i);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * gettype(name)
+ *
+ * Return the type of option "name".
+ */
+
+static int
+gettype(char *name)
+{
+ int optdef;
+
+ optdef = findopt(name);
+ if (optdef != -1)
+ return (optdefs[optdef].type);
+ return (OPT_TYPE_ANY);
+}
+
+/*
+ * nfs_validate_security_mode(mode)
+ *
+ * is the specified mode string a valid one for use with NFS?
+ */
+
+static int
+nfs_validate_security_mode(char *mode)
+{
+ seconfig_t secinfo;
+ int err;
+
+ (void) memset(&secinfo, '\0', sizeof (secinfo));
+ err = nfs_getseconfig_byname(mode, &secinfo);
+ if (err == SC_NOERROR)
+ return (1);
+ return (0);
+}
+
+/*
+ * nfs_is_security_opt(tok)
+ *
+ * check to see if tok represents an option that is only valid in some
+ * security flavor.
+ */
+
+static int
+nfs_is_security_opt(char *tok)
+{
+ int i;
+
+ for (i = 0; seclist[i] != NULL; i++) {
+ if (strcmp(tok, seclist[i]) == 0)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * find_security(seclist, sec)
+ *
+ * Walk the current list of security flavors and return true if it is
+ * present, else return false.
+ */
+
+static int
+find_security(struct securities *seclist, sa_security_t sec)
+{
+ while (seclist != NULL) {
+ if (seclist->security == sec)
+ return (1);
+ seclist = seclist->next;
+ }
+ return (0);
+}
+
+/*
+ * make_security_list(group, securitymodes, proto)
+ * go through the list of securitymodes and add them to the
+ * group's list of security optionsets. We also keep a list of
+ * those optionsets so we don't have to find them later. All of
+ * these will get copies of the same properties.
+ */
+
+static struct securities *
+make_security_list(sa_group_t group, char *securitymodes, char *proto)
+{
+ char *tok, *next = NULL;
+ struct securities *curp, *headp = NULL, *prev;
+ sa_security_t check;
+ int freetok = 0;
+
+ for (tok = securitymodes; tok != NULL; tok = next) {
+ next = strchr(tok, ':');
+ if (next != NULL)
+ *next++ = '\0';
+ if (strcmp(tok, "default") == 0) {
+ /* resolve default into the real type */
+ tok = nfs_space_alias(tok);
+ freetok = 1;
+ }
+ check = sa_get_security(group, tok, proto);
+
+ /* add to the security list if it isn't there already */
+ if (check == NULL || !find_security(headp, check)) {
+ curp = (struct securities *)calloc(1,
+ sizeof (struct securities));
+ if (curp != NULL) {
+ if (check == NULL) {
+ curp->security = sa_create_security(group, tok,
+ proto);
+ } else {
+ curp->security = check;
+ }
+ /*
+ * note that the first time through the loop,
+ * headp will be NULL and prev will be
+ * undefined. Since headp is NULL, we set
+ * both it and prev to the curp (first
+ * structure to be allocated).
+ *
+ * later passes through the loop will have
+ * headp not being NULL and prev will be used
+ * to allocate at the end of the list.
+ */
+ if (headp == NULL) {
+ headp = curp;
+ prev = curp;
+ } else {
+ prev->next = curp;
+ prev = curp;
+ }
+ }
+ }
+ if (freetok) {
+ freetok = 0;
+ sa_free_attr_string(tok);
+ }
+ }
+ return (headp);
+}
+
+static void
+free_security_list(struct securities *sec)
+{
+ struct securities *next;
+ if (sec != NULL) {
+ for (next = sec->next; sec != NULL; sec = next) {
+ next = sec->next;
+ free(sec);
+ }
+ }
+}
+
+/*
+ * nfs_alistcat(str1, str2, sep)
+ *
+ * concatenate str1 and str2 into a new string using sep as a separate
+ * character. If memory allocation fails, return NULL;
+ */
+
+static char *
+nfs_alistcat(char *str1, char *str2, char sep)
+{
+ char *newstr;
+ size_t len;
+
+ len = strlen(str1) + strlen(str2) + 2;
+ newstr = (char *)malloc(len);
+ if (newstr != NULL)
+ (void) snprintf(newstr, len, "%s%c%s", str1, sep, str2);
+ return (newstr);
+}
+
+/*
+ * add_security_prop(sec, name, value, persist)
+ *
+ * Add the property to the securities structure. This accumulates
+ * properties for as part of parsing legacy options.
+ */
+
+static int
+add_security_prop(struct securities *sec, char *name, char *value,
+ int persist, int iszfs)
+{
+ sa_property_t prop;
+ int ret = SA_OK;
+
+ for (; sec != NULL; sec = sec->next) {
+ if (value == NULL) {
+ if (strcmp(name, SHOPT_RW) == 0 || strcmp(name, SHOPT_RO) == 0)
+ value = "*";
+ else
+ value = "true";
+ }
+ prop = sa_get_property(sec->security, name);
+ if (prop != NULL) {
+ char *oldvalue;
+ char *newvalue;
+ /*
+ * The security options of ro/rw/root might appear
+ * multiple times. If they do, the values need to be
+ * merged into an access list. If it was previously
+ * empty, the new value alone is added.
+ */
+ oldvalue = sa_get_property_attr(prop, "value");
+ if (oldvalue != NULL) {
+ newvalue = nfs_alistcat(oldvalue, value, ':');
+ if (newvalue != NULL)
+ value = newvalue;
+ (void) sa_remove_property(prop);
+ prop = sa_create_property(name, value);
+ ret = sa_add_property(sec->security, prop);
+ if (newvalue != NULL)
+ free(newvalue);
+ if (oldvalue != NULL)
+ sa_free_attr_string(oldvalue);
+ }
+ } else {
+ prop = sa_create_property(name, value);
+ ret = sa_add_property(sec->security, prop);
+ }
+ if (ret == SA_OK && !iszfs) {
+ ret = sa_commit_properties(sec->security, !persist);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * check to see if group/share is persistent.
+ */
+static int
+is_persistent(sa_group_t group)
+{
+ char *type;
+ int persist = 1;
+
+ type = sa_get_group_attr(group, "type");
+ if (type != NULL && strcmp(type, "persist") != 0)
+ persist = 0;
+ if (type != NULL)
+ sa_free_attr_string(type);
+ return (persist);
+}
+
+/*
+ * invalid_security(options)
+ *
+ * search option string for any invalid sec= type.
+ * return true (1) if any are not valid else false (0)
+ */
+static int
+invalid_security(char *options)
+{
+ char *copy, *base, *token, *value;
+ int ret = 0;
+
+ copy = strdup(options);
+ token = base = copy;
+ while (token != NULL && ret == 0) {
+ token = strtok(base, ",");
+ base = NULL;
+ if (token != NULL) {
+ value = strchr(token, '=');
+ if (value != NULL)
+ *value++ = '\0';
+ if (strcmp(token, "sec") == 0) {
+ /* have security flavors so check them */
+ char *tok, *next;
+ for (next = NULL, tok = value; tok != NULL; tok = next) {
+ next = strchr(tok, ':');
+ if (next != NULL)
+ *next++ = '\0';
+ ret = !nfs_validate_security_mode(tok);
+ if (ret)
+ break;
+ }
+ }
+ }
+ }
+ if (copy != NULL)
+ free(copy);
+ return (ret);
+}
+
+/*
+ * nfs_parse_legacy_options(group, options)
+ *
+ * Parse the old style options into internal format and store on the
+ * specified group. Group could be a share for full legacy support.
+ */
+
+static int
+nfs_parse_legacy_options(sa_group_t group, char *options)
+{
+ char *dup = strdup(options);
+ char *base;
+ char *token;
+ sa_optionset_t optionset;
+ struct securities *security_list = NULL;
+ sa_property_t prop;
+ int ret = SA_OK;
+ int iszfs = 0;
+ sa_group_t parent;
+ int persist = 0;
+ char *lasts;
+
+ /* do we have an existing optionset? */
+ optionset = sa_get_optionset(group, "nfs");
+ if (optionset == NULL) {
+ /* didn't find existing optionset so create one */
+ optionset = sa_create_optionset(group, "nfs");
+ } else {
+ /*
+ * have an existing optionset so we need to compare
+ * options in order to detect errors. For now, we
+ * assume that the first optionset is the correct one
+ * and the others will be the same. This needs to be
+ * fixed before the final code is ready.
+ */
+ return (ret);
+ }
+
+ if (strcmp(options, SHOPT_RW) == 0) {
+ /*
+ * there is a special case of only the option "rw"
+ * being the default option. We don't have to do
+ * anything.
+ */
+ return (ret);
+ }
+
+ /*
+ * check if security types are present and validate them. If
+ * any are not legal, fail.
+ */
+
+ if (invalid_security(options)) {
+ return (SA_INVALID_SECURITY);
+ }
+
+ /*
+ * in order to not attempt to change ZFS properties unless
+ * absolutely necessary, we never do it in the legacy parsing.
+ */
+ if (sa_is_share(group)) {
+ char *zfs;
+ parent = sa_get_parent_group(group);
+ if (parent != NULL) {
+ zfs = sa_get_group_attr(parent, "zfs");
+ if (zfs != NULL) {
+ sa_free_attr_string(zfs);
+ iszfs++;
+ }
+ }
+ } else {
+ iszfs = sa_group_is_zfs(group);
+ }
+
+ /*
+ * we need to step through each option in the string and then
+ * add either the option or the security option as needed. If
+ * this is not a persistent share, don't commit to the
+ * repository.
+ */
+ persist = is_persistent(group);
+ base = dup;
+ token = dup;
+ lasts = NULL;
+ while (token != NULL) {
+ ret = SA_OK;
+ token = strtok_r(base, ",", &lasts);
+ base = NULL;
+ if (token != NULL) {
+ char *value;
+ /*
+ * if the option has a value, it will have an '=' to
+ * separate the name from the value. The following
+ * code will result in value != NULL and token
+ * pointing to just the name if there is a value.
+ */
+ value = strchr(token, '=');
+ if (value != NULL) {
+ *value++ = '\0';
+ }
+ if (strcmp(token, "sec") == 0 || strcmp(token, "secure") == 0) {
+ /*
+ * Once in security parsing, we only
+ * do security. We do need to move
+ * between the security node and the
+ * toplevel. The security tag goes on
+ * the root while the following ones
+ * go on the security.
+ */
+ if (security_list != NULL) {
+ /* have an old list so close it and start the new */
+ free_security_list(security_list);
+ }
+ if (strcmp(token, "secure") == 0) {
+ value = "dh";
+ } else {
+ if (value == NULL) {
+ ret = SA_SYNTAX_ERR;
+ break;
+ }
+ }
+ security_list = make_security_list(group, value, "nfs");
+ } else {
+ /*
+ * Note that the "old" syntax allowed a
+ * default security model This must be
+ * accounted for and internally converted to
+ * "standard" security structure.
+ */
+ if (nfs_is_security_opt(token)) {
+ if (security_list == NULL) {
+ /*
+ * need to have a security option. This
+ * will be "closed" when a defined "sec="
+ * option is seen. This is technically an
+ * error but will be allowed with warning.
+ */
+ security_list = make_security_list(group,
+ "default",
+ "nfs");
+ }
+ if (security_list != NULL) {
+ ret = add_security_prop(security_list, token,
+ value, persist,
+ iszfs);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ } else {
+ /* regular options */
+ if (value == NULL) {
+ if (strcmp(token, SHOPT_RW) == 0 ||
+ strcmp(token, SHOPT_RO) == 0)
+ value = "*";
+ else if (strcmp(token, SHOPT_LOG) == 0)
+ value = "global";
+ else
+ value = "true";
+ }
+ prop = sa_create_property(token, value);
+ ret = sa_add_property(optionset, prop);
+ if (ret != SA_OK) {
+ break;
+ }
+ if (!iszfs) {
+ ret = sa_commit_properties(optionset, !persist);
+ }
+ }
+ }
+ }
+ }
+ if (security_list != NULL)
+ free_security_list(security_list);
+ if (dup != NULL)
+ free(dup);
+ return (ret);
+}
+
+/*
+ * is_a_number(number)
+ *
+ * is the string a number in one of the forms we want to use?
+ */
+
+static int
+is_a_number(char *number)
+{
+ int ret = 1;
+ int hex = 0;
+
+ if (strncmp(number, "0x", 2) == 0) {
+ number += 2;
+ hex = 1;
+ } else if (*number == '-')
+ number++; /* skip the minus */
+
+ while (ret == 1 && *number != '\0') {
+ if (hex) {
+ ret = isxdigit(*number++);
+ } else {
+ ret = isdigit(*number++);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Look for the specified tag in the configuration file. If it is found,
+ * enable logging and set the logging configuration information for exp.
+ */
+static void
+configlog(struct exportdata *exp, char *tag)
+{
+ nfsl_config_t *configlist = NULL, *configp;
+ int error = 0;
+ char globaltag[] = DEFAULTTAG;
+
+ /*
+ * Sends config errors to stderr
+ */
+ nfsl_errs_to_syslog = B_FALSE;
+
+ /*
+ * get the list of configuration settings
+ */
+ error = nfsl_getconfig_list(&configlist);
+ if (error) {
+ (void) fprintf(stderr,
+ gettext("Cannot get log configuration: %s\n"),
+ strerror(error));
+ }
+
+ if (tag == NULL)
+ tag = globaltag;
+ if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) {
+ nfsl_freeconfig_list(&configlist);
+ (void) fprintf(stderr,
+ gettext("No tags matching \"%s\"\n"), tag);
+ /* bad configuration */
+ error = ENOENT;
+ goto err;
+ }
+
+ if ((exp->ex_tag = strdup(tag)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ exp->ex_flags |= EX_LOG;
+ if (configp->nc_rpclogpath != NULL)
+ exp->ex_flags |= EX_LOG_ALLOPS;
+out:
+ if (configlist != NULL)
+ nfsl_freeconfig_list(&configlist);
+
+err:
+ if (error != 0) {
+ if (exp->ex_flags != NULL)
+ free(exp->ex_tag);
+ if (exp->ex_log_buffer != NULL)
+ free(exp->ex_log_buffer);
+ (void) fprintf(stderr,
+ gettext("Cannot set log configuration: %s\n"),
+ strerror(error));
+ }
+}
+
+/*
+ * fill_export_from_optionset(export, optionset)
+ *
+ * In order to share, we need to set all the possible general options
+ * into the export structure. Share info will be filled in by the
+ * caller. Various property values get turned into structure specific
+ * values.
+ */
+
+static int
+fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset)
+{
+ sa_property_t option;
+ int ret = SA_OK;
+
+ for (option = sa_get_property(optionset, NULL);
+ option != NULL; option = sa_get_next_property(option)) {
+ char *name;
+ char *value;
+ uint32_t val;
+
+ /*
+ * since options may be set/reset multiple times, always do an
+ * explicit set or clear of the option. This allows defaults
+ * to be set and then the protocol specifici to override.
+ */
+
+ name = sa_get_property_attr(option, "type");
+ value = sa_get_property_attr(option, "value");
+ switch (findopt(name)) {
+ case OPT_ANON:
+ if (value != NULL && is_a_number(value)) {
+ val = strtoul(value, NULL, 0);
+ } else {
+ struct passwd *pw;
+ pw = getpwnam(value != NULL ? value : "nobody");
+ if (pw != NULL) {
+ val = pw->pw_uid;
+ } else {
+ val = UID_NOBODY;
+ }
+ endpwent();
+ }
+ export->ex_anon = val;
+ break;
+ case OPT_NOSUID:
+ if (value != NULL &&
+ (strcasecmp(value, "true") == 0 || strcmp(value, "1") == 0))
+ export->ex_flags |= EX_NOSUID;
+ else
+ export->ex_flags &= ~EX_NOSUID;
+ break;
+ case OPT_ACLOK:
+ if (value != NULL &&
+ (strcasecmp(value, "true") == 0 ||
+ strcmp(value, "1") == 0))
+ export->ex_flags |= EX_ACLOK;
+ else
+ export->ex_flags &= ~EX_ACLOK;
+ break;
+ case OPT_NOSUB:
+ if (value != NULL &&
+ (strcasecmp(value, "true") == 0 || strcmp(value, "1") == 0))
+ export->ex_flags |= EX_NOSUB;
+ else
+ export->ex_flags &= ~EX_NOSUB;
+ break;
+ case OPT_PUBLIC:
+ if (value != NULL &&
+ (strcasecmp(value, "true") == 0 || strcmp(value, "1") == 0))
+ export->ex_flags |= EX_PUBLIC;
+ else
+ export->ex_flags &= ~EX_PUBLIC;
+ break;
+ case OPT_INDEX:
+ if (value != NULL &&
+ (strcmp(value, "..") == 0 || strchr(value, '/') != NULL)) {
+ /* this is an error */
+ (void) printf(gettext("NFS: index=\"%s\" not valid;"
+ "must be a filename.\n"),
+ value);
+ break;
+ }
+ if (value != NULL && *value != '\0' &&
+ strcmp(value, ".") != 0) {
+ /* valid index file string */
+ if (export->ex_index != NULL) {
+ /* left over from "default" */
+ free(export->ex_index);
+ }
+ export->ex_index = strdup(value); /* remember to free */
+ if (export->ex_index == NULL) {
+ (void) printf(gettext("NFS: out of memory setting "
+ "index property\n"));
+ break;
+ }
+ export->ex_flags |= EX_INDEX;
+ }
+ break;
+ case OPT_LOG:
+ if (value == NULL)
+ value = strdup("global");
+ if (value != NULL)
+ configlog(export, strlen(value) ? value : "global");
+ break;
+ case OPT_CKSUM:
+ /* TBD: not ready yet */
+ break;
+ default:
+ /* have a syntactic error */
+ (void) printf(gettext("NFS: unrecognized option %s=%s\n"),
+ name, value != NULL ? value : "");
+ break;
+ }
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (value != NULL)
+ sa_free_attr_string(value);
+ }
+ return (ret);
+}
+
+/*
+ * cleanup_export(export)
+ *
+ * Cleanup the allocated areas so we don't leak memory
+ */
+
+static void
+cleanup_export(struct exportdata *export)
+{
+ int i;
+
+ if (export->ex_index != NULL)
+ free(export->ex_index);
+ if (export->ex_secinfo != NULL) {
+ for (i = 0; i < export->ex_seccnt; i++)
+ if (export->ex_secinfo[i].s_rootnames != NULL) {
+ free(export->ex_secinfo[i].s_rootnames);
+ }
+ free(export->ex_secinfo);
+ }
+}
+
+/*
+ * Given a seconfig entry and a colon-separated
+ * list of names, allocate an array big enough
+ * to hold the root list, then convert each name to
+ * a principal name according to the security
+ * info and assign it to an array element.
+ * Return the array and its size.
+ */
+static caddr_t *
+get_rootnames(seconfig_t *sec, char *list, int *count)
+{
+ caddr_t *a;
+ int c, i;
+ char *host, *p;
+
+ /*
+ * Count the number of strings in the list.
+ * This is the number of colon separators + 1.
+ */
+ c = 1;
+ for (p = list; *p; p++)
+ if (*p == ':')
+ c++;
+ *count = c;
+
+ a = (caddr_t *)malloc(c * sizeof (char *));
+ if (a == NULL) {
+ (void) printf(gettext("get_rootnames: no memory\n"));
+ } else {
+ for (i = 0; i < c; i++) {
+ host = strtok(list, ":");
+ if (!nfs_get_root_principal(sec, host, &a[i])) {
+ free(a);
+ a = NULL;
+ break;
+ }
+ list = NULL;
+ }
+ }
+
+ return (a);
+}
+
+/*
+ * fill_security_from_secopts(sp, secopts)
+ *
+ * Fill the secinfo structure from the secopts optionset.
+ */
+
+static int
+fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts)
+{
+ sa_property_t prop;
+ char *type;
+ int longform;
+ int err = 0;
+
+ type = sa_get_security_attr(secopts, "sectype");
+ if (type != NULL) {
+ /* named security type needs secinfo to be filled in */
+ err = nfs_getseconfig_byname(type, &sp->s_secinfo);
+ sa_free_attr_string(type);
+ if (err != SC_NOERROR)
+ return (err);
+ } else {
+ /* default case */
+ err = nfs_getseconfig_default(&sp->s_secinfo);
+ if (err != SC_NOERROR)
+ return (err);
+ }
+
+ for (prop = sa_get_property(secopts, NULL);
+ prop != NULL; prop = sa_get_next_property(prop)) {
+ char *name;
+ char *value;
+
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+
+ longform = value != NULL && strcmp(value, "*") != 0;
+
+ switch (findopt(name)) {
+ case OPT_RO:
+ sp->s_flags |= longform ? M_ROL : M_RO;
+ break;
+ case OPT_RW:
+ sp->s_flags |= longform ? M_RWL : M_RW;
+ break;
+ case OPT_ROOT:
+ sp->s_flags |= M_ROOT;
+ /*
+ * if we are using AUTH_UNIX, handle like other things
+ * such as RO/RW
+ */
+ if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX)
+ continue;
+ /* not AUTH_UNIX */
+ if (value != NULL)
+ sp->s_rootnames = get_rootnames(&sp->s_secinfo, value,
+ &sp->s_rootcnt);
+ break;
+ case OPT_WINDOW:
+ if (value != NULL) {
+ sp->s_window = atoi(value);
+ if (sp->s_window < 0)
+ sp->s_window = DEF_WIN; /* just in case */
+ }
+ break;
+ default:
+ break;
+ }
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (value != NULL)
+ sa_free_attr_string(value);
+ }
+ /* if rw/ro options not set, use default of RW */
+ if ((sp->s_flags & NFS_RWMODES) == 0)
+ sp->s_flags |= M_RW;
+ return (err);
+}
+
+/*
+ * This is for testing only
+ * It displays the export structure that
+ * goes into the kernel.
+ */
+static void
+printarg(char *path, struct exportdata *ep)
+{
+ int i, j;
+ struct secinfo *sp;
+
+ if (debug == 0)
+ return;
+
+ (void) printf("%s:\n", path);
+ (void) printf("\tex_version = %d\n", ep->ex_version);
+ (void) printf("\tex_path = %s\n", ep->ex_path);
+ (void) printf("\tex_pathlen = %d\n", ep->ex_pathlen);
+ (void) printf("\tex_flags: (0x%02x) ", ep->ex_flags);
+ if (ep->ex_flags & EX_NOSUID)
+ (void) printf("NOSUID ");
+ if (ep->ex_flags & EX_ACLOK)
+ (void) printf("ACLOK ");
+ if (ep->ex_flags & EX_PUBLIC)
+ (void) printf("PUBLIC ");
+ if (ep->ex_flags & EX_NOSUB)
+ (void) printf("NOSUB ");
+ if (ep->ex_flags & EX_LOG)
+ (void) printf("LOG ");
+ if (ep->ex_flags & EX_LOG_ALLOPS)
+ (void) printf("LOG_ALLOPS ");
+ if (ep->ex_flags == 0)
+ (void) printf("(none)");
+ (void) printf("\n");
+ if (ep->ex_flags & EX_LOG) {
+ (void) printf("\tex_log_buffer = %s\n",
+ (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)"));
+ (void) printf("\tex_tag = %s\n",
+ (ep->ex_tag ? ep->ex_tag : "(NULL)"));
+ }
+ (void) printf("\tex_anon = %d\n", ep->ex_anon);
+ (void) printf("\tex_seccnt = %d\n", ep->ex_seccnt);
+ (void) printf("\n");
+ for (i = 0; i < ep->ex_seccnt; i++) {
+ sp = &ep->ex_secinfo[i];
+ (void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name);
+ (void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags);
+ if (sp->s_flags & M_ROOT) (void) printf("M_ROOT ");
+ if (sp->s_flags & M_RO) (void) printf("M_RO ");
+ if (sp->s_flags & M_ROL) (void) printf("M_ROL ");
+ if (sp->s_flags & M_RW) (void) printf("M_RW ");
+ if (sp->s_flags & M_RWL) (void) printf("M_RWL ");
+ if (sp->s_flags == 0) (void) printf("(none)");
+ (void) printf("\n");
+ (void) printf("\t\ts_window = %d\n", sp->s_window);
+ (void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt);
+ (void) fflush(stdout);
+ for (j = 0; j < sp->s_rootcnt; j++)
+ (void) printf("%s ", sp->s_rootnames[j] ?
+ sp->s_rootnames[j] : "<null>");
+ (void) printf("\n\n");
+ }
+}
+
+/*
+ * count_security(opts)
+ *
+ * Count the number of security types (flavors). The optionset has
+ * been populated with the security flavors as a holding mechanism.
+ * We later use this number to allocate data structures.
+ */
+
+static int
+count_security(sa_optionset_t opts)
+{
+ int count = 0;
+ sa_property_t prop;
+ if (opts != NULL) {
+ for (prop = sa_get_property(opts, NULL); prop != NULL;
+ prop = sa_get_next_property(prop)) {
+ count++;
+ }
+ }
+ return (count);
+}
+
+/*
+ * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep)
+ *
+ * provides a mechanism to format NFS properties into legacy output
+ * format. If the buffer would overflow, it is reallocated and grown
+ * as appropriate. Special cases of converting internal form of values
+ * to those used by "share" are done. this function does one property
+ * at a time.
+ */
+
+static void
+nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
+ sa_property_t prop, int sep)
+{
+ char *name;
+ char *value;
+ int curlen;
+ char *buff = *rbuff;
+ size_t buffsize = *rbuffsize;
+
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (buff != NULL)
+ curlen = strlen(buff);
+ else
+ curlen = 0;
+ if (name != NULL) {
+ int len;
+ len = strlen(name) + sep;
+
+ /*
+ * A future RFE would be to replace this with more
+ * generic code and to possibly handle more types.
+ */
+ switch (gettype(name)) {
+ case OPT_TYPE_BOOLEAN:
+ if (value != NULL && strcasecmp(value, "false") == 0) {
+ *name = '\0';
+ }
+ if (value != NULL)
+ sa_free_attr_string(value);
+ value = NULL;
+ break;
+ case OPT_TYPE_ACCLIST:
+ if (value != NULL && strcmp(value, "*") == 0) {
+ sa_free_attr_string(value);
+ value = NULL;
+ } else {
+ if (value != NULL)
+ len += 1 + strlen(value);
+ }
+ break;
+ case OPT_TYPE_LOGTAG:
+ if (value != NULL && strlen(value) == 0) {
+ sa_free_attr_string(value);
+ value = NULL;
+ } else {
+ if (value != NULL)
+ len += 1 + strlen(value);
+ }
+ break;
+ default:
+ if (value != NULL)
+ len += 1 + strlen(value);
+ break;
+ }
+ while (buffsize <= (curlen + len)) {
+ /* need more room */
+ buffsize += incr;
+ buff = realloc(buff, buffsize);
+ if (buff == NULL) {
+ /* realloc failed so free everything */
+ if (*rbuff != NULL)
+ free(*rbuff);
+ }
+ *rbuff = buff;
+ *rbuffsize = buffsize;
+ if (buff == NULL) {
+ return;
+ }
+ }
+ if (buff == NULL)
+ return;
+ if (value == NULL)
+ (void) snprintf(buff + curlen, buffsize - curlen,
+ "%s%s", sep ? "," : "",
+ name, value != NULL ? value : "");
+ else
+ (void) snprintf(buff + curlen, buffsize - curlen,
+ "%s%s=%s", sep ? "," : "",
+ name, value != NULL ? value : "");
+ }
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (value != NULL)
+ sa_free_attr_string(value);
+}
+
+/*
+ * nfs_format_options(group, hier)
+ *
+ * format all the options on the group into an old-style option
+ * string. If hier is non-zero, walk up the tree to get inherited
+ * options.
+ */
+
+static char *
+nfs_format_options(sa_group_t group, int hier)
+{
+ sa_optionset_t options = NULL;
+ sa_optionset_t secoptions;
+ sa_property_t prop, secprop;
+ sa_security_t security;
+ char *buff;
+ size_t buffsize;
+
+ options = sa_get_derived_optionset(group, "nfs", hier);
+
+ /*
+ * have a an optionset relative to this item, if any. format
+ * these then add any security definitions.
+ */
+ buff = malloc(OPT_CHUNK);
+ if (buff != NULL) {
+ int sep = 0;
+ buff[0] = '\0';
+ buffsize = OPT_CHUNK;
+ /*
+ * do the default set first but skip any option that is also
+ * in the protocol specific optionset.
+ */
+ if (options != NULL) {
+ for (prop = sa_get_property(options, NULL); prop != NULL;
+ prop = sa_get_next_property(prop)) {
+ /*
+ * use this one since we skipped any of these that
+ * were also in optdefault
+ */
+ nfs_sprint_option(&buff, &buffsize, OPT_CHUNK, prop, sep);
+ if (buff == NULL) {
+ /*
+ * buff could become NULL if there isn't
+ * enough memory for nfs_sprint_option to
+ * realloc() as necessary. We can't really do
+ * anything about it at this point so we
+ * return NULL. The caller should handle the
+ * failure. Note that this
+ */
+ return (buff);
+ }
+ sep = 1;
+ }
+ }
+ secoptions = (sa_optionset_t)sa_get_all_security_types(group,
+ "nfs", hier);
+ if (secoptions != NULL) {
+ for (secprop = sa_get_property(secoptions, NULL);
+ secprop != NULL; secprop = sa_get_next_property(secprop)) {
+ char *sectype;
+
+ sectype = sa_get_property_attr(secprop, "type");
+ security = (sa_security_t)sa_get_derived_security(group,
+ sectype,
+ "nfs", hier);
+ if (security != NULL) {
+ if (sectype != NULL) {
+ prop = sa_create_property("sec", sectype);
+ nfs_sprint_option(&buff, &buffsize, OPT_CHUNK,
+ prop, sep);
+ (void) sa_remove_property(prop);
+ sep = 1;
+ }
+ for (prop = sa_get_property(security, NULL);
+ prop != NULL;
+ prop = sa_get_next_property(prop)) {
+
+ nfs_sprint_option(&buff, &buffsize, OPT_CHUNK,
+ prop, sep);
+ if (buff == NULL) {
+ /* catastrophic memory failure */
+ sa_free_derived_optionset(secoptions);
+ if (security != NULL)
+ sa_free_derived_optionset(security);
+ if (sectype != NULL)
+ sa_free_attr_string(sectype);
+ if (options != NULL)
+ sa_free_derived_optionset(options);
+ return (buff);
+ }
+ sep = 1;
+ }
+ sa_free_derived_optionset(security);
+ }
+ if (sectype != NULL)
+ sa_free_attr_string(sectype);
+ }
+ sa_free_derived_optionset(secoptions);
+ }
+ }
+ if (options != NULL)
+ sa_free_derived_optionset(options);
+ return (buff);
+}
+/*
+ * Append an entry to the nfslogtab file
+ */
+static int
+nfslogtab_add(dir, buffer, tag)
+ char *dir, *buffer, *tag;
+{
+ FILE *f;
+ struct logtab_ent lep;
+ int error = 0;
+
+ /*
+ * Open the file for update and create it if necessary.
+ * This may leave the I/O offset at the end of the file,
+ * so rewind back to the beginning of the file.
+ */
+ f = fopen(NFSLOGTAB, "a+");
+ if (f == NULL) {
+ error = errno;
+ goto out;
+ }
+ rewind(f);
+
+ if (lockf(fileno(f), F_LOCK, 0L) < 0) {
+ (void) fprintf(stderr, gettext(
+ "share complete, however failed to lock %s "
+ "for update: %s\n"), NFSLOGTAB, strerror(errno));
+ error = -1;
+ goto out;
+ }
+
+ if (logtab_deactivate_after_boot(f) == -1) {
+ (void) fprintf(stderr, gettext(
+ "share complete, however could not deactivate "
+ "entries in %s\n"), NFSLOGTAB);
+ error = -1;
+ goto out;
+ }
+
+ /*
+ * Remove entries matching buffer and sharepoint since we're
+ * going to replace it with perhaps an entry with a new tag.
+ */
+ if (logtab_rement(f, buffer, dir, NULL, -1)) {
+ (void) fprintf(stderr, gettext(
+ "share complete, however could not remove matching "
+ "entries in %s\n"), NFSLOGTAB);
+ error = -1;
+ goto out;
+ }
+
+ /*
+ * Deactivate all active entries matching this sharepoint
+ */
+ if (logtab_deactivate(f, NULL, dir, NULL)) {
+ (void) fprintf(stderr, gettext(
+ "share complete, however could not deactivate matching "
+ "entries in %s\n"), NFSLOGTAB);
+ error = -1;
+ goto out;
+ }
+
+ lep.le_buffer = buffer;
+ lep.le_path = dir;
+ lep.le_tag = tag;
+ lep.le_state = LES_ACTIVE;
+
+ /*
+ * Add new sharepoint / buffer location to nfslogtab
+ */
+ if (logtab_putent(f, &lep) < 0) {
+ (void) fprintf(stderr, gettext(
+ "share complete, however could not add %s to %s\n"),
+ dir, NFSLOGTAB);
+ error = -1;
+ }
+
+out:
+ if (f != NULL)
+ (void) fclose(f);
+ return (error);
+}
+
+/*
+ * Deactivate an entry from the nfslogtab file
+ */
+static int
+nfslogtab_deactivate(path)
+ char *path;
+{
+ FILE *f;
+ int error = 0;
+
+ f = fopen(NFSLOGTAB, "r+");
+ if (f == NULL) {
+ error = errno;
+ goto out;
+ }
+ if (lockf(fileno(f), F_LOCK, 0L) < 0) {
+ error = errno;
+ (void) fprintf(stderr, gettext(
+ "share complete, however could not lock %s for "
+ "update: %s\n"), NFSLOGTAB, strerror(error));
+ goto out;
+ }
+ if (logtab_deactivate(f, NULL, path, NULL) == -1) {
+ error = -1;
+ (void) fprintf(stderr,
+ gettext("share complete, however could not "
+ "deactivate %s in %s\n"), path, NFSLOGTAB);
+ goto out;
+ }
+
+out: if (f != NULL)
+ (void) fclose(f);
+
+ return (error);
+}
+
+/*
+ * public_exists(share)
+ *
+ * check to see if public option is set on any other share than the
+ * one specified.
+ */
+static int
+public_exists(sa_share_t skipshare)
+{
+ sa_share_t share;
+ sa_group_t group;
+ sa_optionset_t opt;
+ sa_property_t prop;
+ int exists = 0;
+
+ for (group = sa_get_group(NULL); group != NULL;
+ group = sa_get_next_group(group)) {
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ if (share == skipshare)
+ continue;
+ opt = sa_get_optionset(share, "nfs");
+ if (opt != NULL) {
+ prop = sa_get_property(opt, "public");
+ if (prop != NULL) {
+ char *shared;
+ shared = sa_get_share_attr(share, "shared");
+ if (shared != NULL) {
+ exists = strcmp(shared, "true") == 0;
+ sa_free_attr_string(shared);
+ goto out;
+ }
+ }
+ }
+ }
+ }
+out:
+ return (exists);
+}
+
+/*
+ * sa_enable_share at the protocol level, enable_share must tell the
+ * implementation that it is to enable the share. This entails
+ * converting the path and options into the appropriate ioctl
+ * calls. It is assumed that all error checking of paths, etc. were
+ * done earlier.
+ */
+static int
+nfs_enable_share(sa_share_t share)
+{
+ struct exportdata export;
+ sa_optionset_t secoptlist;
+ struct secinfo *sp;
+ int num_secinfo;
+ sa_optionset_t opt;
+ sa_security_t sec;
+ sa_property_t prop;
+ char *path;
+ int err = SA_OK;
+
+ /* Don't drop core if the NFS module isn't loaded. */
+ (void) signal(SIGSYS, SIG_IGN);
+
+ /* get the path since it is important in several places */
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL)
+ return (SA_NO_SUCH_PATH);
+
+ /*
+ * find the optionsets and security sets. There may not be
+ * any or there could be one or two for each of optionset and
+ * security may have multiple, one per security type per
+ * protocol type.
+ */
+ opt = sa_get_derived_optionset(share, "nfs", 1);
+ secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1);
+ if (secoptlist != NULL)
+ num_secinfo = MAX(1, count_security(secoptlist));
+ else
+ num_secinfo = 1;
+
+ /*
+ * walk through the options and fill in the structure
+ * appropriately.
+ */
+
+ (void) memset(&export, '\0', sizeof (export));
+
+ /*
+ * do non-security options first since there is only one after
+ * the derived group is constructed.
+ */
+ export.ex_version = EX_CURRENT_VERSION;
+ export.ex_anon = UID_NOBODY; /* this is our default value */
+ export.ex_index = NULL;
+ export.ex_path = path;
+ export.ex_pathlen = strlen(path) + 1;
+
+ sp = calloc(num_secinfo, sizeof (struct secinfo));
+
+ if (opt != NULL)
+ err = fill_export_from_optionset(&export, opt);
+
+ /*
+ * check to see if "public" is set. If it is, then make sure
+ * no other share has it set. If it is already used, fail.
+ */
+
+ if (export.ex_flags & EX_PUBLIC && public_exists(share)) {
+ (void) printf(gettext("NFS: Cannot share more than one file "
+ "system with 'public' property\n"));
+ err = SA_NOT_ALLOWED;
+ goto out;
+ }
+
+ if (sp == NULL) {
+ /* failed to alloc memory */
+ (void) printf("NFS: no memory for security\n");
+ err = SA_NO_MEMORY;
+ } else {
+ int i;
+ export.ex_secinfo = sp;
+ /* get default secinfo */
+ export.ex_seccnt = num_secinfo;
+ /*
+ * since we must have one security option defined, we
+ * init to the default and then override as we find
+ * defined security options. This handles the case
+ * where we have no defined options but we need to set
+ * up one.
+ */
+ sp[0].s_window = DEF_WIN;
+ sp[0].s_rootnames = NULL;
+ /* setup a default in case no properties defined */
+ if (nfs_getseconfig_default(&sp[0].s_secinfo)) {
+ (void) printf(gettext("NFS: nfs_getseconfig_default: failed to "
+ "get default security mode\n"));
+ err = SA_CONFIG_ERR;
+ }
+ if (secoptlist != NULL) {
+ for (i = 0, prop = sa_get_property(secoptlist, NULL);
+ prop != NULL && i < num_secinfo;
+ prop = sa_get_next_property(prop), i++) {
+ char *sectype;
+
+ sectype = sa_get_property_attr(prop, "type");
+ /* if sectype is NULL, we can't do anything so skip */
+ if (sectype == NULL)
+ continue;
+ sec = (sa_security_t)sa_get_derived_security(share,
+ sectype,
+ "nfs", 1);
+ sp[i].s_window = DEF_WIN;
+ sp[i].s_rootcnt = 0;
+ sp[i].s_rootnames = NULL;
+
+ (void) fill_security_from_secopts(&sp[i], sec);
+ if (sec != NULL)
+ sa_free_derived_security(sec);
+ if (sectype != NULL)
+ sa_free_attr_string(sectype);
+ }
+ }
+ /*
+ * when we get here, we can do the exportfs system call and
+ * initiate thinsg. We probably want to enable the nfs.server
+ * service first if it isn't running within SMF.
+ */
+ /* check nfs.server status and start if needed */
+
+ /* now add the share to the internal tables */
+ printarg(path, &export);
+ /*
+ * call the exportfs system call which is implemented
+ * via the nfssys() call as the EXPORTFS subfunction.
+ */
+ if ((err = exportfs(path, &export)) < 0) {
+ err = SA_SYSTEM_ERR;
+ switch (errno) {
+ case EREMOTE:
+ (void) printf(gettext("NFS: Cannot share remote file"
+ "system: %s\n"),
+ path);
+ break;
+ case EPERM:
+ if (getzoneid() != GLOBAL_ZONEID) {
+ (void) printf(gettext("NFS: Cannot share file systems "
+ "in non-global zones: %s\n"), path);
+ err = SA_NOT_SUPPORTED;
+ break;
+ }
+ err = SA_NO_PERMISSION;
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ } else {
+ /* update sharetab with an add/modify */
+ (void) sa_update_sharetab(share, "nfs");
+ }
+ }
+
+ if (err == SA_OK) {
+ /*
+ * enable services as needed. This should probably be
+ * done elsewhere in order to minimize the calls to
+ * check services.
+ */
+ /*
+ * check to see if logging and other services need to
+ * be triggered, but only if there wasn't an
+ * error. This is probably where sharetab should be
+ * updated with the NFS specific entry.
+ */
+ if (export.ex_flags & EX_LOG) {
+ /* enable logging */
+ if (nfslogtab_add(path, export.ex_log_buffer,
+ export.ex_tag) != 0) {
+ (void) fprintf(stderr,
+ gettext("Could not enable logging for %s\n"),
+ path);
+ }
+ _check_services(service_list_logging);
+ } else {
+ /*
+ * don't have logging so remove it from file. It might
+ * not be thre, but that doesn't matter.
+ */
+ (void) nfslogtab_deactivate(path);
+ _check_services(service_list_default);
+ }
+ }
+
+out:
+ if (path != NULL)
+ free(path);
+
+ cleanup_export(&export);
+ if (opt != NULL)
+ sa_free_derived_optionset(opt);
+ if (secoptlist != NULL)
+ (void) sa_destroy_optionset(secoptlist);
+ return (err);
+}
+
+/*
+ * nfs_disable_share(share)
+ *
+ * Unshare the specified share. How much error checking should be
+ * done? We only do basic errors for now.
+ */
+static int
+nfs_disable_share(char *share)
+{
+ int err;
+ int ret = SA_OK;
+
+ if (share != NULL) {
+ err = exportfs(share, NULL);
+ if (err < 0) {
+ /* TBD: only an error in some cases - need better analysis */
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = SA_NO_PERMISSION;
+ if (getzoneid() != GLOBAL_ZONEID) {
+ ret = SA_NOT_SUPPORTED;
+ }
+ break;
+ case EINVAL:
+ case ENOENT:
+ ret = SA_NO_SUCH_PATH;
+ break;
+ default:
+ ret = SA_SYSTEM_ERR;
+ break;
+ }
+ }
+ if (ret == SA_OK || ret == SA_NO_SUCH_PATH) {
+ (void) sa_delete_sharetab(share, "nfs");
+ /* just in case it was logged */
+ (void) nfslogtab_deactivate(share);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * check ro vs rw values. Over time this may get beefed up.
+ * for now it just does simple checks.
+ */
+
+static int
+check_rorw(char *v1, char *v2)
+{
+ int ret = SA_OK;
+ if (strcmp(v1, v2) == 0)
+ ret = SA_VALUE_CONFLICT;
+ return (ret);
+}
+
+/*
+ * nfs_validate_property(property, parent)
+ *
+ * Check that the property has a legitimate value for its type.
+ */
+
+static int
+nfs_validate_property(sa_property_t property, sa_optionset_t parent)
+{
+ int ret = SA_OK;
+ char *propname;
+ char *other;
+ int optindex;
+ nfsl_config_t *configlist;
+ sa_group_t parent_group;
+ char *value;
+
+ propname = sa_get_property_attr(property, "type");
+
+ if ((optindex = findopt(propname)) < 0)
+ ret = SA_NO_SUCH_PROP;
+
+ /* need to validate value range here as well */
+
+ if (ret == SA_OK) {
+ parent_group = sa_get_parent_group((sa_share_t)parent);
+ if (optdefs[optindex].share && !sa_is_share(parent_group)) {
+ ret = SA_PROP_SHARE_ONLY;
+ }
+ }
+ if (ret == SA_OK) {
+ value = sa_get_property_attr(property, "value");
+ if (value != NULL) {
+ /* first basic type checking */
+ switch (optdefs[optindex].type) {
+ case OPT_TYPE_NUMBER:
+ /* check that the value is all digits */
+ if (!is_a_number(value))
+ ret = SA_BAD_VALUE;
+ break;
+ case OPT_TYPE_BOOLEAN:
+ if (strlen(value) == 0 ||
+ strcasecmp(value, "true") == 0 ||
+ strcmp(value, "1") == 0 ||
+ strcasecmp(value, "false") == 0 ||
+ strcmp(value, "0") == 0) {
+ ret = SA_OK;
+ } else {
+ ret = SA_BAD_VALUE;
+ }
+ break;
+ case OPT_TYPE_USER:
+ if (!is_a_number(value)) {
+ struct passwd *pw;
+ /* in this case it would have to be a user name */
+ pw = getpwnam(value);
+ if (pw == NULL)
+ ret = SA_BAD_VALUE;
+ endpwent();
+ } else {
+ uint64_t intval;
+ intval = strtoull(value, NULL, 0);
+ if (intval > UID_MAX && intval != ~0)
+ ret = SA_BAD_VALUE;
+ }
+ break;
+ case OPT_TYPE_FILE:
+ if (strcmp(value, "..") == 0 ||
+ strchr(value, '/') != NULL) {
+ ret = SA_BAD_VALUE;
+ }
+ break;
+ case OPT_TYPE_ACCLIST:
+ /*
+ * access list handling. Should eventually
+ * validate that all the values make sense.
+ * Also, ro and rw may have cross value
+ * conflicts.
+ */
+ if (strcmp(propname, SHOPT_RO) == 0)
+ other = SHOPT_RW;
+ else if (strcmp(propname, SHOPT_RW) == 0)
+ other = SHOPT_RO;
+ else
+ other = NULL;
+ if (other != NULL && parent != NULL) {
+ /* compare rw(ro) with ro(rw) */
+ sa_property_t oprop;
+ oprop = sa_get_property(parent, other);
+ if (oprop != NULL) {
+ /* only potential confusion if other exists */
+ char *ovalue;
+ ovalue = sa_get_property_attr(oprop, "value");
+ if (ovalue != NULL) {
+ ret = check_rorw(value, ovalue);
+ sa_free_attr_string(ovalue);
+ }
+ }
+ }
+ break;
+ case OPT_TYPE_LOGTAG:
+ if (nfsl_getconfig_list(&configlist) == 0) {
+ int error;
+ if (value == NULL || strlen(value) == 0) {
+ if (value != NULL)
+ sa_free_attr_string(value);
+ value = strdup("global");
+ }
+ if (nfsl_findconfig(configlist, value, &error) == NULL)
+ ret = SA_BAD_VALUE;
+ nfsl_freeconfig_list(&configlist);
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ break;
+ case OPT_TYPE_STRING:
+ /* whatever is here should be ok */
+ break;
+ default:
+ break;
+ }
+ sa_free_attr_string(value);
+ if (ret == SA_OK && optdefs[optindex].check != NULL) {
+ /* do the property specific check */
+ ret = optdefs[optindex].check(property);
+ }
+ }
+ }
+
+ if (propname != NULL)
+ sa_free_attr_string(propname);
+ return (ret);
+}
+
+/*
+ * Protocol management functions
+ *
+ * properties defined in the default files are defined in
+ * proto_option_defs for parsing and validation.
+ */
+
+struct proto_option_defs {
+ char *tag;
+ char *name; /* display name -- remove protocol identifier */
+ int index;
+ int type;
+ union {
+ int intval;
+ char *string;
+ } defvalue;
+ uint32_t svcs;
+ int32_t minval;
+ int32_t maxval;
+ char *file;
+ int (*check)(char *);
+} proto_options[] = {
+#define PROTO_OPT_NFSD_SERVERS 0
+ {"nfsd_servers",
+ "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 16, SVC_NFSD,
+ 1, INT32_MAX, NFSADMIN},
+#define PROTO_OPT_LOCKD_LISTEN_BACKLOG 1
+ {"lockd_listen_backlog",
+ "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG,
+ OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX, NFSADMIN},
+#define PROTO_OPT_LOCKD_SERVERS 2
+ {"lockd_servers",
+ "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 20,
+ SVC_LOCKD, 1, INT32_MAX, NFSADMIN},
+#define PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT 3
+ {"lockd_retransmit_timeout",
+ "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT,
+ OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX, NFSADMIN},
+#define PROTO_OPT_GRACE_PERIOD 4
+ {"grace_period",
+ "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90,
+ SVC_LOCKD, 0, INT32_MAX, NFSADMIN},
+#define PROTO_OPT_NFS_SERVER_VERSMIN 5
+ {"nfs_server_versmin",
+ "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER,
+ (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
+ NFS_VERSMAX, NFSADMIN},
+#define PROTO_OPT_NFS_SERVER_VERSMAX 6
+ {"nfs_server_versmax",
+ "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER,
+ (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
+ NFS_VERSMAX, NFSADMIN},
+#define PROTO_OPT_NFS_CLIENT_VERSMIN 7
+ {"nfs_client_versmin",
+ "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER,
+ (int)NFS_VERSMIN_DEFAULT, NULL, NFS_VERSMIN, NFS_VERSMAX,
+ NFSADMIN},
+#define PROTO_OPT_NFS_CLIENT_VERSMAX 8
+ {"nfs_client_versmax",
+ "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER,
+ (int)NFS_VERSMAX_DEFAULT, NULL, NFS_VERSMIN, NFS_VERSMAX,
+ NFSADMIN},
+#define PROTO_OPT_NFS_SERVER_DELEGATION 9
+ {"nfs_server_delegation",
+ "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION,
+ OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0,
+ NFSADMIN},
+#define PROTO_OPT_NFSMAPID_DOMAIN 10
+ {"nfsmapid_domain",
+ "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN,
+ NULL, SVC_NFSMAPID, 0, 0, NFSADMIN},
+#define PROTO_OPT_NFSD_MAX_CONNECTIONS 11
+ {"nfsd_max_connections",
+ "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS,
+ OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX, NFSADMIN},
+#define PROTO_OPT_NFSD_PROTOCOL 12
+ {"nfsd_protocol",
+ "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0,
+ SVC_NFSD, 0, 0, NFSADMIN},
+#define PROTO_OPT_NFSD_LISTEN_BACKLOG 13
+ {"nfsd_listen_backlog",
+ "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG,
+ OPT_TYPE_NUMBER, 0,
+ SVC_LOCKD, 0, INT32_MAX, NFSADMIN},
+ {NULL}
+};
+
+/*
+ * the protoset holds the defined options so we don't have to read
+ * them multiple times
+ */
+sa_protocol_properties_t protoset;
+
+static int
+findprotoopt(char *name, int whichname)
+{
+ int i;
+ for (i = 0; proto_options[i].tag != NULL; i++) {
+ if (whichname == 1) {
+ if (strcasecmp(proto_options[i].name, name) == 0)
+ return (i);
+ } else {
+ if (strcasecmp(proto_options[i].tag, name) == 0)
+ return (i);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * fixcaselower(str)
+ *
+ * convert a string to lower case (inplace).
+ */
+
+static void
+fixcaselower(char *str)
+{
+ while (*str) {
+ *str = tolower(*str);
+ str++;
+ }
+}
+
+/*
+ * fixcaseupper(str)
+ *
+ * convert a string to upper case (inplace).
+ */
+
+static void
+fixcaseupper(char *str)
+{
+ while (*str) {
+ *str = toupper(*str);
+ str++;
+ }
+}
+
+/*
+ * initprotofromdefault()
+ *
+ * read the default file(s) and add the defined values to the
+ * protoset. Note that default values are known from the built in
+ * table in case the file doesn't have a definition.
+ */
+
+static int
+initprotofromdefault()
+{
+ FILE *nfs;
+ char buff[BUFSIZ];
+ char *name;
+ char *value;
+ sa_property_t prop;
+ int index;
+
+ protoset = sa_create_protocol_properties("nfs");
+
+ if (protoset != NULL) {
+ nfs = fopen(NFSADMIN, "r");
+ if (nfs != NULL) {
+ while (fgets(buff, sizeof (buff), nfs) != NULL) {
+ switch (buff[0]) {
+ case '\n':
+ case '#':
+ /* skip */
+ break;
+ default:
+ name = buff;
+ buff[strlen(buff) - 1] = '\0';
+ value = strchr(name, '=');
+ if (value != NULL) {
+ *value++ = '\0';
+ if ((index = findprotoopt(name, 0)) >= 0) {
+ fixcaselower(name);
+ prop = sa_create_property(
+ proto_options[index].name,
+ value);
+ (void) sa_add_protocol_property(protoset, prop);
+ }
+ }
+ }
+ }
+ if (nfs != NULL)
+ (void) fclose(nfs);
+ }
+ }
+ if (protoset == NULL)
+ return (SA_NO_MEMORY);
+ return (SA_OK);
+}
+
+/*
+ * add_default()
+ *
+ * Add the default values for any property not defined in the parsing
+ * of the default files.
+ */
+
+static void
+add_defaults()
+{
+ int i;
+ char number[MAXDIGITS];
+
+ for (i = 0; proto_options[i].tag != NULL; i++) {
+ sa_property_t prop;
+ prop = sa_get_protocol_property(protoset, proto_options[i].name);
+ if (prop == NULL) {
+ /* add the default value */
+ switch (proto_options[i].type) {
+ case OPT_TYPE_NUMBER:
+ (void) snprintf(number, sizeof (number), "%d",
+ proto_options[i].defvalue.intval);
+ prop = sa_create_property(proto_options[i].name, number);
+ break;
+ case OPT_TYPE_BOOLEAN:
+ prop = sa_create_property(proto_options[i].name,
+ proto_options[i].defvalue.intval ?
+ "true" : "false");
+ break;
+ }
+ if (prop != NULL)
+ (void) sa_add_protocol_property(protoset, prop);
+ }
+ }
+}
+
+static void
+free_protoprops()
+{
+ xmlFreeNode(protoset);
+}
+
+/*
+ * nfs_init()
+ *
+ * Initialize the NFS plugin.
+ */
+
+static int
+nfs_init()
+{
+ int ret = SA_OK;
+
+ if (sa_plugin_ops.sa_init != nfs_init)
+ (void) printf(gettext("NFS plugin not properly initialized\n"));
+
+ ret = initprotofromdefault();
+ add_defaults();
+
+ return (ret);
+}
+
+/*
+ * nfs_fini()
+ *
+ * uninitialize the NFS plugin. Want to avoid memory leaks.
+ */
+
+static void
+nfs_fini()
+{
+ free_protoprops();
+}
+
+/*
+ * nfs_get_proto_set()
+ *
+ * Return an optionset with all the protocol specific properties in
+ * it.
+ */
+
+static sa_protocol_properties_t
+nfs_get_proto_set()
+{
+ return (protoset);
+}
+
+struct deffile {
+ struct deffile *next;
+ char *line;
+};
+
+/*
+ * read_default_file(fname)
+ *
+ * Read the specified default file. We return a list of entries. This
+ * get used for adding or removing values.
+ */
+
+static struct deffile *
+read_default_file(char *fname)
+{
+ FILE *file;
+ struct deffile *defs = NULL;
+ struct deffile *newdef;
+ struct deffile *prevdef = NULL;
+ char buff[BUFSIZ * 2];
+
+ file = fopen(fname, "r");
+ if (file != NULL) {
+ while (fgets(buff, sizeof (buff), file) != NULL) {
+ newdef = (struct deffile *)calloc(1, sizeof (struct deffile));
+ if (newdef != NULL) {
+ newdef->line = strdup(buff);
+ if (defs == NULL) {
+ prevdef = defs = newdef;
+ } else {
+ prevdef->next = newdef;
+ prevdef = newdef;
+ }
+ }
+ }
+ }
+ (void) fclose(file);
+ return (defs);
+}
+
+static void
+free_default_file(struct deffile *defs)
+{
+ struct deffile *curdefs = NULL;
+
+ while (defs != NULL) {
+ curdefs = defs;
+ defs = defs->next;
+ if (curdefs->line != NULL)
+ free(curdefs->line);
+ free(curdefs);
+ }
+}
+
+/*
+ * write_default_file(fname, defs)
+ *
+ * Write the default file back.
+ */
+
+static int
+write_default_file(char *fname, struct deffile *defs)
+{
+ FILE *file;
+ int ret = SA_OK;
+ sigset_t old, new;
+
+ file = fopen(fname, "w+");
+ if (file != NULL) {
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ while (defs != NULL) {
+ (void) fputs(defs->line, file);
+ defs = defs->next;
+ }
+ (void) fsync(fileno(file));
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ (void) fclose(file);
+ } else {
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = SA_NO_PERMISSION;
+ break;
+ default:
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ return (ret);
+}
+
+
+/*
+ * set_default_file_value(tag, value)
+ *
+ * Set the default file value for tag to value. Then rewrite the file.
+ * tag and value are always set. The caller must ensure this.
+ */
+
+#define MAX_STRING_LENGTH 256
+static int
+set_default_file_value(char *tag, char *value)
+{
+ int ret = SA_OK;
+ struct deffile *root;
+ struct deffile *defs;
+ struct deffile *prev;
+ char string[MAX_STRING_LENGTH];
+ int len;
+ int update = 0;
+
+ (void) snprintf(string, MAX_STRING_LENGTH, "%s=", tag);
+ len = strlen(string);
+
+ root = defs = read_default_file(NFSADMIN);
+ if (root == NULL) {
+ if (errno == EPERM || errno == EACCES)
+ ret = SA_NO_PERMISSION;
+ else
+ ret = SA_SYSTEM_ERR;
+ } else {
+ while (defs != NULL) {
+ if (defs->line != NULL &&
+ strncasecmp(defs->line, string, len) == 0) {
+ /* replace with the new value */
+ free(defs->line);
+ fixcaseupper(tag);
+ (void) snprintf(string, sizeof (string), "%s=%s\n",
+ tag, value);
+ string[MAX_STRING_LENGTH - 1] = '\0';
+ defs->line = strdup(string);
+ update = 1;
+ break;
+ }
+ defs = defs->next;
+ }
+ if (!update) {
+ defs = root;
+ /* didn't find, so see if it is a comment */
+ (void) snprintf(string, MAX_STRING_LENGTH, "#%s=", tag);
+ len = strlen(string);
+ while (defs != NULL) {
+ if (strncasecmp(defs->line, string, len) == 0) {
+ /* replace with the new value */
+ free(defs->line);
+ fixcaseupper(tag);
+ (void) snprintf(string, sizeof (string),
+ "%s=%s\n", tag, value);
+ string[MAX_STRING_LENGTH - 1] = '\0';
+ defs->line = strdup(string);
+ update = 1;
+ break;
+ }
+ defs = defs->next;
+ }
+ }
+ if (!update) {
+ fixcaseupper(tag);
+ (void) snprintf(string, sizeof (string), "%s=%s\n",
+ tag, value);
+ prev = root;
+ while (prev->next != NULL)
+ prev = prev->next;
+ defs = malloc(sizeof (struct deffile));
+ prev->next = defs;
+ if (defs != NULL) {
+ defs->next = NULL;
+ defs->line = strdup(string);
+ }
+ }
+ if (update) {
+ ret = write_default_file(NFSADMIN, root);
+ }
+ free_default_file(root);
+ }
+ return (ret);
+}
+
+/*
+ * restart_service(svcs)
+ *
+ * Walk through the bit mask of services that need to be restarted in
+ * order to use the new property values. Some properties affect
+ * multiple daemons.
+ */
+
+static void
+restart_service(uint32_t svcs)
+{
+ uint32_t mask;
+ for (mask = 1; svcs != 0; mask <<= 1) {
+ switch (svcs & mask) {
+ case SVC_LOCKD:
+ (void) smf_restart_instance(LOCKD);
+ break;
+ case SVC_STATD:
+ (void) smf_restart_instance(STATD);
+ break;
+ case SVC_NFSD:
+ (void) smf_restart_instance(NFSD);
+ break;
+ case SVC_MOUNTD:
+ (void) smf_restart_instance(MOUNTD);
+ break;
+ case SVC_NFS4CBD:
+ (void) smf_restart_instance(NFS4CBD);
+ break;
+ case SVC_NFSMAPID:
+ (void) smf_restart_instance(NFSMAPID);
+ break;
+ case SVC_RQUOTAD:
+ (void) smf_restart_instance(RQUOTAD);
+ break;
+ case SVC_NFSLOGD:
+ (void) smf_restart_instance(NFSLOGD);
+ break;
+ }
+ svcs &= ~mask;
+ }
+}
+
+/*
+ * nfs_validate_proto_prop(index, name, value)
+ *
+ * Verify that the property specifed by name can take the new
+ * value. This is a sanity check to prevent bad values getting into
+ * the default files.
+ */
+
+static int
+nfs_validate_proto_prop(int index, char *name, char *value)
+{
+ int ret = SA_OK;
+ char *cp;
+#ifdef lint
+ name = name;
+#endif
+
+ switch (proto_options[index].type) {
+ case OPT_TYPE_NUMBER:
+ if (!is_a_number(value))
+ ret = SA_BAD_VALUE;
+ else {
+ int val;
+ val = strtoul(value, NULL, 0);
+ if (val < proto_options[index].minval ||
+ val > proto_options[index].maxval)
+ ret = SA_BAD_VALUE;
+ }
+ break;
+ case OPT_TYPE_DOMAIN:
+ /*
+ * needs to be a qualified domain so will have at least
+ * one period and other characters on either side of it.
+ */
+ cp = strchr(value, '.');
+ if (cp == NULL || cp == value ||strchr(value, '@') != NULL)
+ ret = SA_BAD_VALUE;
+ break;
+ case OPT_TYPE_BOOLEAN:
+ if (strlen(value) == 0 ||
+ strcasecmp(value, "true") == 0 ||
+ strcmp(value, "1") == 0 ||
+ strcasecmp(value, "false") == 0 ||
+ strcmp(value, "0") == 0) {
+ ret = SA_OK;
+ } else {
+ ret = SA_BAD_VALUE;
+ }
+ break;
+ case OPT_TYPE_ONOFF:
+ if (strcasecmp(value, "on") != 0 &&
+ strcasecmp(value, "off") != 0) {
+ ret = SA_BAD_VALUE;
+ }
+ break;
+ case OPT_TYPE_PROTOCOL:
+ if (strcasecmp(value, "all") != 0 &&
+ strcasecmp(value, "tcp") != 0 &&
+ strcasecmp(value, "udp") != 0)
+ ret = SA_BAD_VALUE;
+ }
+ return (ret);
+}
+
+/*
+ * nfs_set_proto_prop(prop)
+ *
+ * check that prop is valid.
+ */
+
+static int
+nfs_set_proto_prop(sa_property_t prop)
+{
+ int ret = SA_OK;
+ char *name;
+ char *value;
+
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (name != NULL && value != NULL) {
+ int index = findprotoopt(name, 1);
+ if (index >= 0) {
+ /* should test for valid value */
+ ret = nfs_validate_proto_prop(index, name, value);
+ if (ret == SA_OK)
+ ret = set_default_file_value(proto_options[index].tag,
+ value);
+ if (ret == SA_OK)
+ restart_service(proto_options[index].svcs);
+ }
+ }
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (value != NULL)
+ sa_free_attr_string(value);
+ return (ret);
+}
+
+/*
+ * nfs_get_status()
+ *
+ * What is the current status of the nfsd? We use the SMF state here.
+ * Caller must free the returned value.
+ */
+
+static char *
+nfs_get_status()
+{
+ char *state;
+ state = smf_get_state(NFSD);
+ return (state != NULL ? state : strdup("-"));
+}
+
+/*
+ * nfs_space_alias(alias)
+ *
+ * Lookup the space (security) name. If it is default, convert to the
+ * real name.
+ */
+
+static char *
+nfs_space_alias(char *space)
+{
+ char *name = space;
+ seconfig_t secconf;
+ if (nfs_getseconfig_default(&secconf) == 0) {
+ if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0)
+ name = secconf.sc_name;
+ }
+ return (strdup(name));
+}
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.h b/usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.h
new file mode 100644
index 0000000000..6e748ea897
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.h
@@ -0,0 +1,131 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * basic API declarations for share management
+ */
+
+#ifndef _LIBSHARE_NFS_H
+#define _LIBSHARE_NFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* property names used by NFS */
+#define SHOPT_RO "ro"
+#define SHOPT_RW "rw"
+
+#define SHOPT_SEC "sec"
+#define SHOPT_SECURE "secure"
+#define SHOPT_ROOT "root"
+#define SHOPT_ANON "anon"
+#define SHOPT_WINDOW "window"
+#define SHOPT_NOSUB "nosub"
+#define SHOPT_NOSUID "nosuid"
+#define SHOPT_ACLOK "aclok"
+#define SHOPT_PUBLIC "public"
+#define SHOPT_INDEX "index"
+#define SHOPT_LOG "log"
+#define SHOPT_CKSUM "cksum"
+
+/*
+ * defined options types. These should be in a file rather than
+ * compiled in. Until there is a plugin mechanism to add new types,
+ * this is sufficient.
+ */
+#define OPT_TYPE_ANY 0
+#define OPT_TYPE_STRING 1
+#define OPT_TYPE_BOOLEAN 2
+#define OPT_TYPE_NUMBER 3
+#define OPT_TYPE_RANGE 4
+#define OPT_TYPE_USER 5
+#define OPT_TYPE_ACCLIST 6
+#define OPT_TYPE_DEPRECATED 7
+#define OPT_TYPE_SECURITY 8
+#define OPT_TYPE_PATH 9
+#define OPT_TYPE_FILE 10
+#define OPT_TYPE_LOGTAG 11
+#define OPT_TYPE_STRINGSET 12
+#define OPT_TYPE_DOMAIN 13
+#define OPT_TYPE_ONOFF 14
+#define OPT_TYPE_PROTOCOL 15
+
+#define OPT_SHARE_ONLY 1
+
+struct option_defs {
+ char *tag;
+ int index;
+ int type;
+ int share; /* share only option */
+ int (*check)(char *);
+};
+
+/*
+ * service bit mask values
+ */
+#define SVC_LOCKD 0x0001
+#define SVC_STATD 0x0002
+#define SVC_NFSD 0x0004
+#define SVC_MOUNTD 0x0008
+#define SVC_NFS4CBD 0x0010
+#define SVC_NFSMAPID 0x0020
+#define SVC_RQUOTAD 0x0040
+#define SVC_NFSLOGD 0x0080
+
+/*
+ * place holder for future service -- will move to daemon_utils.h when
+ * fully implemented.
+ */
+#define NFSLOGD "svc:/network/nfs/log:default"
+
+/* The NFS export structure flags for read/write modes */
+#define NFS_RWMODES (M_RO|M_ROL|M_RW|M_RWL)
+
+/* other values */
+/* max size of 64-bit integer in digits plus a bit extra */
+#define MAXDIGITS 32
+
+/* external variable */
+extern boolean_t nfsl_errs_to_syslog;
+
+/* imported functions */
+extern int exportfs(char *, struct exportdata *);
+extern void _check_services(char **);
+extern int nfs_getseconfig_default(seconfig_t *);
+extern int nfs_getseconfig_byname(char *, seconfig_t *);
+extern bool_t nfs_get_root_principal(seconfig_t *, char *, caddr_t *);
+extern int nfsl_getconfig_list(nfsl_config_t **);
+extern void nfsl_freeconfig_list(nfsl_config_t **);
+extern nfsl_config_t *nfsl_findconfig(nfsl_config_t *, char *, int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSHARE_NFS_H */
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/plugins/sparc/Makefile b/usr/src/cmd/dfs.cmds/sharemgr/plugins/sparc/Makefile
new file mode 100644
index 0000000000..9201a4391d
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/plugins/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/sharemgr.h b/usr/src/cmd/dfs.cmds/sharemgr/sharemgr.h
new file mode 100644
index 0000000000..5eeddb4a04
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/sharemgr.h
@@ -0,0 +1,117 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SHAREMGR_H
+#define _SHAREMGR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * shareadm internal interfaces
+ */
+
+typedef enum {
+ USAGE_ADD_SHARE,
+ USAGE_CREATE,
+ USAGE_DELETE,
+ USAGE_DISABLE,
+ USAGE_ENABLE,
+ USAGE_LIST,
+ USAGE_MOVE_SHARE,
+ USAGE_REMOVE_SHARE,
+ USAGE_SET,
+ USAGE_SET_SECURITY,
+ USAGE_SET_SHARE,
+ USAGE_SHOW,
+ USAGE_SHARE,
+ USAGE_START,
+ USAGE_STOP,
+ USAGE_UNSET,
+ USAGE_UNSET_SECURITY,
+ USAGE_UNSHARE
+} sa_usage_t;
+
+/* sharectl specific usage message values */
+typedef enum {
+ USAGE_CTL_GET,
+ USAGE_CTL_SET,
+ USAGE_CTL_STATUS
+} sc_usage_t;
+
+typedef struct sa_command {
+ char *cmdname;
+ int flags;
+ int (*cmdfunc)(int, int, char **);
+ int cmdidx;
+ int priv; /* requires RBAC authorizations */
+} sa_command_t;
+
+#define CMD_ALIAS 0x0001
+#define CMD_NODISPLAY 0x0002 /* don't display command */
+
+#define SVC_AUTH_VALUE "value_authorization"
+#define SVC_AUTH_ACTION "action_authorization"
+#define SVC_SET 0x01 /* need value permissions */
+#define SVC_ACTION 0x02 /* need action permissions */
+
+#define ZFS_SHAREALL "/usr/sbin/zfs share -a"
+
+/*
+ * functions/values for manipulating options
+ */
+#define OPT_ADD_OK 0
+#define OPT_ADD_SYNTAX -1
+#define OPT_ADD_SECURITY -2
+#define OPT_ADD_PROPERTY -3
+#define OPT_ADD_MEMORY -4
+
+/* option list structure */
+struct options {
+ struct options *next;
+ char *optname;
+ char *optvalue;
+};
+
+/* general list structure */
+struct list {
+ struct list *next;
+ void *item;
+ void *itemdata;
+};
+
+/* shareutil entry points */
+ extern int add_opt(struct options **, char *, int);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SHAREMGR_H */
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/sharemgr_main.c b/usr/src/cmd/dfs.cmds/sharemgr/sharemgr_main.c
new file mode 100644
index 0000000000..8c57470d96
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/sharemgr_main.c
@@ -0,0 +1,152 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include <libshare.h>
+#include "sharemgr.h"
+#include <libintl.h>
+#include <locale.h>
+
+char *protocol = NULL;
+static int help = 0;
+
+static int run_command(char *, int, char **, char *);
+extern sa_command_t *sa_lookup(char *, char *);
+extern void sub_command_help(char *proto);
+
+static void
+global_help()
+{
+ (void) printf(gettext("usage: sharemgr [-h | <command> [options]]\n"));
+ sub_command_help(NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int rval;
+ char *command = NULL;
+
+ /*
+ * make sure locale and gettext domain is setup
+ */
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * initialize the plugin architecture.
+ * Plugins are needed in the event of a global help
+ * request.
+ */
+
+ sa_init(SA_INIT_SHARE_API);
+ optind = 1; /* reset to beginning */
+
+ /*
+ * parse enough of command line to get protocol, if any.
+ * Note that options need to come "after" the subcommand.
+ */
+ command = basename(argv[0]);
+ if (strcmp(command, "share") != 0 && strcmp(command, "unshare") != 0) {
+ while ((c = getopt(argc, argv, "h?")) != EOF) {
+ switch (c) {
+ default:
+ case 'h':
+ case '?':
+ help = 1;
+ break;
+ }
+ }
+ if (argc == 1)
+ help = 1;
+ }
+
+ if (strcmp(command, "sharemgr") == 0) {
+ command = argv[optind];
+ argv++;
+ argc--;
+ }
+
+ if (help) {
+ /* no subcommand */
+ global_help();
+ exit(SA_OK);
+ }
+
+ /*
+ * now have enough to parse rest of command line
+ */
+ rval = run_command(command, argc, argv, protocol);
+
+ sa_fini();
+ return (rval);
+}
+
+static int
+run_command(char *command, int argc, char *argv[], char *proto)
+{
+ sa_command_t *cmdvec;
+ int ret;
+
+ /*
+ * To get here, we know there should be a command due to the
+ * preprocessing done earlier. Need to find the protocol
+ * that is being affected. If no protocol, then it is ALL
+ * protocols.
+ *
+ * We don't currently use the protocol here at this point. It
+ * is left in as a placeholder for the future addition of
+ * protocol specific sub-commands.
+ *
+ * Known sub-commands are handled at this level. An unknown
+ * command will be passed down to the shared object that
+ * actually implements it. We can do this since the semantics
+ * of the common sub-commands is well defined.
+ */
+
+ cmdvec = sa_lookup(command, proto);
+ if (cmdvec == NULL) {
+ (void) printf(gettext("command %s not found\n"), command);
+ exit(1);
+ }
+ /*
+ * need to check priviledges and restrict what can be done
+ * based on least priviledge and sub-command so pass this in
+ * as a flag.
+ */
+ ret = cmdvec->cmdfunc(cmdvec->priv, argc, argv);
+ return (ret);
+}
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/shareutil.c b/usr/src/cmd/dfs.cmds/sharemgr/shareutil.c
new file mode 100644
index 0000000000..8df023b605
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/shareutil.c
@@ -0,0 +1,84 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include "sharemgr.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * Utility functions shared by sharemgr and sharectl.
+ */
+
+/*
+ * add_opt(optlist, optarg, security?)
+ * Add a new parsed option to the option list provided.
+ * If the option is a security option, only add if we are
+ * processing security options.
+ */
+int
+add_opt(struct options **optlistp, char *optarg, int unset)
+{
+ struct options *newopt, *tmp, *optlist;
+
+ optlist = *optlistp;
+ newopt = (struct options *)malloc(sizeof (struct options));
+ if (newopt != NULL) {
+ char *optname;
+ char *optvalue;
+
+ /* extract property/value pair */
+ optname = optarg;
+ if (!unset) {
+ optvalue = strchr(optname, '=');
+ if (optvalue == NULL) {
+ free(newopt);
+ return (OPT_ADD_SYNTAX);
+ }
+ *optvalue++ = '\0'; /* separate the halves */
+ } else {
+ optvalue = NULL;
+ }
+
+ newopt->optname = optname;
+ newopt->optvalue = optvalue;
+ newopt->next = NULL;
+ if (optlist == NULL) {
+ optlist = newopt;
+ } else {
+ for (tmp = optlist; tmp->next != NULL;
+ tmp = tmp->next) {
+ }
+ tmp->next = newopt;
+ }
+ *optlistp = optlist;
+ return (OPT_ADD_OK);
+ }
+ return (OPT_ADD_MEMORY);
+}
diff --git a/usr/src/cmd/dfs.cmds/sharemgr/sparc/Makefile b/usr/src/cmd/dfs.cmds/sharemgr/sparc/Makefile
new file mode 100644
index 0000000000..9201a4391d
--- /dev/null
+++ b/usr/src/cmd/dfs.cmds/sharemgr/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all
diff --git a/usr/src/cmd/dfs.cmds/unshareall/unshareall.sh b/usr/src/cmd/dfs.cmds/unshareall/unshareall.sh
index a0fa50166d..d8f72193b2 100644
--- a/usr/src/cmd/dfs.cmds/unshareall/unshareall.sh
+++ b/usr/src/cmd/dfs.cmds/unshareall/unshareall.sh
@@ -3,9 +3,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -20,9 +19,8 @@
#
# CDDL HEADER END
#
-# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
-# All Rights Reserved
-
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.3 */
# unshareall -- unshare resources
@@ -52,14 +50,11 @@ fi
if [ "$fsys" ] # for each file system ...
then
fsys=`echo $fsys|tr ',' ' '`
+ for i in $fsys
+ do
+ /usr/sbin/sharemgr stop -P $fsys -a
+ done
else # for every file system ...
- fsys=`sed 's/^\([^# ]*\).*/\1/' /etc/dfs/fstypes`
+ /usr/sbin/sharemgr stop -a
fi
-for i in $fsys
-do
- for path in `sed -n "s/^\([^ ]*\)[ ]*[^ ]*[ ]*${i}.*/\1/p" /etc/dfs/sharetab`
- do
- /usr/sbin/unshare -F $i $path
- done
-done
diff --git a/usr/src/cmd/fs.d/nfs/Makefile b/usr/src/cmd/fs.d/nfs/Makefile
index 41478658b9..9d737f3004 100644
--- a/usr/src/cmd/fs.d/nfs/Makefile
+++ b/usr/src/cmd/fs.d/nfs/Makefile
@@ -32,11 +32,11 @@
include $(SRC)/Makefile.master
-SUBDIR1= exportfs nfsd rquotad unshare \
+SUBDIR1= exportfs nfsd rquotad \
statd nfsstat mountd dfshares \
- nfsfind nfs4cbd
+ nfsfind nfs4cbd share
SUBDIR2= clear_locks umount showmount \
- share mount dfmounts nfslog nfsmapid
+ mount dfmounts nfslog nfsmapid
SUBDIR3= etc svc
SUBDIRS= $(SUBDIR1) $(SUBDIR2) $(SUBDIR3)
diff --git a/usr/src/cmd/fs.d/nfs/share/Makefile b/usr/src/cmd/fs.d/nfs/share/Makefile
index bdac2a81a1..8d29746c82 100644
--- a/usr/src/cmd/fs.d/nfs/share/Makefile
+++ b/usr/src/cmd/fs.d/nfs/share/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -22,44 +21,16 @@
#
#ident "%Z%%M% %I% %E% SMI"
#
-#
-# Copyright 1993-1999,2003 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-FSTYPE= nfs
-LIBPROG= share
-ATTMK= $(LIBPROG)
-COMMON= nfs_sec.o sharetab.o nfslog_config.o nfslogtab.o
-LOCAL= $(LIBPROG).o issubdir.o
-OBJS= $(LOCAL) $(COMMON)
-SRCS= $(LOCAL:%.o=%.c) $(COMMON:%.o=../lib/%.c)
+FSTYPE= nfs
OTHERINSTALL= $(ROOTETC)/dfs/fstypes
-#
-# Message catalog
-#
-POFILE= share.po
-
-
-catalog: $(POFILE)
-
-$(POFILE): $(SRCS)
- $(RM) $@
- $(COMPILE.cpp) $(SRCS) > $(POFILE).i
- $(XGETTEXT) $(XGETFLAGS) $(POFILE).i
- sed "/^domain/d" messages.po > $@
- $(RM) $(POFILE).i messages.po
-
include ../../Makefile.fstype
-LDLIBS += -lnsl
-#
-# uncomment to test vol fh
-#
-#CFLAGS += -DVOLATILE_FH_TEST
-
$(ROOTETC)/dfs/fstypes := FILEMODE= 644
$(ROOTETC)/dfs/fstypes := OWNER= root
$(ROOTETC)/dfs/fstypes := GROUP= root
@@ -67,23 +38,3 @@ $(ROOTETC)/dfs/fstypes := GROUP= root
$(ROOTETC)/dfs/%: %
$(INS.file)
-$(LIBPROG): $(OBJS)
- $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
- $(POST_PROCESS)
-
-nfs_sec.o: ../lib/nfs_sec.c
- $(COMPILE.c) ../lib/nfs_sec.c
-
-sharetab.o: ../lib/sharetab.c
- $(COMPILE.c) ../lib/sharetab.c
-
-nfslog_config.o: ../lib/nfslog_config.c
- $(COMPILE.c) ../lib/nfslog_config.c
-
-nfslogtab.o: ../lib/nfslogtab.c
- $(COMPILE.c) ../lib/nfslogtab.c
-
-lint: lint_SRCS
-
-clean:
- $(RM) $(OBJS)
diff --git a/usr/src/cmd/fs.d/nfs/share/share.c b/usr/src/cmd/fs.d/nfs/share/share.c
deleted file mode 100644
index fb7605d104..0000000000
--- a/usr/src/cmd/fs.d/nfs/share/share.c
+++ /dev/null
@@ -1,1257 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
-/* All Rights Reserved */
-
-/*
- * University Copyright- Copyright (c) 1982, 1986, 1988
- * The Regents of the University of California
- * All Rights Reserved
- *
- * University Acknowledgment- Portions of this document are derived from
- * software developed by the University of California, Berkeley, and its
- * contributors.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-/*
- * nfs share
- */
-#define _REENTRANT
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/param.h> /* for UID_NOBODY */
-#include <sys/stat.h>
-#include <errno.h>
-#include <rpc/rpc.h>
-#include <netconfig.h>
-#include <netdir.h>
-#include <nfs/nfs_sec.h>
-#include <nfs/export.h>
-#include <locale.h>
-#include <zone.h>
-#include <rpcsvc/daemon_utils.h>
-#include "../lib/nfslog_config.h"
-#include "../lib/sharetab.h"
-#include "../lib/nfslogtab.h"
-
-#define RET_OK 0
-#define RET_ERR 32
-
-static int addlogconfig(struct exportdata *, char *, nfsl_config_t *);
-static void configlog(struct exportdata *, char *);
-static int direq(char *, char *);
-static int newopts(char *);
-static void parseopts_old(struct exportdata *, char *);
-static void parseopts_new(struct exportdata *, char *);
-static void pr_err(char *, ...);
-static int shareable(char *);
-static int sharetab_add(char *, char *, char *, char *, int);
-static int sharepub_exist(char *);
-static caddr_t *get_rootnames(seconfig_t *, char *, int *);
-static void usage();
-static void exportindex(struct exportdata *, char *);
-static int nfslogtab_add(char *, char *, char *);
-static int nfslogtab_deactivate(char *);
-
-extern int issubdir();
-extern int exportfs();
-int nfs_getseconfig_byname(char *, seconfig_t *);
-static void printarg(char *, struct exportdata *);
-static struct exportdata ex;
-
-/*
- * list of support services needed
- */
-static char *service_list[] =
- { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NULL };
-
-int
-main(int argc, char *argv[])
-{
- extern int optind;
- extern char *optarg;
- char dir[MAXPATHLEN];
- char *res = "-";
- char *opts = "rw";
- char *descr = "";
- char *services;
- int c;
- int replace = 0;
- int verbose = 0;
-
- /* Don't drop core if the NFS module isn't loaded. */
- signal(SIGSYS, SIG_IGN);
-
- (void) setlocale(LC_ALL, "");
-#if !defined(TEXT_DOMAIN)
-#define TEXT_DOMAIN "SYS_TEST"
-#endif
- (void) textdomain(TEXT_DOMAIN);
-
- while ((c = getopt(argc, argv, "o:d:v")) != EOF) {
- switch (c) {
- case 'o':
- opts = optarg;
- break;
- case 'd':
- descr = optarg;
- break;
- case 'v':
- verbose++;
- break;
- default:
- usage();
- exit(RET_ERR);
- }
- }
-
- if (argc <= optind || argc - optind > 2) {
- usage();
- exit(RET_ERR);
- }
- if (realpath(argv[optind], dir) == NULL)
- pr_err("%s: %s\n", argv[optind], strerror(errno));
-
- if (argc - optind > 1)
- res = argv[optind + 1];
-
- if (getenv("SHARE_NOINUSE_CHECK") == NULL) {
- switch (shareable(dir)) {
- case 0:
- exit(RET_ERR);
- break;
- case 1:
- break;
- case 2:
- replace = 1;
- break;
- }
- }
-
- ex.ex_path = dir;
- ex.ex_pathlen = strlen(dir) + 1;
-
- if (newopts(opts))
- parseopts_new(&ex, opts);
- else
- parseopts_old(&ex, opts);
-
- /*
- * If -o public was specified, check for any existing directory
- * shared with -o public. If so, fail.
- */
- if (ex.ex_flags & EX_PUBLIC) {
- if (sharepub_exist(dir) == 1) {
- errno = 0;
- pr_err(
- gettext("Cannot share more than filesystem with"
- " 'public' option\n"));
- }
- }
-
- if (verbose)
- printarg(dir, &ex);
-
- if (exportfs(dir, &ex) < 0) {
- switch (errno) {
- case EREMOTE:
- pr_err(gettext("Cannot share remote filesystem: %s\n"),
- dir);
- break;
- case EPERM:
- if (getzoneid() != GLOBAL_ZONEID) {
- pr_err(gettext("Cannot share filesystems "
- "in non-global zones: %s\n"),
- dir);
- break;
- }
- /* FALLTHRU */
- default:
- pr_err("%s: %s\n", dir, strerror(errno));
- break;
- }
- }
-
- if (sharetab_add(dir, res, opts, descr, replace) < 0)
- exit(RET_ERR);
-
- if (ex.ex_flags & EX_LOG)
- if (nfslogtab_add(dir, ex.ex_log_buffer, ex.ex_tag))
- exit(RET_ERR);
-
- /*
- * enable services as needed.
- */
- _check_services(service_list);
-
- return (RET_OK);
-}
-
-/*
- * Check if there already is an entry shared with -o public.
- */
-static int
-sharepub_exist(char *dir)
-{
- struct share *sh;
- int res;
- FILE *f;
- char *val;
-
- f = fopen(SHARETAB, "r");
-
- if (f == NULL) {
- if (errno == ENOENT)
- return (0);
- pr_err("%s: %s\n", SHARETAB, strerror(errno));
- }
-
- while ((res = getshare(f, &sh)) > 0) {
- if (strcmp(sh->sh_fstype, "nfs") != 0)
- continue;
-
- if (strcmp(sh->sh_path, dir) == 0)
- continue;
-
- if (val = getshareopt(sh->sh_opts, SHOPT_PUBLIC)) {
- free(val);
- return (1);
- }
- }
-
- if (res < 0) {
- pr_err(gettext("error reading %s\n"), SHARETAB);
- (void) fclose(f);
- }
-
- (void) fclose(f);
- return (0);
-}
-
-/*
- * Check the nfs share entries in sharetab file.
- * Returns:
- * 0 dir not shareable
- * 1 dir is shareable
- * 2 dir is already shared (can modify options)
- */
-static int
-shareable(path)
- char *path;
-{
- FILE *f;
- struct share *sh;
- struct stat st;
- int res;
-
- errno = 0;
- if (*path != '/')
- pr_err(gettext("%s: not a full pathname\n"), path);
-
- if (stat(path, &st) < 0) /* does it exist ? */
- pr_err(gettext("%s: %s\n"), path, strerror(errno));
-
- /*
- * We make the assumption that if we can't open the SHARETAB
- * file for some reason other than it doesn't exist, then we
- * won't share the directory. Since we can't complete the
- * operation correctly, then let's not do it at all.
- */
- f = fopen(SHARETAB, "r");
- if (f == NULL) {
- if (errno == ENOENT)
- return (1);
- pr_err("%s: %s\n", SHARETAB, strerror(errno));
- return (0);
- }
-
- while ((res = getshare(f, &sh)) > 0) {
- if (strcmp(sh->sh_fstype, "nfs") != 0)
- continue;
-
- if (direq(path, sh->sh_path)) {
- (void) fclose(f);
- return (2);
- }
-
- if (issubdir(sh->sh_path, path)) {
- pr_err(gettext("%s: sub-directory "
- "(%s) already shared\n"),
- path, sh->sh_path);
- }
- if (issubdir(path, sh->sh_path)) {
- pr_err(gettext("%s: parent-directory "
- "(%s) already shared\n"),
- path, sh->sh_path);
- }
- }
-
- if (res < 0)
- pr_err(gettext("error reading %s\n"), SHARETAB);
-
- (void) fclose(f);
- return (1);
-}
-
-static int
-direq(dir1, dir2)
- char *dir1, *dir2;
-{
- struct stat st1, st2;
-
- if (strcmp(dir1, dir2) == 0)
- return (1);
- if (stat(dir1, &st1) < 0 || stat(dir2, &st2) < 0)
- return (0);
- return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev);
-}
-
-static char *optlist[] = {
-#define OPT_RO 0
- SHOPT_RO,
-#define OPT_RW 1
- SHOPT_RW,
-#define OPT_ROOT 2
- SHOPT_ROOT,
-#define OPT_SECURE 3
- SHOPT_SECURE,
-#define OPT_ANON 4
- SHOPT_ANON,
-#define OPT_WINDOW 5
- SHOPT_WINDOW,
-#define OPT_NOSUID 6
- SHOPT_NOSUID,
-#define OPT_ACLOK 7
- SHOPT_ACLOK,
-#define OPT_NOSUB 8
- SHOPT_NOSUB,
-#define OPT_SEC 9
- SHOPT_SEC,
-#define OPT_PUBLIC 10
- SHOPT_PUBLIC,
-#define OPT_INDEX 11
- SHOPT_INDEX,
-#define OPT_LOG 12
- SHOPT_LOG,
-#ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */
-#define OPT_VOLFH 13
- SHOPT_VOLFH,
-#endif /* VOLATILE_FH_TEST */
- NULL
-};
-
-/*
- * If the option string contains a "sec="
- * option, then use new option syntax.
- */
-static int
-newopts(char *opts)
-{
- char *p, *val;
-
- p = strdup(opts);
- if (p == NULL)
- pr_err(gettext("opts: no memory\n"));
-
- while (*p)
- if (getsubopt(&p, optlist, &val) == OPT_SEC)
- return (1);
-
- return (0);
-}
-
-#ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */
-/*
- * Set the ex_flags to indicate which fh expire type. Return 0 for success,
- * error otherwise.
- */
-static int
-nfs4_set_volfh_flags(struct exportdata *exp, char *volfhtypes)
-{
- char *voltype, *next;
- int err = 0;
-
- for (voltype = volfhtypes; !err && voltype != NULL; voltype = next) {
- while (*voltype == ':') voltype++;
- next = strchr(voltype, ':');
- if (next != NULL)
- *next = '\0';
- if (strcmp(voltype, "any") == 0)
- exp->ex_flags |= EX_VOLFH;
- else if (strcmp(voltype, "rnm") == 0)
- exp->ex_flags |= EX_VOLRNM;
- else if (strcmp(voltype, "mig") == 0)
- exp->ex_flags |= EX_VOLMIG;
- else if (strcmp(voltype, "noexpopen") == 0)
- exp->ex_flags |= EX_NOEXPOPEN;
- else {
- err = EINVAL; /* invalid arg */
- }
- if (next != NULL)
- *next = ':';
- }
- return (err);
-}
-#endif /* VOLATILE_FH_TEST */
-
-#define badnum(x) ((x) == NULL || !isdigit(*(x)))
-#define DEF_WIN 30000
-
-/*
- * Parse the share options from the "-o" flag.
- * The extracted data is moved into the exports
- * structure which is passed into the kernel via
- * the exportfs() system call.
- */
-static void
-parseopts_old(struct exportdata *exp, char *opts)
-{
- char *p, *savep, *val, *rootlist;
- struct secinfo *sp;
- int done_aclok = 0;
- int done_nosuid = 0;
- int done_anon = 0;
-
-
- p = strdup(opts);
- if (p == NULL)
- pr_err(gettext("opts: no memory\n"));
-
- exp->ex_version = 2;
- exp->ex_anon = UID_NOBODY;
- exp->ex_seccnt = 1;
- exp->ex_index = NULL;
-
- sp = (struct secinfo *)calloc(1, sizeof (struct secinfo));
- if (sp == NULL)
- pr_err(gettext("ex_secinfo: no memory\n"));
- exp->ex_secinfo = sp;
-
- /*
- * Initialize some fields
- */
- sp->s_flags = 0;
- sp->s_window = DEF_WIN;
- sp->s_rootcnt = 0;
- sp->s_rootnames = NULL;
-
- if (nfs_getseconfig_default(&sp->s_secinfo))
- pr_err(gettext("failed to get default security mode\n"));
-
- while (*p) {
- savep = p;
- switch (getsubopt(&p, optlist, &val)) {
-
- case OPT_RO:
-
- sp->s_flags |= val ? M_ROL : M_RO;
-
- if (sp->s_flags & M_RO && sp->s_flags & M_RW)
- pr_err(gettext("rw vs ro conflict\n"));
- if (sp->s_flags & M_RO && sp->s_flags & M_ROL)
- pr_err(gettext("Ambiguous ro options\n"));
- break;
-
- case OPT_RW:
-
- sp->s_flags |= val ? M_RWL : M_RW;
-
- if (sp->s_flags & M_RO && sp->s_flags & M_RW)
- pr_err(gettext("ro vs rw conflict\n"));
- if (sp->s_flags & M_RW && sp->s_flags & M_RWL)
- pr_err(gettext("Ambiguous rw options\n"));
- break;
-
- case OPT_ROOT:
- if (val == NULL)
- pr_err(gettext("missing root list\n"));
- rootlist = val;
- sp->s_flags |= M_ROOT;
- break;
-
- case OPT_SECURE:
- if (nfs_getseconfig_byname("dh", &sp->s_secinfo)) {
- pr_err(gettext("invalid sec name\n"));
- }
- break;
-
- case OPT_ANON:
- if (done_anon++)
- pr_err(gettext("option anon repeated\n"));
-
- if (!val) {
- pr_err(gettext("missing anon value\n"));
- }
- /* check for special "-1" value, which is ok */
- if (strcmp(val, "-1") != 0 && badnum(val)) {
- pr_err(gettext("invalid anon value\n"));
- }
- exp->ex_anon = atoi(val);
- break;
-
- case OPT_WINDOW:
- if (badnum(val))
- pr_err(gettext("invalid window value\n"));
- sp->s_window = atoi(val);
- break;
-
- case OPT_NOSUID:
- if (done_nosuid++)
- pr_err(gettext("option nosuid repeated\n"));
-
- exp->ex_flags |= EX_NOSUID;
- break;
-
- case OPT_ACLOK:
- if (done_aclok++)
- pr_err(gettext("option aclok repeated\n"));
- exp->ex_flags |= EX_ACLOK;
- break;
-
- case OPT_NOSUB:
- /*
- * The "don't allow mount of subdirectories" option.
- */
- exp->ex_flags |= EX_NOSUB;
- break;
-
- case OPT_PUBLIC:
- exp->ex_flags |= EX_PUBLIC;
- break;
-
- case OPT_INDEX:
- exportindex(exp, val);
- break;
-
- case OPT_LOG:
- configlog(exp, val);
- break;
-
-#ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */
- case OPT_VOLFH:
- /* volatile filehandles - expire on share */
- if (val == NULL)
- pr_err(gettext("missing volatile fh types\n"));
- if (nfs4_set_volfh_flags(exp, val))
- pr_err(gettext("invalid volatile fh types\n"));
- break;
-#endif /* VOLATILE_FH_TEST */
-
- default:
- pr_err(gettext("invalid share option: '%s'\n"), savep);
- }
- }
- if (sp->s_flags & M_ROOT && sp->s_secinfo.sc_rpcnum != AUTH_UNIX) {
- sp->s_rootnames = get_rootnames(&sp->s_secinfo, rootlist,
- &sp->s_rootcnt);
- if (sp->s_rootnames == NULL)
- pr_err(gettext("Bad root list\n"));
- }
-
- /*
- * Set uninitialized flags to "rw"
- */
- if ((sp->s_flags & (M_RO|M_RW|M_RWL|M_ROL)) == 0)
- sp->s_flags |= M_RW;
-}
-
-/*
- * Parse the new share options from the "-o" flag.
- * Parsing is more complicated than the old case
- * Since we may be setting up multiple secinfo entries.
- * Syntax is more restrictive: the flavor-dependent
- * options: ro, rw, root, window can only follow
- * a sec option.
- */
-static void
-parseopts_new(struct exportdata *exp, char *opts)
-{
- char *p, *q, *savep, *val;
- char *f, *lasts;
- struct secinfo *sp1, *sp, *pt;
- int i, secopt;
- int count = 0;
- int done_aclok = 0;
- int done_nosuid = 0;
- int done_anon = 0;
-
- exp->ex_version = 2;
- exp->ex_anon = UID_NOBODY;
- exp->ex_index = NULL;
-
- p = strdup(opts);
- if (p == NULL)
- pr_err(gettext("opts: no memory\n"));
-
- /*
- * Count the number of security modes
- */
- while (*p) {
- switch (getsubopt(&p, optlist, &val)) {
- case OPT_SECURE:
- pr_err(gettext("Cannot mix options "
- "secure and sec\n"));
- break;
- case OPT_SEC:
- count++;
- for (q = val; *q; q++)
- if (*q == ':')
- count++;
- break;
- }
- }
-
- exp->ex_seccnt = count;
-
- sp = (struct secinfo *)calloc(count, sizeof (struct secinfo));
- if (sp == NULL)
- pr_err(gettext("ex_secinfo: no memory\n"));
-
- /*
- * Initialize some fields
- */
- for (i = 0; i < count; i++) {
- sp[i].s_flags = 0;
- sp[i].s_window = DEF_WIN;
- sp[i].s_rootcnt = 0;
- sp[i].s_rootnames = NULL;
- }
-
- exp->ex_secinfo = sp;
- sp1 = sp;
-
- p = strdup(opts);
- if (p == NULL)
- pr_err(gettext("opts: no memory\n"));
-
- if (nfs_getseconfig_default(&sp->s_secinfo))
- pr_err(gettext("failed to get default security mode\n"));
-
- secopt = 0;
-
- while (*p) {
- savep = p;
- switch (getsubopt(&p, optlist, &val)) {
-
- case OPT_SEC:
- if (secopt)
- sp++;
- sp1 = sp;
- secopt++;
-
- while ((f = strtok_r(val, ":", &lasts)) != NULL) {
- if (nfs_getseconfig_byname(f, &sp->s_secinfo))
- pr_err(gettext("Invalid security mode"
- " \"%s\"\n"), f);
- val = NULL;
- if (lasts)
- sp++;
- }
- break;
-
- case OPT_RO:
- if (secopt == 0)
- pr_err(gettext("need sec option before ro\n"));
-
- sp->s_flags |= val ? M_ROL : M_RO;
-
- if (sp->s_flags & M_RO && sp->s_flags & M_RW)
- pr_err(gettext("rw vs ro conflict\n"));
- if (sp->s_flags & M_RO && sp->s_flags & M_ROL)
- pr_err(gettext("Ambiguous ro options\n"));
-
- for (pt = sp1; pt < sp; pt++)
- pt->s_flags = sp->s_flags;
- break;
-
- case OPT_RW:
- if (secopt == 0)
- pr_err(gettext("need sec option before rw\n"));
-
- sp->s_flags |= val ? M_RWL : M_RW;
-
- if (sp->s_flags & M_RO && sp->s_flags & M_RW)
- pr_err(gettext("ro vs rw conflict\n"));
- if (sp->s_flags & M_RW && sp->s_flags & M_RWL)
- pr_err(gettext("Ambiguous rw options\n"));
-
- for (pt = sp1; pt < sp; pt++)
- pt->s_flags = sp->s_flags;
- break;
-
- case OPT_ROOT:
- if (secopt == 0)
- pr_err(gettext("need sec option before "
- "root\n"));
-
- if (val == NULL)
- pr_err(gettext("missing root list\n"));
-
- for (pt = sp1; pt <= sp; pt++) {
- pt->s_flags |= M_ROOT;
-
- /*
- * Can treat AUTH_UNIX root lists
- * as a special case and have
- * the nfsauth service check the
- * list just like any other access
- * list, i.e. supports netgroups,
- * domain suffixes, etc.
- */
- if (pt->s_secinfo.sc_rpcnum == AUTH_UNIX)
- continue;
-
- /*
- * Root lists for other sec types
- * need to be checked in the
- * kernel. Build a list of names
- * to be fed into the kernel via
- * exportfs().
- */
- pt->s_rootnames =
- get_rootnames(&pt->s_secinfo, val,
- &pt->s_rootcnt);
- if (pt->s_rootnames == NULL)
- pr_err(gettext("Bad root list\n"));
- }
- break;
-
- case OPT_WINDOW:
- if (secopt == 0)
- pr_err(gettext("need sec option before "
- "window\n"));
-
- if (badnum(val))
- pr_err(gettext("invalid window value\n"));
- sp->s_window = atoi(val);
-
- for (pt = sp1; pt < sp; pt++)
- pt->s_window = sp->s_window;
- break;
-
- case OPT_ANON:
- if (done_anon++)
- pr_err(gettext("option anon repeated\n"));
-
- if (!val) {
- pr_err(gettext("missing anon value\n"));
- }
- /* check for special "-1" value, which is ok */
- if (strcmp(val, "-1") != 0 && badnum(val))
- pr_err(gettext("invalid anon value\n"));
-
- exp->ex_anon = atoi(val);
- break;
-
- case OPT_NOSUID:
- if (done_nosuid++)
- pr_err(gettext("option nosuid repeated\n"));
-
- exp->ex_flags |= EX_NOSUID;
- break;
-
- case OPT_ACLOK:
- if (done_aclok++)
- pr_err(gettext("option aclok repeated\n"));
- exp->ex_flags |= EX_ACLOK;
- break;
-
- case OPT_NOSUB:
- /*
- * The "don't allow mount of subdirectories" option.
- */
- exp->ex_flags |= EX_NOSUB;
- break;
-
- case OPT_PUBLIC:
- exp->ex_flags |= EX_PUBLIC;
- break;
-
- case OPT_INDEX:
- exportindex(exp, val);
- break;
-
- case OPT_LOG:
- configlog(exp, val);
- break;
-
-#ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */
- case OPT_VOLFH:
- /* volatile filehandles - expire on share */
- if (val == NULL)
- pr_err(gettext("missing volatile fh types\n"));
- if (nfs4_set_volfh_flags(exp, val))
- pr_err(gettext("invalid volatile fh types\n"));
- break;
-#endif /* VOLATILE_FH_TEST */
-
- default:
- pr_err(gettext("invalid share option: '%s'\n"), savep);
- }
- }
-
- /*
- * Set uninitialized flags to "rw"
- */
- sp = exp->ex_secinfo;
- for (i = 0; i < count; i++) {
- if ((sp[i].s_flags & (M_RO|M_RW|M_RWL|M_ROL)) == 0)
- sp[i].s_flags |= M_RW;
- }
-}
-
-/*
- * check the argument specified with the index option and set
- * export index file and flags
- */
-static void
-exportindex(struct exportdata *exp, char *val)
-{
- char *p = val;
-
- if (val == NULL)
- goto badindexarg;
-
- p = val;
- while (*p != '\0') {
- if (*p == '/')
- goto badindexarg;
- p++;
- }
-
- if (strcmp(val, "..") == 0)
- goto badindexarg;
-
- /*
- * treat a "." or an empty index string as if the
- * index option is not present.
- */
- if (val[0] == '\0' || (strcmp(val, ".") == 0))
- return;
-
- exp->ex_index = strdup(val);
- if (!exp->ex_index) {
- pr_err(gettext("exportindex: out of memory\n"));
- return;
- }
- exp->ex_flags |= EX_INDEX;
-
- return;
-
-badindexarg:
- pr_err(gettext("index option requires a filename as argument\n"));
-}
-
-/*
- * Given a seconfig entry and a colon-separated
- * list of names, allocate an array big enough
- * to hold the root list, then convert each name to
- * a principal name according to the security
- * info and assign it to an array element.
- * Return the array and its size.
- */
-static caddr_t *
-get_rootnames(seconfig_t *sec, char *list, int *count)
-{
- caddr_t *a;
- int c, i;
- char *host, *p;
-
- list = strdup(list);
- if (list == NULL)
- pr_err(gettext("get_rootnames: no memory\n"));
-
- /*
- * Count the number of strings in the list.
- * This is the number of colon separators + 1.
- */
- c = 1;
- for (p = list; *p; p++)
- if (*p == ':')
- c++;
- *count = c;
-
- a = (caddr_t *)malloc(c * sizeof (char *));
- if (a == NULL)
- pr_err(gettext("get_rootnames: no memory\n"));
-
- for (i = 0; i < c; i++) {
- host = strtok(list, ":");
- if (!nfs_get_root_principal(sec, host, &a[i])) {
- a = NULL;
- break;
- }
- list = NULL;
- }
-
- return (a);
-}
-
-/*
- * Append an entry to the sharetab file
- */
-static int
-sharetab_add(dir, res, opts, descr, replace)
- char *dir, *res, *opts, *descr;
- int replace;
-{
- int ret;
- FILE *f;
- struct share sh;
- int logging = 0;
-
- /*
- * Open the file for update and create it if necessary.
- * This may leave the I/O offset at the end of the file,
- * so rewind back to the beginning of the file.
- */
- f = fopen(SHARETAB, "a+");
- if (f == NULL) {
- pr_err("%s: %s\n", SHARETAB, strerror(errno));
- return (-1);
- }
- rewind(f);
-
- if (lockf(fileno(f), F_LOCK, 0L) < 0) {
- pr_err(gettext("cannot lock %s: %s\n"),
- SHARETAB, strerror(errno));
- (void) fclose(f);
- return (-1);
- }
-
- /*
- * If re-sharing an old share with new options
- * then first remove the old share entry.
- */
- if (replace) {
- if ((ret = remshare(f, dir, &logging)) < 0) {
- switch (ret) {
- case -1:
- pr_err(gettext("share complete, however, "
- "failed to remove old sharetab"
- " entry, no memory\n"));
- break;
- case -2:
- pr_err(gettext("share complete, however, "
- "sharetab may be corrupt, ftrucate call"
- " failure during sharetab update\n"));
- break;
- case -3:
- pr_err(gettext("share complete, however, "
- "failed to remove old sharetab entry,"
- " corrupt sharetab file\n"));
- break;
- }
- }
-
- if (logging) {
- /*
- * Entry replaced was logged, deactivate it in
- * nfslogtab.
- */
- (void) nfslogtab_deactivate(dir);
- }
- }
-
- sh.sh_path = dir;
- sh.sh_res = res;
- sh.sh_fstype = "nfs";
- sh.sh_opts = opts;
- sh.sh_descr = descr;
-
- if (putshare(f, &sh) < 0)
- pr_err(gettext("addshare: couldn't add %s to %s\n"),
- dir, SHARETAB);
-
- (void) fclose(f);
- return (0);
-}
-
-/*
- * Append an entry to the nfslogtab file
- */
-static int
-nfslogtab_add(dir, buffer, tag)
- char *dir, *buffer, *tag;
-{
- FILE *f;
- struct logtab_ent lep;
- int error = 0;
-
- /*
- * Open the file for update and create it if necessary.
- * This may leave the I/O offset at the end of the file,
- * so rewind back to the beginning of the file.
- */
- f = fopen(NFSLOGTAB, "a+");
- if (f == NULL) {
- error = errno;
- pr_err(gettext(
- "share complete, however failed to open %s "
- "for update: %s\n"), NFSLOGTAB, strerror(errno));
- goto out;
- }
- rewind(f);
-
- if (lockf(fileno(f), F_LOCK, 0L) < 0) {
- pr_err(gettext(
- "share complete, however failed to lock %s "
- "for update: %s\n"), NFSLOGTAB, strerror(errno));
- error = -1;
- goto out;
- }
-
- if (logtab_deactivate_after_boot(f) == -1) {
- pr_err(gettext(
- "share complete, however could not deactivate "
- "entries in %s\n"), NFSLOGTAB);
- error = -1;
- goto out;
- }
-
- /*
- * Remove entries matching buffer and sharepoint since we're
- * going to replace it with perhaps an entry with a new tag.
- */
- if (logtab_rement(f, buffer, dir, NULL, -1)) {
- pr_err(gettext(
- "share complete, however could not remove matching "
- "entries in %s\n"), NFSLOGTAB);
- error = -1;
- goto out;
- }
-
- /*
- * Deactivate all active entries matching this sharepoint
- */
- if (logtab_deactivate(f, NULL, dir, NULL)) {
- pr_err(gettext(
- "share complete, however could not deactivate matching "
- "entries in %s\n"), NFSLOGTAB);
- error = -1;
- goto out;
- }
-
- lep.le_buffer = buffer;
- lep.le_path = dir;
- lep.le_tag = tag;
- lep.le_state = LES_ACTIVE;
-
- /*
- * Add new sharepoint / buffer location to nfslogtab
- */
- if (logtab_putent(f, &lep) < 0) {
- pr_err(gettext(
- "share complete, however could not add %s to %s\n"),
- dir, NFSLOGTAB);
- error = -1;
- }
-
-out:
- if (f != NULL)
- (void) fclose(f);
- return (error);
-}
-
-/*
- * Deactivate an entry from the nfslogtab file
- */
-static int
-nfslogtab_deactivate(path)
- char *path;
-{
- FILE *f;
- int error = 0;
-
- f = fopen(NFSLOGTAB, "r+");
- if (f == NULL) {
- error = errno;
- fprintf(stderr, gettext(
- "share complete, however could not open %s for "
- "update: %s\n"), NFSLOGTAB, strerror(error));
- goto out;
- }
- if (lockf(fileno(f), F_LOCK, 0L) < 0) {
- error = errno;
- pr_err(gettext(
- "share complete, however could not lock %s for "
- "update: %s\n"), NFSLOGTAB, strerror(error));
- goto out;
- }
- if (logtab_deactivate(f, NULL, path, NULL) == -1) {
- error = -1;
- pr_err(gettext("share complete, however could not "
- "deactivate %s in %s\n"), path, NFSLOGTAB);
- goto out;
- }
-
-out: if (f != NULL)
- (void) fclose(f);
-
- return (error);
-}
-
-static void
-usage()
-{
- (void) fprintf(stderr,
- "Usage: share [-o options] [-d description] pathname [resource]\n");
-}
-
-/*
- * This is for testing only
- * It displays the export structure that
- * goes into the kernel.
- */
-static void
-printarg(char *path, struct exportdata *ep)
-{
- int i, j;
- struct secinfo *sp;
-
- printf("%s:\n", path);
- printf("\tex_version = %d\n", ep->ex_version);
- printf("\tex_path = %s\n", ep->ex_path);
- printf("\tex_pathlen = %d\n", ep->ex_pathlen);
- printf("\tex_flags: (0x%02x) ", ep->ex_flags);
- if (ep->ex_flags & EX_NOSUID)
- printf("NOSUID ");
- if (ep->ex_flags & EX_ACLOK)
- printf("ACLOK ");
- if (ep->ex_flags & EX_PUBLIC)
- printf("PUBLIC ");
- if (ep->ex_flags & EX_NOSUB)
- printf("NOSUB ");
- if (ep->ex_flags & EX_LOG)
- printf("LOG ");
- if (ep->ex_flags & EX_LOG_ALLOPS)
- printf("LOG_ALLOPS ");
- if (ep->ex_flags == 0)
- printf("(none)");
- printf("\n");
- if (ep->ex_flags & EX_LOG) {
- printf("\tex_log_buffer = %s\n",
- (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)"));
- printf("\tex_tag = %s\n", (ep->ex_tag ? ep->ex_tag : "(NULL)"));
- }
- printf("\tex_anon = %d\n", ep->ex_anon);
- printf("\tex_seccnt = %d\n", ep->ex_seccnt);
- printf("\n");
- for (i = 0; i < ep->ex_seccnt; i++) {
- sp = &ep->ex_secinfo[i];
- printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name);
- printf("\t\ts_flags: (0x%02x) ", sp->s_flags);
- if (sp->s_flags & M_ROOT) printf("M_ROOT ");
- if (sp->s_flags & M_RO) printf("M_RO ");
- if (sp->s_flags & M_ROL) printf("M_ROL ");
- if (sp->s_flags & M_RW) printf("M_RW ");
- if (sp->s_flags & M_RWL) printf("M_RWL ");
- if (sp->s_flags == 0) printf("(none)");
- printf("\n");
- printf("\t\ts_window = %d\n", sp->s_window);
- printf("\t\ts_rootcnt = %d ", sp->s_rootcnt);
- for (j = 0; j < sp->s_rootcnt; j++)
- printf("%s ", sp->s_rootnames[j]);
- printf("\n\n");
- }
-}
-
-/*
- * Look for the specified tag in the configuration file. If it is found,
- * enable logging and set the logging configuration information for exp.
- */
-static void
-configlog(struct exportdata *exp, char *tag)
-{
- nfsl_config_t *configlist, *configp;
- int error = 0;
- char globaltag[] = DEFAULTTAG;
-
- /*
- * Sends config errors to stderr
- */
- nfsl_errs_to_syslog = B_FALSE;
-
- /*
- * get the list of configuration settings
- */
- error = nfsl_getconfig_list(&configlist);
- if (error) {
- pr_err(gettext("Cannot get log configuration: %s\n"),
- strerror(error));
- }
-
- if (tag == NULL)
- tag = globaltag;
- if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) {
- nfsl_freeconfig_list(&configlist);
- pr_err(gettext("No tags matching \"%s\"\n"), tag);
- }
-
- if ((exp->ex_tag = strdup(tag)) == NULL) {
- error = ENOMEM;
- goto out;
- }
- if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) {
- error = ENOMEM;
- goto out;
- }
- exp->ex_flags |= EX_LOG;
- if (configp->nc_rpclogpath != NULL)
- exp->ex_flags |= EX_LOG_ALLOPS;
-out:
- nfsl_freeconfig_list(&configlist);
- if (error != 0) {
- if (exp->ex_flags != NULL)
- free(exp->ex_tag);
- if (exp->ex_log_buffer != NULL)
- free(exp->ex_log_buffer);
- pr_err(gettext("Cannot set log configuration: %m"),
- strerror(error));
- }
-}
-
-/*VARARGS1*/
-static void
-pr_err(char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- (void) fprintf(stderr, "share_nfs: ");
- (void) vfprintf(stderr, fmt, ap);
- va_end(ap);
- exit(RET_ERR);
-}
diff --git a/usr/src/cmd/fs.d/nfs/svc/nfs-server b/usr/src/cmd/fs.d/nfs/svc/nfs-server
index 82b48ced37..fc5e243d42 100644
--- a/usr/src/cmd/fs.d/nfs/svc/nfs-server
+++ b/usr/src/cmd/fs.d/nfs/svc/nfs-server
@@ -40,26 +40,15 @@ case "$1" in
exit $SMF_EXIT_OK
fi
- # Share any ZFS filesystems marked for sharing. The environment
- # variable prevents share(1M) from checking whether each filesystem
- # is in use, an O(n^2) operation with no purpose at this time
-
- if [ -x /usr/sbin/zfs ]; then
- SHARE_NOINUSE_CHECK=1; export SHARE_NOINUSE_CHECK
- /usr/sbin/zfs share -a
- unset SHARE_NOINUSE_CHECK
- fi
-
- # If /etc/dfs/dfstab exists and has non-blank or non-commented-out
- # lines, then run shareall to export them.
+ # Share all file systems enabled for sharing. sharemgr understands
+ # regular shares and ZFS shares and will handle both. Technically,
+ # the shares would have been started long before getting here since
+ # nfsd has a dependency on them.
startnfsd=0
- if [ -f /etc/dfs/dfstab ] && /usr/bin/egrep -v '^[ ]*(#|$)' \
- /etc/dfs/dfstab >/dev/null 2>&1; then
-
- /usr/sbin/shareall -F nfs
- fi
+ # restart stopped shares from the repository
+ /usr/sbin/sharemgr start -P nfs -a
# Start up mountd and nfsd if anything is exported.
@@ -71,15 +60,6 @@ case "$1" in
[ -f /etc/dfs/sharetab ] && /usr/bin/chmod 644 /etc/dfs/sharetab
- # Start nfslogd if /etc/nfs/nfslogtab exists and is not empty
- # This means that either we just shared new filesystems with
- # logging enabled, or they were shared in the previous session
- # and their working buffers haven't been processed yet.
-
- if [ -s /etc/nfs/nfslogtab ]; then
- /usr/lib/nfs/nfslogd &
- fi
-
# Options for nfsd are now set in /etc/default/nfs
if [ $startnfsd -ne 0 ]; then
/usr/lib/nfs/mountd
@@ -93,23 +73,15 @@ case "$1" in
;;
'refresh')
- /usr/sbin/shareall -F nfs
+ /usr/sbin/sharemgr start -P nfs -a
;;
'stop')
/usr/bin/pkill -x -u 0,1 -z $zone '(nfsd|mountd)'
- # Unshare shared ZFS filesystems.
-
- if [ -x /usr/sbin/zfs ]; then
- /usr/sbin/zfs unshare -a
- fi
-
- # Unshare remaining shared filesystems.
+ # Unshare all shared file systems using NFS
- if /usr/bin/grep -s nfs /etc/dfs/sharetab >/dev/null; then
- /usr/sbin/unshareall -F nfs
- fi
+ /usr/sbin/sharemgr stop -P nfs -a
#
# Wait up to 10 seconds for nfslogd to gracefully handle SIGHUP
diff --git a/usr/src/cmd/fs.d/nfs/svc/server.xml b/usr/src/cmd/fs.d/nfs/svc/server.xml
index 6490af3efb..e1621ff08a 100644
--- a/usr/src/cmd/fs.d/nfs/svc/server.xml
+++ b/usr/src/cmd/fs.d/nfs/svc/server.xml
@@ -1,29 +1,29 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!--
- Copyright 2005 Sun Microsystems, Inc. All rights reserved.
- Use is subject to license terms.
CDDL HEADER START
- The contents of this file are subject to the terms of the
- Common Development and Distribution License, Version 1.0 only
- (the "License"). You may not use this file except in compliance
- with the License.
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
- You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- or http://www.opensolaris.org/os/licensing.
- See the License for the specific language governing permissions
- and limitations under the License.
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
- When distributing Covered Code, include this CDDL HEADER in each
- file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- If applicable, add the following below this CDDL HEADER, with the
- fields enclosed by brackets "[]" replaced with your own identifying
- information: Portions Copyright [yyyy] [name of copyright owner]
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
CDDL HEADER END
+ Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
ident "%Z%%M% %I% %E% SMI"
NOTE: This service manifest is not editable; its contents will
@@ -89,6 +89,13 @@
<service_fmri value='svc:/network/rpc/gss' />
</dependency>
+ <dependency name='share-group'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/shares/group' />
+ </dependency>
+
<!-- Must have all local filesystems mounted before we share them -->
<dependency name='filesystem-local'
grouping='require_all'
diff --git a/usr/src/cmd/fs.d/nfs/unshare/unshare.c b/usr/src/cmd/fs.d/nfs/unshare/unshare.c
deleted file mode 100644
index 68675d0416..0000000000
--- a/usr/src/cmd/fs.d/nfs/unshare/unshare.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
-/* All Rights Reserved */
-
-/*
- * University Copyright- Copyright (c) 1982, 1986, 1988
- * The Regents of the University of California
- * All Rights Reserved
- *
- * University Acknowledgment- Portions of this document are derived from
- * software developed by the University of California, Berkeley, and its
- * contributors.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-/*
- * nfs unshare
- */
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <errno.h>
-#include <signal.h>
-#include <stdlib.h>
-#include "../lib/sharetab.h"
-#include "../lib/nfslogtab.h"
-
-#define RET_OK 0
-#define RET_ERR 32
-
-static int do_unshare();
-static void pr_err(char *, ...);
-static int sharetab_del();
-static int nfslogtab_deactivate();
-static void usage();
-
-int
-main(argc, argv)
- int argc;
- char **argv;
-{
- char dir[MAXPATHLEN];
-
- if (argc != 2) {
- usage();
- exit(1);
- }
-
- /* Don't drop core if the NFS module isn't loaded. */
- signal(SIGSYS, SIG_IGN);
-
- if (realpath(argv[1], dir) == NULL) {
- pr_err("%s: %s\n", argv[1], strerror(errno));
- exit(RET_ERR);
- }
-
- return (do_unshare(dir));
-}
-
-static int
-do_unshare(path)
- char *path;
-{
- int logging = 0;
-
- if (exportfs(path, NULL) < 0) {
- if (errno == EINVAL)
- pr_err("%s: not shared\n", path);
- else
- pr_err("%s: %s\n", path, strerror(errno));
- return (RET_ERR);
- }
-
- if (sharetab_del(path, &logging) < 0)
- return (RET_ERR);
-
- if (logging) {
- if (nfslogtab_deactivate(path) < 0)
- return (RET_ERR);
- }
-
- return (RET_OK);
-}
-
-/*
- * Remove an entry from the sharetab file.
- */
-static int
-sharetab_del(path, logging)
- char *path;
- int *logging;
-{
- FILE *f;
-
- f = fopen(SHARETAB, "r+");
- if (f == NULL) {
- pr_err("%s: %s\n", SHARETAB, strerror(errno));
- return (-1);
- }
- if (lockf(fileno(f), F_LOCK, 0L) < 0) {
- pr_err("cannot lock %s: %s\n", SHARETAB, strerror(errno));
- (void) fclose(f);
- return (-1);
- }
- if (remshare(f, path, logging) < 0) {
- pr_err("remshare\n");
- (void) fclose(f);
- return (-1);
- }
- (void) fclose(f);
- return (0);
-}
-
-/*
- * Deactivate an entry from the nfslogtab file.
- */
-static int
-nfslogtab_deactivate(path)
- char *path;
-{
- FILE *f;
- int error = 0;
-
- f = fopen(NFSLOGTAB, "r+");
- if (f == NULL) {
- error = errno;
- pr_err("%s: %s\n", NFSLOGTAB, strerror(error));
- goto out;
- }
- if (lockf(fileno(f), F_LOCK, 0L) < 0) {
- error = errno;
- pr_err("cannot lock %s: %s\n", NFSLOGTAB, strerror(error));
- goto out;
- }
- if (logtab_deactivate(f, NULL, path, NULL) == -1) {
- error = -1;
- pr_err("logtab_deactivate\n");
- goto out;
- }
-
-out: if (error) {
- pr_err("could not deactivate %s entry in %s\n",
- path, NFSLOGTAB);
- }
-
- if (f != NULL)
- (void) fclose(f);
-
- return (error);
-}
-
-static void
-usage()
-{
- (void) fprintf(stderr, "Usage: unshare { pathname | resource }\n");
-}
-
-/*VARARGS1*/
-static void
-pr_err(char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- (void) fprintf(stderr, "nfs unshare: ");
- (void) vfprintf(stderr, fmt, ap);
- va_end(ap);
-}
diff --git a/usr/src/cmd/initpkg/dfstab.sh b/usr/src/cmd/initpkg/dfstab.sh
index 2c93f5eef8..9ee99002e8 100644
--- a/usr/src/cmd/initpkg/dfstab.sh
+++ b/usr/src/cmd/initpkg/dfstab.sh
@@ -3,9 +3,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -23,20 +22,17 @@
# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
# All Rights Reserved
#
-#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1.2.1 */
case "$MACH" in
"u3b2"|"sparc"|"i386"|"ppc" )
echo "
-# Place share(1M) commands here for automatic execution
-# on entering init state 3.
-#
-# Issue the command 'svcadm enable network/nfs/server' to
-# run the NFS daemon processes and the share commands, after adding
-# the very first entry to this file.
+# Do not modify this file directly.
+# Use the sharemgr(1m) command for all share management
+# This file is reconstructed and only maintained for backward
+# compatibility. Configuration lines could be lost.
#
# share [-F fstype] [ -o options] [-d \"<text>\"] <pathname> [resource]
# .e.g,
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 7fd139658c..b08aa23cd9 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -223,6 +223,7 @@ SUBDIRS += \
brand \
policykit \
hal \
+ libshare \
$($(MACH)_SUBDIRS)
sparc_SUBDIRS= .WAIT \
@@ -276,6 +277,7 @@ MSGSUBDIRS= \
libsasl \
libldap5 \
libsecdb \
+ libshare \
libsldap \
libslp \
libsmedia \
@@ -385,6 +387,7 @@ HDRSUBDIRS= \
udapl \
libmapid \
libkrb5 \
+ libshare \
$($(MACH)_HDRSUBDIRS)
$(CLOSED_BUILD)HDRSUBDIRS += \
@@ -500,6 +503,7 @@ libzfs_jni: libdiskmgt libnvpair libzfs
libzpool: libavl libumem libnvpair
libsec: libavl
brand: libc libsocket
+libshare: libscf libzfs libuuid libfsmgt libsecdb
#
# The reason this rule checks for the existence of the
diff --git a/usr/src/lib/libsecdb/exec_attr.txt b/usr/src/lib/libsecdb/exec_attr.txt
index 07dc34f7bb..020c2ab0c6 100644
--- a/usr/src/lib/libsecdb/exec_attr.txt
+++ b/usr/src/lib/libsecdb/exec_attr.txt
@@ -95,6 +95,7 @@ File System Management:solaris:cmd:::/usr/sbin/quotaoff:uid=0;gid=sys
File System Management:solaris:cmd:::/usr/sbin/quotaon:uid=0;gid=sys
File System Management:suser:cmd:::/usr/sbin/ramdiskadm:euid=0
File System Management:suser:cmd:::/usr/sbin/share:uid=0;gid=root
+File System Management:suser:cmd:::/usr/sbin/sharemgr:uid=0;gid=root
File System Management:suser:cmd:::/usr/sbin/shareall:uid=0;gid=root
File System Management:suser:cmd:::/usr/sbin/swap:euid=0
File System Management:suser:cmd:::/usr/sbin/umount:uid=0
diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt
index a79f9aabe4..cae7588282 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -42,7 +42,7 @@ Log Management:::Manage log files:help=RtLogMngmnt.html
Basic Solaris User:::Automatically assigned rights:auths=solaris.profmgr.read,solaris.jobs.user,solaris.mail.mailq,solaris.device.mount.removable;profiles=All;help=RtDefault.html
Device Security:::Manage devices and Volume Manager:auths=solaris.device.*;help=RtDeviceSecurity.html
DHCP Management:::Manage the DHCP service:auths=solaris.dhcpmgr.*;help=RtDHCPMngmnt.html
-File System Management:::Manage, mount, share file systems:auths=solaris.smf.manage.autofs;help=RtFileSysMngmnt.html
+File System Management:::Manage, mount, share file systems:auths=solaris.smf.manage.autofs,solaris.smf.manage.shares.*,solaris.smf.value.shares.*;help=RtFileSysMngmnt.html
File System Security:::Manage file system security attributes:help=RtFileSysSecurity.html
HAL Management:::Manage HAL SMF service:auths=solaris.smf.manage.hal;help=RtHALMngmnt.html
Mail Management:::Manage sendmail & queues:auths=solaris.smf.manage.sendmail;help=RtMailMngmnt.html
diff --git a/usr/src/lib/libshare/Makefile b/usr/src/lib/libshare/Makefile
new file mode 100644
index 0000000000..afdcaee749
--- /dev/null
+++ b/usr/src/lib/libshare/Makefile
@@ -0,0 +1,68 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../../Makefile.master
+
+include ../Makefile.lib
+
+HDRS = libshare.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+ROOTHDRDIR= $(ROOT)/usr/include
+ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%)
+
+MSGFILES= common/libshare.c common/libsharecore.c common/scfutil.c \
+ common/parser.c common/libshare_zfs.c
+POFILE= libshare.po
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+_msg: $(MSGDOMAINPOFILE)
+
+
+FRC:
+
+include ../Makefile.targ
+include ../../Makefile.msg.targ
diff --git a/usr/src/lib/libshare/Makefile.com b/usr/src/lib/libshare/Makefile.com
new file mode 100644
index 0000000000..d444041cf7
--- /dev/null
+++ b/usr/src/lib/libshare/Makefile.com
@@ -0,0 +1,81 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+LIBRARY = libshare.a
+VERS = .1
+NFSLIB_DIR = $(SRC)/cmd/fs.d/nfs/lib
+NFSSECSRC = $(NFSLIB_DIR)/nfs_sec.c
+NFSSHARETAB = $(NFSLIB_DIR)/sharetab.c
+
+LIBOBJS = libshare.o libsharecore.o scfutil.o libshare_zfs.o \
+ plugin.o parser.o issubdir.o
+OTHOBJS = sharetab.o nfs_sec.o
+OBJECTS = $(LIBOBJS) $(OTHOBJS)
+COMMON = ../common
+
+SRCS = $(OBJECTS:%.o=../%.c)
+LIBSRCS = $(LIBOBJS:%.o=$(COMMON)/%.c)
+POFILES = $(OBJECTS:%.o=%.po)
+POFILE = libshare.po
+
+include ../../Makefile.lib
+
+ROOTDIRS= $(ROOT)/usr/include
+
+ROOTHDRS= $(HDRS:%=$(ROOTDIRS)/%)
+
+CHECKHDRS= $(HDRS:%.h=%.check)
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc -lnsl -lscf -lxml2 -lzfs -luuid -lfsmgt
+$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
+
+SRCDIR = ../common
+MAPDIR = ../spec/$(TRANSMACH)
+SPECMAPFILE = $(MAPDIR)/mapfile
+
+#add nfs/lib directory as part of the include path
+CFLAGS += $(CCVERBOSE) -g
+CPPFLAGS += -D_REENTRANT -I$(SRC)/lib/libfsmgt/common \
+ -I$(SRC)/cmd/fs.d/nfs/lib -I/usr/include/libxml2 \
+ -I../common
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+# we don't want to lint the sharetab and nfs_sec files
+lint: $$(LIBSRCS)
+ $(LINT.c) $(LINTCHECKFLAGS) $(LIBSRCS) $(LDLIBS)
+
+pics/%.o: $(NFSLIB_DIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+include ../../Makefile.targ
+
+$(POFILE): $(POFILES)
+ $(RM) $@; cat $(POFILES) > $@
diff --git a/usr/src/cmd/fs.d/nfs/share/issubdir.c b/usr/src/lib/libshare/common/issubdir.c
index d24a4b89e5..d24a4b89e5 100644
--- a/usr/src/cmd/fs.d/nfs/share/issubdir.c
+++ b/usr/src/lib/libshare/common/issubdir.c
diff --git a/usr/src/lib/libshare/common/libshare.c b/usr/src/lib/libshare/common/libshare.c
new file mode 100644
index 0000000000..65ab1fc55f
--- /dev/null
+++ b/usr/src/lib/libshare/common/libshare.c
@@ -0,0 +1,2665 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Share control API
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include "libshare.h"
+#include "libshare_impl.h"
+#include <libscf.h>
+#include "scfutil.h"
+#include <ctype.h>
+#include <libintl.h>
+
+#if _NOT_SMF
+#define CONFIG_FILE "/var/tmp/share.cfg"
+#define CONFIG_FILE_TMP "/var/tmp/share.cfg.tmp"
+#endif
+#define TSTAMP(tm) (uint64_t)(((uint64_t)tm.tv_sec << 32) | \
+ (tm.tv_nsec & 0xffffffff))
+
+/*
+ * internal data structures
+ */
+
+static xmlNodePtr sa_config_tree; /* the current config */
+static xmlDocPtr sa_config_doc = NULL; /* current config document */
+extern struct sa_proto_plugin *sap_proto_list;
+
+/* current SMF/SVC repository handle */
+static scfutilhandle_t *scf_handle = NULL;
+extern void getlegacyconfig(char *, xmlNodePtr *);
+extern int gettransients(xmlNodePtr *);
+extern int sa_valid_property(void *, char *, sa_property_t);
+extern char *sa_fstype(char *);
+extern int sa_is_share(void *);
+extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
+extern int sa_group_is_zfs(sa_group_t);
+extern int sa_path_is_zfs(char *);
+extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
+extern void update_legacy_config(void);
+extern int issubdir(char *, char *);
+
+static int sa_initialized = 0;
+
+/* helper functions */
+
+char *
+sa_errorstr(int err)
+{
+ static char errstr[32];
+ char *ret = NULL;
+
+ switch (err) {
+ case SA_OK:
+ ret = gettext("ok");
+ break;
+ case SA_NO_SUCH_PATH:
+ ret = gettext("path doesn't exist");
+ break;
+ case SA_NO_MEMORY:
+ ret = gettext("no memory");
+ break;
+ case SA_DUPLICATE_NAME:
+ ret = gettext("name in use");
+ break;
+ case SA_BAD_PATH:
+ ret = gettext("bad path");
+ break;
+ case SA_NO_SUCH_GROUP:
+ ret = gettext("no such group");
+ break;
+ case SA_CONFIG_ERR:
+ ret = gettext("configuration error");
+ break;
+ case SA_SYSTEM_ERR:
+ ret = gettext("system error");
+ break;
+ case SA_SYNTAX_ERR:
+ ret = gettext("syntax error");
+ break;
+ case SA_NO_PERMISSION:
+ ret = gettext("no permission");
+ break;
+ case SA_BUSY:
+ ret = gettext("busy");
+ break;
+ case SA_NO_SUCH_PROP:
+ ret = gettext("no such property");
+ break;
+ case SA_INVALID_NAME:
+ ret = gettext("invalid name");
+ break;
+ case SA_INVALID_PROTOCOL:
+ ret = gettext("invalid protocol");
+ break;
+ case SA_NOT_ALLOWED:
+ ret = gettext("operation not allowed");
+ break;
+ case SA_BAD_VALUE:
+ ret = gettext("bad property value");
+ break;
+ case SA_INVALID_SECURITY:
+ ret = gettext("invalid security type");
+ break;
+ case SA_NO_SUCH_SECURITY:
+ ret = gettext("security type not found");
+ break;
+ case SA_VALUE_CONFLICT:
+ ret = gettext("property value conflict");
+ break;
+ case SA_NOT_IMPLEMENTED:
+ ret = gettext("not implemented");
+ break;
+ case SA_INVALID_PATH:
+ ret = gettext("invalid path");
+ break;
+ case SA_NOT_SUPPORTED:
+ ret = gettext("operation not supported");
+ break;
+ case SA_PROP_SHARE_ONLY:
+ ret = gettext("property not valid for group");
+ break;
+ case SA_NOT_SHARED:
+ ret = gettext("not shared");
+ break;
+ default:
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("unknown %d"), err);
+ ret = errstr;
+ }
+ return (ret);
+}
+
+/*
+ * get_legacy_timestamp(root, path)
+ * gets the timestamp of the last time sharemgr updated the legacy
+ * files. This is used to determine if someone has modified them by
+ * hand.
+ */
+static uint64_t
+get_legacy_timestamp(xmlNodePtr root, char *path)
+{
+ uint64_t tval = 0;
+ xmlNodePtr node;
+ xmlChar *lpath = NULL;
+ xmlChar *timestamp = NULL;
+
+ for (node = root->xmlChildrenNode; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
+ /* a possible legacy node for this path */
+ lpath = xmlGetProp(node, (xmlChar *)"path");
+ if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) {
+ /* now have the node, extract the data */
+ timestamp = xmlGetProp(node, (xmlChar *)"timestamp");
+ if (timestamp != NULL) {
+ tval = strtoull((char *)timestamp, NULL, 0);
+ break;
+ }
+ }
+ if (lpath != NULL) {
+ xmlFree(lpath);
+ lpath = NULL;
+ }
+ }
+ }
+ if (lpath != NULL)
+ xmlFree(lpath);
+ if (timestamp != NULL)
+ xmlFree(timestamp);
+ return (tval);
+}
+
+/*
+ * set_legacy_timestamp(root, path, timevalue)
+ *
+ * add the current timestamp value to the configuration for use in
+ * determining when to update the legacy files. For SMF, this
+ * property is kept in default/operation/legacy_timestamp
+ */
+
+static void
+set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval)
+{
+ xmlNodePtr node;
+ xmlChar *lpath = NULL;
+
+ for (node = root->xmlChildrenNode; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
+ /* a possible legacy node for this path */
+ lpath = xmlGetProp(node, (xmlChar *)"path");
+ if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) {
+ xmlFree(lpath);
+ break;
+ }
+ if (lpath != NULL)
+ xmlFree(lpath);
+ }
+ }
+ if (node == NULL) {
+ /* need to create the first legacy timestamp node */
+ node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL);
+ }
+ if (node != NULL) {
+ char tstring[32];
+ int ret;
+
+ (void) snprintf(tstring, sizeof (tstring), "%lld", tval);
+ xmlSetProp(node, (xmlChar *)"timestamp", (xmlChar *)tstring);
+ xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path);
+ /* now commit to SMF */
+ ret = sa_get_instance(scf_handle, "default");
+ if (ret == SA_OK) {
+ ret = sa_start_transaction(scf_handle, "operation");
+ if (ret == SA_OK) {
+ ret = sa_set_property(scf_handle, "legacy-timestamp",
+ tstring);
+ if (ret == SA_OK) {
+ (void) sa_end_transaction(scf_handle);
+ } else {
+ sa_abort_transaction(scf_handle);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * is_shared(share)
+ *
+ * determine if the specified share is currently shared or not.
+ */
+static int
+is_shared(sa_share_t share)
+{
+ char *shared;
+ int result = 0; /* assume not */
+
+ shared = sa_get_share_attr(share, "shared");
+ if (shared != NULL) {
+ if (strcmp(shared, "true") == 0)
+ result = 1;
+ sa_free_attr_string(shared);
+ }
+ return (result);
+}
+
+/*
+ * checksubdir determines if the specified path is a subdirectory of
+ * another share. It calls issubdir() from the old share
+ * implementation to do the complicated work.
+ */
+static int
+checksubdir(char *newpath)
+{
+ sa_group_t group;
+ sa_share_t share;
+ int issub;
+ char *path = NULL;
+
+ for (issub = 0, group = sa_get_group(NULL);
+ group != NULL && !issub;
+ group = sa_get_next_group(group)) {
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ /*
+ * The original behavior of share never checked
+ * against the permanent configuration
+ * (/etc/dfs/dfstab). PIT has a number of cases where
+ * it depends on this older behavior even though it
+ * could be considered incorrect. We may tighten this
+ * up in the future.
+ */
+ if (!is_shared(share))
+ continue;
+
+ path = sa_get_share_attr(share, "path");
+ if (newpath != NULL &&
+ (strcmp(path, newpath) == 0 || issubdir(newpath, path) ||
+ issubdir(path, newpath))) {
+ sa_free_attr_string(path);
+ path = NULL;
+ issub = SA_INVALID_PATH;
+ break;
+ }
+ sa_free_attr_string(path);
+ path = NULL;
+ }
+ }
+ if (path != NULL)
+ sa_free_attr_string(path);
+ return (issub);
+}
+
+/*
+ * validpath(path)
+ * determine if the provided path is valid for a share. It shouldn't
+ * be a sub-dir of an already shared path or the parent directory of a
+ * share path.
+ */
+static int
+validpath(char *path)
+{
+ int error = SA_OK;
+ struct stat st;
+ sa_share_t share;
+ char *fstype;
+
+ if (*path != '/') {
+ return (SA_BAD_PATH);
+ }
+ if (stat(path, &st) < 0) {
+ error = SA_NO_SUCH_PATH;
+ } else {
+ share = sa_find_share(path);
+ if (share != NULL) {
+ error = SA_DUPLICATE_NAME;
+ }
+ if (error == SA_OK) {
+ /*
+ * check for special case with file system that might
+ * have restrictions. For now, ZFS is the only case
+ * since it has its own idea of how to configure
+ * shares. We do this before subdir checking since
+ * things like ZFS will do that for us. This should
+ * also be done via plugin interface.
+ */
+ fstype = sa_fstype(path);
+ if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
+ if (sa_zfs_is_shared(path))
+ error = SA_DUPLICATE_NAME;
+ }
+ if (fstype != NULL)
+ sa_free_fstype(fstype);
+ }
+ if (error == SA_OK) {
+ error = checksubdir(path);
+ }
+ }
+ return (error);
+}
+
+/*
+ * check to see if group/share is persistent.
+ */
+static int
+is_persistent(sa_group_t group)
+{
+ char *type;
+ int persist = 1;
+
+ type = sa_get_group_attr(group, "type");
+ if (type != NULL && strcmp(type, "transient") == 0)
+ persist = 0;
+ if (type != NULL)
+ sa_free_attr_string(type);
+ return (persist);
+}
+
+/*
+ * sa_valid_group_name(name)
+ *
+ * check that the "name" contains only valid characters and otherwise
+ * fits the required naming conventions. Valid names must start with
+ * an alphabetic and the remainder may consist of only alphanumeric
+ * plus the '-' and '_' characters. This name limitation comes from
+ * inherent limitations in SMF.
+ */
+
+int
+sa_valid_group_name(char *name)
+{
+ int ret = 1;
+ ssize_t len;
+
+ if (name != NULL && isalpha(*name)) {
+ char c;
+ len = strlen(name);
+ if (len < (scf_max_name_len - sizeof ("group:"))) {
+ for (c = *name++; c != '\0' && ret != 0; c = *name++) {
+ if (!isalnum(c) && c != '-' && c != '_')
+ ret = 0;
+ }
+ } else {
+ ret = 0;
+ }
+ } else {
+ ret = 0;
+ }
+ return (ret);
+}
+
+
+/*
+ * is_zfs_group(group)
+ * Determine if the specified group is a ZFS sharenfs group
+ */
+static int
+is_zfs_group(sa_group_t group)
+{
+ int ret = 0;
+ xmlNodePtr parent;
+ xmlChar *zfs;
+
+ if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0) {
+ parent = (xmlNodePtr)sa_get_parent_group(group);
+ } else {
+ parent = (xmlNodePtr)group;
+ }
+ zfs = xmlGetProp(parent, (xmlChar *)"zfs");
+ if (zfs != NULL) {
+ xmlFree(zfs);
+ ret = 1;
+ }
+ return (ret);
+}
+
+/*
+ * sa_optionset_name(optionset, oname, len, id)
+ * return the SMF name for the optionset. If id is not NULL, it
+ * will have the GUID value for a share and should be used
+ * instead of the keyword "optionset" which is used for
+ * groups. If the optionset doesn't have a protocol type
+ * associated with it, "default" is used. This shouldn't happen
+ * at this point but may be desirable in the future if there are
+ * protocol independent properties added. The name is returned in
+ * oname.
+ */
+
+static int
+sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id)
+{
+ char *proto;
+
+ if (id == NULL)
+ id = "optionset";
+
+ proto = sa_get_optionset_attr(optionset, "type");
+ len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default");
+
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ return (len);
+}
+
+/*
+ * sa_security_name(optionset, oname, len, id)
+ *
+ * return the SMF name for the security. If id is not NULL, it will
+ * have the GUID value for a share and should be used instead of the
+ * keyword "optionset" which is used for groups. If the optionset
+ * doesn't have a protocol type associated with it, "default" is
+ * used. This shouldn't happen at this point but may be desirable in
+ * the future if there are protocol independent properties added. The
+ * name is returned in oname. The security type is also encoded into
+ * the name. In the future, this wil *be handled a bit differently.
+ */
+
+static int
+sa_security_name(sa_security_t security, char *oname, size_t len, char *id)
+{
+ char *proto;
+ char *sectype;
+
+ if (id == NULL)
+ id = "optionset";
+
+ proto = sa_get_security_attr(security, "type");
+ sectype = sa_get_security_attr(security, "sectype");
+ len = snprintf(oname, len, "%s_%s_%s", id,
+ proto ? proto : "default",
+ sectype ? sectype : "default");
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ if (sectype != NULL)
+ sa_free_attr_string(sectype);
+ return (len);
+}
+
+/*
+ * sa_init()
+ * Initialize the API
+ * find all the shared objects
+ * init the tables with all objects
+ * read in the current configuration
+ */
+
+void
+sa_init(int init_service)
+{
+ struct stat st;
+ int legacy = 0;
+ uint64_t tval = 0;
+
+ if (!sa_initialized) {
+ /* get protocol specific structures */
+ (void) proto_plugin_init();
+ if (init_service & SA_INIT_SHARE_API) {
+ /*
+ * since we want to use SMF, initialize an svc handle
+ * and find out what is there.
+ */
+ scf_handle = sa_scf_init();
+ if (scf_handle != NULL) {
+ (void) sa_get_config(scf_handle, &sa_config_tree,
+ &sa_config_doc);
+ tval = get_legacy_timestamp(sa_config_tree,
+ SA_LEGACY_DFSTAB);
+ if (tval == 0) {
+ /* first time so make sure default is setup */
+ sa_group_t defgrp;
+ sa_optionset_t opt;
+ defgrp = sa_get_group("default");
+ if (defgrp != NULL) {
+ opt = sa_get_optionset(defgrp, NULL);
+ if (opt == NULL)
+ /* NFS is the default for default */
+ opt = sa_create_optionset(defgrp, "nfs");
+ }
+ }
+ if (stat(SA_LEGACY_DFSTAB, &st) >= 0 &&
+ tval != TSTAMP(st.st_ctim)) {
+ getlegacyconfig(SA_LEGACY_DFSTAB, &sa_config_tree);
+ if (stat(SA_LEGACY_DFSTAB, &st) >= 0)
+ set_legacy_timestamp(sa_config_tree,
+ SA_LEGACY_DFSTAB,
+ TSTAMP(st.st_ctim));
+ }
+ legacy |= sa_get_zfs_shares("zfs");
+ legacy |= gettransients(&sa_config_tree);
+ }
+ }
+ }
+}
+
+/*
+ * sa_fini()
+ * Uninitialize the API structures including the configuration
+ * data structures
+ */
+
+void
+sa_fini()
+{
+ if (sa_initialized) {
+ /* free the config trees */
+ sa_initialized = 0;
+ if (sa_config_doc != NULL)
+ xmlFreeDoc(sa_config_doc);
+ sa_config_tree = NULL;
+ sa_config_doc = NULL;
+ sa_scf_fini(scf_handle);
+ (void) proto_plugin_init();
+ }
+}
+
+/*
+ * sa_get_protocols(char **protocol)
+ * Get array of protocols that are supported
+ * Returns pointer to an allocated and NULL terminated
+ * array of strings. Caller must free.
+ * This really should be determined dynamically.
+ * If there aren't any defined, return -1.
+ * Use free() to return memory.
+ */
+
+int
+sa_get_protocols(char ***protocols)
+{
+ int numproto = -1;
+
+ if (protocols != NULL) {
+ struct sa_proto_plugin *plug;
+ for (numproto = 0, plug = sap_proto_list; plug != NULL;
+ plug = plug->plugin_next) {
+ numproto++;
+ }
+
+ *protocols = calloc(numproto + 1, sizeof (char *));
+ if (*protocols != NULL) {
+ int ret = 0;
+ for (plug = sap_proto_list; plug != NULL;
+ plug = plug->plugin_next) {
+ /* faking for now */
+ (*protocols)[ret++] = plug->plugin_ops->sa_protocol;
+ }
+ } else {
+ numproto = -1;
+ }
+ }
+ return (numproto);
+}
+
+/*
+ * find_group_by_name(node, group)
+ *
+ * search the XML document subtree specified by node to find the group
+ * specified by group. Searching subtree allows subgroups to be
+ * searched for.
+ */
+
+static xmlNodePtr
+find_group_by_name(xmlNodePtr node, xmlChar *group)
+{
+ xmlChar *name = NULL;
+
+ for (node = node->xmlChildrenNode; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
+ /* if no groupname, return the first found */
+ if (group == NULL)
+ break;
+ name = xmlGetProp(node, (xmlChar *)"name");
+ if (name != NULL &&
+ xmlStrcmp(name, group) == 0) {
+ break;
+ }
+ if (name != NULL) {
+ xmlFree(name);
+ name = NULL;
+ }
+ }
+ }
+ if (name != NULL)
+ xmlFree(name);
+ return (node);
+}
+
+/*
+ * sa_get_group(groupname)
+ * Return the "group" specified. If groupname is NULL,
+ * return the first group of the list of groups.
+ */
+sa_group_t
+sa_get_group(char *groupname)
+{
+ xmlNodePtr node = NULL;
+ char *subgroup = NULL;
+ char *group = NULL;
+
+ if (sa_config_tree != NULL) {
+ if (groupname != NULL) {
+ group = strdup(groupname);
+ subgroup = strchr(group, '/');
+ if (subgroup != NULL)
+ *subgroup++ = '\0';
+ }
+ node = find_group_by_name(sa_config_tree, (xmlChar *)group);
+ /* if a subgroup, find it before returning */
+ if (subgroup != NULL && node != NULL) {
+ node = find_group_by_name(node, (xmlChar *)subgroup);
+ }
+ }
+ if (node != NULL && (char *)group != NULL)
+ (void) sa_get_instance(scf_handle, (char *)group);
+ if (group != NULL)
+ free(group);
+ return ((sa_group_t)(node));
+}
+
+/*
+ * sa_get_next_group(group)
+ * Return the "next" group after the specified group from
+ * the internal group list. NULL if there are no more.
+ */
+sa_group_t
+sa_get_next_group(sa_group_t group)
+{
+ xmlNodePtr ngroup = NULL;
+ if (group != NULL) {
+ for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
+ ngroup = ngroup->next) {
+ if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
+ break;
+ }
+ }
+ return ((sa_group_t)ngroup);
+}
+
+/*
+ * sa_get_share(group, sharepath)
+ * Return the share object for the share specified. The share
+ * must be in the specified group. Return NULL if not found.
+ */
+sa_share_t
+sa_get_share(sa_group_t group, char *sharepath)
+{
+ xmlNodePtr node = NULL;
+ xmlChar *path;
+
+ /*
+ * For future scalability, this should end up building a cache
+ * since it will get called regularly by the mountd and info
+ * services.
+ */
+ if (group != NULL) {
+ for (node = ((xmlNodePtr)group)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
+ if (sharepath == NULL) {
+ break;
+ } else {
+ /* is it the correct share? */
+ path = xmlGetProp(node, (xmlChar *)"path");
+ if (path != NULL &&
+ xmlStrcmp(path, (xmlChar *)sharepath) == 0) {
+ xmlFree(path);
+ break;
+ }
+ xmlFree(path);
+ }
+ }
+ }
+ }
+ return ((sa_share_t)node);
+}
+
+/*
+ * sa_get_next_share(share)
+ * Return the next share following the specified share
+ * from the internal list of shares. Returns NULL if there
+ * are no more shares. The list is relative to the same
+ * group.
+ */
+sa_share_t
+sa_get_next_share(sa_share_t share)
+{
+ xmlNodePtr node = NULL;
+
+ if (share != NULL) {
+ for (node = ((xmlNodePtr)share)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
+ break;
+ }
+ }
+ }
+ return ((sa_share_t)node);
+}
+
+/*
+ * _sa_get_child_node(node, type)
+ *
+ * find the child node of the specified node that has "type". This is
+ * used to implement several internal functions.
+ */
+
+static xmlNodePtr
+_sa_get_child_node(xmlNodePtr node, xmlChar *type)
+{
+ xmlNodePtr child;
+ for (child = node->xmlChildrenNode; child != NULL;
+ child = child->next)
+ if (xmlStrcmp(child->name, type) == 0)
+ return (child);
+ return ((xmlNodePtr)NULL);
+}
+
+/*
+ * find_share(group, path)
+ *
+ * Search all the shares in the specified group for one that has the
+ * specified path.
+ */
+
+static sa_share_t
+find_share(sa_group_t group, char *sharepath)
+{
+ sa_share_t share;
+ char *path;
+
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ path = sa_get_share_attr(share, "path");
+ if (path != NULL && strcmp(path, sharepath) == 0) {
+ sa_free_attr_string(path);
+ break;
+ }
+ if (path != NULL)
+ sa_free_attr_string(path);
+ }
+ return (share);
+}
+
+/*
+ * sa_get_sub_group(group)
+ *
+ * Get the first sub-group of group. The sa_get_next_group() function
+ * can be used to get the rest. This is currently only used for ZFS
+ * sub-groups but could be used to implement a more general mechanism.
+ */
+
+sa_group_t
+sa_get_sub_group(sa_group_t group)
+{
+ return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
+ (xmlChar *)"group"));
+}
+
+/*
+ * sa_find_share(sharepath)
+ * Finds a share regardless of group. In the future, this
+ * function should utilize a cache and hash table of some kind.
+ * The current assumption is that a path will only be shared
+ * once. In the future, this may change as implementation of
+ * resource names comes into being.
+ */
+sa_share_t
+sa_find_share(char *sharepath)
+{
+ sa_group_t group;
+ sa_group_t zgroup;
+ sa_share_t share = NULL;
+ int done = 0;
+
+ for (group = sa_get_group(NULL); group != NULL && !done;
+ group = sa_get_next_group(group)) {
+ if (is_zfs_group(group)) {
+ for (zgroup = (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
+ (xmlChar *)"group");
+ zgroup != NULL; zgroup = sa_get_next_group(zgroup)) {
+ share = find_share(zgroup, sharepath);
+ if (share != NULL)
+ break;
+ }
+ } else {
+ share = find_share(group, sharepath);
+ }
+ if (share != NULL)
+ break;
+ }
+ return (share);
+}
+
+/*
+ * sa_check_path(group, path)
+ *
+ * check that path is a valid path relative to the group. Currently,
+ * we are ignoring the group and checking only the NFS rules. Later,
+ * we may want to use the group to then check against the protocols
+ * enabled on the group.
+ */
+
+int
+sa_check_path(sa_group_t group, char *path)
+{
+#ifdef lint
+ group = group;
+#endif
+ return (validpath(path));
+}
+
+/*
+ * _sa_add_share(group, sharepath, persist, *error)
+ *
+ * common code for all types of add_share. sa_add_share() is the
+ * public API, we also need to be able to do this when parsing legacy
+ * files and construction of the internal configuration while
+ * extracting config info from SMF.
+ */
+
+sa_share_t
+_sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
+{
+ xmlNodePtr node = NULL;
+ int err;
+
+ err = SA_OK; /* assume success */
+
+ node = xmlNewChild((xmlNodePtr)group, NULL,
+ (xmlChar *)"share", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
+ xmlSetProp(node, (xmlChar *)"type", persist ?
+ (xmlChar *)"persist" : (xmlChar *)"transient");
+ if (persist != SA_SHARE_TRANSIENT) {
+ /*
+ * persistent shares come in two flavors: SMF and
+ * ZFS. Sort this one out based on target group and
+ * path type. Currently, only NFS is supported in the
+ * ZFS group and it is always on.
+ */
+ if (sa_group_is_zfs(group) && sa_path_is_zfs(sharepath)) {
+ err = sa_zfs_set_sharenfs(group, sharepath, 1);
+ } else {
+ err = sa_commit_share(scf_handle, group,
+ (sa_share_t)node);
+ }
+ }
+ if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) {
+ /* called by the dfstab parser so could be a show */
+ err = SA_OK;
+ }
+ if (err != SA_OK) {
+ /*
+ * we couldn't commit to the repository so undo
+ * our internal state to reflect reality.
+ */
+ xmlUnlinkNode(node);
+ xmlFreeNode(node);
+ node = NULL;
+ }
+ } else {
+ err = SA_NO_MEMORY;
+ }
+ if (error != NULL)
+ *error = err;
+ return (node);
+}
+
+/*
+ * sa_add_share(group, sharepath, persist, *error)
+ *
+ * Add a new share object to the specified group. The share will
+ * have the specified sharepath and will only be constructed if
+ * it is a valid path to be shared. NULL is returned on error
+ * and a detailed error value will be returned via the error
+ * pointer.
+ */
+sa_share_t
+sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
+{
+ xmlNodePtr node = NULL;
+ sa_share_t dup;
+
+ if ((dup = sa_find_share(sharepath)) == NULL &&
+ (*error = sa_check_path(group, sharepath)) == SA_OK) {
+ node = _sa_add_share(group, sharepath, persist, error);
+ }
+ if (dup != NULL)
+ *error = SA_DUPLICATE_NAME;
+
+ return ((sa_share_t)node);
+}
+
+/*
+ * sa_enable_share(share, protocol)
+ * Enable the specified share to the specified protocol.
+ * If protocol is NULL, then all protocols.
+ */
+int
+sa_enable_share(sa_share_t share, char *protocol)
+{
+ char *sharepath;
+ struct stat st;
+ int err = 0;
+
+ sharepath = sa_get_share_attr(share, "path");
+ if (stat(sharepath, &st) < 0) {
+ err = SA_NO_SUCH_PATH;
+ } else {
+ /* tell the server about the share */
+ if (protocol != NULL) {
+ /* lookup protocol specific handler */
+ err = sa_proto_share(protocol, share);
+ if (err == SA_OK)
+ (void) sa_set_share_attr(share, "shared", "true");
+ } else {
+ /* tell all protocols */
+ err = sa_proto_share("nfs", share); /* only NFS for now */
+ (void) sa_set_share_attr(share, "shared", "true");
+ }
+ }
+ if (sharepath != NULL)
+ sa_free_attr_string(sharepath);
+ return (err);
+}
+
+/*
+ * sa_disable_share(share, protocol)
+ * Disable the specified share to the specified protocol.
+ * If protocol is NULL, then all protocols.
+ */
+int
+sa_disable_share(sa_share_t share, char *protocol)
+{
+ char *path;
+ char *shared;
+ int ret = SA_OK;
+
+ path = sa_get_share_attr(share, "path");
+ shared = sa_get_share_attr(share, "shared");
+
+ if (protocol != NULL) {
+ ret = sa_proto_unshare(protocol, path);
+ } else {
+ /* need to do all protocols */
+ ret = sa_proto_unshare("nfs", path);
+ }
+ if (ret == SA_OK)
+ (void) sa_set_share_attr(share, "shared", NULL);
+ if (path != NULL)
+ sa_free_attr_string(path);
+ if (shared != NULL)
+ sa_free_attr_string(shared);
+ return (ret);
+}
+
+/*
+ * sa_remove_share(share)
+ *
+ * remove the specified share from its containing group.
+ * Remove from the SMF or ZFS configuration space.
+ */
+
+int
+sa_remove_share(sa_share_t share)
+{
+ sa_group_t group;
+ int ret = SA_OK;
+ char *type;
+ int transient = 0;
+ char *groupname;
+ char *zfs;
+
+ type = sa_get_share_attr(share, "type");
+ group = sa_get_parent_group(share);
+ zfs = sa_get_group_attr(group, "zfs");
+ groupname = sa_get_group_attr(group, "name");
+ if (type != NULL && strcmp(type, "persist") != 0)
+ transient = 1;
+ if (type != NULL)
+ sa_free_attr_string(type);
+
+ /* remove the node from its group then free the memory */
+
+ /*
+ * need to test if "busy"
+ */
+ /* only do SMF action if permanent */
+ if (!transient || zfs != NULL) {
+ /* remove from legacy dfstab as well as possible SMF */
+ ret = sa_delete_legacy(share);
+ if (ret == SA_OK) {
+ if (!sa_group_is_zfs(group)) {
+ ret = sa_delete_share(scf_handle, group, share);
+ } else {
+ char *sharepath = sa_get_share_attr(share, "path");
+ if (sharepath != NULL) {
+ ret = sa_zfs_set_sharenfs(group, sharepath, 0);
+ sa_free_attr_string(sharepath);
+ }
+ }
+ }
+ }
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+ if (zfs != NULL)
+ sa_free_attr_string(zfs);
+
+ xmlUnlinkNode((xmlNodePtr)share);
+ xmlFreeNode((xmlNodePtr)share);
+ return (ret);
+}
+
+/*
+ * sa_move_share(group, share)
+ *
+ * move the specified share to the specified group. Update SMF
+ * appropriately.
+ */
+
+int
+sa_move_share(sa_group_t group, sa_share_t share)
+{
+ sa_group_t oldgroup;
+ int ret = SA_OK;
+
+ /* remove the node from its group then free the memory */
+
+ oldgroup = sa_get_parent_group(share);
+ if (oldgroup != group) {
+ xmlUnlinkNode((xmlNodePtr)share);
+ /* now that the share isn't in its old group, add to the new one */
+ xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
+ /* need to deal with SMF */
+ if (ret == SA_OK) {
+ /*
+ * need to remove from old group first and then add to
+ * new group. Ideally, we would do the other order but
+ * need to avoid having the share in two groups at the
+ * same time.
+ */
+ ret = sa_delete_share(scf_handle, oldgroup, share);
+ }
+ ret = sa_commit_share(scf_handle, group, share);
+ }
+ return (ret);
+}
+
+/*
+ * sa_get_parent_group(share)
+ *
+ * Return the containg group for the share. If a group was actually
+ * passed in, we don't want a parent so return NULL.
+ */
+
+sa_group_t
+sa_get_parent_group(sa_share_t share)
+{
+ xmlNodePtr node = NULL;
+ if (share != NULL) {
+ node = ((xmlNodePtr)share)->parent;
+ /*
+ * make sure parent is a group and not sharecfg since
+ * we may be cheating and passing in a group.
+ * Eventually, groups of groups might come into being.
+ */
+ if (node == NULL ||
+ xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
+ node = NULL;
+ }
+ return ((sa_group_t)node);
+}
+
+/*
+ * _sa_create_group(groupname)
+ *
+ * Create a group in the document. The caller will need to deal with
+ * configuration store and activation.
+ */
+
+sa_group_t
+_sa_create_group(char *groupname)
+{
+ xmlNodePtr node = NULL;
+
+ if (sa_valid_group_name(groupname)) {
+ node = xmlNewChild(sa_config_tree, NULL,
+ (xmlChar *)"group", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
+ xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
+ }
+ }
+ return ((sa_group_t)node);
+}
+
+/*
+ * _sa_create_zfs_group(group, groupname)
+ *
+ * Create a ZFS subgroup under the specified group. This may
+ * eventually form the basis of general sub-groups, but is currently
+ * restricted to ZFS.
+ */
+sa_group_t
+_sa_create_zfs_group(sa_group_t group, char *groupname)
+{
+ xmlNodePtr node = NULL;
+
+ node = xmlNewChild((xmlNodePtr)group, NULL,
+ (xmlChar *)"group", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
+ xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
+ }
+
+ return ((sa_group_t)node);
+}
+
+/*
+ * sa_create_group(groupname, *error)
+ *
+ * Create a new group with groupname. Need to validate that it is a
+ * legal name for SMF and the construct the SMF service instance of
+ * svc:/network/shares/group to implement the group. All necessary
+ * operational properties must be added to the group at this point
+ * (via the SMF transaction model).
+ */
+sa_group_t
+sa_create_group(char *groupname, int *error)
+{
+ xmlNodePtr node = NULL;
+ sa_group_t group;
+ int ret;
+ char rbacstr[256];
+
+ ret = SA_OK;
+
+ if (scf_handle == NULL) {
+ ret = SA_SYSTEM_ERR;
+ goto err;
+ }
+
+ group = sa_get_group(groupname);
+ if (group != NULL) {
+ ret = SA_DUPLICATE_NAME;
+ } else {
+ if (sa_valid_group_name(groupname)) {
+ node = xmlNewChild(sa_config_tree, NULL,
+ (xmlChar *)"group", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
+ /* default to the group being enabled */
+ xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
+ ret = sa_create_instance(scf_handle, groupname);
+ if (ret == SA_OK) {
+ ret = sa_start_transaction(scf_handle, "operation");
+ }
+ if (ret == SA_OK) {
+ ret = sa_set_property(scf_handle, "state", "enabled");
+ if (ret == SA_OK) {
+ ret = sa_end_transaction(scf_handle);
+ } else {
+ sa_abort_transaction(scf_handle);
+ }
+ }
+ if (ret == SA_OK) {
+ /* initialize the RBAC strings */
+ ret = sa_start_transaction(scf_handle, "general");
+ if (ret == SA_OK) {
+ (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s",
+ SA_RBAC_MANAGE, groupname);
+ ret = sa_set_property(scf_handle,
+ "action_authorization",
+ rbacstr);
+ }
+ if (ret == SA_OK) {
+ (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s",
+ SA_RBAC_VALUE, groupname);
+ ret = sa_set_property(scf_handle,
+ "value_authorization",
+ rbacstr);
+ }
+ if (ret == SA_OK) {
+ ret = sa_end_transaction(scf_handle);
+ } else {
+ sa_abort_transaction(scf_handle);
+ }
+ }
+ if (ret != SA_OK) {
+ /*
+ * Couldn't commit the group so we need to
+ * undo internally.
+ */
+ xmlUnlinkNode(node);
+ xmlFreeNode(node);
+ node = NULL;
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ } else {
+ ret = SA_INVALID_NAME;
+ }
+ }
+err:
+ if (error != NULL)
+ *error = ret;
+ return ((sa_group_t)node);
+}
+
+/*
+ * sa_remove_group(group)
+ *
+ * Remove the specified group. This deletes from the SMF repository.
+ * All property groups and properties are removed.
+ */
+
+int
+sa_remove_group(sa_group_t group)
+{
+ char *name;
+ int ret = SA_OK;
+
+ name = sa_get_group_attr(group, "name");
+ if (name != NULL) {
+ ret = sa_delete_instance(scf_handle, name);
+ sa_free_attr_string(name);
+ }
+ xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
+ xmlFreeNode((xmlNodePtr)group); /* now it is gone */
+ return (ret);
+}
+
+/*
+ * sa_update_config()
+ *
+ * Used to update legacy files that need to be updated in bulk
+ * Currently, this is a placeholder and will go away in a future
+ * release.
+ */
+
+int
+sa_update_config()
+{
+ struct stat st;
+
+ /*
+ * do legacy files first so we can tell when they change.
+ * This will go away when we start updating individual records
+ * rather than the whole file.
+ */
+ update_legacy_config();
+ /* update legacy timestamp */
+ if (stat(SA_LEGACY_DFSTAB, &st) >= 0) {
+ set_legacy_timestamp(sa_config_tree, SA_LEGACY_DFSTAB,
+ TSTAMP(st.st_ctim));
+ }
+ return (SA_OK);
+}
+
+/*
+ * get_node_attr(node, tag)
+ *
+ * Get the speficied tag(attribute) if it exists on the node. This is
+ * used internally by a number of attribute oriented functions.
+ */
+
+static char *
+get_node_attr(void *nodehdl, char *tag)
+{
+ xmlNodePtr node = (xmlNodePtr)nodehdl;
+ xmlChar *name = NULL;
+
+ if (node != NULL) {
+ name = xmlGetProp(node, (xmlChar *)tag);
+ }
+ return ((char *)name);
+}
+
+/*
+ * get_node_attr(node, tag)
+ *
+ * Set the speficied tag(attribute) to the specified value This is
+ * used internally by a number of attribute oriented functions. It
+ * doesn't update the repository, only the internal document state.
+ */
+
+void
+set_node_attr(void *nodehdl, char *tag, char *value)
+{
+ xmlNodePtr node = (xmlNodePtr)nodehdl;
+ if (node != NULL && tag != NULL) {
+ if (value != NULL) {
+ xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value);
+ } else {
+ xmlUnsetProp(node, (xmlChar *)tag);
+ }
+ }
+}
+
+/*
+ * sa_get_group_attr(group, tag)
+ *
+ * Get the specied attribute, if defined, for the group.
+ */
+
+char *
+sa_get_group_attr(sa_group_t group, char *tag)
+{
+ return (get_node_attr((void *)group, tag));
+}
+
+/*
+ * sa_set_group_attr(group, tag, value)
+ *
+ * set the specified tag/attribute on the group using value as its
+ * value.
+ *
+ * This will result in setting the property in the SMF repository as
+ * well as in the internal document.
+ */
+
+int
+sa_set_group_attr(sa_group_t group, char *tag, char *value)
+{
+ int ret;
+ char *groupname;
+
+ groupname = sa_get_group_attr(group, "name");
+ ret = sa_get_instance(scf_handle, groupname);
+ if (ret == SA_OK) {
+ set_node_attr((void *)group, tag, value);
+ ret = sa_start_transaction(scf_handle, "operation");
+ if (ret == SA_OK) {
+ ret = sa_set_property(scf_handle, tag, value);
+ if (ret == SA_OK)
+ (void) sa_end_transaction(scf_handle);
+ else {
+ sa_abort_transaction(scf_handle);
+ }
+ }
+ }
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+ return (ret);
+}
+
+/*
+ * sa_get_share_attr(share, tag)
+ *
+ * Return the value of the tag/attribute set on the specified
+ * share. Returns NULL if the tag doesn't exist.
+ */
+
+char *
+sa_get_share_attr(sa_share_t share, char *tag)
+{
+ return (get_node_attr((void *)share, tag));
+}
+
+/*
+ * sa_get_resource(group, resource)
+ *
+ * Search all the shares in the speified group for a share with a
+ * resource name matching the one specified.
+ *
+ * In the future, it may be advantageous to allow group to be NULL and
+ * search all groups but that isn't needed at present.
+ */
+
+sa_share_t
+sa_get_resource(sa_group_t group, char *resource)
+{
+ sa_share_t share = NULL;
+ char *name = NULL;
+
+ if (resource != NULL) {
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ name = sa_get_share_attr(share, "resource");
+ if (name != NULL) {
+ if (strcmp(name, resource) == 0)
+ break;
+ sa_free_attr_string(name);
+ name = NULL;
+ }
+ }
+ if (name != NULL)
+ sa_free_attr_string(name);
+ }
+ return ((sa_share_t)share);
+}
+
+/*
+ * _sa_set_share_description(share, description)
+ *
+ * Add a description tag with text contents to the specified share.
+ * A separate XML tag is used rather than a property.
+ */
+
+xmlNodePtr
+_sa_set_share_description(sa_share_t share, char *content)
+{
+ xmlNodePtr node;
+ node = xmlNewChild((xmlNodePtr)share,
+ NULL, (xmlChar *)"description", NULL);
+ xmlNodeSetContent(node, (xmlChar *)content);
+ return (node);
+}
+
+/*
+ * sa_set_share_attr(share, tag, value)
+ *
+ * Set the share attribute specified by tag to the specified value. In
+ * the case of "resource", enforce a no duplicates in a group rule. If
+ * the share is not transient, commit the changes to the repository
+ * else just update the share internally.
+ */
+
+int
+sa_set_share_attr(sa_share_t share, char *tag, char *value)
+{
+ sa_group_t group;
+ sa_share_t resource;
+ int ret = SA_OK;
+
+ group = sa_get_parent_group(share);
+
+ /*
+ * There are some attributes that may have specific
+ * restrictions on them. Initially, only "resource" has
+ * special meaning that needs to be checked. Only one instance
+ * of a resource name may exist within a group.
+ */
+
+ if (strcmp(tag, "resource") == 0) {
+ resource = sa_get_resource(group, value);
+ if (resource != share && resource != NULL)
+ ret = SA_DUPLICATE_NAME;
+ }
+ if (ret == SA_OK) {
+ set_node_attr((void *)share, tag, value);
+ if (group != NULL) {
+ char *type;
+ /* we can probably optimize this some */
+ type = sa_get_share_attr(share, "type");
+ if (type == NULL || strcmp(type, "transient") != 0)
+ ret = sa_commit_share(scf_handle, group, share);
+ if (type != NULL)
+ sa_free_attr_string(type);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_get_property_attr(prop, tag)
+ *
+ * Get the value of the specified property attribute. Standard
+ * attributes are "type" and "value".
+ */
+
+char *
+sa_get_property_attr(sa_property_t prop, char *tag)
+{
+ return (get_node_attr((void *)prop, tag));
+}
+
+/*
+ * sa_get_optionset_attr(prop, tag)
+ *
+ * Get the value of the specified property attribute. Standard
+ * attribute is "type".
+ */
+
+char *
+sa_get_optionset_attr(sa_property_t optionset, char *tag)
+{
+ return (get_node_attr((void *)optionset, tag));
+
+}
+
+/*
+ * sa_set_optionset_attr(optionset, tag, value)
+ *
+ * Set the specified attribute(tag) to the specified value on the
+ * optionset.
+ */
+
+void
+sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
+{
+ set_node_attr((void *)optionset, tag, value);
+}
+
+/*
+ * sa_free_attr_string(string)
+ *
+ * Free the string that was returned in one of the sa_get_*_attr()
+ * functions.
+ */
+
+void
+sa_free_attr_string(char *string)
+{
+ xmlFree((xmlChar *)string);
+}
+
+/*
+ * sa_get_optionset(group, proto)
+ *
+ * Return the optionset, if it exists, that is associated with the
+ * specified protocol.
+ */
+
+sa_optionset_t
+sa_get_optionset(void *group, char *proto)
+{
+ xmlNodePtr node;
+ xmlChar *value = NULL;
+
+ for (node = ((xmlNodePtr)group)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
+ value = xmlGetProp(node, (xmlChar *)"type");
+ if (proto != NULL) {
+ if (value != NULL &&
+ xmlStrcmp(value, (xmlChar *)proto) == 0) {
+ break;
+ }
+ if (value != NULL) {
+ xmlFree(value);
+ value = NULL;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ if (value != NULL)
+ xmlFree(value);
+ return ((sa_optionset_t)node);
+}
+
+/*
+ * sa_get_next_optionset(optionset)
+ *
+ * Return the next optionset in the group. NULL if this was the last.
+ */
+
+sa_optionset_t
+sa_get_next_optionset(sa_optionset_t optionset)
+{
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)optionset)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
+ break;
+ }
+ }
+ return ((sa_optionset_t)node);
+}
+
+/*
+ * sa_get_security(group, sectype, proto)
+ *
+ * Return the security optionset. The internal name is a hold over
+ * from the implementation and will be changed before the API is
+ * finalized. This is really a named optionset that can be negotiated
+ * as a group of properties (like NFS security options).
+ */
+
+sa_security_t
+sa_get_security(sa_group_t group, char *sectype, char *proto)
+{
+ xmlNodePtr node;
+ xmlChar *value = NULL;
+
+ for (node = ((xmlNodePtr)group)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
+ if (proto != NULL) {
+ value = xmlGetProp(node, (xmlChar *)"type");
+ if (value == NULL ||
+ (value != NULL &&
+ xmlStrcmp(value, (xmlChar *)proto) != 0)) {
+ /* it doesn't match so continue */
+ xmlFree(value);
+ value = NULL;
+ continue;
+ }
+ }
+ if (value != NULL) {
+ xmlFree(value);
+ value = NULL;
+ }
+ /* potential match */
+ if (sectype != NULL) {
+ value = xmlGetProp(node, (xmlChar *)"sectype");
+ if (value != NULL &&
+ xmlStrcmp(value, (xmlChar *)sectype) == 0) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ if (value != NULL) {
+ xmlFree(value);
+ value = NULL;
+ }
+ }
+ if (value != NULL)
+ xmlFree(value);
+ return ((sa_security_t)node);
+}
+
+/*
+ * sa_get_next_security(security)
+ *
+ * Get the next security optionset if one exists.
+ */
+
+sa_security_t
+sa_get_next_security(sa_security_t security)
+{
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)security)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
+ break;
+ }
+ }
+ return ((sa_security_t)node);
+}
+
+/*
+ * sa_get_property(optionset, prop)
+ *
+ * Get the property object with the name specified in prop from the
+ * optionset.
+ */
+
+sa_property_t
+sa_get_property(sa_optionset_t optionset, char *prop)
+{
+ xmlNodePtr node = (xmlNodePtr)optionset;
+ xmlChar *value = NULL;
+
+ if (optionset == NULL)
+ return (NULL);
+
+ for (node = node->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
+ if (prop == NULL)
+ break;
+ value = xmlGetProp(node, (xmlChar *)"type");
+ if (value != NULL && xmlStrcmp(value, (xmlChar *)prop) == 0) {
+ break;
+ }
+ if (value != NULL) {
+ xmlFree(value);
+ value = NULL;
+ }
+ }
+ }
+ if (value != NULL)
+ xmlFree(value);
+ if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
+ /* avoid a non option node -- it is possible to be a text node */
+ node = NULL;
+ }
+ return ((sa_property_t)node);
+}
+
+/*
+ * sa_get_next_property(property)
+ *
+ * Get the next property following the specified property. NULL if
+ * this was the last.
+ */
+
+sa_property_t
+sa_get_next_property(sa_property_t property)
+{
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)property)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
+ break;
+ }
+ }
+ return ((sa_property_t)node);
+}
+
+/*
+ * sa_set_share_description(share, content)
+ *
+ * Set the description of share to content.
+ */
+
+int
+sa_set_share_description(sa_share_t share, char *content)
+{
+ xmlNodePtr node;
+ sa_group_t group;
+ int ret = SA_OK;
+
+ for (node = ((xmlNodePtr)share)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
+ break;
+ }
+ }
+ group = sa_get_parent_group(share);
+ /* no existing description but want to add */
+ if (node == NULL && content != NULL) {
+ /* add a description */
+ node = _sa_set_share_description(share, content);
+ } else if (node != NULL && content != NULL) {
+ /* update a description */
+ xmlNodeSetContent(node, (xmlChar *)content);
+ } else if (node != NULL && content == NULL) {
+ /* remove an existing description */
+ xmlUnlinkNode(node);
+ xmlFreeNode(node);
+ }
+ if (group != NULL && is_persistent((sa_group_t)share))
+ ret = sa_commit_share(scf_handle, group, share);
+ return (ret);
+}
+
+/*
+ * fixproblemchars(string)
+ *
+ * don't want any newline or tab characters in the text since these
+ * could break display of data and legacy file formats.
+ */
+static void
+fixproblemchars(char *str)
+{
+ int c;
+ for (c = *str; c != '\0'; c = *++str) {
+ if (c == '\t' || c == '\n')
+ *str = ' ';
+ else if (c == '"')
+ *str = '\'';
+ }
+}
+
+/*
+ * sa_get_share_description(share)
+ *
+ * Return the description text for the specified share if it
+ * exists. NULL if no description exists.
+ */
+
+char *
+sa_get_share_description(sa_share_t share)
+{
+ xmlChar *description = NULL;
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)share)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
+ break;
+ }
+ }
+ if (node != NULL) {
+ description = xmlNodeGetContent((xmlNodePtr)share);
+ fixproblemchars((char *)description);
+ }
+ return ((char *)description);
+}
+
+/*
+ * sa_free(share_description(description)
+ *
+ * Free the description string.
+ */
+
+void
+sa_free_share_description(char *description)
+{
+ xmlFree((xmlChar *)description);
+}
+
+/*
+ * sa_create_optionset(group, proto)
+ *
+ * Create an optionset for the specified protocol in the specied
+ * group. This is manifested as a property group within SMF.
+ */
+
+sa_optionset_t
+sa_create_optionset(sa_group_t group, char *proto)
+{
+ sa_optionset_t optionset;
+ sa_group_t parent = group;
+
+ optionset = sa_get_optionset(group, proto);
+ if (optionset != NULL) {
+ /* can't have a duplicate protocol */
+ optionset = NULL;
+ } else {
+ optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
+ NULL,
+ (xmlChar *)"optionset",
+ NULL);
+ /*
+ * only put to repository if on a group and we were
+ * able to create an optionset.
+ */
+ if (optionset != NULL) {
+ char oname[256];
+ char *groupname;
+ char *id = NULL;
+
+ if (sa_is_share(group))
+ parent = sa_get_parent_group((sa_share_t)group);
+
+ sa_set_optionset_attr(optionset, "type", proto);
+
+ if (sa_is_share(group)) {
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ }
+ (void) sa_optionset_name(optionset, oname,
+ sizeof (oname), id);
+ groupname = sa_get_group_attr(parent, "name");
+ if (groupname != NULL && is_persistent(group)) {
+ (void) sa_get_instance(scf_handle, groupname);
+ sa_free_attr_string(groupname);
+ (void) sa_create_pgroup(scf_handle, oname);
+ }
+ if (id != NULL)
+ sa_free_attr_string(id);
+ }
+ }
+ return (optionset);
+}
+
+/*
+ * sa_get_property_parent(property)
+ *
+ * Given a property, return the object it is a property of. This will
+ * be an optionset of some type.
+ */
+
+static sa_optionset_t
+sa_get_property_parent(sa_property_t property)
+{
+ xmlNodePtr node = NULL;
+
+ if (property != NULL) {
+ node = ((xmlNodePtr)property)->parent;
+ }
+ return ((sa_optionset_t)node);
+}
+
+/*
+ * sa_get_optionset_parent(optionset)
+ *
+ * Return the parent of the specified optionset. This could be a group
+ * or a share.
+ */
+
+static sa_group_t
+sa_get_optionset_parent(sa_optionset_t optionset)
+{
+ xmlNodePtr node = NULL;
+
+ if (optionset != NULL) {
+ node = ((xmlNodePtr)optionset)->parent;
+ }
+ return ((sa_group_t)node);
+}
+
+/*
+ * zfs_needs_update(share)
+ *
+ * In order to avoid making multiple updates to a ZFS share when
+ * setting properties, the share attribute "changed" will be set to
+ * true when a property is added or modifed. When done adding
+ * properties, we can then detect that an update is needed. We then
+ * clear the state here to detect additional changes.
+ */
+
+static int
+zfs_needs_update(sa_share_t share)
+{
+ char *attr;
+ int result = 0;
+
+ attr = sa_get_share_attr(share, "changed");
+ if (attr != NULL) {
+ sa_free_attr_string(attr);
+ result = 1;
+ }
+ set_node_attr((void *)share, "changed", NULL);
+ return (result);
+}
+
+/*
+ * zfs_set_update(share)
+ *
+ * Set the changed attribute of the share to true.
+ */
+
+static void
+zfs_set_update(sa_share_t share)
+{
+ set_node_attr((void *)share, "changed", "true");
+}
+
+/*
+ * sa_commit_properties(optionset, clear)
+ *
+ * Check if SMF or ZFS config and either update or abort the pending
+ * changes.
+ */
+
+int
+sa_commit_properties(sa_optionset_t optionset, int clear)
+{
+ sa_group_t group;
+ sa_group_t parent;
+ int zfs = 0;
+ int needsupdate = 0;
+ int ret = SA_OK;
+
+ group = sa_get_optionset_parent(optionset);
+ if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
+ /* only update ZFS if on a share */
+ parent = sa_get_parent_group(group);
+ zfs++;
+ if (parent != NULL && is_zfs_group(parent)) {
+ needsupdate = zfs_needs_update(group);
+ } else {
+ zfs = 0;
+ }
+ }
+ if (zfs) {
+ if (!clear && needsupdate)
+ ret = sa_zfs_update((sa_share_t)group);
+ } else {
+ if (clear)
+ (void) sa_abort_transaction(scf_handle);
+ else
+ ret = sa_end_transaction(scf_handle);
+ }
+ return (ret);
+}
+
+/*
+ * sa_destroy_optionset(optionset)
+ *
+ * Remove the optionset from its group. Update the repostory to
+ * reflect this change.
+ */
+
+int
+sa_destroy_optionset(sa_optionset_t optionset)
+{
+ char name[256];
+ int len;
+ int ret;
+ char *id = NULL;
+ sa_group_t group;
+ int ispersist = 1;
+
+ /* now delete the prop group */
+ group = sa_get_optionset_parent(optionset);
+ if (group != NULL && sa_is_share(group)) {
+ ispersist = is_persistent(group);
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ }
+ if (ispersist) {
+ len = sa_optionset_name(optionset, name, sizeof (name), id);
+ if (len > 0) {
+ ret = sa_delete_pgroup(scf_handle, name);
+ }
+ }
+ xmlUnlinkNode((xmlNodePtr)optionset);
+ xmlFreeNode((xmlNodePtr)optionset);
+ if (id != NULL)
+ sa_free_attr_string(id);
+ return (ret);
+}
+
+/* private to the implementation */
+int
+_sa_remove_optionset(sa_optionset_t optionset)
+{
+ int ret = SA_OK;
+
+ xmlUnlinkNode((xmlNodePtr)optionset);
+ xmlFreeNode((xmlNodePtr)optionset);
+ return (ret);
+}
+
+/*
+ * sa_create_security(group, sectype, proto)
+ *
+ * Create a security optionset (one that has a type name and a
+ * proto). Security is left over from a pure NFS implementation. The
+ * naming will change in the future when the API is released.
+ */
+sa_security_t
+sa_create_security(sa_group_t group, char *sectype, char *proto)
+{
+ sa_security_t security;
+ char *id = NULL;
+ sa_group_t parent;
+ char *groupname = NULL;
+
+ if (group != NULL && sa_is_share(group)) {
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ parent = sa_get_parent_group(group);
+ if (parent != NULL)
+ groupname = sa_get_group_attr(parent, "name");
+ } else if (group != NULL) {
+ groupname = sa_get_group_attr(group, "name");
+ }
+
+ security = sa_get_security(group, sectype, proto);
+ if (security != NULL) {
+ /* can't have a duplicate security option */
+ security = NULL;
+ } else {
+ security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
+ NULL,
+ (xmlChar *)"security",
+ NULL);
+ if (security != NULL) {
+ char oname[256];
+ sa_set_security_attr(security, "type", proto);
+
+ sa_set_security_attr(security, "sectype", sectype);
+ (void) sa_security_name(security, oname,
+ sizeof (oname), id);
+ if (groupname != NULL && is_persistent(group)) {
+ (void) sa_get_instance(scf_handle, groupname);
+ (void) sa_create_pgroup(scf_handle, oname);
+ }
+ }
+ }
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+ return (security);
+}
+
+/*
+ * sa_destroy_security(security)
+ *
+ * Remove the specified optionset from the document and the
+ * configuration.
+ */
+
+int
+sa_destroy_security(sa_security_t security)
+{
+ char name[256];
+ int len;
+ int ret = SA_OK;
+ char *id = NULL;
+ sa_group_t group;
+ int iszfs = 0;
+ int ispersist = 1;
+
+ group = sa_get_optionset_parent(security);
+
+ if (group != NULL)
+ iszfs = sa_group_is_zfs(group);
+
+ if (group != NULL && !iszfs) {
+ if (sa_is_share(group))
+ ispersist = is_persistent(group);
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ }
+ if (ispersist) {
+ len = sa_security_name(security, name, sizeof (name), id);
+ if (!iszfs && len > 0) {
+ ret = sa_delete_pgroup(scf_handle, name);
+ }
+ }
+ xmlUnlinkNode((xmlNodePtr)security);
+ xmlFreeNode((xmlNodePtr)security);
+ if (iszfs) {
+ ret = sa_zfs_update(group);
+ }
+ if (id != NULL)
+ sa_free_attr_string(id);
+ return (ret);
+}
+
+/*
+ * sa_get_security_attr(optionset, tag)
+ *
+ * Return the specified attribute value from the optionset.
+ */
+
+char *
+sa_get_security_attr(sa_property_t optionset, char *tag)
+{
+ return (get_node_attr((void *)optionset, tag));
+
+}
+
+/*
+ * sa_set_security_attr(optionset, tag, value)
+ *
+ * Set the optioset attribute specied by tag to the specified value.
+ */
+
+void
+sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
+{
+ set_node_attr((void *)optionset, tag, value);
+}
+
+/*
+ * is_nodetype(node, type)
+ *
+ * Check to see if node is of the type specified.
+ */
+
+static int
+is_nodetype(void *node, char *type)
+{
+ return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
+}
+
+/*
+ * sa_set_prop_by_prop(optionset, group, prop, type)
+ *
+ * Add/remove/update the specified property prop into the optionset or
+ * share. If a share, sort out which property group based on GUID. In
+ * all cases, the appropriate transaction is set (or ZFS share is
+ * marked as needing an update)
+ */
+
+#define SA_PROP_OP_REMOVE 1
+#define SA_PROP_OP_ADD 2
+#define SA_PROP_OP_UPDATE 3
+static int
+sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
+ sa_property_t prop, int type)
+{
+ char *name;
+ char *valstr;
+ int ret = SA_OK;
+ scf_transaction_entry_t *entry;
+ scf_value_t *value;
+ int opttype; /* 1 == optionset, 0 == security */
+ char *id = NULL;
+ int iszfs = 0;
+ int isshare = 0;
+ sa_group_t parent = NULL;
+
+ if (!is_persistent(group)) {
+ /*
+ * if the group/share is not persistent we don't need
+ * to do anything here
+ */
+ return (SA_OK);
+ }
+ name = sa_get_property_attr(prop, "type");
+ valstr = sa_get_property_attr(prop, "value");
+ entry = scf_entry_create(scf_handle->handle);
+ opttype = is_nodetype((void *)optionset, "optionset");
+
+ if (valstr != NULL && entry != NULL) {
+ if (sa_is_share(group)) {
+ isshare = 1;
+ parent = sa_get_parent_group(group);
+ if (parent != NULL) {
+ iszfs = is_zfs_group(parent);
+ }
+ } else {
+ iszfs = is_zfs_group(group);
+ }
+ if (!iszfs) {
+ if (scf_handle->trans == NULL) {
+ char oname[256];
+ char *groupname = NULL;
+ if (isshare) {
+ if (parent != NULL) {
+ groupname = sa_get_group_attr(parent, "name");
+ }
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ } else {
+ groupname = sa_get_group_attr(group, "name");
+ }
+ if (groupname != NULL) {
+ ret = sa_get_instance(scf_handle, groupname);
+ sa_free_attr_string(groupname);
+ }
+ if (opttype)
+ (void) sa_optionset_name(optionset, oname,
+ sizeof (oname), id);
+ else
+ (void) sa_security_name(optionset, oname,
+ sizeof (oname), id);
+ ret = sa_start_transaction(scf_handle, oname);
+ }
+ if (ret == SA_OK) {
+ switch (type) {
+ case SA_PROP_OP_REMOVE:
+ ret = scf_transaction_property_delete(scf_handle->trans,
+ entry,
+ name);
+ break;
+ case SA_PROP_OP_ADD:
+ case SA_PROP_OP_UPDATE:
+ value = scf_value_create(scf_handle->handle);
+ if (value != NULL) {
+ if (type == SA_PROP_OP_ADD)
+ ret = scf_transaction_property_new(
+ scf_handle->trans,
+ entry,
+ name,
+ SCF_TYPE_ASTRING);
+ else
+ ret = scf_transaction_property_change(
+ scf_handle->trans,
+ entry,
+ name,
+ SCF_TYPE_ASTRING);
+ if (ret == 0) {
+ ret = scf_value_set_astring(value, valstr);
+ if (ret == 0)
+ ret = scf_entry_add_value(entry, value);
+ if (ret != 0) {
+ scf_value_destroy(value);
+ ret = SA_SYSTEM_ERR;
+ }
+ } else {
+ scf_entry_destroy(entry);
+ ret = SA_SYSTEM_ERR;
+ }
+ break;
+ }
+ }
+ }
+ } else {
+ /*
+ * ZFS update. The calling function would have updated
+ * the internal XML structure. Just need to flag it as
+ * changed for ZFS.
+ */
+ zfs_set_update((sa_share_t)group);
+ }
+ }
+
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (valstr != NULL)
+ sa_free_attr_string(valstr);
+ else if (entry != NULL)
+ scf_entry_destroy(entry);
+
+ if (ret == -1)
+ ret = SA_SYSTEM_ERR;
+
+ return (ret);
+}
+
+/*
+ * sa_create_property(name, value)
+ *
+ * Create a new property with the specified name and value.
+ */
+
+sa_property_t
+sa_create_property(char *name, char *value)
+{
+ xmlNodePtr node;
+
+ node = xmlNewNode(NULL, (xmlChar *)"option");
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
+ xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
+ }
+ return ((sa_property_t)node);
+}
+
+/*
+ * sa_add_property(object, property)
+ *
+ * Add the specified property to the object. Issue the appropriate
+ * transaction or mark a ZFS object as needing an update.
+ */
+
+int
+sa_add_property(void *object, sa_property_t property)
+{
+ int ret = SA_OK;
+ sa_group_t parent;
+ sa_group_t group;
+ char *proto;
+
+ proto = sa_get_optionset_attr(object, "type");
+ if (property != NULL) {
+ if ((ret = sa_valid_property(object, proto, property)) == SA_OK) {
+ property = (sa_property_t)xmlAddChild((xmlNodePtr)object,
+ (xmlNodePtr)property);
+ } else {
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ return (ret);
+ }
+ }
+
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+
+ parent = sa_get_parent_group(object);
+ if (!is_persistent(parent)) {
+ return (ret);
+ }
+
+ if (sa_is_share(parent))
+ group = sa_get_parent_group(parent);
+ else
+ group = parent;
+
+ if (property == NULL)
+ ret = SA_NO_MEMORY;
+ else {
+ char oname[256];
+
+ if (!is_zfs_group(group)) {
+ char *id = NULL;
+ if (sa_is_share((sa_group_t)parent)) {
+ id = sa_get_share_attr((sa_share_t)parent, "id");
+ }
+ if (scf_handle->trans == NULL) {
+ if (is_nodetype(object, "optionset"))
+ (void) sa_optionset_name((sa_optionset_t)object,
+ oname, sizeof (oname), id);
+ else
+ (void) sa_security_name((sa_optionset_t)object,
+ oname, sizeof (oname), id);
+ ret = sa_start_transaction(scf_handle, oname);
+ }
+ if (ret == SA_OK) {
+ char *name;
+ char *value;
+ name = sa_get_property_attr(property, "type");
+ value = sa_get_property_attr(property, "value");
+ if (name != NULL && value != NULL) {
+ if (scf_handle->scf_state == SCH_STATE_INIT)
+ ret = sa_set_property(scf_handle, name, value);
+ } else
+ ret = SA_CONFIG_ERR;
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (value != NULL)
+ sa_free_attr_string(value);
+ }
+ if (id != NULL)
+ sa_free_attr_string(id);
+ } else {
+ /*
+ * ZFS is a special case. We do want to allow editing
+ * property/security lists since we can have a better
+ * syntax and we also want to keep things consistent
+ * when possible.
+ *
+ * Right now, we defer until the sa_commit_properties
+ * so we can get them all at once. We do need to mark
+ * the share as "changed"
+ */
+ zfs_set_update((sa_share_t)parent);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_remove_property(property)
+ *
+ * Remove the specied property from its containing object. Update the
+ * repository as appropriate.
+ */
+
+int
+sa_remove_property(sa_property_t property)
+{
+ int ret = SA_OK;
+
+ if (property != NULL) {
+ sa_optionset_t optionset;
+ sa_group_t group;
+ optionset = sa_get_property_parent(property);
+ if (optionset != NULL) {
+ group = sa_get_optionset_parent(optionset);
+ if (group != NULL) {
+ ret = sa_set_prop_by_prop(optionset, group, property,
+ SA_PROP_OP_REMOVE);
+ }
+ }
+ xmlUnlinkNode((xmlNodePtr)property);
+ xmlFreeNode((xmlNodePtr)property);
+ } else {
+ ret = SA_NO_SUCH_PROP;
+ }
+ return (ret);
+}
+
+/*
+ * sa_update_property(property, value)
+ *
+ * Update the specified property to the new value. If value is NULL,
+ * we currently treat this as a remove.
+ */
+
+int
+sa_update_property(sa_property_t property, char *value)
+{
+ int ret = SA_OK;
+ if (value == NULL) {
+ return (sa_remove_property(property));
+ } else {
+ sa_optionset_t optionset;
+ sa_group_t group;
+ set_node_attr((void *)property, "value", value);
+ optionset = sa_get_property_parent(property);
+ if (optionset != NULL) {
+ group = sa_get_optionset_parent(optionset);
+ if (group != NULL) {
+ ret = sa_set_prop_by_prop(optionset, group, property,
+ SA_PROP_OP_UPDATE);
+ }
+ } else {
+ ret = SA_NO_SUCH_PROP;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * _sa_get_next_error(node)
+ *
+ * Get the next (first if node==NULL) error node in the
+ * document. "error" nodes are added if there were syntax errors
+ * during parsing of the /etc/dfs/dfstab file. They are preserved in
+ * comments and recreated in the doc on the next parse.
+ */
+
+xmlNodePtr
+_sa_get_next_error(xmlNodePtr node)
+{
+ if (node == NULL) {
+ for (node = sa_config_tree->xmlChildrenNode;
+ node != NULL; node = node->next)
+ if (xmlStrcmp(node->name, (xmlChar *)"error") == 0)
+ return (node);
+ } else {
+ for (node = node->next; node != NULL; node = node->next)
+ if (xmlStrcmp(node->name, (xmlChar *)"error") == 0)
+ return (node);
+ }
+ return (node);
+}
+
+/*
+ * sa_get_protocol_property(propset, prop)
+ *
+ * Get the specified protocol specific property. These are global to
+ * the protocol and not specific to a group or share.
+ */
+
+sa_property_t
+sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
+{
+ xmlNodePtr node = (xmlNodePtr)propset;
+ xmlChar *value = NULL;
+
+ for (node = node->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
+ if (prop == NULL)
+ break;
+ value = xmlGetProp(node, (xmlChar *)"type");
+ if (value != NULL &&
+ xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
+ break;
+ }
+ if (value != NULL) {
+ xmlFree(value);
+ value = NULL;
+ }
+ }
+ }
+ if (value != NULL)
+ xmlFree(value);
+ if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
+ /* avoid a non option node -- it is possible to be a text node */
+ node = NULL;
+ }
+ return ((sa_property_t)node);
+}
+
+/*
+ * sa_get_next_protocol_property(prop)
+ *
+ * Get the next protocol specific property in the list.
+ */
+
+sa_property_t
+sa_get_next_protocol_property(sa_property_t prop)
+{
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)prop)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
+ break;
+ }
+ }
+ return ((sa_property_t)node);
+}
+
+/*
+ * sa_set_protocol_property(prop, value)
+ *
+ * Set the specified property to have the new value. The protocol
+ * specific plugin will then be called to update the property.
+ */
+
+int
+sa_set_protocol_property(sa_property_t prop, char *value)
+{
+ sa_protocol_properties_t propset;
+ char *proto;
+ int ret = SA_INVALID_PROTOCOL;
+
+ propset = ((xmlNodePtr)prop)->parent;
+ if (propset != NULL) {
+ proto = sa_get_optionset_attr(propset, "type");
+ if (proto != NULL) {
+ set_node_attr((xmlNodePtr)prop, "value", value);
+ ret = sa_proto_set_property(proto, prop);
+ sa_free_attr_string(prop);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_add_protocol_property(propset, prop)
+ *
+ * Add a new property to the protocol sepcific property set.
+ */
+
+int
+sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
+{
+ xmlNodePtr node;
+
+ /* should check for legitimacy */
+ node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
+ if (node != NULL)
+ return (SA_OK);
+ return (SA_NO_MEMORY);
+}
+
+/*
+ * sa_create_protocol_properties(proto)
+ *
+ * Create a protocol specifity property set.
+ */
+
+sa_protocol_properties_t
+sa_create_protocol_properties(char *proto)
+{
+ xmlNodePtr node;
+ node = xmlNewNode(NULL, (xmlChar *)"propertyset");
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
+ }
+ return (node);
+}
diff --git a/usr/src/lib/libshare/common/libshare.h b/usr/src/lib/libshare/common/libshare.h
new file mode 100644
index 0000000000..1c172f6f2a
--- /dev/null
+++ b/usr/src/lib/libshare/common/libshare.h
@@ -0,0 +1,217 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * basic API declarations for share management
+ */
+
+#ifndef _LIBSHARE_H
+#define _LIBSHARE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Basic datatypes for most functions
+ */
+typedef void *sa_group_t;
+typedef void *sa_share_t;
+typedef void *sa_property_t;
+typedef void *sa_optionset_t;
+typedef void *sa_security_t;
+typedef void *sa_protocol_properties_t;
+
+typedef void *sa_handle_t; /* opaque handle to access core functions */
+
+/*
+ * defined error values
+ */
+
+#define SA_OK 0
+#define SA_NO_SUCH_PATH 1 /* provided path doesn't exist */
+#define SA_NO_MEMORY 2 /* no memory for data structures */
+#define SA_DUPLICATE_NAME 3 /* object name is already in use */
+#define SA_BAD_PATH 4 /* not a full path */
+#define SA_NO_SUCH_GROUP 5 /* group is not defined */
+#define SA_CONFIG_ERR 6 /* system configuration error */
+#define SA_SYSTEM_ERR 7 /* system error, use errno */
+#define SA_SYNTAX_ERR 8 /* syntax error on command line */
+#define SA_NO_PERMISSION 9 /* no permission for operation */
+#define SA_BUSY 10 /* resource is busy */
+#define SA_NO_SUCH_PROP 11 /* property doesn't exist */
+#define SA_INVALID_NAME 12 /* name of object is invalid */
+#define SA_INVALID_PROTOCOL 13 /* specified protocol not valid */
+#define SA_NOT_ALLOWED 14 /* operation not allowed */
+#define SA_BAD_VALUE 15 /* bad value for property */
+#define SA_INVALID_SECURITY 16 /* invalid security type */
+#define SA_NO_SUCH_SECURITY 17 /* security set not found */
+#define SA_VALUE_CONFLICT 18 /* property value conflict */
+#define SA_NOT_IMPLEMENTED 19 /* plugin interface not implemented */
+#define SA_INVALID_PATH 20 /* path is sub-dir of existing share */
+#define SA_NOT_SUPPORTED 21 /* operation not supported for proto */
+#define SA_PROP_SHARE_ONLY 22 /* property valid on share only */
+#define SA_NOT_SHARED 23 /* path is not shared */
+
+/* API Initialization */
+#define SA_INIT_SHARE_API 0x0001 /* init share specific interface */
+#define SA_INIT_CONTROL_API 0x0002 /* init control specific interface */
+
+/* not part of API returns */
+#define SA_LEGACY_ERR 32 /* share/unshare error return */
+
+/*
+ * other defined values
+ */
+
+#define SA_MAX_NAME_LEN 100 /* must fit service instance name */
+#define SA_SHARE_PERMANENT 2 /* share goes to repository */
+#define SA_SHARE_LEGACY 1 /* share is in dfstab only */
+#define SA_SHARE_TRANSIENT 0 /* shared but not across reboot */
+
+/* RBAC related */
+#define SA_RBAC_MANAGE "solaris.smf.manage.shares"
+#define SA_RBAC_VALUE "solaris.smf.value.shares"
+
+/*
+ * legacy files
+ */
+
+#define SA_LEGACY_DFSTAB "/etc/dfs/dfstab"
+#define SA_LEGACY_SHARETAB "/etc/dfs/sharetab"
+
+/*
+ * SMF related
+ */
+
+#define SA_SVC_FMRI_BASE "svc:/network/shares/group"
+
+/* initialization */
+extern void sa_init(int);
+extern void sa_fini(void);
+extern int sa_update_config(void);
+extern char *sa_errorstr(int);
+
+/* protocol names */
+extern int sa_get_protocols(char ***);
+extern int sa_valid_protocol(char *);
+
+/* group control (create, remove, etc) */
+extern sa_group_t sa_create_group(char *, int *);
+extern int sa_remove_group(sa_group_t);
+extern sa_group_t sa_get_group(char *);
+extern sa_group_t sa_get_next_group(sa_group_t);
+extern char *sa_get_group_attr(sa_group_t, char *);
+extern int sa_set_group_attr(sa_group_t, char *, char *);
+extern sa_group_t sa_get_sub_group(sa_group_t);
+extern int sa_valid_group_name(char *);
+
+/* share control */
+extern sa_share_t sa_add_share(sa_group_t, char *, int, int *);
+extern int sa_check_path(sa_group_t, char *);
+extern int sa_move_share(sa_group_t, sa_share_t);
+extern int sa_remove_share(sa_share_t);
+extern sa_share_t sa_get_share(sa_group_t, char *);
+extern sa_share_t sa_get_resource(sa_group_t, char *);
+extern sa_share_t sa_find_share(char *);
+extern sa_share_t sa_get_next_share(sa_share_t);
+extern char *sa_get_share_attr(sa_share_t, char *);
+extern char *sa_get_share_description(sa_share_t);
+extern sa_group_t sa_get_parent_group(sa_share_t);
+extern int sa_set_share_attr(sa_share_t, char *, char *);
+extern int sa_set_share_description(sa_share_t, char *);
+extern int sa_enable_share(sa_group_t, char *);
+extern int sa_disable_share(sa_group_t, char *);
+extern int sa_is_share(void *);
+
+/* data structure free calls */
+extern void sa_free_attr_string(char *);
+extern void sa_free_share_description(char *);
+
+/* optionset control */
+extern sa_optionset_t sa_get_optionset(sa_group_t, char *);
+extern sa_optionset_t sa_get_next_optionset(sa_group_t);
+extern char *sa_get_optionset_attr(sa_optionset_t, char *);
+extern void sa_set_optionset_attr(sa_optionset_t, char *, char *);
+extern sa_optionset_t sa_create_optionset(sa_group_t, char *);
+extern int sa_destroy_optionset(sa_optionset_t);
+extern sa_optionset_t sa_get_derived_optionset(void *, char *, int);
+extern void sa_free_derived_optionset(sa_optionset_t);
+
+/* property functions */
+extern sa_optionset_t sa_get_property(sa_optionset_t, char *);
+extern sa_optionset_t sa_get_next_property(sa_group_t);
+extern char *sa_get_property_attr(sa_property_t, char *);
+extern sa_property_t sa_create_property(char *, char *);
+extern int sa_add_property(void *, sa_property_t);
+extern int sa_update_property(sa_property_t, char *);
+extern int sa_remove_property(sa_property_t);
+extern int sa_commit_properties(sa_optionset_t, int);
+extern int sa_valid_property(void *, char *, sa_property_t);
+
+/* security control */
+extern sa_security_t sa_get_security(sa_group_t, char *, char *);
+extern sa_security_t sa_get_next_security(sa_security_t);
+extern char *sa_get_security_attr(sa_optionset_t, char *);
+extern sa_security_t sa_create_security(sa_group_t, char *, char *);
+extern int sa_destroy_security(sa_security_t);
+extern void sa_set_security_attr(sa_security_t, char *, char *);
+extern sa_optionset_t sa_get_all_security_types(void *, char *, int);
+extern sa_security_t sa_get_derived_security(void *, char *, char *, int);
+extern void sa_free_derived_security(sa_security_t);
+
+/* protocol specific interfaces */
+extern int sa_parse_legacy_options(sa_group_t, char *, char *);
+extern char *sa_proto_legacy_format(char *, sa_group_t, int);
+extern int sa_is_security(char *, char *);
+extern sa_protocol_properties_t sa_proto_get_properties(char *);
+extern sa_property_t sa_get_protocol_property(sa_protocol_properties_t, char *);
+extern sa_property_t sa_get_next_protocol_property(sa_property_t);
+extern int sa_set_protocol_property(sa_property_t, char *);
+extern char *sa_get_protocol_status(char *);
+extern void sa_format_free(char *);
+extern sa_protocol_properties_t sa_create_protocol_properties(char *);
+extern int sa_add_protocol_property(sa_protocol_properties_t, sa_property_t);
+extern int sa_proto_valid_prop(char *, sa_property_t, sa_optionset_t);
+extern int sa_proto_valid_space(char *, char *);
+extern char *sa_proto_space_alias(char *, char *);
+
+/* handle legacy (dfstab/sharetab) files */
+extern int sa_delete_legacy(sa_share_t);
+extern int sa_update_legacy(sa_share_t, char *);
+extern int sa_update_sharetab(sa_share_t, char *);
+extern int sa_delete_sharetab(char *, char *);
+
+/* ZFS functions */
+extern int sa_zfs_is_shared(char *);
+extern int sa_group_is_zfs(sa_group_t);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSHARE_H */
diff --git a/usr/src/lib/libshare/common/libshare_impl.h b/usr/src/lib/libshare/common/libshare_impl.h
new file mode 100644
index 0000000000..0bb846e863
--- /dev/null
+++ b/usr/src/lib/libshare/common/libshare_impl.h
@@ -0,0 +1,143 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * basic declarations for implementation of the share management
+ * libraries.
+ */
+
+#ifndef _LIBSHARE_IMPL_H
+#define _LIBSHARE_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libshare.h>
+#include <libscf.h>
+#include <scfutil.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* directory to find plugin modules in */
+#define SA_LIB_DIR "/usr/lib/fs"
+
+/* default group name for dfstab file */
+#define SA_DEFAULT_FILE_GRP "sys"
+
+typedef void *sa_phandle_t;
+
+#define SA_PLUGIN_VERSION 1
+struct sa_plugin_ops {
+ int sa_version;
+ char *sa_protocol; /* protocol name */
+ int (*sa_init)();
+ void (*sa_fini)();
+ int (*sa_share)(sa_share_t); /* start sharing */
+ int (*sa_unshare)(char *); /* stop sharing */
+ int (*sa_valid_prop)(sa_property_t, sa_optionset_t);
+ int (*sa_valid_space)(char *); /* is name valid optionspace? */
+ int (*sa_security_prop)(char *); /* property is security */
+ int (*sa_legacy_opts)(sa_group_t, char *); /* parse legacy opts */
+ char *(*sa_legacy_format)(sa_group_t, int);
+ int (*sa_set_proto_prop)(sa_property_t);
+ sa_protocol_properties_t (*sa_get_proto_set)();
+ char *(*sa_get_proto_status)();
+ char *(*sa_space_alias)(char *);
+ int (*sa_update_legacy)(sa_share_t);
+ int (*sa_delete_legacy)(sa_share_t);
+ int (*sa_run_command)(int, int, char **); /* proto specific */
+ int (*sa_command_help)();
+};
+
+struct sa_proto_handle {
+ int sa_num_proto;
+ char **sa_proto;
+ struct sa_plugin_ops **sa_ops;
+};
+
+typedef struct propertylist {
+ struct propertylist *pl_next;
+ int pl_type;
+ union propval {
+ sa_optionset_t pl_optionset;
+ sa_security_t pl_security;
+ void *pl_void;
+ } pl_value;
+} property_list_t;
+
+extern int sa_proto_share(char *, sa_share_t);
+extern int sa_proto_unshare(char *, char *);
+extern int sa_proto_valid_prop(char *, sa_property_t, sa_optionset_t);
+extern int sa_proto_security_prop(char *, char *);
+extern int sa_proto_legacy_opts(char *, sa_group_t, char *);
+
+/* internal utility functions */
+extern sa_optionset_t sa_get_derived_optionset(sa_group_t, char *, int);
+extern void sa_free_derived_optionset(sa_optionset_t);
+extern sa_optionset_t sa_get_all_security_types(void *, char *, int);
+extern sa_security_t sa_get_derived_security(void *, char *, char *, int);
+extern void sa_free_derived_security(sa_security_t);
+extern sa_protocol_properties_t sa_create_protocol_properties(char *);
+extern int sa_start_transaction(scfutilhandle_t *, char *);
+extern int sa_end_transaction(scfutilhandle_t *);
+extern void sa_abort_transaction(scfutilhandle_t *);
+extern int sa_commit_share(scfutilhandle_t *, sa_group_t, sa_share_t);
+extern int sa_set_property(scfutilhandle_t *, char *, char *);
+extern void sa_free_fstype(char *fstyp);
+extern int sa_delete_share(scfutilhandle_t *, sa_group_t, sa_share_t);
+extern int sa_delete_instance(scfutilhandle_t *, char *);
+extern int sa_create_pgroup(scfutilhandle_t *, char *);
+extern int sa_delete_pgroup(scfutilhandle_t *, char *);
+
+/* ZFS functions */
+extern int sa_get_zfs_shares(char *);
+extern int sa_zfs_update(sa_share_t);
+
+/* plugin specific functions */
+extern int proto_plugin_init();
+extern int sa_proto_set_property(char *, sa_property_t);
+extern int sa_proto_delete_legacy(char *, sa_share_t);
+extern int sa_proto_update_legacy(char *, sa_share_t);
+
+#define PL_TYPE_PROPERTY 0
+#define PL_TYPE_SECURITY 1
+
+/* values only used by the internal dfstab/sharetab parser */
+#define SA_SHARE_PARSER 0x1000
+
+/* plugin handler only */
+struct sa_proto_plugin {
+ struct sa_proto_plugin *plugin_next;
+ struct sa_plugin_ops *plugin_ops;
+ void *plugin_handle;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSHARE_IMPL_H */
diff --git a/usr/src/lib/libshare/common/libshare_zfs.c b/usr/src/lib/libshare/common/libshare_zfs.c
new file mode 100644
index 0000000000..0a8f327edd
--- /dev/null
+++ b/usr/src/lib/libshare/common/libshare_zfs.c
@@ -0,0 +1,496 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libfsmgt.h"
+#include <libzfs.h>
+#include <string.h>
+#include <libshare.h>
+#include "libshare_impl.h"
+
+extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
+extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
+extern char *sa_fstype(char *);
+extern void set_node_attr(void *, char *, char *);
+extern int sa_is_share(void *);
+/*
+ * File system specific code for ZFS
+ */
+
+/*
+ * get_zfs_dataset(path)
+ *
+ * get the name of the ZFS dataset the path is equivalent to. The
+ * dataset name is used for get/set of ZFS properties since libzfs
+ * requires a dataset to do a zfs_open().
+ */
+
+static char *
+get_zfs_dataset(char *path)
+{
+ fs_mntlist_t *list;
+ fs_mntlist_t *cur;
+ int err;
+ char *dataset = NULL;
+
+ list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL,
+ NULL, 0, &err);
+ for (cur = list; cur != NULL; cur = cur->next) {
+ if (strcmp(path, cur->mountp) == 0 ||
+ strncmp(path, cur->mountp, strlen(cur->mountp)) == 0) {
+ /*
+ * we want the longest resource so keep trying. This
+ * check avoids dropping out on a partial match. ZFS
+ * resources are ordered when mounted in order to
+ * ensure inheritence of properties.
+ */
+ dataset = cur->resource;
+ }
+ }
+ if (dataset != NULL) {
+ dataset = strdup(dataset);
+ }
+ fs_free_mount_list(list);
+ return (dataset);
+}
+
+/*
+ * get_zfs_property(dataset, property)
+ *
+ * Get the file system property specified from the ZFS dataset.
+ */
+
+static char *
+get_zfs_property(char *dataset, zfs_prop_t property)
+{
+ zfs_handle_t *handle = NULL;
+ char shareopts[ZFS_MAXPROPLEN];
+ libzfs_handle_t *libhandle;
+
+ libhandle = libzfs_init();
+ if (libhandle != NULL) {
+ handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
+ if (handle != NULL) {
+ if (zfs_prop_get(handle, property, shareopts,
+ sizeof (shareopts), NULL, NULL, 0,
+ FALSE) == 0) {
+ zfs_close(handle);
+ libzfs_fini(libhandle);
+ return (strdup(shareopts));
+ }
+ zfs_close(handle);
+ }
+ libzfs_fini(libhandle);
+ }
+ return (NULL);
+}
+
+/*
+ * sa_zfs_is_shared(path)
+ *
+ * Check to see if the ZFS path provided has the sharenfs option set
+ * or not.
+ */
+
+int
+sa_zfs_is_shared(char *path)
+{
+ int ret = 0;
+ char *dataset;
+ zfs_handle_t *handle = NULL;
+ char shareopts[ZFS_MAXPROPLEN];
+ libzfs_handle_t *libhandle;
+
+ dataset = get_zfs_dataset(path);
+ if (dataset != NULL) {
+ libhandle = libzfs_init();
+ if (libhandle != NULL) {
+ handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
+ if (handle != NULL) {
+ if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
+ sizeof (shareopts), NULL, NULL, 0,
+ FALSE) == 0 &&
+ strcmp(shareopts, "off") != 0)
+ ret = 1; /* it is shared */
+ zfs_close(handle);
+ }
+ libzfs_fini(libhandle);
+ }
+ free(dataset);
+ }
+ return (ret);
+}
+
+/*
+ * find_or_create_group(groupname, proto, *err)
+ *
+ * While walking the ZFS tree, we need to add shares to a defined
+ * group. If the group doesn't exist, create it first, making sure it
+ * is marked as a ZFS group.
+ *
+ * Not that all ZFS shares are in a subgroup of the top level group
+ * "zfs".
+ */
+
+static sa_group_t
+find_or_create_group(char *groupname, char *proto, int *err)
+{
+ sa_group_t group;
+ sa_optionset_t optionset;
+ int ret = SA_OK;
+
+ /*
+ * we check to see if the "zfs" group exists. Since this
+ * should be the top level group, we don't want the
+ * parent. This is to make sure the zfs group has been created
+ * and to created if it hasn't been.
+ */
+ group = sa_get_group(groupname);
+ if (group == NULL) {
+ group = sa_create_group(groupname, &ret);
+ if (group != NULL)
+ ret = sa_set_group_attr(group, "zfs", "true");
+ }
+ if (group != NULL) {
+ if (proto != NULL) {
+ optionset = sa_get_optionset(group, proto);
+ if (optionset == NULL) {
+ optionset = sa_create_optionset(group, proto);
+ } else {
+ char **protolist;
+ int numprotos, i;
+ numprotos = sa_get_protocols(&protolist);
+ for (i = 0; i < numprotos; i++) {
+ optionset = sa_create_optionset(group, protolist[i]);
+ }
+ if (protolist != NULL)
+ free(protolist);
+ }
+ }
+ }
+ if (err != NULL)
+ *err = ret;
+ return (group);
+}
+
+/*
+ * sa_get_zfs_shares(groupname)
+ *
+ * Walk the mnttab for all zfs mounts and determine which are
+ * shared. Find or create the appropriate group/sub-group to contain
+ * the shares.
+ *
+ * All shares are in a sub-group that will hold the properties. This
+ * allows representing the inherited property model.
+ */
+
+int
+sa_get_zfs_shares(char *groupname)
+{
+ sa_group_t group;
+ sa_group_t zfsgroup;
+ int legacy = 0;
+ int err;
+ fs_mntlist_t *list;
+ fs_mntlist_t *cur;
+ zfs_handle_t *handle = NULL;
+ char shareopts[ZFS_MAXPROPLEN];
+ sa_share_t share;
+ zfs_source_t source;
+ char sourcestr[ZFS_MAXPROPLEN];
+ libzfs_handle_t *libhandle;
+
+ /*
+ * if we can't access libzfs, don't bother doing anything.
+ */
+ libhandle = libzfs_init();
+ if (libhandle == NULL)
+ return (SA_SYSTEM_ERR);
+
+ zfsgroup = find_or_create_group(groupname, "nfs", &err);
+ if (zfsgroup != NULL) {
+ /*
+ * need to walk the mounted ZFS pools and datasets to
+ * find shares that are possible.
+ */
+ list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL,
+ NULL, 0, &err);
+ group = zfsgroup;
+ for (cur = list; cur != NULL; cur = cur->next) {
+ handle = zfs_open(libhandle, cur->resource,
+ ZFS_TYPE_FILESYSTEM);
+ if (handle != NULL) {
+ source = ZFS_SRC_ALL;
+ if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
+ sizeof (shareopts), &source, sourcestr,
+ ZFS_MAXPROPLEN,
+ FALSE) == 0 &&
+ strcmp(shareopts, "off") != 0) {
+ /* it is shared so add to list */
+ share = sa_find_share(cur->mountp);
+ err = SA_OK;
+ if (share != NULL) {
+ /*
+ * A zfs file system had been shared
+ * through tradiditional methods
+ * (share/dfstab or added to a non-zfs
+ * group. Now it has been added to a
+ * ZFS group via the zfs
+ * command. Remove from previous
+ * config and setup with current
+ * options.
+ */
+ err = sa_remove_share(share);
+ share = NULL;
+ }
+ if (err == SA_OK) {
+ if (source & ZFS_SRC_INHERITED) {
+ share = _sa_add_share(group, cur->mountp,
+ SA_SHARE_TRANSIENT,
+ &err);
+ } else {
+ group = _sa_create_zfs_group(zfsgroup,
+ cur->resource);
+ set_node_attr(group, "zfs", "true");
+ share = _sa_add_share(group, cur->mountp,
+ SA_SHARE_TRANSIENT,
+ &err);
+ if (err == SA_OK) {
+ char *options;
+ if (strcmp(shareopts, "on") != 0) {
+ options = strdup(shareopts);
+ if (options != NULL) {
+ err = sa_parse_legacy_options(group,
+ options,
+ "nfs");
+ free(options);
+ }
+ /* unmark the share's changed state */
+ set_node_attr(share, "changed", NULL);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (list != NULL)
+ fs_free_mount_list(list);
+ }
+ if (libhandle != NULL)
+ libzfs_fini(libhandle);
+ return (legacy);
+}
+
+#define COMMAND "/usr/sbin/zfs"
+
+/*
+ * sa_zfs_set_sharenfs(group, path, on)
+ *
+ * Update the "sharenfs" property on the path. If on is true, then set
+ * to the properties on the group or "on" if no properties are
+ * defined. Set to "off" if on is false.
+ */
+
+int
+sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
+{
+ int ret = SA_NOT_IMPLEMENTED;
+ char *command;
+
+ command = malloc(ZFS_MAXPROPLEN * 2);
+ if (command != NULL) {
+ char *opts = NULL;
+ char *dataset;
+ FILE *pfile;
+ /* for now, NFS is always available for "zfs" */
+ if (on) {
+ opts = sa_proto_legacy_format("nfs", group, 1);
+ if (opts != NULL && strlen(opts) == 0) {
+ free(opts);
+ opts = strdup("on");
+ }
+ }
+ dataset = get_zfs_dataset(path);
+ if (dataset != NULL) {
+ (void) snprintf(command, ZFS_MAXPROPLEN * 2,
+ "%s set sharenfs=\"%s\" %s", COMMAND,
+ opts != NULL ? opts : "off",
+ dataset);
+ pfile = popen(command, "r");
+ if (pfile != NULL) {
+ ret = pclose(pfile);
+ if (ret != 0)
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ if (opts != NULL)
+ free(opts);
+ if (dataset != NULL)
+ free(dataset);
+ free(command);
+ }
+ return (ret);
+}
+
+/*
+ * sa_zfs_update(group)
+ *
+ * call back to ZFS to update the share if necessary.
+ * Don't do it if it isn't a real change.
+ */
+int
+sa_zfs_update(sa_group_t group)
+{
+ sa_optionset_t protopt;
+ sa_group_t parent;
+ char *command;
+ char *optstring;
+ int ret = SA_OK;
+ int doupdate = 0;
+ FILE *pfile;
+
+ if (sa_is_share(group))
+ parent = sa_get_parent_group(group);
+ else
+ parent = group;
+
+ if (parent != NULL) {
+ command = malloc(ZFS_MAXPROPLEN * 2);
+ if (command == NULL)
+ return (SA_NO_MEMORY);
+
+ *command = '\0';
+ for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
+ protopt = sa_get_next_optionset(protopt)) {
+
+ char *proto = sa_get_optionset_attr(protopt, "type");
+ char *path;
+ char *dataset = NULL;
+ char *zfsopts = NULL;
+
+ if (sa_is_share(group)) {
+ path = sa_get_share_attr((sa_share_t)group, "path");
+ if (path != NULL) {
+ dataset = get_zfs_dataset(path);
+ sa_free_attr_string(path);
+ }
+ } else {
+ dataset = sa_get_group_attr(group, "name");
+ }
+ /* update only when there is an optstring found */
+ doupdate = 0;
+ if (proto != NULL && dataset != NULL) {
+ optstring = sa_proto_legacy_format(proto, group, 1);
+ zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS);
+
+ if (optstring != NULL && zfsopts != NULL) {
+ if (strcmp(optstring, zfsopts) != 0)
+ doupdate++;
+ }
+
+ if (doupdate) {
+ if (optstring != NULL && strlen(optstring) > 0) {
+ (void) snprintf(command, ZFS_MAXPROPLEN * 2,
+ "%s set sharenfs=%s %s", COMMAND,
+ optstring, dataset);
+ } else {
+ (void) snprintf(command, ZFS_MAXPROPLEN * 2,
+ "%s set sharenfs=on %s", COMMAND,
+ dataset);
+ }
+ pfile = popen(command, "r");
+ if (pfile != NULL)
+ ret = pclose(pfile);
+ switch (ret) {
+ default:
+ case 1:
+ ret = SA_SYSTEM_ERR;
+ break;
+ case 2:
+ ret = SA_SYNTAX_ERR;
+ break;
+ case 0:
+ break;
+ }
+ }
+ if (optstring != NULL) {
+ free(optstring);
+ }
+ if (zfsopts != NULL)
+ free(zfsopts);
+ }
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ if (dataset != NULL)
+ free(dataset);
+ }
+ free(command);
+ }
+ return (ret);
+}
+
+/*
+ * sa_group_is_zfs(group)
+ *
+ * Given the group, determine if the zfs attribute is set.
+ */
+
+int
+sa_group_is_zfs(sa_group_t group)
+{
+ char *zfs;
+ int ret = 0;
+
+ zfs = sa_get_group_attr(group, "zfs");
+ if (zfs != NULL) {
+ ret = 1;
+ sa_free_attr_string(zfs);
+ }
+ return (ret);
+}
+
+/*
+ * sa_path_is_zfs(path)
+ *
+ * Check to see if the file system path represents is of type "zfs".
+ */
+
+int
+sa_path_is_zfs(char *path)
+{
+ char *fstype;
+ int ret = 0;
+
+ fstype = sa_fstype(path);
+ if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
+ ret = 1;
+ }
+ if (fstype != NULL)
+ sa_free_fstype(fstype);
+ return (ret);
+}
diff --git a/usr/src/lib/libshare/common/libsharecore.c b/usr/src/lib/libshare/common/libsharecore.c
new file mode 100644
index 0000000000..9e1348feb6
--- /dev/null
+++ b/usr/src/lib/libshare/common/libsharecore.c
@@ -0,0 +1,1801 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * core library for common functions across all config store types
+ * and file systems to be exported. This includes legacy dfstab/sharetab
+ * parsing. Need to eliminate XML where possible.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include "libshare.h"
+#include "libshare_impl.h"
+#include "libfsmgt.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <grp.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <signal.h>
+#include <libintl.h>
+
+#include "sharetab.h"
+
+#define DFSTAB_NOTICE_LINES 5
+static char *notice[DFSTAB_NOTICE_LINES] = {
+ "# Do not modify this file directly.\n",
+ "# Use the sharemgr(1m) command for all share management\n",
+ "# This file is reconstructed and only maintained for backward\n",
+ "# compatibility. Configuration lines could be lost.\n",
+ "#\n"
+};
+
+#define STRNCAT(x, y, z) (xmlChar *)strncat((char *)x, (char *)y, z)
+
+/* will be much smaller, but this handles bad syntax in the file */
+#define MAXARGSFORSHARE 256
+
+/* used internally only */
+typedef
+struct sharelist {
+ struct sharelist *next;
+ int persist;
+ char *path;
+ char *resource;
+ char *fstype;
+ char *options;
+ char *description;
+ char *group;
+ char *origline;
+ int lineno;
+} xfs_sharelist_t;
+static void parse_dfstab(char *, xmlNodePtr);
+extern char *get_token(char *);
+static void dfs_free_list(xfs_sharelist_t *);
+/* prototypes */
+void getlegacyconfig(char *, xmlNodePtr *);
+extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
+extern xmlNodePtr _sa_get_next_error(xmlNodePtr);
+extern sa_group_t _sa_create_group(char *);
+static void outdfstab(FILE *, xfs_sharelist_t *);
+extern int _sa_remove_optionset(sa_optionset_t);
+extern int set_node_share(void *, char *, char *);
+extern void set_node_attr(void *, char *, char *);
+
+/*
+ * alloc_sharelist()
+ *
+ * allocator function to return an zfs_sharelist_t
+ */
+
+static xfs_sharelist_t *
+alloc_sharelist()
+{
+ xfs_sharelist_t *item;
+
+ item = (xfs_sharelist_t *)malloc(sizeof (xfs_sharelist_t));
+ if (item != NULL)
+ (void) memset(item, '\0', sizeof (xfs_sharelist_t));
+ return (item);
+}
+
+/*
+ * fix_notice(list)
+ *
+ * Look at the beginning of the current /etc/dfs/dfstab file and add
+ * the do not modify notice if it doesn't exist.
+ */
+
+static xfs_sharelist_t *
+fix_notice(xfs_sharelist_t *list)
+{
+ xfs_sharelist_t *item, *prev;
+ int i;
+
+ if (list->path == NULL && list->description != NULL &&
+ strcmp(list->description, notice[0]) != 0) {
+ for (prev = NULL, i = 0; i < DFSTAB_NOTICE_LINES; i++) {
+ item = alloc_sharelist();
+ if (item != NULL) {
+ item->description = strdup(notice[i]);
+ if (prev == NULL) {
+ item->next = list;
+ prev = item;
+ list = item;
+ } else {
+ item->next = prev->next;
+ prev->next = item;
+ prev = item;
+ }
+ }
+ }
+ }
+ return (list);
+}
+
+/*
+ * getdfstab(dfs)
+ *
+ * Returns an zfs_sharelist_t list of lines from the dfstab file
+ * pointed to by the FILE pointer dfs. Each entry is parsed and the
+ * original line is also preserved. Used in parsing and updating the
+ * dfstab file.
+ */
+
+static xfs_sharelist_t *
+getdfstab(FILE *dfs)
+{
+ char buff[_POSIX_ARG_MAX]; /* reasonable size given syntax of share */
+ char *bp;
+ char *token;
+ char *args[MAXARGSFORSHARE];
+ int argc;
+ int c;
+ static int line = 0;
+ xfs_sharelist_t *item, *first, *last;
+
+ if (dfs != NULL) {
+ first = NULL;
+ line = 0;
+ while (fgets(buff, sizeof (buff), dfs) != NULL) {
+ line++;
+ bp = buff;
+ if (buff[0] == '#') {
+ item = alloc_sharelist();
+ if (item != NULL) {
+ /* if no path, then comment */
+ item->lineno = line;
+ item->description = strdup(buff);
+ if (first == NULL) {
+ first = item;
+ last = item;
+ } else {
+ last->next = item;
+ last = item;
+ }
+ } else {
+ break;
+ }
+ continue;
+ } else if (buff[0] == '\n') {
+ continue;
+ }
+ optind = 1;
+ item = alloc_sharelist();
+ if (item == NULL) {
+ break;
+ } else if (first == NULL) {
+ first = item;
+ last = item;
+ } else {
+ last->next = item;
+ last = item;
+ }
+ item->lineno = line;
+ item->origline = strdup(buff);
+ (void) get_token(NULL); /* reset to new pointers */
+ argc = 0;
+ while ((token = get_token(bp)) != NULL) {
+ if (argc < MAXARGSFORSHARE) {
+ args[argc++] = token;
+ }
+ }
+ while ((c = getopt(argc, args, "F:o:d:pg:")) != -1) {
+ switch (c) {
+ case 'p':
+ item->persist = 1;
+ break;
+ case 'F':
+ item->fstype = strdup(optarg);
+ break;
+ case 'o':
+ item->options = strdup(optarg);
+ break;
+ case 'd':
+ item->description = strdup(optarg);
+ break;
+ case 'g':
+ item->group = strdup(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+ if (optind < argc) {
+ item->path = strdup(args[optind]);
+ optind++;
+ if (optind < argc) {
+ char *resource;
+ char *optgroup;
+ /* resource and/or groupname */
+ resource = args[optind];
+ optgroup = strchr(resource, '@');
+ if (optgroup != NULL) {
+ *optgroup++ = '\0';
+ }
+ if (optgroup != NULL)
+ item->group = strdup(optgroup);
+ if (resource != NULL && strlen(resource) > 0)
+ item->resource = strdup(resource);
+ }
+ }
+ }
+ if (item->fstype == NULL)
+ item->fstype = strdup("nfs"); /* this is the default */
+ }
+ first = fix_notice(first);
+ return (first);
+}
+
+/*
+ * finddfsentry(list, path)
+ *
+ * Look for path in the zfs_sharelist_t list and return the tnry if it
+ * exists.
+ */
+
+static xfs_sharelist_t *
+finddfsentry(xfs_sharelist_t *list, char *path)
+{
+ xfs_sharelist_t *item;
+
+ for (item = list; item != NULL; item = item->next) {
+ if (item->path != NULL && strcmp(item->path, path) == 0)
+ return (item);
+ }
+ return (NULL);
+}
+
+/*
+ * remdfsentry(list, path, proto)
+ *
+ * Remove the specified path (with protocol) from the list. This will
+ * remove it from dfstab when the file is rewritten.
+ */
+
+static xfs_sharelist_t *
+remdfsentry(xfs_sharelist_t *list, char *path, char *proto)
+{
+ xfs_sharelist_t *item, *prev = NULL;
+
+
+ for (item = prev = list; item != NULL; item = item->next) {
+ /* skip comment entry but don't lose it */
+ if (item->path == NULL) {
+ prev = item;
+ continue;
+ }
+ /* if proto is NULL, remove all protocols */
+ if (proto == NULL || (strcmp(item->path, path) == 0 &&
+ (item->fstype != NULL && strcmp(item->fstype, proto) == 0)))
+ break;
+ if (item->fstype == NULL &&
+ (proto == NULL || strcmp(proto, "nfs") == 0))
+ break;
+ prev = item;
+ }
+ if (item != NULL) {
+ if (item == prev) {
+ list = item->next; /* this must be the first one */
+ } else {
+ prev->next = item->next;
+ }
+ item->next = NULL;
+ dfs_free_list(item);
+ }
+ return (list);
+}
+
+/*
+ * remdfsline(list, line)
+ *
+ * Remove the line specified from the list.
+ */
+
+static xfs_sharelist_t *
+remdfsline(xfs_sharelist_t *list, char *line)
+{
+ xfs_sharelist_t *item, *prev = NULL;
+
+ for (item = prev = list; item != NULL; item = item->next) {
+ /* skip comment entry but don't lose it */
+ if (item->path == NULL) {
+ prev = item;
+ continue;
+ }
+ if (strcmp(item->origline, line) == 0) {
+ break;
+ }
+ prev = item;
+ }
+ if (item != NULL) {
+ if (item == prev) {
+ list = item->next; /* this must be the first one */
+ } else {
+ prev->next = item->next;
+ }
+ item->next = NULL;
+ dfs_free_list(item);
+ }
+ return (list);
+}
+
+/*
+ * adddfsentry(list, share, proto)
+ *
+ * Add an entry to the dfstab list for share (relative to proto). This
+ * is used to update dfstab for legacy purposes.
+ */
+
+static xfs_sharelist_t *
+adddfsentry(xfs_sharelist_t *list, sa_share_t share, char *proto)
+{
+ xfs_sharelist_t *item, *tmp;
+ sa_group_t parent;
+ char *groupname;
+
+ item = alloc_sharelist();
+ if (item != NULL) {
+ parent = sa_get_parent_group(share);
+ groupname = sa_get_group_attr(parent, "name");
+ if (strcmp(groupname, "default") == 0) {
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ }
+ item->path = sa_get_share_attr(share, "path");
+ item->resource = sa_get_share_attr(share, "resource");
+ item->group = groupname;
+ item->fstype = strdup(proto);
+ item->options = sa_proto_legacy_format(proto, share, 1);
+ if (item->options != NULL && strlen(item->options) == 0) {
+ free(item->options);
+ item->options = NULL;
+ }
+ item->description = sa_get_share_description(share);
+ if (item->description != NULL && strlen(item->description) == 0) {
+ sa_free_share_description(item->description);
+ item->description = NULL;
+ }
+ if (list == NULL) {
+ list = item;
+ } else {
+ for (tmp = list; tmp->next != NULL; tmp = tmp->next)
+ /* do nothing */;
+ tmp->next = item;
+ }
+ }
+ return (list);
+}
+
+/*
+ * outdfstab(dfstab, list)
+ *
+ * Output the list to dfstab making sure the file is truncated.
+ * Comments and errors are preserved.
+ */
+
+static void
+outdfstab(FILE *dfstab, xfs_sharelist_t *list)
+{
+ xfs_sharelist_t *item;
+
+ (void) ftruncate(fileno(dfstab), 0);
+
+ for (item = list; item != NULL; item = item->next) {
+ if (item->path != NULL) {
+ if (*item->path == '/')
+ (void) fprintf(dfstab, "share %s%s%s%s%s%s%s %s%s%s%s%s\n",
+ (item->fstype != NULL) ? "-F " : "",
+ (item->fstype != NULL) ? item->fstype : "",
+ (item->options != NULL) ? " -o " : "",
+ (item->options != NULL) ? item->options : "",
+ (item->description != NULL) ? " -d \"" : "",
+ (item->description != NULL) ?
+ item->description : "",
+ (item->description != NULL) ? "\"" : "",
+ item->path,
+ ((item->resource != NULL) ||
+ (item->group != NULL)) ? " " : "",
+ (item->resource != NULL) ? item->resource : "",
+ item->group != NULL ? "@" : "",
+ item->group != NULL ? item->group : "");
+ else
+ (void) fprintf(dfstab, "%s", item->origline);
+ } else {
+ if (item->description != NULL) {
+ (void) fprintf(dfstab, "%s", item->description);
+ } else {
+ (void) fprintf(dfstab, "%s", item->origline);
+ }
+ }
+ }
+}
+
+/*
+ * open_dfstab(file)
+ *
+ * Open the specified dfstab file. If the owner/group/perms are wrong,
+ * fix them.
+ */
+
+static FILE *
+open_dfstab(char *file)
+{
+ struct group *grp;
+ struct group group;
+ char *buff;
+ int grsize;
+ FILE *dfstab;
+
+ dfstab = fopen(file, "r+");
+ if (dfstab == NULL) {
+ dfstab = fopen(file, "w+");
+ }
+ if (dfstab != NULL) {
+ grsize = sysconf(_SC_GETGR_R_SIZE_MAX);
+ buff = malloc(grsize);
+ if (buff != NULL)
+ grp = getgrnam_r(SA_DEFAULT_FILE_GRP, &group, buff, grsize);
+ else
+ grp = getgrnam(SA_DEFAULT_FILE_GRP); /* take the risk */
+ (void) fchmod(fileno(dfstab), 0644);
+ (void) fchown(fileno(dfstab), 0,
+ grp != NULL ? grp->gr_gid : 3);
+ if (buff != NULL)
+ free(buff);
+ rewind(dfstab);
+ }
+ return (dfstab);
+}
+
+/*
+ * sa_comment_line(line, err)
+ *
+ * Add a comment to the dfstab file with err as a prefix to the
+ * original line.
+ */
+
+static void
+sa_comment_line(char *line, char *err)
+{
+ FILE *dfstab;
+ xfs_sharelist_t *list;
+ sigset_t old, new;
+
+ dfstab = open_dfstab(SA_LEGACY_DFSTAB);
+ if (dfstab != NULL) {
+ (void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ (void) lockf(fileno(dfstab), F_LOCK, 0);
+ list = getdfstab(dfstab);
+ rewind(dfstab);
+ (void) remdfsline(list, line);
+ outdfstab(dfstab, list);
+ (void) fprintf(dfstab, "# Error: %s: %s", err, line);
+ (void) fsync(fileno(dfstab));
+ (void) lockf(fileno(dfstab), F_ULOCK, 0);
+ (void) fclose(dfstab);
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ if (list != NULL)
+ dfs_free_list(list);
+ }
+}
+
+/*
+ * sa_delete_legacy(share)
+ *
+ * Delete the specified share from the legacy config file.
+ */
+
+int
+sa_delete_legacy(sa_share_t share)
+{
+ FILE *dfstab;
+ int err;
+ int ret = SA_OK;
+ xfs_sharelist_t *list;
+ char *path;
+ sa_optionset_t optionset;
+ sa_group_t parent;
+ sigset_t old, new;
+
+ dfstab = open_dfstab(SA_LEGACY_DFSTAB);
+ if (dfstab != NULL) {
+ (void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ path = sa_get_share_attr(share, "path");
+ parent = sa_get_parent_group(share);
+ if (parent != NULL) {
+ (void) lockf(fileno(dfstab), F_LOCK, 0);
+ list = getdfstab(dfstab);
+ rewind(dfstab);
+ for (optionset = sa_get_optionset(parent, NULL);
+ optionset != NULL;
+ optionset = sa_get_next_optionset(optionset)) {
+ char *proto = sa_get_optionset_attr(optionset, "type");
+ if (list != NULL && proto != NULL)
+ (void) remdfsentry(list, path, proto);
+ if (proto == NULL)
+ ret = SA_NO_MEMORY;
+ /*
+ * may want to only do the dfstab if this call
+ * returns NOT IMPLEMENTED but it shouldn't
+ * hurt.
+ */
+ if (ret == SA_OK) {
+ err = sa_proto_delete_legacy(proto, share);
+ if (err != SA_NOT_IMPLEMENTED)
+ ret = err;
+ }
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ }
+ outdfstab(dfstab, list);
+ if (list != NULL)
+ dfs_free_list(list);
+ (void) fflush(dfstab);
+ (void) lockf(fileno(dfstab), F_ULOCK, 0);
+ }
+ (void) fsync(fileno(dfstab));
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ (void) fclose(dfstab);
+ sa_free_attr_string(path);
+ } else {
+ if (errno == EACCES || errno == EPERM) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_update_legacy(share, proto)
+ *
+ * There is an assumption that dfstab will be the most common form of
+ * legacy configuration file for shares, but not the only one. Because
+ * of that, dfstab handling is done in the main code with calls to
+ * this function and protocol specific calls to deal with formating
+ * options into dfstab/share compatible syntax. Since not everything
+ * will be dfstab, there is a provision for calling a protocol
+ * specific plugin interface that allows the protocol plugin to do its
+ * own legacy files and skip the dfstab update.
+ */
+
+int
+sa_update_legacy(sa_share_t share, char *proto)
+{
+ FILE *dfstab;
+ int ret = SA_OK;
+ xfs_sharelist_t *list;
+ char *path;
+ sigset_t old, new;
+ char *persist;
+
+ ret = sa_proto_update_legacy(proto, share);
+ if (ret != SA_NOT_IMPLEMENTED)
+ return (ret);
+ /* do the dfstab format */
+ persist = sa_get_share_attr(share, "type");
+ /*
+ * only update if the share is not transient -- no share type
+ * set or the type is not "transient".
+ */
+ if (persist == NULL || strcmp(persist, "transient") != 0) {
+ dfstab = open_dfstab(SA_LEGACY_DFSTAB);
+ if (dfstab != NULL) {
+ (void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ path = sa_get_share_attr(share, "path");
+ (void) lockf(fileno(dfstab), F_LOCK, 0);
+ list = getdfstab(dfstab);
+ rewind(dfstab);
+ if (list != NULL)
+ list = remdfsentry(list, path, proto);
+ list = adddfsentry(list, share, proto);
+ outdfstab(dfstab, list);
+ (void) fflush(dfstab);
+ (void) lockf(fileno(dfstab), F_ULOCK, 0);
+ (void) fsync(fileno(dfstab));
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ (void) fclose(dfstab);
+ sa_free_attr_string(path);
+ if (list != NULL)
+ dfs_free_list(list);
+ } else {
+ if (errno == EACCES || errno == EPERM) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ }
+ if (persist != NULL)
+ sa_free_attr_string(persist);
+ return (ret);
+}
+
+/*
+ * sa_is_security(optname, proto)
+ *
+ * Check to see if optname is a security (named optionset) specific
+ * property for the specified protocol.
+ */
+
+int
+sa_is_security(char *optname, char *proto)
+{
+ int ret = 0;
+ if (proto != NULL)
+ ret = sa_proto_security_prop(proto, optname);
+ return (ret);
+}
+
+/*
+ * add_syntax_comment(root, line, err, todfstab)
+ *
+ * add a comment to the document indicating a syntax error. If
+ * todfstab is set, write it back to the dfstab file as well.
+ */
+
+static void
+add_syntax_comment(xmlNodePtr root, char *line, char *err, int todfstab)
+{
+ xmlNodePtr node;
+
+ node = xmlNewChild(root, NULL, (xmlChar *)"error", (xmlChar *)line);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)err);
+ }
+ if (todfstab)
+ sa_comment_line(line, err);
+}
+
+/*
+ * sa_is_share(object)
+ *
+ * returns true of the object is of type "share".
+ */
+
+int
+sa_is_share(void *object)
+{
+ if (object != NULL) {
+ if (strcmp((char *)((xmlNodePtr)object)->name, "share") == 0)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * _sa_remove_property(property)
+ *
+ * remove a property only from the document.
+ */
+
+static void
+_sa_remove_property(sa_property_t property)
+{
+ xmlUnlinkNode((xmlNodePtr)property);
+ xmlFreeNode((xmlNodePtr)property);
+}
+
+/*
+ * sa_parse_legacy_options(group, options, proto)
+ *
+ * In order to support legacy configurations, we allow the protocol
+ * specific plugin to parse legacy syntax options (like those in
+ * /etc/dfs/dfstab). This adds a new optionset to the group (or
+ * share).
+ *
+ * Once the optionset has been created, we then get the derived
+ * optionset of the parent (options from the optionset of the parent
+ * and any parent it might have) and remove those from the created
+ * optionset. This avoids duplication of options.
+ */
+
+int
+sa_parse_legacy_options(sa_group_t group, char *options, char *proto)
+{
+ int ret = SA_INVALID_PROTOCOL;
+ sa_group_t parent;
+ parent = sa_get_parent_group(group);
+
+ if (proto != NULL)
+ ret = sa_proto_legacy_opts(proto, group, options);
+ /*
+ * if in a group, remove the inherited options and security
+ */
+ if (ret == SA_OK) {
+ if (parent != NULL) {
+ sa_optionset_t optionset;
+ sa_property_t popt, prop;
+ sa_optionset_t localoptions;
+ /* find parent options to remove from child */
+ optionset = sa_get_derived_optionset(parent, proto, 1);
+ localoptions = sa_get_optionset(group, proto);
+ if (optionset != NULL) {
+ for (popt = sa_get_property(optionset, NULL);
+ popt != NULL;
+ popt = sa_get_next_property(popt)) {
+ char *tag;
+ char *value1;
+ char *value2;
+
+ tag = sa_get_property_attr(popt, "type");
+ if (tag != NULL) {
+ prop = sa_get_property(localoptions, tag);
+ if (prop != NULL) {
+ value1 = sa_get_property_attr(popt, "value");
+ value2 = sa_get_property_attr(prop, "value");
+ if (value1 != NULL && value2 != NULL &&
+ strcmp(value1, value2) == 0) {
+ /* remove the property from the child */
+ (void) _sa_remove_property(prop);
+ }
+ if (value1 != NULL)
+ sa_free_attr_string(value1);
+ if (value2 != NULL)
+ sa_free_attr_string(value2);
+ }
+ sa_free_attr_string(tag);
+ }
+ }
+ prop = sa_get_property(localoptions, NULL);
+ if (prop == NULL && sa_is_share(group)) {
+ /*
+ * all properties removed so remove the
+ * optionset if it is on a share
+ */
+ (void) _sa_remove_optionset(localoptions);
+ }
+ sa_free_derived_optionset(optionset);
+ }
+ /*
+ * need to remove security here. If there are no
+ * security options on the local group/share, don't
+ * bother since those are the only ones that would be
+ * affected.
+ */
+ localoptions = sa_get_all_security_types(group, proto, 0);
+ if (localoptions != NULL) {
+ for (prop = sa_get_property(localoptions, NULL);
+ prop != NULL; prop = sa_get_next_property(prop)) {
+ char *tag;
+ sa_security_t security;
+ tag = sa_get_property_attr(prop, "type");
+ if (tag != NULL) {
+ security = sa_get_security(group, tag, proto);
+ sa_free_attr_string(tag);
+ for (popt = sa_get_property(security, NULL);
+ popt != NULL;
+ popt = sa_get_next_property(popt)) {
+ char *value1;
+ char *value2;
+
+ /* remove duplicates from this level */
+ value1 = sa_get_property_attr(popt, "value");
+ value2 = sa_get_property_attr(prop, "value");
+ if (value1 != NULL && value2 != NULL &&
+ strcmp(value1, value2) == 0) {
+ /* remove the property from the child */
+ (void) _sa_remove_property(prop);
+ }
+ if (value1 != NULL)
+ sa_free_attr_string(value1);
+ if (value2 != NULL)
+ sa_free_attr_string(value2);
+ }
+ }
+ }
+ (void) sa_destroy_optionset(localoptions);
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * dfs_free_list(list)
+ *
+ * Free the data in each list entry of the list as well as freeing the
+ * entries themselves. We need to avoid memory leaks and don't want to
+ * dereference any NULL members.
+ */
+
+static void
+dfs_free_list(xfs_sharelist_t *list)
+{
+ xfs_sharelist_t *entry;
+ for (entry = list; entry != NULL; entry = list) {
+ if (entry->path != NULL)
+ free(entry->path);
+ if (entry->resource != NULL)
+ free(entry->resource);
+ if (entry->fstype != NULL)
+ free(entry->fstype);
+ if (entry->options != NULL)
+ free(entry->options);
+ if (entry->description != NULL)
+ free(entry->description);
+ if (entry->origline != NULL)
+ free(entry->origline);
+ if (entry->group != NULL)
+ free(entry->group);
+ list = list->next;
+ free(entry);
+ }
+}
+
+/*
+ * parse_dfstab(dfstab, root)
+ *
+ * Open and read the existing dfstab, parsing each line and adding it
+ * to the internal configuration. Make sure syntax errors, etc are
+ * preserved as comments.
+ */
+
+static void
+parse_dfstab(char *dfstab, xmlNodePtr root)
+{
+ sa_share_t share;
+ sa_group_t group;
+ sa_group_t sgroup = NULL;
+ sa_group_t defgroup;
+ xfs_sharelist_t *head, *list;
+ int err;
+ int defined_group;
+ FILE *dfs;
+ char *oldprops;
+
+ /* read the dfstab format file and fill in the doc tree */
+
+ dfs = fopen(dfstab, "r");
+ if (dfs == NULL) {
+ return;
+ }
+
+ defgroup = sa_get_group("default");
+
+ for (head = list = getdfstab(dfs);
+ list != NULL;
+ list = list->next) {
+ share = NULL;
+ group = NULL;
+ defined_group = 0;
+ err = 0;
+
+ if (list->origline == NULL) {
+ /*
+ * Comment line that we will likely skip.
+ * If the line has the syntax:
+ * # error: string: string
+ * It should be preserved until manually deleted.
+ */
+ if (list->description != NULL &&
+ strncmp(list->description, "# Error: ", 9) == 0) {
+ char *line;
+ char *error;
+ char *cmd;
+ line = strdup(list->description);
+ if (line != NULL) {
+ error = line + 9;
+ cmd = strchr(error, ':');
+ if (cmd != NULL) {
+ int len;
+ *cmd = '\0';
+ cmd += 2;
+ len = strlen(cmd);
+ cmd[len - 1] = '\0';
+ add_syntax_comment(root, cmd, error, 0);
+ }
+ free(line);
+ }
+ }
+ continue;
+ }
+ if (list->path != NULL && strlen(list->path) > 0 &&
+ *list->path == '/') {
+ share = sa_find_share(list->path);
+ if (share != NULL)
+ sgroup = sa_get_parent_group(share);
+ else
+ sgroup = NULL;
+ } else {
+ (void) printf(gettext("No share specified in dfstab: "
+ "line %d: %s\n"),
+ list->lineno, list->origline);
+ add_syntax_comment(root, list->origline,
+ gettext("No share specified"),
+ 1);
+ continue;
+ }
+ if (list->group != NULL && strlen(list->group) > 0) {
+ group = sa_get_group(list->group);
+ defined_group = 1;
+ } else {
+ group = defgroup;
+ }
+ if (defined_group && group == NULL) {
+ (void) printf(gettext("Unknown group used in dfstab: "
+ "line %d: %s\n"),
+ list->lineno, list->origline);
+ add_syntax_comment(root, list->origline,
+ gettext("Unknown group specified"), 1);
+ continue;
+ }
+ if (group != NULL) {
+ if (share == NULL) {
+ if (!defined_group && group == defgroup) {
+ /* this is an OK add for legacy */
+ share = sa_add_share(defgroup, list->path,
+ SA_SHARE_PERMANENT | SA_SHARE_PARSER,
+ &err);
+ if (share != NULL) {
+ if (list->description != NULL &&
+ strlen(list->description) > 0)
+ (void) sa_set_share_description(share,
+ list->description);
+ if (list->options != NULL &&
+ strlen(list->options) > 0) {
+ (void) sa_parse_legacy_options(share,
+ list->options,
+ list->fstype);
+ }
+ if (list->resource != NULL)
+ (void) sa_set_share_attr(share, "resource",
+ list->resource);
+ } else {
+ (void) printf(gettext("Error in dfstab: "
+ "line %d: %s\n"),
+ list->lineno, list->origline);
+ if (err != SA_BAD_PATH)
+ add_syntax_comment(root, list->origline,
+ gettext("Syntax"), 1);
+ else
+ add_syntax_comment(root, list->origline,
+ gettext("Path"), 1);
+ continue;
+ }
+ }
+ } else {
+ if (group != sgroup) {
+ (void) printf(gettext("Attempt to change"
+ "configuration in"
+ "dfstab: line %d: %s\n"),
+ list->lineno, list->origline);
+ add_syntax_comment(root, list->origline,
+ gettext("Attempt to change configuration"), 1);
+ continue;
+ }
+ /* its the same group but could have changed options */
+ oldprops = sa_proto_legacy_format(list->fstype, share, 0);
+ if (oldprops != NULL) {
+ if (list->options != NULL &&
+ strcmp(oldprops, list->options) != 0) {
+ sa_optionset_t opts;
+ sa_security_t secs;
+ /* possibly different values */
+ opts = sa_get_optionset((sa_group_t)share,
+ list->fstype);
+ (void) sa_destroy_optionset(opts);
+ for (secs = sa_get_security((sa_group_t)share,
+ NULL, list->fstype);
+ secs != NULL;
+ secs = sa_get_security((sa_group_t)share,
+ NULL, list->fstype)) {
+ (void) sa_destroy_security(secs);
+ }
+ (void) sa_parse_legacy_options(share,
+ list->options,
+ list->fstype);
+ }
+ }
+ }
+ } else {
+ /* shouldn't happen */
+ err = SA_CONFIG_ERR;
+ }
+
+ }
+ dfs_free_list(head);
+}
+
+/*
+ * legacy_removes(group, file)
+ *
+ * Find any shares that are "missing" from the legacy file. These
+ * should be removed from the configuration since they are likely from
+ * a legacy app or the admin modified the dfstab file directly. We
+ * have to support this even if it is not the recommended way to do
+ * things.
+ */
+
+static void
+legacy_removes(sa_group_t group, char *file)
+{
+ sa_share_t share;
+ char *path;
+ xfs_sharelist_t *list, *item;
+ FILE *dfstab;
+
+ dfstab = fopen(file, "r");
+ if (dfstab != NULL) {
+ list = getdfstab(dfstab);
+ (void) fclose(dfstab);
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ /* now see if the share is in the dfstab file */
+ path = sa_get_share_attr(share, "path");
+ if (path != NULL) {
+ item = finddfsentry(list, path);
+ if (item == NULL) {
+ /* the share was removed this way */
+ (void) sa_remove_share(share);
+ /* start over since the list was broken */
+ share = sa_get_share(group, NULL);
+ }
+ sa_free_attr_string(path);
+ }
+ }
+ if (list != NULL)
+ dfs_free_list(list);
+ }
+}
+
+/*
+ * getlegacyconfig(path, root)
+ *
+ * Parse dfstab and build the legacy configuration. This only gets
+ * called when a change was detected.
+ */
+
+void
+getlegacyconfig(char *path, xmlNodePtr *root)
+{
+ sa_group_t defgroup;
+
+ if (root != NULL) {
+ if (*root == NULL)
+ *root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
+ if (*root != NULL) {
+ if (strcmp(path, SA_LEGACY_DFSTAB) == 0) {
+ /*
+ * walk the default shares and find anything
+ * missing. we do this first to make sure it
+ * is cleaned up since there may be legacy
+ * code add/del via dfstab and we need to
+ * cleanup SMF.
+ */
+ defgroup = sa_get_group("default");
+ if (defgroup != NULL) {
+ legacy_removes(defgroup, path);
+ }
+ /* parse the dfstab and add anything new */
+ parse_dfstab(path, *root);
+ }
+ }
+ }
+}
+
+/*
+ * parse_sharetab(void)
+ *
+ * Read the /etc/dfs/sharetab file via libfsmgt and see which entries
+ * don't exist in the repository. These shares are marked transient.
+ * We also need to see if they are ZFS shares since ZFS bypasses the
+ * SMF repository.
+ */
+
+int
+parse_sharetab(void)
+{
+ fs_sharelist_t *list, *tmplist;
+ int err = 0;
+ sa_share_t share;
+ sa_group_t group;
+ sa_group_t lgroup;
+ char *groupname;
+ int legacy = 0;
+
+ list = fs_get_share_list(&err);
+ if (list == NULL)
+ return (legacy);
+
+ lgroup = sa_get_group("default");
+
+ for (tmplist = list; tmplist != NULL; tmplist = tmplist->next) {
+ group = NULL;
+ share = sa_find_share(tmplist->path);
+ if (share == NULL) {
+ /*
+ * this share is transient so needs to be
+ * added. Initially, this will be under
+ * default(legacy) unless it is a ZFS
+ * share. If zfs, we need a zfs group.
+ */
+ if (tmplist->resource != NULL &&
+ (groupname = strchr(tmplist->resource, '@')) != NULL) {
+ /* there is a defined group */
+ *groupname++ = '\0';
+ group = sa_get_group(groupname);
+ if (group != NULL) {
+ share = _sa_add_share(group, tmplist->path,
+ SA_SHARE_TRANSIENT, &err);
+ } else {
+ (void) printf(gettext("Group for temporary share"
+ "not found: %s\n"),
+ tmplist->path);
+ share = _sa_add_share(lgroup, tmplist->path,
+ SA_SHARE_TRANSIENT, &err);
+ }
+ } else {
+ if (sa_zfs_is_shared(tmplist->path)) {
+ group = sa_get_group("zfs");
+ if (group == NULL) {
+ group = sa_create_group("zfs", &err);
+ if (group == NULL && err == SA_NO_PERMISSION) {
+ group = _sa_create_group("zfs");
+ }
+ if (group != NULL) {
+ (void) sa_create_optionset(group,
+ tmplist->fstype);
+ (void) sa_set_group_attr(group, "zfs", "true");
+ }
+ }
+ if (group != NULL) {
+ share = _sa_add_share(group, tmplist->path,
+ SA_SHARE_TRANSIENT, &err);
+ }
+ } else {
+ share = _sa_add_share(lgroup, tmplist->path,
+ SA_SHARE_TRANSIENT, &err);
+ }
+ }
+ if (share == NULL)
+ (void) printf(gettext("Problem with transient: %s\n"),
+ sa_errorstr(err));
+ if (share != NULL)
+ set_node_attr(share, "shared", "true");
+
+ if (err == SA_OK) {
+ if (tmplist->options != NULL &&
+ strlen(tmplist->options) > 0) {
+ (void) sa_parse_legacy_options(share,
+ tmplist->options,
+ tmplist->fstype);
+ }
+ if (tmplist->resource != NULL &&
+ strcmp(tmplist->resource, "-") != 0)
+ set_node_attr(share, "resource", tmplist->resource);
+ if (tmplist->description != NULL) {
+ xmlNodePtr node;
+ node = xmlNewChild((xmlNodePtr)share, NULL,
+ (xmlChar *)"description", NULL);
+ xmlNodeSetContent(node,
+ (xmlChar *)tmplist->description);
+ }
+ legacy = 1;
+ }
+ } else {
+ /*
+ * if this is a legacy share, mark as shared so we
+ * only update sharetab appropriately.
+ */
+ set_node_attr(share, "shared", "true");
+ }
+ }
+ fs_free_share_list(list);
+ return (legacy);
+}
+
+/*
+ * get the transient shares from the sharetab (or other) file. since
+ * these are transient, they only appear in the working file and not
+ * in a repository.
+ */
+int
+gettransients(xmlNodePtr *root)
+{
+ int legacy = 0;
+
+ if (root != NULL) {
+ if (*root == NULL)
+ *root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
+ if (*root != NULL) {
+ legacy = parse_sharetab();
+ }
+ }
+ return (legacy);
+}
+
+/*
+ * sa_has_prop(optionset, prop)
+ *
+ * Is the specified property a member of the optionset?
+ */
+
+int
+sa_has_prop(sa_optionset_t optionset, sa_property_t prop)
+{
+ char *name;
+ sa_property_t otherprop;
+ int result = 0;
+
+ if (optionset != NULL) {
+ name = sa_get_property_attr(prop, "type");
+ if (name != NULL) {
+ otherprop = sa_get_property(optionset, name);
+ if (otherprop != NULL)
+ result = 1;
+ sa_free_attr_string(name);
+ }
+ }
+ return (result);
+}
+
+/*
+ * Update legacy files
+ *
+ * Provides functions to add/remove/modify individual entries
+ * in dfstab and sharetab
+ */
+
+void
+update_legacy_config(void)
+{
+ /*
+ * no longer used -- this is a placeholder in case we need to
+ * add it back later.
+ */
+}
+
+/*
+ * sa_valid_property(object, proto, property)
+ *
+ * check to see if the specified property is valid relative to the
+ * specified protocol. The protocol plugin is called to do the work.
+ */
+
+int
+sa_valid_property(void *object, char *proto, sa_property_t property)
+{
+ int ret = SA_OK;
+
+ if (proto != NULL && property != NULL) {
+ ret = sa_proto_valid_prop(proto, property, object);
+ }
+
+ return (ret);
+}
+
+/*
+ * sa_fstype(path)
+ *
+ * Given path, return the string representing the path's file system
+ * type. This is used to discover ZFS shares.
+ */
+
+char *
+sa_fstype(char *path)
+{
+ int err;
+ struct stat st;
+
+ err = stat(path, &st);
+ if (err < 0) {
+ err = SA_NO_SUCH_PATH;
+ } else {
+ err = SA_OK;
+ }
+ if (err == SA_OK) {
+ /* have a valid path at this point */
+ return (strdup(st.st_fstype));
+ }
+ return (NULL);
+}
+
+void
+sa_free_fstype(char *type)
+{
+ free(type);
+}
+
+/*
+ * sa_get_derived_optionset(object, proto, hier)
+ *
+ * Work backward to the top of the share object tree and start
+ * copying protocol specific optionsets into a newly created
+ * optionset that doesn't have a parent (it will be freed
+ * later). This provides for the property inheritence model. That
+ * is, properties closer to the share take precedence over group
+ * level. This also provides for groups of groups in the future.
+ */
+
+sa_optionset_t
+sa_get_derived_optionset(void *object, char *proto, int hier)
+{
+ sa_optionset_t newoptionset;
+ sa_optionset_t optionset;
+ sa_group_t group;
+
+ if (hier &&
+ (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
+ newoptionset = sa_get_derived_optionset((void *)group, proto, hier);
+ } else {
+ newoptionset = (sa_optionset_t)xmlNewNode(NULL,
+ (xmlChar *)"optionset");
+ if (newoptionset != NULL) {
+ sa_set_optionset_attr(newoptionset, "type", proto);
+ }
+ }
+ /* dont' do anything if memory wasn't allocated */
+ if (newoptionset == NULL)
+ return (NULL);
+
+ /* found the top so working back down the stack */
+ optionset = sa_get_optionset((sa_optionset_t)object, proto);
+ if (optionset != NULL) {
+ sa_property_t prop;
+ /* add optionset to the newoptionset */
+ for (prop = sa_get_property(optionset, NULL);
+ prop != NULL; prop = sa_get_next_property(prop)) {
+ sa_property_t newprop;
+ char *name;
+ char *value;
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (name != NULL) {
+ newprop = sa_get_property(newoptionset, name);
+ /* replace the value with the new value */
+ if (newprop != NULL) {
+ /*
+ * only set if value is non NULL, old value ok
+ * if it is NULL.
+ */
+ if (value != NULL)
+ set_node_attr(newprop, "value", value);
+ } else {
+ /* an entirely new property */
+ if (value != NULL) {
+ newprop = sa_create_property(name, value);
+ if (newprop != NULL) {
+ newprop = (sa_property_t)
+ xmlAddChild((xmlNodePtr)newoptionset,
+ (xmlNodePtr)newprop);
+ }
+ }
+ }
+ sa_free_attr_string(name);
+ }
+ if (value != NULL)
+ sa_free_attr_string(value);
+ }
+ }
+ return (newoptionset);
+}
+
+void
+sa_free_derived_optionset(sa_optionset_t optionset)
+{
+ /* while it shouldn't be linked, it doesn't hurt */
+ if (optionset != NULL) {
+ xmlUnlinkNode((xmlNodePtr) optionset);
+ xmlFreeNode((xmlNodePtr) optionset);
+ }
+}
+
+/*
+ * sa_get_all_security_types(object, proto, hier)
+ *
+ * find all the security types set for this object. This is
+ * preliminary to getting a derived security set. The return value is an
+ * optionset containg properties which are the sectype values found by
+ * walking up the XML document struture. The returned optionset
+ * is a derived optionset.
+ *
+ * If hier is 0, only look at object. If non-zero, walk up the tree.
+ */
+sa_optionset_t
+sa_get_all_security_types(void *object, char *proto, int hier)
+{
+ sa_optionset_t options;
+ sa_security_t security;
+ sa_group_t group;
+ sa_property_t prop;
+
+ options = NULL;
+
+ if (hier &&
+ (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
+ options = sa_get_all_security_types((void *)group, proto, hier);
+ } else {
+ options = (sa_optionset_t)xmlNewNode(NULL,
+ (xmlChar *)"optionset");
+ }
+ /* hit the top so collect the security types working back */
+ if (options != NULL) {
+ for (security = sa_get_security((sa_group_t)object, NULL, NULL);
+ security != NULL; security = sa_get_next_security(security)) {
+ char *type;
+ char *sectype;
+
+ type = sa_get_security_attr(security, "type");
+ if (type != NULL) {
+ if (strcmp(type, proto) != 0) {
+ sa_free_attr_string(type);
+ continue;
+ }
+ sectype = sa_get_security_attr(security, "sectype");
+ if (sectype != NULL) {
+ /*
+ * have a security type, check to see if
+ * already present in optionset and add if it
+ * isn't.
+ */
+ if (sa_get_property(options, sectype) == NULL) {
+ prop = sa_create_property(sectype, "true");
+ if (prop != NULL)
+ prop = (sa_property_t)
+ xmlAddChild((xmlNodePtr)options,
+ (xmlNodePtr)prop);
+ }
+ sa_free_attr_string(sectype);
+ }
+ sa_free_attr_string(type);
+ }
+ }
+ }
+ return (options);
+}
+
+/*
+ * sa_get_derived_security(object, sectype, proto, hier)
+ *
+ * Get the derived security(named optionset) for the object given the
+ * sectype and proto. If hier is non-zero, walk up the tree to get all
+ * properties defined for this object, otherwise just those on the
+ * object.
+ */
+
+sa_security_t
+sa_get_derived_security(void *object, char *sectype, char *proto, int hier)
+{
+ sa_security_t newsecurity;
+ sa_security_t security;
+ sa_group_t group;
+
+ if (hier &&
+ (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
+ newsecurity = sa_get_derived_security((void *)group,
+ sectype, proto, hier);
+ } else {
+ newsecurity = (sa_security_t)xmlNewNode(NULL,
+ (xmlChar *)"security");
+ if (newsecurity != NULL) {
+ sa_set_security_attr(newsecurity, "type", proto);
+ sa_set_security_attr(newsecurity, "sectype", sectype);
+ }
+ }
+ /* dont' do anything if memory wasn't allocated */
+ if (newsecurity == NULL)
+ return (NULL);
+
+ /* found the top so working back down the stack */
+ security = sa_get_security((sa_security_t)object, sectype, proto);
+ if (security != NULL) {
+ sa_property_t prop;
+ /* add security to the newsecurity */
+ for (prop = sa_get_property(security, NULL);
+ prop != NULL; prop = sa_get_next_property(prop)) {
+ sa_property_t newprop;
+ char *name;
+ char *value;
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (name != NULL) {
+ newprop = sa_get_property(newsecurity, name);
+ /* replace the value with the new value */
+ if (newprop != NULL) {
+ /*
+ * only set if value is non NULL, old value ok
+ * if it is NULL.
+ */
+ if (value != NULL)
+ set_node_attr(newprop, name, value);
+ } else {
+ /* an entirely new property */
+ if (value != NULL) {
+ newprop = sa_create_property(name, value);
+ newprop = (sa_property_t)
+ xmlAddChild((xmlNodePtr)newsecurity,
+ (xmlNodePtr)newprop);
+ }
+ }
+ sa_free_attr_string(name);
+ }
+ if (value != NULL)
+ sa_free_attr_string(value);
+ }
+ }
+ return (newsecurity);
+}
+
+void
+sa_free_derived_security(sa_security_t security)
+{
+ /* while it shouldn't be linked, it doesn't hurt */
+ if (security != NULL) {
+ xmlUnlinkNode((xmlNodePtr)security);
+ xmlFreeNode((xmlNodePtr)security);
+ }
+}
+
+/*
+ * sharetab utility functions
+ *
+ * makes use of the original sharetab.c from fs.d/nfs/lib
+ */
+
+/*
+ * fillshare(share, proto, sh)
+ *
+ * Fill the struct share with values obtained from the share object.
+ */
+static void
+fillshare(sa_share_t share, char *proto, struct share *sh)
+{
+ char *groupname = NULL;
+ char *value;
+ sa_group_t group;
+ char *buff;
+ char *zfs;
+
+ group = sa_get_parent_group(share);
+ if (group != NULL) {
+ zfs = sa_get_group_attr(group, "zfs");
+ groupname = sa_get_group_attr(group, "name");
+
+ if (groupname != NULL &&
+ (strcmp(groupname, "default") == 0 || zfs != NULL)) {
+ /*
+ * since the groupname is either "default" or the
+ * group is a ZFS group, we don't want to keep
+ * groupname. We do want it if it is any other type of
+ * group.
+ */
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ }
+ if (zfs != NULL)
+ sa_free_attr_string(zfs);
+ }
+
+ value = sa_get_share_attr(share, "path");
+ if (value != NULL) {
+ sh->sh_path = strdup(value);
+ sa_free_attr_string(value);
+ }
+
+ value = sa_get_share_attr(share, "resource");
+ if (value != NULL || groupname != NULL) {
+ int len = 0;
+
+ if (value != NULL)
+ len += strlen(value);
+ if (groupname != NULL)
+ len += strlen(groupname);
+ len += 3; /* worst case */
+ buff = malloc(len);
+ (void) snprintf(buff, len, "%s%s%s",
+ (value != NULL && strlen(value) > 0) ? value : "-",
+ groupname != NULL ? "@" : "",
+ groupname != NULL ? groupname : "");
+ sh->sh_res = buff;
+ if (value != NULL)
+ sa_free_attr_string(value);
+ if (groupname != NULL) {
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ }
+ } else {
+ sh->sh_res = strdup("-");
+ }
+
+ sh->sh_fstype = strdup(proto);
+ value = sa_proto_legacy_format(proto, share, 1);
+ if (value != NULL) {
+ if (strlen(value) > 0)
+ sh->sh_opts = strdup(value);
+ else
+ sh->sh_opts = strdup("rw");
+ free(value);
+ } else
+ sh->sh_opts = strdup("rw");
+
+ value = sa_get_share_description(share);
+ if (value != NULL) {
+ sh->sh_descr = strdup(value);
+ sa_free_share_description(value);
+ } else
+ sh->sh_descr = strdup("");
+}
+
+/*
+ * emptyshare(sh)
+ *
+ * Free the strings in the non-NULL members of sh.
+ */
+
+static void
+emptyshare(struct share *sh)
+{
+ if (sh->sh_path != NULL)
+ free(sh->sh_path);
+ sh->sh_path = NULL;
+ if (sh->sh_res != NULL)
+ free(sh->sh_res);
+ sh->sh_res = NULL;
+ if (sh->sh_fstype != NULL)
+ free(sh->sh_fstype);
+ sh->sh_fstype = NULL;
+ if (sh->sh_opts != NULL)
+ free(sh->sh_opts);
+ sh->sh_opts = NULL;
+ if (sh->sh_descr != NULL)
+ free(sh->sh_descr);
+ sh->sh_descr = NULL;
+}
+
+/*
+ * sa_update_sharetab(share, proto)
+ *
+ * Update the sharetab file with info from the specified share.
+ * This could be an update or add.
+ */
+
+int
+sa_update_sharetab(sa_share_t share, char *proto)
+{
+ int ret = SA_OK;
+ struct share shtab;
+ char *path;
+ int logging = 0;
+ FILE *sharetab;
+ sigset_t old, new;
+
+ path = sa_get_share_attr(share, "path");
+ if (path != NULL) {
+ (void) memset(&shtab, '\0', sizeof (shtab));
+ sharetab = fopen(SA_LEGACY_SHARETAB, "r+");
+ if (sharetab == NULL) {
+ sharetab = fopen(SA_LEGACY_SHARETAB, "w+");
+ }
+ if (sharetab != NULL) {
+ (void) setvbuf(sharetab, NULL, _IOLBF, BUFSIZ * 8);
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ (void) lockf(fileno(sharetab), F_LOCK, 0);
+ (void) remshare(sharetab, path, &logging);
+ /* fill in share structure and write it out */
+ (void) fillshare(share, proto, &shtab);
+ (void) putshare(sharetab, &shtab);
+ emptyshare(&shtab);
+ (void) fflush(sharetab);
+ (void) lockf(fileno(sharetab), F_ULOCK, 0);
+ (void) fsync(fileno(sharetab));
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ (void) fclose(sharetab);
+ } else {
+ if (errno == EACCES || errno == EPERM) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ sa_free_attr_string(path);
+ }
+ return (ret);
+}
+
+/*
+ * sa_delete_sharetab(path, proto)
+ *
+ * remove the specified share from sharetab.
+ */
+
+int
+sa_delete_sharetab(char *path, char *proto)
+{
+ int ret = SA_OK;
+ int logging = 0;
+ FILE *sharetab;
+ sigset_t old, new;
+#ifdef lint
+ proto = proto;
+#endif
+
+ if (path != NULL) {
+ sharetab = fopen(SA_LEGACY_SHARETAB, "r+");
+ if (sharetab == NULL) {
+ sharetab = fopen(SA_LEGACY_SHARETAB, "w+");
+ }
+ if (sharetab != NULL) {
+ /* should block keyboard level signals around the lock */
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ (void) lockf(fileno(sharetab), F_LOCK, 0);
+ ret = remshare(sharetab, path, &logging);
+ (void) fflush(sharetab);
+ (void) lockf(fileno(sharetab), F_ULOCK, 0);
+ (void) fsync(fileno(sharetab));
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ (void) fclose(sharetab);
+ } else {
+ if (errno == EACCES || errno == EPERM) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ }
+ return (ret);
+}
diff --git a/usr/src/lib/libshare/common/llib-lshare b/usr/src/lib/libshare/common/llib-lshare
new file mode 100644
index 0000000000..d426f1bf58
--- /dev/null
+++ b/usr/src/lib/libshare/common/llib-lshare
@@ -0,0 +1,32 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include "libshare.h"
diff --git a/usr/src/lib/libshare/common/mapfile-vers b/usr/src/lib/libshare/common/mapfile-vers
new file mode 100644
index 0000000000..9585f9a386
--- /dev/null
+++ b/usr/src/lib/libshare/common/mapfile-vers
@@ -0,0 +1,110 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+SUNWprivate_1.1 {
+ global:
+ sa_get_optionset;
+ sa_create_property;
+ sa_get_property;
+ sa_create_security;
+ sa_update_legacy;
+ sa_get_group_attr;
+ sa_free_share_description;
+ sa_remove_group;
+ sa_add_property;
+ sa_get_sub_group;
+ sa_get_next_optionset;
+ sa_update_property;
+ sa_destroy_security;
+ sa_update_sharetab;
+ sa_create_protocol_properties;
+ sa_get_group;
+ sa_get_security;
+ sa_get_protocol_property;
+ sa_add_share;
+ sa_valid_group_name;
+ sa_get_optionset_attr;
+ sa_remove_property;
+ sa_set_security_attr;
+ sa_delete_sharetab;
+ sa_format_free;
+ sa_add_protocol_property;
+ sa_check_path;
+ sa_free_derived_optionset;
+ sa_is_security;
+ sa_get_protocols;
+ sa_get_parent_group;
+ sa_set_optionset_attr;
+ sa_commit_properties;
+ sa_parse_legacy_options;
+ sa_zfs_is_shared;
+ sa_get_derived_optionset;
+ sa_move_share;
+ sa_group_is_zfs;
+ sa_update_config;
+ sa_get_share_attr;
+ sa_create_optionset;
+ sa_valid_property;
+ sa_proto_legacy_format;
+ sa_proto_valid_space;
+ sa_proto_space_alias;
+ sa_get_next_protocol_property;
+ sa_remove_share;
+ sa_is_share;
+ sa_get_share_description;
+ sa_get_share;
+ sa_get_next_group;
+ sa_set_share_attr;
+ sa_destroy_optionset;
+ sa_get_derived_security;
+ sa_proto_get_properties;
+ sa_get_all_security_types;
+ sa_get_resource;
+ sa_set_share_description;
+ sa_set_group_attr;
+ sa_disable_share;
+ sa_get_next_property;
+ sa_get_next_security;
+ sa_get_protocol_status;
+ sa_init;
+ sa_find_share;
+ sa_set_protocol_property;
+ sa_fini;
+ sa_errorstr;
+ sa_get_next_share;
+ sa_free_attr_string;
+ sa_get_property_attr;
+ sa_get_security_attr;
+ sa_delete_legacy;
+ sa_free_derived_security;
+ sa_enable_share;
+ sa_create_group;
+ sa_valid_protocol;
+ local:
+ *;
+};
+
diff --git a/usr/src/lib/libshare/common/parser.c b/usr/src/lib/libshare/common/parser.c
new file mode 100644
index 0000000000..3bd8f9f303
--- /dev/null
+++ b/usr/src/lib/libshare/common/parser.c
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#define TK_INIT 0
+#define TK_TOKEN 1
+#define TK_SKIPWHITE 2
+#define TK_QUOTED 3
+
+/*
+ * assumes quoted strings are delimited by white space (i.e sp
+ * "string" sp). Backslash can be used to quote a quote mark.
+ * quoted strings will have the quotes stripped.
+ */
+
+char *
+get_token(char *string)
+{
+ static char *orig = NULL;
+ static char *curp;
+ char *ret;
+ int state = TK_INIT;
+ int c;
+ int quotechar;
+
+ if (string != orig || string == NULL) {
+ orig = string;
+ curp = string;
+ if (string == NULL) {
+ return (NULL);
+ }
+ }
+ ret = curp;
+ while ((c = *curp) != '\0') {
+ switch (state) {
+ case TK_SKIPWHITE:
+ case TK_INIT:
+ if (isspace(c)) {
+ while (*curp && isspace(*curp))
+ curp++;
+ ret = curp;
+ }
+ if (c == '"' || c == '\'') {
+ state = TK_QUOTED;
+ curp++;
+ ret = curp;
+ quotechar = c; /* want to match for close */
+ } else {
+ state = TK_TOKEN;
+ }
+ break;
+ case TK_TOKEN:
+ switch (c) {
+ case '\\':
+ curp++;
+ if (*curp) {
+ curp++;
+ break;
+ } else {
+ return (ret);
+ }
+ break;
+ default:
+ if (*curp == '\0' || isspace(c)) {
+ *curp++ = '\0';
+ return (ret);
+ }
+ curp++;
+ break;
+ }
+ break;
+ case TK_QUOTED:
+ switch (c) {
+ case '\\':
+ curp++;
+ if (*curp) {
+ curp++;
+ break;
+ }
+ curp++;
+ break;
+ default:
+ if (c == '\0' || c == quotechar) {
+ *curp++ = '\0';
+ return (ret);
+ }
+ curp++;
+ break;
+ }
+ break;
+ }
+ }
+ return (NULL);
+}
diff --git a/usr/src/lib/libshare/common/plugin.c b/usr/src/lib/libshare/common/plugin.c
new file mode 100644
index 0000000000..54b95d8fbb
--- /dev/null
+++ b/usr/src/lib/libshare/common/plugin.c
@@ -0,0 +1,458 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libshare.h>
+#include "libshare_impl.h"
+#include <dlfcn.h>
+#include <link.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <libintl.h>
+
+/*
+ * protocol plugin interface
+ *
+ * finds plugins and makes them accessible. This is only "used" by
+ * libshare.so.
+ */
+
+struct sa_proto_plugin *sap_proto_list;
+
+static struct sa_proto_handle sa_proto_handle;
+
+void proto_plugin_fini();
+
+/*
+ * proto_plugin_init()
+ *
+ * Initialize the protocol specific plugin modules.
+ *
+ * Walk /usr/lib/fs/\* for libshare_*.so modules. That is,
+ * /usr/lib/fs/nfs/libshare_nfs.so. The protocol specific directory
+ * would have a modules with name libshare_<proto>.so. If one is
+ * found, initialize it and add to the internal list of
+ * protocols. These are used for protocol specifici operations.
+ */
+
+int
+proto_plugin_init()
+{
+ struct sa_proto_plugin *proto;
+ int num_protos = 0;
+ int err;
+ struct sa_plugin_ops *plugin_ops;
+ void *dlhandle;
+ DIR *dir;
+ struct dirent *dent;
+ int ret = SA_OK;
+ struct stat st;
+
+ /*
+ * should walk "/usr/lib/fs/" for files of the form:
+ * libshare_*.so
+ */
+ dir = opendir(SA_LIB_DIR);
+ if (dir != NULL) {
+ while (ret == SA_OK && (dent = readdir(dir)) != NULL) {
+ char path[MAXPATHLEN];
+ (void) snprintf(path, MAXPATHLEN,
+ "%s/%s/libshare_%s.so", SA_LIB_DIR,
+ dent->d_name, dent->d_name);
+ if (stat(path, &st) < 0) {
+ /* file doesn't exist, so don't try to map it */
+ continue;
+ }
+ dlhandle = dlopen(path, RTLD_NOW|RTLD_GLOBAL|RTLD_WORLD);
+ if (dlhandle != NULL) {
+ plugin_ops = (struct sa_plugin_ops *)
+ dlsym(dlhandle, "sa_plugin_ops");
+ proto = (struct sa_proto_plugin *)
+ calloc(1, sizeof (struct sa_proto_plugin));
+ if (proto != NULL) {
+ proto->plugin_ops = plugin_ops;
+ proto->plugin_handle = dlhandle;
+ num_protos++;
+ proto->plugin_next = sap_proto_list;
+ sap_proto_list = proto;
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error in plugin for protocol %s: %s\n"),
+ dent->d_name, dlerror());
+ }
+ }
+ (void) closedir(dir);
+ }
+ if (ret == SA_OK) {
+ sa_proto_handle.sa_proto =
+ (char **)calloc(num_protos, sizeof (char *));
+ sa_proto_handle.sa_ops =
+ (struct sa_plugin_ops **)calloc(num_protos,
+ sizeof (struct sa_plugin_ops *));
+ if (sa_proto_handle.sa_proto != NULL &&
+ sa_proto_handle.sa_ops != NULL) {
+ int i;
+ struct sa_proto_plugin *tmp;
+ for (i = 0, tmp = sap_proto_list; i < num_protos;
+ tmp = tmp->plugin_next) {
+ err = 0;
+ if (tmp->plugin_ops->sa_init != NULL)
+ err = tmp->plugin_ops->sa_init();
+ if (err == SA_OK) {
+ /* only include if the init succeeded or was NULL */
+ sa_proto_handle.sa_num_proto++;
+ sa_proto_handle.sa_ops[i] = tmp->plugin_ops;
+ sa_proto_handle.sa_proto[i] =
+ tmp->plugin_ops->sa_protocol;
+ i++;
+ }
+ }
+ }
+ } else {
+ /* there was an error, so cleanup prior to return of failure. */
+ proto_plugin_fini();
+ }
+ return (ret);
+}
+
+/*
+ * proto_plugin_fini()
+ *
+ * uninitialize all the plugin modules.
+ */
+
+void
+proto_plugin_fini()
+{
+ /*
+ * free up all the protocols, calling their fini, if there is
+ * one.
+ */
+ while (sap_proto_list != NULL) {
+ struct sa_proto_plugin *next;
+ next = sap_proto_list->plugin_next;
+ sap_proto_list->plugin_ops->sa_fini();
+ if (sap_proto_list->plugin_handle != NULL)
+ (void) dlclose(sap_proto_list->plugin_handle);
+ free(sap_proto_list);
+ sap_proto_list = next;
+ }
+ if (sa_proto_handle.sa_ops != NULL) {
+ free(sa_proto_handle.sa_ops);
+ sa_proto_handle.sa_ops = NULL;
+ }
+ if (sa_proto_handle.sa_proto != NULL) {
+ free(sa_proto_handle.sa_proto);
+ sa_proto_handle.sa_proto = NULL;
+ }
+ sa_proto_handle.sa_num_proto = 0;
+}
+
+/*
+ * find_protocol(proto)
+ *
+ * Search the plugin list for the specified protocol and return the
+ * ops vector. NULL if protocol is not defined.
+ */
+
+static struct sa_plugin_ops *
+find_protocol(char *proto)
+{
+ int i;
+
+ if (proto != NULL) {
+ for (i = 0; i < sa_proto_handle.sa_num_proto; i++) {
+ if (strcmp(proto, sa_proto_handle.sa_proto[i]) == 0)
+ return (sa_proto_handle.sa_ops[i]);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * sa_proto_share(proto, share)
+ *
+ * Activate a share for the specified protocol.
+ */
+
+int
+sa_proto_share(char *proto, sa_share_t share)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_INVALID_PROTOCOL;
+
+ if (ops != NULL && ops->sa_share != NULL)
+ ret = ops->sa_share(share);
+ return (ret);
+}
+
+/*
+ * sa_proto_unshare(proto, path)
+ *
+ * Deactivate (unshare) the path for this protocol.
+ */
+
+int
+sa_proto_unshare(char *proto, char *path)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_INVALID_PROTOCOL;
+
+ if (ops != NULL && ops->sa_unshare != NULL)
+ ret = ops->sa_unshare(path);
+ return (ret);
+}
+
+/*
+ * sa_proto_valid_prop(proto, prop, opt)
+ *
+ * check to see if the specified prop is valid for this protocol.
+ */
+
+int
+sa_proto_valid_prop(char *proto, sa_property_t prop, sa_optionset_t opt)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = 0;
+
+ if (ops != NULL && ops->sa_valid_prop != NULL)
+ ret = ops->sa_valid_prop(prop, opt);
+ return (ret);
+}
+
+/*
+ * sa_proto_valid_space(proto, space)
+ *
+ * check if space is valid optionspace for proto.
+ * Protocols that don't implement this don't support spaces.
+ */
+int
+sa_proto_valid_space(char *proto, char *token)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = 0;
+
+ if (ops != NULL && ops->sa_valid_space != NULL)
+ ret = ops->sa_valid_space(token);
+ return (ret);
+}
+
+/*
+ * sa_proto_space_alias(proto, space)
+ *
+ * if the name for space is an alias, return its proper name. This is
+ * used to translate "default" values into proper form.
+ */
+char *
+sa_proto_space_alias(char *proto, char *space)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ char *ret = space;
+
+ if (ops != NULL && ops->sa_space_alias != NULL)
+ ret = ops->sa_space_alias(space);
+ return (ret);
+}
+
+/*
+ * sa_proto_security_prop(proto, token)
+ *
+ * Check to see if the property name in token is a valid named
+ * optionset property.
+ */
+
+int
+sa_proto_security_prop(char *proto, char *token)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = 0;
+
+ if (ops != NULL && ops->sa_security_prop != NULL)
+ ret = ops->sa_security_prop(token);
+ return (ret);
+}
+
+/*
+ * sa_proto_legacy_opts(proto, grouup, options)
+ *
+ * Have the protocol specific parser parse the options string and add
+ * an appropriate optionset to group.
+ */
+
+int
+sa_proto_legacy_opts(char *proto, sa_group_t group, char *options)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_INVALID_PROTOCOL;
+
+ if (ops != NULL && ops->sa_legacy_opts != NULL)
+ ret = ops->sa_legacy_opts(group, options);
+ return (ret);
+}
+
+/*
+ * sa_proto_legacy_format(proto, group, hier)
+ *
+ * Return a legacy format string representing either the group's
+ * properties or the groups hierarchical properties.
+ */
+
+char *
+sa_proto_legacy_format(char *proto, sa_group_t group, int hier)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ char *ret = NULL;
+
+ if (ops != NULL && ops->sa_legacy_format != NULL)
+ ret = ops->sa_legacy_format(group, hier);
+ return (ret);
+}
+
+void
+sa_format_free(char *str)
+{
+ free(str);
+}
+
+/*
+ * sharectl related API functions
+ */
+
+/*
+ * sa_proto_get_properties(proto)
+ *
+ * Return the set of properties that are specific to the
+ * protocol. These are usually in /etc/dfs/<proto> and related files,
+ * but only the protocol module knows which ones for sure.
+ */
+
+sa_protocol_properties_t
+sa_proto_get_properties(char *proto)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ sa_protocol_properties_t props = NULL;
+
+ if (ops != NULL && ops->sa_get_proto_set != NULL)
+ props = ops->sa_get_proto_set();
+ return (props);
+}
+
+/*
+ * sa_proto_set_property(proto, prop)
+ *
+ * Update the protocol specifiec property.
+ */
+
+int
+sa_proto_set_property(char *proto, sa_property_t prop)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_OK;
+ if (ops != NULL && ops->sa_set_proto_prop != NULL)
+ ret = ops->sa_set_proto_prop(prop);
+ return (ret);
+}
+
+/*
+ * sa_valid_protocol(proto)
+ *
+ * check to see if the protocol specified is defined by a
+ * plugin. Returns true (1) or false (0)
+ */
+
+int
+sa_valid_protocol(char *proto)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ return (ops != NULL);
+}
+
+/*
+ * Return the current operational status of the protocol
+ */
+
+char *
+sa_get_protocol_status(char *proto)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ char *ret = NULL;
+ if (ops != NULL && ops->sa_get_proto_status != NULL)
+ ret = ops->sa_get_proto_status(proto);
+ return (ret);
+}
+
+/*
+ * sa_proto_update_legacy(proto, share)
+ *
+ * Update the protocol specific legacy files if necessary for the
+ * specified share.
+ */
+
+int
+sa_proto_update_legacy(char *proto, sa_share_t share)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_NOT_IMPLEMENTED;
+
+ if (ops != NULL) {
+ if (ops->sa_update_legacy != NULL)
+ ret = ops->sa_update_legacy(share);
+ }
+ return (ret);
+}
+
+/*
+ * sa_delete_legacy(proto, share)
+ *
+ * remove the specified share from the protocol specific legacy files.
+ */
+
+int
+sa_proto_delete_legacy(char *proto, sa_share_t share)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_OK;
+
+ if (ops != NULL) {
+ if (ops->sa_delete_legacy != NULL)
+ ret = ops->sa_delete_legacy(share);
+ } else {
+ if (proto != NULL)
+ ret = SA_NOT_IMPLEMENTED;
+ else
+ ret = SA_INVALID_PROTOCOL;
+ }
+ return (ret);
+}
diff --git a/usr/src/lib/libshare/common/scfutil.c b/usr/src/lib/libshare/common/scfutil.c
new file mode 100644
index 0000000000..88447471a6
--- /dev/null
+++ b/usr/src/lib/libshare/common/scfutil.c
@@ -0,0 +1,1443 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* helper functions for using libscf with sharemgr */
+
+#include <libscf.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include "libshare.h"
+#include "libshare_impl.h"
+#include "scfutil.h"
+#include <string.h>
+#include <errno.h>
+#include <uuid/uuid.h>
+#include <sys/param.h>
+
+ssize_t scf_max_name_len;
+extern struct sa_proto_plugin *sap_proto_list;
+
+/*
+ * The SMF facility uses some properties that must exist. We want to
+ * skip over these when processing protocol options.
+ */
+static char *skip_props[] = {
+ "modify_authorization",
+ "action_authorization",
+ "value_authorization",
+ NULL
+};
+
+/*
+ * sa_scf_fini(handle)
+ *
+ * must be called when done. Called with the handle allocated in
+ * sa_scf_init(), it cleans up the state and frees any SCF resources
+ * still in use. Called by sa_fini().
+ */
+
+void
+sa_scf_fini(scfutilhandle_t *handle)
+{
+ if (handle != NULL) {
+ int unbind = 0;
+ if (handle->scope != NULL) {
+ unbind = 1;
+ scf_scope_destroy(handle->scope);
+ }
+ if (handle->service != NULL)
+ scf_service_destroy(handle->service);
+ if (handle->pg != NULL)
+ scf_pg_destroy(handle->pg);
+ if (handle->handle != NULL) {
+ handle->scf_state = SCH_STATE_UNINIT;
+ if (unbind)
+ (void) scf_handle_unbind(handle->handle);
+ scf_handle_destroy(handle->handle);
+ }
+ free(handle);
+ }
+}
+
+/*
+ * sa_scf_init()
+ *
+ * must be called before using any of the SCF functions. Called by
+ * sa_init() during the API setup.
+ */
+
+scfutilhandle_t *
+sa_scf_init()
+{
+ scfutilhandle_t *handle;
+
+ scf_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
+ if (scf_max_name_len <= 0)
+ scf_max_name_len = SA_MAX_NAME_LEN + 1;
+
+ handle = calloc(1, sizeof (scfutilhandle_t));
+ if (handle != NULL) {
+ handle->scf_state = SCH_STATE_INITIALIZING;
+ handle->handle = scf_handle_create(SCF_VERSION);
+ if (handle->handle != NULL) {
+ if (scf_handle_bind(handle->handle) == 0) {
+ handle->scope = scf_scope_create(handle->handle);
+ handle->service = scf_service_create(handle->handle);
+ handle->pg = scf_pg_create(handle->handle);
+ handle->instance = scf_instance_create(handle->handle);
+ if (scf_handle_get_scope(handle->handle,
+ SCF_SCOPE_LOCAL, handle->scope) == 0) {
+ if (scf_scope_get_service(handle->scope,
+ SA_GROUP_SVC_NAME,
+ handle->service) != 0) {
+ goto err;
+ }
+ handle->scf_state = SCH_STATE_INIT;
+ if (sa_get_instance(handle, "default") != SA_OK) {
+ char **protolist;
+ int numprotos, i;
+ sa_group_t defgrp;
+ defgrp = sa_create_group("default", NULL);
+ if (defgrp != NULL) {
+ numprotos = sa_get_protocols(&protolist);
+ for (i = 0; i < numprotos; i++) {
+ (void) sa_create_optionset(defgrp,
+ protolist[i]);
+ }
+ if (protolist != NULL)
+ free(protolist);
+ }
+ }
+ } else {
+ goto err;
+ }
+ } else {
+ goto err;
+ }
+ } else {
+ free(handle);
+ handle = NULL;
+ (void) printf("libshare could not access SMF repository: %s\n",
+ scf_strerror(scf_error()));
+ }
+ }
+ return (handle);
+
+ /* error handling/unwinding */
+err:
+ (void) sa_scf_fini(handle);
+ (void) printf("libshare SMF initialization problem: %s\n",
+ scf_strerror(scf_error()));
+ return (NULL);
+}
+
+/*
+ * get_scf_limit(name)
+ *
+ * Since we use scf_limit a lot and do the same check and return the
+ * same value if it fails, implement as a function for code
+ * simplification. Basically, if name isn't found, return MAXPATHLEN
+ * (1024) so we have a reasonable default buffer size.
+ */
+static ssize_t
+get_scf_limit(uint32_t name)
+{
+ ssize_t vallen;
+
+ vallen = scf_limit(name);
+ if (vallen == (ssize_t)-1)
+ vallen = MAXPATHLEN;
+ return (vallen);
+}
+
+/*
+ * skip_property(name)
+ *
+ * internal function to check to see if a property is an SMF magic
+ * property that needs to be skipped.
+ */
+static int
+skip_property(char *name)
+{
+ int i;
+
+ for (i = 0; skip_props[i] != NULL; i++)
+ if (strcmp(name, skip_props[i]) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * generate_unique_sharename(sharename)
+ *
+ * Shares are represented in SMF as property groups. Due to share
+ * paths containing characters that are not allowed in SMF names and
+ * the need to be unique, we use UUIDs to construct a unique name.
+ */
+
+static void
+generate_unique_sharename(char *sharename)
+{
+ uuid_t uuid;
+
+ uuid_generate(uuid);
+ (void) strcpy(sharename, "S-");
+ uuid_unparse(uuid, sharename + 2);
+}
+
+/*
+ * valid_protocol(proto)
+ *
+ * check to see if the specified protocol is a valid one for the
+ * general sharemgr facility. We determine this by checking which
+ * plugin protocols were found.
+ */
+
+static int
+valid_protocol(char *proto)
+{
+ struct sa_proto_plugin *plugin;
+ for (plugin = sap_proto_list; plugin != NULL;
+ plugin = plugin->plugin_next)
+ if (strcmp(proto, plugin->plugin_ops->sa_protocol) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype)
+ *
+ * extract the name property group and create the specified type of
+ * node on the provided group. type will be optionset or security.
+ */
+
+static int
+sa_extract_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_propertygroup_t *pg,
+ char *nodetype, char *proto, char *sectype)
+{
+ xmlNodePtr node;
+ scf_iter_t *iter;
+ scf_property_t *prop;
+ scf_value_t *value;
+ char *name;
+ char *valuestr;
+ ssize_t vallen;
+ int ret = SA_OK;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+
+ node = xmlNewChild(root, NULL, (xmlChar *)nodetype, NULL);
+ if (node != NULL) {
+ if (proto != NULL)
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
+ if (sectype != NULL)
+ xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype);
+ /*
+ * have node to work with so iterate over the properties
+ * in the pg and create option sub nodes.
+ */
+ iter = scf_iter_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ prop = scf_property_create(handle->handle);
+ name = malloc(scf_max_name_len);
+ valuestr = malloc(vallen);
+ /*
+ * want to iterate through the properties and add them
+ * to the base optionset.
+ */
+ if (iter != NULL && value != NULL && prop != NULL &&
+ valuestr != NULL && name != NULL) {
+ if (scf_iter_pg_properties(iter, pg) == 0) {
+ /* now iterate the properties in the group */
+ while (scf_iter_next_property(iter, prop) > 0) {
+ /* have a property */
+ if (scf_property_get_name(prop, name,
+ scf_max_name_len) > 0) {
+ /* some properties are part of the framework */
+ if (skip_property(name))
+ continue;
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr,
+ vallen) >= 0) {
+ sa_property_t saprop;
+ saprop = sa_create_property(name,
+ valuestr);
+ if (saprop != NULL) {
+ /*
+ * since in SMF, don't
+ * recurse. Use xmlAddChild
+ * directly, instead.
+ */
+ xmlAddChild(node,
+ (xmlNodePtr) saprop);
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ /* cleanup to avoid memory leaks */
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+ if (name != NULL)
+ free(name);
+ if (valuestr != NULL)
+ free(valuestr);
+ }
+ return (ret);
+}
+
+/*
+ * sa_extract_attrs(root, handle, instance)
+ *
+ * local function to extract the actual attributes/properties from the
+ * property group of the service instance. These are the well known
+ * attributes of "state" and "zfs". If additional attributes are
+ * added, they should be added here.
+ */
+
+static void
+sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_instance_t *instance)
+{
+ scf_property_t *prop;
+ scf_value_t *value;
+ char *valuestr;
+ ssize_t vallen;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ prop = scf_property_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ valuestr = malloc(vallen);
+ if (prop != NULL && value != NULL && valuestr != NULL &&
+ scf_instance_get_pg(instance, "operation",
+ handle->pg) == 0) {
+ /*
+ * have a property group with desired name so now get
+ * the known attributes.
+ */
+ if (scf_pg_get_property(handle->pg, "state", prop) == 0) {
+ /* found the property so get the value */
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr, vallen) >= 0) {
+ xmlSetProp(root, (xmlChar *)"state",
+ (xmlChar *)valuestr);
+ }
+ }
+ }
+ if (scf_pg_get_property(handle->pg, "zfs", prop) == 0) {
+ /* found the property so get the value */
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr, vallen) > 0) {
+ xmlSetProp(root, (xmlChar *)"zfs",
+ (xmlChar *)valuestr);
+ }
+ }
+ }
+ }
+ if (valuestr != NULL)
+ free(valuestr);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+}
+
+/*
+ * list of known share attributes.
+ */
+
+static char *share_attr[] = {
+ "path",
+ "id",
+ "resource",
+ NULL,
+};
+
+static int
+is_share_attr(char *name)
+{
+ int i;
+ for (i = 0; share_attr[i] != NULL; i++)
+ if (strcmp(name, share_attr[i]) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * sa_share_from_pgroup
+ *
+ * extract the share definition from the share property group. We do
+ * some sanity checking to avoid bad data.
+ *
+ * Since this is only constructing the internal data structures, we
+ * don't use the sa_* functions most of the time.
+ */
+void
+sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_propertygroup_t *pg, char *id)
+{
+ xmlNodePtr node;
+ char *name;
+ scf_iter_t *iter;
+ scf_property_t *prop;
+ scf_value_t *value;
+ ssize_t vallen;
+ char *valuestr;
+ int ret = SA_OK;
+
+ /*
+ * While preliminary check (starts with 'S') passed before
+ * getting here. Need to make sure it is in ID syntax
+ * (Snnnnnn). Note that shares with properties have similar
+ * pgroups.
+ */
+ vallen = strlen(id);
+ if (*id == SA_SHARE_PG_PREFIX[0] && vallen == SA_SHARE_PG_LEN) {
+ uuid_t uuid;
+ if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) != 0 ||
+ uuid_parse(id + 2, uuid) < 0)
+ return;
+ } else {
+ return;
+ }
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+
+ iter = scf_iter_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ prop = scf_property_create(handle->handle);
+ name = malloc(scf_max_name_len);
+ valuestr = malloc(vallen);
+
+ /*
+ * construct the share XML node. It is similar to sa_add_share
+ * but never changes the repository. Also, there won't be any
+ * ZFS or transient shares. Root will be the group it is
+ * associated with.
+ */
+ node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL);
+ if (node != NULL) {
+ /*
+ * make sure the UUID part of the property group is
+ * stored in the share "id" property. We use this
+ * later.
+ */
+ xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id);
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)"persist");
+ }
+
+ if (iter != NULL && value != NULL && prop != NULL && name != NULL) {
+ /* iterate over the share pg properties */
+ if (scf_iter_pg_properties(iter, pg) == 0) {
+ while (scf_iter_next_property(iter, prop) > 0) {
+ ret = SA_SYSTEM_ERR; /* assume the worst */
+ if (scf_property_get_name(prop, name,
+ scf_max_name_len) > 0) {
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr,
+ vallen) >= 0) {
+ ret = SA_OK;
+ }
+ }
+ }
+ if (ret == SA_OK) {
+ if (is_share_attr(name)) {
+ /*
+ * if a share attr, then simple -
+ * usually path and resource name
+ */
+ xmlSetProp(node, (xmlChar *)name,
+ (xmlChar *)valuestr);
+ } else {
+ if (strcmp(name, "description") == 0) {
+ /* we have a description node */
+ xmlNodePtr desc;
+ desc = xmlNewChild(node, NULL,
+ (xmlChar *)"description",
+ NULL);
+ if (desc != NULL)
+ xmlNodeSetContent(desc,
+ (xmlChar *)valuestr);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (name != NULL)
+ free(name);
+ if (valuestr != NULL)
+ free(valuestr);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+}
+
+/*
+ * find_share_by_id(shareid)
+ *
+ * Search all shares in all groups until we find the share represented
+ * by "id".
+ */
+
+static sa_share_t
+find_share_by_id(char *shareid)
+{
+ sa_group_t group;
+ sa_share_t share = NULL;
+ char *id = NULL;
+ int done = 0;
+
+ for (group = sa_get_group(NULL); group != NULL && !done;
+ group = sa_get_next_group(group)) {
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ id = sa_get_share_attr(share, "id");
+ if (id != NULL && strcmp(id, shareid) == 0) {
+ sa_free_attr_string(id);
+ id = NULL;
+ done++;
+ break;
+ }
+ if (id != NULL) {
+ sa_free_attr_string(id);
+ id = NULL;
+ }
+ }
+ }
+ return (share);
+}
+
+/*
+ * sa_share_props_from_pgroup(root, handle, pg, id)
+ *
+ * extract share properties from the SMF property group. More sanity
+ * checks are done and the share object is created. We ignore some
+ * errors that could exist in the repository and only worry about
+ * property groups that validate in naming.
+ */
+
+static int
+sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_propertygroup_t *pg, char *id)
+{
+ xmlNodePtr node;
+ char *name;
+ scf_iter_t *iter;
+ scf_property_t *prop;
+ scf_value_t *value;
+ ssize_t vallen;
+ char *valuestr;
+ int ret = SA_OK;
+ char *sectype = NULL;
+ char *proto;
+ sa_share_t share;
+
+ /*
+ * While preliminary check (starts with 'S') passed before
+ * getting here. Need to make sure it is in ID syntax
+ * (Snnnnnn). Note that shares with properties have similar
+ * pgroups. If the pg name is more than SA_SHARE_PG_LEN
+ * characters, it is likely one of the protocol/security
+ * versions.
+ */
+ vallen = strlen(id);
+ if (*id == SA_SHARE_PG_PREFIX[0] && vallen > SA_SHARE_PG_LEN) {
+ uuid_t uuid;
+ if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) == 0) {
+ proto = strchr(id, '_');
+ if (proto == NULL)
+ return (ret);
+ *proto++ = '\0';
+ if (uuid_parse(id + SA_SHARE_PG_PREFIXLEN, uuid) < 0)
+ return (ret);
+ /*
+ * probably a legal optionset so check a few more
+ * syntax points below.
+ */
+ if (*proto == '\0') {
+ /* not a valid proto (null) */
+ return (ret);
+ }
+ sectype = strchr(proto, '_');
+ if (sectype != NULL)
+ *sectype++ = '\0';
+ if (!valid_protocol(proto))
+ return (ret);
+ }
+ } else {
+ /*
+ * it is ok to not have what we thought since someone might
+ * have added a name via SMF.
+ */
+ return (ret);
+ }
+
+ /*
+ * to get here, we have a valid protocol and possibly a
+ * security. We now have to find the share that it is really
+ * associated with. The "id" portion of the pgroup name will
+ * match.
+ */
+
+ share = find_share_by_id(id);
+ if (share == NULL)
+ return (SA_BAD_PATH);
+
+ root = (xmlNodePtr)share;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+
+ iter = scf_iter_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ prop = scf_property_create(handle->handle);
+ name = malloc(scf_max_name_len);
+ valuestr = malloc(vallen);
+
+ if (sectype == NULL)
+ node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL);
+ else {
+ node = xmlNewChild(root, NULL, (xmlChar *)"security", NULL);
+ if (node != NULL)
+ xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype);
+ }
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
+ /* now find the properties */
+ if (iter != NULL && value != NULL && prop != NULL && name != NULL) {
+ /* iterate over the share pg properties */
+ if (scf_iter_pg_properties(iter, pg) == 0) {
+ while (scf_iter_next_property(iter, prop) > 0) {
+ ret = SA_SYSTEM_ERR; /* assume the worst */
+ if (scf_property_get_name(prop, name,
+ scf_max_name_len) > 0) {
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr,
+ vallen) >= 0) {
+ ret = SA_OK;
+ }
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret == SA_OK) {
+ sa_property_t prop;
+ prop = sa_create_property(name, valuestr);
+ if (prop != NULL)
+ prop = (sa_property_t)xmlAddChild(node,
+ (xmlNodePtr)prop);
+ else
+ ret = SA_NO_MEMORY;
+ }
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+ if (name != NULL)
+ free(name);
+ if (valuestr != NULL)
+ free(valuestr);
+ return (ret);
+}
+
+/*
+ * sa_extract_group(root, handle, instance)
+ *
+ * get the config info for this instance of a group and create the XML
+ * subtree from it.
+ */
+
+static int
+sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_instance_t *instance)
+{
+ char *buff;
+ xmlNodePtr node;
+ scf_iter_t *iter;
+ char *proto;
+ char *sectype;
+ int have_shares = 0;
+ int has_proto = 0;
+ int is_default = 0;
+ int ret = SA_OK;
+ int err;
+
+ buff = malloc(scf_max_name_len);
+ iter = scf_iter_create(handle->handle);
+ if (buff != NULL) {
+ if (scf_instance_get_name(instance, buff,
+ scf_max_name_len) > 0) {
+ node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff);
+ if (strcmp(buff, "default") == 0)
+ is_default++;
+ sa_extract_attrs(node, handle, instance);
+ /*
+ * Iterate through all the property groups
+ * looking for those with security or
+ * optionset prefixes. The names of the
+ * matching pgroups are parsed to get the
+ * protocol, and for security, the sectype.
+ * Syntax is as follows:
+ * optionset | optionset_<proto>
+ * security_default | security_<proto>_<sectype>
+ * "operation" is handled by
+ * sa_extract_attrs().
+ */
+ if (iter != NULL) {
+ if (scf_iter_instance_pgs(iter, instance) == 0) {
+ while (scf_iter_next_pg(iter, handle->pg) > 0) {
+ /* have a pgroup so sort it out */
+ ret = scf_pg_get_name(handle->pg, buff,
+ scf_max_name_len);
+ if (ret > 0) {
+ if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
+ sa_share_from_pgroup(node, handle,
+ handle->pg,
+ buff);
+ have_shares++;
+ } else if (strncmp(buff, "optionset", 9) ==
+ 0) {
+ char *nodetype = "optionset";
+ /* have an optionset */
+ sectype = NULL;
+ proto = strchr(buff, '_');
+ if (proto != NULL) {
+ *proto++ = '\0';
+ sectype = strchr(proto, '_');
+ if (sectype != NULL) {
+ *sectype++ = '\0';
+ nodetype = "security";
+ }
+ }
+ ret = sa_extract_pgroup(node, handle,
+ handle->pg,
+ nodetype,
+ proto, sectype);
+ has_proto++;
+ } else if (strncmp(buff,
+ "security", 8) == 0) {
+ /*
+ * have a security (note that
+ * this should change in the
+ * future)
+ */
+ proto = strchr(buff, '_');
+ sectype = NULL;
+ if (proto != NULL) {
+ *proto++ = '\0';
+ sectype = strchr(proto, '_');
+ if (sectype != NULL)
+ *sectype++ = '\0';
+ if (strcmp(proto, "default") == 0)
+ proto = NULL;
+ }
+ ret = sa_extract_pgroup(node, handle,
+ handle->pg,
+ "security", proto,
+ sectype);
+ has_proto++;
+ }
+ /* ignore everything else */
+ }
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ /*
+ * Make sure we have a valid default group.
+ * On first boot, default won't have any
+ * protocols defined and won't be enabled (but
+ * should be).
+ */
+ if (is_default) {
+ char *state = sa_get_group_attr((sa_group_t)node,
+ "state");
+ char **protos;
+ int numprotos;
+ int i;
+
+ if (state == NULL) {
+ /* set attribute to enabled */
+ (void) sa_set_group_attr((sa_group_t)node,
+ "state",
+ "enabled");
+ /* we can assume no protocols */
+ numprotos = sa_get_protocols(&protos);
+ for (i = 0; i < numprotos; i++)
+ (void) sa_create_optionset((sa_group_t)node,
+ protos[i]);
+ if (numprotos > 0)
+ free(protos);
+ } else {
+ sa_free_attr_string(state);
+ }
+ }
+ /* do a second pass if shares were found */
+ if (have_shares &&
+ scf_iter_instance_pgs(iter, instance) == 0) {
+ while (scf_iter_next_pg(iter, handle->pg) > 0) {
+ /*
+ * have a pgroup so see if it is a
+ * share optionset
+ */
+ err = scf_pg_get_name(handle->pg, buff,
+ scf_max_name_len);
+ if (err > 0) {
+ if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
+ ret = sa_share_props_from_pgroup(node,
+ handle,
+ handle->pg,
+ buff);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (buff != NULL)
+ free(buff);
+ return (ret);
+}
+
+/*
+ * sa_extract_defaults(root, handle, instance)
+ *
+ * local function to find the default properties that live in the
+ * default instance's "operation" proprerty group.
+ */
+
+static void
+sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_instance_t *instance)
+{
+ xmlNodePtr node;
+ scf_property_t *prop;
+ scf_value_t *value;
+ char *valuestr;
+ ssize_t vallen;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ prop = scf_property_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ valuestr = malloc(vallen);
+ if (prop != NULL && value != NULL && vallen != NULL &&
+ scf_instance_get_pg(instance, "operation",
+ handle->pg) == 0) {
+ if (scf_pg_get_property(handle->pg,
+ "legacy-timestamp", prop) == 0) {
+ /* found the property so get the value */
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr, vallen) > 0) {
+ node = xmlNewChild(root, NULL, (xmlChar *)"legacy",
+ NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"timestamp",
+ (xmlChar *)valuestr);
+ xmlSetProp(node, (xmlChar *)"path",
+ (xmlChar *)SA_LEGACY_DFSTAB);
+ }
+ }
+ }
+ }
+ }
+ if (valuestr != NULL)
+ free(valuestr);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+}
+
+
+/*
+ * sa_get_config(handle, root, doc)
+ *
+ * walk the SMF repository for /network/shares/group and find all the
+ * instances. These become group names. Then add the XML structure
+ * below the groups based on property groups and properties.
+ */
+int
+sa_get_config(scfutilhandle_t *handle, xmlNodePtr *root, xmlDocPtr *doc)
+{
+ int ret = SA_OK;
+ scf_instance_t *instance;
+ scf_iter_t *iter;
+ char buff[BUFSIZ * 2];
+
+ *doc = xmlNewDoc((xmlChar *)"1.0");
+ *root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
+ instance = scf_instance_create(handle->handle);
+ iter = scf_iter_create(handle->handle);
+ if (*doc != NULL && *root != NULL && instance != NULL && iter != NULL) {
+ xmlDocSetRootElement(*doc, *root);
+ if ((ret = scf_iter_service_instances(iter,
+ handle->service)) == 0) {
+ while ((ret = scf_iter_next_instance(iter,
+ instance)) > 0) {
+ if (scf_instance_get_name(instance, buff,
+ sizeof (buff)) > 0) {
+ if (strcmp(buff, "default") == 0)
+ sa_extract_defaults(*root, handle, instance);
+ ret = sa_extract_group(*root, handle, instance);
+ }
+ }
+ }
+ } else {
+ /* if we can't create the document, cleanup */
+ if (*doc != NULL)
+ xmlFreeDoc(*doc);
+ if (*root != NULL)
+ xmlFreeNode(*root);
+ *doc = NULL;
+ *root = NULL;
+ }
+ /* always cleanup these */
+ if (instance != NULL)
+ scf_instance_destroy(instance);
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ return (ret);
+}
+
+/*
+ * sa_get_instance(handle, instance)
+ *
+ * get the instance of the group service. This is actually the
+ * specific group name. The instance is needed for all property and
+ * control operations.
+ */
+
+int
+sa_get_instance(scfutilhandle_t *handle, char *instname)
+{
+ if (scf_service_get_instance(handle->service, instname,
+ handle->instance) != 0) {
+ return (SA_NO_SUCH_GROUP);
+ }
+ return (SA_OK);
+}
+
+/*
+ * sa_create_instance(handle, instname)
+ *
+ * Create a new SMF service instance. There can only be one with a
+ * given name.
+ */
+
+int
+sa_create_instance(scfutilhandle_t *handle, char *instname)
+{
+ int ret = SA_OK;
+ char instance[SA_GROUP_INST_LEN];
+ if (scf_service_add_instance(handle->service, instname,
+ handle->instance) != 0) {
+ /* better error returns need to be added based on real error */
+ if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
+ ret = SA_NO_PERMISSION;
+ else
+ ret = SA_DUPLICATE_NAME;
+ } else {
+ /* have the service created, so enable it */
+ (void) snprintf(instance, sizeof (instance), "%s:%s",
+ SA_SVC_FMRI_BASE, instname);
+ (void) smf_enable_instance(instance, 0);
+ }
+ return (ret);
+}
+
+/*
+ * sa_delete_instance(handle, instname)
+ *
+ * When a group goes away, we also remove the service instance.
+ */
+
+int
+sa_delete_instance(scfutilhandle_t *handle, char *instname)
+{
+ int ret;
+
+ if (strcmp(instname, "default") == 0) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ if ((ret = sa_get_instance(handle, instname)) == SA_OK) {
+ if (scf_instance_delete(handle->instance) != 0)
+ /* need better analysis */
+ ret = SA_NO_PERMISSION;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_create_pgroup(handle, pgroup)
+ *
+ * create a new property group
+ */
+
+int
+sa_create_pgroup(scfutilhandle_t *handle, char *pgroup)
+{
+ int ret = SA_OK;
+ /*
+ * only create a handle if it doesn't exist. It is ok to exist
+ * since the pg handle will be set as a side effect.
+ */
+ if (handle->pg == NULL) {
+ handle->pg = scf_pg_create(handle->handle);
+ }
+ /*
+ * if the pgroup exists, we are done. If it doesn't, then we
+ * need to actually add one to the service instance.
+ */
+ if (scf_instance_get_pg(handle->instance,
+ pgroup, handle->pg) != 0) {
+ /* doesn't exist so create one */
+ if (scf_instance_add_pg(handle->instance, pgroup,
+ SCF_GROUP_APPLICATION, 0,
+ handle->pg) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SA_NO_PERMISSION;
+ break;
+ default:
+ ret = SA_SYSTEM_ERR;
+ break;
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_delete_pgroup(handle, pgroup)
+ *
+ * remove the property group from the current instance of the service,
+ * but only if it actually exists.
+ */
+
+int
+sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup)
+{
+ int ret = SA_OK;
+ /*
+ * only delete if it does exist.
+ */
+ if (scf_instance_get_pg(handle->instance,
+ pgroup, handle->pg) == 0) {
+ /* does exist so delete it */
+ if (scf_pg_delete(handle->pg) != 0) {
+ ret = SA_SYSTEM_ERR;
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret == SA_SYSTEM_ERR &&
+ scf_error() == SCF_ERROR_PERMISSION_DENIED) {
+ ret = SA_NO_PERMISSION;
+ }
+ return (ret);
+}
+
+/*
+ * sa_start_transaction(handle, pgroup)
+ *
+ * Start an SMF transaction so we can deal with properties. it would
+ * be nice to not have to expose this, but we have to in order to
+ * optimize.
+ *
+ * Basic model is to hold the transaction in the handle and allow
+ * property adds/deletes/updates to be added then close the
+ * transaction (or abort). There may eventually be a need to handle
+ * other types of transaction mechanisms but we don't do that now.
+ *
+ * An sa_start_transaction must be followed by either an
+ * sa_end_transaction or sa_abort_transaction before another
+ * sa_start_transaction can be done.
+ */
+
+int
+sa_start_transaction(scfutilhandle_t *handle, char *propgroup)
+{
+ int ret = SA_OK;
+ /*
+ * lookup the property group and create it if it doesn't already
+ * exist.
+ */
+ if (handle->scf_state == SCH_STATE_INIT) {
+ ret = sa_create_pgroup(handle, propgroup);
+ if (ret == SA_OK) {
+ handle->trans = scf_transaction_create(handle->handle);
+ if (handle->trans != NULL) {
+ if (scf_transaction_start(handle->trans, handle->pg) != 0) {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret != SA_OK) {
+ scf_transaction_destroy(handle->trans);
+ handle->trans = NULL;
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ }
+ if (ret == SA_SYSTEM_ERR &&
+ scf_error() == SCF_ERROR_PERMISSION_DENIED) {
+ ret = SA_NO_PERMISSION;
+ }
+ return (ret);
+}
+
+/*
+ * sa_end_transaction(handle)
+ *
+ * Commit the changes that were added to the transaction in the
+ * handle. Do all necessary cleanup.
+ */
+
+int
+sa_end_transaction(scfutilhandle_t *handle)
+{
+ int ret = SA_OK;
+
+ if (handle->trans == NULL) {
+ ret = SA_SYSTEM_ERR;
+ } else {
+ if (scf_transaction_commit(handle->trans) < 0)
+ ret = SA_SYSTEM_ERR;
+ scf_transaction_destroy_children(handle->trans);
+ scf_transaction_destroy(handle->trans);
+ handle->trans = NULL;
+ }
+ return (ret);
+}
+
+/*
+ * sa_abort_transaction(handle)
+ *
+ * Abort the changes that were added to the transaction in the
+ * handle. Do all necessary cleanup.
+ */
+
+void
+sa_abort_transaction(scfutilhandle_t *handle)
+{
+ if (handle->trans != NULL) {
+ scf_transaction_reset_all(handle->trans);
+ scf_transaction_destroy_children(handle->trans);
+ scf_transaction_destroy(handle->trans);
+ handle->trans = NULL;
+ }
+}
+
+/*
+ * sa_set_property(handle, prop, value)
+ *
+ * set a property transaction entry into the pending SMF transaction.
+ */
+
+int
+sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr)
+{
+ int ret = SA_OK;
+ scf_value_t *value;
+ scf_transaction_entry_t *entry;
+ /*
+ * properties must be set in transactions and don't take
+ * effect until the transaction has been ended/committed.
+ */
+ value = scf_value_create(handle->handle);
+ entry = scf_entry_create(handle->handle);
+ if (value != NULL && entry != NULL) {
+ if (scf_transaction_property_change(handle->trans, entry,
+ propname,
+ SCF_TYPE_ASTRING) == 0 ||
+ scf_transaction_property_new(handle->trans, entry,
+ propname,
+ SCF_TYPE_ASTRING) == 0) {
+ if (scf_value_set_astring(value, valstr) == 0) {
+ if (scf_entry_add_value(entry, value) != 0) {
+ ret = SA_SYSTEM_ERR;
+ scf_value_destroy(value);
+ }
+ /* the value is in the transaction */
+ value = NULL;
+ } else {
+ /* value couldn't be constructed */
+ ret = SA_SYSTEM_ERR;
+ }
+ /* the entry is in the transaction */
+ entry = NULL;
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret == SA_SYSTEM_ERR) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SA_NO_PERMISSION;
+ break;
+ }
+ }
+ /*
+ * cleanup if there were any errors that didn't leave these
+ * values where they would be cleaned up later.
+ */
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (entry != NULL)
+ scf_entry_destroy(entry);
+ return (ret);
+}
+
+/*
+ * sa_commit_share(handle, group, share)
+ *
+ * commit this share to the repository.
+ * properties are added if they exist but can be added later.
+ * Need to add to dfstab and sharetab, if appropriate.
+ */
+int
+sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
+{
+ int ret = SA_OK;
+ char *groupname;
+ char *name;
+ char *resource;
+ char *description;
+ char *sharename;
+ ssize_t proplen;
+ char *propstring;
+
+ /*
+ * don't commit in the zfs group. We do commit legacy
+ * (default) and all other groups/shares. ZFS is handled
+ * through the ZFS configuration rather than SMF.
+ */
+
+ groupname = sa_get_group_attr(group, "name");
+ if (groupname != NULL) {
+ if (strcmp(groupname, "zfs") == 0) {
+ /*
+ * adding to the ZFS group will result in the sharenfs
+ * property being set but we don't want to do anything
+ * SMF related at this point.
+ */
+ sa_free_attr_string(groupname);
+ return (ret);
+ }
+ }
+
+ proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ propstring = malloc(proplen);
+ if (propstring == NULL)
+ ret = SA_NO_MEMORY;
+
+ if (groupname != NULL && ret == SA_OK) {
+ ret = sa_get_instance(handle, groupname);
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ sharename = sa_get_share_attr(share, "id");
+ if (sharename == NULL) {
+ /* slipped by */
+ char shname[SA_SHARE_UUID_BUFLEN];
+ generate_unique_sharename(shname);
+ xmlSetProp((xmlNodePtr)share, (xmlChar *)"id",
+ (xmlChar *)shname);
+ sharename = strdup(shname);
+ }
+ if (sharename != NULL) {
+ /*
+ * have a share name allocated so create a pgroup
+ * for it. It may already exist, but that is OK.
+ */
+ ret = sa_create_pgroup(handle, sharename);
+ if (ret == SA_OK) {
+ /*
+ * now start the transaction for the
+ * properties that define this share. They may
+ * exist so attempt to update before create.
+ */
+ ret = sa_start_transaction(handle, sharename);
+ }
+ if (ret == SA_OK) {
+ name = sa_get_share_attr(share, "path");
+ if (name != NULL) {
+ /* there needs to be a path for a share to exist */
+ ret = sa_set_property(handle, "path", name);
+ sa_free_attr_string(name);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ }
+ if (ret == SA_OK) {
+ resource = sa_get_share_attr(share, "resource");
+ if (resource != NULL) {
+ ret = sa_set_property(handle, "resource", resource);
+ sa_free_attr_string(resource);
+ }
+ }
+ if (ret == SA_OK) {
+ description = sa_get_share_description(share);
+ if (description != NULL) {
+ ret = sa_set_property(handle, "description",
+ description);
+ sa_free_share_description(description);
+ }
+ }
+ /* make sure we cleanup the transaction */
+ if (ret == SA_OK) {
+ ret = sa_end_transaction(handle);
+ } else {
+ sa_abort_transaction(handle);
+ }
+ free(sharename);
+ }
+ }
+ if (ret == SA_SYSTEM_ERR) {
+ int err = scf_error();
+ if (err == SCF_ERROR_PERMISSION_DENIED)
+ ret = SA_NO_PERMISSION;
+ }
+ if (propstring != NULL)
+ free(propstring);
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+
+ return (ret);
+}
+
+/*
+ * sa_delete_share(handle, group, share)
+ *
+ * remove the specified share from the group (and service instance).
+ */
+
+int
+sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
+{
+ int ret = SA_OK;
+ char *groupname = NULL;
+ char *shareid = NULL;
+ sa_optionset_t opt;
+ sa_security_t sec;
+ ssize_t proplen;
+ char *propstring;
+
+ proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ propstring = malloc(proplen);
+ if (propstring == NULL)
+ ret = SA_NO_MEMORY;
+
+ if (ret == SA_OK) {
+ groupname = sa_get_group_attr(group, "name");
+ shareid = sa_get_share_attr(share, "id");
+ if (groupname != NULL && shareid != NULL) {
+ ret = sa_get_instance(handle, groupname);
+ if (ret == SA_OK) {
+ /* if a share has properties, remove them */
+ ret = sa_delete_pgroup(handle, shareid);
+ for (opt = sa_get_optionset(share, NULL); opt != NULL;
+ opt = sa_get_next_optionset(opt)) {
+ char *proto;
+ proto = sa_get_optionset_attr(opt, "type");
+ if (proto != NULL) {
+ (void) snprintf(propstring, proplen, "%s_%s",
+ shareid, proto);
+ ret = sa_delete_pgroup(handle, propstring);
+ sa_free_attr_string(proto);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ }
+ /*
+ * if a share has security/negotiable
+ * properties, remove them.
+ */
+ for (sec = sa_get_security(share, NULL, NULL); sec != NULL;
+ sec = sa_get_next_security(sec)) {
+ char *proto;
+ char *sectype;
+ proto = sa_get_security_attr(sec, "type");
+ sectype = sa_get_security_attr(sec, "sectype");
+ if (proto != NULL && sectype != NULL) {
+ (void) snprintf(propstring, proplen, "%s_%s_%s",
+ shareid,
+ proto, sectype);
+ ret = sa_delete_pgroup(handle, propstring);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ if (sectype != NULL)
+ sa_free_attr_string(sectype);
+ }
+ }
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+ if (shareid != NULL)
+ sa_free_attr_string(shareid);
+ if (propstring != NULL)
+ free(propstring);
+
+ return (ret);
+}
diff --git a/usr/src/lib/libshare/common/scfutil.h b/usr/src/lib/libshare/common/scfutil.h
new file mode 100644
index 0000000000..da21d32785
--- /dev/null
+++ b/usr/src/lib/libshare/common/scfutil.h
@@ -0,0 +1,81 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * basic API declarations for share management
+ */
+
+#ifndef _SCFUTIL_H
+#define _SCFUTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <libxml/tree.h>
+
+typedef struct scfutilhandle {
+ scf_handle_t *handle;
+ int scf_state;
+ scf_service_t *service;
+ scf_scope_t *scope;
+ scf_transaction_t *trans;
+ scf_transaction_entry_t *entry;
+ scf_propertygroup_t *pg;
+ scf_instance_t *instance;
+} scfutilhandle_t;
+
+#define SCH_STATE_UNINIT 0
+#define SCH_STATE_INITIALIZING 1
+#define SCH_STATE_INIT 2
+
+extern void sa_scf_fini(scfutilhandle_t *);
+extern scfutilhandle_t *sa_scf_init();
+extern int sa_get_config(scfutilhandle_t *, xmlNodePtr *, xmlDocPtr *);
+extern int sa_get_instance(scfutilhandle_t *, char *);
+extern int sa_create_instance(scfutilhandle_t *, char *);
+
+/*
+ * Shares are held in a property group with name of the form
+ * S-<GUID>. The total length of the name is 38 characters.
+ */
+#define SA_SHARE_PG_PREFIX "S-"
+#define SA_SHARE_PG_PREFIXLEN 2
+#define SA_SHARE_PG_LEN 38
+#define SA_SHARE_UUID_BUFLEN 64
+
+/*
+ * service instance related defines
+ */
+#define SA_GROUP_SVC_NAME "network/shares/group"
+#define SA_GROUP_INST_LEN 256
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SCFUTIL_H */
diff --git a/usr/src/lib/libshare/i386/Makefile b/usr/src/lib/libshare/i386/Makefile
new file mode 100644
index 0000000000..729341c543
--- /dev/null
+++ b/usr/src/lib/libshare/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libshare/sparc/Makefile b/usr/src/lib/libshare/sparc/Makefile
new file mode 100644
index 0000000000..729341c543
--- /dev/null
+++ b/usr/src/lib/libshare/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/pkgdefs/SUNWarc/prototype_com b/usr/src/pkgdefs/SUNWarc/prototype_com
index c1f63747c0..7272645f33 100644
--- a/usr/src/pkgdefs/SUNWarc/prototype_com
+++ b/usr/src/pkgdefs/SUNWarc/prototype_com
@@ -173,6 +173,8 @@ s none usr/lib/llib-lsecdb=../../lib/llib-lsecdb
s none usr/lib/llib-lsecdb.ln=../../lib/llib-lsecdb.ln
s none usr/lib/llib-lsendfile=../../lib/llib-lsendfile
s none usr/lib/llib-lsendfile.ln=../../lib/llib-lsendfile.ln
+f none usr/lib/llib-lshare.ln 644 root bin
+f none usr/lib/llib-lshare 644 root bin
f none usr/lib/llib-lsip.ln 644 root bin
f none usr/lib/llib-lsip 644 root bin
f none usr/lib/llib-lsldap.ln 644 root bin
diff --git a/usr/src/pkgdefs/SUNWcsr/prototype_com b/usr/src/pkgdefs/SUNWcsr/prototype_com
index 793af55b99..eef2ff5e0b 100644
--- a/usr/src/pkgdefs/SUNWcsr/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsr/prototype_com
@@ -480,6 +480,8 @@ d none var/svc/manifest/network/ldap 0755 root sys
f manifest var/svc/manifest/network/ldap/client.xml 0444 root sys
d none var/svc/manifest/network/ssl 0755 root sys
f manifest var/svc/manifest/network/ssl/kssl-proxy.xml 0444 root sys
+d none var/svc/manifest/network/shares 0755 root sys
+f manifest var/svc/manifest/network/shares/group.xml 0444 root sys
d none var/svc/manifest/platform 755 root sys
d none var/svc/manifest/site 755 root sys
d none var/svc/manifest/system 755 root sys
diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com
index d888597079..28a4274825 100644
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com
@@ -822,7 +822,6 @@ s none usr/sbin/routeadm=../../sbin/routeadm
f none usr/sbin/rpcbind 555 root bin
f none usr/sbin/sacadm 4755 root sys
f none usr/sbin/setmnt 555 root bin
-f none usr/sbin/share 555 root bin
f none usr/sbin/shareall 555 root bin
f none usr/sbin/shutdown 755 root sys
f none usr/sbin/smbios 555 root bin
@@ -852,7 +851,12 @@ s none usr/sbin/ufsrestore=../lib/fs/ufs/ufsrestore
s none usr/sbin/umount=../../sbin/umount
f none usr/sbin/umountall 555 root sys
f none usr/sbin/unlink 555 root bin
-f none usr/sbin/unshare 555 root bin
+f none usr/sbin/sharectl 555 root bin
+f none usr/sbin/sharemgr 555 root bin
+f none usr/lib/libshare.so.1 755 root bin
+s none usr/lib/libshare.so=./libshare.so.1
+l none usr/sbin/share=../../usr/sbin/sharemgr
+l none usr/sbin/unshare=../../usr/sbin/sharemgr
f none usr/sbin/unshareall 555 root bin
l none usr/sbin/update_drv=../../usr/lib/isaexec
f none usr/sbin/useradd 555 root sys
diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com
index 4331dbb4d1..7306f102cd 100644
--- a/usr/src/pkgdefs/SUNWhea/prototype_com
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com
@@ -198,6 +198,7 @@ f none usr/include/librcm.h 644 root bin
f none usr/include/libscf.h 0644 root bin
f none usr/include/libscf_priv.h 0644 root bin
f none usr/include/libsysevent.h 644 root bin
+f none usr/include/libshare.h 644 root bin
f none usr/include/libsysevent_impl.h 644 root bin
f none usr/include/libsvm.h 644 root bin
f none usr/include/libtsnet.h 644 root bin
diff --git a/usr/src/pkgdefs/SUNWnfssu/prototype_com b/usr/src/pkgdefs/SUNWnfssu/prototype_com
index 95e8347677..5c8b4f04aa 100644
--- a/usr/src/pkgdefs/SUNWnfssu/prototype_com
+++ b/usr/src/pkgdefs/SUNWnfssu/prototype_com
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,7 @@
#
# CDDL HEADER END
#
-#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -49,8 +47,7 @@ d none usr 755 root sys
d none usr/lib 755 root bin
d none usr/lib/fs 755 root sys
d none usr/lib/fs/nfs 755 root sys
-f none usr/lib/fs/nfs/share 555 root bin
-f none usr/lib/fs/nfs/unshare 555 root bin
+f none usr/lib/fs/nfs/libshare_nfs.so 555 root bin
d none usr/lib/nfs 755 root sys
f none usr/lib/nfs/mountd 555 root bin
f none usr/lib/nfs/nfsd 555 root bin