operation.c 24.7 KB
Newer Older
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 1998-2020 The OpenLDAP Foundation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */

#include "portable.h"

#include "lutil.h"
#include "slap.h"

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
ber_tag_t
slap_req2res( ber_tag_t tag )
{
    switch ( tag ) {
        case LDAP_REQ_ADD:
        case LDAP_REQ_BIND:
        case LDAP_REQ_COMPARE:
        case LDAP_REQ_EXTENDED:
        case LDAP_REQ_MODIFY:
        case LDAP_REQ_MODRDN:
            tag++;
            break;

        case LDAP_REQ_DELETE:
            tag = LDAP_RES_DELETE;
            break;

        case LDAP_REQ_ABANDON:
        case LDAP_REQ_UNBIND:
            tag = LBER_SEQUENCE;
            break;

        case LDAP_REQ_SEARCH:
            tag = LDAP_RES_SEARCH_RESULT;
            break;

        default:
            tag = LBER_SEQUENCE;
    }

    return tag;
}

const char *
slap_msgtype2str( ber_tag_t tag )
{
    switch ( tag ) {
        case LDAP_REQ_ABANDON: return "abandon request";
        case LDAP_REQ_ADD: return "add request";
        case LDAP_REQ_BIND: return "bind request";
        case LDAP_REQ_COMPARE: return "compare request";
        case LDAP_REQ_DELETE: return "delete request";
        case LDAP_REQ_EXTENDED: return "extended request";
        case LDAP_REQ_MODIFY: return "modify request";
        case LDAP_REQ_RENAME: return "rename request";
        case LDAP_REQ_SEARCH: return "search request";
        case LDAP_REQ_UNBIND: return "unbind request";

        case LDAP_RES_ADD: return "add result";
        case LDAP_RES_BIND: return "bind result";
        case LDAP_RES_COMPARE: return "compare result";
        case LDAP_RES_DELETE: return "delete result";
        case LDAP_RES_EXTENDED: return "extended result";
        case LDAP_RES_INTERMEDIATE: return "intermediate response";
        case LDAP_RES_MODIFY: return "modify result";
        case LDAP_RES_RENAME: return "rename result";
        case LDAP_RES_SEARCH_ENTRY: return "search-entry response";
        case LDAP_RES_SEARCH_REFERENCE: return "search-reference response";
        case LDAP_RES_SEARCH_RESULT: return "search result";
    }
    return "unknown message";
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
84
85
86
87
88
int
operation_client_cmp( const void *left, const void *right )
{
    const Operation *l = left, *r = right;

89
    assert( l->o_client_connid == r->o_client_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
90
91
92
93
94
95
96
97
98
99
    return ( l->o_client_msgid < r->o_client_msgid ) ?
            -1 :
            ( l->o_client_msgid > r->o_client_msgid );
}

int
operation_upstream_cmp( const void *left, const void *right )
{
    const Operation *l = left, *r = right;

100
    assert( l->o_upstream_connid == r->o_upstream_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
101
102
103
104
105
    return ( l->o_upstream_msgid < r->o_upstream_msgid ) ?
            -1 :
            ( l->o_upstream_msgid > r->o_upstream_msgid );
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * Free the operation, subject to there being noone else holding a reference
 * to it.
 *
 * Both operation_destroy_from_* functions are the same, two implementations
 * exist to cater for the fact that either side (client or upstream) might
 * decide to destroy it and each holds a different mutex.
 *
 * Due to the fact that we rely on mutexes on both connections which have a
 * different timespan from the operation, we have to take the following race
 * into account:
 *
 * Trigger
 * - both operation_destroy_from_client and operation_destroy_from_upstream
 *   are called at the same time (each holding its mutex), several times
 *   before one of them finishes
 * - either or both connections might have started the process of being
 *   destroyed
 *
 * We need to detect that the race has happened and only allow one of them to
 * free the operation (we use o_freeing != 0 to announce+detect that).
 *
 * In case the caller was in the process of destroying the connection and the
 * race had been won by the mirror caller, it will increment c_refcnt on its
 * connection and make sure to postpone the final step in
 * client/upstream_destroy(). Testing o_freeing for the mirror side's token
 * allows the winner to detect that it has been a party to the race and a token
 * in c_refcnt has been deposited on its behalf.
134
135
136
 *
 * Beware! This widget really touches all the mutexes we have and showcases the
 * issues with maintaining so many mutex ordering restrictions.
Ondřej Kuzník's avatar
Ondřej Kuzník committed
137
 */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
138
void
Ondřej Kuzník's avatar
Ondřej Kuzník committed
139
operation_destroy_from_client( Operation *op )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
140
{
141
    Connection *upstream, *client = op->o_client;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
142
    Backend *b = NULL;
143
    int race_state, detach_client = !client->c_live;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
144
145
146
147
148
149
150
151
152

    Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
            "op=%p attempting to release operation\n",
            op );

    /* 1. liveness/refcnt adjustment and test */
    op->o_client_refcnt -= op->o_client_live;
    op->o_client_live = 0;

153
    assert( op->o_client_refcnt <= client->c_refcnt );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
154
155
156
157
158
159
    if ( op->o_client_refcnt ) {
        Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
                "op=%p not dead yet\n",
                op );
        return;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
160

Ondřej Kuzník's avatar
Ondřej Kuzník committed
161
162
    /* 2. Remove from the operation map and TODO adjust the pending op count */
    tavl_delete( &client->c_ops, op, operation_client_cmp );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
163

Ondřej Kuzník's avatar
Ondřej Kuzník committed
164
165
    /* 3. Detect whether we entered a race to free op and indicate that to any
     * others */
166
    ldap_pvt_thread_mutex_lock( &op->o_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
167
168
    race_state = op->o_freeing;
    op->o_freeing |= SLAP_OP_FREEING_CLIENT;
169
170
171
172
    ldap_pvt_thread_mutex_unlock( &op->o_mutex );

    CONNECTION_UNLOCK_INCREF(client);

173
    if ( detach_client ) {
174
        ldap_pvt_thread_mutex_lock( &operation_mutex );
175
        op->o_client = NULL;
176
        ldap_pvt_thread_mutex_unlock( &operation_mutex );
177
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
178
179
180
181
182
183
184
185
186

    /* 4. If we lost the race, deal with it */
    if ( race_state ) {
        /*
         * We have raced to destroy op and the first one to lose on this side,
         * leave a refcnt token on client so we don't destroy it before the
         * other side has finished (it knows we did that when it examines
         * o_freeing again).
         */
187
        if ( !detach_client && race_state == SLAP_OP_FREEING_UPSTREAM ) {
188
            Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
189
190
191
                    "op=%p lost race, increased client refcnt connid=%lu "
                    "to refcnt=%d\n",
                    op, client->c_connid, client->c_refcnt );
192
193
194
195
196
197
198
            CONNECTION_LOCK(client);
        } else {
            Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
                    "op=%p lost race with another "
                    "operation_destroy_from_client\n",
                    op );
            CONNECTION_LOCK_DECREF(client);
199
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
200
201
202
203
204
        return;
    }

    /* 5. If we raced the upstream side and won, reclaim the token */
    ldap_pvt_thread_mutex_lock( &operation_mutex );
205
206
    upstream = op->o_upstream;
    if ( upstream ) {
207
        CONNECTION_LOCK(upstream);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
208
209
210
    }
    ldap_pvt_thread_mutex_unlock( &operation_mutex );

211
212
213
214
215
216
217
218
219
    ldap_pvt_thread_mutex_lock( &op->o_mutex );
    if ( upstream && ( op->o_freeing & SLAP_OP_FREEING_UPSTREAM ) ) {
        /*
         * We have raced to destroy op and won. To avoid freeing the connection
         * under us, a refcnt token has been left over for us on the upstream,
         * decref and see whether we are in charge of freeing it
         */
        upstream->c_refcnt--;
        Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
220
221
                "op=%p other side lost race with us, upstream connid=%lu\n",
                op, upstream->c_connid );
222
223
224
    }
    ldap_pvt_thread_mutex_unlock( &op->o_mutex );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
225
226
227
228
229
230
231
    /* 6. liveness/refcnt adjustment and test */
    op->o_upstream_refcnt -= op->o_upstream_live;
    op->o_upstream_live = 0;
    if ( op->o_upstream_refcnt ) {
        Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
                "op=%p other side still alive, refcnt=%d\n",
                op, op->o_upstream_refcnt );
232

Ondřej Kuzník's avatar
Ondřej Kuzník committed
233
        /* There must have been no race if op is still alive */
234
        ldap_pvt_thread_mutex_lock( &op->o_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
235
236
        op->o_freeing &= ~SLAP_OP_FREEING_CLIENT;
        assert( op->o_freeing == 0 );
237
238
239
        ldap_pvt_thread_mutex_unlock( &op->o_mutex );

        assert( upstream != NULL );
240
        UPSTREAM_UNLOCK_OR_DESTROY(upstream);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
241
242
        CONNECTION_LOCK_DECREF(client);
        return;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
243
244
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
245
    /* 7. Remove from the operation map and adjust the pending op count */
246
247
248
249
250
251
    if ( upstream ) {
        if ( tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ) ) {
            upstream->c_n_ops_executing--;
            b = (Backend *)upstream->c_private;
        }
        UPSTREAM_UNLOCK_OR_DESTROY(upstream);
252

253
254
255
256
257
        if ( b ) {
            ldap_pvt_thread_mutex_lock( &b->b_mutex );
            b->b_n_ops_executing--;
            ldap_pvt_thread_mutex_unlock( &b->b_mutex );
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
258
    }
259

Ondřej Kuzník's avatar
Ondřej Kuzník committed
260
261
262
263
264
265
    /* 8. Release the operation */
    Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
            "op=%p destroyed operation from client connid=%lu, "
            "client msgid=%d\n",
            op, op->o_client_connid, op->o_client_msgid );
    ber_free( op->o_ber, 1 );
266
    ldap_pvt_thread_mutex_destroy( &op->o_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
267
    ch_free( op );
268
269

    CONNECTION_LOCK_DECREF(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
270
271
272
273
274
275
276
277
}

/*
 * See operation_destroy_from_client.
 */
void
operation_destroy_from_upstream( Operation *op )
{
278
    Connection *client, *upstream = op->o_upstream;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
279
    Backend *b = NULL;
280
    int race_state, detach_upstream = !upstream->c_live;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
281
282
283
284
285
286
287
288
289

    Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
            "op=%p attempting to release operation\n",
            op );

    /* 1. liveness/refcnt adjustment and test */
    op->o_upstream_refcnt -= op->o_upstream_live;
    op->o_upstream_live = 0;

290
    assert( op->o_upstream_refcnt <= upstream->c_refcnt );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
291
292
293
294
295
296
297
298
299
300
301
302
303
    if ( op->o_upstream_refcnt ) {
        Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
                "op=%p not dead yet\n",
                op );
        return;
    }

    /* 2. Remove from the operation map and adjust the pending op count */
    if ( tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ) ) {
        upstream->c_n_ops_executing--;
        b = (Backend *)upstream->c_private;
    }

304
305
306
307
308
    ldap_pvt_thread_mutex_lock( &op->o_mutex );
    race_state = op->o_freeing;
    op->o_freeing |= SLAP_OP_FREEING_UPSTREAM;
    ldap_pvt_thread_mutex_unlock( &op->o_mutex );

309
310
    CONNECTION_UNLOCK_INCREF(upstream);

Ondřej Kuzník's avatar
Ondřej Kuzník committed
311
312
    /* 3. Detect whether we entered a race to free op */
    ldap_pvt_thread_mutex_lock( &operation_mutex );
313
314
315
    if ( detach_upstream ) {
        op->o_upstream = NULL;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
316
317
    ldap_pvt_thread_mutex_unlock( &operation_mutex );

318
319
320
321
322
323
    if ( b ) {
        ldap_pvt_thread_mutex_lock( &b->b_mutex );
        b->b_n_ops_executing--;
        ldap_pvt_thread_mutex_unlock( &b->b_mutex );
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
324
    /* 4. If we lost the race, deal with it */
325
    if ( race_state ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
326
327
328
329
330
331
        /*
         * We have raced to destroy op and the first one to lose on this side,
         * leave a refcnt token on upstream so we don't destroy it before the
         * other side has finished (it knows we did that when it examines
         * o_freeing again).
         */
332
        if ( !detach_upstream && race_state == SLAP_OP_FREEING_CLIENT ) {
333
            Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
334
335
336
                    "op=%p lost race, increased upstream refcnt connid=%lu "
                    "to refcnt=%d\n",
                    op, upstream->c_connid, upstream->c_refcnt );
337
338
339
340
341
342
343
344
            CONNECTION_LOCK(upstream);
        } else {
            Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
                    "op=%p lost race with another "
                    "operation_destroy_from_upstream\n",
                    op );
            CONNECTION_LOCK_DECREF(upstream);
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
345
        return;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
346
347
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
348
349
    /* 5. If we raced the client side and won, reclaim the token */
    ldap_pvt_thread_mutex_lock( &operation_mutex );
350
351
    client = op->o_client;
    if ( client ) {
352
        CONNECTION_LOCK(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
353
354
355
    }
    ldap_pvt_thread_mutex_unlock( &operation_mutex );

356
357
358
359
360
361
362
363
364
    ldap_pvt_thread_mutex_lock( &op->o_mutex );
    if ( client && ( op->o_freeing & SLAP_OP_FREEING_CLIENT ) ) {
        /*
         * We have raced to destroy op and won. To avoid freeing the connection
         * under us, a refcnt token has been left over for us on the client,
         * decref and see whether we are in charge of freeing it
         */
        client->c_refcnt--;
        Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
365
366
                "op=%p other side lost race with us, client connid=%lu\n",
                op, client->c_connid );
367
368
369
    }
    ldap_pvt_thread_mutex_unlock( &op->o_mutex );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
370
371
372
373
374
375
376
377
    /* 6. liveness/refcnt adjustment and test */
    op->o_client_refcnt -= op->o_client_live;
    op->o_client_live = 0;
    if ( op->o_client_refcnt ) {
        Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
                "op=%p other side still alive, refcnt=%d\n",
                op, op->o_client_refcnt );
        /* There must have been no race if op is still alive */
378
        ldap_pvt_thread_mutex_lock( &op->o_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
379
380
        op->o_freeing &= ~SLAP_OP_FREEING_UPSTREAM;
        assert( op->o_freeing == 0 );
381
382
383
        ldap_pvt_thread_mutex_unlock( &op->o_mutex );

        assert( client != NULL );
384
        CLIENT_UNLOCK_OR_DESTROY(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
385
386
387
388
389
        CONNECTION_LOCK_DECREF(upstream);
        return;
    }

    /* 7. Remove from the operation map and TODO adjust the pending op count */
390
391
392
393
    if ( client ) {
        tavl_delete( &client->c_ops, op, operation_client_cmp );
        CLIENT_UNLOCK_OR_DESTROY(client);
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
394
395
396
397
398
399
400

    /* 8. Release the operation */
    Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
            "op=%p destroyed operation from client connid=%lu, "
            "client msgid=%d\n",
            op, op->o_client_connid, op->o_client_msgid );
    ber_free( op->o_ber, 1 );
401
    ldap_pvt_thread_mutex_destroy( &op->o_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
402
    ch_free( op );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
403
404

    CONNECTION_LOCK_DECREF(upstream);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
405
406
}

407
408
409
/*
 * Entered holding c_mutex for now.
 */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
410
411
412
413
414
415
416
417
418
419
Operation *
operation_init( Connection *c, BerElement *ber )
{
    Operation *op;
    ber_tag_t tag;
    ber_len_t len;
    int rc;

    op = ch_calloc( 1, sizeof(Operation) );
    op->o_client = c;
420
    op->o_client_connid = c->c_connid;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
421
    op->o_ber = ber;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
422

423
424
    ldap_pvt_thread_mutex_init( &op->o_mutex );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
425
426
427
    op->o_client_live = op->o_client_refcnt = 1;
    op->o_upstream_live = op->o_upstream_refcnt = 1;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
428
429
430
431
432
433
434
435
    tag = ber_get_int( ber, &op->o_client_msgid );
    if ( tag != LDAP_TAG_MSGID ) {
        goto fail;
    }

    rc = tavl_insert( &c->c_ops, op, operation_client_cmp, avl_dup_error );
    if ( rc ) {
        Debug( LDAP_DEBUG_PACKETS, "operation_init: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
436
437
                "several operations with same msgid=%d in-flight "
                "from client connid=%lu\n",
438
                op->o_client_msgid, op->o_client_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
        goto fail;
    }

    tag = op->o_tag = ber_skip_element( ber, &op->o_request );
    switch ( tag ) {
        case LBER_ERROR:
            rc = -1;
            break;
    }
    if ( rc ) {
        tavl_delete( &c->c_ops, op, operation_client_cmp );
        goto fail;
    }

    tag = ber_peek_tag( ber, &len );
    if ( tag == LDAP_TAG_CONTROLS ) {
        ber_skip_element( ber, &op->o_ctrls );
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
458
    Debug( LDAP_DEBUG_TRACE, "operation_init: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
459
            "set up a new operation, %s with msgid=%d for client connid=%lu\n",
460
461
            slap_msgtype2str( op->o_tag ), op->o_client_msgid,
            op->o_client_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
462

463
    c->c_n_ops_executing++;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
464
465
466
467
468
469
470
    return op;

fail:
    ch_free( op );
    return NULL;
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
471
472
473
void
operation_abandon( Operation *op )
{
474
    Connection *c;
475
476
    BerElement *ber;
    Backend *b;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
477
478
    int rc;

479
480
    ldap_pvt_thread_mutex_lock( &operation_mutex );
    c = op->o_upstream;
481
482
    if ( !c ) {
        c = op->o_client;
483
        assert( c );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
484

485
        /* Caller should hold a reference on client */
486
        CONNECTION_LOCK(c);
487
        ldap_pvt_thread_mutex_unlock( &operation_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
488
        operation_destroy_from_client( op );
489
490
491
        CLIENT_UNLOCK_OR_DESTROY(c);
        return;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
492

493
    CONNECTION_LOCK(c);
494
    ldap_pvt_thread_mutex_unlock( &operation_mutex );
495
496
497
498
499
500
501
    if ( tavl_delete( &c->c_ops, op, operation_upstream_cmp ) == NULL ) {
        /* The operation has already been abandoned or finished */
        goto done;
    }
    c->c_n_ops_executing--;
    b = (Backend *)c->c_private;
    CONNECTION_UNLOCK_INCREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
502

503
504
505
    ldap_pvt_thread_mutex_lock( &b->b_mutex );
    b->b_n_ops_executing--;
    ldap_pvt_thread_mutex_unlock( &b->b_mutex );
506

507
    ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
508

509
510
511
512
513
514
515
516
517
    ber = c->c_pendingber;
    if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
        Debug( LDAP_DEBUG_ANY, "operation_abandon: "
                "ber_alloc failed\n" );
        ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
        CONNECTION_LOCK_DECREF(c);
        goto done;
    }
    c->c_pendingber = ber;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
518

519
520
521
    rc = ber_printf( ber, "t{titi}", LDAP_TAG_MESSAGE,
            LDAP_TAG_MSGID, c->c_next_msgid++,
            LDAP_REQ_ABANDON, op->o_upstream_msgid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
522

523
524
525
526
    if ( rc == -1 ) {
        ber_free( ber, 1 );
        c->c_pendingber = NULL;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
527

528
    ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
529

530
531
    if ( rc != -1 ) {
        upstream_write_cb( -1, 0, c );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
532
533
    }

534
    CONNECTION_LOCK_DECREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
535
done:
Ondřej Kuzník's avatar
Ondřej Kuzník committed
536
    operation_destroy_from_upstream( op );
537
    UPSTREAM_UNLOCK_OR_DESTROY(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
538
539
}

540
541
542
int
request_abandon( Connection *c, Operation *op )
{
543
    Operation *request, needle = { .o_client_connid = c->c_connid };
544
    int rc = LDAP_SUCCESS;
545

546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
    /* parse two's complement integer */
    if ( !BER_BVISEMPTY( &op->o_request ) ) {
        unsigned char *buf = (unsigned char *)op->o_request.bv_val;
        ber_len_t i;
        ber_int_t netnum = buf[0] & 0xff;

        /* sign extend */
        netnum = ( netnum ^ 0x80 ) - 0x80;

        /* shift in the bytes */
        for ( i = 1; i < op->o_request.bv_len; i++ ) {
            netnum = ( netnum << 8 ) | buf[i];
        }

        needle.o_client_msgid = netnum;
561
562
563
564
    }

    request = tavl_find( c->c_ops, &needle, operation_client_cmp );
    if ( !request ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
565
566
567
568
        Debug( LDAP_DEBUG_TRACE, "request_abandon: "
                "connid=%lu msgid=%d requests abandon of an operation "
                "msgid=%d not being processed anymore\n",
                c->c_connid, op->o_client_msgid, needle.o_client_msgid );
569
570
        goto done;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
571
572
573
574
    Debug( LDAP_DEBUG_TRACE, "request_abandon: "
            "connid=%lu msgid=%d abandoning %s msgid=%d\n",
            c->c_connid, op->o_client_msgid, slap_msgtype2str( request->o_tag ),
            needle.o_client_msgid );
575
576
577
578
579
580

    CONNECTION_UNLOCK_INCREF(c);
    operation_abandon( request );
    CONNECTION_LOCK_DECREF(c);

done:
Ondřej Kuzník's avatar
Ondřej Kuzník committed
581
    operation_destroy_from_client( op );
582
583
584
    return rc;
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
585
void
586
587
588
589
590
operation_send_reject(
        Operation *op,
        int result,
        const char *msg,
        int send_anyway )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
591
{
592
    Connection *c;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
593
594
595
    BerElement *ber;
    int found;

596
    Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
597
            "rejecting %s from client connid=%lu with message: \"%s\"\n",
598
            slap_msgtype2str( op->o_tag ), op->o_client_connid, msg );
599

600
601
602
603
604
605
606
607
    ldap_pvt_thread_mutex_lock( &operation_mutex );
    c = op->o_client;
    if ( !c ) {
        c = op->o_upstream;
        /* One of the connections has initiated this and keeps a reference, if
         * client is dead, it must have been the upstream */
        assert( c );
        CONNECTION_LOCK(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
608
609
610
        Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
                "not sending msgid=%d, client connid=%lu is dead\n",
                op->o_client_msgid, op->o_client_connid );
611
612
613
614
615
        ldap_pvt_thread_mutex_unlock( &operation_mutex );
        operation_destroy_from_upstream( op );
        UPSTREAM_UNLOCK_OR_DESTROY(c);
        return;
    }
616
    CONNECTION_LOCK(c);
617
618
    ldap_pvt_thread_mutex_unlock( &operation_mutex );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
619
    found = ( tavl_delete( &c->c_ops, op, operation_client_cmp ) == op );
620
    if ( !found && !send_anyway ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
621
622
623
624
        Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
                "msgid=%d not scheduled for client connid=%lu anymore, "
                "not sending\n",
                op->o_client_msgid, c->c_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
625
        goto done;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
626
627
    }

628
    CONNECTION_UNLOCK_INCREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
629
630
631
632
633
    ldap_pvt_thread_mutex_lock( &c->c_io_mutex );

    ber = c->c_pendingber;
    if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
        ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
634
635
636
        CONNECTION_LOCK_DECREF(c);
        operation_destroy_from_client( op );
        CLIENT_DESTROY(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
637
638
639
640
641
642
643
644
645
646
647
648
        return;
    }
    c->c_pendingber = ber;

    ber_printf( ber, "t{tit{ess}}", LDAP_TAG_MESSAGE,
            LDAP_TAG_MSGID, op->o_client_msgid,
            slap_req2res( op->o_tag ), result, "", msg );

    ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );

    client_write_cb( -1, 0, c );

649
    CONNECTION_LOCK_DECREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
650
651
done:
    operation_destroy_from_client( op );
652
    CLIENT_UNLOCK_OR_DESTROY(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
653
654
}

655
656
657
658
659
660
661
/*
 * Upstream is shutting down, signal the client if necessary, but we have to
 * call operation_destroy_from_upstream ourselves to detach upstream from the
 * op.
 *
 * Only called from upstream_destroy.
 */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
662
663
664
void
operation_lost_upstream( Operation *op )
{
665
666
667
668
669
670
    Connection *c = op->o_upstream;
    CONNECTION_LOCK(c);
    op->o_upstream_refcnt++;
    /* Matching the op reference on the connection as well */
    CONNECTION_UNLOCK_INCREF(c);

Ondřej Kuzník's avatar
Ondřej Kuzník committed
671
    operation_send_reject( op, LDAP_UNAVAILABLE,
672
            "connection to the remote server has been severed", 0 );
673
674
675
676
677

    CONNECTION_LOCK_DECREF(c);
    op->o_upstream_refcnt--;
    operation_destroy_from_upstream( op );
    CONNECTION_UNLOCK(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
678
679
}

680
681
int
request_process( Connection *client, Operation *op )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
682
683
{
    BerElement *output;
684
    Connection *upstream;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
685
    ber_int_t msgid;
686
687
    int rc = LDAP_SUCCESS;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
688
    op->o_client_refcnt++;
689
    CONNECTION_UNLOCK_INCREF(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
690

Ondřej Kuzník's avatar
Ondřej Kuzník committed
691
692
    upstream = backend_select( op );
    if ( !upstream ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
693
        Debug( LDAP_DEBUG_STATS, "request_process: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
694
695
                "connid=%lu, msgid=%d no available connection found\n",
                op->o_client_connid, op->o_client_msgid );
696
697
698

        operation_send_reject(
                op, LDAP_UNAVAILABLE, "no connections available", 1 );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
699
700
        goto fail;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
701
    op->o_upstream = upstream;
702
    op->o_upstream_connid = upstream->c_connid;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
703

Ondřej Kuzník's avatar
Ondřej Kuzník committed
704
    output = upstream->c_pendingber;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
705
    if ( output == NULL && (output = ber_alloc()) == NULL ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
706
707
        goto fail;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
708
    upstream->c_pendingber = output;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
709

710
    CONNECTION_LOCK_DECREF(upstream);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
711
712
713
    op->o_upstream_msgid = msgid = upstream->c_next_msgid++;
    rc = tavl_insert(
            &upstream->c_ops, op, operation_upstream_cmp, avl_dup_error );
714
    CONNECTION_UNLOCK_INCREF(upstream);
715
716
717
718
719
720

    Debug( LDAP_DEBUG_TRACE, "request_process: "
            "client connid=%lu added %s msgid=%d to upstream connid=%lu as "
            "msgid=%d\n",
            op->o_client_connid, slap_msgtype2str( op->o_tag ),
            op->o_client_msgid, op->o_upstream_connid, op->o_upstream_msgid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
721
722
    assert( rc == LDAP_SUCCESS );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
723
724
    if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) &&
            client->c_type != SLAP_C_PRIVILEGED ) {
725
        CONNECTION_LOCK_DECREF(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
726
727
        Debug( LDAP_DEBUG_TRACE, "request_process: "
                "proxying identity %s to upstream\n",
Ondřej Kuzník's avatar
Ondřej Kuzník committed
728
                client->c_auth.bv_val );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
729
730
731
732
        ber_printf( output, "t{titOt{{sbO}" /* "}}" */, LDAP_TAG_MESSAGE,
                LDAP_TAG_MSGID, msgid,
                op->o_tag, &op->o_request,
                LDAP_TAG_CONTROLS,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
733
                LDAP_CONTROL_PROXY_AUTHZ, 1, &client->c_auth );
734
        CONNECTION_UNLOCK_INCREF(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
735
736

        if ( !BER_BVISNULL( &op->o_ctrls ) ) {
737
            ber_write( output, op->o_ctrls.bv_val, op->o_ctrls.bv_len, 0 );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
738
        }
739

Ondřej Kuzník's avatar
Ondřej Kuzník committed
740
741
742
743
744
745
746
        ber_printf( output, /* "{{" */ "}}" );
    } else {
        ber_printf( output, "t{titOtO}", LDAP_TAG_MESSAGE,
                LDAP_TAG_MSGID, msgid,
                op->o_tag, &op->o_request,
                LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
747
    ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
748

Ondřej Kuzník's avatar
Ondřej Kuzník committed
749
    upstream_write_cb( -1, 0, upstream );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
750

751
752
753
    CONNECTION_LOCK_DECREF(upstream);
    UPSTREAM_UNLOCK_OR_DESTROY(upstream);

754
    CONNECTION_LOCK_DECREF(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
755
756
757
    if ( !--op->o_client_refcnt ) {
        operation_destroy_from_client( op );
    }
758
    return rc;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
759

Ondřej Kuzník's avatar
Ondřej Kuzník committed
760
fail:
Ondřej Kuzník's avatar
Ondřej Kuzník committed
761
762
    if ( upstream ) {
        ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
763
        CONNECTION_LOCK_DECREF(upstream);
764
        upstream->c_n_ops_executing--;
765
        UPSTREAM_UNLOCK_OR_DESTROY(upstream);
766
        operation_send_reject( op, LDAP_OTHER, "internal error", 0 );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
767
    }
768
    CONNECTION_LOCK_DECREF(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
769
770
    op->o_client_refcnt--;
    operation_destroy_from_client( op );
771
772
    CLIENT_UNLOCK_OR_DESTROY(client);
    return -1;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
773
}