varnish-cache/bin/varnishd/cache/cache_ban.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2015 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 */
30
31
#include "config.h"
32
33
#include <pcre.h>
34
35
#include "cache_varnishd.h"
36
#include "cache_ban.h"
37
#include "cache_objhead.h"
38
39
#include "vcli_serve.h"
40
#include "vend.h"
41
#include "vmb.h"
42
43
struct lock ban_mtx;
44
int ban_shutdown;
45
struct banhead_s ban_head = VTAILQ_HEAD_INITIALIZER(ban_head);
46
struct ban * volatile ban_start;
47
48
static pthread_t ban_thread;
49
static int ban_holds;
50
uint64_t bans_persisted_bytes;
51
uint64_t bans_persisted_fragmentation;
52
53
struct ban_test {
54
        uint8_t                 oper;
55
        uint8_t                 arg1;
56
        const char              *arg1_spec;
57
        const char              *arg2;
58
        const void              *arg2_spec;
59
};
60
61
/*--------------------------------------------------------------------
62
 * Storage handling of bans
63
 */
64
65
static struct ban *
66 26
ban_alloc(void)
67
{
68
        struct ban *b;
69
70 26
        ALLOC_OBJ(b, BAN_MAGIC);
71 26
        if (b != NULL)
72 26
                VTAILQ_INIT(&b->objcore);
73 26
        return (b);
74
}
75
76
void
77 50
BAN_Free(struct ban *b)
78
{
79
80 50
        CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
81 50
        AZ(b->refcount);
82 50
        assert(VTAILQ_EMPTY(&b->objcore));
83
84 50
        if (b->spec != NULL)
85 50
                free(b->spec);
86 50
        FREE_OBJ(b);
87 50
}
88
89
/*--------------------------------------------------------------------
90
 * Get/release holds which prevent the ban_lurker from starting.
91
 * Holds are held while stevedores load zombie objects.
92
 */
93
94
void
95 38
BAN_Hold(void)
96
{
97
98 38
        Lck_Lock(&ban_mtx);
99
        /* Once holds are released, we allow no more */
100 38
        assert(ban_holds > 0);
101 38
        ban_holds++;
102 38
        Lck_Unlock(&ban_mtx);
103 38
}
104
105
void
106 723
BAN_Release(void)
107
{
108
109 723
        Lck_Lock(&ban_mtx);
110 723
        assert(ban_holds > 0);
111 723
        ban_holds--;
112 723
        Lck_Unlock(&ban_mtx);
113 723
        if (ban_holds == 0)
114 685
                WRK_BgThread(&ban_thread, "ban-lurker", ban_lurker, NULL);
115 723
}
116
117
/*--------------------------------------------------------------------
118
 * Extract time and length from ban-spec
119
 */
120
121
double
122 376
ban_time(const uint8_t *banspec)
123
{
124
        double t;
125
126
        assert(sizeof t == (BANS_LENGTH - BANS_TIMESTAMP));
127 376
        memcpy(&t, banspec, sizeof t);
128 376
        return (t);
129
}
130
131
unsigned
132 3912
ban_len(const uint8_t *banspec)
133
{
134
        unsigned u;
135
136 3912
        u = vbe32dec(banspec + BANS_LENGTH);
137 3912
        return (u);
138
}
139
140
int
141 92
ban_equal(const uint8_t *bs1, const uint8_t *bs2)
142
{
143
        unsigned u;
144
145
        /*
146
         * Compare two ban-strings.
147
         */
148 92
        u = vbe32dec(bs1 + BANS_LENGTH);
149 92
        if (u != vbe32dec(bs2 + BANS_LENGTH))
150 36
                return (0);
151 56
        return (!memcmp(bs1 + BANS_LENGTH, bs2 + BANS_LENGTH, u - BANS_LENGTH));
152
}
153
154
void
155 727
ban_mark_completed(struct ban *b, struct VSC_main *stats)
156
{
157
        unsigned ln;
158
159 727
        CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
160 727
        Lck_AssertHeld(&ban_mtx);
161
162 727
        AN(b->spec);
163 727
        if (!(b->flags & BANS_FLAG_COMPLETED)) {
164 727
                ln = ban_len(b->spec);
165 727
                b->flags |= BANS_FLAG_COMPLETED;
166 727
                b->spec[BANS_FLAGS] |= BANS_FLAG_COMPLETED;
167 727
                VWMB();
168 727
                vbe32enc(b->spec + BANS_LENGTH, BANS_HEAD_LEN);
169 727
                stats->bans_completed++;
170 727
                bans_persisted_fragmentation += ln - ban_len(b->spec);
171
                /*
172
                 * XXX absolute update of gauges - may be inaccurate for
173
                 * Pool_Sumstat race
174
                 */
175 727
                VSC_C_main->bans_persisted_fragmentation =
176
                    bans_persisted_fragmentation;
177
        }
178 727
}
179
180
/*--------------------------------------------------------------------
181
 * Access a lump of bytes in a ban test spec
182
 */
