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 1452
BAN_Release(void)
107
{
108
109 1452
        Lck_Lock(&ban_mtx);
110 1452
        assert(ban_holds > 0);
111 1452
        ban_holds--;
112 1452
        Lck_Unlock(&ban_mtx);
113 1452
        if (ban_holds == 0)
114 1376
                WRK_BgThread(&ban_thread, "ban-lurker", ban_lurker, NULL);
115 1452
}
116
117
/*--------------------------------------------------------------------
118
 * Extract time and length from ban-spec
119
 */
120
121
double
122 818
ban_time(const uint8_t *banspec)
123
{
124
        double t;
125
126
        assert(sizeof t == (BANS_LENGTH - BANS_TIMESTAMP));
127 818
        memcpy(&t, banspec, sizeof t);
128 818
        return (t);
129
}
130
131
unsigned
132 7902
ban_len(const uint8_t *banspec)
133
{
134
        unsigned u;
135
136 7902
        u = vbe32dec(banspec + BANS_LENGTH);
137 7902
        return (u);
138
}
139
140
int
141 194
ban_equal(const uint8_t *bs1, const uint8_t *bs2)
142
{
143
        unsigned u;
144
145
        /*
146
         * Compare two ban-strings.
147
         */
148 194
        u = vbe32dec(bs1 + BANS_LENGTH);
149 194
        if (u != vbe32dec(bs2 + BANS_LENGTH))
150 80
                return (0);
151 114
        return (!memcmp(bs1 + BANS_LENGTH, bs2 + BANS_LENGTH, u - BANS_LENGTH));
152
}
153
154
void
155 1460
ban_mark_completed(struct ban *b)
156
{
157
        unsigned ln;
158
159 1460
        CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
160 1460
        Lck_AssertHeld(&ban_mtx);
161
162 1460
        AN(b->spec);
163 1460
        if (!(b->flags & BANS_FLAG_COMPLETED)) {
164 1460
                ln = ban_len(b->spec);
165 1460
                b->flags |= BANS_FLAG_COMPLETED;
166 1460
                b->spec[BANS_FLAGS] |= BANS_FLAG_COMPLETED;
167 1460
                VWMB();
168 1460
                vbe32enc(b->spec + BANS_LENGTH, BANS_HEAD_LEN);
169 1460
                VSC_C_main->bans_completed++;
170 1460
                bans_persisted_fragmentation += ln - ban_len(b->spec);
171 1460
                VSC_C_main->bans_persisted_fragmentation =
172
                    bans_persisted_fragmentation;
173
        }
174 1460
}
175
176
/*--------------------------------------------------------------------
177
 * Access a lump of bytes in a ban test spec
178
 */
179
180
static const void *
181 384
ban_get_lump(const uint8_t **bs)
182
{
183
        const void *r;
184
        unsigned ln;
185
186 2382
        while (**bs == 0xff)
187 1614
                *bs += 1;
188 384
        ln = vbe32dec(*bs);
189 384
        *bs += PRNDUP(sizeof(uint32_t));
190 384
        assert(PAOK(*bs));
191 384
        r = (const void*)*bs;
192 384
        *bs += ln;
193 384
        return (r);
194
}
195
196
/*--------------------------------------------------------------------
197
 * Pick a test apart from a spec string
198
 */
199
200
static void
201 288
ban_iter(const uint8_t **bs, struct ban_test *bt)
202
{
203
204 288
        memset(bt, 0, sizeof *bt);
205 288
        bt->arg1 = *(*bs)++;
206 288
        if (bt->arg1 == BANS_ARG_REQHTTP || bt->arg1 == BANS_ARG_OBJHTTP) {
207 136
                bt->arg1_spec = (const char *)*bs;
208 136
                (*bs) += (*bs)[0] + 2;
209
        }
210 288
        bt->arg2 = ban_get_lump(bs);
211 288
        bt->oper = *(*bs)++;
212 288
        if (bt->oper == BANS_OPER_MATCH || bt->oper == BANS_OPER_NMATCH)
213 96
                bt->arg2_spec = ban_get_lump(bs);
214 288
}
215
216
/*--------------------------------------------------------------------
217
 * A new object is created, grab a reference to the newest ban
218
 */
