Blob Blame History Raw
diff --git a/aclocal/libevent.m4 b/aclocal/libevent.m4
index b5ac00f..e0b820b 100644
--- a/aclocal/libevent.m4
+++ b/aclocal/libevent.m4
@@ -1,12 +1,12 @@
 dnl Checks for libevent
 AC_DEFUN([AC_LIBEVENT], [
 
-  dnl Check for libevent, but do not add -levent to LIBS
-  AC_CHECK_LIB([event], [event_dispatch], [LIBEVENT=-levent],
+  dnl Check for libevent, but do not add -levent_core to LIBS
+  AC_CHECK_LIB([event_core], [event_base_dispatch], [LIBEVENT=-levent_core],
                [AC_MSG_ERROR([libevent not found.])])
   AC_SUBST(LIBEVENT)
 
-  AC_CHECK_HEADERS([event.h], ,
+  AC_CHECK_HEADERS([event2/event.h], ,
                    [AC_MSG_ERROR([libevent headers not found.])])
 
 ])dnl
diff --git a/configure.ac b/configure.ac
index 942f3c0..dbb795f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -638,13 +638,12 @@ my_am_cflags="\
  -Werror=parentheses \
  -Werror=aggregate-return \
  -Werror=unused-result \
- -Wno-cast-function-type \
  -fno-strict-aliasing \
 "
 
 AC_DEFUN([CHECK_CCSUPPORT], [
   my_save_cflags="$CFLAGS"
-  CFLAGS=$1
+  CFLAGS="-Werror $1"
   AC_MSG_CHECKING([whether CC supports $1])
   AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
     [AC_MSG_RESULT([yes])]
@@ -658,9 +657,10 @@ CHECK_CCSUPPORT([-Werror=format-overflow=2], [flg1])
 CHECK_CCSUPPORT([-Werror=int-conversion], [flg2])
 CHECK_CCSUPPORT([-Werror=incompatible-pointer-types], [flg3])
 CHECK_CCSUPPORT([-Werror=misleading-indentation], [flg4])
+CHECK_CCSUPPORT([-Wno-cast-function-type], [flg5])
 AX_GCC_FUNC_ATTRIBUTE([format])
 
-AC_SUBST([AM_CFLAGS], ["$my_am_cflags $flg1 $flg2 $flg3 $flg4"])
+AC_SUBST([AM_CFLAGS], ["$my_am_cflags $flg1 $flg2 $flg3 $flg4 $flg5"])
 
 # Make sure that $ACLOCAL_FLAGS are used during a rebuild
 AC_SUBST([ACLOCAL_AMFLAGS], ["-I $ac_macro_dir \$(ACLOCAL_FLAGS)"])
diff --git a/support/include/xcommon.h b/support/include/xcommon.h
index 30b0403..efde83c 100644
--- a/support/include/xcommon.h
+++ b/support/include/xcommon.h
@@ -7,7 +7,7 @@
  */
 
 #ifndef _XMALLOC_H
-#define _MALLOC_H
+#define _XMALLOC_H
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
diff --git a/support/nfs/exports.c b/support/nfs/exports.c
index 97eb318..037febd 100644
--- a/support/nfs/exports.c
+++ b/support/nfs/exports.c
@@ -838,6 +838,7 @@ struct export_features *get_export_features(void)
 	close(fd);
 	if (c == -1)
 		goto err;
+	buf[c] = 0;
 	c = sscanf(buf, "%x %x", &ef.flags, &ef.secinfo_flags);
 	if (c != 2)
 		goto err;
diff --git a/support/nfs/xlog.c b/support/nfs/xlog.c
index 687d862..86acd6a 100644
--- a/support/nfs/xlog.c
+++ b/support/nfs/xlog.c
@@ -156,13 +156,29 @@ xlog_enabled(int fac)
 void
 xlog_backend(int kind, const char *fmt, va_list args)
 {
-	va_list args2;
-
 	if (!(kind & (L_ALL)) && !(logging && (kind & logmask)))
 		return;
 
-	if (log_stderr)
+	if (log_stderr) {
+		va_list		args2;
+#ifdef VERBOSE_PRINTF
+		time_t		now;
+		struct tm	*tm;
+
+		time(&now);
+		tm = localtime(&now);
+		fprintf(stderr, "%s[%d] %04d-%02d-%02d %02d:%02d:%02d ",
+				log_name, log_pid,
+				tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday,
+				tm->tm_hour, tm->tm_min, tm->tm_sec);
+#else
+		fprintf(stderr, "%s: ", log_name);
+#endif
 		va_copy(args2, args);
+		vfprintf(stderr, fmt, args2);
+		fprintf(stderr, "\n");
+		va_end(args2);
+	}
 
 	if (log_syslog) {
 		switch (kind) {
@@ -185,25 +201,6 @@ xlog_backend(int kind, const char *fmt, va_list args)
 		}
 	}
 
-	if (log_stderr) {
-#ifdef VERBOSE_PRINTF
-		time_t		now;
-		struct tm	*tm;
-
-		time(&now);
-		tm = localtime(&now);
-		fprintf(stderr, "%s[%d] %04d-%02d-%02d %02d:%02d:%02d ",
-				log_name, log_pid,
-				tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday,
-				tm->tm_hour, tm->tm_min, tm->tm_sec);
-#else
-		fprintf(stderr, "%s: ", log_name);
-#endif
-		vfprintf(stderr, fmt, args2);
-		fprintf(stderr, "\n");
-		va_end(args2);
-	}
-
 	if (kind == L_FATAL)
 		exit(1);
 }
diff --git a/support/nfsidmap/libnfsidmap.c b/support/nfsidmap/libnfsidmap.c
index bce448c..0a912e5 100644
--- a/support/nfsidmap/libnfsidmap.c
+++ b/support/nfsidmap/libnfsidmap.c
@@ -237,24 +237,40 @@ static int load_translation_plugin(char *method, struct mapping_plugin *plgn)
 {
 	void *dl = NULL;
 	struct trans_func *trans = NULL;
-	libnfsidmap_plugin_init_t init_func;
+	libnfsidmap_plugin_init_t init_func = NULL;
 	char plgname[128];
 	int ret = 0;
 
-	snprintf(plgname, sizeof(plgname), "%s/%s.so", PATH_PLUGINS, method);
+	/* Look for library using search path first to allow overriding */
+	snprintf(plgname, sizeof(plgname), "%s.so", method);
 
 	dl = dlopen(plgname, RTLD_NOW | RTLD_LOCAL);
-	if (dl == NULL) {
-		IDMAP_LOG(1, ("libnfsidmap: Unable to load plugin: %s",
-			  dlerror()));
-		return -1;
+	if (dl != NULL) {
+		/* Is it really one of our libraries */
+		init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC);
+		if (init_func == NULL) {
+			dlclose(dl);
+			dl = NULL;
+		}
 	}
-	init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC);
-	if (init_func == NULL) {
-		IDMAP_LOG(1, ("libnfsidmap: Unable to get init function: %s",
-			  dlerror()));
-		dlclose(dl);
-		return -1;
+
+	if (dl == NULL) {
+		/* Fallback to hard-coded path */
+		snprintf(plgname, sizeof(plgname), "%s/%s.so", PATH_PLUGINS, method);
+
+		dl = dlopen(plgname, RTLD_NOW | RTLD_LOCAL);
+		if (dl == NULL) {
+			IDMAP_LOG(1, ("libnfsidmap: Unable to load plugin: %s: %s",
+				  plgname, dlerror()));
+			return -1;
+		}
+		init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC);
+		if (init_func == NULL) {
+			IDMAP_LOG(1, ("libnfsidmap: Unable to get init function: %s: %s",
+				  plgname, dlerror()));
+			dlclose(dl);
+			return -1;
+		}
 	}
 	trans = init_func();
 	if (trans == NULL) {
@@ -496,6 +512,19 @@ out:
 	return ret ? -ENOENT: 0;
 }
 
+void nfs4_term_name_mapping(void)
+{
+	if (nfs4_plugins)
+		unload_plugins(nfs4_plugins);
+	if (gss_plugins)
+		unload_plugins(gss_plugins);
+
+	nfs4_plugins = gss_plugins = NULL;
+
+	free_local_realms();
+	conf_cleanup();
+}
+
 int
 nfs4_get_default_domain(char *UNUSED(server), char *domain, size_t len)
 {
diff --git a/support/nfsidmap/nfsidmap.h b/support/nfsidmap/nfsidmap.h
index 1063065..5a79568 100644
--- a/support/nfsidmap/nfsidmap.h
+++ b/support/nfsidmap/nfsidmap.h
@@ -50,6 +50,7 @@ typedef struct _extra_mapping_params {
 typedef void (*nfs4_idmap_log_function_t)(const char *, ...);
 
 int nfs4_init_name_mapping(char *conffile);
+void nfs4_term_name_mapping(void);
 int nfs4_get_default_domain(char *server, char *domain, size_t len);
 int nfs4_uid_to_name(uid_t uid, char *domain, char *name, size_t len);
 int nfs4_gid_to_name(gid_t gid, char *domain, char *name, size_t len);
diff --git a/support/nfsidmap/nfsidmap_common.c b/support/nfsidmap/nfsidmap_common.c
index f89b82e..4d2cb14 100644
--- a/support/nfsidmap/nfsidmap_common.c
+++ b/support/nfsidmap/nfsidmap_common.c
@@ -34,12 +34,21 @@ static char * toupper_str(char *s)
         return s;
 }
 
+static struct conf_list *local_realms = NULL;
+
+void free_local_realms(void)
+{
+	if (local_realms) {
+		conf_free_list(local_realms);
+		local_realms = NULL;
+	}
+}
+
 /* Get list of "local equivalent" realms.  Meaning the list of realms
  * where john@REALM.A is considered the same user as john@REALM.B
  * If not specified, default to upper-case of local domain name */
 struct conf_list *get_local_realms(void)
 {
-	static struct conf_list *local_realms = NULL;
 	if (local_realms) return local_realms;
 
 	local_realms = conf_get_list("General", "Local-Realms");
diff --git a/support/nfsidmap/nfsidmap_private.h b/support/nfsidmap/nfsidmap_private.h
index f1af55f..a5cb6dd 100644
--- a/support/nfsidmap/nfsidmap_private.h
+++ b/support/nfsidmap/nfsidmap_private.h
@@ -37,6 +37,7 @@
 #include "conffile.h"
 
 struct conf_list *get_local_realms(void);
+void free_local_realms(void);
 int get_nostrip(void);
 int get_reformat_group(void);
 
diff --git a/support/nfsidmap/nss.c b/support/nfsidmap/nss.c
index 9d46499..669760b 100644
--- a/support/nfsidmap/nss.c
+++ b/support/nfsidmap/nss.c
@@ -467,6 +467,17 @@ static int nss_plugin_init(void)
 	return 0;
 }
 
+/*
+ * Called by dlclose(). See dlopen(3) man page
+ */
+__attribute__((destructor))
+static int nss_plugin_term(void)
+{
+	free_local_realms();
+	conf_cleanup();
+	return 0;
+}
+
 
 struct trans_func nss_trans = {
 	.name		= "nsswitch",
diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service
index 24118d6..06c1adb 100644
--- a/systemd/nfs-server.service
+++ b/systemd/nfs-server.service
@@ -1,18 +1,18 @@
 [Unit]
 Description=NFS server and services
 DefaultDependencies=no
-Requires= network.target proc-fs-nfsd.mount
-Requires= nfs-mountd.service
+Requires=network.target proc-fs-nfsd.mount
+Requires=nfs-mountd.service
 Wants=rpcbind.socket network-online.target
 Wants=rpc-statd.service nfs-idmapd.service
 Wants=rpc-statd-notify.service
 Wants=nfsdcld.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
-After= nfsdcld.service
-Before= rpc-statd-notify.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
+After=nfsdcld.service
+Before=rpc-statd-notify.service
 
 # GSS services dependencies and ordering
 Wants=auth-rpcgss-module.service
diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py
index 014f38a..00adc96 100755
--- a/tools/mountstats/mountstats.py
+++ b/tools/mountstats/mountstats.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 # -*- python-mode -*-
 """Parse /proc/self/mountstats and display it in human readable form
 """
@@ -560,7 +560,10 @@ class DeviceData:
         # the reference to them.  so we build new lists here
         # for the result object.
         for op in result.__rpc_data['ops']:
-            result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op]))
+            try:
+                result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op]))
+            except KeyError:
+                continue
 
         # update the remaining keys
         if protocol == 'udp':
diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
index b7e98a2..4f5e8a6 100755
--- a/tools/nfs-iostat/nfs-iostat.py
+++ b/tools/nfs-iostat/nfs-iostat.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 # -*- python-mode -*-
 """Emulate iostat for NFS mount points using /proc/self/mountstats
 """
@@ -213,8 +213,11 @@ class DeviceData:
         # the reference to them.  so we build new lists here
         # for the result object.
         for op in result.__rpc_data['ops']:
-            result.__rpc_data[op] = list(map(
-                difference, self.__rpc_data[op], old_stats.__rpc_data[op]))
+            try:
+                result.__rpc_data[op] = list(map(
+                    difference, self.__rpc_data[op], old_stats.__rpc_data[op]))
+            except KeyError:
+                continue
 
         # update the remaining keys we care about
         result.__rpc_data['rpcsends'] -= old_stats.__rpc_data['rpcsends']
