All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients
@ 2012-10-24 15:25 Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 01/10] nfsdcltrack: fix segfault in sqlite debug logging Jeff Layton
                   ` (9 more replies)
  0 siblings, 10 replies; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

This is a respin of the set that I sent on Oct 1st. The main differences
from that set are:

- a new legacy transition mechanism has been added and the manpage has
  been updated to document it

- the nfsdcld daemon is being removed wholesale. Steve D. requested that
  we just go ahead and pull the plug on it, and I didn't see any reason
  to object

- the default is now to build nfsdcltrack if all of the dependencies
  for it are in place

It's a fairly simple program and I've tested it as best I can. At this
point, I'd like to see this patchset go into the next nfs-utils release.

Jeff Layton (10):
  nfsdcltrack: fix segfault in sqlite debug logging
  nfsdcltrack: rename the nfsdcld directory and options to nfsdcltrack
  nfsdcltrack: remove pointless sqlite_topdir variable
  nfsdcltrack: break out a function to open the database handle
  nfsdcltrack: add a new "one-shot" program for manipulating the client
    tracking db
  nfsdcltrack: add a legacy transition mechanism
  nfsdcltrack: add a manpage for nfsdcltrack
  nfsdcltrack: remove the nfsdcld daemon
  nfsdcltrack: update the README about server startup order
  nfsdcltrack: flip the default in autoconf to "yes" for it

 README                            |  23 +-
 configure.ac                      |  34 ++-
 utils/Makefile.am                 |   4 +-
 utils/nfsdcld/Makefile.am         |  14 -
 utils/nfsdcld/nfsdcld.c           | 607 --------------------------------------
 utils/nfsdcld/nfsdcld.man         | 185 ------------
 utils/nfsdcld/sqlite.c            | 386 ------------------------
 utils/nfsdcld/sqlite.h            |  29 --
 utils/nfsdcltrack/Makefile.am     |  13 +
 utils/nfsdcltrack/nfsdcltrack.c   | 526 +++++++++++++++++++++++++++++++++
 utils/nfsdcltrack/nfsdcltrack.man | 211 +++++++++++++
 utils/nfsdcltrack/sqlite.c        | 400 +++++++++++++++++++++++++
 utils/nfsdcltrack/sqlite.h        |  30 ++
 13 files changed, 1206 insertions(+), 1256 deletions(-)
 delete mode 100644 utils/nfsdcld/Makefile.am
 delete mode 100644 utils/nfsdcld/nfsdcld.c
 delete mode 100644 utils/nfsdcld/nfsdcld.man
 delete mode 100644 utils/nfsdcld/sqlite.c
 delete mode 100644 utils/nfsdcld/sqlite.h
 create mode 100644 utils/nfsdcltrack/Makefile.am
 create mode 100644 utils/nfsdcltrack/nfsdcltrack.c
 create mode 100644 utils/nfsdcltrack/nfsdcltrack.man
 create mode 100644 utils/nfsdcltrack/sqlite.c
 create mode 100644 utils/nfsdcltrack/sqlite.h

-- 
1.7.11.7


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v2 01/10] nfsdcltrack: fix segfault in sqlite debug logging
  2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
@ 2012-10-24 15:25 ` Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 02/10] nfsdcltrack: rename the nfsdcld directory and options to nfsdcltrack Jeff Layton
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

One too few arguments...

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 utils/nfsdcld/sqlite.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/utils/nfsdcld/sqlite.c b/utils/nfsdcld/sqlite.c
index bb2519d..fc882c6 100644
--- a/utils/nfsdcld/sqlite.c
+++ b/utils/nfsdcld/sqlite.c
@@ -320,7 +320,7 @@ sqlite_check_client(const unsigned char *clname, const size_t namelen)
 	}
 
 	ret = sqlite3_column_int(stmt, 0);
-	xlog(D_GENERAL, "%s: select returned %d rows", ret);
+	xlog(D_GENERAL, "%s: select returned %d rows", __func__, ret);
 	if (ret != 1) {
 		ret = -EACCES;
 		goto out_err;
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 02/10] nfsdcltrack: rename the nfsdcld directory and options to nfsdcltrack
  2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 01/10] nfsdcltrack: fix segfault in sqlite debug logging Jeff Layton
