]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/nfsdcld/nfsdcld.c
nfsdcld: make it watch for inotify events in the containing directory
[nfs-utils.git] / utils / nfsdcld / nfsdcld.c
index 2d118681abbb5f11809898a072cbea1a5d944bf1..6a1eba8f7d3214ecfbd78f1dc7f6ae34bf644e58 100644 (file)
@@ -32,6 +32,8 @@
 #include <sys/types.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <libgen.h>
+#include <sys/inotify.h>
 
 #include "xlog.h"
 #include "nfslib.h"
@@ -54,6 +56,8 @@ struct cld_client {
 
 /* global variables */
 static char *pipepath = DEFAULT_CLD_PATH;
+static int             inotify_fd = -1;
+static struct event    pipedir_event;
 
 static struct option longopts[] =
 {
@@ -74,6 +78,8 @@ usage(char *progname)
        printf("%s [ -hFd ] [ -p pipe ] [ -s dir ]\n", progname);
 }
 
+#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX)
+
 static int
 cld_pipe_open(struct cld_client *clnt)
 {
@@ -97,18 +103,149 @@ cld_pipe_open(struct cld_client *clnt)
        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);
-       if (ret)
-               return ret;
+       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;
+       }
 
-       event_add(&clnt->cl_event, NULL);
-       return 0;
+       /* 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
@@ -382,6 +519,7 @@ main(int argc, char **argv)
                xlog(L_ERROR, "%s: event_dispatch failed: %m", __func__);
 
        close(clnt.cl_fd);
+       close(inotify_fd);
 out:
        free(progname);
        return rc;