219
220
void
221 2174
BAN_NewObjCore(struct objcore *oc)
222
{
223
224 2174
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
225 2174
        AZ(oc->ban);
226 2174
        AN(oc->objhead);
227 2174
        Lck_Lock(&ban_mtx);
228 2174
        oc->ban = ban_start;
229 2174
        ban_start->refcount++;
230 2174
        VTAILQ_INSERT_TAIL(&ban_start->objcore, oc, ban_list);
231 2174
        Lck_Unlock(&ban_mtx);
232 2174
}
233
234
/*--------------------------------------------------------------------
235
 * An object is destroyed, release its ban reference
236
 */
237
238
void
239 2282
BAN_DestroyObj(struct objcore *oc)
240
{
241
242 2282
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
243 2282
        Lck_Lock(&ban_mtx);
244 2282
        CHECK_OBJ_ORNULL(oc->ban, BAN_MAGIC);
245 2282
        if (oc->ban != NULL) {
246 502
                assert(oc->ban->refcount > 0);
247 502
                oc->ban->refcount--;
248 502
                VTAILQ_REMOVE(&oc->ban->objcore, oc, ban_list);
249 502
                oc->ban = NULL;
250
        }
251 2282
        Lck_Unlock(&ban_mtx);
252 2282
}
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 2738
ban_export(void)
303
{
304
        struct ban *b;
305
        struct vsb *vsb;
306
        unsigned ln;
307
308 2738
        Lck_AssertHeld(&ban_mtx);
309 2738
        ln = bans_persisted_bytes - bans_persisted_fragmentation;
310 2738
        vsb = VSB_new_auto();
311 2738
        AN(vsb);
312 5592
        VTAILQ_FOREACH_REVERSE(b, &ban_head, banhead_s, list)
313 2854
                AZ(VSB_bcat(vsb, b->spec, ban_len(b->spec)));
314 2738
        AZ(VSB_finish(vsb));
315 2738
        assert(VSB_len(vsb) == ln);
316 2738
        STV_BanExport((const uint8_t *)VSB_data(vsb), VSB_len(vsb));
317 2738
        VSB_destroy(&vsb);
318 5476
        VSC_C_main->bans_persisted_bytes =
319 2738
            bans_persisted_bytes = ln;
320 5476
        VSC_C_main->bans_persisted_fragmentation =
321 2738
            bans_persisted_fragmentation = 0;
322 2738
}
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 1488
ban_info_new(const uint8_t *ban, unsigned len)
332
{
333
        /* XXX martin pls review if ban_mtx needs to be held */
334 1488
        Lck_AssertHeld(&ban_mtx);
335 1488
        if (STV_BanInfoNew(ban, len))
336 0
                ban_export();
337 1488
}
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 68
        ASSERT_CLI();
363 68
        Lck_AssertHeld(&ban_mtx);
364
365 68
        t0 = ban_time(ban);
366 68
        assert(len == ban_len(ban));
367
368 148
        VTAILQ_FOREACH(b, &ban_head, list) {
369 116
                t1 = ban_time(b->spec);
370 116
                assert(t1 < t2);
371 116
                t2 = t1;
372 116
                if (t1 == t0)
373 16
                        return;
374 100
                if (t1 < t0)
375 20
                        break;
376 80
                if (ban_equal(b->spec, ban))
377 48
                        duplicate = 1;
378
        }
379
380 52
        VSC_C_main->bans++;
381 52
        VSC_C_main->bans_added++;
382
383 52
        b2 = ban_alloc();
384 52
        AN(b2);
385 52
        b2->spec = malloc(len);
386 52
        AN(b2->spec);
387 52
        memcpy(b2->spec, ban, len);
388 52
        if (ban[BANS_FLAGS] & BANS_FLAG_REQ) {
389 10
                VSC_C_main->bans_req++;
390 10
                b2->flags |= BANS_FLAG_REQ;
391
        }
