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