@ 2012-10-24 15:25 ` Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 03/10] nfsdcltrack: remove pointless sqlite_topdir variable Jeff Layton
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

We'll soon be adding a new nfsdcltrack program, at which point it won't
make much sense to call this directory and the config option "nfsdcld".
Rename it to be a bit more generic.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 configure.ac                  |  20 +-
 utils/Makefile.am             |   4 +-
 utils/nfsdcld/Makefile.am     |  14 -
 utils/nfsdcld/nfsdcld.c       | 607 ------------------------------------------
 utils/nfsdcld/nfsdcld.man     | 185 -------------
 utils/nfsdcld/sqlite.c        | 386 ---------------------------
 utils/nfsdcld/sqlite.h        |  29 --
 utils/nfsdcltrack/Makefile.am |  14 +
 utils/nfsdcltrack/nfsdcld.c   | 607 ++++++++++++++++++++++++++++++++++++++++++
 utils/nfsdcltrack/nfsdcld.man | 185 +++++++++++++
 utils/nfsdcltrack/sqlite.c    | 386 +++++++++++++++++++++++++++
 utils/nfsdcltrack/sqlite.h    |  29 ++
 12 files changed, 1233 insertions(+), 1233 deletions(-)
 delete mode 100644 utils/nfsdcld/Makefile.am
 delete mode 100644 utils/nfsdcld/nfsdcld.c
 delete mode 100644 utils/nfsdcld/nfsdcld.man
 delete mode 100644 utils/nfsdcld/sqlite.c
 delete mode 100644 utils/nfsdcld/sqlite.h
 create mode 100644 utils/nfsdcltrack/Makefile.am
 create mode 100644 utils/nfsdcltrack/nfsdcld.c
 create mode 100644 utils/nfsdcltrack/nfsdcld.man
 create mode 100644 utils/nfsdcltrack/sqlite.c
 create mode 100644 utils/nfsdcltrack/sqlite.h

diff --git a/configure.ac b/configure.ac
index a174bf4..65d1bea 100644
--- a/configure.ac
+++ b/configure.ac
@@ -185,11 +185,11 @@ else
 	AM_CONDITIONAL(MOUNT_CONFIG, [test "$enable_mount" = "yes"])
 fi
 
-AC_ARG_ENABLE(nfsdcld,
-	[AC_HELP_STRING([--enable-nfsdcld],
-			[Create nfsdcld NFSv4 clientid tracking daemon. @<:@default=no@:>@])],
-	enable_nfsdcld=$enableval,
-	enable_nfsdcld="no")
+AC_ARG_ENABLE(nfsdcltrack,
+	[AC_HELP_STRING([--enable-nfsdcltrack],
+			[enable NFSv4 clientid tracking programs @<:@default=no@:>@])],
+	enable_nfsdctrack=$enableval,
+	enable_nfsdcltrack="no")
 
 dnl Check for TI-RPC library and headers
 AC_LIBTIRPC
@@ -269,12 +269,12 @@ if test "$enable_nfsv4" = yes; then
   dnl Check for sqlite3
   AC_SQLITE3_VERS
 
-  if test "$enable_nfsdcld" = "yes"; then
+  if test "$enable_nfsdcltrack" = "yes"; then
 	AC_CHECK_HEADERS([libgen.h sys/inotify.h], ,
-		AC_MSG_ERROR([Cannot find header needed for nfsdcld]))
+		AC_MSG_ERROR([Cannot find header needed for nfsdcltrack]))
 
   	if test "$libsqlite3_cv_is_recent" != "yes" ; then
-		AC_MSG_ERROR([nfsdcld requires sqlite3])
+		AC_MSG_ERROR([nfsdcltrack requires sqlite3])
 	fi
   fi
 
@@ -292,7 +292,7 @@ if test "$enable_nfsv41" = yes; then
 fi
 
 dnl enable nfsidmap when its support by libnfsidmap
-AM_CONDITIONAL(CONFIG_NFSDCLD, [test "$enable_nfsdcld" = "yes" ])
+AM_CONDITIONAL(CONFIG_NFSDCLTRACK, [test "$enable_nfsdcltrack" = "yes" ])
 AM_CONDITIONAL(CONFIG_NFSIDMAP, [test "$ac_cv_header_keyutils_h$ac_cv_lib_nfsidmap_nfs4_owner_to_uid" = "yesyes"])
 
 
@@ -477,7 +477,7 @@ AC_CONFIG_FILES([
 	tools/nfs-iostat/Makefile
 	utils/Makefile
 	utils/blkmapd/Makefile
-	utils/nfsdcld/Makefile
+	utils/nfsdcltrack/Makefile
 	utils/exportfs/Makefile
 	utils/gssd/Makefile
 	utils/idmapd/Makefile
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 09045dd..b892dc8 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -21,8 +21,8 @@ if CONFIG_MOUNT
 OPTDIRS += mount
 endif
 
-if CONFIG_NFSDCLD
-OPTDIRS += nfsdcld
+if CONFIG_NFSDCLTRACK
+OPTDIRS += nfsdcltrack
 endif
 
 SUBDIRS = \
diff --git a/utils/nfsdcld/Makefile.am b/utils/nfsdcld/Makefile.am
deleted file mode 100644
index 073a71b..0000000
--- a/utils/nfsdcld/Makefile.am
+++ /dev/null
@@ -1,14 +0,0 @@
-## Process this file with automake to produce Makefile.in
-
-man8_MANS	= nfsdcld.man
-EXTRA_DIST	= $(man8_MANS)
-
-AM_CFLAGS	+= -D_LARGEFILE64_SOURCE
-sbin_PROGRAMS	= nfsdcld
-
-nfsdcld_SOURCES = nfsdcld.c sqlite.c
-
-nfsdcld_LDADD = ../../support/nfs/libnfs.a $(LIBEVENT) $(LIBSQLITE) $(LIBCAP)
-
-MAINTAINERCLEANFILES = Makefile.in
-
diff --git a/utils/nfsdcld/nfsdcld.c b/utils/nfsdcld/nfsdcld.c
deleted file mode 100644
index 473d069..0000000
--- a/utils/nfsdcld/nfsdcld.c
+++ /dev/null
@@ -1,607 +0,0 @@
-/*
- * nfsdcld.c -- NFSv4 client name tracking daemon
- *
- * Copyright (C) 2011  Red Hat, Jeff Layton <jlayton@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <errno.h>
-#include <event.h>
-#include <stdbool.h>
-#include <getopt.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <libgen.h>
-#include <sys/inotify.h>
-#ifdef HAVE_SYS_CAPABILITY_H
-#include <sys/prctl.h>
-#include <sys/capability.h>
-#endif
-
-#include "xlog.h"
-#include "nfslib.h"
-#include "cld.h"
-#include "sqlite.h"
-
-#ifndef PIPEFS_DIR
-#define PIPEFS_DIR NFS_STATEDIR "/rpc_pipefs"
-#endif
-
-#define DEFAULT_CLD_PATH	PIPEFS_DIR "/nfsd/cld"
-
-#ifndef CLD_DEFAULT_STORAGEDIR
-#define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcld"
-#endif
-
-#define UPCALL_VERSION		1
-
-/* private data structures */
-struct cld_client {
-	int			cl_fd;
-	struct event		cl_event;
-	struct cld_msg	cl_msg;
-};
-
-/* global variables */
-static char *pipepath = DEFAULT_CLD_PATH;
-static int 		inotify_fd = -1;
-static struct event	pipedir_event;
-
-static struct option longopts[] =
-{
-	{ "help", 0, NULL, 'h' },
-	{ "foreground", 0, NULL, 'F' },
-	{ "debug", 0, NULL, 'd' },
-	{ "pipe", 1, NULL, 'p' },
-	{ "storagedir", 1, NULL, 's' },
-	{ NULL, 0, 0, 0 },
-};
-
-/* forward declarations */
-static void cldcb(int UNUSED(fd), short which, void *data);
-
-static void
-usage(char *progname)
-{
-	printf("%s [ -hFd ] [ -p pipe ] [ -s dir ]\n", progname);
-}
-
-static int
-cld_set_caps(void)
-{
-	int ret = 0;
-#ifdef HAVE_SYS_CAPABILITY_H
-	unsigned long i;
-	cap_t caps;
-
-	if (getuid() != 0) {
-		xlog(L_ERROR, "Not running as root. Daemon won't be able to "
-			      "open the pipe after dropping capabilities!");
-		return -EINVAL;
-	}
-
-	/* prune the bounding set to nothing */
-	for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0 ; ++i) {
-		ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
-		if (ret) {
-			xlog(L_ERROR, "Unable to prune capability %lu from "
-				      "bounding set: %m", i);
-			return -errno;
-		}
-	}
-
-	/* get a blank capset */
-	caps = cap_init();
-	if (caps == NULL) {
-		xlog(L_ERROR, "Unable to get blank capability set: %m");
-		return -errno;
-	}
-
-	/* reset the process capabilities */
-	if (cap_set_proc(caps) != 0) {
-		xlog(L_ERROR, "Unable to set process capabilities: %m");
-		ret = -errno;
-	}
-	cap_free(caps);
-#endif
-	return ret;
-}
-
-#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX)
-
-static int
-cld_pipe_open(struct cld_client *clnt)
-{
-	int fd;
-
-	xlog(D_GENERAL, "%s: opening upcall pipe %s", __func__, pipepath);
-	fd = open(pipepath, O_RDWR, 0);
-	if (fd < 0) {
-		xlog(D_GENERAL, "%s: open of %s failed: %m", __func__, pipepath);
-		return -errno;
-	}
-
-	if (clnt->cl_event.ev_flags & EVLIST_INIT)
-		event_del(&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);
-	/* event_add is done by the caller */
-	return 0;
-}
-
-static void
-cld_inotify_cb(int UNUSED(fd), short which, void *data)
-{
-	int ret;
-	size_t elen;
-	ssize_t rret;
-	char evbuf[INOTIFY_EVENT_MAX];
-	char *dirc = NULL, *pname;
-	struct inotify_event *event = (struct inotify_event *)evbuf;
-	struct cld_client *clnt = data;
-
-	if (which != EV_READ)
-		return;
-
-	xlog(D_GENERAL, "%s: called for EV_READ", __func__);
-
-	dirc = strndup(pipepath, PATH_MAX);
-	if (!dirc) {
-		xlog(L_ERROR, "%s: unable to allocate memory", __func__);
-		goto out;
-	}
-
-	rret = read(inotify_fd, evbuf, INOTIFY_EVENT_MAX);
-	if (rret < 0) {
-		xlog(L_ERROR, "%s: read from inotify fd failed: %m", __func__);
-		goto out;
-	}
-
-	/* check to see if we have a filename in the evbuf */
-	if (!event->len) {
-		xlog(D_GENERAL, "%s: no filename in inotify event", __func__);
-		goto out;
-	}
-
-	pname = basename(dirc);
-	elen = strnlen(event->name, event->len);
-
-	/* does the filename match our pipe? */
-	if (strlen(pname) != elen || memcmp(pname, event->name, elen)) {
-		xlog(D_GENERAL, "%s: wrong filename (%s)", __func__,
-				event->name);
-		goto out;
-	}
-
-	ret = cld_pipe_open(clnt);
-	switch (ret) {
-	case 0:
-		/* readd the event for the cl_event pipe */
-		event_add(&clnt->cl_event, NULL);
-		break;
-	case -ENOENT:
-		/* pipe must have disappeared, wait for it to come back */
-		goto out;
-	default:
-		/* anything else is fatal */
-		xlog(L_FATAL, "%s: unable to open new pipe (%d). Aborting.",
-			ret, __func__);
-		exit(ret);
-	}
-
-out:
-	event_add(&pipedir_event, NULL);
-	free(dirc);
-}
-
-static int
-cld_inotify_setup(void)
-{
-	int ret;
-	char *dirc, *dname;
-
-	dirc = strndup(pipepath, PATH_MAX);
-	if (!dirc) {
-		xlog_err("%s: unable to allocate memory", __func__);
-		ret = -ENOMEM;
-		goto out_free;
-	}
-
-	dname = dirname(dirc);
-
-	inotify_fd = inotify_init();
-	if (inotify_fd < 0) {
-		xlog_err("%s: inotify_init failed: %m", __func__);
-		ret = -errno;
-		goto out_free;
-	}
-
-	ret = inotify_add_watch(inotify_fd, dname, IN_CREATE);
-	if (ret < 0) {
-		xlog_err("%s: inotify_add_watch failed: %m", __func__);
-		ret = -errno;
-		goto out_err;
-	}
-
-out_free:
-	free(dirc);
-	return 0;
-out_err:
-	close(inotify_fd);
-	goto out_free;
-}
-
-/*
- * Set an inotify watch on the directory that should contain the pipe, and then
- * try to open it. If it fails with anything but -ENOENT, return the error
- * immediately.
- *
- * If it succeeds, then set up the pipe event handler. At that point, set up
- * the inotify event handler and go ahead and return success.
- */
-static int
-cld_pipe_init(struct cld_client *clnt)
-{
-	int ret;
-
-	xlog(D_GENERAL, "%s: init pipe handlers", __func__);
-
-	ret = cld_inotify_setup();
-	if (ret != 0)
-		goto out;
-
-	clnt->cl_fd = -1;
-	ret = cld_pipe_open(clnt);
-	switch (ret) {
-	case 0:
-		/* add the event and we're good to go */
-		event_add(&clnt->cl_event, NULL);
-		break;
-	case -ENOENT:
-		/* ignore this error -- cld_inotify_cb will handle it */
-		ret = 0;
-		break;
-	default:
-		/* anything else is fatal */
-		close(inotify_fd);
-		goto out;
-	}
-
-	/* set event for inotify read */
-	event_set(&pipedir_event, inotify_fd, EV_READ, cld_inotify_cb, clnt);
-	event_add(&pipedir_event, NULL);
-out:
-	return ret;
-}
-
-static void
-cld_not_implemented(struct cld_client *clnt)
-{
-	int ret;
-	ssize_t bsize, wsize;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	xlog(D_GENERAL, "%s: downcalling with not implemented error", __func__);
-
-	/* set up reply */
-	cmsg->cm_status = -EOPNOTSUPP;
-
-	bsize = sizeof(*cmsg);
-
-	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
-	if (wsize != bsize)
-		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
-			 __func__, wsize);
-
-	/* reopen pipe, just to be sure */
-	ret = cld_pipe_open(clnt);
-	if (ret) {
-		xlog(L_FATAL, "%s: unable to reopen pipe: %d", __func__, ret);
-		exit(ret);
-	}
-}
-
-static void
-cld_create(struct cld_client *clnt)
-{
-	int ret;
-	ssize_t bsize, wsize;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	xlog(D_GENERAL, "%s: create client record.", __func__);
-
-	ret = sqlite_insert_client(cmsg->cm_u.cm_name.cn_id,
-				   cmsg->cm_u.cm_name.cn_len);
-
-	cmsg->cm_status = ret ? -EREMOTEIO : ret;
-
-	bsize = sizeof(*cmsg);
-
-	xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status);
-	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
-	if (wsize != bsize) {
-		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
-			 __func__, wsize);
-		ret = cld_pipe_open(clnt);
-		if (ret) {
-			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
-					__func__, ret);
-			exit(ret);
-		}
-	}
-}
-
-static void
-cld_remove(struct cld_client *clnt)
-{
-	int ret;
-	ssize_t bsize, wsize;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	xlog(D_GENERAL, "%s: remove client record.", __func__);
-
-	ret = sqlite_remove_client(cmsg->cm_u.cm_name.cn_id,
-				   cmsg->cm_u.cm_name.cn_len);
-
-	cmsg->cm_status = ret ? -EREMOTEIO : ret;
-
-	bsize = sizeof(*cmsg);
-
-	xlog(D_GENERAL, "%s: downcall with status %d", __func__,
-			cmsg->cm_status);
-	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
-	if (wsize != bsize) {
-		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
-			 __func__, wsize);
-		ret = cld_pipe_open(clnt);
-		if (ret) {
-			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
-					__func__, ret);
-			exit(ret);
-		}
-	}
-}
-
-static void
-cld_check(struct cld_client *clnt)
-{
-	int ret;
-	ssize_t bsize, wsize;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	xlog(D_GENERAL, "%s: check client record", __func__);
-
-	ret = sqlite_check_client(cmsg->cm_u.cm_name.cn_id,
-				  cmsg->cm_u.cm_name.cn_len);
-
-	/* set up reply */
-	cmsg->cm_status = ret ? -EACCES : ret;
-
-	bsize = sizeof(*cmsg);
-
-	xlog(D_GENERAL, "%s: downcall with status %d", __func__,
-			cmsg->cm_status);
-	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
-	if (wsize != bsize) {
-		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
-			 __func__, wsize);
-		ret = cld_pipe_open(clnt);
-		if (ret) {
-			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
-					__func__, ret);
-			exit(ret);
-		}
-	}
-}
-
-static void
-cld_gracedone(struct cld_client *clnt)
-{
-	int ret;
-	ssize_t bsize, wsize;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	xlog(D_GENERAL, "%s: grace done. cm_gracetime=%ld", __func__,
-			cmsg->cm_u.cm_gracetime);
-
-	ret = sqlite_remove_unreclaimed(cmsg->cm_u.cm_gracetime);
-
-	/* set up reply: downcall with 0 status */
-	cmsg->cm_status = ret ? -EREMOTEIO : ret;
-
-	bsize = sizeof(*cmsg);
-
-	xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status);
-	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
-	if (wsize != bsize) {
-		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
-			 __func__, wsize);
-		ret = cld_pipe_open(clnt);
-		if (ret) {
-			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
-					__func__, ret);
-			exit(ret);
-		}
-	}
-}
-
-static void
-cldcb(int UNUSED(fd), short which, void *data)
-{
-	ssize_t len;
-	struct cld_client *clnt = data;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	if (which != EV_READ)
-		goto out;
-
-	len = atomicio(read, clnt->cl_fd, cmsg, sizeof(*cmsg));
-	if (len <= 0) {
-		xlog(L_ERROR, "%s: pipe read failed: %m", __func__);
-		cld_pipe_open(clnt);
-		goto out;
-	}
-
-	if (cmsg->cm_vers != UPCALL_VERSION) {
-		xlog(L_ERROR, "%s: unsupported upcall version: %hu",
-				cmsg->cm_vers);
-		cld_pipe_open(clnt);
-		goto out;
-	}
-
-	switch(cmsg->cm_cmd) {
-	case Cld_Create:
-		cld_create(clnt);
-		break;
-	case Cld_Remove:
-		cld_remove(clnt);
-		break;
-	case Cld_Check:
-		cld_check(clnt);
-		break;
-	case Cld_GraceDone:
-		cld_gracedone(clnt);
-		break;
-	default:
-		xlog(L_WARNING, "%s: command %u is not yet implemented",
-				__func__, cmsg->cm_cmd);
-		cld_not_implemented(clnt);
-	}
-out:
-	event_add(&clnt->cl_event, NULL);
-}
-
-int
-main(int argc, char **argv)
-{
-	char arg;
-	int rc = 0;
-	bool foreground = false;
-	char *progname;
-	char *storagedir = CLD_DEFAULT_STORAGEDIR;
-	struct cld_client clnt;
-
-	memset(&clnt, 0, sizeof(clnt));
-
-	progname = strdup(basename(argv[0]));
-	if (!progname) {
-		fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]);
-		return 1;
-	}
-
-	event_init();
-	xlog_syslog(0);
-	xlog_stderr(1);
-
-	/* process command-line options */
-	while ((arg = getopt_long(argc, argv, "hdFp:s:", longopts,
-				  NULL)) != EOF) {
-		switch (arg) {
-		case 'd':
-			xlog_config(D_ALL, 1);
-			break;
-		case 'F':
-			foreground = true;
-			break;
-		case 'p':
-			pipepath = optarg;
-			break;
-		case 's':
-			storagedir = optarg;
-			break;
-		default:
-			usage(progname);
-			return 0;
-		}
-	}
-
-
-	xlog_open(progname);
-	if (!foreground) {
-		xlog_syslog(1);
-		xlog_stderr(0);
-		rc = daemon(0, 0);
-		if (rc) {
-			xlog(L_ERROR, "Unable to daemonize: %m");
-			goto out;
-		}
-	}
-
-	/* drop all capabilities */
-	rc = cld_set_caps();
-	if (rc)
-		goto out;
-
-	/*
-	 * now see if the storagedir is writable by root w/o CAP_DAC_OVERRIDE.
-	 * If it isn't then give the user a warning but proceed as if
-	 * everything is OK. If the DB has already been created, then
-	 * everything might still work. If it doesn't exist at all, then
-	 * assume that the maindb init will be able to create it. Fail on
-	 * anything else.
-	 */
-	if (access(storagedir, W_OK) == -1) {
-		switch (errno) {
-		case EACCES:
-			xlog(L_WARNING, "Storage directory %s is not writable. "
-					"Should be owned by root and writable "
-					"by owner!", storagedir);
-			break;
-		case ENOENT:
-			/* ignore and assume that we can create dir as root */
-			break;
-		default:
-			xlog(L_ERROR, "Unexpected error when checking access "
-				      "on %s: %m", storagedir);
-			rc = -errno;
-			goto out;
-		}
-	}
-
-	/* set up storage db */
-	rc = sqlite_maindb_init(storagedir);
-	if (rc) {
-		xlog(L_ERROR, "Failed to open main database: %d", rc);
-		goto out;
-	}
-
-	/* set up event handler */
-	rc = cld_pipe_init(&clnt);
-	if (rc)
-		goto out;
-
-	xlog(D_GENERAL, "%s: Starting event dispatch handler.", __func__);
-	rc = event_dispatch();
-	if (rc < 0)
-		xlog(L_ERROR, "%s: event_dispatch failed: %m", __func__);
-
-	close(clnt.cl_fd);
-	close(inotify_fd);
-out:
-	free(progname);
-	return rc;
-}
diff --git a/utils/nfsdcld/nfsdcld.man b/utils/nfsdcld/nfsdcld.man
deleted file mode 100644
index 9ddaf64..0000000
--- a/utils/nfsdcld/nfsdcld.man
+++ /dev/null
@@ -1,185 +0,0 @@
-.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.13)
-.\"
-.\" Standard preamble:
-.\" ========================================================================
-.de Sp \" Vertical space (when we can't use .PP)
-.if t .sp .5v
-.if n .sp
-..
-.de Vb \" Begin verbatim text
-.ft CW
-.nf
-.ne \\$1
-..
-.de Ve \" End verbatim text
-.ft R
-.fi
-..
-.\" Set up some character translations and predefined strings.  \*(-- will
-.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
-.\" double quote, and \*(R" will give a right double quote.  \*(C+ will
-.\" give a nicer C++.  Capital omega is used to do unbreakable dashes and
-.\" therefore won't be available.  \*(C` and \*(C' expand to `' in nroff,
-.\" nothing in troff, for use with C<>.
-.tr \(*W-
-.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
-.ie n \{\
-.    ds -- \(*W-
-.    ds PI pi
-.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
-.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
-.    ds L" ""
-.    ds R" ""
-.    ds C` ""
-.    ds C' ""
-'br\}
-.el\{\
-.    ds -- \|\(em\|
-.    ds PI \(*p
-.    ds L" ``
-.    ds R" ''
-'br\}
-.\"
-.\" Escape single quotes in literal strings from groff's Unicode transform.
-.ie \n(.g .ds Aq \(aq
-.el       .ds Aq '
-.\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
-.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
-.\" entries marked with X<> in POD.  Of course, you'll have to process the
-.\" output yourself in some meaningful fashion.
-.ie \nF \{\
-.    de IX
-.    tm Index:\\$1\t\\n%\t"\\$2"
-..
-.    nr % 0
-.    rr F
-.\}
-.el \{\
-.    de IX
-..
-.\}
-.\"
-.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
-.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
-.    \" fudge factors for nroff and troff
-.if n \{\
-.    ds #H 0
-.    ds #V .8m
-.    ds #F .3m
-.    ds #[ \f1
-.    ds #] \fP
-.\}
-.if t \{\
-.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
-.    ds #V .6m
-.    ds #F 0
-.    ds #[ \&
-.    ds #] \&
-.\}
-.    \" simple accents for nroff and troff
-.if n \{\
-.    ds ' \&
-.    ds ` \&
-.    ds ^ \&
-.    ds , \&
-.    ds ~ ~
-.    ds /
-.\}
-.if t \{\
-.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
-.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
-.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
-.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
-.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
-.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
-.\}
-.    \" troff and (daisy-wheel) nroff accents
-.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
-.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
-.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
-.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
-.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
-.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
-.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
-.ds ae a\h'-(\w'a'u*4/10)'e
-.ds Ae A\h'-(\w'A'u*4/10)'E
-.    \" corrections for vroff
-.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
-.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
-.    \" for low resolution devices (crt and lpr)
-.if \n(.H>23 .if \n(.V>19 \
-\{\
-.    ds : e
-.    ds 8 ss
-.    ds o a
-.    ds d- d\h'-1'\(ga
-.    ds D- D\h'-1'\(hy
-.    ds th \o'bp'
-.    ds Th \o'LP'
-.    ds ae ae
-.    ds Ae AE
-.\}
-.rm #[ #] #H #V #F C
-.\" ========================================================================
-.\"
-.IX Title "NFSDCLD 8"
-.TH NFSDCLD 8 "2011-12-21" "" ""
-.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
-.\" way too many mistakes in technical documents.
-.if n .ad l
-.nh
-.SH "NAME"
-nfsdcld \- NFSv4 Client Tracking Daemon
-.SH "SYNOPSIS"
-.IX Header "SYNOPSIS"
-nfsdcld [\-d] [\-F] [\-p path] [\-s stable storage dir]
-.SH "DESCRIPTION"
-.IX Header "DESCRIPTION"
-nfsdcld is the NFSv4 client tracking daemon. It is not necessary to run
-this daemon on machines that are not acting as NFSv4 servers.
-.PP
-When a network partition is combined with a server reboot, there are
-edge conditions that can cause the server to grant lock reclaims when
-other clients have taken conflicting locks in the interim. A more detailed
-explanation of this issue is described in \s-1RFC\s0 3530, section 8.6.3.
-.PP
-In order to prevent these problems, the server must track a small amount
-of per-client information on stable storage. This daemon provides the
-userspace piece of that functionality.
-.SH "OPTIONS"
-.IX Header "OPTIONS"
-.IP "\fB\-d\fR, \fB\-\-debug\fR" 4
-.IX Item "-d, --debug"
-Enable debug level logging.
-.IP "\fB\-F\fR, \fB\-\-foreground\fR" 4
-.IX Item "-F, --foreground"
-Runs the daemon in the foreground and prints all output to stderr
-.IP "\fB\-p\fR \fIpipe\fR, \fB\-\-pipe\fR=\fIpipe\fR" 4
-.IX Item "-p pipe, --pipe=pipe"
-Location of the \*(L"cld\*(R" upcall pipe. The default value is
-\&\fI/var/lib/nfs/rpc_pipefs/nfsd/cld\fR. If the pipe does not exist when the
-daemon starts then it will wait for it to be created.
-.IP "\fB\-s\fR \fIstorage_dir\fR, \fB\-\-storagedir\fR=\fIstorage_dir\fR" 4
-.IX Item "-s storagedir, --storagedir=storage_dir"
-Directory where stable storage information should be kept. The default
-value is \fI/var/lib/nfs/nfsdcld\fR.
-.SH "NOTES"
-.IX Header "NOTES"
-The Linux kernel NFSv4 server has historically tracked this information
-on stable storage by manipulating information on the filesystem
-directly, in the directory to which \fI/proc/fs/nfsd/nfsv4recoverydir\fR
-points.
-.PP
-This daemon requires a kernel that supports the nfsdcld upcall. If the
-kernel does not support the new upcall, or is using the legacy client
-name tracking code then it will not create the pipe that nfsdcld uses to
-talk to the kernel.
-.PP
-This daemon should be run as root, as the pipe that it uses to communicate
-with the kernel is only accessable by root. The daemon however does drop all
-superuser capabilities after starting. Because of this, the \fIstoragedir\fR
-should be owned by root, and be readable and writable by owner.
-.SH "AUTHORS"
-.IX Header "AUTHORS"
-The nfsdcld daemon was developed by Jeff Layton <jlayton@redhat.com>.
diff --git a/utils/nfsdcld/sqlite.c b/utils/nfsdcld/sqlite.c
deleted file mode 100644
index fc882c6..0000000
--- a/utils/nfsdcld/sqlite.c
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * Copyright (C) 2011  Red Hat, Jeff Layton <jlayton@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-/*
- * Explanation:
- *
- * This file contains the code to manage the sqlite backend database for the
- * clstated upcall daemon.
- *
- * The main database is called main.sqlite and contains the following tables:
- *
- * parameters: simple key/value pairs for storing database info
- *
- * clients: one column containing a BLOB with the as sent by the client
- * 	    and a timestamp (in epoch seconds) of when the record was
- * 	    established
- *
- * FIXME: should we also record the fsid being accessed?
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <dirent.h>
-#include <errno.h>
-#include <event.h>
-#include <stdbool.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sqlite3.h>
-#include <linux/limits.h>
-
-#include "xlog.h"
-
-#define CLD_SQLITE_SCHEMA_VERSION 1
-
-/* in milliseconds */
-#define CLD_SQLITE_BUSY_TIMEOUT 10000
-
-/* private data structures */
-
-/* global variables */
-
-/* top level DB directory */
-static char *sqlite_topdir;
-
-/* reusable pathname and sql command buffer */
-static char buf[PATH_MAX];
-
-/* global database handle */
-static sqlite3 *dbh;
-
-/* forward declarations */
-
-/* make a directory, ignoring EEXIST errors unless it's not a directory */
-static int
-mkdir_if_not_exist(char *dirname)
-{
-	int ret;
-	struct stat statbuf;
-
-	ret = mkdir(dirname, S_IRWXU);
-	if (ret && errno != EEXIST)
-		return -errno;
-
-	ret = stat(dirname, &statbuf);
-	if (ret)
-		return -errno;
-
-	if (!S_ISDIR(statbuf.st_mode))
-		ret = -ENOTDIR;
-
-	return ret;
-}
-
-/*
- * Open the "main" database, and attempt to initialize it by creating the
- * parameters table and inserting the schema version into it. Ignore any errors
- * from that, and then attempt to select the version out of it again. If the
- * version appears wrong, then assume that the DB is corrupt or has been
- * upgraded, and return an error. If all of that works, then attempt to create
- * the "clients" table.
- */
-int
-sqlite_maindb_init(char *topdir)
-{
-	int ret;
-	char *err = NULL;
-	sqlite3_stmt *stmt = NULL;
-
-	sqlite_topdir = topdir;
-
-	ret = mkdir_if_not_exist(sqlite_topdir);
-	if (ret)
-		return ret;
-
-	ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", sqlite_topdir);
-	if (ret < 0)
-		return ret;
-
-	buf[PATH_MAX - 1] = '\0';
-
-	ret = sqlite3_open(buf, &dbh);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "Unable to open main database: %d", ret);
-		return ret;
-	}
-
-	ret = sqlite3_busy_timeout(dbh, CLD_SQLITE_BUSY_TIMEOUT);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "Unable to set sqlite busy timeout: %d", ret);
-		goto out_err;
-	}
-
-	/* Try to create table */
-	ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS parameters "
-				"(key TEXT PRIMARY KEY, value TEXT);",
-				NULL, NULL, &err);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "Unable to create parameter table: %d", ret);
-		goto out_err;
-	}
-
-	/* insert version into table -- ignore error if it fails */
-	ret = snprintf(buf, sizeof(buf),
-		       "INSERT OR IGNORE INTO parameters values (\"version\", "
-		       "\"%d\");", CLD_SQLITE_SCHEMA_VERSION);
-	if (ret < 0) {
-		goto out_err;
-	} else if ((size_t)ret >= sizeof(buf)) {
-		ret = -EINVAL;
-		goto out_err;
-	}
-
-	ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "Unable to insert into parameter table: %d",
-				ret);
-		goto out_err;
-	}
-
-	ret = sqlite3_prepare_v2(dbh,
-		"SELECT value FROM parameters WHERE key == \"version\";",
-		 -1, &stmt, NULL);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "Unable to prepare select statement: %d", ret);
-		goto out_err;
-	}
-
-	/* check schema version */
-	ret = sqlite3_step(stmt);
-	if (ret != SQLITE_ROW) {
-		xlog(L_ERROR, "Select statement execution failed: %s",
-				sqlite3_errmsg(dbh));
-		goto out_err;
-	}
-
-	/* process SELECT result */
-	ret = sqlite3_column_int(stmt, 0);
-	if (ret != CLD_SQLITE_SCHEMA_VERSION) {
-		xlog(L_ERROR, "Unsupported database schema version! "
-			"Expected %d, got %d.",
-			CLD_SQLITE_SCHEMA_VERSION, ret);
-		ret = -EINVAL;
-		goto out_err;
-	}
-
-	/* now create the "clients" table */
-	ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS clients "
-				"(id BLOB PRIMARY KEY, time INTEGER);",
-				NULL, NULL, &err);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "Unable to create clients table: %s", err);
-		goto out_err;
-	}
-
-	sqlite3_free(err);
-	sqlite3_finalize(stmt);
-	return 0;
-
-out_err:
-	if (err) {
-		xlog(L_ERROR, "sqlite error: %s", err);
-		sqlite3_free(err);
-	}
-	sqlite3_finalize(stmt);
-	sqlite3_close(dbh);
-	return ret;
-}
-
-/*
- * Create a client record
- *
- * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0)
- */
-int
-sqlite_insert_client(const unsigned char *clname, const size_t namelen)
-{
-	int ret;
-	sqlite3_stmt *stmt = NULL;
-
-	ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients VALUES "
-				      "(?, strftime('%s', 'now'));", -1,
-					&stmt, NULL);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "%s: insert statement prepare failed: %s",
-			__func__, sqlite3_errmsg(dbh));
-		return ret;
-	}
-
-	ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
-				SQLITE_STATIC);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
-				sqlite3_errmsg(dbh));
-		goto out_err;
-	}
-
-	ret = sqlite3_step(stmt);
-	if (ret == SQLITE_DONE)
-		ret = SQLITE_OK;
-	else
-		xlog(L_ERROR, "%s: unexpected return code from insert: %s",
-				__func__, sqlite3_errmsg(dbh));
-
-out_err:
-	xlog(D_GENERAL, "%s: returning %d", __func__, ret);
-	sqlite3_finalize(stmt);
-	return ret;
-}
-
-/* Remove a client record */
-int
-sqlite_remove_client(const unsigned char *clname, const size_t namelen)
-{
-	int ret;
-	sqlite3_stmt *stmt = NULL;
-
-	ret = sqlite3_prepare_v2(dbh, "DELETE FROM clients WHERE id==?", -1,
-				 &stmt, NULL);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "%s: statement prepare failed: %s",
-				__func__, sqlite3_errmsg(dbh));
-		goto out_err;
-	}
-
-	ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
-				SQLITE_STATIC);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
-				sqlite3_errmsg(dbh));
-		goto out_err;
-	}
-
-	ret = sqlite3_step(stmt);
-	if (ret == SQLITE_DONE)
-		ret = SQLITE_OK;
-	else
-		xlog(L_ERROR, "%s: unexpected return code from delete: %d",
-				__func__, ret);
-
-out_err:
-	xlog(D_GENERAL, "%s: returning %d", __func__, ret);
-	sqlite3_finalize(stmt);
-	return ret;
-}
-
-/*
- * Is the given clname in the clients table? If so, then update its timestamp
- * and return success. If the record isn't present, or the update fails, then
- * return an error.
- */
-int
-sqlite_check_client(const unsigned char *clname, const size_t namelen)
-{
-	int ret;
-	sqlite3_stmt *stmt = NULL;
-
-	ret = sqlite3_prepare_v2(dbh, "SELECT count(*) FROM clients WHERE "
-				      "id==?", -1, &stmt, NULL);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "%s: unable to prepare update statement: %s",
-				__func__, sqlite3_errmsg(dbh));
-		goto out_err;
-	}
-
-	ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
-				SQLITE_STATIC);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "%s: bind blob failed: %s",
-				__func__, sqlite3_errmsg(dbh));
-		goto out_err;
-	}
-
-	ret = sqlite3_step(stmt);
-	if (ret != SQLITE_ROW) {
-		xlog(L_ERROR, "%s: unexpected return code from select: %d",
-				__func__, ret);
-		goto out_err;
-	}
-
-	ret = sqlite3_column_int(stmt, 0);
-	xlog(D_GENERAL, "%s: select returned %d rows", __func__, ret);
-	if (ret != 1) {
-		ret = -EACCES;
-		goto out_err;
-	}
-
-	sqlite3_finalize(stmt);
-	stmt = NULL;
-	ret = sqlite3_prepare_v2(dbh, "UPDATE OR FAIL clients SET "
-				      "time=strftime('%s', 'now') WHERE id==?",
-				 -1, &stmt, NULL);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "%s: unable to prepare update statement: %s",
-				__func__, sqlite3_errmsg(dbh));
-		goto out_err;
-	}
-
-	ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
-				SQLITE_STATIC);
-	if (ret != SQLITE_OK) {
-		xlog(L_ERROR, "%s: bind blob failed: %s",
-				__func__, sqlite3_errmsg(dbh));
-		goto out_err;
-	}
-
-	ret = sqlite3_step(stmt);
-	if (ret == SQLITE_DONE)
-		ret = SQLITE_OK;
-	else
-		xlog(L_ERROR, "%s: unexpected return code from update: %s",
-				__func__, sqlite3_errmsg(dbh));
-
-out_err:
-	xlog(D_GENERAL, "%s: returning %d", __func__, ret);
-	sqlite3_finalize(stmt);
-	return ret;
-}
-
-/*
- * remove any client records that were not reclaimed since grace_start.
- */
-int
-sqlite_remove_unreclaimed(time_t grace_start)
-{
-	int ret;
-	char *err = NULL;
-
-	ret = snprintf(buf, sizeof(buf), "DELETE FROM clients WHERE time < %ld",
-			grace_start);
-	if (ret < 0) {
-		return ret;
-	} else if ((size_t)ret >= sizeof(buf)) {
-		ret = -EINVAL;
-		return ret;
-	}
-
-	ret = sqlite3_exec(dbh, buf, NULL, NULL, &err);
-	if (ret != SQLITE_OK)
-		xlog(L_ERROR, "%s: delete failed: %s", __func__, err);
-
-	xlog(D_GENERAL, "%s: returning %d", __func__, ret);
-	sqlite3_free(err);
-	return ret;
-}
diff --git a/utils/nfsdcld/sqlite.h b/utils/nfsdcld/sqlite.h
deleted file mode 100644
index c85e7d6..0000000
--- a/utils/nfsdcld/sqlite.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2011  Red Hat, Jeff Layton <jlayton@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef _SQLITE_H_
-#define _SQLITE_H_
-
-int sqlite_maindb_init(char *topdir);
-int sqlite_insert_client(const unsigned char *clname, const size_t namelen);
-int sqlite_remove_client(const unsigned char *clname, const size_t namelen);
-int sqlite_check_client(const unsigned char *clname, const size_t namelen);
-int sqlite_remove_unreclaimed(const time_t grace_start);
-
-#endif /* _SQLITE_H */
diff --git a/utils/nfsdcltrack/Makefile.am b/utils/nfsdcltrack/Makefile.am
new file mode 100644
index 0000000..073a71b
--- /dev/null
+++ b/utils/nfsdcltrack/Makefile.am
@@ -0,0 +1,14 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS	= nfsdcld.man
+EXTRA_DIST	= $(man8_MANS)
+
+AM_CFLAGS	+= -D_LARGEFILE64_SOURCE
+sbin_PROGRAMS	= nfsdcld
+
+nfsdcld_SOURCES = nfsdcld.c sqlite.c
+
+nfsdcld_LDADD = ../../support/nfs/libnfs.a $(LIBEVENT) $(LIBSQLITE) $(LIBCAP)
+
+MAINTAINERCLEANFILES = Makefile.in
+
diff --git a/utils/nfsdcltrack/nfsdcld.c b/utils/nfsdcltrack/nfsdcld.c
new file mode 100644
index 0000000..473d069
--- /dev/null
+++ b/utils/nfsdcltrack/nfsdcld.c
@@ -0,0 +1,607 @@
+/*
+ * nfsdcld.c -- NFSv4 client name tracking daemon
+ *
+ * Copyright (C) 2011  Red Hat, Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <errno.h>
+#include <event.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/inotify.h>
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
+#include "xlog.h"
+#include "nfslib.h"
+#include "cld.h"
+#include "sqlite.h"
+
+#ifndef PIPEFS_DIR
+#define PIPEFS_DIR NFS_STATEDIR "/rpc_pipefs"
+#endif
+
+#define DEFAULT_CLD_PATH	PIPEFS_DIR "/nfsd/cld"
+
+#ifndef CLD_DEFAULT_STORAGEDIR
+#define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcld"
+#endif
+
+#define UPCALL_VERSION		1
+
+/* private data structures */
+struct cld_client {
+	int			cl_fd;
+	struct event		cl_event;
+	struct cld_msg	cl_msg;
+};
+
+/* global variables */
+static char *pipepath = DEFAULT_CLD_PATH;
+static int 		inotify_fd = -1;
+static struct event	pipedir_event;
+
+static struct option longopts[] =
+{
+	{ "help", 0, NULL, 'h' },
+	{ "foreground", 0, NULL, 'F' },
+	{ "debug", 0, NULL, 'd' },
+	{ "pipe", 1, NULL, 'p' },
+	{ "storagedir", 1, NULL, 's' },
+	{ NULL, 0, 0, 0 },
+};
+
+/* forward declarations */
+static void cldcb(int UNUSED(fd), short which, void *data);
+
+static void
+usage(char *progname)
+{
+	printf("%s [ -hFd ] [ -p pipe ] [ -s dir ]\n", progname);
+}
+
+static int
+cld_set_caps(void)
+{
+	int ret = 0;
+#ifdef HAVE_SYS_CAPABILITY_H
+	unsigned long i;
+	cap_t caps;
+
+	if (getuid() != 0) {
+		xlog(L_ERROR, "Not running as root. Daemon won't be able to "
+			      "open the pipe after dropping capabilities!");
+		return -EINVAL;
+	}
+
+	/* prune the bounding set to nothing */
+	for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0 ; ++i) {
+		ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+		if (ret) {
+			xlog(L_ERROR, "Unable to prune capability %lu from "
+				      "bounding set: %m", i);
+			return -errno;
+		}
+	}
+
+	/* get a blank capset */
+	caps = cap_init();
+	if (caps == NULL) {
+		xlog(L_ERROR, "Unable to get blank capability set: %m");
+		return -errno;
+	}
+
+	/* reset the process capabilities */
+	if (cap_set_proc(caps) != 0) {
+		xlog(L_ERROR, "Unable to set process capabilities: %m");
+		ret = -errno;
+	}
+	cap_free(caps);
+#endif
+	return ret;
+}
+
+#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX)
+
+static int
+cld_pipe_open(struct cld_client *clnt)
+{
+	int fd;
+
+	xlog(D_GENERAL, "%s: opening upcall pipe %s", __func__, pipepath);
+	fd = open(pipepath, O_RDWR, 0);
+	if (fd < 0) {
+		xlog(D_GENERAL, "%s: open of %s failed: %m", __func__, pipepath);
+		return -errno;
+	}
+
+	if (clnt->cl_event.ev_flags & EVLIST_INIT)
+		event_del(&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);
+	/* event_add is done by the caller */
+	return 0;
+}
+
+static void
+cld_inotify_cb(int UNUSED(fd), short which, void *data)
+{
+	int ret;
+	size_t elen;
+	ssize_t rret;
+	char evbuf[INOTIFY_EVENT_MAX];
+	char *dirc = NULL, *pname;
+	struct inotify_event *event = (struct inotify_event *)evbuf;
+	struct cld_client *clnt = data;
+
+	if (which != EV_READ)
+		return;
+
+	xlog(D_GENERAL, "%s: called for EV_READ", __func__);
+
+	dirc = strndup(pipepath, PATH_MAX);
+	if (!dirc) {
+		xlog(L_ERROR, "%s: unable to allocate memory", __func__);
+		goto out;
+	}
+
+	rret = read(inotify_fd, evbuf, INOTIFY_EVENT_MAX);
+	if (rret < 0) {
+		xlog(L_ERROR, "%s: read from inotify fd failed: %m", __func__);
+		goto out;
+	}
+
+	/* check to see if we have a filename in the evbuf */
+	if (!event->len) {
+		xlog(D_GENERAL, "%s: no filename in inotify event", __func__);
+		goto out;
+	}
+
+	pname = basename(dirc);
+	elen = strnlen(event->name, event->len);
+
+	/* does the filename match our pipe? */
+	if (strlen(pname) != elen || memcmp(pname, event->name, elen)) {
+		xlog(D_GENERAL, "%s: wrong filename (%s)", __func__,
+				event->name);
+		goto out;
+	}
+
+	ret = cld_pipe_open(clnt);
+	switch (ret) {
+	case 0:
+		/* readd the event for the cl_event pipe */
+		event_add(&clnt->cl_event, NULL);
+		break;
+	case -ENOENT:
+		/* pipe must have disappeared, wait for it to come back */
+		goto out;
+	default:
+		/* anything else is fatal */
+		xlog(L_FATAL, "%s: unable to open new pipe (%d). Aborting.",
+			ret, __func__);
+		exit(ret);
+	}
+
+out:
+	event_add(&pipedir_event, NULL);
+	free(dirc);
+}
+
+static int
+cld_inotify_setup(void)
+{
+	int ret;
+	char *dirc, *dname;
+
+	dirc = strndup(pipepath, PATH_MAX);
+	if (!dirc) {
+		xlog_err("%s: unable to allocate memory", __func__);
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	dname = dirname(dirc);
+
+	inotify_fd = inotify_init();
+	if (inotify_fd < 0) {
+		xlog_err("%s: inotify_init failed: %m", __func__);
+		ret = -errno;
+		goto out_free;
+	}
+
+	ret = inotify_add_watch(inotify_fd, dname, IN_CREATE);
+	if (ret < 0) {
+		xlog_err("%s: inotify_add_watch failed: %m", __func__);
+		ret = -errno;
+		goto out_err;
+	}
+
+out_free:
+	free(dirc);
+	return 0;
+out_err:
+	close(inotify_fd);
+	goto out_free;
+}
+
+/*
+ * Set an inotify watch on the directory that should contain the pipe, and then
+ * try to open it. If it fails with anything but -ENOENT, return the error
+ * immediately.
+ *
+ * If it succeeds, then set up the pipe event handler. At that point, set up
+ * the inotify event handler and go ahead and return success.
+ */
+static int
+cld_pipe_init(struct cld_client *clnt)
+{
+	int ret;
+
+	xlog(D_GENERAL, "%s: init pipe handlers", __func__);
+
+	ret = cld_inotify_setup();
+	if (ret != 0)
+		goto out;
+
+	clnt->cl_fd = -1;
+	ret = cld_pipe_open(clnt);
+	switch (ret) {
+	case 0:
+		/* add the event and we're good to go */
+		event_add(&clnt->cl_event, NULL);
+		break;
+	case -ENOENT:
+		/* ignore this error -- cld_inotify_cb will handle it */
+		ret = 0;
+		break;
+	default:
+		/* anything else is fatal */
+		close(inotify_fd);
+		goto out;
+	}
+
+	/* set event for inotify read */
+	event_set(&pipedir_event, inotify_fd, EV_READ, cld_inotify_cb, clnt);
+	event_add(&pipedir_event, NULL);
+out:
+	return ret;
+}
+
+static void
+cld_not_implemented(struct cld_client *clnt)
+{
+	int ret;
+	ssize_t bsize, wsize;
+	struct cld_msg *cmsg = &clnt->cl_msg;
+
+	xlog(D_GENERAL, "%s: downcalling with not implemented error", __func__);
+
+	/* set up reply */
+	cmsg->cm_status = -EOPNOTSUPP;
+
+	bsize = sizeof(*cmsg);
+
+	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+	if (wsize != bsize)
+		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
+			 __func__, wsize);
+
+	/* reopen pipe, just to be sure */
+	ret = cld_pipe_open(clnt);
+	if (ret) {
+		xlog(L_FATAL, "%s: unable to reopen pipe: %d", __func__, ret);
+		exit(ret);
+	}
+}
+
+static void
+cld_create(struct cld_client *clnt)
+{
+	int ret;
+	ssize_t bsize, wsize;
+	struct cld_msg *cmsg = &clnt->cl_msg;
+
+	xlog(D_GENERAL, "%s: create client record.", __func__);
+
+	ret = sqlite_insert_client(cmsg->cm_u.cm_name.cn_id,
+				   cmsg->cm_u.cm_name.cn_len);
+
+	cmsg->cm_status = ret ? -EREMOTEIO : ret;
+
+	bsize = sizeof(*cmsg);
+
+	xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status);
+	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+	if (wsize != bsize) {
+		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
+			 __func__, wsize);
+		ret = cld_pipe_open(clnt);
+		if (ret) {
+			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
+					__func__, ret);
+			exit(ret);
+		}
+	}
+}
+
+static void
+cld_remove(struct cld_client *clnt)
+{
+	int ret;
+	ssize_t bsize, wsize;
+	struct cld_msg *cmsg = &clnt->cl_msg;
+
+	xlog(D_GENERAL, "%s: remove client record.", __func__);
+
+	ret = sqlite_remove_client(cmsg->cm_u.cm_name.cn_id,
+				   cmsg->cm_u.cm_name.cn_len);
+
+	cmsg->cm_status = ret ? -EREMOTEIO : ret;
+
+	bsize = sizeof(*cmsg);
+
+	xlog(D_GENERAL, "%s: downcall with status %d", __func__,
+			cmsg->cm_status);
+	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+	if (wsize != bsize) {
+		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
+			 __func__, wsize);
+		ret = cld_pipe_open(clnt);
+		if (ret) {
+			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
+					__func__, ret);
+			exit(ret);
+		}
+	}
+}
+
+static void
+cld_check(struct cld_client *clnt)
+{
+	int ret;
+	ssize_t bsize, wsize;
+	struct cld_msg *cmsg = &clnt->cl_msg;
+
+	xlog(D_GENERAL, "%s: check client record", __func__);
+
+	ret = sqlite_check_client(cmsg->cm_u.cm_name.cn_id,
+				  cmsg->cm_u.cm_name.cn_len);
+
+	/* set up reply */
+	cmsg->cm_status = ret ? -EACCES : ret;
+
+	bsize = sizeof(*cmsg);
+
+	xlog(D_GENERAL, "%s: downcall with status %d", __func__,
+			cmsg->cm_status);
+	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+	if (wsize != bsize) {
+		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
+			 __func__, wsize);
+		ret = cld_pipe_open(clnt);
+		if (ret) {
+			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
+					__func__, ret);
+			exit(ret);
+		}
+	}
+}
+
+static void
+cld_gracedone(struct cld_client *clnt)
+{
+	int ret;
+	ssize_t bsize, wsize;
+	struct cld_msg *cmsg = &clnt->cl_msg;
+
+	xlog(D_GENERAL, "%s: grace done. cm_gracetime=%ld", __func__,
+			cmsg->cm_u.cm_gracetime);
+
+	ret = sqlite_remove_unreclaimed(cmsg->cm_u.cm_gracetime);
+
+	/* set up reply: downcall with 0 status */
+	cmsg->cm_status = ret ? -EREMOTEIO : ret;
+
+	bsize = sizeof(*cmsg);
+
+	xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status);
+	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+	if (wsize != bsize) {
+		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
+			 __func__, wsize);
+		ret = cld_pipe_open(clnt);
+		if (ret) {
+			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
+					__func__, ret);
+			exit(ret);
+		}
+	}
+}
+
+static void
+cldcb(int UNUSED(fd), short which, void *data)
+{
+	ssize_t len;
+	struct cld_client *clnt = data;
+	struct cld_msg *cmsg = &clnt->cl_msg;
+
+	if (which != EV_READ)
+		goto out;
+
+	len = atomicio(read, clnt->cl_fd, cmsg, sizeof(*cmsg));
+	if (len <= 0) {
+		xlog(L_ERROR, "%s: pipe read failed: %m", __func__);
+		cld_pipe_open(clnt);
+		goto out;
+	}
+
+	if (cmsg->cm_vers != UPCALL_VERSION) {
+		xlog(L_ERROR, "%s: unsupported upcall version: %hu",
+				cmsg->cm_vers);
+		cld_pipe_open(clnt);
+		goto out;
+	}
+
+	switch(cmsg->cm_cmd) {
+	case Cld_Create:
+		cld_create(clnt);
+		break;
+	case Cld_Remove:
+		cld_remove(clnt);
+		break;
+	case Cld_Check:
+		cld_check(clnt);
+		break;
+	case Cld_GraceDone:
+		cld_gracedone(clnt);
+		break;
+	default:
+		xlog(L_WARNING, "%s: command %u is not yet implemented",
+				__func__, cmsg->cm_cmd);
+		cld_not_implemented(clnt);
+	}
+out:
+	event_add(&clnt->cl_event, NULL);
+}
+
+int
+main(int argc, char **argv)
+{
+	char arg;
+	int rc = 0;
+	bool foreground = false;
+	char *progname;
+	char *storagedir = CLD_DEFAULT_STORAGEDIR;
+	struct cld_client clnt;
+
+	memset(&clnt, 0, sizeof(clnt));
+
+	progname = strdup(basename(argv[0]));
+	if (!progname) {
+		fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]);
+		return 1;
+	}
+
+	event_init();
+	xlog_syslog(0);
+	xlog_stderr(1);
+
+	/* process command-line options */
+	while ((arg = getopt_long(argc, argv, "hdFp:s:", longopts,
+				  NULL)) != EOF) {
+		switch (arg) {
+		case 'd':
+			xlog_config(D_ALL, 1);
+			break;
+		case 'F':
+			foreground = true;
+			break;
+		case 'p':
+			pipepath = optarg;
+			break;
+		case 's':
+			storagedir = optarg;
+			break;
+		default:
+			usage(progname);
+			return 0;
+		}
+	}
+
+
+	xlog_open(progname);
+	if (!foreground) {
+		xlog_syslog(1);
+		xlog_stderr(0);
+		rc = daemon(0, 0);
+		if (rc) {
+			xlog(L_ERROR, "Unable to daemonize: %m");
+			goto out;
+		}
+	}
+
+	/* drop all capabilities */
+	rc = cld_set_caps();
+	if (rc)
+		goto out;
+
+	/*
+	 * now see if the storagedir is writable by root w/o CAP_DAC_OVERRIDE.
+	 * If it isn't then give the user a warning but proceed as if
+	 * everything is OK. If the DB has already been created, then
+	 * everything might still work. If it doesn't exist at all, then
+	 * assume that the maindb init will be able to create it. Fail on
+	 * anything else.
+	 */
+	if (access(storagedir, W_OK) == -1) {
+		switch (errno) {
+		case EACCES:
+			xlog(L_WARNING, "Storage directory %s is not writable. "
+					"Should be owned by root and writable "
+					"by owner!", storagedir);
+			break;
+		case ENOENT:
+			/* ignore and assume that we can create dir as root */
+			break;
+		default:
+			xlog(L_ERROR, "Unexpected error when checking access "
+				      "on %s: %m", storagedir);
+			rc = -errno;
+			goto out;
+		}
+	}
+
+	/* set up storage db */
+	rc = sqlite_maindb_init(storagedir);
+	if (rc) {
+		xlog(L_ERROR, "Failed to open main database: %d", rc);
+		goto out;
+	}
+
+	/* set up event handler */
+	rc = cld_pipe_init(&clnt);
+	if (rc)
+		goto out;
+
+	xlog(D_GENERAL, "%s: Starting event dispatch handler.", __func__);
+	rc = event_dispatch();
+	if (rc < 0)
+		xlog(L_ERROR, "%s: event_dispatch failed: %m", __func__);
+
+	close(clnt.cl_fd);
+	close(inotify_fd);
+out:
+	free(progname);
+	return rc;
+}
diff --git a/utils/nfsdcltrack/nfsdcld.man b/utils/nfsdcltrack/nfsdcld.man
new file mode 100644
index 0000000..9ddaf64
--- /dev/null
+++ b/utils/nfsdcltrack/nfsdcld.man
@@ -0,0 +1,185 @@
+.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.13)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  \*(C+ will
+.\" give a nicer C++.  Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available.  \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` ""
+.    ds C' ""
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD.  Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.ie \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.el \{\
+.    de IX
+..
+.\}
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "NFSDCLD 8"
+.TH NFSDCLD 8 "2011-12-21" "" ""
+.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+nfsdcld \- NFSv4 Client Tracking Daemon
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+nfsdcld [\-d] [\-F] [\-p path] [\-s stable storage dir]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+nfsdcld is the NFSv4 client tracking daemon. It is not necessary to run
+this daemon on machines that are not acting as NFSv4 servers.
+.PP
+When a network partition is combined with a server reboot, there are
+edge conditions that can cause the server to grant lock reclaims when
+other clients have taken conflicting locks in the interim. A more detailed
+explanation of this issue is described in \s-1RFC\s0 3530, section 8.6.3.
+.PP
+In order to prevent these problems, the server must track a small amount
+of per-client information on stable storage. This daemon provides the
+userspace piece of that functionality.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-d\fR, \fB\-\-debug\fR" 4
+.IX Item "-d, --debug"
+Enable debug level logging.
+.IP "\fB\-F\fR, \fB\-\-foreground\fR" 4
+.IX Item "-F, --foreground"
+Runs the daemon in the foreground and prints all output to stderr
+.IP "\fB\-p\fR \fIpipe\fR, \fB\-\-pipe\fR=\fIpipe\fR" 4
+.IX Item "-p pipe, --pipe=pipe"
+Location of the \*(L"cld\*(R" upcall pipe. The default value is
+\&\fI/var/lib/nfs/rpc_pipefs/nfsd/cld\fR. If the pipe does not exist when the
+daemon starts then it will wait for it to be created.
+.IP "\fB\-s\fR \fIstorage_dir\fR, \fB\-\-storagedir\fR=\fIstorage_dir\fR" 4
+.IX Item "-s storagedir, --storagedir=storage_dir"
+Directory where stable storage information should be kept. The default
+value is \fI/var/lib/nfs/nfsdcld\fR.
+.SH "NOTES"
+.IX Header "NOTES"
+The Linux kernel NFSv4 server has historically tracked this information
+on stable storage by manipulating information on the filesystem
+directly, in the directory to which \fI/proc/fs/nfsd/nfsv4recoverydir\fR
+points.
+.PP
+This daemon requires a kernel that supports the nfsdcld upcall. If the
+kernel does not support the new upcall, or is using the legacy client
+name tracking code then it will not create the pipe that nfsdcld uses to
+talk to the kernel.
+.PP
+This daemon should be run as root, as the pipe that it uses to communicate
+with the kernel is only accessable by root. The daemon however does drop all
+superuser capabilities after starting. Because of this, the \fIstoragedir\fR
+should be owned by root, and be readable and writable by owner.
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+The nfsdcld daemon was developed by Jeff Layton <jlayton@redhat.com>.
diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c
new file mode 100644
index 0000000..fc882c6
--- /dev/null
+++ b/utils/nfsdcltrack/sqlite.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2011  Red Hat, Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Explanation:
+ *
+ * This file contains the code to manage the sqlite backend database for the
+ * clstated upcall daemon.
+ *
+ * The main database is called main.sqlite and contains the following tables:
+ *
+ * parameters: simple key/value pairs for storing database info
+ *
+ * clients: one column containing a BLOB with the as sent by the client
+ * 	    and a timestamp (in epoch seconds) of when the record was
+ * 	    established
+ *
+ * FIXME: should we also record the fsid being accessed?
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <dirent.h>
+#include <errno.h>
+#include <event.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sqlite3.h>
+#include <linux/limits.h>
+
+#include "xlog.h"
+
+#define CLD_SQLITE_SCHEMA_VERSION 1
+
+/* in milliseconds */
+#define CLD_SQLITE_BUSY_TIMEOUT 10000
+
+/* private data structures */
+
+/* global variables */
+
+/* top level DB directory */
+static char *sqlite_topdir;
+
+/* reusable pathname and sql command buffer */
+static char buf[PATH_MAX];
+
+/* global database handle */
+static sqlite3 *dbh;
+
+/* forward declarations */
+
+/* make a directory, ignoring EEXIST errors unless it's not a directory */
+static int
+mkdir_if_not_exist(char *dirname)
+{
+	int ret;
+	struct stat statbuf;
+
+	ret = mkdir(dirname, S_IRWXU);
+	if (ret && errno != EEXIST)
+		return -errno;
+
+	ret = stat(dirname, &statbuf);
+	if (ret)
+		return -errno;
+
+	if (!S_ISDIR(statbuf.st_mode))
+		ret = -ENOTDIR;
+
+	return ret;
+}
+
+/*
+ * Open the "main" database, and attempt to initialize it by creating the
+ * parameters table and inserting the schema version into it. Ignore any errors
+ * from that, and then attempt to select the version out of it again. If the
+ * version appears wrong, then assume that the DB is corrupt or has been
+ * upgraded, and return an error. If all of that works, then attempt to create
+ * the "clients" table.
+ */
+int
+sqlite_maindb_init(char *topdir)
+{
+	int ret;
+	char *err = NULL;
+	sqlite3_stmt *stmt = NULL;
+
+	sqlite_topdir = topdir;
+
+	ret = mkdir_if_not_exist(sqlite_topdir);
+	if (ret)
+		return ret;
+
+	ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", sqlite_topdir);
+	if (ret < 0)
+		return ret;
+
+	buf[PATH_MAX - 1] = '\0';
+
+	ret = sqlite3_open(buf, &dbh);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "Unable to open main database: %d", ret);
+		return ret;
+	}
+
+	ret = sqlite3_busy_timeout(dbh, CLD_SQLITE_BUSY_TIMEOUT);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "Unable to set sqlite busy timeout: %d", ret);
+		goto out_err;
+	}
+
+	/* Try to create table */
+	ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS parameters "
+				"(key TEXT PRIMARY KEY, value TEXT);",
+				NULL, NULL, &err);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "Unable to create parameter table: %d", ret);
+		goto out_err;
+	}
+
+	/* insert version into table -- ignore error if it fails */
+	ret = snprintf(buf, sizeof(buf),
+		       "INSERT OR IGNORE INTO parameters values (\"version\", "
+		       "\"%d\");", CLD_SQLITE_SCHEMA_VERSION);
+	if (ret < 0) {
+		goto out_err;
+	} else if ((size_t)ret >= sizeof(buf)) {
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "Unable to insert into parameter table: %d",
+				ret);
+		goto out_err;
+	}
+
+	ret = sqlite3_prepare_v2(dbh,
+		"SELECT value FROM parameters WHERE key == \"version\";",
+		 -1, &stmt, NULL);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "Unable to prepare select statement: %d", ret);
+		goto out_err;
+	}
+
+	/* check schema version */
+	ret = sqlite3_step(stmt);
+	if (ret != SQLITE_ROW) {
+		xlog(L_ERROR, "Select statement execution failed: %s",
+				sqlite3_errmsg(dbh));
+		goto out_err;
+	}
+
+	/* process SELECT result */
+	ret = sqlite3_column_int(stmt, 0);
+	if (ret != CLD_SQLITE_SCHEMA_VERSION) {
+		xlog(L_ERROR, "Unsupported database schema version! "
+			"Expected %d, got %d.",
+			CLD_SQLITE_SCHEMA_VERSION, ret);
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	/* now create the "clients" table */
+	ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS clients "
+				"(id BLOB PRIMARY KEY, time INTEGER);",
+				NULL, NULL, &err);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "Unable to create clients table: %s", err);
+		goto out_err;
+	}
+
+	sqlite3_free(err);
+	sqlite3_finalize(stmt);
+	return 0;
+
+out_err:
+	if (err) {
+		xlog(L_ERROR, "sqlite error: %s", err);
+		sqlite3_free(err);
+	}
+	sqlite3_finalize(stmt);
+	sqlite3_close(dbh);
+	return ret;
+}
+
+/*
+ * Create a client record
+ *
+ * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0)
+ */
+int
+sqlite_insert_client(const unsigned char *clname, const size_t namelen)
+{
+	int ret;
+	sqlite3_stmt *stmt = NULL;
+
+	ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients VALUES "
+				      "(?, strftime('%s', 'now'));", -1,
+					&stmt, NULL);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "%s: insert statement prepare failed: %s",
+			__func__, sqlite3_errmsg(dbh));
+		return ret;
+	}
+
+	ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+				SQLITE_STATIC);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
+				sqlite3_errmsg(dbh));
+		goto out_err;
+	}
+
+	ret = sqlite3_step(stmt);
+	if (ret == SQLITE_DONE)
+		ret = SQLITE_OK;
+	else
+		xlog(L_ERROR, "%s: unexpected return code from insert: %s",
+				__func__, sqlite3_errmsg(dbh));
+
+out_err:
+	xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+	sqlite3_finalize(stmt);
+	return ret;
+}
+
+/* Remove a client record */
+int
+sqlite_remove_client(const unsigned char *clname, const size_t namelen)
+{
+	int ret;
+	sqlite3_stmt *stmt = NULL;
+
+	ret = sqlite3_prepare_v2(dbh, "DELETE FROM clients WHERE id==?", -1,
+				 &stmt, NULL);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "%s: statement prepare failed: %s",
+				__func__, sqlite3_errmsg(dbh));
+		goto out_err;
+	}
+
+	ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+				SQLITE_STATIC);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
+				sqlite3_errmsg(dbh));
+		goto out_err;
+	}
+
+	ret = sqlite3_step(stmt);
+	if (ret == SQLITE_DONE)
+		ret = SQLITE_OK;
+	else
+		xlog(L_ERROR, "%s: unexpected return code from delete: %d",
+				__func__, ret);
+
+out_err:
+	xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+	sqlite3_finalize(stmt);
+	return ret;
+}
+
+/*
+ * Is the given clname in the clients table? If so, then update its timestamp
+ * and return success. If the record isn't present, or the update fails, then
+ * return an error.
+ */
+int
+sqlite_check_client(const unsigned char *clname, const size_t namelen)
+{
+	int ret;
+	sqlite3_stmt *stmt = NULL;
+
+	ret = sqlite3_prepare_v2(dbh, "SELECT count(*) FROM clients WHERE "
+				      "id==?", -1, &stmt, NULL);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "%s: unable to prepare update statement: %s",
+				__func__, sqlite3_errmsg(dbh));
+		goto out_err;
+	}
+
+	ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+				SQLITE_STATIC);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "%s: bind blob failed: %s",
+				__func__, sqlite3_errmsg(dbh));
+		goto out_err;
+	}
+
+	ret = sqlite3_step(stmt);
+	if (ret != SQLITE_ROW) {
+		xlog(L_ERROR, "%s: unexpected return code from select: %d",
+				__func__, ret);
+		goto out_err;
+	}
+
+	ret = sqlite3_column_int(stmt, 0);
+	xlog(D_GENERAL, "%s: select returned %d rows", __func__, ret);
+	if (ret != 1) {
+		ret = -EACCES;
+		goto out_err;
+	}
+
+	sqlite3_finalize(stmt);
+	stmt = NULL;
+	ret = sqlite3_prepare_v2(dbh, "UPDATE OR FAIL clients SET "
+				      "time=strftime('%s', 'now') WHERE id==?",
+				 -1, &stmt, NULL);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "%s: unable to prepare update statement: %s",
+				__func__, sqlite3_errmsg(dbh));
+		goto out_err;
+	}
+
+	ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+				SQLITE_STATIC);
+	if (ret != SQLITE_OK) {
+		xlog(L_ERROR, "%s: bind blob failed: %s",
+				__func__, sqlite3_errmsg(dbh));
+		goto out_err;
+	}
+
+	ret = sqlite3_step(stmt);
+	if (ret == SQLITE_DONE)
+		ret = SQLITE_OK;
+	else
+		xlog(L_ERROR, "%s: unexpected return code from update: %s",
+				__func__, sqlite3_errmsg(dbh));
+
+out_err:
+	xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+	sqlite3_finalize(stmt);
+	return ret;
+}
+
+/*
+ * remove any client records that were not reclaimed since grace_start.
+ */
+int
+sqlite_remove_unreclaimed(time_t grace_start)
+{
+	int ret;
+	char *err = NULL;
+
+	ret = snprintf(buf, sizeof(buf), "DELETE FROM clients WHERE time < %ld",
+			grace_start);
+	if (ret < 0) {
+		return ret;
+	} else if ((size_t)ret >= sizeof(buf)) {
+		ret = -EINVAL;
+		return ret;
+	}
+
+	ret = sqlite3_exec(dbh, buf, NULL, NULL, &err);
+	if (ret != SQLITE_OK)
+		xlog(L_ERROR, "%s: delete failed: %s", __func__, err);
+
+	xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+	sqlite3_free(err);
+	return ret;
+}
diff --git a/utils/nfsdcltrack/sqlite.h b/utils/nfsdcltrack/sqlite.h
new file mode 100644
index 0000000..c85e7d6
--- /dev/null
+++ b/utils/nfsdcltrack/sqlite.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011  Red Hat, Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _SQLITE_H_
+#define _SQLITE_H_
+
+int sqlite_maindb_init(char *topdir);
+int sqlite_insert_client(const unsigned char *clname, const size_t namelen);
+int sqlite_remove_client(const unsigned char *clname, const size_t namelen);
+int sqlite_check_client(const unsigned char *clname, const size_t namelen);
+int sqlite_remove_unreclaimed(const time_t grace_start);
+
+#endif /* _SQLITE_H */
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 03/10] nfsdcltrack: remove pointless sqlite_topdir variable
  2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 01/10] nfsdcltrack: fix segfault in sqlite debug logging Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 02/10] nfsdcltrack: rename the nfsdcld directory and options to nfsdcltrack Jeff Layton
