]> git.sur5r.net Git - openldap/blob - servers/slapd/txn.c
b366e90bf2db32fbcf5e3f3d1dfe0c65c2af62d3
[openldap] / servers / slapd / txn.c
1 /* txn.c - LDAP Transactions */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2017 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/socket.h>
22 #include <ac/string.h>
23 #include <ac/unistd.h>
24
25 #include "slap.h"
26
27 #include <lber_pvt.h>
28 #include <lutil.h>
29
30 #ifdef LDAP_X_TXN
31 const struct berval slap_EXOP_TXN_START = BER_BVC(LDAP_EXOP_X_TXN_START);
32 const struct berval slap_EXOP_TXN_END = BER_BVC(LDAP_EXOP_X_TXN_END);
33
34 int txn_start_extop(
35         Operation *op, SlapReply *rs )
36 {
37         int rc;
38         struct berval *bv;
39
40         Statslog( LDAP_DEBUG_STATS, "%s TXN START\n",
41                 op->o_log_prefix, 0, 0, 0, 0 );
42
43         if( op->ore_reqdata != NULL ) {
44                 rs->sr_text = "no request data expected";
45                 return LDAP_PROTOCOL_ERROR;
46         }
47
48         op->o_bd = op->o_conn->c_authz_backend;
49         if( backend_check_restrictions( op, rs,
50                 (struct berval *)&slap_EXOP_TXN_START ) != LDAP_SUCCESS )
51         {
52                 return rs->sr_err;
53         }
54
55         /* acquire connection lock */
56         ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
57
58         if( op->o_conn->c_txn != CONN_TXN_INACTIVE ) {
59                 rs->sr_text = "Too many transactions";
60                 rc = LDAP_BUSY;
61                 goto done;
62         }
63
64         assert( op->o_conn->c_txn_backend == NULL );
65         op->o_conn->c_txn = CONN_TXN_SPECIFY;
66
67         bv = (struct berval *) ch_malloc( sizeof (struct berval) );
68         bv->bv_len = 0;
69         bv->bv_val = NULL;
70
71         rs->sr_rspdata = bv;
72         rc = LDAP_SUCCESS;
73
74 done:
75         /* release connection lock */
76         ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
77         return rc;
78 }
79
80 int txn_spec_ctrl(
81         Operation *op, SlapReply *rs, LDAPControl *ctrl )
82 {
83         if ( !ctrl->ldctl_iscritical ) {
84                 rs->sr_text = "txnSpec control must be marked critical";
85                 return LDAP_PROTOCOL_ERROR;
86         }
87         if( op->o_txnSpec ) {
88                 rs->sr_text = "txnSpec control provided multiple times";
89                 return LDAP_PROTOCOL_ERROR;
90         }
91
92         if ( ctrl->ldctl_value.bv_val == NULL ) {
93                 rs->sr_text = "no transaction identifier provided";
94                 return LDAP_PROTOCOL_ERROR;
95         }
96         if ( ctrl->ldctl_value.bv_len != 0 ) {
97                 rs->sr_text = "invalid transaction identifier";
98                 return LDAP_X_TXN_ID_INVALID;
99         }
100
101         if ( op->o_preread ) { /* temporary limitation */
102                 rs->sr_text = "cannot perform pre-read in transaction";
103                 return LDAP_UNWILLING_TO_PERFORM;
104         } 
105         if ( op->o_postread ) { /* temporary limitation */
106                 rs->sr_text = "cannot perform post-read in transaction";
107                 return LDAP_UNWILLING_TO_PERFORM;
108         }
109
110         op->o_txnSpec = SLAP_CONTROL_CRITICAL;
111         return LDAP_SUCCESS;
112 }
113
114 typedef struct txn_rctrls {
115         struct txn_rctrls *tr_next;
116         ber_int_t       tr_msgid;
117         LDAPControl ** tr_ctrls;
118 } txn_rctrls;
119
120 static int txn_result( Operation *op, SlapReply *rs )
121 {
122         if ( rs->sr_ctrls ) {
123                 txn_rctrls **t0, *tr;
124                 for ( t0 = (txn_rctrls **) &op->o_callback->sc_private; *t0;
125                         t0 = &(*t0)->tr_next )
126                         ;
127                 tr = op->o_tmpalloc( sizeof( txn_rctrls ), op->o_tmpmemctx );
128                 tr->tr_next = NULL;
129                 *t0 = tr;
130                 tr->tr_msgid = op->o_msgid;
131                 tr->tr_ctrls = ldap_controls_dup( rs->sr_ctrls );
132         }
133         return rs->sr_err;
134 }
135
136 static int txn_put_ctrls( Operation *op, BerElement *ber, txn_rctrls *tr )
137 {
138         txn_rctrls *next;
139         int i;
140         ber_printf( ber, "{" );
141         for ( ; tr; tr  = next ) {
142                 next = tr->tr_next;
143                 ber_printf( ber, "{it{", tr->tr_msgid, LDAP_TAG_CONTROLS );
144                 for ( i = 0; tr->tr_ctrls[i]; i++ )
145                         ldap_pvt_put_control( tr->tr_ctrls[i], ber );
146                 ber_printf( ber, "}}" );
147                 ldap_controls_free( tr->tr_ctrls );
148                 op->o_tmpfree( tr, op->o_tmpmemctx );
149         }
150         ber_printf( ber, "}" );
151         return 0;
152 }
153
154 int txn_end_extop(
155         Operation *op, SlapReply *rs )
156 {
157         int rc;
158         BerElementBuffer berbuf;
159         BerElement *ber = (BerElement *)&berbuf;
160         ber_tag_t tag;
161         ber_len_t len;
162         ber_int_t commit=1;
163         struct berval txnid;
164         Operation *o, *p;
165         Connection *c = op->o_conn;
166
167         Statslog( LDAP_DEBUG_STATS, "%s TXN END\n",
168                 op->o_log_prefix, 0, 0, 0, 0 );
169
170         if( op->ore_reqdata == NULL ) {
171                 rs->sr_text = "request data expected";
172                 return LDAP_PROTOCOL_ERROR;
173         }
174         if( op->ore_reqdata->bv_len == 0 ) {
175                 rs->sr_text = "empty request data";
176                 return LDAP_PROTOCOL_ERROR;
177         }
178
179         op->o_bd = c->c_authz_backend;
180         if( backend_check_restrictions( op, rs,
181                 (struct berval *)&slap_EXOP_TXN_END ) != LDAP_SUCCESS )
182         {
183                 return rs->sr_err;
184         }
185
186         ber_init2( ber, op->ore_reqdata, 0 );
187
188         tag = ber_scanf( ber, "{" /*}*/ );
189         if( tag == LBER_ERROR ) {
190                 rs->sr_text = "request data decoding error";
191                 return LDAP_PROTOCOL_ERROR;
192         }
193
194         tag = ber_peek_tag( ber, &len );
195         if( tag == LBER_BOOLEAN ) {
196                 tag = ber_scanf( ber, "b", &commit );
197                 if( tag == LBER_ERROR ) {
198                         rs->sr_text = "request data decoding error";
199                         return LDAP_PROTOCOL_ERROR;
200                 }
201         }
202
203         tag = ber_scanf( ber, /*{*/ "m}", &txnid );
204         if( tag == LBER_ERROR ) {
205                 rs->sr_text = "request data decoding error";
206                 return LDAP_PROTOCOL_ERROR;
207         }
208
209         if( txnid.bv_len ) {
210                 rs->sr_text = "invalid transaction identifier";
211                 return LDAP_X_TXN_ID_INVALID;
212         }
213
214         /* acquire connection lock */
215         ldap_pvt_thread_mutex_lock( &c->c_mutex );
216
217         if( c->c_txn != CONN_TXN_SPECIFY ) {
218                 rs->sr_text = "invalid transaction identifier";
219                 rc = LDAP_X_TXN_ID_INVALID;
220                 goto done;
221         }
222         c->c_txn = CONN_TXN_SETTLE;
223
224         if( commit ) {
225                 slap_callback cb = {0};
226                 OpExtra *txn = NULL;
227                 if ( op->o_abandon ) {
228                         goto drain;
229                 }
230
231                 if( LDAP_STAILQ_EMPTY(&c->c_txn_ops) ) {
232                         /* no updates to commit */
233                         rs->sr_text = "no updates to commit";
234                         rc = LDAP_OPERATIONS_ERROR;
235                         goto settled;
236                 }
237
238                 cb.sc_response = txn_result;
239                 LDAP_STAILQ_FOREACH( o, &c->c_txn_ops, o_next ) {
240                         o->o_bd = c->c_txn_backend;
241                         p = o;
242                         if ( !txn ) {
243                                 rc = o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_BEGIN, &txn );
244                                 if ( rc ) {
245                                         rs->sr_text = "couldn't start DB transaction";
246                                         rc = LDAP_OTHER;
247                                         goto drain;
248                                 }
249                         } else {
250                                 LDAP_SLIST_INSERT_HEAD( &o->o_extra, txn, oe_next );
251                         }
252                         cb.sc_next = o->o_callback;
253                         o->o_callback = &cb;
254                         {
255                                 SlapReply rs = {REP_RESULT};
256                                 int opidx = slap_req2op( o->o_tag );
257                                 assert( opidx != SLAP_OP_LAST );
258                                 o->o_threadctx = op->o_threadctx;
259                                 o->o_tid = op->o_tid;
260                                 ldap_pvt_thread_mutex_unlock( &c->c_mutex );
261                                 rc = (&o->o_bd->bd_info->bi_op_bind)[opidx]( o, &rs );
262                                 ldap_pvt_thread_mutex_lock( &c->c_mutex );
263                         }
264                         if ( rc ) {
265                                 struct berval *bv = NULL;
266                                 BerElementBuffer berbuf;
267                                 BerElement *ber = (BerElement *)&berbuf;
268
269                                 ber_init_w_nullc( ber, LBER_USE_DER );
270                                 ber_printf( ber, "{i", o->o_msgid );
271                                 if ( cb.sc_private )
272                                         txn_put_ctrls( op, ber, cb.sc_private );
273                                 ber_printf( ber, "}" );
274                                 ber_flatten( ber, &bv );
275                                 ber_free_buf( ber );
276                                 rs->sr_rspdata = bv;
277                                 o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_ABORT, &txn );
278                                 goto drain;
279                         }
280                 }
281                 if ( cb.sc_private ) {
282                         struct berval *bv = NULL;
283                         BerElementBuffer berbuf;
284                         BerElement *ber = (BerElement *)&berbuf;
285
286                         ber_init_w_nullc( ber, LBER_USE_DER );
287                         ber_printf( ber, "{" );
288                         txn_put_ctrls( op, ber, cb.sc_private );
289                         ber_printf( ber, "}" );
290                         ber_flatten( ber, &bv );
291                         ber_free_buf( ber );
292                         rs->sr_rspdata = bv;
293                 }
294                 o = p;
295                 rc = o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_COMMIT, &txn );
296                 if ( rc ) {
297                         rs->sr_text = "transaction commit failed";
298                         rc = LDAP_OTHER;
299                 }
300         } else {
301                 rs->sr_text = "transaction aborted";
302                 rc = LDAP_SUCCESS;
303         }
304
305 drain:
306         /* drain txn ops list */
307         while (( o = LDAP_STAILQ_FIRST( &c->c_txn_ops )) != NULL ) {
308                 LDAP_STAILQ_REMOVE_HEAD( &c->c_txn_ops, o_next );
309                 LDAP_STAILQ_NEXT( o, o_next ) = NULL;
310                 slap_op_free( o, NULL );
311         }
312
313 settled:
314         assert( LDAP_STAILQ_EMPTY(&c->c_txn_ops) );
315         assert( c->c_txn == CONN_TXN_SETTLE );
316         c->c_txn = CONN_TXN_INACTIVE;
317         c->c_txn_backend = NULL;
318
319 done:
320         /* release connection lock */
321         ldap_pvt_thread_mutex_unlock( &c->c_mutex );
322
323         return rc;
324 }
325
326 int txn_preop( Operation *op, SlapReply *rs )
327 {
328         int settle = 0;
329
330         /* acquire connection lock */
331         ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
332         if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
333                 rs->sr_text = "invalid transaction identifier";
334                 rs->sr_err = LDAP_X_TXN_ID_INVALID;
335                 goto txnReturn;
336         } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
337                 settle=1;
338                 goto txnReturn;
339         }
340
341         if( op->o_conn->c_txn_backend == NULL ) {
342                 op->o_conn->c_txn_backend = op->o_bd;
343
344         } else if( op->o_conn->c_txn_backend != op->o_bd ) {
345                 rs->sr_text = "transaction cannot span multiple database contexts";
346                 rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
347                 goto txnReturn;
348         }
349
350         /* insert operation into transaction */
351         LDAP_STAILQ_REMOVE( &op->o_conn->c_ops, op, Operation, o_next );
352         LDAP_STAILQ_INSERT_TAIL( &op->o_conn->c_txn_ops, op, o_next );
353
354 txnReturn:
355         /* release connection lock */
356         ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
357
358         if( !settle ) {
359                 send_ldap_result( op, rs );
360                 if ( !rs->sr_err )
361                         rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
362                 return rs->sr_err;
363         }
364         return LDAP_SUCCESS;    /* proceed with operation */
365 }
366
367 #endif /* LDAP_X_TXN */