r21111: Reorganize the change notify params a bit. We now have the per-share
[Samba.git] / source / smbd / notify_internal.c
blobafa4e0922bb49332c105eb5669614b6a3262f179
1 /*
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.
27 #include "includes.h"
28 #include "librpc/gen_ndr/ndr_notify.h"
30 struct notify_context {
31 struct tdb_wrap *w;
32 struct server_id server;
33 struct messaging_context *messaging_ctx;
34 struct notify_list *list;
35 struct notify_array *array;
36 int seqnum;
37 struct sys_notify_context *sys_notify_ctx;
41 struct notify_list {
42 struct notify_list *next, *prev;
43 void *private_data;
44 void (*callback)(void *, const struct notify_event *);
45 void *sys_notify_handle;
46 int depth;
49 #define NOTIFY_KEY "notify array"
51 #define NOTIFY_ENABLE "notify:enable"
52 #define NOTIFY_ENABLE_DEFAULT True
54 static NTSTATUS notify_remove_all(struct notify_context *notify);
55 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
56 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
59 destroy the notify context
61 static int notify_destructor(struct notify_context *notify)
63 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
64 notify_remove_all(notify);
65 return 0;
69 Open up the notify.tdb database. You should close it down using
70 talloc_free(). We need the messaging_ctx to allow for notifications
71 via internal messages
73 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
74 struct messaging_context *messaging_ctx,
75 struct event_context *ev,
76 connection_struct *conn)
78 struct notify_context *notify;
80 if (!lp_change_notify(conn->params)) {
81 return NULL;
84 notify = talloc(mem_ctx, struct notify_context);
85 if (notify == NULL) {
86 return NULL;
89 notify->w = tdb_wrap_open(notify, lock_path("notify.tdb"),
90 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
91 O_RDWR|O_CREAT, 0644);
92 if (notify->w == NULL) {
93 talloc_free(notify);
94 return NULL;
97 notify->server = server;
98 notify->messaging_ctx = messaging_ctx;
99 notify->list = NULL;
100 notify->array = NULL;
101 notify->seqnum = tdb_get_seqnum(notify->w->tdb);
103 talloc_set_destructor(notify, notify_destructor);
105 /* register with the messaging subsystem for the notify
106 message type */
107 messaging_register(notify->messaging_ctx, notify,
108 MSG_PVFS_NOTIFY, notify_handler);
110 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
112 return notify;
117 lock the notify db
119 static NTSTATUS notify_lock(struct notify_context *notify)
121 if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
122 return NT_STATUS_INTERNAL_DB_CORRUPTION;
124 return NT_STATUS_OK;
128 unlock the notify db
130 static void notify_unlock(struct notify_context *notify)
132 tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
136 load the notify array
138 static NTSTATUS notify_load(struct notify_context *notify)
140 TDB_DATA dbuf;
141 DATA_BLOB blob;
142 NTSTATUS status;
143 int seqnum;
145 seqnum = tdb_get_seqnum(notify->w->tdb);
147 if (seqnum == notify->seqnum && notify->array != NULL) {
148 return NT_STATUS_OK;
151 notify->seqnum = seqnum;
153 talloc_free(notify->array);
154 notify->array = talloc_zero(notify, struct notify_array);
155 NT_STATUS_HAVE_NO_MEMORY(notify->array);
157 dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
158 if (dbuf.dptr == NULL) {
159 return NT_STATUS_OK;
162 blob.data = (uint8 *)dbuf.dptr;
163 blob.length = dbuf.dsize;
165 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
166 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
167 free(dbuf.dptr);
169 return status;
173 compare notify entries for sorting
175 static int notify_compare(const void *p1, const void *p2)
177 const struct notify_entry *e1 = (const struct notify_entry *)p1;
178 const struct notify_entry *e2 = (const struct notify_entry *)p2;
179 return strcmp(e1->path, e2->path);
183 save the notify array
185 static NTSTATUS notify_save(struct notify_context *notify)
187 TDB_DATA dbuf;
188 DATA_BLOB blob;
189 NTSTATUS status;
190 int ret;
191 TALLOC_CTX *tmp_ctx;
193 /* if possible, remove some depth arrays */
194 while (notify->array->num_depths > 0 &&
195 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
196 notify->array->num_depths--;
199 /* we might just be able to delete the record */
200 if (notify->array->num_depths == 0) {
201 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
202 if (ret != 0) {
203 return NT_STATUS_INTERNAL_DB_CORRUPTION;
205 return NT_STATUS_OK;
208 tmp_ctx = talloc_new(notify);
209 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
211 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
212 (ndr_push_flags_fn_t)ndr_push_notify_array);
213 if (!NT_STATUS_IS_OK(status)) {
214 talloc_free(tmp_ctx);
215 return status;
218 dbuf.dptr = (char *)blob.data;
219 dbuf.dsize = blob.length;
221 ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
222 talloc_free(tmp_ctx);
223 if (ret != 0) {
224 return NT_STATUS_INTERNAL_DB_CORRUPTION;
227 return NT_STATUS_OK;
232 handle incoming notify messages
234 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
235 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
237 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
238 NTSTATUS status;
239 struct notify_event ev;
240 TALLOC_CTX *tmp_ctx = talloc_new(notify);
241 struct notify_list *listel;
243 if (tmp_ctx == NULL) {
244 return;
247 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
248 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
249 if (!NT_STATUS_IS_OK(status)) {
250 talloc_free(tmp_ctx);
251 return;
254 for (listel=notify->list;listel;listel=listel->next) {
255 if (listel->private_data == ev.private_data) {
256 listel->callback(listel->private_data, &ev);
257 break;
261 talloc_free(tmp_ctx);
265 callback from sys_notify telling us about changes from the OS
267 static void sys_notify_callback(struct sys_notify_context *ctx,
268 void *ptr, struct notify_event *ev)
270 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
271 ev->private_data = listel;
272 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
273 ev->action, ev->path));
274 listel->callback(listel->private_data, ev);
278 add an entry to the notify array
280 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
281 void *private_data, int depth)
283 int i;
284 struct notify_depth *d;
285 struct notify_entry *ee;
287 /* possibly expand the depths array */
288 if (depth >= notify->array->num_depths) {
289 d = talloc_realloc(notify->array, notify->array->depth,
290 struct notify_depth, depth+1);
291 NT_STATUS_HAVE_NO_MEMORY(d);
292 for (i=notify->array->num_depths;i<=depth;i++) {
293 ZERO_STRUCT(d[i]);
295 notify->array->depth = d;
296 notify->array->num_depths = depth+1;
298 d = &notify->array->depth[depth];
300 /* expand the entries array */
301 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
302 d->num_entries+1);
303 NT_STATUS_HAVE_NO_MEMORY(ee);
304 d->entries = ee;
306 d->entries[d->num_entries] = *e;
307 d->entries[d->num_entries].private_data = private_data;
308 d->entries[d->num_entries].server = notify->server;
309 d->entries[d->num_entries].path_len = strlen(e->path);
310 d->num_entries++;
312 d->max_mask |= e->filter;
313 d->max_mask_subdir |= e->subdir_filter;
315 if (d->num_entries > 1) {
316 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
319 /* recalculate the maximum masks */
320 d->max_mask = 0;
321 d->max_mask_subdir = 0;
323 for (i=0;i<d->num_entries;i++) {
324 d->max_mask |= d->entries[i].filter;
325 d->max_mask_subdir |= d->entries[i].subdir_filter;
328 return notify_save(notify);
332 add a notify watch. This is called when a notify is first setup on a open
333 directory handle.
335 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
336 void (*callback)(void *, const struct notify_event *),
337 void *private_data)
339 struct notify_entry e = *e0;
340 NTSTATUS status;
341 char *tmp_path = NULL;
342 struct notify_list *listel;
343 size_t len;
344 int depth;
346 /* see if change notify is enabled at all */
347 if (notify == NULL) {
348 return NT_STATUS_NOT_IMPLEMENTED;
351 status = notify_lock(notify);
352 NT_STATUS_NOT_OK_RETURN(status);
354 status = notify_load(notify);
355 if (!NT_STATUS_IS_OK(status)) {
356 goto done;
359 /* cope with /. on the end of the path */
360 len = strlen(e.path);
361 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
362 tmp_path = talloc_strndup(notify, e.path, len-2);
363 if (tmp_path == NULL) {
364 status = NT_STATUS_NO_MEMORY;
365 goto done;
367 e.path = tmp_path;
370 depth = count_chars(e.path, '/');
372 listel = talloc_zero(notify, struct notify_list);
373 if (listel == NULL) {
374 status = NT_STATUS_NO_MEMORY;
375 goto done;
378 listel->private_data = private_data;
379 listel->callback = callback;
380 listel->depth = depth;
381 DLIST_ADD(notify->list, listel);
383 /* ignore failures from sys_notify */
384 if (notify->sys_notify_ctx != NULL) {
386 this call will modify e.filter and e.subdir_filter
387 to remove bits handled by the backend
389 status = sys_notify_watch(notify->sys_notify_ctx, &e,
390 sys_notify_callback, listel,
391 &listel->sys_notify_handle);
392 if (NT_STATUS_IS_OK(status)) {
393 talloc_steal(listel, listel->sys_notify_handle);
397 /* if the system notify handler couldn't handle some of the
398 filter bits, or couldn't handle a request for recursion
399 then we need to install it in the array used for the
400 intra-samba notify handling */
401 if (e.filter != 0 || e.subdir_filter != 0) {
402 status = notify_add_array(notify, &e, private_data, depth);
405 done:
406 notify_unlock(notify);
407 talloc_free(tmp_path);
409 return status;
413 remove a notify watch. Called when the directory handle is closed
415 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
417 NTSTATUS status;
418 struct notify_list *listel;
419 int i, depth;
420 struct notify_depth *d;
422 /* see if change notify is enabled at all */
423 if (notify == NULL) {
424 return NT_STATUS_NOT_IMPLEMENTED;
427 for (listel=notify->list;listel;listel=listel->next) {
428 if (listel->private_data == private_data) {
429 DLIST_REMOVE(notify->list, listel);
430 break;
433 if (listel == NULL) {
434 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
437 depth = listel->depth;
439 talloc_free(listel);
441 status = notify_lock(notify);
442 NT_STATUS_NOT_OK_RETURN(status);
444 status = notify_load(notify);
445 if (!NT_STATUS_IS_OK(status)) {
446 notify_unlock(notify);
447 return status;
450 if (depth >= notify->array->num_depths) {
451 notify_unlock(notify);
452 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
455 /* we only have to search at the depth of this element */
456 d = &notify->array->depth[depth];
458 for (i=0;i<d->num_entries;i++) {
459 if (private_data == d->entries[i].private_data &&
460 cluster_id_equal(&notify->server, &d->entries[i].server)) {
461 break;
464 if (i == d->num_entries) {
465 notify_unlock(notify);
466 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
469 if (i < d->num_entries-1) {
470 memmove(&d->entries[i], &d->entries[i+1],
471 sizeof(d->entries[i])*(d->num_entries-(i+1)));
473 d->num_entries--;
475 status = notify_save(notify);
477 notify_unlock(notify);
479 return status;
483 remove all notify watches for this messaging server
485 static NTSTATUS notify_remove_all(struct notify_context *notify)
487 NTSTATUS status;
488 int i, depth, del_count=0;
490 if (notify->list == NULL) {
491 return NT_STATUS_OK;
494 status = notify_lock(notify);
495 NT_STATUS_NOT_OK_RETURN(status);
497 status = notify_load(notify);
498 if (!NT_STATUS_IS_OK(status)) {
499 notify_unlock(notify);
500 return status;
503 /* we have to search for all entries across all depths, looking for matches
504 for our server id */
505 for (depth=0;depth<notify->array->num_depths;depth++) {
506 struct notify_depth *d = &notify->array->depth[depth];
507 for (i=0;i<d->num_entries;i++) {
508 if (cluster_id_equal(&notify->server, &d->entries[i].server)) {
509 if (i < d->num_entries-1) {
510 memmove(&d->entries[i], &d->entries[i+1],
511 sizeof(d->entries[i])*(d->num_entries-(i+1)));
513 i--;
514 d->num_entries--;
515 del_count++;
520 if (del_count > 0) {
521 status = notify_save(notify);
524 notify_unlock(notify);
526 return status;
531 send a notify message to another messaging server
533 static void notify_send(struct notify_context *notify, struct notify_entry *e,
534 const char *path, uint32_t action)
536 struct notify_event ev;
537 DATA_BLOB data;
538 NTSTATUS status;
539 TALLOC_CTX *tmp_ctx;
541 ev.action = action;
542 ev.path = path;
543 ev.private_data = e->private_data;
545 tmp_ctx = talloc_new(notify);
547 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
548 (ndr_push_flags_fn_t)ndr_push_notify_event);
549 if (!NT_STATUS_IS_OK(status)) {
550 talloc_free(tmp_ctx);
551 return;
554 status = messaging_send(notify->messaging_ctx, e->server,
555 MSG_PVFS_NOTIFY, &data);
556 talloc_free(tmp_ctx);
561 trigger a notify message for anyone waiting on a matching event
563 This function is called a lot, and needs to be very fast. The unusual data structure
564 and traversal is designed to be fast in the average case, even for large numbers of
565 notifies
567 void notify_trigger(struct notify_context *notify,
568 uint32_t action, uint32_t filter, const char *path)
570 NTSTATUS status;
571 int depth;
572 const char *p, *next_p;
574 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
575 "path=%s\n", (unsigned)action, (unsigned)filter, path));
577 /* see if change notify is enabled at all */
578 if (notify == NULL) {
579 return;
582 status = notify_load(notify);
583 if (!NT_STATUS_IS_OK(status)) {
584 return;
587 /* loop along the given path, working with each directory depth separately */
588 for (depth=0,p=path;
589 p && depth < notify->array->num_depths;
590 p=next_p,depth++) {
591 int p_len = p - path;
592 int min_i, max_i, i;
593 struct notify_depth *d = &notify->array->depth[depth];
594 next_p = strchr(p+1, '/');
596 /* see if there are any entries at this depth */
597 if (d->num_entries == 0) continue;
599 /* try to skip based on the maximum mask. If next_p is
600 NULL then we know it will be a 'this directory'
601 match, otherwise it must be a subdir match */
602 if (next_p != NULL) {
603 if (0 == (filter & d->max_mask_subdir)) {
604 continue;
606 } else {
607 if (0 == (filter & d->max_mask)) {
608 continue;
612 /* we know there is an entry here worth looking
613 for. Use a bisection search to find the first entry
614 with a matching path */
615 min_i = 0;
616 max_i = d->num_entries-1;
618 while (min_i < max_i) {
619 struct notify_entry *e;
620 int cmp;
621 i = (min_i+max_i)/2;
622 e = &d->entries[i];
623 cmp = strncmp(path, e->path, p_len);
624 if (cmp == 0) {
625 if (p_len == e->path_len) {
626 max_i = i;
627 } else {
628 max_i = i-1;
630 } else if (cmp < 0) {
631 max_i = i-1;
632 } else {
633 min_i = i+1;
637 if (min_i != max_i) {
638 /* none match */
639 continue;
642 /* we now know that the entries start at min_i */
643 for (i=min_i;i<d->num_entries;i++) {
644 struct notify_entry *e = &d->entries[i];
645 if (p_len != e->path_len ||
646 strncmp(path, e->path, p_len) != 0) break;
647 if (next_p != NULL) {
648 if (0 == (filter & e->subdir_filter)) {
649 continue;
651 } else {
652 if (0 == (filter & e->filter)) {
653 continue;
656 notify_send(notify, e, path + e->path_len + 1, action);