@ 2012-10-24 15:25 ` Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 04/10] nfsdcltrack: break out a function to open the database handle Jeff Layton
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

This is holdover from an earlier version of the code and doesn't really
provide any benefit. Also, mark the topdir and dirname arguments const
since they should never be changed.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 utils/nfsdcltrack/sqlite.c | 13 ++++---------
 utils/nfsdcltrack/sqlite.h |  2 +-
 2 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c
index fc882c6..c19af7e 100644
--- a/utils/nfsdcltrack/sqlite.c
+++ b/utils/nfsdcltrack/sqlite.c
@@ -61,9 +61,6 @@
 
 /* global variables */
 
-/* top level DB directory */
-static char *sqlite_topdir;
-
 /* reusable pathname and sql command buffer */
 static char buf[PATH_MAX];
 
@@ -74,7 +71,7 @@ static sqlite3 *dbh;
 
 /* make a directory, ignoring EEXIST errors unless it's not a directory */
 static int
-mkdir_if_not_exist(char *dirname)
+mkdir_if_not_exist(const char *dirname)
 {
 	int ret;
 	struct stat statbuf;
@@ -102,19 +99,17 @@ mkdir_if_not_exist(char *dirname)
  * the "clients" table.
  */
 int
-sqlite_maindb_init(char *topdir)
+sqlite_maindb_init(const char *topdir)
 {
 	int ret;
 	char *err = NULL;
 	sqlite3_stmt *stmt = NULL;
 
-	sqlite_topdir = topdir;
-
-	ret = mkdir_if_not_exist(sqlite_topdir);
+	ret = mkdir_if_not_exist(topdir);
 	if (ret)
 		return ret;
 
-	ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", sqlite_topdir);
+	ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", topdir);
 	if (ret < 0)
 		return ret;
 
diff --git a/utils/nfsdcltrack/sqlite.h b/utils/nfsdcltrack/sqlite.h
index c85e7d6..8748948 100644
--- a/utils/nfsdcltrack/sqlite.h
+++ b/utils/nfsdcltrack/sqlite.h
@@ -20,7 +20,7 @@
 #ifndef _SQLITE_H_
 #define _SQLITE_H_
 
-int sqlite_maindb_init(char *topdir);
+int sqlite_maindb_init(const char *topdir);
 int sqlite_insert_client(const unsigned char *clname, const size_t namelen);
 int sqlite_remove_client(const unsigned char *clname, const size_t namelen);
 int sqlite_check_client(const unsigned char *clname, const size_t namelen);
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 04/10] nfsdcltrack: break out a function to open the database handle
  2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
                   ` (2 preceding siblings ...)
  2012-10-24 15:25 ` [PATCH v2 03/10] nfsdcltrack: remove pointless sqlite_topdir variable Jeff Layton
@ 2012-10-24 15:25 ` Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 05/10] nfsdcltrack: add a new "one-shot" program for manipulating the client tracking db Jeff Layton
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

