]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/syncprov.c
Self-contained detached operation
[openldap] / servers / slapd / overlays / syncprov.c
1 /* syncprov.c - syncrepl provider */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2004 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /* ACKNOWLEDGEMENTS:
16  * This work was initially developed by Howard Chu for inclusion in
17  * OpenLDAP Software.
18  */
19
20 #include "portable.h"
21
22 #ifdef SLAPD_OVER_SYNCPROV
23
24 #include <ac/string.h>
25 #include "lutil.h"
26 #include "slap.h"
27
28 /* A queued result of a persistent search */
29 typedef struct syncres {
30         struct syncres *s_next;
31         struct berval s_dn;
32         struct berval s_ndn;
33         struct berval s_uuid;
34         struct berval s_csn;
35         char s_mode;
36         char s_isreference;
37 } syncres;
38
39 /* Record of a persistent search */
40 typedef struct syncops {
41         struct syncops *s_next;
42         struct berval   s_base;         /* ndn of search base */
43         ID              s_eid;          /* entryID of search base */
44         Operation       *s_op;          /* search op */
45         long    s_sid;
46         long    s_rid;
47         struct berval s_filterstr;
48         int             s_flags;        /* search status */
49         struct syncres *s_res;
50         struct syncres *s_restail;
51         ldap_pvt_thread_mutex_t s_mutex;
52 } syncops;
53
54 static int      sync_cid;
55
56 #define PS_IS_REFRESHING        0x01
57
58 /* Record of which searches matched at premodify step */
59 typedef struct syncmatches {
60         struct syncmatches *sm_next;
61         syncops *sm_op;
62 } syncmatches;
63
64 typedef struct syncprov_info_t {
65         syncops         *si_ops;
66         struct berval   si_ctxcsn;      /* ldapsync context */
67         int             si_gotcsn;      /* is our ctxcsn up to date? */
68         ldap_pvt_thread_mutex_t si_csn_mutex;
69         ldap_pvt_thread_mutex_t si_ops_mutex;
70         char            si_ctxcsnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
71 } syncprov_info_t;
72
73 typedef struct opcookie {
74         slap_overinst *son;
75         syncmatches *smatches;
76         struct berval sdn;      /* DN of entry, for deletes */
77         struct berval sndn;
78         struct berval suuid;    /* UUID of entry */
79         struct berval sctxcsn;
80         int sreference; /* Is the entry a reference? */
81 } opcookie;
82
83 typedef struct fbase_cookie {
84         struct berval *fdn;     /* DN of a modified entry, for scope testing */
85         syncops *fss;   /* persistent search we're testing against */
86         int fbase;      /* if TRUE we found the search base and it's still valid */
87         int fscope;     /* if TRUE then fdn is within the psearch scope */
88 } fbase_cookie;
89
90 static AttributeName csn_anlist[2];
91 static AttributeName uuid_anlist[2];
92
93 /* syncprov_findbase:
94  *   finds the true DN of the base of a search (with alias dereferencing) and
95  * checks to make sure the base entry doesn't get replaced with a different
96  * entry (e.g., swapping trees via ModDN, or retargeting an alias). If a
97  * change is detected, any persistent search on this base must be terminated /
98  * reloaded.
99  *   On the first call, we just save the DN and entryID. On subsequent calls
100  * we compare the DN and entryID with the saved values.
101  */
102 static int
103 findbase_cb( Operation *op, SlapReply *rs )
104 {
105         slap_callback *sc = op->o_callback;
106
107         if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
108                 fbase_cookie *fc = sc->sc_private;
109
110                 /* If no entryID, we're looking for the first time.
111                  * Just store whatever we got.
112                  */
113                 if ( fc->fss->s_eid == NOID ) {
114                         fc->fbase = 1;
115                         fc->fss->s_eid = rs->sr_entry->e_id;
116                         ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
117
118                 } else if ( rs->sr_entry->e_id == fc->fss->s_eid &&
119                         dn_match( &rs->sr_entry->e_nname, &fc->fss->s_base )) {
120
121                 /* OK, the DN is the same and the entryID is the same. Now
122                  * see if the fdn resides in the scope.
123                  */
124                         fc->fbase = 1;
125                         switch ( fc->fss->s_op->ors_scope ) {
126                         case LDAP_SCOPE_BASE:
127                                 fc->fscope = dn_match( fc->fdn, &rs->sr_entry->e_nname );
128                                 break;
129                         case LDAP_SCOPE_ONELEVEL: {
130                                 struct berval pdn;
131                                 dnParent( fc->fdn, &pdn );
132                                 fc->fscope = dn_match( &pdn, &rs->sr_entry->e_nname );
133                                 break; }
134                         case LDAP_SCOPE_SUBTREE:
135                                 fc->fscope = dnIsSuffix( fc->fdn, &rs->sr_entry->e_nname );
136                                 break;
137 #ifdef LDAP_SCOPE_SUBORDINATE
138                         case LDAP_SCOPE_SUBORDINATE:
139                                 fc->fscope = dnIsSuffix( fc->fdn, &rs->sr_entry->e_nname ) &&
140                                         !dn_match( fc->fdn, &rs->sr_entry->e_nname );
141                                 break;
142 #endif
143                         }
144                 }
145         }
146         return LDAP_SUCCESS;
147 }
148
149 static int
150 syncprov_findbase( Operation *op, fbase_cookie *fc )
151 {
152         opcookie *opc = op->o_callback->sc_private;
153         slap_overinst *on = opc->son;
154         syncprov_info_t         *si = on->on_bi.bi_private;
155
156         slap_callback cb = {0};
157         Operation fop;
158         SlapReply frs = { REP_RESULT };
159         int rc;
160
161         fop = *op;
162
163         cb.sc_response = findbase_cb;
164         cb.sc_private = fc;
165
166         fop.o_sync_mode &= SLAP_CONTROL_MASK;   /* turn off sync mode */
167         fop.o_callback = &cb;
168         fop.o_tag = LDAP_REQ_SEARCH;
169         fop.ors_scope = LDAP_SCOPE_BASE;
170         fop.ors_deref = fc->fss->s_op->ors_deref;
171         fop.ors_slimit = 1;
172         fop.ors_tlimit = SLAP_NO_LIMIT;
173         fop.ors_attrs = slap_anlist_no_attrs;
174         fop.ors_attrsonly = 1;
175         fop.ors_filter = fc->fss->s_op->ors_filter;
176         fop.ors_filterstr = fc->fss->s_op->ors_filterstr;
177
178         fop.o_req_ndn = fc->fss->s_op->o_req_ndn;
179
180         fop.o_bd->bd_info = on->on_info->oi_orig;
181         rc = fop.o_bd->be_search( &fop, &frs );
182         fop.o_bd->bd_info = (BackendInfo *)on;
183
184         if ( fc->fbase ) return LDAP_SUCCESS;
185
186         /* If entryID has changed, then the base of this search has
187          * changed. Invalidate the psearch.
188          */
189         return LDAP_NO_SUCH_OBJECT;
190 }
191
192 /* syncprov_findcsn:
193  *   This function has three different purposes, but they all use a search
194  * that filters on entryCSN so they're combined here.
195  * 1: when the current contextCSN is unknown (i.e., at server start time)
196  * and a syncrepl search has arrived with a cookie, we search for all entries
197  * with CSN >= the cookie CSN, and store the maximum as our contextCSN. Also,
198  * we expect to find the cookie CSN in the search results, and note if we did
199  * or not. If not, we assume the cookie is stale. (This may be too restrictive,
200  * notice case 2.)
201  *
202  * 2: when the current contextCSN is known and we have a sync cookie, we search
203  * for one entry with CSN <= the cookie CSN. (Used to search for =.) If an
204  * entry is found, the cookie CSN is valid, otherwise it is stale. Case 1 is
205  * considered a special case of case 2, and both are generally called the
206  * "find CSN" task.
207  *
208  * 3: during a refresh phase, we search for all entries with CSN <= the cookie
209  * CSN, and generate Present records for them. We always collect this result
210  * in SyncID sets, even if there's only one match.
211  */
212 #define FIND_CSN        1
213 #define FIND_PRESENT    2
214
215 typedef struct fcsn_cookie {
216         struct berval maxcsn;
217         int gotmatch;
218 } fcsn_cookie;
219
220 static int
221 findcsn_cb( Operation *op, SlapReply *rs )
222 {
223         slap_callback *sc = op->o_callback;
224
225         if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
226                 /* If the private pointer is set, it points to an fcsn_cookie
227                  * and we want to record the maxcsn and match state.
228                  */
229                 if ( sc->sc_private ) {
230                         int i;
231                         fcsn_cookie *fc = sc->sc_private;
232                         syncrepl_state *srs = op->o_controls[sync_cid];
233                         Attribute *a = attr_find(rs->sr_entry->e_attrs,
234                                 slap_schema.si_ad_entryCSN );
235                         i = ber_bvcmp( &a->a_vals[0], srs->sr_state.ctxcsn );
236                         if ( i == 0 ) fc->gotmatch = 1;
237                         i = ber_bvcmp( &a->a_vals[0], &fc->maxcsn );
238                         if ( i > 0 ) {
239                                 fc->maxcsn.bv_len = a->a_vals[0].bv_len;
240                                 strcpy(fc->maxcsn.bv_val, a->a_vals[0].bv_val );
241                         }
242                 } else {
243                 /* Otherwise, if the private pointer is not set, we just
244                  * want to know if any entry matched the filter.
245                  */
246                         sc->sc_private = (void *)1;
247                 }
248         }
249         return LDAP_SUCCESS;
250 }
251
252 /* Build a list of entryUUIDs for sending in a SyncID set */
253
254 typedef struct fpres_cookie {
255         int num;
256         BerVarray uuids;
257 } fpres_cookie;
258
259 static int
260 findpres_cb( Operation *op, SlapReply *rs )
261 {
262         slap_callback *sc = op->o_callback;
263         fpres_cookie *pc = sc->sc_private;
264         int ret = SLAP_CB_CONTINUE;
265
266         if ( rs->sr_type == REP_SEARCH ) {
267                 ret = slap_build_syncUUID_set( op, &pc->uuids, rs->sr_entry );
268                 if ( ret > 0 ) {
269                         pc->num++;
270                         ret = LDAP_SUCCESS;
271                         if ( pc->num == SLAP_SYNCUUID_SET_SIZE ) {
272                                 rs->sr_rspoid = LDAP_SYNC_INFO;
273                                 ret = slap_send_syncinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
274                                         0, pc->uuids, 0 );
275                                 ber_bvarray_free_x( pc->uuids, op->o_tmpmemctx );
276                                 pc->uuids = NULL;
277                                 pc->num = 0;
278                         }
279                 } else {
280                         ret = LDAP_OTHER;
281                 }
282         } else if ( rs->sr_type == REP_RESULT ) {
283                 ret = rs->sr_err;
284                 if ( pc->num ) {
285                         rs->sr_rspoid = LDAP_SYNC_INFO;
286                         ret = slap_send_syncinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
287                                 0, pc->uuids, 0 );
288                         ber_bvarray_free_x( pc->uuids, op->o_tmpmemctx );
289                         pc->uuids = NULL;
290                         pc->num = 0;
291                 }
292         }
293         return ret;
294 }
295
296
297 static int
298 syncprov_findcsn( Operation *op, int mode )
299 {
300         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
301         syncprov_info_t         *si = on->on_bi.bi_private;
302
303         slap_callback cb = {0};
304         Operation fop;
305         SlapReply frs = { REP_RESULT };
306         char buf[LDAP_LUTIL_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
307         char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
308         struct berval fbuf;
309         Filter cf;
310         AttributeAssertion eq;
311         int rc;
312         fcsn_cookie fcookie;
313         fpres_cookie pcookie;
314         int locked = 0;
315         syncrepl_state *srs = op->o_controls[sync_cid];
316
317         if ( srs->sr_state.ctxcsn->bv_len >= LDAP_LUTIL_CSNSTR_BUFSIZE ) {
318                 return LDAP_OTHER;
319         }
320
321         fop = *op;
322         fop.o_sync_mode &= SLAP_CONTROL_MASK;   /* turn off sync_mode */
323
324         fbuf.bv_val = buf;
325         if ( mode == FIND_CSN ) {
326                 if ( !si->si_gotcsn ) {
327                         /* If we don't know the current ctxcsn, find it */
328                         ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
329                         locked = 1;
330                 }
331                 if ( !si->si_gotcsn ) {
332                         cf.f_choice = LDAP_FILTER_GE;
333                         fop.ors_attrsonly = 0;
334                         fop.ors_attrs = csn_anlist;
335                         fop.ors_slimit = SLAP_NO_LIMIT;
336                         cb.sc_private = &fcookie;
337                         fcookie.maxcsn.bv_val = cbuf;
338                         fcookie.maxcsn.bv_len = 0;
339                         fcookie.gotmatch = 0;
340                         fbuf.bv_len = sprintf( buf, "(entryCSN>=%s)", srs->sr_state.ctxcsn->bv_val );
341                 } else {
342                         if ( locked ) {
343                                 ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
344                                 locked = 0;
345                         }
346                         cf.f_choice = LDAP_FILTER_LE;
347                         fop.ors_attrsonly = 1;
348                         fop.ors_attrs = slap_anlist_no_attrs;
349                         fop.ors_slimit = 1;
350                         cb.sc_private = NULL;
351                         fbuf.bv_len = sprintf( buf, "(entryCSN<=%s)", srs->sr_state.ctxcsn->bv_val );
352                 }
353                 cb.sc_response = findcsn_cb;
354
355         } else if ( mode == FIND_PRESENT ) {
356                 cf.f_choice = LDAP_FILTER_LE;
357                 fop.ors_attrsonly = 0;
358                 fop.ors_attrs = uuid_anlist;
359                 fop.ors_slimit = SLAP_NO_LIMIT;
360                 /* We want pure entries, not referrals */
361                 fop.o_managedsait = SLAP_CONTROL_CRITICAL;
362                 cb.sc_private = &pcookie;
363                 cb.sc_response = findpres_cb;
364                 pcookie.num = 0;
365                 pcookie.uuids = NULL;
366                 fbuf.bv_len = sprintf( buf, "(entryCSN<=%s)", srs->sr_state.ctxcsn->bv_val );
367         }
368         cf.f_ava = &eq;
369         cf.f_av_desc = slap_schema.si_ad_entryCSN;
370         cf.f_av_value = *srs->sr_state.ctxcsn;
371         cf.f_next = NULL;
372
373         fop.o_callback = &cb;
374         fop.ors_tlimit = SLAP_NO_LIMIT;
375         fop.ors_filter = &cf;
376         fop.ors_filterstr = fbuf;
377
378         fop.o_bd->bd_info = on->on_info->oi_orig;
379         rc = fop.o_bd->be_search( &fop, &frs );
380         fop.o_bd->bd_info = (BackendInfo *)on;
381
382         if ( mode == FIND_CSN ) {
383                 if ( !si->si_gotcsn ) {
384                         strcpy(si->si_ctxcsnbuf, fcookie.maxcsn.bv_val);
385                         si->si_ctxcsn.bv_len = fcookie.maxcsn.bv_len;
386                         si->si_gotcsn = 1;
387                         ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
388                         if ( fcookie.gotmatch ) return LDAP_SUCCESS;
389                         
390                 } else {
391                         if ( cb.sc_private ) return LDAP_SUCCESS;
392                 }
393         } else if ( mode == FIND_PRESENT ) {
394                 return LDAP_SUCCESS;
395         }
396
397         /* If matching CSN was not found, invalidate the context. */
398         return LDAP_NO_SUCH_OBJECT;
399 }
400
401 /* Queue a persistent search response if still in Refresh stage */
402 static int
403 syncprov_qresp( opcookie *opc, syncops *so, int mode )
404 {
405         syncres *sr;
406
407         sr = ch_malloc(sizeof(syncres) + opc->suuid.bv_len + 1 +
408                 opc->sdn.bv_len + 1 + opc->sndn.bv_len + 1 + opc->sctxcsn.bv_len + 1 );
409         sr->s_next = NULL;
410         sr->s_dn.bv_val = (char *)(sr + 1);
411         sr->s_mode = mode;
412         sr->s_isreference = opc->sreference;
413         sr->s_ndn.bv_val = lutil_strcopy( sr->s_dn.bv_val, opc->sdn.bv_val );
414         *(sr->s_ndn.bv_val++) = '\0';
415         sr->s_uuid.bv_val = lutil_strcopy( sr->s_ndn.bv_val, opc->sndn.bv_val );
416         *(sr->s_uuid.bv_val++) = '\0';
417         sr->s_csn.bv_val = lutil_strcopy( sr->s_uuid.bv_val, opc->suuid.bv_val );
418
419         if ( !so->s_res ) {
420                 so->s_res = sr;
421         } else {
422                 so->s_restail->s_next = sr;
423         }
424         so->s_restail = sr;
425         ldap_pvt_thread_mutex_unlock( &so->s_mutex );
426         return LDAP_SUCCESS;
427 }
428
429 /* Send a persistent search response */
430 static int
431 syncprov_sendresp( Operation *op, opcookie *opc, syncops *so, Entry *e, int mode, int queue )
432 {
433         slap_overinst *on = opc->son;
434         syncprov_info_t *si = on->on_bi.bi_private;
435
436         SlapReply rs = { REP_SEARCH };
437         LDAPControl *ctrls[2];
438         struct berval cookie;
439         Entry e_uuid = {0};
440         Attribute a_uuid = {0};
441         Operation sop = *so->s_op;
442         Opheader ohdr;
443
444         ohdr = *sop.o_hdr;
445         sop.o_hdr = &ohdr;
446         sop.o_tmpmemctx = op->o_tmpmemctx;
447         sop.o_bd = op->o_bd;
448         sop.o_controls = op->o_controls;
449
450         if ( queue && (so->s_flags & PS_IS_REFRESHING) ) {
451                 ldap_pvt_thread_mutex_lock( &so->s_mutex );
452                 if ( so->s_flags & PS_IS_REFRESHING )
453                         return syncprov_qresp( opc, so, mode );
454                 ldap_pvt_thread_mutex_unlock( &so->s_mutex );
455         }
456
457         ctrls[1] = NULL;
458         slap_compose_sync_cookie( op, &cookie, &opc->sctxcsn,
459                 so->s_sid, so->s_rid );
460
461         e_uuid.e_attrs = &a_uuid;
462         a_uuid.a_desc = slap_schema.si_ad_entryUUID;
463         a_uuid.a_nvals = &opc->suuid;
464         rs.sr_err = slap_build_sync_state_ctrl( &sop, &rs, &e_uuid,
465                 mode, ctrls, 0, 1, &cookie );
466
467         rs.sr_entry = e;
468         rs.sr_ctrls = ctrls;
469         switch( mode ) {
470         case LDAP_SYNC_ADD:
471                 if ( opc->sreference ) {
472                         rs.sr_ref = get_entry_referrals( &sop, e );
473                         send_search_reference( &sop, &rs );
474                         ber_bvarray_free( rs.sr_ref );
475                         break;
476                 }
477                 /* fallthru */
478         case LDAP_SYNC_MODIFY:
479                 rs.sr_attrs = sop.ors_attrs;
480                 send_search_entry( &sop, &rs );
481                 break;
482         case LDAP_SYNC_DELETE:
483                 e_uuid.e_attrs = NULL;
484                 e_uuid.e_name = opc->sdn;
485                 e_uuid.e_nname = opc->sndn;
486                 rs.sr_entry = &e_uuid;
487                 if ( opc->sreference ) {
488                         struct berval bv;
489                         bv.bv_val = NULL;
490                         bv.bv_len = 0;
491                         rs.sr_ref = &bv;
492                         send_search_reference( &sop, &rs );
493                 } else {
494                         send_search_entry( &sop, &rs );
495                 }
496                 break;
497         default:
498                 assert(0);
499         }
500         free( rs.sr_ctrls[0] );
501         return rs.sr_err;
502 }
503
504 static void
505 syncprov_matchops( Operation *op, opcookie *opc, int saveit )
506 {
507         slap_overinst *on = opc->son;
508         syncprov_info_t         *si = on->on_bi.bi_private;
509
510         fbase_cookie fc;
511         syncops *ss;
512         Entry *e;
513         Attribute *a;
514         int rc;
515         struct berval newdn;
516
517         fc.fdn = &op->o_req_ndn;
518         /* compute new DN */
519         if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
520                 struct berval pdn;
521                 if ( op->orr_nnewSup ) pdn = *op->orr_nnewSup;
522                 else dnParent( fc.fdn, &pdn );
523                 build_new_dn( &newdn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
524                 fc.fdn = &newdn;
525         }
526         if ( op->o_tag != LDAP_REQ_ADD ) {
527                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
528                 rc = be_entry_get_rw( op, fc.fdn, NULL, NULL, 0, &e );
529                 op->o_bd->bd_info = (BackendInfo *)on;
530                 if ( rc ) return;
531         } else {
532                 e = op->ora_e;
533         }
534
535         if ( saveit ) {
536                 ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
537                 ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
538                 opc->sreference = is_entry_referral( e );
539         }
540         if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
541                 a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
542                 if ( a )
543                         ber_dupbv_x( &opc->suuid, &a->a_nvals[0], op->o_tmpmemctx );
544         }
545
546         ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
547         for (ss = si->si_ops; ss; ss=ss->s_next)
548         {
549                 syncmatches *sm;
550                 int found = 0;
551
552                 /* validate base */
553                 fc.fss = ss;
554                 fc.fbase = 0;
555                 fc.fscope = 0;
556                 rc = syncprov_findbase( op, &fc );
557                 if ( rc != LDAP_SUCCESS ) continue;
558
559                 /* If we're sending results now, look for this op in old matches */
560                 if ( !saveit ) {
561                         syncmatches *old;
562                         for ( sm=opc->smatches, old=(syncmatches *)&opc->smatches; sm;
563                                 old=sm, sm=sm->sm_next ) {
564                                 if ( sm->sm_op == ss ) {
565                                         found = 1;
566                                         old->sm_next = sm->sm_next;
567                                         op->o_tmpfree( sm, op->o_tmpmemctx );
568                                         break;
569                                 }
570                         }
571                 }
572
573                 /* check if current o_req_dn is in scope and matches filter */
574                 if ( fc.fscope && test_filter( op, e, ss->s_op->ors_filter ) ==
575                         LDAP_COMPARE_TRUE ) {
576                         if ( saveit ) {
577                                 sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
578                                 sm->sm_next = opc->smatches;
579                                 sm->sm_op = ss;
580                                 opc->smatches = sm;
581                         } else {
582                                 /* if found send UPDATE else send ADD */
583                                 syncprov_sendresp( op, opc, ss, e,
584                                         found ? LDAP_SYNC_MODIFY : LDAP_SYNC_ADD, 1 );
585                         }
586                 } else if ( !saveit && found ) {
587                         /* send DELETE */
588                         syncprov_sendresp( op, opc, ss, NULL, LDAP_SYNC_DELETE, 1 );
589                 }
590         }
591         ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
592         if ( op->o_tag != LDAP_REQ_ADD ) {
593                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
594                 be_entry_release_r( op, e );
595                 op->o_bd->bd_info = (BackendInfo *)on;
596         }
597 }
598
599 static int
600 syncprov_op_cleanup( Operation *op, SlapReply *rs )
601 {
602         slap_callback *cb = op->o_callback;
603         opcookie *opc = cb->sc_private;
604         syncmatches *sm, *snext;
605
606         for (sm = opc->smatches; sm; sm=snext) {
607                 snext = sm->sm_next;
608                 op->o_tmpfree( sm, op->o_tmpmemctx );
609         }
610         op->o_callback = cb->sc_next;
611         op->o_tmpfree(cb, op->o_tmpmemctx);
612 }
613
614 static int
615 syncprov_op_response( Operation *op, SlapReply *rs )
616 {
617         opcookie *opc = op->o_callback->sc_private;
618         slap_overinst *on = opc->son;
619         syncprov_info_t         *si = on->on_bi.bi_private;
620         syncmatches *sm;
621
622         if ( rs->sr_err == LDAP_SUCCESS )
623         {
624                 struct berval maxcsn;
625                 char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
626
627                 cbuf[0] = '\0';
628                 ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
629                 slap_get_commit_csn( op, &maxcsn );
630                 if ( maxcsn.bv_val ) {
631                         strcpy( cbuf, maxcsn.bv_val );
632                         if ( ber_bvcmp( &maxcsn, &si->si_ctxcsn ) > 0 ) {
633                                 strcpy( si->si_ctxcsnbuf, cbuf );
634                                 si->si_ctxcsn.bv_len = maxcsn.bv_len;
635                         }
636                         si->si_gotcsn = 1;
637                 }
638                 ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
639
640                 opc->sctxcsn.bv_len = maxcsn.bv_len;
641                 opc->sctxcsn.bv_val = cbuf;
642
643                 if ( si->si_ops ) {
644                         switch(op->o_tag) {
645                         case LDAP_REQ_ADD:
646                         case LDAP_REQ_MODIFY:
647                         case LDAP_REQ_MODRDN:
648                         case LDAP_REQ_EXTENDED:
649                                 syncprov_matchops( op, opc, 0 );
650                                 break;
651                         case LDAP_REQ_DELETE:
652                                 /* for each match in opc->smatches:
653                                  *   send DELETE msg
654                                  */
655                                 for ( sm = opc->smatches; sm; sm=sm->sm_next ) {
656                                         syncprov_sendresp( op, opc, sm->sm_op, NULL,
657                                                 LDAP_SYNC_DELETE, 1 );
658                                 }
659                                 break;
660                         }
661                 }
662
663         }
664         return SLAP_CB_CONTINUE;
665 }
666
667 #if 0
668 static int
669 syncprov_op_compare( Operation *op, SlapReply *rs )
670 {
671         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
672         syncprov_info_t         *si = on->on_bi.bi_private;
673         int rc = SLAP_CB_CONTINUE;
674
675         if ( dn_match( &op->o_req_ndn, &si->si_e->e_nname ) )
676         {
677                 Attribute *a;
678
679                 ldap_pvt_thread_mutex_lock( &si->si_e_mutex );
680
681                 if ( get_assert( op ) &&
682                         ( test_filter( op, si->si_e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
683                 {
684                         rs->sr_err = LDAP_ASSERTION_FAILED;
685                         goto return_results;
686                 }
687
688                 rs->sr_err = access_allowed( op, si->si_e, op->oq_compare.rs_ava->aa_desc,
689                         &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
690                 if ( ! rs->sr_err ) {
691                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
692                         goto return_results;
693                 }
694
695                 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
696
697                 for ( a = attr_find( si->si_e->e_attrs, op->oq_compare.rs_ava->aa_desc );
698                         a != NULL;
699                         a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
700                 {
701                         rs->sr_err = LDAP_COMPARE_FALSE;
702
703                         if ( value_find_ex( op->oq_compare.rs_ava->aa_desc,
704                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
705                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
706                                 a->a_nvals, &op->oq_compare.rs_ava->aa_value, op->o_tmpmemctx ) == 0 )
707                         {
708                                 rs->sr_err = LDAP_COMPARE_TRUE;
709                                 break;
710                         }
711                 }
712
713 return_results:;
714
715                 ldap_pvt_thread_mutex_unlock( &si->si_e_mutex );
716
717                 send_ldap_result( op, rs );
718
719                 if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
720                         rs->sr_err = LDAP_SUCCESS;
721                 }
722                 rc = rs->sr_err;
723         }
724
725         return SLAP_CB_CONTINUE;
726 }
727 #endif
728         
729 static int
730 syncprov_op_mod( Operation *op, SlapReply *rs )
731 {
732         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
733         syncprov_info_t         *si = on->on_bi.bi_private;
734
735         slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx);
736         opcookie *opc = (opcookie *)(cb+1);
737         opc->son = on;
738         cb->sc_response = syncprov_op_response;
739         cb->sc_cleanup = syncprov_op_cleanup;
740         cb->sc_private = opc;
741         cb->sc_next = op->o_callback;
742         op->o_callback = cb;
743
744         if ( si->si_ops && op->o_tag != LDAP_REQ_ADD )
745                 syncprov_matchops( op, opc, 1 );
746
747         return SLAP_CB_CONTINUE;
748 }
749
750 static int
751 syncprov_op_extended( Operation *op, SlapReply *rs )
752 {
753         if ( exop_is_write( op ))
754                 return syncprov_op_mod( op, rs );
755
756         return SLAP_CB_CONTINUE;
757 }
758
759 typedef struct searchstate {
760         slap_overinst *ss_on;
761         syncops *ss_so;
762 } searchstate;
763
764 static int
765 syncprov_search_cleanup( Operation *op, SlapReply *rs )
766 {
767         searchstate *ss = op->o_callback->sc_private;
768         if ( rs->sr_ctrls ) {
769                 free( rs->sr_ctrls[0] );
770                 op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
771         }
772         return 0;
773 }
774
775 static void
776 syncprov_detach_op( Operation *op, syncops *so )
777 {
778         Operation *op2;
779         int i, alen = 0;
780         size_t size;
781         char *ptr;
782
783         /* count the search attrs */
784         for (i=0; op->ors_attrs && op->ors_attrs[i].an_name.bv_val; i++) {
785                 alen += op->ors_attrs[i].an_name.bv_len + 1;
786         }
787         /* Make a new copy of the operation */
788         size = sizeof(Operation) + sizeof(Opheader) +
789                 (i ? ( (i+1) * sizeof(AttributeName) + alen) : 0) +
790                 op->o_req_dn.bv_len + 1 +
791                 op->o_req_ndn.bv_len + 1 +
792                 op->o_ndn.bv_len + 1 +
793                 so->s_filterstr.bv_len + 1;
794         op2 = (Operation *)ch_malloc( size );
795         *op2 = *op;
796         op2->o_hdr = (Opheader *)(op2+1);
797         *op2->o_hdr = *op->o_hdr;
798         if ( i ) {
799                 op2->ors_attrs = (AttributeName *)(op2->o_hdr + 1);
800                 ptr = (char *)(op2->ors_attrs+i+1);
801                 for (i=0; op->ors_attrs[i].an_name.bv_val; i++) {
802                         op2->ors_attrs[i] = op->ors_attrs[i];
803                         op2->ors_attrs[i].an_name.bv_val = ptr;
804                         ptr = lutil_strcopy( ptr, op->ors_attrs[i].an_name.bv_val ) + 1;
805                 }
806         } else {
807                 ptr = (char *)(op2->o_hdr + 1);
808         }
809         op2->o_ndn.bv_val = ptr;
810         ptr = lutil_strcopy(ptr, op->o_ndn.bv_val) + 1;
811         op2->o_dn = op2->o_ndn;
812         op2->o_req_dn.bv_val = ptr;
813         ptr = lutil_strcopy(ptr, op->o_req_dn.bv_val) + 1;
814         op2->o_req_ndn.bv_val = ptr;
815         ptr = lutil_strcopy(ptr, op->o_req_ndn.bv_val) + 1;
816         op2->ors_filterstr.bv_val = ptr;
817         strcpy( ptr, so->s_filterstr.bv_val );
818         op2->ors_filterstr.bv_len = so->s_filterstr.bv_len;
819         op2->ors_filter = str2filter( ptr );
820         op2->o_controls = NULL;
821         op2->o_callback = NULL;
822         so->s_op = op2;
823
824         /* Increment number of ops so that idletimeout ignores us */
825         ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
826         op->o_conn->c_n_ops_executing++;
827         ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
828 }
829
830 static int
831 syncprov_search_response( Operation *op, SlapReply *rs )
832 {
833         searchstate *ss = op->o_callback->sc_private;
834         slap_overinst *on = ss->ss_on;
835         syncprov_info_t         *si = on->on_bi.bi_private;
836         syncrepl_state *srs = op->o_controls[sync_cid];
837
838         if ( rs->sr_type == REP_SEARCH || rs->sr_type == REP_SEARCHREF ) {
839                 int i;
840                 if ( srs->sr_state.ctxcsn ) {
841                         Attribute *a = attr_find( rs->sr_entry->e_attrs,
842                                 slap_schema.si_ad_entryCSN );
843                         /* Don't send the ctx entry twice */
844                         if ( bvmatch( &a->a_nvals[0], srs->sr_state.ctxcsn ))
845                                 return LDAP_SUCCESS;
846                 }
847                 rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
848                         op->o_tmpmemctx );
849                 rs->sr_ctrls[1] = NULL;
850                 rs->sr_err = slap_build_sync_state_ctrl( op, rs, rs->sr_entry,
851                         LDAP_SYNC_ADD, rs->sr_ctrls, 0, 0, NULL );
852         } else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) {
853                 struct berval cookie;
854
855                 slap_compose_sync_cookie( op, &cookie,
856                         &op->ors_filter->f_and->f_ava->aa_value,
857                         srs->sr_state.sid, srs->sr_state.rid );
858
859                 /* Is this a regular refresh? */
860                 if ( !ss->ss_so ) {
861                         rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
862                                 op->o_tmpmemctx );
863                         rs->sr_ctrls[1] = NULL;
864                         rs->sr_err = slap_build_sync_done_ctrl( op, rs, rs->sr_ctrls,
865                                 0, 1, &cookie, LDAP_SYNC_REFRESH_PRESENTS );
866                 } else {
867                         int locked = 0;
868                 /* It's RefreshAndPersist, transition to Persist phase */
869                         rs->sr_rspoid = LDAP_SYNC_INFO;
870                         slap_send_syncinfo( op, rs, rs->sr_nentries ?
871                                 LDAP_TAG_SYNC_REFRESH_PRESENT : LDAP_TAG_SYNC_REFRESH_DELETE,
872                                 &cookie, 1, NULL, 0 );
873                         /* Flush any queued persist messages */
874                         if ( ss->ss_so->s_res ) {
875                                 syncres *sr, *srnext;
876                                 Entry *e;
877                                 opcookie opc;
878
879                                 opc.son = on;
880                                 ldap_pvt_thread_mutex_lock( &ss->ss_so->s_mutex );
881                                 locked = 1;
882                                 for (sr = ss->ss_so->s_res; sr; sr=srnext) {
883                                         int rc = LDAP_SUCCESS;
884                                         srnext = sr->s_next;
885                                         opc.sdn = sr->s_dn;
886                                         opc.sndn = sr->s_ndn;
887                                         opc.suuid = sr->s_uuid;
888                                         opc.sctxcsn = sr->s_csn;
889                                         opc.sreference = sr->s_isreference;
890                                         e = NULL;
891                                         
892                                         if ( sr->s_mode != LDAP_SYNC_DELETE ) {
893                                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
894                                                 rc = be_entry_get_rw( op, &opc.sndn, NULL, NULL, 0, &e );
895                                                 op->o_bd->bd_info = (BackendInfo *)on;
896                                         }
897                                         if ( rc == LDAP_SUCCESS )
898                                                 syncprov_sendresp( op, &opc, ss->ss_so, e,
899                                                         sr->s_mode, 0 );
900
901                                         if ( e ) {
902                                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
903                                                 be_entry_release_r( op, e );
904                                                 op->o_bd->bd_info = (BackendInfo *)on;
905                                         }
906                                         ch_free( sr );
907                                 }
908                                 ss->ss_so->s_res = NULL;
909                                 ss->ss_so->s_restail = NULL;
910                         }
911
912                         /* Turn off the refreshing flag */
913                         ss->ss_so->s_flags ^= PS_IS_REFRESHING;
914                         if ( locked )
915                                 ldap_pvt_thread_mutex_unlock( &ss->ss_so->s_mutex );
916
917                         /* Detach this Op from frontend control */
918                         syncprov_detach_op( op, ss->ss_so );
919
920                         return LDAP_SUCCESS;
921                 }
922         }
923
924         return SLAP_CB_CONTINUE;
925 }
926
927 static int
928 syncprov_op_search( Operation *op, SlapReply *rs )
929 {
930         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
931         syncprov_info_t         *si = (syncprov_info_t *)on->on_bi.bi_private;
932         slap_callback   *cb;
933         int gotstate = 0, nochange = 0;
934         Filter *fand, *fava;
935         syncops *sop = NULL;
936         searchstate *ss;
937         syncrepl_state *srs;
938
939         if ( !(op->o_sync_mode & SLAP_SYNC_REFRESH) ) return SLAP_CB_CONTINUE;
940
941         if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
942                 send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "illegal value for derefAliases" );
943                 return rs->sr_err;
944         }
945
946         srs = op->o_controls[sync_cid];
947
948         /* If this is a persistent search, set it up right away */
949         if ( op->o_sync_mode & SLAP_SYNC_PERSIST ) {
950                 syncops so = {0};
951                 fbase_cookie fc;
952                 opcookie opc;
953                 slap_callback sc;
954
955                 fc.fss = &so;
956                 fc.fbase = 0;
957                 so.s_eid = NOID;
958                 so.s_op = op;
959                 so.s_flags = PS_IS_REFRESHING;
960                 /* syncprov_findbase expects to be called as a callback... */
961                 sc.sc_private = &opc;
962                 opc.son = on;
963                 cb = op->o_callback;
964                 op->o_callback = &sc;
965                 rs->sr_err = syncprov_findbase( op, &fc );
966                 op->o_callback = cb;
967
968                 if ( rs->sr_err != LDAP_SUCCESS ) {
969                         send_ldap_result( op, rs );
970                         return rs->sr_err;
971                 }
972                 sop = ch_malloc( sizeof( syncops ));
973                 *sop = so;
974                 ldap_pvt_thread_mutex_init( &sop->s_mutex );
975                 ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
976                 sop->s_sid = srs->sr_state.sid;
977                 sop->s_rid = srs->sr_state.rid;
978                 sop->s_next = si->si_ops;
979                 si->si_ops = sop;
980                 ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
981         }
982
983         /* If we have a cookie, handle the PRESENT lookups
984          */
985         if ( srs->sr_state.ctxcsn ) {
986                 /* Is the CSN in a valid format? */
987                 if ( srs->sr_state.ctxcsn->bv_len >= LDAP_LUTIL_CSNSTR_BUFSIZE ) {
988                         send_ldap_error( op, rs, LDAP_OTHER, "invalid sync cookie" );
989                         return rs->sr_err;
990                 }
991                 /* Is the CSN still present in the database? */
992                 if ( syncprov_findcsn( op, FIND_CSN ) != LDAP_SUCCESS ) {
993                         /* No, so a reload is required */
994 #if 0           /* the consumer doesn't seem to send this hint */
995                         if ( op->o_sync_rhint == 0 ) {
996                                 send_ldap_error( op, rs, LDAP_SYNC_REFRESH_REQUIRED, "sync cookie is stale" );
997                                 return rs->sr_err;
998                         }
999 #endif
1000                 } else {
1001                         gotstate = 1;
1002                         /* If just Refreshing and nothing has changed, shortcut it */
1003                         if ( bvmatch( srs->sr_state.ctxcsn, &si->si_ctxcsn )) {
1004                                 nochange = 1;
1005                                 if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
1006                                         LDAPControl     *ctrls[2];
1007
1008                                         ctrls[0] = NULL;
1009                                         ctrls[1] = NULL;
1010                                         slap_build_sync_done_ctrl( op, rs, ctrls, 0, 0,
1011                                                 NULL, LDAP_SYNC_REFRESH_DELETES );
1012                                         rs->sr_ctrls = ctrls;
1013                                         rs->sr_err = LDAP_SUCCESS;
1014                                         send_ldap_result( op, rs );
1015                                         return rs->sr_err;
1016                                 }
1017                                 goto shortcut;
1018                         } else 
1019                         /* If context has changed, check for Present UUIDs */
1020                         if ( syncprov_findcsn( op, FIND_PRESENT ) != LDAP_SUCCESS ) {
1021                                 send_ldap_result( op, rs );
1022                                 return rs->sr_err;
1023                         }
1024                 }
1025         }
1026
1027         /* If we didn't get a cookie and we don't know our contextcsn, try to
1028          * find it anyway.
1029          */
1030         if ( !gotstate && !si->si_gotcsn ) {
1031                 struct berval bv = BER_BVC("1"), *old;
1032                 
1033                 old = srs->sr_state.ctxcsn;
1034                 srs->sr_state.ctxcsn = &bv;
1035                 syncprov_findcsn( op, FIND_CSN );
1036                 srs->sr_state.ctxcsn = old;
1037         }
1038
1039         /* Append CSN range to search filter, save original filter
1040          * for persistent search evaluation
1041          */
1042         if ( sop ) {
1043                 sop->s_filterstr= op->ors_filterstr;
1044         }
1045
1046         fand = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1047         fand->f_choice = LDAP_FILTER_AND;
1048         fand->f_next = NULL;
1049         fava = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1050         fava->f_choice = LDAP_FILTER_LE;
1051         fava->f_ava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
1052         fava->f_ava->aa_desc = slap_schema.si_ad_entryCSN;
1053         ber_dupbv_x( &fava->f_ava->aa_value, &si->si_ctxcsn, op->o_tmpmemctx );
1054         fand->f_and = fava;
1055         if ( gotstate ) {
1056                 fava->f_next = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1057                 fava = fava->f_next;
1058                 fava->f_choice = LDAP_FILTER_GE;
1059                 fava->f_ava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
1060                 fava->f_ava->aa_desc = slap_schema.si_ad_entryCSN;
1061                 ber_dupbv_x( &fava->f_ava->aa_value, srs->sr_state.ctxcsn, op->o_tmpmemctx );
1062         }
1063         fava->f_next = op->ors_filter;
1064         op->ors_filter = fand;
1065         filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
1066
1067 shortcut:
1068         /* Let our callback add needed info to returned entries */
1069         cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(searchstate), op->o_tmpmemctx);
1070         ss = (searchstate *)(cb+1);
1071         ss->ss_on = on;
1072         ss->ss_so = sop;
1073         cb->sc_response = syncprov_search_response;
1074         cb->sc_cleanup = syncprov_search_cleanup;
1075         cb->sc_private = ss;
1076         cb->sc_next = op->o_callback;
1077         op->o_callback = cb;
1078
1079         op->o_sync_mode &= SLAP_CONTROL_MASK;
1080
1081         /* If this is a persistent search and no changes were reported during
1082          * the refresh phase, just invoke the response callback to transition
1083          * us into persist phase
1084          */
1085         if ( nochange ) {
1086                 rs->sr_err = LDAP_SUCCESS;
1087                 rs->sr_nentries = 0;
1088                 send_ldap_result( op, rs );
1089                 return rs->sr_err;
1090         }
1091         return SLAP_CB_CONTINUE;
1092 }
1093
1094 static int
1095 syncprov_db_config(
1096         BackendDB       *be,
1097         const char      *fname,
1098         int             lineno,
1099         int             argc,
1100         char    **argv
1101 )
1102 {
1103         slap_overinst           *on = (slap_overinst *)be->bd_info;
1104         syncprov_info_t         *si = (syncprov_info_t *)on->on_bi.bi_private;
1105
1106 #if 0
1107         if ( strcasecmp( argv[ 0 ], "syncprov-checkpoint" ) == 0 ) {
1108                 if ( argc != 3 ) {
1109                         fprintf( stderr, "%s: line %d: wrong number of arguments in "
1110                                 "\"syncprov-checkpoint <ops> <minutes>\"\n", fname, lineno );
1111                         return -1;
1112                 }
1113                 si->si_chkops = atoi( argv[1] );
1114                 si->si_chktime = atoi( argv[2] ) * 60;
1115
1116         } else {
1117                 return SLAP_CONF_UNKNOWN;
1118         }
1119 #endif
1120
1121         return SLAP_CONF_UNKNOWN;
1122 }
1123
1124 static int
1125 syncprov_db_init(
1126         BackendDB *be
1127 )
1128 {
1129         slap_overinst   *on = (slap_overinst *)be->bd_info;
1130         syncprov_info_t *si;
1131
1132         si = ch_calloc(1, sizeof(syncprov_info_t));
1133         on->on_bi.bi_private = si;
1134         ldap_pvt_thread_mutex_init( &si->si_csn_mutex );
1135         ldap_pvt_thread_mutex_init( &si->si_ops_mutex );
1136         si->si_ctxcsn.bv_val = si->si_ctxcsnbuf;
1137
1138         csn_anlist[0].an_desc = slap_schema.si_ad_entryCSN;
1139         csn_anlist[0].an_name = slap_schema.si_ad_entryCSN->ad_cname;
1140
1141         uuid_anlist[0].an_desc = slap_schema.si_ad_entryUUID;
1142         uuid_anlist[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
1143
1144         sync_cid = slap_cids.sc_LDAPsync;
1145
1146         return 0;
1147 }
1148
1149 static int
1150 syncprov_db_destroy(
1151         BackendDB *be
1152 )
1153 {
1154         slap_overinst   *on = (slap_overinst *)be->bd_info;
1155         syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
1156
1157         if ( si ) {
1158                 ldap_pvt_thread_mutex_destroy( &si->si_ops_mutex );
1159                 ldap_pvt_thread_mutex_destroy( &si->si_csn_mutex );
1160                 ch_free( si );
1161         }
1162
1163         return 0;
1164 }
1165
1166 /* This overlay is set up for dynamic loading via moduleload. For static
1167  * configuration, you'll need to arrange for the slap_overinst to be
1168  * initialized and registered by some other function inside slapd.
1169  */
1170
1171 static slap_overinst            syncprov;
1172
1173 int
1174 syncprov_init()
1175 {
1176         syncprov.on_bi.bi_type = "syncprov";
1177         syncprov.on_bi.bi_db_init = syncprov_db_init;
1178         syncprov.on_bi.bi_db_config = syncprov_db_config;
1179         syncprov.on_bi.bi_db_destroy = syncprov_db_destroy;
1180
1181         syncprov.on_bi.bi_op_add = syncprov_op_mod;
1182 #if 0
1183         syncprov.on_bi.bi_op_compare = syncprov_op_compare;
1184 #endif
1185         syncprov.on_bi.bi_op_delete = syncprov_op_mod;
1186         syncprov.on_bi.bi_op_modify = syncprov_op_mod;
1187         syncprov.on_bi.bi_op_modrdn = syncprov_op_mod;
1188         syncprov.on_bi.bi_op_search = syncprov_op_search;
1189         syncprov.on_bi.bi_extended = syncprov_op_extended;
1190
1191 #if 0
1192         syncprov.on_response = syncprov_response;
1193 #endif
1194
1195         return overlay_register( &syncprov );
1196 }
1197
1198 #if SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC
1199 int
1200 init_module( int argc, char *argv[] )
1201 {
1202         return syncprov_init();
1203 }
1204 #endif /* SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC */
1205
1206 #endif /* defined(SLAPD_OVER_SYNCPROV) */