392 52
        if (duplicate)
393 36
                VSC_C_main->bans_dups++;
394 52
        if (duplicate || (ban[BANS_FLAGS] & BANS_FLAG_COMPLETED))
395 38
                ban_mark_completed(b2);
396 52
        if (b == NULL)
397 32
                VTAILQ_INSERT_TAIL(&ban_head, b2, list);
398
        else
399 20
                VTAILQ_INSERT_BEFORE(b, b2, list);
400 52
        bans_persisted_bytes += len;
401 52
        VSC_C_main->bans_persisted_bytes = bans_persisted_bytes;
402
403
        /* Hunt down older duplicates */
404 82
        for (b = VTAILQ_NEXT(b2, list); b != NULL; b = VTAILQ_NEXT(b, list)) {
405 30
                if (b->flags & BANS_FLAG_COMPLETED)
406 28
                        continue;
407 2
                if (ban_equal(b->spec, ban)) {
408 0
                        ban_mark_completed(b);
409 0
                        VSC_C_main->bans_dups++;
410
                }
411
        }
412
}
413
414
/*--------------------------------------------------------------------
415
 * Reload a series of persisted ban specs
416
 */
417
418
void
419 76
BAN_Reload(const uint8_t *ptr, unsigned len)
420
{
421
        const uint8_t *pe;
422
        unsigned l;
423
424 76
        AZ(ban_shutdown);
425 76
        pe = ptr + len;
426 76
        Lck_Lock(&ban_mtx);
427 220
        while (ptr < pe) {
428
                /* XXX: This can be optimized by traversing the live
429
                 * ban list together with the reload list (combining
430
                 * the loops in BAN_Reload and ban_reload). */
431 68
                l = ban_len(ptr);
432 68
                assert(ptr + l <= pe);
433 68
                ban_reload(ptr, l);
434 68
                ptr += l;
435
        }
436 76
        Lck_Unlock(&ban_mtx);
437 76
}
438
439
/*--------------------------------------------------------------------
440
 * Get a bans timestamp
441
 */
442
443
double
444 138
BAN_Time(const struct ban *b)
445
{
446
447 138
        if (b == NULL)
448 44
                return (0.0);
449
450 94
        CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
451 94
        return (ban_time(b->spec));
452
}
453
454
/*--------------------------------------------------------------------
455
 * Evaluate ban-spec
456
 */
457
458
int
459 96
ban_evaluate(struct worker *wrk, const uint8_t *bs, struct objcore *oc,
460
    const struct http *reqhttp, unsigned *tests)
461
{
462
        struct ban_test bt;
463
        const uint8_t *be;
464
        const char *p;
465
        const char *arg1;
466
467 96
        be = bs + ban_len(bs);
468 96
        bs += BANS_HEAD_LEN;
469 266
        while (bs < be) {
470 104
                (*tests)++;
471 104
                ban_iter(&bs, &bt);
472 104
                arg1 = NULL;
473 104
                switch (bt.arg1) {
474
                case BANS_ARG_URL:
475 52
                        AN(reqhttp);
476 52
                        arg1 = reqhttp->hd[HTTP_HDR_URL].b;
477 52
                        break;
478
                case BANS_ARG_REQHTTP:
479 8
                        AN(reqhttp);
480 8
                        (void)http_GetHdr(reqhttp, bt.arg1_spec, &p);
481 8
                        arg1 = p;
482 8
                        break;
483
                case BANS_ARG_OBJHTTP:
484 42
                        arg1 = HTTP_GetHdrPack(wrk, oc, bt.arg1_spec);
485 42
                        break;
486
                case BANS_ARG_OBJSTATUS:
487 2
                        arg1 = HTTP_GetHdrPack(wrk, oc, H__Status);
488 2
                        break;
489
                default:
490 0
                        WRONG("Wrong BAN_ARG code");
491
                }
492
493 104
                switch (bt.oper) {
494
                case BANS_OPER_EQ:
495 54
                        if (arg1 == NULL || strcmp(arg1, bt.arg2))
496 22
                                return (0);
497 32
                        break;
498
                case BANS_OPER_NEQ:
499 6
                        if (arg1 != NULL && !strcmp(arg1, bt.arg2))
500 4
                                return (0);
501 2
                        break;
502
                case BANS_OPER_MATCH:
503 88
                        if (arg1 == NULL ||
504 44
                            pcre_exec(bt.arg2_spec, NULL, arg1, strlen(arg1),
505
                            0, 0, NULL, 0) < 0)
506 4
                                return (0);
507 40
                        break;
508
                case BANS_OPER_NMATCH:
509 0
                        if (arg1 != NULL &&
510 0
                            pcre_exec(bt.arg2_spec, NULL, arg1, strlen(arg1),
511
                            0, 0, NULL, 0) >= 0)
512 0
                                return (0);
513 0
                        break;
514
                default:
515 0
                        WRONG("Wrong BAN_OPER code");
516
                }
517
        }
518 66
        return (1);
519
}
520
521
/*--------------------------------------------------------------------
522
 * Check an object against all applicable bans
523
 *
524
 * Return:
525
 *      -1 not all bans checked, but none of the checked matched
526
 *              Only if !has_req
527
 *      0 No bans matched, object moved to ban_start.
528
 *      1 Ban matched, object removed from ban list.
529
 */