183
184
static const void *
185 172
ban_get_lump(const uint8_t **bs)
186
{
187
        const void *r;
188
        unsigned ln;
189
190 1095
        while (**bs == 0xff)
191 751
                *bs += 1;
192 172
        ln = vbe32dec(*bs);
193 172
        *bs += PRNDUP(sizeof(uint32_t));
194 172
        assert(PAOK(*bs));
195 172
        r = (const void*)*bs;
196 172
        *bs += ln;
197 172
        return (r);
198
}
199
200
/*--------------------------------------------------------------------
201
 * Pick a test apart from a spec string
202
 */
203
204
static void
205 131
ban_iter(const uint8_t **bs, struct ban_test *bt)
206
{
207
208 131
        memset(bt, 0, sizeof *bt);
209 131
        bt->arg1 = *(*bs)++;
210 131
        if (bt->arg1 == BANS_ARG_REQHTTP || bt->arg1 == BANS_ARG_OBJHTTP) {
211 62
                bt->arg1_spec = (const char *)*bs;
212 62
                (*bs) += (*bs)[0] + 2;
213
        }
214 131
        bt->arg2 = ban_get_lump(bs);
215 131
        bt->oper = *(*bs)++;
216 131
        if (bt->oper == BANS_OPER_MATCH || bt->oper == BANS_OPER_NMATCH)
217 41
                bt->arg2_spec = ban_get_lump(bs);
218 131
}
219
220
/*--------------------------------------------------------------------
221
 * A new object is created, grab a reference to the newest ban
222
 */
223
224
void
225 1082
BAN_NewObjCore(struct objcore *oc)
226
{
227
228 1082
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
229 1082
        AZ(oc->ban);
230 1082
        AN(oc->objhead);
231 1082
        Lck_Lock(&ban_mtx);
232 1082
        oc->ban = ban_start;
233 1082
        ban_start->refcount++;
234 1082
        VTAILQ_INSERT_TAIL(&ban_start->objcore, oc, ban_list);
235 1082
        Lck_Unlock(&ban_mtx);
236 1082
}
237
238
/*--------------------------------------------------------------------
239
 * An object is destroyed, release its ban reference
240
 */
241
242
void
243 1099
BAN_DestroyObj(struct objcore *oc)
244
{
245
246 1099
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
247 1099
        Lck_Lock(&ban_mtx);
248 1099
        CHECK_OBJ_ORNULL(oc->ban, BAN_MAGIC);
249 1099
        if (oc->ban != NULL) {
250 235
                assert(oc->ban->refcount > 0);
251 235
                oc->ban->refcount--;
252 235
                VTAILQ_REMOVE(&oc->ban->objcore, oc, ban_list);
253 235
                oc->ban = NULL;
254
        }
255 1099
        Lck_Unlock(&ban_mtx);
256 1099
}
257
258
/*--------------------------------------------------------------------
259
 * Find a ban based on a timestamp.
260
 * Assume we have a BAN_Hold, so list traversal is safe.
261
 */
262
263
struct ban *
264 16
BAN_FindBan(double t0)
265
{
266
        struct ban *b;
267
        double t1;
268
269 16
        assert(ban_holds > 0);
270 44
        VTAILQ_FOREACH(b, &ban_head, list) {
271 44
                t1 = ban_time(b->spec);
272 44
                if (t1 == t0)
273 16
                        return (b);
274 28
                if (t1 < t0)
275 0
                        break;
276
        }
277 0
        return (NULL);
278
}
279
280
/*--------------------------------------------------------------------
281
 * Grab a reference to a ban and associate the objcore with that ban.
282
 * Assume we have a BAN_Hold, so list traversal is safe.
283
 */
