/* seqmod.c - sequenced modifies */
/* This work is part of OpenLDAP Software .
*
* Copyright 2004-2024 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"
#ifdef SLAPD_OVER_SEQMOD
#include "slap.h"
#include "slap-config.h"
/* This overlay serializes concurrent attempts to modify a single entry */
typedef struct modtarget {
struct modtarget *mt_next;
struct modtarget *mt_tail;
Operation *mt_op;
} modtarget;
typedef struct seqmod_info {
Avlnode *sm_mods; /* entries being modified */
ldap_pvt_thread_mutex_t sm_mutex;
} seqmod_info;
static int
sm_avl_cmp( const void *c1, const void *c2 )
{
const modtarget *m1, *m2;
int rc;
m1 = c1; m2 = c2;
rc = m1->mt_op->o_req_ndn.bv_len - m2->mt_op->o_req_ndn.bv_len;
if ( rc ) return rc;
return ber_bvcmp( &m1->mt_op->o_req_ndn, &m2->mt_op->o_req_ndn );
}
static int
seqmod_op_cleanup( Operation *op, SlapReply *rs )
{
slap_callback *sc = op->o_callback;
seqmod_info *sm = sc->sc_private;
modtarget *mt, mtdummy;
Avlnode *av;
mtdummy.mt_op = op;
/* This op is done, remove it */
ldap_pvt_thread_mutex_lock( &sm->sm_mutex );
av = ldap_avl_find2( sm->sm_mods, &mtdummy, sm_avl_cmp );
assert(av != NULL);
mt = av->avl_data;
/* If there are more, promote the next one */
if ( mt->mt_next ) {
av->avl_data = mt->mt_next;
mt->mt_next->mt_tail = mt->mt_tail;
} else {
ldap_avl_delete( &sm->sm_mods, mt, sm_avl_cmp );
}
ldap_pvt_thread_mutex_unlock( &sm->sm_mutex );
op->o_callback = sc->sc_next;
op->o_tmpfree( sc, op->o_tmpmemctx );
return 0;
}
static int
seqmod_op_mod( Operation *op, SlapReply *rs )
{
slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
seqmod_info *sm = on->on_bi.bi_private;
modtarget *mt;
Avlnode *av;
slap_callback *cb;
cb = op->o_tmpcalloc( 1, sizeof(slap_callback) + sizeof(modtarget),
op->o_tmpmemctx );
mt = (modtarget *)(cb+1);
mt->mt_next = NULL;
mt->mt_tail = mt;
mt->mt_op = op;
/* See if we're already modifying this entry - don't allow
* near-simultaneous mods of the same entry
*/
ldap_pvt_thread_mutex_lock( &sm->sm_mutex );
av = ldap_avl_find2( sm->sm_mods, mt, sm_avl_cmp );
if ( av ) {
modtarget *mtp = av->avl_data;
mtp->mt_tail->mt_next = mt;
mtp->mt_tail = mt;
/* Wait for this op to get to head of list */
while ( mtp != mt ) {
ldap_pvt_thread_mutex_unlock( &sm->sm_mutex );
ldap_pvt_thread_yield();
/* Let it finish - should use a condition
* variable here... */
ldap_pvt_thread_mutex_lock( &sm->sm_mutex );
mtp = av->avl_data;
}
} else {
/* Record that we're modifying this now */
ldap_avl_insert( &sm->sm_mods, mt, sm_avl_cmp, ldap_avl_dup_error );
}
ldap_pvt_thread_mutex_unlock( &sm->sm_mutex );
cb->sc_cleanup = seqmod_op_cleanup;
cb->sc_private = sm;
cb->sc_next = op->o_callback;
op->o_callback = cb;
return SLAP_CB_CONTINUE;
}
static int
seqmod_op_extended(
Operation *op,
SlapReply *rs
)
{
if ( exop_is_write( op )) return seqmod_op_mod( op, rs );
else return SLAP_CB_CONTINUE;
}
static int
seqmod_db_open(
BackendDB *be,
ConfigReply *cr
)
{
slap_overinst *on = (slap_overinst *)be->bd_info;
seqmod_info *sm;
sm = ch_calloc(1, sizeof(seqmod_info));
on->on_bi.bi_private = sm;
ldap_pvt_thread_mutex_init( &sm->sm_mutex );
return 0;
}
static int
seqmod_db_close(
BackendDB *be,
ConfigReply *cr
)
{
slap_overinst *on = (slap_overinst *)be->bd_info;
seqmod_info *sm = (seqmod_info *)on->on_bi.bi_private;
if ( sm ) {
ldap_pvt_thread_mutex_destroy( &sm->sm_mutex );
ch_free( sm );
on->on_bi.bi_private = NULL;
}
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 seqmod;
int
seqmod_initialize()
{
seqmod.on_bi.bi_type = "seqmod";
seqmod.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
seqmod.on_bi.bi_db_open = seqmod_db_open;
seqmod.on_bi.bi_db_close = seqmod_db_close;
seqmod.on_bi.bi_op_modify = seqmod_op_mod;
seqmod.on_bi.bi_op_modrdn = seqmod_op_mod;
seqmod.on_bi.bi_extended = seqmod_op_extended;
return overlay_register( &seqmod );
}
#if SLAPD_OVER_SEQMOD == SLAPD_MOD_DYNAMIC
int
init_module( int argc, char *argv[] )
{
return seqmod_initialize();
}
#endif /* SLAPD_OVER_SEQMOD == SLAPD_MOD_DYNAMIC */
#endif /* defined(SLAPD_OVER_SEQMOD) */