@@ -380,6 +383,8 @@ class DeviceData:
         sends = float(self.__rpc_data['rpcsends'])
         if sample_time == 0:
             sample_time = float(self.__nfs_data['age'])
+        if sample_time == 0:
+            sample_time = 1;
         return (sends / sample_time)
 
     def display_iostats(self, sample_time, which):
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index a04a789..cde5e51 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -85,8 +85,11 @@ grab_lockfile()
 static void
 release_lockfile()
 {
-	if (_lockfd != -1)
+	if (_lockfd != -1) {
 		lockf(_lockfd, F_ULOCK, 0);
+		close(_lockfd);
+		_lockfd = -1;
+	}
 }
 
 int
@@ -184,6 +187,7 @@ main(int argc, char **argv)
 			xtab_export_read();
 			dump(f_verbose, f_export_format);
 			free_state_path_names(&etab);
+			export_freeall();
 			return 0;
 		}
 	}
@@ -225,6 +229,7 @@ main(int argc, char **argv)
 	xtab_export_write();
 	cache_flush(force_flush);
 	free_state_path_names(&etab);
+	export_freeall();
 
 	return export_errno;
 }
diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am
index 321046b..21d3bb8 100644
--- a/utils/gssd/Makefile.am
+++ b/utils/gssd/Makefile.am
@@ -67,7 +67,6 @@ gssd_CFLAGS = \
 svcgssd_SOURCES = \
 	$(COMMON_SRCS) \
 	svcgssd.c \
-	svcgssd_main_loop.c \
 	svcgssd_mech2file.c \
 	svcgssd_proc.c \
 	svcgssd_krb5.c \
@@ -78,6 +77,7 @@ svcgssd_SOURCES = \
 svcgssd_LDADD = \
 	../../support/nfs/libnfs.la \
 	../../support/nfsidmap/libnfsidmap.la \
+	$(LIBEVENT) \
 	$(RPCSECGSS_LIBS) \
 	$(KRBLIBS) $(GSSAPI_LIBS) $(LIBTIRPC)
 
diff --git a/utils/gssd/gss_names.c b/utils/gssd/gss_names.c
index 2a7f3a1..982b96f 100644
--- a/utils/gssd/gss_names.c
+++ b/utils/gssd/gss_names.c
@@ -110,10 +110,12 @@ get_hostbased_client_name(gss_name_t client_name, gss_OID mech,
 	/* For Kerberos, transform the NT_KRB5_PRINCIPAL name to
 	 * an NT_HOSTBASED_SERVICE name */
 	if (g_OID_equal(&krb5oid, mech)) {
-		if (get_krb5_hostbased_name(&name, &cname) == 0)
-			*hostbased_name = cname;
+		if (get_krb5_hostbased_name(&name, &cname) != 0)
+			goto out_rel_buf;
+		*hostbased_name = cname;
 	} else {
 		printerr(1, "WARNING: unknown/unsupport mech OID\n");
+		goto out_rel_buf;
 	}
 
 	res = 0;
diff --git a/utils/gssd/gss_util.c b/utils/gssd/gss_util.c
index 2e6d40f..a4b2777 100644
--- a/utils/gssd/gss_util.c
+++ b/utils/gssd/gss_util.c
@@ -339,3 +339,9 @@ out:
 	return retval;
 }
 
+void
+gssd_cleanup(void)
+{
+	u_int32_t min_stat;
+	gss_release_cred(&min_stat, &gssd_creds);
+}
diff --git a/utils/gssd/gss_util.h b/utils/gssd/gss_util.h
index aa9f778..4da64e3 100644
--- a/utils/gssd/gss_util.h
+++ b/utils/gssd/gss_util.h
@@ -41,6 +41,7 @@ int gssd_acquire_cred(char *server_name, const gss_OID oid);
 void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat,
 	const gss_OID mech);
 int gssd_check_mechs(void);
+void gssd_cleanup(void);
 
 #ifndef HAVE_LIBGSSGLUE
 #include <gssapi/gssapi_krb5.h>
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
index 588da0f..85bc4b0 100644
--- a/utils/gssd/gssd.c
+++ b/utils/gssd/gssd.c
@@ -64,7 +64,7 @@
 #include <fcntl.h>
 #include <dirent.h>
 #include <netdb.h>
-#include <event.h>
+#include <event2/event.h>
 
 #include "gssd.h"
 #include "err_util.h"
@@ -77,7 +77,7 @@ static char *pipefs_path = GSSD_PIPEFS_DIR;
 static DIR *pipefs_dir;
 static int pipefs_fd;
 static int inotify_fd;
-struct event inotify_ev;
+struct event *inotify_ev;
 
 char *keytabfile = GSSD_DEFAULT_KEYTAB_FILE;
 char **ccachesearch;
@@ -90,9 +90,9 @@ char *ccachedir = NULL;
 /* Avoid DNS reverse lookups on server names */
 static bool avoid_dns = true;
 static bool use_gssproxy = false;
-int thread_started = false;
-pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER;
-pthread_cond_t pcond = PTHREAD_COND_INITIALIZER;
+pthread_mutex_t clp_lock = PTHREAD_MUTEX_INITIALIZER;
+static bool signal_received = false;
+static struct event_base *evbase = NULL;
 
 TAILQ_HEAD(topdir_list_head, topdir) topdir_list;
 
@@ -359,20 +359,28 @@ out:
 	free(port);
 }
 
+/* Actually frees clp and fields that might be used from other
+ * threads if was last reference.
+ */
 static void
-gssd_destroy_client(struct clnt_info *clp)
+gssd_free_client(struct clnt_info *clp)
 {
-	if (clp->krb5_fd >= 0) {
+	int refcnt;
+
+	pthread_mutex_lock(&clp_lock);
+	refcnt = --clp->refcount;
+	pthread_mutex_unlock(&clp_lock);
+	if (refcnt > 0)
+		return;
+
+	printerr(3, "freeing client %s\n", clp->relpath);
+
+	if (clp->krb5_fd >= 0)
 		close(clp->krb5_fd);
-		event_del(&clp->krb5_ev);
-	}
 
-	if (clp->gssd_fd >= 0) {
+	if (clp->gssd_fd >= 0)
 		close(clp->gssd_fd);
-		event_del(&clp->gssd_ev);
-	}
 
-	inotify_rm_watch(inotify_fd, clp->wd);
 	free(clp->relpath);
 	free(clp->servicename);
 	free(clp->servername);
@@ -380,6 +388,30 @@ gssd_destroy_client(struct clnt_info *clp)
 	free(clp);
 }
 
+/* Called when removing from clnt_list to tear down event handling.
+ * Will then free clp if was last reference.
+ */
+static void
+gssd_destroy_client(struct clnt_info *clp)
+{
+	printerr(3, "destroying client %s\n", clp->relpath);
+
+	if (clp->krb5_ev) {
+		event_del(clp->krb5_ev);
+		event_free(clp->krb5_ev);
+		clp->krb5_ev = NULL;
+	}
+
+	if (clp->gssd_ev) {
+		event_del(clp->gssd_ev);
+		event_free(clp->gssd_ev);
+		clp->gssd_ev = NULL;
+	}
+
+	inotify_rm_watch(inotify_fd, clp->wd);
+	gssd_free_client(clp);
+}
+
 static void gssd_scan(void);
 
 static int
@@ -416,11 +448,21 @@ static struct clnt_upcall_info *alloc_upcall_info(struct clnt_info *clp)
 	info = malloc(sizeof(struct clnt_upcall_info));
 	if (info == NULL)
 		return NULL;
+
+	pthread_mutex_lock(&clp_lock);
+	clp->refcount++;
+	pthread_mutex_unlock(&clp_lock);
 	info->clp = clp;
 
 	return info;
 }
 
