2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2006
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 this is the change notify database. It implements mechanisms for
23 storing current change notify waiters in a tdb, and checking if a
24 given event matches any of the stored notify waiiters.
28 #include "system/filesys.h"
29 #include "lib/tdb/include/tdb.h"
30 #include "lib/tdb/include/tdbutil.h"
31 #include "messaging/messaging.h"
33 #include "lib/messaging/irpc.h"
34 #include "librpc/gen_ndr/ndr_notify.h"
35 #include "dlinklist.h"
37 struct notify_context {
40 struct messaging_context *messaging_ctx;
41 struct notify_list *list;
42 struct notify_array *array;
48 struct notify_list *next, *prev;
50 void (*callback)(void *, const struct notify_event *);
53 #define NOTIFY_KEY "notify array"
55 static NTSTATUS notify_remove_all(struct notify_context *notify);
56 static void notify_handler(struct messaging_context *msg_ctx, void *private,
57 uint32_t msg_type, uint32_t server_id, DATA_BLOB *data);
60 destroy the notify context
62 static int notify_destructor(void *p)
64 struct notify_context *notify = talloc_get_type(p, struct notify_context);
65 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
66 notify_remove_all(notify);
71 Open up the notify.tdb database. You should close it down using
72 talloc_free(). We need the messaging_ctx to allow for notifications
75 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, uint32_t server,
76 struct messaging_context *messaging_ctx)
79 struct notify_context *notify;
81 notify = talloc(mem_ctx, struct notify_context);
86 path = smbd_tmp_path(notify, "notify.tdb");
87 notify->w = tdb_wrap_open(notify, path, 0,
89 O_RDWR|O_CREAT, 0600);
91 if (notify->w == NULL) {
96 notify->server = server;
97 notify->messaging_ctx = messaging_ctx;
100 notify->seqnum = tdb_get_seqnum(notify->w->tdb);
102 talloc_set_destructor(notify, notify_destructor);
104 /* register with the messaging subsystem for the notify
106 messaging_register(notify->messaging_ctx, notify,
107 MSG_PVFS_NOTIFY, notify_handler);
113 load the notify array
115 static NTSTATUS notify_load(struct notify_context *notify)
122 seqnum = tdb_get_seqnum(notify->w->tdb);
124 if (seqnum == notify->seqnum && notify->array != NULL) {
128 notify->seqnum = seqnum;
130 talloc_free(notify->array);
131 notify->array = talloc_zero(notify, struct notify_array);
132 NT_STATUS_HAVE_NO_MEMORY(notify->array);
134 dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
135 if (dbuf.dptr == NULL) {
139 blob.data = dbuf.dptr;
140 blob.length = dbuf.dsize;
142 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
143 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
151 save the notify array
153 static NTSTATUS notify_save(struct notify_context *notify)
161 if (notify->array->num_entries == 0) {
162 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
164 return NT_STATUS_INTERNAL_DB_CORRUPTION;
169 tmp_ctx = talloc_new(notify);
171 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
172 (ndr_push_flags_fn_t)ndr_push_notify_array);
173 if (!NT_STATUS_IS_OK(status)) {
174 talloc_free(tmp_ctx);
178 dbuf.dptr = blob.data;
179 dbuf.dsize = blob.length;
181 ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
182 talloc_free(tmp_ctx);
184 return NT_STATUS_INTERNAL_DB_CORRUPTION;
192 handle incoming notify messages
194 static void notify_handler(struct messaging_context *msg_ctx, void *private,
195 uint32_t msg_type, uint32_t server_id, DATA_BLOB *data)
197 struct notify_context *notify = talloc_get_type(private, struct notify_context);
199 struct notify_event ev;
200 TALLOC_CTX *tmp_ctx = talloc_new(notify);
201 struct notify_list *listel;
203 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
204 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
205 if (!NT_STATUS_IS_OK(status)) {
206 talloc_free(tmp_ctx);
210 for (listel=notify->list;listel;listel=listel->next) {
211 if (listel->private == ev.private) {
212 listel->callback(listel->private, &ev);
217 talloc_free(tmp_ctx);
221 add a notify watch. This is called when a notify is first setup on a open
224 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e,
225 void (*callback)(void *, const struct notify_event *),
229 struct notify_list *listel;
231 status = notify_load(notify);
232 NT_STATUS_NOT_OK_RETURN(status);
234 notify->array->entries = talloc_realloc(notify->array, notify->array->entries,
236 notify->array->num_entries+1);
238 if (notify->array->entries == NULL) {
239 return NT_STATUS_NO_MEMORY;
242 notify->array->entries[notify->array->num_entries] = *e;
243 notify->array->entries[notify->array->num_entries].private = private;
244 notify->array->entries[notify->array->num_entries].server = notify->server;
245 notify->array->num_entries++;
247 status = notify_save(notify);
248 NT_STATUS_NOT_OK_RETURN(status);
250 listel = talloc(notify, struct notify_list);
251 NT_STATUS_HAVE_NO_MEMORY(listel);
253 listel->private = private;
254 listel->callback = callback;
255 DLIST_ADD(notify->list, listel);
261 remove a notify watch. Called when the directory handle is closed
263 NTSTATUS notify_remove(struct notify_context *notify, void *private)
266 struct notify_list *listel;
269 for (listel=notify->list;listel;listel=listel->next) {
270 if (listel->private == private) {
271 DLIST_REMOVE(notify->list, listel);
275 if (listel == NULL) {
276 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
279 status = notify_load(notify);
280 NT_STATUS_NOT_OK_RETURN(status);
282 for (i=0;i<notify->array->num_entries;i++) {
283 if (notify->server == notify->array->entries[i].server &&
284 private == notify->array->entries[i].private) {
288 if (i == notify->array->num_entries) {
289 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
292 if (i < notify->array->num_entries-1) {
293 memmove(¬ify->array->entries[i], ¬ify->array->entries[i+1],
294 sizeof(notify->array->entries[i])*(notify->array->num_entries-(i+1)));
296 notify->array->num_entries--;
298 return notify_save(notify);
302 remove all notify watches for this messaging server
304 static NTSTATUS notify_remove_all(struct notify_context *notify)
309 if (notify->list == NULL) {
313 status = notify_load(notify);
314 NT_STATUS_NOT_OK_RETURN(status);
316 for (i=0;i<notify->array->num_entries;i++) {
317 if (notify->server == notify->array->entries[i].server) {
318 if (i < notify->array->num_entries-1) {
319 memmove(¬ify->array->entries[i], ¬ify->array->entries[i+1],
320 sizeof(notify->array->entries[i])*(notify->array->num_entries-(i+1)));
323 notify->array->num_entries--;
328 return notify_save(notify);
333 see if a notify event matches
335 static BOOL notify_match(struct notify_context *notify, struct notify_entry *e,
336 const char *path, uint32_t filter)
340 if (!(filter & e->filter)) {
344 len = strlen(e->path);
346 if (strncmp(path, e->path, len) != 0) {
350 if (path[len] != '/') {
355 if (strchr(&path[len+1], '/') != NULL) {
365 send a notify message to another messaging server
367 static void notify_send(struct notify_context *notify, struct notify_entry *e,
368 const char *path, uint32_t action)
370 struct notify_event ev;
377 ev.private = e->private;
379 tmp_ctx = talloc_new(notify);
381 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
382 (ndr_push_flags_fn_t)ndr_push_notify_event);
383 if (!NT_STATUS_IS_OK(status)) {
384 talloc_free(tmp_ctx);
388 status = messaging_send(notify->messaging_ctx, e->server,
389 MSG_PVFS_NOTIFY, &data);
390 talloc_free(tmp_ctx);
394 trigger a notify message for anyone waiting on a matching event
396 void notify_trigger(struct notify_context *notify,
397 uint32_t action, uint32_t filter, const char *path)
402 status = notify_load(notify);
403 if (!NT_STATUS_IS_OK(status)) {
407 /* this needs to be changed to a log(n) search */
408 for (i=0;i<notify->array->num_entries;i++) {
409 if (notify_match(notify, ¬ify->array->entries[i], path, filter)) {
410 notify_send(notify, ¬ify->array->entries[i],
411 path + strlen(notify->array->entries[i].path) + 1,