r17186: "async" word abuse clean-up part 2
[kai/samba.git] / source4 / dsdb / samdb / ldb_modules / partition.c
1 /* 
2    Partitions ldb module
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
5
6    * NOTICE: this module is NOT released under the GNU LGPL license as
7    * other ldb code. This module is release under the GNU GPL v2 or
8    * later license.
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program 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
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb partitions module
29  *
30  *  Description: Implement LDAP partitions
31  *
32  *  Author: Andrew Bartlett
33  */
34
35 #include "includes.h"
36 #include "ldb/include/includes.h"
37
38 struct partition {
39         struct ldb_module *module;
40         const char *backend;
41         struct ldb_dn *dn;
42 };
43 struct partition_private_data {
44         struct partition **partitions;
45 };
46
47 struct partition_context {
48         struct ldb_module *module;
49         struct ldb_request *orig_req;
50
51         struct ldb_request **search_req;
52         BOOL *finished_search;
53         int num_searches;
54 };
55
56 static struct ldb_handle *partition_init_handle(struct ldb_request *req, struct ldb_module *module)
57 {
58         struct partition_context *ac;
59         struct ldb_handle *h;
60
61         h = talloc_zero(req, struct ldb_handle);
62         if (h == NULL) {
63                 ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
64                 return NULL;
65         }
66
67         h->module = module;
68
69         ac = talloc_zero(h, struct partition_context);
70         if (ac == NULL) {
71                 ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
72                 talloc_free(h);
73                 return NULL;
74         }
75
76         h->private_data = (void *)ac;
77
78         ac->module = module;
79         ac->orig_req = req;
80
81         return h;
82 }
83
84 struct ldb_module *make_module_for_next_request(TALLOC_CTX *mem_ctx, 
85                                                 struct ldb_context *ldb,
86                                                 struct ldb_module *module) 
87 {
88         struct ldb_module *current;
89         static const struct ldb_module_ops ops; /* zero */
90         current = talloc_zero(mem_ctx, struct ldb_module);
91         if (current == NULL) {
92                 return module;
93         }
94         
95         current->ldb = ldb;
96         current->ops = &ops;
97         current->prev = NULL;
98         current->next = module;
99         return current;
100 }
101
102 struct ldb_module *find_backend(struct ldb_module *module, struct ldb_request *req, const struct ldb_dn *dn)
103 {
104         int i;
105         struct partition_private_data *data = talloc_get_type(module->private_data, 
106                                                               struct partition_private_data);
107         /* Look at base DN */
108         /* Figure out which partition it is under */
109         /* Skip the lot if 'data' isn't here yet (initialistion) */
110         for (i=0; data && data->partitions && data->partitions[i]; i++) {
111                 if (ldb_dn_compare_base(module->ldb, 
112                                         data->partitions[i]->dn, 
113                                         dn) == 0) {
114                         return make_module_for_next_request(req, module->ldb, data->partitions[i]->module);
115                 }
116         }
117
118         return module;
119 };
120
121 static int partition_send_search(struct partition_context *ac, struct ldb_module *partition)
122 {
123         int ret;
124         struct ldb_module *next = make_module_for_next_request(ac->module, ac->module->ldb, partition);
125         
126         ac->search_req = talloc_realloc(ac, ac->search_req, 
127                                         struct ldb_request *, ac->num_searches + 1);
128         if (!ac->search_req) {
129                 ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac->module->ldb, "Out of memory!"));
130                 return LDB_ERR_OPERATIONS_ERROR;
131         }
132         ac->search_req[ac->num_searches] = talloc(ac, struct ldb_request);
133         if (ac->search_req[ac->num_searches] == NULL) {
134                 ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac->module->ldb, "Out of memory!"));
135                 return LDB_ERR_OPERATIONS_ERROR;
136         }
137         
138         *ac->search_req[ac->num_searches] = *ac->orig_req; /* copy the request */
139         
140         /* Spray off search requests to all backends */
141         ret = ldb_next_request(next, ac->search_req[ac->num_searches]); 
142         if (ret != LDB_SUCCESS) {
143                 return ret;
144         }
145         
146         ac->num_searches++;
147         return LDB_SUCCESS;
148 }
149
150 /* search */
151 static int partition_search(struct ldb_module *module, struct ldb_request *req)
152 {
153         /* Find backend */
154         struct partition_private_data *data = talloc_get_type(module->private_data, 
155                                                               struct partition_private_data);
156         /* issue request */
157
158         /* (later) consider if we should be searching multiple
159          * partitions (for 'invisible' partition behaviour */
160         if (ldb_get_opaque(module->ldb, "global_catalog")) {
161                 int ret, i;
162                 struct ldb_handle *h;
163                 struct partition_context *ac;
164                 
165                 h = partition_init_handle(req, module);
166                 if (!h) {
167                         return LDB_ERR_OPERATIONS_ERROR;
168                 }
169                 /* return our own handle to deal with this call */
170                 req->handle = h;
171                 
172                 ac = talloc_get_type(h->private_data, struct partition_context);
173                 
174                 ac->orig_req = req;
175                 ac->num_searches = 0;
176
177                 for (i=0; data && data->partitions && data->partitions[i]; i++) {
178                         /* Find all partitions under the search base */
179                         if (ldb_dn_compare_base(module->ldb, 
180                                                 req->op.search.base,
181                                                 data->partitions[i]->dn) == 0) {
182                                 ret = partition_send_search(ac, data->partitions[i]->module);
183                                 if (ret != LDB_SUCCESS) {
184                                         return ret;
185                                 }
186                         }
187                 }
188
189                 /* Perhaps we didn't match any partitions.  Try the main partition, then all partitions */
190                 if (ac->num_searches == 0) {
191                         ret = partition_send_search(ac, module->next);
192                         if (ret != LDB_SUCCESS) {
193                                 return ret;
194                         }
195                         for (i=0; data && data->partitions && data->partitions[i]; i++) {
196                                 ret = partition_send_search(ac, data->partitions[i]->module);
197                                 if (ret != LDB_SUCCESS) {
198                                         return ret;
199                                 }
200                         }
201                 }
202                 
203                 ac->finished_search = talloc_zero_array(ac, BOOL, ac->num_searches);
204                 if (!ac->finished_search) {
205                         return LDB_ERR_OPERATIONS_ERROR;
206                 }
207                 return LDB_SUCCESS;
208         } else {
209                 struct ldb_module *backend = find_backend(module, req, req->op.search.base);
210         
211                 return ldb_next_request(backend, req);
212         }
213 }
214
215 /* add */
216 static int partition_add(struct ldb_module *module, struct ldb_request *req)
217 {
218         /* Find backend */
219         struct ldb_module *backend = find_backend(module, req, req->op.add.message->dn);
220         
221         /* issue request */
222
223         return ldb_next_request(backend, req);
224 }
225
226 /* modify */
227 static int partition_modify(struct ldb_module *module, struct ldb_request *req)
228 {
229         /* Find backend */
230         struct ldb_module *backend = find_backend(module, req, req->op.mod.message->dn);
231         
232         /* issue request */
233
234         return ldb_next_request(backend, req);
235 }
236
237 /* delete */
238 static int partition_delete(struct ldb_module *module, struct ldb_request *req)
239 {
240         /* Find backend */
241         struct ldb_module *backend = find_backend(module, req, req->op.del.dn);
242         
243         /* issue request */
244
245         return ldb_next_request(backend, req);
246 }
247
248 /* rename */
249 static int partition_rename(struct ldb_module *module, struct ldb_request *req)
250 {
251         /* Find backend */
252         struct ldb_module *backend = find_backend(module, req, req->op.rename.olddn);
253         struct ldb_module *backend2 = find_backend(module, req, req->op.rename.newdn);
254
255         if (backend->next != backend2->next) {
256                 return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
257         }
258
259         /* issue request */
260
261         /* (later) consider if we should be searching multiple partitions */
262         return ldb_next_request(backend, req);
263 }
264
265 /* start a transaction */
266 static int partition_start_trans(struct ldb_module *module)
267 {
268         int i, ret;
269         struct partition_private_data *data = talloc_get_type(module->private_data, 
270                                                               struct partition_private_data);
271         /* Look at base DN */
272         /* Figure out which partition it is under */
273         /* Skip the lot if 'data' isn't here yet (initialistion) */
274         ret = ldb_next_start_trans(module);
275         if (ret != LDB_SUCCESS) {
276                 return ret;
277         }
278
279         for (i=0; data && data->partitions && data->partitions[i]; i++) {
280                 struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
281
282                 ret = ldb_next_start_trans(next);
283                 talloc_free(next);
284                 if (ret != LDB_SUCCESS) {
285                         /* Back it out, if it fails on one */
286                         for (i--; i >= 0; i--) {
287                                 next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
288                                 ldb_next_del_trans(next);
289                                 talloc_free(next);
290                         }
291                         return ret;
292                 }
293         }
294         return LDB_SUCCESS;
295 }
296
297 /* end a transaction */
298 static int partition_end_trans(struct ldb_module *module)
299 {
300         int i, ret, ret2 = LDB_SUCCESS;
301         struct partition_private_data *data = talloc_get_type(module->private_data, 
302                                                               struct partition_private_data);
303         ret = ldb_next_end_trans(module);
304         if (ret != LDB_SUCCESS) {
305                 return ret;
306         }
307
308         /* Look at base DN */
309         /* Figure out which partition it is under */
310         /* Skip the lot if 'data' isn't here yet (initialistion) */
311         for (i=0; data && data->partitions && data->partitions[i]; i++) {
312                 struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
313                 
314                 ret = ldb_next_end_trans(next);
315                 talloc_free(next);
316                 if (ret != LDB_SUCCESS) {
317                         ret2 = ret;
318                 }
319         }
320
321         if (ret != LDB_SUCCESS) {
322                 /* Back it out, if it fails on one */
323                 for (i=0; data && data->partitions && data->partitions[i]; i++) {
324                         struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
325                         ldb_next_del_trans(next);
326                         talloc_free(next);
327                 }
328         }
329         return ret;
330 }
331
332 /* delete a transaction */
333 static int partition_del_trans(struct ldb_module *module)
334 {
335         int i, ret, ret2 = LDB_SUCCESS;
336         struct partition_private_data *data = talloc_get_type(module->private_data, 
337                                                               struct partition_private_data);
338         ret = ldb_next_del_trans(module);
339         if (ret != LDB_SUCCESS) {
340                 ret2 = ret;
341         }
342
343         /* Look at base DN */
344         /* Figure out which partition it is under */
345         /* Skip the lot if 'data' isn't here yet (initialistion) */
346         for (i=0; data && data->partitions && data->partitions[i]; i++) {
347                 struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
348                 
349                 ret = ldb_next_del_trans(next);
350                 talloc_free(next);
351                 if (ret != LDB_SUCCESS) {
352                         ret2 = ret;
353                 }
354         }
355         return ret2;
356 }
357
358 static int partition_sequence_number(struct ldb_module *module, struct ldb_request *req)
359 {
360         int i, ret;
361         uint64_t seq_number = 0;
362         struct partition_private_data *data = talloc_get_type(module->private_data, 
363                                                               struct partition_private_data);
364         ret = ldb_next_request(module, req);
365         if (ret != LDB_SUCCESS) {
366                 return ret;
367         }
368         seq_number = seq_number + req->op.seq_num.seq_num;
369
370         /* Look at base DN */
371         /* Figure out which partition it is under */
372         /* Skip the lot if 'data' isn't here yet (initialistion) */
373         for (i=0; data && data->partitions && data->partitions[i]; i++) {
374                 struct ldb_module *next = make_module_for_next_request(req, module->ldb, data->partitions[i]->module);
375                 
376                 ret = ldb_next_request(next, req);
377                 talloc_free(next);
378                 if (ret != LDB_SUCCESS) {
379                         return ret;
380                 }
381                 seq_number = seq_number + req->op.seq_num.seq_num;
382         }
383         req->op.seq_num.seq_num = seq_number;
384         return LDB_SUCCESS;
385 }
386
387 static int sort_compare(void *void1,
388                         void *void2, void *opaque)
389 {
390         struct ldb_context *ldb = talloc_get_type(opaque, struct ldb_context);
391         struct partition **pp1 = void1;
392         struct partition **pp2 = void2;
393         struct partition *partition1 = talloc_get_type(*pp1, struct partition);
394         struct partition *partition2 = talloc_get_type(*pp2, struct partition);
395
396         return ldb_dn_compare(ldb, partition1->dn, partition2->dn);
397 }
398
399 static int partition_init(struct ldb_module *module)
400 {
401         int ret, i;
402         TALLOC_CTX *mem_ctx = talloc_new(module);
403         static const char *attrs[] = { "partition", NULL };
404         struct ldb_result *res;
405         struct ldb_message *msg;
406         struct ldb_message_element *partition_attributes;
407
408         struct partition_private_data *data;
409
410         if (!mem_ctx) {
411                 return LDB_ERR_OPERATIONS_ERROR;
412         }
413
414         data = talloc(mem_ctx, struct partition_private_data);
415         if (data == NULL) {
416                 return LDB_ERR_OPERATIONS_ERROR;
417         }
418
419         ret = ldb_search(module->ldb, ldb_dn_explode(mem_ctx, "@PARTITION"),
420                          LDB_SCOPE_BASE,
421                          NULL, attrs,
422                          &res);
423         if (ret != LDB_SUCCESS) {
424                 talloc_free(mem_ctx);
425                 return ret;
426         }
427         talloc_steal(mem_ctx, res);
428         if (res->count == 0) {
429                 talloc_free(mem_ctx);
430                 return ldb_next_init(module);
431         }
432
433         if (res->count > 1) {
434                 talloc_free(mem_ctx);
435                 return LDB_ERR_CONSTRAINT_VIOLATION;
436         }
437
438         msg = res->msgs[0];
439
440         partition_attributes = ldb_msg_find_element(msg, "partition");
441         if (!partition_attributes) {
442                 ldb_set_errstring(module->ldb, 
443                                   talloc_asprintf(module, "partition_init: "
444                                                   "no partitions specified"));
445                 return LDB_ERR_CONSTRAINT_VIOLATION;
446         }
447         data->partitions = talloc_array(data, struct partition *, partition_attributes->num_values + 1);
448         if (!data->partitions) {
449                 talloc_free(mem_ctx);
450                 return LDB_ERR_OPERATIONS_ERROR;
451         }
452         for (i=0; i < partition_attributes->num_values; i++) {
453                 char *base = talloc_strdup(data->partitions, (char *)partition_attributes->values[i].data);
454                 char *p = strchr(base, ':');
455                 if (!p) {
456                         ldb_set_errstring(module->ldb, 
457                                           talloc_asprintf(module, "partition_init: "
458                                                           "invalid form for partition record (missing ':'): %s", base));
459                         return LDB_ERR_CONSTRAINT_VIOLATION;
460                 }
461                 p[0] = '\0';
462                 p++;
463                 if (!p[0]) {
464                         ldb_set_errstring(module->ldb, 
465                                           talloc_asprintf(module, "partition_init: "
466                                                           "invalid form for partition record (missing backend database): %s", base));
467                         return LDB_ERR_CONSTRAINT_VIOLATION;
468                 }
469                 data->partitions[i] = talloc(data->partitions, struct partition);
470                 if (!data->partitions[i]) {
471                         talloc_free(mem_ctx);
472                         return LDB_ERR_OPERATIONS_ERROR;
473                 }
474
475                 data->partitions[i]->dn = ldb_dn_explode(data->partitions[i], base);
476                 if (!data->partitions[i]->dn) {
477                         ldb_set_errstring(module->ldb, 
478                                           talloc_asprintf(module, "partition_init: "
479                                                           "invalid DN in partition record: %s", base));
480                         return LDB_ERR_CONSTRAINT_VIOLATION;
481                 }
482
483                 data->partitions[i]->backend = private_path(data->partitions[i], p);
484                 ret = ldb_connect_backend(module->ldb, data->partitions[i]->backend, NULL, &data->partitions[i]->module);
485                 if (ret != LDB_SUCCESS) {
486                         return ret;
487                 }
488         }
489         data->partitions[i] = NULL;
490
491         /* sort these into order, most to least specific */
492         ldb_qsort(data->partitions, partition_attributes->num_values, sizeof(*data->partitions), 
493                   module->ldb, sort_compare);
494
495         for (i=0; data->partitions[i]; i++) {
496                 struct ldb_request *req;
497                 req = talloc_zero(mem_ctx, struct ldb_request);
498                 if (req == NULL) {
499                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "partition: Out of memory!\n");
500                         return LDB_ERR_OPERATIONS_ERROR;
501                 }
502                 
503                 req->operation = LDB_REQ_REGISTER_PARTITION;
504                 req->op.reg_partition.dn = data->partitions[i]->dn;
505                 
506                 ret = ldb_request(module->ldb, req);
507                 if (ret != LDB_SUCCESS) {
508                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "partition: Unable to register partition with rootdse!\n");
509                         return LDB_ERR_OTHER;
510                 }
511                 talloc_free(req);
512         }
513
514         module->private_data = data;
515         talloc_steal(module, data);
516         
517         talloc_free(mem_ctx);
518         return ldb_next_init(module);
519 }
520
521 static int partition_wait_none(struct ldb_handle *handle) {
522         struct partition_context *ac;
523         int ret;
524         int i;
525     
526         if (!handle || !handle->private_data) {
527                 return LDB_ERR_OPERATIONS_ERROR;
528         }
529
530         if (handle->state == LDB_ASYNC_DONE) {
531                 return handle->status;
532         }
533
534         handle->state = LDB_ASYNC_PENDING;
535         handle->status = LDB_SUCCESS;
536
537         ac = talloc_get_type(handle->private_data, struct partition_context);
538
539         for (i=0; i < ac->num_searches; i++) {
540                 ret = ldb_wait(ac->search_req[i]->handle, LDB_WAIT_NONE);
541                 
542                 if (ret != LDB_SUCCESS) {
543                         handle->status = ret;
544                         goto done;
545                 }
546                 if (ac->search_req[i]->handle->status != LDB_SUCCESS) {
547                         handle->status = ac->search_req[i]->handle->status;
548                         goto done;
549                 }
550                 
551                 if (ac->search_req[i]->handle->state != LDB_ASYNC_DONE) {
552                         return LDB_SUCCESS;
553                 }
554         }
555
556         ret = LDB_SUCCESS;
557
558 done:
559         handle->state = LDB_ASYNC_DONE;
560         return ret;
561 }
562
563
564 static int partition_wait_all(struct ldb_handle *handle) {
565
566         int ret;
567
568         while (handle->state != LDB_ASYNC_DONE) {
569                 ret = partition_wait_none(handle);
570                 if (ret != LDB_SUCCESS) {
571                         return ret;
572                 }
573         }
574
575         return handle->status;
576 }
577
578 static int partition_wait(struct ldb_handle *handle, enum ldb_wait_type type)
579 {
580         if (type == LDB_WAIT_ALL) {
581                 return partition_wait_all(handle);
582         } else {
583                 return partition_wait_none(handle);
584         }
585 }
586
587 static const struct ldb_module_ops partition_ops = {
588         .name              = "partition",
589         .init_context      = partition_init,
590         .search            = partition_search,
591         .add               = partition_add,
592         .modify            = partition_modify,
593         .del               = partition_delete,
594         .rename            = partition_rename,
595         .start_transaction = partition_start_trans,
596         .end_transaction   = partition_end_trans,
597         .del_transaction   = partition_del_trans,
598         .sequence_number   = partition_sequence_number,
599         .wait              = partition_wait
600 };
601
602 int ldb_partition_init(void)
603 {
604         return ldb_register_module(&partition_ops);
605 }