+void free_upcall_info(struct clnt_upcall_info *info)
+{
+	gssd_free_client(info->clp);
+	free(info);
+}
+
 /* For each upcall read the upcall info into the buffer, then create a
  * thread in a detached state so that resources are released back into
  * the system without the need for a join.
@@ -438,13 +480,13 @@ gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data)
 	info->lbuflen = read(clp->gssd_fd, info->lbuf, sizeof(info->lbuf));
 	if (info->lbuflen <= 0 || info->lbuf[info->lbuflen-1] != '\n') {
 		printerr(0, "WARNING: %s: failed reading request\n", __func__);
-		free(info);
+		free_upcall_info(info);
 		return;
 	}
 	info->lbuf[info->lbuflen-1] = 0;
 
 	if (start_upcall_thread(handle_gssd_upcall, info))
-		free(info);
+		free_upcall_info(info);
 }
 
 static void
@@ -461,12 +503,12 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data)
 			sizeof(info->uid)) < (ssize_t)sizeof(info->uid)) {
 		printerr(0, "WARNING: %s: failed reading uid from krb5 "
 			 "upcall pipe: %s\n", __func__, strerror(errno));
-		free(info);
+		free_upcall_info(info);
 		return;
 	}
 
 	if (start_upcall_thread(handle_krb5_upcall, info))
-		free(info);
+		free_upcall_info(info);
 }
 
 static struct clnt_info *
@@ -478,6 +520,8 @@ gssd_get_clnt(struct topdir *tdi, const char *name)
 		if (!strcmp(clp->name, name))
 			return clp;
 
+	printerr(3, "creating client %s/%s\n", tdi->name, name);
+
 	clp = calloc(1, sizeof(struct clnt_info));
 	if (!clp) {
 		printerr(0, "ERROR: can't malloc clnt_info: %s\n",
@@ -501,6 +545,7 @@ gssd_get_clnt(struct topdir *tdi, const char *name)
 	clp->name = clp->relpath + strlen(tdi->name) + 1;
 	clp->krb5_fd = -1;
 	clp->gssd_fd = -1;
+	clp->refcount = 1;
 
 	TAILQ_INSERT_HEAD(&tdi->clnt_list, clp, list);
 	return clp;
@@ -515,11 +560,8 @@ static int
 gssd_scan_clnt(struct clnt_info *clp)
 {
 	int clntfd;
-	bool gssd_was_closed;
-	bool krb5_was_closed;
 
-	gssd_was_closed = clp->gssd_fd < 0 ? true : false;
-	krb5_was_closed = clp->krb5_fd < 0 ? true : false;
+	printerr(3, "scanning client %s\n", clp->relpath);
 
 	clntfd = openat(pipefs_fd, clp->relpath, O_RDONLY);
 	if (clntfd < 0) {
@@ -535,16 +577,30 @@ gssd_scan_clnt(struct clnt_info *clp)
 	if (clp->gssd_fd == -1 && clp->krb5_fd == -1)
 		clp->krb5_fd = openat(clntfd, "krb5", O_RDWR | O_NONBLOCK);
 
-	if (gssd_was_closed && clp->gssd_fd >= 0) {
-		event_set(&clp->gssd_ev, clp->gssd_fd, EV_READ | EV_PERSIST,
-			  gssd_clnt_gssd_cb, clp);
-		event_add(&clp->gssd_ev, NULL);
+	if (!clp->gssd_ev && clp->gssd_fd >= 0) {
+		clp->gssd_ev = event_new(evbase, clp->gssd_fd, EV_READ | EV_PERSIST,
+					 gssd_clnt_gssd_cb, clp);
+		if (!clp->gssd_ev) {
+			printerr(0, "ERROR: %s: can't create gssd event for %s: %s\n",
+				 __FUNCTION__, clp->relpath, strerror(errno));
+			close(clp->gssd_fd);
+			clp->gssd_fd = -1;
+		} else {
+			event_add(clp->gssd_ev, NULL);
+		}
 	}
 
-	if (krb5_was_closed && clp->krb5_fd >= 0) {
-		event_set(&clp->krb5_ev, clp->krb5_fd, EV_READ | EV_PERSIST,
-			  gssd_clnt_krb5_cb, clp);
-		event_add(&clp->krb5_ev, NULL);
+	if (!clp->krb5_ev && clp->krb5_fd >= 0) {
+		clp->krb5_ev = event_new(evbase, clp->krb5_fd, EV_READ | EV_PERSIST,
+					 gssd_clnt_krb5_cb, clp);
+		if (!clp->krb5_ev) {
+			printerr(0, "ERROR: %s: can't create krb5 event for %s: %s\n",
+				 __FUNCTION__, clp->relpath, strerror(errno));
+			close(clp->krb5_fd);
+			clp->krb5_fd = -1;
+		} else {
+			event_add(clp->krb5_ev, NULL);
+		}
 	}
 
 	if (clp->krb5_fd == -1 && clp->gssd_fd == -1)
@@ -651,7 +707,7 @@ gssd_scan_topdir(const char *name)
 		if (clp->scanned)
 			continue;
 
-		printerr(3, "destroying client %s\n", clp->relpath);
+		printerr(3, "orphaned client %s\n", clp->relpath);
 		saveprev = clp->list.tqe_prev;
 		TAILQ_REMOVE(&tdi->clnt_list, clp, list);
 		gssd_destroy_client(clp);
@@ -748,12 +804,16 @@ gssd_inotify_clnt(struct topdir *tdi, struct clnt_info *clp, const struct inotif
 	} else if (ev->mask & IN_DELETE) {
 		if (!strcmp(ev->name, "gssd") && clp->gssd_fd >= 0) {
 			close(clp->gssd_fd);
-			event_del(&clp->gssd_ev);
+			event_del(clp->gssd_ev);
+			event_free(clp->gssd_ev);
+			clp->gssd_ev = NULL;
 			clp->gssd_fd = -1;
 
 		} else if (!strcmp(ev->name, "krb5") && clp->krb5_fd >= 0) {
 			close(clp->krb5_fd);
-			event_del(&clp->krb5_ev);
+			event_del(clp->krb5_ev);
+			event_free(clp->krb5_ev);
+			clp->krb5_ev = NULL;
 			clp->krb5_fd = -1;
 		}
 
@@ -826,10 +886,15 @@ found:
 static void
 sig_die(int signal)
 {
-	if (root_uses_machine_creds)
-		gssd_destroy_krb5_machine_creds();
+	if (signal_received) {
+		gssd_destroy_krb5_principals(root_uses_machine_creds);
+		printerr(1, "forced exiting on signal %d\n", signal);
+		exit(0);
+	}
+
+	signal_received = true;
 	printerr(1, "exiting on signal %d\n", signal);
-	exit(0);
+	event_base_loopexit(evbase, NULL);
 }
 
 static void
@@ -886,9 +951,10 @@ main(int argc, char *argv[])
 	int rpc_verbosity = 0;
 	int opt;
 	int i;
+	int rc;
 	extern char *optarg;
 	char *progname;
-	struct event sighup_ev;
+	struct event *sighup_ev;
 
 	read_gss_conf();
 
@@ -1027,7 +1093,11 @@ main(int argc, char *argv[])
 	if (gssd_check_mechs() != 0)
 		errx(1, "Problem with gssapi library");
 
-	event_init();
+	evbase = event_base_new();
+	if (!evbase) {
+		printerr(0, "ERROR: failed to create event base: %s\n", strerror(errno));
+		exit(EXIT_FAILURE);
+	}
 
 	pipefs_dir = opendir(pipefs_path);
 	if (!pipefs_dir) {
@@ -1049,18 +1119,51 @@ main(int argc, char *argv[])
 
 	signal(SIGINT, sig_die);
 	signal(SIGTERM, sig_die);
-	signal_set(&sighup_ev, SIGHUP, gssd_scan_cb, NULL);
-	signal_add(&sighup_ev, NULL);
-	event_set(&inotify_ev, inotify_fd, EV_READ | EV_PERSIST, gssd_inotify_cb, NULL);
-	event_add(&inotify_ev, NULL);
+	sighup_ev = evsignal_new(evbase, SIGHUP, gssd_scan_cb, NULL);
+	if (!sighup_ev) {
+		printerr(0, "ERROR: failed to create SIGHUP event: %s\n", strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+	evsignal_add(sighup_ev, NULL);
+	inotify_ev = event_new(evbase, inotify_fd, EV_READ | EV_PERSIST,
+			       gssd_inotify_cb, NULL);
+	if (!inotify_ev) {
+		printerr(0, "ERROR: failed to create inotify event: %s\n", strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+	event_add(inotify_ev, NULL);
 
 	TAILQ_INIT(&topdir_list);
 	gssd_scan();
 	daemon_ready();
 
-	event_dispatch();
+	rc = event_base_dispatch(evbase);
 
-	printerr(0, "ERROR: event_dispatch() returned!\n");
-	return EXIT_FAILURE;
-}
+	printerr(0, "event_dispatch() returned %i!\n", rc);
+
+	gssd_destroy_krb5_principals(root_uses_machine_creds);
+
+	while (!TAILQ_EMPTY(&topdir_list)) {
+		struct topdir *tdi = TAILQ_FIRST(&topdir_list);
+		TAILQ_REMOVE(&topdir_list, tdi, list);
+		while (!TAILQ_EMPTY(&tdi->clnt_list)) {
+			struct clnt_info *clp = TAILQ_FIRST(&tdi->clnt_list);
+			TAILQ_REMOVE(&tdi->clnt_list, clp, list);
+			gssd_destroy_client(clp);
+		}
+		free(tdi);
+	}
+
+	event_free(inotify_ev);
+	event_free(sighup_ev);
+	event_base_free(evbase);
 
+	close(inotify_fd);
+	close(pipefs_fd);
+	closedir(pipefs_dir);
+
+	free(preferred_realm);
+	free(ccachesearch);
+
+	return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
index f4f5975..1e8c58d 100644
--- a/utils/gssd/gssd.h
+++ b/utils/gssd/gssd.h
@@ -62,13 +62,10 @@ extern int			root_uses_machine_creds;
 extern unsigned int 		context_timeout;
 extern unsigned int rpc_timeout;
 extern char			*preferred_realm;
-extern pthread_mutex_t ple_lock;
-extern pthread_cond_t pcond;
-extern pthread_mutex_t pmutex;
-extern int thread_started;
 
 struct clnt_info {
 	TAILQ_ENTRY(clnt_info)	list;
+	int			refcount;
 	int			wd;
 	bool			scanned;
 	char			*name;
@@ -79,9 +76,9 @@ struct clnt_info {
 	int			vers;
 	char			*protocol;
 	int			krb5_fd;
-	struct event		krb5_ev;
+	struct event		*krb5_ev;
 	int			gssd_fd;
-	struct event		gssd_ev;
+	struct event		*gssd_ev;
 	struct			sockaddr_storage addr;
 };
 
@@ -94,6 +91,7 @@ struct clnt_upcall_info {
 
 void handle_krb5_upcall(struct clnt_upcall_info *clp);
 void handle_gssd_upcall(struct clnt_upcall_info *clp);
+void free_upcall_info(struct clnt_upcall_info *info);
 
 
 #endif /* _RPC_GSSD_H_ */
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
index 8fe6605..e830f49 100644
--- a/utils/gssd/gssd_proc.c
+++ b/utils/gssd/gssd_proc.c
@@ -149,9 +149,10 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
 	char    *buf = NULL, *p = NULL, *end = NULL;
 	unsigned int timeout = context_timeout;
 	unsigned int buf_size = 0;
+	pthread_t tid = pthread_self();
 
-	printerr(2, "doing downcall: lifetime_rec=%u acceptor=%.*s\n",
-		lifetime_rec, acceptor->length, acceptor->value);
+	printerr(2, "do_downcall(0x%x): lifetime_rec=%u acceptor=%.*s\n",
+		tid, lifetime_rec, acceptor->length, acceptor->value);
 	buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
 		sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
 		sizeof(context_token->length) + context_token->length +
@@ -177,7 +178,7 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
 	return;
 out_err:
 	free(buf);
-	printerr(1, "Failed to write downcall!\n");
+	printerr(1, "do_downcall(0x%x): Failed to write downcall!\n", tid);
 	return;
 }
 