When we add a new usermodehelper upcall program to do the database
access, the existing "init" function will be overkill every time
we start up the program.

Break out the database handle establishment routine into a separate
function that we can call from each upcall command in the one-shot
program.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 utils/nfsdcltrack/sqlite.c | 49 ++++++++++++++++++++++++++++++++--------------
 utils/nfsdcltrack/sqlite.h |  1 +
 2 files changed, 35 insertions(+), 15 deletions(-)

diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c
index c19af7e..bac6789 100644
--- a/utils/nfsdcltrack/sqlite.c
+++ b/utils/nfsdcltrack/sqlite.c
@@ -90,24 +90,15 @@ mkdir_if_not_exist(const char *dirname)
 	return ret;
 }
 
-/*
- * Open the "main" database, and attempt to initialize it by creating the
- * parameters table and inserting the schema version into it. Ignore any errors
- * from that, and then attempt to select the version out of it again. If the
- * version appears wrong, then assume that the DB is corrupt or has been
- * upgraded, and return an error. If all of that works, then attempt to create
- * the "clients" table.
- */
+/* Open the database and set up the database handle for it */
 int
-sqlite_maindb_init(const char *topdir)
+sqlite_prepare_dbh(const char *topdir)
 {
 	int ret;
-	char *err = NULL;
-	sqlite3_stmt *stmt = NULL;
 
-	ret = mkdir_if_not_exist(topdir);
-	if (ret)
-		return ret;
+	/* Do nothing if the database handle is already set up */
+	if (dbh)
+		return 0;
 
 	ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", topdir);
 	if (ret < 0)
@@ -118,15 +109,43 @@ sqlite_maindb_init(const char *topdir)
 	ret = sqlite3_open(buf, &dbh);
 	if (ret != SQLITE_OK) {
 		xlog(L_ERROR, "Unable to open main database: %d", ret);
+		dbh = NULL;
 		return ret;
 	}
 
 	ret = sqlite3_busy_timeout(dbh, CLD_SQLITE_BUSY_TIMEOUT);
 	if (ret != SQLITE_OK) {
 		xlog(L_ERROR, "Unable to set sqlite busy timeout: %d", ret);
-		goto out_err;
+		sqlite3_close(dbh);
+		dbh = NULL;
 	}
 
+	return ret;
+}
+
+/*
+ * Open the "main" database, and attempt to initialize it by creating the
+ * parameters table and inserting the schema version into it. Ignore any errors
+ * from that, and then attempt to select the version out of it again. If the
+ * version appears wrong, then assume that the DB is corrupt or has been
+ * upgraded, and return an error. If all of that works, then attempt to create
+ * the "clients" table.
+ */
+int
+sqlite_maindb_init(const char *topdir)
+{
+	int ret;
+	char *err = NULL;
+	sqlite3_stmt *stmt = NULL;
+
+	ret = mkdir_if_not_exist(topdir);
+	if (ret)
+		return ret;
+
+	ret = sqlite_prepare_dbh(topdir);
+	if (ret)
+		return ret;
+
 	/* Try to create table */
 	ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS parameters "
 				"(key TEXT PRIMARY KEY, value TEXT);",
diff --git a/utils/nfsdcltrack/sqlite.h b/utils/nfsdcltrack/sqlite.h
index 8748948..ebf04c3 100644
--- a/utils/nfsdcltrack/sqlite.h
+++ b/utils/nfsdcltrack/sqlite.h
@@ -20,6 +20,7 @@
 #ifndef _SQLITE_H_
 #define _SQLITE_H_
 
+int sqlite_prepare_dbh(const char *topdir);
 int sqlite_maindb_init(const char *topdir);
 int sqlite_insert_client(const unsigned char *clname, const size_t namelen);
 int sqlite_remove_client(const unsigned char *clname, const size_t namelen);
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 05/10] nfsdcltrack: add a new "one-shot" program for manipulating the client tracking db
  2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
                   ` (3 preceding siblings ...)
  2012-10-24 15:25 ` [PATCH v2 04/10] nfsdcltrack: break out a function to open the database handle Jeff Layton
@ 2012-10-24 15:25 ` Jeff Layton
  2012-10-25 12:56   ` Steve Dickson
  2012-10-24 15:25 ` [PATCH v2 06/10] nfsdcltrack: add a legacy transition mechanism Jeff Layton
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

Usermode helper upcalls are all the rage these days for infrequent
upcalls, since they make it rather idiot-proof. No running daemon is
required, so there's really no setup beyond ensuring that the callout
exists and is runnable.

This program adds a callout program to nfs-utils for that purpose. The
storage engine on the backend is identical to the one used by nfsdcld.
This just adds a new frontend for it.

For now, building with --enable-nfsdcltrack gives you both nfsdcld and
nfsdcltrack programs. A later patch will remove nfsdcld altogether.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 utils/nfsdcltrack/Makefile.am   |   6 +-
 utils/nfsdcltrack/nfsdcltrack.c | 441 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 445 insertions(+), 2 deletions(-)
 create mode 100644 utils/nfsdcltrack/nfsdcltrack.c

diff --git a/utils/nfsdcltrack/Makefile.am b/utils/nfsdcltrack/Makefile.am
index 073a71b..afae455 100644
--- a/utils/nfsdcltrack/Makefile.am
+++ b/utils/nfsdcltrack/Makefile.am
@@ -4,11 +4,13 @@ man8_MANS	= nfsdcld.man
 EXTRA_DIST	= $(man8_MANS)
 
 AM_CFLAGS	+= -D_LARGEFILE64_SOURCE
-sbin_PROGRAMS	= nfsdcld
+sbin_PROGRAMS	= nfsdcld nfsdcltrack
 
 nfsdcld_SOURCES = nfsdcld.c sqlite.c
-
 nfsdcld_LDADD = ../../support/nfs/libnfs.a $(LIBEVENT) $(LIBSQLITE) $(LIBCAP)
 
+nfsdcltrack_SOURCES = nfsdcltrack.c sqlite.c
+nfsdcltrack_LDADD = ../../support/nfs/libnfs.a $(LIBSQLITE) $(LIBCAP)
+
 MAINTAINERCLEANFILES = Makefile.in
 
diff --git a/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c
new file mode 100644
index 0000000..f8c3810
--- /dev/null
+++ b/utils/nfsdcltrack/nfsdcltrack.c
@@ -0,0 +1,441 @@
+/*
+ * nfsdcltrack.c -- NFSv4 client name tracking program
+ *
+ * Copyright (C) 2012 Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/inotify.h>
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
+#include "xlog.h"
+#include "sqlite.h"
+
+#ifndef CLD_DEFAULT_STORAGEDIR
+#define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcltrack"
+#endif
+
+/* defined by RFC 3530 */
+#define NFS4_OPAQUE_LIMIT	1024
+
+/* private data structures */
+struct cltrack_cmd {
+	char *name;
+	bool needs_arg;
+	int (*func)(const char *arg);
+};
+
+/* forward declarations */
+static int cltrack_init(const char *unused);
+static int cltrack_create(const char *id);
+static int cltrack_remove(const char *id);
+static int cltrack_check(const char *id);
+static int cltrack_gracedone(const char *gracetime);
+
+/* global variables */
+static struct option longopts[] =
+{
+	{ "help", 0, NULL, 'h' },
+	{ "debug", 0, NULL, 'd' },
+	{ "foreground", 0, NULL, 'f' },
+	{ "storagedir", 1, NULL, 's' },
+	{ NULL, 0, 0, 0 },
+};
+
+static struct cltrack_cmd commands[] =
+{
+	{ "init", false, cltrack_init },
+	{ "create", true, cltrack_create },
+	{ "remove", true, cltrack_remove },
+	{ "check", true, cltrack_check },
+	{ "gracedone", true, cltrack_gracedone },
+	{ NULL, false, NULL },
+};
+
+static char *storagedir = CLD_DEFAULT_STORAGEDIR;
+
+/* common buffer for holding id4 blobs */
+static unsigned char blob[NFS4_OPAQUE_LIMIT];
+
+static void
+usage(char *progname)
+{
+	printf("%s [ -hfd ] [ -s dir ] < cmd > < arg >\n", progname);
+	printf("Where < cmd > is one of the following and takes the following < arg >:\n");
+	printf("    init\n");
+	printf("    create <nfs_client_id4>\n");
+	printf("    remove <nfs_client_id4>\n");
+	printf("    check  <nfs_client_id4>\n");
+	printf("    gracedone <epoch time>\n");
+}
+
+
+/**
+ * hex_to_bin - convert a hex digit to its real value
+ * @ch: ascii character represents hex digit
+ *
+ * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
+ * input.
+ *
+ * Note: borrowed from lib/hexdump.c in the Linux kernel sources.
+ */
+static int
+hex_to_bin(char ch)
+{
+	if ((ch >= '0') && (ch <= '9'))
+		return ch - '0';
+	ch = tolower(ch);
+	if ((ch >= 'a') && (ch <= 'f'))
+		return ch - 'a' + 10;
+	return -1;
+}
+
+/**
+ * hex_str_to_bin - convert a hexidecimal string into a binary blob
+ *
+ * @src: string of hex digit pairs
+ * @dst: destination buffer to hold binary data
+ * @dstsize: size of the destination buffer
+ *
+ * Walk a string of hex digit pairs and convert them into binary data. Returns
+ * the resulting length of the binary data or a negative error code. If the
+ * data will not fit in the buffer, it returns -ENOBUFS (but will likely have
+ * clobbered the dst buffer in the process of determining that). If there are
+ * non-hexidecimal characters in the src, or an odd number of them then it
+ * returns -EINVAL.
+ */
+static ssize_t
+hex_str_to_bin(const char *src, unsigned char *dst, ssize_t dstsize)
+{
+	unsigned char *tmpdst = dst;
+
+	while (*src) {
+		int hi, lo;
+
+		/* make sure we don't overrun the dst buffer */
+		if ((tmpdst - dst) >= dstsize)
+			return -ENOBUFS;
+
+		hi = hex_to_bin(*src++);
+
+		/* did we get an odd number of characters? */
+		if (!*src)
+			return -EINVAL;
+		lo = hex_to_bin(*src++);
+
+		/* one of the characters isn't a hex digit */
+		if (hi < 0 || lo < 0)
+			return -EINVAL;
+
+		/* now place it in the dst buffer */
+		*tmpdst++ = (hi << 4) | lo;
+	}
+
+	return (ssize_t)(tmpdst - dst);
+}
+
+/*
+ * This program will almost always be run with root privileges since the
+ * kernel will call out to run it. Drop all capabilities prior to doing
+ * anything important to limit the exposure to potential compromise.
+ *
+ * FIXME: should we setuid to a different user early on instead?
+ */
+static int
+cltrack_set_caps(void)
+{
+	int ret = 0;
+#ifdef HAVE_SYS_CAPABILITY_H
+	unsigned long i;
+	cap_t caps;
+
+	/* prune the bounding set to nothing */
+	for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0 ; ++i) {
+		ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+		if (ret) {
+			xlog(L_ERROR, "Unable to prune capability %lu from "
+				      "bounding set: %m", i);
+			return -errno;
+		}
+	}
+
+	/* get a blank capset */
+	caps = cap_init();
+	if (caps == NULL) {
+		xlog(L_ERROR, "Unable to get blank capability set: %m");
+		return -errno;
+	}
+
+	/* reset the process capabilities */
+	if (cap_set_proc(caps) != 0) {
+		xlog(L_ERROR, "Unable to set process capabilities: %m");
+		ret = -errno;
+	}
+	cap_free(caps);
+#endif
+	return ret;
+}
+
+static int
+cltrack_init(const char __attribute__((unused)) *unused)
+{
+	int ret;
+
+	/*
+	 * see if the storagedir is writable by root w/o CAP_DAC_OVERRIDE.
+	 * If it isn't then give the user a warning but proceed as if
+	 * everything is OK. If the DB has already been created, then
+	 * everything might still work. If it doesn't exist at all, then
+	 * assume that the maindb init will be able to create it. Fail on
+	 * anything else.
+	 */
+	if (access(storagedir, W_OK) == -1) {
+		switch (errno) {
+		case EACCES:
+			xlog(L_WARNING, "Storage directory %s is not writable. "
+					"Should be owned by root and writable "
+					"by owner!", storagedir);
+			break;
+		case ENOENT:
+			/* ignore and assume that we can create dir as root */
+			break;
+		default:
+			xlog(L_ERROR, "Unexpected error when checking access "
+				      "on %s: %m", storagedir);
+			return -errno;
+		}
+	}
+
+	/* set up storage db */
+	ret = sqlite_maindb_init(storagedir);
+	if (ret) {
+		xlog(L_ERROR, "Failed to init database: %d", ret);
+		/*
+		 * Convert any error here into -EACCES. It's not truly
+		 * accurate in all cases, but it should cause the kernel to
+		 * stop upcalling until the problem is resolved.
+		 */
+		ret = -EACCES;
+	}
+	return ret;
+}
+
+static int
+cltrack_create(const char *id)
+{
+	int ret;
+	ssize_t len;
+
+	xlog(D_GENERAL, "%s: create client record.", __func__);
+
+	ret = sqlite_prepare_dbh(storagedir);
+	if (ret)
+		return ret;
+
+	len = hex_str_to_bin(id, blob, sizeof(blob));
+	if (len < 0)
+		return (int)len;
+
+	ret = sqlite_insert_client(blob, len);
+
+	return ret ? -EREMOTEIO : ret;
+}
+
+static int
+cltrack_remove(const char *id)
+{
+	int ret;
+	ssize_t len;
+
+	xlog(D_GENERAL, "%s: remove client record.", __func__);
+
+	ret = sqlite_prepare_dbh(storagedir);
+	if (ret)
+		return ret;
+
+	len = hex_str_to_bin(id, blob, sizeof(blob));
+	if (len < 0)
+		return (int)len;
+
+	ret = sqlite_remove_client(blob, len);
+
+	return ret ? -EREMOTEIO : ret;
+}
+
+static int
+cltrack_check(const char *id)
+{
+	int ret;
+	ssize_t len;
+
+	xlog(D_GENERAL, "%s: check client record", __func__);
+
+	ret = sqlite_prepare_dbh(storagedir);
+	if (ret)
+		return ret;
+
+	len = hex_str_to_bin(id, blob, sizeof(blob));
+	if (len < 0)
+		return (int)len;
+
+	ret = sqlite_check_client(blob, len);
+
+	return ret ? -EPERM : ret;
+}
+
+static int
+cltrack_gracedone(const char *timestr)
+{
+	int ret;
+	char *tail;
+	time_t gracetime;
+
+
+	ret = sqlite_prepare_dbh(storagedir);
+	if (ret)
+		return ret;
+
+	errno = 0;
+	gracetime = strtol(timestr, &tail, 0);
+
+	/* did the resulting value overflow? (Probably -ERANGE here) */
+	if (errno)
+		return -errno;
+
+	/* string wasn't fully converted */
+	if (*tail)
+		return -EINVAL;
+
+	xlog(D_GENERAL, "%s: grace done. gracetime=%ld", __func__, gracetime);
+
+	ret = sqlite_remove_unreclaimed(gracetime);
+
+	return ret ? -EREMOTEIO : ret;
+}
+
+static struct cltrack_cmd *
+find_cmd(char *cmdname)
+{
+	struct cltrack_cmd *current = &commands[0];
+
+	while (current->name) {
+		if (!strcmp(cmdname, current->name))
+			return current;
+		++current;
+	}
+
+	xlog(L_ERROR, "%s: '%s' doesn't match any known command",
+			__func__, cmdname);
+	return NULL;
+}
+
+int
+main(int argc, char **argv)
+{
+	char arg;
+	int rc = 0;
+	char *progname, *cmdarg = NULL;
+	struct cltrack_cmd *cmd;
+
+	progname = strdup(basename(argv[0]));
+	if (!progname) {
+		fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]);
+		return 1;
+	}
+
+	xlog_syslog(1);
+	xlog_stderr(0);
+
+	/* process command-line options */
+	while ((arg = getopt_long(argc, argv, "hdfs:", longopts,
+				  NULL)) != EOF) {
+		switch (arg) {
+		case 'd':
+			xlog_config(D_ALL, 1);
+		case 'f':
+			xlog_syslog(0);
+			xlog_stderr(1);
+			break;
+		case 's':
+			storagedir = optarg;
+			break;
+		default:
+			usage(progname);
+			return 0;
+		}
+	}
+
+	xlog_open(progname);
+
+	/* we expect a command, at least */
+	if (optind >= argc) {
+		xlog(L_ERROR, "Missing command name\n");
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* drop all capabilities */
+	rc = cltrack_set_caps();
+	if (rc)
+		goto out;
+
+	cmd = find_cmd(argv[optind]);
+	if (!cmd) {
+		/*
+		 * In the event that we get a command that we don't understand
+		 * then return a distinct error. The kernel can use this to
+		 * determine a new kernel/old userspace situation and cope
+		 * with it.
+		 */
+		rc = -ENOSYS;
+		goto out;
+	}
+
+	/* populate arg var if command needs it */
+	if (cmd->needs_arg) {
+		if (optind + 1 >= argc) {
+			xlog(L_ERROR, "Command %s requires an argument\n",
+				cmd->name);
+			rc = -EINVAL;
+			goto out;
+		}
+		cmdarg = argv[optind + 1];
+	}
+	rc = cmd->func(cmdarg);
+out:
+	free(progname);
+	return rc;
+}
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 06/10] nfsdcltrack: add a legacy transition mechanism
  2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
                   ` (4 preceding siblings ...)
  2012-10-24 15:25 ` [PATCH v2 05/10] nfsdcltrack: add a new "one-shot" program for manipulating the client tracking db Jeff Layton
@ 2012-10-24 15:25 ` Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 07/10] nfsdcltrack: add a manpage for nfsdcltrack Jeff Layton
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

