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
*newr
;
89 int new_id
= priv
->next_free_id
++;
91 /* TODO: we should have a limit on the number of
92 * outstanding paged searches
95 newr
= talloc(priv
, struct results_store
);
96 if (!newr
) return NULL
;
98 newr
->cookie
= talloc_asprintf(newr
, "%d", new_id
);
104 newr
->timestamp
= time(NULL
);
106 newr
->num_sent
= 0; /* To be removed */
107 newr
->result
= NULL
; /* To be removed */
110 newr
->num_entries
= 0;
111 newr
->first_ref
= NULL
;
112 newr
->controls
= NULL
;
114 /* put this entry as first */
116 newr
->next
= priv
->store
;
117 if (priv
->store
!= NULL
) priv
->store
->prev
= newr
;
120 talloc_set_destructor(newr
, 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
, "Out of Memory");
150 ac
= talloc_zero(h
, struct paged_context
);
152 ldb_set_errstring(module
->ldb
, "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
, "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 ac
->store
->last
->next
= NULL
;
197 if (ares
->type
== LDB_REPLY_REFERRAL
) {
198 if (ac
->store
->first_ref
== NULL
) {
199 ac
->store
->first_ref
= ac
->store
->last_ref
= talloc(ac
->store
, struct message_store
);
201 ac
->store
->last_ref
->next
= talloc(ac
->store
, struct message_store
);
202 ac
->store
->last_ref
= ac
->store
->last_ref
->next
;
204 if (ac
->store
->last_ref
== NULL
) {
208 ac
->store
->last_ref
->r
= talloc_steal(ac
->store
->last
, ares
);
209 ac
->store
->last_ref
->next
= NULL
;
212 if (ares
->type
== LDB_REPLY_DONE
) {
213 ac
->store
->controls
= talloc_move(ac
->store
, ares
->controls
);
221 return LDB_ERR_OPERATIONS_ERROR
;
224 static int paged_search(struct ldb_module
*module
, struct ldb_request
*req
)
226 struct ldb_control
*control
;
227 struct private_data
*private_data
;
228 struct ldb_paged_control
*paged_ctrl
;
229 struct ldb_control
**saved_controls
;
230 struct paged_context
*ac
;
231 struct ldb_handle
*h
;
234 /* check if there's a paged request control */
235 control
= get_control_from_list(req
->controls
, LDB_CONTROL_PAGED_RESULTS_OID
);
236 if (control
== NULL
) {
237 /* not found go on */
238 return ldb_next_request(module
, req
);
241 private_data
= talloc_get_type(module
->private_data
, struct private_data
);
245 if (!req
->callback
|| !req
->context
) {
246 ldb_set_errstring(module
->ldb
,
247 "Async interface called with NULL callback function or NULL context");
248 return LDB_ERR_OPERATIONS_ERROR
;
251 paged_ctrl
= talloc_get_type(control
->data
, struct ldb_paged_control
);
253 return LDB_ERR_PROTOCOL_ERROR
;
256 h
= init_handle(req
, module
, req
->context
, req
->callback
);
258 return LDB_ERR_OPERATIONS_ERROR
;
260 ac
= talloc_get_type(h
->private_data
, struct paged_context
);
262 ac
->size
= paged_ctrl
->size
;
264 /* check if it is a continuation search the store */
265 if (paged_ctrl
->cookie_len
== 0) {
267 ac
->store
= new_store(private_data
);
268 if (ac
->store
== NULL
) {
270 return LDB_ERR_UNWILLING_TO_PERFORM
;
273 ac
->store
->req
= talloc(ac
->store
, struct ldb_request
);
275 return LDB_ERR_OPERATIONS_ERROR
;
277 ac
->store
->req
->operation
= req
->operation
;
278 ac
->store
->req
->op
.search
.base
= req
->op
.search
.base
;
279 ac
->store
->req
->op
.search
.scope
= req
->op
.search
.scope
;
280 ac
->store
->req
->op
.search
.tree
= req
->op
.search
.tree
;
281 ac
->store
->req
->op
.search
.attrs
= req
->op
.search
.attrs
;
282 ac
->store
->req
->controls
= req
->controls
;
284 /* save it locally and remove it from the list */
285 /* we do not need to replace them later as we
286 * are keeping the original req intact */
287 if (!save_controls(control
, ac
->store
->req
, &saved_controls
)) {
288 return LDB_ERR_OPERATIONS_ERROR
;
291 ac
->store
->req
->context
= ac
;
292 ac
->store
->req
->callback
= paged_search_callback
;
293 ldb_set_timeout_from_prev_req(module
->ldb
, req
, ac
->store
->req
);
295 ret
= ldb_next_request(module
, ac
->store
->req
);
298 struct results_store
*current
= NULL
;
300 for (current
= private_data
->store
; current
; current
= current
->next
) {
301 if (strcmp(current
->cookie
, paged_ctrl
->cookie
) == 0) {
302 current
->timestamp
= time(NULL
);
306 if (current
== NULL
) {
308 return LDB_ERR_UNWILLING_TO_PERFORM
;
317 /* check if it is an abandon */
319 talloc_free(ac
->store
);
320 h
->status
= LDB_SUCCESS
;
321 h
->state
= LDB_ASYNC_DONE
;
325 /* TODO: age out old outstanding requests */
331 static int paged_results(struct ldb_handle
*handle
)
333 struct paged_context
*ac
;
334 struct ldb_paged_control
*paged
;
335 struct ldb_reply
*ares
;
336 struct message_store
*msg
;
337 int i
, num_ctrls
, ret
;
339 ac
= talloc_get_type(handle
->private_data
, struct paged_context
);
341 if (ac
->store
== NULL
)
342 return LDB_ERR_OPERATIONS_ERROR
;
344 while (ac
->store
->num_entries
> 0 && ac
->size
> 0) {
345 msg
= ac
->store
->first
;
346 ret
= ac
->up_callback(ac
->module
->ldb
, ac
->up_context
, msg
->r
);
347 if (ret
!= LDB_SUCCESS
) {
348 handle
->status
= ret
;
349 handle
->state
= LDB_ASYNC_DONE
;
353 ac
->store
->first
= msg
->next
;
355 ac
->store
->num_entries
--;
359 handle
->state
= LDB_ASYNC_DONE
;
361 while (ac
->store
->first_ref
!= NULL
) {
362 msg
= ac
->store
->first_ref
;
363 ret
= ac
->up_callback(ac
->module
->ldb
, ac
->up_context
, msg
->r
);
364 if (ret
!= LDB_SUCCESS
) {
365 handle
->status
= ret
;
366 handle
->state
= LDB_ASYNC_DONE
;
370 ac
->store
->first_ref
= msg
->next
;
374 ares
= talloc_zero(ac
->store
, struct ldb_reply
);
376 handle
->status
= LDB_ERR_OPERATIONS_ERROR
;
377 return handle
->status
;
382 if (ac
->store
->controls
!= NULL
) {
383 ares
->controls
= ac
->store
->controls
;
384 while (ares
->controls
[i
]) i
++; /* counting */
386 ares
->controls
= talloc_move(ares
, ac
->store
->controls
);
390 ares
->controls
= talloc_realloc(ares
, ares
->controls
, struct ldb_control
*, num_ctrls
);
391 if (ares
->controls
== NULL
) {
392 handle
->status
= LDB_ERR_OPERATIONS_ERROR
;
393 return handle
->status
;
396 ares
->controls
[i
] = talloc(ares
->controls
, struct ldb_control
);
397 if (ares
->controls
[i
] == NULL
) {
398 handle
->status
= LDB_ERR_OPERATIONS_ERROR
;
399 return handle
->status
;
402 ares
->controls
[i
]->oid
= talloc_strdup(ares
->controls
[i
], LDB_CONTROL_PAGED_RESULTS_OID
);
403 if (ares
->controls
[i
]->oid
== NULL
) {
404 handle
->status
= LDB_ERR_OPERATIONS_ERROR
;
405 return handle
->status
;
408 ares
->controls
[i
]->critical
= 0;
409 ares
->controls
[i
+ 1] = NULL
;
411 paged
= talloc(ares
->controls
[i
], struct ldb_paged_control
);
413 handle
->status
= LDB_ERR_OPERATIONS_ERROR
;
414 return handle
->status
;
417 ares
->controls
[i
]->data
= paged
;
421 paged
->cookie
= NULL
;
422 paged
->cookie_len
= 0;
424 paged
->size
= ac
->store
->num_entries
;
425 paged
->cookie
= talloc_strdup(paged
, ac
->store
->cookie
);
426 paged
->cookie_len
= strlen(paged
->cookie
) + 1;
429 ares
->type
= LDB_REPLY_DONE
;
431 ret
= ac
->up_callback(ac
->module
->ldb
, ac
->up_context
, ares
);
433 handle
->status
= ret
;
438 static int paged_wait(struct ldb_handle
*handle
, enum ldb_wait_type type
)
440 struct paged_context
*ac
;
443 if (!handle
|| !handle
->private_data
) {
444 return LDB_ERR_OPERATIONS_ERROR
;
447 if (handle
->state
== LDB_ASYNC_DONE
) {
448 return handle
->status
;
451 handle
->state
= LDB_ASYNC_PENDING
;
453 ac
= talloc_get_type(handle
->private_data
, struct paged_context
);
455 if (ac
->store
->req
->handle
->state
== LDB_ASYNC_DONE
) {
456 /* if lower level is finished we do not need to call it anymore */
457 /* return all we have until size == 0 or we empty storage */
458 ret
= paged_results(handle
);
460 /* we are done, if num_entries is zero free the storage
461 * as that mean we delivered the last batch */
462 if (ac
->store
->num_entries
== 0) {
463 talloc_free(ac
->store
);
469 if (type
== LDB_WAIT_ALL
) {
470 while (ac
->store
->req
->handle
->state
!= LDB_ASYNC_DONE
) {
471 ret
= ldb_wait(ac
->store
->req
->handle
, type
);
472 if (ret
!= LDB_SUCCESS
) {
473 handle
->state
= LDB_ASYNC_DONE
;
474 handle
->status
= ret
;
479 ret
= paged_results(handle
);
481 /* we are done, if num_entries is zero free the storage
482 * as that mean we delivered the last batch */
483 if (ac
->store
->num_entries
== 0) {
484 talloc_free(ac
->store
);
490 ret
= ldb_wait(ac
->store
->req
->handle
, type
);
491 if (ret
!= LDB_SUCCESS
) {
492 handle
->state
= LDB_ASYNC_DONE
;
493 handle
->status
= ret
;
497 handle
->status
= ret
;
499 if (ac
->store
->num_entries
>= ac
->size
||
500 ac
->store
->req
->handle
->state
== LDB_ASYNC_DONE
) {
502 ret
= paged_results(handle
);
504 /* we are done, if num_entries is zero free the storage
505 * as that mean we delivered the last batch */
506 if (ac
->store
->num_entries
== 0) {
507 talloc_free(ac
->store
);
514 static int paged_request_init(struct ldb_module
*module
)
516 struct private_data
*data
;
517 struct ldb_request
*req
;
520 data
= talloc(module
, struct private_data
);
522 return LDB_ERR_OTHER
;
525 data
->next_free_id
= 1;
527 module
->private_data
= data
;
529 req
= talloc(module
, struct ldb_request
);
531 return LDB_ERR_OPERATIONS_ERROR
;
534 req
->operation
= LDB_REQ_REGISTER_CONTROL
;
535 req
->op
.reg_control
.oid
= LDB_CONTROL_PAGED_RESULTS_OID
;
536 req
->controls
= NULL
;
538 ret
= ldb_request(module
->ldb
, req
);
539 if (ret
!= LDB_SUCCESS
) {
540 ldb_debug(module
->ldb
, LDB_DEBUG_ERROR
, "paged_request: Unable to register control with rootdse!\n");
542 return LDB_ERR_OTHER
;
546 return ldb_next_init(module
);
549 static const struct ldb_module_ops paged_ops
= {
550 .name
= "paged_results",
551 .search
= paged_search
,
553 .init_context
= paged_request_init
556 int ldb_paged_results_init(void)
558 return ldb_register_module(&paged_ops
);