operation.c revision 1.1 1 1.1 christos /* $NetBSD: operation.c,v 1.1 2021/08/14 16:05:25 christos Exp $ */
2 1.1 christos
3 1.1 christos /* $OpenLDAP$ */
4 1.1 christos /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 1.1 christos *
6 1.1 christos * Copyright 1998-2021 The OpenLDAP Foundation.
7 1.1 christos * All rights reserved.
8 1.1 christos *
9 1.1 christos * Redistribution and use in source and binary forms, with or without
10 1.1 christos * modification, are permitted only as authorized by the OpenLDAP
11 1.1 christos * Public License.
12 1.1 christos *
13 1.1 christos * A copy of this license is available in the file LICENSE in the
14 1.1 christos * top-level directory of the distribution or, alternatively, at
15 1.1 christos * <http://www.OpenLDAP.org/license.html>.
16 1.1 christos */
17 1.1 christos
18 1.1 christos #include <sys/cdefs.h>
19 1.1 christos __RCSID("$NetBSD: operation.c,v 1.1 2021/08/14 16:05:25 christos Exp $");
20 1.1 christos
21 1.1 christos #include "portable.h"
22 1.1 christos
23 1.1 christos #include "lutil.h"
24 1.1 christos #include "lload.h"
25 1.1 christos
26 1.1 christos ldap_pvt_thread_mutex_t lload_pin_mutex;
27 1.1 christos unsigned long lload_next_pin = 1;
28 1.1 christos
29 1.1 christos ber_tag_t
30 1.1 christos slap_req2res( ber_tag_t tag )
31 1.1 christos {
32 1.1 christos switch ( tag ) {
33 1.1 christos case LDAP_REQ_ADD:
34 1.1 christos case LDAP_REQ_BIND:
35 1.1 christos case LDAP_REQ_COMPARE:
36 1.1 christos case LDAP_REQ_EXTENDED:
37 1.1 christos case LDAP_REQ_MODIFY:
38 1.1 christos case LDAP_REQ_MODRDN:
39 1.1 christos tag++;
40 1.1 christos break;
41 1.1 christos
42 1.1 christos case LDAP_REQ_DELETE:
43 1.1 christos tag = LDAP_RES_DELETE;
44 1.1 christos break;
45 1.1 christos
46 1.1 christos case LDAP_REQ_ABANDON:
47 1.1 christos case LDAP_REQ_UNBIND:
48 1.1 christos tag = LBER_SEQUENCE;
49 1.1 christos break;
50 1.1 christos
51 1.1 christos case LDAP_REQ_SEARCH:
52 1.1 christos tag = LDAP_RES_SEARCH_RESULT;
53 1.1 christos break;
54 1.1 christos
55 1.1 christos default:
56 1.1 christos tag = LBER_SEQUENCE;
57 1.1 christos }
58 1.1 christos
59 1.1 christos return tag;
60 1.1 christos }
61 1.1 christos
62 1.1 christos const char *
63 1.1 christos lload_msgtype2str( ber_tag_t tag )
64 1.1 christos {
65 1.1 christos switch ( tag ) {
66 1.1 christos case LDAP_REQ_ABANDON: return "abandon request";
67 1.1 christos case LDAP_REQ_ADD: return "add request";
68 1.1 christos case LDAP_REQ_BIND: return "bind request";
69 1.1 christos case LDAP_REQ_COMPARE: return "compare request";
70 1.1 christos case LDAP_REQ_DELETE: return "delete request";
71 1.1 christos case LDAP_REQ_EXTENDED: return "extended request";
72 1.1 christos case LDAP_REQ_MODIFY: return "modify request";
73 1.1 christos case LDAP_REQ_RENAME: return "rename request";
74 1.1 christos case LDAP_REQ_SEARCH: return "search request";
75 1.1 christos case LDAP_REQ_UNBIND: return "unbind request";
76 1.1 christos
77 1.1 christos case LDAP_RES_ADD: return "add result";
78 1.1 christos case LDAP_RES_BIND: return "bind result";
79 1.1 christos case LDAP_RES_COMPARE: return "compare result";
80 1.1 christos case LDAP_RES_DELETE: return "delete result";
81 1.1 christos case LDAP_RES_EXTENDED: return "extended result";
82 1.1 christos case LDAP_RES_INTERMEDIATE: return "intermediate response";
83 1.1 christos case LDAP_RES_MODIFY: return "modify result";
84 1.1 christos case LDAP_RES_RENAME: return "rename result";
85 1.1 christos case LDAP_RES_SEARCH_ENTRY: return "search-entry response";
86 1.1 christos case LDAP_RES_SEARCH_REFERENCE: return "search-reference response";
87 1.1 christos case LDAP_RES_SEARCH_RESULT: return "search result";
88 1.1 christos }
89 1.1 christos return "unknown message";
90 1.1 christos }
91 1.1 christos
92 1.1 christos int
93 1.1 christos operation_client_cmp( const void *left, const void *right )
94 1.1 christos {
95 1.1 christos const LloadOperation *l = left, *r = right;
96 1.1 christos
97 1.1 christos assert( l->o_client_connid == r->o_client_connid );
98 1.1 christos if ( l->o_client_msgid || r->o_client_msgid ) {
99 1.1 christos return ( l->o_client_msgid < r->o_client_msgid ) ?
100 1.1 christos -1 :
101 1.1 christos ( l->o_client_msgid > r->o_client_msgid );
102 1.1 christos } else {
103 1.1 christos return ( l->o_pin_id < r->o_pin_id ) ? -1 :
104 1.1 christos ( l->o_pin_id > r->o_pin_id );
105 1.1 christos }
106 1.1 christos }
107 1.1 christos
108 1.1 christos int
109 1.1 christos operation_upstream_cmp( const void *left, const void *right )
110 1.1 christos {
111 1.1 christos const LloadOperation *l = left, *r = right;
112 1.1 christos
113 1.1 christos assert( l->o_upstream_connid == r->o_upstream_connid );
114 1.1 christos if ( l->o_upstream_msgid || r->o_upstream_msgid ) {
115 1.1 christos return ( l->o_upstream_msgid < r->o_upstream_msgid ) ?
116 1.1 christos -1 :
117 1.1 christos ( l->o_upstream_msgid > r->o_upstream_msgid );
118 1.1 christos } else {
119 1.1 christos return ( l->o_pin_id < r->o_pin_id ) ? -1 :
120 1.1 christos ( l->o_pin_id > r->o_pin_id );
121 1.1 christos }
122 1.1 christos }
123 1.1 christos
124 1.1 christos /*
125 1.1 christos * Entered holding c_mutex for now.
126 1.1 christos */
127 1.1 christos LloadOperation *
128 1.1 christos operation_init( LloadConnection *c, BerElement *ber )
129 1.1 christos {
130 1.1 christos LloadOperation *op;
131 1.1 christos ber_tag_t tag;
132 1.1 christos ber_len_t len;
133 1.1 christos int rc;
134 1.1 christos
135 1.1 christos if ( !IS_ALIVE( c, c_live ) ) {
136 1.1 christos return NULL;
137 1.1 christos }
138 1.1 christos
139 1.1 christos op = ch_calloc( 1, sizeof(LloadOperation) );
140 1.1 christos op->o_client = c;
141 1.1 christos op->o_client_connid = c->c_connid;
142 1.1 christos op->o_ber = ber;
143 1.1 christos op->o_start = slap_get_time();
144 1.1 christos
145 1.1 christos ldap_pvt_thread_mutex_init( &op->o_link_mutex );
146 1.1 christos
147 1.1 christos op->o_refcnt = 1;
148 1.1 christos
149 1.1 christos tag = ber_get_int( ber, &op->o_client_msgid );
150 1.1 christos if ( tag != LDAP_TAG_MSGID ) {
151 1.1 christos goto fail;
152 1.1 christos }
153 1.1 christos
154 1.1 christos if ( !op->o_client_msgid ) {
155 1.1 christos goto fail;
156 1.1 christos }
157 1.1 christos
158 1.1 christos CONNECTION_ASSERT_LOCKED(c);
159 1.1 christos rc = ldap_tavl_insert( &c->c_ops, op, operation_client_cmp, ldap_avl_dup_error );
160 1.1 christos if ( rc ) {
161 1.1 christos Debug( LDAP_DEBUG_PACKETS, "operation_init: "
162 1.1 christos "several operations with same msgid=%d in-flight "
163 1.1 christos "from client connid=%lu\n",
164 1.1 christos op->o_client_msgid, op->o_client_connid );
165 1.1 christos goto fail;
166 1.1 christos }
167 1.1 christos
168 1.1 christos tag = op->o_tag = ber_skip_element( ber, &op->o_request );
169 1.1 christos switch ( tag ) {
170 1.1 christos case LBER_ERROR:
171 1.1 christos rc = -1;
172 1.1 christos break;
173 1.1 christos }
174 1.1 christos if ( rc ) {
175 1.1 christos ldap_tavl_delete( &c->c_ops, op, operation_client_cmp );
176 1.1 christos goto fail;
177 1.1 christos }
178 1.1 christos
179 1.1 christos tag = ber_peek_tag( ber, &len );
180 1.1 christos if ( tag == LDAP_TAG_CONTROLS ) {
181 1.1 christos ber_skip_element( ber, &op->o_ctrls );
182 1.1 christos }
183 1.1 christos
184 1.1 christos switch ( op->o_tag ) {
185 1.1 christos case LDAP_REQ_BIND:
186 1.1 christos lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_received++;
187 1.1 christos break;
188 1.1 christos default:
189 1.1 christos lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_received++;
190 1.1 christos break;
191 1.1 christos }
192 1.1 christos
193 1.1 christos Debug( LDAP_DEBUG_STATS, "operation_init: "
194 1.1 christos "received a new operation, %s with msgid=%d for client "
195 1.1 christos "connid=%lu\n",
196 1.1 christos lload_msgtype2str( op->o_tag ), op->o_client_msgid,
197 1.1 christos op->o_client_connid );
198 1.1 christos
199 1.1 christos c->c_n_ops_executing++;
200 1.1 christos return op;
201 1.1 christos
202 1.1 christos fail:
203 1.1 christos ch_free( op );
204 1.1 christos return NULL;
205 1.1 christos }
206 1.1 christos
207 1.1 christos void
208 1.1 christos operation_destroy( LloadOperation *op )
209 1.1 christos {
210 1.1 christos Debug( LDAP_DEBUG_TRACE, "operation_destroy: "
211 1.1 christos "op=%p destroyed operation from client connid=%lu, "
212 1.1 christos "client msgid=%d\n",
213 1.1 christos op, op->o_client_connid, op->o_client_msgid );
214 1.1 christos
215 1.1 christos assert( op->o_refcnt == 0 );
216 1.1 christos assert( op->o_client == NULL );
217 1.1 christos assert( op->o_upstream == NULL );
218 1.1 christos
219 1.1 christos ber_free( op->o_ber, 1 );
220 1.1 christos ldap_pvt_thread_mutex_destroy( &op->o_link_mutex );
221 1.1 christos ch_free( op );
222 1.1 christos }
223 1.1 christos
224 1.1 christos int
225 1.1 christos operation_unlink( LloadOperation *op )
226 1.1 christos {
227 1.1 christos LloadConnection *client, *upstream;
228 1.1 christos uintptr_t prev_refcnt;
229 1.1 christos int result = 0;
230 1.1 christos
231 1.1 christos if ( !( prev_refcnt = try_release_ref(
232 1.1 christos &op->o_refcnt, op, (dispose_cb *)operation_destroy ) ) ) {
233 1.1 christos return result;
234 1.1 christos }
235 1.1 christos
236 1.1 christos assert( prev_refcnt == 1 );
237 1.1 christos
238 1.1 christos Debug( LDAP_DEBUG_TRACE, "operation_unlink: "
239 1.1 christos "unlinking operation between client connid=%lu and upstream "
240 1.1 christos "connid=%lu "
241 1.1 christos "client msgid=%d\n",
242 1.1 christos op->o_client_connid, op->o_upstream_connid, op->o_client_msgid );
243 1.1 christos
244 1.1 christos checked_lock( &op->o_link_mutex );
245 1.1 christos client = op->o_client;
246 1.1 christos upstream = op->o_upstream;
247 1.1 christos
248 1.1 christos op->o_client = NULL;
249 1.1 christos op->o_upstream = NULL;
250 1.1 christos checked_unlock( &op->o_link_mutex );
251 1.1 christos
252 1.1 christos assert( client || upstream );
253 1.1 christos
254 1.1 christos if ( client ) {
255 1.1 christos result |= operation_unlink_client( op, client );
256 1.1 christos operation_update_global_rejected( op );
257 1.1 christos }
258 1.1 christos
259 1.1 christos if ( upstream ) {
260 1.1 christos result |= operation_unlink_upstream( op, upstream );
261 1.1 christos }
262 1.1 christos
263 1.1 christos return result;
264 1.1 christos }
265 1.1 christos
266 1.1 christos int
267 1.1 christos operation_unlink_client( LloadOperation *op, LloadConnection *client )
268 1.1 christos {
269 1.1 christos LloadOperation *removed;
270 1.1 christos int result = 0;
271 1.1 christos
272 1.1 christos Debug( LDAP_DEBUG_TRACE, "operation_unlink_client: "
273 1.1 christos "unlinking operation op=%p msgid=%d client connid=%lu\n",
274 1.1 christos op, op->o_client_msgid, op->o_client_connid );
275 1.1 christos
276 1.1 christos CONNECTION_LOCK(client);
277 1.1 christos if ( (removed = ldap_tavl_delete(
278 1.1 christos &client->c_ops, op, operation_client_cmp )) ) {
279 1.1 christos result = LLOAD_OP_DETACHING_CLIENT;
280 1.1 christos
281 1.1 christos assert( op == removed );
282 1.1 christos client->c_n_ops_executing--;
283 1.1 christos
284 1.1 christos if ( client->c_state == LLOAD_C_BINDING ) {
285 1.1 christos client->c_state = LLOAD_C_READY;
286 1.1 christos if ( !BER_BVISNULL( &client->c_auth ) ) {
287 1.1 christos ber_memfree( client->c_auth.bv_val );
288 1.1 christos BER_BVZERO( &client->c_auth );
289 1.1 christos }
290 1.1 christos if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
291 1.1 christos ber_memfree( client->c_sasl_bind_mech.bv_val );
292 1.1 christos BER_BVZERO( &client->c_sasl_bind_mech );
293 1.1 christos }
294 1.1 christos if ( op->o_pin_id ) {
295 1.1 christos client->c_pin_id = 0;
296 1.1 christos }
297 1.1 christos }
298 1.1 christos }
299 1.1 christos if ( client->c_state == LLOAD_C_CLOSING && !client->c_ops ) {
300 1.1 christos CONNECTION_DESTROY(client);
301 1.1 christos } else {
302 1.1 christos CONNECTION_UNLOCK(client);
303 1.1 christos }
304 1.1 christos
305 1.1 christos return result;
306 1.1 christos }
307 1.1 christos
308 1.1 christos int
309 1.1 christos operation_unlink_upstream( LloadOperation *op, LloadConnection *upstream )
310 1.1 christos {
311 1.1 christos LloadOperation *removed;
312 1.1 christos LloadBackend *b = NULL;
313 1.1 christos int result = 0;
314 1.1 christos
315 1.1 christos Debug( LDAP_DEBUG_TRACE, "operation_unlink_upstream: "
316 1.1 christos "unlinking operation op=%p msgid=%d upstream connid=%lu\n",
317 1.1 christos op, op->o_upstream_msgid, op->o_upstream_connid );
318 1.1 christos
319 1.1 christos CONNECTION_LOCK(upstream);
320 1.1 christos if ( (removed = ldap_tavl_delete(
321 1.1 christos &upstream->c_ops, op, operation_upstream_cmp )) ) {
322 1.1 christos result |= LLOAD_OP_DETACHING_UPSTREAM;
323 1.1 christos
324 1.1 christos assert( op == removed );
325 1.1 christos upstream->c_n_ops_executing--;
326 1.1 christos
327 1.1 christos if ( upstream->c_state == LLOAD_C_BINDING ) {
328 1.1 christos assert( op->o_tag == LDAP_REQ_BIND && upstream->c_ops == NULL );
329 1.1 christos upstream->c_state = LLOAD_C_READY;
330 1.1 christos if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
331 1.1 christos ber_memfree( upstream->c_sasl_bind_mech.bv_val );
332 1.1 christos BER_BVZERO( &upstream->c_sasl_bind_mech );
333 1.1 christos }
334 1.1 christos }
335 1.1 christos operation_update_conn_counters( op, upstream );
336 1.1 christos b = upstream->c_backend;
337 1.1 christos }
338 1.1 christos if ( upstream->c_state == LLOAD_C_CLOSING && !upstream->c_ops ) {
339 1.1 christos CONNECTION_DESTROY(upstream);
340 1.1 christos } else {
341 1.1 christos CONNECTION_UNLOCK(upstream);
342 1.1 christos }
343 1.1 christos
344 1.1 christos if ( b ) {
345 1.1 christos checked_lock( &b->b_mutex );
346 1.1 christos b->b_n_ops_executing--;
347 1.1 christos operation_update_backend_counters( op, b );
348 1.1 christos checked_unlock( &b->b_mutex );
349 1.1 christos }
350 1.1 christos
351 1.1 christos return result;
352 1.1 christos }
353 1.1 christos
354 1.1 christos int
355 1.1 christos operation_send_abandon( LloadOperation *op, LloadConnection *upstream )
356 1.1 christos {
357 1.1 christos BerElement *ber;
358 1.1 christos int rc = -1;
359 1.1 christos
360 1.1 christos if ( !IS_ALIVE( upstream, c_live ) ) {
361 1.1 christos return rc;
362 1.1 christos }
363 1.1 christos
364 1.1 christos checked_lock( &upstream->c_io_mutex );
365 1.1 christos ber = upstream->c_pendingber;
366 1.1 christos if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
367 1.1 christos Debug( LDAP_DEBUG_ANY, "operation_send_abandon: "
368 1.1 christos "ber_alloc failed\n" );
369 1.1 christos goto done;
370 1.1 christos }
371 1.1 christos upstream->c_pendingber = ber;
372 1.1 christos
373 1.1 christos Debug( LDAP_DEBUG_TRACE, "operation_send_abandon: "
374 1.1 christos "abandoning %s msgid=%d on connid=%lu\n",
375 1.1 christos lload_msgtype2str( op->o_tag ), op->o_upstream_msgid,
376 1.1 christos op->o_upstream_connid );
377 1.1 christos
378 1.1 christos if ( op->o_tag == LDAP_REQ_BIND ) {
379 1.1 christos rc = ber_printf( ber, "t{tit{ist{s}}}", LDAP_TAG_MESSAGE,
380 1.1 christos LDAP_TAG_MSGID, upstream->c_next_msgid++,
381 1.1 christos LDAP_REQ_BIND, LDAP_VERSION3, "", LDAP_AUTH_SASL, "" );
382 1.1 christos } else {
383 1.1 christos rc = ber_printf( ber, "t{titi}", LDAP_TAG_MESSAGE,
384 1.1 christos LDAP_TAG_MSGID, upstream->c_next_msgid++,
385 1.1 christos LDAP_REQ_ABANDON, op->o_upstream_msgid );
386 1.1 christos }
387 1.1 christos
388 1.1 christos if ( rc < 0 ) {
389 1.1 christos ber_free( ber, 1 );
390 1.1 christos upstream->c_pendingber = NULL;
391 1.1 christos goto done;
392 1.1 christos }
393 1.1 christos rc = LDAP_SUCCESS;
394 1.1 christos
395 1.1 christos done:
396 1.1 christos checked_unlock( &upstream->c_io_mutex );
397 1.1 christos return rc;
398 1.1 christos }
399 1.1 christos
400 1.1 christos /*
401 1.1 christos * Will remove the operation from its upstream and if it was still there,
402 1.1 christos * sends an abandon request.
403 1.1 christos *
404 1.1 christos * Being called from client_reset or request_abandon, the following hold:
405 1.1 christos * - noone else is processing the read part of the client connection (no new
406 1.1 christos * operations come in there - relevant for the c_state checks)
407 1.1 christos * - op->o_client_refcnt > op->o_client_live (and it follows that op->o_client != NULL)
408 1.1 christos */
409 1.1 christos void
410 1.1 christos operation_abandon( LloadOperation *op )
411 1.1 christos {
412 1.1 christos LloadConnection *c;
413 1.1 christos
414 1.1 christos checked_lock( &op->o_link_mutex );
415 1.1 christos c = op->o_upstream;
416 1.1 christos checked_unlock( &op->o_link_mutex );
417 1.1 christos if ( !c || !IS_ALIVE( c, c_live ) ) {
418 1.1 christos goto done;
419 1.1 christos }
420 1.1 christos
421 1.1 christos /* for now consider all abandoned operations completed,
422 1.1 christos * perhaps add a separate counter later */
423 1.1 christos op->o_res = LLOAD_OP_COMPLETED;
424 1.1 christos if ( !operation_unlink_upstream( op, c ) ) {
425 1.1 christos /* The operation has already been abandoned or finished */
426 1.1 christos Debug( LDAP_DEBUG_TRACE, "operation_abandon: "
427 1.1 christos "%s from connid=%lu msgid=%d not present in connid=%lu any "
428 1.1 christos "more\n",
429 1.1 christos lload_msgtype2str( op->o_tag ), op->o_client_connid,
430 1.1 christos op->o_client_msgid, op->o_upstream_connid );
431 1.1 christos goto done;
432 1.1 christos }
433 1.1 christos
434 1.1 christos if ( operation_send_abandon( op, c ) == LDAP_SUCCESS ) {
435 1.1 christos connection_write_cb( -1, 0, c );
436 1.1 christos }
437 1.1 christos
438 1.1 christos done:
439 1.1 christos operation_unlink( op );
440 1.1 christos }
441 1.1 christos
442 1.1 christos void
443 1.1 christos operation_send_reject(
444 1.1 christos LloadOperation *op,
445 1.1 christos int result,
446 1.1 christos const char *msg,
447 1.1 christos int send_anyway )
448 1.1 christos {
449 1.1 christos LloadConnection *c;
450 1.1 christos BerElement *ber;
451 1.1 christos int found;
452 1.1 christos
453 1.1 christos Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
454 1.1 christos "rejecting %s from client connid=%lu with message: \"%s\"\n",
455 1.1 christos lload_msgtype2str( op->o_tag ), op->o_client_connid, msg );
456 1.1 christos
457 1.1 christos checked_lock( &op->o_link_mutex );
458 1.1 christos c = op->o_client;
459 1.1 christos checked_unlock( &op->o_link_mutex );
460 1.1 christos if ( !c || !IS_ALIVE( c, c_live ) ) {
461 1.1 christos Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
462 1.1 christos "not sending msgid=%d, client connid=%lu is dead\n",
463 1.1 christos op->o_client_msgid, op->o_client_connid );
464 1.1 christos
465 1.1 christos goto done;
466 1.1 christos }
467 1.1 christos
468 1.1 christos found = operation_unlink_client( op, c );
469 1.1 christos if ( !found && !send_anyway ) {
470 1.1 christos Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
471 1.1 christos "msgid=%d not scheduled for client connid=%lu anymore, "
472 1.1 christos "not sending\n",
473 1.1 christos op->o_client_msgid, c->c_connid );
474 1.1 christos goto done;
475 1.1 christos }
476 1.1 christos
477 1.1 christos if ( op->o_client_msgid == 0 ) {
478 1.1 christos assert( op->o_saved_msgid == 0 && op->o_pin_id );
479 1.1 christos Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
480 1.1 christos "operation pin=%lu is just a pin, not sending\n",
481 1.1 christos op->o_pin_id );
482 1.1 christos goto done;
483 1.1 christos }
484 1.1 christos
485 1.1 christos checked_lock( &c->c_io_mutex );
486 1.1 christos ber = c->c_pendingber;
487 1.1 christos if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
488 1.1 christos checked_unlock( &c->c_io_mutex );
489 1.1 christos Debug( LDAP_DEBUG_ANY, "operation_send_reject: "
490 1.1 christos "ber_alloc failed, closing connid=%lu\n",
491 1.1 christos c->c_connid );
492 1.1 christos CONNECTION_LOCK_DESTROY(c);
493 1.1 christos goto done;
494 1.1 christos }
495 1.1 christos c->c_pendingber = ber;
496 1.1 christos
497 1.1 christos ber_printf( ber, "t{tit{ess}}", LDAP_TAG_MESSAGE,
498 1.1 christos LDAP_TAG_MSGID, op->o_client_msgid,
499 1.1 christos slap_req2res( op->o_tag ), result, "", msg );
500 1.1 christos
501 1.1 christos checked_unlock( &c->c_io_mutex );
502 1.1 christos
503 1.1 christos connection_write_cb( -1, 0, c );
504 1.1 christos
505 1.1 christos done:
506 1.1 christos operation_unlink( op );
507 1.1 christos }
508 1.1 christos
509 1.1 christos /*
510 1.1 christos * Upstream is shutting down, signal the client if necessary, but we have to
511 1.1 christos * call operation_destroy_from_upstream ourselves to detach upstream from the
512 1.1 christos * op.
513 1.1 christos *
514 1.1 christos * Only called from upstream_destroy.
515 1.1 christos */
516 1.1 christos void
517 1.1 christos operation_lost_upstream( LloadOperation *op )
518 1.1 christos {
519 1.1 christos operation_send_reject( op, LDAP_OTHER,
520 1.1 christos "connection to the remote server has been severed", 0 );
521 1.1 christos }
522 1.1 christos
523 1.1 christos int
524 1.1 christos connection_timeout( LloadConnection *upstream, void *arg )
525 1.1 christos {
526 1.1 christos LloadOperation *op;
527 1.1 christos TAvlnode *ops = NULL, *node, *next;
528 1.1 christos LloadBackend *b = upstream->c_backend;
529 1.1 christos time_t threshold = *(time_t *)arg;
530 1.1 christos int rc, nops = 0;
531 1.1 christos
532 1.1 christos CONNECTION_LOCK(upstream);
533 1.1 christos for ( node = ldap_tavl_end( upstream->c_ops, TAVL_DIR_LEFT ); node &&
534 1.1 christos ((LloadOperation *)node->avl_data)->o_start <
535 1.1 christos threshold; /* shortcut */
536 1.1 christos node = next ) {
537 1.1 christos LloadOperation *found_op;
538 1.1 christos
539 1.1 christos next = ldap_tavl_next( node, TAVL_DIR_RIGHT );
540 1.1 christos op = node->avl_data;
541 1.1 christos
542 1.1 christos /* Have we received another response since? */
543 1.1 christos if ( op->o_last_response && op->o_last_response >= threshold ) {
544 1.1 christos continue;
545 1.1 christos }
546 1.1 christos
547 1.1 christos op->o_res = LLOAD_OP_FAILED;
548 1.1 christos found_op = ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
549 1.1 christos assert( op == found_op );
550 1.1 christos
551 1.1 christos if ( upstream->c_state == LLOAD_C_BINDING ) {
552 1.1 christos assert( op->o_tag == LDAP_REQ_BIND && upstream->c_ops == NULL );
553 1.1 christos upstream->c_state = LLOAD_C_READY;
554 1.1 christos if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
555 1.1 christos ber_memfree( upstream->c_sasl_bind_mech.bv_val );
556 1.1 christos BER_BVZERO( &upstream->c_sasl_bind_mech );
557 1.1 christos }
558 1.1 christos }
559 1.1 christos
560 1.1 christos rc = ldap_tavl_insert( &ops, op, operation_upstream_cmp, ldap_avl_dup_error );
561 1.1 christos assert( rc == LDAP_SUCCESS );
562 1.1 christos
563 1.1 christos Debug( LDAP_DEBUG_STATS2, "connection_timeout: "
564 1.1 christos "timing out %s from connid=%lu msgid=%d sent to connid=%lu as "
565 1.1 christos "msgid=%d\n",
566 1.1 christos lload_msgtype2str( op->o_tag ), op->o_client_connid,
567 1.1 christos op->o_client_msgid, op->o_upstream_connid,
568 1.1 christos op->o_upstream_msgid );
569 1.1 christos nops++;
570 1.1 christos }
571 1.1 christos
572 1.1 christos if ( nops == 0 ) {
573 1.1 christos CONNECTION_UNLOCK(upstream);
574 1.1 christos return LDAP_SUCCESS;
575 1.1 christos }
576 1.1 christos upstream->c_n_ops_executing -= nops;
577 1.1 christos upstream->c_counters.lc_ops_failed += nops;
578 1.1 christos Debug( LDAP_DEBUG_STATS, "connection_timeout: "
579 1.1 christos "timing out %d operations for connid=%lu\n",
580 1.1 christos nops, upstream->c_connid );
581 1.1 christos CONNECTION_UNLOCK(upstream);
582 1.1 christos
583 1.1 christos checked_lock( &b->b_mutex );
584 1.1 christos b->b_n_ops_executing -= nops;
585 1.1 christos checked_unlock( &b->b_mutex );
586 1.1 christos
587 1.1 christos for ( node = ldap_tavl_end( ops, TAVL_DIR_LEFT ); node;
588 1.1 christos node = ldap_tavl_next( node, TAVL_DIR_RIGHT ) ) {
589 1.1 christos op = node->avl_data;
590 1.1 christos
591 1.1 christos operation_send_reject( op,
592 1.1 christos op->o_tag == LDAP_REQ_SEARCH ? LDAP_TIMELIMIT_EXCEEDED :
593 1.1 christos LDAP_ADMINLIMIT_EXCEEDED,
594 1.1 christos "upstream did not respond in time", 0 );
595 1.1 christos
596 1.1 christos if ( rc == LDAP_SUCCESS ) {
597 1.1 christos rc = operation_send_abandon( op, upstream );
598 1.1 christos }
599 1.1 christos operation_unlink( op );
600 1.1 christos }
601 1.1 christos
602 1.1 christos /* TODO: if operation_send_abandon failed, we need to kill the upstream */
603 1.1 christos if ( rc == LDAP_SUCCESS ) {
604 1.1 christos connection_write_cb( -1, 0, upstream );
605 1.1 christos }
606 1.1 christos
607 1.1 christos CONNECTION_LOCK(upstream);
608 1.1 christos if ( upstream->c_state == LLOAD_C_CLOSING && !upstream->c_ops ) {
609 1.1 christos CONNECTION_DESTROY(upstream);
610 1.1 christos } else {
611 1.1 christos CONNECTION_UNLOCK(upstream);
612 1.1 christos }
613 1.1 christos
614 1.1 christos /* just dispose of the AVL, most operations should already be gone */
615 1.1 christos ldap_tavl_free( ops, NULL );
616 1.1 christos return LDAP_SUCCESS;
617 1.1 christos }
618 1.1 christos
619 1.1 christos void
620 1.1 christos operations_timeout( evutil_socket_t s, short what, void *arg )
621 1.1 christos {
622 1.1 christos struct event *self = arg;
623 1.1 christos LloadBackend *b;
624 1.1 christos time_t threshold;
625 1.1 christos
626 1.1 christos Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
627 1.1 christos "running timeout task\n" );
628 1.1 christos if ( !lload_timeout_api ) goto done;
629 1.1 christos
630 1.1 christos threshold = slap_get_time() - lload_timeout_api->tv_sec;
631 1.1 christos
632 1.1 christos LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
633 1.1 christos epoch_t epoch;
634 1.1 christos
635 1.1 christos checked_lock( &b->b_mutex );
636 1.1 christos if ( b->b_n_ops_executing == 0 ) {
637 1.1 christos checked_unlock( &b->b_mutex );
638 1.1 christos continue;
639 1.1 christos }
640 1.1 christos
641 1.1 christos epoch = epoch_join();
642 1.1 christos
643 1.1 christos Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
644 1.1 christos "timing out binds for backend uri=%s\n",
645 1.1 christos b->b_uri.bv_val );
646 1.1 christos connections_walk_last( &b->b_mutex, &b->b_bindconns, b->b_last_bindconn,
647 1.1 christos connection_timeout, &threshold );
648 1.1 christos
649 1.1 christos Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
650 1.1 christos "timing out other operations for backend uri=%s\n",
651 1.1 christos b->b_uri.bv_val );
652 1.1 christos connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn,
653 1.1 christos connection_timeout, &threshold );
654 1.1 christos
655 1.1 christos epoch_leave( epoch );
656 1.1 christos checked_unlock( &b->b_mutex );
657 1.1 christos }
658 1.1 christos done:
659 1.1 christos Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
660 1.1 christos "timeout task finished\n" );
661 1.1 christos evtimer_add( self, lload_timeout_api );
662 1.1 christos }
663 1.1 christos
664 1.1 christos void
665 1.1 christos operation_update_global_rejected( LloadOperation *op )
666 1.1 christos {
667 1.1 christos if ( op->o_res == LLOAD_OP_REJECTED ) {
668 1.1 christos assert( op->o_upstream_connid == 0 );
669 1.1 christos switch ( op->o_tag ) {
670 1.1 christos case LDAP_REQ_BIND:
671 1.1 christos lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_rejected++;
672 1.1 christos break;
673 1.1 christos default:
674 1.1 christos lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_rejected++;
675 1.1 christos break;
676 1.1 christos }
677 1.1 christos }
678 1.1 christos }
679 1.1 christos
680 1.1 christos void
681 1.1 christos operation_update_conn_counters( LloadOperation *op, LloadConnection *upstream )
682 1.1 christos {
683 1.1 christos if ( op->o_res == LLOAD_OP_COMPLETED ) {
684 1.1 christos upstream->c_counters.lc_ops_completed++;
685 1.1 christos } else {
686 1.1 christos upstream->c_counters.lc_ops_failed++;
687 1.1 christos }
688 1.1 christos }
689 1.1 christos
690 1.1 christos void
691 1.1 christos operation_update_backend_counters( LloadOperation *op, LloadBackend *b )
692 1.1 christos {
693 1.1 christos int stat_type = op->o_tag == LDAP_REQ_BIND ? LLOAD_STATS_OPS_BIND :
694 1.1 christos LLOAD_STATS_OPS_OTHER;
695 1.1 christos
696 1.1 christos assert( b != NULL );
697 1.1 christos if ( op->o_res == LLOAD_OP_COMPLETED ) {
698 1.1 christos b->b_counters[stat_type].lc_ops_completed++;
699 1.1 christos } else {
700 1.1 christos b->b_counters[stat_type].lc_ops_failed++;
701 1.1 christos }
702 1.1 christos }
703