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.
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
;
36 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 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
, ¬ify
->server
);
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
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
)) {
89 notify
= talloc(mem_ctx
, struct notify_context
);
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
) {
102 notify
->server
= server
;
103 notify
->messaging_ctx
= messaging_ctx
;
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
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
);
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
);
128 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
134 load the notify array
136 static NTSTATUS
notify_load(struct notify_context
*notify
, struct db_record
*rec
)
143 seqnum
= notify
->db
->get_seqnum(notify
->db
);
145 if (seqnum
== notify
->seqnum
&& notify
->array
!= NULL
) {
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
);
156 if (notify
->db
->fetch(notify
->db
, notify
, notify
->key
, &dbuf
) != 0) {
157 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
163 blob
.data
= (uint8
*)dbuf
.dptr
;
164 blob
.length
= dbuf
.dsize
;
166 if (blob
.length
== 0) {
167 status
= NT_STATUS_OK
;
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
);
179 talloc_free(dbuf
.dptr
);
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
)
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
);
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
);
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
);
249 struct notify_event ev
;
250 TALLOC_CTX
*tmp_ctx
= talloc_new(notify
);
251 struct notify_list
*listel
;
253 if (tmp_ctx
== NULL
) {
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
);
264 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
265 if (listel
->private_data
== ev
.private_data
) {
266 listel
->callback(listel
->private_data
, &ev
);
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
)
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
++) {
306 notify
->array
->depth
= d
;
307 notify
->array
->num_depths
= depth
+1;
309 d
= ¬ify
->array
->depth
[depth
];
311 /* expand the entries array */
312 ee
= talloc_realloc(notify
->array
->depth
, d
->entries
, struct notify_entry
,
314 NT_STATUS_HAVE_NO_MEMORY(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
);
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 */
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
346 NTSTATUS
notify_add(struct notify_context
*notify
, struct notify_entry
*e0
,
347 void (*callback
)(void *, const struct notify_event
*),
350 struct notify_entry e
= *e0
;
352 char *tmp_path
= NULL
;
353 struct notify_list
*listel
;
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
)) {
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
;
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
;
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
);
420 talloc_free(tmp_path
);
426 remove a notify watch. Called when the directory handle is closed
428 NTSTATUS
notify_remove(struct notify_context
*notify
, void *private_data
)
431 struct notify_list
*listel
;
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
);
447 if (listel
== NULL
) {
448 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
451 depth
= listel
->depth
;
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
)) {
464 if (depth
>= notify
->array
->num_depths
) {
466 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
469 /* we only have to search at the depth of this element */
470 d
= ¬ify
->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(¬ify
->server
, &d
->entries
[i
].server
)) {
478 if (i
== d
->num_entries
) {
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)));
489 status
= notify_save(notify
, rec
);
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
)
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
)) {
515 /* we have to search for all entries across all depths, looking for matches
517 for (depth
=0;depth
<notify
->array
->num_depths
;depth
++) {
518 struct notify_depth
*d
= ¬ify
->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)));
533 status
= notify_save(notify
, rec
);
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
;
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
);
566 status
= messaging_send(notify
->messaging_ctx
, e
->server
,
567 MSG_PVFS_NOTIFY
, &data
);
568 talloc_free(tmp_ctx
);
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
580 void notify_trigger(struct notify_context
*notify
,
581 uint32_t action
, uint32_t filter
, const char *path
)
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
) {
596 status
= notify_load(notify
, NULL
);
597 if (!NT_STATUS_IS_OK(status
)) {
601 /* loop along the given path, working with each directory depth separately */
603 p
&& depth
< notify
->array
->num_depths
;
605 int p_len
= p
- path
;
607 struct notify_depth
*d
= ¬ify
->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
)) {
621 if (0 == (filter
& d
->max_mask
)) {
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 */
630 max_i
= d
->num_entries
-1;
632 while (min_i
< max_i
) {
633 struct notify_entry
*e
;
637 cmp
= strncmp(path
, e
->path
, p_len
);
639 if (p_len
== e
->path_len
) {
644 } else if (cmp
< 0) {
651 if (min_i
!= max_i
) {
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
)) {
666 if (0 == (filter
& e
->filter
)) {
670 status
= notify_send(notify
, e
, path
+ e
->path_len
+ 1,
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
);