284
285
void
286 16
BAN_RefBan(struct objcore *oc, struct ban *b)
287
{
288
289 16
        Lck_Lock(&ban_mtx);
290 16
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
291 16
        AZ(oc->ban);
292 16
        CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
293 16
        assert(ban_holds > 0);
294 16
        b->refcount++;
295 16
        VTAILQ_INSERT_TAIL(&b->objcore, oc, ban_list);
296 16
        oc->ban = b;
297 16
        Lck_Unlock(&ban_mtx);
298 16
}
299
300
/*--------------------------------------------------------------------
301
 * Compile a full ban list and export this area to the stevedores for
302
 * persistence.
303
 */
304
305
static void
306 1364
ban_export(void)
307
{
308
        struct ban *b;
309
        struct vsb *vsb;
310
        unsigned ln;
311
312 1364
        Lck_AssertHeld(&ban_mtx);
313 1364
        ln = bans_persisted_bytes - bans_persisted_fragmentation;
314 1364
        vsb = VSB_new_auto();
315 1364
        AN(vsb);
316 2784
        VTAILQ_FOREACH_REVERSE(b, &ban_head, banhead_s, list)
317 1420
                AZ(VSB_bcat(vsb, b->spec, ban_len(b->spec)));
318 1364
        AZ(VSB_finish(vsb));
319 1364
        assert(VSB_len(vsb) == ln);
320 1364
        STV_BanExport((const uint8_t *)VSB_data(vsb), VSB_len(vsb));
321 1364
        VSB_destroy(&vsb);
322
        /*
323
         * XXX absolute update of gauges - may be inaccurate for Pool_Sumstat
324
         * race
325
         */
326 2728
        VSC_C_main->bans_persisted_bytes =
327 1364
            bans_persisted_bytes = ln;
328 2728
        VSC_C_main->bans_persisted_fragmentation =
329 1364
            bans_persisted_fragmentation = 0;
330 1364
}
331
332
/*
333
 * For both of these we do a full export on info failure to remove
334
 * holes in the exported list.
335
 * XXX: we should keep track of the size of holes in the last exported list
336
 * XXX: check if the ban_export should be batched in ban_cleantail
337
 */
338
void
339 739
ban_info_new(const uint8_t *ban, unsigned len)
340
{
341
        /* XXX martin pls review if ban_mtx needs to be held */
342 739
        Lck_AssertHeld(&ban_mtx);
343 739
        if (STV_BanInfoNew(ban, len))
344 0
                ban_export();
345 739
}
346
347
void
348 50
ban_info_drop(const uint8_t *ban, unsigned len)
349
{
350
        /* XXX martin pls review if ban_mtx needs to be held */
351 50
        Lck_AssertHeld(&ban_mtx);
352 50
        if (STV_BanInfoDrop(ban, len))
353 0
                ban_export();
354 50
}
355
356
/*--------------------------------------------------------------------
357
 * Put a skeleton ban in the list, unless there is an identical,
358
 * time & condition, ban already in place.
359
 *
360
 * If a newer ban has same condition, mark the inserted ban COMPLETED,
361
 * also mark any older bans, with the same condition COMPLETED.
362
 */
