dnsp: Parse TXT records
[Samba/gebeck_regimport.git] / source3 / smbd / notify_internal.c
blobacb438b1189d403c43e278e758295a7509831309
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 "librpc/gen_ndr/ndr_notify.h"
28 #include "librpc/gen_ndr/messaging.h"
29 #include "dbwrap.h"
31 struct notify_context {
32 struct db_context *db_recursive;
33 struct db_context *db_onelevel;
34 struct server_id server;
35 struct messaging_context *messaging_ctx;
36 struct notify_list *list;
37 struct notify_array *array;
38 int seqnum;
39 struct sys_notify_context *sys_notify_ctx;
40 TDB_DATA key;
44 struct notify_list {
45 struct notify_list *next, *prev;
46 void *private_data;
47 void (*callback)(void *, const struct notify_event *);
48 void *sys_notify_handle;
49 int depth;
52 #define NOTIFY_KEY "notify array"
54 #define NOTIFY_ENABLE "notify:enable"
55 #define NOTIFY_ENABLE_DEFAULT True
57 static NTSTATUS notify_remove_all(struct notify_context *notify,
58 const struct server_id *server);
59 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
60 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
63 destroy the notify context
65 static int notify_destructor(struct notify_context *notify)
67 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
69 if (notify->list != NULL) {
70 notify_remove_all(notify, &notify->server);
73 return 0;
77 Open up the notify.tdb database. You should close it down using
78 talloc_free(). We need the messaging_ctx to allow for notifications
79 via internal messages
81 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
82 struct messaging_context *messaging_ctx,
83 struct event_context *ev,
84 connection_struct *conn)
86 struct notify_context *notify;
88 if (!lp_change_notify(conn->params)) {
89 return NULL;
92 notify = talloc(mem_ctx, struct notify_context);
93 if (notify == NULL) {
94 return NULL;
97 notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
98 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
99 O_RDWR|O_CREAT, 0644);
100 if (notify->db_recursive == NULL) {
101 talloc_free(notify);
102 return NULL;
105 notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
106 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
107 O_RDWR|O_CREAT, 0644);
108 if (notify->db_onelevel == NULL) {
109 talloc_free(notify);
110 return NULL;
113 notify->server = server;
114 notify->messaging_ctx = messaging_ctx;
115 notify->list = NULL;
116 notify->array = NULL;
117 notify->seqnum = notify->db_recursive->get_seqnum(
118 notify->db_recursive);
119 notify->key = string_term_tdb_data(NOTIFY_KEY);
121 talloc_set_destructor(notify, notify_destructor);
123 /* register with the messaging subsystem for the notify
124 message type */
125 messaging_register(notify->messaging_ctx, notify,
126 MSG_PVFS_NOTIFY, notify_handler);
128 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
130 return notify;
133 bool notify_internal_parent_init(TALLOC_CTX *mem_ctx)
135 struct tdb_wrap *db1, *db2;
137 if (lp_clustering()) {
138 return true;
142 * Open the tdbs in the parent process (smbd) so that our
143 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
144 * work.
147 db1 = tdb_wrap_open(mem_ctx, lock_path("notify.tdb"),
148 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
149 O_RDWR|O_CREAT, 0644);
150 if (db1 == NULL) {
151 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno)));
152 return false;
154 db2 = tdb_wrap_open(mem_ctx, lock_path("notify_onelevel.tdb"),
155 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT, 0644);
156 if (db2 == NULL) {
157 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
158 strerror(errno)));
159 TALLOC_FREE(db1);
160 return false;
162 return true;
166 lock and fetch the record
168 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
170 *rec = notify->db_recursive->fetch_locked(notify->db_recursive,
171 notify, notify->key);
172 if (*rec == NULL) {
173 return NT_STATUS_INTERNAL_DB_CORRUPTION;
175 return NT_STATUS_OK;
179 load the notify array
181 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
183 TDB_DATA dbuf;
184 DATA_BLOB blob;
185 NTSTATUS status;
186 int seqnum;
188 seqnum = notify->db_recursive->get_seqnum(notify->db_recursive);
190 if (seqnum == notify->seqnum && notify->array != NULL) {
191 return NT_STATUS_OK;
194 notify->seqnum = seqnum;
196 talloc_free(notify->array);
197 notify->array = TALLOC_ZERO_P(notify, struct notify_array);
198 NT_STATUS_HAVE_NO_MEMORY(notify->array);
200 if (!rec) {
201 if (notify->db_recursive->fetch(notify->db_recursive, notify,
202 notify->key, &dbuf) != 0) {
203 return NT_STATUS_INTERNAL_DB_CORRUPTION;
205 } else {
206 dbuf = rec->value;
209 blob.data = (uint8 *)dbuf.dptr;
210 blob.length = dbuf.dsize;
212 status = NT_STATUS_OK;
213 if (blob.length > 0) {
214 enum ndr_err_code ndr_err;
215 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
216 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
217 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
218 /* 1. log that we got a corrupt notify_array
219 * 2. clear the variable the garbage was stored into to not trip
220 * over it next time this method is entered with the same seqnum
221 * 3. delete it from the database */
222 DEBUG(2, ("notify_array is corrupt, discarding it\n"));
224 ZERO_STRUCTP(notify->array);
225 if (rec != NULL) {
226 rec->delete_rec(rec);
229 } else {
230 if (DEBUGLEVEL >= 10) {
231 DEBUG(10, ("notify_load:\n"));
232 NDR_PRINT_DEBUG(notify_array, notify->array);
238 if (!rec) {
239 talloc_free(dbuf.dptr);
242 return status;
246 compare notify entries for sorting
248 static int notify_compare(const struct notify_entry *e1, const struct notify_entry *e2)
250 return strcmp(e1->path, e2->path);
254 save the notify array
256 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
258 TDB_DATA dbuf;
259 DATA_BLOB blob;
260 NTSTATUS status;
261 enum ndr_err_code ndr_err;
262 TALLOC_CTX *tmp_ctx;
264 /* if possible, remove some depth arrays */
265 while (notify->array->num_depths > 0 &&
266 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
267 notify->array->num_depths--;
270 /* we might just be able to delete the record */
271 if (notify->array->num_depths == 0) {
272 return rec->delete_rec(rec);
275 tmp_ctx = talloc_new(notify);
276 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
278 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
279 (ndr_push_flags_fn_t)ndr_push_notify_array);
280 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
281 talloc_free(tmp_ctx);
282 return ndr_map_error2ntstatus(ndr_err);
285 if (DEBUGLEVEL >= 10) {
286 DEBUG(10, ("notify_save:\n"));
287 NDR_PRINT_DEBUG(notify_array, notify->array);
290 dbuf.dptr = blob.data;
291 dbuf.dsize = blob.length;
293 status = rec->store(rec, dbuf, TDB_REPLACE);
294 talloc_free(tmp_ctx);
296 return status;
301 handle incoming notify messages
303 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
304 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
306 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
307 enum ndr_err_code ndr_err;
308 struct notify_event ev;
309 TALLOC_CTX *tmp_ctx = talloc_new(notify);
310 struct notify_list *listel;
312 if (tmp_ctx == NULL) {
313 return;
316 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
317 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
318 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
319 talloc_free(tmp_ctx);
320 return;
323 for (listel=notify->list;listel;listel=listel->next) {
324 if (listel->private_data == ev.private_data) {
325 listel->callback(listel->private_data, &ev);
326 break;
330 talloc_free(tmp_ctx);
334 callback from sys_notify telling us about changes from the OS
336 static void sys_notify_callback(struct sys_notify_context *ctx,
337 void *ptr, struct notify_event *ev)
339 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
340 ev->private_data = listel;
341 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
342 ev->action, ev->path));
343 listel->callback(listel->private_data, ev);
347 add an entry to the notify array
349 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
350 struct notify_entry *e,
351 void *private_data, int depth)
353 int i;
354 struct notify_depth *d;
355 struct notify_entry *ee;
357 /* possibly expand the depths array */
358 if (depth >= notify->array->num_depths) {
359 d = talloc_realloc(notify->array, notify->array->depth,
360 struct notify_depth, depth+1);
361 NT_STATUS_HAVE_NO_MEMORY(d);
362 for (i=notify->array->num_depths;i<=depth;i++) {
363 ZERO_STRUCT(d[i]);
365 notify->array->depth = d;
366 notify->array->num_depths = depth+1;
368 d = &notify->array->depth[depth];
370 /* expand the entries array */
371 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
372 d->num_entries+1);
373 NT_STATUS_HAVE_NO_MEMORY(ee);
374 d->entries = ee;
376 d->entries[d->num_entries] = *e;
377 d->entries[d->num_entries].private_data = private_data;
378 d->entries[d->num_entries].server = notify->server;
379 d->entries[d->num_entries].path_len = strlen(e->path);
380 d->num_entries++;
382 d->max_mask |= e->filter;
383 d->max_mask_subdir |= e->subdir_filter;
385 TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
387 /* recalculate the maximum masks */
388 d->max_mask = 0;
389 d->max_mask_subdir = 0;
391 for (i=0;i<d->num_entries;i++) {
392 d->max_mask |= d->entries[i].filter;
393 d->max_mask_subdir |= d->entries[i].subdir_filter;
396 return notify_save(notify, rec);
400 Add a non-recursive watch
403 static void notify_add_onelevel(struct notify_context *notify,
404 struct notify_entry *e, void *private_data)
406 struct notify_entry_array *array;
407 struct db_record *rec;
408 DATA_BLOB blob;
409 TDB_DATA dbuf;
410 enum ndr_err_code ndr_err;
411 NTSTATUS status;
413 array = talloc_zero(talloc_tos(), struct notify_entry_array);
414 if (array == NULL) {
415 return;
418 rec = notify->db_onelevel->fetch_locked(
419 notify->db_onelevel, talloc_tos(),
420 make_tdb_data((uint8_t *)&e->dir_id, sizeof(e->dir_id)));
421 if (rec == NULL) {
422 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
423 "\n", file_id_string_tos(&e->dir_id)));
424 TALLOC_FREE(array);
425 return;
428 blob.data = (uint8_t *)rec->value.dptr;
429 blob.length = rec->value.dsize;
431 if (blob.length > 0) {
432 ndr_err = ndr_pull_struct_blob(&blob, array, array,
433 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
434 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
435 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
436 ndr_errstr(ndr_err)));
437 TALLOC_FREE(array);
438 return;
440 if (DEBUGLEVEL >= 10) {
441 DEBUG(10, ("notify_add_onelevel:\n"));
442 NDR_PRINT_DEBUG(notify_entry_array, array);
446 array->entries = talloc_realloc(array, array->entries,
447 struct notify_entry,
448 array->num_entries+1);
449 if (array->entries == NULL) {
450 TALLOC_FREE(array);
451 return;
453 array->entries[array->num_entries] = *e;
454 array->entries[array->num_entries].private_data = private_data;
455 array->entries[array->num_entries].server = notify->server;
456 array->num_entries += 1;
458 ndr_err = ndr_push_struct_blob(&blob, rec, array,
459 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
460 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
461 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
462 ndr_errstr(ndr_err)));
463 TALLOC_FREE(array);
464 return;
467 if (DEBUGLEVEL >= 10) {
468 DEBUG(10, ("notify_add_onelevel:\n"));
469 NDR_PRINT_DEBUG(notify_entry_array, array);
472 dbuf.dptr = blob.data;
473 dbuf.dsize = blob.length;
475 status = rec->store(rec, dbuf, TDB_REPLACE);
476 TALLOC_FREE(array);
477 if (!NT_STATUS_IS_OK(status)) {
478 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
479 nt_errstr(status)));
480 return;
482 e->filter = 0;
483 return;
488 add a notify watch. This is called when a notify is first setup on a open
489 directory handle.
491 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
492 void (*callback)(void *, const struct notify_event *),
493 void *private_data)
495 struct notify_entry e = *e0;
496 NTSTATUS status;
497 char *tmp_path = NULL;
498 struct notify_list *listel;
499 size_t len;
500 int depth;
501 struct db_record *rec;
503 /* see if change notify is enabled at all */
504 if (notify == NULL) {
505 return NT_STATUS_NOT_IMPLEMENTED;
508 status = notify_fetch_locked(notify, &rec);
509 NT_STATUS_NOT_OK_RETURN(status);
511 status = notify_load(notify, rec);
512 if (!NT_STATUS_IS_OK(status)) {
513 talloc_free(rec);
514 return status;
517 /* cope with /. on the end of the path */
518 len = strlen(e.path);
519 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
520 tmp_path = talloc_strndup(notify, e.path, len-2);
521 if (tmp_path == NULL) {
522 status = NT_STATUS_NO_MEMORY;
523 goto done;
525 e.path = tmp_path;
528 depth = count_chars(e.path, '/');
530 listel = TALLOC_ZERO_P(notify, struct notify_list);
531 if (listel == NULL) {
532 status = NT_STATUS_NO_MEMORY;
533 goto done;
536 listel->private_data = private_data;
537 listel->callback = callback;
538 listel->depth = depth;
539 DLIST_ADD(notify->list, listel);
541 /* ignore failures from sys_notify */
542 if (notify->sys_notify_ctx != NULL) {
544 this call will modify e.filter and e.subdir_filter
545 to remove bits handled by the backend
547 status = sys_notify_watch(notify->sys_notify_ctx, &e,
548 sys_notify_callback, listel,
549 &listel->sys_notify_handle);
550 if (NT_STATUS_IS_OK(status)) {
551 talloc_steal(listel, listel->sys_notify_handle);
555 if (e.filter != 0) {
556 notify_add_onelevel(notify, &e, private_data);
557 status = NT_STATUS_OK;
560 /* if the system notify handler couldn't handle some of the
561 filter bits, or couldn't handle a request for recursion
562 then we need to install it in the array used for the
563 intra-samba notify handling */
564 if (e.filter != 0 || e.subdir_filter != 0) {
565 status = notify_add_array(notify, rec, &e, private_data, depth);
568 done:
569 talloc_free(rec);
570 talloc_free(tmp_path);
572 return status;
575 NTSTATUS notify_remove_onelevel(struct notify_context *notify,
576 const struct file_id *fid,
577 void *private_data)
579 struct notify_entry_array *array;
580 struct db_record *rec;
581 DATA_BLOB blob;
582 TDB_DATA dbuf;
583 enum ndr_err_code ndr_err;
584 NTSTATUS status;
585 int i;
587 if (notify == NULL) {
588 return NT_STATUS_NOT_IMPLEMENTED;
591 array = talloc_zero(talloc_tos(), struct notify_entry_array);
592 if (array == NULL) {
593 return NT_STATUS_NO_MEMORY;
596 rec = notify->db_onelevel->fetch_locked(
597 notify->db_onelevel, array,
598 make_tdb_data((uint8_t *)fid, sizeof(*fid)));
599 if (rec == NULL) {
600 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
601 "\n", file_id_string_tos(fid)));
602 TALLOC_FREE(array);
603 return NT_STATUS_INTERNAL_DB_CORRUPTION;
606 blob.data = (uint8_t *)rec->value.dptr;
607 blob.length = rec->value.dsize;
609 if (blob.length > 0) {
610 ndr_err = ndr_pull_struct_blob(&blob, array, array,
611 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
612 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
613 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
614 ndr_errstr(ndr_err)));
615 TALLOC_FREE(array);
616 return ndr_map_error2ntstatus(ndr_err);
618 if (DEBUGLEVEL >= 10) {
619 DEBUG(10, ("notify_remove_onelevel:\n"));
620 NDR_PRINT_DEBUG(notify_entry_array, array);
624 for (i=0; i<array->num_entries; i++) {
625 if ((private_data == array->entries[i].private_data) &&
626 cluster_id_equal(&notify->server,
627 &array->entries[i].server)) {
628 break;
632 if (i == array->num_entries) {
633 TALLOC_FREE(array);
634 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
637 array->entries[i] = array->entries[array->num_entries-1];
638 array->num_entries -= 1;
640 if (array->num_entries == 0) {
641 rec->delete_rec(rec);
642 TALLOC_FREE(array);
643 return NT_STATUS_OK;
646 ndr_err = ndr_push_struct_blob(&blob, rec, array,
647 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
648 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
649 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
650 ndr_errstr(ndr_err)));
651 TALLOC_FREE(array);
652 return ndr_map_error2ntstatus(ndr_err);
655 if (DEBUGLEVEL >= 10) {
656 DEBUG(10, ("notify_add_onelevel:\n"));
657 NDR_PRINT_DEBUG(notify_entry_array, array);
660 dbuf.dptr = blob.data;
661 dbuf.dsize = blob.length;
663 status = rec->store(rec, dbuf, TDB_REPLACE);
664 TALLOC_FREE(array);
665 if (!NT_STATUS_IS_OK(status)) {
666 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
667 nt_errstr(status)));
668 return status;
670 return NT_STATUS_OK;
674 remove a notify watch. Called when the directory handle is closed
676 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
678 NTSTATUS status;
679 struct notify_list *listel;
680 int i, depth;
681 struct notify_depth *d;
682 struct db_record *rec;
684 /* see if change notify is enabled at all */
685 if (notify == NULL) {
686 return NT_STATUS_NOT_IMPLEMENTED;
689 for (listel=notify->list;listel;listel=listel->next) {
690 if (listel->private_data == private_data) {
691 DLIST_REMOVE(notify->list, listel);
692 break;
695 if (listel == NULL) {
696 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
699 depth = listel->depth;
701 talloc_free(listel);
703 status = notify_fetch_locked(notify, &rec);
704 NT_STATUS_NOT_OK_RETURN(status);
706 status = notify_load(notify, rec);
707 if (!NT_STATUS_IS_OK(status)) {
708 talloc_free(rec);
709 return status;
712 if (depth >= notify->array->num_depths) {
713 talloc_free(rec);
714 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
717 /* we only have to search at the depth of this element */
718 d = &notify->array->depth[depth];
720 for (i=0;i<d->num_entries;i++) {
721 if (private_data == d->entries[i].private_data &&
722 cluster_id_equal(&notify->server, &d->entries[i].server)) {
723 break;
726 if (i == d->num_entries) {
727 talloc_free(rec);
728 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
731 if (i < d->num_entries-1) {
732 memmove(&d->entries[i], &d->entries[i+1],
733 sizeof(d->entries[i])*(d->num_entries-(i+1)));
735 d->num_entries--;
737 status = notify_save(notify, rec);
739 talloc_free(rec);
741 return status;
745 remove all notify watches for a messaging server
747 static NTSTATUS notify_remove_all(struct notify_context *notify,
748 const struct server_id *server)
750 NTSTATUS status;
751 int i, depth, del_count=0;
752 struct db_record *rec;
754 status = notify_fetch_locked(notify, &rec);
755 NT_STATUS_NOT_OK_RETURN(status);
757 status = notify_load(notify, rec);
758 if (!NT_STATUS_IS_OK(status)) {
759 talloc_free(rec);
760 return status;
763 /* we have to search for all entries across all depths, looking for matches
764 for the server id */
765 for (depth=0;depth<notify->array->num_depths;depth++) {
766 struct notify_depth *d = &notify->array->depth[depth];
767 for (i=0;i<d->num_entries;i++) {
768 if (cluster_id_equal(server, &d->entries[i].server)) {
769 if (i < d->num_entries-1) {
770 memmove(&d->entries[i], &d->entries[i+1],
771 sizeof(d->entries[i])*(d->num_entries-(i+1)));
773 i--;
774 d->num_entries--;
775 del_count++;
780 if (del_count > 0) {
781 status = notify_save(notify, rec);
784 talloc_free(rec);
786 return status;
791 send a notify message to another messaging server
793 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
794 const char *path, uint32_t action)
796 struct notify_event ev;
797 DATA_BLOB data;
798 NTSTATUS status;
799 enum ndr_err_code ndr_err;
800 TALLOC_CTX *tmp_ctx;
802 ev.action = action;
803 ev.path = path;
804 ev.private_data = e->private_data;
806 tmp_ctx = talloc_new(notify);
808 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev,
809 (ndr_push_flags_fn_t)ndr_push_notify_event);
810 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
811 talloc_free(tmp_ctx);
812 return ndr_map_error2ntstatus(ndr_err);
815 status = messaging_send(notify->messaging_ctx, e->server,
816 MSG_PVFS_NOTIFY, &data);
817 talloc_free(tmp_ctx);
818 return status;
821 void notify_onelevel(struct notify_context *notify, uint32_t action,
822 uint32_t filter, struct file_id fid, const char *name)
824 struct notify_entry_array *array;
825 TDB_DATA dbuf;
826 DATA_BLOB blob;
827 bool have_dead_entries = false;
828 int i;
830 if (notify == NULL) {
831 return;
834 array = talloc_zero(talloc_tos(), struct notify_entry_array);
835 if (array == NULL) {
836 return;
839 if (notify->db_onelevel->fetch(
840 notify->db_onelevel, array,
841 make_tdb_data((uint8_t *)&fid, sizeof(fid)),
842 &dbuf) == -1) {
843 TALLOC_FREE(array);
844 return;
847 blob.data = (uint8 *)dbuf.dptr;
848 blob.length = dbuf.dsize;
850 if (blob.length > 0) {
851 enum ndr_err_code ndr_err;
852 ndr_err = ndr_pull_struct_blob(&blob, array, array,
853 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
854 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
855 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
856 ndr_errstr(ndr_err)));
857 TALLOC_FREE(array);
858 return;
860 if (DEBUGLEVEL >= 10) {
861 DEBUG(10, ("notify_onelevel:\n"));
862 NDR_PRINT_DEBUG(notify_entry_array, array);
866 for (i=0; i<array->num_entries; i++) {
867 struct notify_entry *e = &array->entries[i];
869 if ((e->filter & filter) != 0) {
870 NTSTATUS status;
872 status = notify_send(notify, e, name, action);
873 if (NT_STATUS_EQUAL(
874 status, NT_STATUS_INVALID_HANDLE)) {
876 * Mark the entry as dead. All entries have a
877 * path set. The marker used here is setting
878 * that to NULL.
880 e->path = NULL;
881 have_dead_entries = true;
886 if (!have_dead_entries) {
887 TALLOC_FREE(array);
888 return;
891 for (i=0; i<array->num_entries; i++) {
892 struct notify_entry *e = &array->entries[i];
893 if (e->path != NULL) {
894 continue;
896 DEBUG(10, ("Deleting notify entries for process %s because "
897 "it's gone\n", procid_str_static(&e->server)));
899 * Potential TODO: This might need optimizing,
900 * notify_remove_onelevel() does a fetch_locked() operation at
901 * every call. But this would only matter if a process with
902 * MANY notifies has died without shutting down properly.
904 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
907 TALLOC_FREE(array);
908 return;
912 trigger a notify message for anyone waiting on a matching event
914 This function is called a lot, and needs to be very fast. The unusual data structure
915 and traversal is designed to be fast in the average case, even for large numbers of
916 notifies
918 void notify_trigger(struct notify_context *notify,
919 uint32_t action, uint32_t filter, const char *path)
921 NTSTATUS status;
922 int depth;
923 const char *p, *next_p;
925 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
926 "path=%s\n", (unsigned)action, (unsigned)filter, path));
928 /* see if change notify is enabled at all */
929 if (notify == NULL) {
930 return;
933 again:
934 status = notify_load(notify, NULL);
935 if (!NT_STATUS_IS_OK(status)) {
936 return;
939 /* loop along the given path, working with each directory depth separately */
940 for (depth=0,p=path;
941 p && depth < notify->array->num_depths;
942 p=next_p,depth++) {
943 int p_len = p - path;
944 int min_i, max_i, i;
945 struct notify_depth *d = &notify->array->depth[depth];
946 next_p = strchr(p+1, '/');
948 /* see if there are any entries at this depth */
949 if (d->num_entries == 0) continue;
951 /* try to skip based on the maximum mask. If next_p is
952 NULL then we know it will be a 'this directory'
953 match, otherwise it must be a subdir match */
954 if (next_p != NULL) {
955 if (0 == (filter & d->max_mask_subdir)) {
956 continue;
958 } else {
959 if (0 == (filter & d->max_mask)) {
960 continue;
964 /* we know there is an entry here worth looking
965 for. Use a bisection search to find the first entry
966 with a matching path */
967 min_i = 0;
968 max_i = d->num_entries-1;
970 while (min_i < max_i) {
971 struct notify_entry *e;
972 int cmp;
973 i = (min_i+max_i)/2;
974 e = &d->entries[i];
975 cmp = strncmp(path, e->path, p_len);
976 if (cmp == 0) {
977 if (p_len == e->path_len) {
978 max_i = i;
979 } else {
980 max_i = i-1;
982 } else if (cmp < 0) {
983 max_i = i-1;
984 } else {
985 min_i = i+1;
989 if (min_i != max_i) {
990 /* none match */
991 continue;
994 /* we now know that the entries start at min_i */
995 for (i=min_i;i<d->num_entries;i++) {
996 struct notify_entry *e = &d->entries[i];
997 if (p_len != e->path_len ||
998 strncmp(path, e->path, p_len) != 0) break;
999 if (next_p != NULL) {
1000 if (0 == (filter & e->subdir_filter)) {
1001 continue;
1003 } else {
1004 if (0 == (filter & e->filter)) {
1005 continue;
1008 status = notify_send(notify, e, path + e->path_len + 1,
1009 action);
1011 if (NT_STATUS_EQUAL(
1012 status, NT_STATUS_INVALID_HANDLE)) {
1013 struct server_id server = e->server;
1015 DEBUG(10, ("Deleting notify entries for "
1016 "process %s because it's gone\n",
1017 procid_str_static(&e->server)));
1018 notify_remove_all(notify, &server);
1019 goto again;