xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/seqmod.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: seqmod.c,v 1.1.1.4 2014/05/28 09:58:52 tron Exp $	*/
2 
3 /* seqmod.c - sequenced modifies */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2004-2014 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 
31 typedef struct modtarget {
32 	struct modtarget *mt_next;
33 	struct modtarget *mt_tail;
34 	Operation *mt_op;
35 } modtarget;
36 
37 typedef struct seqmod_info {
38 	Avlnode		*sm_mods;	/* entries being modified */
39 	ldap_pvt_thread_mutex_t	sm_mutex;
40 } seqmod_info;
41 
42 static int
43 sm_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 
55 static int
56 seqmod_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 
85 static int
86 seqmod_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 
133 static int
134 seqmod_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 
143 static int
144 seqmod_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 
160 static int
161 seqmod_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 
183 static slap_overinst 		seqmod;
184 
185 int
186 seqmod_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
200 int
201 init_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