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.
28 #include "librpc/gen_ndr/ndr_notify.h"
30 struct notify_context
{
32 struct server_id server
;
33 struct messaging_context
*messaging_ctx
;
34 struct notify_list
*list
;
35 struct notify_array
*array
;
37 struct sys_notify_context
*sys_notify_ctx
;
42 struct notify_list
*next
, *prev
;
44 void (*callback
)(void *, const struct notify_event
*);
45 void *sys_notify_handle
;
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 static void notify_handler(struct messaging_context
*msg_ctx
, void *private_data
,
56 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
);
59 destroy the notify context
61 static int notify_destructor(struct notify_context
*notify
)
63 messaging_deregister(notify
->messaging_ctx
, MSG_PVFS_NOTIFY
, notify
);
64 notify_remove_all(notify
);
69 Open up the notify.tdb database. You should close it down using
70 talloc_free(). We need the messaging_ctx to allow for notifications
73 struct notify_context
*notify_init(TALLOC_CTX
*mem_ctx
, struct server_id server
,
74 struct messaging_context
*messaging_ctx
,
75 struct event_context
*ev
,
76 connection_struct
*conn
)
78 struct notify_context
*notify
;
80 if (!lp_change_notify(conn
->params
)) {
84 notify
= talloc(mem_ctx
, struct notify_context
);
89 notify
->w
= tdb_wrap_open(notify
, lock_path("notify.tdb"),
90 0, TDB_SEQNUM
|TDB_CLEAR_IF_FIRST
,
91 O_RDWR
|O_CREAT
, 0644);
92 if (notify
->w
== NULL
) {
97 notify
->server
= server
;
98 notify
->messaging_ctx
= messaging_ctx
;
100 notify
->array
= NULL
;
101 notify
->seqnum
= tdb_get_seqnum(notify
->w
->tdb
);
103 talloc_set_destructor(notify
, notify_destructor
);
105 /* register with the messaging subsystem for the notify
107 messaging_register(notify
->messaging_ctx
, notify
,
108 MSG_PVFS_NOTIFY
, notify_handler
);
110 notify
->sys_notify_ctx
= sys_notify_context_create(conn
, notify
, ev
);
119 static NTSTATUS
notify_lock(struct notify_context
*notify
)
121 if (tdb_lock_bystring(notify
->w
->tdb
, NOTIFY_KEY
) != 0) {
122 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
130 static void notify_unlock(struct notify_context
*notify
)
132 tdb_unlock_bystring(notify
->w
->tdb
, NOTIFY_KEY
);
136 load the notify array
138 static NTSTATUS
notify_load(struct notify_context
*notify
)
145 seqnum
= tdb_get_seqnum(notify
->w
->tdb
);
147 if (seqnum
== notify
->seqnum
&& notify
->array
!= NULL
) {
151 notify
->seqnum
= seqnum
;
153 talloc_free(notify
->array
);
154 notify
->array
= talloc_zero(notify
, struct notify_array
);
155 NT_STATUS_HAVE_NO_MEMORY(notify
->array
);
157 dbuf
= tdb_fetch_bystring(notify
->w
->tdb
, NOTIFY_KEY
);
158 if (dbuf
.dptr
== NULL
) {
162 blob
.data
= (uint8
*)dbuf
.dptr
;
163 blob
.length
= dbuf
.dsize
;
165 status
= ndr_pull_struct_blob(&blob
, notify
->array
, notify
->array
,
166 (ndr_pull_flags_fn_t
)ndr_pull_notify_array
);
173 compare notify entries for sorting
175 static int notify_compare(const void *p1
, const void *p2
)
177 const struct notify_entry
*e1
= (const struct notify_entry
*)p1
;
178 const struct notify_entry
*e2
= (const struct notify_entry
*)p2
;
179 return strcmp(e1
->path
, e2
->path
);
183 save the notify array
185 static NTSTATUS
notify_save(struct notify_context
*notify
)
193 /* if possible, remove some depth arrays */
194 while (notify
->array
->num_depths
> 0 &&
195 notify
->array
->depth
[notify
->array
->num_depths
-1].num_entries
== 0) {
196 notify
->array
->num_depths
--;
199 /* we might just be able to delete the record */
200 if (notify
->array
->num_depths
== 0) {
201 ret
= tdb_delete_bystring(notify
->w
->tdb
, NOTIFY_KEY
);
203 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
208 tmp_ctx
= talloc_new(notify
);
209 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx
);
211 status
= ndr_push_struct_blob(&blob
, tmp_ctx
, notify
->array
,
212 (ndr_push_flags_fn_t
)ndr_push_notify_array
);
213 if (!NT_STATUS_IS_OK(status
)) {
214 talloc_free(tmp_ctx
);
218 dbuf
.dptr
= (char *)blob
.data
;
219 dbuf
.dsize
= blob
.length
;
221 ret
= tdb_store_bystring(notify
->w
->tdb
, NOTIFY_KEY
, dbuf
, TDB_REPLACE
);
222 talloc_free(tmp_ctx
);
224 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
232 handle incoming notify messages
234 static void notify_handler(struct messaging_context
*msg_ctx
, void *private_data
,
235 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
)
237 struct notify_context
*notify
= talloc_get_type(private_data
, struct notify_context
);
239 struct notify_event ev
;
240 TALLOC_CTX
*tmp_ctx
= talloc_new(notify
);
241 struct notify_list
*listel
;
243 if (tmp_ctx
== NULL
) {
247 status
= ndr_pull_struct_blob(data
, tmp_ctx
, &ev
,
248 (ndr_pull_flags_fn_t
)ndr_pull_notify_event
);
249 if (!NT_STATUS_IS_OK(status
)) {
250 talloc_free(tmp_ctx
);
254 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
255 if (listel
->private_data
== ev
.private_data
) {
256 listel
->callback(listel
->private_data
, &ev
);
261 talloc_free(tmp_ctx
);
265 callback from sys_notify telling us about changes from the OS
267 static void sys_notify_callback(struct sys_notify_context
*ctx
,
268 void *ptr
, struct notify_event
*ev
)
270 struct notify_list
*listel
= talloc_get_type(ptr
, struct notify_list
);
271 ev
->private_data
= listel
;
272 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
273 ev
->action
, ev
->path
));
274 listel
->callback(listel
->private_data
, ev
);
278 add an entry to the notify array
280 static NTSTATUS
notify_add_array(struct notify_context
*notify
, struct notify_entry
*e
,
281 void *private_data
, int depth
)
284 struct notify_depth
*d
;
285 struct notify_entry
*ee
;
287 /* possibly expand the depths array */
288 if (depth
>= notify
->array
->num_depths
) {
289 d
= talloc_realloc(notify
->array
, notify
->array
->depth
,
290 struct notify_depth
, depth
+1);
291 NT_STATUS_HAVE_NO_MEMORY(d
);
292 for (i
=notify
->array
->num_depths
;i
<=depth
;i
++) {
295 notify
->array
->depth
= d
;
296 notify
->array
->num_depths
= depth
+1;
298 d
= ¬ify
->array
->depth
[depth
];
300 /* expand the entries array */
301 ee
= talloc_realloc(notify
->array
->depth
, d
->entries
, struct notify_entry
,
303 NT_STATUS_HAVE_NO_MEMORY(ee
);
306 d
->entries
[d
->num_entries
] = *e
;
307 d
->entries
[d
->num_entries
].private_data
= private_data
;
308 d
->entries
[d
->num_entries
].server
= notify
->server
;
309 d
->entries
[d
->num_entries
].path_len
= strlen(e
->path
);
312 d
->max_mask
|= e
->filter
;
313 d
->max_mask_subdir
|= e
->subdir_filter
;
315 if (d
->num_entries
> 1) {
316 qsort(d
->entries
, d
->num_entries
, sizeof(d
->entries
[0]), notify_compare
);
319 /* recalculate the maximum masks */
321 d
->max_mask_subdir
= 0;
323 for (i
=0;i
<d
->num_entries
;i
++) {
324 d
->max_mask
|= d
->entries
[i
].filter
;
325 d
->max_mask_subdir
|= d
->entries
[i
].subdir_filter
;
328 return notify_save(notify
);
332 add a notify watch. This is called when a notify is first setup on a open
335 NTSTATUS
notify_add(struct notify_context
*notify
, struct notify_entry
*e0
,
336 void (*callback
)(void *, const struct notify_event
*),
339 struct notify_entry e
= *e0
;
341 char *tmp_path
= NULL
;
342 struct notify_list
*listel
;
346 /* see if change notify is enabled at all */
347 if (notify
== NULL
) {
348 return NT_STATUS_NOT_IMPLEMENTED
;
351 status
= notify_lock(notify
);
352 NT_STATUS_NOT_OK_RETURN(status
);
354 status
= notify_load(notify
);
355 if (!NT_STATUS_IS_OK(status
)) {
359 /* cope with /. on the end of the path */
360 len
= strlen(e
.path
);
361 if (len
> 1 && e
.path
[len
-1] == '.' && e
.path
[len
-2] == '/') {
362 tmp_path
= talloc_strndup(notify
, e
.path
, len
-2);
363 if (tmp_path
== NULL
) {
364 status
= NT_STATUS_NO_MEMORY
;
370 depth
= count_chars(e
.path
, '/');
372 listel
= talloc_zero(notify
, struct notify_list
);
373 if (listel
== NULL
) {
374 status
= NT_STATUS_NO_MEMORY
;
378 listel
->private_data
= private_data
;
379 listel
->callback
= callback
;
380 listel
->depth
= depth
;
381 DLIST_ADD(notify
->list
, listel
);
383 /* ignore failures from sys_notify */
384 if (notify
->sys_notify_ctx
!= NULL
) {
386 this call will modify e.filter and e.subdir_filter
387 to remove bits handled by the backend
389 status
= sys_notify_watch(notify
->sys_notify_ctx
, &e
,
390 sys_notify_callback
, listel
,
391 &listel
->sys_notify_handle
);
392 if (NT_STATUS_IS_OK(status
)) {
393 talloc_steal(listel
, listel
->sys_notify_handle
);
397 /* if the system notify handler couldn't handle some of the
398 filter bits, or couldn't handle a request for recursion
399 then we need to install it in the array used for the
400 intra-samba notify handling */
401 if (e
.filter
!= 0 || e
.subdir_filter
!= 0) {
402 status
= notify_add_array(notify
, &e
, private_data
, depth
);
406 notify_unlock(notify
);
407 talloc_free(tmp_path
);
413 remove a notify watch. Called when the directory handle is closed
415 NTSTATUS
notify_remove(struct notify_context
*notify
, void *private_data
)
418 struct notify_list
*listel
;
420 struct notify_depth
*d
;
422 /* see if change notify is enabled at all */
423 if (notify
== NULL
) {
424 return NT_STATUS_NOT_IMPLEMENTED
;
427 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
428 if (listel
->private_data
== private_data
) {
429 DLIST_REMOVE(notify
->list
, listel
);
433 if (listel
== NULL
) {
434 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
437 depth
= listel
->depth
;
441 status
= notify_lock(notify
);
442 NT_STATUS_NOT_OK_RETURN(status
);
444 status
= notify_load(notify
);
445 if (!NT_STATUS_IS_OK(status
)) {
446 notify_unlock(notify
);
450 if (depth
>= notify
->array
->num_depths
) {
451 notify_unlock(notify
);
452 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
455 /* we only have to search at the depth of this element */
456 d
= ¬ify
->array
->depth
[depth
];
458 for (i
=0;i
<d
->num_entries
;i
++) {
459 if (private_data
== d
->entries
[i
].private_data
&&
460 cluster_id_equal(¬ify
->server
, &d
->entries
[i
].server
)) {
464 if (i
== d
->num_entries
) {
465 notify_unlock(notify
);
466 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
469 if (i
< d
->num_entries
-1) {
470 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
471 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
475 status
= notify_save(notify
);
477 notify_unlock(notify
);
483 remove all notify watches for this messaging server
485 static NTSTATUS
notify_remove_all(struct notify_context
*notify
)
488 int i
, depth
, del_count
=0;
490 if (notify
->list
== NULL
) {
494 status
= notify_lock(notify
);
495 NT_STATUS_NOT_OK_RETURN(status
);
497 status
= notify_load(notify
);
498 if (!NT_STATUS_IS_OK(status
)) {
499 notify_unlock(notify
);
503 /* we have to search for all entries across all depths, looking for matches
505 for (depth
=0;depth
<notify
->array
->num_depths
;depth
++) {
506 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
507 for (i
=0;i
<d
->num_entries
;i
++) {
508 if (cluster_id_equal(¬ify
->server
, &d
->entries
[i
].server
)) {
509 if (i
< d
->num_entries
-1) {
510 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
511 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
521 status
= notify_save(notify
);
524 notify_unlock(notify
);
531 send a notify message to another messaging server
533 static void notify_send(struct notify_context
*notify
, struct notify_entry
*e
,
534 const char *path
, uint32_t action
)
536 struct notify_event ev
;
543 ev
.private_data
= e
->private_data
;
545 tmp_ctx
= talloc_new(notify
);
547 status
= ndr_push_struct_blob(&data
, tmp_ctx
, &ev
,
548 (ndr_push_flags_fn_t
)ndr_push_notify_event
);
549 if (!NT_STATUS_IS_OK(status
)) {
550 talloc_free(tmp_ctx
);
554 status
= messaging_send(notify
->messaging_ctx
, e
->server
,
555 MSG_PVFS_NOTIFY
, &data
);
556 talloc_free(tmp_ctx
);
561 trigger a notify message for anyone waiting on a matching event
563 This function is called a lot, and needs to be very fast. The unusual data structure
564 and traversal is designed to be fast in the average case, even for large numbers of
567 void notify_trigger(struct notify_context
*notify
,
568 uint32_t action
, uint32_t filter
, const char *path
)
572 const char *p
, *next_p
;
574 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
575 "path=%s\n", (unsigned)action
, (unsigned)filter
, path
));
577 /* see if change notify is enabled at all */
578 if (notify
== NULL
) {
582 status
= notify_load(notify
);
583 if (!NT_STATUS_IS_OK(status
)) {
587 /* loop along the given path, working with each directory depth separately */
589 p
&& depth
< notify
->array
->num_depths
;
591 int p_len
= p
- path
;
593 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
594 next_p
= strchr(p
+1, '/');
596 /* see if there are any entries at this depth */
597 if (d
->num_entries
== 0) continue;
599 /* try to skip based on the maximum mask. If next_p is
600 NULL then we know it will be a 'this directory'
601 match, otherwise it must be a subdir match */
602 if (next_p
!= NULL
) {
603 if (0 == (filter
& d
->max_mask_subdir
)) {
607 if (0 == (filter
& d
->max_mask
)) {
612 /* we know there is an entry here worth looking
613 for. Use a bisection search to find the first entry
614 with a matching path */
616 max_i
= d
->num_entries
-1;
618 while (min_i
< max_i
) {
619 struct notify_entry
*e
;
623 cmp
= strncmp(path
, e
->path
, p_len
);
625 if (p_len
== e
->path_len
) {
630 } else if (cmp
< 0) {
637 if (min_i
!= max_i
) {
642 /* we now know that the entries start at min_i */
643 for (i
=min_i
;i
<d
->num_entries
;i
++) {
644 struct notify_entry
*e
= &d
->entries
[i
];
645 if (p_len
!= e
->path_len
||
646 strncmp(path
, e
->path
, p_len
) != 0) break;
647 if (next_p
!= NULL
) {
648 if (0 == (filter
& e
->subdir_filter
)) {
652 if (0 == (filter
& e
->filter
)) {
656 notify_send(notify
, e
, path
+ e
->path_len
+ 1, action
);