If the kernel passes the legacy recdir path in the environment, then we
can use that to transition from the old legacy tracker to the new one.

On a "check" operation, if there is no record of the client in the
database, check to see if there is a matching recoverydir. If there
isn't then just refuse the reclaim. If there is, then insert a new
record for this client into the db, and remove the legacy recoverydir.
If either of those operations fail, then refuse the reclaim.

On a "gracedone" operation, clean out the entire legacy recoverydir
after purging any unreclaimed records from the db. There's not much
we can do if this fails, so just log a warning if it does.

Note that this is a one-way conversion. If the user later boots back
into an older kernel, it will have no knowledge of the new database.

In principle, we could create a tool that would walk the clients
table, md5 hash the clientids and create directories in the
v4recovery dir. Doing that automatically would be pretty difficult
however.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 utils/nfsdcltrack/nfsdcltrack.c | 85 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c
index f8c3810..604e586 100644
--- a/utils/nfsdcltrack/nfsdcltrack.c
+++ b/utils/nfsdcltrack/nfsdcltrack.c
@@ -36,6 +36,7 @@
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/inotify.h>
+#include <dirent.h>
 #ifdef HAVE_SYS_CAPABILITY_H
 #include <sys/prctl.h>
 #include <sys/capability.h>
@@ -296,6 +297,48 @@ cltrack_remove(const char *id)
 }
 
 static int
+cltrack_check_legacy(const unsigned char *blob, const ssize_t len)
+{
+	int ret;
+	struct stat st;
+	char *recdir = getenv("NFSDCLTRACK_LEGACY_RECDIR");
+
+	if (!recdir) {
+		xlog(D_GENERAL, "No NFSDCLTRACK_LEGACY_RECDIR env var");
+		return -EOPNOTSUPP;
+	}
+
+	/* fail recovery on any stat failure */
+	ret = stat(recdir, &st);
+	if (ret) {
+		xlog(D_GENERAL, "Unable to stat %s: %d", recdir, errno);
+		return -errno;
+	}
+
+	/* fail if it isn't a directory */
+	if (!S_ISDIR(st.st_mode)) {
+		xlog(D_GENERAL, "%s is not a directory: mode=0%o", recdir
+				, st.st_mode);
+		return -ENOTDIR;
+	}
+
+	/* Dir exists, try to insert record into db */
+	ret = sqlite_insert_client(blob, len);
+	if (ret) {
+		xlog(D_GENERAL, "Failed to insert client: %d", ret);
+		return -EREMOTEIO;
+	}
+
+	/* remove the legacy recoverydir */
+	ret = rmdir(recdir);
+	if (ret) {
+		xlog(D_GENERAL, "Failed to rmdir %s: %d", recdir, errno);
+		return -errno;
+	}
+	return 0;
+}
+
+static int
 cltrack_check(const char *id)
 {
 	int ret;
@@ -312,10 +355,50 @@ cltrack_check(const char *id)
 		return (int)len;
 
 	ret = sqlite_check_client(blob, len);
+	if (ret)
+		ret = cltrack_check_legacy(blob, len);
 
 	return ret ? -EPERM : ret;
 }
 
+/* Clean out the v4recoverydir -- best effort here */
+static void
+cltrack_legacy_gracedone(void)
+{
+	DIR *v4recovery;
+	struct dirent *entry;
+	char *dirname = getenv("NFSDCLTRACK_LEGACY_TOPDIR");
+
+	if (!dirname)
+		return;
+
+	v4recovery = opendir(dirname);
+	if (!v4recovery)
+		return;
+
+	while ((entry = readdir(v4recovery))) {
+		int len;
+
+		/* borrow the clientid blob for this */
+		len = snprintf((char *)blob, sizeof(blob), "%s/%s", dirname,
+				entry->d_name);
+
+		/* if there's a problem, then skip this entry */
+		if (len < 0 || (size_t)len >= sizeof(blob)) {
+			xlog(L_WARNING, "%s: unable to build filename for %s!",
+				__func__, entry->d_name);
+			continue;
+		}
+
+		len = rmdir((char *)blob);
+		if (len)
+			xlog(L_WARNING, "%s: unable to rmdir %s: %d", __func__,
+				(char *)blob, len);
+	}
+
+	closedir(v4recovery);
+}
+
 static int
 cltrack_gracedone(const char *timestr)
 {
@@ -343,6 +426,8 @@ cltrack_gracedone(const char *timestr)
 
 	ret = sqlite_remove_unreclaimed(gracetime);
 
+	cltrack_legacy_gracedone();
+
 	return ret ? -EREMOTEIO : ret;
 }
 
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 07/10] nfsdcltrack: add a manpage for nfsdcltrack
  2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
                   ` (5 preceding siblings ...)
  2012-10-24 15:25 ` [PATCH v2 06/10] nfsdcltrack: add a legacy transition mechanism Jeff Layton
@ 2012-10-24 15:25 ` Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 08/10] nfsdcltrack: remove the nfsdcld daemon Jeff Layton
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 utils/nfsdcltrack/Makefile.am     |   2 +-
 utils/nfsdcltrack/nfsdcltrack.man | 211 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 212 insertions(+), 1 deletion(-)
 create mode 100644 utils/nfsdcltrack/nfsdcltrack.man

diff --git a/utils/nfsdcltrack/Makefile.am b/utils/nfsdcltrack/Makefile.am
index afae455..1746a25 100644
--- a/utils/nfsdcltrack/Makefile.am
+++ b/utils/nfsdcltrack/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-man8_MANS	= nfsdcld.man
+man8_MANS	= nfsdcld.man nfsdcltrack.man
 EXTRA_DIST	= $(man8_MANS)
 
 AM_CFLAGS	+= -D_LARGEFILE64_SOURCE
diff --git a/utils/nfsdcltrack/nfsdcltrack.man b/utils/nfsdcltrack/nfsdcltrack.man
new file mode 100644
index 0000000..47007df
--- /dev/null
+++ b/utils/nfsdcltrack/nfsdcltrack.man
@@ -0,0 +1,211 @@
+.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  \*(C+ will
+.\" give a nicer C++.  Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available.  \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` ""
+.    ds C' ""
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD.  Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.ie \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.el \{\
+.    de IX
+..
+.\}
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "NFSDCLTRACK 8"
+.TH NFSDCLTRACK 8 "2012-10-24" "" ""
+.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+nfsdcltrack \- NFSv4 Client Tracking Callout Program
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+nfsdcltrack [\-d] [\-f] [\-s stable storage dir] <command> <args...>
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+nfsdcltack is the NFSv4 client tracking callout program. It is not necessary
+to install this daemon on machines that are not acting as NFSv4 servers.
+.PP
+When a network partition is combined with a server reboot, there are
+edge conditions that can cause the server to grant lock reclaims when
+other clients have taken conflicting locks in the interim. A more detailed
+explanation of this issue is described in \s-1RFC\s0 3530, section 8.6.3.
+.PP
+In order to prevent these problems, the server must track a small amount
+of per-client information on stable storage. This program provides the
+userspace piece of that functionality. When the kernel needs to manipulate
+the database that stores this info, it will execute this program to handle
+it.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-d\fR, \fB\-\-debug\fR" 4
+.IX Item "-d, --debug"
+Enable debug level logging.
+.IP "\fB\-f\fR, \fB\-\-foreground\fR" 4
+.IX Item "-f, --foreground"
+Log to stderr instead of syslog.
+.IP "\fB\-s\fR \fIstoragedir\fR, \fB\-\-storagedir\fR=\fIstorage_dir\fR" 4
+.IX Item "-s storagedir, --storagedir=storage_dir"
+Directory where stable storage information should be kept. The default
+value is \fI/var/lib/nfs/nfsdcltrack\fR.
+.SH "COMMANDS"
+.IX Header "COMMANDS"
+nfsdcltrack requires a command for each invocation. Supported commands
+are:
+.IP "\fBinit\fR" 4
+.IX Item "init"
+Initialize the database. This command requires no argument.
+.IP "\fBcreate\fR" 4
+.IX Item "create"
+Create a new client record (or update the timestamp on an existing one). This command requires a hex-encoded nfs_client_id4 as an argument.
+.IP "\fBremove\fR" 4
+.IX Item "remove"
+Remove a client record from the database. This command requires a hex-encoded nfs_client_id4 as an argument.
+.IP "\fBcheck\fR" 4
+.IX Item "check"
+Check to see if a nfs_client_id4 is allowed to reclaim. This command requires a hex-encoded nfs_client_id4 as an argument.
+.IP "\fBgracedone\fR" 4
+.IX Item "gracedone"
+Remove any unreclaimed client records from the database. This command requires a epoch boot time as an argument.
+.SH "LEGACY TRANSITION MECHANISM"
+.IX Header "LEGACY TRANSITION MECHANISM"
+The Linux kernel NFSv4 server has historically tracked this information
+on stable storage by manipulating information on the filesystem
+directly, in the directory to which \fI/proc/fs/nfsd/nfsv4recoverydir\fR
+points. If the kernel passes the correct information, then nfsdcltrack
+can use it to allow a seamless transition from the old client tracking
+scheme to the new one.
+.PP
+On a \fBcheck\fR operation, if there is no record of the client in the
+database, nfsdcltrack will look to see if the \fB\s-1NFSDCLTRACK_LEGACY_RECDIR\s0\fR
+environment variable is set. If it is, then it will fetch that value and
+see if a directory exists by that name. If it does, then the check
+operation will succeed and the directory will be removed.
+.PP
+On a \fBgracedone\fR operation, nfsdcltrack will look to see if the
+\&\fB\s-1NFSDCLTRACK_LEGACY_TOPDIR\s0\fR environment variable is set. If it is, then
+it will attempt to clean out that directory prior to exiting.
+.PP
+Note that this transition is one-way. If the machine subsequently reboots
+back into an older kernel that does not support the nfsdcltrack upcall
+then the clients will not be able to recover their state.
+.SH "NOTES"
+.IX Header "NOTES"
+This program requires a kernel that supports the nfsdcltrack usermodehelper
+upcall. This support was first added to mainline kernels in 3.8.
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+nfsdcltrack was developed by Jeff Layton <jlayton@redhat.com>.
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 08/10] nfsdcltrack: remove the nfsdcld daemon
  2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
                   ` (6 preceding siblings ...)
  2012-10-24 15:25 ` [PATCH v2 07/10] nfsdcltrack: add a manpage for nfsdcltrack Jeff Layton
