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
{
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
;
37 struct sys_notify_context
*sys_notify_ctx
;
43 struct notify_list
*next
, *prev
;
45 void (*callback
)(void *, const struct notify_event
*);
46 void *sys_notify_handle
;
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
, ¬ify
->server
);
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
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
)) {
90 notify
= talloc(mem_ctx
, struct notify_context
);
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
) {
103 notify
->server
= server
;
104 notify
->messaging_ctx
= messaging_ctx
;
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
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
);
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
);
129 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
135 load the notify array
137 static NTSTATUS
notify_load(struct notify_context
*notify
, struct db_record
*rec
)
144 seqnum
= notify
->db
->get_seqnum(notify
->db
);
146 if (seqnum
== notify
->seqnum
&& notify
->array
!= NULL
) {
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
);
157 if (notify
->db
->fetch(notify
->db
, notify
, notify
->key
, &dbuf
) != 0) {
158 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
164 blob
.data
= (uint8
*)dbuf
.dptr
;
165 blob
.length
= dbuf
.dsize
;
167 if (blob
.length
== 0) {
168 status
= NT_STATUS_OK
;
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
);
180 talloc_free(dbuf
.dptr
);
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
)
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
);
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
);
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
);
250 struct notify_event ev
;
251 TALLOC_CTX
*tmp_ctx
= talloc_new(notify
);
252 struct notify_list
*listel
;
254 if (tmp_ctx
== NULL
) {
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
);
265 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
266 if (listel
->private_data
== ev
.private_data
) {
267 listel
->callback(listel
->private_data
, &ev
);
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
)
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
++) {
307 notify
->array
->depth
= d
;
308 notify
->array
->num_depths
= depth
+1;
310 d
= ¬ify
->array
->depth
[depth
];
312 /* expand the entries array */
313 ee
= talloc_realloc(notify
->array
->depth
, d
->entries
, struct notify_entry
,
315 NT_STATUS_HAVE_NO_MEMORY(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
);
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 */
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
347 NTSTATUS
notify_add(struct notify_context
*notify
, struct notify_entry
*e0
,
348 void (*callback
)(void *, const struct notify_event
*),
351 struct notify_entry e
= *e0
;
353 char *tmp_path
= NULL
;
354 struct notify_list
*listel
;
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
)) {
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
;
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
;
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
);
421 talloc_free(tmp_path
);
427 remove a notify watch. Called when the directory handle is closed
429 NTSTATUS
notify_remove(struct notify_context
*notify
, void *private_data
)
432 struct notify_list
*listel
;
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
);
448 if (listel
== NULL
) {
449 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
452 depth
= listel
->depth
;
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
)) {
465 if (depth
>= notify
->array
->num_depths
) {
467 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
470 /* we only have to search at the depth of this element */
471 d
= ¬ify
->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(¬ify
->server
, &d
->entries
[i
].server
)) {
479 if (i
== d
->num_entries
) {
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)));
490 status
= notify_save(notify
, rec
);
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
)
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
)) {
516 /* we have to search for all entries across all depths, looking for matches
518 for (depth
=0;depth
<notify
->array
->num_depths
;depth
++) {
519 struct notify_depth
*d
= ¬ify
->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)));
534 status
= notify_save(notify
, rec
);
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
;
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
);
567 status
= messaging_send(notify
->messaging_ctx
, e
->server
,
568 MSG_PVFS_NOTIFY
, &data
);
569 talloc_free(tmp_ctx
);
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
581 void notify_trigger(struct notify_context
*notify
,
582 uint32_t action
, uint32_t filter
, const char *path
)
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
) {
597 status
= notify_load(notify
, NULL
);
598 if (!NT_STATUS_IS_OK(status
)) {
602 /* loop along the given path, working with each directory depth separately */
604 p
&& depth
< notify
->array
->num_depths
;
606 int p_len
= p
- path
;
608 struct notify_depth
*d
= ¬ify
->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
)) {
622 if (0 == (filter
& d
->max_mask
)) {
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 */
631 max_i
= d
->num_entries
-1;
633 while (min_i
< max_i
) {
634 struct notify_entry
*e
;
638 cmp
= strncmp(path
, e
->path
, p_len
);
640 if (p_len
== e
->path_len
) {
645 } else if (cmp
< 0) {
652 if (min_i
!= max_i
) {
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
)) {
667 if (0 == (filter
& e
->filter
)) {
671 status
= notify_send(notify
, e
, path
+ e
->path_len
+ 1,
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
);