r14755: the change notify code now passes most of the RAW-NOTIFY test. Still
[Samba/bb.git] / source4 / ntvfs / common / notify.c
blobbc04c830f162096bb36a4a5b11fb48ce4a1cb6c4
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 "system/filesys.h"
29 #include "lib/tdb/include/tdb.h"
30 #include "lib/tdb/include/tdbutil.h"
31 #include "messaging/messaging.h"
32 #include "db_wrap.h"
33 #include "lib/messaging/irpc.h"
34 #include "librpc/gen_ndr/ndr_notify.h"
35 #include "dlinklist.h"
37 struct notify_context {
38 struct tdb_wrap *w;
39 uint32_t server;
40 struct messaging_context *messaging_ctx;
41 struct notify_list *list;
42 struct notify_array *array;
46 struct notify_list {
47 struct notify_list *next, *prev;
48 void *private;
49 void (*callback)(void *, const struct notify_event *);
52 #define NOTIFY_KEY "notify array"
54 static NTSTATUS notify_remove_all(struct notify_context *notify);
55 static void notify_handler(struct messaging_context *msg_ctx, void *private,
56 uint32_t msg_type, uint32_t server_id, DATA_BLOB *data);
59 destroy the notify context
61 static int notify_destructor(void *p)
63 struct notify_context *notify = talloc_get_type(p, struct notify_context);
64 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
65 notify_remove_all(notify);
66 return 0;
70 Open up the notify.tdb database. You should close it down using
71 talloc_free(). We need the messaging_ctx to allow for notifications
72 via internal messages
74 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, uint32_t server,
75 struct messaging_context *messaging_ctx)
77 char *path;
78 struct notify_context *notify;
80 notify = talloc(mem_ctx, struct notify_context);
81 if (notify == NULL) {
82 return NULL;
85 path = smbd_tmp_path(notify, "notify.tdb");
86 notify->w = tdb_wrap_open(notify, path, 0,
87 TDB_DEFAULT,
88 O_RDWR|O_CREAT, 0600);
89 talloc_free(path);
90 if (notify->w == NULL) {
91 talloc_free(notify);
92 return NULL;
95 notify->server = server;
96 notify->messaging_ctx = messaging_ctx;
97 notify->list = NULL;
98 notify->array = NULL;
100 talloc_set_destructor(notify, notify_destructor);
102 /* register with the messaging subsystem for the notify
103 message type */
104 messaging_register(notify->messaging_ctx, notify,
105 MSG_PVFS_NOTIFY, notify_handler);
107 return notify;
111 load the notify array
113 static NTSTATUS notify_load(struct notify_context *notify)
115 TDB_DATA dbuf;
116 DATA_BLOB blob;
117 NTSTATUS status;
119 talloc_free(notify->array);
120 notify->array = talloc_zero(notify, struct notify_array);
121 NT_STATUS_HAVE_NO_MEMORY(notify->array);
123 dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
124 if (dbuf.dptr == NULL) {
125 return NT_STATUS_OK;
128 blob.data = dbuf.dptr;
129 blob.length = dbuf.dsize;
131 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
132 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
133 free(dbuf.dptr);
135 return status;
140 save the notify array
142 static NTSTATUS notify_save(struct notify_context *notify)
144 TDB_DATA dbuf;
145 DATA_BLOB blob;
146 NTSTATUS status;
147 int ret;
148 TALLOC_CTX *tmp_ctx;
150 if (notify->array->num_entries == 0) {
151 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
152 if (ret != 0) {
153 return NT_STATUS_INTERNAL_DB_CORRUPTION;
155 return NT_STATUS_OK;
158 tmp_ctx = talloc_new(notify);
160 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
161 (ndr_push_flags_fn_t)ndr_push_notify_array);
162 if (!NT_STATUS_IS_OK(status)) {
163 talloc_free(tmp_ctx);
164 return status;
167 dbuf.dptr = blob.data;
168 dbuf.dsize = blob.length;
170 ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
171 talloc_free(tmp_ctx);
172 if (ret != 0) {
173 return NT_STATUS_INTERNAL_DB_CORRUPTION;
176 return NT_STATUS_OK;
181 handle incoming notify messages
183 static void notify_handler(struct messaging_context *msg_ctx, void *private,
184 uint32_t msg_type, uint32_t server_id, DATA_BLOB *data)
186 struct notify_context *notify = talloc_get_type(private, struct notify_context);
187 NTSTATUS status;
188 struct notify_event ev;
189 TALLOC_CTX *tmp_ctx = talloc_new(notify);
190 struct notify_list *listel;
192 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
193 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
194 if (!NT_STATUS_IS_OK(status)) {
195 talloc_free(tmp_ctx);
196 return;
199 for (listel=notify->list;listel;listel=listel->next) {
200 if (listel->private == ev.private) {
201 listel->callback(listel->private, &ev);
202 break;
206 talloc_free(tmp_ctx);
210 add a notify watch. This is called when a notify is first setup on a open
211 directory handle.
213 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e,
214 void (*callback)(void *, const struct notify_event *),
215 void *private)
217 NTSTATUS status;
218 struct notify_list *listel;
220 status = notify_load(notify);
221 NT_STATUS_NOT_OK_RETURN(status);
223 notify->array->entries = talloc_realloc(notify->array, notify->array->entries,
224 struct notify_entry,
225 notify->array->num_entries+1);
227 if (notify->array->entries == NULL) {
228 return NT_STATUS_NO_MEMORY;
231 notify->array->entries[notify->array->num_entries] = *e;
232 notify->array->entries[notify->array->num_entries].private = private;
233 notify->array->entries[notify->array->num_entries].server = notify->server;
234 notify->array->num_entries++;
236 status = notify_save(notify);
237 NT_STATUS_NOT_OK_RETURN(status);
239 listel = talloc(notify, struct notify_list);
240 NT_STATUS_HAVE_NO_MEMORY(listel);
242 listel->private = private;
243 listel->callback = callback;
244 DLIST_ADD(notify->list, listel);
246 return status;
250 remove a notify watch. Called when the directory handle is closed
252 NTSTATUS notify_remove(struct notify_context *notify, void *private)
254 NTSTATUS status;
255 struct notify_list *listel;
256 int i;
258 for (listel=notify->list;listel;listel=listel->next) {
259 if (listel->private == private) {
260 DLIST_REMOVE(notify->list, listel);
261 break;
264 if (listel == NULL) {
265 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
268 status = notify_load(notify);
269 NT_STATUS_NOT_OK_RETURN(status);
271 for (i=0;i<notify->array->num_entries;i++) {
272 if (notify->server == notify->array->entries[i].server &&
273 private == notify->array->entries[i].private) {
274 break;
277 if (i == notify->array->num_entries) {
278 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
281 if (i < notify->array->num_entries-1) {
282 memmove(&notify->array->entries[i], &notify->array->entries[i+1],
283 sizeof(notify->array->entries[i])*(notify->array->num_entries-(i+1)));
285 notify->array->num_entries--;
287 return notify_save(notify);
291 remove all notify watches for this messaging server
293 static NTSTATUS notify_remove_all(struct notify_context *notify)
295 NTSTATUS status;
296 int i;
298 if (notify->list == NULL) {
299 return NT_STATUS_OK;
302 status = notify_load(notify);
303 NT_STATUS_NOT_OK_RETURN(status);
305 for (i=0;i<notify->array->num_entries;i++) {
306 if (notify->server == notify->array->entries[i].server) {
307 if (i < notify->array->num_entries-1) {
308 memmove(&notify->array->entries[i], &notify->array->entries[i+1],
309 sizeof(notify->array->entries[i])*(notify->array->num_entries-(i+1)));
311 i--;
312 notify->array->num_entries--;
317 return notify_save(notify);
322 see if a notify event matches
324 static BOOL notify_match(struct notify_context *notify, struct notify_entry *e,
325 const char *path, uint32_t action)
327 size_t len = strlen(e->path);
329 /* TODO: check action */
331 if (strncmp(path, e->path, len) != 0) {
332 return False;
335 if (path[len] != '/') {
336 return False;
339 if (!e->recursive) {
340 if (strchr(&path[len+1], '/') != NULL) {
341 return False;
345 return True;
350 send a notify message to another messaging server
352 static void notify_send(struct notify_context *notify, struct notify_entry *e,
353 const char *path, uint32_t action)
355 struct notify_event ev;
356 DATA_BLOB data;
357 NTSTATUS status;
358 TALLOC_CTX *tmp_ctx;
360 ev.action = action;
361 ev.path = path;
362 ev.private = e->private;
364 tmp_ctx = talloc_new(notify);
366 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
367 (ndr_push_flags_fn_t)ndr_push_notify_event);
368 if (!NT_STATUS_IS_OK(status)) {
369 talloc_free(tmp_ctx);
370 return;
373 status = messaging_send(notify->messaging_ctx, e->server,
374 MSG_PVFS_NOTIFY, &data);
375 talloc_free(tmp_ctx);
379 trigger a notify message for anyone waiting on a matching event
381 void notify_trigger(struct notify_context *notify,
382 uint32_t action, const char *path)
384 NTSTATUS status;
385 int i;
387 status = notify_load(notify);
388 if (!NT_STATUS_IS_OK(status)) {
389 return;
392 /* this needs to be changed to a log(n) search */
393 for (i=0;i<notify->array->num_entries;i++) {
394 if (notify_match(notify, &notify->array->entries[i], path, action)) {
395 notify_send(notify, &notify->array->entries[i],
396 path + strlen(notify->array->entries[i].path) + 1,
397 action);