4 Copyright (C) Simo Sorce 2005-2006
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 * Component: ldb paged results control module
30 * Description: this module caches a complete search and sends back
31 * results in chunks as asked by the client
37 #include "ldb/include/includes.h"
39 struct message_store {
40 /* keep the whole ldb_reply as an optimization
41 * instead of freeing and talloc-ing the container
44 struct message_store *next;
47 struct results_store {
50 int num_sent; /* To be removed */
51 struct ldb_result *result; /* To be removed */
52 struct results_store *prev;
53 struct results_store *next;
55 struct message_store *first;
56 struct message_store *last;
59 struct message_store *first_ref;
60 struct message_store *last_ref;
62 struct ldb_control **controls;
64 struct ldb_request *req;
70 struct results_store *store;
74 int store_destructor(struct results_store *store)
77 store->prev->next = store->next;
80 store->next->prev = store->prev;
86 static struct results_store *new_store(struct private_data *priv)
88 struct results_store *new;
89 int new_id = priv->next_free_id++;
91 /* TODO: we should have a limit on the number of
92 * outstanding paged searches
95 new = talloc(priv, struct results_store);
96 if (!new) return NULL;
98 new->cookie = talloc_asprintf(new, "%d", new_id);
104 new->timestamp = time(NULL);
106 new->num_sent = 0; /* To be removed */
107 new->result = NULL; /* To be removed */
110 new->num_entries = 0;
111 new->first_ref = NULL;
112 new->controls = NULL;
114 /* put this entry as first */
116 new->next = priv->store;
117 if (priv->store != NULL) priv->store->prev = new;
120 talloc_set_destructor(new, store_destructor);
125 struct paged_context {
126 struct ldb_module *module;
128 int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
132 struct results_store *store;
135 static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
137 int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
139 struct paged_context *ac;
140 struct ldb_handle *h;
142 h = talloc_zero(mem_ctx, struct ldb_handle);
144 ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
150 ac = talloc_zero(h, struct paged_context);
152 ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
157 h->private_data = (void *)ac;
159 h->state = LDB_ASYNC_INIT;
160 h->status = LDB_SUCCESS;
163 ac->up_context = context;
164 ac->up_callback = callback;
169 static int paged_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
171 struct paged_context *ac = NULL;
173 if (!context || !ares) {
174 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
178 ac = talloc_get_type(context, struct paged_context);
180 if (ares->type == LDB_REPLY_ENTRY) {
181 if (ac->store->first == NULL) {
182 ac->store->first = ac->store->last = talloc(ac->store, struct message_store);
184 ac->store->last->next = talloc(ac->store, struct message_store);
185 ac->store->last = ac->store->last->next;
187 if (ac->store->last == NULL) {
191 ac->store->num_entries++;
193 ac->store->last->r = talloc_steal(ac->store->last, ares);
194 if (ac->store->last->r == NULL) {
197 ac->store->last->next = NULL;
200 if (ares->type == LDB_REPLY_REFERRAL) {
201 if (ac->store->first_ref == NULL) {
202 ac->store->first_ref = ac->store->last_ref = talloc(ac->store, struct message_store);
204 ac->store->last_ref->next = talloc(ac->store, struct message_store);
205 ac->store->last_ref = ac->store->last_ref->next;
207 if (ac->store->last_ref == NULL) {
211 ac->store->last_ref->r = talloc_steal(ac->store->last, ares);
212 if (ac->store->last_ref->r == NULL) {
215 ac->store->last_ref->next = NULL;
218 if (ares->type == LDB_REPLY_DONE) {
219 if (ares->controls) {
220 ac->store->controls = talloc_steal(ac->store, ares->controls);
221 if (! ac->store->controls) {
232 return LDB_ERR_OPERATIONS_ERROR;
235 static int paged_search(struct ldb_module *module, struct ldb_request *req)
237 struct ldb_control *control;
238 struct private_data *private_data;
239 struct ldb_paged_control *paged_ctrl;
240 struct ldb_control **saved_controls;
241 struct paged_context *ac;
242 struct ldb_handle *h;
245 /* check if there's a paged request control */
246 control = get_control_from_list(req->controls, LDB_CONTROL_PAGED_RESULTS_OID);
247 if (control == NULL) {
248 /* not found go on */
249 return ldb_next_request(module, req);
252 private_data = talloc_get_type(module->private_data, struct private_data);
256 if (!req->callback || !req->context) {
257 ldb_set_errstring(module->ldb, talloc_asprintf(module,
258 "Async interface called with NULL callback function or NULL context"));
259 return LDB_ERR_OPERATIONS_ERROR;
262 paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
264 return LDB_ERR_PROTOCOL_ERROR;
267 h = init_handle(req, module, req->context, req->callback);
269 return LDB_ERR_OPERATIONS_ERROR;
271 ac = talloc_get_type(h->private_data, struct paged_context);
273 ac->size = paged_ctrl->size;
275 /* check if it is a continuation search the store */
276 if (paged_ctrl->cookie_len == 0) {
278 ac->store = new_store(private_data);
279 if (ac->store == NULL) {
281 return LDB_ERR_UNWILLING_TO_PERFORM;
284 ac->store->req = talloc(ac->store, struct ldb_request);
286 return LDB_ERR_OPERATIONS_ERROR;
288 ac->store->req->operation = req->operation;
289 ac->store->req->op.search.base = req->op.search.base;
290 ac->store->req->op.search.scope = req->op.search.scope;
291 ac->store->req->op.search.tree = req->op.search.tree;
292 ac->store->req->op.search.attrs = req->op.search.attrs;
293 ac->store->req->controls = req->controls;
295 /* save it locally and remove it from the list */
296 /* we do not need to replace them later as we
297 * are keeping the original req intact */
298 if (!save_controls(control, ac->store->req, &saved_controls)) {
299 return LDB_ERR_OPERATIONS_ERROR;
302 ac->store->req->context = ac;
303 ac->store->req->callback = paged_search_callback;
304 ldb_set_timeout_from_prev_req(module->ldb, req, ac->store->req);
306 ret = ldb_next_request(module, ac->store->req);
309 struct results_store *current = NULL;
311 for (current = private_data->store; current; current = current->next) {
312 if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
313 current->timestamp = time(NULL);
317 if (current == NULL) {
319 return LDB_ERR_UNWILLING_TO_PERFORM;
328 /* check if it is an abandon */
330 talloc_free(ac->store);
331 h->status = LDB_SUCCESS;
332 h->state = LDB_ASYNC_DONE;
336 /* TODO: age out old outstanding requests */
342 static int paged_results(struct ldb_handle *handle)
344 struct paged_context *ac;
345 struct ldb_paged_control *paged;
346 struct ldb_reply *ares;
347 struct message_store *msg;
348 int i, num_ctrls, ret;
350 ac = talloc_get_type(handle->private_data, struct paged_context);
352 if (ac->store == NULL)
353 return LDB_ERR_OPERATIONS_ERROR;
355 while (ac->store->num_entries > 0 && ac->size > 0) {
356 msg = ac->store->first;
357 ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
358 if (ret != LDB_SUCCESS) {
359 handle->status = ret;
360 handle->state = LDB_ASYNC_DONE;
364 ac->store->first = msg->next;
366 ac->store->num_entries--;
370 handle->state = LDB_ASYNC_DONE;
372 while (ac->store->first_ref != NULL) {
373 msg = ac->store->first_ref;
374 ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
375 if (ret != LDB_SUCCESS) {
376 handle->status = ret;
377 handle->state = LDB_ASYNC_DONE;
381 ac->store->first_ref = msg->next;
385 ares = talloc_zero(ac->store, struct ldb_reply);
387 handle->status = LDB_ERR_OPERATIONS_ERROR;
388 return handle->status;
393 if (ac->store->controls != NULL) {
394 ares->controls = ac->store->controls;
395 while (ares->controls[i]) i++; /* counting */
397 ares->controls = talloc_steal(ares, ac->store->controls);
401 ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, num_ctrls);
402 if (ares->controls == NULL) {
403 handle->status = LDB_ERR_OPERATIONS_ERROR;
404 return handle->status;
407 ares->controls[i] = talloc(ares->controls, struct ldb_control);
408 if (ares->controls[i] == NULL) {
409 handle->status = LDB_ERR_OPERATIONS_ERROR;
410 return handle->status;
413 ares->controls[i]->oid = talloc_strdup(ares->controls[i], LDB_CONTROL_PAGED_RESULTS_OID);
414 if (ares->controls[i]->oid == NULL) {
415 handle->status = LDB_ERR_OPERATIONS_ERROR;
416 return handle->status;
419 ares->controls[i]->critical = 0;
420 ares->controls[i + 1] = NULL;
422 paged = talloc(ares->controls[i], struct ldb_paged_control);
424 handle->status = LDB_ERR_OPERATIONS_ERROR;
425 return handle->status;
428 ares->controls[i]->data = paged;
432 paged->cookie = NULL;
433 paged->cookie_len = 0;
435 paged->size = ac->store->num_entries;
436 paged->cookie = talloc_strdup(paged, ac->store->cookie);
437 paged->cookie_len = strlen(paged->cookie) + 1;
440 ares->type = LDB_REPLY_DONE;
442 ret = ac->up_callback(ac->module->ldb, ac->up_context, ares);
444 handle->status = ret;
449 static int paged_wait(struct ldb_handle *handle, enum ldb_wait_type type)
451 struct paged_context *ac;
454 if (!handle || !handle->private_data) {
455 return LDB_ERR_OPERATIONS_ERROR;
458 if (handle->state == LDB_ASYNC_DONE) {
459 return handle->status;
462 handle->state = LDB_ASYNC_PENDING;
464 ac = talloc_get_type(handle->private_data, struct paged_context);
466 if (ac->store->req->handle->state == LDB_ASYNC_DONE) {
467 /* if lower level is finished we do not need to call it anymore */
468 /* return all we have until size == 0 or we empty storage */
469 ret = paged_results(handle);
471 /* we are done, if num_entries is zero free the storage
472 * as that mean we delivered the last batch */
473 if (ac->store->num_entries == 0) {
474 talloc_free(ac->store);
480 if (type == LDB_WAIT_ALL) {
481 while (ac->store->req->handle->state != LDB_ASYNC_DONE) {
482 ret = ldb_wait(ac->store->req->handle, type);
483 if (ret != LDB_SUCCESS) {
484 handle->state = LDB_ASYNC_DONE;
485 handle->status = ret;
490 ret = paged_results(handle);
492 /* we are done, if num_entries is zero free the storage
493 * as that mean we delivered the last batch */
494 if (ac->store->num_entries == 0) {
495 talloc_free(ac->store);
501 ret = ldb_wait(ac->store->req->handle, type);
502 if (ret != LDB_SUCCESS) {
503 handle->state = LDB_ASYNC_DONE;
504 handle->status = ret;
508 handle->status = ret;
510 if (ac->store->num_entries >= ac->size ||
511 ac->store->req->handle->state == LDB_ASYNC_DONE) {
513 ret = paged_results(handle);
515 /* we are done, if num_entries is zero free the storage
516 * as that mean we delivered the last batch */
517 if (ac->store->num_entries == 0) {
518 talloc_free(ac->store);
525 static int paged_request_init(struct ldb_module *module)
527 struct private_data *data;
528 struct ldb_request *req;
531 data = talloc(module, struct private_data);
533 return LDB_ERR_OTHER;
536 data->next_free_id = 1;
538 module->private_data = data;
540 req = talloc(module, struct ldb_request);
542 return LDB_ERR_OPERATIONS_ERROR;
545 req->operation = LDB_REQ_REGISTER_CONTROL;
546 req->op.reg_control.oid = LDB_CONTROL_PAGED_RESULTS_OID;
547 req->controls = NULL;
549 ret = ldb_request(module->ldb, req);
550 if (ret != LDB_SUCCESS) {
551 ldb_debug(module->ldb, LDB_DEBUG_ERROR, "paged_request: Unable to register control with rootdse!\n");
553 return LDB_ERR_OTHER;
557 return ldb_next_init(module);
560 static const struct ldb_module_ops paged_ops = {
561 .name = "paged_results",
562 .search = paged_search,
564 .init_context = paged_request_init
567 int ldb_paged_results_init(void)
569 return ldb_register_module(&paged_ops);