@ 2012-10-24 15:25 ` Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 09/10] nfsdcltrack: update the README about server startup order Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 10/10] nfsdcltrack: flip the default in autoconf to "yes" for it Jeff Layton
  9 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

Since we want to move to using the usermodehelper upcall
unconditionally, just remove nfsdcld. The kernel code to handle
this will be formally deprecated in 3.10 as well.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 utils/nfsdcltrack/Makefile.am |   7 +-
 utils/nfsdcltrack/nfsdcld.c   | 607 ------------------------------------------
 utils/nfsdcltrack/nfsdcld.man | 185 -------------
 3 files changed, 2 insertions(+), 797 deletions(-)
 delete mode 100644 utils/nfsdcltrack/nfsdcld.c
 delete mode 100644 utils/nfsdcltrack/nfsdcld.man

diff --git a/utils/nfsdcltrack/Makefile.am b/utils/nfsdcltrack/Makefile.am
index 1746a25..a860ffb 100644
--- a/utils/nfsdcltrack/Makefile.am
+++ b/utils/nfsdcltrack/Makefile.am
@@ -1,13 +1,10 @@
 ## Process this file with automake to produce Makefile.in
 
-man8_MANS	= nfsdcld.man nfsdcltrack.man
+man8_MANS	= nfsdcltrack.man
 EXTRA_DIST	= $(man8_MANS)
 
 AM_CFLAGS	+= -D_LARGEFILE64_SOURCE
-sbin_PROGRAMS	= nfsdcld nfsdcltrack
-
-nfsdcld_SOURCES = nfsdcld.c sqlite.c
-nfsdcld_LDADD = ../../support/nfs/libnfs.a $(LIBEVENT) $(LIBSQLITE) $(LIBCAP)
+sbin_PROGRAMS	= nfsdcltrack
 
 nfsdcltrack_SOURCES = nfsdcltrack.c sqlite.c
 nfsdcltrack_LDADD = ../../support/nfs/libnfs.a $(LIBSQLITE) $(LIBCAP)
diff --git a/utils/nfsdcltrack/nfsdcld.c b/utils/nfsdcltrack/nfsdcld.c
deleted file mode 100644
index 473d069..0000000
--- a/utils/nfsdcltrack/nfsdcld.c
+++ /dev/null
@@ -1,607 +0,0 @@
-/*
- * nfsdcld.c -- NFSv4 client name tracking daemon
- *
- * Copyright (C) 2011  Red Hat, Jeff Layton <jlayton@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <errno.h>
-#include <event.h>
-#include <stdbool.h>
-#include <getopt.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <libgen.h>
-#include <sys/inotify.h>
-#ifdef HAVE_SYS_CAPABILITY_H
-#include <sys/prctl.h>
-#include <sys/capability.h>
-#endif
-
-#include "xlog.h"
-#include "nfslib.h"
-#include "cld.h"
-#include "sqlite.h"
-
-#ifndef PIPEFS_DIR
-#define PIPEFS_DIR NFS_STATEDIR "/rpc_pipefs"
-#endif
-
-#define DEFAULT_CLD_PATH	PIPEFS_DIR "/nfsd/cld"
-
-#ifndef CLD_DEFAULT_STORAGEDIR
-#define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcld"
-#endif
-
-#define UPCALL_VERSION		1
-
-/* private data structures */
-struct cld_client {
-	int			cl_fd;
-	struct event		cl_event;
-	struct cld_msg	cl_msg;
-};
-
-/* global variables */
-static char *pipepath = DEFAULT_CLD_PATH;
-static int 		inotify_fd = -1;
-static struct event	pipedir_event;
-
-static struct option longopts[] =
-{
-	{ "help", 0, NULL, 'h' },
-	{ "foreground", 0, NULL, 'F' },
-	{ "debug", 0, NULL, 'd' },
-	{ "pipe", 1, NULL, 'p' },
-	{ "storagedir", 1, NULL, 's' },
-	{ NULL, 0, 0, 0 },
-};
-
-/* forward declarations */
-static void cldcb(int UNUSED(fd), short which, void *data);
-
-static void
-usage(char *progname)
-{
-	printf("%s [ -hFd ] [ -p pipe ] [ -s dir ]\n", progname);
-}
-
-static int
-cld_set_caps(void)
-{
-	int ret = 0;
-#ifdef HAVE_SYS_CAPABILITY_H
-	unsigned long i;
-	cap_t caps;
-
-	if (getuid() != 0) {
-		xlog(L_ERROR, "Not running as root. Daemon won't be able to "
-			      "open the pipe after dropping capabilities!");
-		return -EINVAL;
-	}
-
-	/* prune the bounding set to nothing */
-	for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0 ; ++i) {
-		ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
-		if (ret) {
-			xlog(L_ERROR, "Unable to prune capability %lu from "
-				      "bounding set: %m", i);
-			return -errno;
-		}
-	}
-
-	/* get a blank capset */
-	caps = cap_init();
-	if (caps == NULL) {
-		xlog(L_ERROR, "Unable to get blank capability set: %m");
-		return -errno;
-	}
-
-	/* reset the process capabilities */
-	if (cap_set_proc(caps) != 0) {
-		xlog(L_ERROR, "Unable to set process capabilities: %m");
-		ret = -errno;
-	}
-	cap_free(caps);
-#endif
-	return ret;
-}
-
-#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX)
-
-static int
-cld_pipe_open(struct cld_client *clnt)
-{
-	int fd;
-
-	xlog(D_GENERAL, "%s: opening upcall pipe %s", __func__, pipepath);
-	fd = open(pipepath, O_RDWR, 0);
-	if (fd < 0) {
-		xlog(D_GENERAL, "%s: open of %s failed: %m", __func__, pipepath);
-		return -errno;
-	}
-
-	if (clnt->cl_event.ev_flags & EVLIST_INIT)
-		event_del(&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);
-	/* event_add is done by the caller */
-	return 0;
-}
-
-static void
-cld_inotify_cb(int UNUSED(fd), short which, void *data)
-{
-	int ret;
-	size_t elen;
-	ssize_t rret;
-	char evbuf[INOTIFY_EVENT_MAX];
-	char *dirc = NULL, *pname;
-	struct inotify_event *event = (struct inotify_event *)evbuf;
-	struct cld_client *clnt = data;
-
-	if (which != EV_READ)
-		return;
-
-	xlog(D_GENERAL, "%s: called for EV_READ", __func__);
-
-	dirc = strndup(pipepath, PATH_MAX);
-	if (!dirc) {
-		xlog(L_ERROR, "%s: unable to allocate memory", __func__);
-		goto out;
-	}
-
-	rret = read(inotify_fd, evbuf, INOTIFY_EVENT_MAX);
-	if (rret < 0) {
-		xlog(L_ERROR, "%s: read from inotify fd failed: %m", __func__);
-		goto out;
-	}
-
-	/* check to see if we have a filename in the evbuf */
-	if (!event->len) {
-		xlog(D_GENERAL, "%s: no filename in inotify event", __func__);
-		goto out;
-	}
-
-	pname = basename(dirc);
-	elen = strnlen(event->name, event->len);
-
-	/* does the filename match our pipe? */
-	if (strlen(pname) != elen || memcmp(pname, event->name, elen)) {
-		xlog(D_GENERAL, "%s: wrong filename (%s)", __func__,
-				event->name);
-		goto out;
-	}
-
-	ret = cld_pipe_open(clnt);
-	switch (ret) {
-	case 0:
-		/* readd the event for the cl_event pipe */
-		event_add(&clnt->cl_event, NULL);
-		break;
-	case -ENOENT:
-		/* pipe must have disappeared, wait for it to come back */
-		goto out;
-	default:
-		/* anything else is fatal */
-		xlog(L_FATAL, "%s: unable to open new pipe (%d). Aborting.",
-			ret, __func__);
-		exit(ret);
-	}
-
-out:
-	event_add(&pipedir_event, NULL);
-	free(dirc);
-}
-
-static int
-cld_inotify_setup(void)
-{
-	int ret;
-	char *dirc, *dname;
-
-	dirc = strndup(pipepath, PATH_MAX);
-	if (!dirc) {
-		xlog_err("%s: unable to allocate memory", __func__);
-		ret = -ENOMEM;
-		goto out_free;
-	}
-
-	dname = dirname(dirc);
-
-	inotify_fd = inotify_init();
-	if (inotify_fd < 0) {
-		xlog_err("%s: inotify_init failed: %m", __func__);
-		ret = -errno;
-		goto out_free;
-	}
-
-	ret = inotify_add_watch(inotify_fd, dname, IN_CREATE);
-	if (ret < 0) {
-		xlog_err("%s: inotify_add_watch failed: %m", __func__);
-		ret = -errno;
-		goto out_err;
-	}
-
-out_free:
-	free(dirc);
-	return 0;
-out_err:
-	close(inotify_fd);
-	goto out_free;
-}
-
-/*
- * Set an inotify watch on the directory that should contain the pipe, and then
- * try to open it. If it fails with anything but -ENOENT, return the error
- * immediately.
- *
- * If it succeeds, then set up the pipe event handler. At that point, set up
- * the inotify event handler and go ahead and return success.
- */
-static int
-cld_pipe_init(struct cld_client *clnt)
-{
-	int ret;
-
-	xlog(D_GENERAL, "%s: init pipe handlers", __func__);
-
-	ret = cld_inotify_setup();
-	if (ret != 0)
-		goto out;
-
-	clnt->cl_fd = -1;
-	ret = cld_pipe_open(clnt);
-	switch (ret) {
-	case 0:
-		/* add the event and we're good to go */
-		event_add(&clnt->cl_event, NULL);
-		break;
-	case -ENOENT:
-		/* ignore this error -- cld_inotify_cb will handle it */
-		ret = 0;
-		break;
-	default:
-		/* anything else is fatal */
-		close(inotify_fd);
-		goto out;
-	}
-
-	/* set event for inotify read */
-	event_set(&pipedir_event, inotify_fd, EV_READ, cld_inotify_cb, clnt);
-	event_add(&pipedir_event, NULL);
-out:
-	return ret;
-}
-
-static void
-cld_not_implemented(struct cld_client *clnt)
-{
-	int ret;
-	ssize_t bsize, wsize;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	xlog(D_GENERAL, "%s: downcalling with not implemented error", __func__);
-
-	/* set up reply */
-	cmsg->cm_status = -EOPNOTSUPP;
-
-	bsize = sizeof(*cmsg);
-
-	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
-	if (wsize != bsize)
-		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
-			 __func__, wsize);
-
-	/* reopen pipe, just to be sure */
-	ret = cld_pipe_open(clnt);
-	if (ret) {
-		xlog(L_FATAL, "%s: unable to reopen pipe: %d", __func__, ret);
-		exit(ret);
-	}
-}
-
-static void
-cld_create(struct cld_client *clnt)
-{
-	int ret;
-	ssize_t bsize, wsize;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	xlog(D_GENERAL, "%s: create client record.", __func__);
-
-	ret = sqlite_insert_client(cmsg->cm_u.cm_name.cn_id,
-				   cmsg->cm_u.cm_name.cn_len);
-
-	cmsg->cm_status = ret ? -EREMOTEIO : ret;
-
-	bsize = sizeof(*cmsg);
-
-	xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status);
-	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
-	if (wsize != bsize) {
-		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
-			 __func__, wsize);
-		ret = cld_pipe_open(clnt);
-		if (ret) {
-			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
-					__func__, ret);
-			exit(ret);
-		}
-	}
-}
-
-static void
-cld_remove(struct cld_client *clnt)
-{
-	int ret;
-	ssize_t bsize, wsize;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	xlog(D_GENERAL, "%s: remove client record.", __func__);
-
-	ret = sqlite_remove_client(cmsg->cm_u.cm_name.cn_id,
-				   cmsg->cm_u.cm_name.cn_len);
-
-	cmsg->cm_status = ret ? -EREMOTEIO : ret;
-
-	bsize = sizeof(*cmsg);
-
-	xlog(D_GENERAL, "%s: downcall with status %d", __func__,
-			cmsg->cm_status);
-	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
-	if (wsize != bsize) {
-		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
-			 __func__, wsize);
-		ret = cld_pipe_open(clnt);
-		if (ret) {
-			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
-					__func__, ret);
-			exit(ret);
-		}
-	}
-}
-
-static void
-cld_check(struct cld_client *clnt)
-{
-	int ret;
-	ssize_t bsize, wsize;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	xlog(D_GENERAL, "%s: check client record", __func__);
-
-	ret = sqlite_check_client(cmsg->cm_u.cm_name.cn_id,
-				  cmsg->cm_u.cm_name.cn_len);
-
-	/* set up reply */
-	cmsg->cm_status = ret ? -EACCES : ret;
-
-	bsize = sizeof(*cmsg);
-
-	xlog(D_GENERAL, "%s: downcall with status %d", __func__,
-			cmsg->cm_status);
-	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
-	if (wsize != bsize) {
-		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
-			 __func__, wsize);
-		ret = cld_pipe_open(clnt);
-		if (ret) {
-			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
-					__func__, ret);
-			exit(ret);
-		}
-	}
-}
-
-static void
-cld_gracedone(struct cld_client *clnt)
-{
-	int ret;
-	ssize_t bsize, wsize;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	xlog(D_GENERAL, "%s: grace done. cm_gracetime=%ld", __func__,
-			cmsg->cm_u.cm_gracetime);
-
-	ret = sqlite_remove_unreclaimed(cmsg->cm_u.cm_gracetime);
-
-	/* set up reply: downcall with 0 status */
-	cmsg->cm_status = ret ? -EREMOTEIO : ret;
-
-	bsize = sizeof(*cmsg);
-
-	xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status);
-	wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
-	if (wsize != bsize) {
-		xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m",
-			 __func__, wsize);
-		ret = cld_pipe_open(clnt);
-		if (ret) {
-			xlog(L_FATAL, "%s: unable to reopen pipe: %d",
-					__func__, ret);
-			exit(ret);
-		}
-	}
-}
-
-static void
-cldcb(int UNUSED(fd), short which, void *data)
-{
-	ssize_t len;
-	struct cld_client *clnt = data;
-	struct cld_msg *cmsg = &clnt->cl_msg;
-
-	if (which != EV_READ)
-		goto out;
-
-	len = atomicio(read, clnt->cl_fd, cmsg, sizeof(*cmsg));
-	if (len <= 0) {
-		xlog(L_ERROR, "%s: pipe read failed: %m", __func__);
-		cld_pipe_open(clnt);
-		goto out;
-	}
-
-	if (cmsg->cm_vers != UPCALL_VERSION) {
-		xlog(L_ERROR, "%s: unsupported upcall version: %hu",
-				cmsg->cm_vers);
-		cld_pipe_open(clnt);
-		goto out;
-	}
-
-	switch(cmsg->cm_cmd) {
-	case Cld_Create:
-		cld_create(clnt);
-		break;
-	case Cld_Remove:
-		cld_remove(clnt);
-		break;
-	case Cld_Check:
-		cld_check(clnt);
-		break;
-	case Cld_GraceDone:
-		cld_gracedone(clnt);
-		break;
-	default:
-		xlog(L_WARNING, "%s: command %u is not yet implemented",
-				__func__, cmsg->cm_cmd);
-		cld_not_implemented(clnt);
-	}
-out:
-	event_add(&clnt->cl_event, NULL);
-}
-
-int
-main(int argc, char **argv)
-{
-	char arg;
-	int rc = 0;
-	bool foreground = false;
-	char *progname;
-	char *storagedir = CLD_DEFAULT_STORAGEDIR;
-	struct cld_client clnt;
-
-	memset(&clnt, 0, sizeof(clnt));
-
-	progname = strdup(basename(argv[0]));
-	if (!progname) {
-		fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]);
-		return 1;
-	}
-
-	event_init();
-	xlog_syslog(0);
-	xlog_stderr(1);
-
-	/* process command-line options */
-	while ((arg = getopt_long(argc, argv, "hdFp:s:", longopts,
-				  NULL)) != EOF) {
-		switch (arg) {
-		case 'd':
-			xlog_config(D_ALL, 1);
-			break;
-		case 'F':
-			foreground = true;
-			break;
-		case 'p':
-			pipepath = optarg;
-			break;
-		case 's':
-			storagedir = optarg;
-			break;
-		default:
-			usage(progname);
-			return 0;
-		}
-	}
-
-
-	xlog_open(progname);
-	if (!foreground) {
-		xlog_syslog(1);
-		xlog_stderr(0);
-		rc = daemon(0, 0);
-		if (rc) {
-			xlog(L_ERROR, "Unable to daemonize: %m");
-			goto out;
-		}
-	}
-
-	/* drop all capabilities */
-	rc = cld_set_caps();
-	if (rc)
-		goto out;
-
-	/*
-	 * now see if the storagedir is writable by root w/o CAP_DAC_OVERRIDE.
-	 * If it isn't then give the user a warning but proceed as if
-	 * everything is OK. If the DB has already been created, then
-	 * everything might still work. If it doesn't exist at all, then
-	 * assume that the maindb init will be able to create it. Fail on
-	 * anything else.
-	 */
-	if (access(storagedir, W_OK) == -1) {
-		switch (errno) {
-		case EACCES:
-			xlog(L_WARNING, "Storage directory %s is not writable. "
-					"Should be owned by root and writable "
-					"by owner!", storagedir);
-			break;
-		case ENOENT:
-			/* ignore and assume that we can create dir as root */
-			break;
-		default:
-			xlog(L_ERROR, "Unexpected error when checking access "
-				      "on %s: %m", storagedir);
-			rc = -errno;
-			goto out;
-		}
-	}
-
-	/* set up storage db */
-	rc = sqlite_maindb_init(storagedir);
-	if (rc) {
-		xlog(L_ERROR, "Failed to open main database: %d", rc);
-		goto out;
-	}
-
-	/* set up event handler */
-	rc = cld_pipe_init(&clnt);
-	if (rc)
-		goto out;
-
-	xlog(D_GENERAL, "%s: Starting event dispatch handler.", __func__);
-	rc = event_dispatch();
-	if (rc < 0)
-		xlog(L_ERROR, "%s: event_dispatch failed: %m", __func__);
-
-	close(clnt.cl_fd);
-	close(inotify_fd);
-out:
-	free(progname);
-	return rc;
-}
diff --git a/utils/nfsdcltrack/nfsdcld.man b/utils/nfsdcltrack/nfsdcld.man
deleted file mode 100644
index 9ddaf64..0000000
--- a/utils/nfsdcltrack/nfsdcld.man
+++ /dev/null
@@ -1,185 +0,0 @@
-.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.13)
-.\"
-.\" Standard preamble:
-.\" ========================================================================
-.de Sp \" Vertical space (when we can't use .PP)
-.if t .sp .5v
-.if n .sp
-..
-.de Vb \" Begin verbatim text
-.ft CW
-.nf
-.ne \\$1
-..
-.de Ve \" End verbatim text
-.ft R
-.fi
-..
-.\" Set up some character translations and predefined strings.  \*(-- will
-.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
-.\" double quote, and \*(R" will give a right double quote.  \*(C+ will
-.\" give a nicer C++.  Capital omega is used to do unbreakable dashes and
-.\" therefore won't be available.  \*(C` and \*(C' expand to `' in nroff,
-.\" nothing in troff, for use with C<>.
-.tr \(*W-
-.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
-.ie n \{\
-.    ds -- \(*W-
-.    ds PI pi
-.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
-.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
-.    ds L" ""
-.    ds R" ""
-.    ds C` ""
-.    ds C' ""
-'br\}
-.el\{\
-.    ds -- \|\(em\|
-.    ds PI \(*p
-.    ds L" ``
-.    ds R" ''
-'br\}
-.\"
-.\" Escape single quotes in literal strings from groff's Unicode transform.
-.ie \n(.g .ds Aq \(aq
-.el       .ds Aq '
-.\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
-.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
-.\" entries marked with X<> in POD.  Of course, you'll have to process the
-.\" output yourself in some meaningful fashion.
-.ie \nF \{\
-.    de IX
-.    tm Index:\\$1\t\\n%\t"\\$2"
-..
-.    nr % 0
-.    rr F
-.\}
-.el \{\
-.    de IX
-..
-.\}
-.\"
-.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
-.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
-.    \" fudge factors for nroff and troff
-.if n \{\
-.    ds #H 0
-.    ds #V .8m
-.    ds #F .3m
-.    ds #[ \f1
-.    ds #] \fP
-.\}
-.if t \{\
-.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
-.    ds #V .6m
-.    ds #F 0
-.    ds #[ \&
-.    ds #] \&
-.\}
-.    \" simple accents for nroff and troff
-.if n \{\
-.    ds ' \&
-.    ds ` \&
-.    ds ^ \&
-.    ds , \&
-.    ds ~ ~
-.    ds /
-.\}
-.if t \{\
-.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
-.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
-.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
-.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
-.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
-.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
-.\}
-.    \" troff and (daisy-wheel) nroff accents
-.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
-.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
-.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
-.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
-.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
-.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
-.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
-.ds ae a\h'-(\w'a'u*4/10)'e
-.ds Ae A\h'-(\w'A'u*4/10)'E
-.    \" corrections for vroff
-.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
-.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
-.    \" for low resolution devices (crt and lpr)
-.if \n(.H>23 .if \n(.V>19 \
-\{\
-.    ds : e
-.    ds 8 ss
-.    ds o a
-.    ds d- d\h'-1'\(ga
-.    ds D- D\h'-1'\(hy
-.    ds th \o'bp'
-.    ds Th \o'LP'
-.    ds ae ae
-.    ds Ae AE
-.\}
-.rm #[ #] #H #V #F C
-.\" ========================================================================
-.\"
-.IX Title "NFSDCLD 8"
-.TH NFSDCLD 8 "2011-12-21" "" ""
-.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
-.\" way too many mistakes in technical documents.
-.if n .ad l
-.nh
-.SH "NAME"
-nfsdcld \- NFSv4 Client Tracking Daemon
-.SH "SYNOPSIS"
-.IX Header "SYNOPSIS"
-nfsdcld [\-d] [\-F] [\-p path] [\-s stable storage dir]
-.SH "DESCRIPTION"
-.IX Header "DESCRIPTION"
-nfsdcld is the NFSv4 client tracking daemon. It is not necessary to run
-this daemon on machines that are not acting as NFSv4 servers.
-.PP
-When a network partition is combined with a server reboot, there are
-edge conditions that can cause the server to grant lock reclaims when
-other clients have taken conflicting locks in the interim. A more detailed
-explanation of this issue is described in \s-1RFC\s0 3530, section 8.6.3.
-.PP
-In order to prevent these problems, the server must track a small amount
-of per-client information on stable storage. This daemon provides the
-userspace piece of that functionality.
-.SH "OPTIONS"
-.IX Header "OPTIONS"
-.IP "\fB\-d\fR, \fB\-\-debug\fR" 4
-.IX Item "-d, --debug"
-Enable debug level logging.
-.IP "\fB\-F\fR, \fB\-\-foreground\fR" 4
-.IX Item "-F, --foreground"
-Runs the daemon in the foreground and prints all output to stderr
-.IP "\fB\-p\fR \fIpipe\fR, \fB\-\-pipe\fR=\fIpipe\fR" 4
-.IX Item "-p pipe, --pipe=pipe"
-Location of the \*(L"cld\*(R" upcall pipe. The default value is
-\&\fI/var/lib/nfs/rpc_pipefs/nfsd/cld\fR. If the pipe does not exist when the
-daemon starts then it will wait for it to be created.
-.IP "\fB\-s\fR \fIstorage_dir\fR, \fB\-\-storagedir\fR=\fIstorage_dir\fR" 4
-.IX Item "-s storagedir, --storagedir=storage_dir"
-Directory where stable storage information should be kept. The default
-value is \fI/var/lib/nfs/nfsdcld\fR.
-.SH "NOTES"
-.IX Header "NOTES"
-The Linux kernel NFSv4 server has historically tracked this information
-on stable storage by manipulating information on the filesystem
-directly, in the directory to which \fI/proc/fs/nfsd/nfsv4recoverydir\fR
-points.
-.PP
-This daemon requires a kernel that supports the nfsdcld upcall. If the
-kernel does not support the new upcall, or is using the legacy client
-name tracking code then it will not create the pipe that nfsdcld uses to
-talk to the kernel.
-.PP
-This daemon should be run as root, as the pipe that it uses to communicate
-with the kernel is only accessable by root. The daemon however does drop all
-superuser capabilities after starting. Because of this, the \fIstoragedir\fR
-should be owned by root, and be readable and writable by owner.
-.SH "AUTHORS"
-.IX Header "AUTHORS"
-The nfsdcld daemon was developed by Jeff Layton <jlayton@redhat.com>.
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 09/10] nfsdcltrack: update the README about server startup order
  2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
                   ` (7 preceding siblings ...)
  2012-10-24 15:25 ` [PATCH v2 08/10] nfsdcltrack: remove the nfsdcld daemon Jeff Layton
@ 2012-10-24 15:25 ` Jeff Layton
  2012-10-24 15:25 ` [PATCH v2 10/10] nfsdcltrack: flip the default in autoconf to "yes" for it Jeff Layton
  9 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

Now that nfsdcld is gone, remove the section about starting it up.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 README | 23 ++---------------------
 1 file changed, 2 insertions(+), 21 deletions(-)

diff --git a/README b/README
index e55b2dd..61702f7 100644
--- a/README
+++ b/README
@@ -108,31 +108,12 @@ scripts can be written to work correctly.
        the lock.
        rpc.statd is only needed for NFSv2 and NFSv3 support.
 
-   E/ nfsdcld
-       This daemon is only needed on kernels that support the nfsdcld
-       upcall, and only if the legacy client ID tracking isn't used. It
-       is also not needed if the server does not support NFSv4.
-
-       To determine whether you need this or not, do the following:
-
-           # cat /proc/fs/nfsd/versions
-
-       That should yield a list of NFS versions that this kernel supports,
-       if "4" or later is not in that list, or they are prefixed with a "-"
-       then you don't need to run this daemon. Next:
-
-           # cat /proc/fs/nfsd/nfsv4recoverydir
-
-       If that file is not present, or the directory that the above command
-       outputs is not present, then this daemon is required in order to
-       support lock recovery by the clients when the server reboots.
-
-   F/ rpc.nfsd
+   E/ rpc.nfsd
        Starting nfsd will automatically start lockd.  The nfs server
        will now be fully active and respond to any requests from
        clients.
        
-   G/ sm-notify
+   F/ sm-notify
        This will notify any client which might have locks from before
        a reboot to try to reclaim their locks.  This should start
        immediately after rpc.nfsd is started so that clients have a
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 10/10] nfsdcltrack: flip the default in autoconf to "yes" for it
  2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
                   ` (8 preceding siblings ...)
  2012-10-24 15:25 ` [PATCH v2 09/10] nfsdcltrack: update the README about server startup order Jeff Layton
@ 2012-10-24 15:25 ` Jeff Layton
  2012-10-25 12:57   ` Steve Dickson
  9 siblings, 1 reply; 18+ messages in thread
From: Jeff Layton @ 2012-10-24 15:25 UTC (permalink / raw)
  To: steved; +Cc: linux-nfs

Allow nfsdcltrack to be built by default if all of the requirements
for it are in place. Set the initial state of $enable_nfsdcltrack
to "maybe", and fix the appropriate tests to just disable building
the binary unless someone explicitly requests it.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 configure.ac | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/configure.ac b/configure.ac
index 65d1bea..aa41e0a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -187,9 +187,9 @@ fi
 
 AC_ARG_ENABLE(nfsdcltrack,
 	[AC_HELP_STRING([--enable-nfsdcltrack],
-			[enable NFSv4 clientid tracking programs @<:@default=no@:>@])],
+			[enable NFSv4 clientid tracking programs @<:@default=yes@:>@])],
 	enable_nfsdctrack=$enableval,
-	enable_nfsdcltrack="no")
+	enable_nfsdcltrack="maybe")
 
 dnl Check for TI-RPC library and headers
 AC_LIBTIRPC
@@ -269,12 +269,22 @@ if test "$enable_nfsv4" = yes; then
   dnl Check for sqlite3
   AC_SQLITE3_VERS
 
-  if test "$enable_nfsdcltrack" = "yes"; then
+  if test "$enable_nfsdcltrack" != "no"; then
 	AC_CHECK_HEADERS([libgen.h sys/inotify.h], ,
-		AC_MSG_ERROR([Cannot find header needed for nfsdcltrack]))
-
-  	if test "$libsqlite3_cv_is_recent" != "yes" ; then
+		if test "$enable_nfsdcltrack" = "yes"; then
+			AC_MSG_ERROR([Cannot find header needed for nfsdcltrack])
+		else
+			AC_MSG_WARN([Cannot find header needed for nfsdcltrack. Disabling it.])
+			enable_nfsdcltrack="no"
+		fi
+	)
+  fi
+  if test "$libsqlite3_cv_is_recent" != "yes" ; then
+	if test "$enable_nfsdcltrack" = "yes"; then
 		AC_MSG_ERROR([nfsdcltrack requires sqlite3])
+	elif test "$enable_nfsdcltrack" != "no"; then
+		AC_MSG_WARN([nfsdcltrack requires sqlite3. Disabling it.])
+		enable_nfsdcltrack="no"
 	fi
   fi
 
@@ -292,7 +302,7 @@ if test "$enable_nfsv41" = yes; then
 fi
 
 dnl enable nfsidmap when its support by libnfsidmap
-AM_CONDITIONAL(CONFIG_NFSDCLTRACK, [test "$enable_nfsdcltrack" = "yes" ])
+AM_CONDITIONAL(CONFIG_NFSDCLTRACK, [test "$enable_nfsdcltrack" != "no" ])
 AM_CONDITIONAL(CONFIG_NFSIDMAP, [test "$ac_cv_header_keyutils_h$ac_cv_lib_nfsidmap_nfs4_owner_to_uid" = "yesyes"])
 
 
-- 
1.7.11.7


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 05/10] nfsdcltrack: add a new "one-shot" program for manipulating the client tracking db
  2012-10-24 15:25 ` [PATCH v2 05/10] nfsdcltrack: add a new "one-shot" program for manipulating the client tracking db Jeff Layton
@ 2012-10-25 12:56   ` Steve Dickson
  2012-10-25 13:53     ` Jeff Layton
  0 siblings, 1 reply; 18+ messages in thread
From: Steve Dickson @ 2012-10-25 12:56 UTC (permalink / raw)
  To: Jeff Layton; +Cc: linux-nfs

Over all it I think it look very good... but a couple small nits at the bottom... 

On 24/10/12 11:25, Jeff Layton wrote:
> Usermode helper upcalls are all the rage these days for infrequent
> upcalls, since they make it rather idiot-proof. No running daemon is
> required, so there's really no setup beyond ensuring that the callout
> exists and is runnable.
> 
> This program adds a callout program to nfs-utils for that purpose. The
> storage engine on the backend is identical to the one used by nfsdcld.
> This just adds a new frontend for it.
> 
> For now, building with --enable-nfsdcltrack gives you both nfsdcld and
> nfsdcltrack programs. A later patch will remove nfsdcld altogether.
> 
> Signed-off-by: Jeff Layton <jlayton@redhat.com>
> ---
>  utils/nfsdcltrack/Makefile.am   |   6 +-
>  utils/nfsdcltrack/nfsdcltrack.c | 441 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 445 insertions(+), 2 deletions(-)
>  create mode 100644 utils/nfsdcltrack/nfsdcltrack.c
> 
> diff --git a/utils/nfsdcltrack/Makefile.am b/utils/nfsdcltrack/Makefile.am
> index 073a71b..afae455 100644
> --- a/utils/nfsdcltrack/Makefile.am
> +++ b/utils/nfsdcltrack/Makefile.am
> @@ -4,11 +4,13 @@ man8_MANS	= nfsdcld.man
>  EXTRA_DIST	= $(man8_MANS)
>  
>  AM_CFLAGS	+= -D_LARGEFILE64_SOURCE
> -sbin_PROGRAMS	= nfsdcld
> +sbin_PROGRAMS	= nfsdcld nfsdcltrack
>  
>  nfsdcld_SOURCES = nfsdcld.c sqlite.c
> -
>  nfsdcld_LDADD = ../../support/nfs/libnfs.a $(LIBEVENT) $(LIBSQLITE) $(LIBCAP)
>  
> +nfsdcltrack_SOURCES = nfsdcltrack.c sqlite.c
> +nfsdcltrack_LDADD = ../../support/nfs/libnfs.a $(LIBSQLITE) $(LIBCAP)
> +
>  MAINTAINERCLEANFILES = Makefile.in
>  
> diff --git a/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c
> new file mode 100644
> index 0000000..f8c3810
> --- /dev/null
> +++ b/utils/nfsdcltrack/nfsdcltrack.c
> @@ -0,0 +1,441 @@
> +/*
> + * nfsdcltrack.c -- NFSv4 client name tracking program
> + *
> + * Copyright (C) 2012 Jeff Layton <jlayton@redhat.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
> + * Boston, MA 02110-1301, USA.
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include "config.h"
> +#endif /* HAVE_CONFIG_H */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <ctype.h>
> +#include <errno.h>
> +#include <stdbool.h>
> +#include <getopt.h>
> +#include <string.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <libgen.h>
> +#include <sys/inotify.h>
> +#ifdef HAVE_SYS_CAPABILITY_H
> +#include <sys/prctl.h>
> +#include <sys/capability.h>
> +#endif
> +
> +#include "xlog.h"
> +#include "sqlite.h"
> +
> +#ifndef CLD_DEFAULT_STORAGEDIR
> +#define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcltrack"
> +#endif
> +
> +/* defined by RFC 3530 */
> +#define NFS4_OPAQUE_LIMIT	1024
> +
> +/* private data structures */
> +struct cltrack_cmd {
> +	char *name;
> +	bool needs_arg;
> +	int (*func)(const char *arg);
> +};
> +
> +/* forward declarations */
> +static int cltrack_init(const char *unused);
> +static int cltrack_create(const char *id);
> +static int cltrack_remove(const char *id);
> +static int cltrack_check(const char *id);
> +static int cltrack_gracedone(const char *gracetime);
> +
> +/* global variables */
> +static struct option longopts[] =
> +{
> +	{ "help", 0, NULL, 'h' },
> +	{ "debug", 0, NULL, 'd' },
> +	{ "foreground", 0, NULL, 'f' },
> +	{ "storagedir", 1, NULL, 's' },
> +	{ NULL, 0, 0, 0 },
> +};
> +
> +static struct cltrack_cmd commands[] =
> +{
> +	{ "init", false, cltrack_init },
> +	{ "create", true, cltrack_create },
> +	{ "remove", true, cltrack_remove },
> +	{ "check", true, cltrack_check },
> +	{ "gracedone", true, cltrack_gracedone },
> +	{ NULL, false, NULL },
> +};
> +
> +static char *storagedir = CLD_DEFAULT_STORAGEDIR;
> +
> +/* common buffer for holding id4 blobs */
> +static unsigned char blob[NFS4_OPAQUE_LIMIT];
> +
> +static void
> +usage(char *progname)
> +{
> +	printf("%s [ -hfd ] [ -s dir ] < cmd > < arg >\n", progname);
> +	printf("Where < cmd > is one of the following and takes the following < arg >:\n");
> +	printf("    init\n");
> +	printf("    create <nfs_client_id4>\n");
> +	printf("    remove <nfs_client_id4>\n");
> +	printf("    check  <nfs_client_id4>\n");
> +	printf("    gracedone <epoch time>\n");
> +}
> +
> +
> +/**
> + * hex_to_bin - convert a hex digit to its real value
> + * @ch: ascii character represents hex digit
> + *
> + * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
> + * input.
> + *
> + * Note: borrowed from lib/hexdump.c in the Linux kernel sources.
> + */
> +static int
> +hex_to_bin(char ch)
> +{
> +	if ((ch >= '0') && (ch <= '9'))
> +		return ch - '0';
> +	ch = tolower(ch);
> +	if ((ch >= 'a') && (ch <= 'f'))
> +		return ch - 'a' + 10;
> +	return -1;
> +}
> +
> +/**
> + * hex_str_to_bin - convert a hexidecimal string into a binary blob
> + *
> + * @src: string of hex digit pairs
> + * @dst: destination buffer to hold binary data
> + * @dstsize: size of the destination buffer
> + *
> + * Walk a string of hex digit pairs and convert them into binary data. Returns
> + * the resulting length of the binary data or a negative error code. If the
> + * data will not fit in the buffer, it returns -ENOBUFS (but will likely have
> + * clobbered the dst buffer in the process of determining that). If there are
> + * non-hexidecimal characters in the src, or an odd number of them then it
> + * returns -EINVAL.
> + */
> +static ssize_t
> +hex_str_to_bin(const char *src, unsigned char *dst, ssize_t dstsize)
> +{
> +	unsigned char *tmpdst = dst;
> +
> +	while (*src) {
> +		int hi, lo;
> +
> +		/* make sure we don't overrun the dst buffer */
> +		if ((tmpdst - dst) >= dstsize)
> +			return -ENOBUFS;
> +
> +		hi = hex_to_bin(*src++);
> +
> +		/* did we get an odd number of characters? */
> +		if (!*src)
> +			return -EINVAL;
> +		lo = hex_to_bin(*src++);
> +
> +		/* one of the characters isn't a hex digit */
> +		if (hi < 0 || lo < 0)
> +			return -EINVAL;
> +
> +		/* now place it in the dst buffer */
> +		*tmpdst++ = (hi << 4) | lo;
> +	}
> +
> +	return (ssize_t)(tmpdst - dst);
> +}
> +
> +/*
> + * This program will almost always be run with root privileges since the
> + * kernel will call out to run it. Drop all capabilities prior to doing
> + * anything important to limit the exposure to potential compromise.
> + *
> + * FIXME: should we setuid to a different user early on instead?
> + */
> +static int
> +cltrack_set_caps(void)
> +{
> +	int ret = 0;
> +#ifdef HAVE_SYS_CAPABILITY_H
> +	unsigned long i;
> +	cap_t caps;
> +
> +	/* prune the bounding set to nothing */
> +	for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0 ; ++i) {
> +		ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
> +		if (ret) {
> +			xlog(L_ERROR, "Unable to prune capability %lu from "
> +				      "bounding set: %m", i);
> +			return -errno;
> +		}
> +	}
> +
> +	/* get a blank capset */
> +	caps = cap_init();
> +	if (caps == NULL) {
> +		xlog(L_ERROR, "Unable to get blank capability set: %m");
> +		return -errno;
> +	}
> +
> +	/* reset the process capabilities */
> +	if (cap_set_proc(caps) != 0) {
> +		xlog(L_ERROR, "Unable to set process capabilities: %m");
> +		ret = -errno;
> +	}
> +	cap_free(caps);
> +#endif
> +	return ret;
> +}
> +
> +static int
> +cltrack_init(const char __attribute__((unused)) *unused)
> +{
> +	int ret;
> +
> +	/*
> +	 * see if the storagedir is writable by root w/o CAP_DAC_OVERRIDE.
> +	 * If it isn't then give the user a warning but proceed as if
> +	 * everything is OK. If the DB has already been created, then
> +	 * everything might still work. If it doesn't exist at all, then
> +	 * assume that the maindb init will be able to create it. Fail on
> +	 * anything else.
> +	 */
> +	if (access(storagedir, W_OK) == -1) {
> +		switch (errno) {
> +		case EACCES:
> +			xlog(L_WARNING, "Storage directory %s is not writable. "
> +					"Should be owned by root and writable "
> +					"by owner!", storagedir);
> +			break;
> +		case ENOENT:
> +			/* ignore and assume that we can create dir as root */
> +			break;
> +		default:
> +			xlog(L_ERROR, "Unexpected error when checking access "
> +				      "on %s: %m", storagedir);
> +			return -errno;
> +		}
> +	}
> +
> +	/* set up storage db */
> +	ret = sqlite_maindb_init(storagedir);
> +	if (ret) {
> +		xlog(L_ERROR, "Failed to init database: %d", ret);
> +		/*
> +		 * Convert any error here into -EACCES. It's not truly
> +		 * accurate in all cases, but it should cause the kernel to
> +		 * stop upcalling until the problem is resolved.
> +		 */
> +		ret = -EACCES;
> +	}
> +	return ret;
> +}
> +
> +static int
> +cltrack_create(const char *id)
> +{
> +	int ret;
> +	ssize_t len;
> +
> +	xlog(D_GENERAL, "%s: create client record.", __func__);
> +
> +	ret = sqlite_prepare_dbh(storagedir);
> +	if (ret)
> +		return ret;
> +
> +	len = hex_str_to_bin(id, blob, sizeof(blob));
> +	if (len < 0)
> +		return (int)len;
> +
> +	ret = sqlite_insert_client(blob, len);
> +
> +	return ret ? -EREMOTEIO : ret;
> +}
> +
> +static int
> +cltrack_remove(const char *id)
> +{
> +	int ret;
> +	ssize_t len;
> +
> +	xlog(D_GENERAL, "%s: remove client record.", __func__);
> +
> +	ret = sqlite_prepare_dbh(storagedir);
> +	if (ret)
> +		return ret;
> +
> +	len = hex_str_to_bin(id, blob, sizeof(blob));
> +	if (len < 0)
> +		return (int)len;
> +
> +	ret = sqlite_remove_client(blob, len);
> +
> +	return ret ? -EREMOTEIO : ret;
> +}
> +
> +static int
> +cltrack_check(const char *id)
> +{
> +	int ret;
> +	ssize_t len;
> +
> +	xlog(D_GENERAL, "%s: check client record", __func__);
> +
> +	ret = sqlite_prepare_dbh(storagedir);
> +	if (ret)
> +		return ret;
> +
> +	len = hex_str_to_bin(id, blob, sizeof(blob));
> +	if (len < 0)
> +		return (int)len;
> +
> +	ret = sqlite_check_client(blob, len);
> +
> +	return ret ? -EPERM : ret;
> +}
> +
> +static int
> +cltrack_gracedone(const char *timestr)
> +{
> +	int ret;
> +	char *tail;
> +	time_t gracetime;
> +
> +
> +	ret = sqlite_prepare_dbh(storagedir);
> +	if (ret)
> +		return ret;
> +
> +	errno = 0;
> +	gracetime = strtol(timestr, &tail, 0);
> +
> +	/* did the resulting value overflow? (Probably -ERANGE here) */
> +	if (errno)
> +		return -errno;
> +
> +	/* string wasn't fully converted */
> +	if (*tail)
> +		return -EINVAL;
> +
> +	xlog(D_GENERAL, "%s: grace done. gracetime=%ld", __func__, gracetime);
> +
> +	ret = sqlite_remove_unreclaimed(gracetime);
> +
> +	return ret ? -EREMOTEIO : ret;
> +}
> +
> +static struct cltrack_cmd *
> +find_cmd(char *cmdname)
> +{
> +	struct cltrack_cmd *current = &commands[0];
> +
> +	while (current->name) {
> +		if (!strcmp(cmdname, current->name))
> +			return current;
> +		++current;
> +	}
> +
> +	xlog(L_ERROR, "%s: '%s' doesn't match any known command",
> +			__func__, cmdname);
> +	return NULL;
> +}
> +
> +int
> +main(int argc, char **argv)
> +{
> +	char arg;
> +	int rc = 0;
> +	char *progname, *cmdarg = NULL;
> +	struct cltrack_cmd *cmd;
> +
> +	progname = strdup(basename(argv[0]));
> +	if (!progname) {
> +		fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]);
> +		return 1;
> +	}
Small nit: Do we really want to fail because we cannot get memory for
the program name? Why not just use the string returned from basename()?

> +
> +	xlog_syslog(1);
> +	xlog_stderr(0);
> +
> +	/* process command-line options */
> +	while ((arg = getopt_long(argc, argv, "hdfs:", longopts,
> +				  NULL)) != EOF) {
> +		switch (arg) {
> +		case 'd':
> +			xlog_config(D_ALL, 1);
> +		case 'f':
> +			xlog_syslog(0);
> +			xlog_stderr(1);
> +			break;
> +		case 's':
> +			storagedir = optarg;
> +			break;
> +		default:
> +			usage(progname);
> +			return 0;
> +		}
> +	}
> +
> +	xlog_open(progname);
> +
> +	/* we expect a command, at least */
> +	if (optind >= argc) {
> +		xlog(L_ERROR, "Missing command name\n");
> +		rc = -EINVAL;
> +		goto out;
> +	}
> +
> +	/* drop all capabilities */
> +	rc = cltrack_set_caps();
> +	if (rc)
> +		goto out;
> +
> +	cmd = find_cmd(argv[optind]);
> +	if (!cmd) {
> +		/*
> +		 * In the event that we get a command that we don't understand
> +		 * then return a distinct error. The kernel can use this to
> +		 * determine a new kernel/old userspace situation and cope
> +		 * with it.
> +		 */
> +		rc = -ENOSYS;
> +		goto out;
> +	}
> +
> +	/* populate arg var if command needs it */
> +	if (cmd->needs_arg) {
> +		if (optind + 1 >= argc) {
> +			xlog(L_ERROR, "Command %s requires an argument\n",
> +				cmd->name);
> +			rc = -EINVAL;
> +			goto out;
> +		}
> +		cmdarg = argv[optind + 1];
> +	}
Is needs_arg really necessary? Once the function is found, just pass 
it the rest of argv and let the function decide if there should
be an argument or not... but again.. just a nit... 

steved. 
> +	rc = cmd->func(cmdarg);
> +out:
> +	free(progname);
> +	return rc;
> +}
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 10/10] nfsdcltrack: flip the default in autoconf to "yes" for it
  2012-10-24 15:25 ` [PATCH v2 10/10] nfsdcltrack: flip the default in autoconf to "yes" for it Jeff Layton
@ 2012-10-25 12:57   ` Steve Dickson
  2012-10-25 14:07     ` Jeff Layton
  0 siblings, 1 reply; 18+ messages in thread
From: Steve Dickson @ 2012-10-25 12:57 UTC (permalink / raw)
  To: Jeff Layton; +Cc: linux-nfs



On 24/10/12 11:25, Jeff Layton wrote:
> Allow nfsdcltrack to be built by default if all of the requirements
> for it are in place. Set the initial state of $enable_nfsdcltrack
> to "maybe", and fix the appropriate tests to just disable building
> the binary unless someone explicitly requests it.
Hmm... I'm not sure I too keen on this "maybe" state... 

So if no flags are given to ./configuration, and not 
all the requirements to build nfsdcltrack exists, the configuration 
will succeed, but the command will not be build. Correct?

But if the  --enable_nfsdcltrack flag is given and not all
the requirements to build nfsdcltrack exist the configuration
will fail. 

I'm thinking we might want to make it a bit more binary. Either
on or off. Like it is with the other conditionally built 
commands... 

steved.
 
> 
> Signed-off-by: Jeff Layton <jlayton@redhat.com>
> ---
>  configure.ac | 24 +++++++++++++++++-------
>  1 file changed, 17 insertions(+), 7 deletions(-)
> 
> diff --git a/configure.ac b/configure.ac
> index 65d1bea..aa41e0a 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -187,9 +187,9 @@ fi
>  
>  AC_ARG_ENABLE(nfsdcltrack,
>  	[AC_HELP_STRING([--enable-nfsdcltrack],
> -			[enable NFSv4 clientid tracking programs @<:@default=no@:>@])],
> +			[enable NFSv4 clientid tracking programs @<:@default=yes@:>@])],
>  	enable_nfsdctrack=$enableval,
> -	enable_nfsdcltrack="no")
> +	enable_nfsdcltrack="maybe")
>  
>  dnl Check for TI-RPC library and headers
>  AC_LIBTIRPC
> @@ -269,12 +269,22 @@ if test "$enable_nfsv4" = yes; then
>    dnl Check for sqlite3
>    AC_SQLITE3_VERS
>  
> -  if test "$enable_nfsdcltrack" = "yes"; then
> +  if test "$enable_nfsdcltrack" != "no"; then
>  	AC_CHECK_HEADERS([libgen.h sys/inotify.h], ,
> -		AC_MSG_ERROR([Cannot find header needed for nfsdcltrack]))
> -
> -  	if test "$libsqlite3_cv_is_recent" != "yes" ; then
> +		if test "$enable_nfsdcltrack" = "yes"; then
> +			AC_MSG_ERROR([Cannot find header needed for nfsdcltrack])
> +		else
> +			AC_MSG_WARN([Cannot find header needed for nfsdcltrack. Disabling it.])
> +			enable_nfsdcltrack="no"
> +		fi
> +	)
> +  fi
> +  if test "$libsqlite3_cv_is_recent" != "yes" ; then
> +	if test "$enable_nfsdcltrack" = "yes"; then
>  		AC_MSG_ERROR([nfsdcltrack requires sqlite3])
> +	elif test "$enable_nfsdcltrack" != "no"; then
> +		AC_MSG_WARN([nfsdcltrack requires sqlite3. Disabling it.])
> +		enable_nfsdcltrack="no"
>  	fi
>    fi
>  
> @@ -292,7 +302,7 @@ if test "$enable_nfsv41" = yes; then
>  fi
>  
>  dnl enable nfsidmap when its support by libnfsidmap
> -AM_CONDITIONAL(CONFIG_NFSDCLTRACK, [test "$enable_nfsdcltrack" = "yes" ])
> +AM_CONDITIONAL(CONFIG_NFSDCLTRACK, [test "$enable_nfsdcltrack" != "no" ])
>  AM_CONDITIONAL(CONFIG_NFSIDMAP, [test "$ac_cv_header_keyutils_h$ac_cv_lib_nfsidmap_nfs4_owner_to_uid" = "yesyes"])
>  
>  
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 05/10] nfsdcltrack: add a new "one-shot" program for manipulating the client tracking db
  2012-10-25 12:56   ` Steve Dickson
@ 2012-10-25 13:53     ` Jeff Layton
  2012-10-25 14:30       ` Steve Dickson
  0 siblings, 1 reply; 18+ messages in thread
From: Jeff Layton @ 2012-10-25 13:53 UTC (permalink / raw)
  To: Steve Dickson; +Cc: linux-nfs

On Thu, 25 Oct 2012 08:56:58 -0400
Steve Dickson <SteveD@redhat.com> wrote:

> Over all it I think it look very good... but a couple small nits at the bottom... 
> 
> On 24/10/12 11:25, Jeff Layton wrote:
> > Usermode helper upcalls are all the rage these days for infrequent
> > upcalls, since they make it rather idiot-proof. No running daemon is
> > required, so there's really no setup beyond ensuring that the callout
> > exists and is runnable.
> > 
> > This program adds a callout program to nfs-utils for that purpose. The
> > storage engine on the backend is identical to the one used by nfsdcld.
> > This just adds a new frontend for it.
> > 
> > For now, building with --enable-nfsdcltrack gives you both nfsdcld and
> > nfsdcltrack programs. A later patch will remove nfsdcld altogether.
> > 
> > Signed-off-by: Jeff Layton <jlayton@redhat.com>
> > ---
> >  utils/nfsdcltrack/Makefile.am   |   6 +-
> >  utils/nfsdcltrack/nfsdcltrack.c | 441 ++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 445 insertions(+), 2 deletions(-)
> >  create mode 100644 utils/nfsdcltrack/nfsdcltrack.c
> > 
> > diff --git a/utils/nfsdcltrack/Makefile.am b/utils/nfsdcltrack/Makefile.am
> > index 073a71b..afae455 100644
> > --- a/utils/nfsdcltrack/Makefile.am
> > +++ b/utils/nfsdcltrack/Makefile.am
> > @@ -4,11 +4,13 @@ man8_MANS	= nfsdcld.man
> >  EXTRA_DIST	= $(man8_MANS)
> >  
> >  AM_CFLAGS	+= -D_LARGEFILE64_SOURCE
> > -sbin_PROGRAMS	= nfsdcld
> > +sbin_PROGRAMS	= nfsdcld nfsdcltrack
> >  
> >  nfsdcld_SOURCES = nfsdcld.c sqlite.c
> > -
> >  nfsdcld_LDADD = ../../support/nfs/libnfs.a $(LIBEVENT) $(LIBSQLITE) $(LIBCAP)
> >  
> > +nfsdcltrack_SOURCES = nfsdcltrack.c sqlite.c
> > +nfsdcltrack_LDADD = ../../support/nfs/libnfs.a $(LIBSQLITE) $(LIBCAP)
> > +
> >  MAINTAINERCLEANFILES = Makefile.in
> >  
> > diff --git a/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c
> > new file mode 100644
> > index 0000000..f8c3810
> > --- /dev/null
> > +++ b/utils/nfsdcltrack/nfsdcltrack.c
> > @@ -0,0 +1,441 @@
> > +/*
> > + * nfsdcltrack.c -- NFSv4 client name tracking program
> > + *
> > + * Copyright (C) 2012 Jeff Layton <jlayton@redhat.com>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version 2
> > + * of the License, or (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
> > + * Boston, MA 02110-1301, USA.
> > + */
> > +
> > +#ifdef HAVE_CONFIG_H
> > +#include "config.h"
> > +#endif /* HAVE_CONFIG_H */
> > +
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <ctype.h>
> > +#include <errno.h>
> > +#include <stdbool.h>
> > +#include <getopt.h>
> > +#include <string.h>
> > +#include <sys/stat.h>
> > +#include <sys/types.h>
> > +#include <fcntl.h>
> > +#include <unistd.h>
> > +#include <libgen.h>
> > +#include <sys/inotify.h>
> > +#ifdef HAVE_SYS_CAPABILITY_H
> > +#include <sys/prctl.h>
> > +#include <sys/capability.h>
> > +#endif
> > +
> > +#include "xlog.h"
> > +#include "sqlite.h"
> > +
> > +#ifndef CLD_DEFAULT_STORAGEDIR
> > +#define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcltrack"
> > +#endif
> > +
> > +/* defined by RFC 3530 */
> > +#define NFS4_OPAQUE_LIMIT	1024
> > +
> > +/* private data structures */
> > +struct cltrack_cmd {
> > +	char *name;
> > +	bool needs_arg;
> > +	int (*func)(const char *arg);
> > +};
> > +
> > +/* forward declarations */
> > +static int cltrack_init(const char *unused);
> > +static int cltrack_create(const char *id);
> > +static int cltrack_remove(const char *id);
> > +static int cltrack_check(const char *id);
> > +static int cltrack_gracedone(const char *gracetime);
> > +
> > +/* global variables */
> > +static struct option longopts[] =
> > +{
> > +	{ "help", 0, NULL, 'h' },
> > +	{ "debug", 0, NULL, 'd' },
> > +	{ "foreground", 0, NULL, 'f' },
> > +	{ "storagedir", 1, NULL, 's' },
> > +	{ NULL, 0, 0, 0 },
> > +};
> > +
> > +static struct cltrack_cmd commands[] =
> > +{
> > +	{ "init", false, cltrack_init },
> > +	{ "create", true, cltrack_create },
> > +	{ "remove", true, cltrack_remove },
> > +	{ "check", true, cltrack_check },
> > +	{ "gracedone", true, cltrack_gracedone },
> > +	{ NULL, false, NULL },
> > +};
> > +
> > +static char *storagedir = CLD_DEFAULT_STORAGEDIR;
> > +
> > +/* common buffer for holding id4 blobs */
> > +static unsigned char blob[NFS4_OPAQUE_LIMIT];
> > +
> > +static void
> > +usage(char *progname)
> > +{
> > +	printf("%s [ -hfd ] [ -s dir ] < cmd > < arg >\n", progname);
> > +	printf("Where < cmd > is one of the following and takes the following < arg >:\n");
> > +	printf("    init\n");
> > +	printf("    create <nfs_client_id4>\n");
> > +	printf("    remove <nfs_client_id4>\n");
> > +	printf("    check  <nfs_client_id4>\n");
> > +	printf("    gracedone <epoch time>\n");
> > +}
> > +
> > +
> > +/**
> > + * hex_to_bin - convert a hex digit to its real value
> > + * @ch: ascii character represents hex digit
> > + *
> > + * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
> > + * input.
> > + *
> > + * Note: borrowed from lib/hexdump.c in the Linux kernel sources.
> > + */
> > +static int
> > +hex_to_bin(char ch)
> > +{
> > +	if ((ch >= '0') && (ch <= '9'))
> > +		return ch - '0';
> > +	ch = tolower(ch);
> > +	if ((ch >= 'a') && (ch <= 'f'))
> > +		return ch - 'a' + 10;
> > +	return -1;
> > +}
> > +
> > +/**
> > + * hex_str_to_bin - convert a hexidecimal string into a binary blob
> > + *
> > + * @src: string of hex digit pairs
> > + * @dst: destination buffer to hold binary data
> > + * @dstsize: size of the destination buffer
> > + *
> > + * Walk a string of hex digit pairs and convert them into binary data. Returns
> > + * the resulting length of the binary data or a negative error code. If the
> > + * data will not fit in the buffer, it returns -ENOBUFS (but will likely have
> > + * clobbered the dst buffer in the process of determining that). If there are
> > + * non-hexidecimal characters in the src, or an odd number of them then it
> > + * returns -EINVAL.
> > + */
> > +static ssize_t
> > +hex_str_to_bin(const char *src, unsigned char *dst, ssize_t dstsize)
> > +{
> > +	unsigned char *tmpdst = dst;
> > +
> > +	while (*src) {
> > +		int hi, lo;
> > +
> > +		/* make sure we don't overrun the dst buffer */
> > +		if ((tmpdst - dst) >= dstsize)
> > +			return -ENOBUFS;
> > +
> > +		hi = hex_to_bin(*src++);
> > +
> > +		/* did we get an odd number of characters? */
> > +		if (!*src)
> > +			return -EINVAL;
> > +		lo = hex_to_bin(*src++);
> > +
> > +		/* one of the characters isn't a hex digit */
> > +		if (hi < 0 || lo < 0)
> > +			return -EINVAL;
> > +
> > +		/* now place it in the dst buffer */
> > +		*tmpdst++ = (hi << 4) | lo;
> > +	}
> > +
> > +	return (ssize_t)(tmpdst - dst);
> > +}
> > +
> > +/*
> > + * This program will almost always be run with root privileges since the
> > + * kernel will call out to run it. Drop all capabilities prior to doing
> > + * anything important to limit the exposure to potential compromise.
> > + *
> > + * FIXME: should we setuid to a different user early on instead?
> > + */
> > +static int
> > +cltrack_set_caps(void)
> > +{
> > +	int ret = 0;
> > +#ifdef HAVE_SYS_CAPABILITY_H
> > +	unsigned long i;
> > +	cap_t caps;
> > +
> > +	/* prune the bounding set to nothing */
> > +	for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0 ; ++i) {
> > +		ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
> > +		if (ret) {
> > +			xlog(L_ERROR, "Unable to prune capability %lu from "
> > +				      "bounding set: %m", i);
> > +			return -errno;
> > +		}
> > +	}
> > +
> > +	/* get a blank capset */
> > +	caps = cap_init();
> > +	if (caps == NULL) {
> > +		xlog(L_ERROR, "Unable to get blank capability set: %m");
> > +		return -errno;
> > +	}
> > +
> > +	/* reset the process capabilities */
> > +	if (cap_set_proc(caps) != 0) {
> > +		xlog(L_ERROR, "Unable to set process capabilities: %m");
> > +		ret = -errno;
> > +	}
> > +	cap_free(caps);
> > +#endif
> > +	return ret;
> > +}
> > +
> > +static int
> > +cltrack_init(const char __attribute__((unused)) *unused)
> > +{
> > +	int ret;
> > +
> > +	/*
> > +	 * see if the storagedir is writable by root w/o CAP_DAC_OVERRIDE.
> > +	 * If it isn't then give the user a warning but proceed as if
> > +	 * everything is OK. If the DB has already been created, then
> > +	 * everything might still work. If it doesn't exist at all, then
> > +	 * assume that the maindb init will be able to create it. Fail on
> > +	 * anything else.
> > +	 */
> > +	if (access(storagedir, W_OK) == -1) {
> > +		switch (errno) {
> > +		case EACCES:
> > +			xlog(L_WARNING, "Storage directory %s is not writable. "
> > +					"Should be owned by root and writable "
> > +					"by owner!", storagedir);
> > +			break;
> > +		case ENOENT:
> > +			/* ignore and assume that we can create dir as root */
> > +			break;
> > +		default:
> > +			xlog(L_ERROR, "Unexpected error when checking access "
> > +				      "on %s: %m", storagedir);
> > +			return -errno;
> > +		}
> > +	}
> > +
> > +	/* set up storage db */
> > +	ret = sqlite_maindb_init(storagedir);
> > +	if (ret) {
> > +		xlog(L_ERROR, "Failed to init database: %d", ret);
> > +		/*
> > +		 * Convert any error here into -EACCES. It's not truly
> > +		 * accurate in all cases, but it should cause the kernel to
> > +		 * stop upcalling until the problem is resolved.
> > +		 */
> > +		ret = -EACCES;
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int
> > +cltrack_create(const char *id)
> > +{
> > +	int ret;
> > +	ssize_t len;
> > +
> > +	xlog(D_GENERAL, "%s: create client record.", __func__);
> > +
> > +	ret = sqlite_prepare_dbh(storagedir);
> > +	if (ret)
> > +		return ret;
> > +
> > +	len = hex_str_to_bin(id, blob, sizeof(blob));
> > +	if (len < 0)
> > +		return (int)len;
> > +
> > +	ret = sqlite_insert_client(blob, len);
> > +
> > +	return ret ? -EREMOTEIO : ret;
> > +}
> > +
> > +static int
> > +cltrack_remove(const char *id)
> > +{
> > +	int ret;
> > +	ssize_t len;
> > +
> > +	xlog(D_GENERAL, "%s: remove client record.", __func__);
> > +
> > +	ret = sqlite_prepare_dbh(storagedir);
> > +	if (ret)
> > +		return ret;
> > +
> > +	len = hex_str_to_bin(id, blob, sizeof(blob));
> > +	if (len < 0)
> > +		return (int)len;
> > +
> > +	ret = sqlite_remove_client(blob, len);
> > +
> > +	return ret ? -EREMOTEIO : ret;
> > +}
> > +
> > +static int
> > +cltrack_check(const char *id)
> > +{
> > +	int ret;
> > +	ssize_t len;
> > +
> > +	xlog(D_GENERAL, "%s: check client record", __func__);
> > +
> > +	ret = sqlite_prepare_dbh(storagedir);
> > +	if (ret)
> > +		return ret;
> > +
> > +	len = hex_str_to_bin(id, blob, sizeof(blob));
> > +	if (len < 0)
> > +		return (int)len;
> > +
> > +	ret = sqlite_check_client(blob, len);
> > +
> > +	return ret ? -EPERM : ret;
> > +}
> > +
> > +static int
> > +cltrack_gracedone(const char *timestr)
> > +{
> > +	int ret;
> > +	char *tail;
> > +	time_t gracetime;
> > +
> > +
> > +	ret = sqlite_prepare_dbh(storagedir);
> > +	if (ret)
> > +		return ret;
> > +
> > +	errno = 0;
> > +	gracetime = strtol(timestr, &tail, 0);
> > +
> > +	/* did the resulting value overflow? (Probably -ERANGE here) */
> > +	if (errno)
> > +		return -errno;
> > +
> > +	/* string wasn't fully converted */
> > +	if (*tail)
> > +		return -EINVAL;
> > +
> > +	xlog(D_GENERAL, "%s: grace done. gracetime=%ld", __func__, gracetime);
> > +
> > +	ret = sqlite_remove_unreclaimed(gracetime);
> > +
> > +	return ret ? -EREMOTEIO : ret;
> > +}
> > +
> > +static struct cltrack_cmd *
> > +find_cmd(char *cmdname)
> > +{
> > +	struct cltrack_cmd *current = &commands[0];
> > +
> > +	while (current->name) {
> > +		if (!strcmp(cmdname, current->name))
> > +			return current;
> > +		++current;
> > +	}
> > +
> > +	xlog(L_ERROR, "%s: '%s' doesn't match any known command",
> > +			__func__, cmdname);
> > +	return NULL;
> > +}
> > +
> > +int
> > +main(int argc, char **argv)
> > +{
> > +	char arg;
> > +	int rc = 0;
> > +	char *progname, *cmdarg = NULL;
> > +	struct cltrack_cmd *cmd;
> > +
> > +	progname = strdup(basename(argv[0]));
> > +	if (!progname) {
> > +		fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]);
> > +		return 1;
> > +	}
> Small nit: Do we really want to fail because we cannot get memory for
> the program name? Why not just use the string returned from basename()?
> 
> > +
> > +	xlog_syslog(1);
> > +	xlog_stderr(0);
> > +
> > +	/* process command-line options */
> > +	while ((arg = getopt_long(argc, argv, "hdfs:", longopts,
> > +				  NULL)) != EOF) {
> > +		switch (arg) {
> > +		case 'd':
> > +			xlog_config(D_ALL, 1);
> > +		case 'f':
> > +			xlog_syslog(0);
> > +			xlog_stderr(1);
> > +			break;
> > +		case 's':
> > +			storagedir = optarg;
> > +			break;
> > +		default:
> > +			usage(progname);
> > +			return 0;
> > +		}
> > +	}
> > +
> > +	xlog_open(progname);
> > +
> > +	/* we expect a command, at least */
> > +	if (optind >= argc) {
> > +		xlog(L_ERROR, "Missing command name\n");
> > +		rc = -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	/* drop all capabilities */
> > +	rc = cltrack_set_caps();
> > +	if (rc)
> > +		goto out;
> > +
> > +	cmd = find_cmd(argv[optind]);
> > +	if (!cmd) {
> > +		/*
> > +		 * In the event that we get a command that we don't understand
> > +		 * then return a distinct error. The kernel can use this to
> > +		 * determine a new kernel/old userspace situation and cope
> > +		 * with it.
> > +		 */
> > +		rc = -ENOSYS;
> > +		goto out;
> > +	}
> > +
> > +	/* populate arg var if command needs it */
> > +	if (cmd->needs_arg) {
> > +		if (optind + 1 >= argc) {
> > +			xlog(L_ERROR, "Command %s requires an argument\n",
> > +				cmd->name);
> > +			rc = -EINVAL;
> > +			goto out;
> > +		}
> > +		cmdarg = argv[optind + 1];
> > +	}
> Is needs_arg really necessary? Once the function is found, just pass 
> it the rest of argv and let the function decide if there should
> be an argument or not... but again.. just a nit... 
> 
> steved. 

No, it's not strictly necessary. The alternative though is to make the
command functions do argv processing, which seemed more ugly to me. I
tend to prefer to keep argv processing in a single place since it can
get sort of hairy to work through the bounds checks after getopt and
such.

If you feel strongly about it, I can try to change it, but I don't
think the result will be cleaner.

> > +	rc = cmd->func(cmdarg);
> > +out:
> > +	free(progname);
> > +	return rc;
> > +}
> > 


-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 10/10] nfsdcltrack: flip the default in autoconf to "yes" for it
  2012-10-25 12:57   ` Steve Dickson