363
364
static void
365 34
ban_reload(const uint8_t *ban, unsigned len)
366
{
367
        struct ban *b, *b2;
368 34
        int duplicate = 0;
369 34
        double t0, t1, t2 = 9e99;
370 34
        struct VSC_main *stats = VSC_C_main;    // XXX accurate?
371 34
        ASSERT_CLI();
372 34
        Lck_AssertHeld(&ban_mtx);
373
374 34
        t0 = ban_time(ban);
375 34
        assert(len == ban_len(ban));
376
377 74
        VTAILQ_FOREACH(b, &ban_head, list) {
378 58
                t1 = ban_time(b->spec);
379 58
                assert(t1 < t2);
380 58
                t2 = t1;
381 58
                if (t1 == t0)
382 8
                        return;
383 50
                if (t1 < t0)
384 10
                        break;
385 40
                if (ban_equal(b->spec, ban))
386 24
                        duplicate = 1;
387
        }
388
389 26
        stats->bans++;
390 26
        stats->bans_added++;
391
392 26
        b2 = ban_alloc();
393 26
        AN(b2);
394 26
        b2->spec = malloc(len);
395 26
        AN(b2->spec);
396 26
        memcpy(b2->spec, ban, len);
397 26
        if (ban[BANS_FLAGS] & BANS_FLAG_REQ) {
398 5
                stats->bans_req++;
399 5
                b2->flags |= BANS_FLAG_REQ;
400
        }
401 26
        if (duplicate)
402 18
                stats->bans_dups++;
403 26
        if (duplicate || (ban[BANS_FLAGS] & BANS_FLAG_COMPLETED))
404 19
                ban_mark_completed(b2, stats);
405 26
        if (b == NULL)
406 16
                VTAILQ_INSERT_TAIL(&ban_head, b2, list);
407
        else
408 10
                VTAILQ_INSERT_BEFORE(b, b2, list);
409 26
        bans_persisted_bytes += len;
410
        /*
411
         * XXX absolute update of gauges - may be inaccurate for Pool_Sumstat
412
         * race
413
         */
414 26
        VSC_C_main->bans_persisted_bytes = bans_persisted_bytes;
415
416
        /* Hunt down older duplicates */
417 41
        for (b = VTAILQ_NEXT(b2, list); b != NULL; b = VTAILQ_NEXT(b, list)) {
418 15
                if (b->flags & BANS_FLAG_COMPLETED)
419 14
                        continue;
420 1
                if (ban_equal(b->spec, ban)) {
421 0
                        ban_mark_completed(b, stats);
422 0
                        stats->bans_dups++;
423
                }
424
        }
425
}
426
427
/*--------------------------------------------------------------------
428
 * Reload a series of persisted ban specs
429
 */
430
431
void
432 38
BAN_Reload(const uint8_t *ptr, unsigned len)
433
{
434
        const uint8_t *pe;
435
        unsigned l;
436
437 38
        AZ(ban_shutdown);
438 38
        pe = ptr + len;
439 38
        Lck_Lock(&ban_mtx);
440 110
        while (ptr < pe) {
441
                /* XXX: This can be optimized by traversing the live
442
                 * ban list together with the reload list (combining
443
                 * the loops in BAN_Reload and ban_reload). */
444 34
                l = ban_len(ptr);
445 34
                assert(ptr + l <= pe);
446 34
                ban_reload(ptr, l);
447 34
                ptr += l;
448
        }
449 38
        Lck_Unlock(&ban_mtx);
450 38
}
451
452
/*--------------------------------------------------------------------
453
 * Get a bans timestamp
454
 */
455
456
double
457 69
BAN_Time(const struct ban *b)
458
{
459
460 69
        if (b == NULL)
461 22
                return (0.0);
462
463 47
        CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
464 47
        return (ban_time(b->spec));
465
}
466
467
/*--------------------------------------------------------------------
468
 * Evaluate ban-spec
469
 */
470
471
int
472 52
ban_evaluate(struct worker *wrk, const uint8_t *bs, struct objcore *oc,
473
    const struct http *reqhttp, unsigned *tests)
474
{
475
        struct ban_test bt;
476
        const uint8_t *be;
477
        const char *p;
478
        const char *arg1;
479
480 52
        be = bs + ban_len(bs);
481 52
        bs += BANS_HEAD_LEN;
482 145
        while (bs < be) {
483 56
                (*tests)++;
484 56
                ban_iter(&bs, &bt);
485 56
                arg1 = NULL;
486 56
                switch (bt.arg1) {
487
                case BANS_ARG_URL:
488 30
                        AN(reqhttp);
489 30
                        arg1 = reqhttp->hd[HTTP_HDR_URL].b;
490 30
                        break;
491
                case BANS_ARG_REQHTTP:
492 4
                        AN(reqhttp);
493 4
                        (void)http_GetHdr(reqhttp, bt.arg1_spec, &p);
494 4
                        arg1 = p;
495 4
                        break;
496
                case BANS_ARG_OBJHTTP:
497 21
                        arg1 = HTTP_GetHdrPack(wrk, oc, bt.arg1_spec);
498 21
                        break;
499
                case BANS_ARG_OBJSTATUS:
500 1
                        arg1 = HTTP_GetHdrPack(wrk, oc, H__Status);
501 1
                        break;
502
                default:
503 0
                        WRONG("Wrong BAN_ARG code");
504
                }
505
506 56
                switch (bt.oper) {
507
                case BANS_OPER_EQ:
508 27
                        if (arg1 == NULL || strcmp(arg1, bt.arg2))
509 11
                                return (0);
510 16
                        break;
511
                case BANS_OPER_NEQ:
512 3
                        if (arg1 != NULL && !strcmp(arg1, bt.arg2))
513 2
                                return (0);
514 1
                        break;
515
                case BANS_OPER_MATCH:
516 52
                        if (arg1 == NULL ||
517 26
                            pcre_exec(bt.arg2_spec, NULL, arg1, strlen(arg1),
518
                            0, 0, NULL, 0) < 0)
519 2
                                return (0);
520 24
                        break;
521
                case BANS_OPER_NMATCH:
522 0
                        if (arg1 != NULL &&
523 0
                            pcre_exec(bt.arg2_spec, NULL, arg1, strlen(arg1),
524
                            0, 0, NULL, 0) >= 0)
525 0
                                return (0);
526 0
                        break;
527
                default:
528 0
                        WRONG("Wrong BAN_OPER code");
529
                }
530
        }
531 37
        return (1);
532
}
533
534
/*--------------------------------------------------------------------
535
 * Check an object against all applicable bans
536
 *
537
 * Return:
538
 *      -1 not all bans checked, but none of the checked matched
539
 *              Only if !has_req
540
 *      0 No bans matched, object moved to ban_start.
541
 *      1 Ban matched, object removed from ban list.
542
 */
