1/* seqmod.c - sequenced modifies */ 2/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 2004-2011 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_SEQMOD 23 24#include "slap.h" 25#include "config.h" 26 27/* This overlay serializes concurrent attempts to modify a single entry */ 28 29typedef struct modtarget { 30 struct modtarget *mt_next; 31 struct modtarget *mt_tail; 32 Operation *mt_op; 33} modtarget; 34 35typedef struct seqmod_info { 36 Avlnode *sm_mods; /* entries being modified */ 37 ldap_pvt_thread_mutex_t sm_mutex; 38} seqmod_info; 39 40static int 41sm_avl_cmp( const void *c1, const void *c2 ) 42{ 43 const modtarget *m1, *m2; 44 int rc; 45 46 m1 = c1; m2 = c2; 47 rc = m1->mt_op->o_req_ndn.bv_len - m2->mt_op->o_req_ndn.bv_len; 48 49 if ( rc ) return rc; 50 return ber_bvcmp( &m1->mt_op->o_req_ndn, &m2->mt_op->o_req_ndn ); 51} 52 53static int 54seqmod_op_cleanup( Operation *op, SlapReply *rs ) 55{ 56 slap_callback *sc = op->o_callback; 57 seqmod_info *sm = sc->sc_private; 58 modtarget *mt, mtdummy; 59 Avlnode *av; 60 61 mtdummy.mt_op = op; 62 /* This op is done, remove it */ 63 ldap_pvt_thread_mutex_lock( &sm->sm_mutex ); 64 av = avl_find2( sm->sm_mods, &mtdummy, sm_avl_cmp ); 65 assert(av != NULL); 66 67 mt = av->avl_data; 68 69 /* If there are more, promote the next one */ 70 if ( mt->mt_next ) { 71 av->avl_data = mt->mt_next; 72 mt->mt_next->mt_tail = mt->mt_tail; 73 } else { 74 avl_delete( &sm->sm_mods, mt, sm_avl_cmp ); 75 } 76 ldap_pvt_thread_mutex_unlock( &sm->sm_mutex ); 77 op->o_callback = sc->sc_next; 78 op->o_tmpfree( sc, op->o_tmpmemctx ); 79 80 return 0; 81} 82 83static int 84seqmod_op_mod( Operation *op, SlapReply *rs ) 85{ 86 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 87 seqmod_info *sm = on->on_bi.bi_private; 88 modtarget *mt; 89 Avlnode *av; 90 slap_callback *cb; 91 92 cb = op->o_tmpcalloc( 1, sizeof(slap_callback) + sizeof(modtarget), 93 op->o_tmpmemctx ); 94 mt = (modtarget *)(cb+1); 95 mt->mt_next = NULL; 96 mt->mt_tail = mt; 97 mt->mt_op = op; 98 99 /* See if we're already modifying this entry - don't allow 100 * near-simultaneous mods of the same entry 101 */ 102 ldap_pvt_thread_mutex_lock( &sm->sm_mutex ); 103 av = avl_find2( sm->sm_mods, mt, sm_avl_cmp ); 104 if ( av ) { 105 modtarget *mtp = av->avl_data; 106 mtp->mt_tail->mt_next = mt; 107 mtp->mt_tail = mt; 108 /* Wait for this op to get to head of list */ 109 while ( mtp != mt ) { 110 ldap_pvt_thread_mutex_unlock( &sm->sm_mutex ); 111 ldap_pvt_thread_yield(); 112 /* Let it finish - should use a condition 113 * variable here... */ 114 ldap_pvt_thread_mutex_lock( &sm->sm_mutex ); 115 mtp = av->avl_data; 116 } 117 } else { 118 /* Record that we're modifying this now */ 119 avl_insert( &sm->sm_mods, mt, sm_avl_cmp, avl_dup_error ); 120 } 121 ldap_pvt_thread_mutex_unlock( &sm->sm_mutex ); 122 123 cb->sc_cleanup = seqmod_op_cleanup; 124 cb->sc_private = sm; 125 cb->sc_next = op->o_callback; 126 op->o_callback = cb; 127 128 return SLAP_CB_CONTINUE; 129} 130 131static int 132seqmod_op_extended( 133 Operation *op, 134 SlapReply *rs 135) 136{ 137 if ( exop_is_write( op )) return seqmod_op_mod( op, rs ); 138 else return SLAP_CB_CONTINUE; 139} 140 141static int 142seqmod_db_open( 143 BackendDB *be, 144 ConfigReply *cr 145) 146{ 147 slap_overinst *on = (slap_overinst *)be->bd_info; 148 seqmod_info *sm; 149 150 sm = ch_calloc(1, sizeof(seqmod_info)); 151 on->on_bi.bi_private = sm; 152 153 ldap_pvt_thread_mutex_init( &sm->sm_mutex ); 154 155 return 0; 156} 157 158static int 159seqmod_db_close( 160 BackendDB *be, 161 ConfigReply *cr 162) 163{ 164 slap_overinst *on = (slap_overinst *)be->bd_info; 165 seqmod_info *sm = (seqmod_info *)on->on_bi.bi_private; 166 167 if ( sm ) { 168 ldap_pvt_thread_mutex_destroy( &sm->sm_mutex ); 169 170 ch_free( sm ); 171 } 172 173 return 0; 174} 175 176/* This overlay is set up for dynamic loading via moduleload. For static 177 * configuration, you'll need to arrange for the slap_overinst to be 178 * initialized and registered by some other function inside slapd. 179 */ 180 181static slap_overinst seqmod; 182 183int 184seqmod_initialize() 185{ 186 seqmod.on_bi.bi_type = "seqmod"; 187 seqmod.on_bi.bi_db_open = seqmod_db_open; 188 seqmod.on_bi.bi_db_close = seqmod_db_close; 189 190 seqmod.on_bi.bi_op_modify = seqmod_op_mod; 191 seqmod.on_bi.bi_op_modrdn = seqmod_op_mod; 192 seqmod.on_bi.bi_extended = seqmod_op_extended; 193 194 return overlay_register( &seqmod ); 195} 196 197#if SLAPD_OVER_SEQMOD == SLAPD_MOD_DYNAMIC 198int 199init_module( int argc, char *argv[] ) 200{ 201 return seqmod_initialize(); 202} 203#endif /* SLAPD_OVER_SEQMOD == SLAPD_MOD_DYNAMIC */ 204 205#endif /* defined(SLAPD_OVER_SEQMOD) */ 206