r23432: Remove superfluous comment.
[Samba/bb.git] / source3 / smbd / notify_internal.c
blob20bfb74a2912d5e1311900da9dda1d238522e9bf
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 db_context *db;
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;
38 TDB_DATA key;
42 struct notify_list {
43 struct notify_list *next, *prev;
44 void *private_data;
45 void (*callback)(void *, const struct notify_event *);
46 void *sys_notify_handle;
47 int depth;
50 #define NOTIFY_KEY "notify array"
52 #define NOTIFY_ENABLE "notify:enable"
53 #define NOTIFY_ENABLE_DEFAULT True
55 static NTSTATUS notify_remove_all(struct notify_context *notify,
56 const struct server_id *server);
57 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
58 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
61 destroy the notify context
63 static int notify_destructor(struct notify_context *notify)
65 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
67 if (notify->list != NULL) {
68 notify_remove_all(notify, &notify->server);
71 return 0;
75 Open up the notify.tdb database. You should close it down using
76 talloc_free(). We need the messaging_ctx to allow for notifications
77 via internal messages
79 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
80 struct messaging_context *messaging_ctx,
81 struct event_context *ev,
82 connection_struct *conn)
84 struct notify_context *notify;
86 if (!lp_change_notify(conn->params)) {
87 return NULL;
90 notify = talloc(mem_ctx, struct notify_context);
91 if (notify == NULL) {
92 return NULL;
95 notify->db = db_open(notify, lock_path("notify.tdb"),
96 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
97 O_RDWR|O_CREAT, 0644);
98 if (notify->db == NULL) {
99 talloc_free(notify);
100 return NULL;
103 notify->server = server;
104 notify->messaging_ctx = messaging_ctx;
105 notify->list = NULL;
106 notify->array = NULL;
107 notify->seqnum = notify->db->get_seqnum(notify->db);
108 notify->key = string_term_tdb_data(NOTIFY_KEY);
110 talloc_set_destructor(notify, notify_destructor);
112 /* register with the messaging subsystem for the notify
113 message type */
114 messaging_register(notify->messaging_ctx, notify,
115 MSG_PVFS_NOTIFY, notify_handler);
117 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
119 return notify;
123 lock and fetch the record
125 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
127 *rec = notify->db->fetch_locked(notify->db, notify, notify->key);
128 if (*rec == NULL) {
129 return NT_STATUS_INTERNAL_DB_CORRUPTION;
131 return NT_STATUS_OK;
135 load the notify array
137 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
139 TDB_DATA dbuf;
140 DATA_BLOB blob;
141 NTSTATUS status;
142 int seqnum;
144 seqnum = notify->db->get_seqnum(notify->db);
146 if (seqnum == notify->seqnum && notify->array != NULL) {
147 return NT_STATUS_OK;
150 notify->seqnum = seqnum;
152 talloc_free(notify->array);
153 notify->array = TALLOC_ZERO_P(notify, struct notify_array);
154 NT_STATUS_HAVE_NO_MEMORY(notify->array);
156 if (!rec) {
157 if (notify->db->fetch(notify->db, notify, notify->key, &dbuf) != 0) {
158 return NT_STATUS_INTERNAL_DB_CORRUPTION;
160 } else {
161 dbuf = rec->value;
164 blob.data = (uint8 *)dbuf.dptr;
165 blob.length = dbuf.dsize;
167 if (blob.length == 0) {
168 status = NT_STATUS_OK;
169 } else {
170 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
171 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
174 if (DEBUGLEVEL >= 10) {
175 DEBUG(10, ("notify_load:\n"));
176 NDR_PRINT_DEBUG(notify_array, notify->array);
179 if (!rec) {
180 talloc_free(dbuf.dptr);
183 return status;
187 compare notify entries for sorting
189 static int notify_compare(const void *p1, const void *p2)
191 const struct notify_entry *e1 = (const struct notify_entry *)p1;
192 const struct notify_entry *e2 = (const struct notify_entry *)p2;
193 return strcmp(e1->path, e2->path);
197 save the notify array
199 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
201 TDB_DATA dbuf;
202 DATA_BLOB blob;
203 NTSTATUS status;
204 TALLOC_CTX *tmp_ctx;
206 /* if possible, remove some depth arrays */
207 while (notify->array->num_depths > 0 &&
208 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
209 notify->array->num_depths--;
212 /* we might just be able to delete the record */
213 if (notify->array->num_depths == 0) {
214 return rec->delete_rec(rec);
217 tmp_ctx = talloc_new(notify);
218 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
220 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
221 (ndr_push_flags_fn_t)ndr_push_notify_array);
222 if (!NT_STATUS_IS_OK(status)) {
223 talloc_free(tmp_ctx);
224 return status;
227 if (DEBUGLEVEL >= 10) {
228 DEBUG(10, ("notify_save:\n"));
229 NDR_PRINT_DEBUG(notify_array, notify->array);
232 dbuf.dptr = blob.data;
233 dbuf.dsize = blob.length;
235 status = rec->store(rec, dbuf, TDB_REPLACE);
236 talloc_free(tmp_ctx);
238 return status;
243 handle incoming notify messages
245 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
246 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
248 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
249 NTSTATUS status;
250 struct notify_event ev;
251 TALLOC_CTX *tmp_ctx = talloc_new(notify);
252 struct notify_list *listel;
254 if (tmp_ctx == NULL) {
255 return;
258 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
259 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
260 if (!NT_STATUS_IS_OK(status)) {
261 talloc_free(tmp_ctx);
262 return;
265 for (listel=notify->list;listel;listel=listel->next) {
266 if (listel->private_data == ev.private_data) {
267 listel->callback(listel->private_data, &ev);
268 break;
272 talloc_free(tmp_ctx);
276 callback from sys_notify telling us about changes from the OS
278 static void sys_notify_callback(struct sys_notify_context *ctx,
279 void *ptr, struct notify_event *ev)
281 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
282 ev->private_data = listel;
283 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
284 ev->action, ev->path));
285 listel->callback(listel->private_data, ev);
289 add an entry to the notify array
291 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
292 struct notify_entry *e,
293 void *private_data, int depth)
295 int i;
296 struct notify_depth *d;
297 struct notify_entry *ee;
299 /* possibly expand the depths array */
300 if (depth >= notify->array->num_depths) {
301 d = talloc_realloc(notify->array, notify->array->depth,
302 struct notify_depth, depth+1);
303 NT_STATUS_HAVE_NO_MEMORY(d);
304 for (i=notify->array->num_depths;i<=depth;i++) {
305 ZERO_STRUCT(d[i]);
307 notify->array->depth = d;
308 notify->array->num_depths = depth+1;
310 d = &notify->array->depth[depth];
312 /* expand the entries array */
313 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
314 d->num_entries+1);
315 NT_STATUS_HAVE_NO_MEMORY(ee);
316 d->entries = ee;
318 d->entries[d->num_entries] = *e;
319 d->entries[d->num_entries].private_data = private_data;
320 d->entries[d->num_entries].server = notify->server;
321 d->entries[d->num_entries].path_len = strlen(e->path);
322 d->num_entries++;
324 d->max_mask |= e->filter;
325 d->max_mask_subdir |= e->subdir_filter;
327 if (d->num_entries > 1) {
328 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
331 /* recalculate the maximum masks */
332 d->max_mask = 0;
333 d->max_mask_subdir = 0;
335 for (i=0;i<d->num_entries;i++) {
336 d->max_mask |= d->entries[i].filter;
337 d->max_mask_subdir |= d->entries[i].subdir_filter;
340 return notify_save(notify, rec);
344 add a notify watch. This is called when a notify is first setup on a open
345 directory handle.
347 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
348 void (*callback)(void *, const struct notify_event *),
349 void *private_data)
351 struct notify_entry e = *e0;
352 NTSTATUS status;
353 char *tmp_path = NULL;
354 struct notify_list *listel;
355 size_t len;
356 int depth;
357 struct db_record *rec;
359 /* see if change notify is enabled at all */
360 if (notify == NULL) {
361 return NT_STATUS_NOT_IMPLEMENTED;
364 status = notify_fetch_locked(notify, &rec);
365 NT_STATUS_NOT_OK_RETURN(status);
367 status = notify_load(notify, rec);
368 if (!NT_STATUS_IS_OK(status)) {
369 talloc_free(rec);
370 return status;
373 /* cope with /. on the end of the path */
374 len = strlen(e.path);
375 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
376 tmp_path = talloc_strndup(notify, e.path, len-2);
377 if (tmp_path == NULL) {
378 status = NT_STATUS_NO_MEMORY;
379 goto done;
381 e.path = tmp_path;
384 depth = count_chars(e.path, '/');
386 listel = TALLOC_ZERO_P(notify, struct notify_list);
387 if (listel == NULL) {
388 status = NT_STATUS_NO_MEMORY;
389 goto done;
392 listel->private_data = private_data;
393 listel->callback = callback;
394 listel->depth = depth;
395 DLIST_ADD(notify->list, listel);
397 /* ignore failures from sys_notify */
398 if (notify->sys_notify_ctx != NULL) {
400 this call will modify e.filter and e.subdir_filter
401 to remove bits handled by the backend
403 status = sys_notify_watch(notify->sys_notify_ctx, &e,
404 sys_notify_callback, listel,
405 &listel->sys_notify_handle);
406 if (NT_STATUS_IS_OK(status)) {
407 talloc_steal(listel, listel->sys_notify_handle);
411 /* if the system notify handler couldn't handle some of the
412 filter bits, or couldn't handle a request for recursion
413 then we need to install it in the array used for the
414 intra-samba notify handling */
415 if (e.filter != 0 || e.subdir_filter != 0) {
416 status = notify_add_array(notify, rec, &e, private_data, depth);
419 done:
420 talloc_free(rec);
421 talloc_free(tmp_path);
423 return status;
427 remove a notify watch. Called when the directory handle is closed
429 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
431 NTSTATUS status;
432 struct notify_list *listel;
433 int i, depth;
434 struct notify_depth *d;
435 struct db_record *rec;
437 /* see if change notify is enabled at all */
438 if (notify == NULL) {
439 return NT_STATUS_NOT_IMPLEMENTED;
442 for (listel=notify->list;listel;listel=listel->next) {
443 if (listel->private_data == private_data) {
444 DLIST_REMOVE(notify->list, listel);
445 break;
448 if (listel == NULL) {
449 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
452 depth = listel->depth;
454 talloc_free(listel);
456 status = notify_fetch_locked(notify, &rec);
457 NT_STATUS_NOT_OK_RETURN(status);
459 status = notify_load(notify, rec);
460 if (!NT_STATUS_IS_OK(status)) {
461 talloc_free(rec);
462 return status;
465 if (depth >= notify->array->num_depths) {
466 talloc_free(rec);
467 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
470 /* we only have to search at the depth of this element */
471 d = &notify->array->depth[depth];
473 for (i=0;i<d->num_entries;i++) {
474 if (private_data == d->entries[i].private_data &&
475 cluster_id_equal(&notify->server, &d->entries[i].server)) {
476 break;
479 if (i == d->num_entries) {
480 talloc_free(rec);
481 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
484 if (i < d->num_entries-1) {
485 memmove(&d->entries[i], &d->entries[i+1],
486 sizeof(d->entries[i])*(d->num_entries-(i+1)));
488 d->num_entries--;
490 status = notify_save(notify, rec);
492 talloc_free(rec);
494 return status;
498 remove all notify watches for a messaging server
500 static NTSTATUS notify_remove_all(struct notify_context *notify,
501 const struct server_id *server)
503 NTSTATUS status;
504 int i, depth, del_count=0;
505 struct db_record *rec;
507 status = notify_fetch_locked(notify, &rec);
508 NT_STATUS_NOT_OK_RETURN(status);
510 status = notify_load(notify, rec);
511 if (!NT_STATUS_IS_OK(status)) {
512 talloc_free(rec);
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, rec);
537 talloc_free(rec);
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, NULL);
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)));
681 notify_remove_all(notify, &server);
682 goto again;