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