1 /* syncprov.c - syncrepl provider */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 2004 The OpenLDAP Foundation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
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>.
16 * This work was initially developed by Howard Chu for inclusion in
22 #define SLAPD_OVER_SYNCPROV SLAPD_MOD_STATIC
24 #ifdef SLAPD_OVER_SYNCPROV
28 /* Record of a persistent search */
29 typedef struct syncops {
30 struct syncops *s_next;
31 struct berval s_base; /* ndn of search base */
32 ID s_eid; /* entryID of search base */
33 Operation *s_op; /* search op */
36 /* Record of which searches matched at premodify step */
37 typedef struct syncmatches {
38 struct syncmatches *sm_next;
42 typedef struct syncprov_info_t {
43 Entry *si_e; /* cached ldapsync context */
45 int si_chkops; /* checkpointing */
47 int si_numops; /* number of ops since last checkpoint */
48 time_t si_chklast; /* time of last checkpoint */
49 ldap_pvt_thread_mutex_t si_e_mutex;
50 ldap_pvt_thread_mutex_t si_ops_mutex;
51 ldap_pvt_thread_mutex_t si_chk_mutex;
54 typedef struct opcookie {
56 syncmatches *smatches;
60 typedef struct findcookie {
66 findbase_cb( Operation *op, SlapReply *rs )
68 slap_callback *sc = op->o_callback;
70 if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
71 findcookie *fc = sc->sc_private;
72 fc->fid = rs->sr_entry->e_id;
73 ber_dupbv_x( &fc->fdn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
79 syncprov_findbase( Operation *op, syncops *ss, findcookie *fc )
81 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
82 syncprov_info_t *si = on->on_bi.bi_private;
86 SlapReply frs = { REP_RESULT };
91 cb.sc_response = findbase_cb;
95 fop.o_tag = LDAP_REQ_SEARCH;
96 fop.ors_scope = LDAP_SCOPE_BASE;
97 fop.ors_deref = ss->s_op->ors_deref;
99 fop.ors_tlimit = SLAP_NO_LIMIT;
100 fop.ors_attrs = slap_anlist_no_attrs;
101 fop.ors_attrsonly = 1;
102 fop.ors_filter = ss->s_op->ors_filter;
103 fop.ors_filterstr = ss->s_op->ors_filterstr;
105 fop.o_req_ndn = ss->s_op->o_req_ndn;
107 rc = fop.o_bd->be_search( &fop, &frs );
109 if ( fc->fid == ss->s_eid ) return LDAP_SUCCESS;
111 /* If entryID has changed, then the base of this search has
112 * changed. Invalidate the psearch.
114 return LDAP_NO_SUCH_OBJECT;
118 syncprov_matchops( Operation *op, opcookie *opc )
120 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
121 syncprov_info_t *si = on->on_bi.bi_private;
123 findcookie fc = { NOID };
129 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
132 a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
134 ber_dupbv_x( &opc->suuid, &a->a_vals[0], op->o_tmpmemctx );
136 ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
137 for (ss = si->si_ops; ss; ss=ss->s_next)
140 rc = syncprov_findbase( op, ss, &fc );
141 if ( rc != LDAP_SUCCESS ) continue;
143 /* check if current o_req_dn is in scope and matches filter */
144 if ( dnIsSuffix( &op->o_req_ndn, &fc.fdn ) && test_filter( op, e,
145 ss->s_op->ors_filter ) == LDAP_COMPARE_TRUE ) {
146 syncmatches *sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
147 sm->sm_next = opc->smatches;
152 ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
153 be_entry_release_r( op, e );
157 syncprov_op_response( Operation *op, SlapReply *rs )
159 slap_callback *cb = op->o_callback;
160 opcookie *opc = (opcookie *)(cb+1);
161 slap_overinst *on = opc->son;
162 syncprov_info_t *si = on->on_bi.bi_private;
164 if ( rs->sr_err == LDAP_SUCCESS )
168 /* for each op in si->si_ops:
170 * check for scope and filter
171 * send ADD msg if matched
174 case LDAP_REQ_DELETE:
175 /* for each match in opc->smatches:
179 case LDAP_REQ_MODIFY:
180 case LDAP_REQ_MODRDN:
181 /* for each op in si->si_ops:
183 * check for scope and filter
185 * if match in opc->smatches, send UPDATE
188 * if match in opc->smatches, send DELETE
191 case LDAP_REQ_EXTENDED:
192 /* for each op in si->si_ops:
194 * check for scope and filter
195 * send UPDATE msg if matched
200 op->o_callback = cb->sc_next;
201 op->o_tmpfree(cb, op->o_tmpmemctx);
202 return SLAP_CB_CONTINUE;
206 syncprov_op_compare( Operation *op, SlapReply *rs )
208 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
209 syncprov_info_t *si = on->on_bi.bi_private;
210 int rc = SLAP_CB_CONTINUE;
212 if ( dn_match( &op->o_req_ndn, &si->si_e->e_nname ) )
216 ldap_pvt_thread_mutex_lock( &si->si_e_mutex );
218 if ( get_assert( op ) &&
219 ( test_filter( op, si->si_e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
221 rs->sr_err = LDAP_ASSERTION_FAILED;
225 rs->sr_err = access_allowed( op, si->si_e, op->oq_compare.rs_ava->aa_desc,
226 &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
227 if ( ! rs->sr_err ) {
228 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
232 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
234 for ( a = attr_find( si->si_e->e_attrs, op->oq_compare.rs_ava->aa_desc );
236 a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
238 rs->sr_err = LDAP_COMPARE_FALSE;
240 if ( value_find_ex( op->oq_compare.rs_ava->aa_desc,
241 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
242 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
243 a->a_nvals, &op->oq_compare.rs_ava->aa_value, op->o_tmpmemctx ) == 0 )
245 rs->sr_err = LDAP_COMPARE_TRUE;
252 ldap_pvt_thread_mutex_unlock( &si->si_e_mutex );
254 send_ldap_result( op, rs );
256 if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
257 rs->sr_err = LDAP_SUCCESS;
262 return SLAP_CB_CONTINUE;
266 syncprov_op_add( Operation *op, SlapReply *rs )
268 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
269 syncprov_info_t *si = on->on_bi.bi_private;
273 slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx);
274 opcookie *opc = (opcookie *)(cb+1);
276 cb->sc_response = syncprov_op_response;
277 cb->sc_private = opc;
278 cb->sc_next = op->o_callback;
282 return SLAP_CB_CONTINUE;
286 syncprov_op_delete( Operation *op, SlapReply *rs )
288 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
289 syncprov_info_t *si = on->on_bi.bi_private;
293 slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx);
294 opcookie *opc = (opcookie *)(cb+1);
296 cb->sc_response = syncprov_op_response;
297 cb->sc_private = opc;
298 cb->sc_next = op->o_callback;
301 syncprov_matchops( op, opc );
304 return SLAP_CB_CONTINUE;
308 syncprov_op_modify( Operation *op, SlapReply *rs )
310 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
311 syncprov_info_t *si = on->on_bi.bi_private;
315 slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx);
316 opcookie *opc = (opcookie *)(cb+1);
318 cb->sc_response = syncprov_op_response;
319 cb->sc_private = opc;
320 cb->sc_next = op->o_callback;
323 syncprov_matchops( op, opc );
326 return SLAP_CB_CONTINUE;
330 syncprov_op_modrdn( Operation *op, SlapReply *rs )
332 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
333 syncprov_info_t *si = on->on_bi.bi_private;
337 slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx);
338 opcookie *opc = (opcookie *)(cb+1);
340 cb->sc_response = syncprov_op_response;
341 cb->sc_private = opc;
342 cb->sc_next = op->o_callback;
345 syncprov_matchops( op, opc );
348 return SLAP_CB_CONTINUE;
351 static const struct berval * write_exop[] = {
352 &slap_EXOP_MODIFY_PASSWD,
357 syncprov_op_extended( Operation *op, SlapReply *rs )
359 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
360 syncprov_info_t *si = on->on_bi.bi_private;
366 for ( i=0; write_exop[i]; i++ )
368 if ( !ber_bvcmp( write_exop[i], &op->oq_extended.rs_reqoid ))
376 slap_callback *cb = op->o_tmpcalloc(1,
377 sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx);
378 opcookie *opc = (opcookie *)(cb+1);
380 cb->sc_response = syncprov_op_response;
381 cb->sc_private = opc;
382 cb->sc_next = op->o_callback;
385 syncprov_matchops( op, opc );
389 return SLAP_CB_CONTINUE;
393 syncprov_op_search( Operation *op, SlapReply *rs )
398 syncprov_response( Operation *op, SlapReply *rs )
400 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
401 syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
403 /* If the operation succeeded and we're checkpointing */
404 if ( rs->sr_err == LDAP_SUCCESS && ( si->si_chkops || si->si_chktime ))
408 switch ( op->o_tag ) {
409 case LDAP_REQ_EXTENDED:
412 /* if not PASSWD_MODIFY, break */
413 for ( i=0; write_exop[i]; i++ )
415 if ( !ber_bvcmp( write_exop[i], &op->oq_extended.rs_reqoid ))
425 case LDAP_REQ_MODIFY:
426 case LDAP_REQ_MODRDN:
427 case LDAP_REQ_DELETE:
428 ldap_pvt_thread_mutex_lock( &si->si_chk_mutex );
432 if ( si->si_numops >= si->si_chkops )
438 if ( si->si_chktime )
440 if ( op->o_time - si->si_chklast >= si->si_chktime )
443 si->si_chklast = op->o_time;
446 ldap_pvt_thread_mutex_unlock( &si->si_chk_mutex );
449 /* write cn=ldapsync to underlying db */
454 return SLAP_CB_CONTINUE;
466 slap_overinst *on = (slap_overinst *)be->bd_info;
467 syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
469 if ( strcasecmp( argv[ 0 ], "syncprov-checkpoint" ) == 0 ) {
471 fprintf( stderr, "%s: line %d: wrong number of arguments in "
472 "\"syncprov-checkpint <ops> <minutes>\"\n", fname, lineno );
475 si->si_chkops = atoi( argv[1] );
476 si->si_chktime = atoi( argv[2] ) * 60;
479 return SLAP_CONF_UNKNOWN;
485 /* Read any existing cn=ldapsync context from the underlying db.
486 * Then search for any entries newer than that. If no value exists,
487 * just generate it. Cache whatever result.
494 slap_overinst *on = (slap_overinst *) be->bd_info;
495 syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
500 /* Write the current cn=ldapsync context into the underlying db.
507 slap_overinst *on = (slap_overinst *) be->bd_info;
508 syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
522 slap_overinst *on = (slap_overinst *)be->bd_info;
525 si = ch_calloc(1, sizeof(syncprov_info_t));
526 on->on_bi.bi_private = si;
528 ldap_pvt_thread_mutex_init( &si->si_e_mutex );
529 ldap_pvt_thread_mutex_init( &si->si_ops_mutex );
530 ldap_pvt_thread_mutex_init( &si->si_chk_mutex );
540 slap_overinst *on = (slap_overinst *)be->bd_info;
541 syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
545 entry_free( si->si_e );
548 ldap_pvt_thread_mutex_destroy( &si->si_chk_mutex );
549 ldap_pvt_thread_mutex_destroy( &si->si_ops_mutex );
550 ldap_pvt_thread_mutex_destroy( &si->si_e_mutex );
558 /* This overlay is set up for dynamic loading via moduleload. For static
559 * configuration, you'll need to arrange for the slap_overinst to be
560 * initialized and registered by some other function inside slapd.
563 static slap_overinst syncprov;
568 syncprov.on_bi.bi_type = "syncprov";
569 syncprov.on_bi.bi_db_init = syncprov_db_init;
570 syncprov.on_bi.bi_db_config = syncprov_db_config;
571 syncprov.on_bi.bi_db_destroy = syncprov_db_destroy;
572 syncprov.on_bi.bi_db_open = syncprov_db_open;
573 syncprov.on_bi.bi_db_close = syncprov_db_close;
575 syncprov.on_bi.bi_op_add = syncprov_op_add;
576 syncprov.on_bi.bi_op_compare = syncprov_op_compare;
577 syncprov.on_bi.bi_op_delete = syncprov_op_delete;
578 syncprov.on_bi.bi_op_modify = syncprov_op_modify;
579 syncprov.on_bi.bi_op_modrdn = syncprov_op_modrdn;
580 syncprov.on_bi.bi_op_search = syncprov_op_search;
581 syncprov.on_bi.bi_extended = syncprov_op_extended;
583 syncprov.on_response = syncprov_response;
585 return overlay_register( &syncprov );
588 #if SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC
590 init_module( int argc, char *argv[] )
592 return syncprov_init();
594 #endif /* SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC */
596 #endif /* defined(SLAPD_OVER_SYNCPROV) */