spoolss: use nstring_array in spoolss_EnumPrinterKey.
[Samba.git] / source3 / smbd / notify_internal.c
blob06da717799b1acb2ff7e4d6d048dff3d0b0f42f4
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 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.
26 #include "includes.h"
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;
35 int seqnum;
36 struct sys_notify_context *sys_notify_ctx;
37 TDB_DATA key;
41 struct notify_list {
42 struct notify_list *next, *prev;
43 void *private_data;
44 void (*callback)(void *, const struct notify_event *);
45 void *sys_notify_handle;
46 int depth;
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, &notify->server);
70 return 0;
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
76 via internal messages
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)) {
86 return NULL;
89 notify = talloc(mem_ctx, struct notify_context);
90 if (notify == NULL) {
91 return NULL;
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) {
98 talloc_free(notify);
99 return NULL;
102 notify->server = server;
103 notify->messaging_ctx = messaging_ctx;
104 notify->list = NULL;
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
112 message type */
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);
118 return notify;
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);
127 if (*rec == NULL) {
128 return NT_STATUS_INTERNAL_DB_CORRUPTION;
130 return NT_STATUS_OK;
134 load the notify array
136 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
138 TDB_DATA dbuf;
139 DATA_BLOB blob;
140 NTSTATUS status;
141 int seqnum;
143 seqnum = notify->db->get_seqnum(notify->db);
145 if (seqnum == notify->seqnum && notify->array != NULL) {
146 return NT_STATUS_OK;
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);
155 if (!rec) {
156 if (notify->db->fetch(notify->db, notify, notify->key, &dbuf) != 0) {
157 return NT_STATUS_INTERNAL_DB_CORRUPTION;
159 } else {
160 dbuf = rec->value;
163 blob.data = (uint8 *)dbuf.dptr;
164 blob.length = dbuf.dsize;
166 status = NT_STATUS_OK;
167 if (blob.length > 0) {
168 enum ndr_err_code ndr_err;
169 ndr_err = ndr_pull_struct_blob(&blob, notify->array, NULL, notify->array,
170 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
171 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
172 status = ndr_map_error2ntstatus(ndr_err);
176 if (DEBUGLEVEL >= 10) {
177 DEBUG(10, ("notify_load:\n"));
178 NDR_PRINT_DEBUG(notify_array, notify->array);
181 if (!rec) {
182 talloc_free(dbuf.dptr);
185 return status;
189 compare notify entries for sorting
191 static int notify_compare(const void *p1, const void *p2)
193 const struct notify_entry *e1 = (const struct notify_entry *)p1;
194 const struct notify_entry *e2 = (const struct notify_entry *)p2;
195 return strcmp(e1->path, e2->path);
199 save the notify array
201 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
203 TDB_DATA dbuf;
204 DATA_BLOB blob;
205 NTSTATUS status;
206 enum ndr_err_code ndr_err;
207 TALLOC_CTX *tmp_ctx;
209 /* if possible, remove some depth arrays */
210 while (notify->array->num_depths > 0 &&
211 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
212 notify->array->num_depths--;
215 /* we might just be able to delete the record */
216 if (notify->array->num_depths == 0) {
217 return rec->delete_rec(rec);
220 tmp_ctx = talloc_new(notify);
221 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
223 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, NULL, notify->array,
224 (ndr_push_flags_fn_t)ndr_push_notify_array);
225 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
226 talloc_free(tmp_ctx);
227 return ndr_map_error2ntstatus(ndr_err);
230 if (DEBUGLEVEL >= 10) {
231 DEBUG(10, ("notify_save:\n"));
232 NDR_PRINT_DEBUG(notify_array, notify->array);
235 dbuf.dptr = blob.data;
236 dbuf.dsize = blob.length;
238 status = rec->store(rec, dbuf, TDB_REPLACE);
239 talloc_free(tmp_ctx);
241 return status;
246 handle incoming notify messages
248 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
249 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
251 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
252 enum ndr_err_code ndr_err;
253 struct notify_event ev;
254 TALLOC_CTX *tmp_ctx = talloc_new(notify);
255 struct notify_list *listel;
257 if (tmp_ctx == NULL) {
258 return;
261 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, NULL, &ev,
262 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
263 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
264 talloc_free(tmp_ctx);
265 return;
268 for (listel=notify->list;listel;listel=listel->next) {
269 if (listel->private_data == ev.private_data) {
270 listel->callback(listel->private_data, &ev);
271 break;
275 talloc_free(tmp_ctx);
279 callback from sys_notify telling us about changes from the OS
281 static void sys_notify_callback(struct sys_notify_context *ctx,
282 void *ptr, struct notify_event *ev)
284 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
285 ev->private_data = listel;
286 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
287 ev->action, ev->path));
288 listel->callback(listel->private_data, ev);
292 add an entry to the notify array
294 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
295 struct notify_entry *e,
296 void *private_data, int depth)
298 int i;
299 struct notify_depth *d;
300 struct notify_entry *ee;
302 /* possibly expand the depths array */
303 if (depth >= notify->array->num_depths) {
304 d = talloc_realloc(notify->array, notify->array->depth,
305 struct notify_depth, depth+1);
306 NT_STATUS_HAVE_NO_MEMORY(d);
307 for (i=notify->array->num_depths;i<=depth;i++) {
308 ZERO_STRUCT(d[i]);
310 notify->array->depth = d;
311 notify->array->num_depths = depth+1;
313 d = &notify->array->depth[depth];
315 /* expand the entries array */
316 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
317 d->num_entries+1);
318 NT_STATUS_HAVE_NO_MEMORY(ee);
319 d->entries = ee;
321 d->entries[d->num_entries] = *e;
322 d->entries[d->num_entries].private_data = private_data;
323 d->entries[d->num_entries].server = notify->server;
324 d->entries[d->num_entries].path_len = strlen(e->path);
325 d->num_entries++;
327 d->max_mask |= e->filter;
328 d->max_mask_subdir |= e->subdir_filter;
330 if (d->num_entries > 1) {
331 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
334 /* recalculate the maximum masks */
335 d->max_mask = 0;
336 d->max_mask_subdir = 0;
338 for (i=0;i<d->num_entries;i++) {
339 d->max_mask |= d->entries[i].filter;
340 d->max_mask_subdir |= d->entries[i].subdir_filter;
343 return notify_save(notify, rec);
347 add a notify watch. This is called when a notify is first setup on a open
348 directory handle.
350 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
351 void (*callback)(void *, const struct notify_event *),
352 void *private_data)
354 struct notify_entry e = *e0;
355 NTSTATUS status;
356 char *tmp_path = NULL;
357 struct notify_list *listel;
358 size_t len;
359 int depth;
360 struct db_record *rec;
362 /* see if change notify is enabled at all */
363 if (notify == NULL) {
364 return NT_STATUS_NOT_IMPLEMENTED;
367 status = notify_fetch_locked(notify, &rec);
368 NT_STATUS_NOT_OK_RETURN(status);
370 status = notify_load(notify, rec);
371 if (!NT_STATUS_IS_OK(status)) {
372 talloc_free(rec);
373 return status;
376 /* cope with /. on the end of the path */
377 len = strlen(e.path);
378 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
379 tmp_path = talloc_strndup(notify, e.path, len-2);
380 if (tmp_path == NULL) {
381 status = NT_STATUS_NO_MEMORY;
382 goto done;
384 e.path = tmp_path;
387 depth = count_chars(e.path, '/');
389 listel = TALLOC_ZERO_P(notify, struct notify_list);
390 if (listel == NULL) {
391 status = NT_STATUS_NO_MEMORY;
392 goto done;
395 listel->private_data = private_data;
396 listel->callback = callback;
397 listel->depth = depth;
398 DLIST_ADD(notify->list, listel);
400 /* ignore failures from sys_notify */
401 if (notify->sys_notify_ctx != NULL) {
403 this call will modify e.filter and e.subdir_filter
404 to remove bits handled by the backend
406 status = sys_notify_watch(notify->sys_notify_ctx, &e,
407 sys_notify_callback, listel,
408 &listel->sys_notify_handle);
409 if (NT_STATUS_IS_OK(status)) {
410 talloc_steal(listel, listel->sys_notify_handle);
414 /* if the system notify handler couldn't handle some of the
415 filter bits, or couldn't handle a request for recursion
416 then we need to install it in the array used for the
417 intra-samba notify handling */
418 if (e.filter != 0 || e.subdir_filter != 0) {
419 status = notify_add_array(notify, rec, &e, private_data, depth);
422 done:
423 talloc_free(rec);
424 talloc_free(tmp_path);
426 return status;
430 remove a notify watch. Called when the directory handle is closed
432 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
434 NTSTATUS status;
435 struct notify_list *listel;
436 int i, depth;
437 struct notify_depth *d;
438 struct db_record *rec;
440 /* see if change notify is enabled at all */
441 if (notify == NULL) {
442 return NT_STATUS_NOT_IMPLEMENTED;
445 for (listel=notify->list;listel;listel=listel->next) {
446 if (listel->private_data == private_data) {
447 DLIST_REMOVE(notify->list, listel);
448 break;
451 if (listel == NULL) {
452 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
455 depth = listel->depth;
457 talloc_free(listel);
459 status = notify_fetch_locked(notify, &rec);
460 NT_STATUS_NOT_OK_RETURN(status);
462 status = notify_load(notify, rec);
463 if (!NT_STATUS_IS_OK(status)) {
464 talloc_free(rec);
465 return status;
468 if (depth >= notify->array->num_depths) {
469 talloc_free(rec);
470 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
473 /* we only have to search at the depth of this element */
474 d = &notify->array->depth[depth];
476 for (i=0;i<d->num_entries;i++) {
477 if (private_data == d->entries[i].private_data &&
478 cluster_id_equal(&notify->server, &d->entries[i].server)) {
479 break;
482 if (i == d->num_entries) {
483 talloc_free(rec);
484 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
487 if (i < d->num_entries-1) {
488 memmove(&d->entries[i], &d->entries[i+1],
489 sizeof(d->entries[i])*(d->num_entries-(i+1)));
491 d->num_entries--;
493 status = notify_save(notify, rec);
495 talloc_free(rec);
497 return status;
501 remove all notify watches for a messaging server
503 static NTSTATUS notify_remove_all(struct notify_context *notify,
504 const struct server_id *server)
506 NTSTATUS status;
507 int i, depth, del_count=0;
508 struct db_record *rec;
510 status = notify_fetch_locked(notify, &rec);
511 NT_STATUS_NOT_OK_RETURN(status);
513 status = notify_load(notify, rec);
514 if (!NT_STATUS_IS_OK(status)) {
515 talloc_free(rec);
516 return status;
519 /* we have to search for all entries across all depths, looking for matches
520 for the server id */
521 for (depth=0;depth<notify->array->num_depths;depth++) {
522 struct notify_depth *d = &notify->array->depth[depth];
523 for (i=0;i<d->num_entries;i++) {
524 if (cluster_id_equal(server, &d->entries[i].server)) {
525 if (i < d->num_entries-1) {
526 memmove(&d->entries[i], &d->entries[i+1],
527 sizeof(d->entries[i])*(d->num_entries-(i+1)));
529 i--;
530 d->num_entries--;
531 del_count++;
536 if (del_count > 0) {
537 status = notify_save(notify, rec);
540 talloc_free(rec);
542 return status;
547 send a notify message to another messaging server
549 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
550 const char *path, uint32_t action)
552 struct notify_event ev;
553 DATA_BLOB data;
554 NTSTATUS status;
555 enum ndr_err_code ndr_err;
556 TALLOC_CTX *tmp_ctx;
558 ev.action = action;
559 ev.path = path;
560 ev.private_data = e->private_data;
562 tmp_ctx = talloc_new(notify);
564 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, NULL, &ev,
565 (ndr_push_flags_fn_t)ndr_push_notify_event);
566 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
567 talloc_free(tmp_ctx);
568 return ndr_map_error2ntstatus(ndr_err);
571 status = messaging_send(notify->messaging_ctx, e->server,
572 MSG_PVFS_NOTIFY, &data);
573 talloc_free(tmp_ctx);
574 return status;
579 trigger a notify message for anyone waiting on a matching event
581 This function is called a lot, and needs to be very fast. The unusual data structure
582 and traversal is designed to be fast in the average case, even for large numbers of
583 notifies
585 void notify_trigger(struct notify_context *notify,
586 uint32_t action, uint32_t filter, const char *path)
588 NTSTATUS status;
589 int depth;
590 const char *p, *next_p;
592 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
593 "path=%s\n", (unsigned)action, (unsigned)filter, path));
595 /* see if change notify is enabled at all */
596 if (notify == NULL) {
597 return;
600 again:
601 status = notify_load(notify, NULL);
602 if (!NT_STATUS_IS_OK(status)) {
603 return;
606 /* loop along the given path, working with each directory depth separately */
607 for (depth=0,p=path;
608 p && depth < notify->array->num_depths;
609 p=next_p,depth++) {
610 int p_len = p - path;
611 int min_i, max_i, i;
612 struct notify_depth *d = &notify->array->depth[depth];
613 next_p = strchr(p+1, '/');
615 /* see if there are any entries at this depth */
616 if (d->num_entries == 0) continue;
618 /* try to skip based on the maximum mask. If next_p is
619 NULL then we know it will be a 'this directory'
620 match, otherwise it must be a subdir match */
621 if (next_p != NULL) {
622 if (0 == (filter & d->max_mask_subdir)) {
623 continue;
625 } else {
626 if (0 == (filter & d->max_mask)) {
627 continue;
631 /* we know there is an entry here worth looking
632 for. Use a bisection search to find the first entry
633 with a matching path */
634 min_i = 0;
635 max_i = d->num_entries-1;
637 while (min_i < max_i) {
638 struct notify_entry *e;
639 int cmp;
640 i = (min_i+max_i)/2;
641 e = &d->entries[i];
642 cmp = strncmp(path, e->path, p_len);
643 if (cmp == 0) {
644 if (p_len == e->path_len) {
645 max_i = i;
646 } else {
647 max_i = i-1;
649 } else if (cmp < 0) {
650 max_i = i-1;
651 } else {
652 min_i = i+1;
656 if (min_i != max_i) {
657 /* none match */
658 continue;
661 /* we now know that the entries start at min_i */
662 for (i=min_i;i<d->num_entries;i++) {
663 struct notify_entry *e = &d->entries[i];
664 if (p_len != e->path_len ||
665 strncmp(path, e->path, p_len) != 0) break;
666 if (next_p != NULL) {
667 if (0 == (filter & e->subdir_filter)) {
668 continue;
670 } else {
671 if (0 == (filter & e->filter)) {
672 continue;
675 status = notify_send(notify, e, path + e->path_len + 1,
676 action);
678 if (NT_STATUS_EQUAL(
679 status, NT_STATUS_INVALID_HANDLE)) {
680 struct server_id server = e->server;
682 DEBUG(10, ("Deleting notify entries for "
683 "process %s because it's gone\n",
684 procid_str_static(&e->server)));
685 notify_remove_all(notify, &server);
686 goto again;