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"
36 #include "ntvfs/sysdep/sys_notify.h"
38 struct notify_context {
41 struct messaging_context *messaging_ctx;
42 struct notify_list *list;
43 struct notify_array *array;
45 struct sys_notify_context *sys_notify_ctx;
50 struct notify_list *next, *prev;
52 void (*callback)(void *, const struct notify_event *);
53 void *sys_notify_handle;
56 #define NOTIFY_KEY "notify array"
58 static NTSTATUS notify_remove_all(struct notify_context *notify);
59 static void notify_handler(struct messaging_context *msg_ctx, void *private,
60 uint32_t msg_type, uint32_t server_id, DATA_BLOB *data);
63 destroy the notify context
65 static int notify_destructor(void *p)
67 struct notify_context *notify = talloc_get_type(p, struct notify_context);
68 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
69 notify_remove_all(notify);
74 Open up the notify.tdb database. You should close it down using
75 talloc_free(). We need the messaging_ctx to allow for notifications
78 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, uint32_t server,
79 struct messaging_context *messaging_ctx,
80 struct event_context *ev)
83 struct notify_context *notify;
85 notify = talloc(mem_ctx, struct notify_context);
90 path = smbd_tmp_path(notify, "notify.tdb");
91 notify->w = tdb_wrap_open(notify, path, 0,
93 O_RDWR|O_CREAT, 0600);
95 if (notify->w == NULL) {
100 notify->server = server;
101 notify->messaging_ctx = messaging_ctx;
103 notify->array = NULL;
104 notify->seqnum = tdb_get_seqnum(notify->w->tdb);
106 talloc_set_destructor(notify, notify_destructor);
108 /* register with the messaging subsystem for the notify
110 messaging_register(notify->messaging_ctx, notify,
111 MSG_PVFS_NOTIFY, notify_handler);
113 notify->sys_notify_ctx = sys_notify_init(-1, notify, ev);
122 static NTSTATUS notify_lock(struct notify_context *notify)
124 if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
125 return NT_STATUS_INTERNAL_DB_CORRUPTION;
133 static void notify_unlock(struct notify_context *notify)
135 tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
139 load the notify array
141 static NTSTATUS notify_load(struct notify_context *notify)
148 seqnum = tdb_get_seqnum(notify->w->tdb);
150 if (seqnum == notify->seqnum && notify->array != NULL) {
154 notify->seqnum = seqnum;
156 talloc_free(notify->array);
157 notify->array = talloc_zero(notify, struct notify_array);
158 NT_STATUS_HAVE_NO_MEMORY(notify->array);
160 dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
161 if (dbuf.dptr == NULL) {
165 blob.data = dbuf.dptr;
166 blob.length = dbuf.dsize;
168 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
169 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
176 compare notify entries for sorting
178 static int notify_compare(const void *p1, const void *p2)
180 const struct notify_entry *e1 = p1, *e2 = p2;
181 return strcmp(e1->path, e2->path);
185 save the notify array
187 static NTSTATUS notify_save(struct notify_context *notify)
195 if (notify->array->num_entries == 0) {
196 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
198 return NT_STATUS_INTERNAL_DB_CORRUPTION;
203 if (notify->array->num_entries > 1) {
204 qsort(notify->array->entries, notify->array->num_entries,
205 sizeof(struct notify_entry), notify_compare);
208 tmp_ctx = talloc_new(notify);
210 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
211 (ndr_push_flags_fn_t)ndr_push_notify_array);
212 if (!NT_STATUS_IS_OK(status)) {
213 talloc_free(tmp_ctx);
217 dbuf.dptr = blob.data;
218 dbuf.dsize = blob.length;
220 ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
221 talloc_free(tmp_ctx);
223 return NT_STATUS_INTERNAL_DB_CORRUPTION;
231 handle incoming notify messages
233 static void notify_handler(struct messaging_context *msg_ctx, void *private,
234 uint32_t msg_type, uint32_t server_id, DATA_BLOB *data)
236 struct notify_context *notify = talloc_get_type(private, struct notify_context);
238 struct notify_event ev;
239 TALLOC_CTX *tmp_ctx = talloc_new(notify);
240 struct notify_list *listel;
242 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
243 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
244 if (!NT_STATUS_IS_OK(status)) {
245 talloc_free(tmp_ctx);
249 for (listel=notify->list;listel;listel=listel->next) {
250 if (listel->private == ev.private) {
251 listel->callback(listel->private, &ev);
256 talloc_free(tmp_ctx);
260 callback from sys_notify telling us about changes from the OS
262 static void sys_notify_callback(struct sys_notify_context *ctx,
263 void *ptr, struct notify_event *ev)
265 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
266 ev->private = listel;
267 listel->callback(listel->private, ev);
271 add a notify watch. This is called when a notify is first setup on a open
274 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e,
275 void (*callback)(void *, const struct notify_event *),
279 struct notify_list *listel;
283 status = notify_lock(notify);
284 NT_STATUS_NOT_OK_RETURN(status);
286 status = notify_load(notify);
287 if (!NT_STATUS_IS_OK(status)) {
288 notify_unlock(notify);
292 notify->array->entries = talloc_realloc(notify->array, notify->array->entries,
294 notify->array->num_entries+1);
296 if (notify->array->entries == NULL) {
297 notify_unlock(notify);
298 return NT_STATUS_NO_MEMORY;
301 /* cope with /. on the end of the path */
302 len = strlen(e->path);
303 if (len > 1 && e->path[len-1] == '.' && e->path[len-2] == '/') {
304 path = talloc_strndup(notify, e->path, len-2);
307 listel = talloc_zero(notify, struct notify_list);
308 NT_STATUS_HAVE_NO_MEMORY(listel);
310 listel->private = private;
311 listel->callback = callback;
312 DLIST_ADD(notify->list, listel);
314 /* ignore failures from sys_notify */
315 status = sys_notify_watch(notify->sys_notify_ctx, e->path, e->filter,
316 sys_notify_callback, listel,
317 &listel->sys_notify_handle);
318 if (NT_STATUS_IS_OK(status)) {
319 talloc_steal(listel, listel->sys_notify_handle);
320 notify_unlock(notify);
322 notify->array->entries[notify->array->num_entries] = *e;
323 notify->array->entries[notify->array->num_entries].private = private;
324 notify->array->entries[notify->array->num_entries].server = notify->server;
327 notify->array->entries[notify->array->num_entries].path = path;
330 notify->array->num_entries++;
332 status = notify_save(notify);
334 notify_unlock(notify);
336 NT_STATUS_NOT_OK_RETURN(status);
347 remove a notify watch. Called when the directory handle is closed
349 NTSTATUS notify_remove(struct notify_context *notify, void *private)
352 struct notify_list *listel;
355 for (listel=notify->list;listel;listel=listel->next) {
356 if (listel->private == private) {
357 DLIST_REMOVE(notify->list, listel);
361 if (listel == NULL) {
362 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
367 status = notify_lock(notify);
368 NT_STATUS_NOT_OK_RETURN(status);
370 status = notify_load(notify);
371 if (!NT_STATUS_IS_OK(status)) {
372 notify_unlock(notify);
376 for (i=0;i<notify->array->num_entries;i++) {
377 if (notify->server == notify->array->entries[i].server &&
378 private == notify->array->entries[i].private) {
382 if (i == notify->array->num_entries) {
383 notify_unlock(notify);
384 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
387 if (i < notify->array->num_entries-1) {
388 memmove(¬ify->array->entries[i], ¬ify->array->entries[i+1],
389 sizeof(notify->array->entries[i])*(notify->array->num_entries-(i+1)));
391 notify->array->num_entries--;
393 status = notify_save(notify);
395 notify_unlock(notify);
401 remove all notify watches for this messaging server
403 static NTSTATUS notify_remove_all(struct notify_context *notify)
408 if (notify->list == NULL) {
412 status = notify_lock(notify);
413 NT_STATUS_NOT_OK_RETURN(status);
415 status = notify_load(notify);
416 if (!NT_STATUS_IS_OK(status)) {
417 notify_unlock(notify);
421 for (i=0;i<notify->array->num_entries;i++) {
422 if (notify->server == notify->array->entries[i].server) {
423 if (i < notify->array->num_entries-1) {
424 memmove(¬ify->array->entries[i], ¬ify->array->entries[i+1],
425 sizeof(notify->array->entries[i])*(notify->array->num_entries-(i+1)));
428 notify->array->num_entries--;
433 status = notify_save(notify);
435 notify_unlock(notify);
442 send a notify message to another messaging server
444 static void notify_send(struct notify_context *notify, struct notify_entry *e,
445 const char *path, uint32_t action)
447 struct notify_event ev;
454 ev.private = e->private;
456 tmp_ctx = talloc_new(notify);
458 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
459 (ndr_push_flags_fn_t)ndr_push_notify_event);
460 if (!NT_STATUS_IS_OK(status)) {
461 talloc_free(tmp_ctx);
465 status = messaging_send(notify->messaging_ctx, e->server,
466 MSG_PVFS_NOTIFY, &data);
467 talloc_free(tmp_ctx);
471 see if a notify event matches
473 static BOOL notify_match(struct notify_context *notify, struct notify_entry *e,
474 const char *path, uint32_t filter)
478 if (!(filter & e->filter)) {
482 len = strlen(e->path);
484 if (strncmp(path, e->path, len) != 0) {
488 if (path[len] != '/') {
493 if (strchr(&path[len+1], '/') != NULL) {
503 trigger a notify message for anyone waiting on a matching event
505 void notify_trigger(struct notify_context *notify,
506 uint32_t action, uint32_t filter, const char *path)
511 status = notify_load(notify);
512 if (!NT_STATUS_IS_OK(status)) {
516 /* TODO: this needs to be changed to a log(n) search */
517 for (i=0;i<notify->array->num_entries;i++) {
518 if (notify_match(notify, ¬ify->array->entries[i], path, filter)) {
519 notify_send(notify, ¬ify->array->entries[i],
520 path + strlen(notify->array->entries[i].path) + 1,