@@ -12,7 +12,8 @@ EXTRA_DIST = mount.x
noinst_LIBRARIES = libexport.a
libexport_a_SOURCES = client.c export.c hostname.c \
xtab.c mount_clnt.c mount_xdr.c \
- cache.c auth.c v4root.c fsloc.c
+ cache.c auth.c v4root.c fsloc.c \
+ v4clients.c
BUILT_SOURCES = $(GENFILES)
noinst_HEADERS = mount.h
@@ -42,13 +42,6 @@
#include "blkid/blkid.h"
#endif
-/*
- * Invoked by RPC service loop
- */
-void cache_set_fds(fd_set *fdset);
-int cache_process_req(fd_set *readfds);
-void cache_process_loop(void);
-
enum nfsd_fsid {
FSID_DEV = 0,
FSID_NUM,
@@ -1537,6 +1530,7 @@ void cache_process_loop(void)
for (;;) {
cache_set_fds(&readfds);
+ v4clients_set_fds(&readfds);
selret = select(FD_SETSIZE, &readfds,
(void *) 0, (void *) 0, (struct timeval *) 0);
@@ -1552,6 +1546,7 @@ void cache_process_loop(void)
default:
cache_process_req(&readfds);
+ v4clients_process(&readfds);
}
}
}
@@ -3,13 +3,14 @@
*
* support/export/export.h
*
- * Declarations for export support
+ * Declarations for export support
*/
#ifndef EXPORT_H
#define EXPORT_H
#include "nfslib.h"
+#include "exportfs.h"
unsigned int auth_reload(void);
nfs_export * auth_authenticate(const char *what,
@@ -17,8 +18,14 @@ nfs_export * auth_authenticate(const char *what,
const char *path);
void cache_open(void);
+void cache_set_fds(fd_set *fdset);
+int cache_process_req(fd_set *readfds);
void cache_process_loop(void);
+void v4clients_init(void);
+void v4clients_set_fds(fd_set *fdset);
+int v4clients_process(fd_set *fdset);
+
struct nfs_fh_len *
cache_get_filehandle(nfs_export *exp, int len, char *p);
int cache_export(nfs_export *exp, char *path);
new file mode 100644
@@ -0,0 +1,177 @@
+/*
+ * support/export/v4clients.c
+ *
+ * Montior clients appearing in, and disappearing from, /proc/fs/nfsd/clients
+ * and log relevant information.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/inotify.h>
+#include <errno.h>
+#include "export.h"
+
+/* search.h declares 'struct entry' and nfs_prot.h
+ * does too. Easiest fix is to trick search.h into
+ * calling its struct "struct Entry".
+ */
+#define entry Entry
+#include <search.h>
+#undef entry
+
+static int clients_fd = -1;
+
+void v4clients_init(void)
+{
+ if (clients_fd >= 0)
+ return;
+ clients_fd = inotify_init1(IN_NONBLOCK);
+ if (clients_fd < 0) {
+ xlog_err("Unable to initialise v4clients watcher: %s\n",
+ strerror(errno));
+ return;
+ }
+ if (inotify_add_watch(clients_fd, "/proc/fs/nfsd/clients",
+ IN_CREATE | IN_DELETE) < 0) {
+ xlog_err("Unable to watch /proc/fs/nfsd/clients: %s\n",
+ strerror(errno));
+ close(clients_fd);
+ clients_fd = -1;
+ return;
+ }
+}
+
+void v4clients_set_fds(fd_set *fdset)
+{
+ if (clients_fd >= 0)
+ FD_SET(clients_fd, fdset);
+}
+
+static void *tree_root;
+
+struct ent {
+ unsigned long num;
+ char *clientid;
+ char *addr;
+ int vers;
+};
+
+static int ent_cmp(const void *av, const void *bv)
+{
+ const struct ent *a = av;
+ const struct ent *b = bv;
+
+ if (a->num < b->num)
+ return -1;
+ if (a->num > b->num)
+ return 1;
+ return 0;
+}
+
+static void free_ent(struct ent *ent)
+{
+ free(ent->clientid);
+ free(ent->addr);
+ free(ent);
+}
+
+static char *dup_line(char *line)
+{
+ char *ret;
+ char *e = strchr(line, '\n');
+ if (!e)
+ e = line + strlen(line);
+ ret = malloc(e - line + 1);
+ if (ret) {
+ memcpy(ret, line, e - line);
+ ret[e-line] = 0;
+ }
+ return ret;
+}
+
+static void add_id(int id)
+{
+ char buf[2048];
+ struct ent **ent;
+ struct ent *key;
+ char *path;
+ FILE *f;
+
+ asprintf(&path, "/proc/fs/nfsd/clients/%d/info", id);
+ f = fopen(path, "r");
+ if (!f) {
+ free(path);
+ return;
+ }
+ key = calloc(1, sizeof(*key));
+ if (!key) {
+ fclose(f);
+ free(path);
+ return;
+ }
+ key->num = id;
+ while (fgets(buf, sizeof(buf), f)) {
+ if (strncmp(buf, "clientid: ", 10) == 0)
+ key->clientid = dup_line(buf+10);
+ if (strncmp(buf, "address: ", 9) == 0)
+ key->addr = dup_line(buf+9);
+ if (strncmp(buf, "minor version: ", 15) == 0)
+ key->vers = atoi(buf+15);
+ }
+ fclose(f);
+ free(path);
+
+ xlog(L_NOTICE, "v4.%d client attached: %s from %s",
+ key->vers, key->clientid, key->addr);
+
+ ent = tsearch(key, &tree_root, ent_cmp);
+
+ if (!ent || *ent != key)
+ /* Already existed, or insertion failed */
+ free_ent(key);
+}
+
+static void del_id(int id)
+{
+ struct ent key = {.num = id};
+ struct ent **e, *ent;
+
+ e = tfind(&key, &tree_root, ent_cmp);
+ if (!e || !*e)
+ return;
+ ent = *e;
+ tdelete(ent, &tree_root, ent_cmp);
+ xlog(L_NOTICE, "v4.%d client detached: %s from %s",
+ ent->vers, ent->clientid, ent->addr);
+ free_ent(ent);
+}
+
+int v4clients_process(fd_set *fdset)
+{
+ char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event))));
+ const struct inotify_event *ev;
+ ssize_t len;
+ char *ptr;
+
+ if (clients_fd < 0 ||
+ !FD_ISSET(clients_fd, fdset))
+ return 0;
+
+ while ((len = read(clients_fd, buf, sizeof(buf))) > 0) {
+ for (ptr = buf; ptr < buf + len;
+ ptr += sizeof(struct inotify_event) + ev->len) {
+ int id;
+ ev = (const struct inotify_event *)ptr;
+
+ id = atoi(ev->name);
+ if (id <= 0)
+ continue;
+ if (ev->mask & IN_CREATE)
+ add_id(id);
+ if (ev->mask & IN_DELETE)
+ del_id(id);
+ }
+ }
+ return 1;
+}
+
@@ -297,6 +297,7 @@ main(int argc, char **argv)
/* Open files now to avoid sharing descriptors among forked processes */
cache_open();
+ v4clients_init();
/* Process incoming upcalls */
cache_process_loop();
@@ -31,6 +31,7 @@
#include "pseudoflavors.h"
#include "nfsd_path.h"
#include "nfslib.h"
+#include "export.h"
extern void my_svc_run(void);
@@ -924,6 +925,7 @@ main(int argc, char **argv)
nfsd_path_init();
/* Open files now to avoid sharing descriptors among forked processes */
cache_open();
+ v4clients_init();
xlog(L_NOTICE, "Version " VERSION " starting");
my_svc_run();
@@ -60,9 +60,4 @@ bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai);
bool namelist_client_matches(nfs_export *exp, char *dom);
bool client_matches(nfs_export *exp, char *dom, struct addrinfo *ai);
-static inline bool is_ipaddr_client(char *dom)
-{
- return dom[0] == '$';
-}
-
#endif /* MOUNTD_H */
@@ -56,10 +56,9 @@
#ifdef HAVE_LIBTIRPC
#include <rpc/rpc_com.h>
#endif
+#include "export.h"
void my_svc_run(void);
-void cache_set_fds(fd_set *fdset);
-int cache_process_req(fd_set *readfds);
#if defined(__GLIBC__) && LONG_MAX != INT_MAX
/* bug in glibc 2.3.6 and earlier, we need
@@ -101,6 +100,7 @@ my_svc_run(void)
readfds = svc_fdset;
cache_set_fds(&readfds);
+ v4clients_set_fds(&readfds);
selret = select(FD_SETSIZE, &readfds,
(void *) 0, (void *) 0, (struct timeval *) 0);
@@ -116,6 +116,7 @@ my_svc_run(void)
default:
selret -= cache_process_req(&readfds);
+ selret -= v4clients_process(&readfds);
if (selret)
svc_getreqset(&readfds);
}
NFSv4 does not have a MOUNT request like NFSv3 does (via the MOUNT protocol). So these cannot be logged. NFSv4 does have SETCLIENTID and EXCHANGE_ID. These are indirectly visible though changes in /proc/fs/nfsd/clients. When a new client attaches, a directory appears. When the client detaches, through a timeout (v4.0) or DESTROY_SESSION (v4.1+) the directory disappears. This patch adds tracking of these changes using inotify, with log messages when a client attaches or detaches. Unfortuantely clients are created in two steps, the second being a confirmation. This results in a temporary client appearing and disappearing. It is not possible (in Linux 5.10) to detect the unconfirmed client, so extra attach/detach messages are generated. This patch also moves some cache* function declarations into a header file, and makes a few related changes to #includes. Signed-off-by: NeilBrown <neilb@suse.de> --- support/export/Makefile.am | 3 - support/export/cache.c | 9 -- support/export/export.h | 9 ++ support/export/v4clients.c | 177 ++++++++++++++++++++++++++++++++++++++++++++ utils/exportd/exportd.c | 1 utils/mountd/mountd.c | 2 utils/mountd/mountd.h | 5 - utils/mountd/svc_run.c | 5 + 8 files changed, 195 insertions(+), 16 deletions(-) create mode 100644 support/export/v4clients.c