s4-provision: don't ask only 3 times for passwd in interactive mode
[Samba/vl.git] / source3 / smbd / notify_internal.c
blob63b7865b1315df50f8411c879b418a527be8ba93
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/tdb_wrap/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,
88 struct messaging_context *messaging_ctx,
89 struct event_context *ev)
91 struct notify_context *notify;
93 notify = talloc(mem_ctx, struct notify_context);
94 if (notify == NULL) {
95 return NULL;
98 notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
99 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
100 O_RDWR|O_CREAT, 0644,
101 DBWRAP_LOCK_ORDER_2);
102 if (notify->db_recursive == NULL) {
103 talloc_free(notify);
104 return NULL;
107 notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
108 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
109 O_RDWR|O_CREAT, 0644,
110 DBWRAP_LOCK_ORDER_2);
111 if (notify->db_onelevel == NULL) {
112 talloc_free(notify);
113 return NULL;
116 notify->server = messaging_server_id(messaging_ctx);
117 notify->messaging_ctx = messaging_ctx;
118 notify->list = NULL;
119 notify->array = NULL;
120 notify->seqnum = dbwrap_get_seqnum(notify->db_recursive);
121 notify->key = string_term_tdb_data(NOTIFY_KEY);
123 talloc_set_destructor(notify, notify_destructor);
125 /* register with the messaging subsystem for the notify
126 message type */
127 messaging_register(notify->messaging_ctx, notify,
128 MSG_PVFS_NOTIFY, notify_handler);
130 notify->sys_notify_ctx = sys_notify_context_create(notify, ev);
132 return notify;
135 bool notify_internal_parent_init(TALLOC_CTX *mem_ctx)
137 struct tdb_wrap *db1, *db2;
138 struct loadparm_context *lp_ctx;
140 if (lp_clustering()) {
141 return true;
144 lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_context());
145 if (lp_ctx == NULL) {
146 DEBUG(0, ("loadparm_init_s3 failed\n"));
147 return false;
150 * Open the tdbs in the parent process (smbd) so that our
151 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
152 * work.
155 db1 = tdb_wrap_open(mem_ctx, lock_path("notify.tdb"),
156 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
157 O_RDWR|O_CREAT, 0644, lp_ctx);
158 if (db1 == NULL) {
159 talloc_unlink(mem_ctx, lp_ctx);
160 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno)));
161 return false;
163 db2 = tdb_wrap_open(mem_ctx, lock_path("notify_onelevel.tdb"),
164 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT, 0644, lp_ctx);
165 talloc_unlink(mem_ctx, lp_ctx);
166 if (db2 == NULL) {
167 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
168 strerror(errno)));
169 TALLOC_FREE(db1);
170 return false;
172 return true;
176 lock and fetch the record
178 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
180 *rec = dbwrap_fetch_locked(notify->db_recursive, notify, notify->key);
181 if (*rec == NULL) {
182 return NT_STATUS_INTERNAL_DB_CORRUPTION;
184 return NT_STATUS_OK;
188 load the notify array
190 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
192 TDB_DATA dbuf;
193 DATA_BLOB blob;
194 NTSTATUS status;
195 int seqnum;
197 seqnum = dbwrap_get_seqnum(notify->db_recursive);
199 if (seqnum == notify->seqnum && notify->array != NULL) {
200 return NT_STATUS_OK;
203 notify->seqnum = seqnum;
205 talloc_free(notify->array);
206 notify->array = talloc_zero(notify, struct notify_array);
207 NT_STATUS_HAVE_NO_MEMORY(notify->array);
209 if (!rec) {
210 status = dbwrap_fetch(notify->db_recursive, notify,
211 notify->key, &dbuf);
212 if (!NT_STATUS_IS_OK(status)) {
213 return NT_STATUS_INTERNAL_DB_CORRUPTION;
215 } else {
216 dbuf = dbwrap_record_get_value(rec);
219 blob.data = (uint8 *)dbuf.dptr;
220 blob.length = dbuf.dsize;
222 status = NT_STATUS_OK;
223 if (blob.length > 0) {
224 enum ndr_err_code ndr_err;
225 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
226 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
227 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
228 /* 1. log that we got a corrupt notify_array
229 * 2. clear the variable the garbage was stored into to not trip
230 * over it next time this method is entered with the same seqnum
231 * 3. delete it from the database */
232 DEBUG(2, ("notify_array is corrupt, discarding it\n"));
234 ZERO_STRUCTP(notify->array);
235 if (rec != NULL) {
236 dbwrap_record_delete(rec);
239 } else {
240 if (DEBUGLEVEL >= 10) {
241 DEBUG(10, ("notify_load:\n"));
242 NDR_PRINT_DEBUG(notify_array, notify->array);
248 if (!rec) {
249 talloc_free(dbuf.dptr);
252 return status;
256 compare notify entries for sorting
258 static int notify_compare(const struct notify_entry *e1, const struct notify_entry *e2)
260 return strcmp(e1->path, e2->path);
264 save the notify array
266 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
268 TDB_DATA dbuf;
269 DATA_BLOB blob;
270 NTSTATUS status;
271 enum ndr_err_code ndr_err;
272 TALLOC_CTX *tmp_ctx;
274 /* if possible, remove some depth arrays */
275 while (notify->array->num_depths > 0 &&
276 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
277 notify->array->num_depths--;
280 /* we might just be able to delete the record */
281 if (notify->array->num_depths == 0) {
282 return dbwrap_record_delete(rec);
285 tmp_ctx = talloc_new(notify);
286 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
288 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
289 (ndr_push_flags_fn_t)ndr_push_notify_array);
290 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
291 talloc_free(tmp_ctx);
292 return ndr_map_error2ntstatus(ndr_err);
295 if (DEBUGLEVEL >= 10) {
296 DEBUG(10, ("notify_save:\n"));
297 NDR_PRINT_DEBUG(notify_array, notify->array);
300 dbuf.dptr = blob.data;
301 dbuf.dsize = blob.length;
303 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
304 talloc_free(tmp_ctx);
306 return status;
311 handle incoming notify messages
313 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
314 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
316 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
317 enum ndr_err_code ndr_err;
318 struct notify_event ev;
319 TALLOC_CTX *tmp_ctx = talloc_new(notify);
320 struct notify_list *listel;
322 if (tmp_ctx == NULL) {
323 return;
326 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
327 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
328 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
329 talloc_free(tmp_ctx);
330 return;
333 for (listel=notify->list;listel;listel=listel->next) {
334 if (listel->private_data == ev.private_data) {
335 listel->callback(listel->private_data, &ev);
336 break;
340 talloc_free(tmp_ctx);
344 callback from sys_notify telling us about changes from the OS
346 static void sys_notify_callback(struct sys_notify_context *ctx,
347 void *ptr, struct notify_event *ev)
349 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
350 ev->private_data = listel;
351 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
352 ev->action, ev->path));
353 listel->callback(listel->private_data, ev);
357 add an entry to the notify array
359 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
360 struct notify_entry *e,
361 void *private_data, int depth)
363 int i;
364 struct notify_depth *d;
365 struct notify_entry *ee;
367 /* possibly expand the depths array */
368 if (depth >= notify->array->num_depths) {
369 d = talloc_realloc(notify->array, notify->array->depth,
370 struct notify_depth, depth+1);
371 NT_STATUS_HAVE_NO_MEMORY(d);
372 for (i=notify->array->num_depths;i<=depth;i++) {
373 ZERO_STRUCT(d[i]);
375 notify->array->depth = d;
376 notify->array->num_depths = depth+1;
378 d = &notify->array->depth[depth];
380 /* expand the entries array */
381 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
382 d->num_entries+1);
383 NT_STATUS_HAVE_NO_MEMORY(ee);
384 d->entries = ee;
386 d->entries[d->num_entries] = *e;
387 d->entries[d->num_entries].private_data = private_data;
388 d->entries[d->num_entries].server = notify->server;
389 d->entries[d->num_entries].path_len = strlen(e->path);
390 d->num_entries++;
392 d->max_mask |= e->filter;
393 d->max_mask_subdir |= e->subdir_filter;
395 TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
397 /* recalculate the maximum masks */
398 d->max_mask = 0;
399 d->max_mask_subdir = 0;
401 for (i=0;i<d->num_entries;i++) {
402 d->max_mask |= d->entries[i].filter;
403 d->max_mask_subdir |= d->entries[i].subdir_filter;
406 return notify_save(notify, rec);
410 Add a non-recursive watch
413 static void notify_add_onelevel(struct notify_context *notify,
414 struct notify_entry *e, void *private_data)
416 struct notify_entry_array *array;
417 struct db_record *rec;
418 DATA_BLOB blob;
419 TDB_DATA dbuf;
420 TDB_DATA value;
421 enum ndr_err_code ndr_err;
422 NTSTATUS status;
424 array = talloc_zero(talloc_tos(), struct notify_entry_array);
425 if (array == NULL) {
426 return;
429 rec = dbwrap_fetch_locked(notify->db_onelevel, array,
430 make_tdb_data((uint8_t *)&e->dir_id,
431 sizeof(e->dir_id)));
432 if (rec == NULL) {
433 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
434 "\n", file_id_string_tos(&e->dir_id)));
435 TALLOC_FREE(array);
436 return;
439 value = dbwrap_record_get_value(rec);
440 blob.data = (uint8_t *)value.dptr;
441 blob.length = value.dsize;
443 if (blob.length > 0) {
444 ndr_err = ndr_pull_struct_blob(&blob, array, array,
445 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
446 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
447 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
448 ndr_errstr(ndr_err)));
449 TALLOC_FREE(array);
450 return;
452 if (DEBUGLEVEL >= 10) {
453 DEBUG(10, ("notify_add_onelevel:\n"));
454 NDR_PRINT_DEBUG(notify_entry_array, array);
458 array->entries = talloc_realloc(array, array->entries,
459 struct notify_entry,
460 array->num_entries+1);
461 if (array->entries == NULL) {
462 TALLOC_FREE(array);
463 return;
465 array->entries[array->num_entries] = *e;
466 array->entries[array->num_entries].private_data = private_data;
467 array->entries[array->num_entries].server = notify->server;
468 array->num_entries += 1;
470 ndr_err = ndr_push_struct_blob(&blob, rec, array,
471 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
472 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
473 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
474 ndr_errstr(ndr_err)));
475 TALLOC_FREE(array);
476 return;
479 if (DEBUGLEVEL >= 10) {
480 DEBUG(10, ("notify_add_onelevel:\n"));
481 NDR_PRINT_DEBUG(notify_entry_array, array);
484 dbuf.dptr = blob.data;
485 dbuf.dsize = blob.length;
487 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
488 TALLOC_FREE(array);
489 if (!NT_STATUS_IS_OK(status)) {
490 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
491 nt_errstr(status)));
492 return;
494 e->filter = 0;
495 return;
500 add a notify watch. This is called when a notify is first setup on a open
501 directory handle.
503 NTSTATUS notify_add(struct notify_context *notify, connection_struct *conn,
504 struct notify_entry *e0,
505 void (*callback)(void *, const struct notify_event *),
506 void *private_data)
508 struct notify_entry e = *e0;
509 NTSTATUS status;
510 struct notify_list *listel;
511 int depth;
513 /* see if change notify is enabled at all */
514 if (notify == NULL) {
515 return NT_STATUS_NOT_IMPLEMENTED;
518 depth = count_chars(e.path, '/');
520 listel = talloc_zero(notify, struct notify_list);
521 if (listel == NULL) {
522 status = NT_STATUS_NO_MEMORY;
523 goto done;
526 listel->private_data = private_data;
527 listel->callback = callback;
528 listel->depth = depth;
529 DLIST_ADD(notify->list, listel);
531 /* ignore failures from sys_notify */
532 if (notify->sys_notify_ctx != NULL) {
534 this call will modify e.filter and e.subdir_filter
535 to remove bits handled by the backend
537 status = sys_notify_watch(notify->sys_notify_ctx, conn,
538 &e, e.path,
539 sys_notify_callback, listel,
540 &listel->sys_notify_handle);
541 if (NT_STATUS_IS_OK(status)) {
542 talloc_steal(listel, listel->sys_notify_handle);
546 if (e.filter != 0) {
547 notify_add_onelevel(notify, &e, private_data);
548 status = NT_STATUS_OK;
551 /* if the system notify handler couldn't handle some of the
552 filter bits, or couldn't handle a request for recursion
553 then we need to install it in the array used for the
554 intra-samba notify handling */
555 if (e.filter != 0 || e.subdir_filter != 0) {
556 struct db_record *rec;
558 status = notify_fetch_locked(notify, &rec);
559 if (!NT_STATUS_IS_OK(status)) {
560 goto done;
562 status = notify_load(notify, rec);
563 if (!NT_STATUS_IS_OK(status)) {
564 TALLOC_FREE(rec);
565 goto done;
567 status = notify_add_array(notify, rec, &e, private_data,
568 depth);
569 TALLOC_FREE(rec);
571 status = NT_STATUS_OK;
572 done:
573 return status;
576 NTSTATUS notify_remove_onelevel(struct notify_context *notify,
577 const struct file_id *fid,
578 void *private_data)
580 struct notify_entry_array *array;
581 struct db_record *rec;
582 DATA_BLOB blob;
583 TDB_DATA dbuf;
584 TDB_DATA value;
585 enum ndr_err_code ndr_err;
586 NTSTATUS status;
587 int i;
589 if (notify == NULL) {
590 return NT_STATUS_NOT_IMPLEMENTED;
593 array = talloc_zero(talloc_tos(), struct notify_entry_array);
594 if (array == NULL) {
595 return NT_STATUS_NO_MEMORY;
598 rec = dbwrap_fetch_locked(
599 notify->db_onelevel, array,
600 make_tdb_data((const uint8_t *)fid, sizeof(*fid)));
601 if (rec == NULL) {
602 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
603 "\n", file_id_string_tos(fid)));
604 TALLOC_FREE(array);
605 return NT_STATUS_INTERNAL_DB_CORRUPTION;
608 value = dbwrap_record_get_value(rec);
609 blob.data = (uint8_t *)value.dptr;
610 blob.length = value.dsize;
612 if (blob.length > 0) {
613 ndr_err = ndr_pull_struct_blob(&blob, array, array,
614 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
615 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
616 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
617 ndr_errstr(ndr_err)));
618 TALLOC_FREE(array);
619 return ndr_map_error2ntstatus(ndr_err);
621 if (DEBUGLEVEL >= 10) {
622 DEBUG(10, ("notify_remove_onelevel:\n"));
623 NDR_PRINT_DEBUG(notify_entry_array, array);
627 for (i=0; i<array->num_entries; i++) {
628 if ((private_data == array->entries[i].private_data) &&
629 procid_equal(&notify->server, &array->entries[i].server)) {
630 break;
634 if (i == array->num_entries) {
635 TALLOC_FREE(array);
636 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
639 array->entries[i] = array->entries[array->num_entries-1];
640 array->num_entries -= 1;
642 if (array->num_entries == 0) {
643 dbwrap_record_delete(rec);
644 TALLOC_FREE(array);
645 return NT_STATUS_OK;
648 ndr_err = ndr_push_struct_blob(&blob, rec, array,
649 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
650 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
651 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
652 ndr_errstr(ndr_err)));
653 TALLOC_FREE(array);
654 return ndr_map_error2ntstatus(ndr_err);
657 if (DEBUGLEVEL >= 10) {
658 DEBUG(10, ("notify_add_onelevel:\n"));
659 NDR_PRINT_DEBUG(notify_entry_array, array);
662 dbuf.dptr = blob.data;
663 dbuf.dsize = blob.length;
665 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
666 TALLOC_FREE(array);
667 if (!NT_STATUS_IS_OK(status)) {
668 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
669 nt_errstr(status)));
670 return status;
672 return NT_STATUS_OK;
676 remove a notify watch. Called when the directory handle is closed
678 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
680 NTSTATUS status;
681 struct notify_list *listel;
682 int i, depth;
683 struct notify_depth *d;
684 struct db_record *rec;
686 /* see if change notify is enabled at all */
687 if (notify == NULL) {
688 return NT_STATUS_NOT_IMPLEMENTED;
691 for (listel=notify->list;listel;listel=listel->next) {
692 if (listel->private_data == private_data) {
693 DLIST_REMOVE(notify->list, listel);
694 break;
697 if (listel == NULL) {
698 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
701 depth = listel->depth;
703 talloc_free(listel);
705 status = notify_fetch_locked(notify, &rec);
706 NT_STATUS_NOT_OK_RETURN(status);
708 status = notify_load(notify, rec);
709 if (!NT_STATUS_IS_OK(status)) {
710 talloc_free(rec);
711 return status;
714 if (depth >= notify->array->num_depths) {
715 talloc_free(rec);
716 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
719 /* we only have to search at the depth of this element */
720 d = &notify->array->depth[depth];
722 for (i=0;i<d->num_entries;i++) {
723 if (private_data == d->entries[i].private_data &&
724 procid_equal(&notify->server, &d->entries[i].server)) {
725 break;
728 if (i == d->num_entries) {
729 talloc_free(rec);
730 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
733 if (i < d->num_entries-1) {
734 memmove(&d->entries[i], &d->entries[i+1],
735 sizeof(d->entries[i])*(d->num_entries-(i+1)));
737 d->num_entries--;
739 status = notify_save(notify, rec);
741 talloc_free(rec);
743 return status;
747 remove all notify watches for a messaging server
749 static NTSTATUS notify_remove_all(struct notify_context *notify,
750 const struct server_id *server)
752 NTSTATUS status;
753 int i, depth, del_count=0;
754 struct db_record *rec;
756 status = notify_fetch_locked(notify, &rec);
757 NT_STATUS_NOT_OK_RETURN(status);
759 status = notify_load(notify, rec);
760 if (!NT_STATUS_IS_OK(status)) {
761 talloc_free(rec);
762 return status;
765 /* we have to search for all entries across all depths, looking for matches
766 for the server id */
767 for (depth=0;depth<notify->array->num_depths;depth++) {
768 struct notify_depth *d = &notify->array->depth[depth];
769 for (i=0;i<d->num_entries;i++) {
770 if (procid_equal(server, &d->entries[i].server)) {
771 if (i < d->num_entries-1) {
772 memmove(&d->entries[i], &d->entries[i+1],
773 sizeof(d->entries[i])*(d->num_entries-(i+1)));
775 i--;
776 d->num_entries--;
777 del_count++;
782 if (del_count > 0) {
783 status = notify_save(notify, rec);
786 talloc_free(rec);
788 return status;
793 send a notify message to another messaging server
795 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
796 const char *path, uint32_t action)
798 struct notify_event ev;
799 DATA_BLOB data;
800 NTSTATUS status;
801 enum ndr_err_code ndr_err;
803 ev.action = action;
804 ev.path = path;
805 ev.private_data = e->private_data;
807 ndr_err = ndr_push_struct_blob(&data, talloc_tos(), &ev,
808 (ndr_push_flags_fn_t)ndr_push_notify_event);
809 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
810 return ndr_map_error2ntstatus(ndr_err);
813 status = messaging_send(notify->messaging_ctx, e->server,
814 MSG_PVFS_NOTIFY, &data);
815 TALLOC_FREE(data.data);
816 return status;
819 void notify_onelevel(struct notify_context *notify, uint32_t action,
820 uint32_t filter, struct file_id fid, const char *name)
822 struct notify_entry_array *array;
823 TDB_DATA dbuf;
824 DATA_BLOB blob;
825 bool have_dead_entries = false;
826 int i;
827 NTSTATUS status;
829 if (notify == NULL) {
830 return;
833 array = talloc_zero(talloc_tos(), struct notify_entry_array);
834 if (array == NULL) {
835 return;
838 status = dbwrap_fetch(notify->db_onelevel, array,
839 make_tdb_data((uint8_t *)&fid, sizeof(fid)),
840 &dbuf);
841 if (!NT_STATUS_IS_OK(status)) {
842 TALLOC_FREE(array);
843 return;
846 blob.data = (uint8 *)dbuf.dptr;
847 blob.length = dbuf.dsize;
849 if (blob.length > 0) {
850 enum ndr_err_code ndr_err;
851 ndr_err = ndr_pull_struct_blob(&blob, array, array,
852 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
853 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
854 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
855 ndr_errstr(ndr_err)));
856 TALLOC_FREE(array);
857 return;
859 if (DEBUGLEVEL >= 10) {
860 DEBUG(10, ("notify_onelevel:\n"));
861 NDR_PRINT_DEBUG(notify_entry_array, array);
865 for (i=0; i<array->num_entries; i++) {
866 struct notify_entry *e = &array->entries[i];
868 if ((e->filter & filter) != 0) {
869 status = notify_send(notify, e, name, action);
870 if (NT_STATUS_EQUAL(
871 status, NT_STATUS_INVALID_HANDLE)) {
873 * Mark the entry as dead. All entries have a
874 * path set. The marker used here is setting
875 * that to NULL.
877 e->path = NULL;
878 have_dead_entries = true;
883 if (!have_dead_entries) {
884 TALLOC_FREE(array);
885 return;
888 for (i=0; i<array->num_entries; i++) {
889 struct notify_entry *e = &array->entries[i];
890 if (e->path != NULL) {
891 continue;
893 DEBUG(10, ("Deleting notify entries for process %s because "
894 "it's gone\n", procid_str_static(&e->server)));
896 * Potential TODO: This might need optimizing,
897 * notify_remove_onelevel() does a fetch_locked() operation at
898 * every call. But this would only matter if a process with
899 * MANY notifies has died without shutting down properly.
901 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
904 TALLOC_FREE(array);
905 return;
909 trigger a notify message for anyone waiting on a matching event
911 This function is called a lot, and needs to be very fast. The unusual data structure
912 and traversal is designed to be fast in the average case, even for large numbers of
913 notifies
915 void notify_trigger(struct notify_context *notify,
916 uint32_t action, uint32_t filter, const char *path)
918 NTSTATUS status;
919 int depth;
920 const char *p, *next_p;
922 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
923 "path=%s\n", (unsigned)action, (unsigned)filter, path));
925 /* see if change notify is enabled at all */
926 if (notify == NULL) {
927 return;
930 again:
931 status = notify_load(notify, NULL);
932 if (!NT_STATUS_IS_OK(status)) {
933 return;
936 /* loop along the given path, working with each directory depth separately */
937 for (depth=0,p=path;
938 p && depth < notify->array->num_depths;
939 p=next_p,depth++) {
940 int p_len = p - path;
941 int min_i, max_i, i;
942 struct notify_depth *d = &notify->array->depth[depth];
943 uint32_t d_max_mask;
944 next_p = strchr(p+1, '/');
946 /* see if there are any entries at this depth */
947 if (d->num_entries == 0) continue;
949 /* try to skip based on the maximum mask. If next_p is
950 NULL then we know it will be a 'this directory'
951 match, otherwise it must be a subdir match */
953 d_max_mask = next_p ? d->max_mask_subdir : d->max_mask;
955 if ((filter & d_max_mask) == 0) {
956 continue;
959 /* we know there is an entry here worth looking
960 for. Use a bisection search to find the first entry
961 with a matching path */
962 min_i = 0;
963 max_i = d->num_entries-1;
965 while (min_i < max_i) {
966 struct notify_entry *e;
967 int cmp;
968 i = (min_i+max_i)/2;
969 e = &d->entries[i];
970 cmp = strncmp(path, e->path, p_len);
971 if (cmp == 0) {
972 if (p_len == e->path_len) {
973 max_i = i;
974 } else {
975 max_i = i-1;
977 } else if (cmp < 0) {
978 max_i = i-1;
979 } else {
980 min_i = i+1;
984 if (min_i != max_i) {
985 /* none match */
986 continue;
989 /* we now know that the entries start at min_i */
990 for (i=min_i;i<d->num_entries;i++) {
991 struct notify_entry *e = &d->entries[i];
992 uint32_t e_filter;
993 if (p_len != e->path_len ||
994 strncmp(path, e->path, p_len) != 0) break;
996 e_filter = next_p ? e->subdir_filter : e->filter;
998 if ((filter & e_filter) == 0) {
999 continue;
1002 status = notify_send(notify, e, path + e->path_len + 1,
1003 action);
1005 if (NT_STATUS_EQUAL(
1006 status, NT_STATUS_INVALID_HANDLE)) {
1007 struct server_id server = e->server;
1009 DEBUG(10, ("Deleting notify entries for "
1010 "process %s because it's gone\n",
1011 procid_str_static(&e->server)));
1012 notify_remove_all(notify, &server);
1013 goto again;