@@ -231,7 +232,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen,
 	switch (sa->sa_family) {
 	case AF_INET:
 		if (s4->sin_port != 0) {
-			printerr(2, "DEBUG: port already set to %d\n",
+			printerr(4, "DEBUG: port already set to %d\n",
 				 ntohs(s4->sin_port));
 			return 1;
 		}
@@ -239,7 +240,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen,
 #ifdef IPV6_SUPPORTED
 	case AF_INET6:
 		if (s6->sin6_port != 0) {
-			printerr(2, "DEBUG: port already set to %d\n",
+			printerr(4, "DEBUG: port already set to %d\n",
 				 ntohs(s6->sin6_port));
 			return 1;
 		}
@@ -548,7 +549,7 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid,
 		uid, tgtname);
 
 	do {
-		gssd_refresh_krb5_machine_credential(clp->servername, NULL,
+		gssd_refresh_krb5_machine_credential(clp->servername,
 						     service, srchost);
 	/*
 	 * Get a list of credential cache names and try each
@@ -730,7 +731,7 @@ handle_krb5_upcall(struct clnt_upcall_info *info)
 	printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath);
 
 	process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL, NULL);
-	free(info);
+	free_upcall_info(info);
 }
 
 void
@@ -747,8 +748,10 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
 	char			*enctypes = NULL;
 	char			*upcall_str;
 	char			*pbuf = info->lbuf;
+	pthread_t tid = pthread_self();
 
-	printerr(2, "\n%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath);
+	printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, 
+		info->lbuf, clp->relpath);
 
 	upcall_str = strdup(info->lbuf);
 	if (upcall_str == NULL) {
@@ -830,6 +833,6 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
 out:
 	free(upcall_str);
 out_nomem:
-	free(info);
+	free_upcall_info(info);
 	return;
 }
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index 8c73748..9bd74b0 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -130,9 +130,28 @@
 #include "gss_util.h"
 #include "krb5_util.h"
 
+/*
+ * List of principals from our keytab that we
+ * will try to use to obtain credentials
+ * (known as a principal list entry (ple))
+ */
+struct gssd_k5_kt_princ {
+	struct gssd_k5_kt_princ *next;
+	// Only protect against deletion, not modification
+	int refcount;
+	// Only set during creation in new_ple()
+	krb5_principal princ;
+	char *realm;
+	// Modified during usage by gssd_get_single_krb5_cred()
+	char *ccname;
+	krb5_timestamp endtime;
+};
+
+
 /* Global list of principals/cache file names for machine credentials */
-struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
-pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER;
+static struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
+/* This mutex protects list modification & ple->ccname */
+static pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER;
 
 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
 int limit_to_legacy_enctypes = 0;
@@ -146,10 +165,22 @@ static int select_krb5_ccache(const struct dirent *d);
 static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
 		const char **cctype, struct dirent **d);
 static int gssd_get_single_krb5_cred(krb5_context context,
-		krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache);
+		krb5_keytab kt, struct gssd_k5_kt_princ *ple);
 static int query_krb5_ccache(const char* cred_cache, char **ret_princname,
 		char **ret_realm);
 
+static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple)
+{
+	if (--ple->refcount)
+		return;
+
+	printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n", ple->ccname, ple->realm);
+	krb5_free_principal(context, ple->princ);
+	free(ple->ccname);
+	free(ple->realm);
+	free(ple);
+}
+
 /*
  * Called from the scandir function to weed out potential krb5
  * credentials cache files
@@ -192,7 +223,8 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
 	int found = 0;
 	struct dirent *best_match_dir = NULL;
 	struct stat best_match_stat, tmp_stat;
-	char buf[PATH_MAX+4+2+256];
+	/* dirname + cctype + d_name + NULL */
+	char buf[PATH_MAX+5+256+1];
 	char *princname = NULL;
 	char *realm = NULL;
 	int score, best_match_score = 0, err = -EACCES;
@@ -349,8 +381,7 @@ gssd_check_if_cc_exists(struct gssd_k5_kt_princ *ple)
 static int
 gssd_get_single_krb5_cred(krb5_context context,
 			  krb5_keytab kt,
-			  struct gssd_k5_kt_princ *ple,
-			  int nocache)
+			  struct gssd_k5_kt_princ *ple)
 {
 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
 	krb5_get_init_creds_opt *init_opts = NULL;
@@ -367,22 +398,26 @@ gssd_get_single_krb5_cred(krb5_context context,
 	char *cache_type;
 	char *pname = NULL;
 	char *k5err = NULL;
+	int nocache = 0;
 
 	memset(&my_creds, 0, sizeof(my_creds));
 
-	if (!nocache && !use_memcache)
+	if (!use_memcache)
 		nocache = gssd_check_if_cc_exists(ple);
 	/*
 	 * Workaround for clock skew among NFS server, NFS client and KDC
 	 * 300 because clock skew must be within 300sec for kerberos
 	 */
 	now += 300;
+	pthread_mutex_lock(&ple_lock);
 	if (ple->ccname && ple->endtime > now && !nocache) {
 		printerr(3, "INFO: Credentials in CC '%s' are good until %d\n",
 			 ple->ccname, ple->endtime);
 		code = 0;
+		pthread_mutex_unlock(&ple_lock);
 		goto out;
 	}
+	pthread_mutex_unlock(&ple_lock);
 
 	if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
 		printerr(0, "ERROR: Unable to get keytab name in "
@@ -435,6 +470,7 @@ gssd_get_single_krb5_cred(krb5_context context,
 	 * Initialize cache file which we're going to be using
 	 */
 
+	pthread_mutex_lock(&ple_lock);
 	if (use_memcache)
 	    cache_type = "MEMORY";
 	else
@@ -444,15 +480,18 @@ gssd_get_single_krb5_cred(krb5_context context,
 		ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
 		GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
 	ple->endtime = my_creds.times.endtime;
-	if (ple->ccname != NULL)
+	if (ple->ccname == NULL || strcmp(ple->ccname, cc_name) != 0) {
 		free(ple->ccname);
-	ple->ccname = strdup(cc_name);
-	if (ple->ccname == NULL) {
-		printerr(0, "ERROR: no storage to duplicate credentials "
-			    "cache name '%s'\n", cc_name);
-		code = ENOMEM;
-		goto out;
+		ple->ccname = strdup(cc_name);
+		if (ple->ccname == NULL) {
+			printerr(0, "ERROR: no storage to duplicate credentials "
+				    "cache name '%s'\n", cc_name);
+			code = ENOMEM;
+			pthread_mutex_unlock(&ple_lock);
+			goto out;
+		}
 	}
+	pthread_mutex_unlock(&ple_lock);
 	if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
 		k5err = gssd_k5_err_msg(context, code);
 		printerr(0, "ERROR: %s while opening credential cache '%s'\n",
@@ -484,12 +523,13 @@ gssd_get_single_krb5_cred(krb5_context context,
 	if (ccache)
 		krb5_cc_close(context, ccache);
 	krb5_free_cred_contents(context, &my_creds);
-	krb5_free_string(context, k5err);
+	free(k5err);
 	return (code);
 }
 
 /*
  * Given a principal, find a matching ple structure
+ * Called with mutex held
  */
 static struct gssd_k5_kt_princ *
 find_ple_by_princ(krb5_context context, krb5_principal princ)
@@ -506,6 +546,7 @@ find_ple_by_princ(krb5_context context, krb5_principal princ)
 
 /*
  * Create, initialize, and add a new ple structure to the global list
+ * Called with mutex held
  */
 static struct gssd_k5_kt_princ *
 new_ple(krb5_context context, krb5_principal princ)
@@ -557,6 +598,7 @@ new_ple(krb5_context context, krb5_principal princ)
 			p->next = ple;
 	}
 
+	ple->refcount = 1;
 	return ple;
 outerr:
 	if (ple) {
@@ -575,13 +617,14 @@ get_ple_by_princ(krb5_context context, krb5_principal princ)
 {
 	struct gssd_k5_kt_princ *ple;
 
-	/* Need to serialize list if we ever become multi-threaded! */
-
 	pthread_mutex_lock(&ple_lock);
 	ple = find_ple_by_princ(context, princ);
 	if (ple == NULL) {
 		ple = new_ple(context, princ);
 	}
+	if (ple != NULL) {
+		ple->refcount++;
+	}
 	pthread_mutex_unlock(&ple_lock);
 
 	return ple;
@@ -715,6 +758,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
 		goto out;
 	}
 
+	printerr(4, "Scanning keytab for %s/*@%s\n", service, realm);
 	while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) {
 		if ((code = krb5_unparse_name(context, kte->principal,
 					      &pname))) {
@@ -723,7 +767,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
 				 "we failed to unparse principal name: %s\n",
 				 k5err);
 			k5_free_kt_entry(context, kte);
-			krb5_free_string(context, k5err);
+			free(k5err);
 			k5err = NULL;
 			continue;
 		}
@@ -746,6 +790,8 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
 				retval = ENOMEM;
 				k5_free_kt_entry(context, kte);
 			} else {
+				release_ple(context, ple);
+				ple = NULL;
 				retval = 0;
 				*found = 1;
 			}
@@ -770,7 +816,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
 	if (retval < 0)
 		retval = 0;
   out:
-	krb5_free_string(context, k5err);
+	free(k5err);
 	return retval;
 }
 
@@ -811,41 +857,42 @@ find_keytab_entry(krb5_context context, krb5_keytab kt,
 	/* Get full local hostname */
 	if (srchost) {
 		strcpy(myhostname, srchost);
-	} else if (gethostname(myhostname, sizeof(myhostname)) == -1) {
-		retval = errno;
-		k5err = gssd_k5_err_msg(context, retval);
-		printerr(1, "%s while getting local hostname\n", k5err);
-		goto out;
+	        strcpy(myhostad, myhostname);
+	} else {
+		/* Borrow myhostad for gethostname(), we need it later anyways */
+		if (gethostname(myhostad, sizeof(myhostad)-1) == -1) {
+			retval = errno;
+			k5err = gssd_k5_err_msg(context, retval);
+			printerr(1, "%s while getting local hostname\n", k5err);
+			goto out;
+		}
+		retval = get_full_hostname(myhostad, myhostname, sizeof(myhostname));
+		if (retval) {
+			/* Don't use myhostname */
+			myhostname[0] = 0;
+		}
 	}
 
 	/* Compute the active directory machine name HOST$ */
-	krb5_appdefault_string(context, "nfs", NULL, "ad_principal_name", 
+	krb5_appdefault_string(context, "nfs", NULL, "ad_principal_name",
 		notsetstr, &adhostoverride);
-	if (strcmp(adhostoverride, notsetstr) != 0) {
-	        printerr (1, 
-				"AD host string overridden with \"%s\" from appdefaults\n", 
-				adhostoverride);
-	        /* No overflow: Windows cannot handle strings longer than 19 chars */
-	        strcpy(myhostad, adhostoverride);
+	if (adhostoverride && strcmp(adhostoverride, notsetstr) != 0) {
+		printerr(1,
+			 "AD host string overridden with \"%s\" from appdefaults\n",
+			 adhostoverride);
+		/* No overflow: Windows cannot handle strings longer than 19 chars */
+		strcpy(myhostad, adhostoverride);
 	} else {
-	        strcpy(myhostad, myhostname);
-	        for (i = 0; myhostad[i] != 0; ++i) {
-	          if (myhostad[i] == '.') break;
-	        }
-	        myhostad[i] = '$';
-	        myhostad[i+1] = 0;
+		/* In this case, it's been pre-filled above */
+		for (i = 0; myhostad[i] != 0; ++i) {
+			if (myhostad[i] == '.') break;
+		}
+		myhostad[i] = '$';
+		myhostad[i+1] = 0;
 	}
 	if (adhostoverride)
 		krb5_free_string(context, adhostoverride);
 
-	if (!srchost) {
-		retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
-		if (retval) {
-			/* Don't use myhostname */
-			myhostname[0] = 0;
-		}
-	}
-
 	code = krb5_get_default_realm(context, &default_realm);
 	if (code) {
 		retval = code;
@@ -927,7 +974,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt,
 				k5err = gssd_k5_err_msg(context, code);
 				printerr(1, "%s while building principal for '%s'\n",
 					 k5err, spn);
-				krb5_free_string(context, k5err);
+				free(k5err);
 				k5err = NULL;
 				continue;
 			}
@@ -937,7 +984,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt,
 				k5err = gssd_k5_err_msg(context, code);
 				printerr(3, "%s while getting keytab entry for '%s'\n",
 					 k5err, spn);
-				krb5_free_string(context, k5err);
+				free(k5err);
 				k5err = NULL;
 				/*
 				 * We tried the active directory machine account
@@ -986,7 +1033,7 @@ out:
 		k5_free_default_realm(context, default_realm);
 	if (realmnames)
 		krb5_free_host_realm(context, realmnames);
-	krb5_free_string(context, k5err);
+	free(k5err);
 	return retval;
 }
 
@@ -1078,6 +1125,93 @@ err_cache:
 	return (*ret_princname && *ret_realm);
 }
 
+/*
+ * Obtain (or refresh if necessary) Kerberos machine credentials
+ * If a ple is passed in, it's reference will be released
+ */
+static int
+gssd_refresh_krb5_machine_credential_internal(char *hostname,
+				     struct gssd_k5_kt_princ *ple,
+				     char *service, char *srchost)
+{
+	krb5_error_code code = 0;
+	krb5_context context;
+	krb5_keytab kt = NULL;;
+	int retval = 0;
+	char *k5err = NULL;
+	const char *svcnames[] = { "$", "root", "nfs", "host", NULL };
+
+	printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n",
+		__func__, hostname, ple, service, srchost);
+
+	/*
+	 * If a specific service name was specified, use it.
+	 * Otherwise, use the default list.
+	 */
+	if (service != NULL && strcmp(service, "*") != 0) {
+		svcnames[0] = service;
+		svcnames[1] = NULL;
+	}
+	if (hostname == NULL && ple == NULL)
+		return EINVAL;
+
+	code = krb5_init_context(&context);
+	if (code) {
+		k5err = gssd_k5_err_msg(NULL, code);
+		printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
+			 __func__, k5err);
+		retval = code;
+		goto out;
+	}
+
+	if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+		k5err = gssd_k5_err_msg(context, code);
+		printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
+			 __func__, k5err, keytabfile);
+		goto out_free_context;
+	}
+
+	if (ple == NULL) {
+		krb5_keytab_entry kte;
+
+		code = find_keytab_entry(context, kt, srchost, hostname,
+					 &kte, svcnames);
+		if (code) {
+			printerr(0, "ERROR: %s: no usable keytab entry found "
+				 "in keytab %s for connection with host %s\n",
+				 __FUNCTION__, keytabfile, hostname);
+			retval = code;
+			goto out_free_kt;
+		}
+
+		ple = get_ple_by_princ(context, kte.principal);
+		k5_free_kt_entry(context, &kte);
+		if (ple == NULL) {
+			char *pname;
+			if ((krb5_unparse_name(context, kte.principal, &pname))) {
+				pname = NULL;
+			}
+			printerr(0, "ERROR: %s: Could not locate or create "
+				 "ple struct for principal %s for connection "
+				 "with host %s\n",
+				 __FUNCTION__, pname ? pname : "<unparsable>",
+				 hostname);
+			if (pname) k5_free_unparsed_name(context, pname);
+			goto out_free_kt;
+		}
+	}
+	retval = gssd_get_single_krb5_cred(context, kt, ple);
+out_free_kt:
+	krb5_kt_close(context, kt);
+out_free_context:
+	if (ple)
+		release_ple(context, ple);
+	krb5_free_context(context);
+out:
+	free(k5err);
+	return retval;
+}
+
 /*==========================*/
 /*===  External routines ===*/
 /*==========================*/
@@ -1092,7 +1226,8 @@ err_cache:
 int
 gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
 {
-	char			buf[PATH_MAX+2+256], dirname[PATH_MAX];
+				/* dirname + cctype + d_name + NULL */
+	char			buf[PATH_MAX+5+256+1], dirname[PATH_MAX];
 	const char		*cctype;
 	struct dirent		*d;
 	int			err, i, j;
@@ -1171,37 +1306,56 @@ gssd_get_krb5_machine_cred_list(char ***list)
 		goto out;
 	}
 
-	/* Need to serialize list if we ever become multi-threaded! */
-
+	pthread_mutex_lock(&ple_lock);
 	for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
-		if (ple->ccname) {
-			/* Make sure cred is up-to-date before returning it */
-			retval = gssd_refresh_krb5_machine_credential(NULL, ple,
-								      NULL, NULL);
-			if (retval)
-				continue;
-			if (i + 1 > listsize) {
-				listsize += listinc;
-				l = (char **)
-					realloc(l, listsize * sizeof(char *));
-				if (l == NULL) {
-					retval = ENOMEM;
-					goto out;
-				}
-			}
-			if ((l[i++] = strdup(ple->ccname)) == NULL) {
+		if (!ple->ccname)
+			continue;
+
+		/* Take advantage of the fact we only remove the ple
+		 * from the list during shutdown. If it's modified
+		 * concurrently at worst we'll just miss a new entry
+		 * before the current ple
+		 *
+		 * gssd_refresh_krb5_machine_credential_internal() will
+		 * release the ple refcount
+		 */
+		ple->refcount++;
+		pthread_mutex_unlock(&ple_lock);
+		/* Make sure cred is up-to-date before returning it */
+		retval = gssd_refresh_krb5_machine_credential_internal(NULL, ple,
+								       NULL, NULL);
+		pthread_mutex_lock(&ple_lock);
+		if (gssd_k5_kt_princ_list == NULL) {
+			/* Looks like we did shutdown... abort */
+			l[i] = NULL;
+			gssd_free_krb5_machine_cred_list(l);
+			retval = ENOMEM;
+			goto out_lock;
+		}
+		if (retval)
+			continue;
+		if (i + 1 > listsize) {
+			listsize += listinc;
+			l = (char **)
+				realloc(l, listsize * sizeof(char *));
+			if (l == NULL) {
 				retval = ENOMEM;
-				goto out;
+				goto out_lock;
 			}
 		}
+		if ((l[i++] = strdup(ple->ccname)) == NULL) {
+			retval = ENOMEM;
+			goto out_lock;
+		}
 	}
 	if (i > 0) {
 		l[i] = NULL;
 		*list = l;
 		retval = 0;
-		goto out;
 	} else
 		free((void *)l);
+out_lock:
+	pthread_mutex_unlock(&ple_lock);
   out:
 	return retval;
 }
@@ -1226,7 +1380,7 @@ gssd_free_krb5_machine_cred_list(char **list)
  * Called upon exit.  Destroys machine credentials.
  */
 void
-gssd_destroy_krb5_machine_creds(void)
+gssd_destroy_krb5_principals(int destroy_machine_creds)
 {
 	krb5_context context;
 	krb5_error_code code = 0;
@@ -1238,33 +1392,38 @@ gssd_destroy_krb5_machine_creds(void)
 	if (code) {
 		k5err = gssd_k5_err_msg(NULL, code);
 		printerr(0, "ERROR: %s while initializing krb5\n", k5err);
-		goto out;
+		free(k5err);
+		return;
 	}
 
-	for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
-		if (!ple->ccname)
-			continue;
-		if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
-			k5err = gssd_k5_err_msg(context, code);
-			printerr(0, "WARNING: %s while resolving credential "
-				    "cache '%s' for destruction\n", k5err,
-				    ple->ccname);
-			krb5_free_string(context, k5err);
-			k5err = NULL;
-			continue;
-		}
+	pthread_mutex_lock(&ple_lock);
+	while (gssd_k5_kt_princ_list) {
+		ple = gssd_k5_kt_princ_list;
+		gssd_k5_kt_princ_list = ple->next;
 
-		if ((code = krb5_cc_destroy(context, ccache))) {
-			k5err = gssd_k5_err_msg(context, code);
-			printerr(0, "WARNING: %s while destroying credential "
-				    "cache '%s'\n", k5err, ple->ccname);
-			krb5_free_string(context, k5err);
-			k5err = NULL;
+		if (destroy_machine_creds && ple->ccname) {
+			if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
+				k5err = gssd_k5_err_msg(context, code);
+				printerr(0, "WARNING: %s while resolving credential "
+					    "cache '%s' for destruction\n", k5err,
+					    ple->ccname);
+				free(k5err);
+				k5err = NULL;
+			}
+
+			if (!code && (code = krb5_cc_destroy(context, ccache))) {
+				k5err = gssd_k5_err_msg(context, code);
+				printerr(0, "WARNING: %s while destroying credential "
+					    "cache '%s'\n", k5err, ple->ccname);
+				free(k5err);
+				k5err = NULL;
+			}
 		}
+
+		release_ple(context, ple);
 	}
+	pthread_mutex_unlock(&ple_lock);
 	krb5_free_context(context);
-  out:
-	krb5_free_string(context, k5err);
 }
 
 /*
@@ -1272,83 +1431,10 @@ gssd_destroy_krb5_machine_creds(void)
  */
 int
 gssd_refresh_krb5_machine_credential(char *hostname,
-				     struct gssd_k5_kt_princ *ple, 
 				     char *service, char *srchost)
 {
-	krb5_error_code code = 0;
-	krb5_context context;
-	krb5_keytab kt = NULL;;
-	int retval = 0;
-	char *k5err = NULL;
-	const char *svcnames[] = { "$", "root", "nfs", "host", NULL };
-
-	printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n",
-		__func__, hostname, ple, service, srchost);
-
-	/*
-	 * If a specific service name was specified, use it.
-	 * Otherwise, use the default list.
-	 */
-	if (service != NULL && strcmp(service, "*") != 0) {
-		svcnames[0] = service;
-		svcnames[1] = NULL;
-	}
-	if (hostname == NULL && ple == NULL)
-		return EINVAL;
-
-	code = krb5_init_context(&context);
-	if (code) {
-		k5err = gssd_k5_err_msg(NULL, code);
-		printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
-			 __func__, k5err);
-		retval = code;
-		goto out;
-	}
-
-	if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
-		k5err = gssd_k5_err_msg(context, code);
-		printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
-			 __func__, k5err, keytabfile);
-		goto out_free_context;
-	}
-
-	if (ple == NULL) {
-		krb5_keytab_entry kte;
-
-		code = find_keytab_entry(context, kt, srchost, hostname,
-					 &kte, svcnames);
-		if (code) {
-			printerr(0, "ERROR: %s: no usable keytab entry found "
-				 "in keytab %s for connection with host %s\n",
-				 __FUNCTION__, keytabfile, hostname);
-			retval = code;
-			goto out_free_kt;
-		}
-
-		ple = get_ple_by_princ(context, kte.principal);
-		k5_free_kt_entry(context, &kte);
-		if (ple == NULL) {
-			char *pname;
-			if ((krb5_unparse_name(context, kte.principal, &pname))) {
-				pname = NULL;
-			}
-			printerr(0, "ERROR: %s: Could not locate or create "
-				 "ple struct for principal %s for connection "
-				 "with host %s\n",
-				 __FUNCTION__, pname ? pname : "<unparsable>",
-				 hostname);
-			if (pname) k5_free_unparsed_name(context, pname);
-			goto out_free_kt;
-		}
-	}
-	retval = gssd_get_single_krb5_cred(context, kt, ple, 0);
-out_free_kt:
-	krb5_kt_close(context, kt);
-out_free_context:
-	krb5_free_context(context);
-out:
-	krb5_free_string(context, k5err);
-	return retval;
+    return gssd_refresh_krb5_machine_credential_internal(hostname, NULL,
+							 service, srchost);
 }
 
 /*
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
index b000b44..2415205 100644
--- a/utils/gssd/krb5_util.h
+++ b/utils/gssd/krb5_util.h
@@ -9,27 +9,13 @@
 #include "gss_oids.h"
 #endif
 
-/*
- * List of principals from our keytab that we
- * will try to use to obtain credentials
- * (known as a principal list entry (ple))
- */
-struct gssd_k5_kt_princ {
-	struct gssd_k5_kt_princ *next;
-	krb5_principal princ;
-	char *ccname;
-	char *realm;
-	krb5_timestamp endtime;
-};
-
 
 int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername,
 				     char *dirname);
 int  gssd_get_krb5_machine_cred_list(char ***list);
 void gssd_free_krb5_machine_cred_list(char **list);
