This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ 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,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
#include "librpc/gen_ndr/ndr_notify.h"
struct notify_context {
- struct tdb_wrap *w;
+ struct db_context *db_recursive;
+ struct db_context *db_onelevel;
struct server_id server;
struct messaging_context *messaging_ctx;
struct notify_list *list;
struct notify_array *array;
int seqnum;
struct sys_notify_context *sys_notify_ctx;
+ TDB_DATA key;
};
#define NOTIFY_ENABLE "notify:enable"
#define NOTIFY_ENABLE_DEFAULT True
-static NTSTATUS notify_remove_all(struct notify_context *notify);
+static NTSTATUS notify_remove_all(struct notify_context *notify,
+ const struct server_id *server);
static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
static int notify_destructor(struct notify_context *notify)
{
messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
- notify_remove_all(notify);
+
+ if (notify->list != NULL) {
+ notify_remove_all(notify, ¬ify->server);
+ }
+
return 0;
}
struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
struct messaging_context *messaging_ctx,
struct event_context *ev,
- struct share_params *scfg)
+ connection_struct *conn)
{
struct notify_context *notify;
- if (!lp_parm_bool(scfg->service, "notify", "enable", True)) {
+ if (!lp_change_notify(conn->params)) {
return NULL;
}
return NULL;
}
- notify->w = tdb_wrap_open(notify, lock_path("notify.tdb"),
- 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
- O_RDWR|O_CREAT, 0644);
- if (notify->w == NULL) {
+ notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
+ 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
+ O_RDWR|O_CREAT, 0644);
+ if (notify->db_recursive == NULL) {
+ talloc_free(notify);
+ return NULL;
+ }
+
+ notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
+ 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR|O_CREAT, 0644);
+ if (notify->db_onelevel == NULL) {
talloc_free(notify);
return NULL;
}
notify->messaging_ctx = messaging_ctx;
notify->list = NULL;
notify->array = NULL;
- notify->seqnum = tdb_get_seqnum(notify->w->tdb);
+ notify->seqnum = notify->db_recursive->get_seqnum(
+ notify->db_recursive);
+ notify->key = string_term_tdb_data(NOTIFY_KEY);
talloc_set_destructor(notify, notify_destructor);
messaging_register(notify->messaging_ctx, notify,
MSG_PVFS_NOTIFY, notify_handler);
- notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
+ notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
return notify;
}
-
-/*
- lock the notify db
-*/
-static NTSTATUS notify_lock(struct notify_context *notify)
+bool notify_internal_parent_init(void)
{
- if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ struct tdb_wrap *db1, *db2;
+
+ if (lp_clustering()) {
+ return true;
}
- return NT_STATUS_OK;
+
+ /*
+ * Open the tdbs in the parent process (smbd) so that our
+ * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
+ * work.
+ */
+
+ db1 = tdb_wrap_open(talloc_autofree_context(), lock_path("notify.tdb"),
+ 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
+ O_RDWR|O_CREAT, 0644);
+ if (db1 == NULL) {
+ DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno)));
+ return false;
+ }
+ db2 = tdb_wrap_open(talloc_autofree_context(),
+ lock_path("notify_onelevel.tdb"),
+ 0, TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0644);
+ if (db2 == NULL) {
+ DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
+ strerror(errno)));
+ TALLOC_FREE(db1);
+ return false;
+ }
+ return true;
}
/*
- unlock the notify db
+ lock and fetch the record
*/
-static void notify_unlock(struct notify_context *notify)
+static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
{
- tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
+ *rec = notify->db_recursive->fetch_locked(notify->db_recursive,
+ notify, notify->key);
+ if (*rec == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ return NT_STATUS_OK;
}
/*
load the notify array
*/
-static NTSTATUS notify_load(struct notify_context *notify)
+static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
{
TDB_DATA dbuf;
DATA_BLOB blob;
NTSTATUS status;
int seqnum;
- seqnum = tdb_get_seqnum(notify->w->tdb);
+ seqnum = notify->db_recursive->get_seqnum(notify->db_recursive);
if (seqnum == notify->seqnum && notify->array != NULL) {
return NT_STATUS_OK;
notify->seqnum = seqnum;
talloc_free(notify->array);
- notify->array = talloc_zero(notify, struct notify_array);
+ notify->array = TALLOC_ZERO_P(notify, struct notify_array);
NT_STATUS_HAVE_NO_MEMORY(notify->array);
- dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
- if (dbuf.dptr == NULL) {
- return NT_STATUS_OK;
+ if (!rec) {
+ if (notify->db_recursive->fetch(notify->db_recursive, notify,
+ notify->key, &dbuf) != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ } else {
+ dbuf = rec->value;
}
blob.data = (uint8 *)dbuf.dptr;
blob.length = dbuf.dsize;
- status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
- (ndr_pull_flags_fn_t)ndr_pull_notify_array);
- free(dbuf.dptr);
+ status = NT_STATUS_OK;
+ if (blob.length > 0) {
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_pull_struct_blob(&blob, notify->array, NULL, notify->array,
+ (ndr_pull_flags_fn_t)ndr_pull_notify_array);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ /* 1. log that we got a corrupt notify_array
+ * 2. clear the variable the garbage was stored into to not trip
+ * over it next time this method is entered with the same seqnum
+ * 3. delete it from the database */
+ DEBUG(2, ("notify_array is corrupt, discarding it\n"));
+
+ ZERO_STRUCTP(notify->array);
+ if (rec != NULL) {
+ rec->delete_rec(rec);
+ }
+
+ } else {
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("notify_load:\n"));
+ NDR_PRINT_DEBUG(notify_array, notify->array);
+ }
+ }
+ }
+
+
+ if (!rec) {
+ talloc_free(dbuf.dptr);
+ }
return status;
}
/*
save the notify array
*/
-static NTSTATUS notify_save(struct notify_context *notify)
+static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
{
TDB_DATA dbuf;
DATA_BLOB blob;
NTSTATUS status;
- int ret;
+ enum ndr_err_code ndr_err;
TALLOC_CTX *tmp_ctx;
/* if possible, remove some depth arrays */
/* we might just be able to delete the record */
if (notify->array->num_depths == 0) {
- ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
- if (ret != 0) {
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
- }
- return NT_STATUS_OK;
+ return rec->delete_rec(rec);
}
tmp_ctx = talloc_new(notify);
NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
- status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, NULL, notify->array,
(ndr_push_flags_fn_t)ndr_push_notify_array);
- if (!NT_STATUS_IS_OK(status)) {
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
talloc_free(tmp_ctx);
- return status;
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("notify_save:\n"));
+ NDR_PRINT_DEBUG(notify_array, notify->array);
}
- dbuf.dptr = (char *)blob.data;
+ dbuf.dptr = blob.data;
dbuf.dsize = blob.length;
-
- ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
+
+ status = rec->store(rec, dbuf, TDB_REPLACE);
talloc_free(tmp_ctx);
- if (ret != 0) {
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
- }
- return NT_STATUS_OK;
+ return status;
}
uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
{
struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
- NTSTATUS status;
+ enum ndr_err_code ndr_err;
struct notify_event ev;
TALLOC_CTX *tmp_ctx = talloc_new(notify);
struct notify_list *listel;
return;
}
- status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
- (ndr_pull_flags_fn_t)ndr_pull_notify_event);
- if (!NT_STATUS_IS_OK(status)) {
+ ndr_err = ndr_pull_struct_blob(data, tmp_ctx, NULL, &ev,
+ (ndr_pull_flags_fn_t)ndr_pull_notify_event);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
talloc_free(tmp_ctx);
return;
}
/*
add an entry to the notify array
*/
-static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
+static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
+ struct notify_entry *e,
void *private_data, int depth)
{
int i;
d->max_mask_subdir |= d->entries[i].subdir_filter;
}
- return notify_save(notify);
+ return notify_save(notify, rec);
+}
+
+/*
+ Add a non-recursive watch
+*/
+
+static void notify_add_onelevel(struct notify_context *notify,
+ struct notify_entry *e, void *private_data)
+{
+ struct notify_entry_array *array;
+ struct db_record *rec;
+ DATA_BLOB blob;
+ TDB_DATA dbuf;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+
+ array = talloc_zero(talloc_tos(), struct notify_entry_array);
+ if (array == NULL) {
+ return;
+ }
+
+ rec = notify->db_onelevel->fetch_locked(
+ notify->db_onelevel, talloc_tos(),
+ make_tdb_data((uint8_t *)&e->dir_id, sizeof(e->dir_id)));
+ if (rec == NULL) {
+ DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
+ "\n", file_id_string_tos(&e->dir_id)));
+ TALLOC_FREE(array);
+ return;
+ }
+
+ blob.data = (uint8_t *)rec->value.dptr;
+ blob.length = rec->value.dsize;
+
+ if (blob.length > 0) {
+ ndr_err = ndr_pull_struct_blob(
+ &blob, array, NULL, array,
+ (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
+ ndr_errstr(ndr_err)));
+ TALLOC_FREE(array);
+ return;
+ }
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("notify_add_onelevel:\n"));
+ NDR_PRINT_DEBUG(notify_entry_array, array);
+ }
+ }
+
+ array->entries = talloc_realloc(array, array->entries,
+ struct notify_entry,
+ array->num_entries+1);
+ if (array->entries == NULL) {
+ TALLOC_FREE(array);
+ return;
+ }
+ array->entries[array->num_entries] = *e;
+ array->entries[array->num_entries].private_data = private_data;
+ array->entries[array->num_entries].server = notify->server;
+ array->num_entries += 1;
+
+ ndr_err = ndr_push_struct_blob(
+ &blob, rec, NULL, array,
+ (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
+ ndr_errstr(ndr_err)));
+ TALLOC_FREE(array);
+ return;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("notify_add_onelevel:\n"));
+ NDR_PRINT_DEBUG(notify_entry_array, array);
+ }
+
+ dbuf.dptr = blob.data;
+ dbuf.dsize = blob.length;
+
+ status = rec->store(rec, dbuf, TDB_REPLACE);
+ TALLOC_FREE(array);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
+ nt_errstr(status)));
+ return;
+ }
+ e->filter = 0;
+ return;
}
+
/*
add a notify watch. This is called when a notify is first setup on a open
directory handle.
struct notify_list *listel;
size_t len;
int depth;
+ struct db_record *rec;
/* see if change notify is enabled at all */
if (notify == NULL) {
return NT_STATUS_NOT_IMPLEMENTED;
}
- status = notify_lock(notify);
+ status = notify_fetch_locked(notify, &rec);
NT_STATUS_NOT_OK_RETURN(status);
- status = notify_load(notify);
+ status = notify_load(notify, rec);
if (!NT_STATUS_IS_OK(status)) {
- goto done;
+ talloc_free(rec);
+ return status;
}
/* cope with /. on the end of the path */
depth = count_chars(e.path, '/');
- listel = talloc_zero(notify, struct notify_list);
+ listel = TALLOC_ZERO_P(notify, struct notify_list);
if (listel == NULL) {
status = NT_STATUS_NO_MEMORY;
goto done;
}
}
+ if (e.filter != 0) {
+ notify_add_onelevel(notify, &e, private_data);
+ status = NT_STATUS_OK;
+ }
+
/* if the system notify handler couldn't handle some of the
filter bits, or couldn't handle a request for recursion
then we need to install it in the array used for the
intra-samba notify handling */
if (e.filter != 0 || e.subdir_filter != 0) {
- status = notify_add_array(notify, &e, private_data, depth);
+ status = notify_add_array(notify, rec, &e, private_data, depth);
}
done:
- notify_unlock(notify);
+ talloc_free(rec);
talloc_free(tmp_path);
return status;
}
+NTSTATUS notify_remove_onelevel(struct notify_context *notify,
+ const struct file_id *fid,
+ void *private_data)
+{
+ struct notify_entry_array *array;
+ struct db_record *rec;
+ DATA_BLOB blob;
+ TDB_DATA dbuf;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+ int i;
+
+ if (notify == NULL) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ array = talloc_zero(talloc_tos(), struct notify_entry_array);
+ if (array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rec = notify->db_onelevel->fetch_locked(
+ notify->db_onelevel, array,
+ make_tdb_data((uint8_t *)fid, sizeof(*fid)));
+ if (rec == NULL) {
+ DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
+ "\n", file_id_string_tos(fid)));
+ TALLOC_FREE(array);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ blob.data = (uint8_t *)rec->value.dptr;
+ blob.length = rec->value.dsize;
+
+ if (blob.length > 0) {
+ ndr_err = ndr_pull_struct_blob(
+ &blob, array, NULL, array,
+ (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
+ ndr_errstr(ndr_err)));
+ TALLOC_FREE(array);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("notify_remove_onelevel:\n"));
+ NDR_PRINT_DEBUG(notify_entry_array, array);
+ }
+ }
+
+ for (i=0; i<array->num_entries; i++) {
+ if ((private_data == array->entries[i].private_data) &&
+ cluster_id_equal(¬ify->server,
+ &array->entries[i].server)) {
+ break;
+ }
+ }
+
+ if (i == array->num_entries) {
+ TALLOC_FREE(array);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ array->entries[i] = array->entries[array->num_entries-1];
+ array->num_entries -= 1;
+
+ if (array->num_entries == 0) {
+ rec->delete_rec(rec);
+ TALLOC_FREE(array);
+ return NT_STATUS_OK;
+ }
+
+ ndr_err = ndr_push_struct_blob(
+ &blob, rec, NULL, array,
+ (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
+ ndr_errstr(ndr_err)));
+ TALLOC_FREE(array);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("notify_add_onelevel:\n"));
+ NDR_PRINT_DEBUG(notify_entry_array, array);
+ }
+
+ dbuf.dptr = blob.data;
+ dbuf.dsize = blob.length;
+
+ status = rec->store(rec, dbuf, TDB_REPLACE);
+ TALLOC_FREE(array);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
/*
remove a notify watch. Called when the directory handle is closed
*/
struct notify_list *listel;
int i, depth;
struct notify_depth *d;
+ struct db_record *rec;
/* see if change notify is enabled at all */
if (notify == NULL) {
talloc_free(listel);
- status = notify_lock(notify);
+ status = notify_fetch_locked(notify, &rec);
NT_STATUS_NOT_OK_RETURN(status);
- status = notify_load(notify);
+ status = notify_load(notify, rec);
if (!NT_STATUS_IS_OK(status)) {
- notify_unlock(notify);
+ talloc_free(rec);
return status;
}
if (depth >= notify->array->num_depths) {
- notify_unlock(notify);
+ talloc_free(rec);
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
}
}
if (i == d->num_entries) {
- notify_unlock(notify);
+ talloc_free(rec);
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
}
d->num_entries--;
- status = notify_save(notify);
+ status = notify_save(notify, rec);
- notify_unlock(notify);
+ talloc_free(rec);
return status;
}
/*
- remove all notify watches for this messaging server
+ remove all notify watches for a messaging server
*/
-static NTSTATUS notify_remove_all(struct notify_context *notify)
+static NTSTATUS notify_remove_all(struct notify_context *notify,
+ const struct server_id *server)
{
NTSTATUS status;
int i, depth, del_count=0;
+ struct db_record *rec;
- if (notify->list == NULL) {
- return NT_STATUS_OK;
- }
-
- status = notify_lock(notify);
+ status = notify_fetch_locked(notify, &rec);
NT_STATUS_NOT_OK_RETURN(status);
- status = notify_load(notify);
+ status = notify_load(notify, rec);
if (!NT_STATUS_IS_OK(status)) {
- notify_unlock(notify);
+ talloc_free(rec);
return status;
}
/* we have to search for all entries across all depths, looking for matches
- for our server id */
+ for the server id */
for (depth=0;depth<notify->array->num_depths;depth++) {
struct notify_depth *d = ¬ify->array->depth[depth];
for (i=0;i<d->num_entries;i++) {
- if (cluster_id_equal(¬ify->server, &d->entries[i].server)) {
+ if (cluster_id_equal(server, &d->entries[i].server)) {
if (i < d->num_entries-1) {
memmove(&d->entries[i], &d->entries[i+1],
sizeof(d->entries[i])*(d->num_entries-(i+1)));
}
if (del_count > 0) {
- status = notify_save(notify);
+ status = notify_save(notify, rec);
}
- notify_unlock(notify);
+ talloc_free(rec);
return status;
}
/*
send a notify message to another messaging server
*/
-static void notify_send(struct notify_context *notify, struct notify_entry *e,
- const char *path, uint32_t action)
+static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
+ const char *path, uint32_t action)
{
struct notify_event ev;
DATA_BLOB data;
NTSTATUS status;
+ enum ndr_err_code ndr_err;
TALLOC_CTX *tmp_ctx;
ev.action = action;
tmp_ctx = talloc_new(notify);
- status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
- (ndr_push_flags_fn_t)ndr_push_notify_event);
- if (!NT_STATUS_IS_OK(status)) {
+ ndr_err = ndr_push_struct_blob(&data, tmp_ctx, NULL, &ev,
+ (ndr_push_flags_fn_t)ndr_push_notify_event);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
talloc_free(tmp_ctx);
- return;
+ return ndr_map_error2ntstatus(ndr_err);
}
status = messaging_send(notify->messaging_ctx, e->server,
MSG_PVFS_NOTIFY, &data);
talloc_free(tmp_ctx);
+ return status;
}
+void notify_onelevel(struct notify_context *notify, uint32_t action,
+ uint32_t filter, struct file_id fid, const char *name)
+{
+ struct notify_entry_array *array;
+ TDB_DATA dbuf;
+ DATA_BLOB blob;
+ bool have_dead_entries = false;
+ int i;
+
+ if (notify == NULL) {
+ return;
+ }
+
+ array = talloc_zero(talloc_tos(), struct notify_entry_array);
+ if (array == NULL) {
+ return;
+ }
+
+ if (notify->db_onelevel->fetch(
+ notify->db_onelevel, array,
+ make_tdb_data((uint8_t *)&fid, sizeof(fid)),
+ &dbuf) == -1) {
+ TALLOC_FREE(array);
+ return;
+ }
+
+ blob.data = (uint8 *)dbuf.dptr;
+ blob.length = dbuf.dsize;
+
+ if (blob.length > 0) {
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_pull_struct_blob(
+ &blob, array, NULL, array,
+ (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
+ ndr_errstr(ndr_err)));
+ TALLOC_FREE(array);
+ return;
+ }
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("notify_onelevel:\n"));
+ NDR_PRINT_DEBUG(notify_entry_array, array);
+ }
+ }
+
+ for (i=0; i<array->num_entries; i++) {
+ struct notify_entry *e = &array->entries[i];
+
+ if ((e->filter & filter) != 0) {
+ NTSTATUS status;
+
+ status = notify_send(notify, e, name, action);
+ if (NT_STATUS_EQUAL(
+ status, NT_STATUS_INVALID_HANDLE)) {
+ /*
+ * Mark the entry as dead. All entries have a
+ * path set. The marker used here is setting
+ * that to NULL.
+ */
+ e->path = NULL;
+ have_dead_entries = true;
+ }
+ }
+ }
+
+ if (!have_dead_entries) {
+ TALLOC_FREE(array);
+ return;
+ }
+
+ for (i=0; i<array->num_entries; i++) {
+ struct notify_entry *e = &array->entries[i];
+ if (e->path != NULL) {
+ continue;
+ }
+ DEBUG(10, ("Deleting notify entries for process %s because "
+ "it's gone\n", procid_str_static(&e->server)));
+ /*
+ * Potential TODO: This might need optimizing,
+ * notify_remove_onelevel() does a fetch_locked() operation at
+ * every call. But this would only matter if a process with
+ * MANY notifies has died without shutting down properly.
+ */
+ notify_remove_onelevel(notify, &e->dir_id, e->private_data);
+ }
+
+ TALLOC_FREE(array);
+ return;
+}
/*
trigger a notify message for anyone waiting on a matching event
return;
}
- status = notify_load(notify);
+ again:
+ status = notify_load(notify, NULL);
if (!NT_STATUS_IS_OK(status)) {
return;
}
continue;
}
}
- notify_send(notify, e, path + e->path_len + 1, action);
+ status = notify_send(notify, e, path + e->path_len + 1,
+ action);
+
+ if (NT_STATUS_EQUAL(
+ status, NT_STATUS_INVALID_HANDLE)) {
+ struct server_id server = e->server;
+
+ DEBUG(10, ("Deleting notify entries for "
+ "process %s because it's gone\n",
+ procid_str_static(&e->server)));
+ notify_remove_all(notify, &server);
+ goto again;
+ }
}
}
}