]> git.sur5r.net Git - openldap/blob - servers/slapd/txn.c
cdd540bfac2b5fc7abe16df3215f340073e47a1a
[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-2014 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 static int txn_result( Operation *op, SlapReply *rs )
115 {
116         return rs->sr_err;
117 }
118
119 int txn_end_extop(
120         Operation *op, SlapReply *rs )
121 {
122         int rc;
123         BerElementBuffer berbuf;
124         BerElement *ber = (BerElement *)&berbuf;
125         ber_tag_t tag;
126         ber_len_t len;
127         ber_int_t commit=1;
128         struct berval txnid;
129         Operation *o, *p;
130         Connection *c = op->o_conn;
131
132         Statslog( LDAP_DEBUG_STATS, "%s TXN END\n",
133                 op->o_log_prefix, 0, 0, 0, 0 );
134
135         if( op->ore_reqdata == NULL ) {
136                 rs->sr_text = "request data expected";
137                 return LDAP_PROTOCOL_ERROR;
138         }
139         if( op->ore_reqdata->bv_len == 0 ) {
140                 rs->sr_text = "empty request data";
141                 return LDAP_PROTOCOL_ERROR;
142         }
143
144         op->o_bd = c->c_authz_backend;
145         if( backend_check_restrictions( op, rs,
146                 (struct berval *)&slap_EXOP_TXN_END ) != LDAP_SUCCESS )
147         {
148                 return rs->sr_err;
149         }
150
151         ber_init2( ber, op->ore_reqdata, 0 );
152
153         tag = ber_scanf( ber, "{" /*}*/ );
154         if( tag == LBER_ERROR ) {
155                 rs->sr_text = "request data decoding error";
156                 return LDAP_PROTOCOL_ERROR;
157         }
158
159         tag = ber_peek_tag( ber, &len );
160         if( tag == LBER_BOOLEAN ) {
161                 tag = ber_scanf( ber, "b", &commit );
162                 if( tag == LBER_ERROR ) {
163                         rs->sr_text = "request data decoding error";
164                         return LDAP_PROTOCOL_ERROR;
165                 }
166         }
167
168         tag = ber_scanf( ber, /*{*/ "m}", &txnid );
169         if( tag == LBER_ERROR ) {
170                 rs->sr_text = "request data decoding error";
171                 return LDAP_PROTOCOL_ERROR;
172         }
173
174         if( txnid.bv_len ) {
175                 rs->sr_text = "invalid transaction identifier";
176                 return LDAP_X_TXN_ID_INVALID;
177         }
178
179         /* acquire connection lock */
180         ldap_pvt_thread_mutex_lock( &c->c_mutex );
181
182         if( c->c_txn != CONN_TXN_SPECIFY ) {
183                 rs->sr_text = "invalid transaction identifier";
184                 rc = LDAP_X_TXN_ID_INVALID;
185                 goto done;
186         }
187         c->c_txn = CONN_TXN_SETTLE;
188
189         if( commit ) {
190                 slap_callback cb = {0};
191                 OpExtra *txn = NULL;
192                 if ( op->o_abandon ) {
193                         goto drain;
194                 }
195
196                 if( LDAP_STAILQ_EMPTY(&c->c_txn_ops) ) {
197                         /* no updates to commit */
198                         rs->sr_text = "no updates to commit";
199                         rc = LDAP_OPERATIONS_ERROR;
200                         goto settled;
201                 }
202
203                 cb.sc_response = txn_result;
204                 LDAP_STAILQ_FOREACH( o, &c->c_txn_ops, o_next ) {
205                         o->o_bd = c->c_txn_backend;
206                         p = o;
207                         if ( !txn ) {
208                                 rc = o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_BEGIN, &txn );
209                                 if ( rc ) {
210                                         rs->sr_text = "couldn't start DB transaction";
211                                         rc = LDAP_OTHER;
212                                         goto drain;
213                                 }
214                         } else {
215                                 LDAP_SLIST_INSERT_HEAD( &o->o_extra, txn, oe_next );
216                         }
217                         cb.sc_next = o->o_callback;
218                         o->o_callback = &cb;
219                         {
220                                 SlapReply rs = {REP_RESULT};
221                                 int opidx = slap_req2op( o->o_tag );
222                                 assert( opidx != SLAP_OP_LAST );
223                                 o->o_threadctx = op->o_threadctx;
224                                 o->o_tid = op->o_tid;
225                                 ldap_pvt_thread_mutex_unlock( &c->c_mutex );
226                                 rc = (&o->o_bd->bd_info->bi_op_bind)[opidx]( o, &rs );
227                                 ldap_pvt_thread_mutex_lock( &c->c_mutex );
228                         }
229                         if ( rc ) {
230                                 struct berval *bv = NULL;
231                                 BerElementBuffer berbuf;
232                                 BerElement *ber = (BerElement *)&berbuf;
233
234                                 ber_init_w_nullc( ber, LBER_USE_DER );
235                                 ber_printf( ber, "{i}", o->o_msgid );
236                                 ber_flatten( ber, &bv );
237                                 ber_free_buf( ber );
238                                 rs->sr_rspdata = bv;
239                                 o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_ABORT, &txn );
240                                 goto drain;
241                         }
242                 }
243                 o = p;
244                 rc = o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_COMMIT, &txn );
245                 if ( rc ) {
246                         rs->sr_text = "transaction commit failed";
247                         rc = LDAP_OTHER;
248                 }
249         } else {
250                 rs->sr_text = "transaction aborted";
251                 rc = LDAP_SUCCESS;
252         }
253
254 drain:
255         /* drain txn ops list */
256         while (( o = LDAP_STAILQ_FIRST( &c->c_txn_ops )) != NULL ) {
257                 LDAP_STAILQ_REMOVE_HEAD( &c->c_txn_ops, o_next );
258                 LDAP_STAILQ_NEXT( o, o_next ) = NULL;
259                 slap_op_free( o, NULL );
260         }
261
262 settled:
263         assert( LDAP_STAILQ_EMPTY(&c->c_txn_ops) );
264         assert( c->c_txn == CONN_TXN_SETTLE );
265         c->c_txn = CONN_TXN_INACTIVE;
266         c->c_txn_backend = NULL;
267
268 done:
269         /* release connection lock */
270         ldap_pvt_thread_mutex_unlock( &c->c_mutex );
271
272         return rc;
273 }
274
275 int txn_preop( Operation *op, SlapReply *rs )
276 {
277         int settle = 0;
278
279         /* acquire connection lock */
280         ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
281         if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
282                 rs->sr_text = "invalid transaction identifier";
283                 rs->sr_err = LDAP_X_TXN_ID_INVALID;
284                 goto txnReturn;
285         } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
286                 settle=1;
287                 goto txnReturn;
288         }
289
290         if( op->o_conn->c_txn_backend == NULL ) {
291                 op->o_conn->c_txn_backend = op->o_bd;
292
293         } else if( op->o_conn->c_txn_backend != op->o_bd ) {
294                 rs->sr_text = "transaction cannot span multiple database contexts";
295                 rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
296                 goto txnReturn;
297         }
298
299         /* insert operation into transaction */
300         LDAP_STAILQ_REMOVE( &op->o_conn->c_ops, op, Operation, o_next );
301         LDAP_STAILQ_INSERT_TAIL( &op->o_conn->c_txn_ops, op, o_next );
302
303 txnReturn:
304         /* release connection lock */
305         ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
306
307         if( !settle ) {
308                 send_ldap_result( op, rs );
309                 if ( !rs->sr_err )
310                         rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
311                 return rs->sr_err;
312         }
313         return LDAP_SUCCESS;    /* proceed with operation */
314 }
315
316 #endif /* LDAP_X_TXN */