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 3 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, see <http://www.gnu.org/licenses/>.
27 * Component: ldb paged results control module
29 * Description: this module caches a complete search and sends back
30 * results in chunks as asked by the client
36 #include "ldb/include/includes.h"
38 struct message_store {
39 /* keep the whole ldb_reply as an optimization
40 * instead of freeing and talloc-ing the container
43 struct message_store *next;
48 struct results_store {
50 struct private_data *priv;
55 struct results_store *prev;
56 struct results_store *next;
58 struct message_store *first;
59 struct message_store *last;
62 struct message_store *first_ref;
63 struct message_store *last_ref;
65 struct ldb_control **controls;
67 struct ldb_request *req;
73 struct results_store *store;
77 int store_destructor(struct results_store *store);
79 int store_destructor(struct results_store *store)
81 DLIST_REMOVE(store->priv->store, store);
85 static struct results_store *new_store(struct private_data *priv)
87 struct results_store *newr;
88 int new_id = priv->next_free_id++;
90 /* TODO: we should have a limit on the number of
91 * outstanding paged searches
94 newr = talloc(priv, struct results_store);
95 if (!newr) return NULL;
99 newr->cookie = talloc_asprintf(newr, "%d", new_id);
105 newr->timestamp = time(NULL);
108 newr->num_entries = 0;
109 newr->first_ref = NULL;
110 newr->controls = NULL;
112 /* put this entry as first */
113 DLIST_ADD(priv->store, newr);
115 talloc_set_destructor(newr, store_destructor);
120 struct paged_context {
121 struct ldb_module *module;
123 int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
127 struct results_store *store;
130 static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
132 int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
134 struct paged_context *ac;
135 struct ldb_handle *h;
137 h = talloc_zero(mem_ctx, struct ldb_handle);
139 ldb_set_errstring(module->ldb, "Out of Memory");
145 ac = talloc_zero(h, struct paged_context);
147 ldb_set_errstring(module->ldb, "Out of Memory");
152 h->private_data = (void *)ac;
154 h->state = LDB_ASYNC_INIT;
155 h->status = LDB_SUCCESS;
158 ac->up_context = context;
159 ac->up_callback = callback;
164 static int paged_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
166 struct paged_context *ac = NULL;
168 if (!context || !ares) {
169 ldb_set_errstring(ldb, "NULL Context or Result in callback");
173 ac = talloc_get_type(context, struct paged_context);
175 if (ares->type == LDB_REPLY_ENTRY) {
176 if (ac->store->first == NULL) {
177 ac->store->first = ac->store->last = talloc(ac->store, struct message_store);
179 ac->store->last->next = talloc(ac->store, struct message_store);
180 ac->store->last = ac->store->last->next;
182 if (ac->store->last == NULL) {
186 ac->store->num_entries++;
188 ac->store->last->r = talloc_steal(ac->store->last, ares);
189 ac->store->last->next = NULL;
192 if (ares->type == LDB_REPLY_REFERRAL) {
193 if (ac->store->first_ref == NULL) {
194 ac->store->first_ref = ac->store->last_ref = talloc(ac->store, struct message_store);
196 ac->store->last_ref->next = talloc(ac->store, struct message_store);
197 ac->store->last_ref = ac->store->last_ref->next;
199 if (ac->store->last_ref == NULL) {
203 ac->store->last_ref->r = talloc_steal(ac->store->last, ares);
204 ac->store->last_ref->next = NULL;
207 if (ares->type == LDB_REPLY_DONE) {
208 ac->store->controls = talloc_move(ac->store, &ares->controls);
216 return LDB_ERR_OPERATIONS_ERROR;
219 static int paged_search(struct ldb_module *module, struct ldb_request *req)
221 struct ldb_control *control;
222 struct private_data *private_data;
223 struct ldb_paged_control *paged_ctrl;
224 struct ldb_control **saved_controls;
225 struct paged_context *ac;
226 struct ldb_handle *h;
229 /* check if there's a paged request control */
230 control = get_control_from_list(req->controls, LDB_CONTROL_PAGED_RESULTS_OID);
231 if (control == NULL) {
232 /* not found go on */
233 return ldb_next_request(module, req);
236 private_data = talloc_get_type(module->private_data, struct private_data);
240 if (!req->callback || !req->context) {
241 ldb_set_errstring(module->ldb,
242 "Async interface called with NULL callback function or NULL context");
243 return LDB_ERR_OPERATIONS_ERROR;
246 paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
248 return LDB_ERR_PROTOCOL_ERROR;
251 h = init_handle(req, module, req->context, req->callback);
253 return LDB_ERR_OPERATIONS_ERROR;
255 ac = talloc_get_type(h->private_data, struct paged_context);
257 ac->size = paged_ctrl->size;
259 /* check if it is a continuation search the store */
260 if (paged_ctrl->cookie_len == 0) {
262 ac->store = new_store(private_data);
263 if (ac->store == NULL) {
265 return LDB_ERR_UNWILLING_TO_PERFORM;
268 ac->store->req = talloc(ac->store, struct ldb_request);
270 return LDB_ERR_OPERATIONS_ERROR;
272 ac->store->req->operation = req->operation;
273 ac->store->req->op.search.base = req->op.search.base;
274 ac->store->req->op.search.scope = req->op.search.scope;
275 ac->store->req->op.search.tree = req->op.search.tree;
276 ac->store->req->op.search.attrs = req->op.search.attrs;
277 ac->store->req->controls = req->controls;
279 /* save it locally and remove it from the list */
280 /* we do not need to replace them later as we
281 * are keeping the original req intact */
282 if (!save_controls(control, ac->store->req, &saved_controls)) {
283 return LDB_ERR_OPERATIONS_ERROR;
286 ac->store->req->context = ac;
287 ac->store->req->callback = paged_search_callback;
288 ldb_set_timeout_from_prev_req(module->ldb, req, ac->store->req);
290 ret = ldb_next_request(module, ac->store->req);
293 struct results_store *current = NULL;
295 for (current = private_data->store; current; current = current->next) {
296 if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
297 current->timestamp = time(NULL);
301 if (current == NULL) {
303 return LDB_ERR_UNWILLING_TO_PERFORM;
312 /* check if it is an abandon */
314 talloc_free(ac->store);
315 h->status = LDB_SUCCESS;
316 h->state = LDB_ASYNC_DONE;
320 /* TODO: age out old outstanding requests */
326 static int paged_results(struct ldb_handle *handle)
328 struct paged_context *ac;
329 struct ldb_paged_control *paged;
330 struct ldb_reply *ares;
331 struct message_store *msg;
332 int i, num_ctrls, ret;
334 ac = talloc_get_type(handle->private_data, struct paged_context);
336 if (ac->store == NULL)
337 return LDB_ERR_OPERATIONS_ERROR;
339 while (ac->store->num_entries > 0 && ac->size > 0) {
340 msg = ac->store->first;
341 ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
342 if (ret != LDB_SUCCESS) {
343 handle->status = ret;
344 handle->state = LDB_ASYNC_DONE;
348 ac->store->first = msg->next;
350 ac->store->num_entries--;
354 handle->state = LDB_ASYNC_DONE;
356 while (ac->store->first_ref != NULL) {
357 msg = ac->store->first_ref;
358 ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
359 if (ret != LDB_SUCCESS) {
360 handle->status = ret;
361 handle->state = LDB_ASYNC_DONE;
365 ac->store->first_ref = msg->next;
369 ares = talloc_zero(ac->store, struct ldb_reply);
371 handle->status = LDB_ERR_OPERATIONS_ERROR;
372 return handle->status;
377 if (ac->store->controls != NULL) {
378 ares->controls = ac->store->controls;
379 while (ares->controls[i]) i++; /* counting */
381 ares->controls = talloc_move(ares, &ac->store->controls);
385 ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, num_ctrls);
386 if (ares->controls == NULL) {
387 handle->status = LDB_ERR_OPERATIONS_ERROR;
388 return handle->status;
391 ares->controls[i] = talloc(ares->controls, struct ldb_control);
392 if (ares->controls[i] == NULL) {
393 handle->status = LDB_ERR_OPERATIONS_ERROR;
394 return handle->status;
397 ares->controls[i]->oid = talloc_strdup(ares->controls[i], LDB_CONTROL_PAGED_RESULTS_OID);
398 if (ares->controls[i]->oid == NULL) {
399 handle->status = LDB_ERR_OPERATIONS_ERROR;
400 return handle->status;
403 ares->controls[i]->critical = 0;
404 ares->controls[i + 1] = NULL;
406 paged = talloc(ares->controls[i], struct ldb_paged_control);
408 handle->status = LDB_ERR_OPERATIONS_ERROR;
409 return handle->status;
412 ares->controls[i]->data = paged;
416 paged->cookie = NULL;
417 paged->cookie_len = 0;
419 paged->size = ac->store->num_entries;
420 paged->cookie = talloc_strdup(paged, ac->store->cookie);
421 paged->cookie_len = strlen(paged->cookie) + 1;
424 ares->type = LDB_REPLY_DONE;
426 ret = ac->up_callback(ac->module->ldb, ac->up_context, ares);
428 handle->status = ret;
433 static int paged_wait(struct ldb_handle *handle, enum ldb_wait_type type)
435 struct paged_context *ac;
438 if (!handle || !handle->private_data) {
439 return LDB_ERR_OPERATIONS_ERROR;
442 if (handle->state == LDB_ASYNC_DONE) {
443 return handle->status;
446 handle->state = LDB_ASYNC_PENDING;
448 ac = talloc_get_type(handle->private_data, struct paged_context);
450 if (ac->store->req->handle->state == LDB_ASYNC_DONE) {
451 /* if lower level is finished we do not need to call it anymore */
452 /* return all we have until size == 0 or we empty storage */
453 ret = paged_results(handle);
455 /* we are done, if num_entries is zero free the storage
456 * as that mean we delivered the last batch */
457 if (ac->store->num_entries == 0) {
458 talloc_free(ac->store);
464 if (type == LDB_WAIT_ALL) {
465 while (ac->store->req->handle->state != LDB_ASYNC_DONE) {
466 ret = ldb_wait(ac->store->req->handle, type);
467 if (ret != LDB_SUCCESS) {
468 handle->state = LDB_ASYNC_DONE;
469 handle->status = ret;
474 ret = paged_results(handle);
476 /* we are done, if num_entries is zero free the storage
477 * as that mean we delivered the last batch */
478 if (ac->store->num_entries == 0) {
479 talloc_free(ac->store);
485 ret = ldb_wait(ac->store->req->handle, type);
486 if (ret != LDB_SUCCESS) {
487 handle->state = LDB_ASYNC_DONE;
488 handle->status = ret;
492 handle->status = ret;
494 if (ac->store->num_entries >= ac->size ||
495 ac->store->req->handle->state == LDB_ASYNC_DONE) {
497 ret = paged_results(handle);
499 /* we are done, if num_entries is zero free the storage
500 * as that mean we delivered the last batch */
501 if (ac->store->num_entries == 0) {
502 talloc_free(ac->store);
509 static int paged_request_init(struct ldb_module *module)
511 struct private_data *data;
512 struct ldb_request *req;
515 data = talloc(module, struct private_data);
517 return LDB_ERR_OTHER;
520 data->next_free_id = 1;
522 module->private_data = data;
524 req = talloc(module, struct ldb_request);
526 return LDB_ERR_OPERATIONS_ERROR;
529 req->operation = LDB_REQ_REGISTER_CONTROL;
530 req->op.reg_control.oid = LDB_CONTROL_PAGED_RESULTS_OID;
531 req->controls = NULL;
533 ret = ldb_request(module->ldb, req);
534 if (ret != LDB_SUCCESS) {
535 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "paged_request: Unable to register control with rootdse!\n");
539 return ldb_next_init(module);
542 static const struct ldb_module_ops paged_ops = {
543 .name = "paged_results",
544 .search = paged_search,
546 .init_context = paged_request_init
549 int ldb_paged_results_init(void)
551 return ldb_register_module(&paged_ops);