walters / rpms / nfs-utils

Forked from rpms/nfs-utils 6 years ago
Clone
fe3c582
diff --git a/.gitignore b/.gitignore
fe3c582
index 126d12c..941aca0 100644
fe3c582
--- a/.gitignore
fe3c582
+++ b/.gitignore
fe3c582
@@ -70,6 +70,7 @@ tests/nsm_client/nlm_sm_inter_svc.c
fe3c582
 tests/nsm_client/nlm_sm_inter_xdr.c
fe3c582
 utils/nfsidmap/nfsidmap
fe3c582
 systemd/nfs-server-generator
fe3c582
+systemd/rpc-pipefs-generator
fe3c582
 systemd/nfs-config.service
fe3c582
 systemd/rpc-gssd.service
fe3c582
 # cscope database files
fe3c582
diff --git a/nfs.conf b/nfs.conf
fe3c582
index 81ece06..0d0ec9b 100644
fe3c582
--- a/nfs.conf
fe3c582
+++ b/nfs.conf
fe3c582
@@ -1,7 +1,10 @@
fe3c582
 #
fe3c582
-# This is a general conifguration for the 
fe3c582
+# This is a general configuration for the
fe3c582
 # NFS daemons and tools
fe3c582
 #
fe3c582
+#[general]
fe3c582
+# pipefs-directory=/var/lib/nfs/rpc_pipefs
fe3c582
+#
fe3c582
 #[exportfs]
fe3c582
 # debug=0
fe3c582
 #
fe3c582
@@ -12,7 +15,6 @@
fe3c582
 # limit-to-legacy-enctypes=0
fe3c582
 # context-timeout=0
fe3c582
 # rpc-timeout=5
fe3c582
-# pipefs-directory=/var/lib/nfs/rpc_pipefs
fe3c582
 # keytab-file=/etc/krb5.keytab
fe3c582
 # cred-cache-directory=
fe3c582
 # preferred-realm=
fe3c582
@@ -42,7 +44,7 @@
fe3c582
 # port=0
fe3c582
 # grace-time=90
fe3c582
 # lease-time=90
fe3c582
-# udp=y
fe3c582
+# udp=n
fe3c582
 # tcp=y
fe3c582
 # vers2=n
fe3c582
 # vers3=y
fe3c582
@@ -65,6 +67,7 @@
fe3c582
 # retry-time=900
fe3c582
 # outgoing-port=
fe3c582
 # outgoing-addr=
fe3c582
+# lift-grace=y
fe3c582
 #
fe3c582
 #[svcgssd]
fe3c582
 # principal=
fe3c582
diff --git a/support/export/xtab.c b/support/export/xtab.c
fe3c582
index 22cf539..d42eeef 100644
fe3c582
--- a/support/export/xtab.c
fe3c582
+++ b/support/export/xtab.c
fe3c582
@@ -14,12 +14,20 @@
fe3c582
 #include <unistd.h>
fe3c582
 #include <stdlib.h>
fe3c582
 #include <string.h>
fe3c582
+#include <sys/types.h>
fe3c582
+#include <sys/stat.h>
fe3c582
+#include <errno.h>
fe3c582
+#include <libgen.h>
fe3c582
 
fe3c582
 #include "nfslib.h"
fe3c582
 #include "exportfs.h"
fe3c582
 #include "xio.h"
fe3c582
 #include "xlog.h"
fe3c582
 #include "v4root.h"
fe3c582
+#include "misc.h"
fe3c582
+
fe3c582
+static char state_base_dirname[PATH_MAX] = NFS_STATEDIR;
fe3c582
+extern struct state_paths etab;
fe3c582
 
fe3c582
 int v4root_needed;
fe3c582
 static void cond_rename(char *newfile, char *oldfile);
fe3c582
@@ -65,7 +73,7 @@ xtab_read(char *xtab, char *lockfn, int is_export)
fe3c582
 int
fe3c582
 xtab_export_read(void)
fe3c582
 {
fe3c582
-	return xtab_read(_PATH_ETAB, _PATH_ETABLCK, 1);
fe3c582
+	return xtab_read(etab.statefn, etab.lockfn, 1);
fe3c582
 }
fe3c582
 
fe3c582
 /*
fe3c582
@@ -112,7 +120,7 @@ xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export)
fe3c582
 int
fe3c582
 xtab_export_write()
fe3c582
 {
fe3c582
-	return xtab_write(_PATH_ETAB, _PATH_ETABTMP, _PATH_ETABLCK, 1);
fe3c582
+	return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn, 1);
fe3c582
 }
fe3c582
 
fe3c582
 /*
fe3c582
@@ -158,3 +166,74 @@ static void cond_rename(char *newfile, char *oldfile)
fe3c582
 	rename(newfile, oldfile);
fe3c582
 	return;
fe3c582
 }
fe3c582
+
fe3c582
+/*
fe3c582
+ * Returns a dynamically allocated, '\0'-terminated buffer
fe3c582
+ * containing an appropriate pathname, or NULL if an error
fe3c582
+ * occurs.  Caller must free the returned result with free(3).
fe3c582
+ */
fe3c582
+static char *
fe3c582
+state_make_pathname(const char *tabname)
fe3c582
+{
fe3c582
+	return generic_make_pathname(state_base_dirname, tabname);
fe3c582
+}
fe3c582
+
fe3c582
+/**
fe3c582
+ * state_setup_basedir - set up basedir
fe3c582
+ * @progname: C string containing name of program, for error messages
fe3c582
+ * @parentdir: C string containing pathname to on-disk state, or NULL
fe3c582
+ *
fe3c582
+ * This runs before logging is set up, so error messages are directed
fe3c582
+ * to stderr.
fe3c582
+ *
fe3c582
+ * Returns true and sets up our basedir, if @parentdir was valid
fe3c582
+ * and usable; otherwise false is returned.
fe3c582
+ */
fe3c582
+_Bool
fe3c582
+state_setup_basedir(const char *progname, const char *parentdir)
fe3c582
+{
fe3c582
+	return generic_setup_basedir(progname, parentdir, state_base_dirname,
fe3c582
+				     PATH_MAX);
fe3c582
+}
fe3c582
+
fe3c582
+int
fe3c582
+setup_state_path_names(const char *progname, const char *statefn,
fe3c582
+		      const char *tmpfn, const char *lockfn,
fe3c582
+		      struct state_paths *paths)
fe3c582
+{
fe3c582
+	paths->statefn = state_make_pathname(statefn);
fe3c582
+	if (!paths->statefn) {
fe3c582
+		fprintf(stderr, "%s: state_make_pathname(%s) failed\n",
fe3c582
+			progname, statefn);
fe3c582
+		goto out_err;
fe3c582
+	}
fe3c582
+	paths->tmpfn = state_make_pathname(tmpfn);
fe3c582
+	if (!paths->tmpfn) {
fe3c582
+		fprintf(stderr, "%s: state_make_pathname(%s) failed\n",
fe3c582
+			progname, tmpfn);
fe3c582
+		goto out_free_statefn;
fe3c582
+	}
fe3c582
+	paths->lockfn = state_make_pathname(lockfn);
fe3c582
+	if (!paths->lockfn) {
fe3c582
+		fprintf(stderr, "%s: state_make_pathname(%s) failed\n",
fe3c582
+			progname, lockfn);
fe3c582
+		goto out_free_tmpfn;
fe3c582
+	}
fe3c582
+	return 1;
fe3c582
+
fe3c582
+out_free_tmpfn:
fe3c582
+	free(paths->tmpfn);
fe3c582
+out_free_statefn:
fe3c582
+	free(paths->statefn);
fe3c582
+out_err:
fe3c582
+	return 0;
fe3c582
+
fe3c582
+}
fe3c582
+
fe3c582
+void
fe3c582
+free_state_path_names(struct state_paths *paths)
fe3c582
+{
fe3c582
+	free(paths->statefn);
fe3c582
+	free(paths->tmpfn);
fe3c582
+	free(paths->lockfn);
fe3c582
+}
fe3c582
diff --git a/support/include/conffile.h b/support/include/conffile.h
fe3c582
index 3fe3a78..2d11a52 100644
fe3c582
--- a/support/include/conffile.h
fe3c582
+++ b/support/include/conffile.h
fe3c582
@@ -48,8 +48,6 @@ struct conf_list {
fe3c582
 	TAILQ_HEAD(conf_list_fields_head, conf_list_node) fields;
fe3c582
 };
fe3c582
 
fe3c582
-extern char    *conf_path;
fe3c582
-
fe3c582
 extern int      conf_begin(void);
fe3c582
 extern int      conf_decode_base64(uint8_t *, uint32_t *, unsigned char *);
fe3c582
 extern int      conf_end(int, int);
fe3c582
@@ -61,9 +59,9 @@ extern int      conf_get_num(char *, char *, int);
fe3c582
 extern _Bool    conf_get_bool(char *, char *, _Bool);
fe3c582
 extern char    *conf_get_str(char *, char *);
fe3c582
 extern char    *conf_get_section(char *, char *, char *);
fe3c582
-extern void     conf_init(void);
fe3c582
+extern void     conf_init(const char *);
fe3c582
+extern void     conf_cleanup(void);
fe3c582
 extern int      conf_match_num(char *, char *, int);
fe3c582
-extern void     conf_reinit(void);
fe3c582
 extern int      conf_remove(int, char *, char *);
fe3c582
 extern int      conf_remove_section(int, char *);
fe3c582
 extern void     conf_report(void);
fe3c582
diff --git a/support/include/misc.h b/support/include/misc.h
fe3c582
index eedc1fe..06e2a0c 100644
fe3c582
--- a/support/include/misc.h
fe3c582
+++ b/support/include/misc.h
fe3c582
@@ -15,6 +15,9 @@
fe3c582
 int	randomkey(unsigned char *keyout, int len);
fe3c582
 int	weakrandomkey(unsigned char *keyout, int len);
fe3c582
 
fe3c582
+char *generic_make_pathname(const char *, const char *);
fe3c582
+_Bool generic_setup_basedir(const char *, const char *, char *, const size_t);
fe3c582
+
fe3c582
 extern int is_mountpoint(char *path);
fe3c582
 
fe3c582
 /* size of the file pointer buffers for rpc procfs files */
fe3c582
diff --git a/support/include/nfs/nfs.h b/support/include/nfs/nfs.h
fe3c582
index 15ecc6b..7933ff5 100644
fe3c582
--- a/support/include/nfs/nfs.h
fe3c582
+++ b/support/include/nfs/nfs.h
fe3c582
@@ -16,8 +16,8 @@
fe3c582
 #define NFSD_MINVERS 2
fe3c582
 #define NFSD_MAXVERS 4
fe3c582
 
fe3c582
-#define NFS4_MINMINOR 1
fe3c582
-#define NFS4_MAXMINOR WORD_BIT
fe3c582
+#define NFS4_MINMINOR 0
fe3c582
+#define NFS4_MAXMINOR (WORD_BIT-1)
fe3c582
 