-void gssd_destroy_krb5_machine_creds(void);
+void gssd_destroy_krb5_principals(int destroy_machine_creds);
 int  gssd_refresh_krb5_machine_credential(char *hostname,
-					  struct gssd_k5_kt_princ *ple, 
 					  char *service, char *srchost);
 char *gssd_k5_err_msg(krb5_context context, krb5_error_code code);
 void gssd_k5_get_default_realm(char **def_realm);
diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c
index ec49b61..3ab2100 100644
--- a/utils/gssd/svcgssd.c
+++ b/utils/gssd/svcgssd.c
@@ -57,20 +57,36 @@
 #include <string.h>
 #include <signal.h>
 #include <nfsidmap.h>
+#include <event2/event.h>
+
 #include "nfslib.h"
 #include "svcgssd.h"
 #include "gss_util.h"
 #include "err_util.h"
 #include "conffile.h"
+#include "misc.h"
+#include "svcgssd_krb5.h"
+
+struct state_paths etab; /* from cacheio.c */
+static bool signal_received = false;
+static struct event_base *evbase = NULL;
+static int nullrpc_fd = -1;
+static struct event *nullrpc_event = NULL;
+static struct event *wait_event = NULL;
 
-struct state_paths etab;
+#define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel"
 
 static void
 sig_die(int signal)
 {
-	/* destroy krb5 machine creds */
+	if (signal_received) {
+		/* destroy krb5 machine creds */
+		printerr(1, "forced exiting on signal %d\n", signal);
+		exit(0);
+	}
+	signal_received = true;
 	printerr(1, "exiting on signal %d\n", signal);
-	exit(0);
+	event_base_loopexit(evbase, NULL);
 }
 
 static void
@@ -89,6 +105,84 @@ usage(char *progname)
 	exit(1);
 }
 
