walters / rpms / nfs-utils

Forked from rpms/nfs-utils 6 years ago
Clone
12f9a07
diff --git a/aclocal/kerberos5.m4 b/aclocal/kerberos5.m4
12f9a07
index 0bf35d3..8a0f3e4 100644
12f9a07
--- a/aclocal/kerberos5.m4
12f9a07
+++ b/aclocal/kerberos5.m4
12f9a07
@@ -43,15 +43,6 @@ AC_DEFUN([AC_KERBEROS_V5],[
12f9a07
                    -f $dir/lib/libgssapi_krb5.so \) ; then
12f9a07
          AC_DEFINE(HAVE_KRB5, 1, [Define this if you have MIT Kerberos libraries])
12f9a07
          KRBDIR="$dir"
12f9a07
-  dnl If we are using MIT K5 1.3.1 and before, we *MUST* use the
12f9a07
-  dnl private function (gss_krb5_ccache_name) to get correct
12f9a07
-  dnl behavior of changing the ccache used by gssapi.
12f9a07
-  dnl Starting in 1.3.2, we *DO NOT* want to use
12f9a07
-  dnl gss_krb5_ccache_name, instead we want to set KRB5CCNAME
12f9a07
-  dnl to get gssapi to use a different ccache
12f9a07
-         if test $K5VERS -le 131; then
12f9a07
-           AC_DEFINE(USE_GSS_KRB5_CCACHE_NAME, 1, [Define this if the private function, gss_krb5_cache_name, must be used to tell the Kerberos library which credentials cache to use. Otherwise, this is done by setting the KRB5CCNAME environment variable])
12f9a07
-         fi
12f9a07
          gssapi_lib=gssapi_krb5
12f9a07
          break
12f9a07
       dnl The following ugly hack brought on by the split installation
12f9a07
@@ -92,8 +83,6 @@ AC_DEFUN([AC_KERBEROS_V5],[
12f9a07
     AC_DEFINE(HAVE_LUCID_CONTEXT_SUPPORT, 1, [Define this if the Kerberos GSS library supports gss_krb5_export_lucid_sec_context]), ,$KRBLIBS)
12f9a07
   AC_CHECK_LIB($gssapi_lib, gss_krb5_set_allowable_enctypes,
12f9a07
     AC_DEFINE(HAVE_SET_ALLOWABLE_ENCTYPES, 1, [Define this if the Kerberos GSS library supports gss_krb5_set_allowable_enctypes]), ,$KRBLIBS)
12f9a07
-  AC_CHECK_LIB($gssapi_lib, gss_krb5_ccache_name,
12f9a07
-    AC_DEFINE(HAVE_GSS_KRB5_CCACHE_NAME, 1, [Define this if the Kerberos GSS library supports gss_krb5_ccache_name]), ,$KRBLIBS)
12f9a07
   AC_CHECK_LIB($gssapi_lib, gss_krb5_free_lucid_sec_context,
12f9a07
     AC_DEFINE(HAVE_GSS_KRB5_FREE_LUCID_SEC_CONTEXT, 1, [Define this if the Kerberos GSS library supports gss_krb5_free_lucid_sec_context]), ,$KRBLIBS)
12f9a07
 
12f9a07
diff --git a/aclocal/libpthread.m4 b/aclocal/libpthread.m4
12f9a07
new file mode 100644
12f9a07
index 0000000..e87d2a0
12f9a07
--- /dev/null
12f9a07
+++ b/aclocal/libpthread.m4
12f9a07
@@ -0,0 +1,13 @@
12f9a07
+dnl Checks for pthreads library and headers
12f9a07
+dnl
12f9a07
+AC_DEFUN([AC_LIBPTHREAD], [
12f9a07
+
12f9a07
+    dnl Check for library, but do not add -lpthreads to LIBS
12f9a07
+    AC_CHECK_LIB([pthread], [pthread_create], [LIBPTHREAD=-lpthread],
12f9a07
+                 [AC_MSG_ERROR([libpthread not found.])])
12f9a07
+  AC_SUBST(LIBPTHREAD)
12f9a07
+
12f9a07
+  AC_CHECK_HEADERS([pthread.h], ,
12f9a07
+                   [AC_MSG_ERROR([libpthread headers not found.])])
12f9a07
+
12f9a07
+])dnl
12f9a07
diff --git a/aclocal/librpcsecgss.m4 b/aclocal/librpcsecgss.m4
12f9a07
deleted file mode 100644
12f9a07
index e833141..0000000
12f9a07
--- a/aclocal/librpcsecgss.m4
12f9a07
+++ /dev/null
12f9a07
@@ -1,21 +0,0 @@
12f9a07
-dnl Checks for rpcsecgss library and headers
12f9a07
-dnl KRB5LIBS must be set before this function is invoked.
12f9a07
-dnl
12f9a07
-AC_DEFUN([AC_LIBRPCSECGSS], [
12f9a07
-
12f9a07
-  dnl libtirpc provides an rpcsecgss API
12f9a07
-  if test "$enable_tirpc" = no; then
12f9a07
-
12f9a07
-    dnl Check for library, but do not add -lrpcsecgss to LIBS
12f9a07
-    AC_CHECK_LIB([rpcsecgss], [authgss_create_default], [librpcsecgss=1],
12f9a07
-                 [AC_MSG_ERROR([librpcsecgss not found.])])
12f9a07
-
12f9a07
-    AC_CHECK_LIB([rpcsecgss], [authgss_set_debug_level],
12f9a07
-                 [AC_DEFINE([HAVE_AUTHGSS_SET_DEBUG_LEVEL], 1,
12f9a07
-                 [Define to 1 if you have the `authgss_set_debug_level' function.])])
12f9a07
-
12f9a07
-    AC_DEFINE([HAVE_AUTHGSS_FREE_PRIVATE_DATA], 1,
12f9a07
-	      [Define to 1 if your rpcsec library provides authgss_free_private_data,])
12f9a07
-  fi
12f9a07
-
12f9a07
-])dnl
12f9a07
diff --git a/aclocal/libtirpc.m4 b/aclocal/libtirpc.m4
12f9a07
index b7de636..27368ff 100644
12f9a07
--- a/aclocal/libtirpc.m4
12f9a07
+++ b/aclocal/libtirpc.m4
12f9a07
@@ -20,6 +20,12 @@ AC_DEFUN([AC_LIBTIRPC], [
12f9a07
                                     [Define to 1 if your rpcsec library provides authgss_free_private_data])],,
12f9a07
                          [${LIBS}])])
12f9a07
 
12f9a07
+     AS_IF([test -n "${LIBTIRPC}"],
12f9a07
+           [AC_CHECK_LIB([tirpc], [libtirpc_set_debug],
12f9a07
+                         [AC_DEFINE([HAVE_LIBTIRPC_SET_DEBUG], [1],
12f9a07
+                                    [Define to 1 if your tirpc library provides libtirpc_set_debug])],,
12f9a07
+                         [${LIBS}])])
12f9a07
+
12f9a07
   AC_SUBST([AM_CPPFLAGS])
12f9a07
   AC_SUBST(LIBTIRPC)
12f9a07
 
12f9a07
diff --git a/configure.ac b/configure.ac
12f9a07
index 25d2ba4..1daf5b8 100644
12f9a07
--- a/configure.ac
12f9a07
+++ b/configure.ac
12f9a07
@@ -382,8 +382,8 @@ if test "$enable_gss" = yes; then
12f9a07
   dnl Check for Kerberos V5
12f9a07
   AC_KERBEROS_V5
12f9a07
 
12f9a07
-  dnl Invoked after AC_KERBEROS_V5; AC_LIBRPCSECGSS needs to have KRBLIBS set
12f9a07
-  AC_LIBRPCSECGSS
12f9a07
+  dnl Check for pthreads
12f9a07
+  AC_LIBPTHREAD
12f9a07
 
12f9a07
   dnl librpcsecgss already has a dependency on libgssapi,
12f9a07
   dnl but we need to make sure we get the right version
12f9a07
diff --git a/support/export/client.c b/support/export/client.c
12f9a07
index 95156f0..2346f99 100644
12f9a07
--- a/support/export/client.c
12f9a07
+++ b/support/export/client.c
12f9a07
@@ -639,7 +639,7 @@ check_netgroup(const nfs_client *clp, const struct addrinfo *ai)
12f9a07
 	const char *netgroup = clp->m_hostname + 1;
12f9a07
 	struct addrinfo *tmp = NULL;
12f9a07
 	struct hostent *hp;
12f9a07
-	char *dot, *hname;
12f9a07
+	char *dot, *hname, *ip;
12f9a07
 	int i, match;
12f9a07
 
12f9a07
 	match = 0;
12f9a07
@@ -686,6 +686,18 @@ check_netgroup(const nfs_client *clp, const struct addrinfo *ai)
12f9a07
 		}
12f9a07
 	}
12f9a07
 
12f9a07
+	/* check whether the IP itself is in the netgroup */
12f9a07
+	ip = calloc(INET6_ADDRSTRLEN, 1);
12f9a07
+	if (inet_ntop(ai->ai_family, &(((struct sockaddr_in *)ai->ai_addr)->sin_addr), ip, INET6_ADDRSTRLEN) == ip) {
12f9a07
+		if (innetgr(netgroup, ip, NULL, NULL)) {
12f9a07
+			free(hname);
12f9a07
+			hname = ip;
12f9a07
+			match = 1;
12f9a07
+			goto out;
12f9a07
+		}
12f9a07
+	}
12f9a07
+	free(ip);
12f9a07
+
12f9a07
 	/* Okay, strip off the domain (if we have one) */
12f9a07
 	dot = strchr(hname, '.');
12f9a07
 	if (dot == NULL)
12f9a07
diff --git a/support/export/hostname.c b/support/export/hostname.c
12f9a07
index 169baa5..5c4c824 100644
12f9a07
--- a/support/export/hostname.c
12f9a07
+++ b/support/export/hostname.c
12f9a07
@@ -69,7 +69,7 @@ host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
12f9a07
 
12f9a07
 	memset(buf, 0, buflen);
12f9a07
 
12f9a07
-	if (sin->sin_family != AF_INET)
12f9a07
+	if (sin->sin_family != AF_INET) {
12f9a07
 		(void)strncpy(buf, "bad family", buflen - 1);
12f9a07
 		return buf;
12f9a07
 	}
12f9a07
@@ -134,12 +134,14 @@ host_pton(const char *paddr)
12f9a07
 			break;
12f9a07
 		}
12f9a07
 		return ai;
12f9a07
+	case EAI_NONAME:
12f9a07
+		break;
12f9a07
 	case EAI_SYSTEM:
12f9a07
-		xlog(D_GENERAL, "%s: failed to convert %s: (%d) %m",
12f9a07
+		xlog(L_WARNING, "%s: failed to convert %s: (%d) %m",
12f9a07
 				__func__, paddr, errno);
12f9a07
 		break;
12f9a07
 	default:
12f9a07
-		xlog(D_GENERAL, "%s: failed to convert %s: %s",
12f9a07
+		xlog(L_WARNING, "%s: failed to convert %s: %s",
12f9a07
 				__func__, paddr, gai_strerror(error));
12f9a07
 		break;
12f9a07
 	}
12f9a07
@@ -228,7 +230,7 @@ host_canonname(const struct sockaddr *sap)
12f9a07
 	default:
12f9a07
 		(void)getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
12f9a07
 							NULL, 0, NI_NUMERICHOST);
12f9a07
-		xlog(D_GENERAL, "%s: failed to resolve %s: %s",
12f9a07
+		xlog(D_PARSE, "%s: failed to resolve %s: %s",
12f9a07
 				__func__, buf, gai_strerror(error));
12f9a07
 		return NULL;
12f9a07
 	}
12f9a07
diff --git a/support/include/ha-callout.h b/support/include/ha-callout.h
12f9a07
index 1164336..a454bdb 100644
12f9a07
--- a/support/include/ha-callout.h
12f9a07
+++ b/support/include/ha-callout.h
12f9a07
@@ -47,7 +47,7 @@ ha_callout(char *event, char *arg1, char *arg2, int arg3)
12f9a07
 			      arg3 < 0 ? NULL : buf,
12f9a07
 			      NULL);
12f9a07
 			perror("execl");
12f9a07
-			exit(2);
12f9a07
+			_exit(2);
12f9a07
 		case -1: perror("fork");
12f9a07
 			break;
12f9a07
 		default: pid = waitpid(pid, &ret, 0);
12f9a07
diff --git a/support/include/nfslib.h b/support/include/nfslib.h
12f9a07
index c9a13cb..ddd71ac 100644
12f9a07
--- a/support/include/nfslib.h
12f9a07
+++ b/support/include/nfslib.h
12f9a07
@@ -176,6 +176,9 @@ size_t  strlcpy(char *, const char *, size_t);
12f9a07
 ssize_t atomicio(ssize_t (*f) (int, void*, size_t),
12f9a07
 		 int, void *, size_t);
12f9a07
 
12f9a07
+#ifdef HAVE_LIBTIRPC_SET_DEBUG
12f9a07
+void  libtirpc_set_debug(char *name, int level, int use_stderr);
12f9a07
+#endif
12f9a07
 
12f9a07
 #define UNUSED(x) UNUSED_ ## x __attribute__((unused))
12f9a07
 
12f9a07
diff --git a/support/include/xcommon.h b/support/include/xcommon.h
12f9a07
index d1a4b18..23c9a13 100644
12f9a07
--- a/support/include/xcommon.h
12f9a07
+++ b/support/include/xcommon.h
12f9a07
@@ -17,6 +17,12 @@
12f9a07
 #include <stdlib.h>
12f9a07
 #include <string.h>
12f9a07
 
12f9a07
+#ifdef MAJOR_IN_MKDEV
12f9a07
+#include <sys/mkdev.h>
12f9a07
+#elif defined(MAJOR_IN_SYSMACROS)
12f9a07
+#include <sys/sysmacros.h>
12f9a07
+#endif
12f9a07
+
12f9a07
 #define streq(s, t)	(strcmp ((s), (t)) == 0)
12f9a07
 
12f9a07
 /* Functions in sundries.c that are used in mount.c and umount.c  */ 
12f9a07
diff --git a/support/nfs/closeall.c b/support/nfs/closeall.c
12f9a07
index 38fb162..a69bf35 100644
12f9a07
--- a/support/nfs/closeall.c
12f9a07
+++ b/support/nfs/closeall.c
12f9a07
@@ -31,6 +31,7 @@ closeall(int min)
12f9a07
 	} else {
12f9a07
 		int fd = sysconf(_SC_OPEN_MAX);
12f9a07
 		while (--fd >= min)
12f9a07
-			(void) close(fd);
12f9a07
+			if(fd >= 0)
12f9a07
+				(void) close(fd);
12f9a07
 	}
12f9a07
 }
12f9a07
diff --git a/support/nfs/mydaemon.c b/support/nfs/mydaemon.c
12f9a07
index 3391eff..343e80b 100644
12f9a07
--- a/support/nfs/mydaemon.c
12f9a07
+++ b/support/nfs/mydaemon.c
12f9a07
@@ -49,6 +49,7 @@
12f9a07
 #include <stdbool.h>
12f9a07
 #include <stdlib.h>
12f9a07
 #include <string.h>
12f9a07
+#include <syslog.h>
12f9a07
 #include <xlog.h>
12f9a07
 
12f9a07
 #include "nfslib.h"
12f9a07
@@ -122,6 +123,7 @@ daemon_init(bool fg)
12f9a07
 	dup2(tempfd, 0);
12f9a07
 	dup2(tempfd, 1);
12f9a07
 	dup2(tempfd, 2);
12f9a07
+	closelog();
12f9a07
 	dup2(pipefds[1], 3);
12f9a07
 	pipefds[1] = 3;
12f9a07
 	closeall(4);
12f9a07
diff --git a/support/nfs/nfsexport.c b/support/nfs/nfsexport.c
12f9a07
index afd7c90..4b13265 100644
12f9a07
--- a/support/nfs/nfsexport.c
12f9a07
+++ b/support/nfs/nfsexport.c
12f9a07
@@ -19,6 +19,7 @@
12f9a07
 
12f9a07
 #include "nfslib.h"
12f9a07
 #include "misc.h"
12f9a07
+#include "xcommon.h"
12f9a07
 
12f9a07
 	/* if /proc/net/rpc/... exists, then 
12f9a07
 	 * write to it, as that interface is more stable.
12f9a07
diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c
12f9a07
index 2900d18..bdf6d2f 100644
12f9a07
--- a/support/nfs/rpc_socket.c
12f9a07
+++ b/support/nfs/rpc_socket.c
12f9a07
@@ -185,7 +185,7 @@ static int nfs_connect_nb(const int fd, const struct sockaddr *sap,
12f9a07
 	 * use it later.
12f9a07
 	 */
12f9a07
 	ret = connect(fd, sap, salen);
12f9a07
-	if (ret < 0 && errno != EINPROGRESS) {
12f9a07
+	if (ret < 0 && errno != EINPROGRESS && errno != EINTR) {
12f9a07
 		ret = -1;
12f9a07
 		goto done;
12f9a07
 	}
12f9a07
@@ -197,10 +197,16 @@ static int nfs_connect_nb(const int fd, const struct sockaddr *sap,
12f9a07
 	FD_ZERO(&rset);
12f9a07
 	FD_SET(fd, &rset);
12f9a07
 
12f9a07
-	ret = select(fd + 1, NULL, &rset, NULL, timeout);
12f9a07
-	if (ret <= 0) {
12f9a07
-		if (ret == 0)
12f9a07
-			errno = ETIMEDOUT;
12f9a07
+	while ((ret = select(fd + 1, NULL, &rset, NULL, timeout)) < 0) {
12f9a07
+		if (errno != EINTR) {
12f9a07
+			ret = -1;
12f9a07
+			goto done;
12f9a07
+		} else {
12f9a07
+			continue;
12f9a07
+		}
12f9a07
+	}
12f9a07
+	if (ret == 0) {
12f9a07
+		errno = ETIMEDOUT;
12f9a07
 		ret = -1;
12f9a07
 		goto done;
12f9a07
 	}
12f9a07
diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
12f9a07
index 5cb5ff6..ef7ff05 100644
12f9a07
--- a/support/nfs/svc_create.c
12f9a07
+++ b/support/nfs/svc_create.c
12f9a07
@@ -133,7 +133,7 @@ svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
12f9a07
 		hint.ai_family = AF_INET6;
12f9a07
 #endif	/* IPV6_SUPPORTED */
12f9a07
 	else {
12f9a07
-		xlog(D_GENERAL, "Unrecognized bind address family: %s",
12f9a07
+		xlog(L_ERROR, "Unrecognized bind address family: %s",
12f9a07
 			nconf->nc_protofmly);
12f9a07
 		return NULL;
12f9a07
 	}
12f9a07
@@ -143,7 +143,7 @@ svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
12f9a07
 	else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
12f9a07
 		hint.ai_protocol = (int)IPPROTO_TCP;
12f9a07
 	else {
12f9a07
-		xlog(D_GENERAL, "Unrecognized bind address protocol: %s",
12f9a07
+		xlog(L_ERROR, "Unrecognized bind address protocol: %s",
12f9a07
 			nconf->nc_proto);
12f9a07
 		return NULL;
12f9a07
 	}
12f9a07
@@ -275,7 +275,7 @@ svc_create_nconf_rand_port(const char *name, const rpcprog_t program,
12f9a07
 	xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0);
12f9a07
 	freeaddrinfo(ai);
12f9a07
 	if (xprt == NULL) {
12f9a07
-		xlog(D_GENERAL, "Failed to create listener xprt "
12f9a07
+		xlog(L_ERROR, "Failed to create listener xprt "
12f9a07
 			"(%s, %u, %s)", name, version, nconf->nc_netid);
12f9a07
 		return 0;
12f9a07
 	}
12f9a07
@@ -286,10 +286,12 @@ svc_create_nconf_rand_port(const char *name, const rpcprog_t program,
12f9a07
 		return 0;
12f9a07
 	}
12f9a07
 
12f9a07
+	rpc_createerr.cf_stat = rpc_createerr.cf_error.re_errno = 0;
12f9a07
 	if (!svc_reg(xprt, program, version, dispatch, nconf)) {
12f9a07
 		/* svc_reg(3) destroys @xprt in this case */
12f9a07
-		xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
12f9a07
-				name, version, nconf->nc_netid);
12f9a07
+		xlog(L_ERROR, "Failed to register (%s, %u, %s): %s",
12f9a07
+				name, version, nconf->nc_netid, 
12f9a07
+				clnt_spcreateerror("svc_reg() err"));
12f9a07
 		return 0;
12f9a07
 	}
12f9a07
 
12f9a07
diff --git a/support/nfs/svc_socket.c b/support/nfs/svc_socket.c
12f9a07
index 99321e7..1fa0d15 100644
12f9a07
--- a/support/nfs/svc_socket.c
12f9a07
+++ b/support/nfs/svc_socket.c
12f9a07
@@ -24,6 +24,7 @@
12f9a07
 #include <sys/socket.h>
12f9a07
 #include <sys/fcntl.h>
12f9a07
 #include <errno.h>
12f9a07
+#include "xlog.h"
12f9a07
 
12f9a07
 #include "config.h"
12f9a07
 
12f9a07
@@ -99,9 +100,9 @@ svcsock_nonblock(int sock)
12f9a07
 	 * connection.
12f9a07
 	 */
12f9a07
 	if ((flags = fcntl(sock, F_GETFL)) < 0)
12f9a07
-		perror(_("svc_socket: can't get socket flags"));
12f9a07
+		xlog(L_ERROR, "svc_socket: can't get socket flags: %m");
12f9a07
 	else if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
12f9a07
-		perror(_("svc_socket: can't set socket flags"));
12f9a07
+		xlog(L_ERROR, "svc_socket: can't set socket flags: %m");
12f9a07
 	else
12f9a07
 		return sock;
12f9a07
 
12f9a07
@@ -119,7 +120,7 @@ svc_socket (u_long number, int type, int protocol, int reuse)
12f9a07
 
12f9a07
   if ((sock = __socket (AF_INET, type, protocol)) < 0)
12f9a07
     {
12f9a07
-      perror (_("svc_socket: socket creation problem"));
12f9a07
+      xlog(L_ERROR, "svc_socket: socket creation problem: %m");
12f9a07
       return sock;
12f9a07
     }
12f9a07
 
12f9a07
@@ -130,7 +131,7 @@ svc_socket (u_long number, int type, int protocol, int reuse)
12f9a07
 			sizeof (ret));
12f9a07
       if (ret < 0)
12f9a07
 	{
12f9a07
-	  perror (_("svc_socket: socket reuse problem"));
12f9a07
+	  xlog(L_ERROR, "svc_socket: socket reuse problem: %m");
12f9a07
 	  return ret;
12f9a07
 	}
12f9a07
     }
12f9a07
@@ -141,7 +142,7 @@ svc_socket (u_long number, int type, int protocol, int reuse)
12f9a07
 
12f9a07
   if (bind(sock, (struct sockaddr *) &addr, len) < 0)
12f9a07
     {
12f9a07
-      perror (_("svc_socket: bind problem"));
12f9a07
+      xlog(L_ERROR, "svc_socket: bind problem: %m");
12f9a07
       (void) __close(sock);
12f9a07
       sock = -1;
12f9a07
     }
12f9a07
diff --git a/support/nsm/file.c b/support/nsm/file.c
12f9a07
index 4711c2c..7a8b504 100644
12f9a07
--- a/support/nsm/file.c
12f9a07
+++ b/support/nsm/file.c
12f9a07
@@ -536,7 +536,8 @@ nsm_get_state(_Bool update)
12f9a07
 		state++;
12f9a07
 
12f9a07
 update:
12f9a07
-	(void)close(fd);
12f9a07
+	if(fd >= 0)
12f9a07
+		(void)close(fd);
12f9a07
 
12f9a07
 	if (update) {
12f9a07
 		state += 2;
12f9a07
diff --git a/systemd/Makefile.am b/systemd/Makefile.am
12f9a07
index 0331926..03f96e9 100644
12f9a07
--- a/systemd/Makefile.am
12f9a07
+++ b/systemd/Makefile.am
12f9a07
@@ -28,9 +28,13 @@ endif
12f9a07
 if CONFIG_GSS
12f9a07
 unit_files += \
12f9a07
     auth-rpcgss-module.service \
12f9a07
-    rpc-gssd.service \
12f9a07
+    rpc-gssd.service
12f9a07
+
12f9a07
+if CONFIG_SVCGSS
12f9a07
+unit_files += \
12f9a07
     rpc-svcgssd.service
12f9a07
 endif
12f9a07
+endif
12f9a07
 
12f9a07
 EXTRA_DIST = $(unit_files)
12f9a07
 
12f9a07
diff --git a/systemd/README b/systemd/README
12f9a07
index bbd7790..7c43df8 100644
12f9a07
--- a/systemd/README
12f9a07
+++ b/systemd/README
12f9a07
@@ -53,7 +53,7 @@ client and systemd cannot specify is two-pronged reverse dependency.
12f9a07
 (i.e. stop this unit if none of these units are running)
12f9a07
 
12f9a07
 Distro specific commandline configuration can be provided by
12f9a07
-installing a script /usr/lib/systemd/scripts/nfs-utils_env.sh
12f9a07
+installing a script /usr/libexec/nfs-utils/nfs-utils_env.sh
12f9a07
 This should write /run/sysconfig/nfs-utils based on configuration
12f9a07
 information such as in /etc/sysconfig/nfs or /etc/defaults/nfs.
12f9a07
 It is run once by nfs-config.service.
12f9a07
diff --git a/systemd/nfs-config.service b/systemd/nfs-config.service
12f9a07
index 7f65305..bd69e84 100644
12f9a07
--- a/systemd/nfs-config.service
12f9a07
+++ b/systemd/nfs-config.service
12f9a07
@@ -5,5 +5,9 @@ DefaultDependencies=no
12f9a07
 
12f9a07
 [Service]
12f9a07
 Type=oneshot
12f9a07
-RemainAfterExit=yes
12f9a07
-ExecStart=/usr/lib/systemd/scripts/nfs-utils_env.sh
12f9a07
+# This service needs to run any time any nfs service
12f9a07
+# is started, so changes to local config files get
12f9a07
+# incorporated.  Having "RemainAfterExit=no" (the default)
12f9a07
+# ensures this happens.
12f9a07
+RemainAfterExit=no
12f9a07
+ExecStart=/usr/libexec/nfs-utils/nfs-utils_env.sh
12f9a07
diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service
12f9a07
index 12b02f2..2ccdc63 100644
12f9a07
--- a/systemd/nfs-server.service
12f9a07
+++ b/systemd/nfs-server.service
12f9a07
@@ -1,13 +1,14 @@
12f9a07
 [Unit]
12f9a07
 Description=NFS server and services
12f9a07
 DefaultDependencies=no
12f9a07
-Requires= network.target proc-fs-nfsd.mount rpcbind.service
12f9a07
+Requires= network.target proc-fs-nfsd.mount
12f9a07
 Requires= nfs-mountd.service
12f9a07
+Wants=rpcbind.socket
12f9a07
 Wants=rpc-statd.service nfs-idmapd.service
12f9a07
 Wants=rpc-statd-notify.service
12f9a07
 
12f9a07
 After= local-fs.target
12f9a07
-After= network.target proc-fs-nfsd.mount rpcbind.service nfs-mountd.service
12f9a07
+After= network.target proc-fs-nfsd.mount rpcbind.socket nfs-mountd.service
12f9a07
 After= nfs-idmapd.service rpc-statd.service
12f9a07
 Before= rpc-statd-notify.service
12f9a07
 
12f9a07
diff --git a/systemd/rpc-statd.service b/systemd/rpc-statd.service
12f9a07
index 14604d7..a02f5c4 100644
12f9a07
--- a/systemd/rpc-statd.service
12f9a07
+++ b/systemd/rpc-statd.service
12f9a07
@@ -2,8 +2,8 @@
12f9a07
 Description=NFS status monitor for NFSv2/3 locking.
12f9a07
 DefaultDependencies=no
12f9a07
 Conflicts=umount.target
12f9a07
-Requires=nss-lookup.target rpcbind.target
12f9a07
-After=network.target nss-lookup.target rpcbind.target
12f9a07
+Requires=nss-lookup.target rpcbind.socket
12f9a07
+After=network.target nss-lookup.target rpcbind.socket
12f9a07
 
12f9a07
 PartOf=nfs-utils.service
12f9a07
 
12f9a07
diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py
12f9a07
index 011bb42..4ca4bc4 100644
12f9a07
--- a/tools/mountstats/mountstats.py
12f9a07
+++ b/tools/mountstats/mountstats.py
12f9a07
@@ -150,6 +150,8 @@ Nfsv3ops = [
12f9a07
     'COMMIT'
12f9a07
 ]
12f9a07
 
12f9a07
+# This list should be kept in-sync with the NFSPROC4_CLNT_* enum in
12f9a07
+# include/linux/nfs4.h in the kernel.
12f9a07
 Nfsv4ops = [
12f9a07
     'NULL',
12f9a07
     'READ',
12f9a07
@@ -204,7 +206,12 @@ Nfsv4ops = [
12f9a07
     'FREE_STATEID',
12f9a07
     'GETDEVICELIST',
12f9a07
     'BIND_CONN_TO_SESSION',
12f9a07
-    'DESTROY_CLIENTID'
12f9a07
+    'DESTROY_CLIENTID',
12f9a07
+    'SEEK',
12f9a07
+    'ALLOCATE',
12f9a07
+    'DEALLOCATE',
12f9a07
+    'LAYOUTSTATS',
12f9a07
+    'CLONE'
12f9a07
 ]
12f9a07
 
12f9a07
 class DeviceData:
12f9a07
@@ -563,7 +570,10 @@ class DeviceData:
12f9a07
         for the nfsstat command.
12f9a07
         """
12f9a07
         for op in new_stats.__rpc_data['ops']:
12f9a07
-            self.__rpc_data[op] = list(map(add, self.__rpc_data[op], new_stats.__rpc_data[op]))
12f9a07
+            try:
12f9a07
+                self.__rpc_data[op] = list(map(add, self.__rpc_data[op], new_stats.__rpc_data[op]))
12f9a07
+            except KeyError:
12f9a07
+                continue
12f9a07
 
12f9a07
     def __print_rpc_op_stats(self, op, sample_time):
12f9a07
         """Print generic stats for one RPC op
12f9a07
diff --git a/utils/blkmapd/device-discovery.c b/utils/blkmapd/device-discovery.c
12f9a07
index b52afe2..052d582 100644
12f9a07
--- a/utils/blkmapd/device-discovery.c
12f9a07
+++ b/utils/blkmapd/device-discovery.c
12f9a07
@@ -51,6 +51,7 @@
12f9a07
 #include <libdevmapper.h>
12f9a07
 
12f9a07
 #include "device-discovery.h"
12f9a07
+#include "xcommon.h"
12f9a07
 
12f9a07
 #define EVENT_SIZE (sizeof(struct inotify_event))
12f9a07
 #define EVENT_BUFSIZE (1024 * EVENT_SIZE)
12f9a07
@@ -427,7 +428,10 @@ void sig_die(int signal)
12f9a07
 	BL_LOG_ERR("exit on signal(%d)\n", signal);
12f9a07
 	exit(1);
12f9a07
 }
12f9a07
-
12f9a07
+static void usage(void)
12f9a07
+{
12f9a07
+	fprintf(stderr, "Usage: blkmapd [-hdf]\n" );
12f9a07
+}
12f9a07
 /* Daemon */
12f9a07
 int main(int argc, char **argv)
12f9a07
 {
12f9a07
@@ -435,7 +439,7 @@ int main(int argc, char **argv)
12f9a07
 	struct stat statbuf;
12f9a07
 	char pidbuf[64];
12f9a07
 
12f9a07
-	while ((opt = getopt(argc, argv, "df")) != -1) {
12f9a07
+	while ((opt = getopt(argc, argv, "hdf")) != -1) {
12f9a07
 		switch (opt) {
12f9a07
 		case 'd':
12f9a07
 			dflag = 1;
12f9a07
@@ -443,6 +447,13 @@ int main(int argc, char **argv)
12f9a07
 		case 'f':
12f9a07
 			fg = 1;
12f9a07
 			break;
12f9a07
+		case 'h':
12f9a07
+			usage();
12f9a07
+			exit(0);
12f9a07
+		default:
12f9a07
+			usage();
12f9a07
+			exit(1);
12f9a07
+			
12f9a07
 		}
12f9a07
 	}
12f9a07
 
12f9a07
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
12f9a07
index 8758231..5db348b 100644
12f9a07
--- a/utils/exportfs/exportfs.c
12f9a07
+++ b/utils/exportfs/exportfs.c
12f9a07
@@ -108,11 +108,14 @@ main(int argc, char **argv)
12f9a07
 	xlog_stderr(1);
12f9a07
 	xlog_syslog(0);
12f9a07
 
12f9a07
-	while ((c = getopt(argc, argv, "afhio:ruvs")) != EOF) {
12f9a07
+	while ((c = getopt(argc, argv, "ad:fhio:ruvs")) != EOF) {
12f9a07
 		switch(c) {
12f9a07
 		case 'a':
12f9a07
 			f_all = 1;
12f9a07
 			break;
12f9a07
+		case 'd':
12f9a07
+			xlog_sconfig(optarg, 1);
12f9a07
+			break;
12f9a07
 		case 'f':
12f9a07
 			force_flush = 1;
12f9a07
 			break;
12f9a07
@@ -405,8 +408,17 @@ unexportfs_parsed(char *hname, char *path, int verbose)
12f9a07
 			hname = ai->ai_canonname;
12f9a07
 	}
12f9a07
 
12f9a07
+	/*
12f9a07
+	 * It's possible the specified path ends with a '/'. But
12f9a07
+	 * the entry from exportlist won't has the trailing '/',
12f9a07
+	 * so need to deal with it.
12f9a07
+	*/
12f9a07
+	size_t nlen = strlen(path);
12f9a07
+	while (path[nlen - 1] == '/')
12f9a07
+		nlen--;
12f9a07
+
12f9a07
 	for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
12f9a07
-		if (path && strcmp(path, exp->m_export.e_path))
12f9a07
+		if (path && strncmp(path, exp->m_export.e_path, nlen))
12f9a07
 			continue;
12f9a07
 		if (htype != exp->m_client->m_type)
12f9a07
 			continue;
12f9a07
@@ -499,9 +511,10 @@ unexportfs(char *arg, int verbose)
12f9a07
 
12f9a07
 static int can_test(void)
12f9a07
 {
12f9a07
-	char buf[1024];
12f9a07
+	char buf[1024] = { 0 };
12f9a07
 	int fd;
12f9a07
 	int n;
12f9a07
+	size_t bufsiz = sizeof(buf);
12f9a07
 
12f9a07
 	fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY);
12f9a07
 	if (fd < 0)
12f9a07
@@ -514,9 +527,9 @@ static int can_test(void)
12f9a07
 	 * commit 2f74f972  (sunrpc: prepare NFS for 2038).
12f9a07
 	 */
12f9a07
 	if (time(NULL) > INT_TO_LONG_THRESHOLD_SECS)
12f9a07
-		sprintf(buf, "nfsd 0.0.0.0 %ld -test-client-\n", LONG_MAX);
12f9a07
+		snprintf(buf, bufsiz-1, "nfsd 0.0.0.0 %ld -test-client-\n", LONG_MAX);
12f9a07
 	else
12f9a07
-		sprintf(buf, "nfsd 0.0.0.0 %d -test-client-\n", INT_MAX);
12f9a07
+		snprintf(buf, bufsiz-1, "nfsd 0.0.0.0 %d -test-client-\n", INT_MAX);
12f9a07
 
12f9a07
 	n = write(fd, buf, strlen(buf));
12f9a07
 	close(fd);
12f9a07
@@ -532,7 +545,8 @@ static int can_test(void)
12f9a07
 
12f9a07
 static int test_export(char *path, int with_fsid)
12f9a07
 {
12f9a07
-	char buf[1024];
12f9a07
+	/* beside max path, buf size should take protocol str into account */
12f9a07
+	char buf[NFS_MAXPATHLEN+1+64] = { 0 };
12f9a07
 	char *bp = buf;
12f9a07
 	int len = sizeof(buf);
12f9a07
 	int fd, n;
12f9a07
@@ -758,7 +772,8 @@ dumpopt(char c, char *fmt, ...)
12f9a07
 static void
12f9a07
 dump(int verbose, int export_format)
12f9a07
 {
12f9a07
-	char buf[1024];
12f9a07
+	/* buf[] size should >= sizeof(struct exportent->e_path) */
12f9a07
+	char buf[NFS_MAXPATHLEN+1] = { 0 };
12f9a07
 	char *bp;
12f9a07
 	int len;
12f9a07
 	nfs_export	*exp;
12f9a07
@@ -866,6 +881,6 @@ error(nfs_export *exp, int err)
12f9a07
 static void
12f9a07
 usage(const char *progname, int n)
12f9a07
 {
12f9a07
-	fprintf(stderr, "usage: %s [-afhioruvs] [host:/path]\n", progname);
12f9a07
+	fprintf(stderr, "usage: %s [-adfhioruvs] [host:/path]\n", progname);
12f9a07
 	exit(n);
12f9a07
 }
12f9a07
diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man
12f9a07
index 75d952a..fdf9260 100644
12f9a07
--- a/utils/exportfs/exportfs.man
12f9a07
+++ b/utils/exportfs/exportfs.man
12f9a07
@@ -88,6 +88,9 @@ appropriate export entry for the host given in
12f9a07
 to be added to the kernel's export table.
12f9a07
 .SH OPTIONS
12f9a07
 .TP
12f9a07
+.B \-d kind " or " \-\-debug kind
12f9a07
+Turn on debugging. Valid kinds are: all, auth, call, general and parse.
12f9a07
+.TP
12f9a07
 .B -a
12f9a07
 Export or unexport all directories.
12f9a07
 .TP
12f9a07
diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am
12f9a07
index cb040b3..3f5f59a 100644
12f9a07
--- a/utils/gssd/Makefile.am
12f9a07
+++ b/utils/gssd/Makefile.am
12f9a07
@@ -49,7 +49,8 @@ gssd_LDADD = \
12f9a07
 	$(RPCSECGSS_LIBS) \
12f9a07
 	$(KRBLIBS) \
12f9a07
 	$(GSSAPI_LIBS) \
12f9a07
-	$(LIBTIRPC)
12f9a07
+	$(LIBTIRPC) \
12f9a07
+	$(LIBPTHREAD)
12f9a07
 
12f9a07
 gssd_LDFLAGS = \
12f9a07
 	$(KRBLDFLAGS)
12f9a07
diff --git a/utils/gssd/context_heimdal.c b/utils/gssd/context_heimdal.c
12f9a07
index 1e8738a..d07103b 100644
12f9a07
--- a/utils/gssd/context_heimdal.c
12f9a07
+++ b/utils/gssd/context_heimdal.c
12f9a07
@@ -260,7 +260,7 @@ serialize_krb5_ctx(gss_ctx_id_t *_ctx, gss_buffer_desc *buf, int32_t *endtime)
12f9a07
 	if (write_heimdal_seq_key(&p, end, ctx)) goto out_err;
12f9a07
 
12f9a07
 	buf->length = p - (char *)buf->value;
12f9a07
-	printerr(2, "serialize_krb5_ctx: returning buffer "
12f9a07
+	printerr(4, "serialize_krb5_ctx: returning buffer "
12f9a07
 		    "with %d bytes\n", buf->length);
12f9a07
 
12f9a07
 	return 0;
12f9a07
diff --git a/utils/gssd/context_lucid.c b/utils/gssd/context_lucid.c
12f9a07
index badbe88..5d77c21 100644
12f9a07
--- a/utils/gssd/context_lucid.c
12f9a07
+++ b/utils/gssd/context_lucid.c
12f9a07
@@ -206,7 +206,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx,
12f9a07
 	if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err;
12f9a07
 
12f9a07
 	/* Protocol 0 here implies DES3 or RC4 */
12f9a07
-	printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol);
12f9a07
+	printerr(4, "%s: protocol %d\n", __FUNCTION__, lctx->protocol);
12f9a07
 	if (lctx->protocol == 0) {
12f9a07
 		enctype = lctx->rfc1964_kd.ctx_key.type;
12f9a07
 		keysize = lctx->rfc1964_kd.ctx_key.length;
12f9a07
@@ -219,7 +219,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx,
12f9a07
 			keysize = lctx->cfx_kd.ctx_key.length;
12f9a07
 		}
12f9a07
 	}
12f9a07
-	printerr(2, "%s: serializing key with enctype %d and size %d\n",
12f9a07
+	printerr(4, "%s: serializing key with enctype %d and size %d\n",
12f9a07
 		 __FUNCTION__, enctype, keysize);
12f9a07
 
12f9a07
 	if (WRITE_BYTES(&p, end, enctype)) goto out_err;
12f9a07
@@ -265,7 +265,7 @@ serialize_krb5_ctx(gss_ctx_id_t *ctx, gss_buffer_desc *buf, int32_t *endtime)
12f9a07
 	gss_krb5_lucid_context_v1_t *lctx = 0;
12f9a07
 	int retcode = 0;
12f9a07
 
12f9a07
-	printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__);
12f9a07
+	printerr(4, "DEBUG: %s: lucid version!\n", __FUNCTION__);
12f9a07
 	maj_stat = gss_export_lucid_sec_context(&min_stat, ctx,
12f9a07
 						1, &return_ctx);
12f9a07
 	if (maj_stat != GSS_S_COMPLETE) {
12f9a07
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
12f9a07
index e480349..9bf7917 100644
12f9a07
--- a/utils/gssd/gssd.c
12f9a07
+++ b/utils/gssd/gssd.c
12f9a07
@@ -87,7 +87,9 @@ unsigned int  rpc_timeout = 5;
12f9a07
 char *preferred_realm = NULL;
12f9a07
 /* Avoid DNS reverse lookups on server names */
12f9a07
 static bool avoid_dns = true;
12f9a07
-
12f9a07
+int thread_started = false;
12f9a07
+pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER;
12f9a07
+pthread_cond_t pcond = PTHREAD_COND_INITIALIZER;
12f9a07
 
12f9a07
 TAILQ_HEAD(topdir_list_head, topdir) topdir_list;
12f9a07
 
12f9a07
@@ -361,20 +363,54 @@ gssd_destroy_client(struct clnt_info *clp)
12f9a07
 
12f9a07
 static void gssd_scan(void);
12f9a07
 
12f9a07
+static inline void 
12f9a07
+wait_for_child_and_detach(pthread_t th)
12f9a07
+{
12f9a07
+	pthread_mutex_lock(&pmutex);
12f9a07
+	while (!thread_started)
12f9a07
+		pthread_cond_wait(&pcond, &pmutex);
12f9a07
+	thread_started = false;
12f9a07
+	pthread_mutex_unlock(&pmutex);
12f9a07
+	pthread_detach(th);
12f9a07
+}
12f9a07
+
12f9a07
+/* For each upcall create a thread, detach from the main process so that
12f9a07
+ * resources are released back into the system without the need for a join.
12f9a07
+ * We need to wait for the child thread to start and consume the event from
12f9a07
+ * the file descriptor.
12f9a07
+ */
12f9a07
 static void
12f9a07
 gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data)
12f9a07
 {
12f9a07
 	struct clnt_info *clp = data;
12f9a07
-
12f9a07
-	handle_gssd_upcall(clp);
12f9a07
+	pthread_t th;
12f9a07
+	int ret;
12f9a07
+
12f9a07
+	ret = pthread_create(&th, NULL, (void *)handle_gssd_upcall,
12f9a07
+				(void *)clp);
12f9a07
+	if (ret != 0) {
12f9a07
+		printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
12f9a07
+			 ret, strerror(errno));
12f9a07
+		return;
12f9a07
+	}
12f9a07
+	wait_for_child_and_detach(th);
12f9a07
 }
12f9a07
 
12f9a07
 static void
12f9a07
 gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data)
12f9a07
 {
12f9a07
 	struct clnt_info *clp = data;
12f9a07
-
12f9a07
-	handle_krb5_upcall(clp);
12f9a07
+	pthread_t th;
12f9a07
+	int ret;
12f9a07
+
12f9a07
+	ret = pthread_create(&th, NULL, (void *)handle_krb5_upcall,
12f9a07
+				(void *)clp);
12f9a07
+	if (ret != 0) {
12f9a07
+		printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
12f9a07
+			 ret, strerror(errno));
12f9a07
+		return;
12f9a07
+	}
12f9a07
+	wait_for_child_and_detach(th);
12f9a07
 }
12f9a07
 
12f9a07
 static struct clnt_info *
12f9a07
@@ -400,8 +436,9 @@ gssd_get_clnt(struct topdir *tdi, const char *name)
12f9a07
 
12f9a07
 	clp->wd = inotify_add_watch(inotify_fd, clp->relpath, IN_CREATE | IN_DELETE);
12f9a07
 	if (clp->wd < 0) {
12f9a07
-		printerr(0, "ERROR: inotify_add_watch failed for %s: %s\n",
12f9a07
-			 clp->relpath, strerror(errno));
12f9a07
+		if (errno != ENOENT)
12f9a07
+			printerr(0, "ERROR: inotify_add_watch failed for %s: %s\n",
12f9a07
+			 	clp->relpath, strerror(errno));
12f9a07
 		goto out;
12f9a07
 	}
12f9a07
 
12f9a07
@@ -556,7 +593,7 @@ gssd_scan_topdir(const char *name)
12f9a07
 		if (clp->scanned)
12f9a07
 			continue;
12f9a07
 
12f9a07
-		printerr(2, "destroying client %s\n", clp->relpath);
12f9a07
+		printerr(3, "destroying client %s\n", clp->relpath);
12f9a07
 		saveprev = clp->list.tqe_prev;
12f9a07
 		TAILQ_REMOVE(&tdi->clnt_list, clp, list);
12f9a07
 		gssd_destroy_client(clp);
12f9a07
@@ -716,7 +753,7 @@ gssd_inotify_cb(int ifd, short UNUSED(which), void *UNUSED(data))
12f9a07
 
12f9a07
 found:
12f9a07
 			if (!tdi) {
12f9a07
-				printerr(1, "inotify event for unknown wd!!! - "
12f9a07
+				printerr(5, "inotify event for unknown wd!!! - "
12f9a07
 					 "ev->wd (%d) ev->name (%s) ev->mask (0x%08x)\n",
12f9a07
 					 ev->wd, ev->len > 0 ? ev->name : "", ev->mask);
12f9a07
 				rescan = true;
12f9a07
@@ -820,7 +857,7 @@ main(int argc, char *argv[])
12f9a07
 	 * the results of getpw*.
12f9a07
 	 */
12f9a07
 	if (setenv("HOME", "/", 1)) {
12f9a07
-		printerr(1, "Unable to set $HOME: %s\n", strerror(errno));
12f9a07
+		printerr(0, "gssd: Unable to set $HOME: %s\n", strerror(errno));
12f9a07
 		exit(1);
12f9a07
 	}
12f9a07
 
12f9a07
@@ -865,14 +902,16 @@ main(int argc, char *argv[])
12f9a07
 		progname = argv[0];
12f9a07
 
12f9a07
 	initerr(progname, verbosity, fg);
12f9a07
-#ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL
12f9a07
-	if (verbosity && rpc_verbosity == 0)
12f9a07
-		rpc_verbosity = verbosity;
12f9a07
-	authgss_set_debug_level(rpc_verbosity);
12f9a07
+#ifdef HAVE_LIBTIRPC_SET_DEBUG
12f9a07
+	/*
12f9a07
+	 * Only set the libtirpc debug level if explicitly requested via -r.
12f9a07
+	 */
12f9a07
+	if (rpc_verbosity > 0)
12f9a07
+		libtirpc_set_debug(progname, rpc_verbosity, fg);
12f9a07
 #else
12f9a07
-        if (rpc_verbosity > 0)
12f9a07
-		printerr(0, "Warning: rpcsec_gss library does not "
12f9a07
-			    "support setting debug level\n");
12f9a07
+	if (rpc_verbosity > 0)
12f9a07
+		printerr(0, "Warning: libtirpc does not "
12f9a07
+			    "support setting debug levels\n");
12f9a07
 #endif
12f9a07
 
12f9a07
 	if (gssd_check_mechs() != 0)
12f9a07
@@ -884,19 +923,19 @@ main(int argc, char *argv[])
12f9a07
 
12f9a07
 	pipefs_dir = opendir(pipefs_path);
12f9a07
 	if (!pipefs_dir) {
12f9a07
-		printerr(1, "ERROR: opendir(%s) failed: %s\n", pipefs_path, strerror(errno));
12f9a07
+		printerr(0, "ERROR: opendir(%s) failed: %s\n", pipefs_path, strerror(errno));
12f9a07
 		exit(EXIT_FAILURE);
12f9a07
 	}
12f9a07
 
12f9a07
 	pipefs_fd = dirfd(pipefs_dir);
12f9a07
 	if (fchdir(pipefs_fd)) {
12f9a07
-		printerr(1, "ERROR: fchdir(%s) failed: %s\n", pipefs_path, strerror(errno));
12f9a07
+		printerr(0, "ERROR: fchdir(%s) failed: %s\n", pipefs_path, strerror(errno));
12f9a07
 		exit(EXIT_FAILURE);
12f9a07
 	}
12f9a07
 
12f9a07
 	inotify_fd = inotify_init1(IN_NONBLOCK);
12f9a07
 	if (inotify_fd == -1) {
12f9a07
-		printerr(1, "ERROR: inotify_init1 failed: %s\n", strerror(errno));
12f9a07
+		printerr(0, "ERROR: inotify_init1 failed: %s\n", strerror(errno));
12f9a07
 		exit(EXIT_FAILURE);
12f9a07
 	}
12f9a07
 
12f9a07
@@ -913,7 +952,7 @@ main(int argc, char *argv[])
12f9a07
 
12f9a07
 	event_dispatch();
12f9a07
 
12f9a07
-	printerr(1, "ERROR: event_dispatch() returned!\n");
12f9a07
+	printerr(0, "ERROR: event_dispatch() returned!\n");
12f9a07
 	return EXIT_FAILURE;
12f9a07
 }
12f9a07
 
12f9a07
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
12f9a07
index c6937c5..565bce3 100644
12f9a07
--- a/utils/gssd/gssd.h
12f9a07
+++ b/utils/gssd/gssd.h
12f9a07
@@ -36,6 +36,7 @@
12f9a07
 #include <gssapi/gssapi.h>
12f9a07
 #include <event.h>
12f9a07
 #include <stdbool.h>
12f9a07
+#include <pthread.h>
12f9a07
 
12f9a07
 #ifndef GSSD_PIPEFS_DIR
12f9a07
 #define GSSD_PIPEFS_DIR		"/var/lib/nfs/rpc_pipefs"
12f9a07
@@ -61,6 +62,10 @@ extern int			root_uses_machine_creds;
12f9a07
 extern unsigned int 		context_timeout;
12f9a07
 extern unsigned int rpc_timeout;
12f9a07
 extern char			*preferred_realm;
12f9a07
+extern pthread_mutex_t ple_lock;
12f9a07
+extern pthread_cond_t pcond;
12f9a07
+extern pthread_mutex_t pmutex;
12f9a07
+extern int thread_started;
12f9a07
 
12f9a07
 struct clnt_info {
12f9a07
 	TAILQ_ENTRY(clnt_info)	list;
12f9a07
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
12f9a07
index 11168b2..b19c595 100644
12f9a07
--- a/utils/gssd/gssd_proc.c
12f9a07
+++ b/utils/gssd/gssd_proc.c
12f9a07
@@ -69,6 +69,7 @@
12f9a07
 #include <netdb.h>
12f9a07
 #include <sys/types.h>
12f9a07
 #include <sys/wait.h>
12f9a07
+#include <syscall.h>
12f9a07
 
12f9a07
 #include "gssd.h"
12f9a07
 #include "err_util.h"
12f9a07
@@ -150,7 +151,7 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
12f9a07
 	unsigned int timeout = context_timeout;
12f9a07
 	unsigned int buf_size = 0;
12f9a07
 
12f9a07
-	printerr(1, "doing downcall: lifetime_rec=%u acceptor=%.*s\n",
12f9a07
+	printerr(2, "doing downcall: lifetime_rec=%u acceptor=%.*s\n",
12f9a07
 		lifetime_rec, acceptor->length, acceptor->value);
12f9a07
 	buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
12f9a07
 		sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
12f9a07
@@ -189,7 +190,7 @@ do_error_downcall(int k5_fd, uid_t uid, int err)
12f9a07
 	unsigned int timeout = 0;
12f9a07
 	int	zero = 0;
12f9a07
 
12f9a07
-	printerr(1, "doing error downcall\n");
12f9a07
+	printerr(2, "doing error downcall\n");
12f9a07
 
12f9a07
 	if (WRITE_BYTES(&p, end, uid)) goto out_err;
12f9a07
 	if (WRITE_BYTES(&p, end, timeout)) goto out_err;
12f9a07
@@ -348,16 +349,9 @@ create_auth_rpc_client(struct clnt_info *clp,
12f9a07
 	printerr(2, "creating %s client for server %s\n", clp->protocol,
12f9a07
 			clp->servername);
12f9a07
 
12f9a07
-	if ((strcmp(clp->protocol, "tcp")) == 0) {
12f9a07
-		protocol = IPPROTO_TCP;
12f9a07
-	} else if ((strcmp(clp->protocol, "udp")) == 0) {
12f9a07
+	protocol = IPPROTO_TCP;
12f9a07
+	if ((strcmp(clp->protocol, "udp")) == 0)
12f9a07
 		protocol = IPPROTO_UDP;
12f9a07
-	} else {
12f9a07
-		printerr(0, "WARNING: unrecognized protocol, '%s', requested "
12f9a07
-			 "for connection to server %s for user with uid %d\n",
12f9a07
-			 clp->protocol, clp->servername, uid);
12f9a07
-		goto out_fail;
12f9a07
-	}
12f9a07
 
12f9a07
 	switch (addr->sa_family) {
12f9a07
 	case AF_INET:
12f9a07
@@ -443,7 +437,7 @@ change_identity(uid_t uid)
12f9a07
 	struct passwd	*pw;
12f9a07
 
12f9a07
 	/* drop list of supplimentary groups first */
12f9a07
-	if (setgroups(0, NULL) != 0) {
12f9a07
+	if (syscall(SYS_setgroups, 0, 0) != 0) {
12f9a07
 		printerr(0, "WARNING: unable to drop supplimentary groups!");
12f9a07
 		return errno;
12f9a07
 	}
12f9a07
@@ -460,20 +454,18 @@ change_identity(uid_t uid)
12f9a07
 		}
12f9a07
 	}
12f9a07
 
12f9a07
-	/*
12f9a07
-	 * Switch the GIDs. Note that we leave the saved-set-gid alone in an
12f9a07
-	 * attempt to prevent attacks via ptrace()
12f9a07
+	/* Switch the UIDs and GIDs. */
12f9a07
+	/* For the threaded version we have to set uid,gid per thread instead
12f9a07
+	 * of per process. glibc setresuid() when called from a thread, it'll
12f9a07
+	 * send a signal to all other threads to synchronize the uid in all
12f9a07
+	 * other threads. To bypass this, we have to call syscall() directly.
12f9a07
 	 */
12f9a07
-	if (setresgid(pw->pw_gid, pw->pw_gid, -1) != 0) {
12f9a07
+	if (syscall(SYS_setresgid, pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
12f9a07
 		printerr(0, "WARNING: failed to set gid to %u!\n", pw->pw_gid);
12f9a07
 		return errno;
12f9a07
 	}
12f9a07
 
12f9a07
-	/*
12f9a07
-	 * Switch UIDs, but leave saved-set-uid alone to prevent ptrace() by
12f9a07
-	 * other processes running with this uid.
12f9a07
-	 */
12f9a07
-	if (setresuid(uid, uid, -1) != 0) {
12f9a07
+	if (syscall(SYS_setresuid, uid, uid, uid) != 0) {
12f9a07
 		printerr(0, "WARNING: Failed to setuid for user with uid %u\n",
12f9a07
 				uid);
12f9a07
 		return errno;
12f9a07
@@ -491,7 +483,7 @@ krb5_not_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
12f9a07
 	char		**dname;
12f9a07
 	int		err, resp = -1;
12f9a07
 
12f9a07
-	printerr(1, "krb5_not_machine_creds: uid %d tgtname %s\n", 
12f9a07
+	printerr(2, "krb5_not_machine_creds: uid %d tgtname %s\n", 
12f9a07
 		uid, tgtname);
12f9a07
 
12f9a07
 	*chg_err = change_identity(uid);
12f9a07
@@ -538,7 +530,7 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
12f9a07
 	int	nocache = 0;
12f9a07
 	int	success = 0;
12f9a07
 
12f9a07
-	printerr(1, "krb5_use_machine_creds: uid %d tgtname %s\n", 
12f9a07
+	printerr(2, "krb5_use_machine_creds: uid %d tgtname %s\n", 
12f9a07
 		uid, tgtname);
12f9a07
 
12f9a07
 	do {
12f9a07
@@ -555,7 +547,15 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
12f9a07
 			goto out;
12f9a07
 		}
12f9a07
 		for (ccname = credlist; ccname && *ccname; ccname++) {
12f9a07
-			gssd_setup_krb5_machine_gss_ccache(*ccname);
12f9a07
+			u_int min_stat;
12f9a07
+
12f9a07
+			if (gss_krb5_ccache_name(&min_stat, *ccname, NULL) !=
12f9a07
+					GSS_S_COMPLETE) {
12f9a07
+				printerr(1, "WARNING: gss_krb5_ccache_name "
12f9a07
+					 "with name '%s' failed (%s)\n",
12f9a07
+					 *ccname, error_message(min_stat));
12f9a07
+				continue;
12f9a07
+			}
12f9a07
 			if ((create_auth_rpc_client(clp, tgtname, rpc_clnt,
12f9a07
 						&auth, uid,
12f9a07
 						AUTHTYPE_KRB5,
12f9a07
@@ -564,7 +564,7 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
12f9a07
 				success++;
12f9a07
 				break;
12f9a07
 			}
12f9a07
-			printerr(2, "WARNING: Failed to create machine krb5"
12f9a07
+			printerr(2, "WARNING: Failed to create machine krb5 "
12f9a07
 				"context with cred cache %s for server %s\n",
12f9a07
 				*ccname, clp->servername);
12f9a07
 		}
12f9a07
@@ -572,12 +572,13 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
12f9a07
 		if (!success) {
12f9a07
 			if(nocache == 0) {
12f9a07
 				nocache++;
12f9a07
-				printerr(2, "WARNING: Machine cache prematurely"					 "expired or corrupted trying to"
12f9a07
-					 "recreate cache for server %s\n",
12f9a07
+				printerr(2, "WARNING: Machine cache prematurely "
12f9a07
+					"expired or corrupted trying to "
12f9a07
+					"recreate cache for server %s\n",
12f9a07
 					clp->servername);
12f9a07
 			} else {
12f9a07
-				printerr(1, "WARNING: Failed to create machine"
12f9a07
-					 "krb5 context with any credentials"
12f9a07
+				printerr(1, "ERROR: Failed to create machine "
12f9a07
+					 "krb5 context with any credentials "
12f9a07
 					 "cache for server %s\n",
12f9a07
 					clp->servername);
12f9a07
 				goto out;
12f9a07
@@ -608,8 +609,6 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
12f9a07
 	gss_OID			mech;
12f9a07
 	gss_buffer_desc		acceptor  = {0};
12f9a07
 
12f9a07
-	printerr(1, "handling krb5 upcall (%s)\n", clp->relpath);
12f9a07
-
12f9a07
 	token.length = 0;
12f9a07
 	token.value = NULL;
12f9a07
 	memset(&pd, 0, sizeof(struct authgss_private_data));
12f9a07
@@ -635,41 +634,9 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
12f9a07
 	 * used for this case is not important.
12f9a07
 	 *
12f9a07
 	 */
12f9a07
-	printerr(2, "%s: service is '%s'\n", __func__,
12f9a07
-		 service ? service : "<null>");
12f9a07
 	if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
12f9a07
 				service == NULL)) {
12f9a07
 
12f9a07
-		/* already running as uid 0 */
12f9a07
-		if (uid == 0)
12f9a07
-			goto no_fork;
12f9a07
-
12f9a07
-		pid = fork();
12f9a07
-		switch(pid) {
12f9a07
-		case 0:
12f9a07
-			/* Child: fall through to rest of function */
12f9a07
-			childpid = getpid();
12f9a07
-			unsetenv("KRB5CCNAME");
12f9a07
-			printerr(1, "CHILD forked pid %d \n", childpid);
12f9a07
-			break;
12f9a07
-		case -1:
12f9a07
-			/* fork() failed! */
12f9a07
-			printerr(0, "WARNING: unable to fork() to handle"
12f9a07
-				"upcall: %s\n", strerror(errno));
12f9a07
-			return;
12f9a07
-		default:
12f9a07
-			/* Parent: just wait on child to exit and return */
12f9a07
-			do {
12f9a07
-				pid = wait(&err;;
12f9a07
-			} while(pid == -1 && errno != -ECHILD);
12f9a07
-
12f9a07
-			if (WIFSIGNALED(err))
12f9a07
-				printerr(0, "WARNING: forked child was killed"
12f9a07
-					 "with signal %d\n", WTERMSIG(err));
12f9a07
-			return;
12f9a07
-		}
12f9a07
-no_fork:
12f9a07
-
12f9a07
 		auth = krb5_not_machine_creds(clp, uid, tgtname, &downcall_err,
12f9a07
 						&err, &rpc_clnt);
12f9a07
 		if (err)
12f9a07
@@ -683,9 +650,7 @@ no_fork:
12f9a07
 			if (auth == NULL)
12f9a07
 				goto out_return_error;
12f9a07
 		} else {
12f9a07
-			printerr(1, "WARNING: Failed to create krb5 context "
12f9a07
-				 "for user with uid %d for server %s\n",
12f9a07
-				 uid, clp->servername);
12f9a07
+			/* krb5_not_machine_creds logs the error */
12f9a07
 			goto out_return_error;
12f9a07
 		}
12f9a07
 	}
12f9a07
@@ -716,7 +681,7 @@ no_fork:
12f9a07
 	 * try to use it after this point.
12f9a07
 	 */
12f9a07
 	if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) {
12f9a07
-		printerr(0, "WARNING: Failed to serialize krb5 context for "
12f9a07
+		printerr(1, "WARNING: Failed to serialize krb5 context for "
12f9a07
 			    "user with uid %d for server %s\n",
12f9a07
 			 uid, clp->servername);
12f9a07
 		goto out_return_error;
12f9a07
@@ -748,17 +713,36 @@ out_return_error:
12f9a07
 	goto out;
12f9a07
 }
12f9a07
 
12f9a07
+/* signal to the parent thread that we have read from the file descriptor.
12f9a07
+ * it should allow the parent to proceed to poll on the descriptor for
12f9a07
+ * the next upcall from the kernel.
12f9a07
+ */
12f9a07
+static inline void
12f9a07
+signal_parent_event_consumed(void)
12f9a07
+{
12f9a07
+	pthread_mutex_lock(&pmutex);
12f9a07
+	thread_started = true;
12f9a07
+	pthread_cond_signal(&pcond);
12f9a07
+	pthread_mutex_unlock(&pmutex);
12f9a07
+}
12f9a07
+
12f9a07
 void
12f9a07
 handle_krb5_upcall(struct clnt_info *clp)
12f9a07
 {
12f9a07
 	uid_t			uid;
12f9a07
+	int 			status;
12f9a07
 
12f9a07
-	if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) {
12f9a07
+	status = read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid);
12f9a07
+	signal_parent_event_consumed();
12f9a07
+
12f9a07
+	if (status) {
12f9a07
 		printerr(0, "WARNING: failed reading uid from krb5 "
12f9a07
 			    "upcall pipe: %s\n", strerror(errno));
12f9a07
 		return;
12f9a07
 	}
12f9a07
 
12f9a07
+	printerr(2, "\n%s: uid %d (%s)\n", __func__, uid, clp->relpath);
12f9a07
+
12f9a07
 	process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL);
12f9a07
 }
12f9a07
 
12f9a07
@@ -775,9 +759,9 @@ handle_gssd_upcall(struct clnt_info *clp)
12f9a07
 	char			*service = NULL;
12f9a07
 	char			*enctypes = NULL;
12f9a07
 
12f9a07
-	printerr(1, "handling gssd upcall (%s)\n", clp->relpath);
12f9a07
-
12f9a07
 	lbuflen = read(clp->gssd_fd, lbuf, sizeof(lbuf));
12f9a07
+	signal_parent_event_consumed();
12f9a07
+
12f9a07
 	if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') {
12f9a07
 		printerr(0, "WARNING: handle_gssd_upcall: "
12f9a07
 			    "failed reading request\n");
12f9a07
@@ -785,7 +769,7 @@ handle_gssd_upcall(struct clnt_info *clp)
12f9a07
 	}
12f9a07
 	lbuf[lbuflen-1] = 0;
12f9a07
 
12f9a07
-	printerr(2, "%s: '%s'\n", __func__, lbuf);
12f9a07
+	printerr(2, "\n%s: '%s' (%s)\n", __func__, lbuf, clp->relpath);
12f9a07
 
12f9a07
 	for (p = strtok(lbuf, " "); p; p = strtok(NULL, " ")) {
12f9a07
 		if (!strncmp(p, "mech=", strlen("mech=")))
12f9a07
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
12f9a07
index ecf17a2..c1e4d2b 100644
12f9a07
--- a/utils/gssd/krb5_util.c
12f9a07
+++ b/utils/gssd/krb5_util.c
12f9a07
@@ -128,6 +128,7 @@
12f9a07
 
12f9a07
 /* Global list of principals/cache file names for machine credentials */
12f9a07
 struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
12f9a07
+pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER;
12f9a07
 
12f9a07
 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
12f9a07
 int limit_to_legacy_enctypes = 0;
12f9a07
@@ -356,7 +357,7 @@ gssd_get_single_krb5_cred(krb5_context context,
12f9a07
 	 */
12f9a07
 	now += 300;
12f9a07
 	if (ple->ccname && ple->endtime > now && !nocache) {
12f9a07
-		printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
12f9a07
+		printerr(3, "INFO: Credentials in CC '%s' are good until %d\n",
12f9a07
 			 ple->ccname, ple->endtime);
12f9a07
 		code = 0;
12f9a07
 		goto out;
12f9a07
@@ -383,7 +384,7 @@ gssd_get_single_krb5_cred(krb5_context context,
12f9a07
 			 "tickets.  May have problems behind a NAT.\n");
12f9a07
 #ifdef TEST_SHORT_LIFETIME
12f9a07
 	/* set a short lifetime (for debugging only!) */
12f9a07
-	printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
12f9a07
+	printerr(1, "WARNING: Using (debug) short machine cred lifetime!\n");
12f9a07
 	krb5_get_init_creds_opt_set_tkt_life(init_opts, 5*60);
12f9a07
 #endif
12f9a07
 	opts = init_opts;
12f9a07
@@ -451,8 +452,7 @@ gssd_get_single_krb5_cred(krb5_context context,
12f9a07
 	}
12f9a07
 
12f9a07
 	code = 0;
12f9a07
-	printerr(2, "Successfully obtained machine credentials for "
12f9a07
-		 "principal '%s' stored in ccache '%s'\n", pname, cc_name);
12f9a07
+	printerr(2, "%s: principal '%s' ccache:'%s'\n", __func__, pname, cc_name);
12f9a07
   out:
12f9a07
 #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
12f9a07
 	if (init_opts)
12f9a07
@@ -468,37 +468,6 @@ gssd_get_single_krb5_cred(krb5_context context,
12f9a07
 }
12f9a07
 
12f9a07
 /*
12f9a07
- * Depending on the version of Kerberos, we either need to use
12f9a07
- * a private function, or simply set the environment variable.
12f9a07
- */
12f9a07
-static void
12f9a07
-gssd_set_krb5_ccache_name(char *ccname)
12f9a07
-{
12f9a07
-#ifdef USE_GSS_KRB5_CCACHE_NAME
12f9a07
-	u_int	maj_stat, min_stat;
12f9a07
-
12f9a07
-	printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
12f9a07
-		 ccname);
12f9a07
-	maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
12f9a07
-	if (maj_stat != GSS_S_COMPLETE) {
12f9a07
-		printerr(0, "WARNING: gss_krb5_ccache_name with "
12f9a07
-			"name '%s' failed (%s)\n",
12f9a07
-			ccname, error_message(min_stat));
12f9a07
-	}
12f9a07
-#else
12f9a07
-	/*
12f9a07
-	 * Set the KRB5CCNAME environment variable to tell the krb5 code
12f9a07
-	 * which credentials cache to use.  (Instead of using the private
12f9a07
-	 * function above for which there is no generic gssapi
12f9a07
-	 * equivalent.)
12f9a07
-	 */
12f9a07
-	printerr(2, "using environment variable to select krb5 ccache %s\n",
12f9a07
-		 ccname);
12f9a07
-	setenv("KRB5CCNAME", ccname, 1);
12f9a07
-#endif
12f9a07
-}
12f9a07
-
12f9a07
-/*
12f9a07
  * Given a principal, find a matching ple structure
12f9a07
  */
12f9a07
 static struct gssd_k5_kt_princ *
12f9a07
@@ -587,10 +556,12 @@ get_ple_by_princ(krb5_context context, krb5_principal princ)
12f9a07
 
12f9a07
 	/* Need to serialize list if we ever become multi-threaded! */
12f9a07
 
12f9a07
+	pthread_mutex_lock(&ple_lock);
12f9a07
 	ple = find_ple_by_princ(context, princ);
12f9a07
 	if (ple == NULL) {
12f9a07
 		ple = new_ple(context, princ);
12f9a07
 	}
12f9a07
+	pthread_mutex_unlock(&ple_lock);
12f9a07
 
12f9a07
 	return ple;
12f9a07
 }
12f9a07
@@ -797,11 +768,11 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *tgtname,
12f9a07
 	char **realmnames = NULL;
12f9a07
 	char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
12f9a07
 	char myhostad[NI_MAXHOST+1];
12f9a07
-	int i, j, retval;
12f9a07
+	int i, j, k, retval;
12f9a07
 	char *default_realm = NULL;
12f9a07
 	char *realm;
12f9a07
 	char *k5err = NULL;
12f9a07
-	int tried_all = 0, tried_default = 0;
12f9a07
+	int tried_all = 0, tried_default = 0, tried_upper = 0;
12f9a07
 	krb5_principal princ;
12f9a07
 	const char *notsetstr = "not set";
12f9a07
 	char *adhostoverride;
12f9a07
@@ -835,7 +806,6 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *tgtname,
12f9a07
 	        strcpy(myhostad, myhostname);
12f9a07
 	        for (i = 0; myhostad[i] != 0; ++i) {
12f9a07
 	          if (myhostad[i] == '.') break;
12f9a07
-	          myhostad[i] = toupper(myhostad[i]);
12f9a07
 	        }
12f9a07
 	        myhostad[i] = '$';
12f9a07
 	        myhostad[i+1] = 0;
12f9a07
@@ -936,6 +906,19 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *tgtname,
12f9a07
 				k5err = gssd_k5_err_msg(context, code);
12f9a07
 				printerr(3, "%s while getting keytab entry for '%s'\n",
12f9a07
 					 k5err, spn);
12f9a07
+				/*
12f9a07
+				 * We tried the active directory machine account
12f9a07
+				 * with the hostname part as-is and failed...
12f9a07
+				 * convert it to uppercase and try again before
12f9a07
+				 * moving on to the svcname
12f9a07
+				 */
12f9a07
+				if (strcmp(svcnames[j],"$") == 0 && !tried_upper) {
12f9a07
+					for (k = 0; myhostad[k] != '$'; ++k) {
12f9a07
+						myhostad[k] = toupper(myhostad[k]);
12f9a07
+					}
12f9a07
+					j--;
12f9a07
+					tried_upper = 1;
12f9a07
+				}
12f9a07
 			} else {
12f9a07
 				printerr(3, "Success getting keytab entry for '%s'\n",spn);
12f9a07
 				retval = 0;
12f9a07
@@ -1080,9 +1063,10 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
12f9a07
 	const char		*cctype;
12f9a07
 	struct dirent		*d;
12f9a07
 	int			err, i, j;
12f9a07
+	u_int			maj_stat, min_stat;
12f9a07
 
12f9a07
-	printerr(2, "getting credentials for client with uid %u for "
12f9a07
-		    "server %s\n", uid, servername);
12f9a07
+	printerr(3, "looking for client creds with uid %u for "
12f9a07
+		    "server %s in %s\n", uid, servername, dirpattern);
12f9a07
 
12f9a07
 	for (i = 0, j = 0; dirpattern[i] != '\0'; i++) {
12f9a07
 		switch (dirpattern[i]) {
12f9a07
@@ -1115,22 +1099,16 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
12f9a07
 
12f9a07
 	printerr(2, "using %s as credentials cache for client with "
12f9a07
 		    "uid %u for server %s\n", buf, uid, servername);
12f9a07
-	gssd_set_krb5_ccache_name(buf);
12f9a07
-	return 0;
12f9a07
-}
12f9a07
 
12f9a07
-/*
12f9a07
- * Let the gss code know where to find the machine credentials ccache.
12f9a07
- *
12f9a07
- * Returns:
12f9a07
- *	void
12f9a07
- */
12f9a07
-void
12f9a07
-gssd_setup_krb5_machine_gss_ccache(char *ccname)
12f9a07
-{
12f9a07
-	printerr(2, "using %s as credentials cache for machine creds\n",
12f9a07
-		 ccname);
12f9a07
-	gssd_set_krb5_ccache_name(ccname);
12f9a07
+	printerr(3, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
12f9a07
+		 buf);
12f9a07
+	maj_stat = gss_krb5_ccache_name(&min_stat, buf, NULL);
12f9a07
+	if (maj_stat != GSS_S_COMPLETE) {
12f9a07
+		printerr(0, "ERROR: unable to get user cred cache '%s' "
12f9a07
+			 "failed (%s)\n", buf, error_message(min_stat));
12f9a07
+		return maj_stat;
12f9a07
+	}
12f9a07
+	return 0;
12f9a07
 }
12f9a07
 
12f9a07
 /*
12f9a07
@@ -1398,16 +1376,21 @@ gssd_acquire_krb5_cred(gss_cred_id_t *gss_cred)
12f9a07
 int
12f9a07
 gssd_acquire_user_cred(gss_cred_id_t *gss_cred)
12f9a07
 {
12f9a07
-	OM_uint32 min_stat;
12f9a07
+	OM_uint32 maj_stat, min_stat;
12f9a07
 	int ret;
12f9a07
 
12f9a07
 	ret = gssd_acquire_krb5_cred(gss_cred);
12f9a07
 
12f9a07
 	/* force validation of cred to check for expiry */
12f9a07
 	if (ret == 0) {
12f9a07
-		if (gss_inquire_cred(&min_stat, *gss_cred, NULL, NULL,
12f9a07
-				     NULL, NULL) != GSS_S_COMPLETE)
12f9a07
-			ret = -1;
12f9a07
+		maj_stat = gss_inquire_cred(&min_stat, *gss_cred, 
12f9a07
+			NULL, NULL, NULL, NULL);
12f9a07
+		if (maj_stat != GSS_S_COMPLETE) {
12f9a07
+			if (get_verbosity() > 0)
12f9a07
+				pgsserr("gss_inquire_cred",
12f9a07
+					maj_stat, min_stat, &krb5oid);
12f9a07
+				ret = -1;
12f9a07
+			}
12f9a07
 	}
12f9a07
 
12f9a07
 	return ret;
12f9a07
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
12f9a07
index a319588..e3bbb07 100644
12f9a07
--- a/utils/gssd/krb5_util.h
12f9a07
+++ b/utils/gssd/krb5_util.h
12f9a07
@@ -27,7 +27,6 @@ int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername,
12f9a07
 				     char *dirname);
12f9a07
 int  gssd_get_krb5_machine_cred_list(char ***list);
12f9a07
 void gssd_free_krb5_machine_cred_list(char **list);
12f9a07
-void gssd_setup_krb5_machine_gss_ccache(char *servername);
12f9a07
 void gssd_destroy_krb5_machine_creds(void);
12f9a07
 int  gssd_refresh_krb5_machine_credential(char *hostname,
12f9a07
 					  struct gssd_k5_kt_princ *ple, 
12f9a07
@@ -55,8 +54,6 @@ int limit_krb5_enctypes(struct rpc_gss_sec *sec);
12f9a07
 #define k5_free_unparsed_name(ctx, name)	free(name)
12f9a07
 #define k5_free_default_realm(ctx, realm)	free(realm)
12f9a07
 #define k5_free_kt_entry(ctx, kte)		krb5_kt_free_entry((ctx),(kte))
12f9a07
-#undef USE_GSS_KRB5_CCACHE_NAME
12f9a07
-#define USE_GSS_KRB5_CCACHE_NAME 1
12f9a07
 #endif
12f9a07
 
12f9a07
 #endif /* KRB5_UTIL_H */
12f9a07
diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c
12f9a07
index f1b4347..0fe7c6d 100644
12f9a07
--- a/utils/gssd/svcgssd.c
12f9a07
+++ b/utils/gssd/svcgssd.c
12f9a07
@@ -135,6 +135,13 @@ main(int argc, char *argv[])
12f9a07
 	if (verbosity && rpc_verbosity == 0)
12f9a07
 		rpc_verbosity = verbosity;
12f9a07
 	authgss_set_debug_level(rpc_verbosity);
12f9a07
+#elif HAVE_LIBTIRPC_SET_DEBUG
12f9a07
+        /*
12f9a07
+	 * Only set the libtirpc debug level if explicitly requested via -r...
12f9a07
+	 * svcgssd is chatty enough as it is.
12f9a07
+	 */
12f9a07
+        if (rpc_verbosity > 0)
12f9a07
+                libtirpc_set_debug(progname, rpc_verbosity, fg);
12f9a07
 #else
12f9a07
 	if (rpc_verbosity > 0)
12f9a07
 		printerr(0, "Warning: rpcsec_gss library does not "
12f9a07
diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c
12f9a07
index 689608a..f4e083a 100644
12f9a07
--- a/utils/idmapd/idmapd.c
12f9a07
+++ b/utils/idmapd/idmapd.c
12f9a07
@@ -199,6 +199,12 @@ flush_nfsd_idmap_cache(void)
12f9a07
 	return ret;
12f9a07
 }
12f9a07
 
12f9a07
+void usage(char *progname)
12f9a07
+{
12f9a07
+	fprintf(stderr, "Usage: %s [-hfvCS] [-p path] [-c path]\n",
12f9a07
+		basename(progname));
12f9a07
+}
12f9a07
+
12f9a07
 int
12f9a07
 main(int argc, char **argv)
12f9a07
 {
12f9a07
@@ -225,16 +231,18 @@ main(int argc, char **argv)
12f9a07
 		progname = argv[0];
12f9a07
 	xlog_open(progname);
12f9a07
 
12f9a07
-#define GETOPTSTR "vfd:p:U:G:c:CS"
12f9a07
+#define GETOPTSTR "hvfd:p:U:G:c:CS"
12f9a07
 	opterr=0; /* Turn off error messages */
12f9a07
 	while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) {
12f9a07
 		if (opt == 'c')
12f9a07
 			conf_path = optarg;
12f9a07
 		if (opt == '?') {
12f9a07
 			if (strchr(GETOPTSTR, optopt))
12f9a07
-				errx(1, "'-%c' option requires an argument.", optopt);
12f9a07
+				warnx("'-%c' option requires an argument.", optopt);
12f9a07
 			else
12f9a07
-				errx(1, "'-%c' is an invalid argument.", optopt);
12f9a07
+				warnx("'-%c' is an invalid argument.", optopt);
12f9a07
+			usage(progname);
12f9a07
+			exit(1);
12f9a07
 		}
12f9a07
 	}
12f9a07
 	optind = 1;
12f9a07
@@ -276,6 +284,9 @@ main(int argc, char **argv)
12f9a07
 		case 'S':
12f9a07
 			clientstart = 0;
12f9a07
 			break;
12f9a07
+		case 'h':
12f9a07
+			usage(progname);
12f9a07
+			exit(0);
12f9a07
 		default:
12f9a07
 			break;
12f9a07
 		}
12f9a07
diff --git a/utils/idmapd/idmapd.man b/utils/idmapd/idmapd.man
12f9a07
index c809f78..b9200c7 100644
12f9a07
--- a/utils/idmapd/idmapd.man
12f9a07
+++ b/utils/idmapd/idmapd.man
12f9a07
@@ -10,8 +10,11 @@
12f9a07
 .Sh SYNOPSIS
12f9a07
 .\" For a program:  program [-abc] file ...
12f9a07
 .Nm rpc.idmapd
12f9a07
-.Op Fl v
12f9a07
+.Op Fl h
12f9a07
 .Op Fl f
12f9a07
+.Op Fl v
12f9a07
+.Op Fl C
12f9a07
+.Op Fl S
12f9a07
 .Op Fl p Ar path
12f9a07
 .Op Fl c Ar path
12f9a07
 .Sh DESCRIPTION
12f9a07
@@ -32,6 +35,8 @@ program.
12f9a07
 .Pp
12f9a07
 The options are as follows:
12f9a07
 .Bl -tag -width Ds_imagedir
12f9a07
+.It Fl h
12f9a07
+Display usage message.
12f9a07
 .It Fl v
12f9a07
 Increases the verbosity level (can be specified multiple times).
12f9a07
 .It Fl f
12f9a07
diff --git a/utils/mount/network.c b/utils/mount/network.c
12f9a07
index b5ed850..0d12613 100644
12f9a07
--- a/utils/mount/network.c
12f9a07
+++ b/utils/mount/network.c
12f9a07
@@ -92,6 +92,7 @@ static const char *nfs_version_opttbl[] = {
12f9a07
 	"v4",
12f9a07
 	"vers",
12f9a07
 	"nfsvers",
12f9a07
+	"minorversion",
12f9a07
 	NULL,
12f9a07
 };
12f9a07
 
12f9a07
@@ -793,6 +794,8 @@ int start_statd(void)
12f9a07
 	if (stat(START_STATD, &stb) == 0) {
12f9a07
 		if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
12f9a07
 			int cnt = STATD_TIMEOUT * 10;
12f9a07
+			int status = 0;
12f9a07
+			char * const envp[1] = { NULL };
12f9a07
 			const struct timespec ts = {
12f9a07
 				.tv_sec = 0,
12f9a07
 				.tv_nsec = 100000000,
12f9a07
@@ -800,14 +803,19 @@ int start_statd(void)
12f9a07
 			pid_t pid = fork();
12f9a07
 			switch (pid) {
12f9a07
 			case 0: /* child */
12f9a07
-				execl(START_STATD, START_STATD, NULL);
12f9a07
+				setgid(0);
12f9a07
+				setuid(0);
12f9a07
+				execle(START_STATD, START_STATD, NULL, envp);
12f9a07
 				exit(1);
12f9a07
 			case -1: /* error */
12f9a07
 				nfs_error(_("%s: fork failed: %s"),
12f9a07
 						progname, strerror(errno));
12f9a07
 				break;
12f9a07
 			default: /* parent */
12f9a07
-				waitpid(pid, NULL,0);
12f9a07
+				if (waitpid(pid, &status,0) == pid &&
12f9a07
+				    status == 0)
12f9a07
+					/* assume it worked */
12f9a07
+					return 1;
12f9a07
 				break;
12f9a07
 			}
12f9a07
 			while (1) {
12f9a07
@@ -1272,7 +1280,11 @@ nfs_nfs_version(struct mount_options *options, struct nfs_version *version)
12f9a07
 	if (!(version->major = strtol(version_val, &cptr, 10)))
12f9a07
 		goto ret_error;
12f9a07
 
12f9a07
-	if (version->major < 4)
12f9a07
+	if (strcmp(nfs_version_opttbl[i], "minorversion") == 0) {
12f9a07
+		version->v_mode = V_SPECIFIC;
12f9a07
+		version->minor = version->major;
12f9a07
+		version->major = 4;
12f9a07
+	} else if (version->major < 4)
12f9a07
 		version->v_mode = V_SPECIFIC;
12f9a07
 
12f9a07
 	if (*cptr == '.') {
12f9a07
@@ -1626,7 +1638,10 @@ int nfs_options2pmap(struct mount_options *options,
12f9a07
 		return 0;
12f9a07
 	if (!nfs_nfs_version(options, &version))
12f9a07
 		return 0;
12f9a07
-	nfs_pmap->pm_vers = version.major;
12f9a07
+	if (version.v_mode == V_DEFAULT)
12f9a07
+		nfs_pmap->pm_vers = 0;
12f9a07
+	else
12f9a07
+		nfs_pmap->pm_vers = version.major;
12f9a07
 	if (!nfs_nfs_protocol(options, &nfs_pmap->pm_prot))
12f9a07
 		return 0;
12f9a07
 	if (!nfs_nfs_port(options, &nfs_pmap->pm_port))
12f9a07
diff --git a/utils/mount/parse_dev.c b/utils/mount/parse_dev.c
12f9a07
index d64b83d..0d3bcb9 100644
12f9a07
--- a/utils/mount/parse_dev.c
12f9a07
+++ b/utils/mount/parse_dev.c
12f9a07
@@ -118,7 +118,8 @@ static int nfs_parse_simple_hostname(const char *dev,
12f9a07
 	if (pathname) {
12f9a07
 		*pathname = strndup(colon, path_len);
12f9a07
 		if (*pathname == NULL) {
12f9a07
-			free(*hostname);
12f9a07
+			if (hostname)
12f9a07
+				free(*hostname);
12f9a07
 			return nfs_pdn_nomem_err();
12f9a07
 		}
12f9a07
 	}
12f9a07
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
12f9a07
index c8f5a6d..d60b484 100644
12f9a07
--- a/utils/mount/stropts.c
12f9a07
+++ b/utils/mount/stropts.c
12f9a07
@@ -383,13 +383,26 @@ static int nfs_validate_options(struct nfsmount_info *mi)
12f9a07
 	if (!nfs_nfs_proto_family(mi->options, &family))
12f9a07
 		return 0;
12f9a07
 
12f9a07
-	hint.ai_family = (int)family;
12f9a07
-	error = getaddrinfo(mi->hostname, NULL, &hint, &mi->address);
12f9a07
-	if (error != 0) {
12f9a07
-		nfs_error(_("%s: Failed to resolve server %s: %s"),
12f9a07
-			progname, mi->hostname, gai_strerror(error));
12f9a07
-		mi->address = NULL;
12f9a07
-		return 0;
12f9a07
+	/*
12f9a07
+	 * A remount is not going to be able to change the server's address,
12f9a07
+	 * nor should we try to resolve another address for the server as we
12f9a07
+	 * may end up with a different address.
12f9a07
+	 */
12f9a07
+	if (mi->flags & MS_REMOUNT) {
12f9a07
+		po_remove_all(mi->options, "addr");
12f9a07
+	} else {
12f9a07
+		hint.ai_family = (int)family;
12f9a07
+		error = getaddrinfo(mi->hostname, NULL, &hint, &mi->address);
12f9a07
+		if (error != 0) {
12f9a07
+			nfs_error(_("%s: Failed to resolve server %s: %s"),
12f9a07
+				progname, mi->hostname, gai_strerror(error));
12f9a07
+			mi->address = NULL;
12f9a07
+			return 0;
12f9a07
+		}
12f9a07
+
12f9a07
+		if (!nfs_append_addr_option(mi->address->ai_addr,
12f9a07
+						mi->address->ai_addrlen, mi->options))
12f9a07
+			return 0;
12f9a07
 	}
12f9a07
 
12f9a07
 	if (!nfs_set_version(mi))
12f9a07
@@ -398,10 +411,6 @@ static int nfs_validate_options(struct nfsmount_info *mi)
12f9a07
 	if (!nfs_append_sloppy_option(mi->options))
12f9a07
 		return 0;
12f9a07
 
12f9a07
-	if (!nfs_append_addr_option(mi->address->ai_addr,
12f9a07
-					mi->address->ai_addrlen, mi->options))
12f9a07
-		return 0;
12f9a07
-
12f9a07
 	return 1;
12f9a07
 }
12f9a07
 
12f9a07
@@ -841,6 +850,9 @@ check_result:
12f9a07
 	case EPROTONOSUPPORT:
12f9a07
 		/* A clear indication that the server or our
12f9a07
 		 * client does not support NFS version 4 and minor */
12f9a07
+	case EINVAL:
12f9a07
+		/* A less clear indication that our client
12f9a07
+		 * does not support NFSv4 minor version. */
12f9a07
 		if (mi->version.v_mode == V_GENERAL &&
12f9a07
 			mi->version.minor == 0)
12f9a07
 				return result;
12f9a07
@@ -957,6 +969,15 @@ static int nfsmount_fg(struct nfsmount_info *mi)
12f9a07
 		if (nfs_try_mount(mi))
12f9a07
 			return EX_SUCCESS;
12f9a07
 
12f9a07
+		if (errno == EBUSY)
12f9a07
+			/* The only cause of EBUSY is if exactly the desired
12f9a07
+			 * filesystem is already mounted.  That can arguably
12f9a07
+			 * be seen as success.  "mount -a" tries to optimise
12f9a07
+			 * out this case but sometimes fails.  Help it out
12f9a07
+			 * by pretending everything is rosy
12f9a07
+			 */
12f9a07
+			return EX_SUCCESS;
12f9a07
+
12f9a07
 		if (nfs_is_permanent_error(errno))
12f9a07
 			break;
12f9a07
 
12f9a07
diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
12f9a07
index 330cab5..894a7a5 100644
12f9a07
--- a/utils/mountd/auth.c
12f9a07
+++ b/utils/mountd/auth.c
12f9a07
@@ -85,7 +85,7 @@ auth_reload()
12f9a07
 {
12f9a07
 	struct stat		stb;
12f9a07
 	static ino_t		last_inode;
12f9a07
-	static int		last_fd;
12f9a07
+	static int		last_fd = -1;
12f9a07
 	static unsigned int	counter;
12f9a07
 	int			fd;
12f9a07
 
12f9a07
@@ -93,11 +93,22 @@ auth_reload()
12f9a07
 		xlog(L_FATAL, "couldn't open %s", _PATH_ETAB);
12f9a07
 	} else if (fstat(fd, &stb) < 0) {
12f9a07
 		xlog(L_FATAL, "couldn't stat %s", _PATH_ETAB);
12f9a07
-	} else if (stb.st_ino == last_inode) {
12f9a07
+		close(fd);
12f9a07
+	} else if (last_fd != -1 && stb.st_ino == last_inode) {
12f9a07
+		/* We opened the etab file before, and its inode
12f9a07
+		 * number hasn't changed since then.
12f9a07
+		 */
12f9a07
 		close(fd);
12f9a07
 		return counter;
12f9a07
 	} else {
12f9a07
-		close(last_fd);
12f9a07
+		/* Need to process entries from the etab file.  Close
12f9a07
+		 * the file descriptor from the previous open (last_fd),
12f9a07
+		 * and keep the current file descriptor open to prevent
12f9a07
+		 * the file system reusing the current inode number
12f9a07
+		 * (last_inode).
12f9a07
+		 */
12f9a07
+		if (last_fd != -1)
12f9a07
+			close(last_fd);
12f9a07
 		last_fd = fd;
12f9a07
 		last_inode = stb.st_ino;
12f9a07
 	}
12f9a07
diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
12f9a07
index 7847446..ec86a22 100644
12f9a07
--- a/utils/mountd/cache.c
12f9a07
+++ b/utils/mountd/cache.c
12f9a07
@@ -31,6 +31,7 @@
12f9a07
 #include "mountd.h"
12f9a07
 #include "fsloc.h"
12f9a07
 #include "pseudoflavors.h"
12f9a07
+#include "xcommon.h"
12f9a07
 
12f9a07
 #ifdef USE_BLKID
12f9a07
 #include "blkid/blkid.h"
12f9a07
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
12f9a07
index 9fe0f40..b584afc 100644
12f9a07
--- a/utils/mountd/mountd.c
12f9a07
+++ b/utils/mountd/mountd.c
12f9a07
@@ -794,9 +794,10 @@ main(int argc, char **argv)
12f9a07
 		}
12f9a07
 
12f9a07
 	/* No more arguments allowed. */
12f9a07
-	if (optind != argc || !version_any())
12f9a07
+	if (optind != argc || !version_any()) {
12f9a07
+		fprintf(stderr, "%s: No protocol versions specified!\n", progname); 
12f9a07
 		usage(progname, 1);
12f9a07
-
12f9a07
+	}
12f9a07
 	if (chdir(state_dir)) {
12f9a07
 		fprintf(stderr, "%s: chdir(%s) failed: %s\n",
12f9a07
 			progname, state_dir, strerror(errno));
12f9a07
@@ -910,7 +911,8 @@ usage(const char *prog, int n)
12f9a07
 "	[-o num|--descriptors num] [-f exports-file|--exports-file=file]\n"
12f9a07
 "	[-p|--port port] [-V version|--nfs-version version]\n"
12f9a07
 "	[-N version|--no-nfs-version version] [-n|--no-tcp]\n"
12f9a07
-"	[-H ha-callout-prog] [-s|--state-directory-path path]\n"
12f9a07
-"	[-g|--manage-gids] [-t num|--num-threads=num] [-u|--no-udp]\n", prog);
12f9a07
+"	[-H ha-callout prog] [-r | --reverse-lookup]\n"
12f9a07
+"	[-s|--state-directory-path path] [-g|--manage-gids]\n"
12f9a07
+"	[-t num|--num-threads=num] [-u|--no-udp]\n", prog);
12f9a07
 	exit(n);
12f9a07
 }
12f9a07
diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
12f9a07
index 7c5bfbe..66e3bba 100644
12f9a07
--- a/utils/mountd/mountd.man
12f9a07
+++ b/utils/mountd/mountd.man
12f9a07
@@ -115,10 +115,7 @@ must be invoked with the option
12f9a07
 .B \-n " or " \-\-no-tcp
12f9a07
 Don't advertise TCP for mount.
12f9a07
 .TP
12f9a07
-.B \-P
12f9a07
-Ignored (compatibility with unfsd??).
12f9a07
-.TP
12f9a07
-.B \-p num " or " \-\-port num
12f9a07
+.B \-p num " or " \-P num " or " \-\-port num
12f9a07
 Specifies the port number used for RPC listener sockets.
12f9a07
 If this option is not specified,
12f9a07
 .B rpc.mountd
12f9a07
diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c
12f9a07
index a2b11d8..dcb430a 100644
12f9a07
--- a/utils/nfsd/nfssvc.c
12f9a07
+++ b/utils/nfsd/nfssvc.c
12f9a07
@@ -168,22 +168,22 @@ nfssvc_setfds(const struct addrinfo *hints, const char *node, const char *port)
12f9a07
 			continue;
12f9a07
 		}
12f9a07
 
12f9a07
-		xlog(D_GENERAL, "Creating %s %s socket.", family, proto);
12f9a07
-
12f9a07
 		/* open socket and prepare to hand it off to kernel */
12f9a07
 		sockfd = socket(addr->ai_family, addr->ai_socktype,
12f9a07
 				addr->ai_protocol);
12f9a07
 		if (sockfd < 0) {
12f9a07
-			if (errno == EAFNOSUPPORT)
12f9a07
-				xlog(L_NOTICE, "address family %s not "
12f9a07
-						"supported by protocol %s",
12f9a07
-						family, proto);
12f9a07
-			else
12f9a07
+			if (errno != EAFNOSUPPORT) {
12f9a07
 				xlog(L_ERROR, "unable to create %s %s socket: "
12f9a07
 				     "errno %d (%m)", family, proto, errno);
12f9a07
-			rc = errno;
12f9a07
-			goto error;
12f9a07
+				rc = errno;
12f9a07
+				goto error;
12f9a07
+			}
12f9a07
+			addr = addr->ai_next;
12f9a07
+			continue;
12f9a07
 		}
12f9a07
+
12f9a07
+		xlog(D_GENERAL, "Created %s %s socket.", family, proto);
12f9a07
+
12f9a07
 #ifdef IPV6_SUPPORTED
12f9a07
 		if (addr->ai_family == AF_INET6 &&
12f9a07
 		    setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) {
12f9a07
@@ -282,7 +282,7 @@ nfssvc_set_rdmaport(const char *port)
12f9a07
 	int fd;
12f9a07
 
12f9a07
 	if (sv)
12f9a07
-		nport = sv->s_port;
12f9a07
+		nport = ntohs(sv->s_port);
12f9a07
 	else {
12f9a07
 		char *ep;
12f9a07
 		nport = strtol(port, &ep, 10);
12f9a07
diff --git a/utils/nfsidmap/nfsidmap.c b/utils/nfsidmap/nfsidmap.c
12f9a07
index 507193b..2abefe9 100644
12f9a07
--- a/utils/nfsidmap/nfsidmap.c
12f9a07
+++ b/utils/nfsidmap/nfsidmap.c
12f9a07
@@ -80,8 +80,9 @@ static int keyring_clear(const char *keyring)
12f9a07
 
12f9a07
 	key = find_key_by_type_and_desc("keyring", keyring, 0);
12f9a07
 	if (key == -1) {
12f9a07
-		xlog_err("'%s' keyring was not found.", keyring);
12f9a07
-		return EXIT_FAILURE;
12f9a07
+		if (verbose)
12f9a07
+			xlog_warn("'%s' keyring was not found.", keyring);
12f9a07
+		return EXIT_SUCCESS;
12f9a07
 	}
12f9a07
 
12f9a07
 	if (keyctl_clear(key) < 0) {
12f9a07
@@ -89,10 +90,9 @@ static int keyring_clear(const char *keyring)
12f9a07
 				(unsigned int)key);
12f9a07
 		return EXIT_FAILURE;
12f9a07
 	}
12f9a07
-	
12f9a07
+
12f9a07
 	if (verbose)
12f9a07
 		xlog_warn("'%s' cleared", keyring);
12f9a07
-
12f9a07
 	return EXIT_SUCCESS;
12f9a07
 }
12f9a07
 
12f9a07
@@ -404,6 +404,11 @@ int main(int argc, char **argv)
12f9a07
 		}
12f9a07
 	}
12f9a07
 
12f9a07
+	if (geteuid() != 0) {
12f9a07
+		xlog_err("Must be run as root.");
12f9a07
+		return EXIT_FAILURE;
12f9a07
+	}
12f9a07
+
12f9a07
 	if ((rc = nfs4_init_name_mapping(PATH_IDMAPDCONF)))  {
12f9a07
 		xlog_errno(rc, "Unable to create name to user id mappings.");
12f9a07
 		return EXIT_FAILURE;
12f9a07
@@ -423,9 +428,9 @@ int main(int argc, char **argv)
12f9a07
 		return keyring_clear(DEFAULT_KEYRING);
12f9a07
 	}
12f9a07
 
12f9a07
-	xlog_stderr(0);
12f9a07
+	xlog_stderr(verbose);
12f9a07
 	if ((argc - optind) != 2) {
12f9a07
-		xlog_err("Bad arg count. Check /etc/request-key.conf");
12f9a07
+		xlog_warn("Bad arg count. Check /etc/request-key.conf");
12f9a07
 		xlog_warn(usage, progname);
12f9a07
 		return EXIT_FAILURE;
12f9a07
 	}
12f9a07
diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c
12f9a07
index 9f481db..8376347 100644
12f9a07
--- a/utils/nfsstat/nfsstat.c
12f9a07
+++ b/utils/nfsstat/nfsstat.c
12f9a07
@@ -31,8 +31,8 @@ enum {
12f9a07
 	SRVPROC3_SZ = 22,
12f9a07
 	CLTPROC3_SZ = 22,
12f9a07
 	SRVPROC4_SZ = 2,
12f9a07
-	CLTPROC4_SZ = 49,
12f9a07
-	SRVPROC4OPS_SZ = 59,
12f9a07
+	CLTPROC4_SZ = 59,
12f9a07
+	SRVPROC4OPS_SZ = 71,
12f9a07
 };
12f9a07
 
12f9a07
 static unsigned int	srvproc2info[SRVPROC2_SZ+2],
12f9a07
@@ -127,19 +127,30 @@ static const char *	nfscltproc4name[CLTPROC4_SZ] = {
12f9a07
 	"remove",    "rename",    "link",    "symlink",     "create",      "pathconf",
12f9a07
 	"statfs",    "readlink",  "readdir", "server_caps", "delegreturn", "getacl",
12f9a07
 	"setacl",    "fs_locations",
12f9a07
-	"rel_lkowner", "secinfo",
12f9a07
+	"rel_lkowner", "secinfo", "fsid_present",
12f9a07
 	/* nfsv4.1 client ops */
12f9a07
 	"exchange_id",
12f9a07
-	"create_ses",
12f9a07
-	"destroy_ses",
12f9a07
+	"create_session",
12f9a07
+	"destroy_session",
12f9a07
 	"sequence",
12f9a07
-	"get_lease_t",
12f9a07
+	"get_lease_time",
12f9a07
 	"reclaim_comp",
12f9a07
 	"layoutget",
12f9a07
 	"getdevinfo",
12f9a07
 	"layoutcommit",
12f9a07
 	"layoutreturn",
12f9a07
-	"getdevlist",
12f9a07
+	"secinfo_no",
12f9a07
+	"test_stateid",
12f9a07
+	"free_stateid",
12f9a07
+	"getdevicelist",
12f9a07
+	"bind_conn_to_ses",
12f9a07
+	"destroy_clientid",
12f9a07
+	/* nfsv4.2 client ops */
12f9a07
+	"seek",
12f9a07
+	"allocate",
12f9a07
+	"deallocate",
12f9a07
+	"layoutstats",
12f9a07
+	"clone",
12f9a07
 };
12f9a07
 
12f9a07
 static const char *     nfssrvproc4opname[SRVPROC4OPS_SZ] = {
12f9a07
@@ -170,6 +181,19 @@ static const char *     nfssrvproc4opname[SRVPROC4OPS_SZ] = {
12f9a07
 	"want_deleg",
12f9a07
 	"destroy_clid",
12f9a07
 	"reclaim_comp",
12f9a07
+	/* nfsv4.2 server ops */
12f9a07
+	"allocate",
12f9a07
+	"copy",
12f9a07
+	"copy_notify",
12f9a07
+	"deallocate",
12f9a07
+	"ioadvise",
12f9a07
+	"layouterror",
12f9a07
+	"layoutstats",
12f9a07
+	"offloadcancel",
12f9a07
+	"offloadstatus",
12f9a07
+	"readplus",
12f9a07
+	"seek",
12f9a07
+	"write_same",
12f9a07
 };
12f9a07
 
12f9a07
 #define LABEL_srvnet		"Server packet stats:\n"
12f9a07
@@ -823,13 +847,13 @@ print_callstats(const char *hdr, const char **names,
12f9a07
 		total += info[i];
12f9a07
 	if (!total)
12f9a07
 		total = 1;
12f9a07
-	for (i = 0; i < nr; i += 6) {
12f9a07
-		for (j = 0; j < 6 && i + j < nr; j++)
12f9a07
-			printf("%-13s", names[i+j]);
12f9a07
+	for (i = 0; i < nr; i += 5) {
12f9a07
+		for (j = 0; j < 5 && i + j < nr; j++)
12f9a07
+			printf("%-17s", names[i+j]);
12f9a07
 		printf("\n");
12f9a07
-		for (j = 0; j < 6 && i + j < nr; j++) {
12f9a07
+		for (j = 0; j < 5 && i + j < nr; j++) {
12f9a07
 			pct = ((unsigned long long) info[i+j]*100)/total;
12f9a07
-			printf("%-8u%3llu%% ", info[i+j], pct);
12f9a07
+			printf("%-8u%3llu%%     ", info[i+j], pct);
12f9a07
 		}
12f9a07
 		printf("\n");
12f9a07
 	}
12f9a07
diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
12f9a07
index c61087c..8cccdb8 100644
12f9a07
--- a/utils/statd/hostname.c
12f9a07
+++ b/utils/statd/hostname.c
12f9a07
@@ -180,9 +180,6 @@ get_nameinfo(const struct sockaddr *sap,
12f9a07
  * Incoming hostnames are looked up to determine the canonical hostname,
12f9a07
  * and incoming presentation addresses are converted to canonical
12f9a07
  * hostnames.
12f9a07
- *
12f9a07
- * We won't monitor peers that don't have a reverse map.  The canonical
12f9a07
- * name gives us a key for our monitor list.
12f9a07
  */
12f9a07
 __attribute__((__malloc__))
12f9a07
 char *
12f9a07
@@ -207,7 +204,7 @@ statd_canonical_name(const char *hostname)
12f9a07
 		result = get_nameinfo(ai->ai_addr, ai->ai_addrlen,
12f9a07
 					buf, (socklen_t)sizeof(buf));
12f9a07
 		freeaddrinfo(ai);
12f9a07
-		if (!result)
12f9a07
+		if (!result || buf[0] == '\0')
12f9a07
 			/* OK to use presentation address,
12f9a07
 			 * if no reverse map exists */
12f9a07
 			return strdup(hostname);
12f9a07
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
12f9a07
index 286a5e2..368bd80 100644
12f9a07
--- a/utils/statd/monitor.c
12f9a07
+++ b/utils/statd/monitor.c
12f9a07
@@ -72,6 +72,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
12f9a07
 		.sin_addr.s_addr	= htonl(INADDR_LOOPBACK),
12f9a07
 	};
12f9a07
 	char *dnsname = NULL;
12f9a07
+	int existing = 0;
12f9a07
 
12f9a07
 	xlog(D_CALL, "Received SM_MON for %s from %s", mon_name, my_name);
12f9a07
 
12f9a07
@@ -148,17 +149,26 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
12f9a07
 		if (statd_matchhostname(NL_MY_NAME(clnt), my_name) &&
12f9a07
 		    NL_MY_PROC(clnt) == id->my_proc &&
12f9a07
 		    NL_MY_PROG(clnt) == id->my_prog &&
12f9a07
-		    NL_MY_VERS(clnt) == id->my_vers &&
12f9a07
-		    memcmp(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE) == 0) {
12f9a07
-			/* Hey!  We already know you guys! */
12f9a07
-			xlog(D_GENERAL,
12f9a07
-				"Duplicate SM_MON request for %s "
12f9a07
-				"from procedure on %s",
12f9a07
-				mon_name, my_name);
12f9a07
+		    NL_MY_VERS(clnt) == id->my_vers) {
12f9a07
+			if (memcmp(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE)) {
12f9a07
+				xlog(D_GENERAL,
12f9a07
+					"Received SM_MON request with new "
12f9a07
+					"cookie for %s from procedure on %s",
12f9a07
+					mon_name, my_name);
12f9a07
+
12f9a07
+				existing = 1; 
12f9a07
+				break;
12f9a07
+			} else {
12f9a07
+				/* Hey!  We already know you guys! */
12f9a07
+				xlog(D_GENERAL,
12f9a07
+					"Duplicate SM_MON request for %s "
12f9a07
+					"from procedure on %s",
12f9a07
+					mon_name, my_name);
12f9a07
 
12f9a07
-			/* But we'll let you pass anyway. */
12f9a07
-			free(dnsname);
12f9a07
-			goto success;
12f9a07
+				/* But we'll let you pass anyway. */
12f9a07
+				free(dnsname);
12f9a07
+				goto success;
12f9a07
+			}
12f9a07
 		}
12f9a07
 		clnt = NL_NEXT(clnt);
12f9a07
 	}
12f9a07
@@ -167,7 +177,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
12f9a07
 	 * We're committed...ignoring errors.  Let's hope that a malloc()
12f9a07
 	 * doesn't fail.  (I should probably fix this assumption.)
12f9a07
 	 */
12f9a07
-	if (!(clnt = nlist_new(my_name, mon_name, 0))) {
12f9a07
+	if (!existing && !(clnt = nlist_new(my_name, mon_name, 0))) {
12f9a07
 		free(dnsname);
12f9a07
 		xlog_warn("out of memory");
12f9a07
 		goto failure;
12f9a07
@@ -180,8 +190,11 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
12f9a07
 	clnt->dns_name = dnsname;
12f9a07
 
12f9a07
 	/*
12f9a07
-	 * Now, Create file on stable storage for host.
12f9a07
+	 * Now, Create file on stable storage for host, first deleting any
12f9a07
+	 * existing records on file.
12f9a07
 	 */
12f9a07
+	nsm_delete_monitored_host(dnsname, mon_name, my_name);
12f9a07
+
12f9a07
 	if (!nsm_insert_monitored_host(dnsname,
12f9a07
 				(struct sockaddr *)(char *)&my_addr, argp)) {
12f9a07
 		nlist_free(NULL, clnt);
12f9a07
@@ -190,7 +203,8 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
12f9a07
 
12f9a07
 	/* PRC: do the HA callout: */
12f9a07
 	ha_callout("add-client", mon_name, my_name, -1);
12f9a07
-	nlist_insert(&rtnl, clnt);
12f9a07
+	if (!existing)
12f9a07
+		nlist_insert(&rtnl, clnt);
12f9a07
 	xlog(D_GENERAL, "MONITORING %s for %s", mon_name, my_name);
12f9a07
  success:
12f9a07
 	result.res_stat = STAT_SUCC;
12f9a07
diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
12f9a07
index 45c84f9..c4f6364 100644
12f9a07
--- a/utils/statd/rmtcall.c
12f9a07
+++ b/utils/statd/rmtcall.c
12f9a07
@@ -113,7 +113,6 @@ statd_get_socket(void)
12f9a07
 	if (sockfd < 0)
12f9a07
 		return -1;
12f9a07
 
12f9a07
-	FD_SET(sockfd, &SVC_FDSET);
12f9a07
 	return sockfd;
12f9a07
 }
12f9a07
 
12f9a07
diff --git a/utils/statd/start-statd b/utils/statd/start-statd
12f9a07
index 14369e5..19e6eb2 100755
12f9a07
--- a/utils/statd/start-statd
12f9a07
+++ b/utils/statd/start-statd
12f9a07
@@ -6,11 +6,19 @@
12f9a07
 # site.
12f9a07
 PATH="/sbin:/usr/sbin:/bin:/usr/bin"
12f9a07
 
12f9a07
+if [ -s /var/run/rpc.statd.pid ] &&
12f9a07
+       [ 1`cat /var/run/rpc.statd.pid` -gt 1 ] &&
12f9a07
+       kill -0 `cat /var/run/rpc.statd.pid` > /dev/null 2>&1
12f9a07
+then
12f9a07
+    # statd already running - must have been slow to respond.
12f9a07
+    exit 0
12f9a07
+fi
12f9a07
 # First try systemd if it's installed.
12f9a07
 if [ -d /run/systemd/system ]; then
12f9a07
     # Quit only if the call worked.
12f9a07
     systemctl start rpc-statd.service && exit
12f9a07
 fi
12f9a07
 
12f9a07
+cd /
12f9a07
 # Fall back to launching it ourselves.
12f9a07
 exec rpc.statd --no-notify
12f9a07
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
12f9a07
index 2b7a167..e5b4c98 100644
12f9a07
--- a/utils/statd/statd.c
12f9a07
+++ b/utils/statd/statd.c
12f9a07
@@ -247,6 +247,7 @@ int main (int argc, char **argv)
12f9a07
 	int port = 0, out_port = 0;
12f9a07
 	int nlm_udp = 0, nlm_tcp = 0;
12f9a07
 	struct rlimit rlim;
12f9a07
+	int notify_sockfd;
12f9a07
 
12f9a07
 	/* Default: daemon mode, no other options */
12f9a07
 	run_mode = 0;
12f9a07
@@ -437,7 +438,7 @@ int main (int argc, char **argv)
12f9a07
 		}
12f9a07
 
12f9a07
 	/* Make sure we have a privilege port for calling into the kernel */
12f9a07
-	if (statd_get_socket() < 0)
12f9a07
+	if ((notify_sockfd = statd_get_socket()) < 0)
12f9a07
 		exit(1);
12f9a07
 
12f9a07
 	/* If sm-notify didn't take all the state files, load
12f9a07
@@ -484,7 +485,7 @@ int main (int argc, char **argv)
12f9a07
 		 * Handle incoming requests:  SM_NOTIFY socket requests, as
12f9a07
 		 * well as callbacks from lockd.
12f9a07
 		 */
12f9a07
-		my_svc_run();	/* I rolled my own, Olaf made it better... */
12f9a07
+		my_svc_run(notify_sockfd);	/* I rolled my own, Olaf made it better... */
12f9a07
 
12f9a07
 		/* Only get here when simulating a crash so we should probably
12f9a07
 		 * start sm-notify running again.  As we have already dropped
12f9a07
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
12f9a07
index a1d8035..231ac7e 100644
12f9a07
--- a/utils/statd/statd.h
12f9a07
+++ b/utils/statd/statd.h
12f9a07
@@ -28,7 +28,7 @@ extern _Bool	statd_present_address(const struct sockaddr *sap, char *buf,
12f9a07
 __attribute__((__malloc__))
12f9a07
 extern char *	statd_canonical_name(const char *hostname);
12f9a07
 
12f9a07
-extern void	my_svc_run(void);
12f9a07
+extern void	my_svc_run(int);
12f9a07
 extern void	notify_hosts(void);
12f9a07
 extern void	shuffle_dirs(void);
12f9a07
 extern int	statd_get_socket(void);
12f9a07
diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c
12f9a07
index d98ecee..28c1ad6 100644
12f9a07
--- a/utils/statd/svc_run.c
12f9a07
+++ b/utils/statd/svc_run.c
12f9a07
@@ -78,7 +78,7 @@ my_svc_exit(void)
12f9a07
  * The heart of the server.  A crib from libc for the most part...
12f9a07
  */
12f9a07
 void
12f9a07
-my_svc_run(void)
12f9a07
+my_svc_run(int sockfd)
12f9a07
 {
12f9a07
 	FD_SET_TYPE	readfds;
12f9a07
 	int             selret;
12f9a07
@@ -96,6 +96,8 @@ my_svc_run(void)
12f9a07
 		}
12f9a07
 
12f9a07
 		readfds = SVC_FDSET;
12f9a07
+		/* Set notify sockfd for waiting for reply */
12f9a07
+		FD_SET(sockfd, &readfds);
12f9a07
 		if (notify) {
12f9a07
 			struct timeval	tv;
12f9a07
 
12f9a07
@@ -125,8 +127,10 @@ my_svc_run(void)
12f9a07
 
12f9a07
 		default:
12f9a07
 			selret -= process_reply(&readfds);
12f9a07
-			if (selret)
12f9a07
+			if (selret) {
12f9a07
+				FD_CLR(sockfd, &readfds);
12f9a07
 				svc_getreqset(&readfds);
12f9a07
+			}
12f9a07
 		}
12f9a07
 	}
12f9a07
 }