Home | History | Annotate | Line # | Download | only in lloadd
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