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"
36
#include "ldb_module.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 *next;
57
struct message_store *first;
58
struct message_store *last;
61
struct message_store *first_ref;
62
struct message_store *last_ref;
64
struct ldb_control **controls;
70
struct results_store *store;
74
static int store_destructor(struct results_store *del)
76
struct private_data *priv = del->priv;
77
struct results_store *loop;
79
if (priv->store == del) {
80
priv->store = del->next;
84
for (loop = priv->store; loop; loop = loop->next) {
85
if (loop->next == del) {
86
loop->next = del->next;
91
/* is not in list ? */
95
static struct results_store *new_store(struct private_data *priv)
97
struct results_store *newr;
98
int new_id = priv->next_free_id++;
100
/* TODO: we should have a limit on the number of
101
* outstanding paged searches
104
newr = talloc(priv, struct results_store);
105
if (!newr) return NULL;
109
newr->cookie = talloc_asprintf(newr, "%d", new_id);
115
newr->timestamp = time(NULL);
118
newr->num_entries = 0;
119
newr->first_ref = NULL;
120
newr->controls = NULL;
122
newr->next = priv->store;
125
talloc_set_destructor(newr, store_destructor);
130
struct paged_context {
131
struct ldb_module *module;
132
struct ldb_request *req;
134
struct results_store *store;
136
struct ldb_control **controls;
139
static int paged_results(struct paged_context *ac)
141
struct ldb_paged_control *paged;
142
struct message_store *msg;
143
int i, num_ctrls, ret;
145
if (ac->store == NULL) {
146
return LDB_ERR_OPERATIONS_ERROR;
149
while (ac->store->num_entries > 0 && ac->size > 0) {
150
msg = ac->store->first;
151
ret = ldb_module_send_entry(ac->req, msg->r->message, msg->r->controls);
152
if (ret != LDB_SUCCESS) {
156
ac->store->first = msg->next;
158
ac->store->num_entries--;
162
while (ac->store->first_ref != NULL) {
163
msg = ac->store->first_ref;
164
ret = ldb_module_send_referral(ac->req, msg->r->referral);
165
if (ret != LDB_SUCCESS) {
169
ac->store->first_ref = msg->next;
173
/* return result done */
177
if (ac->store->controls != NULL) {
178
while (ac->store->controls[i]) i++; /* counting */
183
ac->controls = talloc_array(ac, struct ldb_control *, num_ctrls +1);
184
if (ac->controls == NULL) {
185
return LDB_ERR_OPERATIONS_ERROR;
187
ac->controls[num_ctrls] = NULL;
189
for (i = 0; i < (num_ctrls -1); i++) {
190
ac->controls[i] = talloc_reference(ac->controls, ac->store->controls[i]);
193
ac->controls[i] = talloc(ac->controls, struct ldb_control);
194
if (ac->controls[i] == NULL) {
195
return LDB_ERR_OPERATIONS_ERROR;
198
ac->controls[i]->oid = talloc_strdup(ac->controls[i],
199
LDB_CONTROL_PAGED_RESULTS_OID);
200
if (ac->controls[i]->oid == NULL) {
201
return LDB_ERR_OPERATIONS_ERROR;
204
ac->controls[i]->critical = 0;
206
paged = talloc(ac->controls[i], struct ldb_paged_control);
208
return LDB_ERR_OPERATIONS_ERROR;
211
ac->controls[i]->data = paged;
215
paged->cookie = NULL;
216
paged->cookie_len = 0;
218
paged->size = ac->store->num_entries;
219
paged->cookie = talloc_strdup(paged, ac->store->cookie);
220
paged->cookie_len = strlen(paged->cookie) + 1;
226
static int paged_search_callback(struct ldb_request *req, struct ldb_reply *ares)
228
struct paged_context *ac ;
229
struct message_store *msg_store;
232
ac = talloc_get_type(req->context, struct paged_context);
235
return ldb_module_done(ac->req, NULL, NULL,
236
LDB_ERR_OPERATIONS_ERROR);
238
if (ares->error != LDB_SUCCESS) {
239
return ldb_module_done(ac->req, ares->controls,
240
ares->response, ares->error);
243
switch (ares->type) {
244
case LDB_REPLY_ENTRY:
245
msg_store = talloc(ac->store, struct message_store);
246
if (msg_store == NULL) {
247
return ldb_module_done(ac->req, NULL, NULL,
248
LDB_ERR_OPERATIONS_ERROR);
250
msg_store->next = NULL;
251
msg_store->r = talloc_steal(msg_store, ares);
253
if (ac->store->first == NULL) {
254
ac->store->first = msg_store;
256
ac->store->last->next = msg_store;
258
ac->store->last = msg_store;
260
ac->store->num_entries++;
264
case LDB_REPLY_REFERRAL:
265
msg_store = talloc(ac->store, struct message_store);
266
if (msg_store == NULL) {
267
return ldb_module_done(ac->req, NULL, NULL,
268
LDB_ERR_OPERATIONS_ERROR);
270
msg_store->next = NULL;
271
msg_store->r = talloc_steal(msg_store, ares);
273
if (ac->store->first_ref == NULL) {
274
ac->store->first_ref = msg_store;
276
ac->store->last_ref->next = msg_store;
278
ac->store->last_ref = msg_store;
283
ac->store->controls = talloc_move(ac->store, &ares->controls);
284
ret = paged_results(ac);
285
return ldb_module_done(ac->req, ac->controls,
286
ares->response, ret);
292
static int paged_search(struct ldb_module *module, struct ldb_request *req)
294
struct ldb_context *ldb;
295
struct ldb_control *control;
296
struct private_data *private_data;
297
struct ldb_paged_control *paged_ctrl;
298
struct ldb_control **saved_controls;
299
struct ldb_request *search_req;
300
struct paged_context *ac;
303
ldb = ldb_module_get_ctx(module);
305
/* check if there's a paged request control */
306
control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
307
if (control == NULL) {
308
/* not found go on */
309
return ldb_next_request(module, req);
312
paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
314
return LDB_ERR_PROTOCOL_ERROR;
317
private_data = talloc_get_type(ldb_module_get_private(module),
318
struct private_data);
320
ac = talloc_zero(req, struct paged_context);
322
ldb_set_errstring(ldb, "Out of Memory");
323
return LDB_ERR_OPERATIONS_ERROR;
328
ac->size = paged_ctrl->size;
330
/* check if it is a continuation search the store */
331
if (paged_ctrl->cookie_len == 0) {
332
if (paged_ctrl->size == 0) {
333
return LDB_ERR_OPERATIONS_ERROR;
336
ac->store = new_store(private_data);
337
if (ac->store == NULL) {
338
return LDB_ERR_OPERATIONS_ERROR;
341
ret = ldb_build_search_req_ex(&search_req, ldb, ac,
343
req->op.search.scope,
345
req->op.search.attrs,
348
paged_search_callback,
351
/* save it locally and remove it from the list */
352
/* we do not need to replace them later as we
353
* are keeping the original req intact */
354
if (!save_controls(control, search_req, &saved_controls)) {
355
return LDB_ERR_OPERATIONS_ERROR;
358
return ldb_next_request(module, search_req);
361
struct results_store *current = NULL;
363
/* TODO: age out old outstanding requests */
364
for (current = private_data->store; current; current = current->next) {
365
if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
366
current->timestamp = time(NULL);
370
if (current == NULL) {
371
return LDB_ERR_UNWILLING_TO_PERFORM;
376
/* check if it is an abandon */
378
return ldb_module_done(req, NULL, NULL,
382
ret = paged_results(ac);
383
if (ret != LDB_SUCCESS) {
384
return ldb_module_done(req, NULL, NULL, ret);
386
return ldb_module_done(req, ac->controls, NULL,
391
static int paged_request_init(struct ldb_module *module)
393
struct ldb_context *ldb;
394
struct private_data *data;
397
ldb = ldb_module_get_ctx(module);
399
data = talloc(module, struct private_data);
401
return LDB_ERR_OTHER;
404
data->next_free_id = 1;
406
ldb_module_set_private(module, data);
408
ret = ldb_mod_register_control(module, LDB_CONTROL_PAGED_RESULTS_OID);
409
if (ret != LDB_SUCCESS) {
410
ldb_debug(ldb, LDB_DEBUG_WARNING,
412
"Unable to register control with rootdse!\n");
415
return ldb_next_init(module);
418
const struct ldb_module_ops ldb_paged_results_module_ops = {
419
.name = "paged_results",
420
.search = paged_search,
421
.init_context = paged_request_init