543
544
int
545 3333
BAN_CheckObject(struct worker *wrk, struct objcore *oc, struct req *req)
546
{
547
        struct ban *b;
548
        struct vsl_log *vsl;
549
        struct ban *b0, *bn;
550
        unsigned tests;
551
        struct VSC_main *stats;
552
553 3333
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
554 3333
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
555 3333
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
556 3333
        Lck_AssertHeld(&oc->objhead->mtx);
557 3333
        assert(oc->refcnt > 0);
558
559 3333
        vsl = req->vsl;
560 3333
        stats = wrk->stats;
561
562 3333
        CHECK_OBJ_NOTNULL(oc->ban, BAN_MAGIC);
563
564
        /* First do an optimistic unlocked check */
565 3333
        b0 = ban_start;
566 3333
        CHECK_OBJ_NOTNULL(b0, BAN_MAGIC);
567
568 3333
        if (b0 == oc->ban)
569 3285
                return (0);
570
571
        /* If that fails, make a safe check */
572 48
        Lck_Lock(&ban_mtx);
573 48
        b0 = ban_start;
574 48
        bn = oc->ban;
575 48
        if (b0 != bn)
576 48
                bn->refcount++;
577 48
        Lck_Unlock(&ban_mtx);
578
579 48
        AN(bn);
580
581 48
        if (b0 == bn)
582 0
                return (0);
583
584 48
        AN(b0);
585 48
        AN(bn);
586
587
        /*
588
         * This loop is safe without locks, because we know we hold
589
         * a refcount on a ban somewhere in the list and we do not
590
         * inspect the list past that ban.
591
         */
592 48
        tests = 0;
593 77
        for (b = b0; b != bn; b = VTAILQ_NEXT(b, list)) {
594 58
                CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
595 58
                if (b->flags & BANS_FLAG_COMPLETED)
596 18
                        continue;
597 40
                if (ban_evaluate(wrk, b->spec, oc, req->http, &tests))
598 29
                        break;
599
        }
600
601 48
        Lck_Lock(&ban_mtx);
602 48
        bn->refcount--;
603 48
        stats->bans_tested++;
604 48
        stats->bans_tests_tested += tests;
605
606 48
        if (b == bn) {
607
                /* not banned */
608 19
                oc->ban->refcount--;
609 19
                VTAILQ_REMOVE(&oc->ban->objcore, oc, ban_list);
610 19
                VTAILQ_INSERT_TAIL(&b0->objcore, oc, ban_list);
611 19
                b0->refcount++;
612 19
                oc->ban = b0;
613 19
                b = NULL;
614
        }
615
616 48
        if (VTAILQ_LAST(&ban_head, banhead_s)->refcount == 0)
617 20
                ban_kick_lurker();
618
619 48
        Lck_Unlock(&ban_mtx);
620
621 48
        if (b == NULL) {
622
                /* not banned */
623 19
                ObjSendEvent(wrk, oc, OEV_BANCHG);
624 19
                return (0);
625
        } else {
626 29
                VSLb(vsl, SLT_ExpBan, "%u banned lookup", ObjGetXID(wrk, oc));
627 29
                stats->bans_obj_killed++;
628 29
                return (1);
629
        }
630
}
631
632
/*--------------------------------------------------------------------
633
 * CLI functions to add bans
634
 */