+static void
+svcgssd_nullrpc_cb(int fd, short UNUSED(which), void *UNUSED(data))
+{
+	char	lbuf[RPC_CHAN_BUF_SIZE];
+	int	lbuflen = 0;
+
+	printerr(1, "reading null request\n");
+
+	lbuflen = read(fd, lbuf, sizeof(lbuf));
+	if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') {
+		printerr(0, "WARNING: handle_nullreq: failed reading request\n");
+		return;
+	}
+	lbuf[lbuflen-1] = 0;
+
+	handle_nullreq(lbuf);
+}
+
+static void
+svcgssd_nullrpc_close(void)
+{
+	if (nullrpc_event) {
+		printerr(2, "closing nullrpc channel %s\n", NULLRPC_FILE);
+		event_free(nullrpc_event);
+		nullrpc_event = NULL;
+	}
+	if (nullrpc_fd != -1) {
+		close(nullrpc_fd);
+		nullrpc_fd = -1;
+	}
+}
+
+static void
+svcgssd_nullrpc_open(void)
+{
+	nullrpc_fd = open(NULLRPC_FILE, O_RDWR);
+	if (nullrpc_fd < 0) {
+		printerr(0, "failed to open %s: %s\n",
+			 NULLRPC_FILE, strerror(errno));
+		return;
+	}
+	nullrpc_event = event_new(evbase, nullrpc_fd, EV_READ | EV_PERSIST,
+				  svcgssd_nullrpc_cb, NULL);
+	if (!nullrpc_event) {
+		printerr(0, "failed to create event for %s: %s\n",
+			 NULLRPC_FILE, strerror(errno));
+		close(nullrpc_fd);
+		nullrpc_fd = -1;
+		return;
+	}
+	event_add(nullrpc_event, NULL);
+	printerr(2, "opened nullrpc channel %s\n", NULLRPC_FILE);
+}
+
+static void
+svcgssd_wait_cb(int UNUSED(fd), short UNUSED(which), void *UNUSED(data))
+{
+	static int times = 0;
+	int rc;
+
+	rc = access(NULLRPC_FILE, R_OK | W_OK);
+	if (rc != 0) {
+		struct timeval t = {times < 10 ? 1 : 10, 0};
+		times++;
+		if (times % 30 == 0)
+			printerr(2, "still waiting for nullrpc channel: %s\n",
+				NULLRPC_FILE);
+		evtimer_add(wait_event, &t);
+		return;
+	}
+
+	svcgssd_nullrpc_open();
+	event_free(wait_event);
+	wait_event = NULL;
+}
+
+
+
 int
 main(int argc, char *argv[])
 {
@@ -102,6 +196,7 @@ main(int argc, char *argv[])
 	char *progname;
 	char *principal = NULL;
 	char *s;
+	int rc;
 
 	conf_init_file(NFS_CONFFILE);
 
@@ -117,6 +212,9 @@ main(int argc, char *argv[])
 	rpc_verbosity = conf_get_num("svcgssd", "RPC-Verbosity", rpc_verbosity);
 	idmap_verbosity = conf_get_num("svcgssd", "IDMAP-Verbosity", idmap_verbosity);
 
+	/* We don't need the config anymore */
+	conf_cleanup();
+
 	while ((opt = getopt(argc, argv, "fivrnp:")) != -1) {
 		switch (opt) {
 			case 'f':
@@ -182,6 +280,12 @@ main(int argc, char *argv[])
 
 	daemon_init(fg);
 
+	evbase = event_base_new();
+	if (!evbase) {
+		printerr(0, "ERROR: failed to create event base: %s\n", strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
 	signal(SIGINT, sig_die);
 	signal(SIGTERM, sig_die);
 	signal(SIGHUP, sig_hup);
@@ -209,10 +313,37 @@ main(int argc, char *argv[])
 		}
 	}
 
+	svcgssd_nullrpc_open();
+	if (!nullrpc_event) {
+		struct timeval t = {1, 0};
+
+		printerr(2, "waiting for nullrpc channel to appear\n");
+		wait_event = evtimer_new(evbase, svcgssd_wait_cb, NULL);
+		if (!wait_event) {
+			printerr(0, "ERROR: failed to create wait event: %s\n",
+				 strerror(errno));
+			exit(EXIT_FAILURE);
+		}
+		evtimer_add(wait_event, &t);
+	}
+
 	daemon_ready();
 
 	nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
-	gssd_run();
-	printerr(0, "gssd_run returned!\n");
-	abort();
+
+	rc = event_base_dispatch(evbase);
+	if (rc < 0)
+		printerr(0, "event_base_dispatch() returned %i!\n", rc);
+
+	svcgssd_nullrpc_close();
+	if (wait_event)
+		event_free(wait_event);
+
+	event_base_free(evbase);
+
+	nfs4_term_name_mapping();
+	svcgssd_free_enctypes();
+	gssd_cleanup();
+
+	return EXIT_SUCCESS;
 }
diff --git a/utils/gssd/svcgssd.h b/utils/gssd/svcgssd.h
index 02b5c7a..e229b98 100644
--- a/utils/gssd/svcgssd.h
+++ b/utils/gssd/svcgssd.h
@@ -35,8 +35,7 @@
 #include <sys/queue.h>
 #include <gssapi/gssapi.h>
 
-void handle_nullreq(int f);
-void gssd_run(void);
+void handle_nullreq(char *cp);
 
 #define GSSD_SERVICE_NAME	"nfs"
 
diff --git a/utils/gssd/svcgssd_krb5.c b/utils/gssd/svcgssd_krb5.c
index 1d44d34..305d475 100644
--- a/utils/gssd/svcgssd_krb5.c
+++ b/utils/gssd/svcgssd_krb5.c
@@ -74,13 +74,7 @@ parse_enctypes(char *enctypes)
 		return 0;
 
 	/* Free any existing cached_enctypes */
-	free(cached_enctypes);
-
-	if (parsed_enctypes != NULL) {
-		free(parsed_enctypes);
-		parsed_enctypes = NULL;
-		parsed_num_enctypes = 0;
-	}
+	svcgssd_free_enctypes();
 
 	/* count the number of commas */
 	for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) {
@@ -162,6 +156,19 @@ out_clean_parsed:
 /*===  External routines ===*/
 /*==========================*/
 
+void
+svcgssd_free_enctypes(void)
+{
+	free(cached_enctypes);
+	cached_enctypes = NULL;
+
+	if (parsed_enctypes != NULL) {
+		free(parsed_enctypes);
+		parsed_enctypes = NULL;
+		parsed_num_enctypes = 0;
+	}
+}
+
 /*
  * Get encryption types supported by the kernel, and then
  * call gss_krb5_set_allowable_enctypes() to limit the
diff --git a/utils/gssd/svcgssd_krb5.h b/utils/gssd/svcgssd_krb5.h
index 07d5eb9..78a90e9 100644
--- a/utils/gssd/svcgssd_krb5.h
+++ b/utils/gssd/svcgssd_krb5.h
@@ -32,5 +32,6 @@
 #define SVCGSSD_KRB5_H
 
 int svcgssd_limit_krb5_enctypes(void);
+void svcgssd_free_enctypes(void);
 
 #endif /* SVCGSSD_KRB5_H */
diff --git a/utils/gssd/svcgssd_main_loop.c b/utils/gssd/svcgssd_main_loop.c
deleted file mode 100644
index 920520d..0000000
--- a/utils/gssd/svcgssd_main_loop.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-  Copyright (c) 2004 The Regents of the University of Michigan.
-  All rights reserved.
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
-
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright
-     notice, this list of conditions and the following disclaimer in the
-     documentation and/or other materials provided with the distribution.
-  3. Neither the name of the University nor the names of its
-     contributors may be used to endorse or promote products derived
-     from this software without specific prior written permission.
-
-  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
-  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif	/* HAVE_CONFIG_H */
-
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <poll.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <netinet/in.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <memory.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "svcgssd.h"
-#include "err_util.h"
-
-void
-gssd_run()
-{
-	int			ret;
-	int			f;
-	struct pollfd		pollfd;
-
-#define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel"
-
-	f = open(NULLRPC_FILE, O_RDWR);
-	if (f < 0) {
-		printerr(0, "failed to open %s: %s\n",
-			 NULLRPC_FILE, strerror(errno));
-		exit(1);
-	}
-	pollfd.fd = f;
-	pollfd.events = POLLIN;
-	while (1) {
-		int save_err;
-
-		pollfd.revents = 0;
-		printerr(1, "entering poll\n");
-		ret = poll(&pollfd, 1, -1);
-		save_err = errno;
-		printerr(1, "leaving poll\n");
-		if (ret < 0) {
-			if (save_err != EINTR)
-				printerr(0, "error return from poll: %s\n",
-					 strerror(save_err));
-		} else if (ret == 0) {
-			/* timeout; shouldn't happen. */
-		} else {
-			if (ret != 1) {
-				printerr(0, "bug: unexpected poll return %d\n",
-						ret);
-				exit(1);
-			}
-			if (pollfd.revents & POLLIN)
-				handle_nullreq(f);
-		}
-	}
-}
diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c
index 72ec254..b403143 100644
--- a/utils/gssd/svcgssd_proc.c
+++ b/utils/gssd/svcgssd_proc.c
@@ -318,7 +318,7 @@ print_hexl(const char *description, unsigned char *cp, int length)
 #endif
 
 void
-handle_nullreq(int f) {
+handle_nullreq(char *cp) {
 	/* XXX initialize to a random integer to reduce chances of unnecessary
 	 * invalidation of existing ctx's on restarting svcgssd. */
 	static u_int32_t	handle_seq = 0;
@@ -340,24 +340,11 @@ handle_nullreq(int f) {
 	u_int32_t		maj_stat = GSS_S_FAILURE, min_stat = 0;
 	u_int32_t		ignore_min_stat;
 	struct svc_cred		cred;
-	char			lbuf[RPC_CHAN_BUF_SIZE];
-	int			lbuflen = 0;
-	char			*cp;
 	int32_t			ctx_endtime;
 	char			*hostbased_name = NULL;
 
 	printerr(1, "handling null request\n");
 
-	lbuflen = read(f, lbuf, sizeof(lbuf));
-	if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') {
-		printerr(0, "WARNING: handle_nullreq: "
-			    "failed reading request\n");
-		return;
-	}
-	lbuf[lbuflen-1] = 0;
-
-	cp = lbuf;
-
 	in_handle.length = (size_t) qword_get(&cp, in_handle.value,
 					      sizeof(in_handle_buf));
 #ifdef DEBUG
diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c
index 893159f..f3d2314 100644
--- a/utils/idmapd/idmapd.c
+++ b/utils/idmapd/idmapd.c
@@ -49,7 +49,7 @@
 
 #include <err.h>
 #include <errno.h>
-#include <event.h>
+#include <event2/event.h>
 #include <fcntl.h>
 #include <dirent.h>
 #include <unistd.h>
@@ -115,7 +115,7 @@ struct idmap_client {
 	int                        ic_fd;
 	int                        ic_dirfd;
 	int                        ic_scanned;
-	struct event               ic_event;
+	struct event              *ic_event;
 	TAILQ_ENTRY(idmap_client)  ic_next;
 };
 static struct idmap_client nfsd_ic[2] = {
@@ -155,6 +155,7 @@ static void idtonameres(struct idmap_msg *);
 static void nametoidres(struct idmap_msg *);
 
 static int nfsdopen(void);
+static void nfsdclose(void);
 static int nfsdopenone(struct idmap_client *);
 static void nfsdreopen_one(struct idmap_client *);
 static void nfsdreopen(void);
@@ -166,6 +167,22 @@ static char pipefsdir[PATH_MAX];
 static char *nobodyuser, *nobodygroup;
 static uid_t nobodyuid;
 static gid_t nobodygid;
+static struct event_base *evbase = NULL;
+static bool signal_received = false;
+static int inotify_fd = -1;
+
+static void
+sig_die(int signal)
+{
+	if (signal_received) {
+		xlog_warn("forced exiting on signal %d\n", signal);
+		exit(0);
+	}
+
+	signal_received = true;
+	xlog_warn("exiting on signal %d\n", signal);
+	event_base_loopexit(evbase, NULL);
+}
 
 static int
 flush_nfsd_cache(char *path, time_t now)
@@ -209,14 +226,14 @@ main(int argc, char **argv)
 {
 	int wd = -1, opt, fg = 0, nfsdret = -1;
 	struct idmap_clientq icq;
-	struct event rootdirev, clntdirev, svrdirev, inotifyev;
-	struct event initialize;
+	struct event *rootdirev = NULL, *clntdirev = NULL,
+		     *svrdirev = NULL, *inotifyev = NULL;
+	struct event *initialize = NULL;
 	struct passwd *pw;
 	struct group *gr;
 	struct stat sb;
 	char *xpipefsdir = NULL;
 	int serverstart = 1, clientstart = 1;
-	int inotify_fd;
 	int ret;
 	char *progname;
 	char *conf_path = NULL;
@@ -289,6 +306,9 @@ main(int argc, char **argv)
 			serverstart = 0;
 	}
 
+	/* Config memory is no longer needed */
+	conf_cleanup();
+
 	while ((opt = getopt(argc, argv, GETOPTSTR)) != -1)
 		switch (opt) {
 		case 'v':
@@ -341,9 +361,11 @@ main(int argc, char **argv)
 	if (nfs4_init_name_mapping(conf_path))
 		errx(1, "Unable to create name to user id mappings.");
 
-	event_init();
+	evbase = event_base_new();
+	if (evbase == NULL)
+		errx(1, "Failed to create event base.");
 
-	if (verbose > 0)
+	if (verbose > 1)
 		xlog_warn("Expiration time is %d seconds.",
 			     cache_entry_expiration);
 	if (serverstart) {
@@ -388,30 +410,44 @@ main(int argc, char **argv)
 		if (inotify_fd == -1) {
 			xlog_err("Unable to initialise inotify_init1: %s\n", strerror(errno));
 		} else {
-			wd = inotify_add_watch(inotify_fd, pipefsdir, IN_CREATE | IN_DELETE | IN_MODIFY);
+			wd = inotify_add_watch(inotify_fd, pipefsdir, IN_CREATE | IN_DELETE);
 			if (wd < 0)
 				xlog_err("Unable to inotify_add_watch(%s): %s\n", pipefsdir, strerror(errno));
 		}
 
 		TAILQ_INIT(&icq);
 
+		signal(SIGINT, sig_die);
+		signal(SIGTERM, sig_die);
+
 		/* These events are persistent */
-		signal_set(&rootdirev, SIGUSR1, dirscancb, &icq);
-		signal_add(&rootdirev, NULL);
-		signal_set(&clntdirev, SIGUSR2, clntscancb, &icq);
-		signal_add(&clntdirev, NULL);
-		signal_set(&svrdirev, SIGHUP, svrreopen, NULL);
-		signal_add(&svrdirev, NULL);
+		rootdirev = evsignal_new(evbase, SIGUSR1, dirscancb, &icq);
+		if (rootdirev == NULL)
+			errx(1, "Failed to create SIGUSR1 event.");
+		evsignal_add(rootdirev, NULL);
+		clntdirev = evsignal_new(evbase, SIGUSR2, clntscancb, &icq);
+		if (clntdirev == NULL)
+			errx(1, "Failed to create SIGUSR2 event.");
+		evsignal_add(clntdirev, NULL);
+		svrdirev = evsignal_new(evbase, SIGHUP, svrreopen, NULL);
+		if (svrdirev == NULL)
+			errx(1, "Failed to create SIGHUP event.");
+		evsignal_add(svrdirev, NULL);
 		if ( wd >= 0) {
-			event_set(&inotifyev, inotify_fd, EV_READ, dirscancb, &icq);
-			event_add(&inotifyev, NULL);
+			inotifyev = event_new(evbase, inotify_fd,
+					      EV_READ | EV_PERSIST, dirscancb, &icq);
+			if (inotifyev == NULL)
+				errx(1, "Failed to create inotify read event.");
+			event_add(inotifyev, NULL);
 		}
 
 		/* Fetch current state */
 		/* (Delay till start of event_dispatch to avoid possibly losing
 		 * a SIGUSR1 between here and the call to event_dispatch().) */
-		evtimer_set(&initialize, dirscancb, &icq);
-		evtimer_add(&initialize, &now);
+		initialize = evtimer_new(evbase, dirscancb, &icq);
+		if (initialize == NULL)
+			errx(1, "Failed to create initialize event.");
+		evtimer_add(initialize, &now);
 	}
 
 	if (nfsdret != 0 && wd < 0)
@@ -419,15 +455,60 @@ main(int argc, char **argv)
 
 	daemon_ready();
 
-	if (event_dispatch() < 0)
+	if (event_base_dispatch(evbase) < 0)
 		xlog_err("main: event_dispatch returns errno %d (%s)",
 			    errno, strerror(errno));
-	/* NOTREACHED */
+
+	nfs4_term_name_mapping();
+	nfsdclose();
+
+	if (inotifyev)
+		event_free(inotifyev);
+	if (inotify_fd != -1)
+		close(inotify_fd);
+
+	if (initialize)
+		event_free(initialize);
+	if (rootdirev)
+		event_free(rootdirev);
+	if (clntdirev)
+		event_free(clntdirev);
+	if (svrdirev)
+		event_free(svrdirev);
+	event_base_free(evbase);
+
 	return 1;
 }
 
 static void
-dirscancb(int UNUSED(fd), short UNUSED(which), void *data)
+flush_inotify(int fd)
+{
+	while (true) {
+		char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
+		const struct inotify_event *ev;
+		ssize_t len;
+		char *ptr;
+
+		len = read(fd, buf, sizeof(buf));
+		if (len == -1 && errno == EINTR)
+			continue;
+
+		if (len <= 0)
+			break;
+
+		for (ptr = buf; ptr < buf + len;
+		     ptr += sizeof(struct inotify_event) + ev->len) {
+
+			ev = (const struct inotify_event *)ptr;
+			if (verbose > 2)
+				xlog_warn("pipefs inotify: wd=%i, mask=0x%08x, len=%i, name=%s",
+				  ev->wd, ev->mask, ev->len, ev->len ? ev->name : "");
+		}
+	}
+}
+
+static void
+dirscancb(int fd, short UNUSED(which), void *data)
 {
 	int nent, i;
 	struct dirent **ents;
@@ -435,6 +516,13 @@ dirscancb(int UNUSED(fd), short UNUSED(which), void *data)
 	char path[PATH_MAX+256]; /* + sizeof(d_name) */
 	struct idmap_clientq *icq = data;
 
+	if (fd != -1)
+		flush_inotify(fd);
+
+	TAILQ_FOREACH(ic, icq, ic_next) {
+		ic->ic_scanned = 0;
+	}
+
 	nent = scandir(pipefsdir, &ents, NULL, alphasort);
 	if (nent == -1) {
 		xlog_warn("dirscancb: scandir(%s): %s", pipefsdir, strerror(errno));
@@ -468,15 +556,15 @@ dirscancb(int UNUSED(fd), short UNUSED(which), void *data)
 			strlcat(path, "/idmap", sizeof(path));
 			strlcpy(ic->ic_path, path, sizeof(ic->ic_path));
 
-			if (verbose > 0)
-				xlog_warn("New client: %s", ic->ic_clid);
-
 			if (nfsopen(ic) == -1) {
 				close(ic->ic_dirfd);
 				free(ic);
 				goto out;
 			}
 
+			if (verbose > 2)
+				xlog_warn("New client: %s", ic->ic_clid);
+
 			ic->ic_id = "Client";
 
 			TAILQ_INSERT_TAIL(icq, ic, ic_next);
@@ -490,17 +578,19 @@ dirscancb(int UNUSED(fd), short UNUSED(which), void *data)
 	while(ic != NULL) {
 		nextic=TAILQ_NEXT(ic, ic_next);
 		if (!ic->ic_scanned) {
-			event_del(&ic->ic_event);
-			close(ic->ic_fd);
-			close(ic->ic_dirfd);
+			if (ic->ic_event)
+				event_free(ic->ic_event);
+			if (ic->ic_fd != -1)
+				close(ic->ic_fd);
+			if (ic->ic_dirfd != -1)
+				close(ic->ic_dirfd);
 			TAILQ_REMOVE(icq, ic, ic_next);
-			if (verbose > 0) {
+			if (verbose > 2) {
 				xlog_warn("Stale client: %s", ic->ic_clid);
 				xlog_warn("\t-> closed %s", ic->ic_path);
 			}
 			free(ic);
-		} else
-			ic->ic_scanned = 0;
+		}
 		ic = nextic;
 	}
 
@@ -546,7 +636,7 @@ nfsdcb(int UNUSED(fd), short which, void *data)
 	unsigned long tmp;
 
 	if (which != EV_READ)
-		goto out;
+		return;
 
 	len = read(ic->ic_fd, buf, sizeof(buf));
 	if (len == 0)
@@ -569,13 +659,13 @@ nfsdcb(int UNUSED(fd), short which, void *data)
 	/* Authentication name -- ignored for now*/
 	if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) {
 		xlog_warn("nfsdcb: bad authentication name in upcall\n");
-		goto out;
+		return;
 	}
 	if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) {
 		xlog_warn("nfsdcb: bad type in upcall\n");
-		goto out;
+		return;
 	}
-	if (verbose > 0)
+	if (verbose > 2)
 		xlog_warn("nfsdcb: authbuf=%s authtype=%s",
 			     authbuf, typebuf);
 
@@ -587,26 +677,26 @@ nfsdcb(int UNUSED(fd), short which, void *data)
 		im.im_conv = IDMAP_CONV_NAMETOID;
 		if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) {
 			xlog_warn("nfsdcb: bad name in upcall\n");
-			goto out;
+			return;
 		}
 		break;
 	case IC_IDNAME:
 		im.im_conv = IDMAP_CONV_IDTONAME;
 		if (getfield(&bp, buf1, sizeof(buf1)) == -1) {
 			xlog_warn("nfsdcb: bad id in upcall\n");
-			goto out;
+			return;
 		}
 		tmp = strtoul(buf1, (char **)NULL, 10);
 		im.im_id = (u_int32_t)tmp;
 		if ((tmp == ULONG_MAX && errno == ERANGE)
 				|| (unsigned long)im.im_id != tmp) {
 			xlog_warn("nfsdcb: id '%s' too big!\n", buf1);
-			goto out;
+			return;
 		}
 		break;
 	default:
 		xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which);
-		goto out;
+		return;
 	}
 
 	imconv(ic, &im);
@@ -667,7 +757,7 @@ nfsdcb(int UNUSED(fd), short which, void *data)
 		break;
 	default:
 		xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which);
-		goto out;
+		return;
 	}
 
 	bsiz = sizeof(buf) - bsiz;
