[PATCH] notifyd

Volker Lendecke Volker.Lendecke at SerNet.DE
Fri Jun 19 05:36:23 MDT 2015


Hi!

Attached find the latest incarnation of the notifyd I've
been talking about for a while now. Survived a few private
autobuilds.

Review appreciated!

Thanks,

Volker

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
http://www.sernet.de, mailto:kontakt at sernet.de
-------------- next part --------------
From ff8b3fb7bf40a83947c2f41cafaac4fe0232dff5 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 9 Jul 2014 12:50:24 +0000
Subject: [PATCH 01/20] lib: Add tevent_req_poll_unix

This makes sync wrappers a bit shorter

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/util/tevent_unix.c |   10 ++++++++++
 lib/util/tevent_unix.h |    2 ++
 2 files changed, 12 insertions(+)

diff --git a/lib/util/tevent_unix.c b/lib/util/tevent_unix.c
index ee7ec8a..63bdaf6 100644
--- a/lib/util/tevent_unix.c
+++ b/lib/util/tevent_unix.c
@@ -61,3 +61,13 @@ int tevent_req_simple_recv_unix(struct tevent_req *req)
 	tevent_req_received(req);
 	return err;
 }
+
+bool tevent_req_poll_unix(struct tevent_req *req, struct tevent_context *ev,
+			  int *err)
+{
+	bool ret = tevent_req_poll(req, ev);
+	if (!ret) {
+		*err = errno;
+	}
+	return ret;
+}
diff --git a/lib/util/tevent_unix.h b/lib/util/tevent_unix.h
index c1b0eb4..39689d6 100644
--- a/lib/util/tevent_unix.h
+++ b/lib/util/tevent_unix.h
@@ -28,5 +28,7 @@
 
 bool tevent_req_is_unix_error(struct tevent_req *req, int *perrno);
 int tevent_req_simple_recv_unix(struct tevent_req *req);
+bool tevent_req_poll_unix(struct tevent_req *req, struct tevent_context *ev,
+			  int *err);
 
 #endif
-- 
1.7.9.5


From 2d628ffdfa3bef49872e43d690cea5a40661fc32 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 26 Apr 2015 11:02:27 +0200
Subject: [PATCH 02/20] lib: Add server_id_db_prune_name

With this you can remove a foreign mapping. Required to clean up dead
processes.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/util/server_id_db.c |   31 ++++++++++++++++++++-----------
 lib/util/server_id_db.h |    2 ++
 2 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/lib/util/server_id_db.c b/lib/util/server_id_db.c
index 7f5b055..8354752 100644
--- a/lib/util/server_id_db.c
+++ b/lib/util/server_id_db.c
@@ -118,22 +118,18 @@ int server_id_db_add(struct server_id_db *db, const char *name)
 	return 0;
 }
 