530
531
int
532 6670
BAN_CheckObject(struct worker *wrk, struct objcore *oc, struct req *req)
533
{
534
        struct ban *b;
535
        struct vsl_log *vsl;
536
        struct ban *b0, *bn;
537
        unsigned tests;
538
539 6670
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
540 6670
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
541 6670
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
542 6670
        Lck_AssertHeld(&oc->objhead->mtx);
543 6670
        assert(oc->refcnt > 0);
544
545 6670
        vsl = req->vsl;
546
547 6670
        CHECK_OBJ_NOTNULL(oc->ban, BAN_MAGIC);
548
549
        /* First do an optimistic unlocked check */
550 6670
        b0 = ban_start;
551 6670
        CHECK_OBJ_NOTNULL(b0, BAN_MAGIC);
552
553 6670
        if (b0 == oc->ban)
554 6582
                return (0);
555
556
        /* If that fails, make a safe check */
557 88
        Lck_Lock(&ban_mtx);
558 88
        b0 = ban_start;
559 88
        bn = oc->ban;
560 88
        if (b0 != bn)
561 88
                bn->refcount++;
562 88
        Lck_Unlock(&ban_mtx);
563
564 88
        AN(bn);
565
566 88
        if (b0 == bn)
567 0
                return (0);
568
569 88
        AN(b0);
570 88
        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 88
        tests = 0;
578 146
        for (b = b0; b != bn; b = VTAILQ_NEXT(b, list)) {
579 108
                CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
580 108
                if (b->flags & BANS_FLAG_COMPLETED)
581 36
                        continue;
582 72
                if (ban_evaluate(wrk, b->spec, oc, req->http, &tests))
583 50
                        break;
584
        }
585
586 88
        Lck_Lock(&ban_mtx);
587 88
        bn->refcount--;
588 88
        VSC_C_main->bans_tested++;
589 88
        VSC_C_main->bans_tests_tested += tests;
590
591 88
        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 88
        if (b != NULL)
601 50
                VSC_C_main->bans_obj_killed++;
602
603 88
        if (VTAILQ_LAST(&ban_head, banhead_s)->refcount == 0)
604 40
                ban_kick_lurker();
605
606 88
        Lck_Unlock(&ban_mtx);
607
608 88
        if (b == NULL) {
609
                /* not banned */
610 38
                ObjSendEvent(wrk, oc, OEV_BANCHG);
611 38
                return (0);
612
        } else {
613 50
                VSLb(vsl, SLT_ExpBan, "%u banned lookup", ObjGetXID(wrk, oc));
614 50
                return (1);
615
        }
616
}
617
618
/*--------------------------------------------------------------------
619
 * CLI functions to add bans
620
 */
