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