@@ -675,9 +765,6 @@ nfsdcb(int UNUSED(fd), short which, void *data)
 	if (atomicio((void*)write, ic->ic_fd, buf, bsiz) != bsiz)
 		xlog_warn("nfsdcb: write(%s) failed: errno %d (%s)",
 			     ic->ic_path, errno, strerror(errno));
-
-out:
-	event_add(&ic->ic_event, NULL);
 }
 
 static void
@@ -721,14 +808,12 @@ nfscb(int UNUSED(fd), short which, void *data)
 	struct idmap_msg im;
 
 	if (which != EV_READ)
-		goto out;
+		return;
 
 	if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) {
 		if (verbose > 0)
 			xlog_warn("nfscb: read(%s): %s", ic->ic_path, strerror(errno));
-		if (errno == EPIPE)
-			return;
-		goto out;
+		return;
 	}
 
 	imconv(ic, &im);
@@ -742,8 +827,19 @@ nfscb(int UNUSED(fd), short which, void *data)
 
 	if (atomicio((void*)write, ic->ic_fd, &im, sizeof(im)) != sizeof(im))
 		xlog_warn("nfscb: write(%s): %s", ic->ic_path, strerror(errno));
-out:
-	event_add(&ic->ic_event, NULL);
+}
+
+static void
+nfsdclose_one(struct idmap_client *ic)
+{
+	if (ic->ic_event) {
+		event_free(ic->ic_event);
+		ic->ic_event = NULL;
+	}
+	if (ic->ic_fd != -1) {
+		close(ic->ic_fd);
+		ic->ic_fd = -1;
+	}
 }
 
 static void
@@ -751,18 +847,22 @@ nfsdreopen_one(struct idmap_client *ic)
 {
 	int fd;
 
-	if (verbose > 0)
+	if (verbose > 2)
 		xlog_warn("ReOpening %s", ic->ic_path);
 
 	if ((fd = open(ic->ic_path, O_RDWR, 0)) != -1) {
-		if ((event_initialized(&ic->ic_event)))
-			event_del(&ic->ic_event);
-		if (ic->ic_fd != -1)
-			close(ic->ic_fd);
+		nfsdclose_one(ic);
 
-		ic->ic_event.ev_fd = ic->ic_fd = fd;
-		event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic);
-		event_add(&ic->ic_event, NULL);
+		ic->ic_fd = fd;
+		ic->ic_event = event_new(evbase, ic->ic_fd, EV_READ | EV_PERSIST, nfsdcb, ic);
+		if (ic->ic_event == NULL) {
+			xlog_warn("nfsdreopen: Failed to create event for '%s'",
+				  ic->ic_path);
+			close(ic->ic_fd);
+			ic->ic_fd = -1;
+			return;
+		}
+		event_add(ic->ic_event, NULL);
 	} else {
 		xlog_warn("nfsdreopen: Opening '%s' failed: errno %d (%s)",
 			ic->ic_path, errno, strerror(errno));
@@ -784,6 +884,13 @@ nfsdopen(void)
 		    nfsdopenone(&nfsd_ic[IC_IDNAME]) == 0) ? 0 : -1);
 }
 
+static void
+nfsdclose(void)
+{
+	nfsdclose_one(&nfsd_ic[IC_NAMEID]);
+	nfsdclose_one(&nfsd_ic[IC_IDNAME]);
+}
+
 static int
 nfsdopenone(struct idmap_client *ic)
 {
@@ -795,10 +902,18 @@ nfsdopenone(struct idmap_client *ic)
 		return (-1);
 	}
 
-	event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic);
-	event_add(&ic->ic_event, NULL);
+	ic->ic_event = event_new(evbase, ic->ic_fd, EV_READ | EV_PERSIST, nfsdcb, ic);
+	if (ic->ic_event == NULL) {
+		if (verbose > 0)
+			xlog_warn("nfsdopenone: Create event for %s failed",
+				  ic->ic_path);
+		close(ic->ic_fd);
+		ic->ic_fd = -1;
+		return (-1);
+	}
+	event_add(ic->ic_event, NULL);
 
-	if (verbose > 0)
+	if (verbose > 2)
 		xlog_warn("Opened %s", ic->ic_path);
 
 	return (0);
@@ -808,25 +923,35 @@ static int
 nfsopen(struct idmap_client *ic)
 {
 	if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
-		switch (errno) {
-		case ENOENT:
-			fcntl(ic->ic_dirfd, F_SETSIG, SIGUSR2);
-			fcntl(ic->ic_dirfd, F_NOTIFY,
-			    DN_CREATE | DN_DELETE | DN_MULTISHOT);
-			break;
-		default:
-			xlog_warn("nfsopen: open(%s): %s", ic->ic_path, strerror(errno));
-			return (-1);
+		if (errno == ENOENT) {
+			char *slash;
+
+			slash = strrchr(ic->ic_path, '/');
+			if (!slash)
+				return -1;
+			*slash = 0;
+			inotify_add_watch(inotify_fd, ic->ic_path, IN_CREATE | IN_ONLYDIR | IN_ONESHOT);
+			*slash = '/';
+			if (verbose > 2)
+				xlog_warn("Path %s not available. waiting...", ic->ic_path);
+			return -1;
 		}
-	} else {
-		event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic);
-		event_add(&ic->ic_event, NULL);
-		fcntl(ic->ic_dirfd, F_NOTIFY, 0);
-		fcntl(ic->ic_dirfd, F_SETSIG, 0);
-		if (verbose > 0)
-			xlog_warn("Opened %s", ic->ic_path);
+
+		xlog_warn("nfsopen: open(%s): %s", ic->ic_path, strerror(errno));
+		return (-1);
 	}
 
+	ic->ic_event = event_new(evbase, ic->ic_fd, EV_READ | EV_PERSIST, nfscb, ic);
+	if (ic->ic_event == NULL) {
+		xlog_warn("nfsopen: Create event for %s failed", ic->ic_path);
+		close(ic->ic_fd);
+		ic->ic_fd = -1;
+		return -1;
+	}
+	event_add(ic->ic_event, NULL);
+	if (verbose > 2)
+		xlog_warn("Opened %s", ic->ic_path);
+
 	return (0);
 }
 
