root/source3/lib/ldb/modules/paged_results.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. store_destructor
  2. new_store
  3. init_handle
  4. paged_search_callback
  5. paged_search
  6. paged_results
  7. paged_wait
  8. paged_request_init
  9. ldb_paged_results_init

   1 /* 
   2    ldb database library
   3 
   4    Copyright (C) Simo Sorce  2005-2006
   5 
   6      ** NOTE! The following LGPL license applies to the ldb
   7      ** library. This does NOT imply that all of Samba is released
   8      ** under the LGPL
   9    
  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.
  14 
  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.
  19 
  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/>.
  22 */
  23 
  24 /*
  25  *  Name: paged_result
  26  *
  27  *  Component: ldb paged results control module
  28  *
  29  *  Description: this module caches a complete search and sends back
  30  *               results in chunks as asked by the client
  31  *
  32  *  Author: Simo Sorce
  33  */
  34 
  35 #include "includes.h"
  36 #include "ldb/include/includes.h"
  37 
  38 struct message_store {
  39         /* keep the whole ldb_reply as an optimization
  40          * instead of freeing and talloc-ing the container
  41          * on each result */
  42         struct ldb_reply *r;
  43         struct message_store *next;
  44 };
  45 
  46 struct private_data;
  47 
  48 struct results_store {
  49 
  50         struct private_data *priv;
  51 
  52         char *cookie;
  53         time_t timestamp;
  54 
  55         struct results_store *prev;
  56         struct results_store *next;
  57         
  58         struct message_store *first;
  59         struct message_store *last;
  60         int num_entries;
  61 
  62         struct message_store *first_ref;
  63         struct message_store *last_ref;
  64 
  65         struct ldb_control **controls;
  66 
  67         struct ldb_request *req;
  68 };
  69 
  70 struct private_data {
  71 
  72         int next_free_id;
  73         struct results_store *store;
  74         
  75 };
  76 
  77 int store_destructor(struct results_store *store);
  78 
  79 int store_destructor(struct results_store *store)
     /* [<][>][^][v][top][bottom][index][help] */
  80 {
  81         if (store->prev) {
  82                 store->prev->next = store->next;
  83         }
  84         if (store->next) {
  85                 store->next->prev = store->prev;
  86         }
  87 
  88         if (store == store->priv->store) {
  89                 store->priv->store = NULL;
  90         }
  91 
  92         return 0;
  93 }
  94 
  95 static struct results_store *new_store(struct private_data *priv)
     /* [<][>][^][v][top][bottom][index][help] */
  96 {
  97         struct results_store *newr;
  98         int new_id = priv->next_free_id++;
  99 
 100         /* TODO: we should have a limit on the number of
 101          * outstanding paged searches
 102          */
 103 
 104         newr = talloc(priv, struct results_store);
 105         if (!newr) return NULL;
 106 
 107         newr->priv = priv;
 108 
 109         newr->cookie = talloc_asprintf(newr, "%d", new_id);
 110         if (!newr->cookie) {
 111                 talloc_free(newr);
 112                 return NULL;
 113         }
 114 
 115         newr->timestamp = time(NULL);
 116 
 117         newr->first = NULL;
 118         newr->num_entries = 0;
 119         newr->first_ref = NULL;
 120         newr->controls = NULL;
 121 
 122         /* put this entry as first */
 123         newr->prev = NULL;
 124         newr->next = priv->store;
 125         if (priv->store != NULL) priv->store->prev = newr;
 126         priv->store = newr;
 127 
 128         talloc_set_destructor(newr, store_destructor);
 129 
 130         return newr;
 131 }
 132 
 133 struct paged_context {
 134         struct ldb_module *module;
 135         void *up_context;
 136         int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
 137 
 138         int size;
 139 
 140         struct results_store *store;
 141 };
 142 
 143 static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
     /* [<][>][^][v][top][bottom][index][help] */
 144                                             void *context,
 145                                             int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
 146 {
 147         struct paged_context *ac;
 148         struct ldb_handle *h;
 149 
 150         h = talloc_zero(mem_ctx, struct ldb_handle);
 151         if (h == NULL) {
 152                 ldb_set_errstring(module->ldb, "Out of Memory");
 153                 return NULL;
 154         }
 155 
 156         h->module = module;
 157 
 158         ac = talloc_zero(h, struct paged_context);
 159         if (ac == NULL) {
 160                 ldb_set_errstring(module->ldb, "Out of Memory");
 161                 talloc_free(h);
 162                 return NULL;
 163         }
 164 
 165         h->private_data = (void *)ac;
 166 
 167         h->state = LDB_ASYNC_INIT;
 168         h->status = LDB_SUCCESS;
 169 
 170         ac->module = module;
 171         ac->up_context = context;
 172         ac->up_callback = callback;
 173 
 174         return h;
 175 }
 176 
 177 static int paged_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
     /* [<][>][^][v][top][bottom][index][help] */
 178 {
 179         struct paged_context *ac = NULL;
 180 
 181         if (!context || !ares) {
 182                 ldb_set_errstring(ldb, "NULL Context or Result in callback");
 183                 goto error;
 184         }
 185 
 186         ac = talloc_get_type(context, struct paged_context);
 187 
 188         if (ares->type == LDB_REPLY_ENTRY) {
 189                 if (ac->store->first == NULL) {
 190                         ac->store->first = ac->store->last = talloc(ac->store, struct message_store);
 191                 } else {
 192                         ac->store->last->next = talloc(ac->store, struct message_store);
 193                         ac->store->last = ac->store->last->next;
 194                 }
 195                 if (ac->store->last == NULL) {
 196                         goto error;
 197                 }
 198 
 199                 ac->store->num_entries++;
 200 
 201                 ac->store->last->r = talloc_steal(ac->store->last, ares);
 202                 ac->store->last->next = NULL;
 203         }
 204 
 205         if (ares->type == LDB_REPLY_REFERRAL) {
 206                 if (ac->store->first_ref == NULL) {
 207                         ac->store->first_ref = ac->store->last_ref = talloc(ac->store, struct message_store);
 208                 } else {
 209                         ac->store->last_ref->next = talloc(ac->store, struct message_store);
 210                         ac->store->last_ref = ac->store->last_ref->next;
 211                 }
 212                 if (ac->store->last_ref == NULL) {
 213                         goto error;
 214                 }
 215 
 216                 ac->store->last_ref->r = talloc_steal(ac->store->last, ares);
 217                 ac->store->last_ref->next = NULL;
 218         }
 219 
 220         if (ares->type == LDB_REPLY_DONE) {
 221                 ac->store->controls = talloc_move(ac->store, &ares->controls);
 222                 talloc_free(ares);
 223         }
 224 
 225         return LDB_SUCCESS;
 226 
 227 error:
 228         talloc_free(ares);
 229         return LDB_ERR_OPERATIONS_ERROR;
 230 }
 231 
 232 static int paged_search(struct ldb_module *module, struct ldb_request *req)
     /* [<][>][^][v][top][bottom][index][help] */
 233 {
 234         struct ldb_control *control;
 235         struct private_data *private_data;
 236         struct ldb_paged_control *paged_ctrl;
 237         struct ldb_control **saved_controls;
 238         struct paged_context *ac;
 239         struct ldb_handle *h;
 240         int ret;
 241 
 242         /* check if there's a paged request control */
 243         control = get_control_from_list(req->controls, LDB_CONTROL_PAGED_RESULTS_OID);
 244         if (control == NULL) {
 245                 /* not found go on */
 246                 return ldb_next_request(module, req);
 247         }
 248 
 249         private_data = talloc_get_type(module->private_data, struct private_data);
 250 
 251         req->handle = NULL;
 252 
 253         if (!req->callback || !req->context) {
 254                 ldb_set_errstring(module->ldb,
 255                                   "Async interface called with NULL callback function or NULL context");
 256                 return LDB_ERR_OPERATIONS_ERROR;
 257         }
 258         
 259         paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
 260         if (!paged_ctrl) {
 261                 return LDB_ERR_PROTOCOL_ERROR;
 262         }
 263 
 264         h = init_handle(req, module, req->context, req->callback);
 265         if (!h) {
 266                 return LDB_ERR_OPERATIONS_ERROR;
 267         }
 268         ac = talloc_get_type(h->private_data, struct paged_context);
 269 
 270         ac->size = paged_ctrl->size;
 271 
 272         /* check if it is a continuation search the store */
 273         if (paged_ctrl->cookie_len == 0) {
 274                 
 275                 ac->store = new_store(private_data);
 276                 if (ac->store == NULL) {
 277                         talloc_free(h);
 278                         return LDB_ERR_UNWILLING_TO_PERFORM;
 279                 }
 280 
 281                 ac->store->req = talloc(ac->store, struct ldb_request);
 282                 if (!ac->store->req)
 283                         return LDB_ERR_OPERATIONS_ERROR;
 284 
 285                 ac->store->req->operation = req->operation;
 286                 ac->store->req->op.search.base = req->op.search.base;
 287                 ac->store->req->op.search.scope = req->op.search.scope;
 288                 ac->store->req->op.search.tree = req->op.search.tree;
 289                 ac->store->req->op.search.attrs = req->op.search.attrs;
 290                 ac->store->req->controls = req->controls;
 291 
 292                 /* save it locally and remove it from the list */
 293                 /* we do not need to replace them later as we
 294                  * are keeping the original req intact */
 295                 if (!save_controls(control, ac->store->req, &saved_controls)) {
 296                         return LDB_ERR_OPERATIONS_ERROR;
 297                 }
 298 
 299                 ac->store->req->context = ac;
 300                 ac->store->req->callback = paged_search_callback;
 301                 ldb_set_timeout_from_prev_req(module->ldb, req, ac->store->req);
 302 
 303                 ret = ldb_next_request(module, ac->store->req);
 304 
 305         } else {
 306                 struct results_store *current = NULL;
 307 
 308                 for (current = private_data->store; current; current = current->next) {
 309                         if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
 310                                 current->timestamp = time(NULL);
 311                                 break;
 312                         }
 313                 }
 314                 if (current == NULL) {
 315                         talloc_free(h);
 316                         return LDB_ERR_UNWILLING_TO_PERFORM;
 317                 }
 318 
 319                 ac->store = current;
 320                 ret = LDB_SUCCESS;
 321         }
 322 
 323         req->handle = h;
 324 
 325         /* check if it is an abandon */
 326         if (ac->size == 0) {
 327                 talloc_free(ac->store);
 328                 h->status = LDB_SUCCESS;
 329                 h->state = LDB_ASYNC_DONE;
 330                 return LDB_SUCCESS;
 331         }
 332 
 333         /* TODO: age out old outstanding requests */
 334 
 335         return ret;
 336 
 337 }
 338 
 339 static int paged_results(struct ldb_handle *handle)
     /* [<][>][^][v][top][bottom][index][help] */
 340 {
 341         struct paged_context *ac;
 342         struct ldb_paged_control *paged;
 343         struct ldb_reply *ares;
 344         struct message_store *msg;
 345         int i, num_ctrls, ret;
 346 
 347         ac = talloc_get_type(handle->private_data, struct paged_context);
 348 
 349         if (ac->store == NULL)
 350                 return LDB_ERR_OPERATIONS_ERROR;
 351 
 352         while (ac->store->num_entries > 0 && ac->size > 0) {
 353                 msg = ac->store->first;
 354                 ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
 355                 if (ret != LDB_SUCCESS) {
 356                         handle->status = ret;
 357                         handle->state = LDB_ASYNC_DONE;
 358                         return ret;
 359                 }
 360 
 361                 ac->store->first = msg->next;
 362                 talloc_free(msg);
 363                 ac->store->num_entries--;
 364                 ac->size--;
 365         }
 366 
 367         handle->state = LDB_ASYNC_DONE;
 368 
 369         while (ac->store->first_ref != NULL) {
 370                 msg = ac->store->first_ref;
 371                 ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
 372                 if (ret != LDB_SUCCESS) {
 373                         handle->status = ret;
 374                         handle->state = LDB_ASYNC_DONE;
 375                         return ret;
 376                 }
 377 
 378                 ac->store->first_ref = msg->next;
 379                 talloc_free(msg);
 380         }
 381 
 382         ares = talloc_zero(ac->store, struct ldb_reply);
 383         if (ares == NULL) {
 384                 handle->status = LDB_ERR_OPERATIONS_ERROR;
 385                 return handle->status;
 386         }
 387         num_ctrls = 2;
 388         i = 0;
 389 
 390         if (ac->store->controls != NULL) {
 391                 ares->controls = ac->store->controls;
 392                 while (ares->controls[i]) i++; /* counting */
 393 
 394                 ares->controls = talloc_move(ares, &ac->store->controls);
 395                 num_ctrls += i;
 396         }
 397 
 398         ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, num_ctrls);
 399         if (ares->controls == NULL) {
 400                 handle->status = LDB_ERR_OPERATIONS_ERROR;
 401                 return handle->status;
 402         }
 403 
 404         ares->controls[i] = talloc(ares->controls, struct ldb_control);
 405         if (ares->controls[i] == NULL) {
 406                 handle->status = LDB_ERR_OPERATIONS_ERROR;
 407                 return handle->status;
 408         }
 409 
 410         ares->controls[i]->oid = talloc_strdup(ares->controls[i], LDB_CONTROL_PAGED_RESULTS_OID);
 411         if (ares->controls[i]->oid == NULL) {
 412                 handle->status = LDB_ERR_OPERATIONS_ERROR;
 413                 return handle->status;
 414         }
 415                 
 416         ares->controls[i]->critical = 0;
 417         ares->controls[i + 1] = NULL;
 418 
 419         paged = talloc(ares->controls[i], struct ldb_paged_control);
 420         if (paged == NULL) {
 421                 handle->status = LDB_ERR_OPERATIONS_ERROR;
 422                 return handle->status;
 423         }
 424         
 425         ares->controls[i]->data = paged;
 426 
 427         if (ac->size > 0) {
 428                 paged->size = 0;
 429                 paged->cookie = NULL;
 430                 paged->cookie_len = 0;
 431         } else {
 432                 paged->size = ac->store->num_entries;
 433                 paged->cookie = talloc_strdup(paged, ac->store->cookie);
 434                 paged->cookie_len = strlen(paged->cookie) + 1;
 435         }
 436 
 437         ares->type = LDB_REPLY_DONE;
 438 
 439         ret = ac->up_callback(ac->module->ldb, ac->up_context, ares);
 440 
 441         handle->status = ret;
 442 
 443         return ret;
 444 }
 445 
 446 static int paged_wait(struct ldb_handle *handle, enum ldb_wait_type type)
     /* [<][>][^][v][top][bottom][index][help] */
 447 {
 448         struct paged_context *ac;
 449         int ret;
 450     
 451         if (!handle || !handle->private_data) {
 452                 return LDB_ERR_OPERATIONS_ERROR;
 453         }
 454 
 455         if (handle->state == LDB_ASYNC_DONE) {
 456                 return handle->status;
 457         }
 458 
 459         handle->state = LDB_ASYNC_PENDING;
 460 
 461         ac = talloc_get_type(handle->private_data, struct paged_context);
 462 
 463         if (ac->store->req->handle->state == LDB_ASYNC_DONE) {
 464                 /* if lower level is finished we do not need to call it anymore */
 465                 /* return all we have until size == 0 or we empty storage */
 466                 ret = paged_results(handle);
 467 
 468                 /* we are done, if num_entries is zero free the storage
 469                  * as that mean we delivered the last batch */
 470                 if (ac->store->num_entries == 0) {
 471                         talloc_free(ac->store);
 472                 }
 473 
 474                 return ret;
 475         }
 476 
 477         if (type == LDB_WAIT_ALL) {
 478                 while (ac->store->req->handle->state != LDB_ASYNC_DONE) {
 479                         ret = ldb_wait(ac->store->req->handle, type);
 480                         if (ret != LDB_SUCCESS) {
 481                                 handle->state = LDB_ASYNC_DONE;
 482                                 handle->status = ret;
 483                                 return ret;
 484                         }
 485                 }
 486 
 487                 ret = paged_results(handle);
 488 
 489                 /* we are done, if num_entries is zero free the storage
 490                  * as that mean we delivered the last batch */
 491                 if (ac->store->num_entries == 0) {
 492                         talloc_free(ac->store);
 493                 }
 494 
 495                 return ret;
 496         }
 497 
 498         ret = ldb_wait(ac->store->req->handle, type);
 499         if (ret != LDB_SUCCESS) {
 500                 handle->state = LDB_ASYNC_DONE;
 501                 handle->status = ret;
 502                 return ret;
 503         }
 504 
 505         handle->status = ret;
 506 
 507         if (ac->store->num_entries >= ac->size ||
 508             ac->store->req->handle->state == LDB_ASYNC_DONE) {
 509 
 510                 ret = paged_results(handle);
 511 
 512                 /* we are done, if num_entries is zero free the storage
 513                  * as that mean we delivered the last batch */
 514                 if (ac->store->num_entries == 0) {
 515                         talloc_free(ac->store);
 516                 }
 517         }
 518 
 519         return ret;
 520 }
 521 
 522 static int paged_request_init(struct ldb_module *module)
     /* [<][>][^][v][top][bottom][index][help] */
 523 {
 524         struct private_data *data;
 525         struct ldb_request *req;
 526         int ret;
 527 
 528         data = talloc(module, struct private_data);
 529         if (data == NULL) {
 530                 return LDB_ERR_OTHER;
 531         }
 532         
 533         data->next_free_id = 1;
 534         data->store = NULL;
 535         module->private_data = data;
 536 
 537         req = talloc(module, struct ldb_request);
 538         if (req == NULL) {
 539                 return LDB_ERR_OPERATIONS_ERROR;
 540         }
 541 
 542         req->operation = LDB_REQ_REGISTER_CONTROL;
 543         req->op.reg_control.oid = LDB_CONTROL_PAGED_RESULTS_OID;
 544         req->controls = NULL;
 545 
 546         ret = ldb_request(module->ldb, req);
 547         if (ret != LDB_SUCCESS) {
 548                 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "paged_request: Unable to register control with rootdse!\n");
 549         }
 550 
 551         talloc_free(req);
 552         return ldb_next_init(module);
 553 }
 554 
 555 static const struct ldb_module_ops paged_ops = {
 556         .name           = "paged_results",
 557         .search         = paged_search,
 558         .wait           = paged_wait,
 559         .init_context   = paged_request_init
 560 };
 561 
 562 int ldb_paged_results_init(void)
     /* [<][>][^][v][top][bottom][index][help] */
 563 {
 564         return ldb_register_module(&paged_ops);
 565 }
 566 

/* [<][>][^][v][top][bottom][index][help] */