/* syncprov.c - syncrepl provider */ /* This work is part of OpenLDAP Software . * * Copyright 2004 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* ACKNOWLEDGEMENTS: * This work was initially developed by Howard Chu for inclusion in * OpenLDAP Software. */ #include "portable.h" #define SLAPD_OVER_SYNCPROV SLAPD_MOD_STATIC #ifdef SLAPD_OVER_SYNCPROV #include "slap.h" /* Record of a persistent search */ typedef struct syncops { struct syncops *s_next; struct berval s_base; /* ndn of search base */ ID s_eid; /* entryID of search base */ Operation *s_op; /* search op */ } syncops; /* Record of which searches matched at premodify step */ typedef struct syncmatches { struct syncmatches *sm_next; syncops *sm_op; } syncmatches; typedef struct syncprov_info_t { Entry *si_e; /* cached ldapsync context */ syncops *si_ops; int si_chkops; /* checkpointing */ int si_chktime; int si_numops; /* number of ops since last checkpoint */ time_t si_chklast; /* time of last checkpoint */ ldap_pvt_thread_mutex_t si_e_mutex; ldap_pvt_thread_mutex_t si_ops_mutex; ldap_pvt_thread_mutex_t si_chk_mutex; } syncprov_info_t; typedef struct opcookie { slap_overinst *son; syncmatches *smatches; struct berval suuid; } opcookie; typedef struct findcookie { ID fid; struct berval fdn; } findcookie; static int findbase_cb( Operation *op, SlapReply *rs ) { slap_callback *sc = op->o_callback; if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) { findcookie *fc = sc->sc_private; fc->fid = rs->sr_entry->e_id; ber_dupbv_x( &fc->fdn, &rs->sr_entry->e_nname, op->o_tmpmemctx ); } return LDAP_SUCCESS; } static int syncprov_findbase( Operation *op, syncops *ss, findcookie *fc ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; syncprov_info_t *si = on->on_bi.bi_private; slap_callback cb; Operation fop; SlapReply frs = { REP_RESULT }; int rc; fop = *op; cb.sc_response = findbase_cb; cb.sc_private = fc; fop.o_callback = &cb; fop.o_tag = LDAP_REQ_SEARCH; fop.ors_scope = LDAP_SCOPE_BASE; fop.ors_deref = ss->s_op->ors_deref; fop.ors_slimit = 1; fop.ors_tlimit = SLAP_NO_LIMIT; fop.ors_attrs = slap_anlist_no_attrs; fop.ors_attrsonly = 1; fop.ors_filter = ss->s_op->ors_filter; fop.ors_filterstr = ss->s_op->ors_filterstr; fop.o_req_ndn = ss->s_op->o_req_ndn; rc = fop.o_bd->be_search( &fop, &frs ); if ( fc->fid == ss->s_eid ) return LDAP_SUCCESS; /* If entryID has changed, then the base of this search has * changed. Invalidate the psearch. */ return LDAP_NO_SUCH_OBJECT; } static void syncprov_matchops( Operation *op, opcookie *opc ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; syncprov_info_t *si = on->on_bi.bi_private; findcookie fc = { NOID }; syncops *ss; Entry *e; Attribute *a; int rc; rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); if ( rc ) return; a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID ); if ( a ) ber_dupbv_x( &opc->suuid, &a->a_vals[0], op->o_tmpmemctx ); ldap_pvt_thread_mutex_lock( &si->si_ops_mutex ); for (ss = si->si_ops; ss; ss=ss->s_next) { /* validate base */ rc = syncprov_findbase( op, ss, &fc ); if ( rc != LDAP_SUCCESS ) continue; /* check if current o_req_dn is in scope and matches filter */ if ( dnIsSuffix( &op->o_req_ndn, &fc.fdn ) && test_filter( op, e, ss->s_op->ors_filter ) == LDAP_COMPARE_TRUE ) { syncmatches *sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx ); sm->sm_next = opc->smatches; sm->sm_op = ss; opc->smatches = sm; } } ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex ); be_entry_release_r( op, e ); } static int syncprov_op_response( Operation *op, SlapReply *rs ) { slap_callback *cb = op->o_callback; opcookie *opc = (opcookie *)(cb+1); slap_overinst *on = opc->son; syncprov_info_t *si = on->on_bi.bi_private; if ( rs->sr_err == LDAP_SUCCESS ) { switch(op->o_tag) { case LDAP_REQ_ADD: /* for each op in si->si_ops: * validate base * check for scope and filter * send ADD msg if matched */ break; case LDAP_REQ_DELETE: /* for each match in opc->smatches: * send DELETE msg */ break; case LDAP_REQ_MODIFY: case LDAP_REQ_MODRDN: /* for each op in si->si_ops: * validate base * check for scope and filter * if match * if match in opc->smatches, send UPDATE * else send ADD * else * if match in opc->smatches, send DELETE */ break; case LDAP_REQ_EXTENDED: /* for each op in si->si_ops: * validate base * check for scope and filter * send UPDATE msg if matched */ break; } } op->o_callback = cb->sc_next; op->o_tmpfree(cb, op->o_tmpmemctx); return SLAP_CB_CONTINUE; } static int syncprov_op_compare( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; syncprov_info_t *si = on->on_bi.bi_private; int rc = SLAP_CB_CONTINUE; if ( dn_match( &op->o_req_ndn, &si->si_e->e_nname ) ) { Attribute *a; ldap_pvt_thread_mutex_lock( &si->si_e_mutex ); if ( get_assert( op ) && ( test_filter( op, si->si_e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) ) { rs->sr_err = LDAP_ASSERTION_FAILED; goto return_results; } rs->sr_err = access_allowed( op, si->si_e, op->oq_compare.rs_ava->aa_desc, &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL ); if ( ! rs->sr_err ) { rs->sr_err = LDAP_INSUFFICIENT_ACCESS; goto return_results; } rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE; for ( a = attr_find( si->si_e->e_attrs, op->oq_compare.rs_ava->aa_desc ); a != NULL; a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) ) { rs->sr_err = LDAP_COMPARE_FALSE; if ( value_find_ex( op->oq_compare.rs_ava->aa_desc, SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, a->a_nvals, &op->oq_compare.rs_ava->aa_value, op->o_tmpmemctx ) == 0 ) { rs->sr_err = LDAP_COMPARE_TRUE; break; } } return_results:; ldap_pvt_thread_mutex_unlock( &si->si_e_mutex ); send_ldap_result( op, rs ); if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) { rs->sr_err = LDAP_SUCCESS; } rc = rs->sr_err; } return SLAP_CB_CONTINUE; } static int syncprov_op_add( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; syncprov_info_t *si = on->on_bi.bi_private; if ( si->si_ops ) { slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx); opcookie *opc = (opcookie *)(cb+1); opc->son = on; cb->sc_response = syncprov_op_response; cb->sc_private = opc; cb->sc_next = op->o_callback; op->o_callback = cb; } return SLAP_CB_CONTINUE; } static int syncprov_op_delete( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; syncprov_info_t *si = on->on_bi.bi_private; if ( si->si_ops ) { slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx); opcookie *opc = (opcookie *)(cb+1); opc->son = on; cb->sc_response = syncprov_op_response; cb->sc_private = opc; cb->sc_next = op->o_callback; op->o_callback = cb; syncprov_matchops( op, opc ); } return SLAP_CB_CONTINUE; } static int syncprov_op_modify( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; syncprov_info_t *si = on->on_bi.bi_private; if ( si->si_ops ) { slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx); opcookie *opc = (opcookie *)(cb+1); opc->son = on; cb->sc_response = syncprov_op_response; cb->sc_private = opc; cb->sc_next = op->o_callback; op->o_callback = cb; syncprov_matchops( op, opc ); } return SLAP_CB_CONTINUE; } static int syncprov_op_modrdn( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; syncprov_info_t *si = on->on_bi.bi_private; if ( si->si_ops ) { slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx); opcookie *opc = (opcookie *)(cb+1); opc->son = on; cb->sc_response = syncprov_op_response; cb->sc_private = opc; cb->sc_next = op->o_callback; op->o_callback = cb; syncprov_matchops( op, opc ); } return SLAP_CB_CONTINUE; } static const struct berval * write_exop[] = { &slap_EXOP_MODIFY_PASSWD, NULL }; static int syncprov_op_extended( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; syncprov_info_t *si = on->on_bi.bi_private; if ( si->si_ops ) { int i, doit = 0; for ( i=0; write_exop[i]; i++ ) { if ( !ber_bvcmp( write_exop[i], &op->oq_extended.rs_reqoid )) { doit = 1; break; } } if ( doit ) { slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(opcookie), op->o_tmpmemctx); opcookie *opc = (opcookie *)(cb+1); opc->son = on; cb->sc_response = syncprov_op_response; cb->sc_private = opc; cb->sc_next = op->o_callback; op->o_callback = cb; syncprov_matchops( op, opc ); } } return SLAP_CB_CONTINUE; } static int syncprov_op_search( Operation *op, SlapReply *rs ) { } static int syncprov_response( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private; /* If the operation succeeded and we're checkpointing */ if ( rs->sr_err == LDAP_SUCCESS && ( si->si_chkops || si->si_chktime )) { int do_check = 0; switch ( op->o_tag ) { case LDAP_REQ_EXTENDED: { int i, doit = 0; /* if not PASSWD_MODIFY, break */ for ( i=0; write_exop[i]; i++ ) { if ( !ber_bvcmp( write_exop[i], &op->oq_extended.rs_reqoid )) { doit = 1; break; } } if ( !doit ) break; } /* else fallthru */ case LDAP_REQ_ADD: case LDAP_REQ_MODIFY: case LDAP_REQ_MODRDN: case LDAP_REQ_DELETE: ldap_pvt_thread_mutex_lock( &si->si_chk_mutex ); if ( si->si_chkops ) { si->si_numops++; if ( si->si_numops >= si->si_chkops ) { do_check = 1; si->si_numops = 0; } } if ( si->si_chktime ) { if ( op->o_time - si->si_chklast >= si->si_chktime ) { do_check = 1; si->si_chklast = op->o_time; } } ldap_pvt_thread_mutex_unlock( &si->si_chk_mutex ); if ( do_check ) { /* write cn=ldapsync to underlying db */ } break; } } return SLAP_CB_CONTINUE; } static int syncprov_db_config( BackendDB *be, const char *fname, int lineno, int argc, char **argv ) { slap_overinst *on = (slap_overinst *)be->bd_info; syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private; if ( strcasecmp( argv[ 0 ], "syncprov-checkpoint" ) == 0 ) { if ( argc != 3 ) { fprintf( stderr, "%s: line %d: wrong number of arguments in " "\"syncprov-checkpint \"\n", fname, lineno ); return -1; } si->si_chkops = atoi( argv[1] ); si->si_chktime = atoi( argv[2] ) * 60; } else { return SLAP_CONF_UNKNOWN; } return 0; } /* Read any existing cn=ldapsync context from the underlying db. * Then search for any entries newer than that. If no value exists, * just generate it. Cache whatever result. */ static int syncprov_db_open( BackendDB *be ) { slap_overinst *on = (slap_overinst *) be->bd_info; syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private; return 0; } /* Write the current cn=ldapsync context into the underlying db. */ static int syncprov_db_close( BackendDB *be ) { slap_overinst *on = (slap_overinst *) be->bd_info; syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private; /* for si->si_ops: * send DONE messages * free si_ops */ return 0; } static int syncprov_db_init( BackendDB *be ) { slap_overinst *on = (slap_overinst *)be->bd_info; syncprov_info_t *si; si = ch_calloc(1, sizeof(syncprov_info_t)); on->on_bi.bi_private = si; ldap_pvt_thread_mutex_init( &si->si_e_mutex ); ldap_pvt_thread_mutex_init( &si->si_ops_mutex ); ldap_pvt_thread_mutex_init( &si->si_chk_mutex ); return 0; } static int syncprov_db_destroy( BackendDB *be ) { slap_overinst *on = (slap_overinst *)be->bd_info; syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private; if ( si ) { if ( si->si_e ) { entry_free( si->si_e ); } ldap_pvt_thread_mutex_destroy( &si->si_chk_mutex ); ldap_pvt_thread_mutex_destroy( &si->si_ops_mutex ); ldap_pvt_thread_mutex_destroy( &si->si_e_mutex ); ch_free( si ); } return 0; } /* This overlay is set up for dynamic loading via moduleload. For static * configuration, you'll need to arrange for the slap_overinst to be * initialized and registered by some other function inside slapd. */ static slap_overinst syncprov; int syncprov_init() { syncprov.on_bi.bi_type = "syncprov"; syncprov.on_bi.bi_db_init = syncprov_db_init; syncprov.on_bi.bi_db_config = syncprov_db_config; syncprov.on_bi.bi_db_destroy = syncprov_db_destroy; syncprov.on_bi.bi_db_open = syncprov_db_open; syncprov.on_bi.bi_db_close = syncprov_db_close; syncprov.on_bi.bi_op_add = syncprov_op_add; syncprov.on_bi.bi_op_compare = syncprov_op_compare; syncprov.on_bi.bi_op_delete = syncprov_op_delete; syncprov.on_bi.bi_op_modify = syncprov_op_modify; syncprov.on_bi.bi_op_modrdn = syncprov_op_modrdn; syncprov.on_bi.bi_op_search = syncprov_op_search; syncprov.on_bi.bi_extended = syncprov_op_extended; syncprov.on_response = syncprov_response; return overlay_register( &syncprov ); } #if SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC int init_module( int argc, char *argv[] ) { return syncprov_init(); } #endif /* SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC */ #endif /* defined(SLAPD_OVER_SYNCPROV) */