-int server_id_db_remove(struct server_id_db *db, const char *name)
+int server_id_db_prune_name(struct server_id_db *db, const char *name,
+			    struct server_id server)
 {
 	struct tdb_context *tdb = db->tdb->tdb;
 	struct server_id_buf buf;
 	TDB_DATA key;
 	uint8_t *data;
-	char *ids, *n, *id;
+	char *ids, *id;
 	int ret;
 
-	n = strv_find(db->names, name);
-	if (n == NULL) {
-		return ENOENT;
-	}
-
 	key = string_term_tdb_data(name);
-	server_id_str_buf(db->pid, &buf);
+	server_id_str_buf(server, &buf);
 
 	ret = tdb_chainlock(tdb, key);
 	if (ret == -1) {
@@ -162,9 +158,22 @@ int server_id_db_remove(struct server_id_db *db, const char *name)
 
 	tdb_chainunlock(tdb, key);
 
-	if (ret == -1) {
-		enum TDB_ERROR err = tdb_error(tdb);
-		return map_unix_error_from_tdb(err);
+	return 0;
+}
+
+int server_id_db_remove(struct server_id_db *db, const char *name)
+{
+	char *n;
+	int ret;
+
+	n = strv_find(db->names, name);
+	if (n == NULL) {
+		return ENOENT;
+	}
+
+	ret = server_id_db_prune_name(db, name, db->pid);
+	if (ret != 0) {
+		return ret;
 	}
 
 	strv_delete(&db->names, n);
diff --git a/lib/util/server_id_db.h b/lib/util/server_id_db.h
index 31b3305..ff86436 100644
--- a/lib/util/server_id_db.h
+++ b/lib/util/server_id_db.h
@@ -32,6 +32,8 @@ struct server_id_db *server_id_db_init(TALLOC_CTX *mem_ctx,
 void server_id_db_reinit(struct server_id_db *db, struct server_id pid);
 int server_id_db_add(struct server_id_db *db, const char *name);
 int server_id_db_remove(struct server_id_db *db, const char *name);
+int server_id_db_prune_name(struct server_id_db *db, const char *name,
+			    struct server_id server);
 int server_id_db_lookup(struct server_id_db *db, const char *name,
 			TALLOC_CTX *mem_ctx, unsigned *num_servers,
 			struct server_id **servers);
-- 
1.7.9.5


From 820b2526cebf3cafd244a6421622e74f82090a54 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 8 Jun 2015 20:46:54 +0000
Subject: [PATCH 03/20] lib: Add server_id_db_pid()

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/util/server_id_db.c |    5 +++++
 lib/util/server_id_db.h |    1 +
 2 files changed, 6 insertions(+)

diff --git a/lib/util/server_id_db.c b/lib/util/server_id_db.c
index 8354752..0874129 100644
--- a/lib/util/server_id_db.c
+++ b/lib/util/server_id_db.c
@@ -74,6 +74,11 @@ void server_id_db_reinit(struct server_id_db *db, struct server_id pid)
 	TALLOC_FREE(db->names);
 }
 
+struct server_id server_id_db_pid(struct server_id_db *db)
+{
+	return db->pid;
+}
+
 static int server_id_db_destructor(struct server_id_db *db)
 {
 	char *name = NULL;
diff --git a/lib/util/server_id_db.h b/lib/util/server_id_db.h
index ff86436..2dcce62 100644
--- a/lib/util/server_id_db.h
+++ b/lib/util/server_id_db.h
@@ -30,6 +30,7 @@ struct server_id_db *server_id_db_init(TALLOC_CTX *mem_ctx,
 				       const char *base_path,
 				       int hash_size, int tdb_flags);
 void server_id_db_reinit(struct server_id_db *db, struct server_id pid);
+struct server_id server_id_db_pid(struct server_id_db *db);
 int server_id_db_add(struct server_id_db *db, const char *name);
 int server_id_db_remove(struct server_id_db *db, const char *name);
 int server_id_db_prune_name(struct server_id_db *db, const char *name,
-- 
1.7.9.5


From c56a988c78df88c62da6411f0338ef413517065b Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 9 Jun 2015 05:03:25 +0000
Subject: [PATCH 04/20] lib: Add server_id_db_set_exclusive

This is used for server names where only one instance can exist.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/server_id_db_util.c |  104 +++++++++++++++++++++++++++++++++++++++
 source3/lib/server_id_db_util.h |   22 +++++++++
 source3/wscript_build           |    1 +
 3 files changed, 127 insertions(+)
 create mode 100644 source3/lib/server_id_db_util.c
 create mode 100644 source3/lib/server_id_db_util.h

diff --git a/source3/lib/server_id_db_util.c b/source3/lib/server_id_db_util.c
new file mode 100644
index 0000000..ead9ed3
--- /dev/null
+++ b/source3/lib/server_id_db_util.c
@@ -0,0 +1,104 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Utils around server_id_db with more dependencies
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "server_id_db_util.h"
+#include "serverid.h"
+
+static int server_id_db_check_exclusive(
+	struct server_id_db *db, const char *name,
+	unsigned num_servers, struct server_id *servers);
+
+int server_id_db_set_exclusive(struct server_id_db *db, const char *name)
+{
+	int ret;
+	unsigned num_servers;
+	struct server_id *servers;
+
+	ret = server_id_db_add(db, name);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = server_id_db_lookup(db, name, talloc_tos(),
+				  &num_servers, &servers);
+	if (ret != 0) {
+		goto done;
+	}
+
+	/*
+	 * Remove entries from the server_id_db for processes that have died
+	 * and could not clean up. This is racy, as two processes could
+	 * simultaneously try to register a name. Both would succeed in the
+	 * server_id_db_add call, and both would see their peer active during
+	 * the check_exclusive call. Both would get an EEXIST, and nobody
+	 * would be able to register itself. But this is okay, as this is
+	 * meant to be a cleanup routine, and normally only one daemon should
+	 * start up at a time anyway. Getting this "right" would mean we would
+	 * have to add locking to server_id_db, or add a dependency on
+	 * serverids_exist to server_id_db. Both are too heavy-weight for my
+	 * taste.
+	 */
+
+	ret = server_id_db_check_exclusive(db, name, num_servers, servers);
+	TALLOC_FREE(servers);
+
+done:
+	if (ret != 0) {
+		server_id_db_remove(db, name);
+	}
+	return ret;
+}
+
+static int server_id_db_check_exclusive(
+	struct server_id_db *db, const char *name,
+	unsigned num_servers, struct server_id *servers)
+{
+	struct server_id me = server_id_db_pid(db);
+	bool exists[num_servers];
+	bool ok;
+	int i;
+
+	ok = serverids_exist(servers, num_servers, exists);
+	if (!ok) {
+		return ENOMEM;
+	}
+
+	for (i=0; i<num_servers; i++) {
+		int ret;
+
+		if (server_id_same_process(&me, &servers[i])) {
+			/*
+			 * I am always around ... :-)
+			 */
+			continue;
+		}
+
+		if (exists[i]) {
+			return EEXIST;
+		}
+
+		ret = server_id_db_prune_name(db, name, servers[i]);
+		if (ret != 0) {
+			return ret;
+		}
+	}
+
+	return 0;
+}
diff --git a/source3/lib/server_id_db_util.h b/source3/lib/server_id_db_util.h
new file mode 100644
index 0000000..5fdd078
--- /dev/null
+++ b/source3/lib/server_id_db_util.h
@@ -0,0 +1,22 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Utils around server_id_db with more dependencies
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "lib/util/server_id_db.h"
+
+int server_id_db_set_exclusive(struct server_id_db *db, const char *name);
diff --git a/source3/wscript_build b/source3/wscript_build
index 231f1c0..3b44ace 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -320,6 +320,7 @@ bld.SAMBA3_SUBSYSTEM('samba3core',
                    lib/id_cache.c
                    lib/talloc_dict.c
                    lib/serverid.c
+                   lib/server_id_db_util.c
                    lib/addrchange.c
                    ../lib/util/debug_s3.c
                    lib/dumpcore.c
-- 
1.7.9.5


From 3951c6e582126527355cf1812c7c1f408bc73e17 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 15:53:53 +0100
Subject: [PATCH 05/20] param: Make "change notify" global

With a central notifyd, we can't do this per share anymore. Notifyd will
only look at absolute paths, not shares.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 docs-xml/smbdotconf/misc/changenotify.xml |    3 +--
 lib/param/param_table.c                   |    4 ++--
 source3/param/loadparm.c                  |    3 ++-
 source3/smbd/service.c                    |    2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/docs-xml/smbdotconf/misc/changenotify.xml b/docs-xml/smbdotconf/misc/changenotify.xml
index 1344dce..70793d6 100644
--- a/docs-xml/smbdotconf/misc/changenotify.xml
+++ b/docs-xml/smbdotconf/misc/changenotify.xml
@@ -1,7 +1,6 @@
 <samba:parameter name="change notify"
-                 context="S"
+                 context="G"
 				 type="boolean"
-                 parm="1"
                  xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
 <description>
 	<para>This parameter specifies whether Samba should reply
diff --git a/lib/param/param_table.c b/lib/param/param_table.c
index 287839f..f80dce6 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -1651,8 +1651,8 @@ struct parm_struct parm_table[] = {
 	{
 		.label		= "change notify",
 		.type		= P_BOOL,
-		.p_class	= P_LOCAL,
-		.offset		= LOCAL_VAR(change_notify),
+		.p_class	= P_GLOBAL,
+		.offset		= GLOBAL_VAR(change_notify),
 		.special	= NULL,
 		.enum_list	= NULL,
 	},
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index cedaf1a..bd24844 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -238,7 +238,6 @@ static struct loadparm_service sDefault =
 	.acl_map_full_control = true,
 	.acl_group_control = false,
 	.acl_allow_execute_always = false,
-	.change_notify = true,
 	.kernel_change_notify = true,
 	.allocation_roundup_size = SMB_ROUNDUP_ALLOCATION_SIZE,
 	.aio_read_size = 0,
@@ -721,6 +720,8 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals)
 	   a large number of sites (tridge) */
 	Globals.hostname_lookups = false;
 
+	Globals.change_notify = true,
+
 	string_set(Globals.ctx, &Globals.passdb_backend, "tdbsam");
 	string_set(Globals.ctx, &Globals.ldap_suffix, "");
 	string_set(Globals.ctx, &Globals.szLdapMachineSuffix, "");
diff --git a/source3/smbd/service.c b/source3/smbd/service.c
index d1148e1..0544967 100644
--- a/source3/smbd/service.c
+++ b/source3/smbd/service.c
@@ -678,7 +678,7 @@ static NTSTATUS make_connection_snum(struct smbXsrv_connection *xconn,
 	on_err_call_dis_hook = true;
 
 	if ((!conn->printer) && (!conn->ipc) &&
-	    lp_change_notify(conn->params)) {
+	    lp_change_notify()) {
 		if (sconn->notify_ctx == NULL) {
 			sconn->notify_ctx = notify_init(
 				sconn, sconn->msg_ctx, sconn->ev_ctx);
-- 
1.7.9.5


From f28b89a6cce67f3c58979449b81a2519eb1c28c0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 16:02:27 +0100
Subject: [PATCH 06/20] param: Make "kernel change notify" global

With a central notifyd, we can't do this per share anymore. Notifyd will
only look at absolute paths, not shares.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 docs-xml/smbdotconf/misc/kernelchangenotify.xml |    3 +--
 lib/param/param_table.c                         |    4 ++--
 source3/modules/vfs_default.c                   |    2 +-
 source3/param/loadparm.c                        |    2 +-
 4 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/docs-xml/smbdotconf/misc/kernelchangenotify.xml b/docs-xml/smbdotconf/misc/kernelchangenotify.xml
index 1e11bb8..6a41dcb 100644
--- a/docs-xml/smbdotconf/misc/kernelchangenotify.xml
+++ b/docs-xml/smbdotconf/misc/kernelchangenotify.xml
@@ -1,7 +1,6 @@
 <samba:parameter name="kernel change notify"
-                 context="S"
+                 context="G"
 				 type="boolean"
-                 parm="1"
                  xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
 <description>
 	<para>This parameter specifies whether Samba should ask the 
diff --git a/lib/param/param_table.c b/lib/param/param_table.c
index f80dce6..58f32a0 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -1667,8 +1667,8 @@ struct parm_struct parm_table[] = {
 	{
 		.label		= "kernel change notify",
 		.type		= P_BOOL,
-		.p_class	= P_LOCAL,
-		.offset		= LOCAL_VAR(kernel_change_notify),
+		.p_class	= P_GLOBAL,
+		.offset		= GLOBAL_VAR(kernel_change_notify),
 		.special	= NULL,
 		.enum_list	= NULL,
 	},
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 9b434a0..8e4953e 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2145,7 +2145,7 @@ static NTSTATUS vfswrap_notify_watch(vfs_handle_struct *vfs_handle,
 	 * Until that is the case, hard-code inotify here.
 	 */
 #ifdef HAVE_INOTIFY
-	if (lp_kernel_change_notify(vfs_handle->conn->params)) {
+	if (lp_kernel_change_notify()) {
 		int ret;
 		if (!lp_parm_bool(-1, "notify", "inotify", True)) {
 			return NT_STATUS_INVALID_SYSTEM_SERVICE;
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index bd24844..710e36c 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -238,7 +238,6 @@ static struct loadparm_service sDefault =
 	.acl_map_full_control = true,
 	.acl_group_control = false,
 	.acl_allow_execute_always = false,
-	.kernel_change_notify = true,
 	.allocation_roundup_size = SMB_ROUNDUP_ALLOCATION_SIZE,
 	.aio_read_size = 0,
 	.aio_write_size = 0,
@@ -721,6 +720,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals)
 	Globals.hostname_lookups = false;
 
 	Globals.change_notify = true,
+	Globals.kernel_change_notify = true,
 
 	string_set(Globals.ctx, &Globals.passdb_backend, "tdbsam");
 	string_set(Globals.ctx, &Globals.ldap_suffix, "");
-- 
1.7.9.5


From 9d63149c6ca2f579832212b87ed186adb5d2f406 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 20 Nov 2014 15:30:51 +0000
Subject: [PATCH 07/20] smbd: Add direct notify_fam support

notifyd won't have the VFS around, it is a systemwide daemon without
a connection to specific shares. To continue FAM support, notifyd
needs to be able to link it directly. This adds code to make fam
equivalent to inotify.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify_fam.c |  305 +++++++++++++++++++++++++++++++++++++++++++++
 source3/smbd/proto.h      |   12 ++
 source3/wscript_build     |    7 +-
 3 files changed, 323 insertions(+), 1 deletion(-)
 create mode 100644 source3/smbd/notify_fam.c

diff --git a/source3/smbd/notify_fam.c b/source3/smbd/notify_fam.c
new file mode 100644
index 0000000..bf9050d
--- /dev/null
+++ b/source3/smbd/notify_fam.c
@@ -0,0 +1,305 @@
+/*
+ * FAM file notification support.
+ *
+ * Copyright (c) James Peach 2005
+ * 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
+ * the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "smbd/smbd.h"
+#include "librpc/gen_ndr/notify.h"
+
+#include <fam.h>
+
+#if !defined(HAVE_FAM_H_FAMCODES_TYPEDEF)
+/* Gamin provides this typedef which means we can't use 'enum FAMCodes' as per
+ * every other FAM implementation. Phooey.
+ */
+typedef enum FAMCodes FAMCodes;
+#endif
+
+/* NOTE: There are multiple versions of FAM floating around the net, each with
+ * slight differences from the original SGI FAM implementation. In this file,
+ * we rely only on the SGI features and do not assume any extensions. For
+ * example, we do not look at FAMErrno, because it is not set by the original
+ * implementation.
+ *
+ * Random FAM links:
+ *	http://oss.sgi.com/projects/fam/
+ *	http://savannah.nongnu.org/projects/fam/
+ *	http://sourceforge.net/projects/bsdfam/
+ */
+
+/* ------------------------------------------------------------------------- */
+
+struct fam_watch_context {
+	struct fam_watch_context *prev, *next;
+	FAMConnection *fam_connection;
+	struct FAMRequest fr;
+	struct sys_notify_context *sys_ctx;
+	void (*callback)(struct sys_notify_context *ctx,
+			 void *private_data,
+			 struct notify_event *ev);
+	void *private_data;
+	uint32_t mask; /* the inotify mask */
+	uint32_t filter; /* the windows completion filter */
+	const char *path;
+};
+
+
+/*
+ * We want one FAM connection per smbd, not one per tcon.
+ */
+static FAMConnection fam_connection;
+static bool fam_connection_initialized = False;
+
+static struct fam_watch_context *fam_notify_list;
+static void fam_handler(struct tevent_context *event_ctx,
+			struct tevent_fd *fd_event,
+			uint16_t flags,
+			void *private_data);
+
+static NTSTATUS fam_open_connection(FAMConnection *fam_conn,
+				    struct tevent_context *event_ctx)
+{
+	int res;
+	char *name;
+
+	ZERO_STRUCTP(fam_conn);
+	FAMCONNECTION_GETFD(fam_conn) = -1;
+
+
+#ifdef HAVE_FAMNOEXISTS
+	/* We should honor outside setting of the GAM_CLIENT_ID. */
+	setenv("GAM_CLIENT_ID","SAMBA",0);
+#endif
+
+	if (asprintf(&name, "smbd (%lu)", (unsigned long)getpid()) == -1) {
+		DEBUG(0, ("No memory\n"));
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	res = FAMOpen2(fam_conn, name);
+
+#ifdef HAVE_FAMNOEXISTS
+	/*
+	 * This reduces the chatter between GAMIN and samba making the pair
+	 * much more reliable.
+	 */
+	FAMNoExists(fam_conn);
+#endif
+
+	SAFE_FREE(name);
+
+	if (res < 0) {
+		DEBUG(10, ("FAM file change notifications not available: %s\n",
+			   FamErrlist[-res]));
+		/*
+		 * No idea how to get NT_STATUS from a FAM result
+		 */
+		FAMCONNECTION_GETFD(fam_conn) = -1;
+		return NT_STATUS_UNEXPECTED_IO_ERROR;
+	}
+
+	if (tevent_add_fd(event_ctx, event_ctx,
+			 FAMCONNECTION_GETFD(fam_conn),
+			 TEVENT_FD_READ, fam_handler,
+			 (void *)fam_conn) == NULL) {
+		DEBUG(0, ("event_add_fd failed\n"));
+		FAMClose(fam_conn);
+		FAMCONNECTION_GETFD(fam_conn) = -1;
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	return NT_STATUS_OK;
+}
+
+static void fam_reopen(FAMConnection *fam_conn,
+		       struct tevent_context *event_ctx,
+		       struct fam_watch_context *notify_list)
+{
+	struct fam_watch_context *ctx;
+
+	DEBUG(5, ("Re-opening FAM connection\n"));
+
+	FAMClose(fam_conn);
+
+	if (!NT_STATUS_IS_OK(fam_open_connection(fam_conn, event_ctx))) {
+		DEBUG(5, ("Re-opening fam connection failed\n"));
+		return;
+	}
+
+	for (ctx = notify_list; ctx; ctx = ctx->next) {
+		FAMMonitorDirectory(fam_conn, ctx->path, &ctx->fr, NULL);
+	}
+}
+
+static void fam_handler(struct tevent_context *event_ctx,
+			struct tevent_fd *fd_event,
+			uint16_t flags,
+			void *private_data)
+{
+	FAMConnection *fam_conn = (FAMConnection *)private_data;
+	FAMEvent fam_event;
+	struct fam_watch_context *ctx;
+	struct notify_event ne;
+
+	if (FAMPending(fam_conn) == 0) {
+		DEBUG(10, ("fam_handler called but nothing pending\n"));
+		return;
+	}
+
+	if (FAMNextEvent(fam_conn, &fam_event) != 1) {
+		DEBUG(5, ("FAMNextEvent returned an error\n"));
+		TALLOC_FREE(fd_event);
+		fam_reopen(fam_conn, event_ctx, fam_notify_list);
+		return;
+	}
+
+	DEBUG(10, ("Got FAMCode %d for %s\n", fam_event.code,
+		   fam_event.filename));
+
+	switch (fam_event.code) {
+	case FAMChanged:
+		ne.action = NOTIFY_ACTION_MODIFIED;
+		break;
+	case FAMCreated:
+		ne.action = NOTIFY_ACTION_ADDED;
+		break;
+	case FAMDeleted:
+		ne.action = NOTIFY_ACTION_REMOVED;
+		break;
+	default:
+		DEBUG(10, ("Ignoring code FAMCode %d for file %s\n",
+			   (int)fam_event.code, fam_event.filename));
+		return;
+	}
+
+	for (ctx = fam_notify_list; ctx; ctx = ctx->next) {
+		if (memcmp(&fam_event.fr, &ctx->fr, sizeof(FAMRequest)) == 0) {
+			break;
+		}
+	}
+
+	if (ctx == NULL) {
+		DEBUG(5, ("Discarding event for file %s\n",
+			  fam_event.filename));
+		return;
+	}
+
+	if ((ne.path = strrchr_m(fam_event.filename, '\\')) == NULL) {
+		ne.path = fam_event.filename;
+	}
+	ne.dir = ctx->path;
+
+	ctx->callback(ctx->sys_ctx, ctx->private_data, &ne);
+}
+
+static int fam_watch_context_destructor(struct fam_watch_context *ctx)
+{
+	if (FAMCONNECTION_GETFD(ctx->fam_connection) != -1) {
+		FAMCancelMonitor(&fam_connection, &ctx->fr);
+	}
+	DLIST_REMOVE(fam_notify_list, ctx);
+	return 0;
+}
+
+/*
+  add a watch. The watch is removed when the caller calls
+  talloc_free() on *handle
+*/
+int _fam_watch(TALLOC_CTX *mem_ctx,
+	      struct sys_notify_context *ctx,
+	      const char *path,
+	      uint32_t *filter,
+	      uint32_t *subdir_filter,
+	      void (*callback)(struct sys_notify_context *ctx,
+			       void *private_data,
+			       struct notify_event *ev),
+	      void *private_data,
+	      void *handle_p)
+{
+	const uint32_t fam_mask = (FILE_NOTIFY_CHANGE_FILE_NAME|
+				   FILE_NOTIFY_CHANGE_DIR_NAME);
+	struct fam_watch_context *watch;
+	void **handle = (void **)handle_p;
+
+	*handle = NULL;
+
+	if ((*filter & fam_mask) == 0) {
+		DEBUG(10, ("filter = %u, ignoring in FAM\n", *filter));
+		return 0;
+	}
+
+	if (!fam_connection_initialized) {
+		if (!NT_STATUS_IS_OK(fam_open_connection(&fam_connection,
+							 ctx->ev))) {
+			/*
+			 * Just let smbd do all the work itself
+			 */
+			return 0;
+		}
+		fam_connection_initialized = True;
+	}
+
+	if (!(watch = talloc(mem_ctx, struct fam_watch_context))) {
+		return ENOMEM;
+	}
+
+	watch->fam_connection = &fam_connection;
+
+	watch->callback = callback;
+	watch->private_data = private_data;
+	watch->sys_ctx = ctx;
+
+	watch->path = talloc_strdup(watch, path);
+	if (watch->path == NULL) {
+		DEBUG(0, ("talloc_asprintf failed\n"));
+		TALLOC_FREE(watch);
+		return ENOMEM;
+	}
+
+	/*
+	 * The FAM module in this early state will only take care of
+	 * FAMCreated and FAMDeleted events, Leave the rest to notifyd
+	 */
+
+	watch->filter = fam_mask;
+	*filter &= ~fam_mask;
+
+	DLIST_ADD(fam_notify_list, watch);
+	talloc_set_destructor(watch, fam_watch_context_destructor);
+
+	/*
+	 * Only directories monitored so far
+	 */
+
+	if (FAMCONNECTION_GETFD(watch->fam_connection) != -1) {
+		FAMMonitorDirectory(watch->fam_connection, watch->path,
+				    &watch->fr, NULL);
+	}
+	else {
+		/*
+		 * If the re-open is successful, this will establish the
+		 * FAMMonitor from the list
+		 */
+		fam_reopen(watch->fam_connection, ctx->ev, fam_notify_list);
+	}
+
+	*handle = watch;
+
+	return 0;
+}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 531b58d..e91c355 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -543,6 +543,18 @@ int inotify_watch(TALLOC_CTX *mem_ctx,
 		  void *private_data,
 		  void *handle_p);
 
+int _fam_watch(TALLOC_CTX *mem_ctx,
+	      struct sys_notify_context *ctx,
+	      const char *path,
+	      uint32_t *filter,
+	      uint32_t *subdir_filter,
+	      void (*callback)(struct sys_notify_context *ctx,
+			       void *private_data,
+			       struct notify_event *ev),
+	      void *private_data,
+	      void *handle_p);
+
+
 /* The following definitions come from smbd/notify_internal.c  */
 
 struct notify_context *notify_init(TALLOC_CTX *mem_ctx,
diff --git a/source3/wscript_build b/source3/wscript_build
index 3b44ace..60d1ea6 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -516,6 +516,9 @@ NOTIFY_SOURCES=''
 if bld.CONFIG_SET("HAVE_INOTIFY"):
     NOTIFY_SOURCES += ' smbd/notify_inotify.c'
 
+if bld.CONFIG_SET('SAMBA_FAM_LIBS'):
+    NOTIFY_SOURCES += ' smbd/notify_fam.c'
+
 bld.SAMBA3_LIBRARY('smbd_base',
                    source='''
                    smbd/server_reload.c
@@ -635,7 +638,9 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    NDR_SMB_ACL
                    netapi
                    NDR_IOCTL
-                   ''' + bld.env['dmapi_lib'],
+                   ''' + bld.env['dmapi_lib']
+                   + (bld.CONFIG_GET('SAMBA_FAM_LIBS')
+                         if bld.CONFIG_SET('SAMBA_FAM_LIBS') else ''),
                    private_library=True)
 
 bld.SAMBA3_SUBSYSTEM('LOCKING',
-- 
1.7.9.5


From 860f6bec81cca531536d16a446dd737c27620ac9 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 16:52:47 +0100
Subject: [PATCH 08/20] smbd: Add the notify daemon

This adds the notify daemon listening on MSG_SMB_NOTIFY_REC_CHANGE
and MSG_SMB_NOTIFY_TRIGGER messages. It relies on ctdbd to distribute
the notify database and events in a cluster.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 librpc/idl/messaging.idl           |   13 +
 source3/smbd/notifyd/notifyd.c     | 1418 ++++++++++++++++++++++++++++++++++++
 source3/smbd/notifyd/notifyd.h     |  143 ++++
 source3/smbd/notifyd/tests.c       |  118 +++
 source3/smbd/notifyd/wscript_build |   12 +
 source3/wscript_build              |    1 +
 6 files changed, 1705 insertions(+)
 create mode 100644 source3/smbd/notifyd/notifyd.c
 create mode 100644 source3/smbd/notifyd/notifyd.h
 create mode 100644 source3/smbd/notifyd/tests.c
 create mode 100644 source3/smbd/notifyd/wscript_build

diff --git a/librpc/idl/messaging.idl b/librpc/idl/messaging.idl
index 2b902ec..ca99f41 100644
--- a/librpc/idl/messaging.idl
+++ b/librpc/idl/messaging.idl
@@ -99,6 +99,13 @@ interface messaging
 		/* Cancel a notify, directory got deleted */
 		MSG_SMB_NOTIFY_CANCEL_DELETED   = 0x0319,
 
+		/* notifyd messages */
+		MSG_SMB_NOTIFY_REC_CHANGE	= 0x031A,
+		MSG_SMB_NOTIFY_TRIGGER		= 0x031B,
+		MSG_SMB_NOTIFY_GET_DB		= 0x031C,
+		MSG_SMB_NOTIFY_DB		= 0x031D,
+		MSG_SMB_NOTIFY_REC_CHANGES	= 0x031E,
+
 		/* winbind messages */
 		MSG_WINBIND_FINISHED		= 0x0401,
 		MSG_WINBIND_FORGET_STATE	= 0x0402,
@@ -152,4 +159,10 @@ interface messaging
 		uint8 num_fds;
 		dlong fds[num_fds];
 	} messaging_rec;
+
+	typedef [public] struct {
+		hyper rec_index;
+		uint32 num_recs;
+		messaging_rec *recs[num_recs];
+	} messaging_reclog;
 }
diff --git a/source3/smbd/notifyd/notifyd.c b/source3/smbd/notifyd/notifyd.c
new file mode 100644
index 0000000..ea48035
--- /dev/null
+++ b/source3/smbd/notifyd/notifyd.c
@@ -0,0 +1,1418 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "librpc/gen_ndr/notify.h"
+#include "librpc/gen_ndr/messaging.h"
+#include "librpc/gen_ndr/server_id.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "messages.h"
+#include "proto.h"
+#include "tdb.h"
+#include "util_tdb.h"
+#include "notifyd.h"
+#include "lib/util/server_id_db.h"
+#include "lib/util/tevent_unix.h"
+#include "ctdbd_conn.h"
+#include "ctdb_srvids.h"
+#include "source3/smbd/proto.h"
+#include "ctdb/include/ctdb_protocol.h"
+#include "server_id_db_util.h"
+#include "lib/util/iov_buf.h"
+#include "messages_util.h"
+
+struct notifyd_peer;
+
+/*
+ * All of notifyd's state
+ */
+
+struct notifyd_state {
+	struct tevent_context *ev;
+	struct messaging_context *msg_ctx;
+	struct ctdbd_connection *ctdbd_conn;
+
+	/*
+	 * Database of everything clients show interest in. Indexed by
+	 * absolute path. The database keys are not 0-terminated
+	 * because the criticial operation, notifyd_trigger, can walk
+	 * the structure from the top without adding intermediate 0s.
+	 * The database records contain an array of
+	 *
+	 * struct notifyd_instance
+	 *
+	 * to be maintained by parsed by notifyd_entry_parse()
+	 */
+	struct db_context *entries;
+
+	/*
+	 * In the cluster case, this is the place where we store a log
+	 * of all MSG_SMB_NOTIFY_REC_CHANGE messages. We just 1:1
+	 * forward them to our peer notifyd's in the cluster once a
+	 * second or when the log grows too large.
+	 */
+
+	struct messaging_reclog *log;
+
+	/*
+	 * Array of companion notifyd's in a cluster. Every notifyd
+	 * broadcasts its messaging_reclog to every other notifyd in
+	 * the cluster. This is done by making ctdb send a message to
+	 * srvid CTDB_SRVID_SAMBA_NOTIFY_PROXY with destination node
+	 * number CTDB_BROADCAST_VNNMAP. Everybody in the cluster who
+	 * had called register_with_ctdbd this srvid will receive the
+	 * broadcasts.
+	 *
+	 * Database replication happens via these broadcasts. Also,
+	 * they serve as liveness indication. If a notifyd receives a
+	 * broadcast from an unknown peer, it will create one for this
+	 * srvid. Also when we don't hear anything from a peer for a
+	 * while, we will discard it.
+	 */
+
+	struct notifyd_peer **peers;
+	size_t num_peers;
+
+	sys_notify_watch_fn sys_notify_watch;
+	struct sys_notify_context *sys_notify_ctx;
+};
+
+/*
+ * notifyd's representation of a notify instance
+ */
+struct notifyd_instance {
+	struct server_id client;
+	struct notify_instance instance;
+
+	void *sys_watch; /* inotify/fam/etc handle */
+
+	/*
+	 * Filters after sys_watch took responsibility of some bits
+	 */
+	uint32_t internal_filter;
+	uint32_t internal_subdir_filter;
+};
+
+struct notifyd_peer {
+	struct notifyd_state *state;
+	struct server_id pid;
+	uint64_t rec_index;
+	struct db_context *db;
+	time_t last_broadcast;
+};
+
+static bool notifyd_rec_change(struct messaging_context *msg_ctx,
+			       struct messaging_rec **prec,
+			       void *private_data);
+static bool notifyd_trigger(struct messaging_context *msg_ctx,
+			    struct messaging_rec **prec,
+			    void *private_data);
+static bool notifyd_get_db(struct messaging_context *msg_ctx,
+			   struct messaging_rec **prec,
+			   void *private_data);
+static bool notifyd_got_db(struct messaging_context *msg_ctx,
+			   struct messaging_rec **prec,
+			   void *private_data);
+static void notifyd_broadcast_reclog(struct ctdbd_connection *ctdbd_conn,
+				     struct server_id src,
+				     struct messaging_reclog *log);
+static void notifyd_sys_callback(struct sys_notify_context *ctx,
+				 void *private_data, struct notify_event *ev);
+
+static struct tevent_req *notifyd_broadcast_reclog_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct ctdbd_connection *ctdbd_conn, struct server_id src,
+	struct messaging_reclog *log);
+static int notifyd_broadcast_reclog_recv(struct tevent_req *req);
+
+static struct tevent_req *notifyd_clean_peers_send(TALLOC_CTX *mem_ctx,
+						   struct tevent_context *ev,
+						   struct notifyd_state *notifyd);
+static int notifyd_clean_peers_recv(struct tevent_req *req);
+
+static int sys_notify_watch_dummy(TALLOC_CTX *mem_ctx,
+				  struct sys_notify_context *ctx,
+				  const char *path,
+				  uint32_t *filter,
+				  uint32_t *subdir_filter,
+				  void (*callback)(struct sys_notify_context *ctx,
+						   void *private_data,
+						   struct notify_event *ev),
+				  void *private_data,
+				  void *handle_p)
+{
+	void **handle = handle_p;
+	*handle = NULL;
+	return 0;
+}
+
+static void notifyd_handler_done(struct tevent_req *subreq);
+static void notifyd_broadcast_reclog_finished(struct tevent_req *subreq);
+static void notifyd_clean_peers_finished(struct tevent_req *subreq);
+static void notifyd_snoop_broadcast(uint32_t src_vnn, uint32_t dst_vnn,
+				    uint64_t dst_srvid,
+				    const uint8_t *msg, size_t msglen,
+				    void *private_data);
+
+struct tevent_req *notifyd_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+				struct messaging_context *msg_ctx,
+				struct ctdbd_connection *ctdbd_conn,
+				sys_notify_watch_fn sys_notify_watch,
+				struct sys_notify_context *sys_notify_ctx)
+{
+	struct tevent_req *req, *subreq;
+	struct notifyd_state *state;
+	struct server_id_db *names_db;
+	NTSTATUS status;
+	int ret;
+
+	req = tevent_req_create(mem_ctx, &state, struct notifyd_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->msg_ctx = msg_ctx;
+	state->ctdbd_conn = ctdbd_conn;
+
+	if (sys_notify_watch == NULL) {
+		sys_notify_watch = sys_notify_watch_dummy;
+	}
+
+	state->sys_notify_watch = sys_notify_watch;
+	state->sys_notify_ctx = sys_notify_ctx;
+
+	state->entries = db_open_rbt(state);
+	if (tevent_req_nomem(state->entries, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = messaging_handler_send(state, ev, msg_ctx,
+					MSG_SMB_NOTIFY_REC_CHANGE,
+					notifyd_rec_change, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_handler_done, req);
+
+	subreq = messaging_handler_send(state, ev, msg_ctx,
+					MSG_SMB_NOTIFY_TRIGGER,
+					notifyd_trigger, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_handler_done, req);
+
+	subreq = messaging_handler_send(state, ev, msg_ctx,
+					MSG_SMB_NOTIFY_GET_DB,
+					notifyd_get_db, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_handler_done, req);
+
+	subreq = messaging_handler_send(state, ev, msg_ctx,
+					MSG_SMB_NOTIFY_DB,
+					notifyd_got_db, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_handler_done, req);
+
+	names_db = messaging_names_db(msg_ctx);
+
+	ret = server_id_db_set_exclusive(names_db, "notify-daemon");
+	if (ret != 0) {
+		DEBUG(10, ("%s: server_id_db_add failed: %s\n",
+			   __func__, strerror(ret)));
+		tevent_req_error(req, ret);
+		return tevent_req_post(req, ev);
+	}
+
+	if (ctdbd_conn == NULL) {
+		/*
+		 * No cluster around, skip the database replication
+		 * engine
+		 */
+		return req;
+	}
+
+	state->log = talloc_zero(state, struct messaging_reclog);
+	if (tevent_req_nomem(state->log, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = notifyd_broadcast_reclog_send(
+		state->log, ev, ctdbd_conn, messaging_server_id(msg_ctx),
+		state->log);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_broadcast_reclog_finished,
+				req);
+
+	subreq = notifyd_clean_peers_send(state, ev, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_clean_peers_finished,
+				req);
+
+	status = register_with_ctdbd(ctdbd_conn, CTDB_SRVID_SAMBA_NOTIFY_PROXY,
+				     notifyd_snoop_broadcast, state);
+	if (!NT_STATUS_IS_OK(status)) {
+		tevent_req_error(req, map_errno_from_nt_status(status));
+		return tevent_req_post(req, ev);
+	}
+
+	return req;
+}
+
+static void notifyd_handler_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	int ret;
+
+	ret = messaging_handler_recv(subreq);
+	TALLOC_FREE(subreq);
+	tevent_req_error(req, ret);
+}
+
+static void notifyd_broadcast_reclog_finished(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	int ret;
+
+	ret = notifyd_broadcast_reclog_recv(subreq);
+	TALLOC_FREE(subreq);
+	tevent_req_error(req, ret);
+}
+
+static void notifyd_clean_peers_finished(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	int ret;
+
+	ret = notifyd_clean_peers_recv(subreq);
+	TALLOC_FREE(subreq);
+	tevent_req_error(req, ret);
+}
+
+int notifyd_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_unix(req);
+}
+
+/*
+ * Parse an entry in the notifyd_context->entries database
+ */
+
+static bool notifyd_parse_entry(uint8_t *buf, size_t buflen,
+				struct notifyd_instance **instances,
+				size_t *num_instances)
+{
+	if ((buflen % sizeof(struct notifyd_instance)) != 0) {
+		DEBUG(1, ("%s: invalid buffer size: %u\n",
+			  __func__, (unsigned)buflen));
+		return false;
+	}
+
+	if (instances != NULL) {
+		*instances = (struct notifyd_instance *)buf;
+	}
+	if (num_instances != NULL) {
+		*num_instances = buflen / sizeof(struct notifyd_instance);
+	}
+	return true;
+}
+
+static bool notifyd_apply_rec_change(
+	const struct server_id *client,
+	const char *path, size_t pathlen,
+	const struct notify_instance *chg,
+	struct db_context *entries,
+	sys_notify_watch_fn sys_notify_watch,
+	struct sys_notify_context *sys_notify_ctx,
+	struct messaging_context *msg_ctx)
+{
+	struct db_record *rec;
+	struct notifyd_instance *instances;
+	size_t num_instances;
+	size_t i;
+	struct notifyd_instance *instance;
+	TDB_DATA value;
+	NTSTATUS status;
+	bool ok = false;
+
+	if (pathlen == 0) {
+		DEBUG(1, ("%s: pathlen==0\n", __func__));
+		return false;
+	}
+	if (path[pathlen-1] != '\0') {
+		DEBUG(1, ("%s: path not 0-terminated\n", __func__));
+		return false;
+	}
+
+	DEBUG(10, ("%s: path=%s, filter=%u, subdir_filter=%u, "
+		   "private_data=%p\n", __func__, path,
+		   (unsigned)chg->filter, (unsigned)chg->subdir_filter,
+		   chg->private_data));
+
+	rec = dbwrap_fetch_locked(
+		entries, entries,
+		make_tdb_data((const uint8_t *)path, pathlen-1));
+
+	if (rec == NULL) {
+		DEBUG(1, ("%s: dbwrap_fetch_locked failed\n", __func__));
+		goto fail;
+	}
+
+	num_instances = 0;
+	value = dbwrap_record_get_value(rec);
+
+	if (value.dsize != 0) {
+		if (!notifyd_parse_entry(value.dptr, value.dsize, NULL,
+					 &num_instances)) {
+			goto fail;
+		}
+	}
+
+	/*
+	 * Overallocate by one instance to avoid a realloc when adding
+	 */
+	instances = talloc_array(rec, struct notifyd_instance,
+				 num_instances + 1);
+	if (instances == NULL) {
+		DEBUG(1, ("%s: talloc failed\n", __func__));
+		goto fail;
+	}
+
+	if (value.dsize != 0) {
+		memcpy(instances, value.dptr, value.dsize);
+	}
+
+	for (i=0; i<num_instances; i++) {
+		instance = &instances[i];
+
+		if (server_id_equal(&instance->client, client) &&
+		    (instance->instance.private_data == chg->private_data)) {
+			break;
+		}
+	}
+
+	if (i < num_instances) {
+		instance->instance = *chg;
+	} else {
+		/*
+		 * We've overallocated for one instance
+		 */
+		instance = &instances[num_instances];
+
+		*instance = (struct notifyd_instance) {
+			.client = *client,
+			.instance = *chg,
+			.internal_filter = chg->filter,
+			.internal_subdir_filter = chg->subdir_filter
+		};
+
+		num_instances += 1;
+	}
+
+	if ((instance->instance.filter != 0) ||
+	    (instance->instance.subdir_filter != 0)) {
+		int ret;
+
+		TALLOC_FREE(instance->sys_watch);
+
+		ret = sys_notify_watch(entries, sys_notify_ctx, path,
+				       &instance->internal_filter,
+				       &instance->internal_subdir_filter,
+				       notifyd_sys_callback, msg_ctx,
+				       &instance->sys_watch);
+		if (ret != 0) {
+			DEBUG(1, ("%s: inotify_watch returned %s\n",
+				  __func__, strerror(errno)));
+		}
+	}
+
+	if ((instance->instance.filter == 0) &&
+	    (instance->instance.subdir_filter == 0)) {
+		/* This is a delete request */
+		TALLOC_FREE(instance->sys_watch);
+		*instance = instances[num_instances-1];
+		num_instances -= 1;
+	}
+
+	DEBUG(10, ("%s: %s has %u instances\n", __func__,
+		   path, (unsigned)num_instances));
+
+	if (num_instances == 0) {
+		status = dbwrap_record_delete(rec);
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(1, ("%s: dbwrap_record_delete returned %s\n",
+				  __func__, nt_errstr(status)));
+			goto fail;
+		}
+	} else {
+		value = make_tdb_data(
+			(uint8_t *)instances,
+			sizeof(struct notifyd_instance) * num_instances);
+
+		status = dbwrap_record_store(rec, value, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(1, ("%s: dbwrap_record_store returned %s\n",
+				  __func__, nt_errstr(status)));
+			goto fail;
+		}
+	}
+
+	ok = true;
+fail:
+	TALLOC_FREE(rec);
+	return ok;
+}
+
+static void notifyd_sys_callback(struct sys_notify_context *ctx,
+				 void *private_data, struct notify_event *ev)
+{
+	struct messaging_context *msg_ctx = talloc_get_type_abort(
+		private_data, struct messaging_context);
+	struct notify_trigger_msg msg;
+	struct iovec iov[4];
+	char slash = '/';
+
+	msg = (struct notify_trigger_msg) {
+		.when = timespec_current(),
+		.action = ev->action,
+		.filter = UINT32_MAX
+	};
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_trigger_msg, path);
+	iov[1].iov_base = discard_const_p(char, ev->dir);
+	iov[1].iov_len = strlen(ev->dir);
+	iov[2].iov_base = &slash;
+	iov[2].iov_len = 1;
+	iov[3].iov_base = discard_const_p(char, ev->path);
+	iov[3].iov_len = strlen(ev->path)+1;
+
+	messaging_send_iov(
+		msg_ctx, messaging_server_id(msg_ctx),
+		MSG_SMB_NOTIFY_TRIGGER, iov, ARRAY_SIZE(iov), NULL, 0);
+}
+
+static bool notifyd_parse_rec_change(uint8_t *buf, size_t bufsize,
+				     struct notify_rec_change_msg **pmsg,
+				     size_t *pathlen)
+{
+	struct notify_rec_change_msg *msg;
+
+	if (bufsize < offsetof(struct notify_rec_change_msg, path) + 1) {
+		DEBUG(1, ("%s: message too short, ignoring: %u\n", __func__,
+			  (unsigned)bufsize));
+		return false;
+	}
+
+	*pmsg = msg = (struct notify_rec_change_msg *)buf;
+	*pathlen = bufsize - offsetof(struct notify_rec_change_msg, path);
+
+	DEBUG(10, ("%s: Got rec_change_msg filter=%u, subdir_filter=%u, "
+		   "private_data=%p, path=%.*s\n",
+		   __func__, (unsigned)msg->instance.filter,
+		   (unsigned)msg->instance.subdir_filter,
+		   msg->instance.private_data, (int)(*pathlen), msg->path));
+
+	return true;
+}
+
+static bool notifyd_rec_change(struct messaging_context *msg_ctx,
+			       struct messaging_rec **prec,
+			       void *private_data)
+{
+	struct notifyd_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_state);
+	struct server_id_buf idbuf;
+	struct messaging_rec *rec = *prec;
+	struct messaging_rec **tmp;
+	struct messaging_reclog *log;
+	struct notify_rec_change_msg *msg;
+	size_t pathlen;
+	bool ok;
+
+	DEBUG(10, ("%s: Got %d bytes from %s\n", __func__,
+		   (unsigned)rec->buf.length,
+		   server_id_str_buf(rec->src, &idbuf)));
+
+	ok = notifyd_parse_rec_change(rec->buf.data, rec->buf.length,
+				      &msg, &pathlen);
+	if (!ok) {
+		return true;
+	}
+
+	ok = notifyd_apply_rec_change(
+		&rec->src, msg->path, pathlen, &msg->instance,
+		state->entries, state->sys_notify_watch, state->sys_notify_ctx,
+		state->msg_ctx);
+	if (!ok) {
+		DEBUG(1, ("%s: notifyd_apply_rec_change failed, ignoring\n",
+			  __func__));
+		return true;
+	}
+
+	if ((state->log == NULL) || (state->ctdbd_conn == NULL)) {
+		return true;
+	}
+	log = state->log;
+
+	tmp = talloc_realloc(log, log->recs, struct messaging_rec *,
+			     log->num_recs+1);
+	if (tmp == NULL) {
+		DEBUG(1, ("%s: talloc_realloc failed, ignoring\n", __func__));
+		return true;
+	}
+	log->recs = tmp;
+
+	log->recs[log->num_recs] = talloc_move(log->recs, prec);
+	log->num_recs += 1;
+
+	if (log->num_recs >= 100) {
+		/*
+		 * Don't let the log grow too large
+		 */
+		notifyd_broadcast_reclog(state->ctdbd_conn,
+					 messaging_server_id(msg_ctx), log);
+	}
+
+	return true;
+}
+
+struct notifyd_trigger_state {
+	struct messaging_context *msg_ctx;
+	struct notify_trigger_msg *msg;
+	bool recursive;
+	bool covered_by_sys_notify;
+};
+
+static void notifyd_trigger_parser(TDB_DATA key, TDB_DATA data,
+				   void *private_data);
+
+static bool notifyd_trigger(struct messaging_context *msg_ctx,
+			    struct messaging_rec **prec,
+			    void *private_data)
+{
+	struct notifyd_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_state);
+	struct server_id my_id = messaging_server_id(msg_ctx);
+	struct messaging_rec *rec = *prec;
+	struct notifyd_trigger_state tstate;
+	const char *path;
+	const char *p, *next_p;
+
+	if (rec->buf.length < offsetof(struct notify_trigger_msg, path) + 1) {
+		DEBUG(1, ("message too short, ignoring: %u\n",
+			  (unsigned)rec->buf.length));
+		return true;
+	}
+	if (rec->buf.data[rec->buf.length-1] != 0) {
+		DEBUG(1, ("%s: path not 0-terminated, ignoring\n", __func__));
+		return true;
+	}
+
+	tstate.msg_ctx = msg_ctx;
+
+	tstate.covered_by_sys_notify = (rec->src.vnn == my_id.vnn);
+	tstate.covered_by_sys_notify &= !server_id_equal(&rec->src, &my_id);
+
+	tstate.msg = (struct notify_trigger_msg *)rec->buf.data;
+	path = tstate.msg->path;
+
+	DEBUG(10, ("%s: Got trigger_msg action=%u, filter=%u, path=%s\n",
+		   __func__, (unsigned)tstate.msg->action,
+		   (unsigned)tstate.msg->filter, path));
+
+	if (path[0] != '/') {
+		DEBUG(1, ("%s: path %s does not start with /, ignoring\n",
+			  __func__, path));
+		return true;
+	}
+
+	for (p = strchr(path+1, '/'); p != NULL; p = next_p) {
+		ptrdiff_t path_len = p - path;
+		TDB_DATA key;
+		uint32_t i;
+
+		next_p = strchr(p+1, '/');
+		tstate.recursive = (next_p != NULL);
+
+		DEBUG(10, ("%s: Trying path %.*s\n", __func__,
+			   (int)path_len, path));
+
+		key = (TDB_DATA) { .dptr = discard_const_p(uint8_t, path),
+				   .dsize = path_len };
+
+		dbwrap_parse_record(state->entries, key,
+				    notifyd_trigger_parser, &tstate);
+
+		if (state->peers == NULL) {
+			continue;
+		}
+
+		if (rec->src.vnn != my_id.vnn) {
+			continue;
+		}
+
+		for (i=0; i<state->num_peers; i++) {
+			if (state->peers[i]->db == NULL) {
+				/*
+				 * Inactive peer, did not get a db yet
+				 */
+				continue;
+			}
+			dbwrap_parse_record(state->peers[i]->db, key,
+					    notifyd_trigger_parser, &tstate);
+		}
+	}
+
+	return true;
+}
+
+static void notifyd_send_delete(struct messaging_context *msg_ctx,
+				TDB_DATA key,
+				struct notifyd_instance *instance);
+
+static void notifyd_trigger_parser(TDB_DATA key, TDB_DATA data,
+				   void *private_data)
+
+{
+	struct notifyd_trigger_state *tstate = private_data;
+	struct notify_event_msg msg = { .action = tstate->msg->action };
+	struct iovec iov[2];
+	size_t path_len = key.dsize;
+	struct notifyd_instance *instances = NULL;
+	size_t num_instances = 0;
+	size_t i;
+
+	if (!notifyd_parse_entry(data.dptr, data.dsize, &instances,
+				 &num_instances)) {
+		DEBUG(1, ("%s: Could not parse notifyd_entry\n", __func__));
+		return;
+	}
+
+	DEBUG(10, ("%s: Found %u instances for %.*s\n", __func__,
+		   (unsigned)num_instances, (int)key.dsize,
+		   (char *)key.dptr));
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_event_msg, path);
+	iov[1].iov_base = tstate->msg->path + path_len + 1;
+	iov[1].iov_len = strlen((char *)(iov[1].iov_base)) + 1;
+
+	for (i=0; i<num_instances; i++) {
+		struct notifyd_instance *instance = &instances[i];
+		struct server_id_buf idbuf;
+		uint32_t i_filter;
+		NTSTATUS status;
+
+		if (tstate->covered_by_sys_notify) {
+			if (tstate->recursive) {
+				i_filter = instance->internal_subdir_filter;
+			} else {
+				i_filter = instance->internal_filter;
+			}
+		} else {
+			if (tstate->recursive) {
+				i_filter = instance->instance.subdir_filter;
+			} else {
+				i_filter = instance->instance.filter;
+			}
+		}
+
+		if ((i_filter & tstate->msg->filter) == 0) {
+			continue;
+		}
+
+		msg.private_data = instance->instance.private_data;
+
+		status = messaging_send_iov(
+			tstate->msg_ctx, instance->client,
+			MSG_PVFS_NOTIFY, iov, ARRAY_SIZE(iov), NULL, 0);
+
+		DEBUG(10, ("%s: messaging_send_iov to %s returned %s\n",
+			   __func__,
+			   server_id_str_buf(instance->client, &idbuf),
+			   nt_errstr(status)));
+
+		if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
+		    procid_is_local(&instance->client)) {
+			/*
+			 * That process has died
+			 */
+			notifyd_send_delete(tstate->msg_ctx, key, instance);
+			continue;
+		}
+
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(1, ("%s: messaging_send_iov returned %s\n",
+				  __func__, nt_errstr(status)));
+		}
+	}
+}
+
+/*
+ * Send a delete request to ourselves to properly discard a notify
+ * record for an smbd that has died.
+ */
+
+static void notifyd_send_delete(struct messaging_context *msg_ctx,
+				TDB_DATA key,
+				struct notifyd_instance *instance)
+{
+	struct notify_rec_change_msg msg = {
+		.instance.private_data = instance->instance.private_data
+	};
+	uint8_t nul = 0;
+	struct iovec iov[3];
+	NTSTATUS status;
+
+	/*
+	 * Send a rec_change to ourselves to delete a dead entry
+	 */
+
+	iov[0] = (struct iovec) {
+		.iov_base = &msg,
+		.iov_len = offsetof(struct notify_rec_change_msg, path) };
+	iov[1] = (struct iovec) { .iov_base = key.dptr, .iov_len = key.dsize };
+	iov[2] = (struct iovec) { .iov_base = &nul, .iov_len = sizeof(nul) };
+
+	status = messaging_send_iov_from(
+		msg_ctx, instance->client, messaging_server_id(msg_ctx),
+		MSG_SMB_NOTIFY_REC_CHANGE, iov, ARRAY_SIZE(iov), NULL, 0);
+
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: messaging_send_iov_from returned %s\n",
+			   __func__, nt_errstr(status)));
+	}
+}
+
+static bool notifyd_get_db(struct messaging_context *msg_ctx,
+			   struct messaging_rec **prec,
+			   void *private_data)
+{
+	struct notifyd_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_state);
+	struct messaging_rec *rec = *prec;
+	struct server_id_buf id1, id2;
+	NTSTATUS status;
+	uint64_t rec_index = UINT64_MAX;
+	uint8_t index_buf[sizeof(uint64_t)];
+	size_t dbsize;
+	uint8_t *buf;
+	struct iovec iov[2];
+
+	dbsize = dbwrap_marshall(state->entries, NULL, 0);
+
+	buf = talloc_array(rec, uint8_t, dbsize);
+	if (buf == NULL) {
+		DEBUG(1, ("%s: talloc_array(%ju) failed\n",
+			  __func__, (uintmax_t)dbsize));
+		return true;
+	}
+
+	dbsize = dbwrap_marshall(state->entries, buf, dbsize);
+
+	if (dbsize != talloc_get_size(buf)) {
+		DEBUG(1, ("%s: dbsize changed: %ju->%ju\n", __func__,
+			  (uintmax_t)talloc_get_size(buf),
+			  (uintmax_t)dbsize));
+		TALLOC_FREE(buf);
+		return true;
+	}
+
+	if (state->log != NULL) {
+		rec_index = state->log->rec_index;
+	}
+	SBVAL(index_buf, 0, rec_index);
+
+	iov[0] = (struct iovec) { .iov_base = index_buf,
+				  .iov_len = sizeof(index_buf) };
+	iov[1] = (struct iovec) { .iov_base = buf,
+				  .iov_len = dbsize };
+
+	DEBUG(10, ("%s: Sending %ju bytes to %s->%s\n", __func__,
+		   (uintmax_t)iov_buflen(iov, ARRAY_SIZE(iov)),
+		   server_id_str_buf(messaging_server_id(msg_ctx), &id1),
+		   server_id_str_buf(rec->src, &id2)));
+
+	status = messaging_send_iov(msg_ctx, rec->src, MSG_SMB_NOTIFY_DB,
+				    iov, ARRAY_SIZE(iov), NULL, 0);
+	TALLOC_FREE(buf);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("%s: messaging_send_iov failed: %s\n",
+			  __func__, nt_errstr(status)));
+	}
+
+	return true;
+}
+
+static int notifyd_add_proxy_syswatches(struct db_record *rec,
+					void *private_data);
+
+static bool notifyd_got_db(struct messaging_context *msg_ctx,
+			   struct messaging_rec **prec,
+			   void *private_data)
+{
+	struct notifyd_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_state);
+	struct messaging_rec *rec = *prec;
+	struct notifyd_peer *p = NULL;
+	struct server_id_buf idbuf;
+	NTSTATUS status;
+	int count;
+	size_t i;
+
+	for (i=0; i<state->num_peers; i++) {
+		if (server_id_equal(&rec->src, &state->peers[i]->pid)) {
+			p = state->peers[i];
+			break;
+		}
+	}
+
+	if (p == NULL) {
+		DEBUG(10, ("%s: Did not find peer for db from %s\n",
+			   __func__, server_id_str_buf(rec->src, &idbuf)));
+		return true;
+	}
+
+	if (rec->buf.length < 8) {
+		DEBUG(10, ("%s: Got short db length %u from %s\n", __func__,
+			   (unsigned)rec->buf.length,
+			   server_id_str_buf(rec->src, &idbuf)));
+		TALLOC_FREE(p);
+		return true;
+	}
+
+	p->rec_index = BVAL(rec->buf.data, 0);
+
+	p->db = db_open_rbt(p);
+	if (p->db == NULL) {
+		DEBUG(10, ("%s: db_open_rbt failed\n", __func__));
+		TALLOC_FREE(p);
+		return true;
+	}
+
+	status = dbwrap_unmarshall(p->db, rec->buf.data + 8,
+				   rec->buf.length - 8);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: dbwrap_unmarshall returned %s for db %s\n",
+			   __func__, nt_errstr(status),
+			   server_id_str_buf(rec->src, &idbuf)));
+		TALLOC_FREE(p);
+		return true;
+	}
+
+	dbwrap_traverse_read(p->db, notifyd_add_proxy_syswatches, state,
+			     &count);
+
+	DEBUG(10, ("%s: Database from %s contained %d records\n", __func__,
+		   server_id_str_buf(rec->src, &idbuf), count));
+
+	return true;
+}
+
+static void notifyd_broadcast_reclog(struct ctdbd_connection *ctdbd_conn,
+				     struct server_id src,
+				     struct messaging_reclog *log)
+{
+	NTSTATUS status;
+	enum ndr_err_code ndr_err;
+	uint8_t msghdr[MESSAGE_HDR_LENGTH];
+	DATA_BLOB blob;
+	struct iovec iov[2];
+
+	if (log == NULL) {
+		return;
+	}
+
+	DEBUG(10, ("%s: rec_index=%ju, num_recs=%u\n", __func__,
+		   (uintmax_t)log->rec_index, (unsigned)log->num_recs));
+
+	message_hdr_put(msghdr, MSG_SMB_NOTIFY_REC_CHANGES, src,
+			(struct server_id) {0 });
+	iov[0] = (struct iovec) { .iov_base = msghdr,
+				  .iov_len = sizeof(msghdr) };
+
+	ndr_err = ndr_push_struct_blob(
+		&blob, log, log,
+		(ndr_push_flags_fn_t)ndr_push_messaging_reclog);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(1, ("%s: ndr_push_messaging_recs failed: %s\n",
+			  __func__, ndr_errstr(ndr_err)));
+		goto done;
+	}
+	iov[1] = (struct iovec) { .iov_base = blob.data,
+				  .iov_len = blob.length };
+
+	status = ctdbd_messaging_send_iov(
+		ctdbd_conn, CTDB_BROADCAST_VNNMAP,
+		CTDB_SRVID_SAMBA_NOTIFY_PROXY, iov, ARRAY_SIZE(iov));
+	TALLOC_FREE(blob.data);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("%s: ctdbd_messaging_send failed: %s\n",
+			  __func__, nt_errstr(status)));
+		goto done;
+	}
+
+	log->rec_index += 1;
+
+done:
+	log->num_recs = 0;
+	TALLOC_FREE(log->recs);
+}
+
+struct notifyd_broadcast_reclog_state {
+	struct tevent_context *ev;
+	struct ctdbd_connection *ctdbd_conn;
+	struct server_id src;
+	struct messaging_reclog *log;
+};
+
+static void notifyd_broadcast_reclog_next(struct tevent_req *subreq);
+
+static struct tevent_req *notifyd_broadcast_reclog_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct ctdbd_connection *ctdbd_conn, struct server_id src,
+	struct messaging_reclog *log)
+{
+	struct tevent_req *req, *subreq;
+	struct notifyd_broadcast_reclog_state *state;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct notifyd_broadcast_reclog_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->ctdbd_conn = ctdbd_conn;
+	state->src = src;
+	state->log = log;
+
+	subreq = tevent_wakeup_send(state, state->ev,
+				    timeval_current_ofs_msec(1000));
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_broadcast_reclog_next, req);
+	return req;
+}
+
+static void notifyd_broadcast_reclog_next(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct notifyd_broadcast_reclog_state *state = tevent_req_data(
+		req, struct notifyd_broadcast_reclog_state);
+	bool ok;
+
+	ok = tevent_wakeup_recv(subreq);
+	TALLOC_FREE(subreq);
+	if (!ok) {
+		tevent_req_oom(req);
+		return;
+	}
+
+	notifyd_broadcast_reclog(state->ctdbd_conn, state->src, state->log);
+
+	subreq = tevent_wakeup_send(state, state->ev,
+				    timeval_current_ofs_msec(1000));
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, notifyd_broadcast_reclog_next, req);
+}
+
+static int notifyd_broadcast_reclog_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_unix(req);
+}
+
+struct notifyd_clean_peers_state {
+	struct tevent_context *ev;
+	struct notifyd_state *notifyd;
+};
+
+static void notifyd_clean_peers_next(struct tevent_req *subreq);
+
+static struct tevent_req *notifyd_clean_peers_send(TALLOC_CTX *mem_ctx,
+						   struct tevent_context *ev,
+						   struct notifyd_state *notifyd)
+{
+	struct tevent_req *req, *subreq;
+	struct notifyd_clean_peers_state *state;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct notifyd_clean_peers_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->notifyd = notifyd;
+
+	subreq = tevent_wakeup_send(state, state->ev,
+				    timeval_current_ofs_msec(30000));
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_clean_peers_next, req);
+	return req;
+}
+
+static void notifyd_clean_peers_next(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct notifyd_clean_peers_state *state = tevent_req_data(
+		req, struct notifyd_clean_peers_state);
+	struct notifyd_state *notifyd = state->notifyd;
+	size_t i;
+	bool ok;
+	time_t now = time(NULL);
+
+	ok = tevent_wakeup_recv(subreq);
+	TALLOC_FREE(subreq);
+	if (!ok) {
+		tevent_req_oom(req);
+		return;
+	}
+
+	i = 0;
+	while (i < notifyd->num_peers) {
+		struct notifyd_peer *p = notifyd->peers[i];
+
+		if ((now - p->last_broadcast) > 60) {
+			struct server_id_buf idbuf;
+
+			/*
+			 * Haven't heard for more than 60 seconds. Call this peer dead
+			 */
+
+			DEBUG(10, ("%s: peer %s died\n", __func__,
+				   server_id_str_buf(p->pid, &idbuf)));
+			/*
+			 * This implicitly decrements notifyd->num_peers
+			 */
+			TALLOC_FREE(p);
+		} else {
+			i += 1;
+		}
+	}
+
+	subreq = tevent_wakeup_send(state, state->ev,
+				    timeval_current_ofs_msec(30000));
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, notifyd_clean_peers_next, req);
+}
+
+static int notifyd_clean_peers_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_unix(req);
+}
+
+static int notifyd_add_proxy_syswatches(struct db_record *rec,
+					void *private_data)
+{
+	struct notifyd_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_state);
+	struct db_context *db = dbwrap_record_get_db(rec);
+	TDB_DATA key = dbwrap_record_get_key(rec);
+	TDB_DATA value = dbwrap_record_get_value(rec);
+	struct notifyd_instance *instances = NULL;
+	size_t num_instances = 0;
+	size_t i;
+	char path[key.dsize+1];
+	bool ok;
+
+	memcpy(path, key.dptr, key.dsize);
+	path[key.dsize] = '\0';
+
+	ok = notifyd_parse_entry(value.dptr, value.dsize, &instances,
+				 &num_instances);
+	if (!ok) {
+		DEBUG(1, ("%s: Could not parse notifyd entry for %s\n",
+			  __func__, path));
+		return 0;
+	}
+
+	for (i=0; i<num_instances; i++) {
+		struct notifyd_instance *instance = &instances[i];
+		uint32_t filter = instance->instance.filter;
+		uint32_t subdir_filter = instance->instance.subdir_filter;
+		int ret;
+
+		ret = state->sys_notify_watch(
+			db, state->sys_notify_ctx, path,
+			&filter, &subdir_filter,
+			notifyd_sys_callback, state->msg_ctx,
+			&instance->sys_watch);
+		if (ret != 0) {
+			DEBUG(1, ("%s: inotify_watch returned %s\n",
+				  __func__, strerror(errno)));
+		}
+	}
+
+	return 0;
+}
+
+static int notifyd_db_del_syswatches(struct db_record *rec, void *private_data)
+{
+	TDB_DATA key = dbwrap_record_get_key(rec);
+	TDB_DATA value = dbwrap_record_get_value(rec);
+	struct notifyd_instance *instances = NULL;
+	size_t num_instances = 0;
+	size_t i;
+	bool ok;
+
+	ok = notifyd_parse_entry(value.dptr, value.dsize, &instances,
+				 &num_instances);
+	if (!ok) {
+		DEBUG(1, ("%s: Could not parse notifyd entry for %.*s\n",
+			  __func__, (int)key.dsize, (char *)key.dptr));
+		return 0;
+	}
+	for (i=0; i<num_instances; i++) {
+		TALLOC_FREE(instances[i].sys_watch);
+	}
+	return 0;
+}
+
+static int notifyd_peer_destructor(struct notifyd_peer *p)
+{
+	struct notifyd_state *state = p->state;
+	size_t i;
+
+	dbwrap_traverse_read(p->db, notifyd_db_del_syswatches, NULL, NULL);
+
+	for (i = 0; i<state->num_peers; i++) {
+		if (p == state->peers[i]) {
+			state->peers[i] = state->peers[state->num_peers-1];
+			state->num_peers -= 1;
+			break;
+		}
+	}
+	return 0;
+}
+
+static struct notifyd_peer *notifyd_peer_new(
+	struct notifyd_state *state, struct server_id pid)
+{
+	struct notifyd_peer *p, **tmp;
+
+	tmp = talloc_realloc(state, state->peers, struct notifyd_peer *,
+			     state->num_peers+1);
+	if (tmp == NULL) {
+		return NULL;
+	}
+	state->peers = tmp;
+
+	p = talloc_zero(state->peers, struct notifyd_peer);
+	if (p == NULL) {
+		return NULL;
+	}
+	p->state = state;
+	p->pid = pid;
+
+	state->peers[state->num_peers] = p;
+	state->num_peers += 1;
+
+	talloc_set_destructor(p, notifyd_peer_destructor);
+
+	return p;
+}
+
+static void notifyd_apply_reclog(struct notifyd_peer *peer,
+				 const uint8_t *msg, size_t msglen)
+{
+	struct notifyd_state *state = peer->state;
+	DATA_BLOB blob = { .data = discard_const_p(uint8_t, msg),
+			   .length = msglen };
+	struct server_id_buf idbuf;
+	struct messaging_reclog *log;
+	enum ndr_err_code ndr_err;
+	uint32_t i;
+
+	if (peer->db == NULL) {
+		/*
+		 * No db yet
+		 */
+		return;
+	}
+
+	log = talloc(peer, struct messaging_reclog);
+	if (log == NULL) {
+		DEBUG(10, ("%s: talloc failed\n", __func__));
+		return;
+	}
+
+	ndr_err = ndr_pull_struct_blob_all(
+		&blob, log, log,
+		(ndr_pull_flags_fn_t)ndr_pull_messaging_reclog);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(10, ("%s: ndr_pull_messaging_reclog failed: %s\n",
+			   __func__, ndr_errstr(ndr_err)));
+		goto fail;
+	}
+
+	DEBUG(10, ("%s: Got %u recs index %ju from %s\n", __func__,
+		   (unsigned)log->num_recs, (uintmax_t)log->rec_index,
+		   server_id_str_buf(peer->pid, &idbuf)));
+
+	if (log->rec_index != peer->rec_index) {
+		DEBUG(3, ("%s: Got rec index %ju from %s, expected %ju\n",
+			  __func__, (uintmax_t)log->rec_index,
+			  server_id_str_buf(peer->pid, &idbuf),
+			  (uintmax_t)peer->rec_index));
+		goto fail;
+	}
+
+	for (i=0; i<log->num_recs; i++) {
+		struct messaging_rec *r = log->recs[i];
+		struct notify_rec_change_msg *chg;
+		size_t pathlen;
+		bool ok;
+
+		ok = notifyd_parse_rec_change(r->buf.data, r->buf.length,
+					      &chg, &pathlen);
+		if (!ok) {
+			DEBUG(3, ("%s: notifyd_parse_rec_change failed\n",
+				  __func__));
+			goto fail;
+		}
+
+		ok = notifyd_apply_rec_change(&r->src, chg->path, pathlen,
+					      &chg->instance, peer->db,
+					      state->sys_notify_watch,
+					      state->sys_notify_ctx,
+					      state->msg_ctx);
+		if (!ok) {
+			DEBUG(3, ("%s: notifyd_apply_rec_change failed\n",
+				  __func__));
+			goto fail;
+		}
+	}
+
+	peer->rec_index += 1;
+
+	TALLOC_FREE(log);
+	return;
+
+fail:
+	DEBUG(10, ("%s: Dropping peer %s\n", __func__,
+		   server_id_str_buf(peer->pid, &idbuf)));
+	TALLOC_FREE(peer);
+}
+
+/*
+ * Receive messaging_reclog (log of MSG_SMB_NOTIFY_REC_CHANGE
+ * messages) broadcasts by other notifyds. Several cases:
+ *
+ * We don't know the source. This creates a new peer. Creating a peer
+ * involves asking the peer for its full database. We assume ordered
+ * messages, so the new database will arrive before the next broadcast
+ * will.
+ *
+ * We know the source and the log index matches. We will apply the log
+ * locally to our peer's db as if we had received it from a local
+ * client.
+ *
+ * We know the source but the log index does not match. This means we
+ * lost a message. We just drop the whole peer and wait for the next
+ * broadcast, which will then trigger a fresh database pull.
+ */
+
+static void notifyd_snoop_broadcast(uint32_t src_vnn, uint32_t dst_vnn,
+				    uint64_t dst_srvid,
+				    const uint8_t *msg, size_t msglen,
+				    void *private_data)
+{
+	struct notifyd_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_state);
+	struct server_id my_id = messaging_server_id(state->msg_ctx);
+	struct notifyd_peer *p;
+	uint32_t i;
+	uint32_t msg_type;
+	struct server_id src, dst;
+	struct server_id_buf idbuf;
+	NTSTATUS status;
+
+	if (msglen < MESSAGE_HDR_LENGTH) {
+		DEBUG(10, ("%s: Got short broadcast\n", __func__));
+		return;
+	}
+	message_hdr_get(&msg_type, &src, &dst, msg);
+
+	if (msg_type != MSG_SMB_NOTIFY_REC_CHANGES) {
+		DEBUG(10, ("%s Got message %u, ignoring\n", __func__,
+			   (unsigned)msg_type));
+		return;
+	}
+	if (server_id_equal(&src, &my_id)) {
+		DEBUG(10, ("%s: Ignoring my own broadcast\n", __func__));
+		return;
+	}
+
+	DEBUG(10, ("%s: Got MSG_SMB_NOTIFY_REC_CHANGES from %s\n",
+		   __func__, server_id_str_buf(src, &idbuf)));
+
+	for (i=0; i<state->num_peers; i++) {
+		if (server_id_equal(&state->peers[i]->pid, &src)) {
+
+			DEBUG(10, ("%s: Applying changes to peer %u\n",
+				   __func__, (unsigned)i));
+
+			notifyd_apply_reclog(state->peers[i],
+					     msg + MESSAGE_HDR_LENGTH,
+					     msglen - MESSAGE_HDR_LENGTH);
+
+			state->peers[i]->last_broadcast = time(NULL);
+			return;
+		}
+	}
+
+	DEBUG(10, ("%s: Creating new peer for %s\n", __func__,
+		   server_id_str_buf(src, &idbuf)));
+
+	p = notifyd_peer_new(state, src);
+	if (p == NULL) {
+		DEBUG(10, ("%s: notifyd_peer_new failed\n", __func__));
+		return;
+	}
+
+	status = messaging_send_buf(state->msg_ctx, src, MSG_SMB_NOTIFY_GET_DB,
+				    NULL, 0);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: messaging_send_buf failed: %s\n",
+			   __func__, nt_errstr(status)));
+		TALLOC_FREE(p);
+		return;
+	}
+}
diff --git a/source3/smbd/notifyd/notifyd.h b/source3/smbd/notifyd/notifyd.h
new file mode 100644
index 0000000..dc0a4e8
--- /dev/null
+++ b/source3/smbd/notifyd/notifyd.h
@@ -0,0 +1,143 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __NOTIFYD_NOTIFYD_H__
+#define __NOTIFYD_NOTIFYD_H__
+
+#include "includes.h"
+#include "librpc/gen_ndr/notify.h"
+#include "librpc/gen_ndr/messaging.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "messages.h"
+#include "tdb.h"
+#include "util_tdb.h"
+
+/*
+ * Filechangenotify based on asynchronous messages
+ *
+ * smbds talk to local notify daemons to inform them about paths they are
+ * interested in. They also tell local notify daemons about changes they have
+ * done to the file system. There's two message types from smbd to
+ * notifyd. The first is used to inform notifyd about changes in notify
+ * interest. These are only sent from smbd to notifyd if the SMB client issues
+ * FileChangeNotify requests.
+ */
+
+/*
+ * The notifyd implementation is designed to cope with multiple daemons taking
+ * care of just a subset of smbds. The goal is to minimize the traffic between
+ * the notify daemons. The idea behind this is a samba/ctdb cluster, but it
+ * could also be used to spread the load of notifyd instances on a single
+ * node, should this become a bottleneck. The following diagram illustrates
+ * the setup. The numbers in the boxes are node:process ids.
+ *
+ *         +-----------+                  +-----------+
+ *         |notifyd 0:5|------------------|notifyd 1:6|
+ *         +-----------+                  +-----------+
+ *            / |  \                         /    \
+ *           /  |   \                       /      \
+ *   +--------+ | +--------+        +--------+   +--------+
+ *   |smbd 0:1| | |smbd 0:4|        |smbd 1:7|   |smbd 1:2|
+ *   +--------+ | +--------+        +--------+   +--------+
+ *              |
+ *     	   +---------+
+ *	   |smbd 0:20|
+ *	   +---------+
+ *
+ * Suppose 0:1 and 0:4 are interested in changes for /foo and 0:20 creates the
+ * file /foo/bar, if everything fully connected, 0:20 would have to send two
+ * local messages, one to 0:1 and one to 0:4. With the notifyd design, 0:20
+ * only has to send one message, it lets notifyd 0:5 do the hard work to
+ * multicast the change to 0:1 and 0:4.
+ *
+ * Now lets assume 1:7 on the other node creates /foo/baz. It tells its
+ * notifyd 1:6 about this change. All 1:6 will know about is that its peer
+ * notifyd 0:5 is interested in the change. Thus it forwards the event to 0:5,
+ * which sees it as if it came from just another local event creator. 0:5 will
+ * multicast the change to 0:1 and 0:4. To prevent notify loops, the message
+ * from 1:6 to 0:5 will carry a "proxied" flag, so that 0:5 will only forward
+ * the event to local clients.
+ */
+
+/*
+ * Data that notifyd maintains per smbd notify instance
+ */
+struct notify_instance {
+	struct timespec creation_time;
+	uint32_t filter;
+	uint32_t subdir_filter;
+	void *private_data;
+};
+
+/* MSG_SMB_NOTIFY_REC_CHANGE payload */
+struct notify_rec_change_msg {
+	struct notify_instance instance;
+	char path[];
+};
+
+/*
+ * The second message from smbd to notifyd is sent whenever an smbd makes a
+ * file system change. It tells notifyd to inform all interested parties about
+ * that change. This is the message that needs to be really fast in smbd
+ * because it is called a lot.
+ */
+
+/* MSG_SMB_NOTIFY_TRIGGER payload */
+struct notify_trigger_msg {
+	struct timespec when;
+	uint32_t action;
+	uint32_t filter;
+	char path[];
+};
+
+/*
+ * In response to a MSG_SMB_NOTIFY_TRIGGER message notifyd walks its database
+ * and sends out the following message to all interested clients
+ */
+
+/* MSG_PVFS_NOTIFY payload */
+struct notify_event_msg {
+	struct timespec when;
+	void *private_data;
+	uint32_t action;
+	char path[];
+};
+
+struct sys_notify_context;
+
+typedef int (*sys_notify_watch_fn)(TALLOC_CTX *mem_ctx,
+				   struct sys_notify_context *ctx,
+				   const char *path,
+				   uint32_t *filter,
+				   uint32_t *subdir_filter,
+				   void (*callback)(struct sys_notify_context *ctx,
+						    void *private_data,
+						    struct notify_event *ev),
+				   void *private_data,
+				   void *handle_p);
+
+struct tevent_req *notifyd_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+				struct messaging_context *msg_ctx,
+				struct ctdbd_connection *ctdbd_conn,
+				sys_notify_watch_fn sys_notify_watch,
+				struct sys_notify_context *sys_notify_ctx);
+int notifyd_recv(struct tevent_req *req);
+
+#endif
diff --git a/source3/smbd/notifyd/tests.c b/source3/smbd/notifyd/tests.c
new file mode 100644
index 0000000..6bcce6a
--- /dev/null
+++ b/source3/smbd/notifyd/tests.c
@@ -0,0 +1,118 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "notifyd.h"
+#include "messages.h"
+#include "lib/util/server_id_db.h"
+
+int main(int argc, const char *argv[])
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct tevent_context *ev;
+	struct messaging_context *msg_ctx;
+	struct server_id_db *names;
+	struct server_id notifyd;
+	struct tevent_req *req;
+	unsigned i;
+	bool ok;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s <smb.conf-file>\n", argv[0]);
+		exit(1);
+	}
+
+	setup_logging(argv[0], DEBUG_STDOUT);
+	lp_load_global(argv[1]);
+
+	ev = tevent_context_init(NULL);
+	if (ev == NULL) {
+		fprintf(stderr, "tevent_context_init failed\n");
+		exit(1);
+	}
+
+	msg_ctx = messaging_init(ev, ev);
+	if (msg_ctx == NULL) {
+		fprintf(stderr, "messaging_init failed\n");
+		exit(1);
+	}
+
+	names = messaging_names_db(msg_ctx);
+
+	ok = server_id_db_lookup_one(names, "notify-daemon", &notifyd);
+	if (!ok) {
+		fprintf(stderr, "no notifyd\n");
+		exit(1);
+	}
+
+	for (i=0; i<50000; i++) {
+		struct notify_rec_change_msg msg = {
+			.instance.filter = UINT32_MAX,
+			.instance.subdir_filter = UINT32_MAX
+		};
+		char path[64];
+		size_t len;
+		struct iovec iov[2];
+		NTSTATUS status;
+
+		len = snprintf(path, sizeof(path), "/tmp%u", i);
+
+		iov[0].iov_base = &msg;
+		iov[0].iov_len = offsetof(struct notify_rec_change_msg, path);
+		iov[1].iov_base = path;
+		iov[1].iov_len = len+1;
+
+		status = messaging_send_iov(
+			msg_ctx, notifyd, MSG_SMB_NOTIFY_REC_CHANGE,
+			iov, ARRAY_SIZE(iov), NULL, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			fprintf(stderr, "messaging_send_iov returned %s\n",
+				nt_errstr(status));
+			exit(1);
+		}
+
+		msg.instance.filter = 0;
+		msg.instance.subdir_filter = 0;
+
+		status = messaging_send_iov(
+			msg_ctx, notifyd, MSG_SMB_NOTIFY_REC_CHANGE,
+			iov, ARRAY_SIZE(iov), NULL, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			fprintf(stderr, "messaging_send_iov returned %s\n",
+				nt_errstr(status));
+			exit(1);
+		}
+	}
+
+	req = messaging_read_send(ev, ev, msg_ctx, MSG_PONG);
+	if (req == NULL) {
+		fprintf(stderr, "messaging_read_send failed\n");
+		exit(1);
+	}
+	messaging_send_buf(msg_ctx, notifyd, MSG_PING, NULL, 0);
+
+	ok = tevent_req_poll(req, ev);
+	if (!ok) {
+		fprintf(stderr, "tevent_req_poll failed\n");
+		exit(1);
+	}
+
+	TALLOC_FREE(frame);
+	return 0;
+}
diff --git a/source3/smbd/notifyd/wscript_build b/source3/smbd/notifyd/wscript_build
new file mode 100644
index 0000000..90a9505
--- /dev/null
+++ b/source3/smbd/notifyd/wscript_build
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+bld.SAMBA3_SUBSYSTEM('notifyd',
+		     source='notifyd.c',
+                     deps='util_tdb TDB_LIB messages_util')
+
+bld.SAMBA3_BINARY('notifyd-tests',
+                  source='tests.c',
+                  install=False,
+                  deps='''
+                    param
+                  ''')
diff --git a/source3/wscript_build b/source3/wscript_build
index 60d1ea6..c02794c 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1526,6 +1526,7 @@ bld.RECURSE('../examples/pdb')
 bld.RECURSE('../examples/VFS')
 bld.RECURSE('lib/netapi/tests')
 bld.RECURSE('lib/netapi/examples')
+bld.RECURSE('smbd/notifyd')
 
 bld.ENFORCE_GROUP_ORDERING()
 bld.CHECK_PROJECT_RULES()
-- 
1.7.9.5


From 45353944ada1882c590bdaab545ca08624c1416d Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 16:55:25 +0100
Subject: [PATCH 09/20] smbd: Start the notify daemon

For this we need the kernel change notify stuff to be global: There's only one
notifyd and we have to pass over the kernel change notify watch function

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/server.c |   95 +++++++++++++++++++++++++++++++++++++++++++++++++
 source3/wscript_build |    1 +
 2 files changed, 96 insertions(+)

diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 20aed16..312dde6 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -49,6 +49,7 @@
 #include "scavenger.h"
 #include "locking/leases_db.h"
 #include "../../ctdb/include/ctdb_protocol.h"
+#include "smbd/notifyd/notifyd.h"
 
 struct smbd_open_socket;
 struct smbd_child_pid;
@@ -404,6 +405,97 @@ static void smb_tell_num_children(struct messaging_context *ctx, void *data,
 	}
 }
 
+static void notifyd_stopped(struct tevent_req *req);
+
+static struct tevent_req *notifyd_req(struct messaging_context *msg_ctx,
+				      struct tevent_context *ev)
+{
+	struct tevent_req *req;
+	sys_notify_watch_fn sys_notify_watch = NULL;
+	struct sys_notify_context *sys_notify_ctx = NULL;
+
+	if (lp_kernel_change_notify()) {
+
+#ifdef HAVE_INOTIFY
+		if (lp_parm_bool(-1, "notify", "inotify", true)) {
+			sys_notify_watch = inotify_watch;
+		}
+#endif
+
+#ifdef HAVE_FAM
+		if (lp_parm_bool(-1, "notify", "fam",
+				 (sys_notify_watch == NULL))) {
+			sys_notify_watch = fam_watch;
+		}
+#endif
+	}
+
+	if (sys_notify_watch != NULL) {
+		sys_notify_ctx = sys_notify_context_create(msg_ctx, ev);
+		if (sys_notify_ctx == NULL) {
+			return NULL;
+		}
+	}
+
+	req = notifyd_send(msg_ctx, ev, msg_ctx,
+			   messaging_ctdbd_connection(),
+			   sys_notify_watch, sys_notify_ctx);
+	if (req == NULL) {
+		TALLOC_FREE(sys_notify_ctx);
+		return NULL;
+	}
+	tevent_req_set_callback(req, notifyd_stopped, msg_ctx);
+
+	return req;
+}
+
+static void notifyd_stopped(struct tevent_req *req)
+{
+	int ret;
+
+	ret = notifyd_recv(req);
+	TALLOC_FREE(req);
+	DEBUG(1, ("notifyd stopped: %s\n", strerror(ret)));
+}
+
+static bool smbd_notifyd_init(struct messaging_context *msg, bool interactive)
+{
+	struct tevent_context *ev = messaging_tevent_context(msg);
+	struct tevent_req *req;
+	pid_t pid;
+	NTSTATUS status;
+
+	if (interactive) {
+		req = notifyd_req(msg, ev);
+		return (req != NULL);
+	}
+
+	pid = fork();
+	if (pid == -1) {
+		DEBUG(1, ("%s: fork failed: %s\n", __func__,
+			  strerror(errno)));
+		return false;
+	}
+
+	if (pid != 0) {
+		return true;
+	}
+
+	status = reinit_after_fork(msg, ev, true);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("%s: reinit_after_fork failed: %s\n",
+			  __func__, nt_errstr(status)));
+		exit(1);
+	}
+
+	req = notifyd_req(msg, ev);
+	if (req == NULL) {
+		exit(1);
+	}
+	tevent_req_set_callback(req, notifyd_stopped, msg);
+	tevent_req_poll(req, ev);
+	return true;
+}
 
 /*
   at most every smbd:cleanuptime seconds (default 20), we scan the BRL
@@ -1485,6 +1577,9 @@ extern void build_options(bool screen);
 	if (!smbd_parent_notify_init(NULL, msg_ctx, ev_ctx)) {
 		exit_daemon("Samba cannot init notification", EACCES);
 	}
+	if (!smbd_notifyd_init(msg_ctx, interactive)) {
+		exit_daemon("Samba cannot init notification", EACCES);
+	}
 
 	if (!messaging_parent_dgm_cleanup_init(msg_ctx)) {
 		exit(1);
diff --git a/source3/wscript_build b/source3/wscript_build
index c02794c..a9fe6e4 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -638,6 +638,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    NDR_SMB_ACL
                    netapi
                    NDR_IOCTL
+                   notifyd
                    ''' + bld.env['dmapi_lib']
                    + (bld.CONFIG_GET('SAMBA_FAM_LIBS')
                          if bld.CONFIG_SET('SAMBA_FAM_LIBS') else ''),
-- 
1.7.9.5


From ab3b014a3fb40487309500667de37371224cd34e Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 16:58:47 +0100
Subject: [PATCH 10/20] smbd: Don't start the notify cleanup anymore

We don't have a database to clean up anymore

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/server.c |  100 -------------------------------------------------
 1 file changed, 100 deletions(-)

diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 312dde6..71ea8eb 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -280,103 +280,6 @@ static void smbd_parent_ctdb_reconfigured(
 			   MSG_SMB_BRL_VALIDATE, NULL, 0);
 }
 
-struct smbd_parent_notify_state {
-	struct tevent_context *ev;
-	struct messaging_context *msg;
-	uint32_t msgtype;
-	struct notify_context *notify;
-};
-
-static int smbd_parent_notify_cleanup(void *private_data);
-static void smbd_parent_notify_cleanup_done(struct tevent_req *req);
-static void smbd_parent_notify_proxy_done(struct tevent_req *req);
-
-static bool smbd_parent_notify_init(TALLOC_CTX *mem_ctx,
-				    struct messaging_context *msg,
-				    struct tevent_context *ev)
-{
-	struct smbd_parent_notify_state *state;
-	struct tevent_req *req;
-
-	state = talloc(mem_ctx, struct smbd_parent_notify_state);
-	if (state == NULL) {
-		return false;
-	}
-	state->msg = msg;
-	state->ev = ev;
-	state->msgtype = MSG_SMB_NOTIFY_CLEANUP;
-
-	state->notify = notify_init(state, msg, ev);
-	if (state->notify == NULL) {
-		goto fail;
-	}
-	req = background_job_send(
-		state, state->ev, state->msg, &state->msgtype, 1,
-		lp_parm_int(-1, "smbd", "notify cleanup interval", 60),
-		smbd_parent_notify_cleanup, state->notify);
-	if (req == NULL) {
-		goto fail;
-	}
-	tevent_req_set_callback(req, smbd_parent_notify_cleanup_done, state);
-
-	if (!lp_clustering()) {
-		return true;
-	}
-
-	req = notify_cluster_proxy_send(state, ev, state->notify);
-	if (req == NULL) {
-		goto fail;
-	}
-	tevent_req_set_callback(req, smbd_parent_notify_proxy_done, state);
-
-	return true;
-fail:
-	TALLOC_FREE(state);
-	return false;
-}
-
-static int smbd_parent_notify_cleanup(void *private_data)
-{
-	struct notify_context *notify = talloc_get_type_abort(
-		private_data, struct notify_context);
-	notify_cleanup(notify);
-	return lp_parm_int(-1, "smbd", "notify cleanup interval", 60);
-}
-
-static void smbd_parent_notify_cleanup_done(struct tevent_req *req)
-{
-	struct smbd_parent_notify_state *state = tevent_req_callback_data(
-		req, struct smbd_parent_notify_state);
-	NTSTATUS status;
-
-	status = background_job_recv(req);
-	TALLOC_FREE(req);
-	DEBUG(1, ("notify cleanup job ended with %s\n", nt_errstr(status)));
-
-	/*
-	 * Provide self-healing: Whatever the error condition was, it
-	 * will have printed it into log.smbd. Just retrying and
-	 * spamming log.smbd once a minute should be fine.
-	 */
-	req = background_job_send(
-		state, state->ev, state->msg, &state->msgtype, 1, 60,
-		smbd_parent_notify_cleanup, state->notify);
-	if (req == NULL) {
-		DEBUG(1, ("background_job_send failed\n"));
-		return;
-	}
-	tevent_req_set_callback(req, smbd_parent_notify_cleanup_done, state);
-}
-
-static void smbd_parent_notify_proxy_done(struct tevent_req *req)
-{
-	int ret;
-
-	ret = notify_cluster_proxy_recv(req);
-	TALLOC_FREE(req);
-	DEBUG(1, ("notify proxy job ended with %s\n", strerror(ret)));
-}
-
 static void add_child_pid(struct smbd_parent_context *parent,
 			  pid_t pid)
 {
@@ -1574,9 +1477,6 @@ extern void build_options(bool screen);
 		exit_daemon("Samba cannot init leases", EACCES);
 	}
 
-	if (!smbd_parent_notify_init(NULL, msg_ctx, ev_ctx)) {
-		exit_daemon("Samba cannot init notification", EACCES);
-	}
 	if (!smbd_notifyd_init(msg_ctx, interactive)) {
 		exit_daemon("Samba cannot init notification", EACCES);
 	}
-- 
1.7.9.5


From 74326492fc65d7f70eb868dfb79d4847ce897a15 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 17:05:16 +0100
Subject: [PATCH 11/20] smbd: Replace the tdb-based notify_internal with
 notify_msg

For the moment, this removes smbstatus -N output. It will come back with
the next commits.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify_internal.c | 1278 ----------------------------------------
 source3/smbd/notify_msg.c      |  268 +++++++++
 source3/smbd/proto.h           |    5 -
 source3/wscript_build          |    4 +-
 4 files changed, 270 insertions(+), 1285 deletions(-)
 delete mode 100644 source3/smbd/notify_internal.c
 create mode 100644 source3/smbd/notify_msg.c

diff --git a/source3/smbd/notify_internal.c b/source3/smbd/notify_internal.c
deleted file mode 100644
index dc50d4f..0000000
--- a/source3/smbd/notify_internal.c
+++ /dev/null
@@ -1,1278 +0,0 @@
-/*
-   Unix SMB/CIFS implementation.
-
-   Copyright (C) Andrew Tridgell 2006
-   Copyright (C) Volker Lendecke 2012
-
-   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 3 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, see <http://www.gnu.org/licenses/>.
-*/
-
-/*
-  this is the change notify database. It implements mechanisms for
-  storing current change notify waiters in a tdb, and checking if a
-  given event matches any of the stored notify waiters.
-*/
-
-#include "includes.h"
-#include "system/filesys.h"
-#include "librpc/gen_ndr/ndr_notify.h"
-#include "dbwrap/dbwrap.h"
-#include "dbwrap/dbwrap_open.h"
-#include "dbwrap/dbwrap_tdb.h"
-#include "smbd/smbd.h"
-#include "messages.h"
-#include "lib/tdb_wrap/tdb_wrap.h"
-#include "util_tdb.h"
-#include "lib/param/param.h"
-#include "lib/dbwrap/dbwrap_cache.h"
-#include "ctdb_srvids.h"
-#include "ctdbd_conn.h"
-#include "ctdb_conn.h"
-#include "lib/util/tevent_unix.h"
-
-struct notify_list {
-	struct notify_list *next, *prev;
-	const char *path;
-	void (*callback)(void *, struct timespec, const struct notify_event *);
-	void *private_data;
-};
-
-struct notify_context {
-	struct messaging_context *msg;
-	struct notify_list *list;
-
-	/*
-	 * The notify database is split up into two databases: One
-	 * relatively static index db and the real notify db with the
-	 * volatile entries.
-	 */
-
-	/*
-	 * "db_notify" is indexed by pathname. Per record it stores an
-	 * array of notify_db_entry structs. These represent the
-	 * notify records as requested by the smb client. This
-	 * database is always held locally, it is never clustered.
-	 */
-	struct db_context *db_notify;
-
-	/*
-	 * "db_index" is indexed by pathname. The records are an array
-	 * of VNNs which have any interest in notifies for this path
-	 * name.
-	 *
-	 * In the non-clustered case this database is cached in RAM by
-	 * means of db_cache_open, which maintains a cache per
-	 * process. Cache consistency is maintained by the tdb
-	 * sequence number.
-	 *
-	 * In the clustered case right now we can not use the tdb
-	 * sequence number, but by means of read only records we
-	 * should be able to avoid a lot of full migrations.
-	 *
-	 * In both cases, it is important to keep the update
-	 * operations to db_index to a minimum. This is achieved by
-	 * delayed deletion. When a db_notify is initially created,
-	 * the db_index record is also created. When more notifies are
-	 * added for a path, then only the db_notify record needs to be
-	 * modified, the db_index record is not touched. When the last
-	 * entry from the db_notify record is deleted, the db_index
-	 * record is not immediately deleted. Instead, the db_notify
-	 * record is replaced with a current timestamp. A regular
-	 * cleanup process will delete all db_index records that are
-	 * older than a minute.
-	 */
-	struct db_context *db_index;
-};
-
-static void notify_trigger_local(struct notify_context *notify,
-				 uint32_t action, uint32_t filter,
-				 const char *path, size_t path_len,
-				 bool recursive);
-static NTSTATUS notify_send(struct notify_context *notify,
-			    struct server_id *pid,
-			    const char *path, uint32_t action,
-			    void *private_data);
-static NTSTATUS notify_add_entry(struct db_record *rec,
-				 const struct notify_db_entry *e,
-				 bool *p_add_idx);
-static NTSTATUS notify_add_idx(struct db_record *rec, uint32_t vnn);
-
-static NTSTATUS notify_del_entry(struct db_record *rec,
-				 const struct server_id *pid,
-				 void *private_data);
-static NTSTATUS notify_del_idx(struct db_record *rec, uint32_t vnn);
-
-static int notify_context_destructor(struct notify_context *notify);
-
-static void notify_handler(struct messaging_context *msg_ctx,
-			   void *private_data, uint32_t msg_type,
-			   struct server_id server_id, DATA_BLOB *data);
-
-struct notify_context *notify_init(TALLOC_CTX *mem_ctx,
-				   struct messaging_context *msg,
-				   struct tevent_context *ev)
-{
-	struct loadparm_context *lp_ctx;
-	struct notify_context *notify;
-	char *db_path;
-
-	notify = talloc(mem_ctx, struct notify_context);
-	if (notify == NULL) {
-		goto fail;
-	}
-	notify->msg = msg;
-	notify->list = NULL;
-
-	lp_ctx = loadparm_init_s3(notify, loadparm_s3_helpers());
-
-	db_path = lock_path("notify.tdb");
-	if (db_path == NULL) {
-		goto fail;
-	}
-
-	notify->db_notify = db_open_tdb(
-		notify, lp_ctx, db_path,
-		0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
-		O_RDWR|O_CREAT, 0644, DBWRAP_LOCK_ORDER_2, DBWRAP_FLAG_NONE);
-	talloc_unlink(notify, lp_ctx);
-	TALLOC_FREE(db_path);
-	if (notify->db_notify == NULL) {
-		goto fail;
-	}
-
-	db_path = lock_path("notify_index.tdb");
-	if (db_path == NULL) {
-		goto fail;
-	}
-
-	notify->db_index = db_open(
-		notify, db_path,
-		0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
-		O_RDWR|O_CREAT, 0644, DBWRAP_LOCK_ORDER_3, DBWRAP_FLAG_NONE);
-	TALLOC_FREE(db_path);
-	if (notify->db_index == NULL) {
-		goto fail;
-	}
-	if (!lp_clustering()) {
-		notify->db_index = db_open_cache(notify, notify->db_index);
-		if (notify->db_index == NULL) {
-			goto fail;
-		}
-	}
-
-	if (notify->msg != NULL) {
-		NTSTATUS status;
-
-		status = messaging_register(notify->msg, notify,
-					    MSG_PVFS_NOTIFY, notify_handler);
-		if (!NT_STATUS_IS_OK(status)) {
-			DEBUG(1, ("messaging_register returned %s\n",
-				  nt_errstr(status)));
-			goto fail;
-		}
-	}
-
-	talloc_set_destructor(notify, notify_context_destructor);
-
-	return notify;
-fail:
-	TALLOC_FREE(notify);
-	return NULL;
-}
-
-static int notify_context_destructor(struct notify_context *notify)
-{
-	DEBUG(10, ("notify_context_destructor called\n"));
-
-	if (notify->msg != NULL) {
-		messaging_deregister(notify->msg, MSG_PVFS_NOTIFY, notify);
-	}
-
-	while (notify->list != NULL) {
-		DEBUG(10, ("Removing private_data=%p\n",
-			   notify->list->private_data));
-		notify_remove(notify, notify->list->private_data);
-	}
-	return 0;
-}
-
-NTSTATUS notify_add(struct notify_context *notify,
-		    const char *path, uint32_t filter, uint32_t subdir_filter,
-		    void (*callback)(void *, struct timespec,
-				     const struct notify_event *),
-		    void *private_data)
-{
-	struct notify_db_entry e;
-	struct notify_list *listel;
-	struct db_record *notify_rec, *idx_rec;
-	bool add_idx;
-	NTSTATUS status;
-	TDB_DATA key, notify_copy;
-
-	if (notify == NULL) {
-		return NT_STATUS_NOT_IMPLEMENTED;
-	}
-
-	DEBUG(10, ("notify_add: path=[%s], private_data=%p\n", path,
-		   private_data));
-
-	listel = talloc(notify, struct notify_list);
-	if (listel == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	listel->callback = callback;
-	listel->private_data = private_data;
-	listel->path = talloc_strdup(listel, path);
-	if (listel->path == NULL) {
-		TALLOC_FREE(listel);
-		return NT_STATUS_NO_MEMORY;
-	}
-	DLIST_ADD(notify->list, listel);
-
-	ZERO_STRUCT(e);
-	e.filter = filter;
-	e.subdir_filter = subdir_filter;
-	e.server = messaging_server_id(notify->msg);
-	e.private_data = private_data;
-
-	key = string_tdb_data(path);
-
-	notify_rec = dbwrap_fetch_locked(notify->db_notify,
-					 talloc_tos(), key);
-	if (notify_rec == NULL) {
-		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
-		goto fail;
-	}
-
-	/*
-	 * Make a copy of the notify_rec for easy restore in case
-	 * updating the index_rec fails;
-	 */
-	notify_copy = dbwrap_record_get_value(notify_rec);
-	if (notify_copy.dsize != 0) {
-		notify_copy.dptr = (uint8_t *)talloc_memdup(
-			notify_rec, notify_copy.dptr,
-			notify_copy.dsize);
-		if (notify_copy.dptr == NULL) {
-			TALLOC_FREE(notify_rec);
-			status = NT_STATUS_NO_MEMORY;
-			goto fail;
-		}
-	}
-
-	if (DEBUGLEVEL >= 10) {
-		NDR_PRINT_DEBUG(notify_db_entry, &e);
-	}
-
-	status = notify_add_entry(notify_rec, &e, &add_idx);
-	if (!NT_STATUS_IS_OK(status)) {
-		goto fail;
-	}
-	if (!add_idx) {
-		/*
-		 * Someone else has added the idx entry already
-		 */
-		TALLOC_FREE(notify_rec);
-		return NT_STATUS_OK;
-	}
-
-	idx_rec = dbwrap_fetch_locked(notify->db_index,
-				      talloc_tos(), key);
-	if (idx_rec == NULL) {
-		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
-		goto restore_notify;
-	}
-	status = notify_add_idx(idx_rec, get_my_vnn());
-	if (!NT_STATUS_IS_OK(status)) {
-		goto restore_notify;
-	}
-
-	TALLOC_FREE(idx_rec);
-	TALLOC_FREE(notify_rec);
-	return NT_STATUS_OK;
-
-restore_notify:
-	if (notify_copy.dsize != 0) {
-		dbwrap_record_store(notify_rec, notify_copy, 0);
-	} else {
-		dbwrap_record_delete(notify_rec);
-	}
-	TALLOC_FREE(notify_rec);
-fail:
-	DLIST_REMOVE(notify->list, listel);
-	TALLOC_FREE(listel);
-	return status;
-}
-
-static NTSTATUS notify_add_entry(struct db_record *rec,
-				 const struct notify_db_entry *e,
-				 bool *p_add_idx)
-{
-	TDB_DATA value = dbwrap_record_get_value(rec);
-	struct notify_db_entry *entries;
-	size_t num_entries;
-	bool add_idx = true;
-	NTSTATUS status;
-
-	if (value.dsize == sizeof(time_t)) {
-		DEBUG(10, ("Re-using deleted entry\n"));
-		value.dsize = 0;
-		add_idx = false;
-	}
-
-	if ((value.dsize % sizeof(struct notify_db_entry)) != 0) {
-		DEBUG(1, ("Invalid value.dsize = %u\n",
-			  (unsigned)value.dsize));
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	num_entries = value.dsize / sizeof(struct notify_db_entry);
-
-	if (num_entries != 0) {
-		add_idx = false;
-	}
-
-	entries = talloc_array(rec, struct notify_db_entry, num_entries + 1);
-	if (entries == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	memcpy(entries, value.dptr, value.dsize);
-
-	entries[num_entries] = *e;
-	value = make_tdb_data((uint8_t *)entries, talloc_get_size(entries));
-	status = dbwrap_record_store(rec, value, 0);
-	TALLOC_FREE(entries);
-	if (!NT_STATUS_IS_OK(status)) {
-		return status;
-	}
-	*p_add_idx = add_idx;
-	return NT_STATUS_OK;
-}
-
-static NTSTATUS notify_add_idx(struct db_record *rec, uint32_t vnn)
-{
-	TDB_DATA value = dbwrap_record_get_value(rec);
-	uint32_t *vnns;
-	size_t i, num_vnns;
-	NTSTATUS status;
-
-	if ((value.dsize % sizeof(uint32_t)) != 0) {
-		DEBUG(1, ("Invalid value.dsize = %u\n",
-			  (unsigned)value.dsize));
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	num_vnns = value.dsize / sizeof(uint32_t);
-	vnns = (uint32_t *)value.dptr;
-
-	for (i=0; i<num_vnns; i++) {
-		if (vnns[i] == vnn) {
-			return NT_STATUS_OK;
-		}
-		if (vnns[i] > vnn) {
-			break;
-		}
-	}
-
-	value.dptr = (uint8_t *)talloc_realloc(
-		rec, value.dptr, uint32_t, num_vnns + 1);
-	if (value.dptr == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	value.dsize = talloc_get_size(value.dptr);
-
-	vnns = (uint32_t *)value.dptr;
-
-	memmove(&vnns[i+1], &vnns[i], sizeof(uint32_t) * (num_vnns - i));
-	vnns[i] = vnn;
-
-	status = dbwrap_record_store(rec, value, 0);
-	if (!NT_STATUS_IS_OK(status)) {
-		return status;
-	}
-	return NT_STATUS_OK;
-}
-
-NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
-{
-	struct server_id pid;
-	struct notify_list *listel;
-	struct db_record *notify_rec;
-	NTSTATUS status;
-
-	if ((notify == NULL) || (notify->msg == NULL)) {
-		return NT_STATUS_NOT_IMPLEMENTED;
-	}
-
-	DEBUG(10, ("notify_remove: private_data=%p\n", private_data));
-
-	pid = messaging_server_id(notify->msg);
-
-	for (listel=notify->list;listel;listel=listel->next) {
-		if (listel->private_data == private_data) {
-			DLIST_REMOVE(notify->list, listel);
-			break;
-		}
-	}
-	if (listel == NULL) {
-		DEBUG(10, ("%p not found\n", private_data));
-		return NT_STATUS_NOT_FOUND;
-	}
-	notify_rec = dbwrap_fetch_locked(notify->db_notify, talloc_tos(),
-					 string_tdb_data(listel->path));
-	TALLOC_FREE(listel);
-	if (notify_rec == NULL) {
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	status = notify_del_entry(notify_rec, &pid, private_data);
-	DEBUG(10, ("del_entry returned %s\n", nt_errstr(status)));
-	TALLOC_FREE(notify_rec);
-	return status;
-}
-
-static NTSTATUS notify_del_entry(struct db_record *rec,
-				 const struct server_id *pid,
-				 void *private_data)
-{
-	TDB_DATA value = dbwrap_record_get_value(rec);
-	struct server_id_buf tmp;
-	struct notify_db_entry *entries;
-	size_t i, num_entries;
-	time_t now;
-
-	DEBUG(10, ("del_entry called for %s %p\n",
-		   server_id_str_buf(*pid, &tmp), private_data));
-
-	if ((value.dsize % sizeof(struct notify_db_entry)) != 0) {
-		DEBUG(1, ("Invalid value.dsize = %u\n",
-			  (unsigned)value.dsize));
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	num_entries = value.dsize / sizeof(struct notify_db_entry);
-	entries = (struct notify_db_entry *)value.dptr;
-
-	for (i=0; i<num_entries; i++) {
-		struct notify_db_entry *e = &entries[i];
-
-		if (DEBUGLEVEL >= 10) {
-			NDR_PRINT_DEBUG(notify_db_entry, e);
-		}
-
-		if (e->private_data != private_data) {
-			continue;
-		}
-		if (serverid_equal(&e->server, pid)) {
-			break;
-		}
-	}
-	if (i == num_entries) {
-		return NT_STATUS_NOT_FOUND;
-	}
-	entries[i] = entries[num_entries-1];
-	value.dsize -= sizeof(struct notify_db_entry);
-
-	if (value.dsize == 0) {
-		now = time(NULL);
-		value.dptr = (uint8_t *)&now;
-		value.dsize = sizeof(now);
-	}
-	return dbwrap_record_store(rec, value, 0);
-}
-
-struct notify_trigger_index_state {
-	TALLOC_CTX *mem_ctx;
-	uint32_t *vnns;
-	uint32_t my_vnn;
-	bool found_my_vnn;
-};
-
-static void notify_trigger_index_parser(TDB_DATA key, TDB_DATA data,
-					void *private_data)
-{
-	struct notify_trigger_index_state *state =
-		(struct notify_trigger_index_state *)private_data;
-	uint32_t *new_vnns;
-	size_t i, num_vnns, num_new_vnns, num_remote_vnns;
-
-	if ((data.dsize % sizeof(uint32_t)) != 0) {
-		DEBUG(1, ("Invalid record size in notify index db: %u\n",
-			  (unsigned)data.dsize));
-		return;
-	}
-	new_vnns = (uint32_t *)data.dptr;
-	num_new_vnns = data.dsize / sizeof(uint32_t);
-	num_remote_vnns = num_new_vnns;
-
-	for (i=0; i<num_new_vnns; i++) {
-		if (new_vnns[i] == state->my_vnn) {
-			state->found_my_vnn = true;
-			num_remote_vnns -= 1;
-		}
-	}
-	if (num_remote_vnns == 0) {
-		return;
-	}
-
-	num_vnns = talloc_array_length(state->vnns);
-	state->vnns = talloc_realloc(state->mem_ctx, state->vnns, uint32_t,
-				     num_vnns + num_remote_vnns);
-	if (state->vnns == NULL) {
-		DEBUG(1, ("talloc_realloc failed\n"));
-		return;
-	}
-
-	for (i=0; i<num_new_vnns; i++) {
-		if (new_vnns[i] != state->my_vnn) {
-			state->vnns[num_vnns] = new_vnns[i];
-			num_vnns += 1;
-		}
-	}
-}
-
-static int vnn_cmp(const void *p1, const void *p2)
-{
-	const uint32_t *vnn1 = (const uint32_t *)p1;
-	const uint32_t *vnn2 = (const uint32_t *)p2;
-
-	if (*vnn1 < *vnn2) {
-		return -1;
-	}
-	if (*vnn1 == *vnn2) {
-		return 0;
-	}
-	return 1;
-}
-
-static bool notify_push_remote_blob(TALLOC_CTX *mem_ctx, uint32_t action,
-				    uint32_t filter, const char *path,
-				    uint8_t **pblob, size_t *pblob_len)
-{
-	struct notify_remote_event ev;
-	DATA_BLOB data;
-	enum ndr_err_code ndr_err;
-
-	ev.action = action;
-	ev.filter = filter;
-	ev.path = path;
-
-	if (DEBUGLEVEL >= 10) {
-		NDR_PRINT_DEBUG(notify_remote_event, &ev);
-	}
-
-	ndr_err = ndr_push_struct_blob(
-		&data, mem_ctx, &ev,
-		(ndr_push_flags_fn_t)ndr_push_notify_remote_event);
-	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-		return false;
-	}
-	*pblob = data.data;
-	*pblob_len = data.length;
-	return true;
-}
-
-static bool notify_pull_remote_blob(TALLOC_CTX *mem_ctx,
-				    const uint8_t *blob, size_t blob_len,
-				    uint32_t *paction, uint32_t *pfilter,
-				    char **path)
-{
-	struct notify_remote_event *ev;
-	enum ndr_err_code ndr_err;
-	DATA_BLOB data;
-	char *p;
-
-	data.data = discard_const_p(uint8_t, blob);
-	data.length = blob_len;
-
-	ev = talloc(mem_ctx, struct notify_remote_event);
-	if (ev == NULL) {
-		return false;
-	}
-
-	ndr_err = ndr_pull_struct_blob(
-		&data, ev, ev,
-		(ndr_pull_flags_fn_t)ndr_pull_notify_remote_event);
-	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-		TALLOC_FREE(ev);
-		return false;
-	}
-	if (DEBUGLEVEL >= 10) {
-		NDR_PRINT_DEBUG(notify_remote_event, ev);
-	}
-	*paction = ev->action;
-	*pfilter = ev->filter;
-	p = discard_const_p(char, ev->path);
-	*path = talloc_move(mem_ctx, &p);
-
-	TALLOC_FREE(ev);
-	return true;
-}
-
-void notify_trigger(struct notify_context *notify,
-		    uint32_t action, uint32_t filter,
-		    const char *dir, const char *name)
-{
-	struct ctdbd_connection *ctdbd_conn;
-	struct notify_trigger_index_state idx_state;
-	const char *p, *next_p;
-	size_t i, num_vnns;
-	uint32_t last_vnn;
-	uint8_t *remote_blob = NULL;
-	size_t remote_blob_len = 0;
-	struct iovec iov;
-	char *path, *to_free;
-	char tmpbuf[PATH_MAX];
-	ssize_t len;
-
-	DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
-		   "dir=%s, name=%s\n", (unsigned)action, (unsigned)filter,
-		   dir, name));
-
-	/* see if change notify is enabled at all */
-	if (notify == NULL) {
-		return;
-	}
-
-	if (dir[0] != '/') {
-		/*
-		 * The rest of this routine assumes an absolute path.
-		 */
-		return;
-	}
-
-	len = full_path_tos(dir, name, tmpbuf, sizeof(tmpbuf),
-			    &path, &to_free);
-	if (len == -1) {
-		DEBUG(1, ("full_path_tos failed\n"));
-		return;
-	}
-
-	idx_state.mem_ctx = talloc_tos();
-	idx_state.vnns = NULL;
-	idx_state.found_my_vnn = false;
-	idx_state.my_vnn = get_my_vnn();
-
-	for (p = strchr(path+1, '/'); p != NULL; p = next_p) {
-		ptrdiff_t path_len = p - path;
-		bool recursive;
-
-		next_p = strchr(p+1, '/');
-		recursive = (next_p != NULL);
-
-		dbwrap_parse_record(
-			notify->db_index,
-			make_tdb_data(discard_const_p(uint8_t, path), path_len),
-			notify_trigger_index_parser, &idx_state);
-
-		if (idx_state.found_my_vnn) {
-			notify_trigger_local(notify, action, filter,
-					     path, path_len, recursive);
-			idx_state.found_my_vnn = false;
-		}
-	}
-
-	if (idx_state.vnns == NULL) {
-		goto done;
-	}
-
-	ctdbd_conn = messaging_ctdbd_connection();
-	if (ctdbd_conn == NULL) {
-		goto done;
-	}
-
-	num_vnns = talloc_array_length(idx_state.vnns);
-	qsort(idx_state.vnns, num_vnns, sizeof(uint32_t), vnn_cmp);
-
-	last_vnn = 0xffffffff;
-
-	if (!notify_push_remote_blob(talloc_tos(), action, filter, path,
-				     &remote_blob, &remote_blob_len)) {
-		DEBUG(1, ("notify_push_remote_blob failed\n"));
-		goto done;
-	}
-
-	iov = (struct iovec) { .iov_base = remote_blob,
-			       .iov_len = remote_blob_len };
-
-	for (i=0; i<num_vnns; i++) {
-		uint32_t vnn = idx_state.vnns[i];
-		NTSTATUS status;
-
-		if (vnn == last_vnn) {
-			continue;
-		}
-
-		status = ctdbd_messaging_send_iov(
-			ctdbd_conn, vnn, CTDB_SRVID_SAMBA_NOTIFY_PROXY,
-			&iov, 1);
-		if (!NT_STATUS_IS_OK(status)) {
-			DEBUG(10, ("ctdbd_messaging_send_iov to vnn %d "
-				   "returned %s, ignoring\n", (int)vnn,
-				   nt_errstr(status)));
-		}
-
-		last_vnn = vnn;
-	}
-
-done:
-	TALLOC_FREE(remote_blob);
-	TALLOC_FREE(idx_state.vnns);
-	TALLOC_FREE(to_free);
-}
-
-static void notify_trigger_local(struct notify_context *notify,
-				 uint32_t action, uint32_t filter,
-				 const char *path, size_t path_len,
-				 bool recursive)
-{
-	TDB_DATA data;
-	struct notify_db_entry *entries;
-	size_t i, num_entries;
-	NTSTATUS status;
-
-	DEBUG(10, ("notify_trigger_local called for %*s, path_len=%d, "
-		   "filter=%d\n", (int)path_len, path, (int)path_len,
-		   (int)filter));
-
-	status = dbwrap_fetch(
-		notify->db_notify, talloc_tos(),
-		make_tdb_data(discard_const_p(uint8_t, path), path_len), &data);
-	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(10, ("dbwrap_fetch returned %s\n",
-			   nt_errstr(status)));
-		return;
-	}
-	if (data.dsize == sizeof(time_t)) {
-		DEBUG(10, ("Got deleted record\n"));
-		goto done;
-	}
-	if ((data.dsize % sizeof(struct notify_db_entry)) != 0) {
-		DEBUG(1, ("Invalid data.dsize = %u\n",
-			  (unsigned)data.dsize));
-		goto done;
-	}
-
-	entries = (struct notify_db_entry *)data.dptr;
-	num_entries = data.dsize / sizeof(struct notify_db_entry);
-
-	DEBUG(10, ("recursive = %s pathlen=%d (%c)\n",
-		   recursive ? "true" : "false", (int)path_len,
-		   path[path_len]));
-
-	for (i=0; i<num_entries; i++) {
-		struct notify_db_entry *e = &entries[i];
-		uint32_t e_filter;
-
-		if (DEBUGLEVEL >= 10) {
-			NDR_PRINT_DEBUG(notify_db_entry, e);
-		}
-
-		e_filter = recursive ? e->subdir_filter : e->filter;
-
-		if ((filter & e_filter) == 0) {
-			continue;
-		}
-
-		if (!procid_is_local(&e->server)) {
-			struct server_id_buf tmp;
-			DEBUG(1, ("internal error: Non-local pid %s in "
-				  "notify.tdb\n",
-				  server_id_str_buf(e->server, &tmp)));
-			continue;
-		}
-
-		status = notify_send(notify, &e->server, path + path_len + 1,
-				     action, e->private_data);
-		if (!NT_STATUS_IS_OK(status)) {
-			DEBUG(10, ("notify_send returned %s\n",
-				   nt_errstr(status)));
-		}
-	}
-
-done:
-	TALLOC_FREE(data.dptr);
-}
-
-struct notify_msg {
-	struct timespec when;
-	void *private_data;
-	uint32_t action;
-	char path[1];
-};
-
-static NTSTATUS notify_send(struct notify_context *notify,
-			    struct server_id *pid,
-			    const char *path, uint32_t action,
-			    void *private_data)
-{
-	struct notify_msg m = {};
-	struct iovec iov[2];
-
-	m.when = timespec_current();
-	m.private_data = private_data;
-	m.action = action;
-
-	iov[0].iov_base = &m;
-	iov[0].iov_len = offsetof(struct notify_msg, path);
-	iov[1].iov_base = discard_const_p(char, path);
-	iov[1].iov_len = strlen(path)+1;
-
-	return messaging_send_iov(notify->msg, *pid, MSG_PVFS_NOTIFY,
-				  iov, ARRAY_SIZE(iov), NULL, 0);
-}
-
-static void notify_handler(struct messaging_context *msg_ctx,
-			   void *private_data, uint32_t msg_type,
-			   struct server_id server_id, DATA_BLOB *data)
-{
-	struct notify_context *notify = talloc_get_type_abort(
-		private_data, struct notify_context);
-	struct notify_msg *m;
-	struct notify_event e;
-	struct notify_list *listel;
-
-	if (data->length == 0) {
-		DEBUG(1, ("%s: Got 0-sized MSG_PVFS_NOTIFY msg\n", __func__));
-		return;
-	}
-	if (data->data[data->length-1] != 0) {
-		DEBUG(1, ("%s: MSG_PVFS_NOTIFY path not 0-terminated\n",
-			  __func__));
-		return;
-	}
-
-	m = (struct notify_msg *)data->data;
-
-	e = (struct notify_event) {
-		.action = m->action,
-		.path = m->path,
-		.private_data = m->private_data,
-		.dir = discard_const_p(char, "")
-	};
-
-	for (listel=notify->list;listel;listel=listel->next) {
-		if (listel->private_data == m->private_data) {
-			listel->callback(listel->private_data, m->when, &e);
-			break;
-		}
-	}
-}
-
-struct notify_walk_idx_state {
-	void (*fn)(const char *path,
-		   uint32_t *vnns, size_t num_vnns,
-		   void *private_data);
-	void *private_data;
-};
-
-static int notify_walk_idx_fn(struct db_record *rec, void *private_data)
-{
-	struct notify_walk_idx_state *state =
-		(struct notify_walk_idx_state *)private_data;
-	TDB_DATA key, value;
-	char *path;
-
-	key = dbwrap_record_get_key(rec);
-	value = dbwrap_record_get_value(rec);
-
-	if ((value.dsize % sizeof(uint32_t)) != 0) {
-		DEBUG(1, ("invalid value size in notify index db: %u\n",
-			  (unsigned)(value.dsize)));
-		return 0;
-	}
-
-	path = talloc_strndup(talloc_tos(), (char *)key.dptr, key.dsize);
-	if (path == NULL) {
-		DEBUG(1, ("talloc_strndup failed\n"));
-		return 0;
-	}
-	state->fn(path, (uint32_t *)value.dptr, value.dsize/sizeof(uint32_t),
-		  state->private_data);
-	TALLOC_FREE(path);
-	return 0;
-}
-
-void notify_walk_idx(struct notify_context *notify,
-		     void (*fn)(const char *path,
-				uint32_t *vnns, size_t num_vnns,
-				void *private_data),
-		     void *private_data)
-{
-	struct notify_walk_idx_state state;
-	state.fn = fn;
-	state.private_data = private_data;
-	dbwrap_traverse_read(notify->db_index, notify_walk_idx_fn, &state,
-			     NULL);
-}
-
-struct notify_walk_state {
-	void (*fn)(const char *path,
-		   struct notify_db_entry *entries, size_t num_entries,
-		   time_t deleted_time, void *private_data);
-	void *private_data;
-};
-
-static int notify_walk_fn(struct db_record *rec, void *private_data)
-{
-	struct notify_walk_state *state =
-		(struct notify_walk_state *)private_data;
-	TDB_DATA key, value;
-	struct notify_db_entry *entries;
-	size_t num_entries;
-	time_t deleted_time;
-	char *path;
-
-	key = dbwrap_record_get_key(rec);
-	value = dbwrap_record_get_value(rec);
-
-	if (value.dsize == sizeof(deleted_time)) {
-		memcpy(&deleted_time, value.dptr, sizeof(deleted_time));
-		entries = NULL;
-		num_entries = 0;
-	} else {
-		if ((value.dsize % sizeof(struct notify_db_entry)) != 0) {
-			DEBUG(1, ("invalid value size in notify db: %u\n",
-				  (unsigned)(value.dsize)));
-			return 0;
-		}
-		entries = (struct notify_db_entry *)value.dptr;
-		num_entries = value.dsize / sizeof(struct notify_db_entry);
-		deleted_time = 0;
-	}
-
-	path = talloc_strndup(talloc_tos(), (char *)key.dptr, key.dsize);
-	if (path == NULL) {
-		DEBUG(1, ("talloc_strndup failed\n"));
-		return 0;
-	}
-	state->fn(path, entries, num_entries, deleted_time,
-		  state->private_data);
-	TALLOC_FREE(path);
-	return 0;
-}
-
-void notify_walk(struct notify_context *notify,
-		 void (*fn)(const char *path,
-			    struct notify_db_entry *entries,
-			    size_t num_entries,
-			    time_t deleted_time, void *private_data),
-		 void *private_data)
-{
-	struct notify_walk_state state;
-	state.fn = fn;
-	state.private_data = private_data;
-	dbwrap_traverse_read(notify->db_notify, notify_walk_fn, &state,
-			     NULL);
-}
-
-struct notify_cleanup_state {
-	TALLOC_CTX *mem_ctx;
-	time_t delete_before;
-	ssize_t array_size;
-	uint32_t num_paths;
-	char **paths;
-};
-
-static void notify_cleanup_collect(
-	const char *path, struct notify_db_entry *entries, size_t num_entries,
-	time_t deleted_time, void *private_data)
-{
-	struct notify_cleanup_state *state =
-		(struct notify_cleanup_state *)private_data;
-	char *p;
-
-	if (num_entries != 0) {
-		return;
-	}
-	if (deleted_time >= state->delete_before) {
-		return;
-	}
-
-	p = talloc_strdup(state->mem_ctx, path);
-	if (p == NULL) {
-		DEBUG(1, ("talloc_strdup failed\n"));
-		return;
-	}
-	add_to_large_array(state->mem_ctx, sizeof(p), (void *)&p,
-			   &state->paths, &state->num_paths,
-			   &state->array_size);
-	if (state->array_size == -1) {
-		TALLOC_FREE(p);
-	}
-}
-
-static bool notify_cleanup_path(struct notify_context *notify,
-			      const char *path, time_t delete_before);
-
-void notify_cleanup(struct notify_context *notify)
-{
-	struct notify_cleanup_state state;
-	uint32_t failure_pool;
-
-	ZERO_STRUCT(state);
-	state.mem_ctx = talloc_stackframe();
-
-	state.delete_before = time(NULL)
-		- lp_parm_int(-1, "smbd", "notify cleanup interval", 60);
-
-	notify_walk(notify, notify_cleanup_collect, &state);
-
-	failure_pool = state.num_paths;
-
-	while (state.num_paths != 0) {
-		size_t idx;
-
-		/*
-		 * This loop is designed to be as kind as possible to
-		 * ctdb. ctdb does not like it if many smbds hammer on a
-		 * single record. If on many nodes the cleanup process starts
-		 * running, it can happen that all of them need to clean up
-		 * records in the same order. This would generate a ctdb
-		 * migrate storm on these records. Randomizing the load across
-		 * multiple records reduces the load on the individual record.
-		 */
-
-		generate_random_buffer((uint8_t *)&idx, sizeof(idx));
-		idx = idx % state.num_paths;
-
-		if (!notify_cleanup_path(notify, state.paths[idx],
-					 state.delete_before)) {
-			/*
-			 * notify_cleanup_path failed, the most likely reason
-			 * is that dbwrap_try_fetch_locked failed due to
-			 * contention. We allow one failed attempt per deleted
-			 * path on average before we give up.
-			 */
-			failure_pool -= 1;
-			if (failure_pool == 0) {
-				/*
-				 * Too many failures. We will come back here,
-				 * maybe next time there is less contention.
-				 */
-				break;
-			}
-		}
-
-		TALLOC_FREE(state.paths[idx]);
-		state.paths[idx] = state.paths[state.num_paths-1];
-		state.num_paths -= 1;
-	}
-	TALLOC_FREE(state.mem_ctx);
-}
-
-static bool notify_cleanup_path(struct notify_context *notify,
-				const char *path, time_t delete_before)
-{
-	struct db_record *notify_rec = NULL;
-	struct db_record *idx_rec = NULL;
-	TDB_DATA key = string_tdb_data(path);
-	TDB_DATA value;
-	time_t deleted;
-	NTSTATUS status;
-
-	notify_rec = dbwrap_fetch_locked(notify->db_notify, talloc_tos(), key);
-	if (notify_rec == NULL) {
-		DEBUG(10, ("Could not fetch notify_rec\n"));
-		return false;
-	}
-	value = dbwrap_record_get_value(notify_rec);
-
-	if (value.dsize != sizeof(deleted)) {
-		DEBUG(10, ("record %s has been re-used\n", path));
-		goto done;
-	}
-	memcpy(&deleted, value.dptr, sizeof(deleted));
-
-	if (deleted >= delete_before) {
-		DEBUG(10, ("record %s too young\n", path));
-		goto done;
-	}
-
-	/*
-	 * Be kind to ctdb and only try one dmaster migration at most.
-	 */
-	idx_rec = dbwrap_try_fetch_locked(notify->db_index, talloc_tos(), key);
-	if (idx_rec == NULL) {
-		DEBUG(10, ("Could not fetch idx_rec\n"));
-		goto done;
-	}
-
-	status = dbwrap_record_delete(notify_rec);
-	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(10, ("Could not delete notify_rec: %s\n",
-			   nt_errstr(status)));
-	}
-
-	status = notify_del_idx(idx_rec, get_my_vnn());
-	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(10, ("Could not delete idx_rec: %s\n",
-			   nt_errstr(status)));
-	}
-
-done:
-	TALLOC_FREE(idx_rec);
-	TALLOC_FREE(notify_rec);
-	return true;
-}
-
-static NTSTATUS notify_del_idx(struct db_record *rec, uint32_t vnn)
-{
-	TDB_DATA value = dbwrap_record_get_value(rec);
-	uint32_t *vnns;
-	size_t i, num_vnns;
-
-	if ((value.dsize % sizeof(uint32_t)) != 0) {
-		DEBUG(1, ("Invalid value.dsize = %u\n",
-			  (unsigned)value.dsize));
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	num_vnns = value.dsize / sizeof(uint32_t);
-	vnns = (uint32_t *)value.dptr;
-
-	for (i=0; i<num_vnns; i++) {
-		if (vnns[i] == vnn) {
-			break;
-		}
-	}
-
-	if (i == num_vnns) {
-		/*
-		 * Not found. Should not happen, but okay...
-		 */
-		return NT_STATUS_OK;
-	}
-
-	memmove(&vnns[i], &vnns[i+1], sizeof(uint32_t) * (num_vnns - i - 1));
-	value.dsize -= sizeof(uint32_t);
-
-	if (value.dsize == 0) {
-		return dbwrap_record_delete(rec);
-	}
-	return dbwrap_record_store(rec, value, 0);
-}
-
-struct notify_cluster_proxy_state {
-	struct tevent_context *ev;
-	struct notify_context *notify;
-	struct ctdb_msg_channel *chan;
-};
-
-static void notify_cluster_proxy_got_chan(struct tevent_req *subreq);
-static void notify_cluster_proxy_got_msg(struct tevent_req *subreq);
-static void notify_cluster_proxy_trigger(struct notify_context *notify,
-					 uint32_t action, uint32_t filter,
-					 char *path);
-
-struct tevent_req *notify_cluster_proxy_send(
-	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-	struct notify_context *notify)
-{
-	struct tevent_req *req, *subreq;
-	struct notify_cluster_proxy_state *state;
-
-	req = tevent_req_create(mem_ctx, &state,
-				struct notify_cluster_proxy_state);
-	if (req == NULL) {
-		return NULL;
-	}
-	state->ev = ev;
-	state->notify = notify;
-
-	subreq = ctdb_msg_channel_init_send(
-		state, state->ev,  lp_ctdbd_socket(),
-		CTDB_SRVID_SAMBA_NOTIFY_PROXY);
-	if (tevent_req_nomem(subreq, req)) {
-		return tevent_req_post(req, ev);
-	}
-	tevent_req_set_callback(subreq, notify_cluster_proxy_got_chan, req);
-	return req;
-}
-
-static void notify_cluster_proxy_got_chan(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct notify_cluster_proxy_state *state = tevent_req_data(
-		req, struct notify_cluster_proxy_state);
-	int ret;
-
-	ret = ctdb_msg_channel_init_recv(subreq, state, &state->chan);
-	TALLOC_FREE(subreq);
-	if (ret != 0) {
-		tevent_req_error(req, ret);
-		return;
-	}
-	subreq = ctdb_msg_read_send(state, state->ev, state->chan);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, notify_cluster_proxy_got_msg, req);
-}
-
-static void notify_cluster_proxy_got_msg(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct notify_cluster_proxy_state *state = tevent_req_data(
-		req, struct notify_cluster_proxy_state);
-	uint8_t *msg;
-	size_t msg_len;
-	uint32_t action, filter;
-	char *path;
-	int ret;
-	bool res;
-
-	ret = ctdb_msg_read_recv(subreq, talloc_tos(), &msg, &msg_len);
-	TALLOC_FREE(subreq);
-	if (ret != 0) {
-		tevent_req_error(req, ret);
-		return;
-	}
-
-	res = notify_pull_remote_blob(talloc_tos(), msg, msg_len,
-				      &action, &filter, &path);
-	TALLOC_FREE(msg);
-	if (!res) {
-		tevent_req_error(req, EIO);
-		return;
-	}
-	notify_cluster_proxy_trigger(state->notify, action, filter, path);
-	TALLOC_FREE(path);
-
-	subreq = ctdb_msg_read_send(state, state->ev, state->chan);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, notify_cluster_proxy_got_msg, req);
-}
-
-static void notify_cluster_proxy_trigger(struct notify_context *notify,
-					 uint32_t action, uint32_t filter,
-					 char *path)
-{
-	const char *p, *next_p;
-
-	for (p = path; p != NULL; p = next_p) {
-		ptrdiff_t path_len = p - path;
-		bool recursive;
-
-		next_p = strchr(p+1, '/');
-		recursive = (next_p != NULL);
-
-		notify_trigger_local(notify, action, filter,
-				     path, path_len, recursive);
-	}
-}
-
-int notify_cluster_proxy_recv(struct tevent_req *req)
-{
-	return tevent_req_simple_recv_unix(req);
-}
diff --git a/source3/smbd/notify_msg.c b/source3/smbd/notify_msg.c
new file mode 100644
index 0000000..b31cb57
--- /dev/null
+++ b/source3/smbd/notify_msg.c
@@ -0,0 +1,268 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "librpc/gen_ndr/notify.h"
+#include "librpc/gen_ndr/messaging.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "messages.h"
+#include "proto.h"
+#include "globals.h"
+#include "tdb.h"
+#include "util_tdb.h"
+#include "lib/util/server_id_db.h"
+#include "smbd/notifyd/notifyd.h"
+
+struct notify_list {
+	struct notify_list *next, *prev;
+	void (*callback)(void *private_data, struct timespec when,
+			 const struct notify_event *ctx);
+	void *private_data;
+	char path[1];
+};
+
+struct notify_context {
+	struct server_id notifyd;
+	struct messaging_context *msg_ctx;
+	struct notify_list *list;
+};
+
+static void notify_handler(struct messaging_context *msg, void *private_data,
+			   uint32_t msg_type, struct server_id src,
+			   DATA_BLOB *data);
+
+struct notify_context *notify_init(TALLOC_CTX *mem_ctx,
+				   struct messaging_context *msg,
+				   struct tevent_context *ev)
+{
+	struct server_id_db *names_db;
+	struct notify_context *ctx;
+	NTSTATUS status;
+
+	ctx = talloc(mem_ctx, struct notify_context);
+	if (ctx == NULL) {
+		return NULL;
+	}
+	ctx->msg_ctx = msg;
+	ctx->list = NULL;
+
+	names_db = messaging_names_db(msg);
+	if (!server_id_db_lookup_one(names_db, "notify-daemon",
+				     &ctx->notifyd)) {
+		DEBUG(1, ("No notify daemon around\n"));
+		TALLOC_FREE(ctx);
+		return NULL;
+	}
+
+	status = messaging_register(msg, ctx, MSG_PVFS_NOTIFY, notify_handler);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("messaging_register failed: %s\n",
+			  nt_errstr(status)));
+		TALLOC_FREE(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+static void notify_handler(struct messaging_context *msg, void *private_data,
+			   uint32_t msg_type, struct server_id src,
+			   DATA_BLOB *data)
+{
+	struct notify_context *ctx = talloc_get_type_abort(
+		private_data, struct notify_context);
+	struct notify_event_msg *event_msg;
+	struct notify_event event;
+	struct notify_list *listel;
+
+	if (data->length < offsetof(struct notify_event_msg, path) + 1) {
+		DEBUG(1, ("message too short: %u\n", (unsigned)data->length));
+		return;
+	}
+	if (data->data[data->length-1] != 0) {
+		DEBUG(1, ("%s: path not 0-terminated\n", __func__));
+		return;
+	}
+
+	event_msg = (struct notify_event_msg *)data->data;
+
+	event.action = event_msg->action;
+	event.path = event_msg->path;
+	event.private_data = event_msg->private_data;
+
+	DEBUG(10, ("%s: Got notify_event action=%u, private_data=%p, "
+		   "path=%s\n", __func__, (unsigned)event.action,
+		   event.private_data, event.path));
+
+	for (listel = ctx->list; listel != NULL; listel = listel->next) {
+		if (listel->private_data == event.private_data) {
+			listel->callback(listel->private_data, event_msg->when,
+					 &event);
+			break;
+		}
+	}
+}
+
+NTSTATUS notify_add(struct notify_context *ctx,
+		    const char *path, uint32_t filter, uint32_t subdir_filter,
+		    void (*callback)(void *, struct timespec,
+				     const struct notify_event *),
+		    void *private_data)
+{
+	struct notify_list *listel;
+	struct notify_rec_change_msg msg = {};
+	struct iovec iov[2];
+	size_t pathlen;
+	NTSTATUS status;
+
+	if (ctx == NULL) {
+		return NT_STATUS_NOT_IMPLEMENTED;
+	}
+
+	DEBUG(10, ("%s: path=[%s], filter=%u, subdir_filter=%u, "
+		   "private_data=%p\n", __func__, path, (unsigned)filter,
+		   (unsigned)subdir_filter, private_data));
+
+	pathlen = strlen(path)+1;
+
+	listel = (struct notify_list *)talloc_size(
+		ctx, offsetof(struct notify_list, path) + pathlen);
+	if (listel == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	listel->callback = callback;
+	listel->private_data = private_data;
+	memcpy(listel->path, path, pathlen);
+
+	clock_gettime_mono(&msg.instance.creation_time);
+	msg.instance.filter = filter;
+	msg.instance.subdir_filter = subdir_filter;
+	msg.instance.private_data = private_data;
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_rec_change_msg, path);
+	iov[1].iov_base = discard_const_p(char, path);
+	iov[1].iov_len = pathlen;
+
+	status =  messaging_send_iov(
+		ctx->msg_ctx, ctx->notifyd, MSG_SMB_NOTIFY_REC_CHANGE,
+		iov, ARRAY_SIZE(iov), NULL, 0);
+
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(listel);
+		DEBUG(10, ("messaging_send_iov returned %s\n",
+			   nt_errstr(status)));
+		return status;
+	}
+
+	DLIST_ADD(ctx->list, listel);
+	return NT_STATUS_OK;
+}
+
+NTSTATUS notify_remove(struct notify_context *ctx, void *private_data)
+{
+	struct notify_list *listel;
+	struct notify_rec_change_msg msg = {};
+	struct iovec iov[2];
+	NTSTATUS status;
+
+	for (listel = ctx->list; listel != NULL; listel = listel->next) {
+		if (listel->private_data == private_data) {
+			DLIST_REMOVE(ctx->list, listel);
+			break;
+		}
+	}
+	if (listel == NULL) {
+		DEBUG(10, ("%p not found\n", private_data));
+		return NT_STATUS_NOT_FOUND;
+	}
+
+	msg.instance.private_data = private_data;
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_rec_change_msg, path);
+	iov[1].iov_base = discard_const_p(char, listel->path);
+	iov[1].iov_len = strlen(listel->path)+1;
+
+	status = messaging_send_iov(
+		ctx->msg_ctx, ctx->notifyd, MSG_SMB_NOTIFY_REC_CHANGE,
+		iov, ARRAY_SIZE(iov), NULL, 0);
+
+	TALLOC_FREE(listel);
+	return status;
+}
+
+void notify_trigger(struct notify_context *ctx,
+		    uint32_t action, uint32_t filter,
+		    const char *dir, const char *name)
+{
+	struct notify_trigger_msg msg;
+	struct iovec iov[4];
+	char slash = '/';
+
+	DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
+		   "dir=%s, name=%s\n", (unsigned)action, (unsigned)filter,
+		   dir, name));
+
+	if (ctx == NULL) {
+		return;
+	}
+
+	msg.when = timespec_current();
+	msg.action = action;
+	msg.filter = filter;
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_trigger_msg, path);
+	iov[1].iov_base = discard_const_p(char, dir);
+	iov[1].iov_len = strlen(dir);
+	iov[2].iov_base = &slash;
+	iov[2].iov_len = 1;
+	iov[3].iov_base = discard_const_p(char, name);
+	iov[3].iov_len = strlen(name)+1;
+
+	messaging_send_iov(
+		ctx->msg_ctx, ctx->notifyd, MSG_SMB_NOTIFY_TRIGGER,
+		iov, ARRAY_SIZE(iov), NULL, 0);
+}
+
+void notify_walk_idx(struct notify_context *notify,
+		     void (*fn)(const char *path,
+				uint32_t *vnns, size_t num_vnns,
+				void *private_data),
+		     void *private_data)
+{
+	return;
+}
+
+void notify_walk(struct notify_context *notify,
+		 void (*fn)(const char *path,
+			    struct notify_db_entry *entries,
+			    size_t num_entries,
+			    time_t deleted_time, void *private_data),
+		 void *private_data)
+{
+	return;
+}
+
+void notify_cleanup(struct notify_context *notify)
+{
+	return;
+}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index e91c355..e4a434a 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -582,11 +582,6 @@ void notify_walk(struct notify_context *notify,
 		 void *private_data);
 void notify_cleanup(struct notify_context *notify);
 
-struct tevent_req *notify_cluster_proxy_send(
-	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-	struct notify_context *notify);
-int notify_cluster_proxy_recv(struct tevent_req *req);
-
 /* The following definitions come from smbd/ntquotas.c  */
 
 int vfs_get_ntquota(files_struct *fsp, enum SMB_QUOTA_TYPE qtype, struct dom_sid *psid, SMB_NTQUOTA_STRUCT *qt);
diff --git a/source3/wscript_build b/source3/wscript_build
index a9fe6e4..80cdb9f 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -608,7 +608,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    smbd/oplock_irix.c
                    smbd/oplock_linux.c
                    smbd/notify.c
-                   smbd/notify_internal.c
+                   smbd/notify_msg.c
                    smbd/build_options.c''' + NOTIFY_SOURCES,
                    deps='''
                    talloc
@@ -1198,7 +1198,7 @@ bld.SAMBA3_BINARY('smbta-util',
                  secrets3
                  param''')
 
-smbstatus_source = 'utils/status.c smbd/notify_internal.c'
+smbstatus_source = 'utils/status.c smbd/notify_msg.c'
 
 if bld.CONFIG_GET("WITH_PROFILE"):
     smbstatus_source += ' utils/status_profile.c'
-- 
1.7.9.5


From 77240ccc033a46fe75b9ea698cc9e0c38ce4068b Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 17:23:18 +0100
Subject: [PATCH 12/20] smbd: Kernel change notify is done by notifyd

smbd itself does not need to call VFS_NOTIFY_WATCH anymore

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify.c |   22 ----------------------
 1 file changed, 22 deletions(-)

diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
index b3079d2..e776749 100644
--- a/source3/smbd/notify.c
+++ b/source3/smbd/notify.c
@@ -236,15 +236,6 @@ static void notify_callback(void *private_data, struct timespec when,
 	notify_fsp(fsp, when, e->action, e->path);
 }
 
-static void sys_notify_callback(struct sys_notify_context *ctx,
-				void *private_data,
-				struct notify_event *e)
-{
-	files_struct *fsp = (files_struct *)private_data;
-	DEBUG(10, ("sys_notify_callback called for %s\n", fsp_str_dbg(fsp)));
-	notify_fsp(fsp, timespec_current(), e->action, e->path);
-}
-
 NTSTATUS change_notify_create(struct files_struct *fsp, uint32_t filter,
 			      bool recursive)
 {
@@ -284,19 +275,6 @@ NTSTATUS change_notify_create(struct files_struct *fsp, uint32_t filter,
 
 	subdir_filter = recursive ? filter : 0;
 
-	if (fsp->conn->sconn->sys_notify_ctx != NULL) {
-		void *sys_notify_handle = NULL;
-
-		status = SMB_VFS_NOTIFY_WATCH(
-			fsp->conn, fsp->conn->sconn->sys_notify_ctx,
-			fullpath, &filter, &subdir_filter,
-			sys_notify_callback, fsp, &sys_notify_handle);
-
-		if (NT_STATUS_IS_OK(status)) {
-			talloc_steal(fsp->notify, sys_notify_handle);
-		}
-	}
-
 	if ((filter != 0) || (subdir_filter != 0)) {
 		status = notify_add(fsp->conn->sconn->notify_ctx,
 				    fullpath, filter, subdir_filter,
-- 
1.7.9.5


From e76c67fc85842020abba0f306dd0d8755d7bd1b7 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 17:28:02 +0100
Subject: [PATCH 13/20] smbd: Remove the notify_fam module

This has been moved to main smbd

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 docs-xml/Samba3-HOWTO/manpages.xml     |    1 -
 docs-xml/manpages/vfs_notify_fam.8.xml |   70 -------
 docs-xml/wscript_build                 |    1 -
 source3/modules/vfs_notify_fam.c       |  316 --------------------------------
 source3/modules/wscript_build          |    7 -
 5 files changed, 395 deletions(-)
 delete mode 100644 docs-xml/manpages/vfs_notify_fam.8.xml
 delete mode 100644 source3/modules/vfs_notify_fam.c

diff --git a/docs-xml/Samba3-HOWTO/manpages.xml b/docs-xml/Samba3-HOWTO/manpages.xml
index 577ac8b..ad23f49 100644
--- a/docs-xml/Samba3-HOWTO/manpages.xml
+++ b/docs-xml/Samba3-HOWTO/manpages.xml
@@ -58,7 +58,6 @@
 	<xi:include href="../manpages/vfs_full_audit.8.xml"/>
 	<xi:include href="../manpages/vfs_gpfs.8.xml"/>
 	<xi:include href="../manpages/vfs_netatalk.8.xml"/>
-	<xi:include href="../manpages/vfs_notify_fam.8.xml"/>
 	<xi:include href="../manpages/vfs_prealloc.8.xml"/>
 	<xi:include href="../manpages/vfs_readahead.8.xml"/>
 	<xi:include href="../manpages/vfs_readonly.8.xml"/>
diff --git a/docs-xml/manpages/vfs_notify_fam.8.xml b/docs-xml/manpages/vfs_notify_fam.8.xml
deleted file mode 100644
index 954aa37..0000000
--- a/docs-xml/manpages/vfs_notify_fam.8.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
-<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
-<refentry id="vfs_notify_fam.8">
-
-<refmeta>
-	<refentrytitle>vfs_notify_fam</refentrytitle>
-	<manvolnum>8</manvolnum>
-	<refmiscinfo class="source">Samba</refmiscinfo>
-	<refmiscinfo class="manual">System Administration tools</refmiscinfo>
-	<refmiscinfo class="version">4.2</refmiscinfo>
-</refmeta>
-
-
-<refnamediv>
-	<refname>vfs_notify_fam</refname>
-	<refpurpose>FAM support for file change notifications</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-	<cmdsynopsis>
-		<command>vfs objects = notify_fam</command>
-	</cmdsynopsis>
-</refsynopsisdiv>
-
-<refsect1>
-	<title>DESCRIPTION</title>
-
-	<para>This VFS module is part of the
-	<citerefentry><refentrytitle>samba</refentrytitle>
-	<manvolnum>7</manvolnum></citerefentry> suite.</para>
-
-	<para>The <command>vfs_notify_fam</command> module makes use of
-	the system FAM (File Alteration Monitor) daemon to implement
-	file change notifications for Windows clients. FAM is generally
-	present only on IRIX and some BSD systems.</para>
-
-	<para>This module is not stackable.</para>
-
-</refsect1>
-
-<refsect1>
-	<title>EXAMPLES</title>
-
-	<para>Support FAM notifications globally:</para>
-
-<programlisting>
-        <smbconfsection name="[global]"/>
-	<smbconfoption name="vfs objects">notify_fam</smbconfoption>
-</programlisting>
-
-</refsect1>
-
-<refsect1>
-	<title>VERSION</title>
-
-	<para>This man page is correct for version 3.0.25 of the Samba suite.
-	</para>
-</refsect1>
-
-<refsect1>
-	<title>AUTHOR</title>
-
-	<para>The original Samba software and related utilities
-	were created by Andrew Tridgell. Samba is now developed
-	by the Samba Team as an Open Source project similar
-	to the way the Linux kernel is developed.</para>
-
-</refsect1>
-
-</refentry>
diff --git a/docs-xml/wscript_build b/docs-xml/wscript_build
index f54ae15..5c42a31 100644
--- a/docs-xml/wscript_build
+++ b/docs-xml/wscript_build
@@ -68,7 +68,6 @@ manpages='''
          manpages/vfs_linux_xfs_sgid.8
          manpages/vfs_media_harmony.8
          manpages/vfs_netatalk.8
-         manpages/vfs_notify_fam.8
          manpages/vfs_prealloc.8
          manpages/vfs_preopen.8
          manpages/vfs_readahead.8
diff --git a/source3/modules/vfs_notify_fam.c b/source3/modules/vfs_notify_fam.c
deleted file mode 100644
index 51ada11..0000000
--- a/source3/modules/vfs_notify_fam.c
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * FAM file notification support.
- *
- * Copyright (c) James Peach 2005
- * 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
- * the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include "includes.h"
-#include "smbd/smbd.h"
-#include "librpc/gen_ndr/notify.h"
-
-#include <fam.h>
-
-#if !defined(HAVE_FAM_H_FAMCODES_TYPEDEF)
-/* Gamin provides this typedef which means we can't use 'enum FAMCodes' as per
- * every other FAM implementation. Phooey.
- */
-typedef enum FAMCodes FAMCodes;
-#endif
-
-/* NOTE: There are multiple versions of FAM floating around the net, each with
- * slight differences from the original SGI FAM implementation. In this file,
- * we rely only on the SGI features and do not assume any extensions. For
- * example, we do not look at FAMErrno, because it is not set by the original
- * implementation.
- *
- * Random FAM links:
- *	http://oss.sgi.com/projects/fam/
- *	http://savannah.nongnu.org/projects/fam/
- *	http://sourceforge.net/projects/bsdfam/
- */
-
-/* ------------------------------------------------------------------------- */
-
-struct fam_watch_context {
-	struct fam_watch_context *prev, *next;
-	FAMConnection *fam_connection;
-	struct FAMRequest fr;
-	struct sys_notify_context *sys_ctx;
-	void (*callback)(struct sys_notify_context *ctx, 
-			 void *private_data,
-			 struct notify_event *ev);
-	void *private_data;
-	uint32_t mask; /* the inotify mask */
-	uint32_t filter; /* the windows completion filter */
-	const char *path;
-};
-
-
-/*
- * We want one FAM connection per smbd, not one per tcon.
- */
-static FAMConnection fam_connection;
-static bool fam_connection_initialized = False;
-
-static struct fam_watch_context *fam_notify_list;
-static void fam_handler(struct tevent_context *event_ctx,
-			struct tevent_fd *fd_event,
-			uint16_t flags,
-			void *private_data);
-
-static NTSTATUS fam_open_connection(FAMConnection *fam_conn,
-				    struct tevent_context *event_ctx)
-{
-	int res;
-	char *name;
-
-	ZERO_STRUCTP(fam_conn);
-	FAMCONNECTION_GETFD(fam_conn) = -1;
-
-
-#ifdef HAVE_FAMNOEXISTS
-	/* We should honor outside setting of the GAM_CLIENT_ID. */
-	setenv("GAM_CLIENT_ID","SAMBA",0);
-#endif
-
-	if (asprintf(&name, "smbd (%lu)", (unsigned long)getpid()) == -1) {
-		DEBUG(0, ("No memory\n"));
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	res = FAMOpen2(fam_conn, name);
-
-#ifdef HAVE_FAMNOEXISTS
-	/*
-	 * This reduces the chatter between GAMIN and samba making the pair
-	 * much more reliable.
-	 */
-	FAMNoExists(fam_conn);
-#endif
-
-	SAFE_FREE(name);
-
-	if (res < 0) {
-		DEBUG(10, ("FAM file change notifications not available\n"));
-		/*
-		 * No idea how to get NT_STATUS from a FAM result
-		 */
-		FAMCONNECTION_GETFD(fam_conn) = -1;
-		return NT_STATUS_UNEXPECTED_IO_ERROR;
-	}
-
-	if (tevent_add_fd(event_ctx, event_ctx,
-			 FAMCONNECTION_GETFD(fam_conn),
-			 TEVENT_FD_READ, fam_handler,
-			 (void *)fam_conn) == NULL) {
-		DEBUG(0, ("event_add_fd failed\n"));
-		FAMClose(fam_conn);
-		FAMCONNECTION_GETFD(fam_conn) = -1;
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	return NT_STATUS_OK;
-}
-
-static void fam_reopen(FAMConnection *fam_conn,
-		       struct tevent_context *event_ctx,
-		       struct fam_watch_context *notify_list)
-{
-	struct fam_watch_context *ctx;
-
-	DEBUG(5, ("Re-opening FAM connection\n"));
-
-	FAMClose(fam_conn);
-
-	if (!NT_STATUS_IS_OK(fam_open_connection(fam_conn, event_ctx))) {
-		DEBUG(5, ("Re-opening fam connection failed\n"));
-		return;
-	}
-
-	for (ctx = notify_list; ctx; ctx = ctx->next) {
-		FAMMonitorDirectory(fam_conn, ctx->path, &ctx->fr, NULL);
-	}
-}
-
-static void fam_handler(struct tevent_context *event_ctx,
-			struct tevent_fd *fd_event,
-			uint16_t flags,
-			void *private_data)
-{
-	FAMConnection *fam_conn = (FAMConnection *)private_data;
-	FAMEvent fam_event;
-	struct fam_watch_context *ctx;
-	struct notify_event ne;
-
-	if (FAMPending(fam_conn) == 0) {
-		DEBUG(10, ("fam_handler called but nothing pending\n"));
-		return;
-	}
-
-	if (FAMNextEvent(fam_conn, &fam_event) != 1) {
-		DEBUG(5, ("FAMNextEvent returned an error\n"));
-		TALLOC_FREE(fd_event);
-		fam_reopen(fam_conn, event_ctx, fam_notify_list);
-		return;
-	}
-
-	DEBUG(10, ("Got FAMCode %d for %s\n", fam_event.code,
-		   fam_event.filename));
-
-	switch (fam_event.code) {
-	case FAMChanged:
-		ne.action = NOTIFY_ACTION_MODIFIED;
-		break;
-	case FAMCreated:
-		ne.action = NOTIFY_ACTION_ADDED;
-		break;
-	case FAMDeleted:
-		ne.action = NOTIFY_ACTION_REMOVED;
-		break;
-	default:
-		DEBUG(10, ("Ignoring code FAMCode %d for file %s\n",
-			   (int)fam_event.code, fam_event.filename));
-		return;
-	}
-
-	for (ctx = fam_notify_list; ctx; ctx = ctx->next) {
-		if (memcmp(&fam_event.fr, &ctx->fr, sizeof(FAMRequest)) == 0) {
-			break;
-		}
-	}
-
-	if (ctx == NULL) {
-		DEBUG(5, ("Discarding event for file %s\n",
-			  fam_event.filename));
-		return;
-	}
-
-	if ((ne.path = strrchr_m(fam_event.filename, '\\')) == NULL) {
-		ne.path = fam_event.filename;
-	}
-
-	ctx->callback(ctx->sys_ctx, ctx->private_data, &ne);
-}
-
-static int fam_watch_context_destructor(struct fam_watch_context *ctx)
-{
-	if (FAMCONNECTION_GETFD(ctx->fam_connection) != -1) {
-		FAMCancelMonitor(&fam_connection, &ctx->fr);
-	}
-	DLIST_REMOVE(fam_notify_list, ctx);
-	return 0;
-}
-
-/*
-  add a watch. The watch is removed when the caller calls
-  talloc_free() on *handle
-*/
-static NTSTATUS fam_watch(vfs_handle_struct *vfs_handle,
-			  struct sys_notify_context *ctx,
-			  const char *path,
-			  uint32_t *filter,
-			  uint32_t *subdir_filter,
-			  void (*callback)(struct sys_notify_context *ctx, 
-					   void *private_data,
-					   struct notify_event *ev),
-			  void *private_data, 
-			  void *handle_p)
-{
-	const uint32_t fam_mask = (FILE_NOTIFY_CHANGE_FILE_NAME|
-				 FILE_NOTIFY_CHANGE_DIR_NAME);
-	struct fam_watch_context *watch;
-	void **handle = (void **)handle_p;
-
-	if ((*filter & fam_mask) == 0) {
-		DEBUG(10, ("filter = %u, ignoring in FAM\n", *filter));
-		return NT_STATUS_OK;
-	}
-
-	if (!fam_connection_initialized) {
-		if (!NT_STATUS_IS_OK(fam_open_connection(&fam_connection,
-							 ctx->ev))) {
-			/*
-			 * Just let smbd do all the work itself
-			 */
-			return NT_STATUS_OK;
-		}
-		fam_connection_initialized = True;
-	}
-
-	if (!(watch = talloc(ctx, struct fam_watch_context))) {
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	watch->fam_connection = &fam_connection;
-
-	watch->callback = callback;
-	watch->private_data = private_data;
-	watch->sys_ctx = ctx;
-
-	watch->path = talloc_strdup(watch, path);
-	if (watch->path == NULL) {
-		DEBUG(0, ("talloc_asprintf failed\n"));
-		TALLOC_FREE(watch);
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	/*
-	 * The FAM module in this early state will only take care of
-	 * FAMCreated and FAMDeleted events, Leave the rest to
-	 * notify_internal.c
-	 */
-
-	watch->filter = fam_mask;
-	*filter &= ~fam_mask;
-
-	DLIST_ADD(fam_notify_list, watch);
-	talloc_set_destructor(watch, fam_watch_context_destructor);
-
-	/*
-	 * Only directories monitored so far
-	 */
-
-	if (FAMCONNECTION_GETFD(watch->fam_connection) != -1) {
-		FAMMonitorDirectory(watch->fam_connection, watch->path,
-				    &watch->fr, NULL);
-	}
-	else {
-		/*
-		 * If the re-open is successful, this will establish the
-		 * FAMMonitor from the list
-		 */
-		fam_reopen(watch->fam_connection, ctx->ev, fam_notify_list);
-	}
-
-	*handle = (void *)watch;
-
-	return NT_STATUS_OK;
-}
-
-/* VFS operations structure */
-
-static struct vfs_fn_pointers notify_fam_fns = {
-	.notify_watch_fn = fam_watch,
-};
-
-
-NTSTATUS vfs_notify_fam_init(void);
-NTSTATUS vfs_notify_fam_init(void)
-{
-	return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "notify_fam",
-				&notify_fam_fns);
-}
diff --git a/source3/modules/wscript_build b/source3/modules/wscript_build
index 30f3bdd..d05e58b 100644
--- a/source3/modules/wscript_build
+++ b/source3/modules/wscript_build
@@ -284,13 +284,6 @@ bld.SAMBA3_MODULE('vfs_gpfs',
 vfs_notify_fam_deps='samba-util '
 if bld.CONFIG_SET('SAMBA_FAM_LIBS'):
    vfs_notify_fam_deps += bld.CONFIG_GET('SAMBA_FAM_LIBS')
-bld.SAMBA3_MODULE('vfs_notify_fam',
-                 subsystem='vfs',
-                 source='vfs_notify_fam.c',
-                 deps=vfs_notify_fam_deps,
-                 init_function='',
-                 internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_notify_fam'),
-                 enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_notify_fam'))
 
 bld.SAMBA3_MODULE('vfs_readahead',
                  subsystem='vfs',
-- 
1.7.9.5


From 769c9eba552f1e1fb10a4fb1348af9aebe9fb7f9 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 9 Jan 2015 12:24:58 +0000
Subject: [PATCH 14/20] notifyd: Add notifyd_parse_db()

The database format notifyd is "private" to it. This makes it
possible for smbcontrol and others to query notifyd's database with
MSG_SMB_NOTIFY_GET_DB and inspect it without having to know exactly what
format it uses.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notifyd/notifyd.c |   71 ++++++++++++++++++++++++++++++++++++++++
 source3/smbd/notifyd/notifyd.h |   13 ++++++++
 2 files changed, 84 insertions(+)

diff --git a/source3/smbd/notifyd/notifyd.c b/source3/smbd/notifyd/notifyd.c
index ea48035..b34b526 100644
--- a/source3/smbd/notifyd/notifyd.c
+++ b/source3/smbd/notifyd/notifyd.c
@@ -1416,3 +1416,74 @@ static void notifyd_snoop_broadcast(uint32_t src_vnn, uint32_t dst_vnn,
 		return;
 	}
 }
+
+struct notifyd_parse_db_state {
+	bool (*fn)(const char *path,
+		   struct server_id server,
+		   const struct notify_instance *instance,
+		   void *private_data);
+	void *private_data;
+};
+
+static bool notifyd_parse_db_parser(TDB_DATA key, TDB_DATA value,
+				    void *private_data)
+{
+	struct notifyd_parse_db_state *state = private_data;
+	char path[key.dsize+1];
+	struct notifyd_instance *instances = NULL;
+	size_t num_instances = 0;
+	size_t i;
+	bool ok;
+
+	memcpy(path, key.dptr, key.dsize);
+	path[key.dsize] = 0;
+
+	ok = notifyd_parse_entry(value.dptr, value.dsize, &instances,
+				 &num_instances);
+	if (!ok) {
+		DEBUG(10, ("%s: Could not parse entry for path %s\n",
+			   __func__, path));
+		return true;
+	}
+
+	for (i=0; i<num_instances; i++) {
+		ok = state->fn(path, instances[i].client,
+			       &instances[i].instance,
+			       state->private_data);
+		if (!ok) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+int notifyd_parse_db(const uint8_t *buf, size_t buflen,
+		     uint64_t *log_index,
+		     bool (*fn)(const char *path,
+				struct server_id server,
+				const struct notify_instance *instance,
+				void *private_data),
+		     void *private_data)
+{
+	struct notifyd_parse_db_state state = {
+		.fn = fn, .private_data = private_data
+	};
+	NTSTATUS status;
+
+	if (buflen < 8) {
+		return EINVAL;
+	}
+	*log_index = BVAL(buf, 0);
+
+	buf += 8;
+	buflen -= 8;
+
+	status = dbwrap_parse_marshall_buf(
+		buf, buflen, notifyd_parse_db_parser, &state);
+	if (!NT_STATUS_IS_OK(status)) {
+		return map_errno_from_nt_status(status);
+	}
+
+	return 0;
+}
diff --git a/source3/smbd/notifyd/notifyd.h b/source3/smbd/notifyd/notifyd.h
index dc0a4e8..672ffba 100644
--- a/source3/smbd/notifyd/notifyd.h
+++ b/source3/smbd/notifyd/notifyd.h
@@ -140,4 +140,17 @@ struct tevent_req *notifyd_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 				struct sys_notify_context *sys_notify_ctx);
 int notifyd_recv(struct tevent_req *req);
 
+/*
+ * Parse a database received via the MSG_SMB_NOTIFY_[GET_]DB messages to the
+ * notify daemon
+ */
+int notifyd_parse_db(const uint8_t *buf, size_t buflen,
+		     uint64_t *log_index,
+		     bool (*fn)(const char *path,
+				struct server_id server,
+				const struct notify_instance *instance,
+				void *private_data),
+		     void *private_data);
+
+
 #endif
-- 
1.7.9.5


From 86f3e3e3b84d15cdfa199422d26bf761d860ad46 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 9 Jan 2015 12:48:56 +0000
Subject: [PATCH 15/20] notify: Re-add notify_walk()

This used to be a tdb traverse wrapper. Now we get the notify db from
notifyd via messages.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify_msg.c |   72 ++++++++++++++++++++++++++++++++++++++++-----
 source3/smbd/proto.h      |   14 +++++----
 source3/utils/status.c    |   44 ++++++++++++++-------------
 3 files changed, 96 insertions(+), 34 deletions(-)

diff --git a/source3/smbd/notify_msg.c b/source3/smbd/notify_msg.c
index b31cb57..2fe4472 100644
--- a/source3/smbd/notify_msg.c
+++ b/source3/smbd/notify_msg.c
@@ -252,14 +252,72 @@ void notify_walk_idx(struct notify_context *notify,
 	return;
 }
 
-void notify_walk(struct notify_context *notify,
-		 void (*fn)(const char *path,
-			    struct notify_db_entry *entries,
-			    size_t num_entries,
-			    time_t deleted_time, void *private_data),
-		 void *private_data)
+NTSTATUS notify_walk(struct notify_context *notify,
+		     bool (*fn)(const char *path, struct server_id server,
+				const struct notify_instance *instance,
+				void *private_data),
+		     void *private_data)
 {
-	return;
+	struct tevent_context *ev;
+	struct tevent_req *req;
+	struct messaging_rec *rec;
+	uint64_t log_idx;
+	NTSTATUS status;
+	int ret;
+	bool ok;
+
+	ev = samba_tevent_context_init(notify);
+	if (ev == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	req = messaging_read_send(ev, ev, notify->msg_ctx, MSG_SMB_NOTIFY_DB);
+	if (req == NULL) {
+		TALLOC_FREE(ev);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	ok = tevent_req_set_endtime(req, ev, timeval_current_ofs(10, 0));
+	if (!ok) {
+		TALLOC_FREE(ev);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	status = messaging_send_buf(notify->msg_ctx, notify->notifyd,
+				    MSG_SMB_NOTIFY_GET_DB, NULL, 0);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: messaging_send_buf failed\n",
+			   nt_errstr(status)));
+		TALLOC_FREE(ev);
+		return status;
+	}
+
+	ok = tevent_req_poll(req, ev);
+	if (!ok) {
+		DEBUG(10, ("%s: tevent_req_poll failed\n", __func__));
+		TALLOC_FREE(ev);
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	ret = messaging_read_recv(req, ev, &rec);
+	if (ret != 0) {
+		DEBUG(10, ("%s: messaging_read_recv failed: %s\n",
+			   __func__, strerror(ret)));
+		TALLOC_FREE(ev);
+		return map_nt_error_from_unix(ret);
+	}
+
+	ret = notifyd_parse_db(rec->buf.data, rec->buf.length, &log_idx,
+			       fn, private_data);
+	if (ret != 0) {
+		DEBUG(10, ("%s: notifyd_parse_db failed: %s\n",
+			   __func__, strerror(ret)));
+		TALLOC_FREE(ev);
+		return map_nt_error_from_unix(ret);
+	}
+
+	TALLOC_FREE(ev);
+	return NT_STATUS_OK;
 }
 
 void notify_cleanup(struct notify_context *notify)
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index e4a434a..79be349 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -574,12 +574,14 @@ void notify_walk_idx(struct notify_context *notify,
 				uint32_t *vnns, size_t num_vnns,
 				void *private_data),
 		     void *private_data);
-void notify_walk(struct notify_context *notify,
-		 void (*fn)(const char *path,
-			    struct notify_db_entry *entries,
-			    size_t num_entries,
-			    time_t deleted_time, void *private_data),
-		 void *private_data);
+
+struct notify_instance;
+NTSTATUS notify_walk(struct notify_context *notify,
+		     bool (*fn)(const char *path, struct server_id server,
+				const struct notify_instance *instance,
+				void *private_data),
+		     void *private_data);
+
 void notify_cleanup(struct notify_context *notify);
 
 /* The following definitions come from smbd/ntquotas.c  */
diff --git a/source3/utils/status.c b/source3/utils/status.c
index afbba69..c11b4ee 100644
--- a/source3/utils/status.c
+++ b/source3/utils/status.c
@@ -45,6 +45,7 @@
 #include "lib/conn_tdb.h"
 #include "serverid.h"
 #include "status_profile.h"
+#include "smbd/notifyd/notifyd.h"
 
 #define SMB_MAXPIDS		2048
 static uid_t 		Ucrit_uid = 0;               /* added by OH */
@@ -326,27 +327,17 @@ static int traverse_sessionid(const char *key, struct sessionid *session,
 }
 
 
-static void print_notify_recs(const char *path,
-			      struct notify_db_entry *entries,
-			      size_t num_entries,
-			      time_t deleted_time, void *private_data)
+static bool print_notify_rec(const char *path, struct server_id server,
+			     const struct notify_instance *instance,
+			     void *private_data)
 {
-	size_t i;
-	d_printf("%s\n", path);
+	struct server_id_buf idbuf;
 
-	if (num_entries == 0) {
-		d_printf("deleted %s\n", time_to_asc(deleted_time));
-	}
-
-	for (i=0; i<num_entries; i++) {
-		struct notify_db_entry *e = &entries[i];
-		struct server_id_buf idbuf;
+	d_printf("%s\\%s\\%x\\%x\n", path, server_id_str_buf(server, &idbuf),
+		 (unsigned)instance->filter,
+		 (unsigned)instance->subdir_filter);
 
-		printf("%s %x %x\n", server_id_str_buf(e->server, &idbuf),
-		       (unsigned)e->filter,
-		       (unsigned)e->subdir_filter);
-	}
-	printf("\n");
+	return true;
 }
 
 int main(int argc, const char *argv[])
@@ -375,7 +366,7 @@ int main(int argc, const char *argv[])
 	};
 	TALLOC_CTX *frame = talloc_stackframe();
 	int ret = 0;
-	struct messaging_context *msg_ctx;
+	struct messaging_context *msg_ctx = NULL;
 	char *db_path;
 	bool ok;
 
@@ -586,11 +577,22 @@ int main(int argc, const char *argv[])
 	if (show_notify) {
 		struct notify_context *n;
 
-		n = notify_init(talloc_tos(), NULL, NULL);
+		if (msg_ctx == NULL) {
+			msg_ctx = messaging_init(
+				NULL, samba_tevent_context_init(NULL));
+			if (msg_ctx == NULL) {
+				fprintf(stderr, "messaging_init failed\n");
+				ret = -1;
+				goto done;
+			}
+		}
+
+		n = notify_init(talloc_tos(), msg_ctx,
+				messaging_tevent_context(msg_ctx));
 		if (n == NULL) {
 			goto done;
 		}
-		notify_walk(n, print_notify_recs, NULL);
+		notify_walk(n, print_notify_rec, NULL);
 		TALLOC_FREE(n);
 	}
 
-- 
1.7.9.5


From 3d94a482991827fc05690b4a1eaec1bac1fa2470 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 12 Dec 2014 15:37:30 +0100
Subject: [PATCH 16/20] smbd: Remove SMB_VFS_NOTIFY_WATCH

No longer needed

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 examples/VFS/skel_opaque.c       |   14 ------------
 examples/VFS/skel_transparent.c  |   16 -------------
 source3/include/vfs.h            |   19 ----------------
 source3/include/vfs_macros.h     |    5 -----
 source3/modules/vfs_ceph.c       |   18 ---------------
 source3/modules/vfs_default.c    |   46 --------------------------------------
 source3/modules/vfs_full_audit.c |   24 --------------------
 source3/modules/vfs_glusterfs.c  |   13 -----------
 source3/modules/vfs_time_audit.c |   29 ------------------------
 source3/smbd/vfs.c               |   16 -------------
 10 files changed, 200 deletions(-)

diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
index 296a855..7021998 100644
--- a/examples/VFS/skel_opaque.c
+++ b/examples/VFS/skel_opaque.c
@@ -490,19 +490,6 @@ static char *skel_realpath(vfs_handle_struct *handle, const char *path)
 	return NULL;
 }
 
-static NTSTATUS skel_notify_watch(struct vfs_handle_struct *handle,
-				  struct sys_notify_context *ctx,
-				  const char *path,
-				  uint32_t *filter,
-				  uint32_t *subdir_filter,
-				  void (*callback) (struct sys_notify_context *
-						    ctx, void *private_data,
-						    struct notify_event *ev),
-				  void *private_data, void *handle_p)
-{
-	return NT_STATUS_NOT_IMPLEMENTED;
-}
-
 static int skel_chflags(vfs_handle_struct *handle, const char *path,
 			uint flags)
 {
@@ -918,7 +905,6 @@ struct vfs_fn_pointers skel_opaque_fns = {
 	.link_fn = skel_link,
 	.mknod_fn = skel_mknod,
 	.realpath_fn = skel_realpath,
-	.notify_watch_fn = skel_notify_watch,
 	.chflags_fn = skel_chflags,
 	.file_id_create_fn = skel_file_id_create,
 	.copy_chunk_send_fn = skel_copy_chunk_send,
diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c
index 10f5ac5..6c6adea 100644
--- a/examples/VFS/skel_transparent.c
+++ b/examples/VFS/skel_transparent.c
@@ -579,21 +579,6 @@ static char *skel_realpath(vfs_handle_struct *handle, const char *path)
 	return SMB_VFS_NEXT_REALPATH(handle, path);
 }
 
-static NTSTATUS skel_notify_watch(struct vfs_handle_struct *handle,
-				  struct sys_notify_context *ctx,
-				  const char *path,
-				  uint32_t *filter,
-				  uint32_t *subdir_filter,
-				  void (*callback) (struct sys_notify_context *ctx,
-						    void *private_data,
-						    struct notify_event *ev),
-				  void *private_data, void *handle_p)
-{
-	return SMB_VFS_NEXT_NOTIFY_WATCH(handle, ctx, path,
-					 filter, subdir_filter, callback,
-					 private_data, handle_p);
-}
-
 static int skel_chflags(vfs_handle_struct *handle, const char *path,
 			uint flags)
 {
@@ -1029,7 +1014,6 @@ struct vfs_fn_pointers skel_transparent_fns = {
 	.link_fn = skel_link,
 	.mknod_fn = skel_mknod,
 	.realpath_fn = skel_realpath,
-	.notify_watch_fn = skel_notify_watch,
 	.chflags_fn = skel_chflags,
 	.file_id_create_fn = skel_file_id_create,
 	.copy_chunk_send_fn = skel_copy_chunk_send,
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index aa1a880..15e87ff 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -624,16 +624,6 @@ struct vfs_fn_pointers {
 	int (*link_fn)(struct vfs_handle_struct *handle, const char *oldpath, const char *newpath);
 	int (*mknod_fn)(struct vfs_handle_struct *handle, const char *path, mode_t mode, SMB_DEV_T dev);
 	char *(*realpath_fn)(struct vfs_handle_struct *handle, const char *path);
-	NTSTATUS (*notify_watch_fn)(struct vfs_handle_struct *handle,
-				    struct sys_notify_context *ctx,
-				    const char *path,
-				    uint32_t *filter,
-				    uint32_t *subdir_filter,
-				    void (*callback)(struct sys_notify_context *ctx,
-						     void *private_data,
-						     struct notify_event *ev),
-				    void *private_data, 
-				    void *handle_p);
 	int (*chflags_fn)(struct vfs_handle_struct *handle, const char *path, unsigned int flags);
 	struct file_id (*file_id_create_fn)(struct vfs_handle_struct *handle,
 					    const SMB_STRUCT_STAT *sbuf);
@@ -1091,15 +1081,6 @@ int smb_vfs_call_link(struct vfs_handle_struct *handle, const char *oldpath,
 int smb_vfs_call_mknod(struct vfs_handle_struct *handle, const char *path,
 		       mode_t mode, SMB_DEV_T dev);
 char *smb_vfs_call_realpath(struct vfs_handle_struct *handle, const char *path);
-NTSTATUS smb_vfs_call_notify_watch(struct vfs_handle_struct *handle,
-				   struct sys_notify_context *ctx,
-				   const char *name,
-				   uint32_t *filter,
-				   uint32_t *subdir_filter,
-				   void (*callback)(struct sys_notify_context *ctx,
-						    void *private_data,
-						    struct notify_event *ev),
-				   void *private_data, void *handle_p);
 int smb_vfs_call_chflags(struct vfs_handle_struct *handle, const char *path,
 			 unsigned int flags);
 struct file_id smb_vfs_call_file_id_create(struct vfs_handle_struct *handle,
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index 9a686c9..eaf0c19 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -336,11 +336,6 @@
 #define SMB_VFS_NEXT_REALPATH(handle, path) \
 	smb_vfs_call_realpath((handle)->next, (path))
 
-#define SMB_VFS_NOTIFY_WATCH(conn, ctx, path, filter, subdir_filter, callback, private_data, handle_p) \
-	smb_vfs_call_notify_watch((conn)->vfs_handles, (ctx), (path), (filter), (subdir_filter), (callback), (private_data), (handle_p))
-#define SMB_VFS_NEXT_NOTIFY_WATCH(conn, ctx, path, filter, subdir_filter, callback, private_data, handle_p) \
-	smb_vfs_call_notify_watch((conn)->next, (ctx), (path), (filter), (subdir_filter), (callback), (private_data), (handle_p))
-
 #define SMB_VFS_CHFLAGS(conn, path, flags) \
 	smb_vfs_call_chflags((conn)->vfs_handles, (path), (flags))
 #define SMB_VFS_NEXT_CHFLAGS(handle, path, flags) \
diff --git a/source3/modules/vfs_ceph.c b/source3/modules/vfs_ceph.c
index ff642fe..0113faa 100644
--- a/source3/modules/vfs_ceph.c
+++ b/source3/modules/vfs_ceph.c
@@ -1011,23 +1011,6 @@ static char *cephwrap_realpath(struct vfs_handle_struct *handle,  const char *pa
 	return result;
 }
 
-static NTSTATUS cephwrap_notify_watch(struct vfs_handle_struct *vfs_handle,
-				     struct sys_notify_context *ctx,
-				     const char *path,
-				     uint32_t *filter,
-				     uint32_t *subdir_filter,
-				     void (*callback)(struct sys_notify_context *ctx,
-						      void *private_data,
-						      struct notify_event *ev),
-				     void *private_data,
-				     void *handle_p)
-{
-	/*
-	 * We cannot call inotify on files the kernel does not know about
-	 */
-	return NT_STATUS_OK;
-}
-
 static int cephwrap_chflags(struct vfs_handle_struct *handle, const char *path,
 			   unsigned int flags)
 {
@@ -1303,7 +1286,6 @@ static struct vfs_fn_pointers ceph_fns = {
 	.link_fn = cephwrap_link,
 	.mknod_fn = cephwrap_mknod,
 	.realpath_fn = cephwrap_realpath,
-	.notify_watch_fn = cephwrap_notify_watch,
 	.chflags_fn = cephwrap_chflags,
 	.get_real_filename_fn = cephwrap_get_real_filename,
 	.connectpath_fn = cephwrap_connectpath,
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 8e4953e..6cd1dbc 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2126,51 +2126,6 @@ static char *vfswrap_realpath(vfs_handle_struct *handle, const char *path)
 	return result;
 }
 
-static NTSTATUS vfswrap_notify_watch(vfs_handle_struct *vfs_handle,
-				     struct sys_notify_context *ctx,
-				     const char *path,
-				     uint32_t *filter,
-				     uint32_t *subdir_filter,
-				     void (*callback)(struct sys_notify_context *ctx, 
-						      void *private_data,
-						      struct notify_event *ev),
-				     void *private_data, void *handle)
-{
-	/*
-	 * So far inotify is the only supported default notify mechanism. If
-	 * another platform like the the BSD's or a proprietary Unix comes
-	 * along and wants another default, we can play the same trick we
-	 * played with Posix ACLs.
-	 *
-	 * Until that is the case, hard-code inotify here.
-	 */
-#ifdef HAVE_INOTIFY
-	if (lp_kernel_change_notify()) {
-		int ret;
-		if (!lp_parm_bool(-1, "notify", "inotify", True)) {
-			return NT_STATUS_INVALID_SYSTEM_SERVICE;
-		}
-		/*
-		 * "ctx->private_data" is not obvious as a talloc context
-		 * here. Without modifying the VFS we don't have a mem_ctx
-		 * available here, and ctx->private_data was used by
-		 * inotify_watch before it got a real talloc parent.
-		 */
-		ret = inotify_watch(ctx->private_data, ctx,
-				    path, filter, subdir_filter,
-				    callback, private_data, handle);
-		if (ret != 0) {
-			return map_nt_error_from_unix(ret);
-		}
-		return NT_STATUS_OK;
-	}
-#endif
-	/*
-	 * Do nothing, leave everything to notify_internal.c
-	 */
-	return NT_STATUS_OK;
-}
-
 static int vfswrap_chflags(vfs_handle_struct *handle, const char *path,
 			   unsigned int flags)
 {
@@ -2637,7 +2592,6 @@ static struct vfs_fn_pointers vfs_default_fns = {
 	.link_fn = vfswrap_link,
 	.mknod_fn = vfswrap_mknod,
 	.realpath_fn = vfswrap_realpath,
-	.notify_watch_fn = vfswrap_notify_watch,
 	.chflags_fn = vfswrap_chflags,
 	.file_id_create_fn = vfswrap_file_id_create,
 	.streaminfo_fn = vfswrap_streaminfo,
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index c6648d1..6de22b1 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -158,7 +158,6 @@ typedef enum _vfs_op_type {
 	SMB_VFS_OP_LINK,
 	SMB_VFS_OP_MKNOD,
 	SMB_VFS_OP_REALPATH,
-	SMB_VFS_OP_NOTIFY_WATCH,
 	SMB_VFS_OP_CHFLAGS,
 	SMB_VFS_OP_FILE_ID_CREATE,
 	SMB_VFS_OP_STREAMINFO,
@@ -286,7 +285,6 @@ static struct {
 	{ SMB_VFS_OP_LINK,	"link" },
 	{ SMB_VFS_OP_MKNOD,	"mknod" },
 	{ SMB_VFS_OP_REALPATH,	"realpath" },
-	{ SMB_VFS_OP_NOTIFY_WATCH, "notify_watch" },
 	{ SMB_VFS_OP_CHFLAGS,	"chflags" },
 	{ SMB_VFS_OP_FILE_ID_CREATE,	"file_id_create" },
 	{ SMB_VFS_OP_STREAMINFO,	"streaminfo" },
@@ -1630,27 +1628,6 @@ static char *smb_full_audit_realpath(vfs_handle_struct *handle,
 	return result;
 }
 
-static NTSTATUS smb_full_audit_notify_watch(struct vfs_handle_struct *handle,
-			struct sys_notify_context *ctx,
-			const char *path,
-			uint32_t *filter,
-			uint32_t *subdir_filter,
-			void (*callback)(struct sys_notify_context *ctx,
-					void *private_data,
-					struct notify_event *ev),
-			void *private_data, void *handle_p)
-{
-	NTSTATUS result;
-
-	result = SMB_VFS_NEXT_NOTIFY_WATCH(handle, ctx, path,
-					   filter, subdir_filter, callback,
-					   private_data, handle_p);
-
-	do_log(SMB_VFS_OP_NOTIFY_WATCH, NT_STATUS_IS_OK(result), handle, "");
-
-	return result;
-}
-
 static int smb_full_audit_chflags(vfs_handle_struct *handle,
 			    const char *path, unsigned int flags)
 {
@@ -2305,7 +2282,6 @@ static struct vfs_fn_pointers vfs_full_audit_fns = {
 	.link_fn = smb_full_audit_link,
 	.mknod_fn = smb_full_audit_mknod,
 	.realpath_fn = smb_full_audit_realpath,
-	.notify_watch_fn = smb_full_audit_notify_watch,
 	.chflags_fn = smb_full_audit_chflags,
 	.file_id_create_fn = smb_full_audit_file_id_create,
 	.streaminfo_fn = smb_full_audit_streaminfo,
diff --git a/source3/modules/vfs_glusterfs.c b/source3/modules/vfs_glusterfs.c
index cd58080..a66887d 100644
--- a/source3/modules/vfs_glusterfs.c
+++ b/source3/modules/vfs_glusterfs.c
@@ -1039,18 +1039,6 @@ static int vfs_gluster_mknod(struct vfs_handle_struct *handle, const char *path,
 	return glfs_mknod(handle->data, path, mode, dev);
 }
 
-static NTSTATUS vfs_gluster_notify_watch(struct vfs_handle_struct *handle,
-					 struct sys_notify_context *ctx,
-					 const char *path, uint32_t *filter,
-					 uint32_t *subdir_filter,
-					 void (*callback) (struct sys_notify_context *ctx,
-							   void *private_data,
-							   struct notify_event *ev),
-					 void *private_data, void *handle_p)
-{
-	return NT_STATUS_NOT_IMPLEMENTED;
-}
-
 static int vfs_gluster_chflags(struct vfs_handle_struct *handle,
 			       const char *path, unsigned int flags)
 {
@@ -1760,7 +1748,6 @@ static struct vfs_fn_pointers glusterfs_fns = {
 	.link_fn = vfs_gluster_link,
 	.mknod_fn = vfs_gluster_mknod,
 	.realpath_fn = vfs_gluster_realpath,
-	.notify_watch_fn = vfs_gluster_notify_watch,
 	.chflags_fn = vfs_gluster_chflags,
 	.file_id_create_fn = NULL,
 	.copy_chunk_send_fn = NULL,
diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c
index 5c43d7c..7efad1e 100644
--- a/source3/modules/vfs_time_audit.c
+++ b/source3/modules/vfs_time_audit.c
@@ -1483,34 +1483,6 @@ static char *smb_time_audit_realpath(vfs_handle_struct *handle,
 	return result;
 }
 
-static NTSTATUS smb_time_audit_notify_watch(struct vfs_handle_struct *handle,
-			struct sys_notify_context *ctx,
-			const char *path,
-			uint32_t *filter,
-			uint32_t *subdir_filter,
-			void (*callback)(struct sys_notify_context *ctx,
-					void *private_data,
-					struct notify_event *ev),
-			void *private_data, void *handle_p)
-{
-	NTSTATUS result;
-	struct timespec ts1,ts2;
-	double timediff;
-
-	clock_gettime_mono(&ts1);
-	result = SMB_VFS_NEXT_NOTIFY_WATCH(handle, ctx, path,
-					   filter, subdir_filter, callback,
-					   private_data, handle_p);
-	clock_gettime_mono(&ts2);
-	timediff = nsec_time_diff(&ts2,&ts1)*1.0e-9;
-
-	if (timediff > audit_timeout) {
-		smb_time_audit_log_fname("notify_watch", timediff, path);
-	}
-
-	return result;
-}
-
 static int smb_time_audit_chflags(vfs_handle_struct *handle,
 				  const char *path, unsigned int flags)
 {
@@ -2507,7 +2479,6 @@ static struct vfs_fn_pointers vfs_time_audit_fns = {
 	.link_fn = smb_time_audit_link,
 	.mknod_fn = smb_time_audit_mknod,
 	.realpath_fn = smb_time_audit_realpath,
-	.notify_watch_fn = smb_time_audit_notify_watch,
 	.chflags_fn = smb_time_audit_chflags,
 	.file_id_create_fn = smb_time_audit_file_id_create,
 	.streaminfo_fn = smb_time_audit_streaminfo,
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 4ab7723..37e67f5 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -2053,22 +2053,6 @@ char *smb_vfs_call_realpath(struct vfs_handle_struct *handle, const char *path)
 	return handle->fns->realpath_fn(handle, path);
 }
 
-NTSTATUS smb_vfs_call_notify_watch(struct vfs_handle_struct *handle,
-				   struct sys_notify_context *ctx,
-				   const char *path,
-				   uint32_t *filter,
-				   uint32_t *subdir_filter,
-				   void (*callback)(struct sys_notify_context *ctx,
-						    void *private_data,
-						    struct notify_event *ev),
-				   void *private_data, void *handle_p)
-{
-	VFS_FIND(notify_watch);
-	return handle->fns->notify_watch_fn(handle, ctx, path,
-					    filter, subdir_filter, callback,
-					    private_data, handle_p);
-}
-
 int smb_vfs_call_chflags(struct vfs_handle_struct *handle, const char *path,
 			 unsigned int flags)
 {
-- 
1.7.9.5


From 0c5ef152a1f973cae5589f2e5073c17ecdb6ad40 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 9 Jan 2015 12:59:46 +0000
Subject: [PATCH 17/20] notify: Remove two now unused stubs

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify_msg.c |   14 --------------
 source3/smbd/proto.h      |    7 -------
 2 files changed, 21 deletions(-)

diff --git a/source3/smbd/notify_msg.c b/source3/smbd/notify_msg.c
index 2fe4472..8641983 100644
--- a/source3/smbd/notify_msg.c
+++ b/source3/smbd/notify_msg.c
@@ -243,15 +243,6 @@ void notify_trigger(struct notify_context *ctx,
 		iov, ARRAY_SIZE(iov), NULL, 0);
 }
 
-void notify_walk_idx(struct notify_context *notify,
-		     void (*fn)(const char *path,
-				uint32_t *vnns, size_t num_vnns,
-				void *private_data),
-		     void *private_data)
-{
-	return;
-}
-
 NTSTATUS notify_walk(struct notify_context *notify,
 		     bool (*fn)(const char *path, struct server_id server,
 				const struct notify_instance *instance,
@@ -319,8 +310,3 @@ NTSTATUS notify_walk(struct notify_context *notify,
 	TALLOC_FREE(ev);
 	return NT_STATUS_OK;
 }
-
-void notify_cleanup(struct notify_context *notify)
-{
-	return;
-}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 79be349..2eac3ec 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -569,11 +569,6 @@ NTSTATUS notify_remove(struct notify_context *notify, void *private_data);
 void notify_trigger(struct notify_context *notify,
 		    uint32_t action, uint32_t filter,
 		    const char *dir, const char *path);
-void notify_walk_idx(struct notify_context *notify,
-		     void (*fn)(const char *path,
-				uint32_t *vnns, size_t num_vnns,
-				void *private_data),
-		     void *private_data);
 
 struct notify_instance;
 NTSTATUS notify_walk(struct notify_context *notify,
@@ -582,8 +577,6 @@ NTSTATUS notify_walk(struct notify_context *notify,
 				void *private_data),
 		     void *private_data);
 
-void notify_cleanup(struct notify_context *notify);
-
 /* The following definitions come from smbd/ntquotas.c  */
 
 int vfs_get_ntquota(files_struct *fsp, enum SMB_QUOTA_TYPE qtype, struct dom_sid *psid, SMB_NTQUOTA_STRUCT *qt);
-- 
1.7.9.5


From e66d50d85b335a870925ac8a8c21c608774c2571 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 15 Jun 2015 12:14:03 +0000
Subject: [PATCH 18/20] utils: add net notify

A little tool to play with the notify daemon

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/utils/net.c        |    8 ++
 source3/utils/net_notify.c |  198 ++++++++++++++++++++++++++++++++++++++++++++
 source3/utils/net_proto.h  |    1 +
 source3/wscript_build      |    1 +
 4 files changed, 208 insertions(+)
 create mode 100644 source3/utils/net_notify.c

diff --git a/source3/utils/net.c b/source3/utils/net.c
index 4a8fad1..8182323 100644
--- a/source3/utils/net.c
+++ b/source3/utils/net.c
@@ -743,6 +743,14 @@ static struct functable net_func[] = {
 		   "'net serverid' commands.")
 	},
 
+	{	"notify",
+		net_notify,
+		NET_TRANSPORT_LOCAL,
+		N_("notifyd client code"),
+		N_("  Use 'net help notify' to get more information about "
+		   "'net notify' commands.")
+	},
+
 #ifdef WITH_FAKE_KASERVER
 	{	"afs",
 		net_afs,
diff --git a/source3/utils/net_notify.c b/source3/utils/net_notify.c
new file mode 100644
index 0000000..7138bcb
--- /dev/null
+++ b/source3/utils/net_notify.c
@@ -0,0 +1,198 @@
+/*
+ * Samba Unix/Linux notifyd client code
+ * Copyright (C) 2015 Volker Lendecke <vl at samba.org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "utils/net.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/server_id_db.h"
+#include "messages.h"
+#include "source3/smbd/notifyd/notifyd.h"
+
+static void net_notify_got_event(struct messaging_context *msg,
+				 void *private_data,
+				 uint32_t msg_type,
+				 struct server_id server_id,
+				 DATA_BLOB *data)
+{
+	struct notify_event_msg *event_msg;
+
+	if (data->length < offsetof(struct notify_event_msg, path) + 1) {
+		d_fprintf(stderr, "message too short\n");
+		return;
+	}
+	if (data->data[data->length-1] != 0) {
+		d_fprintf(stderr, "path not 0-terminated\n");
+		return;
+	}
+
+	event_msg = (struct notify_event_msg *)data->data;
+
+	d_printf("%u %s\n", (unsigned)event_msg->action,
+		 event_msg->path);
+}
+
+static int net_notify_listen(struct net_context *c, int argc,
+			     const char **argv)
+{
+	struct messaging_context *msg_ctx = c->msg_ctx;
+	struct tevent_context *ev = messaging_tevent_context(msg_ctx);
+	struct server_id_db *names_db = messaging_names_db(msg_ctx);
+	struct server_id notifyd;
+	struct server_id_buf idbuf;
+	struct notify_rec_change_msg msg;
+	struct iovec iov[2];
+	NTSTATUS status;
+	bool ok;
+
+	if (argc != 3) {
+		d_printf("Usage: net notify listen <path> <filter> "
+			 "<subdir-filter>\n");
+		return -1;
+	}
+
+	ok = server_id_db_lookup_one(names_db, "notify-daemon", &notifyd);
+	if (!ok) {
+		fprintf(stderr, "no notify daemon found\n");
+		return -1;
+	}
+
+	printf("notify daemon: %s\n", server_id_str_buf(notifyd, &idbuf));
+
+	msg = (struct notify_rec_change_msg) {
+		.instance.filter = atoi(argv[1]),
+		.instance.subdir_filter = atoi(argv[2])
+	};
+	iov[0] = (struct iovec) {
+		.iov_base = &msg,
+		.iov_len = offsetof(struct notify_rec_change_msg, path)
+	};
+	iov[1] = (struct iovec) {
+		.iov_base = discard_const_p(char, argv[0]),
+		.iov_len = strlen(argv[0])+1
+	};
+
+	status = messaging_register(c->msg_ctx, NULL, MSG_PVFS_NOTIFY,
+				    net_notify_got_event);
+	if (!NT_STATUS_IS_OK(status)) {
+		d_fprintf(stderr, "messaging_register failed: %s\n",
+			  nt_errstr(status));
+		return -1;
+	}
+
+	status = messaging_send_iov(
+		c->msg_ctx, notifyd, MSG_SMB_NOTIFY_REC_CHANGE,
+		iov, ARRAY_SIZE(iov), NULL, 0);
+	if (!NT_STATUS_IS_OK(status)) {
+		d_fprintf(stderr, "Sending rec_change to %s returned %s\n",
+			  server_id_str_buf(notifyd, &idbuf),
+			  nt_errstr(status));
+		return -1;
+	}
+
+	while (true) {
+		int ret;
+
+		ret = tevent_loop_once(ev);
+		if (ret != 0) {
+			d_fprintf(stderr, "tevent_loop_once failed: %s\n",
+				  strerror(errno));
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int net_notify_trigger(struct net_context *c, int argc,
+			      const char **argv)
+{
+	struct messaging_context *msg_ctx = c->msg_ctx;
+	struct server_id_db *names_db = messaging_names_db(msg_ctx);
+	struct server_id notifyd;
+	struct server_id_buf idbuf;
+	struct notify_trigger_msg msg;
+	struct iovec iov[2];
+	NTSTATUS status;
+	bool ok;
+
+	if (argc != 3) {
+		d_printf("Usage: net notify trigger <path> <action> "
+			 "<filter>\n");
+		return -1;
+	}
+
+	ok = server_id_db_lookup_one(names_db, "notify-daemon", &notifyd);
+	if (!ok) {
+		fprintf(stderr, "no notify daemon found\n");
+		return -1;
+	}
+
+	printf("notify daemon: %s\n", server_id_str_buf(notifyd, &idbuf));
+
+	msg = (struct notify_trigger_msg) {
+		.action = atoi(argv[1]), .filter = atoi(argv[2])
+	};
+
+	iov[0] = (struct iovec) {
+		.iov_base = &msg,
+		.iov_len = offsetof(struct notify_trigger_msg, path)
+	};
+	iov[1] = (struct iovec) {
+		.iov_base = discard_const_p(char, argv[0]),
+		.iov_len = strlen(argv[0])+1
+	};
+
+	status = messaging_send_iov(
+		c->msg_ctx, notifyd, MSG_SMB_NOTIFY_TRIGGER,
+		iov, ARRAY_SIZE(iov), NULL, 0);
+	if (!NT_STATUS_IS_OK(status)) {
+		d_printf("Sending rec_change to %s returned %s\n",
+			 server_id_str_buf(notifyd, &idbuf),
+			 nt_errstr(status));
+		return -1;
+	}
+
+	return 0;
+}
+
+int net_notify(struct net_context *c, int argc, const char **argv)
+{
+	struct functable func[] = {
+		{ "listen",
+		  net_notify_listen,
+		  NET_TRANSPORT_LOCAL,
+		  N_("Register for a path and listen for changes"),
+		  N_("net notify listen <path>")
+		},
+		{ "trigger",
+		  net_notify_trigger,
+		  NET_TRANSPORT_LOCAL,
+		  N_("Simulate a trigger action"),
+		  N_("net notify trigger <path> <action> <filter>")
+		},
+		{NULL, NULL, 0, NULL, NULL}
+	};
+
+	if (c->msg_ctx == NULL) {
+		d_fprintf(stderr, "No connection to messaging, need to run "
+			  "as root\n");
+		return -1;
+	}
+
+	return net_run_function(c, argc, argv, "net notify", func);
+}
diff --git a/source3/utils/net_proto.h b/source3/utils/net_proto.h
index 27cdb8d..093aa4b 100644
--- a/source3/utils/net_proto.h
+++ b/source3/utils/net_proto.h
@@ -461,4 +461,5 @@ int net_rpc_trust(struct net_context *c, int argc, const char **argv);
 /* The following definitions come from utils/net_rpc_conf.c */
 int net_rpc_conf(struct net_context *c, int argc, const char **argv);
 
+int net_notify(struct net_context *c, int argc, const char **argv);
 #endif /*  _NET_PROTO_H_  */
diff --git a/source3/wscript_build b/source3/wscript_build
index 80cdb9f..f58d0c5 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1123,6 +1123,7 @@ bld.SAMBA3_BINARY('net',
                  utils/net_rpc_trust.c
                  utils/net_rpc_conf.c
                  utils/net_afs.c
+                 utils/net_notify.c
                  registry/reg_parse.c
                  registry/reg_format.c
                  registry/reg_import.c
-- 
1.7.9.5


From fd94413e624efc42729602421e9c199184740c55 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 16 Jun 2015 14:57:14 +0000
Subject: [PATCH 19/20] notifyd: Add notiyfdd

A little standalone notify daemon to play around with.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notifyd/notifydd.c    |   83 ++++++++++++++++++++++++++++++++++++
 source3/smbd/notifyd/wscript_build |    7 +++
 2 files changed, 90 insertions(+)
 create mode 100644 source3/smbd/notifyd/notifydd.c

diff --git a/source3/smbd/notifyd/notifydd.c b/source3/smbd/notifyd/notifydd.c
new file mode 100644
index 0000000..6856b3e
--- /dev/null
+++ b/source3/smbd/notifyd/notifydd.c
@@ -0,0 +1,83 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "notifyd.h"
+#include <tevent.h>
+#include "lib/util/tevent_unix.h"
+
+int main(int argc, const char *argv[])
+{
+	TALLOC_CTX *frame;
+	struct tevent_context *ev;
+	struct messaging_context *msg;
+	struct tevent_req *req;
+	int err, ret;
+	bool ok;
+
+	talloc_enable_leak_report_full();
+
+	frame = talloc_stackframe();
+
+	setup_logging("notifyd", DEBUG_DEFAULT_STDOUT);
+	lp_set_cmdline("log level", "10");
+
+	lp_load_initial_only(get_dyn_CONFIGFILE());
+
+	ev = samba_tevent_context_init(frame);
+	if (ev == NULL) {
+		fprintf(stderr, "samba_tevent_context_init failed\n");
+		return 1;
+	}
+
+	msg = messaging_init(ev, ev);
+	if (msg == NULL) {
+		fprintf(stderr, "messaging_init failed\n");
+		return 1;
+	}
+
+	if (!lp_load_global(get_dyn_CONFIGFILE())) {
+		fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+			get_dyn_CONFIGFILE());
+		return 1;
+	}
+
+	req = notifyd_send(ev, ev, msg, messaging_ctdbd_connection(),
+			   NULL, NULL);
+	if (req == NULL) {
+		fprintf(stderr, "notifyd_send failed\n");
+		return 1;
+	}
+
+	ok = tevent_req_poll_unix(req, ev, &err);
+	if (!ok) {
+		fprintf(stderr, "tevent_req_poll_unix failed: %s\n",
+			strerror(err));
+		return 1;
+	}
+
+	ret = notifyd_recv(req);
+
+	printf("notifyd_recv returned %d (%s)\n", ret,
+	       ret ? strerror(ret) : "ok");
+
+	TALLOC_FREE(frame);
+
+	return 0;
+}
diff --git a/source3/smbd/notifyd/wscript_build b/source3/smbd/notifyd/wscript_build
index 90a9505..d9976bd 100644
--- a/source3/smbd/notifyd/wscript_build
+++ b/source3/smbd/notifyd/wscript_build
@@ -10,3 +10,10 @@ bld.SAMBA3_BINARY('notifyd-tests',
                   deps='''
                     param
                   ''')
+
+bld.SAMBA3_BINARY('notifydd',
+                  source='notifydd.c',
+                  install=False,
+                  deps='''notifyd
+                    param
+                  ''')
-- 
1.7.9.5


From 933c85ef7f8d7f78fdbcd9d2a669f8c9bf6465ab Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 3 May 2015 16:30:45 +0000
Subject: [PATCH 20/20] Remove ctdb_conn.[ch]

This was only used in notify_internal.c

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/ctdb_conn.c         |  547 ---------------------------------------
 source3/lib/ctdb_conn.h         |   65 -----
 source3/lib/ctdb_dummy.c        |   71 -----
 source3/torture/proto.h         |    1 -
 source3/torture/test_ctdbconn.c |  239 -----------------
 source3/torture/torture.c       |    1 -
 source3/wscript_build           |    2 -
 7 files changed, 926 deletions(-)
 delete mode 100644 source3/lib/ctdb_conn.c
 delete mode 100644 source3/lib/ctdb_conn.h
 delete mode 100644 source3/torture/test_ctdbconn.c

diff --git a/source3/lib/ctdb_conn.c b/source3/lib/ctdb_conn.c
deleted file mode 100644
index 4e1b3e5..0000000
--- a/source3/lib/ctdb_conn.c
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
-   Unix SMB/CIFS implementation.
-   Samba3 ctdb connection handling
-   Copyright (C) Volker Lendecke 2012
-
-   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 3 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, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "includes.h"
-#include "lib/util/tevent_unix.h"
-#include "ctdb_conn.h"
-
-#include <tdb.h>
-
-#include <ctdb_protocol.h>
-
-#include "lib/async_req/async_sock.h"
-
-struct ctdb_conn {
-	int fd;
-	struct tevent_queue *outqueue;
-};
-
-struct ctdb_conn_init_state {
-	struct sockaddr_un addr;
-	struct ctdb_conn *conn;
-};
-
-/*
- * use the callbacks of async_connect_send to make sure
- * we are connecting to CTDB as root
- */
-static void before_connect_cb(void *private_data) {
-	become_root();
-}
-
-static void after_connect_cb(void *private_data) {
-	unbecome_root();
-}
-
-static void ctdb_conn_init_done(struct tevent_req *subreq);
-static int ctdb_conn_destructor(struct ctdb_conn *conn);
-
-struct tevent_req *ctdb_conn_init_send(TALLOC_CTX *mem_ctx,
-				       struct tevent_context *ev,
-				       const char *sock)
-{
-	struct tevent_req *req, *subreq;
-	struct ctdb_conn_init_state *state;
-	size_t len;
-
-	req = tevent_req_create(mem_ctx, &state, struct ctdb_conn_init_state);
-	if (req == NULL) {
-		return NULL;
-	}
-
-	if (!lp_clustering()) {
-		tevent_req_error(req, ENOSYS);
-		return tevent_req_post(req, ev);
-	}
-
-	state->conn = talloc(state, struct ctdb_conn);
-	if (tevent_req_nomem(state->conn, req)) {
-		return tevent_req_post(req, ev);
-	}
-
-	state->conn->outqueue = tevent_queue_create(
-		state->conn, "ctdb outqueue");
-	if (tevent_req_nomem(state->conn->outqueue, req)) {
-		return tevent_req_post(req, ev);
-	}
-
-	state->conn->fd = socket(AF_UNIX, SOCK_STREAM, 0);
-	if (state->conn->fd == -1) {
-		tevent_req_error(req, errno);
-		return tevent_req_post(req, ev);
-	}
-	talloc_set_destructor(state->conn, ctdb_conn_destructor);
-
-	state->addr.sun_family = AF_UNIX;
-
-	len = strlcpy(state->addr.sun_path, sock,
-		      sizeof(state->addr.sun_path));
-	if (len >= sizeof(state->addr.sun_path)) {
-		tevent_req_error(req, ENAMETOOLONG);
-		return tevent_req_post(req, ev);
-	}
-
-	subreq = async_connect_send(state, ev, state->conn->fd,
-				    (struct sockaddr *)&state->addr,
-				    sizeof(state->addr), before_connect_cb,
-				    after_connect_cb, NULL);
-	if (tevent_req_nomem(subreq, req)) {
-		return tevent_req_post(req, ev);
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_init_done, req);
-	return req;
-}
-
-static int ctdb_conn_destructor(struct ctdb_conn *c)
-{
-	if (c->fd != -1) {
-		close(c->fd);
-		c->fd = -1;
-	}
-	return 0;
-}
-
-static void ctdb_conn_init_done(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	int ret, err;
-
-	ret = async_connect_recv(subreq, &err);
-	TALLOC_FREE(subreq);
-	if (ret == -1) {
-		tevent_req_error(req, err);
-		return;
-	}
-	tevent_req_done(req);
-}
-
-int ctdb_conn_init_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-			struct ctdb_conn **pconn)
-{
-	struct ctdb_conn_init_state *state = tevent_req_data(
-		req, struct ctdb_conn_init_state);
-	int err;
-
-	if (tevent_req_is_unix_error(req, &err)) {
-		return err;
-	}
-	*pconn = talloc_move(mem_ctx, &state->conn);
-
-	return 0;
-}
-
-struct ctdb_conn_control_state {
-	struct tevent_context *ev;
-	struct ctdb_conn *conn;
-	struct ctdb_req_control req;
-	struct iovec iov[2];
-	struct ctdb_reply_control *reply;
-};
-
-static void ctdb_conn_control_written(struct tevent_req *subreq);
-static void ctdb_conn_control_done(struct tevent_req *subreq);
-static ssize_t ctdb_packet_more(uint8_t *buf, size_t buflen, void *p);
-
-struct tevent_req *ctdb_conn_control_send(TALLOC_CTX *mem_ctx,
-					  struct tevent_context *ev,
-					  struct ctdb_conn *conn,
-					  uint32_t vnn, uint32_t opcode,
-					  uint64_t srvid, uint32_t flags,
-					  uint8_t *data, size_t datalen)
-{
-	struct tevent_req *req, *subreq;
-	struct ctdb_conn_control_state *state;
-	struct ctdb_req_header *hdr;
-
-	req = tevent_req_create(mem_ctx, &state,
-				struct ctdb_conn_control_state);
-	if (req == NULL) {
-		return NULL;
-	}
-	state->ev = ev;
-	state->conn = conn;
-
-	hdr = &state->req.hdr;
-	hdr->length = offsetof(struct ctdb_req_control, data) + datalen;
-	hdr->ctdb_magic    = CTDB_MAGIC;
-	hdr->ctdb_version  = CTDB_PROTOCOL;
-	hdr->operation     = CTDB_REQ_CONTROL;
-	hdr->reqid         = 1; /* FIXME */
-	hdr->destnode      = vnn;
-	state->req.opcode  = opcode;
-	state->req.srvid   = srvid;
-	state->req.datalen = datalen;
-	state->req.flags   = flags;
-
-	state->iov[0].iov_base = &state->req;
-	state->iov[0].iov_len = offsetof(struct ctdb_req_control, data);
-	state->iov[1].iov_base = data;
-	state->iov[1].iov_len = datalen;
-
-	subreq = writev_send(state, ev, conn->outqueue, conn->fd, false,
-			     state->iov, 2);
-	if (tevent_req_nomem(subreq, req)) {
-		return tevent_req_post(req, ev);
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_control_written, req);
-	return req;
-}
-
-static void ctdb_conn_control_written(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct ctdb_conn_control_state *state = tevent_req_data(
-		req, struct ctdb_conn_control_state);
-	ssize_t written;
-	int err;
-
-	written = writev_recv(subreq, &err);
-	TALLOC_FREE(subreq);
-	if (written == -1) {
-		tevent_req_error(req, err);
-		return;
-	}
-	subreq = read_packet_send(
-		state, state->ev, state->conn->fd, sizeof(uint32_t),
-		ctdb_packet_more, NULL);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_control_done, req);
-}
-
-static ssize_t ctdb_packet_more(uint8_t *buf, size_t buflen, void *p)
-{
-	uint32_t len;
-
-	if (buflen > sizeof(uint32_t)) {
-		/* Been here, done */
-		return 0;
-	}
-	memcpy(&len, buf, sizeof(len));
-
-	if (len < sizeof(uint32_t)) {
-		return -1;
-	}
-
-	return (len - sizeof(uint32_t));
-}
-
-static void ctdb_conn_control_done(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct ctdb_conn_control_state *state = tevent_req_data(
-		req, struct ctdb_conn_control_state);
-	ssize_t nread;
-	uint8_t *buf;
-	int err;
-
-	nread = read_packet_recv(subreq, state, &buf, &err);
-	TALLOC_FREE(subreq);
-	if (nread == -1) {
-		tevent_req_error(req, err);
-		return;
-	}
-	state->reply = (struct ctdb_reply_control *)buf;
-	tevent_req_done(req);
-}
-
-int ctdb_conn_control_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-			   struct ctdb_reply_control **preply)
-{
-	struct ctdb_conn_control_state *state = tevent_req_data(
-		req, struct ctdb_conn_control_state);
-	int err;
-
-	if (tevent_req_is_unix_error(req, &err)) {
-		return err;
-	}
-	if (preply != NULL) {
-		*preply = talloc_move(mem_ctx, &state->reply);
-	}
-	return 0;
-}
-
-struct ctdb_conn_msg_write_state {
-	struct ctdb_req_message ctdb_msg;
-	struct iovec iov[2];
-};
-
-static void ctdb_conn_msg_write_done(struct tevent_req *subreq);
-
-struct tevent_req *ctdb_conn_msg_write_send(TALLOC_CTX *mem_ctx,
-					    struct tevent_context *ev,
-					    struct ctdb_conn *conn,
-					    uint32_t vnn, uint64_t srvid,
-					    uint8_t *msg, size_t msg_len)
-{
-	struct tevent_req *req, *subreq;
-	struct ctdb_conn_msg_write_state *state;
-	struct ctdb_req_header *h;
-
-	req = tevent_req_create(mem_ctx, &state,
-				struct ctdb_conn_msg_write_state);
-	if (req == NULL) {
-		return NULL;
-	}
-
-	h = &state->ctdb_msg.hdr;
-
-	h->length = offsetof(struct ctdb_req_message, data) + msg_len;
-	h->ctdb_magic = CTDB_MAGIC;
-	h->ctdb_version = CTDB_PROTOCOL;
-	h->generation = 1;
-	h->operation  = CTDB_REQ_MESSAGE;
-	h->destnode   = vnn;
-	h->srcnode    = CTDB_CURRENT_NODE;
-	h->reqid      = 0;
-	state->ctdb_msg.srvid   = srvid;
-	state->ctdb_msg.datalen = msg_len;
-
-	state->iov[0].iov_base = &state->ctdb_msg;
-	state->iov[0].iov_len = offsetof(struct ctdb_req_message, data);
-	state->iov[1].iov_base = msg;
-	state->iov[1].iov_len = msg_len;
-
-	subreq = writev_send(state, ev, conn->outqueue, conn->fd, false,
-			     state->iov, 2);
-	if (tevent_req_nomem(subreq, req)) {
-		return tevent_req_post(req, ev);
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_msg_write_done, req);
-	return req;
-}
-
-static void ctdb_conn_msg_write_done(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	ssize_t written;
-	int err;
-
-	written = writev_recv(subreq, &err);
-	TALLOC_FREE(subreq);
-	if (written == -1) {
-		tevent_req_error(req, err);
-		return;
-	}
-	tevent_req_done(req);
-}
-
-int ctdb_conn_msg_write_recv(struct tevent_req *req)
-{
-	return tevent_req_simple_recv_unix(req);
-}
-
-struct ctdb_msg_channel {
-	struct ctdb_conn *conn;
-};
-
-struct ctdb_msg_channel_init_state {
-	struct tevent_context *ev;
-	struct ctdb_conn *conn;
-	uint64_t srvid;
-	struct ctdb_msg_channel *channel;
-};
-
-static void ctdb_msg_channel_init_connected(struct tevent_req *subreq);
-static void ctdb_msg_channel_init_registered_srvid(struct tevent_req *subreq);
-
-struct tevent_req *ctdb_msg_channel_init_send(
-	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-	const char *sock, uint64_t srvid)
-{
-	struct tevent_req *req, *subreq;
-	struct ctdb_msg_channel_init_state *state;
-
-	req = tevent_req_create(mem_ctx, &state,
-				struct ctdb_msg_channel_init_state);
-	if (req == NULL) {
-		return NULL;
-	}
-	state->ev = ev;
-	state->srvid = srvid;
-
-	subreq = ctdb_conn_init_send(state, ev, sock);
-	if (tevent_req_nomem(subreq, req)) {
-		return tevent_req_post(req, ev);
-	}
-	tevent_req_set_callback(subreq, ctdb_msg_channel_init_connected, req);
-	return req;
-}
-
-static void ctdb_msg_channel_init_connected(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct ctdb_msg_channel_init_state *state = tevent_req_data(
-		req, struct ctdb_msg_channel_init_state);
-	int ret;
-
-	ret = ctdb_conn_init_recv(subreq, state, &state->conn);
-	TALLOC_FREE(subreq);
-	if (tevent_req_error(req, ret)) {
-		return;
-	}
-	subreq = ctdb_conn_control_send(state, state->ev, state->conn,
-					CTDB_CURRENT_NODE,
-					CTDB_CONTROL_REGISTER_SRVID,
-					state->srvid, 0, NULL, 0);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(
-		subreq, ctdb_msg_channel_init_registered_srvid,	req);
-}
-
-static void ctdb_msg_channel_init_registered_srvid(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct ctdb_msg_channel_init_state *state = tevent_req_data(
-		req, struct ctdb_msg_channel_init_state);
-	struct ctdb_reply_control *reply;
-	int ret;
-
-	ret = ctdb_conn_control_recv(subreq, talloc_tos(), &reply);
-	TALLOC_FREE(subreq);
-	if (tevent_req_error(req, ret)) {
-		return;
-	}
-	if (reply->status != 0) {
-		tevent_req_error(req, EIO);
-		return;
-	}
-	state->channel = talloc(state, struct ctdb_msg_channel);
-	if (tevent_req_nomem(state->channel, req)) {
-		return;
-	}
-	state->channel->conn = talloc_move(state->channel, &state->conn);
-	tevent_req_done(req);
-}
-
-int ctdb_msg_channel_init_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-			       struct ctdb_msg_channel **pchannel)
-{
-	struct ctdb_msg_channel_init_state *state = tevent_req_data(
-		req, struct ctdb_msg_channel_init_state);
-	int err;
-
-	if (tevent_req_is_unix_error(req, &err)) {
-		return err;
-	}
-	*pchannel = talloc_move(mem_ctx, &state->channel);
-	return 0;
-}
-
-struct ctdb_msg_read_state {
-	size_t buflen;
-	uint8_t *buf;
-};
-
-static void ctdb_msg_channel_got_msg(struct tevent_req *subreq);
-
-struct tevent_req *ctdb_msg_read_send(TALLOC_CTX *mem_ctx,
-				      struct tevent_context *ev,
-				      struct ctdb_msg_channel *channel)
-{
-	struct tevent_req *req, *subreq;
-	struct ctdb_msg_read_state *state;
-
-	req = tevent_req_create(mem_ctx, &state,
-				struct ctdb_msg_read_state);
-	if (req == NULL) {
-		return NULL;
-	}
-	subreq = read_packet_send(state, ev, channel->conn->fd,
-		sizeof(uint32_t), ctdb_packet_more, NULL);
-	if (tevent_req_nomem(subreq, req)) {
-		return tevent_req_post(req, ev);
-	}
-	tevent_req_set_callback(subreq, ctdb_msg_channel_got_msg, req);
-	return req;
-}
-
-static void ctdb_msg_channel_got_msg(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct ctdb_msg_read_state *state = tevent_req_data(
-		req, struct ctdb_msg_read_state);
-	ssize_t nread;
-	uint8_t *buf;
-	int err;
-
-	nread = read_packet_recv(subreq, state, &buf, &err);
-	if (nread == -1) {
-		tevent_req_error(req, err);
-		return;
-	}
-	state->buflen = nread;
-	state->buf = buf;
-	tevent_req_done(req);
-}
-
-int ctdb_msg_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-		       uint8_t **pmsg, size_t *pmsg_len)
-{
-	struct ctdb_msg_read_state *state = tevent_req_data(
-		req, struct ctdb_msg_read_state);
-	struct ctdb_req_header *hdr;
-	struct ctdb_req_message *msg;
-	uint8_t *buf;
-	int err;
-
-	if (tevent_req_is_unix_error(req, &err)) {
-		return err;
-	}
-
-	hdr = (struct ctdb_req_header *)state->buf;
-	if (hdr->length != state->buflen) {
-		DEBUG(10, ("Got invalid header length\n"));
-		return EIO;
-	}
-	if (hdr->operation != CTDB_REQ_MESSAGE) {
-		DEBUG(10, ("Expected %d (CTDB_REQ_MESSAGE), got %d\n",
-			   CTDB_REQ_MESSAGE, (int)hdr->operation));
-		return EIO;
-	}
-	if (hdr->length < offsetof(struct ctdb_req_message, data)) {
-		DEBUG(10, ("Got short msg, len=%d\n", (int)hdr->length));
-		return EIO;
-	}
-
-	msg = (struct ctdb_req_message *)hdr;
-	if (msg->datalen >
-	    hdr->length - offsetof(struct ctdb_req_message, data)) {
-		DEBUG(10, ("Got invalid datalen %d\n", (int)msg->datalen));
-		return EIO;
-	}
-
-	buf = (uint8_t *)talloc_memdup(mem_ctx, msg->data, msg->datalen);
-	if (buf == NULL) {
-		return ENOMEM;
-	}
-	*pmsg = buf;
-	*pmsg_len = msg->datalen;
-	return 0;
-}
diff --git a/source3/lib/ctdb_conn.h b/source3/lib/ctdb_conn.h
deleted file mode 100644
index 21a69c7..0000000
--- a/source3/lib/ctdb_conn.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-   Unix SMB/CIFS implementation.
-   Samba3 ctdb connection handling
-   Copyright (C) Volker Lendecke 2012
-
-   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 3 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, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _CTDB_CONN_H
-#define _CTDB_CONN_H
-
-#include "tevent.h"
-#include "librpc/gen_ndr/messaging.h"
-
-struct ctdb_conn;
-struct ctdb_reply_control;
-
-struct tevent_req *ctdb_conn_control_send(TALLOC_CTX *mem_ctx,
-					  struct tevent_context *ev,
-					  struct ctdb_conn *conn,
-					  uint32_t vnn, uint32_t opcode,
-					  uint64_t srvid, uint32_t flags,
-					  uint8_t *data, size_t datalen);
-int ctdb_conn_control_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-			   struct ctdb_reply_control **preply);
-
-struct tevent_req *ctdb_conn_init_send(TALLOC_CTX *mem_ctx,
-				       struct tevent_context *ev,
-				       const char *sock);
-int ctdb_conn_init_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-			struct ctdb_conn **pconn);
-
-struct tevent_req *ctdb_conn_msg_write_send(TALLOC_CTX *mem_ctx,
-					    struct tevent_context *ev,
-					    struct ctdb_conn *conn,
-					    uint32_t vnn, uint64_t srvid,
-					    uint8_t *msg, size_t msg_len);
-int ctdb_conn_msg_write_recv(struct tevent_req *req);
-
-struct ctdb_msg_channel;
-
-struct tevent_req *ctdb_msg_channel_init_send(
-	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-	const char *sock, uint64_t srvid);
-int ctdb_msg_channel_init_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-			       struct ctdb_msg_channel **pchannel);
-
-struct tevent_req *ctdb_msg_read_send(TALLOC_CTX *mem_ctx,
-				      struct tevent_context *ev,
-				      struct ctdb_msg_channel *channel);
-int ctdb_msg_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-		       uint8_t **pmsg, size_t *pmsg_len);
-
-#endif /* _CTDB_CONN_H */
diff --git a/source3/lib/ctdb_dummy.c b/source3/lib/ctdb_dummy.c
index 2df6c28..abefa00 100644
--- a/source3/lib/ctdb_dummy.c
+++ b/source3/lib/ctdb_dummy.c
@@ -19,7 +19,6 @@
 
 #include "includes.h"
 #include "messages.h"
-#include "ctdb_conn.h"
 #include "ctdbd_conn.h"
 #include "lib/dbwrap/dbwrap.h"
 #include "lib/dbwrap/dbwrap_ctdb.h"
@@ -81,76 +80,6 @@ bool ctdb_processes_exist(struct ctdbd_connection *conn,
 	return false;
 }
 
-struct dummy_state {
-	uint8_t dummy;
-};
-
-static struct tevent_req *dummy_send(TALLOC_CTX *mem_ctx,
-				     struct tevent_context *ev)
-{
-	struct tevent_req *req;
-	struct dummy_state *state;
-	req = tevent_req_create(mem_ctx, &state, struct dummy_state);
-	if (req == NULL) {
-		return NULL;
-	}
-	tevent_req_done(req);
-	return tevent_req_post(req, ev);
-}
-
-struct tevent_req *ctdb_conn_init_send(TALLOC_CTX *mem_ctx,
-				       struct tevent_context *ev,
-				       const char *sock)
-{
-	return dummy_send(mem_ctx, ev);
-}
-
-int ctdb_conn_init_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-			struct ctdb_conn **pconn)
-{
-	return ENOSYS;
-}
-
-struct tevent_req *ctdb_conn_msg_write_send(TALLOC_CTX *mem_ctx,
-					    struct tevent_context *ev,
-					    struct ctdb_conn *conn,
-					    uint32_t vnn, uint64_t srvid,
-					    uint8_t *msg, size_t msg_len)
-{
-	return dummy_send(mem_ctx, ev);
-}
-
-int ctdb_conn_msg_write_recv(struct tevent_req *req)
-{
-	return ENOSYS;
-}
-
-struct tevent_req *ctdb_msg_channel_init_send(
-	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-	const char *sock, uint64_t srvid)
-{
-	return dummy_send(mem_ctx, ev);
-}
-
-int ctdb_msg_channel_init_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-			       struct ctdb_msg_channel **pchannel)
-{
-	return ENOSYS;
-}
-
-struct tevent_req *ctdb_msg_read_send(TALLOC_CTX *mem_ctx,
-				      struct tevent_context *ev,
-				      struct ctdb_msg_channel *channel)
-{
-	return dummy_send(mem_ctx, ev);
-}
-
-int ctdb_msg_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-		       uint8_t **pmsg, size_t *pmsg_len)
-{
-	return ENOSYS;
-}
-
 struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx,
 				const char *name,
 				int hash_size, int tdb_flags,
diff --git a/source3/torture/proto.h b/source3/torture/proto.h
index ab422d5..fc7c33f 100644
--- a/source3/torture/proto.h
+++ b/source3/torture/proto.h
@@ -105,7 +105,6 @@ bool run_cleanup1(int dummy);
 bool run_cleanup2(int dummy);
 bool run_cleanup3(int dummy);
 bool run_cleanup4(int dummy);
-bool run_ctdb_conn(int dummy);
 bool run_notify_bench2(int dummy);
 bool run_notify_bench3(int dummy);
 bool run_dbwrap_watch1(int dummy);
diff --git a/source3/torture/test_ctdbconn.c b/source3/torture/test_ctdbconn.c
deleted file mode 100644
index 1f80cff..0000000
--- a/source3/torture/test_ctdbconn.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
-   Unix SMB/CIFS implementation.
-   Test new ctdb API
-   Copyright (C) Volker Lendecke 2012
-
-   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 3 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, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "includes.h"
-#include "torture/proto.h"
-
-#include "ctdb_conn.h"
-#include "ctdbd_conn.h"
-#include "lib/util/tevent_unix.h"
-#include "tdb.h"
-
-#include "ctdb_protocol.h"
-
-#include "messages.h"
-
-struct ctdb_conn_test_state {
-	struct tevent_context *ev;
-	struct ctdb_conn *conn;
-	struct ctdb_msg_channel *channel;
-	int msgno;
-};
-
-static void ctdb_conn_test_got_conn(struct tevent_req *subreq);
-static void ctdb_conn_test_got_pnn(struct tevent_req *subreq);
-static void ctdb_conn_test_got_channel(struct tevent_req *subreq);
-static void ctdb_conn_test_got_msg(struct tevent_req *subreq);
-static void ctdb_conn_test_msg_sent(struct tevent_req *subreq);
-
-static struct tevent_req *ctdb_conn_test_send(TALLOC_CTX *mem_ctx,
-					      struct tevent_context *ev)
-{
-	struct tevent_req *req, *subreq;
-	struct ctdb_conn_test_state *state;
-
-	req = tevent_req_create(mem_ctx, &state, struct ctdb_conn_test_state);
-	if (req == NULL) {
-		return NULL;
-	}
-	state->ev = ev;
-
-	subreq = ctdb_conn_init_send(mem_ctx, ev, lp_ctdbd_socket());
-	if (tevent_req_nomem(subreq, req)) {
-		return tevent_req_post(req, ev);
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_test_got_conn, req);
-	return req;
-}
-
-static void ctdb_conn_test_got_conn(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct ctdb_conn_test_state *state = tevent_req_data(
-		req, struct ctdb_conn_test_state);
-	uint64_t ret;
-
-	ret = ctdb_conn_init_recv(subreq, state, &state->conn);
-	TALLOC_FREE(subreq);
-	if (tevent_req_error(req, ret)) {
-		return;
-	}
-	subreq = ctdb_conn_control_send(state, state->ev, state->conn,
-					CTDB_CURRENT_NODE,
-					CTDB_CONTROL_GET_PNN, 0, 0, NULL, 0);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_test_got_pnn, req);
-}
-
-static void ctdb_conn_test_got_pnn(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct ctdb_conn_test_state *state = tevent_req_data(
-		req, struct ctdb_conn_test_state);
-	int ret;
-	struct ctdb_reply_control *reply;
-
-	ret = ctdb_conn_control_recv(subreq, talloc_tos(), &reply);
-	TALLOC_FREE(subreq);
-	if (tevent_req_error(req, ret)) {
-		return;
-	}
-	printf("vnn=%d\n", (int)reply->status);
-
-	subreq = ctdb_msg_channel_init_send(
-		state, state->ev, lp_ctdbd_socket(), 999999);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_test_got_channel, req);
-}
-
-static void ctdb_conn_test_got_channel(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct ctdb_conn_test_state *state = tevent_req_data(
-		req, struct ctdb_conn_test_state);
-	int ret;
-
-	ret = ctdb_msg_channel_init_recv(subreq, state, &state->channel);
-	TALLOC_FREE(subreq);
-	if (tevent_req_error(req, ret)) {
-		return;
-	}
-
-	subreq = ctdb_msg_read_send(state, state->ev, state->channel);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_test_got_msg, req);
-
-	state->msgno += 1;
-
-	subreq = ctdb_conn_msg_write_send(
-		state, state->ev, state->conn, CTDB_CURRENT_NODE, 999999,
-		(uint8_t *)&state->msgno, sizeof(state->msgno));
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_test_msg_sent, req);
-}
-
-static void ctdb_conn_test_got_msg(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct ctdb_conn_test_state *state = tevent_req_data(
-		req, struct ctdb_conn_test_state);
-	uint8_t *buf;
-	size_t buf_len;
-	int ret;
-
-	ret = ctdb_msg_read_recv(subreq, talloc_tos(), &buf, &buf_len);
-	TALLOC_FREE(subreq);
-	if (tevent_req_error(req, ret)) {
-		return;
-	}
-	if (buf_len != sizeof(int)) {
-		printf("got invalid msg\n");
-		tevent_req_error(req, EINVAL);
-		return;
-	}
-	memcpy(&ret, buf, buf_len);
-	printf("got msg %d\n", ret);
-	if (ret == 5) {
-		tevent_req_done(req);
-		return;
-	}
-
-	subreq = ctdb_msg_read_send(state, state->ev, state->channel);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_test_got_msg, req);
-}
-
-static void ctdb_conn_test_msg_sent(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct ctdb_conn_test_state *state = tevent_req_data(
-		req, struct ctdb_conn_test_state);
-	int ret;
-
-	ret = ctdb_conn_msg_write_recv(subreq);
-	TALLOC_FREE(subreq);
-	if (tevent_req_error(req, ret)) {
-		return;
-	}
-	state->msgno += 1;
-
-	if (state->msgno >= 10) {
-		return;
-	}
-
-	subreq = ctdb_conn_msg_write_send(
-		state, state->ev, state->conn, CTDB_CURRENT_NODE, 999999,
-		(uint8_t *)&state->msgno, sizeof(state->msgno));
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, ctdb_conn_test_msg_sent, req);
-}
-
-static int ctdb_conn_test_recv(struct tevent_req *req)
-{
-	int err;
-	if (tevent_req_is_unix_error(req, &err)) {
-		return err;
-	}
-	return 0;
-}
-
-bool run_ctdb_conn(int dummy)
-{
-	struct tevent_context *ev;
-	struct tevent_req *req;
-	int ret;
-
-	ev = samba_tevent_context_init(talloc_tos());
-	if (ev == NULL) {
-		fprintf(stderr, "tevent_context_init failed\n");
-		return false;
-	}
-	req = ctdb_conn_test_send(ev, ev);
-	if (req == NULL) {
-		fprintf(stderr, "ctdb_conn_test_send failed\n");
-		return false;
-	}
-	if (!tevent_req_poll(req, ev)) {
-		fprintf(stderr, "tevent_req_poll failed\n");
-		return false;
-	}
-	ret = ctdb_conn_test_recv(req);
-	TALLOC_FREE(req);
-	printf("ctdb_conn_test returned %s\n",
-	       ret ? strerror(ret) : "success");
-	TALLOC_FREE(ev);
-	return (ret == 0);
-}
diff --git a/source3/torture/torture.c b/source3/torture/torture.c
index d5ec4c6..bfd04f4 100644
--- a/source3/torture/torture.c
+++ b/source3/torture/torture.c
@@ -9597,7 +9597,6 @@ static struct {
 	{ "LOCAL-SUBSTITUTE", run_local_substitute, 0},
 	{ "LOCAL-GENCACHE", run_local_gencache, 0},
 	{ "LOCAL-TALLOC-DICT", run_local_talloc_dict, 0},
-	{ "LOCAL-CTDB-CONN", run_ctdb_conn, 0},
 	{ "LOCAL-DBWRAP-WATCH1", run_dbwrap_watch1, 0 },
 	{ "LOCAL-MESSAGING-READ1", run_messaging_read1, 0 },
 	{ "LOCAL-MESSAGING-READ2", run_messaging_read2, 0 },
diff --git a/source3/wscript_build b/source3/wscript_build
index f58d0c5..df894d5 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -272,8 +272,6 @@ if bld.CONFIG_GET("CTDB_CFLAGS") and bld.CONFIG_GET("CTDB_INCLUDE"):
                      lib/dbwrap/dbwrap_ctdb.c
                      lib/messages_ctdbd.c
                      lib/ctdbd_conn.c
-                     lib/ctdb_conn.c
-                     torture/test_ctdbconn.c
                    '''
     SAMBA_CLUSTER_SUPPORT_DEPS='''
                      talloc
-- 
1.7.9.5



More information about the samba-technical mailing list