r20681: implement the DSDB_EXTENDED_REPLICATED_OBJECTS operation.
[sfrench/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2004-2006
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6    Copyright (C) Andrew Tridgell 2005
7    Copyright (C) Stefan Metzmacher 2007
8
9      ** NOTE! The following LGPL license applies to the ldb
10      ** library. This does NOT imply that all of Samba is released
11      ** under the LGPL
12    
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Lesser General Public
15    License as published by the Free Software Foundation; either
16    version 2 of the License, or (at your option) any later version.
17
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Lesser General Public License for more details.
22
23    You should have received a copy of the GNU Lesser General Public
24    License along with this library; if not, write to the Free Software
25    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 */
27
28 /*
29  *  Name: ldb
30  *
31  *  Component: ldb repl_meta_data module
32  *
33  *  Description: - add a unique objectGUID onto every new record,
34  *               - handle whenCreated, whenChanged timestamps
35  *               - handle uSNCreated, uSNChanged numbers
36  *               - handle replPropertyMetaData attribute
37  *
38  *  Author: Simo Sorce
39  *  Author: Stefan Metzmacher
40  */
41
42 #include "includes.h"
43 #include "lib/ldb/include/ldb.h"
44 #include "lib/ldb/include/ldb_errors.h"
45 #include "lib/ldb/include/ldb_private.h"
46 #include "dsdb/samdb/samdb.h"
47 #include "librpc/gen_ndr/ndr_misc.h"
48 #include "librpc/gen_ndr/ndr_drsblobs.h"
49
50 struct replmd_replicated_request {
51         struct ldb_module *module;
52         struct ldb_handle *handle;
53         struct ldb_request *orig_req;
54
55         struct dsdb_extended_replicated_objects *objs;
56
57         uint32_t index_current;
58
59         struct {
60                 TALLOC_CTX *mem_ctx;
61                 struct ldb_request *search_req;
62                 struct ldb_message *search_msg;
63                 int search_ret;
64                 struct ldb_request *change_req;
65                 int change_ret;
66         } sub;
67 };
68
69 static struct replmd_replicated_request *replmd_replicated_init_handle(struct ldb_module *module,
70                                                                        struct ldb_request *req,
71                                                                        struct dsdb_extended_replicated_objects *objs)
72 {
73         struct replmd_replicated_request *ar;
74         struct ldb_handle *h;
75
76         h = talloc_zero(req, struct ldb_handle);
77         if (h == NULL) {
78                 ldb_set_errstring(module->ldb, "Out of Memory");
79                 return NULL;
80         }
81
82         h->module       = module;
83         h->state        = LDB_ASYNC_PENDING;
84         h->status       = LDB_SUCCESS;
85
86         ar = talloc_zero(h, struct replmd_replicated_request);
87         if (ar == NULL) {
88                 ldb_set_errstring(module->ldb, "Out of Memory");
89                 talloc_free(h);
90                 return NULL;
91         }
92
93         h->private_data = ar;
94
95         ar->module      = module;
96         ar->handle      = h;
97         ar->orig_req    = req;
98         ar->objs        = objs;
99
100         req->handle = h;
101
102         return ar;
103 }
104
105 static struct ldb_message_element *replmd_find_attribute(const struct ldb_message *msg, const char *name)
106 {
107         int i;
108
109         for (i = 0; i < msg->num_elements; i++) {
110                 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
111                         return &msg->elements[i];
112                 }
113         }
114
115         return NULL;
116 }
117
118 /*
119   add a time element to a record
120 */
121 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
122 {
123         struct ldb_message_element *el;
124         char *s;
125
126         if (ldb_msg_find_element(msg, attr) != NULL) {
127                 return 0;
128         }
129
130         s = ldb_timestring(msg, t);
131         if (s == NULL) {
132                 return -1;
133         }
134
135         if (ldb_msg_add_string(msg, attr, s) != 0) {
136                 return -1;
137         }
138
139         el = ldb_msg_find_element(msg, attr);
140         /* always set as replace. This works because on add ops, the flag
141            is ignored */
142         el->flags = LDB_FLAG_MOD_REPLACE;
143
144         return 0;
145 }
146
147 /*
148   add a uint64_t element to a record
149 */
150 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
151 {
152         struct ldb_message_element *el;
153
154         if (ldb_msg_find_element(msg, attr) != NULL) {
155                 return 0;
156         }
157
158         if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
159                 return -1;
160         }
161
162         el = ldb_msg_find_element(msg, attr);
163         /* always set as replace. This works because on add ops, the flag
164            is ignored */
165         el->flags = LDB_FLAG_MOD_REPLACE;
166
167         return 0;
168 }
169
170 static int replmd_add_replicated(struct ldb_module *module, struct ldb_request *req, struct ldb_control *ctrl)
171 {
172         struct ldb_control **saved_ctrls;
173         int ret;
174
175         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_replicated\n");
176
177         if (!save_controls(ctrl, req, &saved_ctrls)) {
178                 return LDB_ERR_OPERATIONS_ERROR;
179         }
180
181         ret = ldb_next_request(module, req);
182         req->controls = saved_ctrls;
183
184         return ret;
185 }
186
187 static int replmd_add_originating(struct ldb_module *module, struct ldb_request *req)
188 {
189         struct ldb_request *down_req;
190         struct ldb_message_element *attribute;
191         struct ldb_message *msg;
192         struct ldb_val v;
193         struct GUID guid;
194         uint64_t seq_num;
195         NTSTATUS nt_status;
196         int ret;
197         time_t t = time(NULL);
198
199         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_originating\n");
200
201         if ((attribute = replmd_find_attribute(req->op.add.message, "objectGUID")) != NULL ) {
202                 return ldb_next_request(module, req);
203         }
204
205         down_req = talloc(req, struct ldb_request);
206         if (down_req == NULL) {
207                 return LDB_ERR_OPERATIONS_ERROR;
208         }
209
210         *down_req = *req;
211
212         /* we have to copy the message as the caller might have it as a const */
213         down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
214         if (msg == NULL) {
215                 talloc_free(down_req);
216                 return LDB_ERR_OPERATIONS_ERROR;
217         }
218
219         /* a new GUID */
220         guid = GUID_random();
221
222         nt_status = ndr_push_struct_blob(&v, msg, &guid, 
223                                          (ndr_push_flags_fn_t)ndr_push_GUID);
224         if (!NT_STATUS_IS_OK(nt_status)) {
225                 talloc_free(down_req);
226                 return LDB_ERR_OPERATIONS_ERROR;
227         }
228
229         ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
230         if (ret) {
231                 talloc_free(down_req);
232                 return ret;
233         }
234         
235         if (add_time_element(msg, "whenCreated", t) != 0 ||
236             add_time_element(msg, "whenChanged", t) != 0) {
237                 talloc_free(down_req);
238                 return LDB_ERR_OPERATIONS_ERROR;
239         }
240
241         /* Get a sequence number from the backend */
242         ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
243         if (ret == LDB_SUCCESS) {
244                 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
245                     add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
246                         talloc_free(down_req);
247                         return LDB_ERR_OPERATIONS_ERROR;
248                 }
249         }
250
251         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
252
253         /* go on with the call chain */
254         ret = ldb_next_request(module, down_req);
255
256         /* do not free down_req as the call results may be linked to it,
257          * it will be freed when the upper level request get freed */
258         if (ret == LDB_SUCCESS) {
259                 req->handle = down_req->handle;
260         }
261
262         return ret;
263 }
264
265 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
266 {
267         struct ldb_control *ctrl;
268
269         /* do not manipulate our control entries */
270         if (ldb_dn_is_special(req->op.add.message->dn)) {
271                 return ldb_next_request(module, req);
272         }
273
274         ctrl = get_control_from_list(req->controls, DSDB_CONTROL_REPLICATED_OBJECT_OID);
275         if (ctrl) {
276                 /* handle replicated objects different */
277                 return replmd_add_replicated(module, req, ctrl);
278         }
279
280         return replmd_add_originating(module, req);
281 }
282
283 static int replmd_modify_replicated(struct ldb_module *module, struct ldb_request *req, struct ldb_control *ctrl)
284 {
285         struct ldb_control **saved_ctrls;
286         int ret;
287
288         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify_replicated\n");
289
290         if (!save_controls(ctrl, req, &saved_ctrls)) {
291                 return LDB_ERR_OPERATIONS_ERROR;
292         }
293
294         ret = ldb_next_request(module, req);
295         req->controls = saved_ctrls;
296
297         return ret;
298 }
299
300 static int replmd_modify_originating(struct ldb_module *module, struct ldb_request *req)
301 {
302         struct ldb_request *down_req;
303         struct ldb_message *msg;
304         int ret;
305         time_t t = time(NULL);
306         uint64_t seq_num;
307
308         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify_originating\n");
309
310         down_req = talloc(req, struct ldb_request);
311         if (down_req == NULL) {
312                 return LDB_ERR_OPERATIONS_ERROR;
313         }
314
315         *down_req = *req;
316
317         /* we have to copy the message as the caller might have it as a const */
318         down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
319         if (msg == NULL) {
320                 talloc_free(down_req);
321                 return LDB_ERR_OPERATIONS_ERROR;
322         }
323
324         if (add_time_element(msg, "whenChanged", t) != 0) {
325                 talloc_free(down_req);
326                 return LDB_ERR_OPERATIONS_ERROR;
327         }
328
329         /* Get a sequence number from the backend */
330         ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
331         if (ret == LDB_SUCCESS) {
332                 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
333                         talloc_free(down_req);
334                         return LDB_ERR_OPERATIONS_ERROR;
335                 }
336         }
337
338         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
339
340         /* go on with the call chain */
341         ret = ldb_next_request(module, down_req);
342
343         /* do not free down_req as the call results may be linked to it,
344          * it will be freed when the upper level request get freed */
345         if (ret == LDB_SUCCESS) {
346                 req->handle = down_req->handle;
347         }
348
349         return ret;
350 }
351
352 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
353 {
354         struct ldb_control *ctrl;
355
356         /* do not manipulate our control entries */
357         if (ldb_dn_is_special(req->op.mod.message->dn)) {
358                 return ldb_next_request(module, req);
359         }
360
361         ctrl = get_control_from_list(req->controls, DSDB_CONTROL_REPLICATED_OBJECT_OID);
362         if (ctrl) {
363                 /* handle replicated objects different */
364                 return replmd_modify_replicated(module, req, ctrl);
365         }
366
367         return replmd_modify_originating(module, req);
368 }
369
370 static int replmd_replicated_request_reply_helper(struct replmd_replicated_request *ar, int ret)
371 {
372         struct ldb_reply *ares = NULL;
373
374         ar->handle->status = ret;
375         ar->handle->state = LDB_ASYNC_DONE;
376
377         if (!ar->orig_req->callback) {
378                 return LDB_SUCCESS;
379         }
380         
381         /* we're done and need to report the success to the caller */
382         ares = talloc_zero(ar, struct ldb_reply);
383         if (!ares) {
384                 ar->handle->status = LDB_ERR_OPERATIONS_ERROR;
385                 ar->handle->state = LDB_ASYNC_DONE;
386                 return LDB_ERR_OPERATIONS_ERROR;
387         }
388
389         ares->type      = LDB_REPLY_EXTENDED;
390         ares->response  = NULL;
391
392         return ar->orig_req->callback(ar->module->ldb, ar->orig_req->context, ares);
393 }
394
395 static int replmd_replicated_request_done(struct replmd_replicated_request *ar)
396 {
397         return replmd_replicated_request_reply_helper(ar, LDB_SUCCESS);
398 }
399
400 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
401 {
402         return replmd_replicated_request_reply_helper(ar, ret);
403 }
404
405 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
406 {
407         int ret = LDB_ERR_OTHER;
408         /* TODO: do some error mapping */
409         return replmd_replicated_request_reply_helper(ar, ret);
410 }
411
412 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
413
414 static int replmd_replicated_apply_add_callback(struct ldb_context *ldb,
415                                                 void *private_data,
416                                                 struct ldb_reply *ares)
417 {
418 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */ 
419         struct replmd_replicated_request *ar = talloc_get_type(private_data,
420                                                struct replmd_replicated_request);
421
422         ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
423         if (ar->sub.change_ret != LDB_SUCCESS) {
424                 return replmd_replicated_request_error(ar, ar->sub.change_ret);
425         }
426
427         talloc_free(ar->sub.mem_ctx);
428         ZERO_STRUCT(ar->sub);
429
430         ar->index_current++;
431
432         return replmd_replicated_apply_next(ar);
433 #else
434         return LDB_SUCCESS;
435 #endif
436 }
437
438 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
439 {
440         NTSTATUS nt_status;
441         struct ldb_message *msg;
442         struct replPropertyMetaDataBlob *md;
443         struct ldb_val md_value;
444         uint32_t i;
445         uint64_t seq_num;
446         int ret;
447
448         msg = ar->objs->objects[ar->index_current].msg;
449         md = ar->objs->objects[ar->index_current].meta_data;
450
451         ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
452         if (ret != LDB_SUCCESS) {
453                 return replmd_replicated_request_error(ar, ret);
454         }
455
456         ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNCreated", seq_num);
457         if (ret != LDB_SUCCESS) {
458                 return replmd_replicated_request_error(ar, ret);
459         }
460
461         ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
462         if (ret != LDB_SUCCESS) {
463                 return replmd_replicated_request_error(ar, ret);
464         }
465
466         md = ar->objs->objects[ar->index_current].meta_data;
467         for (i=0; i < md->ctr.ctr1.count; i++) {
468                 md->ctr.ctr1.array[i].local_usn = seq_num;
469         }
470         nt_status = ndr_push_struct_blob(&md_value, msg, md,
471                                          (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
472         if (!NT_STATUS_IS_OK(nt_status)) {
473                 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
474         }
475         ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
476         if (ret != LDB_SUCCESS) {
477                 return replmd_replicated_request_error(ar, ret);
478         }
479
480         ret = ldb_build_add_req(&ar->sub.change_req,
481                                 ar->module->ldb,
482                                 ar->sub.mem_ctx,
483                                 msg,
484                                 NULL,
485                                 ar,
486                                 replmd_replicated_apply_add_callback);
487         if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
488
489 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */ 
490         return ldb_next_request(ar->module, ar->sub.change_req);
491 #else
492         ret = ldb_next_request(ar->module, ar->sub.change_req);
493         if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
494
495         ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
496         if (ar->sub.change_ret != LDB_SUCCESS) {
497                 return replmd_replicated_request_error(ar, ar->sub.change_ret);
498         }
499
500         talloc_free(ar->sub.mem_ctx);
501         ZERO_STRUCT(ar->sub);
502
503         ar->index_current++;
504
505         return LDB_SUCCESS;
506 #endif
507 }
508
509 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
510 {
511 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */ 
512 #error sorry replmd_replicated_apply_merge not implemented
513 #else
514         ldb_debug(ar->module->ldb, LDB_DEBUG_FATAL,
515                   "replmd_replicated_apply_merge: ignore [%u]\n",
516                   ar->index_current);
517
518         talloc_free(ar->sub.mem_ctx);
519         ZERO_STRUCT(ar->sub);
520
521         ar->index_current++;
522
523         return LDB_SUCCESS;
524 #endif
525 }
526
527 static int replmd_replicated_apply_search_callback(struct ldb_context *ldb,
528                                                    void *private_data,
529                                                    struct ldb_reply *ares)
530 {
531         struct replmd_replicated_request *ar = talloc_get_type(private_data,
532                                                struct replmd_replicated_request);
533         bool is_done = false;
534
535         switch (ares->type) {
536         case LDB_REPLY_ENTRY:
537                 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
538                 break;
539         case LDB_REPLY_REFERRAL:
540                 /* we ignore referrals */
541                 break;
542         case LDB_REPLY_EXTENDED:
543         case LDB_REPLY_DONE:
544                 is_done = true;
545         }
546
547         talloc_free(ares);
548
549 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */ 
550         if (is_done) {
551                 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
552                 if (ar->sub.search_ret != LDB_SUCCESS) {
553                         return replmd_replicated_request_error(ar, ar->sub.search_ret);
554                 }
555                 if (ar->sub.search_msg) {
556                         return replmd_replicated_apply_merge(ar);
557                 }
558                 return replmd_replicated_apply_add(ar);
559         }
560 #endif
561         return LDB_SUCCESS;
562 }
563
564 static int replmd_replicated_apply_search(struct replmd_replicated_request *ar)
565 {
566         int ret;
567         char *tmp_str;
568         char *filter;
569
570         tmp_str = ldb_binary_encode(ar->sub.mem_ctx, ar->objs->objects[ar->index_current].guid_value);
571         if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
572
573         filter = talloc_asprintf(ar->sub.mem_ctx, "(objectGUID=%s)", tmp_str);
574         if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
575         talloc_free(tmp_str);
576
577         ret = ldb_build_search_req(&ar->sub.search_req,
578                                    ar->module->ldb,
579                                    ar->sub.mem_ctx,
580                                    ar->objs->partition_dn,
581                                    LDB_SCOPE_SUBTREE,
582                                    filter,
583                                    NULL,
584                                    NULL,
585                                    ar,
586                                    replmd_replicated_apply_search_callback);
587         if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
588
589 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */ 
590         return ldb_next_request(ar->module, ar->sub.search_req);
591 #else
592         ret = ldb_next_request(ar->module, ar->sub.search_req);
593         if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
594
595         ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
596         if (ar->sub.search_ret != LDB_SUCCESS) {
597                 return replmd_replicated_request_error(ar, ar->sub.search_ret);
598         }
599         if (ar->sub.search_msg) {
600                 return replmd_replicated_apply_merge(ar);
601         }
602
603         return replmd_replicated_apply_add(ar);
604 #endif
605 }
606
607 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
608 {
609         if (ar->index_current >= ar->objs->num_objects) {
610                 return replmd_replicated_request_done(ar);
611         }
612
613         ar->sub.mem_ctx = talloc_new(ar);
614         if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
615
616         return replmd_replicated_apply_search(ar);
617 }
618
619 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
620 {
621         struct dsdb_extended_replicated_objects *objs;
622         struct replmd_replicated_request *ar;
623
624         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
625
626         objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
627         if (!objs) {
628                 return LDB_ERR_PROTOCOL_ERROR;
629         }
630
631         ar = replmd_replicated_init_handle(module, req, objs);
632         if (!ar) {
633                 return LDB_ERR_OPERATIONS_ERROR;
634         }
635
636 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */ 
637         return replmd_replicated_apply_next(ar);
638 #else
639         while (req->handle->state != LDB_ASYNC_DONE) {
640                 replmd_replicated_apply_next(ar);
641         }
642
643         return LDB_SUCCESS;
644 #endif
645 }
646
647 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
648 {
649         if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
650                 return replmd_extended_replicated_objects(module, req);
651         }
652
653         return ldb_next_request(module, req);
654 }
655
656 static int replmd_wait_none(struct ldb_handle *handle) {
657         struct replmd_replicated_request *ar;
658     
659         if (!handle || !handle->private_data) {
660                 return LDB_ERR_OPERATIONS_ERROR;
661         }
662
663         ar = talloc_get_type(handle->private_data, struct replmd_replicated_request);
664         if (!ar) {
665                 return LDB_ERR_OPERATIONS_ERROR;
666         }
667
668         /* we do only sync calls */
669         if (handle->state != LDB_ASYNC_DONE) {
670                 return LDB_ERR_OPERATIONS_ERROR;
671         }
672
673         return handle->status;
674 }
675
676 static int replmd_wait_all(struct ldb_handle *handle) {
677
678         int ret;
679
680         while (handle->state != LDB_ASYNC_DONE) {
681                 ret = replmd_wait_none(handle);
682                 if (ret != LDB_SUCCESS) {
683                         return ret;
684                 }
685         }
686
687         return handle->status;
688 }
689
690 static int replmd_wait(struct ldb_handle *handle, enum ldb_wait_type type)
691 {
692         if (type == LDB_WAIT_ALL) {
693                 return replmd_wait_all(handle);
694         } else {
695                 return replmd_wait_none(handle);
696         }
697 }
698
699 static const struct ldb_module_ops replmd_ops = {
700         .name          = "repl_meta_data",
701         .add           = replmd_add,
702         .modify        = replmd_modify,
703         .extended      = replmd_extended,
704         .wait          = replmd_wait
705 };
706
707 int repl_meta_data_module_init(void)
708 {
709         return ldb_register_module(&replmd_ops);
710 }