txn.c revision 1.1
1/* txn.c - LDAP Transactions */
2/* $OpenLDAP: pkg/ldap/servers/slapd/txn.c,v 1.6.2.3 2008/02/11 23:26:45 kurt Exp $ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 1998-2008 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
31const struct berval slap_EXOP_TXN_START = BER_BVC(LDAP_EXOP_X_TXN_START);
32const struct berval slap_EXOP_TXN_END = BER_BVC(LDAP_EXOP_X_TXN_END);
33
34int 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
74done:
75	/* release connection lock */
76	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
77	return rc;
78}
79
80int 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
114int txn_end_extop(
115	Operation *op, SlapReply *rs )
116{
117	int rc;
118	BerElementBuffer berbuf;
119	BerElement *ber = (BerElement *)&berbuf;
120	ber_tag_t tag;
121	ber_len_t len;
122	ber_int_t commit=1;
123	struct berval txnid;
124
125	Statslog( LDAP_DEBUG_STATS, "%s TXN END\n",
126		op->o_log_prefix, 0, 0, 0, 0 );
127
128	if( op->ore_reqdata == NULL ) {
129		rs->sr_text = "request data expected";
130		return LDAP_PROTOCOL_ERROR;
131	}
132	if( op->ore_reqdata->bv_len == 0 ) {
133		rs->sr_text = "empty request data";
134		return LDAP_PROTOCOL_ERROR;
135	}
136
137	op->o_bd = op->o_conn->c_authz_backend;
138	if( backend_check_restrictions( op, rs,
139		(struct berval *)&slap_EXOP_TXN_END ) != LDAP_SUCCESS )
140	{
141		return rs->sr_err;
142	}
143
144	ber_init2( ber, op->ore_reqdata, 0 );
145
146	tag = ber_scanf( ber, "{" /*}*/ );
147	if( tag == LBER_ERROR ) {
148		rs->sr_text = "request data decoding error";
149		return LDAP_PROTOCOL_ERROR;
150	}
151
152	tag = ber_peek_tag( ber, &len );
153	if( tag == LBER_BOOLEAN ) {
154		tag = ber_scanf( ber, "b", &commit );
155		if( tag == LBER_ERROR ) {
156			rs->sr_text = "request data decoding error";
157			return LDAP_PROTOCOL_ERROR;
158		}
159	}
160
161	tag = ber_scanf( ber, /*{*/ "m}", &txnid );
162	if( tag == LBER_ERROR ) {
163		rs->sr_text = "request data decoding error";
164		return LDAP_PROTOCOL_ERROR;
165	}
166
167	if( txnid.bv_len ) {
168		rs->sr_text = "invalid transaction identifier";
169		return LDAP_X_TXN_ID_INVALID;
170	}
171
172	/* acquire connection lock */
173	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
174
175	if( op->o_conn->c_txn != CONN_TXN_SPECIFY ) {
176		rs->sr_text = "invalid transaction identifier";
177		rc = LDAP_X_TXN_ID_INVALID;
178		goto done;
179	}
180	op->o_conn->c_txn = CONN_TXN_SETTLE;
181
182	if( commit ) {
183		if ( op->o_abandon ) {
184		}
185
186		if( LDAP_STAILQ_EMPTY(&op->o_conn->c_txn_ops) ) {
187			/* no updates to commit */
188			rs->sr_text = "no updates to commit";
189			rc = LDAP_OPERATIONS_ERROR;
190			goto settled;
191		}
192
193		rs->sr_text = "not yet implemented";
194		rc = LDAP_UNWILLING_TO_PERFORM;
195
196	} else {
197		rs->sr_text = "transaction aborted";
198		rc = LDAP_SUCCESS;;
199	}
200
201drain:
202	/* drain txn ops list */
203
204settled:
205	assert( LDAP_STAILQ_EMPTY(&op->o_conn->c_txn_ops) );
206	assert( op->o_conn->c_txn == CONN_TXN_SETTLE );
207	op->o_conn->c_txn = CONN_TXN_INACTIVE;
208	op->o_conn->c_txn_backend = NULL;
209
210done:
211	/* release connection lock */
212	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
213
214	return rc;
215}
216
217#endif /* LDAP_X_TXN */
218