Fix bug #5914 - Build failure: redefinition of struct name_list
[Samba.git] / source / smbd / notify_internal.c
blobd2b69c9d9e6274f27017718f8be568b0471f2ab1
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 2 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, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 this is the change notify database. It implements mechanisms for
23 storing current change notify waiters in a tdb, and checking if a
24 given event matches any of the stored notify waiiters.
27 #include "includes.h"
28 #include "librpc/gen_ndr/ndr_notify.h"
30 struct notify_context {
31 struct tdb_wrap *w;
32 struct server_id server;
33 struct messaging_context *messaging_ctx;
34 struct notify_list *list;
35 struct notify_array *array;
36 int seqnum;
37 struct sys_notify_context *sys_notify_ctx;
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->w = tdb_wrap_open(notify, lock_path("notify.tdb"),
95 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
96 O_RDWR|O_CREAT, 0644);
97 if (notify->w == 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 = tdb_get_seqnum(notify->w->tdb);
108 talloc_set_destructor(notify, notify_destructor);
110 /* register with the messaging subsystem for the notify
111 message type */
112 messaging_register(notify->messaging_ctx, notify,
113 MSG_PVFS_NOTIFY, notify_handler);
115 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
117 return notify;
122 lock the notify db
124 static NTSTATUS notify_lock(struct notify_context *notify)
126 if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
127 return NT_STATUS_INTERNAL_DB_CORRUPTION;
129 return NT_STATUS_OK;
133 unlock the notify db
135 static void notify_unlock(struct notify_context *notify)
137 tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
141 load the notify array
143 static NTSTATUS notify_load(struct notify_context *notify)
145 TDB_DATA dbuf;
146 DATA_BLOB blob;
147 NTSTATUS status;
148 int seqnum;
150 seqnum = tdb_get_seqnum(notify->w->tdb);
152 if (seqnum == notify->seqnum && notify->array != NULL) {
153 return NT_STATUS_OK;
156 notify->seqnum = seqnum;
158 talloc_free(notify->array);
159 notify->array = TALLOC_ZERO_P(notify, struct notify_array);
160 NT_STATUS_HAVE_NO_MEMORY(notify->array);
162 dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
163 if (dbuf.dptr == NULL) {
164 return NT_STATUS_OK;
167 blob.data = (uint8 *)dbuf.dptr;
168 blob.length = dbuf.dsize;
170 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
171 (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 free(dbuf.dptr);
180 return status;
184 compare notify entries for sorting
186 static int notify_compare(const void *p1, const void *p2)
188 const struct notify_entry *e1 = (const struct notify_entry *)p1;
189 const struct notify_entry *e2 = (const struct notify_entry *)p2;
190 return strcmp(e1->path, e2->path);
194 save the notify array
196 static NTSTATUS notify_save(struct notify_context *notify)
198 TDB_DATA dbuf;
199 DATA_BLOB blob;
200 NTSTATUS status;
201 int ret;
202 TALLOC_CTX *tmp_ctx;
204 /* if possible, remove some depth arrays */
205 while (notify->array->num_depths > 0 &&
206 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
207 notify->array->num_depths--;
210 /* we might just be able to delete the record */
211 if (notify->array->num_depths == 0) {
212 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
213 if (ret != 0) {
214 return NT_STATUS_INTERNAL_DB_CORRUPTION;
216 return NT_STATUS_OK;
219 tmp_ctx = talloc_new(notify);
220 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
222 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
223 (ndr_push_flags_fn_t)ndr_push_notify_array);
224 if (!NT_STATUS_IS_OK(status)) {
225 talloc_free(tmp_ctx);
226 return status;
229 if (DEBUGLEVEL >= 10) {
230 DEBUG(10, ("notify_save:\n"));
231 NDR_PRINT_DEBUG(notify_array, notify->array);
234 dbuf.dptr = (char *)blob.data;
235 dbuf.dsize = blob.length;
237 ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
238 talloc_free(tmp_ctx);
239 if (ret != 0) {
240 return NT_STATUS_INTERNAL_DB_CORRUPTION;
243 return NT_STATUS_OK;
248 handle incoming notify messages
250 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
251 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
253 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
254 NTSTATUS status;
255 struct notify_event ev;
256 TALLOC_CTX *tmp_ctx = talloc_new(notify);
257 struct notify_list *listel;
259 if (tmp_ctx == NULL) {
260 return;
263 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
264 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
265 if (!NT_STATUS_IS_OK(status)) {
266 talloc_free(tmp_ctx);
267 return;
270 for (listel=notify->list;listel;listel=listel->next) {
271 if (listel->private_data == ev.private_data) {
272 listel->callback(listel->private_data, &ev);
273 break;
277 talloc_free(tmp_ctx);
281 callback from sys_notify telling us about changes from the OS
283 static void sys_notify_callback(struct sys_notify_context *ctx,
284 void *ptr, struct notify_event *ev)
286 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
287 ev->private_data = listel;
288 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
289 ev->action, ev->path));
290 listel->callback(listel->private_data, ev);
294 add an entry to the notify array
296 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
297 void *private_data, int depth)
299 int i;
300 struct notify_depth *d;
301 struct notify_entry *ee;
303 /* possibly expand the depths array */
304 if (depth >= notify->array->num_depths) {
305 d = talloc_realloc(notify->array, notify->array->depth,
306 struct notify_depth, depth+1);
307 NT_STATUS_HAVE_NO_MEMORY(d);
308 for (i=notify->array->num_depths;i<=depth;i++) {
309 ZERO_STRUCT(d[i]);
311 notify->array->depth = d;
312 notify->array->num_depths = depth+1;
314 d = &notify->array->depth[depth];
316 /* expand the entries array */
317 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
318 d->num_entries+1);
319 NT_STATUS_HAVE_NO_MEMORY(ee);
320 d->entries = ee;
322 d->entries[d->num_entries] = *e;
323 d->entries[d->num_entries].private_data = private_data;
324 d->entries[d->num_entries].server = notify->server;
325 d->entries[d->num_entries].path_len = strlen(e->path);
326 d->num_entries++;
328 d->max_mask |= e->filter;
329 d->max_mask_subdir |= e->subdir_filter;
331 if (d->num_entries > 1) {
332 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
335 /* recalculate the maximum masks */
336 d->max_mask = 0;
337 d->max_mask_subdir = 0;
339 for (i=0;i<d->num_entries;i++) {
340 d->max_mask |= d->entries[i].filter;
341 d->max_mask_subdir |= d->entries[i].subdir_filter;
344 return notify_save(notify);
348 add a notify watch. This is called when a notify is first setup on a open
349 directory handle.
351 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
352 void (*callback)(void *, const struct notify_event *),
353 void *private_data)
355 struct notify_entry e = *e0;
356 NTSTATUS status;
357 char *tmp_path = NULL;
358 struct notify_list *listel;
359 size_t len;
360 int depth;
362 /* see if change notify is enabled at all */
363 if (notify == NULL) {
364 return NT_STATUS_NOT_IMPLEMENTED;
367 status = notify_lock(notify);
368 NT_STATUS_NOT_OK_RETURN(status);
370 status = notify_load(notify);
371 if (!NT_STATUS_IS_OK(status)) {
372 goto done;
375 /* cope with /. on the end of the path */
376 len = strlen(e.path);
377 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
378 tmp_path = talloc_strndup(notify, e.path, len-2);
379 if (tmp_path == NULL) {
380 status = NT_STATUS_NO_MEMORY;
381 goto done;
383 e.path = tmp_path;
386 depth = count_chars(e.path, '/');
388 listel = TALLOC_ZERO_P(notify, struct notify_list);
389 if (listel == NULL) {
390 status = NT_STATUS_NO_MEMORY;
391 goto done;
394 listel->private_data = private_data;
395 listel->callback = callback;
396 listel->depth = depth;
397 DLIST_ADD(notify->list, listel);
399 /* ignore failures from sys_notify */
400 if (notify->sys_notify_ctx != NULL) {
402 this call will modify e.filter and e.subdir_filter
403 to remove bits handled by the backend
405 status = sys_notify_watch(notify->sys_notify_ctx, &e,
406 sys_notify_callback, listel,
407 &listel->sys_notify_handle);
408 if (NT_STATUS_IS_OK(status)) {
409 talloc_steal(listel, listel->sys_notify_handle);
413 /* if the system notify handler couldn't handle some of the
414 filter bits, or couldn't handle a request for recursion
415 then we need to install it in the array used for the
416 intra-samba notify handling */
417 if (e.filter != 0 || e.subdir_filter != 0) {
418 status = notify_add_array(notify, &e, private_data, depth);
421 done:
422 notify_unlock(notify);
423 talloc_free(tmp_path);
425 return status;
429 remove a notify watch. Called when the directory handle is closed
431 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
433 NTSTATUS status;
434 struct notify_list *listel;
435 int i, depth;
436 struct notify_depth *d;
438 /* see if change notify is enabled at all */
439 if (notify == NULL) {
440 return NT_STATUS_NOT_IMPLEMENTED;
443 for (listel=notify->list;listel;listel=listel->next) {
444 if (listel->private_data == private_data) {
445 DLIST_REMOVE(notify->list, listel);
446 break;
449 if (listel == NULL) {
450 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
453 depth = listel->depth;
455 talloc_free(listel);
457 status = notify_lock(notify);
458 NT_STATUS_NOT_OK_RETURN(status);
460 status = notify_load(notify);
461 if (!NT_STATUS_IS_OK(status)) {
462 notify_unlock(notify);
463 return status;
466 if (depth >= notify->array->num_depths) {
467 notify_unlock(notify);
468 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
471 /* we only have to search at the depth of this element */
472 d = &notify->array->depth[depth];
474 for (i=0;i<d->num_entries;i++) {
475 if (private_data == d->entries[i].private_data &&
476 cluster_id_equal(&notify->server, &d->entries[i].server)) {
477 break;
480 if (i == d->num_entries) {
481 notify_unlock(notify);
482 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
485 if (i < d->num_entries-1) {
486 memmove(&d->entries[i], &d->entries[i+1],
487 sizeof(d->entries[i])*(d->num_entries-(i+1)));
489 d->num_entries--;
491 status = notify_save(notify);
493 notify_unlock(notify);
495 return status;
499 remove all notify watches for a messaging server
501 static NTSTATUS notify_remove_all(struct notify_context *notify,
502 const struct server_id *server)
504 NTSTATUS status;
505 int i, depth, del_count=0;
507 status = notify_lock(notify);
508 NT_STATUS_NOT_OK_RETURN(status);
510 status = notify_load(notify);
511 if (!NT_STATUS_IS_OK(status)) {
512 notify_unlock(notify);
513 return status;
516 /* we have to search for all entries across all depths, looking for matches
517 for the server id */
518 for (depth=0;depth<notify->array->num_depths;depth++) {
519 struct notify_depth *d = &notify->array->depth[depth];
520 for (i=0;i<d->num_entries;i++) {
521 if (cluster_id_equal(server, &d->entries[i].server)) {
522 if (i < d->num_entries-1) {
523 memmove(&d->entries[i], &d->entries[i+1],
524 sizeof(d->entries[i])*(d->num_entries-(i+1)));
526 i--;
527 d->num_entries--;
528 del_count++;
533 if (del_count > 0) {
534 status = notify_save(notify);
537 notify_unlock(notify);
539 return status;
544 send a notify message to another messaging server
546 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
547 const char *path, uint32_t action)
549 struct notify_event ev;
550 DATA_BLOB data;
551 NTSTATUS status;
552 TALLOC_CTX *tmp_ctx;
554 ev.action = action;
555 ev.path = path;
556 ev.private_data = e->private_data;
558 tmp_ctx = talloc_new(notify);
560 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
561 (ndr_push_flags_fn_t)ndr_push_notify_event);
562 if (!NT_STATUS_IS_OK(status)) {
563 talloc_free(tmp_ctx);
564 return status;
567 status = messaging_send(notify->messaging_ctx, e->server,
568 MSG_PVFS_NOTIFY, &data);
569 talloc_free(tmp_ctx);
570 return status;
575 trigger a notify message for anyone waiting on a matching event
577 This function is called a lot, and needs to be very fast. The unusual data structure
578 and traversal is designed to be fast in the average case, even for large numbers of
579 notifies
581 void notify_trigger(struct notify_context *notify,
582 uint32_t action, uint32_t filter, const char *path)
584 NTSTATUS status;
585 int depth;
586 const char *p, *next_p;
588 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
589 "path=%s\n", (unsigned)action, (unsigned)filter, path));
591 /* see if change notify is enabled at all */
592 if (notify == NULL) {
593 return;
596 again:
597 status = notify_load(notify);
598 if (!NT_STATUS_IS_OK(status)) {
599 return;
602 /* loop along the given path, working with each directory depth separately */
603 for (depth=0,p=path;
604 p && depth < notify->array->num_depths;
605 p=next_p,depth++) {
606 int p_len = p - path;
607 int min_i, max_i, i;
608 struct notify_depth *d = &notify->array->depth[depth];
609 next_p = strchr(p+1, '/');
611 /* see if there are any entries at this depth */
612 if (d->num_entries == 0) continue;
614 /* try to skip based on the maximum mask. If next_p is
615 NULL then we know it will be a 'this directory'
616 match, otherwise it must be a subdir match */
617 if (next_p != NULL) {
618 if (0 == (filter & d->max_mask_subdir)) {
619 continue;
621 } else {
622 if (0 == (filter & d->max_mask)) {
623 continue;
627 /* we know there is an entry here worth looking
628 for. Use a bisection search to find the first entry
629 with a matching path */
630 min_i = 0;
631 max_i = d->num_entries-1;
633 while (min_i < max_i) {
634 struct notify_entry *e;
635 int cmp;
636 i = (min_i+max_i)/2;
637 e = &d->entries[i];
638 cmp = strncmp(path, e->path, p_len);
639 if (cmp == 0) {
640 if (p_len == e->path_len) {
641 max_i = i;
642 } else {
643 max_i = i-1;
645 } else if (cmp < 0) {
646 max_i = i-1;
647 } else {
648 min_i = i+1;
652 if (min_i != max_i) {
653 /* none match */
654 continue;
657 /* we now know that the entries start at min_i */
658 for (i=min_i;i<d->num_entries;i++) {
659 struct notify_entry *e = &d->entries[i];
660 if (p_len != e->path_len ||
661 strncmp(path, e->path, p_len) != 0) break;
662 if (next_p != NULL) {
663 if (0 == (filter & e->subdir_filter)) {
664 continue;
666 } else {
667 if (0 == (filter & e->filter)) {
668 continue;
671 status = notify_send(notify, e, path + e->path_len + 1,
672 action);
674 if (NT_STATUS_EQUAL(
675 status, NT_STATUS_INVALID_HANDLE)) {
676 struct server_id server = e->server;
678 DEBUG(10, ("Deleting notify entries for "
679 "process %s because it's gone\n",
680 procid_str_static(&e->server.id)));
681 notify_remove_all(notify, &server);
682 goto again;