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