Merge branch 'v3-2-test' of ssh://git.samba.org/data/git/samba into v3-2-test
[Samba/gebeck_regimport.git] / source / smbd / notify_internal.c
blob48ca572a25f91ba2f5909b8fdddacc46b6c7d3fe
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"
29 struct notify_context {
30 struct db_context *db;
31 struct server_id server;
32 struct messaging_context *messaging_ctx;
33 struct notify_list *list;
34 struct notify_array *array;
35 int seqnum;
36 struct sys_notify_context *sys_notify_ctx;
37 TDB_DATA key;
41 struct notify_list {
42 struct notify_list *next, *prev;
43 void *private_data;
44 void (*callback)(void *, const struct notify_event *);
45 void *sys_notify_handle;
46 int depth;
49 #define NOTIFY_KEY "notify array"
51 #define NOTIFY_ENABLE "notify:enable"
52 #define NOTIFY_ENABLE_DEFAULT True
54 static NTSTATUS notify_remove_all(struct notify_context *notify,
55 const struct server_id *server);
56 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
57 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
60 destroy the notify context
62 static int notify_destructor(struct notify_context *notify)
64 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
66 if (notify->list != NULL) {
67 notify_remove_all(notify, &notify->server);
70 return 0;
74 Open up the notify.tdb database. You should close it down using
75 talloc_free(). We need the messaging_ctx to allow for notifications
76 via internal messages
78 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
79 struct messaging_context *messaging_ctx,
80 struct event_context *ev,
81 connection_struct *conn)
83 struct notify_context *notify;
85 if (!lp_change_notify(conn->params)) {
86 return NULL;
89 notify = talloc(mem_ctx, struct notify_context);
90 if (notify == NULL) {
91 return NULL;
94 notify->db = db_open(notify, lock_path("notify.tdb"),
95 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
96 O_RDWR|O_CREAT, 0644);
97 if (notify->db == NULL) {
98 talloc_free(notify);
99 return NULL;
102 notify->server = server;
103 notify->messaging_ctx = messaging_ctx;
104 notify->list = NULL;
105 notify->array = NULL;
106 notify->seqnum = notify->db->get_seqnum(notify->db);
107 notify->key = string_term_tdb_data(NOTIFY_KEY);
109 talloc_set_destructor(notify, notify_destructor);
111 /* register with the messaging subsystem for the notify
112 message type */
113 messaging_register(notify->messaging_ctx, notify,
114 MSG_PVFS_NOTIFY, notify_handler);
116 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
118 return notify;
122 lock and fetch the record
124 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
126 *rec = notify->db->fetch_locked(notify->db, notify, notify->key);
127 if (*rec == NULL) {
128 return NT_STATUS_INTERNAL_DB_CORRUPTION;
130 return NT_STATUS_OK;
134 load the notify array
136 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
138 TDB_DATA dbuf;
139 DATA_BLOB blob;
140 NTSTATUS status;
141 int seqnum;
143 seqnum = notify->db->get_seqnum(notify->db);
145 if (seqnum == notify->seqnum && notify->array != NULL) {
146 return NT_STATUS_OK;
149 notify->seqnum = seqnum;
151 talloc_free(notify->array);
152 notify->array = TALLOC_ZERO_P(notify, struct notify_array);
153 NT_STATUS_HAVE_NO_MEMORY(notify->array);
155 if (!rec) {
156 if (notify->db->fetch(notify->db, notify, notify->key, &dbuf) != 0) {
157 return NT_STATUS_INTERNAL_DB_CORRUPTION;
159 } else {
160 dbuf = rec->value;
163 blob.data = (uint8 *)dbuf.dptr;
164 blob.length = dbuf.dsize;
166 if (blob.length == 0) {
167 status = NT_STATUS_OK;
168 } else {
169 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
170 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
173 if (DEBUGLEVEL >= 10) {
174 DEBUG(10, ("notify_load:\n"));
175 NDR_PRINT_DEBUG(notify_array, notify->array);
178 if (!rec) {
179 talloc_free(dbuf.dptr);
182 return status;
186 compare notify entries for sorting
188 static int notify_compare(const void *p1, const void *p2)
190 const struct notify_entry *e1 = (const struct notify_entry *)p1;
191 const struct notify_entry *e2 = (const struct notify_entry *)p2;
192 return strcmp(e1->path, e2->path);
196 save the notify array
198 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
200 TDB_DATA dbuf;
201 DATA_BLOB blob;
202 NTSTATUS status;
203 TALLOC_CTX *tmp_ctx;
205 /* if possible, remove some depth arrays */
206 while (notify->array->num_depths > 0 &&
207 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
208 notify->array->num_depths--;
211 /* we might just be able to delete the record */
212 if (notify->array->num_depths == 0) {
213 return rec->delete_rec(rec);
216 tmp_ctx = talloc_new(notify);
217 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
219 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
220 (ndr_push_flags_fn_t)ndr_push_notify_array);
221 if (!NT_STATUS_IS_OK(status)) {
222 talloc_free(tmp_ctx);
223 return status;
226 if (DEBUGLEVEL >= 10) {
227 DEBUG(10, ("notify_save:\n"));
228 NDR_PRINT_DEBUG(notify_array, notify->array);
231 dbuf.dptr = blob.data;
232 dbuf.dsize = blob.length;
234 status = rec->store(rec, dbuf, TDB_REPLACE);
235 talloc_free(tmp_ctx);
237 return status;
242 handle incoming notify messages
244 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
245 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
247 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
248 NTSTATUS status;
249 struct notify_event ev;
250 TALLOC_CTX *tmp_ctx = talloc_new(notify);
251 struct notify_list *listel;
253 if (tmp_ctx == NULL) {
254 return;
257 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
258 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
259 if (!NT_STATUS_IS_OK(status)) {
260 talloc_free(tmp_ctx);
261 return;
264 for (listel=notify->list;listel;listel=listel->next) {
265 if (listel->private_data == ev.private_data) {
266 listel->callback(listel->private_data, &ev);
267 break;
271 talloc_free(tmp_ctx);
275 callback from sys_notify telling us about changes from the OS
277 static void sys_notify_callback(struct sys_notify_context *ctx,
278 void *ptr, struct notify_event *ev)
280 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
281 ev->private_data = listel;
282 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
283 ev->action, ev->path));
284 listel->callback(listel->private_data, ev);
288 add an entry to the notify array
290 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
291 struct notify_entry *e,
292 void *private_data, int depth)
294 int i;
295 struct notify_depth *d;
296 struct notify_entry *ee;
298 /* possibly expand the depths array */
299 if (depth >= notify->array->num_depths) {
300 d = talloc_realloc(notify->array, notify->array->depth,
301 struct notify_depth, depth+1);
302 NT_STATUS_HAVE_NO_MEMORY(d);
303 for (i=notify->array->num_depths;i<=depth;i++) {
304 ZERO_STRUCT(d[i]);
306 notify->array->depth = d;
307 notify->array->num_depths = depth+1;
309 d = &notify->array->depth[depth];
311 /* expand the entries array */
312 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
313 d->num_entries+1);
314 NT_STATUS_HAVE_NO_MEMORY(ee);
315 d->entries = ee;
317 d->entries[d->num_entries] = *e;
318 d->entries[d->num_entries].private_data = private_data;
319 d->entries[d->num_entries].server = notify->server;
320 d->entries[d->num_entries].path_len = strlen(e->path);
321 d->num_entries++;
323 d->max_mask |= e->filter;
324 d->max_mask_subdir |= e->subdir_filter;
326 if (d->num_entries > 1) {
327 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
330 /* recalculate the maximum masks */
331 d->max_mask = 0;
332 d->max_mask_subdir = 0;
334 for (i=0;i<d->num_entries;i++) {
335 d->max_mask |= d->entries[i].filter;
336 d->max_mask_subdir |= d->entries[i].subdir_filter;
339 return notify_save(notify, rec);
343 add a notify watch. This is called when a notify is first setup on a open
344 directory handle.
346 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
347 void (*callback)(void *, const struct notify_event *),
348 void *private_data)
350 struct notify_entry e = *e0;
351 NTSTATUS status;
352 char *tmp_path = NULL;
353 struct notify_list *listel;
354 size_t len;
355 int depth;
356 struct db_record *rec;
358 /* see if change notify is enabled at all */
359 if (notify == NULL) {
360 return NT_STATUS_NOT_IMPLEMENTED;
363 status = notify_fetch_locked(notify, &rec);
364 NT_STATUS_NOT_OK_RETURN(status);
366 status = notify_load(notify, rec);
367 if (!NT_STATUS_IS_OK(status)) {
368 talloc_free(rec);
369 return status;
372 /* cope with /. on the end of the path */
373 len = strlen(e.path);
374 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
375 tmp_path = talloc_strndup(notify, e.path, len-2);
376 if (tmp_path == NULL) {
377 status = NT_STATUS_NO_MEMORY;
378 goto done;
380 e.path = tmp_path;
383 depth = count_chars(e.path, '/');
385 listel = TALLOC_ZERO_P(notify, struct notify_list);
386 if (listel == NULL) {
387 status = NT_STATUS_NO_MEMORY;
388 goto done;
391 listel->private_data = private_data;
392 listel->callback = callback;
393 listel->depth = depth;
394 DLIST_ADD(notify->list, listel);
396 /* ignore failures from sys_notify */
397 if (notify->sys_notify_ctx != NULL) {
399 this call will modify e.filter and e.subdir_filter
400 to remove bits handled by the backend
402 status = sys_notify_watch(notify->sys_notify_ctx, &e,
403 sys_notify_callback, listel,
404 &listel->sys_notify_handle);
405 if (NT_STATUS_IS_OK(status)) {
406 talloc_steal(listel, listel->sys_notify_handle);
410 /* if the system notify handler couldn't handle some of the
411 filter bits, or couldn't handle a request for recursion
412 then we need to install it in the array used for the
413 intra-samba notify handling */
414 if (e.filter != 0 || e.subdir_filter != 0) {
415 status = notify_add_array(notify, rec, &e, private_data, depth);
418 done:
419 talloc_free(rec);
420 talloc_free(tmp_path);
422 return status;
426 remove a notify watch. Called when the directory handle is closed
428 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
430 NTSTATUS status;
431 struct notify_list *listel;
432 int i, depth;
433 struct notify_depth *d;
434 struct db_record *rec;
436 /* see if change notify is enabled at all */
437 if (notify == NULL) {
438 return NT_STATUS_NOT_IMPLEMENTED;
441 for (listel=notify->list;listel;listel=listel->next) {
442 if (listel->private_data == private_data) {
443 DLIST_REMOVE(notify->list, listel);
444 break;
447 if (listel == NULL) {
448 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
451 depth = listel->depth;
453 talloc_free(listel);
455 status = notify_fetch_locked(notify, &rec);
456 NT_STATUS_NOT_OK_RETURN(status);
458 status = notify_load(notify, rec);
459 if (!NT_STATUS_IS_OK(status)) {
460 talloc_free(rec);
461 return status;
464 if (depth >= notify->array->num_depths) {
465 talloc_free(rec);
466 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
469 /* we only have to search at the depth of this element */
470 d = &notify->array->depth[depth];
472 for (i=0;i<d->num_entries;i++) {
473 if (private_data == d->entries[i].private_data &&
474 cluster_id_equal(&notify->server, &d->entries[i].server)) {
475 break;
478 if (i == d->num_entries) {
479 talloc_free(rec);
480 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
483 if (i < d->num_entries-1) {
484 memmove(&d->entries[i], &d->entries[i+1],
485 sizeof(d->entries[i])*(d->num_entries-(i+1)));
487 d->num_entries--;
489 status = notify_save(notify, rec);
491 talloc_free(rec);
493 return status;
497 remove all notify watches for a messaging server
499 static NTSTATUS notify_remove_all(struct notify_context *notify,
500 const struct server_id *server)
502 NTSTATUS status;
503 int i, depth, del_count=0;
504 struct db_record *rec;
506 status = notify_fetch_locked(notify, &rec);
507 NT_STATUS_NOT_OK_RETURN(status);
509 status = notify_load(notify, rec);
510 if (!NT_STATUS_IS_OK(status)) {
511 talloc_free(rec);
512 return status;
515 /* we have to search for all entries across all depths, looking for matches
516 for the server id */
517 for (depth=0;depth<notify->array->num_depths;depth++) {
518 struct notify_depth *d = &notify->array->depth[depth];
519 for (i=0;i<d->num_entries;i++) {
520 if (cluster_id_equal(server, &d->entries[i].server)) {
521 if (i < d->num_entries-1) {
522 memmove(&d->entries[i], &d->entries[i+1],
523 sizeof(d->entries[i])*(d->num_entries-(i+1)));
525 i--;
526 d->num_entries--;
527 del_count++;
532 if (del_count > 0) {
533 status = notify_save(notify, rec);
536 talloc_free(rec);
538 return status;
543 send a notify message to another messaging server
545 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
546 const char *path, uint32_t action)
548 struct notify_event ev;
549 DATA_BLOB data;
550 NTSTATUS status;
551 TALLOC_CTX *tmp_ctx;
553 ev.action = action;
554 ev.path = path;
555 ev.private_data = e->private_data;
557 tmp_ctx = talloc_new(notify);
559 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
560 (ndr_push_flags_fn_t)ndr_push_notify_event);
561 if (!NT_STATUS_IS_OK(status)) {
562 talloc_free(tmp_ctx);
563 return status;
566 status = messaging_send(notify->messaging_ctx, e->server,
567 MSG_PVFS_NOTIFY, &data);
568 talloc_free(tmp_ctx);
569 return status;
574 trigger a notify message for anyone waiting on a matching event
576 This function is called a lot, and needs to be very fast. The unusual data structure
577 and traversal is designed to be fast in the average case, even for large numbers of
578 notifies
580 void notify_trigger(struct notify_context *notify,
581 uint32_t action, uint32_t filter, const char *path)
583 NTSTATUS status;
584 int depth;
585 const char *p, *next_p;
587 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
588 "path=%s\n", (unsigned)action, (unsigned)filter, path));
590 /* see if change notify is enabled at all */
591 if (notify == NULL) {
592 return;
595 again:
596 status = notify_load(notify, NULL);
597 if (!NT_STATUS_IS_OK(status)) {
598 return;
601 /* loop along the given path, working with each directory depth separately */
602 for (depth=0,p=path;
603 p && depth < notify->array->num_depths;
604 p=next_p,depth++) {
605 int p_len = p - path;
606 int min_i, max_i, i;
607 struct notify_depth *d = &notify->array->depth[depth];
608 next_p = strchr(p+1, '/');
610 /* see if there are any entries at this depth */
611 if (d->num_entries == 0) continue;
613 /* try to skip based on the maximum mask. If next_p is
614 NULL then we know it will be a 'this directory'
615 match, otherwise it must be a subdir match */
616 if (next_p != NULL) {
617 if (0 == (filter & d->max_mask_subdir)) {
618 continue;
620 } else {
621 if (0 == (filter & d->max_mask)) {
622 continue;
626 /* we know there is an entry here worth looking
627 for. Use a bisection search to find the first entry
628 with a matching path */
629 min_i = 0;
630 max_i = d->num_entries-1;
632 while (min_i < max_i) {
633 struct notify_entry *e;
634 int cmp;
635 i = (min_i+max_i)/2;
636 e = &d->entries[i];
637 cmp = strncmp(path, e->path, p_len);
638 if (cmp == 0) {
639 if (p_len == e->path_len) {
640 max_i = i;
641 } else {
642 max_i = i-1;
644 } else if (cmp < 0) {
645 max_i = i-1;
646 } else {
647 min_i = i+1;
651 if (min_i != max_i) {
652 /* none match */
653 continue;
656 /* we now know that the entries start at min_i */
657 for (i=min_i;i<d->num_entries;i++) {
658 struct notify_entry *e = &d->entries[i];
659 if (p_len != e->path_len ||
660 strncmp(path, e->path, p_len) != 0) break;
661 if (next_p != NULL) {
662 if (0 == (filter & e->subdir_filter)) {
663 continue;
665 } else {
666 if (0 == (filter & e->filter)) {
667 continue;
670 status = notify_send(notify, e, path + e->path_len + 1,
671 action);
673 if (NT_STATUS_EQUAL(
674 status, NT_STATUS_INVALID_HANDLE)) {
675 struct server_id server = e->server;
677 DEBUG(10, ("Deleting notify entries for "
678 "process %s because it's gone\n",
679 procid_str_static(&e->server)));
680 notify_remove_all(notify, &server);
681 goto again;