4 Copyright (C) Simo Sorce 2005-2008
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
35 #include "ldb_includes.h"
37 struct message_store
{
38 /* keep the whole ldb_reply as an optimization
39 * instead of freeing and talloc-ing the container
42 struct message_store
*next
;
47 struct results_store
{
49 struct private_data
*priv
;
54 struct results_store
*next
;
56 struct message_store
*first
;
57 struct message_store
*last
;
60 struct message_store
*first_ref
;
61 struct message_store
*last_ref
;
63 struct ldb_control
**controls
;
69 struct results_store
*store
;
73 int store_destructor(struct results_store
*del
)
75 struct private_data
*priv
= del
->priv
;
76 struct results_store
*loop
;
78 if (priv
->store
== del
) {
79 priv
->store
= del
->next
;
83 for (loop
= priv
->store
; loop
; loop
= loop
->next
) {
84 if (loop
->next
== del
) {
85 loop
->next
= del
->next
;
90 /* is not in list ? */
94 static struct results_store
*new_store(struct private_data
*priv
)
96 struct results_store
*newr
;
97 int new_id
= priv
->next_free_id
++;
99 /* TODO: we should have a limit on the number of
100 * outstanding paged searches
103 newr
= talloc(priv
, struct results_store
);
104 if (!newr
) return NULL
;
108 newr
->cookie
= talloc_asprintf(newr
, "%d", new_id
);
114 newr
->timestamp
= time(NULL
);
117 newr
->num_entries
= 0;
118 newr
->first_ref
= NULL
;
119 newr
->controls
= NULL
;
121 newr
->next
= priv
->store
;
124 talloc_set_destructor(newr
, store_destructor
);
129 struct paged_context
{
130 struct ldb_module
*module
;
131 struct ldb_request
*req
;
133 struct results_store
*store
;
135 struct ldb_control
**controls
;
138 static int paged_results(struct paged_context
*ac
)
140 struct ldb_paged_control
*paged
;
141 struct message_store
*msg
;
142 int i
, num_ctrls
, ret
;
144 if (ac
->store
== NULL
) {
145 return LDB_ERR_OPERATIONS_ERROR
;
148 while (ac
->store
->num_entries
> 0 && ac
->size
> 0) {
149 msg
= ac
->store
->first
;
150 ret
= ldb_module_send_entry(ac
->req
, msg
->r
->message
);
151 if (ret
!= LDB_SUCCESS
) {
155 ac
->store
->first
= msg
->next
;
157 ac
->store
->num_entries
--;
161 while (ac
->store
->first_ref
!= NULL
) {
162 msg
= ac
->store
->first_ref
;
163 ret
= ldb_module_send_referral(ac
->req
, msg
->r
->referral
);
164 if (ret
!= LDB_SUCCESS
) {
168 ac
->store
->first_ref
= msg
->next
;
172 /* return result done */
176 if (ac
->store
->controls
!= NULL
) {
177 while (ac
->store
->controls
[i
]) i
++; /* counting */
182 ac
->controls
= talloc_array(ac
, struct ldb_control
*, num_ctrls
+1);
183 if (ac
->controls
== NULL
) {
184 return LDB_ERR_OPERATIONS_ERROR
;
186 ac
->controls
[num_ctrls
] = NULL
;
188 for (i
= 0; i
< (num_ctrls
-1); i
++) {
189 ac
->controls
[i
] = talloc_reference(ac
->controls
, ac
->store
->controls
[i
]);
192 ac
->controls
[i
] = talloc(ac
->controls
, struct ldb_control
);
193 if (ac
->controls
[i
] == NULL
) {
194 return LDB_ERR_OPERATIONS_ERROR
;
197 ac
->controls
[i
]->oid
= talloc_strdup(ac
->controls
[i
],
198 LDB_CONTROL_PAGED_RESULTS_OID
);
199 if (ac
->controls
[i
]->oid
== NULL
) {
200 return LDB_ERR_OPERATIONS_ERROR
;
203 ac
->controls
[i
]->critical
= 0;
205 paged
= talloc(ac
->controls
[i
], struct ldb_paged_control
);
207 return LDB_ERR_OPERATIONS_ERROR
;
210 ac
->controls
[i
]->data
= paged
;
214 paged
->cookie
= NULL
;
215 paged
->cookie_len
= 0;
217 paged
->size
= ac
->store
->num_entries
;
218 paged
->cookie
= talloc_strdup(paged
, ac
->store
->cookie
);
219 paged
->cookie_len
= strlen(paged
->cookie
) + 1;
225 static int paged_search_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
227 struct paged_context
*ac
;
228 struct message_store
*msg_store
;
231 ac
= talloc_get_type(req
->context
, struct paged_context
);
234 return ldb_module_done(ac
->req
, NULL
, NULL
,
235 LDB_ERR_OPERATIONS_ERROR
);
237 if (ares
->error
!= LDB_SUCCESS
) {
238 return ldb_module_done(ac
->req
, ares
->controls
,
239 ares
->response
, ares
->error
);
242 switch (ares
->type
) {
243 case LDB_REPLY_ENTRY
:
244 msg_store
= talloc(ac
->store
, struct message_store
);
245 if (msg_store
== NULL
) {
246 return ldb_module_done(ac
->req
, NULL
, NULL
,
247 LDB_ERR_OPERATIONS_ERROR
);
249 msg_store
->next
= NULL
;
250 msg_store
->r
= talloc_steal(msg_store
, ares
);
252 if (ac
->store
->first
== NULL
) {
253 ac
->store
->first
= msg_store
;
255 ac
->store
->last
->next
= msg_store
;
257 ac
->store
->last
= msg_store
;
259 ac
->store
->num_entries
++;
263 case LDB_REPLY_REFERRAL
:
264 msg_store
= talloc(ac
->store
, struct message_store
);
265 if (msg_store
== NULL
) {
266 return ldb_module_done(ac
->req
, NULL
, NULL
,
267 LDB_ERR_OPERATIONS_ERROR
);
269 msg_store
->next
= NULL
;
270 msg_store
->r
= talloc_steal(msg_store
, ares
);
272 if (ac
->store
->first_ref
== NULL
) {
273 ac
->store
->first_ref
= msg_store
;
275 ac
->store
->last_ref
->next
= msg_store
;
277 ac
->store
->last_ref
= msg_store
;
282 ac
->store
->controls
= talloc_move(ac
->store
, &ares
->controls
);
283 ret
= paged_results(ac
);
284 return ldb_module_done(ac
->req
, ac
->controls
,
285 ares
->response
, ret
);
291 static int paged_search(struct ldb_module
*module
, struct ldb_request
*req
)
293 struct ldb_control
*control
;
294 struct private_data
*private_data
;
295 struct ldb_paged_control
*paged_ctrl
;
296 struct ldb_control
**saved_controls
;
297 struct ldb_request
*search_req
;
298 struct paged_context
*ac
;
301 /* check if there's a paged request control */
302 control
= ldb_request_get_control(req
, LDB_CONTROL_PAGED_RESULTS_OID
);
303 if (control
== NULL
) {
304 /* not found go on */
305 return ldb_next_request(module
, req
);
308 paged_ctrl
= talloc_get_type(control
->data
, struct ldb_paged_control
);
310 return LDB_ERR_PROTOCOL_ERROR
;
313 private_data
= talloc_get_type(module
->private_data
, struct private_data
);
315 ac
= talloc_zero(req
, struct paged_context
);
317 ldb_set_errstring(module
->ldb
, "Out of Memory");
318 return LDB_ERR_OPERATIONS_ERROR
;
323 ac
->size
= paged_ctrl
->size
;
325 /* check if it is a continuation search the store */
326 if (paged_ctrl
->cookie_len
== 0) {
327 if (paged_ctrl
->size
== 0) {
328 return LDB_ERR_OPERATIONS_ERROR
;
331 ac
->store
= new_store(private_data
);
332 if (ac
->store
== NULL
) {
333 return LDB_ERR_OPERATIONS_ERROR
;
336 ret
= ldb_build_search_req_ex(&search_req
, module
->ldb
, ac
,
338 req
->op
.search
.scope
,
340 req
->op
.search
.attrs
,
343 paged_search_callback
,
346 /* save it locally and remove it from the list */
347 /* we do not need to replace them later as we
348 * are keeping the original req intact */
349 if (!save_controls(control
, search_req
, &saved_controls
)) {
350 return LDB_ERR_OPERATIONS_ERROR
;
353 return ldb_next_request(module
, search_req
);
356 struct results_store
*current
= NULL
;
358 /* TODO: age out old outstanding requests */
359 for (current
= private_data
->store
; current
; current
= current
->next
) {
360 if (strcmp(current
->cookie
, paged_ctrl
->cookie
) == 0) {
361 current
->timestamp
= time(NULL
);
365 if (current
== NULL
) {
366 return LDB_ERR_UNWILLING_TO_PERFORM
;
371 /* check if it is an abandon */
373 return ldb_module_done(req
, NULL
, NULL
,
377 ret
= paged_results(ac
);
378 if (ret
!= LDB_SUCCESS
) {
379 return ldb_module_done(req
, NULL
, NULL
, ret
);
381 return ldb_module_done(req
, ac
->controls
, NULL
,
386 static int paged_request_init(struct ldb_module
*module
)
388 struct private_data
*data
;
391 data
= talloc(module
, struct private_data
);
393 return LDB_ERR_OTHER
;
396 data
->next_free_id
= 1;
398 module
->private_data
= data
;
400 ret
= ldb_mod_register_control(module
, LDB_CONTROL_PAGED_RESULTS_OID
);
401 if (ret
!= LDB_SUCCESS
) {
402 ldb_debug(module
->ldb
, LDB_DEBUG_WARNING
,
404 "Unable to register control with rootdse!\n");
407 return ldb_next_init(module
);
410 const struct ldb_module_ops ldb_paged_results_module_ops
= {
411 .name
= "paged_results",
412 .search
= paged_search
,
413 .init_context
= paged_request_init