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