1/*	$NetBSD: txn.c,v 1.1.1.3 2010/12/12 15:22:52 adam Exp $	*/
2
3/* txn.c - LDAP Transactions */
4/* OpenLDAP: pkg/ldap/servers/slapd/txn.c,v 1.6.2.5 2010/04/13 20:23:22 kurt Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1998-2010 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18
19#include "portable.h"
20
21#include <stdio.h>
22
23#include <ac/socket.h>
24#include <ac/string.h>
25#include <ac/unistd.h>
26
27#include "slap.h"
28
29#include <lber_pvt.h>
30#include <lutil.h>
31
32#ifdef LDAP_X_TXN
33const struct berval slap_EXOP_TXN_START = BER_BVC(LDAP_EXOP_X_TXN_START);
34const struct berval slap_EXOP_TXN_END = BER_BVC(LDAP_EXOP_X_TXN_END);
35
36int txn_start_extop(
37	Operation *op, SlapReply *rs )
38{
39	int rc;
40	struct berval *bv;
41
42	Statslog( LDAP_DEBUG_STATS, "%s TXN START\n",
43		op->o_log_prefix, 0, 0, 0, 0 );
44
45	if( op->ore_reqdata != NULL ) {
46		rs->sr_text = "no request data expected";
47		return LDAP_PROTOCOL_ERROR;
48	}
49
50	op->o_bd = op->o_conn->c_authz_backend;
51	if( backend_check_restrictions( op, rs,
52		(struct berval *)&slap_EXOP_TXN_START ) != LDAP_SUCCESS )
53	{
54		return rs->sr_err;
55	}
56
57	/* acquire connection lock */
58	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
59
60	if( op->o_conn->c_txn != CONN_TXN_INACTIVE ) {
61		rs->sr_text = "Too many transactions";
62		rc = LDAP_BUSY;
63		goto done;
64	}
65
66	assert( op->o_conn->c_txn_backend == NULL );
67	op->o_conn->c_txn = CONN_TXN_SPECIFY;
68
69	bv = (struct berval *) ch_malloc( sizeof (struct berval) );
70	bv->bv_len = 0;
71	bv->bv_val = NULL;
72
73	rs->sr_rspdata = bv;
74	rc = LDAP_SUCCESS;
75
76done:
77	/* release connection lock */
78	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
79	return rc;
80}
81
82int txn_spec_ctrl(
83	Operation *op, SlapReply *rs, LDAPControl *ctrl )
84{
85	if ( !ctrl->ldctl_iscritical ) {
86		rs->sr_text = "txnSpec control must be marked critical";
87		return LDAP_PROTOCOL_ERROR;
88	}
89	if( op->o_txnSpec ) {
90		rs->sr_text = "txnSpec control provided multiple times";
91		return LDAP_PROTOCOL_ERROR;
92	}
93
94	if ( ctrl->ldctl_value.bv_val == NULL ) {
95		rs->sr_text = "no transaction identifier provided";
96		return LDAP_PROTOCOL_ERROR;
97	}
98	if ( ctrl->ldctl_value.bv_len != 0 ) {
99		rs->sr_text = "invalid transaction identifier";
100		return LDAP_X_TXN_ID_INVALID;
101	}
102
103	if ( op->o_preread ) { /* temporary limitation */
104		rs->sr_text = "cannot perform pre-read in transaction";
105		return LDAP_UNWILLING_TO_PERFORM;
106	}
107	if ( op->o_postread ) { /* temporary limitation */
108		rs->sr_text = "cannot perform post-read in transaction";
109		return LDAP_UNWILLING_TO_PERFORM;
110	}
111
112	op->o_txnSpec = SLAP_CONTROL_CRITICAL;
113	return LDAP_SUCCESS;
114}
115
116int txn_end_extop(
117	Operation *op, SlapReply *rs )
118{
119	int rc;
120	BerElementBuffer berbuf;
121	BerElement *ber = (BerElement *)&berbuf;
122	ber_tag_t tag;
123	ber_len_t len;
124	ber_int_t commit=1;
125	struct berval txnid;
126
127	Statslog( LDAP_DEBUG_STATS, "%s TXN END\n",
128		op->o_log_prefix, 0, 0, 0, 0 );
129
130	if( op->ore_reqdata == NULL ) {
131		rs->sr_text = "request data expected";
132		return LDAP_PROTOCOL_ERROR;
133	}
134	if( op->ore_reqdata->bv_len == 0 ) {
135		rs->sr_text = "empty request data";
136		return LDAP_PROTOCOL_ERROR;
137	}
138
139	op->o_bd = op->o_conn->c_authz_backend;
140	if( backend_check_restrictions( op, rs,
141		(struct berval *)&slap_EXOP_TXN_END ) != LDAP_SUCCESS )
142	{
143		return rs->sr_err;
144	}
145
146	ber_init2( ber, op->ore_reqdata, 0 );
147
148	tag = ber_scanf( ber, "{" /*}*/ );
149	if( tag == LBER_ERROR ) {
150		rs->sr_text = "request data decoding error";
151		return LDAP_PROTOCOL_ERROR;
152	}
153
154	tag = ber_peek_tag( ber, &len );
155	if( tag == LBER_BOOLEAN ) {
156		tag = ber_scanf( ber, "b", &commit );
157		if( tag == LBER_ERROR ) {
158			rs->sr_text = "request data decoding error";
159			return LDAP_PROTOCOL_ERROR;
160		}
161	}
162
163	tag = ber_scanf( ber, /*{*/ "m}", &txnid );
164	if( tag == LBER_ERROR ) {
165		rs->sr_text = "request data decoding error";
166		return LDAP_PROTOCOL_ERROR;
167	}
168
169	if( txnid.bv_len ) {
170		rs->sr_text = "invalid transaction identifier";
171		return LDAP_X_TXN_ID_INVALID;
172	}
173
174	/* acquire connection lock */
175	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
176
177	if( op->o_conn->c_txn != CONN_TXN_SPECIFY ) {
178		rs->sr_text = "invalid transaction identifier";
179		rc = LDAP_X_TXN_ID_INVALID;
180		goto done;
181	}
182	op->o_conn->c_txn = CONN_TXN_SETTLE;
183
184	if( commit ) {
185		if ( op->o_abandon ) {
186		}
187
188		if( LDAP_STAILQ_EMPTY(&op->o_conn->c_txn_ops) ) {
189			/* no updates to commit */
190			rs->sr_text = "no updates to commit";
191			rc = LDAP_OPERATIONS_ERROR;
192			goto settled;
193		}
194
195		rs->sr_text = "not yet implemented";
196		rc = LDAP_UNWILLING_TO_PERFORM;
197
198	} else {
199		rs->sr_text = "transaction aborted";
200		rc = LDAP_SUCCESS;;
201	}
202
203drain:
204	/* drain txn ops list */
205
206settled:
207	assert( LDAP_STAILQ_EMPTY(&op->o_conn->c_txn_ops) );
208	assert( op->o_conn->c_txn == CONN_TXN_SETTLE );
209	op->o_conn->c_txn = CONN_TXN_INACTIVE;
210	op->o_conn->c_txn_backend = NULL;
211
212done:
213	/* release connection lock */
214	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
215
216	return rc;
217}
218
219#endif /* LDAP_X_TXN */
220