diff options
Diffstat (limited to 'usr')
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 |