621
622
static void v_matchproto_(cli_func_t)
623 86
ccf_ban(struct cli *cli, const char * const *av, void *priv)
624
{
625
        int narg, i;
626
        struct ban_proto *bp;
627 86
        const char *err = NULL;
628
629
        (void)priv;
630
631
        /* First do some cheap checks on the arguments */
632 386
        for (narg = 0; av[narg + 2] != NULL; narg++)
633 300
                continue;
634 86
        if ((narg % 4) != 3) {
635 4
                VCLI_Out(cli, "Wrong number of arguments");
636 4
                VCLI_SetResult(cli, CLIS_PARAM);
637 4
                return;
638
        }
639 88
        for (i = 3; i < narg; i += 4) {
640 8
                if (strcmp(av[i + 2], "&&")) {
641 2
                        VCLI_Out(cli, "Found \"%s\" expected &&", av[i + 2]);
642 2
                        VCLI_SetResult(cli, CLIS_PARAM);
643 2
                        return;
644
                }
645
        }
646
647 80
        bp = BAN_Build();
648 80
        if (bp == NULL) {
649 0
                VCLI_Out(cli, "Out of Memory");
650 0
                VCLI_SetResult(cli, CLIS_CANT);
651 0
                return;
652
        }
653 164
        for (i = 0; i < narg; i += 4) {
654 86
                err = BAN_AddTest(bp, av[i + 2], av[i + 3], av[i + 4]);
655 86
                if (err)
656 2
                        break;
657
        }
658
659 80
        if (err == NULL) {
660
                // XXX racy - grab wstat lock?
661 78
                err = BAN_Commit(bp);
662
        }
663
664 80
        if (err != NULL) {
665 2
                VCLI_Out(cli, "%s", err);
666 2
                BAN_Abandon(bp);
667 2
                VCLI_SetResult(cli, CLIS_PARAM);
668
        }
669
}
670
671
static void
672 320
ban_render(struct cli *cli, const uint8_t *bs, int quote)
673
{
674
        struct ban_test bt;
675
        const uint8_t *be;
676
677 320
        be = bs + ban_len(bs);
678 320
        bs += BANS_HEAD_LEN;
679 824
        while (bs < be) {
680 184
                ban_iter(&bs, &bt);
681 184
                switch (bt.arg1) {
682
                case BANS_ARG_URL:
683 90
                        VCLI_Out(cli, "req.url");
684 90
                        break;
685
                case BANS_ARG_REQHTTP:
686 48
                        VCLI_Out(cli, "req.http.%.*s",
687 48
                            bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
688 24
                        break;
689
                case BANS_ARG_OBJHTTP:
690 124
                        VCLI_Out(cli, "obj.http.%.*s",
691 124
                            bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
692 62
                        break;
693
                case BANS_ARG_OBJSTATUS:
694 8
                        VCLI_Out(cli, "obj.status");
695 8
                        break;
696
                default:
697 0
                        WRONG("Wrong BANS_ARG");
698
                }
699 184
                switch (bt.oper) {
700 116
                case BANS_OPER_EQ:      VCLI_Out(cli, " == "); break;
701 16
                case BANS_OPER_NEQ:     VCLI_Out(cli, " != "); break;
702 52
                case BANS_OPER_MATCH:   VCLI_Out(cli, " ~ "); break;
703 0
                case BANS_OPER_NMATCH:  VCLI_Out(cli, " !~ "); break;
704
                default:
705 0
                        WRONG("Wrong BANS_OPER");
706
                }
707 184
                if (quote)
708 26
                        VCLI_Quote(cli, bt.arg2);
709
                else
710 158
                        VCLI_Out(cli, "%s", bt.arg2);
711 184
                if (bs < be)
712 12
                        VCLI_Out(cli, " && ");
713
        }
714 320
}
715
716
static void
717 100
ban_list(struct cli *cli, struct ban *bl)
718
{
719
        struct ban *b;
720
        int64_t o;
721
722 100
        VCLI_Out(cli, "Present bans:\n");
723 370
        VTAILQ_FOREACH(b, &ban_head, list) {
724 270
                o = bl == b ? 1 : 0;
725 540
                VCLI_Out(cli, "%10.6f %5ju %s", ban_time(b->spec),
726 270
                    (intmax_t)(b->refcount - o),
727 270
                    b->flags & BANS_FLAG_COMPLETED ? "C" : "-");
728 270
                if (DO_DEBUG(DBG_LURKER)) {
729 152
                        VCLI_Out(cli, "%s%s %p ",
730 76
                            b->flags & BANS_FLAG_REQ ? "R" : "-",
731 76
                            b->flags & BANS_FLAG_OBJ ? "O" : "-",
732
                            b);
733
                }
734 270
                VCLI_Out(cli, "  ");
735 270
                ban_render(cli, b->spec, 0);
736 270
                VCLI_Out(cli, "\n");
737 270
                if (VCLI_Overflow(cli))
738 0
                        break;
739 270
                if (DO_DEBUG(DBG_LURKER)) {
740 76
                        Lck_Lock(&ban_mtx);
741
                        struct objcore *oc;
742 134
                        VTAILQ_FOREACH(oc, &b->objcore, ban_list)
743 58
                                VCLI_Out(cli, "  oc = %p\n", oc);
744 76
                        Lck_Unlock(&ban_mtx);
745
                }
746
        }
747 100
}
748
749
static void
750 12
ban_list_json(struct cli *cli, const char * const *av, struct ban *bl)
751
{
752
        struct ban *b;
753
        int64_t o;
754 12
        int n = 0;
755
        int ocs;
756
757 12
        VCLI_JSON_begin(cli, 2, av);
758 12
        VCLI_Out(cli, ",\n");
759 62
        VTAILQ_FOREACH(b, &ban_head, list) {
760 50
                o = bl == b ? 1 : 0;
761 50
                VCLI_Out(cli, "%s", n ? ",\n" : "");
762 50
                n++;
763 50
                VCLI_Out(cli, "{\n");
764 50
                VSB_indent(cli->sb, 2);
765 50
                VCLI_Out(cli, "\"time\": %.6f,\n", ban_time(b->spec));
766 50
                VCLI_Out(cli, "\"refs\": %ju,\n", (intmax_t)(b->refcount - o));
767 50
                VCLI_Out(cli, "\"completed\": %s,\n",
768 50
                         b->flags & BANS_FLAG_COMPLETED ? "true" : "false");
769 50
                VCLI_Out(cli, "\"spec\": \"");
770 50
                ban_render(cli, b->spec, 1);
771 50
                VCLI_Out(cli, "\"");
772
773 50
                if (DO_DEBUG(DBG_LURKER)) {
774 12
                        VCLI_Out(cli, ",\n");
775 12
                        VCLI_Out(cli, "\"req_tests\": %s,\n",
776 12
                                 b->flags & BANS_FLAG_REQ ? "true" : "false");
777 12
                        VCLI_Out(cli, "\"obj_tests\": %s,\n",
778 12
                                 b->flags & BANS_FLAG_OBJ ? "true" : "false");
779 12
                        VCLI_Out(cli, "\"pointer\": \"%p\",\n", b);
780 12
                        if (VCLI_Overflow(cli))
781 0
                                break;
782
783 12
                        ocs = 0;
784 12
                        VCLI_Out(cli, "\"objcores\": [\n");
785 12
                        VSB_indent(cli->sb, 2);
786 12
                        Lck_Lock(&ban_mtx);
787
                        struct objcore *oc;
788 12
                        VTAILQ_FOREACH(oc, &b->objcore, ban_list) {
789 0
                                if (ocs)
790 0
                                        VCLI_Out(cli, ",\n");
791 0
                                VCLI_Out(cli, "%p", oc);
792 0
                                ocs++;
793
                        }
794 12
                        Lck_Unlock(&ban_mtx);
795 12
                        VSB_indent(cli->sb, -2);
796 12
                        VCLI_Out(cli, "\n]");
797
                }
798 50
                VSB_indent(cli->sb, -2);
799 50
                VCLI_Out(cli, "\n}");
800
        }
801 12
        VCLI_JSON_end(cli);
802 12
}
803
804
static void v_matchproto_(cli_func_t)
805 112
ccf_ban_list(struct cli *cli, const char * const *av, void *priv)
806
{
807
        struct ban *bl;
808
809
        (void)priv;
810
811
        /* Get a reference so we are safe to traverse the list */
812 112
        Lck_Lock(&ban_mtx);
813 112
        bl = VTAILQ_LAST(&ban_head, banhead_s);
814 112
        bl->refcount++;
815 112
        Lck_Unlock(&ban_mtx);
816
817 112
        if (av[2] != NULL && strcmp(av[2], "-j") == 0)
818 12
                ban_list_json(cli, av, bl);
819
        else
820 100
                ban_list(cli, bl);
821
822 112
        Lck_Lock(&ban_mtx);
823 112
        bl->refcount--;
824 112
        ban_kick_lurker();      // XXX: Mostly for testcase b00009.vtc
825 112
        Lck_Unlock(&ban_mtx);
826 112
}
827
828
static struct cli_proto ban_cmds[] = {
829
        { CLICMD_BAN,                           "", ccf_ban },
830
        { CLICMD_BAN_LIST,                      "", ccf_ban_list,
831
          ccf_ban_list },
832
        { NULL }
833
};
834
835
/*--------------------------------------------------------------------
836
 */
837
838
void
839 1376
BAN_Compile(void)
840
{
841
        struct ban *b;
842
843
        /*
844
         * All bans have been read from all persistent stevedores. Export
845
         * the compiled list
846
         */
847
848 1376
        ASSERT_CLI();
849 1376
        AZ(ban_shutdown);
850
851 1376
        Lck_Lock(&ban_mtx);
852
853
        /* Report the place-holder ban */
854 1376
        b = VTAILQ_FIRST(&ban_head);
855 1376
        ban_info_new(b->spec, ban_len(b->spec));
856
857 1376
        ban_export();
858
859 1376
        Lck_Unlock(&ban_mtx);
860
861 1376
        ban_start = VTAILQ_FIRST(&ban_head);
862 1376
        BAN_Release();
863 1376
}
864
865
void
866 1376
BAN_Init(void)
867
{
868
        struct ban_proto *bp;
869
870 1376
        Lck_New(&ban_mtx, lck_ban);
871 1376
        CLI_AddFuncs(ban_cmds);
872
873 1376
        ban_holds = 1;
874
875
        /* Add a placeholder ban */
876 1376
        bp = BAN_Build();
877 1376
        AN(bp);
878 1376
        AZ(pthread_cond_init(&ban_lurker_cond, NULL));
879 1376
        AZ(BAN_Commit(bp));
880 1376
        Lck_Lock(&ban_mtx);
881 1376
        ban_mark_completed(VTAILQ_FIRST(&ban_head));
882 1376
        Lck_Unlock(&ban_mtx);
883 1376
}
884
885
/*--------------------------------------------------------------------
886
 * Shutdown of the ban system.
887
 *
888
 * When this function returns, no new bans will be accepted, and no
889
 * bans will be dropped (ban lurker thread stopped), so that no
890
 * STV_BanInfo calls will be executed.
891
 */
892
893
void
894 1362
BAN_Shutdown(void)
895
{
896
        void *status;
897
898 1362
        Lck_Lock(&ban_mtx);
899 1362
        ban_shutdown = 1;
900 1362
        ban_kick_lurker();
901 1362
        Lck_Unlock(&ban_mtx);
902
903 1362
        AZ(pthread_join(ban_thread, &status));
904 1362
        AZ(status);
905
906 1362
        Lck_Lock(&ban_mtx);
907
        /* Export the ban list to compact it */
908 1362
        ban_export();
909 1362
        Lck_Unlock(&ban_mtx);
910 1362
}