ctdb-server: Remove duplicate logic
[samba4-gss.git] / source4 / ntvfs / common / notify.c
bloba434fab998c1bd90f42507bbfe562706566eb163
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 3 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, see <http://www.gnu.org/licenses/>.
21 this is the change notify database. It implements mechanisms for
22 storing current change notify waiters in a tdb, and checking if a
23 given event matches any of the stored notify waiiters.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "messaging/messaging.h"
29 #include "lib/messaging/irpc.h"
30 #include "librpc/gen_ndr/ndr_notify.h"
31 #include "../lib/util/dlinklist.h"
32 #include "ntvfs/common/ntvfs_common.h"
33 #include "ntvfs/sysdep/sys_notify.h"
34 #include "cluster/cluster.h"
35 #include "param/param.h"
36 #include "lib/util/tsort.h"
37 #include "lib/dbwrap/dbwrap.h"
38 #include "../lib/util/util_tdb.h"
40 struct notify_context {
41 struct db_context *db;
42 struct server_id server;
43 struct imessaging_context *imessaging_ctx;
44 struct notify_list *list;
45 struct notify_array *array;
46 int64_t seqnum;
47 struct sys_notify_context *sys_notify_ctx;
51 struct notify_list {
52 struct notify_list *next, *prev;
53 void *private_data;
54 void (*callback)(void *, const struct notify_event *);
55 void *sys_notify_handle;
56 int depth;
59 #define NOTIFY_KEY "notify array"
61 #define NOTIFY_ENABLE "notify:enable"
62 #define NOTIFY_ENABLE_DEFAULT true
64 static NTSTATUS notify_remove_all(struct notify_context *notify);
65 static void notify_handler(struct imessaging_context *msg_ctx,
66 void *private_data,
67 uint32_t msg_type,
68 struct server_id server_id,
69 size_t num_fds,
70 int *fds,
71 DATA_BLOB *data);
74 destroy the notify context
76 static int notify_destructor(struct notify_context *notify)
78 imessaging_deregister(notify->imessaging_ctx, MSG_PVFS_NOTIFY, notify);
79 notify_remove_all(notify);
80 return 0;
84 Open up the notify.tdb database. You should close it down using
85 talloc_free(). We need the imessaging_ctx to allow for notifications
86 via internal messages
88 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
89 struct imessaging_context *imessaging_ctx,
90 struct loadparm_context *lp_ctx,
91 struct tevent_context *ev,
92 struct share_config *scfg)
94 struct notify_context *notify;
96 if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != true) {
97 return NULL;
100 if (ev == NULL) {
101 return NULL;
104 notify = talloc(mem_ctx, struct notify_context);
105 if (notify == NULL) {
106 return NULL;
109 notify->db = cluster_db_tmp_open(notify, lp_ctx, "notify", TDB_SEQNUM);
110 if (notify->db == NULL) {
111 talloc_free(notify);
112 return NULL;
115 notify->server = server;
116 notify->imessaging_ctx = imessaging_ctx;
117 notify->list = NULL;
118 notify->array = NULL;
119 notify->seqnum = dbwrap_get_seqnum(notify->db);
121 talloc_set_destructor(notify, notify_destructor);
123 /* register with the messaging subsystem for the notify
124 message type */
125 imessaging_register(notify->imessaging_ctx, notify,
126 MSG_PVFS_NOTIFY, notify_handler);
128 notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
130 return notify;
135 lock the notify db
137 static struct db_record *notify_lock(struct notify_context *notify)
139 TDB_DATA key = string_term_tdb_data(NOTIFY_KEY);
141 return dbwrap_fetch_locked(notify->db, notify, key);
144 static void notify_unlock(struct db_record *lock)
146 talloc_free(lock);
150 load the notify array
152 static NTSTATUS notify_load(struct notify_context *notify)
154 TDB_DATA dbuf;
155 DATA_BLOB blob;
156 enum ndr_err_code ndr_err;
157 int seqnum;
158 NTSTATUS status;
160 seqnum = dbwrap_get_seqnum(notify->db);
162 if (seqnum == notify->seqnum && notify->array != NULL) {
163 return NT_STATUS_OK;
166 notify->seqnum = seqnum;
168 talloc_free(notify->array);
169 notify->array = talloc_zero(notify, struct notify_array);
170 NT_STATUS_HAVE_NO_MEMORY(notify->array);
172 status = dbwrap_fetch_bystring(notify->db, notify, NOTIFY_KEY, &dbuf);
173 if (!NT_STATUS_IS_OK(status)) {
174 return NT_STATUS_OK;
177 blob.data = dbuf.dptr;
178 blob.length = dbuf.dsize;
180 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
181 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
182 talloc_free(dbuf.dptr);
183 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
184 return ndr_map_error2ntstatus(ndr_err);
187 return NT_STATUS_OK;
191 compare notify entries for sorting
193 static int notify_compare(const void *p1, const void *p2)
195 const struct notify_entry *e1 = p1, *e2 = p2;
196 return strcmp(e1->path, e2->path);
200 save the notify array
202 static NTSTATUS notify_save(struct notify_context *notify)
204 TDB_DATA dbuf;
205 DATA_BLOB blob;
206 enum ndr_err_code ndr_err;
207 TALLOC_CTX *tmp_ctx;
208 NTSTATUS status;
210 /* if possible, remove some depth arrays */
211 while (notify->array->num_depths > 0 &&
212 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
213 notify->array->num_depths--;
216 /* we might just be able to delete the record */
217 if (notify->array->num_depths == 0) {
218 return dbwrap_delete_bystring(notify->db, NOTIFY_KEY);
221 tmp_ctx = talloc_new(notify);
222 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
224 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
225 (ndr_push_flags_fn_t)ndr_push_notify_array);
226 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
227 talloc_free(tmp_ctx);
228 return ndr_map_error2ntstatus(ndr_err);
231 dbuf.dptr = blob.data;
232 dbuf.dsize = blob.length;
234 status = dbwrap_store_bystring(notify->db, NOTIFY_KEY, dbuf,
235 TDB_REPLACE);
236 talloc_free(tmp_ctx);
237 return status;
242 handle incoming notify messages
244 static void notify_handler(struct imessaging_context *msg_ctx,
245 void *private_data,
246 uint32_t msg_type,
247 struct server_id server_id,
248 size_t num_fds,
249 int *fds,
250 DATA_BLOB *data)
252 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
253 enum ndr_err_code ndr_err;
254 struct notify_event ev;
255 TALLOC_CTX *tmp_ctx = talloc_new(notify);
256 struct notify_list *listel;
258 if (num_fds != 0) {
259 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
260 return;
263 if (tmp_ctx == NULL) {
264 return;
267 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
268 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
269 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
270 talloc_free(tmp_ctx);
271 return;
274 for (listel=notify->list;listel;listel=listel->next) {
275 if (listel->private_data == ev.private_data) {
276 listel->callback(listel->private_data, &ev);
277 break;
281 talloc_free(tmp_ctx);
285 callback from sys_notify telling us about changes from the OS
287 static void sys_notify_callback(struct sys_notify_context *ctx,
288 void *ptr, struct notify_event *ev)
290 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
291 ev->private_data = listel;
292 listel->callback(listel->private_data, ev);
296 add an entry to the notify array
298 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
299 void *private_data, int depth)
301 int i;
302 struct notify_depth *d;
303 struct notify_entry *ee;
305 /* possibly expand the depths array */
306 if (depth >= notify->array->num_depths) {
307 d = talloc_realloc(notify->array, notify->array->depth,
308 struct notify_depth, depth+1);
309 NT_STATUS_HAVE_NO_MEMORY(d);
310 for (i=notify->array->num_depths;i<=depth;i++) {
311 ZERO_STRUCT(d[i]);
313 notify->array->depth = d;
314 notify->array->num_depths = depth+1;
316 d = &notify->array->depth[depth];
318 /* expand the entries array */
319 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
320 d->num_entries+1);
321 NT_STATUS_HAVE_NO_MEMORY(ee);
322 d->entries = ee;
324 d->entries[d->num_entries] = *e;
325 d->entries[d->num_entries].private_data = private_data;
326 d->entries[d->num_entries].server = notify->server;
327 d->entries[d->num_entries].path_len = strlen(e->path);
328 d->num_entries++;
330 d->max_mask |= e->filter;
331 d->max_mask_subdir |= e->subdir_filter;
333 TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
335 /* recalculate the maximum masks */
336 d->max_mask = 0;
337 d->max_mask_subdir = 0;
339 for (i=0;i<d->num_entries;i++) {
340 d->max_mask |= d->entries[i].filter;
341 d->max_mask_subdir |= d->entries[i].subdir_filter;
344 return notify_save(notify);
348 add a notify watch. This is called when a notify is first setup on a open
349 directory handle.
351 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
352 void (*callback)(void *, const struct notify_event *),
353 void *private_data)
355 struct notify_entry e = *e0;
356 NTSTATUS status;
357 char *tmp_path = NULL;
358 struct notify_list *listel;
359 size_t len;
360 int depth;
361 struct db_record *locked;
363 /* see if change notify is enabled at all */
364 if (notify == NULL) {
365 return NT_STATUS_NOT_IMPLEMENTED;
368 locked = notify_lock(notify);
369 if (!locked) {
370 return NT_STATUS_INTERNAL_DB_CORRUPTION;
373 status = notify_load(notify);
374 if (!NT_STATUS_IS_OK(status)) {
375 goto done;
378 /* cope with /. on the end of the path */
379 len = strlen(e.path);
380 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
381 tmp_path = talloc_strndup(notify, e.path, len-2);
382 if (tmp_path == NULL) {
383 status = NT_STATUS_NO_MEMORY;
384 goto done;
386 e.path = tmp_path;
389 depth = count_chars(e.path, '/');
391 listel = talloc_zero(notify, struct notify_list);
392 if (listel == NULL) {
393 status = NT_STATUS_NO_MEMORY;
394 goto done;
397 listel->private_data = private_data;
398 listel->callback = callback;
399 listel->depth = depth;
400 DLIST_ADD(notify->list, listel);
402 /* ignore failures from sys_notify */
403 if (notify->sys_notify_ctx != NULL) {
405 this call will modify e.filter and e.subdir_filter
406 to remove bits handled by the backend
408 status = sys_notify_watch(notify->sys_notify_ctx, &e,
409 sys_notify_callback, listel,
410 &listel->sys_notify_handle);
411 if (NT_STATUS_IS_OK(status)) {
412 talloc_steal(listel, listel->sys_notify_handle);
416 /* if the system notify handler couldn't handle some of the
417 filter bits, or couldn't handle a request for recursion
418 then we need to install it in the array used for the
419 intra-samba notify handling */
420 if (e.filter != 0 || e.subdir_filter != 0) {
421 status = notify_add_array(notify, &e, private_data, depth);
424 done:
425 notify_unlock(locked);
426 talloc_free(tmp_path);
428 return status;
432 remove a notify watch. Called when the directory handle is closed
434 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
436 NTSTATUS status;
437 struct notify_list *listel;
438 int i, depth;
439 struct notify_depth *d;
440 struct db_record *locked;
442 /* see if change notify is enabled at all */
443 if (notify == NULL) {
444 return NT_STATUS_NOT_IMPLEMENTED;
447 for (listel=notify->list;listel;listel=listel->next) {
448 if (listel->private_data == private_data) {
449 DLIST_REMOVE(notify->list, listel);
450 break;
453 if (listel == NULL) {
454 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
457 depth = listel->depth;
459 talloc_free(listel);
461 locked = notify_lock(notify);
462 if (!locked) {
463 return NT_STATUS_INTERNAL_DB_CORRUPTION;
466 status = notify_load(notify);
467 if (!NT_STATUS_IS_OK(status)) {
468 notify_unlock(locked);
469 return status;
472 if (depth >= notify->array->num_depths) {
473 notify_unlock(locked);
474 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
477 /* we only have to search at the depth of this element */
478 d = &notify->array->depth[depth];
480 for (i=0;i<d->num_entries;i++) {
481 if (private_data == d->entries[i].private_data &&
482 cluster_id_equal(&notify->server, &d->entries[i].server)) {
483 break;
486 if (i == d->num_entries) {
487 notify_unlock(locked);
488 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
491 if (i < d->num_entries-1) {
492 memmove(&d->entries[i], &d->entries[i+1],
493 sizeof(d->entries[i])*(d->num_entries-(i+1)));
495 d->num_entries--;
497 status = notify_save(notify);
499 notify_unlock(locked);
501 return status;
505 remove all notify watches for this messaging server
507 static NTSTATUS notify_remove_all(struct notify_context *notify)
509 NTSTATUS status;
510 int i, depth, del_count=0;
511 struct db_record *locked;
513 if (notify->list == NULL) {
514 return NT_STATUS_OK;
517 locked = notify_lock(notify);
518 if (!locked) {
519 return NT_STATUS_INTERNAL_DB_CORRUPTION;
522 status = notify_load(notify);
523 if (!NT_STATUS_IS_OK(status)) {
524 notify_unlock(locked);
525 return status;
528 /* we have to search for all entries across all depths, looking for matches
529 for our server id */
530 for (depth=0;depth<notify->array->num_depths;depth++) {
531 struct notify_depth *d = &notify->array->depth[depth];
532 for (i=0;i<d->num_entries;i++) {
533 if (cluster_id_equal(&notify->server, &d->entries[i].server)) {
534 if (i < d->num_entries-1) {
535 memmove(&d->entries[i], &d->entries[i+1],
536 sizeof(d->entries[i])*(d->num_entries-(i+1)));
538 i--;
539 d->num_entries--;
540 del_count++;
545 if (del_count > 0) {
546 status = notify_save(notify);
549 notify_unlock(locked);
551 return status;
556 send a notify message to another messaging server
558 static void notify_send(struct notify_context *notify, struct notify_entry *e,
559 const char *path, uint32_t action)
561 struct notify_event ev;
562 DATA_BLOB data;
563 NTSTATUS status;
564 enum ndr_err_code ndr_err;
565 TALLOC_CTX *tmp_ctx;
567 ev.action = action;
568 ev.dir = discard_const_p(char, "");
569 ev.path = path;
570 ev.private_data = e->private_data;
572 tmp_ctx = talloc_new(notify);
574 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev, (ndr_push_flags_fn_t)ndr_push_notify_event);
575 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
576 talloc_free(tmp_ctx);
577 return;
580 status = imessaging_send(notify->imessaging_ctx, e->server,
581 MSG_PVFS_NOTIFY, &data);
582 if (!NT_STATUS_IS_OK(status)) {
583 talloc_free(tmp_ctx);
584 return;
587 talloc_free(tmp_ctx);
592 trigger a notify message for anyone waiting on a matching event
594 This function is called a lot, and needs to be very fast. The unusual data structure
595 and traversal is designed to be fast in the average case, even for large numbers of
596 notifies
598 void notify_trigger(struct notify_context *notify,
599 uint32_t action, uint32_t filter, const char *path)
601 NTSTATUS status;
602 int depth;
603 const char *p, *next_p;
605 /* see if change notify is enabled at all */
606 if (notify == NULL) {
607 return;
610 status = notify_load(notify);
611 if (!NT_STATUS_IS_OK(status)) {
612 return;
615 /* loop along the given path, working with each directory depth separately */
616 for (depth=0,p=path;
617 p && depth < notify->array->num_depths;
618 p=next_p,depth++) {
619 int p_len = p - path;
620 int min_i, max_i, i;
621 struct notify_depth *d = &notify->array->depth[depth];
622 next_p = strchr(p+1, '/');
624 /* see if there are any entries at this depth */
625 if (d->num_entries == 0) continue;
627 /* try to skip based on the maximum mask. If next_p is
628 NULL then we know it will be a 'this directory'
629 match, otherwise it must be a subdir match */
630 if (next_p != NULL) {
631 if (0 == (filter & d->max_mask_subdir)) {
632 continue;
634 } else {
635 if (0 == (filter & d->max_mask)) {
636 continue;
640 /* we know there is an entry here worth looking
641 for. Use a bisection search to find the first entry
642 with a matching path */
643 min_i = 0;
644 max_i = d->num_entries-1;
646 while (min_i < max_i) {
647 struct notify_entry *e;
648 int cmp;
649 i = (min_i+max_i)/2;
650 e = &d->entries[i];
651 cmp = strncmp(path, e->path, p_len);
652 if (cmp == 0) {
653 if (p_len == e->path_len) {
654 max_i = i;
655 } else {
656 max_i = i-1;
658 } else if (cmp < 0) {
659 max_i = i-1;
660 } else {
661 min_i = i+1;
665 if (min_i != max_i) {
666 /* none match */
667 continue;
670 /* we now know that the entries start at min_i */
671 for (i=min_i;i<d->num_entries;i++) {
672 struct notify_entry *e = &d->entries[i];
673 if (p_len != e->path_len ||
674 strncmp(path, e->path, p_len) != 0) break;
675 if (next_p != NULL) {
676 if (0 == (filter & e->subdir_filter)) {
677 continue;
679 } else {
680 if (0 == (filter & e->filter)) {
681 continue;
684 notify_send(notify, e, path + e->path_len + 1, action);