@ 2012-10-25 14:07     ` Jeff Layton
  2012-10-25 14:28       ` Steve Dickson
  0 siblings, 1 reply; 18+ messages in thread
From: Jeff Layton @ 2012-10-25 14:07 UTC (permalink / raw)
  To: Steve Dickson; +Cc: linux-nfs

On Thu, 25 Oct 2012 08:57:17 -0400
Steve Dickson <SteveD@redhat.com> wrote:

> 
> 
> On 24/10/12 11:25, Jeff Layton wrote:
> > Allow nfsdcltrack to be built by default if all of the requirements
> > for it are in place. Set the initial state of $enable_nfsdcltrack
> > to "maybe", and fix the appropriate tests to just disable building
> > the binary unless someone explicitly requests it.
> Hmm... I'm not sure I too keen on this "maybe" state... 
> 

Would it help if we renamed it to
"yes_but_only_if_requirements_are_met" ? :)

> So if no flags are given to ./configuration, and not 
> all the requirements to build nfsdcltrack exists, the configuration 
> will succeed, but the command will not be build. Correct?
> 

Correct.

> But if the  --enable_nfsdcltrack flag is given and not all
> the requirements to build nfsdcltrack exist the configuration
> will fail. 
> 

Correct.

> I'm thinking we might want to make it a bit more binary. Either
> on or off. Like it is with the other conditionally built 
> commands... 
> 