635
636
static void v_matchproto_(cli_func_t)
637 41
ccf_ban(struct cli *cli, const char * const *av, void *priv)
638
{
639
        int narg, i;
640
        struct ban_proto *bp;
641 41
        const char *err = NULL;
642
643
        (void)priv;
644
645
        /* First do some cheap checks on the arguments */
646 185
        for (narg = 0; av[narg + 2] != NULL; narg++)
647 144
                continue;
648 41
        if ((narg % 4) != 3) {
649 2
                VCLI_Out(cli, "Wrong number of arguments");
650 2
                VCLI_SetResult(cli, CLIS_PARAM);
651 2
                return;
652
        }
653 42
        for (i = 3; i < narg; i += 4) {
654 4
                if (strcmp(av[i + 2], "&&")) {
655 1
                        VCLI_Out(cli, "Found \"%s\" expected &&", av[i + 2]);
656 1
                        VCLI_SetResult(cli, CLIS_PARAM);
657 1
                        return;
658
                }
659
        }
660
661 38
        bp = BAN_Build();
662 38
        if (bp == NULL) {
663 0
                VCLI_Out(cli, "Out of Memory");
664 0
                VCLI_SetResult(cli, CLIS_CANT);
665 0
                return;
666
        }
667 78
        for (i = 0; i < narg; i += 4) {
668 41
                err = BAN_AddTest(bp, av[i + 2], av[i + 3], av[i + 4]);
669 41
                if (err)
670 1
                        break;
671
        }
672
673 38
        if (err == NULL) {
674
                // XXX racy - grab wstat lock?
675 37
                err = BAN_Commit(bp, VSC_C_main);
676
        }
677
678 38
        if (err != NULL) {
679 1
                VCLI_Out(cli, "%s", err);
680 1
                BAN_Abandon(bp);
681 1
                VCLI_SetResult(cli, CLIS_PARAM);
682
        }
683
}
684
685
static void
686 133
ban_render(struct cli *cli, const uint8_t *bs)
687
{
688
        struct ban_test bt;
689
        const uint8_t *be;
690
691 133
        be = bs + ban_len(bs);
692 133
        bs += BANS_HEAD_LEN;
693 341
        while (bs < be) {
694 75
                ban_iter(&bs, &bt);
695 75
                switch (bt.arg1) {
696
                case BANS_ARG_URL:
697 36
                        VCLI_Out(cli, "req.url");
698 36
                        break;
699
                case BANS_ARG_REQHTTP:
700 20
                        VCLI_Out(cli, "req.http.%.*s",
701 20
                            bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
702 10
                        break;
703
                case BANS_ARG_OBJHTTP:
704 54
                        VCLI_Out(cli, "obj.http.%.*s",
705 54
                            bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
706 27
                        break;
707
                case BANS_ARG_OBJSTATUS:
708 2
                        VCLI_Out(cli, "obj.status");
709 2
                        break;
710
                default:
711 0
                        WRONG("Wrong BANS_ARG");
712
                }
713 75
                switch (bt.oper) {
714 52
                case BANS_OPER_EQ:      VCLI_Out(cli, " == "); break;
715 8
                case BANS_OPER_NEQ:     VCLI_Out(cli, " != "); break;
716 15
                case BANS_OPER_MATCH:   VCLI_Out(cli, " ~ "); break;
717 0
                case BANS_OPER_NMATCH:  VCLI_Out(cli, " !~ "); break;
718
                default:
719 0
                        WRONG("Wrong BANS_OPER");
720
                }
721 75
                VCLI_Out(cli, "%s", bt.arg2);
722 75
                if (bs < be)
723 5
                        VCLI_Out(cli, " && ");
724
        }
725 133
}
726
727
static void v_matchproto_(cli_func_t)
728 49
ccf_ban_list(struct cli *cli, const char * const *av, void *priv)
729
{
730
        struct ban *b, *bl;
731
        int64_t o;
732
733
        (void)av;
734
        (void)priv;
735
736
        /* Get a reference so we are safe to traverse the list */
737 49
        Lck_Lock(&ban_mtx);
738 49
        bl = VTAILQ_LAST(&ban_head, banhead_s);
739 49
        bl->refcount++;
740 49
        Lck_Unlock(&ban_mtx);
741
742 49
        VCLI_Out(cli, "Present bans:\n");
743 182
        VTAILQ_FOREACH(b, &ban_head, list) {
744 133
                o = bl == b ? 1 : 0;
745 266
                VCLI_Out(cli, "%10.6f %5ju %s", ban_time(b->spec),
746 133
                    (intmax_t)(b->refcount - o),
747 133
                    b->flags & BANS_FLAG_COMPLETED ? "C" : "-");
748 133
                if (DO_DEBUG(DBG_LURKER)) {
749 86
                        VCLI_Out(cli, "%s%s %p ",
750 43
                            b->flags & BANS_FLAG_REQ ? "R" : "-",
751 43
                            b->flags & BANS_FLAG_OBJ ? "O" : "-",
752
                            b);
753
                }
754 133
                VCLI_Out(cli, "  ");
755 133
                ban_render(cli, b->spec);
756 133
                VCLI_Out(cli, "\n");
757 133
                if (VCLI_Overflow(cli))
758 0
                        break;
759 133
                if (DO_DEBUG(DBG_LURKER)) {
760 43
                        Lck_Lock(&ban_mtx);
761
                        struct objcore *oc;
762 72
                        VTAILQ_FOREACH(oc, &b->objcore, ban_list)
763 29
                                VCLI_Out(cli, "  oc = %p\n", oc);
764 43
                        Lck_Unlock(&ban_mtx);
765
                }
766
        }
767
768 49
        Lck_Lock(&ban_mtx);
769 49
        bl->refcount--;
770 49
        ban_kick_lurker();      // XXX: Mostly for testcase b00009.vtc
771 49
        Lck_Unlock(&ban_mtx);
772 49
}
773
774
static struct cli_proto ban_cmds[] = {
775
        { CLICMD_BAN,                           "", ccf_ban },
776
        { CLICMD_BAN_LIST,                      "", ccf_ban_list },
777
        { NULL }
778
};
779
780
/*--------------------------------------------------------------------
781
 */
782
783
void
784 685
BAN_Compile(void)
785
{
786
        struct ban *b;
787
788
        /*
789
         * All bans have been read from all persistent stevedores. Export
790
         * the compiled list
791
         */
792
793 685
        ASSERT_CLI();
794 685
        AZ(ban_shutdown);
795
796 685
        Lck_Lock(&ban_mtx);
797
798
        /* Report the place-holder ban */
799 685
        b = VTAILQ_FIRST(&ban_head);
800 685
        ban_info_new(b->spec, ban_len(b->spec));
801
802 685
        ban_export();
803
804 685
        Lck_Unlock(&ban_mtx);
805
806 685
        ban_start = VTAILQ_FIRST(&ban_head);
807 685
        BAN_Release();
808 685
}
809
810
void
811 685
BAN_Init(void)
812
{
813
        struct ban_proto *bp;
814
815 685
        Lck_New(&ban_mtx, lck_ban);
816 685
        CLI_AddFuncs(ban_cmds);
817
818 685
        ban_holds = 1;
819
820
        /* Add a placeholder ban */
821 685
        bp = BAN_Build();
822 685
        AN(bp);
823 685
        AZ(pthread_cond_init(&ban_lurker_cond, NULL));
824 685
        AZ(BAN_Commit(bp, VSC_C_main));
825 685
        Lck_Lock(&ban_mtx);
826 685
        ban_mark_completed(VTAILQ_FIRST(&ban_head), VSC_C_main);
827 685
        Lck_Unlock(&ban_mtx);
828 685
}
829
830
/*--------------------------------------------------------------------
831
 * Shutdown of the ban system.
832
 *
833
 * When this function returns, no new bans will be accepted, and no
834
 * bans will be dropped (ban lurker thread stopped), so that no
835
 * STV_BanInfo calls will be executed.
836
 */
837
838
void
839 679
BAN_Shutdown(void)
840
{
841
        void *status;
842
843 679
        Lck_Lock(&ban_mtx);
844 679
        ban_shutdown = 1;
845 679
        ban_kick_lurker();
846 679
        Lck_Unlock(&ban_mtx);
847
848 679
        AZ(pthread_join(ban_thread, &status));
849 679
        AZ(status);
850
851 679
        Lck_Lock(&ban_mtx);
852
        /* Export the ban list to compact it */
853 679
        ban_export();
854 679
        Lck_Unlock(&ban_mtx);
855 679
}