diff --git a/utils/mount/network.c b/utils/mount/network.c
index 6ac913d..d9c0b51 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -1268,14 +1268,14 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program)
 int
 nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *version)
 {
-	char *version_key, *version_val, *cptr;
+	char *version_key, *version_val = NULL, *cptr;
 	int i, found = 0;
 
 	version->v_mode = V_DEFAULT;
 
 	for (i = 0; nfs_version_opttbl[i]; i++) {
 		if (po_contains_prefix(options, nfs_version_opttbl[i],
-								&version_key) == PO_FOUND) {
+				       &version_key) == PO_FOUND) {
 			found++;
 			break;
 		}
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
index 901f995..91a976b 100644
--- a/utils/mount/stropts.c
+++ b/utils/mount/stropts.c
@@ -1094,9 +1094,7 @@ static int nfsmount_fg(struct nfsmount_info *mi)
 		if (nfs_try_mount(mi))
 			return EX_SUCCESS;
 
-#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
 		if (errno == EBUSY && is_mountpoint(mi->node)) {
-#pragma GCC diagnostic warning "-Wdiscarded-qualifiers"
 			/*
 			 * EBUSY can happen when mounting a filesystem that
 			 * is already mounted or when the context= are
diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index 6cba288..ea74067 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -57,7 +57,7 @@ enum nfsd_fsid {
 };
 
 #undef is_mountpoint
-static int is_mountpoint(char *path)
+static int is_mountpoint(const char *path)
 {
 	return check_is_mountpoint(path, nfsd_path_lstat);
 }
diff --git a/utils/nfsdcld/cld-internal.h b/utils/nfsdcld/cld-internal.h
index cc283da..3576515 100644
--- a/utils/nfsdcld/cld-internal.h
+++ b/utils/nfsdcld/cld-internal.h
@@ -26,7 +26,7 @@
 
 struct cld_client {
 	int			cl_fd;
-	struct event		cl_event;
+	struct event		*cl_event;
 	union {
 		struct cld_msg		cl_msg;
 #if UPCALL_VERSION >= 2
diff --git a/utils/nfsdcld/legacy.c b/utils/nfsdcld/legacy.c
index 3c6bea6..b89374c 100644
--- a/utils/nfsdcld/legacy.c
+++ b/utils/nfsdcld/legacy.c
@@ -48,35 +48,28 @@ legacy_load_clients_from_recdir(int *num_records)
 	int fd;
 	DIR *v4recovery;
 	struct dirent *entry;
-	char recdirname[PATH_MAX];
+	char recdirname[PATH_MAX+1];
 	char buf[NFS4_OPAQUE_LIMIT];
-	struct stat st;
 	char *nl;
+	ssize_t n;
 
 	fd = open(NFSD_RECDIR_FILE, O_RDONLY);
 	if (fd < 0) {
 		xlog(D_GENERAL, "Unable to open %s: %m", NFSD_RECDIR_FILE);
 		return;
 	}
-	if (read(fd, recdirname, PATH_MAX) < 0) {
+	n = read(fd, recdirname, PATH_MAX);
+	close(fd);
+	if (n < 0) {
 		xlog(D_GENERAL, "Unable to read from %s: %m", NFSD_RECDIR_FILE);
 		return;
 	}
-	close(fd);
 	/* the output from the proc file isn't null-terminated */
+	recdirname[PATH_MAX] = '\0';
 	nl = strchr(recdirname, '\n');
 	if (!nl)
 		return;
 	*nl = '\0';
-	if (stat(recdirname, &st) < 0) {
-		xlog(D_GENERAL, "Unable to stat %s: %d", recdirname, errno);
-		return;
-	}
-	if (!S_ISDIR(st.st_mode)) {
-		xlog(D_GENERAL, "%s is not a directory: mode=0%o", recdirname
-				, st.st_mode);
-		return;
-	}
 	v4recovery = opendir(recdirname);
 	if (!v4recovery)
 		return;
@@ -123,35 +116,28 @@ legacy_clear_recdir(void)
 	int fd;
 	DIR *v4recovery;
 	struct dirent *entry;
-	char recdirname[PATH_MAX];
+	char recdirname[PATH_MAX+1];
 	char dirname[PATH_MAX];
-	struct stat st;
 	char *nl;
+	ssize_t n;
 
 	fd = open(NFSD_RECDIR_FILE, O_RDONLY);
 	if (fd < 0) {
 		xlog(D_GENERAL, "Unable to open %s: %m", NFSD_RECDIR_FILE);
 		return;
 	}
-	if (read(fd, recdirname, PATH_MAX) < 0) {
+	n = read(fd, recdirname, PATH_MAX);
+	close(fd);
+	if (n < 0) {
 		xlog(D_GENERAL, "Unable to read from %s: %m", NFSD_RECDIR_FILE);
 		return;
 	}
-	close(fd);
 	/* the output from the proc file isn't null-terminated */
+	recdirname[PATH_MAX] = '\0';
 	nl = strchr(recdirname, '\n');
 	if (!nl)
 		return;
 	*nl = '\0';
-	if (stat(recdirname, &st) < 0) {
-		xlog(D_GENERAL, "Unable to stat %s: %d", recdirname, errno);
-		return;
-	}
-	if (!S_ISDIR(st.st_mode)) {
-		xlog(D_GENERAL, "%s is not a directory: mode=0%o", recdirname
-				, st.st_mode);
-		return;
-	}
 	v4recovery = opendir(recdirname);
 	if (!v4recovery)
 		return;
diff --git a/utils/nfsdcld/nfsdcld.c b/utils/nfsdcld/nfsdcld.c
index be65562..636c398 100644
--- a/utils/nfsdcld/nfsdcld.c
+++ b/utils/nfsdcld/nfsdcld.c
@@ -24,9 +24,10 @@
 #endif /* HAVE_CONFIG_H */
 
 #include <errno.h>
-#include <event.h>
+#include <event2/event.h>
 #include <stdbool.h>
 #include <getopt.h>
+#include <signal.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -66,8 +67,10 @@
 static char pipefs_dir[PATH_MAX] = DEFAULT_PIPEFS_DIR;
 static char pipepath[PATH_MAX];
 static int 		inotify_fd = -1;
-static struct event	pipedir_event;
+static struct event	 *pipedir_event;
+static struct event_base *evbase;
 static bool old_kernel = false;
+static bool signal_received = false;
 
 uint64_t current_epoch;
 uint64_t recovery_epoch;
@@ -88,6 +91,19 @@ static struct option longopts[] =
 /* forward declarations */
 static void cldcb(int UNUSED(fd), short which, void *data);
 
+static void
+sig_die(int signal)
+{
+	if (signal_received) {
+		xlog(D_GENERAL, "forced exiting on signal %d\n", signal);
+		exit(0);
+	}
+
+	signal_received = true;
+	xlog(D_GENERAL, "exiting on signal %d\n", signal);
+	event_base_loopexit(evbase, NULL);
+}
+
 static void
 usage(char *progname)
 {
@@ -141,6 +157,7 @@ static int
 cld_pipe_open(struct cld_client *clnt)
 {
 	int fd;
+	struct event *ev;
 
 	xlog(D_GENERAL, "%s: opening upcall pipe %s", __func__, pipepath);
 	fd = open(pipepath, O_RDWR, 0);
@@ -149,13 +166,22 @@ cld_pipe_open(struct cld_client *clnt)
 		return -errno;
 	}
 
-	if (event_initialized(&clnt->cl_event))
-		event_del(&clnt->cl_event);
+	ev = event_new(evbase, fd, EV_READ, cldcb, clnt);
+	if (ev == NULL) {
+		xlog(D_GENERAL, "%s: failed to create event for %s", __func__, pipepath);
+		close(fd);
+		return -ENOMEM;
+	}
+
+	if (clnt->cl_event && event_initialized(clnt->cl_event)) {
+		event_del(clnt->cl_event);
+		event_free(clnt->cl_event);
+	}
 	if (clnt->cl_fd >= 0)
 		close(clnt->cl_fd);
 
 	clnt->cl_fd = fd;
-	event_set(&clnt->cl_event, clnt->cl_fd, EV_READ, cldcb, clnt);
+	clnt->cl_event = ev;
 	/* event_add is done by the caller */
 	return 0;
 }
@@ -208,7 +234,7 @@ cld_inotify_cb(int UNUSED(fd), short which, void *data)
 	switch (ret) {
 	case 0:
 		/* readd the event for the cl_event pipe */
-		event_add(&clnt->cl_event, NULL);
+		event_add(clnt->cl_event, NULL);
 		break;
 	case -ENOENT:
 		/* pipe must have disappeared, wait for it to come back */
@@ -221,7 +247,7 @@ cld_inotify_cb(int UNUSED(fd), short which, void *data)
 	}
 
 out:
-	event_add(&pipedir_event, NULL);
+	event_add(pipedir_event, NULL);
 	free(dirc);
 }
 
@@ -252,11 +278,12 @@ cld_inotify_setup(void)
 		xlog_err("%s: inotify_add_watch failed: %m", __func__);
 		ret = -errno;
 		goto out_err;
-	}
+	} else
+		ret = 0;
 
 out_free:
 	free(dirc);
-	return 0;
+	return ret;
 out_err:
 	close(inotify_fd);
 	goto out_free;
@@ -286,7 +313,7 @@ cld_pipe_init(struct cld_client *clnt)
 	switch (ret) {
 	case 0:
 		/* add the event and we're good to go */
-		event_add(&clnt->cl_event, NULL);
+		event_add(clnt->cl_event, NULL);
 		break;
 	case -ENOENT:
 		/* ignore this error -- cld_inotify_cb will handle it */
@@ -299,8 +326,12 @@ cld_pipe_init(struct cld_client *clnt)
 	}
 
 	/* set event for inotify read */
-	event_set(&pipedir_event, inotify_fd, EV_READ, cld_inotify_cb, clnt);
-	event_add(&pipedir_event, NULL);
+	pipedir_event = event_new(evbase, inotify_fd, EV_READ, cld_inotify_cb, clnt);
+	if (pipedir_event == NULL) {
+		close(inotify_fd);
+		return -ENOMEM;
+	}
+	event_add(pipedir_event, NULL);
 out:
 	return ret;
 }
@@ -331,6 +362,7 @@ cld_check_grace_period(void)
 	if (read(fd, &c, 1) < 0) {
 		xlog(L_WARNING, "Unable to read from %s: %m",
 			NFSD_END_GRACE_FILE);
+		close(fd);
 		return 1;
 	}
 	close(fd);
@@ -737,7 +769,7 @@ cldcb(int UNUSED(fd), short which, void *data)
 		cld_not_implemented(clnt);
 	}
 out:
-	event_add(&clnt->cl_event, NULL);
+	event_add(clnt->cl_event, NULL);
 }
 
 int
@@ -762,7 +794,11 @@ main(int argc, char **argv)
 		return 1;
 	}
 
-	event_init();
+	evbase = event_base_new();
+	if (evbase == NULL) {
+		fprintf(stderr, "%s: unable to allocate event base.\n", argv[0]);
+		return 1;
+	}
 	xlog_syslog(0);
 	xlog_stderr(1);
 
@@ -795,6 +831,7 @@ main(int argc, char **argv)
 			break;
 		default:
 			usage(progname);
+			free(progname);
 			return 0;
 		}
 	}
@@ -859,14 +896,27 @@ main(int argc, char **argv)
 	if (rc)
 		goto out;
 
+	signal(SIGINT, sig_die);
+	signal(SIGTERM, sig_die);
+
 	xlog(D_GENERAL, "%s: Starting event dispatch handler.", __func__);
-	rc = event_dispatch();
+	rc = event_base_dispatch(evbase);
 	if (rc < 0)
 		xlog(L_ERROR, "%s: event_dispatch failed: %m", __func__);
 
-	close(clnt.cl_fd);
-	close(inotify_fd);
 out:
+	if (clnt.cl_event)
+		event_free(clnt.cl_event);
+	if (clnt.cl_fd != -1)
+		close(clnt.cl_fd);
+	if (pipedir_event)
+		event_free(pipedir_event);
+	if (inotify_fd != -1)
+		close(inotify_fd);
+
+	event_base_free(evbase);
+	sqlite_shutdown();
+
 	free(progname);
 	return rc;
 }
diff --git a/utils/nfsdcld/sqlite.c b/utils/nfsdcld/sqlite.c
index 6666c86..03016fb 100644
--- a/utils/nfsdcld/sqlite.c
+++ b/utils/nfsdcld/sqlite.c
@@ -48,7 +48,6 @@
 
 #include <dirent.h>
 #include <errno.h>
-#include <event.h>
 #include <stdbool.h>
 #include <string.h>
 #include <sys/stat.h>
@@ -380,7 +379,7 @@ sqlite_maindb_init_v4(void)
 				&err);
 	if (ret != SQLITE_OK) {
 		xlog(L_ERROR, "Unable to begin transaction: %s", err);
-		return ret;
+		goto out;
 	}
 
 	/*
@@ -831,7 +830,6 @@ sqlite_prepare_dbh(const char *topdir)
 	switch (ret) {
 	case CLD_SQLITE_LATEST_SCHEMA_VERSION:
 		/* DB is already set up. Do nothing */
-		ret = 0;
 		break;
 	case 3:
 		/* Old DB -- update to new schema */
@@ -868,6 +866,8 @@ sqlite_prepare_dbh(const char *topdir)
 	}
 
 	ret = sqlite_startup_query_grace();
+	if (ret)
+		goto out_close;
 
 	ret = sqlite_query_first_time(&first_time);
 	if (ret)
@@ -1330,20 +1330,26 @@ sqlite_iterate_recovery(int (*cb)(struct cld_client *clnt), struct cld_client *c
 	}
 
 	while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) {
+		const void *id;
+		int id_len;
+
+		id = sqlite3_column_blob(stmt, 0);
+		id_len = sqlite3_column_bytes(stmt, 0);
+		if (id_len > NFS4_OPAQUE_LIMIT)
+			id_len = NFS4_OPAQUE_LIMIT;
+
 		memset(&cmsg->cm_u, 0, sizeof(cmsg->cm_u));
 #if UPCALL_VERSION >= 2
-		memcpy(&cmsg->cm_u.cm_clntinfo.cc_name.cn_id,
-			sqlite3_column_blob(stmt, 0), NFS4_OPAQUE_LIMIT);
-		cmsg->cm_u.cm_clntinfo.cc_name.cn_len = sqlite3_column_bytes(stmt, 0);
+		memcpy(&cmsg->cm_u.cm_clntinfo.cc_name.cn_id, id, id_len);
+		cmsg->cm_u.cm_clntinfo.cc_name.cn_len = id_len;
 		if (sqlite3_column_bytes(stmt, 1) > 0) {
 			memcpy(&cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data,
 				sqlite3_column_blob(stmt, 1), SHA256_DIGEST_SIZE);
 			cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = sqlite3_column_bytes(stmt, 1);
 		}
 #else
-		memcpy(&cmsg->cm_u.cm_name.cn_id, sqlite3_column_blob(stmt, 0),
-			NFS4_OPAQUE_LIMIT);
-		cmsg->cm_u.cm_name.cn_len = sqlite3_column_bytes(stmt, 0);
+		memcpy(&cmsg->cm_u.cm_name.cn_id, id, id_len);
+		cmsg->cm_u.cm_name.cn_len = id_len;
 #endif
 		cb(clnt);
 	}
@@ -1404,3 +1410,18 @@ sqlite_first_time_done(void)
 	sqlite3_free(err);
 	return ret;
 }
+
+/*
+ * Closes all sqlite3 resources and shuts down the library.
+ *
+ */
+void
+sqlite_shutdown(void)
+{
+	if (dbh != NULL) {
+		sqlite3_close(dbh);
+		dbh = NULL;
+	}
+
+	sqlite3_shutdown();
+}
diff --git a/utils/nfsdcld/sqlite.h b/utils/nfsdcld/sqlite.h
index 0a26ad6..044236c 100644
--- a/utils/nfsdcld/sqlite.h
+++ b/utils/nfsdcld/sqlite.h
@@ -34,4 +34,5 @@ int sqlite_iterate_recovery(int (*cb)(struct cld_client *clnt), struct cld_clien
 int sqlite_delete_cltrack_records(void);
 int sqlite_first_time_done(void);
 
+void sqlite_shutdown(void);
 #endif /* _SQLITE_H */
diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c
index 2801201..f79aebb 100644
--- a/utils/nfsdcltrack/sqlite.c
+++ b/utils/nfsdcltrack/sqlite.c
@@ -40,7 +40,7 @@
 
 #include <dirent.h>
 #include <errno.h>
-#include <event.h>
+#include <stdio.h>
 #include <stdbool.h>
 #include <string.h>
 #include <sys/stat.h>