So you want to fail the configure stage if all of the requirements for
nfsdcltrack aren't present? That doesn't sound good to me. Note that we
do have "tristate" handling already for stuff like the --disable-uuid
option...

> steved.
>  
> > 
> > Signed-off-by: Jeff Layton <jlayton@redhat.com>
> > ---
> >  configure.ac | 24 +++++++++++++++++-------
> >  1 file changed, 17 insertions(+), 7 deletions(-)
> > 
> > diff --git a/configure.ac b/configure.ac
> > index 65d1bea..aa41e0a 100644
> > --- a/configure.ac
> > +++ b/configure.ac
> > @@ -187,9 +187,9 @@ fi
> >  
> >  AC_ARG_ENABLE(nfsdcltrack,
> >  	[AC_HELP_STRING([--enable-nfsdcltrack],
> > -			[enable NFSv4 clientid tracking programs @<:@default=no@:>@])],
> > +			[enable NFSv4 clientid tracking programs @<:@default=yes@:>@])],
> >  	enable_nfsdctrack=$enableval,
> > -	enable_nfsdcltrack="no")
> > +	enable_nfsdcltrack="maybe")
> >  
> >  dnl Check for TI-RPC library and headers
> >  AC_LIBTIRPC
> > @@ -269,12 +269,22 @@ if test "$enable_nfsv4" = yes; then
> >    dnl Check for sqlite3
> >    AC_SQLITE3_VERS
> >  
> > -  if test "$enable_nfsdcltrack" = "yes"; then
> > +  if test "$enable_nfsdcltrack" != "no"; then
> >  	AC_CHECK_HEADERS([libgen.h sys/inotify.h], ,
> > -		AC_MSG_ERROR([Cannot find header needed for nfsdcltrack]))
> > -
> > -  	if test "$libsqlite3_cv_is_recent" != "yes" ; then
> > +		if test "$enable_nfsdcltrack" = "yes"; then
> > +			AC_MSG_ERROR([Cannot find header needed for nfsdcltrack])
> > +		else
> > +			AC_MSG_WARN([Cannot find header needed for nfsdcltrack. Disabling it.])
> > +			enable_nfsdcltrack="no"
> > +		fi
> > +	)
> > +  fi
> > +  if test "$libsqlite3_cv_is_recent" != "yes" ; then
> > +	if test "$enable_nfsdcltrack" = "yes"; then
> >  		AC_MSG_ERROR([nfsdcltrack requires sqlite3])
> > +	elif test "$enable_nfsdcltrack" != "no"; then
> > +		AC_MSG_WARN([nfsdcltrack requires sqlite3. Disabling it.])
> > +		enable_nfsdcltrack="no"
> >  	fi
> >    fi
> >  
> > @@ -292,7 +302,7 @@ if test "$enable_nfsv41" = yes; then
> >  fi
> >  
> >  dnl enable nfsidmap when its support by libnfsidmap
> > -AM_CONDITIONAL(CONFIG_NFSDCLTRACK, [test "$enable_nfsdcltrack" = "yes" ])
> > +AM_CONDITIONAL(CONFIG_NFSDCLTRACK, [test "$enable_nfsdcltrack" != "no" ])
> >  AM_CONDITIONAL(CONFIG_NFSIDMAP, [test "$ac_cv_header_keyutils_h$ac_cv_lib_nfsidmap_nfs4_owner_to_uid" = "yesyes"])
> >  
> >  
> > 


-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 10/10] nfsdcltrack: flip the default in autoconf to "yes" for it
  2012-10-25 14:07     ` Jeff Layton
@ 2012-10-25 14:28       ` Steve Dickson
  2012-10-25 14:34         ` Jeff Layton
  0 siblings, 1 reply; 18+ messages in thread
From: Steve Dickson @ 2012-10-25 14:28 UTC (permalink / raw)
  To: Jeff Layton; +Cc: linux-nfs



On 25/10/12 10:07, Jeff Layton wrote:
> On Thu, 25 Oct 2012 08:57:17 -0400
> Steve Dickson <SteveD@redhat.com> wrote:
> 
>>
>>
>> On 24/10/12 11:25, Jeff Layton wrote:
>>> Allow nfsdcltrack to be built by default if all of the requirements
>>> for it are in place. Set the initial state of $enable_nfsdcltrack
>>> to "maybe", and fix the appropriate tests to just disable building
>>> the binary unless someone explicitly requests it.
>> Hmm... I'm not sure I too keen on this "maybe" state... 
>>
> 
> Would it help if we renamed it to
> "yes_but_only_if_requirements_are_met" ? :)
:-) 

> 
>> So if no flags are given to ./configuration, and not 
>> all the requirements to build nfsdcltrack exists, the configuration 
>> will succeed, but the command will not be build. Correct?
>>
> 
> Correct.
> 
>> But if the  --enable_nfsdcltrack flag is given and not all
>> the requirements to build nfsdcltrack exist the configuration
>> will fail. 
>>
> 
> Correct.
> 
>> I'm thinking we might want to make it a bit more binary. Either
>> on or off. Like it is with the other conditionally built 
>> commands... 
>>
> 
> So you want to fail the configure stage if all of the requirements for
> nfsdcltrack aren't present? That doesn't sound good to me. Note that we
> do have "tristate" handling already for stuff like the --disable-uuid
> option...
I'm thinking there it might cause confusion to silently not build
a binary, when the expectation is this should be there. I'm thinking
a failure of the config script would remove that confusion... And
as long as there a way to mask that failure out (aka --enable_nfsdcltrack=no
or --disable_nfsdcltrack) it will make it more explicit to what is or
is not happening... 

 
steved. 
> 
>> steved.
>>  
>>>
>>> Signed-off-by: Jeff Layton <jlayton@redhat.com>
>>> ---
>>>  configure.ac | 24 +++++++++++++++++-------
>>>  1 file changed, 17 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/configure.ac b/configure.ac
>>> index 65d1bea..aa41e0a 100644
>>> --- a/configure.ac
>>> +++ b/configure.ac
>>> @@ -187,9 +187,9 @@ fi
>>>  
>>>  AC_ARG_ENABLE(nfsdcltrack,
>>>  	[AC_HELP_STRING([--enable-nfsdcltrack],
>>> -			[enable NFSv4 clientid tracking programs @<:@default=no@:>@])],
>>> +			[enable NFSv4 clientid tracking programs @<:@default=yes@:>@])],
>>>  	enable_nfsdctrack=$enableval,
>>> -	enable_nfsdcltrack="no")
>>> +	enable_nfsdcltrack="maybe")
>>>  
>>>  dnl Check for TI-RPC library and headers
>>>  AC_LIBTIRPC
>>> @@ -269,12 +269,22 @@ if test "$enable_nfsv4" = yes; then
>>>    dnl Check for sqlite3
>>>    AC_SQLITE3_VERS
>>>  
>>> -  if test "$enable_nfsdcltrack" = "yes"; then
>>> +  if test "$enable_nfsdcltrack" != "no"; then
>>>  	AC_CHECK_HEADERS([libgen.h sys/inotify.h], ,
>>> -		AC_MSG_ERROR([Cannot find header needed for nfsdcltrack]))
>>> -
>>> -  	if test "$libsqlite3_cv_is_recent" != "yes" ; then
>>> +		if test "$enable_nfsdcltrack" = "yes"; then
>>> +			AC_MSG_ERROR([Cannot find header needed for nfsdcltrack])
>>> +		else
>>> +			AC_MSG_WARN([Cannot find header needed for nfsdcltrack. Disabling it.])
>>> +			enable_nfsdcltrack="no"
>>> +		fi
>>> +	)
>>> +  fi
>>> +  if test "$libsqlite3_cv_is_recent" != "yes" ; then
>>> +	if test "$enable_nfsdcltrack" = "yes"; then
>>>  		AC_MSG_ERROR([nfsdcltrack requires sqlite3])
>>> +	elif test "$enable_nfsdcltrack" != "no"; then
>>> +		AC_MSG_WARN([nfsdcltrack requires sqlite3. Disabling it.])
>>> +		enable_nfsdcltrack="no"
>>>  	fi
>>>    fi
>>>  
>>> @@ -292,7 +302,7 @@ if test "$enable_nfsv41" = yes; then
>>>  fi
>>>  
>>>  dnl enable nfsidmap when its support by libnfsidmap
>>> -AM_CONDITIONAL(CONFIG_NFSDCLTRACK, [test "$enable_nfsdcltrack" = "yes" ])
>>> +AM_CONDITIONAL(CONFIG_NFSDCLTRACK, [test "$enable_nfsdcltrack" != "no" ])
>>>  AM_CONDITIONAL(CONFIG_NFSIDMAP, [test "$ac_cv_header_keyutils_h$ac_cv_lib_nfsidmap_nfs4_owner_to_uid" = "yesyes"])
>>>  
>>>  
>>>
> 
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 05/10] nfsdcltrack: add a new "one-shot" program for manipulating the client tracking db
  2012-10-25 13:53     ` Jeff Layton
@ 2012-10-25 14:30       ` Steve Dickson
  0 siblings, 0 replies; 18+ messages in thread
From: Steve Dickson @ 2012-10-25 14:30 UTC (permalink / raw)
  To: Jeff Layton; +Cc: linux-nfs



On 25/10/12 09:53, Jeff Layton wrote:
>> Is needs_arg really necessary? Once the function is found, just pass 
>> > it the rest of argv and let the function decide if there should
>> > be an argument or not... but again.. just a nit... 
>> > 
>> > steved. 
> No, it's not strictly necessary. The alternative though is to make the
> command functions do argv processing, which seemed more ugly to me. I
> tend to prefer to keep argv processing in a single place since it can
> get sort of hairy to work through the bounds checks after getopt and
> such.
> 
> If you feel strongly about it, I can try to change it, but I don't
> think the result will be cleaner.
> 
Like I said its a nit... Just a different way to look at it... no
issues here keeping it as is... 

steved.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 10/10] nfsdcltrack: flip the default in autoconf to "yes" for it
  2012-10-25 14:28       ` Steve Dickson
@ 2012-10-25 14:34         ` Jeff Layton
  0 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2012-10-25 14:34 UTC (permalink / raw)
  To: Steve Dickson; +Cc: linux-nfs

On Thu, 25 Oct 2012 10:28:24 -0400
Steve Dickson <SteveD@redhat.com> wrote:

> 
> 
> On 25/10/12 10:07, Jeff Layton wrote:
> > On Thu, 25 Oct 2012 08:57:17 -0400
> > Steve Dickson <SteveD@redhat.com> wrote:
> > 
> >>
> >>
> >> On 24/10/12 11:25, Jeff Layton wrote:
> >>> Allow nfsdcltrack to be built by default if all of the requirements
> >>> for it are in place. Set the initial state of $enable_nfsdcltrack
> >>> to "maybe", and fix the appropriate tests to just disable building
> >>> the binary unless someone explicitly requests it.
> >> Hmm... I'm not sure I too keen on this "maybe" state... 
> >>
> > 
> > Would it help if we renamed it to
> > "yes_but_only_if_requirements_are_met" ? :)
> :-) 
> 
> > 
> >> So if no flags are given to ./configuration, and not 
> >> all the requirements to build nfsdcltrack exists, the configuration 
> >> will succeed, but the command will not be build. Correct?
> >>
> > 
> > Correct.
> > 
> >> But if the  --enable_nfsdcltrack flag is given and not all
> >> the requirements to build nfsdcltrack exist the configuration
> >> will fail. 
> >>
> > 
> > Correct.
> > 
> >> I'm thinking we might want to make it a bit more binary. Either
> >> on or off. Like it is with the other conditionally built 
> >> commands... 
> >>
> > 
> > So you want to fail the configure stage if all of the requirements for
> > nfsdcltrack aren't present? That doesn't sound good to me. Note that we
> > do have "tristate" handling already for stuff like the --disable-uuid
> > option...
> I'm thinking there it might cause confusion to silently not build
> a binary, when the expectation is this should be there. I'm thinking
> a failure of the config script would remove that confusion... And
> as long as there a way to mask that failure out (aka --enable_nfsdcltrack=no
> or --disable_nfsdcltrack) it will make it more explicit to what is or
> is not happening... 
> 
>  

It's not silent. It does throw a warning in this case, but that is
likely to get lost in the noise. It's your call -- if you want to fail
the build by default if the requirements aren't present, then I'm ok
with that.

-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2012-10-25 14:34 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-24 15:25 [PATCH v2 00/10] nfsdcltrack: create a new usermodehelper upcall program for tracking clients Jeff Layton
2012-10-24 15:25 ` [PATCH v2 01/10] nfsdcltrack: fix segfault in sqlite debug logging Jeff Layton
2012-10-24 15:25 ` [PATCH v2 02/10] nfsdcltrack: rename the nfsdcld directory and options to nfsdcltrack Jeff Layton
2012-10-24 15:25 ` [PATCH v2 03/10] nfsdcltrack: remove pointless sqlite_topdir variable Jeff Layton
2012-10-24 15:25 ` [PATCH v2 04/10] nfsdcltrack: break out a function to open the database handle Jeff Layton
2012-10-24 15:25 ` [PATCH v2 05/10] nfsdcltrack: add a new "one-shot" program for manipulating the client tracking db Jeff Layton
2012-10-25 12:56   ` Steve Dickson
2012-10-25 13:53     ` Jeff Layton
2012-10-25 14:30       ` Steve Dickson
2012-10-24 15:25 ` [PATCH v2 06/10] nfsdcltrack: add a legacy transition mechanism Jeff Layton
2012-10-24 15:25 ` [PATCH v2 07/10] nfsdcltrack: add a manpage for nfsdcltrack Jeff Layton
2012-10-24 15:25 ` [PATCH v2 08/10] nfsdcltrack: remove the nfsdcld daemon Jeff Layton
2012-10-24 15:25 ` [PATCH v2 09/10] nfsdcltrack: update the README about server startup order Jeff Layton
2012-10-24 15:25 ` [PATCH v2 10/10] nfsdcltrack: flip the default in autoconf to "yes" for it Jeff Layton
2012-10-25 12:57   ` Steve Dickson
2012-10-25 14:07     ` Jeff Layton
2012-10-25 14:28       ` Steve Dickson
2012-10-25 14:34         ` Jeff Layton

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.