s4 dns: Move record lookup to dns_utils.c
[Samba/gebeck_regimport.git] / source3 / smbd / notify_internal.c
blob484a31c5be8a287f051781c7a33ea08839149434
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 "librpc/gen_ndr/ndr_notify.h"
29 #include "dbwrap/dbwrap.h"
30 #include "dbwrap/dbwrap_open.h"
31 #include "smbd/smbd.h"
32 #include "messages.h"
33 #include "lib/util/tdb_wrap.h"
34 #include "util_tdb.h"
35 #include "lib/param/param.h"
37 struct notify_context {
38 struct db_context *db_recursive;
39 struct db_context *db_onelevel;
40 struct server_id server;
41 struct messaging_context *messaging_ctx;
42 struct notify_list *list;
43 struct notify_array *array;
44 int seqnum;
45 struct sys_notify_context *sys_notify_ctx;
46 TDB_DATA key;
50 struct notify_list {
51 struct notify_list *next, *prev;
52 void *private_data;
53 void (*callback)(void *, const struct notify_event *);
54 void *sys_notify_handle;
55 int depth;
58 #define NOTIFY_KEY "notify array"
60 #define NOTIFY_ENABLE "notify:enable"
61 #define NOTIFY_ENABLE_DEFAULT True
63 static NTSTATUS notify_remove_all(struct notify_context *notify,
64 const struct server_id *server);
65 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
66 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
69 destroy the notify context
71 static int notify_destructor(struct notify_context *notify)
73 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
75 if (notify->list != NULL) {
76 notify_remove_all(notify, &notify->server);
79 return 0;
83 Open up the notify.tdb database. You should close it down using
84 talloc_free(). We need the messaging_ctx to allow for notifications
85 via internal messages
87 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
88 struct messaging_context *messaging_ctx,
89 struct event_context *ev,
90 connection_struct *conn)
92 struct notify_context *notify;
94 if (!lp_change_notify(conn->params)) {
95 return NULL;
98 notify = talloc(mem_ctx, struct notify_context);
99 if (notify == NULL) {
100 return NULL;
103 notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
104 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
105 O_RDWR|O_CREAT, 0644);
106 if (notify->db_recursive == NULL) {
107 talloc_free(notify);
108 return NULL;
111 notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
112 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
113 O_RDWR|O_CREAT, 0644);
114 if (notify->db_onelevel == NULL) {
115 talloc_free(notify);
116 return NULL;
119 notify->server = server;
120 notify->messaging_ctx = messaging_ctx;
121 notify->list = NULL;
122 notify->array = NULL;
123 notify->seqnum = dbwrap_get_seqnum(notify->db_recursive);
124 notify->key = string_term_tdb_data(NOTIFY_KEY);
126 talloc_set_destructor(notify, notify_destructor);
128 /* register with the messaging subsystem for the notify
129 message type */
130 messaging_register(notify->messaging_ctx, notify,
131 MSG_PVFS_NOTIFY, notify_handler);
133 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
135 return notify;
138 bool notify_internal_parent_init(TALLOC_CTX *mem_ctx)
140 struct tdb_wrap *db1, *db2;
141 struct loadparm_context *lp_ctx;
143 if (lp_clustering()) {
144 return true;
147 lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_context());
148 if (lp_ctx == NULL) {
149 DEBUG(0, ("loadparm_init_s3 failed\n"));
150 return false;
153 * Open the tdbs in the parent process (smbd) so that our
154 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
155 * work.
158 db1 = tdb_wrap_open(mem_ctx, lock_path("notify.tdb"),
159 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
160 O_RDWR|O_CREAT, 0644, lp_ctx);
161 if (db1 == NULL) {
162 talloc_unlink(mem_ctx, lp_ctx);
163 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno)));
164 return false;
166 db2 = tdb_wrap_open(mem_ctx, lock_path("notify_onelevel.tdb"),
167 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT, 0644, lp_ctx);
168 talloc_unlink(mem_ctx, lp_ctx);
169 if (db2 == NULL) {
170 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
171 strerror(errno)));
172 TALLOC_FREE(db1);
173 return false;
175 return true;
179 lock and fetch the record
181 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
183 *rec = dbwrap_fetch_locked(notify->db_recursive, notify, notify->key);
184 if (*rec == NULL) {
185 return NT_STATUS_INTERNAL_DB_CORRUPTION;
187 return NT_STATUS_OK;
191 load the notify array
193 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
195 TDB_DATA dbuf;
196 DATA_BLOB blob;
197 NTSTATUS status;
198 int seqnum;
200 seqnum = dbwrap_get_seqnum(notify->db_recursive);
202 if (seqnum == notify->seqnum && notify->array != NULL) {
203 return NT_STATUS_OK;
206 notify->seqnum = seqnum;
208 talloc_free(notify->array);
209 notify->array = talloc_zero(notify, struct notify_array);
210 NT_STATUS_HAVE_NO_MEMORY(notify->array);
212 if (!rec) {
213 status = dbwrap_fetch(notify->db_recursive, notify,
214 notify->key, &dbuf);
215 if (!NT_STATUS_IS_OK(status)) {
216 return NT_STATUS_INTERNAL_DB_CORRUPTION;
218 } else {
219 dbuf = dbwrap_record_get_value(rec);
222 blob.data = (uint8 *)dbuf.dptr;
223 blob.length = dbuf.dsize;
225 status = NT_STATUS_OK;
226 if (blob.length > 0) {
227 enum ndr_err_code ndr_err;
228 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
229 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
230 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
231 /* 1. log that we got a corrupt notify_array
232 * 2. clear the variable the garbage was stored into to not trip
233 * over it next time this method is entered with the same seqnum
234 * 3. delete it from the database */
235 DEBUG(2, ("notify_array is corrupt, discarding it\n"));
237 ZERO_STRUCTP(notify->array);
238 if (rec != NULL) {
239 dbwrap_record_delete(rec);
242 } else {
243 if (DEBUGLEVEL >= 10) {
244 DEBUG(10, ("notify_load:\n"));
245 NDR_PRINT_DEBUG(notify_array, notify->array);
251 if (!rec) {
252 talloc_free(dbuf.dptr);
255 return status;
259 compare notify entries for sorting
261 static int notify_compare(const struct notify_entry *e1, const struct notify_entry *e2)
263 return strcmp(e1->path, e2->path);
267 save the notify array
269 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
271 TDB_DATA dbuf;
272 DATA_BLOB blob;
273 NTSTATUS status;
274 enum ndr_err_code ndr_err;
275 TALLOC_CTX *tmp_ctx;
277 /* if possible, remove some depth arrays */
278 while (notify->array->num_depths > 0 &&
279 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
280 notify->array->num_depths--;
283 /* we might just be able to delete the record */
284 if (notify->array->num_depths == 0) {
285 return dbwrap_record_delete(rec);
288 tmp_ctx = talloc_new(notify);
289 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
291 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
292 (ndr_push_flags_fn_t)ndr_push_notify_array);
293 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
294 talloc_free(tmp_ctx);
295 return ndr_map_error2ntstatus(ndr_err);
298 if (DEBUGLEVEL >= 10) {
299 DEBUG(10, ("notify_save:\n"));
300 NDR_PRINT_DEBUG(notify_array, notify->array);
303 dbuf.dptr = blob.data;
304 dbuf.dsize = blob.length;
306 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
307 talloc_free(tmp_ctx);
309 return status;
314 handle incoming notify messages
316 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
317 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
319 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
320 enum ndr_err_code ndr_err;
321 struct notify_event ev;
322 TALLOC_CTX *tmp_ctx = talloc_new(notify);
323 struct notify_list *listel;
325 if (tmp_ctx == NULL) {
326 return;
329 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
330 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
331 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
332 talloc_free(tmp_ctx);
333 return;
336 for (listel=notify->list;listel;listel=listel->next) {
337 if (listel->private_data == ev.private_data) {
338 listel->callback(listel->private_data, &ev);
339 break;
343 talloc_free(tmp_ctx);
347 callback from sys_notify telling us about changes from the OS
349 static void sys_notify_callback(struct sys_notify_context *ctx,
350 void *ptr, struct notify_event *ev)
352 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
353 ev->private_data = listel;
354 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
355 ev->action, ev->path));
356 listel->callback(listel->private_data, ev);
360 add an entry to the notify array
362 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
363 struct notify_entry *e,
364 void *private_data, int depth)
366 int i;
367 struct notify_depth *d;
368 struct notify_entry *ee;
370 /* possibly expand the depths array */
371 if (depth >= notify->array->num_depths) {
372 d = talloc_realloc(notify->array, notify->array->depth,
373 struct notify_depth, depth+1);
374 NT_STATUS_HAVE_NO_MEMORY(d);
375 for (i=notify->array->num_depths;i<=depth;i++) {
376 ZERO_STRUCT(d[i]);
378 notify->array->depth = d;
379 notify->array->num_depths = depth+1;
381 d = &notify->array->depth[depth];
383 /* expand the entries array */
384 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
385 d->num_entries+1);
386 NT_STATUS_HAVE_NO_MEMORY(ee);
387 d->entries = ee;
389 d->entries[d->num_entries] = *e;
390 d->entries[d->num_entries].private_data = private_data;
391 d->entries[d->num_entries].server = notify->server;
392 d->entries[d->num_entries].path_len = strlen(e->path);
393 d->num_entries++;
395 d->max_mask |= e->filter;
396 d->max_mask_subdir |= e->subdir_filter;
398 TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
400 /* recalculate the maximum masks */
401 d->max_mask = 0;
402 d->max_mask_subdir = 0;
404 for (i=0;i<d->num_entries;i++) {
405 d->max_mask |= d->entries[i].filter;
406 d->max_mask_subdir |= d->entries[i].subdir_filter;
409 return notify_save(notify, rec);
413 Add a non-recursive watch
416 static void notify_add_onelevel(struct notify_context *notify,
417 struct notify_entry *e, void *private_data)
419 struct notify_entry_array *array;
420 struct db_record *rec;
421 DATA_BLOB blob;
422 TDB_DATA dbuf;
423 TDB_DATA value;
424 enum ndr_err_code ndr_err;
425 NTSTATUS status;
427 array = talloc_zero(talloc_tos(), struct notify_entry_array);
428 if (array == NULL) {
429 return;
432 rec = dbwrap_fetch_locked(notify->db_onelevel, talloc_tos(),
433 make_tdb_data((uint8_t *)&e->dir_id,
434 sizeof(e->dir_id)));
435 if (rec == NULL) {
436 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
437 "\n", file_id_string_tos(&e->dir_id)));
438 TALLOC_FREE(array);
439 return;
442 value = dbwrap_record_get_value(rec);
443 blob.data = (uint8_t *)value.dptr;
444 blob.length = value.dsize;
446 if (blob.length > 0) {
447 ndr_err = ndr_pull_struct_blob(&blob, array, array,
448 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
449 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
450 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
451 ndr_errstr(ndr_err)));
452 TALLOC_FREE(array);
453 return;
455 if (DEBUGLEVEL >= 10) {
456 DEBUG(10, ("notify_add_onelevel:\n"));
457 NDR_PRINT_DEBUG(notify_entry_array, array);
461 array->entries = talloc_realloc(array, array->entries,
462 struct notify_entry,
463 array->num_entries+1);
464 if (array->entries == NULL) {
465 TALLOC_FREE(array);
466 return;
468 array->entries[array->num_entries] = *e;
469 array->entries[array->num_entries].private_data = private_data;
470 array->entries[array->num_entries].server = notify->server;
471 array->num_entries += 1;
473 ndr_err = ndr_push_struct_blob(&blob, rec, array,
474 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
475 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
476 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
477 ndr_errstr(ndr_err)));
478 TALLOC_FREE(array);
479 return;
482 if (DEBUGLEVEL >= 10) {
483 DEBUG(10, ("notify_add_onelevel:\n"));
484 NDR_PRINT_DEBUG(notify_entry_array, array);
487 dbuf.dptr = blob.data;
488 dbuf.dsize = blob.length;
490 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
491 TALLOC_FREE(array);
492 if (!NT_STATUS_IS_OK(status)) {
493 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
494 nt_errstr(status)));
495 return;
497 e->filter = 0;
498 return;
503 add a notify watch. This is called when a notify is first setup on a open
504 directory handle.
506 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
507 void (*callback)(void *, const struct notify_event *),
508 void *private_data)
510 struct notify_entry e = *e0;
511 NTSTATUS status;
512 char *tmp_path = NULL;
513 struct notify_list *listel;
514 size_t len;
515 int depth;
516 struct db_record *rec;
518 /* see if change notify is enabled at all */
519 if (notify == NULL) {
520 return NT_STATUS_NOT_IMPLEMENTED;
523 status = notify_fetch_locked(notify, &rec);
524 NT_STATUS_NOT_OK_RETURN(status);
526 status = notify_load(notify, rec);
527 if (!NT_STATUS_IS_OK(status)) {
528 talloc_free(rec);
529 return status;
532 /* cope with /. on the end of the path */
533 len = strlen(e.path);
534 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
535 tmp_path = talloc_strndup(notify, e.path, len-2);
536 if (tmp_path == NULL) {
537 status = NT_STATUS_NO_MEMORY;
538 goto done;
540 e.path = tmp_path;
543 depth = count_chars(e.path, '/');
545 listel = talloc_zero(notify, struct notify_list);
546 if (listel == NULL) {
547 status = NT_STATUS_NO_MEMORY;
548 goto done;
551 listel->private_data = private_data;
552 listel->callback = callback;
553 listel->depth = depth;
554 DLIST_ADD(notify->list, listel);
556 /* ignore failures from sys_notify */
557 if (notify->sys_notify_ctx != NULL) {
559 this call will modify e.filter and e.subdir_filter
560 to remove bits handled by the backend
562 status = sys_notify_watch(notify->sys_notify_ctx, &e,
563 sys_notify_callback, listel,
564 &listel->sys_notify_handle);
565 if (NT_STATUS_IS_OK(status)) {
566 talloc_steal(listel, listel->sys_notify_handle);
570 if (e.filter != 0) {
571 notify_add_onelevel(notify, &e, private_data);
572 status = NT_STATUS_OK;
575 /* if the system notify handler couldn't handle some of the
576 filter bits, or couldn't handle a request for recursion
577 then we need to install it in the array used for the
578 intra-samba notify handling */
579 if (e.filter != 0 || e.subdir_filter != 0) {
580 status = notify_add_array(notify, rec, &e, private_data, depth);
583 done:
584 talloc_free(rec);
585 talloc_free(tmp_path);
587 return status;
590 NTSTATUS notify_remove_onelevel(struct notify_context *notify,
591 const struct file_id *fid,
592 void *private_data)
594 struct notify_entry_array *array;
595 struct db_record *rec;
596 DATA_BLOB blob;
597 TDB_DATA dbuf;
598 TDB_DATA value;
599 enum ndr_err_code ndr_err;
600 NTSTATUS status;
601 int i;
603 if (notify == NULL) {
604 return NT_STATUS_NOT_IMPLEMENTED;
607 array = talloc_zero(talloc_tos(), struct notify_entry_array);
608 if (array == NULL) {
609 return NT_STATUS_NO_MEMORY;
612 rec = dbwrap_fetch_locked(
613 notify->db_onelevel, array,
614 make_tdb_data((const uint8_t *)fid, sizeof(*fid)));
615 if (rec == NULL) {
616 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
617 "\n", file_id_string_tos(fid)));
618 TALLOC_FREE(array);
619 return NT_STATUS_INTERNAL_DB_CORRUPTION;
622 value = dbwrap_record_get_value(rec);
623 blob.data = (uint8_t *)value.dptr;
624 blob.length = value.dsize;
626 if (blob.length > 0) {
627 ndr_err = ndr_pull_struct_blob(&blob, array, array,
628 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
629 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
630 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
631 ndr_errstr(ndr_err)));
632 TALLOC_FREE(array);
633 return ndr_map_error2ntstatus(ndr_err);
635 if (DEBUGLEVEL >= 10) {
636 DEBUG(10, ("notify_remove_onelevel:\n"));
637 NDR_PRINT_DEBUG(notify_entry_array, array);
641 for (i=0; i<array->num_entries; i++) {
642 if ((private_data == array->entries[i].private_data) &&
643 cluster_id_equal(&notify->server,
644 &array->entries[i].server)) {
645 break;
649 if (i == array->num_entries) {
650 TALLOC_FREE(array);
651 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
654 array->entries[i] = array->entries[array->num_entries-1];
655 array->num_entries -= 1;
657 if (array->num_entries == 0) {
658 dbwrap_record_delete(rec);
659 TALLOC_FREE(array);
660 return NT_STATUS_OK;
663 ndr_err = ndr_push_struct_blob(&blob, rec, array,
664 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
665 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
666 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
667 ndr_errstr(ndr_err)));
668 TALLOC_FREE(array);
669 return ndr_map_error2ntstatus(ndr_err);
672 if (DEBUGLEVEL >= 10) {
673 DEBUG(10, ("notify_add_onelevel:\n"));
674 NDR_PRINT_DEBUG(notify_entry_array, array);
677 dbuf.dptr = blob.data;
678 dbuf.dsize = blob.length;
680 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
681 TALLOC_FREE(array);
682 if (!NT_STATUS_IS_OK(status)) {
683 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
684 nt_errstr(status)));
685 return status;
687 return NT_STATUS_OK;
691 remove a notify watch. Called when the directory handle is closed
693 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
695 NTSTATUS status;
696 struct notify_list *listel;
697 int i, depth;
698 struct notify_depth *d;
699 struct db_record *rec;
701 /* see if change notify is enabled at all */
702 if (notify == NULL) {
703 return NT_STATUS_NOT_IMPLEMENTED;
706 for (listel=notify->list;listel;listel=listel->next) {
707 if (listel->private_data == private_data) {
708 DLIST_REMOVE(notify->list, listel);
709 break;
712 if (listel == NULL) {
713 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
716 depth = listel->depth;
718 talloc_free(listel);
720 status = notify_fetch_locked(notify, &rec);
721 NT_STATUS_NOT_OK_RETURN(status);
723 status = notify_load(notify, rec);
724 if (!NT_STATUS_IS_OK(status)) {
725 talloc_free(rec);
726 return status;
729 if (depth >= notify->array->num_depths) {
730 talloc_free(rec);
731 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
734 /* we only have to search at the depth of this element */
735 d = &notify->array->depth[depth];
737 for (i=0;i<d->num_entries;i++) {
738 if (private_data == d->entries[i].private_data &&
739 cluster_id_equal(&notify->server, &d->entries[i].server)) {
740 break;
743 if (i == d->num_entries) {
744 talloc_free(rec);
745 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
748 if (i < d->num_entries-1) {
749 memmove(&d->entries[i], &d->entries[i+1],
750 sizeof(d->entries[i])*(d->num_entries-(i+1)));
752 d->num_entries--;
754 status = notify_save(notify, rec);
756 talloc_free(rec);
758 return status;
762 remove all notify watches for a messaging server
764 static NTSTATUS notify_remove_all(struct notify_context *notify,
765 const struct server_id *server)
767 NTSTATUS status;
768 int i, depth, del_count=0;
769 struct db_record *rec;
771 status = notify_fetch_locked(notify, &rec);
772 NT_STATUS_NOT_OK_RETURN(status);
774 status = notify_load(notify, rec);
775 if (!NT_STATUS_IS_OK(status)) {
776 talloc_free(rec);
777 return status;
780 /* we have to search for all entries across all depths, looking for matches
781 for the server id */
782 for (depth=0;depth<notify->array->num_depths;depth++) {
783 struct notify_depth *d = &notify->array->depth[depth];
784 for (i=0;i<d->num_entries;i++) {
785 if (cluster_id_equal(server, &d->entries[i].server)) {
786 if (i < d->num_entries-1) {
787 memmove(&d->entries[i], &d->entries[i+1],
788 sizeof(d->entries[i])*(d->num_entries-(i+1)));
790 i--;
791 d->num_entries--;
792 del_count++;
797 if (del_count > 0) {
798 status = notify_save(notify, rec);
801 talloc_free(rec);
803 return status;
808 send a notify message to another messaging server
810 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
811 const char *path, uint32_t action)
813 struct notify_event ev;
814 DATA_BLOB data;
815 NTSTATUS status;
816 enum ndr_err_code ndr_err;
817 TALLOC_CTX *tmp_ctx;
819 ev.action = action;
820 ev.path = path;
821 ev.private_data = e->private_data;
823 tmp_ctx = talloc_new(notify);
825 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev,
826 (ndr_push_flags_fn_t)ndr_push_notify_event);
827 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
828 talloc_free(tmp_ctx);
829 return ndr_map_error2ntstatus(ndr_err);
832 status = messaging_send(notify->messaging_ctx, e->server,
833 MSG_PVFS_NOTIFY, &data);
834 talloc_free(tmp_ctx);
835 return status;
838 void notify_onelevel(struct notify_context *notify, uint32_t action,
839 uint32_t filter, struct file_id fid, const char *name)
841 struct notify_entry_array *array;
842 TDB_DATA dbuf;
843 DATA_BLOB blob;
844 bool have_dead_entries = false;
845 int i;
846 NTSTATUS status;
848 if (notify == NULL) {
849 return;
852 array = talloc_zero(talloc_tos(), struct notify_entry_array);
853 if (array == NULL) {
854 return;
857 status = dbwrap_fetch(notify->db_onelevel, array,
858 make_tdb_data((uint8_t *)&fid, sizeof(fid)),
859 &dbuf);
860 if (!NT_STATUS_IS_OK(status)) {
861 TALLOC_FREE(array);
862 return;
865 blob.data = (uint8 *)dbuf.dptr;
866 blob.length = dbuf.dsize;
868 if (blob.length > 0) {
869 enum ndr_err_code ndr_err;
870 ndr_err = ndr_pull_struct_blob(&blob, array, array,
871 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
872 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
873 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
874 ndr_errstr(ndr_err)));
875 TALLOC_FREE(array);
876 return;
878 if (DEBUGLEVEL >= 10) {
879 DEBUG(10, ("notify_onelevel:\n"));
880 NDR_PRINT_DEBUG(notify_entry_array, array);
884 for (i=0; i<array->num_entries; i++) {
885 struct notify_entry *e = &array->entries[i];
887 if ((e->filter & filter) != 0) {
888 status = notify_send(notify, e, name, action);
889 if (NT_STATUS_EQUAL(
890 status, NT_STATUS_INVALID_HANDLE)) {
892 * Mark the entry as dead. All entries have a
893 * path set. The marker used here is setting
894 * that to NULL.
896 e->path = NULL;
897 have_dead_entries = true;
902 if (!have_dead_entries) {
903 TALLOC_FREE(array);
904 return;
907 for (i=0; i<array->num_entries; i++) {
908 struct notify_entry *e = &array->entries[i];
909 if (e->path != NULL) {
910 continue;
912 DEBUG(10, ("Deleting notify entries for process %s because "
913 "it's gone\n", procid_str_static(&e->server)));
915 * Potential TODO: This might need optimizing,
916 * notify_remove_onelevel() does a fetch_locked() operation at
917 * every call. But this would only matter if a process with
918 * MANY notifies has died without shutting down properly.
920 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
923 TALLOC_FREE(array);
924 return;
928 trigger a notify message for anyone waiting on a matching event
930 This function is called a lot, and needs to be very fast. The unusual data structure
931 and traversal is designed to be fast in the average case, even for large numbers of
932 notifies
934 void notify_trigger(struct notify_context *notify,
935 uint32_t action, uint32_t filter, const char *path)
937 NTSTATUS status;
938 int depth;
939 const char *p, *next_p;
941 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
942 "path=%s\n", (unsigned)action, (unsigned)filter, path));
944 /* see if change notify is enabled at all */
945 if (notify == NULL) {
946 return;
949 again:
950 status = notify_load(notify, NULL);
951 if (!NT_STATUS_IS_OK(status)) {
952 return;
955 /* loop along the given path, working with each directory depth separately */
956 for (depth=0,p=path;
957 p && depth < notify->array->num_depths;
958 p=next_p,depth++) {
959 int p_len = p - path;
960 int min_i, max_i, i;
961 struct notify_depth *d = &notify->array->depth[depth];
962 next_p = strchr(p+1, '/');
964 /* see if there are any entries at this depth */
965 if (d->num_entries == 0) continue;
967 /* try to skip based on the maximum mask. If next_p is
968 NULL then we know it will be a 'this directory'
969 match, otherwise it must be a subdir match */
970 if (next_p != NULL) {
971 if (0 == (filter & d->max_mask_subdir)) {
972 continue;
974 } else {
975 if (0 == (filter & d->max_mask)) {
976 continue;
980 /* we know there is an entry here worth looking
981 for. Use a bisection search to find the first entry
982 with a matching path */
983 min_i = 0;
984 max_i = d->num_entries-1;
986 while (min_i < max_i) {
987 struct notify_entry *e;
988 int cmp;
989 i = (min_i+max_i)/2;
990 e = &d->entries[i];
991 cmp = strncmp(path, e->path, p_len);
992 if (cmp == 0) {
993 if (p_len == e->path_len) {
994 max_i = i;
995 } else {
996 max_i = i-1;
998 } else if (cmp < 0) {
999 max_i = i-1;
1000 } else {
1001 min_i = i+1;
1005 if (min_i != max_i) {
1006 /* none match */
1007 continue;
1010 /* we now know that the entries start at min_i */
1011 for (i=min_i;i<d->num_entries;i++) {
1012 struct notify_entry *e = &d->entries[i];
1013 if (p_len != e->path_len ||
1014 strncmp(path, e->path, p_len) != 0) break;
1015 if (next_p != NULL) {
1016 if (0 == (filter & e->subdir_filter)) {
1017 continue;
1019 } else {
1020 if (0 == (filter & e->filter)) {
1021 continue;
1024 status = notify_send(notify, e, path + e->path_len + 1,
1025 action);
1027 if (NT_STATUS_EQUAL(
1028 status, NT_STATUS_INVALID_HANDLE)) {
1029 struct server_id server = e->server;
1031 DEBUG(10, ("Deleting notify entries for "
1032 "process %s because it's gone\n",
1033 procid_str_static(&e->server)));
1034 notify_remove_all(notify, &server);
1035 goto again;