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 an entry to the notify array
273 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
274 const char *path, void *private)
276 notify->array->entries[notify->array->num_entries] = *e;
277 notify->array->entries[notify->array->num_entries].private = private;
278 notify->array->entries[notify->array->num_entries].server = notify->server;
281 notify->array->entries[notify->array->num_entries].path = path;
284 notify->array->num_entries++;
286 return notify_save(notify);
290 add a notify watch. This is called when a notify is first setup on a open
293 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e,
294 void (*callback)(void *, const struct notify_event *),
298 struct notify_list *listel;
302 status = notify_lock(notify);
303 NT_STATUS_NOT_OK_RETURN(status);
305 status = notify_load(notify);
306 if (!NT_STATUS_IS_OK(status)) {
307 notify_unlock(notify);
311 notify->array->entries = talloc_realloc(notify->array, notify->array->entries,
313 notify->array->num_entries+1);
315 if (notify->array->entries == NULL) {
316 notify_unlock(notify);
317 return NT_STATUS_NO_MEMORY;
320 /* cope with /. on the end of the path */
321 len = strlen(e->path);
322 if (len > 1 && e->path[len-1] == '.' && e->path[len-2] == '/') {
323 path = talloc_strndup(notify, e->path, len-2);
326 listel = talloc_zero(notify, struct notify_list);
327 NT_STATUS_HAVE_NO_MEMORY(listel);
329 listel->private = private;
330 listel->callback = callback;
331 DLIST_ADD(notify->list, listel);
333 /* ignore failures from sys_notify */
334 if (notify->sys_notify_ctx != NULL) {
335 status = sys_notify_watch(notify->sys_notify_ctx, e->path, e->filter,
336 sys_notify_callback, listel,
337 &listel->sys_notify_handle);
338 if (NT_STATUS_IS_OK(status)) {
339 /* if the kernel handler has said it can handle this notify then
340 we don't need to add it to the array */
341 talloc_steal(listel, listel->sys_notify_handle);
346 status = notify_add_array(notify, e, path, private);
349 notify_unlock(notify);
356 remove a notify watch. Called when the directory handle is closed
358 NTSTATUS notify_remove(struct notify_context *notify, void *private)
361 struct notify_list *listel;
364 for (listel=notify->list;listel;listel=listel->next) {
365 if (listel->private == private) {
366 DLIST_REMOVE(notify->list, listel);
370 if (listel == NULL) {
371 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
376 status = notify_lock(notify);
377 NT_STATUS_NOT_OK_RETURN(status);
379 status = notify_load(notify);
380 if (!NT_STATUS_IS_OK(status)) {
381 notify_unlock(notify);
385 for (i=0;i<notify->array->num_entries;i++) {
386 if (notify->server == notify->array->entries[i].server &&
387 private == notify->array->entries[i].private) {
391 if (i == notify->array->num_entries) {
392 notify_unlock(notify);
393 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
396 if (i < notify->array->num_entries-1) {
397 memmove(¬ify->array->entries[i], ¬ify->array->entries[i+1],
398 sizeof(notify->array->entries[i])*(notify->array->num_entries-(i+1)));
400 notify->array->num_entries--;
402 status = notify_save(notify);
404 notify_unlock(notify);
410 remove all notify watches for this messaging server
412 static NTSTATUS notify_remove_all(struct notify_context *notify)
417 if (notify->list == NULL) {
421 status = notify_lock(notify);
422 NT_STATUS_NOT_OK_RETURN(status);
424 status = notify_load(notify);
425 if (!NT_STATUS_IS_OK(status)) {
426 notify_unlock(notify);
430 for (i=0;i<notify->array->num_entries;i++) {
431 if (notify->server == notify->array->entries[i].server) {
432 if (i < notify->array->num_entries-1) {
433 memmove(¬ify->array->entries[i], ¬ify->array->entries[i+1],
434 sizeof(notify->array->entries[i])*(notify->array->num_entries-(i+1)));
437 notify->array->num_entries--;
442 status = notify_save(notify);
444 notify_unlock(notify);
451 send a notify message to another messaging server
453 static void notify_send(struct notify_context *notify, struct notify_entry *e,
454 const char *path, uint32_t action)
456 struct notify_event ev;
463 ev.private = e->private;
465 tmp_ctx = talloc_new(notify);
467 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
468 (ndr_push_flags_fn_t)ndr_push_notify_event);
469 if (!NT_STATUS_IS_OK(status)) {
470 talloc_free(tmp_ctx);
474 status = messaging_send(notify->messaging_ctx, e->server,
475 MSG_PVFS_NOTIFY, &data);
476 talloc_free(tmp_ctx);
480 see if a notify event matches
482 static BOOL notify_match(struct notify_context *notify, struct notify_entry *e,
483 const char *path, uint32_t filter)
487 if (!(filter & e->filter)) {
491 len = strlen(e->path);
493 if (strncmp(path, e->path, len) != 0) {
497 if (path[len] != '/') {
502 if (strchr(&path[len+1], '/') != NULL) {
512 trigger a notify message for anyone waiting on a matching event
514 void notify_trigger(struct notify_context *notify,
515 uint32_t action, uint32_t filter, const char *path)
520 status = notify_load(notify);
521 if (!NT_STATUS_IS_OK(status)) {
525 /* TODO: this needs to be changed to a log(n) search */
526 for (i=0;i<notify->array->num_entries;i++) {
527 if (notify_match(notify, ¬ify->array->entries[i], path, filter)) {
528 notify_send(notify, ¬ify->array->entries[i],
529 path + strlen(notify->array->entries[i].path) + 1,