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