fe3c582
 struct nfs_fh_len {
fe3c582
 	int		fh_size;
fe3c582
@@ -27,21 +27,24 @@ struct nfs_fh_len {
fe3c582
 
fe3c582
 #define NFSCTL_UDPBIT		      (1 << (17 - 1))
fe3c582
 #define NFSCTL_TCPBIT		      (1 << (18 - 1))
fe3c582
+#define NFSCTL_PROTODEFAULT	      (NFSCTL_TCPBIT)
fe3c582
 
fe3c582
 #define NFSCTL_VERUNSET(_cltbits, _v) ((_cltbits) &= ~(1 << ((_v) - 1))) 
fe3c582
+#define NFSCTL_MINORUNSET(_cltbits, _v) ((_cltbits) &= ~(1 << (_v)))
fe3c582
 #define NFSCTL_UDPUNSET(_cltbits)     ((_cltbits) &= ~NFSCTL_UDPBIT) 
fe3c582
 #define NFSCTL_TCPUNSET(_cltbits)     ((_cltbits) &= ~NFSCTL_TCPBIT) 
fe3c582
 
fe3c582
 #define NFSCTL_VERISSET(_cltbits, _v) ((_cltbits) & (1 << ((_v) - 1))) 
fe3c582
+#define NFSCTL_MINORISSET(_cltbits, _v) ((_cltbits) & (1 << (_v)))
fe3c582
 #define NFSCTL_UDPISSET(_cltbits)     ((_cltbits) & NFSCTL_UDPBIT) 
fe3c582
 #define NFSCTL_TCPISSET(_cltbits)     ((_cltbits) & NFSCTL_TCPBIT) 
fe3c582
 
fe3c582
 #define NFSCTL_VERDEFAULT (0xc)       /* versions 3 and 4 */
fe3c582
 #define NFSCTL_VERSET(_cltbits, _v)   ((_cltbits) |= (1 << ((_v) - 1))) 
fe3c582
+#define NFSCTL_MINORSET(_cltbits, _v)   ((_cltbits) |= (1 << (_v)))
fe3c582
 #define NFSCTL_UDPSET(_cltbits)       ((_cltbits) |= NFSCTL_UDPBIT)
fe3c582
 #define NFSCTL_TCPSET(_cltbits)       ((_cltbits) |= NFSCTL_TCPBIT)
fe3c582
 
fe3c582
 #define NFSCTL_ANYPROTO(_cltbits)     ((_cltbits) & (NFSCTL_UDPBIT | NFSCTL_TCPBIT))
fe3c582
-#define NFSCTL_ALLBITS (~0)
fe3c582
 
fe3c582
 #endif /* _NFS_NFS_H */
fe3c582
diff --git a/support/include/nfslib.h b/support/include/nfslib.h
fe3c582
index 1498977..ab8b2bf 100644
fe3c582
--- a/support/include/nfslib.h
fe3c582
+++ b/support/include/nfslib.h
fe3c582
@@ -35,29 +35,24 @@
fe3c582
 #ifndef _PATH_IDMAPDCONF
fe3c582
 #define _PATH_IDMAPDCONF	"/etc/idmapd.conf"
fe3c582
 #endif
fe3c582
-#ifndef _PATH_ETAB
fe3c582
-#define _PATH_ETAB		NFS_STATEDIR "/etab"
fe3c582
-#endif
fe3c582
-#ifndef _PATH_ETABTMP
fe3c582
-#define _PATH_ETABTMP		NFS_STATEDIR "/etab.tmp"
fe3c582
-#endif
fe3c582
-#ifndef _PATH_ETABLCK
fe3c582
-#define _PATH_ETABLCK		NFS_STATEDIR "/.etab.lock"
fe3c582
-#endif
fe3c582
-#ifndef _PATH_RMTAB
fe3c582
-#define _PATH_RMTAB		NFS_STATEDIR "/rmtab"
fe3c582
-#endif
fe3c582
-#ifndef _PATH_RMTABTMP
fe3c582
-#define _PATH_RMTABTMP		_PATH_RMTAB ".tmp"
fe3c582
-#endif
fe3c582
-#ifndef _PATH_RMTABLCK
fe3c582
-#define _PATH_RMTABLCK		NFS_STATEDIR "/.rmtab.lock"
fe3c582
-#endif
fe3c582
 #ifndef _PATH_PROC_EXPORTS
fe3c582
 #define	_PATH_PROC_EXPORTS	"/proc/fs/nfs/exports"
fe3c582
 #define	_PATH_PROC_EXPORTS_ALT	"/proc/fs/nfsd/exports"
fe3c582
 #endif
fe3c582
 
fe3c582
+#define ETAB		"etab"
fe3c582
+#define ETABTMP		"etab.tmp"
fe3c582
+#define ETABLCK 	".etab.lock"
fe3c582
+#define RMTAB		"rmtab"
fe3c582
+#define RMTABTMP	"rmtab.tmp"
fe3c582
+#define RMTABLCK	".rmtab.lock"
fe3c582
+
fe3c582
+struct state_paths {
fe3c582
+	char *statefn;
fe3c582
+	char *tmpfn;
fe3c582
+	char *lockfn;
fe3c582
+};
fe3c582
+
fe3c582
 /* Maximum number of security flavors on an export: */
fe3c582
 #define SECFLAVOR_COUNT 8
fe3c582
 
fe3c582
@@ -120,6 +115,10 @@ void			fputrmtabent(FILE *fp, struct rmtabent *xep, long *pos);
fe3c582
 void			fendrmtabent(FILE *fp);
fe3c582
 void			frewindrmtabent(FILE *fp);
fe3c582
 
fe3c582
+_Bool state_setup_basedir(const char *, const char *);
fe3c582
+int setup_state_path_names(const char *, const char *, const char *, const char *, struct state_paths *);
fe3c582
+void free_state_path_names(struct state_paths *);
fe3c582
+
fe3c582
 /* mydaemon */
fe3c582
 void daemon_init(bool fg);
fe3c582
 void daemon_ready(void);
fe3c582
diff --git a/support/misc/Makefile.am b/support/misc/Makefile.am
fe3c582
index 1048580..8936b0d 100644
fe3c582
--- a/support/misc/Makefile.am
fe3c582
+++ b/support/misc/Makefile.am
fe3c582
@@ -1,6 +1,6 @@
fe3c582
 ## Process this file with automake to produce Makefile.in
fe3c582
 
fe3c582
 noinst_LIBRARIES = libmisc.a
fe3c582
-libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c
fe3c582
+libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c file.c
fe3c582
 
fe3c582
 MAINTAINERCLEANFILES = Makefile.in
fe3c582
diff --git a/support/misc/file.c b/support/misc/file.c
fe3c582
new file mode 100644
fe3c582
index 0000000..63597df
fe3c582
--- /dev/null
fe3c582
+++ b/support/misc/file.c
fe3c582
@@ -0,0 +1,110 @@
fe3c582
+/*
fe3c582
+ * Copyright 2009 Oracle.  All rights reserved.
fe3c582
+ * Copyright 2017 Red Hat, Inc.  All rights reserved.
fe3c582
+ *
fe3c582
+ * This file is part of nfs-utils.
fe3c582
+ *
fe3c582
+ * nfs-utils is free software; you can redistribute it and/or modify
fe3c582
+ * it under the terms of the GNU General Public License as published by
fe3c582
+ * the Free Software Foundation; either version 2 of the License, or
fe3c582
+ * (at your option) any later version.
fe3c582
+ *
fe3c582
+ * nfs-utils is distributed in the hope that it will be useful,
fe3c582
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
fe3c582
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
fe3c582
+ * GNU General Public License for more details.
fe3c582
+ *
fe3c582
+ * You should have received a copy of the GNU General Public License
fe3c582
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
fe3c582
+ */
fe3c582
+
fe3c582
+#include <sys/stat.h>
fe3c582
+
fe3c582
+#include <string.h>
fe3c582
+#include <libgen.h>
fe3c582
+#include <stdio.h>
fe3c582
+#include <errno.h>
fe3c582
+#include <dirent.h>
fe3c582
+#include <stdlib.h>
fe3c582
+#include <stdbool.h>
fe3c582
+
fe3c582
+#include "xlog.h"
fe3c582
+#include "misc.h"
fe3c582
+
fe3c582
+/*
fe3c582
+ * Returns a dynamically allocated, '\0'-terminated buffer
fe3c582
+ * containing an appropriate pathname, or NULL if an error
fe3c582
+ * occurs.  Caller must free the returned result with free(3).
fe3c582
+ */
fe3c582
+__attribute__((__malloc__))
fe3c582
+char *
fe3c582
+generic_make_pathname(const char *base, const char *leaf)
fe3c582
+{
fe3c582
+	size_t size;
fe3c582
+	char *path;
fe3c582
+	int len;
fe3c582
+
fe3c582
+	size = strlen(base) + strlen(leaf) + 2;
fe3c582
+	if (size > PATH_MAX)
fe3c582
+		return NULL;
fe3c582
+
fe3c582
+	path = malloc(size);
fe3c582
+	if (path == NULL)
fe3c582
+		return NULL;
fe3c582
+
fe3c582
+	len = snprintf(path, size, "%s/%s", base, leaf);
fe3c582
+	if ((len < 0) || ((size_t)len >= size)) {
fe3c582
+		free(path);
fe3c582
+		return NULL;
fe3c582
+	}
fe3c582
+
fe3c582
+	return path;
fe3c582
+}
fe3c582
+
fe3c582
+
fe3c582
+/**
fe3c582
+ * generic_setup_basedir - set up basedir
fe3c582
+ * @progname: C string containing name of program, for error messages
fe3c582
+ * @parentdir: C string containing pathname to on-disk state, or NULL
fe3c582
+ * @base: character buffer to contain the basedir that is set up
fe3c582
+ * @baselen: size of @base in bytes
fe3c582
+ *
fe3c582
+ * This runs before logging is set up, so error messages are directed
fe3c582
+ * to stderr.
fe3c582
+ *
fe3c582
+ * Returns true and sets up our basedir, if @parentdir was valid
fe3c582
+ * and usable; otherwise false is returned.
fe3c582
+ */
fe3c582
+_Bool
fe3c582
+generic_setup_basedir(const char *progname, const char *parentdir, char *base,
fe3c582
+		      const size_t baselen)
fe3c582
+{
fe3c582
+	static char buf[PATH_MAX];
fe3c582
+	struct stat st;
fe3c582
+	char *path;
fe3c582
+
fe3c582
+	/* First: test length of name and whether it exists */
fe3c582
+	if ((strlen(parentdir) >= baselen) || (strlen(parentdir) >= PATH_MAX)) {
fe3c582
+		(void)fprintf(stderr, "%s: Directory name too long: %s",
fe3c582
+				progname, parentdir);
fe3c582
+		return false;
fe3c582
+	}
fe3c582
+	if (lstat(parentdir, &st) == -1) {
fe3c582
+		(void)fprintf(stderr, "%s: Failed to stat %s: %s",
fe3c582
+				progname, parentdir, strerror(errno));
fe3c582
+		return false;
fe3c582
+	}
fe3c582
+
fe3c582
+	/* Ensure we have a clean directory pathname */
fe3c582
+	strncpy(buf, parentdir, sizeof(buf));
fe3c582
+	path = dirname(buf);
fe3c582
+	if (*path == '.') {
fe3c582
+		(void)fprintf(stderr, "%s: Unusable directory %s",
fe3c582
+				progname, parentdir);
fe3c582
+		return false;
fe3c582
+	}
fe3c582
+
fe3c582
+	xlog(D_CALL, "Using %s as the state directory", parentdir);
fe3c582
+	strcpy(base, parentdir);
fe3c582
+	return true;
fe3c582
+}
fe3c582
diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c
fe3c582
index e5e2579..9912afa 100644
fe3c582
--- a/support/nfs/cacheio.c
fe3c582
+++ b/support/nfs/cacheio.c
fe3c582
@@ -27,6 +27,8 @@
fe3c582
 #include <time.h>
fe3c582
 #include <errno.h>
fe3c582
 
fe3c582
+extern struct state_paths etab;
fe3c582
+
fe3c582
 void qword_add(char **bpp, int *lp, char *str)
fe3c582
 {
fe3c582
 	char *bp = *bpp;
fe3c582
@@ -199,7 +201,7 @@ int qword_get_uint(char **bpp, unsigned int *anint)
fe3c582
 }
fe3c582
 
fe3c582
 /* flush the kNFSd caches.
fe3c582
- * Set the flush time to the mtime of _PATH_ETAB or
fe3c582
+ * Set the flush time to the mtime of the etab state file or
fe3c582
  * if force, to now.
fe3c582
  * the caches to flush are:
fe3c582
  *  auth.unix.ip nfsd.export nfsd.fh
fe3c582
@@ -228,7 +230,7 @@ cache_flush(int force)
fe3c582
 	};
fe3c582
 	now = time(0);
fe3c582
 	if (force ||
fe3c582
-	    stat(_PATH_ETAB, &stb) != 0 ||
fe3c582
+	    stat(etab.statefn, &stb) != 0 ||
fe3c582
 	    stb.st_mtime > now)
fe3c582
 		stb.st_mtime = time(0);
fe3c582
 	
fe3c582
diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
fe3c582
index e717c1e..8690218 100644
fe3c582
--- a/support/nfs/conffile.c
fe3c582
+++ b/support/nfs/conffile.c
fe3c582
@@ -30,6 +30,10 @@
fe3c582
  * This code was written under funding by Ericsson Radio Systems.
fe3c582
  */
fe3c582
 
fe3c582
+#ifdef HAVE_CONFIG_H
fe3c582
+#include <config.h>
fe3c582
+#endif
fe3c582
+
fe3c582
 #include <sys/param.h>
fe3c582
 #include <sys/mman.h>
fe3c582
 #include <sys/socket.h>
fe3c582
@@ -52,9 +56,11 @@
fe3c582
 #pragma GCC visibility push(hidden)
fe3c582
 
fe3c582
 static void conf_load_defaults(void);
fe3c582
-static int conf_load(int trans, char *path);
fe3c582
+static char * conf_load(const char *path);
fe3c582
 static int conf_set(int , char *, char *, char *, 
fe3c582
 	char *, int , int );
fe3c582
+static void conf_parse(int trans, char *buf, 
fe3c582
+	char **section, char **subsection);
fe3c582
 
fe3c582
 struct conf_trans {
fe3c582
 	TAILQ_ENTRY (conf_trans) link;
fe3c582
@@ -73,8 +79,10 @@ TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue;
fe3c582
 /*
fe3c582
  * Radix-64 Encoding.
fe3c582
  */
fe3c582
+#if 0
fe3c582
 static const uint8_t bin2asc[]
fe3c582
   = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
fe3c582
+#endif
fe3c582
 
fe3c582
 static const uint8_t asc2bin[] =
fe3c582
 {
fe3c582
@@ -105,7 +113,6 @@ struct conf_binding {
fe3c582
   int is_default;
fe3c582
 };
fe3c582
 
fe3c582
-char *conf_path;
fe3c582
 LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256];
fe3c582
 
fe3c582
 static __inline__ uint8_t
fe3c582
@@ -199,7 +206,6 @@ conf_set_now(char *section, char *arg, char *tag,
fe3c582
 	node->tag = strdup(tag);
fe3c582
 	node->value = strdup(value);
fe3c582
 	node->is_default = is_default;
fe3c582
-
fe3c582
 	LIST_INSERT_HEAD(&conf_bindings[conf_hash (section)], node, link);
fe3c582
 	return 0;
fe3c582
 }
fe3c582
@@ -209,17 +215,10 @@ conf_set_now(char *section, char *arg, char *tag,
fe3c582
  * headers and feed tag-value pairs into our configuration database.
fe3c582
  */
fe3c582
 static void
fe3c582
-conf_parse_line(int trans, char *line, size_t sz)
fe3c582
+conf_parse_line(int trans, char *line, int lineno, char **section, char **subsection)
fe3c582
 {
fe3c582
 	char *val, *ptr;
fe3c582
-	size_t i;
fe3c582
-	size_t j;
fe3c582
-	static char *section = 0;
fe3c582
-	static char *arg = 0;
fe3c582
-	static int ln = 0;
fe3c582
 
fe3c582
-	/* Lines starting with '#' or ';' are comments.  */
fe3c582
-	ln++;
fe3c582
 	/* Ignore blank lines */
fe3c582
 	if (*line == '\0')
fe3c582
 		return;
fe3c582
@@ -228,123 +227,188 @@ conf_parse_line(int trans, char *line, size_t sz)
fe3c582
 	while (isblank(*line)) 
fe3c582
 		line++;
fe3c582
 
fe3c582
+	/* Lines starting with '#' or ';' are comments.  */
fe3c582
 	if (*line == '#' || *line == ';')
fe3c582
 		return;
fe3c582
 
fe3c582
 	/* '[section]' parsing...  */
fe3c582
 	if (*line == '[') {
fe3c582
 		line++;
fe3c582
+
fe3c582
+		if (*section) {
fe3c582
+			free(*section);
fe3c582
+			*section = NULL;
fe3c582
+		}
fe3c582
+		if (*subsection) {
fe3c582
+			free(*subsection);
fe3c582
+			*subsection = NULL;
fe3c582
+		}
fe3c582
+
fe3c582
 		/* Strip off any blanks after '[' */
fe3c582
 		while (isblank(*line)) 
fe3c582
 			line++;
fe3c582
-		for (i = 0; i < sz; i++) {
fe3c582
-			if (line[i] == ']') {
fe3c582
-				break;
fe3c582
-			}
fe3c582
-		}
fe3c582
-		if (section)
fe3c582
-			free(section);
fe3c582
-		if (i == sz) {
fe3c582
+
fe3c582
+		/* find the closing ] */
fe3c582
+		ptr = strchr(line, ']');
fe3c582
+		if (ptr == NULL) {
fe3c582
 			xlog_warn("config file error: line %d: "
fe3c582
- 				"non-matched ']', ignoring until next section", ln);
fe3c582
-			section = 0;
fe3c582
+ 				"non-matched ']', ignoring until next section", lineno);
fe3c582
 			return;
fe3c582
 		}
fe3c582
+
fe3c582
+		/* just ignore everything after the closing ] */
fe3c582
+		*(ptr--) = '\0';
fe3c582
+
fe3c582
 		/* Strip off any blanks before ']' */
fe3c582
-		val = line;
fe3c582
-		j=0;
fe3c582
-		while (*val && !isblank(*val)) 
fe3c582
-			val++, j++;
fe3c582
-		if (*val)
fe3c582
-			i = j;
fe3c582
-		section = malloc(i+1);
fe3c582
-		if (!section) {
fe3c582
-			xlog_warn("conf_parse_line: %d: malloc (%lu) failed", ln,
fe3c582
-						(unsigned long)i);
fe3c582
+		while (ptr >= line && isblank(*ptr)) 
fe3c582
+			*(ptr--)='\0';
fe3c582
+
fe3c582
+		/* look for an arg to split from the section name */
fe3c582
+		val = strchr(line, '"');
fe3c582
+		if (val != NULL) {
fe3c582
+			ptr = val - 1;
fe3c582
+			*(val++) = '\0';
fe3c582
+
fe3c582
+			/* trim away any whitespace before the " */
fe3c582
+			while (ptr > line && isblank(*ptr))
fe3c582
+				*(ptr--)='\0';
fe3c582
+		}
fe3c582
+
fe3c582
+		/* copy the section name */
fe3c582
+		*section = strdup(line);
fe3c582
+		if (!*section) {
fe3c582
+			xlog_warn("conf_parse_line: %d: malloc failed", lineno);
fe3c582
 			return;
fe3c582
 		}
fe3c582
-		strncpy(section, line, i);
fe3c582
-		section[i] = '\0';
fe3c582
 
fe3c582
-		if (arg) 
fe3c582
-			free(arg);
fe3c582
-		arg = 0;
fe3c582
+		/* there is no arg, we are done */
fe3c582
+		if (val == NULL) return;
fe3c582
 
fe3c582
+		/* check for the closing " */
fe3c582
 		ptr = strchr(val, '"');
fe3c582
-		if (ptr == NULL)
fe3c582
-			return;
fe3c582
-		line = ++ptr;
fe3c582
-		while (*ptr && *ptr != '"' && *ptr != ']')
fe3c582
-			ptr++;
fe3c582
-		if (*ptr == '\0' || *ptr == ']') {
fe3c582
+		if (ptr == NULL) {
fe3c582
 			xlog_warn("config file error: line %d: "
fe3c582
- 				"non-matched '\"', ignoring until next section", ln);
fe3c582
-		}  else {
fe3c582
-			*ptr = '\0';
fe3c582
-			arg = strdup(line);
fe3c582
-			if (!arg) 
fe3c582
-				xlog_warn("conf_parse_line: %d: malloc arg failed", ln);
fe3c582
+ 				"non-matched '\"', ignoring until next section", lineno);
fe3c582
+			return;
fe3c582
 		}
fe3c582
+		*ptr = '\0';
fe3c582
+		*subsection = strdup(val);
fe3c582
+		if (!*subsection) 
fe3c582
+			xlog_warn("conf_parse_line: %d: malloc arg failed", lineno);
fe3c582
 		return;
fe3c582
 	}
fe3c582
 
fe3c582
 	/* Deal with assignments.  */
fe3c582
-	for (i = 0; i < sz; i++) {
fe3c582
-		if (line[i] == '=') {
fe3c582
-			/* If no section, we are ignoring the lines.  */
fe3c582
-			if (!section) {
fe3c582
+	ptr = strchr(line, '=');
fe3c582
+
fe3c582
+	/* not an assignment line */
fe3c582
+	if (ptr == NULL) {
fe3c582
+		/* Other non-empty lines are weird.  */
fe3c582
+		if (line[strspn(line, " \t")])
fe3c582
 			xlog_warn("config file error: line %d: "
fe3c582
-				"ignoring line due to no section", ln);
fe3c582
-				return;
fe3c582
-			}
fe3c582
-			line[strcspn (line, " \t=")] = '\0';
fe3c582
-			val = line + i + 1 + strspn (line + i + 1, " \t");
fe3c582
-
fe3c582
-			if (val[0] == '"') {
fe3c582
-				val ++;
fe3c582
-				j = strcspn(val, "\"");
fe3c582
-				val[j] = 0;
fe3c582
-			} else if (val[0] == '\'') {
fe3c582
-				val ++;
fe3c582
-				j = strcspn(val, "'");
fe3c582
-				val[j] = 0;
fe3c582
-			} else {
fe3c582
-				/* Skip trailing spaces and comments */
fe3c582
-				for (j = 0; val[j]; j++) {
fe3c582
-					if ((val[j] == '#' || val[j] == ';')
fe3c582
-					    && (j == 0 || isspace(val[j-1]))) {
fe3c582
-						val[j] = '\0';
fe3c582
-						break;
fe3c582
-					}
fe3c582
-				}
fe3c582
-				while (j && isspace(val[j-1]))
fe3c582
-					val[--j] = '\0';
fe3c582
-			}
fe3c582
-			if (strcasecmp(line, "include") == 0)
fe3c582
-				conf_load(trans, val);
fe3c582
-			else
fe3c582
-				/* XXX Perhaps should we not ignore errors?  */
fe3c582
-				conf_set(trans, section, arg, line, val, 0, 0);
fe3c582
+				"line not empty and not an assignment", lineno);
fe3c582
+		return;
fe3c582
+	}
fe3c582
+
fe3c582
+	/* If no section, we are ignoring the line.  */
fe3c582
+	if (!*section) {
fe3c582
+		xlog_warn("config file error: line %d: "
fe3c582
+			"ignoring line due to no section", lineno);
fe3c582
+		return;
fe3c582
+	}
fe3c582
+
fe3c582
+	val = ptr + 1;
fe3c582
+	*(ptr--) = '\0';
fe3c582
+
fe3c582
+	/* strip spaces before and after the = */
fe3c582
+	while (ptr >= line && isblank(*ptr))
fe3c582
+		*(ptr--)='\0';
fe3c582
+	while (*val != '\0' && isblank(*val))
fe3c582
+		val++;
fe3c582
+
fe3c582
+	if (*val == '"') {
fe3c582
+		val++;
fe3c582
+		ptr = strchr(val, '"');
fe3c582
+		if (ptr == NULL) {
fe3c582
+			xlog_warn("config file error: line %d: "
fe3c582
+				"unmatched quotes", lineno);
fe3c582
+			return;
fe3c582
+		}
fe3c582
+		*ptr = '\0';
fe3c582
+	} else
fe3c582
+	if (*val == '\'') {
fe3c582
+		val++;
fe3c582
+		ptr = strchr(val, '\'');
fe3c582
+		if (ptr == NULL) {
fe3c582
+			xlog_warn("config file error: line %d: "
fe3c582
+				"unmatched quotes", lineno);
fe3c582
 			return;
fe3c582
 		}
fe3c582
+		*ptr = '\0';
fe3c582
+	} else {
fe3c582
+		/* Trim any trailing spaces and comments */
fe3c582
+		if ((ptr=strchr(val, '#'))!=NULL)
fe3c582
+			*ptr = '\0';
fe3c582
+		if ((ptr=strchr(val, ';'))!=NULL)
fe3c582
+			*ptr = '\0';
fe3c582
+
fe3c582
+		ptr = val + strlen(val) - 1;
fe3c582
+		while (ptr > val && isspace(*ptr))
fe3c582
+			*(ptr--) = '\0';
fe3c582
 	}
fe3c582
-	/* Other non-empty lines are weird.  */
fe3c582
-	i = strspn(line, " \t");
fe3c582
-	if (line[i])
fe3c582
-		xlog_warn("config file error: line %d:", ln);
fe3c582
 
fe3c582
-	return;
fe3c582
+	if (*line == '\0') {
fe3c582
+		xlog_warn("config file error: line %d: "
fe3c582
+			"missing tag in assignment", lineno);
fe3c582
+		return;
fe3c582
+	}
fe3c582
+	if (*val == '\0') {
fe3c582
+		xlog_warn("config file error: line %d: "
fe3c582
+			"missing value in assignment", lineno);
fe3c582
+		return;
fe3c582
+	}
fe3c582
+
fe3c582
+	if (strcasecmp(line, "include")==0) {
fe3c582
+		/* load and parse subordinate config files */
fe3c582
+		char * subconf = conf_load(val);
fe3c582
+		if (subconf == NULL) {
fe3c582
+			xlog_warn("config file error: line %d: "
fe3c582
+			"error loading included config", lineno);
fe3c582
+			return;
fe3c582
+		}
fe3c582
+
fe3c582
+		/* copy the section data so the included file can inherit it
fe3c582
+		 * without accidentally changing it for us */
fe3c582
+		char * inc_section = NULL;
fe3c582
+		char * inc_subsection = NULL;
fe3c582
+		if (*section != NULL) {
fe3c582
+			inc_section = strdup(*section);
fe3c582
+			if (*subsection != NULL)
fe3c582
+				inc_subsection = strdup(*subsection);
fe3c582
+		}
fe3c582
+
fe3c582
+		conf_parse(trans, subconf, &inc_section, &inc_subsection);
fe3c582
+
fe3c582
+		if (inc_section) free(inc_section);
fe3c582
+		if (inc_subsection) free(inc_subsection);
fe3c582
+		free(subconf);
fe3c582
+	} else {
fe3c582
+		/* XXX Perhaps should we not ignore errors?  */
fe3c582
+		conf_set(trans, *section, *subsection, line, val, 0, 0);
fe3c582
+	}
fe3c582
 }
fe3c582
 
fe3c582
 /* Parse the mapped configuration file.  */
fe3c582
 static void
fe3c582
-conf_parse(int trans, char *buf, size_t sz)
fe3c582
+conf_parse(int trans, char *buf, char **section, char **subsection)
fe3c582
 {
fe3c582
 	char *cp = buf;
fe3c582
-	char *bufend = buf + sz;
fe3c582
+	char *bufend = NULL;
fe3c582
 	char *line;
fe3c582
+	int lineno = 0;
fe3c582
 
fe3c582
 	line = cp;
fe3c582
+	bufend = buf + strlen(buf);
fe3c582
 	while (cp < bufend) {
fe3c582
 		if (*cp == '\n') {
fe3c582
 			/* Check for escaped newlines.  */
fe3c582
@@ -352,7 +416,8 @@ conf_parse(int trans, char *buf, size_t sz)
fe3c582
 				*(cp - 1) = *cp = ' ';
fe3c582
 			else {
fe3c582
 				*cp = '\0';
fe3c582
-				conf_parse_line(trans, line, cp - line);
fe3c582
+				lineno++;
fe3c582
+				conf_parse_line(trans, line, lineno, section, subsection);
fe3c582
 				line = cp + 1;
fe3c582
 			}
fe3c582
 		}
fe3c582
@@ -369,33 +434,21 @@ conf_load_defaults(void)
fe3c582
 	return;
fe3c582
 }
fe3c582
 
fe3c582
-void
fe3c582
-conf_init (void)
fe3c582
-{
fe3c582
-	unsigned int i;
fe3c582
-
fe3c582
-	for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
fe3c582
-		LIST_INIT (&conf_bindings[i]);
fe3c582
-
fe3c582
-	TAILQ_INIT (&conf_trans_queue);
fe3c582
-	conf_reinit();
fe3c582
-}
fe3c582
-
fe3c582
-static int
fe3c582
-conf_load(int trans, char *path)
fe3c582
+static char *
fe3c582
+conf_load(const char *path)
fe3c582
 {
fe3c582
 	struct stat sb;
fe3c582
 	if ((stat (path, &sb) == 0) || (errno != ENOENT)) {
fe3c582
-		char *new_conf_addr;
fe3c582
+		char *new_conf_addr = NULL;
fe3c582
 		size_t sz = sb.st_size;
fe3c582
 		int fd = open (path, O_RDONLY, 0);
fe3c582
 
fe3c582
 		if (fd == -1) {
fe3c582
 			xlog_warn("conf_reinit: open (\"%s\", O_RDONLY) failed", path);
fe3c582
-			return -1;
fe3c582
+			return NULL;
fe3c582
 		}
fe3c582
 
fe3c582
-		new_conf_addr = malloc(sz);
fe3c582
+		new_conf_addr = malloc(sz+1);
fe3c582
 		if (!new_conf_addr) {
fe3c582
 			xlog_warn("conf_reinit: malloc (%lu) failed", (unsigned long)sz);
fe3c582
 			goto fail;
fe3c582
@@ -410,42 +463,103 @@ conf_load(int trans, char *path)
fe3c582
 		close(fd);
fe3c582
 
fe3c582
 		/* XXX Should we not care about errors and rollback?  */
fe3c582
-		conf_parse(trans, new_conf_addr, sz);
fe3c582
-		free(new_conf_addr);
fe3c582
-		return 0;
fe3c582
+		new_conf_addr[sz] = '\0';
fe3c582
+		return new_conf_addr;
fe3c582
 	fail:
fe3c582
 		close(fd);
fe3c582
-		free(new_conf_addr);
fe3c582
+		if (new_conf_addr) free(new_conf_addr);
fe3c582
+	}
fe3c582
+	return NULL;
fe3c582
+}
fe3c582
+
fe3c582
+/* remove and free up any existing config state */
fe3c582
+static void conf_free_bindings(void)
fe3c582
+{
fe3c582
+	unsigned int i;
fe3c582
+	for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) {
fe3c582
+		struct conf_binding *cb, *next;
fe3c582
+
fe3c582
+		cb = LIST_FIRST(&conf_bindings[i]);
fe3c582
+		for (; cb; cb = next) {
fe3c582
+			next = LIST_NEXT(cb, link);
fe3c582
+			LIST_REMOVE(cb, link);
fe3c582
+			free(cb->section);
fe3c582
+			free(cb->arg);
fe3c582
+			free(cb->tag);
fe3c582
+			free(cb->value);
fe3c582
+			free(cb);
fe3c582
+		}
fe3c582
+		LIST_INIT(&conf_bindings[i]);
fe3c582
 	}
fe3c582
-	return -1;
fe3c582
 }
fe3c582
 
fe3c582
 /* Open the config file and map it into our address space, then parse it.  */
fe3c582
-void
fe3c582
-conf_reinit(void)
fe3c582
+static void
fe3c582
+conf_reinit(const char *conf_file)
fe3c582
 {
fe3c582
-	struct conf_binding *cb = 0;
fe3c582
 	int trans;
fe3c582
-	unsigned int i;
fe3c582
+	char * conf_data;
fe3c582
 
fe3c582
 	trans = conf_begin();
fe3c582
-	if (conf_load(trans, conf_path) < 0)
fe3c582
+	conf_data = conf_load(conf_file);
fe3c582
+
fe3c582
+	if (conf_data == NULL)
fe3c582
 		return;
fe3c582
 
fe3c582
 	/* Load default configuration values.  */
fe3c582
 	conf_load_defaults();
fe3c582
 
fe3c582
+	/* Parse config contents into the transaction queue */
fe3c582
+	char *section = NULL;
fe3c582
+	char *subsection = NULL;
fe3c582
+	conf_parse(trans, conf_data, &section, &subsection);
fe3c582
+	if (section) free(section);
fe3c582
+	if (subsection) free(subsection);
fe3c582
+	free(conf_data);
fe3c582
+
fe3c582
 	/* Free potential existing configuration.  */
fe3c582
-	for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) {
fe3c582
-		cb = LIST_FIRST (&conf_bindings[i]);
fe3c582
-		for (; cb; cb = LIST_FIRST (&conf_bindings[i]))
fe3c582
-			conf_remove_now(cb->section, cb->tag);
fe3c582
-	}
fe3c582
+	conf_free_bindings();
fe3c582
 
fe3c582
+	/* Apply the new configuration values */
fe3c582
 	conf_end(trans, 1);
fe3c582
 	return;
fe3c582
 }
fe3c582
 
fe3c582
+void
fe3c582
+conf_init (const char *conf_file)
fe3c582
+{
fe3c582
+	unsigned int i;
fe3c582
+
fe3c582
+	for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
fe3c582
+		LIST_INIT (&conf_bindings[i]);
fe3c582
+
fe3c582
+	TAILQ_INIT (&conf_trans_queue);
fe3c582
+
fe3c582
+	if (conf_file == NULL) conf_file=NFS_CONFFILE;
fe3c582
+	conf_reinit(conf_file);
fe3c582
+}
fe3c582
+
fe3c582
+/* 
fe3c582
+ * Empty the config and free up any used memory 
fe3c582
+ */
fe3c582
+void
fe3c582
+conf_cleanup(void)
fe3c582
+{
fe3c582
+	conf_free_bindings();
fe3c582
+
fe3c582
+	struct conf_trans *node, *next;
fe3c582
+	for (node = TAILQ_FIRST(&conf_trans_queue); node; node = next) {
fe3c582
+		next = TAILQ_NEXT(node, link);
fe3c582
+		TAILQ_REMOVE (&conf_trans_queue, node, link);
fe3c582
+		if (node->section) free(node->section);
fe3c582
+		if (node->arg) free(node->arg);
fe3c582
+		if (node->tag) free(node->tag);
fe3c582
+		if (node->value) free(node->value);
fe3c582
+		free (node);
fe3c582
+	}
fe3c582
+	TAILQ_INIT(&conf_trans_queue);
fe3c582
+}
fe3c582
+
fe3c582
 /*
fe3c582
  * Return the numeric value denoted by TAG in section SECTION or DEF
fe3c582
  * if that tag does not exist.
fe3c582
@@ -533,7 +647,7 @@ retry:
fe3c582
 				 * or from environment
fe3c582
 				 */
fe3c582
 				char *env = getenv(cb->value+1);
fe3c582
-				if (env)
fe3c582
+				if (env && *env)
fe3c582
 					return env;
fe3c582
 				section = "environment";
fe3c582
 				tag = cb->value + 1;
fe3c582
diff --git a/support/nfs/rmtab.c b/support/nfs/rmtab.c
fe3c582
index 59dfbdf..2ecb2cc 100644
fe3c582
--- a/support/nfs/rmtab.c
fe3c582
+++ b/support/nfs/rmtab.c
fe3c582
@@ -33,12 +33,14 @@
fe3c582
 
fe3c582
 static FILE	*rmfp = NULL;
fe3c582
 
fe3c582
+extern struct state_paths rmtab;
fe3c582
+
fe3c582
 int
fe3c582
 setrmtabent(char *type)
fe3c582
 {
fe3c582
 	if (rmfp)
fe3c582
 		fclose(rmfp);
fe3c582
-	rmfp = fsetrmtabent(_PATH_RMTAB, type);
fe3c582
+	rmfp = fsetrmtabent(rmtab.statefn, type);
fe3c582
 	return (rmfp != NULL);
fe3c582
 }
fe3c582
 
fe3c582
diff --git a/support/nsm/file.c b/support/nsm/file.c
fe3c582
index aafa755..52f5401 100644
fe3c582
--- a/support/nsm/file.c
fe3c582
+++ b/support/nsm/file.c
fe3c582
@@ -88,6 +88,7 @@
fe3c582
 
fe3c582
 #include "xlog.h"
fe3c582
 #include "nsm.h"
fe3c582
+#include "misc.h"
fe3c582
 
fe3c582
 #define RPCARGSLEN	(4 * (8 + 1))
fe3c582
 #define LINELEN		(RPCARGSLEN + SM_PRIV_SIZE * 2 + 1)
fe3c582
@@ -170,25 +171,7 @@ __attribute__((__malloc__))
fe3c582
 static char *
fe3c582
 nsm_make_pathname(const char *directory)
fe3c582
 {
fe3c582
-	size_t size;
fe3c582
-	char *path;
fe3c582
-	int len;
fe3c582
-
fe3c582
-	size = strlen(nsm_base_dirname) + strlen(directory) + 2;
fe3c582
-	if (size > PATH_MAX)
fe3c582
-		return NULL;
fe3c582
-
fe3c582
-	path = malloc(size);
fe3c582
-	if (path == NULL)
fe3c582
-		return NULL;
fe3c582
-
fe3c582
-	len = snprintf(path, size, "%s/%s", nsm_base_dirname, directory);
fe3c582
-	if (error_check(len, size)) {
fe3c582
-		free(path);
fe3c582
-		return NULL;
fe3c582
-	}
fe3c582
-
fe3c582
-	return path;
fe3c582
+	return generic_make_pathname(nsm_base_dirname, directory);
fe3c582
 }
fe3c582
 
fe3c582
 /*
fe3c582
@@ -293,29 +276,8 @@ out:
fe3c582
 _Bool
fe3c582
 nsm_setup_pathnames(const char *progname, const char *parentdir)
fe3c582
 {
fe3c582
-	static char buf[PATH_MAX];
fe3c582
-	struct stat st;
fe3c582
-	char *path;
fe3c582
-
fe3c582
-	/* First: test length of name and whether it exists */
fe3c582
-	if (lstat(parentdir, &st) == -1) {
fe3c582
-		(void)fprintf(stderr, "%s: Failed to stat %s: %s",
fe3c582
-				progname, parentdir, strerror(errno));
fe3c582
-		return false;
fe3c582
-	}
fe3c582
-
fe3c582
-	/* Ensure we have a clean directory pathname */
fe3c582
-	strncpy(buf, parentdir, sizeof(buf));
fe3c582
-	path = dirname(buf);
fe3c582
-	if (*path == '.') {
fe3c582
-		(void)fprintf(stderr, "%s: Unusable directory %s",
fe3c582
-				progname, parentdir);
fe3c582
-		return false;
fe3c582
-	}
fe3c582
-
fe3c582
-	xlog(D_CALL, "Using %s as the state directory", parentdir);
fe3c582
-	strncpy(nsm_base_dirname, parentdir, sizeof(nsm_base_dirname));
fe3c582
-	return true;
fe3c582
+	return generic_setup_basedir(progname, parentdir, nsm_base_dirname,
fe3c582
+				     PATH_MAX);
fe3c582
 }
fe3c582
 
fe3c582
 /**
fe3c582
diff --git a/systemd/Makefile.am b/systemd/Makefile.am
fe3c582
index 0d15b9f..eef53c4 100644
fe3c582
--- a/systemd/Makefile.am
fe3c582
+++ b/systemd/Makefile.am
fe3c582
@@ -4,6 +4,7 @@ MAINTAINERCLEANFILES = Makefile.in
fe3c582
 
fe3c582
 unit_files =  \
fe3c582
     nfs-client.target \
fe3c582
+    rpc_pipefs.target \
fe3c582
     \
fe3c582
     nfs-mountd.service \
fe3c582
     nfs-server.service \
fe3c582
@@ -42,14 +43,23 @@ EXTRA_DIST = $(unit_files) $(man5_MANS) $(man7_MANS)
fe3c582
 unit_dir = /usr/lib/systemd/system
fe3c582
 generator_dir = /usr/lib/systemd/system-generators
fe3c582
 
fe3c582
-EXTRA_PROGRAMS	= nfs-server-generator
fe3c582
+EXTRA_PROGRAMS	= nfs-server-generator rpc-pipefs-generator
fe3c582
 genexecdir = $(generator_dir)
fe3c582
+
fe3c582
+COMMON_SRCS = systemd.c systemd.h
fe3c582
+
fe3c582
+nfs_server_generator_SOURCES = $(COMMON_SRCS) nfs-server-generator.c
fe3c582
+
fe3c582
+rpc_pipefs_generator_SOURCES = $(COMMON_SRCS) rpc-pipefs-generator.c
fe3c582
+
fe3c582
 nfs_server_generator_LDADD = ../support/export/libexport.a \
fe3c582
 			     ../support/nfs/libnfs.a \
fe3c582
 			     ../support/misc/libmisc.a
fe3c582
 
fe3c582
+rpc_pipefs_generator_LDADD = ../support/nfs/libnfs.a
fe3c582
+
fe3c582
 if INSTALL_SYSTEMD
fe3c582
-genexec_PROGRAMS = nfs-server-generator
fe3c582
+genexec_PROGRAMS = nfs-server-generator rpc-pipefs-generator
fe3c582
 install-data-hook: $(unit_files)
fe3c582
 	mkdir -p $(DESTDIR)/$(unitdir)
fe3c582
 	cp $(unit_files) $(DESTDIR)/$(unitdir)
fe3c582
diff --git a/systemd/nfs-blkmap.service b/systemd/nfs-blkmap.service
fe3c582
index ddc324e..2bbcee6 100644
fe3c582
--- a/systemd/nfs-blkmap.service
fe3c582
+++ b/systemd/nfs-blkmap.service
fe3c582
@@ -2,8 +2,8 @@
fe3c582
 Description=pNFS block layout mapping daemon
fe3c582
 DefaultDependencies=no
fe3c582
 Conflicts=umount.target
fe3c582
-After=var-lib-nfs-rpc_pipefs.mount
fe3c582
-Requires=var-lib-nfs-rpc_pipefs.mount
fe3c582
+After=rpc_pipefs.target
fe3c582
+Requires=rpc_pipefs.target
fe3c582
 
fe3c582
 PartOf=nfs-utils.service
fe3c582
 
fe3c582
diff --git a/systemd/nfs-idmapd.service b/systemd/nfs-idmapd.service
fe3c582
index acca86b..f38fe52 100644
fe3c582
--- a/systemd/nfs-idmapd.service
fe3c582
+++ b/systemd/nfs-idmapd.service
fe3c582
@@ -1,8 +1,8 @@
fe3c582
 [Unit]
fe3c582
 Description=NFSv4 ID-name mapping service
fe3c582
 DefaultDependencies=no
fe3c582
-Requires=var-lib-nfs-rpc_pipefs.mount
fe3c582
-After=var-lib-nfs-rpc_pipefs.mount local-fs.target
fe3c582
+Requires=rpc_pipefs.target
fe3c582
+After=rpc_pipefs.target local-fs.target
fe3c582
 
fe3c582
 BindsTo=nfs-server.service
fe3c582
 
fe3c582
diff --git a/systemd/nfs-mountd.service b/systemd/nfs-mountd.service
fe3c582
index 15e828b..e8ece53 100644
fe3c582
--- a/systemd/nfs-mountd.service
fe3c582
+++ b/systemd/nfs-mountd.service
fe3c582
@@ -2,8 +2,10 @@
fe3c582
 Description=NFS Mount Daemon
fe3c582
 DefaultDependencies=no
fe3c582
 Requires=proc-fs-nfsd.mount
fe3c582
+Wants=network-online.target
fe3c582
 After=proc-fs-nfsd.mount
fe3c582
-After=network.target local-fs.target
fe3c582
+After=network-online.target local-fs.target
fe3c582
+After=rpcbind.socket
fe3c582
 BindsTo=nfs-server.service
fe3c582
 
fe3c582
 [Service]
fe3c582
diff --git a/systemd/nfs-server-generator.c b/systemd/nfs-server-generator.c
fe3c582
index cc99969..737f109 100644
fe3c582
--- a/systemd/nfs-server-generator.c
fe3c582
+++ b/systemd/nfs-server-generator.c
fe3c582
@@ -29,6 +29,7 @@
fe3c582
 #include "misc.h"
fe3c582
 #include "nfslib.h"
fe3c582
 #include "exportfs.h"
fe3c582
+#include "systemd.h"
fe3c582
 
fe3c582
 /* A simple "set of strings" to remove duplicates
fe3c582
  * found in /etc/exports
fe3c582
@@ -55,38 +56,31 @@ static int is_unique(struct list **lp, char *path)
fe3c582
 	return 1;
fe3c582
 }
fe3c582
 
fe3c582
-/* We need to convert a path name to a systemd unit
fe3c582
- * name.  This requires some translation ('/' -> '-')
fe3c582
- * and some escaping.
fe3c582
- */
fe3c582
-static void systemd_escape(FILE *f, char *path)
fe3c582
+static int has_noauto_flag(char *path)
fe3c582
 {
fe3c582
-	while (*path == '/')
fe3c582
-		path++;
fe3c582
-	if (!*path) {
fe3c582
-		/* "/" becomes "-", otherwise leading "/" is ignored */
fe3c582
-		fputs("-", f);
fe3c582
-		return;
fe3c582
-	}
fe3c582
-	while (*path) {
fe3c582
-		char c = *path++;
fe3c582
-
fe3c582
-		if (c == '/') {
fe3c582
-			/* multiple non-trailing slashes become '-' */
fe3c582
-			while (*path == '/')
fe3c582
-				path++;
fe3c582
-			if (*path)
fe3c582
-				fputs("-", f);
fe3c582
-		} else if (isalnum(c) || c == ':' || c == '.')
fe3c582
-			fputc(c, f);
fe3c582
-		else
fe3c582
-			fprintf(f, "\\x%02x", c & 0xff);
fe3c582
+	FILE		*fstab;
fe3c582
+	struct mntent	*mnt;
fe3c582
+
fe3c582
+	fstab = setmntent("/etc/fstab", "r");
fe3c582
+	if (!fstab)
fe3c582
+		return 0;
fe3c582
+
fe3c582
+	while ((mnt = getmntent(fstab)) != NULL) {
fe3c582
+		int l = strlen(mnt->mnt_dir);
fe3c582
+		if (strncmp(mnt->mnt_dir, path, l) != 0)
fe3c582
+			continue;
fe3c582
+		if (path[l] && path[l] != '/')
fe3c582
+			continue;
fe3c582
+		if (hasmntopt(mnt, "noauto"))
fe3c582
+			break;
fe3c582
 	}
fe3c582
+	fclose(fstab);
fe3c582
+	return mnt != NULL;
fe3c582
 }
fe3c582
 
fe3c582
 int main(int argc, char *argv[])
fe3c582
 {
fe3c582
-	char		*path;
fe3c582
+	char		*path, *spath;
fe3c582
 	char		dirbase[] = "/nfs-server.service.d";
fe3c582
 	char		filebase[] = "/order-with-mounts.conf";
fe3c582
 	nfs_export	*exp;
fe3c582
@@ -124,6 +118,10 @@ int main(int argc, char *argv[])
fe3c582
 		for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
fe3c582
 			if (!is_unique(&list, exp->m_export.e_path))
fe3c582
 				continue;
fe3c582
+			if (exp->m_export.e_mountpoint)
fe3c582
+				continue;
fe3c582
+			if (has_noauto_flag(exp->m_export.e_path))
fe3c582
+				continue;
fe3c582
 			if (strchr(exp->m_export.e_path, ' '))
fe3c582
 				fprintf(f, "RequiresMountsFor=\"%s\"\n",
fe3c582
 					exp->m_export.e_path);
fe3c582
@@ -141,9 +139,15 @@ int main(int argc, char *argv[])
fe3c582
 		if (strcmp(mnt->mnt_type, "nfs") != 0 &&
fe3c582
 		    strcmp(mnt->mnt_type, "nfs4") != 0)
fe3c582
 			continue;
fe3c582
-		fprintf(f, "Before= ");
fe3c582
-		systemd_escape(f, mnt->mnt_dir);
fe3c582
-		fprintf(f, ".mount\n");
fe3c582
+
fe3c582
+		spath = systemd_escape(mnt->mnt_dir, ".mount");
fe3c582
+		if (!spath) {
fe3c582
+			fprintf(stderr, 
fe3c582
+				"nfs-server-generator: convert path failed: %s\n",
fe3c582
+				mnt->mnt_dir);
fe3c582
+			continue;
fe3c582
+		}
fe3c582
+		fprintf(f, "Before=%s\n", spath);
fe3c582
 	}
fe3c582
 
fe3c582
 	fclose(fstab);
fe3c582
diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service
fe3c582
index 5be5de6..136552b 100644
fe3c582
--- a/systemd/nfs-server.service
fe3c582
+++ b/systemd/nfs-server.service
fe3c582
@@ -3,12 +3,12 @@ Description=NFS server and services
fe3c582
 DefaultDependencies=no
fe3c582
 Requires= network.target proc-fs-nfsd.mount
fe3c582
 Requires= nfs-mountd.service
fe3c582
-Wants=rpcbind.socket
fe3c582
+Wants=rpcbind.socket network-online.target
fe3c582
 Wants=rpc-statd.service nfs-idmapd.service
fe3c582
 Wants=rpc-statd-notify.service
fe3c582
 
fe3c582
-After= local-fs.target
fe3c582
-After= network.target proc-fs-nfsd.mount rpcbind.socket nfs-mountd.service
fe3c582
+After= network-online.target local-fs.target
fe3c582
+After= proc-fs-nfsd.mount rpcbind.socket nfs-mountd.service
fe3c582
 After= nfs-idmapd.service rpc-statd.service
fe3c582
 Before= rpc-statd-notify.service
fe3c582
 
fe3c582
diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man
fe3c582
index 91c49a0..189b052 100644
fe3c582
--- a/systemd/nfs.conf.man
fe3c582
+++ b/systemd/nfs.conf.man
fe3c582
@@ -96,6 +96,18 @@ value, which can be one or more from the list
fe3c582
 .BR all .
fe3c582
 When a list is given, the members should be comma-separated.
fe3c582
 .TP
fe3c582
+.B general
fe3c582
+Recognized values:
fe3c582
+.BR pipefs-directory .
fe3c582
+
fe3c582
+See
fe3c582
+.BR blkmapd (8),
fe3c582
+.BR rpc.idmapd (8),
fe3c582
+and
fe3c582
+.BR rpc.gssd (8)
fe3c582
+for details.
fe3c582
+
fe3c582
+.TP
fe3c582
 .B nfsdcltrack
fe3c582
 Recognized values:
fe3c582
 .BR storagedir .
fe3c582
@@ -154,6 +166,13 @@ section, are used to configure mountd.  See
fe3c582
 .BR rpc.mountd (8)
fe3c582
 for details.
fe3c582
 
fe3c582
+The
fe3c582
+.B state-directory-path
fe3c582
+value in the
fe3c582
+.B [mountd]
fe3c582
+section is also used by
fe3c582
+.BR exportfs (8).
fe3c582
+
fe3c582
 .TP
fe3c582
 .B statd
fe3c582
 Recognized values:
fe3c582
@@ -198,7 +217,6 @@ Recognized values:
fe3c582
 .BR limit-to-legacy-enctypes ,
fe3c582
 .BR context-timeout ,
fe3c582
 .BR rpc-timeout ,
fe3c582
-.BR pipefs-directory ,
fe3c582
 .BR keytab-file ,
fe3c582
 .BR cred-cache-directory ,
fe3c582
 .BR preferred-realm .
fe3c582
diff --git a/systemd/rpc-gssd.service.in b/systemd/rpc-gssd.service.in
fe3c582
index b353027..6807db3 100644
fe3c582
--- a/systemd/rpc-gssd.service.in
fe3c582
+++ b/systemd/rpc-gssd.service.in
fe3c582
@@ -2,8 +2,8 @@
fe3c582
 Description=RPC security service for NFS client and server
fe3c582
 DefaultDependencies=no
fe3c582
 Conflicts=umount.target
fe3c582
-Requires=var-lib-nfs-rpc_pipefs.mount
fe3c582
-After=var-lib-nfs-rpc_pipefs.mount
fe3c582
+Requires=rpc_pipefs.target
fe3c582
+After=rpc_pipefs.target
fe3c582
 
fe3c582
 ConditionPathExists=@_sysconfdir@/krb5.keytab
fe3c582
 
fe3c582
diff --git a/systemd/rpc-pipefs-generator.c b/systemd/rpc-pipefs-generator.c
fe3c582
new file mode 100644
fe3c582
index 0000000..59eee87
fe3c582
--- /dev/null
fe3c582
+++ b/systemd/rpc-pipefs-generator.c
fe3c582
@@ -0,0 +1,137 @@
fe3c582
+/*
fe3c582
+ * rpc-pipefs-generator:
fe3c582
+ *   systemd generator to create ordering dependencies between
fe3c582
+ *   nfs services and the rpc_pipefs mountpoint
fe3c582
+ */
fe3c582
+
fe3c582
+#ifdef HAVE_CONFIG_H
fe3c582
+#include <config.h>
fe3c582
+#endif
fe3c582
+
fe3c582
+#include <sys/stat.h>
fe3c582
+#include <sys/types.h>
fe3c582
+#include <unistd.h>
fe3c582
+#include <stdlib.h>
fe3c582
+#include <string.h>
fe3c582
+#include <ctype.h>
fe3c582
+#include <stdio.h>
fe3c582
+#include <mntent.h>
fe3c582
+
fe3c582
+#include "nfslib.h"
fe3c582
+#include "conffile.h"
fe3c582
+#include "systemd.h"
fe3c582
+
fe3c582
+#define RPC_PIPEFS_DEFAULT "/var/lib/nfs/rpc_pipefs"
fe3c582
+
fe3c582
+static int generate_mount_unit(const char *pipefs_path, const char *pipefs_unit,
fe3c582
+			       const char *dirname)
fe3c582
+{
fe3c582
+	char	*path;
fe3c582
+	FILE	*f;
fe3c582
+
fe3c582
+	path = malloc(strlen(dirname) + 1 + strlen(pipefs_unit));
fe3c582
+	if (!path)
fe3c582
+		return 1;
fe3c582
+	sprintf(path, "%s/%s", dirname, pipefs_unit);
fe3c582
+	f = fopen(path, "w");
fe3c582
+	if (!f)
fe3c582
+		return 1;
fe3c582
+
fe3c582
+	fprintf(f, "# Automatically generated by rpc-pipefs-generator\n\n[Unit]\n");
fe3c582
+	fprintf(f, "Description=RPC Pipe File System\n");
fe3c582
+	fprintf(f, "DefaultDependencies=no\n");
fe3c582
+	fprintf(f, "After=systemd-tmpfiles-setup.service\n");
fe3c582
+	fprintf(f, "Conflicts=umount.target\n");
fe3c582
+	fprintf(f, "\n[Mount]\n");
fe3c582
+	fprintf(f, "What=sunrpc\n");
fe3c582
+	fprintf(f, "Where=%s\n", pipefs_path);
fe3c582
+	fprintf(f, "Type=rpc_pipefs\n");
fe3c582
+
fe3c582
+	fclose(f);
fe3c582
+	return 0;
fe3c582
+}
fe3c582
+
fe3c582
+static
fe3c582
+int generate_target(char *pipefs_path, const char *dirname)
fe3c582
+{
fe3c582
+	char	*path;
fe3c582
+	char	filebase[] = "/rpc_pipefs.target";
fe3c582
+	char	*pipefs_unit;
fe3c582
+	FILE	*f;
fe3c582
+	int 	ret = 0;
fe3c582
+
fe3c582
+	pipefs_unit = systemd_escape(pipefs_path, ".mount");
fe3c582
+	if (!pipefs_unit)
fe3c582
+		return 1;
fe3c582
+
fe3c582
+	ret = generate_mount_unit(pipefs_path, pipefs_unit, dirname);
fe3c582
+	if (ret)
fe3c582
+		return ret;
fe3c582
+
fe3c582
+	path = malloc(strlen(dirname) + 1 + sizeof(filebase));
fe3c582
+	if (!path)
fe3c582
+		return 2;
fe3c582
+	sprintf(path, "%s", dirname);
fe3c582
+	mkdir(path, 0755);
fe3c582
+	strcat(path, filebase);
fe3c582
+	f = fopen(path, "w");
fe3c582
+	if (!f)
fe3c582
+		return 1;
fe3c582
+
fe3c582
+	fprintf(f, "# Automatically generated by rpc-pipefs-generator\n\n[Unit]\n");
fe3c582
+	fprintf(f, "Requires=%s\n", pipefs_unit);
fe3c582
+	fprintf(f, "After=%s\n", pipefs_unit);
fe3c582
+	fclose(f);
fe3c582
+
fe3c582
+	return 0;
fe3c582
+}
fe3c582
+
fe3c582
+static int is_non_pipefs_mountpoint(char *path)
fe3c582
+{
fe3c582
+	FILE		*mtab;
fe3c582
+	struct mntent	*mnt;
fe3c582
+
fe3c582
+	mtab = setmntent("/etc/mtab", "r");
fe3c582
+	if (!mtab)
fe3c582
+		return 0;
fe3c582
+
fe3c582
+	while ((mnt = getmntent(mtab)) != NULL) {
fe3c582
+		if (strlen(mnt->mnt_dir) != strlen(path))
fe3c582
+			continue;
fe3c582
+		if (strncmp(mnt->mnt_dir, path, strlen(mnt->mnt_dir)))
fe3c582
+			continue;
fe3c582
+		if (strncmp(mnt->mnt_type, "rpc_pipefs", strlen(mnt->mnt_type)))
fe3c582
+			break;
fe3c582
+	}
fe3c582
+	fclose(mtab);
fe3c582
+	return mnt != NULL;
fe3c582
+}
fe3c582
+
fe3c582
+int main(int argc, char *argv[])
fe3c582
+{
fe3c582
+	int 	ret;
fe3c582
+	char	*s;
fe3c582
+
fe3c582
+	/* Avoid using any external services */
fe3c582
+	xlog_syslog(0);
fe3c582
+
fe3c582
+	if (argc != 4 || argv[1][0] != '/') {
fe3c582
+		fprintf(stderr, "rpc-pipefs-generator: create systemd dependencies for nfs services\n");
fe3c582
+		fprintf(stderr, "Usage: normal-dir early-dir late-dir\n");
fe3c582
+		exit(1);
fe3c582
+	}
fe3c582
+
fe3c582
+	conf_init(NFS_CONFFILE);
fe3c582
+	s = conf_get_str("general", "pipefs-directory");
fe3c582
+	if (!s)
fe3c582
+		exit(0);
fe3c582
+	if (strlen(s) == strlen(RPC_PIPEFS_DEFAULT) &&
fe3c582
+			strcmp(s, RPC_PIPEFS_DEFAULT) == 0)
fe3c582
+		exit(0);
fe3c582
+
fe3c582
+	if (is_non_pipefs_mountpoint(s))
fe3c582
+		exit(1);
fe3c582
+
fe3c582
+	ret = generate_target(s, argv[1]);
fe3c582
+	exit(ret);
fe3c582
+}
fe3c582
diff --git a/systemd/rpc-statd-notify.service b/systemd/rpc-statd-notify.service
fe3c582
index 7bfc9b1..687fe31 100644
fe3c582
--- a/systemd/rpc-statd-notify.service
fe3c582
+++ b/systemd/rpc-statd-notify.service
fe3c582
@@ -1,8 +1,8 @@
fe3c582
 [Unit]
fe3c582
 Description=Notify NFS peers of a restart
fe3c582
 DefaultDependencies=no
fe3c582
-Requires=network.target
fe3c582
-After=local-fs.target network.target nss-lookup.target
fe3c582
+Wants=network-online.target
fe3c582
+After=local-fs.target network-online.target nss-lookup.target
fe3c582
 
fe3c582
 # if we run an nfs server, it needs to be running before we
fe3c582
 # tell clients that it has restarted.
fe3c582
diff --git a/systemd/rpc-statd.service b/systemd/rpc-statd.service
fe3c582
index 60d600f..f41ae20 100644
fe3c582
--- a/systemd/rpc-statd.service
fe3c582
+++ b/systemd/rpc-statd.service
fe3c582
@@ -3,7 +3,8 @@ Description=NFS status monitor for NFSv2/3 locking.
fe3c582
 DefaultDependencies=no
fe3c582
 Conflicts=umount.target
fe3c582
 Requires=nss-lookup.target rpcbind.socket
fe3c582
-After=network.target nss-lookup.target rpcbind.socket
fe3c582
+Wants=network-online.target
fe3c582
+After=network-online.target nss-lookup.target rpcbind.socket
fe3c582
 
fe3c582
 PartOf=nfs-utils.service
fe3c582
 
fe3c582
diff --git a/systemd/rpc-svcgssd.service b/systemd/rpc-svcgssd.service
fe3c582
index 7187e3c..cb2bcd4 100644
fe3c582
--- a/systemd/rpc-svcgssd.service
fe3c582
+++ b/systemd/rpc-svcgssd.service
fe3c582
@@ -1,8 +1,7 @@
fe3c582
 [Unit]
fe3c582
 Description=RPC security service for NFS server
fe3c582
 DefaultDependencies=no
fe3c582
-Requires=var-lib-nfs-rpc_pipefs.mount
fe3c582
-After=var-lib-nfs-rpc_pipefs.mount local-fs.target
fe3c582
+After=local-fs.target
fe3c582
 PartOf=nfs-server.service
fe3c582
 PartOf=nfs-utils.service
fe3c582
 
fe3c582
diff --git a/systemd/rpc_pipefs.target b/systemd/rpc_pipefs.target
fe3c582
new file mode 100644
fe3c582
index 0000000..01d4d27
fe3c582
--- /dev/null
fe3c582
+++ b/systemd/rpc_pipefs.target
fe3c582
@@ -0,0 +1,3 @@
fe3c582
+[Unit]
fe3c582
+Requires=var-lib-nfs-rpc_pipefs.mount
fe3c582
+After=var-lib-nfs-rpc_pipefs.mount
fe3c582
diff --git a/systemd/systemd.c b/systemd/systemd.c
fe3c582
new file mode 100644
fe3c582
index 0000000..17820d4
fe3c582
--- /dev/null
fe3c582
+++ b/systemd/systemd.c
fe3c582
@@ -0,0 +1,133 @@
fe3c582
+/*
fe3c582
+ * Helper functions for systemd generators in nfs-utils.
fe3c582
+ *
fe3c582
+ * Currently just systemd_escape().
fe3c582
+ */
fe3c582
+
fe3c582
+#include <stdio.h>
fe3c582
+#include <stdlib.h>
fe3c582
+#include <ctype.h>
fe3c582
+#include <string.h>
fe3c582
+
fe3c582
+static const char hex[16] =
fe3c582
+{
fe3c582
+  '0', '1', '2', '3', '4', '5', '6', '7',
fe3c582
+  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
fe3c582
+};
fe3c582
+
fe3c582
+/*
fe3c582
+ * determine length of the string that systemd_escape() needs to allocate
fe3c582
+ */
fe3c582
+static int systemd_len(char *path)
fe3c582
+{
fe3c582
+	char *p;
fe3c582
+	int len = 0;
fe3c582
+
fe3c582
+	p = path;
fe3c582
+	while (*p == '/')
fe3c582
+		/* multiple leading "/" are ignored */
fe3c582
+		p++;
fe3c582
+
fe3c582
+	if (!*p)
fe3c582
+		/* root directory "/" becomes is encoded as a single "-" */
fe3c582
+		return 1;
fe3c582
+
fe3c582
+	if (*p == '.')
fe3c582
+		/*
fe3c582
+		 * replace "." with "\x2d" escape sequence if
fe3c582
+		 * it's the first character in escaped path
fe3c582
+		 * */
fe3c582
+		len += 4;
fe3c582
+
fe3c582
+	while (*p) {
fe3c582
+		unsigned char c = *p++;
fe3c582
+
fe3c582
+		if (c == '/') {
fe3c582
+			/* multiple non-trailing slashes become '-' */
fe3c582
+			while (*p == '/')
fe3c582
+				p++;
fe3c582
+			if (*p)
fe3c582
+				len++;
fe3c582
+		} else if (isalnum(c) || c == ':' || c == '.' || c == '_')
fe3c582
+			/* these characters are not replaced */
fe3c582
+			len++;
fe3c582
+		else
fe3c582
+			/* replace with "\x2d" escape sequence */
fe3c582
+			len += 4;
fe3c582
+	}
fe3c582
+
fe3c582
+	return len;
fe3c582
+}
fe3c582
+
fe3c582
+/*
fe3c582
+ * convert c to "\x2d" escape sequence and append to string
fe3c582
+ * at position p, advancing p
fe3c582
+ */
fe3c582
+static char *hexify(unsigned char c, char *p)
fe3c582
+{
fe3c582
+	*p++ = '\\';
fe3c582
+	*p++ = 'x';
fe3c582
+	*p++ = hex[c >> 4];
fe3c582
+	*p++ = hex[c & 0xf];
fe3c582
+	return p;
fe3c582
+}
fe3c582
+
fe3c582
+/*
fe3c582
+ * convert a path to a unit name according to the logic in systemd.unit(5):
fe3c582
+ *
fe3c582
+ *     Basically, given a path, "/" is replaced by "-", and all other
fe3c582
+ *     characters which are not ASCII alphanumerics are replaced by C-style
fe3c582
+ *     "\x2d" escapes (except that "_" is never replaced and "." is only
fe3c582
+ *     replaced when it would be the first character in the escaped path).
fe3c582
+ *     The root directory "/" is encoded as single dash, while otherwise the
fe3c582
+ *     initial and ending "/" are removed from all paths during
fe3c582
+ *     transformation.
fe3c582
+ *
fe3c582
+ * NB: Although the systemd.unit(5) doesn't mention it, the ':' character
fe3c582
+ * is not escaped.
fe3c582
+ */
fe3c582
+char *systemd_escape(char *path, char *suffix)
fe3c582
+{
fe3c582
+	char *result;
fe3c582
+	char *p;
fe3c582
+	int len;
fe3c582
+
fe3c582
+	len = systemd_len(path);
fe3c582
+	result = malloc(len + strlen(suffix) + 1);
fe3c582
+	p = result;
fe3c582
+	while (*path == '/')
fe3c582
+		/* multiple leading "/" are ignored */
fe3c582
+		path++;
fe3c582
+	if (!*path) {
fe3c582
+		/* root directory "/" becomes is encoded as a single "-" */
fe3c582
+		*p++ = '-';
fe3c582
+		goto out;
fe3c582
+	}
fe3c582
+	if (*path == '.')
fe3c582
+		/*
fe3c582
+		 * replace "." with "\x2d" escape sequence if
fe3c582
+		 * it's the first character in escaped path
fe3c582
+		 * */
fe3c582
+		p = hexify(*path++, p);
fe3c582
+
fe3c582
+	while (*path) {
fe3c582
+		unsigned char c = *path++;
fe3c582
+
fe3c582
+		if (c == '/') {
fe3c582
+			/* multiple non-trailing slashes become '-' */
fe3c582
+			while (*path == '/')
fe3c582
+				path++;
fe3c582
+			if (*path)
fe3c582
+				*p++ = '-';
fe3c582
+		} else if (isalnum(c) || c == ':' || c == '.' || c == '_')
fe3c582
+			/* these characters are not replaced */
fe3c582
+			*p++ = c;
fe3c582
+		else
fe3c582
+			/* replace with "\x2d" escape sequence */
fe3c582
+			p = hexify(c, p);
fe3c582
+	}
fe3c582
+
fe3c582
+out:
fe3c582
+	sprintf(p, "%s", suffix);
fe3c582
+	return result;
fe3c582
+}
fe3c582
diff --git a/systemd/systemd.h b/systemd/systemd.h
fe3c582
new file mode 100644
fe3c582
index 0000000..25235ec
fe3c582
--- /dev/null
fe3c582
+++ b/systemd/systemd.h
fe3c582
@@ -0,0 +1,6 @@
fe3c582
+#ifndef SYSTEMD_H
fe3c582
+#define SYSTEMD_H
fe3c582
+
fe3c582
+char *systemd_escape(char *path, char *suffix);
fe3c582
+
fe3c582
+#endif /* SYSTEMD_H */
fe3c582
diff --git a/utils/blkmapd/blkmapd.man b/utils/blkmapd/blkmapd.man
fe3c582
index 914b80f..4b3d3f0 100644
fe3c582
--- a/utils/blkmapd/blkmapd.man
fe3c582
+++ b/utils/blkmapd/blkmapd.man
fe3c582
@@ -43,9 +43,24 @@ Performs device discovery only then exits.
fe3c582
 Runs
fe3c582
 .B blkmapd
fe3c582
 in the foreground and sends output to stderr (as opposed to syslogd)
fe3c582
+.SH CONFIGURATION FILE
fe3c582
+The
fe3c582
+.B blkmapd
fe3c582
+daemon recognizes the following value from the
fe3c582
+.B [general]
fe3c582
+section of the
fe3c582
+.I /etc/nfs.conf
fe3c582
+configuration file:
fe3c582
+.TP
fe3c582
+.B pipefs-directory
fe3c582
+Tells
fe3c582
+.B blkmapd
fe3c582
+where to look for the rpc_pipefs filesystem.  The default value is
fe3c582
+.IR /var/lib/nfs/rpc_pipefs .
fe3c582
 .SH SEE ALSO
fe3c582
 .BR nfs (5),
fe3c582
-.BR dmsetup (8)
fe3c582
+.BR dmsetup (8),
fe3c582
+.BR nfs.conf (5)
fe3c582
 .sp
fe3c582
 RFC 5661 for the NFS version 4.1 specification.
fe3c582
 .br
fe3c582
diff --git a/utils/blkmapd/device-discovery.c b/utils/blkmapd/device-discovery.c
fe3c582
index 8eb3fd0..c66669d 100644
fe3c582
--- a/utils/blkmapd/device-discovery.c
fe3c582
+++ b/utils/blkmapd/device-discovery.c
fe3c582
@@ -50,21 +50,36 @@
fe3c582
 #include <errno.h>
fe3c582
 #include <libdevmapper.h>
fe3c582
 
fe3c582
+#ifdef HAVE_CONFIG_H
fe3c582
+#include "config.h"
fe3c582
+#endif /* HAVE_CONFIG_H */
fe3c582
+
fe3c582
 #include "device-discovery.h"
fe3c582
 #include "xcommon.h"
fe3c582
+#include "nfslib.h"
fe3c582
+#include "conffile.h"
fe3c582
 
fe3c582
 #define EVENT_SIZE (sizeof(struct inotify_event))
fe3c582
 #define EVENT_BUFSIZE (1024 * EVENT_SIZE)
fe3c582
 
fe3c582
-#define BL_PIPE_FILE	"/var/lib/nfs/rpc_pipefs/nfs/blocklayout"
fe3c582
-#define NFSPIPE_DIR	"/var/lib/nfs/rpc_pipefs/nfs"
fe3c582
 #define RPCPIPE_DIR	"/var/lib/nfs/rpc_pipefs"
fe3c582
 #define PID_FILE	"/var/run/blkmapd.pid"
fe3c582
 
fe3c582
+#define CONF_SAVE(w, f) do {			\
fe3c582
+	char *p = f;				\
fe3c582
+	if (p != NULL)				\
fe3c582
+		(w) = p;			\
fe3c582
+} while (0)
fe3c582
+
fe3c582
+static char bl_pipe_file[PATH_MAX];
fe3c582
+static char nfspipe_dir[PATH_MAX];
fe3c582
+static char rpcpipe_dir[PATH_MAX];
fe3c582
+
fe3c582
 struct bl_disk *visible_disk_list;
fe3c582
 int    bl_watch_fd, bl_pipe_fd, nfs_pipedir_wfd, rpc_pipedir_wfd;
fe3c582
 int    pidfd = -1;
fe3c582
 
fe3c582
+
fe3c582
 struct bl_disk_path *bl_get_path(const char *filepath,
fe3c582
 				 struct bl_disk_path *paths)
fe3c582
 {
fe3c582
@@ -358,8 +373,8 @@ static void bl_rpcpipe_cb(void)
fe3c582
 				continue;
fe3c582
 			if (event->mask & IN_CREATE) {
fe3c582
 				BL_LOG_WARNING("nfs pipe dir created\n");
fe3c582
-				bl_watch_dir(NFSPIPE_DIR, &nfs_pipedir_wfd);
fe3c582
-				bl_pipe_fd = open(BL_PIPE_FILE, O_RDWR);
fe3c582
+				bl_watch_dir(nfspipe_dir, &nfs_pipedir_wfd);
fe3c582
+				bl_pipe_fd = open(bl_pipe_file, O_RDWR);
fe3c582
 			} else if (event->mask & IN_DELETE) {
fe3c582
 				BL_LOG_WARNING("nfs pipe dir deleted\n");
fe3c582
 				inotify_rm_watch(bl_watch_fd, nfs_pipedir_wfd);
fe3c582
@@ -372,7 +387,7 @@ static void bl_rpcpipe_cb(void)
fe3c582
 				continue;
fe3c582
 			if (event->mask & IN_CREATE) {
fe3c582
 				BL_LOG_WARNING("blocklayout pipe file created\n");
fe3c582
-				bl_pipe_fd = open(BL_PIPE_FILE, O_RDWR);
fe3c582
+				bl_pipe_fd = open(bl_pipe_file, O_RDWR);
fe3c582
 				if (bl_pipe_fd < 0)
fe3c582
 					BL_LOG_ERR("open %s failed: %s\n",
fe3c582
 						event->name, strerror(errno));
fe3c582
@@ -437,6 +452,18 @@ int main(int argc, char **argv)
fe3c582
 {
fe3c582
 	int opt, dflag = 0, fg = 0, ret = 1;
fe3c582
 	char pidbuf[64];
fe3c582
+	char *xrpcpipe_dir = NULL;
fe3c582
+
fe3c582
+	strncpy(rpcpipe_dir, RPCPIPE_DIR, sizeof(rpcpipe_dir));
fe3c582
+	conf_init(NFS_CONFFILE);
fe3c582
+	CONF_SAVE(xrpcpipe_dir, conf_get_str("general", "pipefs-directory"));
fe3c582
+	if (xrpcpipe_dir != NULL)
fe3c582
+		strlcpy(rpcpipe_dir, xrpcpipe_dir, sizeof(rpcpipe_dir));
fe3c582
+
fe3c582
+	strncpy(nfspipe_dir, rpcpipe_dir, sizeof(nfspipe_dir));
fe3c582
+	strlcat(nfspipe_dir, "/nfs", sizeof(nfspipe_dir));
fe3c582
+	strncpy(bl_pipe_file, rpcpipe_dir, sizeof(bl_pipe_file));
fe3c582
+	strlcat(bl_pipe_file, "/nfs/blocklayout", sizeof(bl_pipe_file));
fe3c582
 
fe3c582
 	while ((opt = getopt(argc, argv, "hdf")) != -1) {
fe3c582
 		switch (opt) {
fe3c582
@@ -496,12 +523,12 @@ int main(int argc, char **argv)
fe3c582
 	}
fe3c582
 
fe3c582
 	/* open pipe file */
fe3c582
-	bl_watch_dir(RPCPIPE_DIR, &rpc_pipedir_wfd);
fe3c582
-	bl_watch_dir(NFSPIPE_DIR, &nfs_pipedir_wfd);
fe3c582
+	bl_watch_dir(rpcpipe_dir, &rpc_pipedir_wfd);
fe3c582
+	bl_watch_dir(nfspipe_dir, &nfs_pipedir_wfd);
fe3c582
 
fe3c582
-	bl_pipe_fd = open(BL_PIPE_FILE, O_RDWR);
fe3c582
+	bl_pipe_fd = open(bl_pipe_file, O_RDWR);
fe3c582
 	if (bl_pipe_fd < 0)
fe3c582
-		BL_LOG_ERR("open pipe file %s failed: %s\n", BL_PIPE_FILE, strerror(errno));
fe3c582
+		BL_LOG_ERR("open pipe file %s failed: %s\n", bl_pipe_file, strerror(errno));
fe3c582
 
fe3c582
 	while (1) {
fe3c582
 		/* discover device when needed */
fe3c582
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
fe3c582
index 61dddfb..beed1b3 100644
fe3c582
--- a/utils/exportfs/exportfs.c
fe3c582
+++ b/utils/exportfs/exportfs.c
fe3c582
@@ -50,7 +50,8 @@ static void release_lockfile(void);
fe3c582
 
fe3c582
 static const char *lockfile = EXP_LOCKFILE;
fe3c582
 static int _lockfd = -1;
fe3c582
-char *conf_path = NFS_CONFFILE;
fe3c582
+
fe3c582
+struct state_paths etab;
fe3c582
 
fe3c582
 /*
fe3c582
  * If we aren't careful, changes made by exportfs can be lost
fe3c582
@@ -95,6 +96,7 @@ main(int argc, char **argv)
fe3c582
 	int	f_ignore = 0;
fe3c582
 	int	i, c;
fe3c582
 	int	force_flush = 0;
fe3c582
+	char	*s;
fe3c582
 
fe3c582
 	if ((progname = strrchr(argv[0], '/')) != NULL)
fe3c582
 		progname++;
fe3c582
@@ -105,9 +107,14 @@ main(int argc, char **argv)
fe3c582
 	xlog_stderr(1);
fe3c582
 	xlog_syslog(0);
fe3c582
 
fe3c582
-	conf_init();
fe3c582
+	conf_init(NFS_CONFFILE);
fe3c582
 	xlog_from_conffile("exportfs");
fe3c582
 
fe3c582
+	/* NOTE: following uses "mountd" section of nfs.conf !!!! */
fe3c582
+	s = conf_get_str("mountd", "state-directory-path");
fe3c582
+	if (s && !state_setup_basedir(argv[0], s))
fe3c582
+		exit(1);
fe3c582
+
fe3c582
 	while ((c = getopt(argc, argv, "ad:fhio:ruvs")) != EOF) {
fe3c582
 		switch(c) {
fe3c582
 		case 'a':
fe3c582
@@ -159,13 +166,17 @@ main(int argc, char **argv)
fe3c582
 		xlog(L_ERROR, "-r and -u are incompatible");
fe3c582
 		return 1;
fe3c582
 	}
fe3c582
+	if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab))
fe3c582
+		return 1;
fe3c582
 	if (optind == argc && ! f_all) {
fe3c582
 		if (force_flush) {
fe3c582
 			cache_flush(1);
fe3c582
+			free_state_path_names(&etab;;
fe3c582
 			return 0;
fe3c582
 		} else {
fe3c582
 			xtab_export_read();
fe3c582
 			dump(f_verbose, f_export_format);
fe3c582
+			free_state_path_names(&etab;;
fe3c582
 			return 0;
fe3c582
 		}
fe3c582
 	}
fe3c582
@@ -206,6 +217,7 @@ main(int argc, char **argv)
fe3c582
 	}
fe3c582
 	xtab_export_write();
fe3c582
 	cache_flush(force_flush);
fe3c582
+	free_state_path_names(&etab;;
fe3c582
 
fe3c582
 	return export_errno;
fe3c582
 }
fe3c582
diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man
fe3c582
index 45b6d83..91d3589 100644
fe3c582
--- a/utils/exportfs/exportfs.man
fe3c582
+++ b/utils/exportfs/exportfs.man
fe3c582
@@ -148,6 +148,29 @@ options.
fe3c582
 .TP
fe3c582
 .B -s
fe3c582
 Display the current export list suitable for /etc/exports.
fe3c582
+
fe3c582
+.SH CONFIGURATION FILE
fe3c582
+The
fe3c582
+.B [exportfs]
fe3c582
+section of the
fe3c582
+.I /etc/nfs.conf
fe3c582
+configuration file can contain a
fe3c582
+.B debug
fe3c582
+value, which can be one or more from the list
fe3c582
+.BR general ,
fe3c582
+.BR call ,
fe3c582
+.BR auth ,
fe3c582
+.BR parse ,
fe3c582
+.BR all .
fe3c582
+When a list is given, the members should be comma-separated.
fe3c582
+
fe3c582
+.B exportfs
fe3c582
+will also recognize the
fe3c582
+.B state-directory-path
fe3c582
+value from the
fe3c582
+.B [mountd]
fe3c582
+section.
fe3c582
+
fe3c582
 .SH DISCUSSION
fe3c582
 .SS Exporting Directories
fe3c582
 The first synopsis shows how to invoke
fe3c582
diff --git a/utils/exportfs/nfsd.man b/utils/exportfs/nfsd.man
fe3c582
index 0c516fa..9efa29f 100644
fe3c582
--- a/utils/exportfs/nfsd.man
fe3c582
+++ b/utils/exportfs/nfsd.man
fe3c582
@@ -105,11 +105,6 @@ clients have for different filesystems.
fe3c582
 The caches are:
fe3c582
 
fe3c582
 .TP
fe3c582
-.B auth.domain
fe3c582
-This cache maps the name of a client (or domain) to an internal data
fe3c582
-structure.  The only access that is possible is to flush the cache.
fe3c582
-
fe3c582
-.TP
fe3c582
 .B auth.unix.ip
fe3c582
 This cache contains a mapping from IP address to the name of the
fe3c582
 authentication domain that the ipaddress should be treated as part of.
fe3c582
@@ -133,7 +128,8 @@ are:
fe3c582
 .B flush
fe3c582
 When a number of seconds since epoch (1 Jan 1970) is written to this
fe3c582
 file, all entries in the cache that were last updated before that file
fe3c582
-become invalidated and will be flushed out.  Writing 1 will flush
fe3c582
+become invalidated and will be flushed out.  Writing a time in the
fe3c582
+future (in seconds since epoch) will flush
fe3c582
 everything.  This is the only file that will always be present.
fe3c582
 
fe3c582
 .TP
fe3c582
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
fe3c582
index 4d18d35..053a223 100644
fe3c582
--- a/utils/gssd/gssd.c
fe3c582
+++ b/utils/gssd/gssd.c
fe3c582
@@ -79,7 +79,6 @@ static int pipefs_fd;
fe3c582
 static int inotify_fd;
fe3c582
 struct event inotify_ev;
fe3c582
 
fe3c582
-char *conf_path = NFS_CONFFILE;
fe3c582
 char *keytabfile = GSSD_DEFAULT_KEYTAB_FILE;
fe3c582
 char **ccachesearch;
fe3c582
 int  use_memcache = 0;
fe3c582
@@ -87,6 +86,7 @@ int  root_uses_machine_creds = 1;
fe3c582
 unsigned int  context_timeout = 0;
fe3c582
 unsigned int  rpc_timeout = 5;
fe3c582
 char *preferred_realm = NULL;
fe3c582
+char *ccachedir = NULL;
fe3c582
 /* Avoid DNS reverse lookups on server names */
fe3c582
 static bool avoid_dns = true;
fe3c582
 int thread_started = false;
fe3c582
@@ -837,21 +837,12 @@ usage(char *progname)
fe3c582
 	exit(1);
fe3c582
 }
fe3c582
 
fe3c582
-int
fe3c582
-main(int argc, char *argv[])
fe3c582
+inline static void 
fe3c582
+read_gss_conf(void)
fe3c582
 {
fe3c582
-	int fg = 0;
fe3c582
-	int verbosity = 0;
fe3c582
-	int rpc_verbosity = 0;
fe3c582
-	int opt;
fe3c582
-	int i;
fe3c582
-	extern char *optarg;
fe3c582
-	char *progname;
fe3c582
-	char *ccachedir = NULL;
fe3c582
-	struct event sighup_ev;
fe3c582
 	char *s;
fe3c582
 
fe3c582
-	conf_init();
fe3c582
+	conf_init(NFS_CONFFILE);
fe3c582
 	use_memcache = conf_get_bool("gssd", "use-memcache", use_memcache);
fe3c582
 	root_uses_machine_creds = conf_get_bool("gssd", "use-machine-creds",
fe3c582
 						root_uses_machine_creds);
fe3c582
@@ -865,6 +856,10 @@ main(int argc, char *argv[])
fe3c582
 	s = conf_get_str("gssd", "pipefs-directory");
fe3c582
 	if (!s)
fe3c582
 		s = conf_get_str("general", "pipefs-directory");
fe3c582
+	else
fe3c582
+		printerr(0, "WARNING: Specifying pipefs-directory in the [gssd] "
fe3c582
+			 "section of %s is deprecated.  Use the [general] "
fe3c582
+			 "section instead.", NFS_CONFFILE);
fe3c582
 	if (s)
fe3c582
 		pipefs_path = s;
fe3c582
 	s = conf_get_str("gssd", "keytab-file");
fe3c582
@@ -877,6 +872,22 @@ main(int argc, char *argv[])
fe3c582
 	if (s)
fe3c582
 		preferred_realm = s;
fe3c582
 
fe3c582
+}
fe3c582
+
fe3c582
+int
fe3c582
+main(int argc, char *argv[])
fe3c582
+{
fe3c582
+	int fg = 0;
fe3c582
+	int verbosity = 0;
fe3c582
+	int rpc_verbosity = 0;
fe3c582
+	int opt;
fe3c582
+	int i;
fe3c582
+	extern char *optarg;
fe3c582
+	char *progname;
fe3c582
+	struct event sighup_ev;
fe3c582
+
fe3c582
+	read_gss_conf();
fe3c582
+
fe3c582
 	while ((opt = getopt(argc, argv, "DfvrlmnMp:k:d:t:T:R:")) != -1) {
fe3c582
 		switch (opt) {
fe3c582
 			case 'f':
fe3c582
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
fe3c582
index 87eef02..e620f0d 100644
fe3c582
--- a/utils/gssd/gssd.man
fe3c582
+++ b/utils/gssd/gssd.man
fe3c582
@@ -335,10 +335,6 @@ Equivalent to
fe3c582
 Equivalent to
fe3c582
 .BR -t .
fe3c582
 .TP
fe3c582
-.B pipefs-directory
fe3c582
-Equivalent to
fe3c582
-.BR -p .
fe3c582
-.TP
fe3c582
 .B keytab-file
fe3c582
 Equivalent to
fe3c582
 .BR -k .
fe3c582
@@ -350,6 +346,14 @@ Equivalent to
fe3c582
 .B preferred-realm
fe3c582
 Equivalent to
fe3c582
 .BR -R .
fe3c582
+.P
fe3c582
+In addtion, the following value is recognized from the
fe3c582
+.B [general]
fe3c582
+section:
fe3c582
+.TP
fe3c582
+.B pipefs-directory
fe3c582
+Equivalent to
fe3c582
+.BR -p .
fe3c582
 
fe3c582
 .SH SEE ALSO
fe3c582
 .BR rpc.svcgssd (8),
fe3c582
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
fe3c582
index d74d372..4fc81c3 100644
fe3c582
--- a/utils/gssd/gssd_proc.c
fe3c582
+++ b/utils/gssd/gssd_proc.c
fe3c582
@@ -729,10 +729,18 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
fe3c582
 	char			*target = NULL;
fe3c582
 	char			*service = NULL;
fe3c582
 	char			*enctypes = NULL;
fe3c582
+	char			*upcall_str;
fe3c582
+	char			*pbuf = info->lbuf;
fe3c582
 
fe3c582
 	printerr(2, "\n%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath);
fe3c582
 
fe3c582
-	for (p = strtok(info->lbuf, " "); p; p = strtok(NULL, " ")) {
fe3c582
+	upcall_str = strdup(info->lbuf);
fe3c582
+	if (upcall_str == NULL) {
fe3c582
+		printerr(0, "ERROR: malloc failure\n");
fe3c582
+		goto out_nomem;
fe3c582
+	}
fe3c582
+
fe3c582
+	while ((p = strsep(&pbuf, " "))) {
fe3c582
 		if (!strncmp(p, "mech=", strlen("mech=")))
fe3c582
 			mech = p + strlen("mech=");
fe3c582
 		else if (!strncmp(p, "uid=", strlen("uid=")))
fe3c582
@@ -748,7 +756,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
fe3c582
 	if (!mech || strlen(mech) < 1) {
fe3c582
 		printerr(0, "WARNING: handle_gssd_upcall: "
fe3c582
 			    "failed to find gss mechanism name "
fe3c582
-			    "in upcall string '%s'\n", info->lbuf);
fe3c582
+			    "in upcall string '%s'\n", upcall_str);
fe3c582
 		goto out;
fe3c582
 	}
fe3c582
 
fe3c582
@@ -761,7 +769,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
fe3c582
 	if (!uidstr) {
fe3c582
 		printerr(0, "WARNING: handle_gssd_upcall: "
fe3c582
 			    "failed to find uid "
fe3c582
-			    "in upcall string '%s'\n", info->lbuf);
fe3c582
+			    "in upcall string '%s'\n", upcall_str);
fe3c582
 		goto out;
fe3c582
 	}
fe3c582
 
fe3c582
@@ -774,7 +782,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
fe3c582
 	if (target && strlen(target) < 1) {
fe3c582
 		printerr(0, "WARNING: handle_gssd_upcall: "
fe3c582
 			 "failed to parse target name "
fe3c582
-			 "in upcall string '%s'\n", info->lbuf);
fe3c582
+			 "in upcall string '%s'\n", upcall_str);
fe3c582
 		goto out;
fe3c582
 	}
fe3c582
 
fe3c582
@@ -789,7 +797,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
fe3c582
 	if (service && strlen(service) < 1) {
fe3c582
 		printerr(0, "WARNING: handle_gssd_upcall: "
fe3c582
 			 "failed to parse service type "
fe3c582
-			 "in upcall string '%s'\n", info->lbuf);
fe3c582
+			 "in upcall string '%s'\n", upcall_str);
fe3c582
 		goto out;
fe3c582
 	}
fe3c582
 
fe3c582
@@ -802,6 +810,8 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
fe3c582
 		do_error_downcall(clp->gssd_fd, uid, -EACCES);
fe3c582
 	}
fe3c582
 out:
fe3c582
+	free(upcall_str);
fe3c582
+out_nomem:
fe3c582
 	free(info);
fe3c582
 	return;
fe3c582
 }
fe3c582
diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c
fe3c582
index 1fb579a..3514ae1 100644
fe3c582
--- a/utils/gssd/svcgssd.c
fe3c582
+++ b/utils/gssd/svcgssd.c
fe3c582
@@ -63,8 +63,6 @@
fe3c582
 #include "err_util.h"
fe3c582
 #include "conffile.h"
fe3c582
 
fe3c582
-char *conf_path = NFS_CONFFILE;
fe3c582
-
fe3c582
 void
fe3c582
 sig_die(int signal)
fe3c582
 {
fe3c582
@@ -103,7 +101,7 @@ main(int argc, char *argv[])
fe3c582
 	char *principal = NULL;
fe3c582
 	char *s;
fe3c582
 
fe3c582
-	conf_init();
fe3c582
+	conf_init(NFS_CONFFILE); 
fe3c582
 
fe3c582
 	s = conf_get_str("svcgssd", "principal");
fe3c582
 	if (!s)
fe3c582
diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c
fe3c582
index f4e083a..c12e878 100644
fe3c582
--- a/utils/idmapd/idmapd.c
fe3c582
+++ b/utils/idmapd/idmapd.c
fe3c582
@@ -165,9 +165,6 @@ static char *nobodyuser, *nobodygroup;
fe3c582
 static uid_t nobodyuid;
fe3c582
 static gid_t nobodygid;
fe3c582
 
fe3c582
-/* Used by conffile.c in libnfs.a */
fe3c582
-char *conf_path;
fe3c582
-
fe3c582
 static int
fe3c582
 flush_nfsd_cache(char *path, time_t now)
fe3c582
 {
fe3c582
@@ -219,8 +216,8 @@ main(int argc, char **argv)
fe3c582
 	int serverstart = 1, clientstart = 1;
fe3c582
 	int ret;
fe3c582
 	char *progname;
fe3c582
+	char *conf_path = NULL;
fe3c582
 
fe3c582
-	conf_path = _PATH_IDMAPDCONF;
fe3c582
 	nobodyuser = NFS4NOBODY_USER;
fe3c582
 	nobodygroup = NFS4NOBODY_GROUP;
fe3c582
 	strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir));
fe3c582
@@ -234,8 +231,11 @@ main(int argc, char **argv)
fe3c582
 #define GETOPTSTR "hvfd:p:U:G:c:CS"
fe3c582
 	opterr=0; /* Turn off error messages */
fe3c582
 	while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) {
fe3c582
-		if (opt == 'c')
fe3c582
+		if (opt == 'c') {
fe3c582
+			warnx("-c is deprecated and may be removed in the "
fe3c582
+			      "future.  See idmapd(8).");
fe3c582
 			conf_path = optarg;
fe3c582
+		}
fe3c582
 		if (opt == '?') {
fe3c582
 			if (strchr(GETOPTSTR, optopt))
fe3c582
 				warnx("'-%c' option requires an argument.", optopt);
fe3c582
@@ -247,17 +247,33 @@ main(int argc, char **argv)
fe3c582
 	}
fe3c582
 	optind = 1;
fe3c582
 
fe3c582
-	if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) {
fe3c582
-		warn("Skipping configuration file \"%s\"", conf_path);
fe3c582
-		conf_path = NULL;
fe3c582
+	if (conf_path) { /* deprecated -c option was specified */
fe3c582
+		if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) {
fe3c582
+			warn("Skipping configuration file \"%s\"", conf_path);
fe3c582
+			conf_path = NULL;
fe3c582
+		} else {
fe3c582
+			conf_init(conf_path);
fe3c582
+			verbose = conf_get_num("General", "Verbosity", 0);
fe3c582
+			cache_entry_expiration = conf_get_num("General",
fe3c582
+					"Cache-Expiration", DEFAULT_IDMAP_CACHE_EXPIRY);
fe3c582
+			CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory"));
fe3c582
+			if (xpipefsdir != NULL)
fe3c582
+				strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir));
fe3c582
+			CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User"));
fe3c582
+			CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group"));
fe3c582
+		}
fe3c582
 	} else {
fe3c582
-		conf_init();
fe3c582
-		verbose = conf_get_num("General", "Verbosity", 0);
fe3c582
-		cache_entry_expiration = conf_get_num("General",
fe3c582
-				"Cache-Expiration", DEFAULT_IDMAP_CACHE_EXPIRY);
fe3c582
+		conf_path = NFS_CONFFILE;
fe3c582
+		conf_init(conf_path);
fe3c582
 		CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory"));
fe3c582
 		if (xpipefsdir != NULL)
fe3c582
 			strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir));
fe3c582
+
fe3c582
+		conf_path = _PATH_IDMAPDCONF;
fe3c582
+		conf_init(conf_path);
fe3c582
+		verbose = conf_get_num("General", "Verbosity", 0);
fe3c582
+		cache_entry_expiration = conf_get_num("General",
fe3c582
+				"cache-expiration", DEFAULT_IDMAP_CACHE_EXPIRY);
fe3c582
 		CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User"));
fe3c582
 		CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group"));
fe3c582
 	}
fe3c582
diff --git a/utils/idmapd/idmapd.man b/utils/idmapd/idmapd.man
fe3c582
index d4ab894..5f34d2b 100644
fe3c582
--- a/utils/idmapd/idmapd.man
fe3c582
+++ b/utils/idmapd/idmapd.man
fe3c582
@@ -73,11 +73,28 @@ The default value is \&"/var/lib/nfs/rpc_pipefs\&".
fe3c582
 .It Fl c Ar path
fe3c582
 Use configuration file
fe3c582
 .Ar path .
fe3c582
+This option is deprecated.
fe3c582
 .It Fl C
fe3c582
 Client-only: perform no idmapping for any NFS server, even if one is detected.
fe3c582
 .It Fl S
fe3c582
 Server-only: perform no idmapping for any NFS client, even if one is detected.
fe3c582
 .El
fe3c582
+.Sh CONFIGURATION FILES
fe3c582
+.Nm
fe3c582
+recognizes the following value from the
fe3c582
+.Sy [general]
fe3c582
+section of the
fe3c582
+.Pa /etc/nfs.conf
fe3c582
+configuration file:
fe3c582
+.Bl -tag -width Ds_imagedir
fe3c582
+.It Sy pipefs-directory
fe3c582
+Equivalent to
fe3c582
+.Sy -p .
fe3c582
+.El
fe3c582
+.Pp
fe3c582
+All other settings related to id mapping are found in the
fe3c582
+.Pa /etc/idmapd.conf
fe3c582
+configuration file.
fe3c582
 .Sh EXAMPLES
fe3c582
 .Cm rpc.idmapd -f -vvv
fe3c582
 .Pp
fe3c582
@@ -94,9 +111,11 @@ messages to console, and with a verbosity level of 3.
fe3c582
 .\" This next request is for sections 1, 6, 7 & 8 only.
fe3c582
 .\" .Sh ENVIRONMENT
fe3c582
 .Sh FILES
fe3c582
-.Pa /etc/idmapd.conf
fe3c582
+.Pa /etc/idmapd.conf ,
fe3c582
+.Pa /etc/nfs.conf
fe3c582
 .Sh SEE ALSO
fe3c582
 .Xr idmapd.conf 5 ,
fe3c582
+.Xr nfs.conf 5 ,
fe3c582
 .Xr nfsidmap 8
fe3c582
 .\".Sh SEE ALSO
fe3c582
 .\".Xr nylon.conf 4
fe3c582
diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c
fe3c582
index 0a4cc04..dc964c7 100644
fe3c582
--- a/utils/mount/configfile.c
fe3c582
+++ b/utils/mount/configfile.c
fe3c582
@@ -51,10 +51,6 @@
fe3c582
 #define NFSMOUNT_SERVER "Server"
fe3c582
 #endif
fe3c582
 
fe3c582
-#ifndef MOUNTOPTS_CONFFILE
fe3c582
-#define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf"
fe3c582
-#endif
fe3c582
-char *conf_path = MOUNTOPTS_CONFFILE;
fe3c582
 enum {
fe3c582
 	MNT_NOARG=0,
fe3c582
 	MNT_INTARG,
fe3c582
diff --git a/utils/mount/mount_config.h b/utils/mount/mount_config.h
fe3c582
index 69ffd1e..e4f8511 100644
fe3c582
--- a/utils/mount/mount_config.h
fe3c582
+++ b/utils/mount/mount_config.h
fe3c582
@@ -20,6 +20,10 @@
fe3c582
 #include "conffile.h"
fe3c582
 #include "xlog.h"
fe3c582
 
fe3c582
+#ifndef MOUNTOPTS_CONFFILE
fe3c582
+#define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf"
fe3c582
+#endif
fe3c582
+
fe3c582
 extern char *conf_get_mntopts(char *, char *, char *);
fe3c582
 
fe3c582
 static inline void mount_config_init(char *program)
fe3c582
@@ -28,7 +32,7 @@ static inline void mount_config_init(char *program)
fe3c582
 	/*
fe3c582
 	 * Read the the default mount options
fe3c582
 	 */
fe3c582
-	conf_init();
fe3c582
+	conf_init(MOUNTOPTS_CONFFILE);
fe3c582
 }
fe3c582
 
fe3c582
 static inline char *mount_config_opts(char *spec,
fe3c582
diff --git a/utils/mount/mount_libmount.c b/utils/mount/mount_libmount.c
fe3c582
index 1f01f7f..2d40657 100644
fe3c582
--- a/utils/mount/mount_libmount.c
fe3c582
+++ b/utils/mount/mount_libmount.c
fe3c582
@@ -188,6 +188,7 @@ static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
fe3c582
 	};
fe3c582
 
fe3c582
 	mnt_context_init_helper(cxt, MNT_ACT_UMOUNT, 0);
fe3c582
+	mnt_context_disable_canonicalize(cxt, 1);
fe3c582
 
fe3c582
 	while ((c = getopt_long (argc, argv, "fvnrlh", longopts, NULL)) != -1) {
fe3c582
 
fe3c582
diff --git a/utils/mount/network.c b/utils/mount/network.c
fe3c582
index 7dceb2d..281e935 100644
fe3c582
--- a/utils/mount/network.c
fe3c582
+++ b/utils/mount/network.c
fe3c582
@@ -33,6 +33,7 @@
fe3c582
 #include <errno.h>
fe3c582
 #include <netdb.h>
fe3c582
 #include <time.h>
fe3c582
+#include <grp.h>
fe3c582
 
fe3c582
 #include <sys/types.h>
fe3c582
 #include <sys/socket.h>
fe3c582
@@ -804,6 +805,7 @@ int start_statd(void)
fe3c582
 			pid_t pid = fork();
fe3c582
 			switch (pid) {
fe3c582
 			case 0: /* child */
fe3c582
+				setgroups(0, NULL);
fe3c582
 				setgid(0);
fe3c582
 				setuid(0);
fe3c582
 				execle(START_STATD, START_STATD, NULL, envp);
fe3c582
@@ -1638,6 +1640,7 @@ int nfs_options2pmap(struct mount_options *options,
fe3c582
 		     struct pmap *nfs_pmap, struct pmap *mnt_pmap)
fe3c582
 {
fe3c582
 	struct nfs_version version;
fe3c582
+	memset(&version, 0, sizeof(version));
fe3c582
 
fe3c582
 	if (!nfs_nfs_program(options, &nfs_pmap->pm_prog))
fe3c582
 		return 0;
fe3c582
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
fe3c582
index 387d734..c2a739b 100644
fe3c582
--- a/utils/mount/stropts.c
fe3c582
+++ b/utils/mount/stropts.c
fe3c582
@@ -73,6 +73,13 @@
fe3c582
 #define NFS_DEF_BG_TIMEOUT_MINUTES	(10000u)
fe3c582
 #endif
fe3c582
 
fe3c582
+#ifndef NFS_DEFAULT_MAJOR
fe3c582
+#define NFS_DEFAULT_MAJOR	4
fe3c582
+#endif
fe3c582
+#ifndef NFS_DEFAULT_MINOR
fe3c582
+#define NFS_DEFAULT_MINOR	2
fe3c582
+#endif
fe3c582
+
fe3c582
 extern int nfs_mount_data_version;
fe3c582
 extern char *progname;
fe3c582
 extern int verbose;
fe3c582
@@ -112,20 +119,28 @@ static void nfs_default_version(struct nfsmount_info *mi)
fe3c582
 	if (mi->version.v_mode == V_DEFAULT &&
fe3c582
 		config_default_vers.v_mode != V_DEFAULT) {
fe3c582
 		mi->version.major = config_default_vers.major;
fe3c582
-		mi->version.minor = config_default_vers.minor;
fe3c582
+		if (config_default_vers.v_mode == V_SPECIFIC)
fe3c582
+			mi->version.minor = config_default_vers.minor;
fe3c582
+		else
fe3c582
+			mi->version.minor = NFS_DEFAULT_MINOR;
fe3c582
 		return;
fe3c582
 	}
fe3c582
 
fe3c582
 	if (mi->version.v_mode == V_GENERAL) {
fe3c582
 		if (config_default_vers.v_mode != V_DEFAULT &&
fe3c582
-		    mi->version.major == config_default_vers.major)
fe3c582
-			mi->version.minor = config_default_vers.minor;
fe3c582
+		    mi->version.major == config_default_vers.major) {
fe3c582
+			if (config_default_vers.v_mode == V_SPECIFIC)
fe3c582
+				mi->version.minor = config_default_vers.minor;
fe3c582
+			else
fe3c582
+				mi->version.minor = NFS_DEFAULT_MINOR;
fe3c582
+		} else
fe3c582
+			mi->version.minor = NFS_DEFAULT_MINOR;
fe3c582
 		return;
fe3c582
 	}
fe3c582
 
fe3c582
 #endif /* MOUNT_CONFIG */
fe3c582
-	mi->version.major = 4;
fe3c582
-	mi->version.minor = 2;
fe3c582
+	mi->version.major = NFS_DEFAULT_MAJOR;
fe3c582
+	mi->version.minor = NFS_DEFAULT_MINOR;
fe3c582
 }
fe3c582
 
fe3c582
 /*
fe3c582
@@ -315,9 +330,12 @@ static int nfs_set_version(struct nfsmount_info *mi)
fe3c582
 	if (!nfs_nfs_version(mi->options, &mi->version))
fe3c582
 		return 0;
fe3c582
 
fe3c582
-	if (strncmp(mi->type, "nfs4", 4) == 0)
fe3c582
-		mi->version.major = 4;
fe3c582
-
fe3c582
+	if (strncmp(mi->type, "nfs4", 4) == 0) {
fe3c582
+		/* Set to default values */
fe3c582
+		mi->version.major = NFS_DEFAULT_MAJOR;
fe3c582
+		mi->version.minor = NFS_DEFAULT_MINOR;
fe3c582
+		mi->version.v_mode = V_GENERAL;
fe3c582
+	}
fe3c582
 	/*
fe3c582
 	 * Before 2.6.32, the kernel NFS client didn't
fe3c582
 	 * support "-t nfs vers=4" mounts, so NFS version
fe3c582
@@ -517,6 +535,10 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options, int checkv4)
fe3c582
 	unsigned long protocol;
fe3c582
 	struct pmap mnt_pmap;
fe3c582
 
fe3c582
+	/* initialize structs */
fe3c582
+	memset(&nfs_pmap, 0, sizeof(struct pmap));
fe3c582
+	memset(&mnt_pmap, 0, sizeof(struct pmap));
fe3c582
+
fe3c582
 	/*
fe3c582
 	 * Version and transport negotiation is not required
fe3c582
 	 * and does not work for RDMA mounts.
fe3c582
@@ -727,13 +749,9 @@ static int nfs_do_mount_v4(struct nfsmount_info *mi,
fe3c582
 	}
fe3c582
 
fe3c582
 	if (mi->version.v_mode != V_SPECIFIC) {
fe3c582
-		if (mi->version.v_mode == V_GENERAL)
fe3c582
-			snprintf(version_opt, sizeof(version_opt) - 1,
fe3c582
-				"vers=%lu", mi->version.major);
fe3c582
-		else
fe3c582
-			snprintf(version_opt, sizeof(version_opt) - 1,
fe3c582
-				"vers=%lu.%lu", mi->version.major,
fe3c582
-				mi->version.minor);
fe3c582
+		snprintf(version_opt, sizeof(version_opt) - 1,
fe3c582
+			"vers=%lu.%lu", mi->version.major,
fe3c582
+			mi->version.minor);
fe3c582
 
fe3c582
 		if (po_append(options, version_opt) == PO_FAILED) {
fe3c582
 			errno = EINVAL;
fe3c582
@@ -834,9 +852,6 @@ check_result:
fe3c582
 	case EINVAL:
fe3c582
 		/* A less clear indication that our client
fe3c582
 		 * does not support NFSv4 minor version. */
fe3c582
-		if (mi->version.v_mode == V_GENERAL &&
fe3c582
-			mi->version.minor == 0)
fe3c582
-				return result;
fe3c582
 		if (mi->version.v_mode != V_SPECIFIC) {
fe3c582
 			if (mi->version.minor > 0) {
fe3c582
 				mi->version.minor--;
fe3c582
@@ -858,19 +873,28 @@ check_result:
fe3c582
 		/* UDP-Only servers won't support v4, but maybe it
fe3c582
 		 * just isn't ready yet.  So try v3, but double-check
fe3c582
 		 * with rpcbind for v4. */
fe3c582
+		if (mi->version.v_mode == V_GENERAL)
fe3c582
+			/* Mustn't try v2,v3 */
fe3c582
+			return result;
fe3c582
 		result = nfs_try_mount_v3v2(mi, TRUE);
fe3c582
 		if (result == 0 && errno == EAGAIN) {
fe3c582
 			/* v4 server seems to be registered now. */
fe3c582
 			result = nfs_try_mount_v4(mi);
fe3c582
 			if (result == 0 && errno != ECONNREFUSED)
fe3c582
 				goto check_result;
fe3c582
-		}
fe3c582
+		} else if (result == 0)
fe3c582
+			/* Restore original errno with v3 failures */
fe3c582
+			errno = ECONNREFUSED;
fe3c582
+
fe3c582
 		return result;
fe3c582
 	default:
fe3c582
 		return result;
fe3c582
 	}
fe3c582
 
fe3c582
 fall_back:
fe3c582
+	if (mi->version.v_mode == V_GENERAL)
fe3c582
+		/* v2,3 fallback not allowed */
fe3c582
+		return result;
fe3c582
 	return nfs_try_mount_v3v2(mi, FALSE);
fe3c582
 }
fe3c582
 
fe3c582
diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
fe3c582
index d065830..8299256 100644
fe3c582
--- a/utils/mountd/auth.c
fe3c582
+++ b/utils/mountd/auth.c
fe3c582
@@ -41,6 +41,8 @@ static nfs_client my_client;
fe3c582
 
fe3c582
 extern int use_ipaddr;
fe3c582
 
fe3c582
+extern struct state_paths etab;
fe3c582
+
fe3c582
 void
fe3c582
 auth_init(void)
fe3c582
 {
fe3c582
@@ -84,10 +86,10 @@ auth_reload()
fe3c582
 	static unsigned int	counter;
fe3c582
 	int			fd;
fe3c582
 
fe3c582
-	if ((fd = open(_PATH_ETAB, O_RDONLY)) < 0) {
fe3c582
-		xlog(L_FATAL, "couldn't open %s", _PATH_ETAB);
fe3c582
+	if ((fd = open(etab.statefn, O_RDONLY)) < 0) {
fe3c582
+		xlog(L_FATAL, "couldn't open %s", etab.statefn);
fe3c582
 	} else if (fstat(fd, &stb) < 0) {
fe3c582
-		xlog(L_FATAL, "couldn't stat %s", _PATH_ETAB);
fe3c582
+		xlog(L_FATAL, "couldn't stat %s", etab.statefn);
fe3c582
 		close(fd);
fe3c582
 	} else if (last_fd != -1 && stb.st_ino == last_inode) {
fe3c582
 		/* We opened the etab file before, and its inode
fe3c582
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
fe3c582
index 61699e6..829f803 100644
fe3c582
--- a/utils/mountd/mountd.c
fe3c582
+++ b/utils/mountd/mountd.c
fe3c582
@@ -29,6 +29,7 @@
fe3c582
 #include "mountd.h"
fe3c582
 #include "rpcmisc.h"
fe3c582
 #include "pseudoflavors.h"
fe3c582
+#include "nfslib.h"
fe3c582
 
fe3c582
 extern void my_svc_run(void);
fe3c582
 
fe3c582
@@ -40,7 +41,8 @@ int reverse_resolve = 0;
fe3c582
 int manage_gids;
fe3c582
 int use_ipaddr = -1;
fe3c582
 
fe3c582
-char *conf_path = NFS_CONFFILE;
fe3c582
+struct state_paths etab;
fe3c582
+struct state_paths rmtab;
fe3c582
 
fe3c582
 /* PRC: a high-availability callout program can be specified with -H
fe3c582
  * When this is done, the program will receive callouts whenever clients
fe3c582
@@ -110,8 +112,8 @@ unregister_services (void)
fe3c582
 static void
fe3c582
 cleanup_lockfiles (void)
fe3c582
 {
fe3c582
-	unlink(_PATH_ETABLCK);
fe3c582
-	unlink(_PATH_RMTABLCK);
fe3c582
+	unlink(etab.lockfn);
fe3c582
+	unlink(rmtab.lockfn);
fe3c582
 }
fe3c582
 
fe3c582
 /* Wait for all worker child processes to exit and reap them */
fe3c582
@@ -181,6 +183,8 @@ fork_workers(void)
fe3c582
 	wait_for_workers();
fe3c582
 	unregister_services();
fe3c582
 	cleanup_lockfiles();
fe3c582
+	free_state_path_names(&etab;;
fe3c582
+	free_state_path_names(&rmtab);
fe3c582
 	xlog(L_NOTICE, "mountd: no more workers, exiting\n");
fe3c582
 	exit(0);
fe3c582
 }
fe3c582
@@ -198,6 +202,8 @@ killer (int sig)
fe3c582
 		wait_for_workers();
fe3c582
 	}
fe3c582
 	cleanup_lockfiles();
fe3c582
+	free_state_path_names(&etab;;
fe3c582
+	free_state_path_names(&rmtab);
fe3c582
 	xlog (L_NOTICE, "Caught signal %d, un-registering and exiting.", sig);
fe3c582
 	exit(0);
fe3c582
 }
fe3c582
@@ -656,7 +662,6 @@ get_exportlist(void)
fe3c582
 int
fe3c582
 main(int argc, char **argv)
fe3c582
 {
fe3c582
-	char    *state_dir = NFS_STATEDIR;
fe3c582
 	char	*progname;
fe3c582
 	char	*s;
fe3c582
 	unsigned int listeners = 0;
fe3c582
@@ -674,7 +679,7 @@ main(int argc, char **argv)
fe3c582
 	else
fe3c582
 		progname = argv[0];
fe3c582
 
fe3c582
-	conf_init();
fe3c582
+	conf_init(NFS_CONFFILE);
fe3c582
 	xlog_from_conffile("mountd");
fe3c582
 	manage_gids = conf_get_bool("mountd", "manage-gids", manage_gids);
fe3c582
 	descriptors = conf_get_num("mountd", "descriptors", descriptors);
fe3c582
@@ -684,8 +689,8 @@ main(int argc, char **argv)
fe3c582
 	ha_callout_prog = conf_get_str("mountd", "ha-callout");
fe3c582
 
fe3c582
 	s = conf_get_str("mountd", "state-directory-path");
fe3c582
-	if (s)
fe3c582
-		state_dir = s;
fe3c582
+	if (s && !state_setup_basedir(argv[0], s))
fe3c582
+		exit(1);
fe3c582
 
fe3c582
 	/* NOTE: following uses "nfsd" section of nfs.conf !!!! */
fe3c582
 	if (conf_get_bool("nfsd", "udp", NFSCTL_UDPISSET(_rpcprotobits)))
fe3c582
@@ -758,7 +763,8 @@ main(int argc, char **argv)
fe3c582
 			reverse_resolve = 1;
fe3c582
 			break;
fe3c582
 		case 's':
fe3c582
-			state_dir = xstrdup(optarg);
fe3c582
+			if (!state_setup_basedir(argv[0], optarg))
fe3c582
+				exit(1);
fe3c582
 			break;
fe3c582
 		case 't':
fe3c582
 			num_threads = atoi (optarg);
fe3c582
@@ -790,11 +796,10 @@ main(int argc, char **argv)
fe3c582
 		fprintf(stderr, "%s: No protocol versions specified!\n", progname); 
fe3c582
 		usage(progname, 1);
fe3c582
 	}
fe3c582
-	if (chdir(state_dir)) {
fe3c582
-		fprintf(stderr, "%s: chdir(%s) failed: %s\n",
fe3c582
-			progname, state_dir, strerror(errno));
fe3c582
-		exit(1);
fe3c582
-	}
fe3c582
+	if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab))
fe3c582
+		return 1;
fe3c582
+	if (!setup_state_path_names(progname, RMTAB, RMTABTMP, RMTABLCK, &rmtab))
fe3c582
+		return 1;
fe3c582
 
fe3c582
 	if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
fe3c582
 		fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
fe3c582
@@ -888,6 +893,8 @@ main(int argc, char **argv)
fe3c582
 
fe3c582
 	xlog(L_ERROR, "RPC service loop terminated unexpectedly. Exiting...\n");
fe3c582
 	unregister_services();
fe3c582
+	free_state_path_names(&etab;;
fe3c582
+	free_state_path_names(&rmtab);
fe3c582
 	exit(1);
fe3c582
 }
fe3c582
 
fe3c582
diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
fe3c582
index 9f0a51f..9978afc 100644
fe3c582
--- a/utils/mountd/mountd.man
fe3c582
+++ b/utils/mountd/mountd.man
fe3c582
@@ -144,7 +144,7 @@ Instead, mount the nfsd filesystem on
fe3c582
 .IR /proc/fs/nfsd .
fe3c582
 .TP
fe3c582
 .BI "\-s," "" " \-\-state\-directory\-path "  directory
fe3c582
-Specify a directory in which to place statd state information.
fe3c582
+Specify a directory in which to place state information (etab and rmtab).
fe3c582
 If this option is not specified the default of
fe3c582
 .I /var/lib/nfs
fe3c582
 is used.
fe3c582
diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
fe3c582
index 527377f..3ae0dbb 100644
fe3c582
--- a/utils/mountd/rmtab.c
fe3c582
+++ b/utils/mountd/rmtab.c
fe3c582
@@ -28,6 +28,8 @@
fe3c582
 
fe3c582
 extern int reverse_resolve;
fe3c582
 
fe3c582
+extern struct state_paths rmtab;
fe3c582
+
fe3c582
 /* If new path is a link do not destroy it but place the
fe3c582
  * file where the link points.
fe3c582
  */
fe3c582
@@ -59,7 +61,7 @@ mountlist_add(char *host, const char *path)
fe3c582
 	int		lockid;
fe3c582
 	long		pos;
fe3c582
 
fe3c582
-	if ((lockid = xflock(_PATH_RMTABLCK, "a")) < 0)
fe3c582
+	if ((lockid = xflock(rmtab.lockfn, "a")) < 0)
fe3c582
 		return;
fe3c582
 	setrmtabent("r+");
fe3c582
 	while ((rep = getrmtabent(1, &pos)) != NULL) {
fe3c582
@@ -99,13 +101,13 @@ mountlist_del(char *hname, const char *path)
fe3c582
 	int		lockid;
fe3c582
 	int		match;
fe3c582
 
fe3c582
-	if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0)
fe3c582
+	if ((lockid = xflock(rmtab.lockfn, "w")) < 0)
fe3c582
 		return;
fe3c582
 	if (!setrmtabent("r")) {
fe3c582
 		xfunlock(lockid);
fe3c582
 		return;
fe3c582
 	}
fe3c582
-	if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) {
fe3c582
+	if (!(fp = fsetrmtabent(rmtab.tmpfn, "w"))) {
fe3c582
 		endrmtabent();
fe3c582
 		xfunlock(lockid);
fe3c582
 		return;
fe3c582
@@ -121,9 +123,9 @@ mountlist_del(char *hname, const char *path)
fe3c582
 		if (!match || rep->r_count)
fe3c582
 			fputrmtabent(fp, rep, NULL);
fe3c582
 	}
fe3c582
-	if (slink_safe_rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) {
fe3c582
+	if (slink_safe_rename(rmtab.tmpfn, rmtab.statefn) < 0) {
fe3c582
 		xlog(L_ERROR, "couldn't rename %s to %s",
fe3c582
-				_PATH_RMTABTMP, _PATH_RMTAB);
fe3c582
+				rmtab.tmpfn, rmtab.statefn);
fe3c582
 	}
fe3c582
 	endrmtabent();	/* close & unlink */
fe3c582
 	fendrmtabent(fp);
fe3c582
@@ -138,7 +140,7 @@ mountlist_del_all(const struct sockaddr *sap)
fe3c582
 	FILE		*fp;
fe3c582
 	int		lockid;
fe3c582
 
fe3c582
-	if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0)
fe3c582
+	if ((lockid = xflock(rmtab.lockfn, "w")) < 0)
fe3c582
 		return;
fe3c582
 	hostname = host_canonname(sap);
fe3c582
 	if (hostname == NULL) {
fe3c582
@@ -151,7 +153,7 @@ mountlist_del_all(const struct sockaddr *sap)
fe3c582
 	if (!setrmtabent("r"))
fe3c582
 		goto out_free;
fe3c582
 
fe3c582
-	if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w")))
fe3c582
+	if (!(fp = fsetrmtabent(rmtab.tmpfn, "w")))
fe3c582
 		goto out_close;
fe3c582
 
fe3c582
 	while ((rep = getrmtabent(1, NULL)) != NULL) {
fe3c582
@@ -160,9 +162,9 @@ mountlist_del_all(const struct sockaddr *sap)
fe3c582
 			continue;
fe3c582
 		fputrmtabent(fp, rep, NULL);
fe3c582
 	}
fe3c582
-	if (slink_safe_rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) {
fe3c582
+	if (slink_safe_rename(rmtab.tmpfn, rmtab.statefn) < 0) {
fe3c582
 		xlog(L_ERROR, "couldn't rename %s to %s",
fe3c582
-				_PATH_RMTABTMP, _PATH_RMTAB);
fe3c582
+				rmtab.tmpfn, rmtab.statefn);
fe3c582
 	}
fe3c582
 	fendrmtabent(fp);
fe3c582
 out_close:
fe3c582
@@ -195,11 +197,11 @@ mountlist_list(void)
fe3c582
 	struct stat		stb;
fe3c582
 	int			lockid;
fe3c582
 
fe3c582
-	if ((lockid = xflock(_PATH_RMTABLCK, "r")) < 0)
fe3c582
+	if ((lockid = xflock(rmtab.lockfn, "r")) < 0)
fe3c582
 		return NULL;
fe3c582
-	if (stat(_PATH_RMTAB, &stb) < 0) {
fe3c582
+	if (stat(rmtab.statefn, &stb) < 0) {
fe3c582
 		xlog(L_ERROR, "can't stat %s: %s",
fe3c582
-				_PATH_RMTAB, strerror(errno));
fe3c582
+				rmtab.statefn, strerror(errno));
fe3c582
 		xfunlock(lockid);
fe3c582
 		return NULL;
fe3c582
 	}
fe3c582
diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c
fe3c582
index 20f4b79..2b38249 100644
fe3c582
--- a/utils/nfsd/nfsd.c
fe3c582
+++ b/utils/nfsd/nfsd.c
fe3c582
@@ -34,8 +34,6 @@
fe3c582
 #define NFSD_NPROC 8
fe3c582
 #endif
fe3c582
 
fe3c582
-char *conf_path = NFS_CONFFILE;
fe3c582
-
fe3c582
 static void	usage(const char *);
fe3c582
 
fe3c582
 static struct option longopts[] =
fe3c582
@@ -44,7 +42,9 @@ static struct option longopts[] =
fe3c582
 	{ "help", 0, 0, 'h' },
fe3c582
 	{ "no-nfs-version", 1, 0, 'N' },
fe3c582
 	{ "nfs-version", 1, 0, 'V' },
fe3c582
+	{ "tcp", 0, 0, 't' },
fe3c582
 	{ "no-tcp", 0, 0, 'T' },
fe3c582
+	{ "udp", 0, 0, 'u' },
fe3c582
 	{ "no-udp", 0, 0, 'U' },
fe3c582
 	{ "port", 1, 0, 'P' },
fe3c582
 	{ "port", 1, 0, 'p' },
fe3c582
@@ -67,8 +67,9 @@ main(int argc, char **argv)
fe3c582
 	int	socket_up = 0;
fe3c582
 	unsigned int minorvers = 0;
fe3c582
 	unsigned int minorversset = 0;
fe3c582
+	unsigned int minormask = 0;
fe3c582
 	unsigned int versbits = NFSCTL_VERDEFAULT;
fe3c582
-	unsigned int protobits = NFSCTL_ALLBITS;
fe3c582
+	unsigned int protobits = NFSCTL_PROTODEFAULT;
fe3c582
 	int grace = -1;
fe3c582
 	int lease = -1;
fe3c582
 
fe3c582
@@ -79,7 +80,7 @@ main(int argc, char **argv)
fe3c582
 	xlog_syslog(0);
fe3c582
 	xlog_stderr(1);
fe3c582
 
fe3c582
-	conf_init();
fe3c582
+	conf_init(NFS_CONFFILE); 
fe3c582
 	xlog_from_conffile("nfsd");
fe3c582
 	count = conf_get_num("nfsd", "threads", count);
fe3c582
 	grace = conf_get_num("nfsd", "grace-time", grace);
fe3c582
@@ -104,10 +105,16 @@ main(int argc, char **argv)
fe3c582
 		else
fe3c582
 			NFSCTL_VERUNSET(versbits, i);
fe3c582
 	}
fe3c582
+
fe3c582
+	nfssvc_get_minormask(&minormask);
fe3c582
 	/* We assume the kernel will default all minor versions to 'on',
fe3c582
 	 * and allow the config file to disable some.
fe3c582
 	 */
fe3c582
-	for (i = NFS4_MINMINOR; i <= NFS4_MAXMINOR; i++) {
fe3c582
+	if (NFSCTL_VERISSET(versbits, 4)) {
fe3c582
+		NFSCTL_MINORSET(minorversset, 0);
fe3c582
+		NFSCTL_MINORSET(minorvers, 0);
fe3c582
+	}
fe3c582
+	for (i = 1; i <= NFS4_MAXMINOR; i++) {
fe3c582
 		char tag[20];
fe3c582
 		sprintf(tag, "vers4.%d", i);
fe3c582
 		/* The default for minor version support is to let the
fe3c582
@@ -119,12 +126,12 @@ main(int argc, char **argv)
fe3c582
 		 * (i.e. don't set the bit in minorversset).
fe3c582
 		 */
fe3c582
 		if (!conf_get_bool("nfsd", tag, 1)) {
fe3c582
-			NFSCTL_VERSET(minorversset, i);
fe3c582
-			NFSCTL_VERUNSET(minorvers, i);
fe3c582
+			NFSCTL_MINORSET(minorversset, i);
fe3c582
+			NFSCTL_MINORUNSET(minorvers, i);
fe3c582
 		}
fe3c582
 		if (conf_get_bool("nfsd", tag, 0)) {
fe3c582
-			NFSCTL_VERSET(minorversset, i);
fe3c582
-			NFSCTL_VERSET(minorvers, i);
fe3c582
+			NFSCTL_MINORSET(minorversset, i);
fe3c582
+			NFSCTL_MINORSET(minorvers, i);
fe3c582
 		}
fe3c582
 	}
fe3c582
 
fe3c582
@@ -138,7 +145,7 @@ main(int argc, char **argv)
fe3c582
 		}
fe3c582
 	}
fe3c582
 
fe3c582
-	while ((c = getopt_long(argc, argv, "dH:hN:V:p:P:sTUrG:L:", longopts, NULL)) != EOF) {
fe3c582
+	while ((c = getopt_long(argc, argv, "dH:hN:V:p:P:stTituUrG:L:", longopts, NULL)) != EOF) {
fe3c582
 		switch(c) {
fe3c582
 		case 'd':
fe3c582
 			xlog_config(D_ALL, 1);
fe3c582
@@ -179,13 +186,17 @@ main(int argc, char **argv)
fe3c582
 			case 4:
fe3c582
 				if (*p == '.') {
fe3c582
 					int i = atoi(p+1);
fe3c582
-					if (i < NFS4_MINMINOR || i > NFS4_MAXMINOR) {
fe3c582
+					if (i < 0 || i > NFS4_MAXMINOR) {
fe3c582
 						fprintf(stderr, "%s: unsupported minor version\n", optarg);
fe3c582
 						exit(1);
fe3c582
 					}
fe3c582
-					NFSCTL_VERSET(minorversset, i);
fe3c582
-					NFSCTL_VERUNSET(minorvers, i);
fe3c582
-					break;
fe3c582
+					NFSCTL_MINORSET(minorversset, i);
fe3c582
+					NFSCTL_MINORUNSET(minorvers, i);
fe3c582
+					if (minorvers != 0)
fe3c582
+						break;
fe3c582
+				} else {
fe3c582
+					minorvers = 0;
fe3c582
+					minorversset = minormask;
fe3c582
 				}
fe3c582
 			case 3:
fe3c582
 			case 2:
fe3c582
@@ -201,14 +212,14 @@ main(int argc, char **argv)
fe3c582
 			case 4:
fe3c582
 				if (*p == '.') {
fe3c582
 					int i = atoi(p+1);
fe3c582
-					if (i < NFS4_MINMINOR || i > NFS4_MAXMINOR) {
fe3c582
+					if (i < 0 || i > NFS4_MAXMINOR) {
fe3c582
 						fprintf(stderr, "%s: unsupported minor version\n", optarg);
fe3c582
 						exit(1);
fe3c582
 					}
fe3c582
-					NFSCTL_VERSET(minorversset, i);
fe3c582
-					NFSCTL_VERSET(minorvers, i);
fe3c582
-					break;
fe3c582
-				}
fe3c582
+					NFSCTL_MINORSET(minorversset, i);
fe3c582
+					NFSCTL_MINORSET(minorvers, i);
fe3c582
+				} else
fe3c582
+					minorvers = minorversset = minormask;
fe3c582
 			case 3:
fe3c582
 			case 2:
fe3c582
 				NFSCTL_VERSET(versbits, c);
fe3c582
@@ -222,9 +233,15 @@ main(int argc, char **argv)
fe3c582
 			xlog_syslog(1);
fe3c582
 			xlog_stderr(0);
fe3c582
 			break;
fe3c582
+		case 't':
fe3c582
+			NFSCTL_TCPSET(protobits);
fe3c582
+			break;
fe3c582
 		case 'T':
fe3c582
 			NFSCTL_TCPUNSET(protobits);
fe3c582
 			break;
fe3c582
+		case 'u':
fe3c582
+			NFSCTL_UDPSET(protobits);
fe3c582
+			break;
fe3c582
 		case 'U':
fe3c582
 			NFSCTL_UDPUNSET(protobits);
fe3c582
 			break;
fe3c582
@@ -372,9 +389,9 @@ usage(const char *prog)
fe3c582
 {
fe3c582
 	fprintf(stderr, "Usage:\n"
fe3c582
 		"%s [-d|--debug] [-H hostname] [-p|-P|--port port]\n"
fe3c582
-		"     [-N|--no-nfs-version version] [-V|--nfs-version version]\n"
fe3c582
-		"     [-s|--syslog] [-T|--no-tcp] [-U|--no-udp] [-r|--rdma=]\n"
fe3c582
-		"     [-G|--grace-time secs] [-L|--leasetime secs] nrservs\n",
fe3c582
+		"   [-N|--no-nfs-version version] [-V|--nfs-version version]\n"
fe3c582
+		"   [-s|--syslog] [-t|--tcp] [-T|--no-tcp] [-u|--udp] [-U|--no-udp]\n"
fe3c582
+		"   [-r|--rdma=] [-G|--grace-time secs] [-L|--leasetime secs] nrservs\n",
fe3c582
 		prog);
fe3c582
 	exit(2);
fe3c582
 }
fe3c582
diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man
fe3c582
index 8901fb6..d83ef86 100644
fe3c582
--- a/utils/nfsd/nfsd.man
fe3c582
+++ b/utils/nfsd/nfsd.man
fe3c582
@@ -57,7 +57,7 @@ This option can be used to request that
fe3c582
 .B rpc.nfsd
fe3c582
 does not offer certain versions of NFS. The current version of
fe3c582
 .B rpc.nfsd
fe3c582
-can support major NFS versions 2,3,4 and the minor versions 4.1 and 4.2.
fe3c582
+can support major NFS versions 2,3,4 and the minor versions 4.0, 4.1 and 4.2.
fe3c582
 .TP
fe3c582
 .B \-s " or " \-\-syslog
fe3c582
 By default,
fe3c582
@@ -67,22 +67,24 @@ logs error messages (and debug messages, if enabled) to stderr. This option make
fe3c582
 log these messages to syslog instead. Note that errors encountered during
fe3c582
 option processing will still be logged to stderr regardless of this option.
fe3c582
 .TP
fe3c582
+.B \-t " or " \-\-tcp
fe3c582
+Instruct the kernel nfs server to open and listen on a TCP socket. This is the default.
fe3c582
+.TP
fe3c582
 .B \-T " or " \-\-no-tcp
fe3c582
-Disable 
fe3c582
-.B rpc.nfsd 
fe3c582
-from accepting TCP connections from clients.
fe3c582
+Instruct the kernel nfs server not to open and listen on a TCP socket.
fe3c582
+.TP
fe3c582
+.B \-u " or " \-\-udp
fe3c582
+Instruct the kernel nfs server to open and listen on a UDP socket.
fe3c582
 .TP
fe3c582
 .B \-U " or " \-\-no-udp
fe3c582
-Disable
fe3c582
-.B rpc.nfsd
fe3c582
-from accepting UDP connections from clients.
fe3c582
+Instruct the kernel nfs server not to open and listen on a UDP socket. This is the default.
fe3c582
 .TP
fe3c582
 .B \-V " or " \-\-nfs-version vers
fe3c582
 This option can be used to request that 
fe3c582
 .B rpc.nfsd
fe3c582
 offer certain versions of NFS. The current version of
fe3c582
 .B rpc.nfsd
fe3c582
-can support major NFS versions 2,3,4 and the minor versions 4.1 and 4.2.
fe3c582
+can support major NFS versions 2,3,4 and the minor versions 4.0, 4.1 and 4.2.
fe3c582
 .TP
fe3c582
 .B \-L " or " \-\-lease-time seconds
fe3c582
 Set the lease-time used for NFSv4.  This corresponds to how often
fe3c582
diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c
fe3c582
index 07f6ff1..e8609c1 100644
fe3c582
--- a/utils/nfsd/nfssvc.c
fe3c582
+++ b/utils/nfsd/nfssvc.c
fe3c582
@@ -330,36 +330,78 @@ nfssvc_set_time(const char *type, const int seconds)
fe3c582
 }
fe3c582
 
fe3c582
 void
fe3c582
+nfssvc_get_minormask(unsigned int *mask)
fe3c582
+{
fe3c582
+	int fd;
fe3c582
+	char *ptr = buf;
fe3c582
+	ssize_t size;
fe3c582
+
fe3c582
+	fd = open(NFSD_VERS_FILE, O_RDONLY);
fe3c582
+	if (fd < 0)
fe3c582
+		return;
fe3c582
+
fe3c582
+	size = read(fd, buf, sizeof(buf));
fe3c582
+	if (size < 0) {
fe3c582
+		xlog(L_ERROR, "Getting versions failed: errno %d (%m)", errno);
fe3c582
+		goto out;
fe3c582
+	}
fe3c582
+	ptr[size] = '\0';
fe3c582
+	for (;;) {
fe3c582
+		unsigned vers, minor = 0;
fe3c582
+		char *token = strtok(ptr, " ");
fe3c582
+
fe3c582
+		if (!token)
fe3c582
+			break;
fe3c582
+		ptr = NULL;
fe3c582
+		if (*token != '+' && *token != '-')
fe3c582
+			continue;
fe3c582
+		if (sscanf(++token, "%u.%u", &vers, &minor) > 0 &&
fe3c582
+		    vers == 4 && minor <= NFS4_MAXMINOR)
fe3c582
+			NFSCTL_MINORSET(*mask, minor);
fe3c582
+	}
fe3c582
+out:
fe3c582
+	close(fd);
fe3c582
+	return;
fe3c582
+}
fe3c582
+
fe3c582
+static int
fe3c582
+nfssvc_print_vers(char *ptr, unsigned size, unsigned vers, unsigned minorvers,
fe3c582
+		int isset)
fe3c582
+{
fe3c582
+	char sign = isset ? '+' : '-';
fe3c582
+	if (minorvers == 0)
fe3c582
+		return snprintf(ptr, size, "%c%u ", sign, vers);
fe3c582
+	return snprintf(ptr, size, "%c%u.%u ", sign, vers, minorvers);
fe3c582
+}
fe3c582
+
fe3c582
+void
fe3c582
 nfssvc_setvers(unsigned int ctlbits, unsigned int minorvers, unsigned int minorversset)
fe3c582
 {
fe3c582
 	int fd, n, off;
fe3c582
-	char *ptr;
fe3c582
 
fe3c582
-	ptr = buf;
fe3c582
 	off = 0;
fe3c582
 	fd = open(NFSD_VERS_FILE, O_WRONLY);
fe3c582
 	if (fd < 0)
fe3c582
 		return;
fe3c582
 
fe3c582
-	for (n = NFS4_MINMINOR; n <= NFS4_MAXMINOR; n++) {
fe3c582
-		if (NFSCTL_VERISSET(minorversset, n)) {
fe3c582
-			if (NFSCTL_VERISSET(minorvers, n))
fe3c582
-				off += snprintf(ptr+off, sizeof(buf) - off, "+4.%d ", n);
fe3c582
-			else
fe3c582
-				off += snprintf(ptr+off, sizeof(buf) - off, "-4.%d ", n);
fe3c582
-		}
fe3c582
-	}
fe3c582
-	for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) {
fe3c582
-		if (NFSCTL_VERISSET(ctlbits, n))
fe3c582
-		    off += snprintf(ptr+off, sizeof(buf) - off, "+%d ", n);
fe3c582
-		else
fe3c582
-		    off += snprintf(ptr+off, sizeof(buf) - off, "-%d ", n);
fe3c582
+	for (n = NFSD_MINVERS; n <= ((NFSD_MAXVERS < 3) ? NFSD_MAXVERS : 3); n++)
fe3c582
+		off += nfssvc_print_vers(&buf[off], sizeof(buf) - off,
fe3c582
+				n, 0, NFSCTL_VERISSET(ctlbits, n));
fe3c582
+
fe3c582
+	for (n = 0; n <= NFS4_MAXMINOR; n++) {
fe3c582
+		if (!NFSCTL_MINORISSET(minorversset, n))
fe3c582
+			continue;
fe3c582
+		off += nfssvc_print_vers(&buf[off], sizeof(buf) - off,
fe3c582
+				4, n, NFSCTL_MINORISSET(minorvers, n));
fe3c582
 	}
fe3c582
+	if (!off--)
fe3c582
+		goto out;
fe3c582
+	buf[off] = '\0';
fe3c582
 	xlog(D_GENERAL, "Writing version string to kernel: %s", buf);
fe3c582
-	snprintf(ptr+off, sizeof(buf) - off, "\n");
fe3c582
+	snprintf(&buf[off], sizeof(buf) - off, "\n");
fe3c582
 	if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf))
fe3c582
 		xlog(L_ERROR, "Setting version failed: errno %d (%m)", errno);
fe3c582
-
fe3c582
+out:
fe3c582
 	close(fd);
fe3c582
 
fe3c582
 	return;
fe3c582
diff --git a/utils/nfsd/nfssvc.h b/utils/nfsd/nfssvc.h
fe3c582
index cd5a7e8..39ebf37 100644
fe3c582
--- a/utils/nfsd/nfssvc.h
fe3c582
+++ b/utils/nfsd/nfssvc.h
fe3c582
@@ -28,3 +28,4 @@ void	nfssvc_set_time(const char *type, const int seconds);
fe3c582
 int	nfssvc_set_rdmaport(const char *port);
fe3c582
 void	nfssvc_setvers(unsigned int ctlbits, unsigned int minorvers4, unsigned int minorvers4set);
fe3c582
 int	nfssvc_threads(int nrservs);
fe3c582
+void	nfssvc_get_minormask(unsigned int *mask);
fe3c582
diff --git a/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c
fe3c582
index 7af9efb..124c923 100644
fe3c582
--- a/utils/nfsdcltrack/nfsdcltrack.c
fe3c582
+++ b/utils/nfsdcltrack/nfsdcltrack.c
fe3c582
@@ -56,8 +56,6 @@
fe3c582
 /* defined by RFC 3530 */
fe3c582
 #define NFS4_OPAQUE_LIMIT	1024
fe3c582
 
fe3c582
-char *conf_path = NFS_CONFFILE;
fe3c582
-
fe3c582
 /* private data structures */
fe3c582
 struct cltrack_cmd {
fe3c582
 	char *name;
fe3c582
@@ -566,7 +564,7 @@ main(int argc, char **argv)
fe3c582
 	xlog_syslog(1);
fe3c582
 	xlog_stderr(0);
fe3c582
 
fe3c582
-	conf_init();
fe3c582
+	conf_init(NFS_CONFFILE); 
fe3c582
 	xlog_from_conffile("nfsdcltrack");
fe3c582
 	val = conf_get_str("nfsdcltrack", "storagedir");
fe3c582
 	if (val)
fe3c582
diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c
fe3c582
index 54cd748..1552eba 100644
fe3c582
--- a/utils/nfsdcltrack/sqlite.c
fe3c582
+++ b/utils/nfsdcltrack/sqlite.c
fe3c582
@@ -101,7 +101,7 @@ sqlite_query_schema_version(void)
fe3c582
 		"SELECT value FROM parameters WHERE key == \"version\";",
fe3c582
 		 -1, &stmt, NULL);
fe3c582
 	if (ret != SQLITE_OK) {
fe3c582
-		xlog(L_ERROR, "Unable to prepare select statement: %s",
fe3c582
+		xlog(D_GENERAL, "Unable to prepare select statement: %s",
fe3c582
 			sqlite3_errmsg(dbh));
fe3c582
 		ret = 0;
fe3c582
 		goto out;
fe3c582
@@ -110,7 +110,7 @@ sqlite_query_schema_version(void)
fe3c582
 	/* query schema version */
fe3c582
 	ret = sqlite3_step(stmt);
fe3c582
 	if (ret != SQLITE_ROW) {
fe3c582
-		xlog(L_ERROR, "Select statement execution failed: %s",
fe3c582
+		xlog(D_GENERAL, "Select statement execution failed: %s",
fe3c582
 				sqlite3_errmsg(dbh));
fe3c582
 		ret = 0;
fe3c582
 		goto out;
fe3c582
diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
fe3c582
index 152b680..ea32075 100644
fe3c582
--- a/utils/statd/Makefile.am
fe3c582
+++ b/utils/statd/Makefile.am
fe3c582
@@ -18,6 +18,7 @@ statd_LDADD = ../../support/nsm/libnsm.a \
fe3c582
 	      $(LIBWRAP) $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
fe3c582
 sm_notify_LDADD = ../../support/nsm/libnsm.a \
fe3c582
 		  ../../support/nfs/libnfs.a \
fe3c582
+	          ../../support/misc/libmisc.a \
fe3c582
 		  $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
fe3c582
 
fe3c582
 EXTRA_DIST = sim_sm_inter.x $(man8_MANS) simulate.c
fe3c582
diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
fe3c582
index 623213e..d216ddb 100644
fe3c582
--- a/utils/statd/sm-notify.c
fe3c582
+++ b/utils/statd/sm-notify.c
fe3c582
@@ -45,6 +45,8 @@
fe3c582
 
fe3c582
 #define NLM_END_GRACE_FILE	"/proc/fs/lockd/nlm_end_grace"
fe3c582
 
fe3c582
+int lift_grace = 1;
fe3c582
+
fe3c582
 struct nsm_host {
fe3c582
 	struct nsm_host *	next;
fe3c582
 	char *			name;
fe3c582
@@ -67,7 +69,6 @@ static _Bool		opt_update_state = true;
fe3c582
 static unsigned int	opt_max_retry = 15 * 60;
fe3c582
 static char *		opt_srcaddr = NULL;
fe3c582
 static char *		opt_srcport = NULL;
fe3c582
-char *			conf_path = NFS_CONFFILE;
fe3c582
 
fe3c582
 static void		notify(const int sock);
fe3c582
 static int		notify_host(int, struct nsm_host *);
fe3c582
@@ -489,11 +490,12 @@ main(int argc, char **argv)
fe3c582
 	else
fe3c582
 		progname = argv[0];
fe3c582
 
fe3c582
-	conf_init();
fe3c582
+	conf_init(NFS_CONFFILE);
fe3c582
 	xlog_from_conffile("sm-notify");
fe3c582
 	opt_max_retry = conf_get_num("sm-notify", "retry-time", opt_max_retry / 60) * 60;
fe3c582
 	opt_srcport = conf_get_str("sm-notify", "outgoing-port");
fe3c582
 	opt_srcaddr = conf_get_str("sm-notify", "outgoing-addr");
fe3c582
+	lift_grace = conf_get_bool("sm-notify", "lift-grace", lift_grace);
fe3c582
 	s = conf_get_str("statd", "state-directory-path");
fe3c582
 	if (s && !nsm_setup_pathnames(argv[0], s))
fe3c582
 		exit(1);
fe3c582
@@ -570,7 +572,8 @@ usage:		fprintf(stderr,
fe3c582
 	(void)nsm_retire_monitored_hosts();
fe3c582
 	if (nsm_load_notify_list(smn_get_host) == 0) {
fe3c582
 		xlog(D_GENERAL, "No hosts to notify; exiting");
fe3c582
-		nsm_lift_grace_period();
fe3c582
+		if (lift_grace)
fe3c582
+			nsm_lift_grace_period();
fe3c582
 		return 0;
fe3c582
 	}
fe3c582
 
fe3c582
diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man
fe3c582
index bb7f6e0..cfe1e4b 100644
fe3c582
--- a/utils/statd/sm-notify.man
fe3c582
+++ b/utils/statd/sm-notify.man
fe3c582
@@ -241,6 +241,24 @@ These have the same effect as the command line options
fe3c582
 .B v
fe3c582
 respectively.
fe3c582
 
fe3c582
+An additional value recognized in the
fe3c582
+.B [sm-notify]
fe3c582
+section is
fe3c582
+.BR lift-grace .
fe3c582
+By default,
fe3c582
+.B sm-notify
fe3c582
+will lift lockd's grace period early if it has no hosts to notify.
fe3c582
+Some high availability configurations will run one
fe3c582
+.B sm-notify
fe3c582
+per floating IP address.  In these configurations, lifting the
fe3c582
+grace period early may prevent clients from reclaiming locks.
fe3c582
+.RB "Setting " lift-grace " to " n
fe3c582
+will prevent
fe3c582
+.B sm-notify
fe3c582
+from ending the grace period early.
fe3c582
+.B lift-grace
fe3c582
+has no corresponding command line option.
fe3c582
+
fe3c582
 The value recognized in the
fe3c582
 .B [statd]
fe3c582
 section is
fe3c582
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
fe3c582
index d333b29..1443715 100644
fe3c582
--- a/utils/statd/statd.c
fe3c582
+++ b/utils/statd/statd.c
fe3c582
@@ -37,7 +37,6 @@
fe3c582
 #include <sys/socket.h>
fe3c582
 
fe3c582
 int	run_mode = 0;		/* foreground logging mode */
fe3c582
-char	*conf_path = NFS_CONFFILE;
fe3c582
 
fe3c582
 /* LH - I had these local to main, but it seemed silly to have 
fe3c582
  * two copies of each - one in main(), one static in log.c... 
fe3c582
@@ -274,7 +273,7 @@ int main (int argc, char **argv)
fe3c582
 	/* Set hostname */
fe3c582
 	MY_NAME = NULL;
fe3c582
 
fe3c582
-	conf_init();
fe3c582
+	conf_init(NFS_CONFFILE);
fe3c582
 	xlog_from_conffile("statd");
fe3c582
 	out_port = conf_get_num("statd", "outgoing-port", out_port);
fe3c582
 	port = conf_get_num("statd", "port", port);