summaryrefslogtreecommitdiff
path: root/source3/smbd/notify_kernel.c
diff options
context:
space:
mode:
authorVolker Lendecke <vlendec@samba.org>2007-01-21 11:49:00 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:17:21 -0500
commitd5206610cd67f88e2cc7d5b2b434e320e81c29d5 (patch)
tree5b085416bd4403cea107f73a1e874199fd1284b1 /source3/smbd/notify_kernel.c
parent57881f749495825f61f8affce921eee46fc7b728 (diff)
downloadsamba-d5206610cd67f88e2cc7d5b2b434e320e81c29d5.tar.gz
samba-d5206610cd67f88e2cc7d5b2b434e320e81c29d5.tar.bz2
samba-d5206610cd67f88e2cc7d5b2b434e320e81c29d5.zip
r20931: This changes the notify infrastructure from a polling-based to an event-driven
based approach. The only remaining hook into the backend is now void *(*notify_add)(TALLOC_CTX *mem_ctx, struct event_context *event_ctx, files_struct *fsp, uint32 *filter); (Should we put this through the VFS, so that others can more easily plug in?) The trick here is that the backend can pick filter bits that the main smbd should not handle anymore. Thanks to tridge for this idea. The backend can notify the main smbd process via void notify_fsp(files_struct *fsp, uint32 action, char *name); The core patch is not big, what makes this more than 1800 lines are the individual backends that are considerably changed but can be reviewed one by one. Based on this I'll continue with inotify now. Volker (This used to be commit 9cd6a8a82792b7b6967141565d043b6337836a5d)
Diffstat (limited to 'source3/smbd/notify_kernel.c')
-rw-r--r--source3/smbd/notify_kernel.c216
1 files changed, 128 insertions, 88 deletions
diff --git a/source3/smbd/notify_kernel.c b/source3/smbd/notify_kernel.c
index 0c20effc3d..0b5784f3d0 100644
--- a/source3/smbd/notify_kernel.c
+++ b/source3/smbd/notify_kernel.c
@@ -3,6 +3,7 @@
Version 3.0
change notify handling - linux kernel based implementation
Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Volker Lendecke 2007
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
@@ -23,10 +24,6 @@
#if HAVE_KERNEL_CHANGE_NOTIFY
-#define FD_PENDING_SIZE 20
-static SIG_ATOMIC_T fd_pending_array[FD_PENDING_SIZE];
-static SIG_ATOMIC_T signals_received;
-
#ifndef DN_ACCESS
#define DN_ACCESS 0x00000001 /* File accessed in directory */
#define DN_MODIFY 0x00000002 /* File modified in directory */
@@ -55,10 +52,16 @@ static SIG_ATOMIC_T signals_received;
determine if a directory has changed.
*****************************************************************************/
-struct change_data {
- int directory_handle;
+struct dnotify_ctx {
+ struct dnotify_ctx *prev, *next;
+
+ int fd;
+ files_struct *fsp;
};
+static struct dnotify_ctx *dnotify_list;
+static int dnotify_signal_pipe[2];
+
/****************************************************************************
The signal handler for change notify.
The Linux kernel has a bug in that we should be able to block any
@@ -68,107 +71,91 @@ struct change_data {
test case for the kernel hackers. JRA.
*****************************************************************************/
-static void signal_handler(int sig, siginfo_t *info, void *unused)
+static void dnotify_signal_handler(int sig, siginfo_t *info, void *unused)
{
- if (signals_received < FD_PENDING_SIZE - 1) {
- fd_pending_array[signals_received] = (SIG_ATOMIC_T)info->si_fd;
- signals_received++;
- } /* Else signal is lost. */
+ int saved_errno;
+
+ /*
+ * According to http://www.opengroup.org/onlinepubs/009695399/ write
+ * to a pipe either writes all or nothing, so we can safely write a
+ * full sizeof(int) and not risk the pipe to become out of sync with
+ * the receiving end.
+ *
+ * We don't care about the result of the write() call. If the pipe is
+ * full, then this signal is lost, we can't do anything about it.
+ */
+
+ saved_errno = errno;
+ write(dnotify_signal_pipe[1], (const void *)&info->si_fd, sizeof(int));
+ errno = saved_errno;
+
sys_select_signal(RT_SIGNAL_NOTIFY);
}
/****************************************************************************
- Check if a change notify should be issued.
- time non-zero means timeout check (used for hash). Ignore this (async method
- where time is zero will be used instead).
+ The upper level handler informed when the pipe is ready for reading
*****************************************************************************/
-static BOOL kernel_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
+static void dnotify_pipe_handler(struct event_context *event_ctx,
+ struct fd_event *event,
+ uint16 flags,
+ void *private_data)
{
- struct change_data *data = (struct change_data *)datap;
- int i;
- BOOL ret = False;
-
- if (t)
- return False;
-
- BlockSignals(True, RT_SIGNAL_NOTIFY);
- for (i = 0; i < signals_received; i++) {
- if (data->directory_handle == (int)fd_pending_array[i]) {
- DEBUG(3,("kernel_check_notify: kernel change notify on %s fd[%d]=%d (signals_received=%d)\n",
- path, i, (int)fd_pending_array[i], (int)signals_received ));
-
- close((int)fd_pending_array[i]);
- fd_pending_array[i] = (SIG_ATOMIC_T)-1;
- if (signals_received - i - 1) {
- memmove((void *)&fd_pending_array[i], (void *)&fd_pending_array[i+1],
- sizeof(SIG_ATOMIC_T)*(signals_received-i-1));
- }
- data->directory_handle = -1;
- signals_received--;
- ret = True;
- break;
- }
+ int res, fd;
+ struct dnotify_ctx *ctx;
+
+ res = read(dnotify_signal_pipe[0], (void *)&fd, sizeof(int));
+
+ if (res == -1) {
+ DEBUG(0, ("Read from the dnotify pipe failed: %s\n",
+ strerror(errno)));
+ TALLOC_FREE(event); /* Don't try again */
+ return;
}
- BlockSignals(False, RT_SIGNAL_NOTIFY);
- return ret;
-}
-/****************************************************************************
- Remove a change notify data structure.
-*****************************************************************************/
+ if (res != sizeof(int)) {
+ smb_panic("read from dnotify pipe gave wrong number of "
+ "bytes\n");
+ }
-static void kernel_remove_notify(void *datap)
-{
- struct change_data *data = (struct change_data *)datap;
- int fd = data->directory_handle;
- if (fd != -1) {
- int i;
- BlockSignals(True, RT_SIGNAL_NOTIFY);
- for (i = 0; i < signals_received; i++) {
- if (fd == (int)fd_pending_array[i]) {
- fd_pending_array[i] = (SIG_ATOMIC_T)-1;
- if (signals_received - i - 1) {
- memmove((void *)&fd_pending_array[i], (void *)&fd_pending_array[i+1],
- sizeof(SIG_ATOMIC_T)*(signals_received-i-1));
- }
- data->directory_handle = -1;
- signals_received--;
- break;
- }
+ for (ctx = dnotify_list; ctx; ctx = ctx->next) {
+ if (ctx->fd == fd) {
+ notify_fsp(ctx->fsp, 0, NULL);
}
- close(fd);
- BlockSignals(False, RT_SIGNAL_NOTIFY);
}
- SAFE_FREE(data);
- DEBUG(3,("kernel_remove_notify: fd=%d\n", fd));
}
/****************************************************************************
Register a change notify request.
*****************************************************************************/
-static void *kernel_register_notify(connection_struct *conn, char *path, uint32 flags)
+static int kernel_register_notify(connection_struct *conn, char *path,
+ uint32 flags)
{
- struct change_data data;
int fd;
unsigned long kernel_flags;
fd = sys_open(path,O_RDONLY, 0);
if (fd == -1) {
- DEBUG(3,("Failed to open directory %s for change notify\n", path));
- return NULL;
+ DEBUG(3,("Failed to open directory %s for change notify\n",
+ path));
+ return -1;
}
if (sys_fcntl_long(fd, F_SETSIG, RT_SIGNAL_NOTIFY) == -1) {
DEBUG(3,("Failed to set signal handler for change notify\n"));
- return NULL;
+ close(fd);
+ return -1;
}
- kernel_flags = DN_CREATE|DN_DELETE|DN_RENAME; /* creation/deletion changes everything! */
+ kernel_flags = DN_CREATE|DN_DELETE|DN_RENAME; /* creation/deletion
+ * changes
+ * everything! */
if (flags & FILE_NOTIFY_CHANGE_FILE) kernel_flags |= DN_MODIFY;
- if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags |= DN_RENAME|DN_DELETE;
+ if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags
+ |= DN_RENAME
+ |DN_DELETE;
if (flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) kernel_flags |= DN_ATTRIB;
if (flags & FILE_NOTIFY_CHANGE_SIZE) kernel_flags |= DN_MODIFY;
if (flags & FILE_NOTIFY_CHANGE_LAST_WRITE) kernel_flags |= DN_MODIFY;
@@ -176,19 +163,20 @@ static void *kernel_register_notify(connection_struct *conn, char *path, uint32
if (flags & FILE_NOTIFY_CHANGE_CREATION) kernel_flags |= DN_CREATE;
if (flags & FILE_NOTIFY_CHANGE_SECURITY) kernel_flags |= DN_ATTRIB;
if (flags & FILE_NOTIFY_CHANGE_EA) kernel_flags |= DN_ATTRIB;
- if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags |= DN_RENAME|DN_DELETE;
+ if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags
+ |= DN_RENAME
+ |DN_DELETE;
if (sys_fcntl_long(fd, F_NOTIFY, kernel_flags) == -1) {
DEBUG(3,("Failed to set async flag for change notify\n"));
- return NULL;
+ close(fd);
+ return -1;
}
- data.directory_handle = fd;
-
- DEBUG(3,("kernel change notify on %s (ntflags=0x%x flags=0x%x) fd=%d\n",
- path, (int)flags, (int)kernel_flags, fd));
+ DEBUG(3,("kernel change notify on %s (ntflags=0x%x flags=0x%x) "
+ "fd=%d\n", path, (int)flags, (int)kernel_flags, fd));
- return (void *)memdup(&data, sizeof(data));
+ return fd;
}
/****************************************************************************
@@ -206,18 +194,74 @@ static BOOL kernel_notify_available(void)
return ret == 0;
}
+static int dnotify_ctx_destructor(struct dnotify_ctx *ctx)
+{
+ close(ctx->fd);
+ DLIST_REMOVE(dnotify_list, ctx);
+ return 0;
+}
+
+static void *kernel_notify_add(TALLOC_CTX *mem_ctx,
+ struct event_context *event_ctx,
+ files_struct *fsp,
+ uint32 *filter)
+{
+ struct dnotify_ctx *ctx;
+
+ if (!(ctx = TALLOC_P(mem_ctx, struct dnotify_ctx))) {
+ DEBUG(0, ("talloc failed\n"));
+ return NULL;
+ }
+
+ ctx->fsp = fsp;
+ ctx->fd = kernel_register_notify(fsp->conn, fsp->fsp_name, *filter);
+
+ if (ctx->fd == -1) {
+ TALLOC_FREE(ctx);
+ return NULL;
+ }
+
+ DLIST_ADD(dnotify_list, ctx);
+ talloc_set_destructor(ctx, dnotify_ctx_destructor);
+
+ return ctx;
+}
+
/****************************************************************************
Setup kernel based change notify.
****************************************************************************/
-struct cnotify_fns *kernel_notify_init(void)
+struct cnotify_fns *kernel_notify_init(struct event_context *event_ctx)
{
static struct cnotify_fns cnotify;
struct sigaction act;
+ if (pipe(dnotify_signal_pipe) == -1) {
+ DEBUG(0, ("Failed to create signal pipe: %s\n",
+ strerror(errno)));
+ return NULL;
+ }
+
+ if ((set_blocking(dnotify_signal_pipe[0], False) == -1)
+ || (set_blocking(dnotify_signal_pipe[1], False) == -1)) {
+ DEBUG(0, ("Failed to set signal pipe to non-blocking: %s\n",
+ strerror(errno)));
+ close(dnotify_signal_pipe[0]);
+ close(dnotify_signal_pipe[1]);
+ return NULL;
+ }
+
+ if (event_add_fd(event_ctx, NULL, dnotify_signal_pipe[0],
+ EVENT_FD_READ, dnotify_pipe_handler, NULL) == NULL) {
+ DEBUG(0, ("Failed to set signal event handler\n"));
+ close(dnotify_signal_pipe[0]);
+ close(dnotify_signal_pipe[1]);
+ return NULL;
+ }
+
ZERO_STRUCT(act);
- act.sa_sigaction = signal_handler;
+ act.sa_sigaction = dnotify_signal_handler;
act.sa_flags = SA_SIGINFO;
sigemptyset( &act.sa_mask );
if (sigaction(RT_SIGNAL_NOTIFY, &act, NULL) != 0) {
@@ -228,11 +272,7 @@ struct cnotify_fns *kernel_notify_init(void)
if (!kernel_notify_available())
return NULL;
- cnotify.register_notify = kernel_register_notify;
- cnotify.check_notify = kernel_check_notify;
- cnotify.remove_notify = kernel_remove_notify;
- cnotify.select_time = -1;
- cnotify.notification_fd = -1;
+ cnotify.notify_add = kernel_notify_add;
/* the signal can start off blocked due to a bug in bash */
BlockSignals(False, RT_SIGNAL_NOTIFY);