vinyl-cache/bin/vinyld/cache/cache_ban.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 */
31
32
#include "config.h"
33
34
#include <stdlib.h>
35
#include <stdio.h>
36
37
#include "cache_int.h"
38
#include "cache_ban.h"
39
#include "cache_objhead.h"
40
41
#include "vcli_serve.h"
42
#include "vend.h"
43
#include "vmb.h"
44
45
/* cache_ban_build.c */
46
void BAN_Build_Init(void);
47
void BAN_Build_Fini(void);
48
49
struct lock ban_mtx;
50
int ban_shutdown;
51
struct banhead_s ban_head = VTAILQ_HEAD_INITIALIZER(ban_head);
52
struct ban * volatile ban_start;
53
54
static pthread_t ban_thread;
55
static int ban_holds;
56
uint64_t bans_persisted_bytes;
57
uint64_t bans_persisted_fragmentation;
58
59
struct ban_test {
60
        uint8_t                 oper;
61
        uint8_t                 arg1;
62
        uint8_t                 arg2_bool;
63
        const char              *arg1_spec;
64
        const char              *arg2;
65
        double                  arg2_double;
66
        const void              *arg2_spec;
67
};
68
69
static const char * const arg_name[BAN_ARGARRSZ + 1] = {
70
#define PVAR(a, b, c) [BAN_ARGIDX(c)] = (a),
71
#include "tbl/ban_vars.h"
72
        [BAN_ARGARRSZ] = NULL
73
};
74
75
/*--------------------------------------------------------------------
76
 * Storage handling of bans
77
 */
78
79
static struct ban *
80 525
ban_alloc(void)
81
{
82
        struct ban *b;
83
84 525
        ALLOC_OBJ(b, BAN_MAGIC);
85 525
        if (b != NULL)
86 525
                VTAILQ_INIT(&b->objcore);
87 525
        return (b);
88
}
89
90
void
91 1934
BAN_Free(struct ban *b)
92
{
93
94 1934
        CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
95 1934
        AZ(b->refcount);
96 1934
        assert(VTAILQ_EMPTY(&b->objcore));
97
98 1934
        if (b->spec != NULL)
99 1934
                free(b->spec);
100 1934
        FREE_OBJ(b);
101 1934
}
102
103
/*--------------------------------------------------------------------
104
 * Get/release holds which prevent the ban_lurker from starting.
105
 * Holds are held while stevedores load zombie objects.
106
 */
107
108
void
109 756
BAN_Hold(void)
110
{
111
112 756
        Lck_Lock(&ban_mtx);
113
        /* Once holds are released, we allow no more */
114 756
        assert(ban_holds > 0);
115 756
        ban_holds++;
116 756
        Lck_Unlock(&ban_mtx);
117 756
}
118
119
void
120 21597
BAN_Release(void)
121
{
122
123 21597
        Lck_Lock(&ban_mtx);
124 21597
        assert(ban_holds > 0);
125 21597
        ban_holds--;
126 21597
        Lck_Unlock(&ban_mtx);
127 21597
        if (ban_holds == 0) {
128 20841
                BANIDX_fini();
129 20841
                WRK_BgThread(&ban_thread, "ban-lurker", ban_lurker, NULL);
130 20841
        }
131 21597
}
132
133
/*--------------------------------------------------------------------
134
 * Extract time and length from ban-spec
135
 */
136
137
vtim_real
138 20286
ban_time(const uint8_t *banspec)
139
{
140
        vtim_real t;
141
        uint64_t u;
142
143 20286
        assert(sizeof t == sizeof u);
144 20286
        assert(sizeof t == (BANS_LENGTH - BANS_TIMESTAMP));
145 20286
        u = vbe64dec(banspec + BANS_TIMESTAMP);
146 20286
        vmemcpy(&t, &u, sizeof t);
147 20286
        return (t);
148
}
149
150
unsigned
151 134965
ban_len(const uint8_t *banspec)
152
{
153
        unsigned u;
154
155 134965
        u = vbe32dec(banspec + BANS_LENGTH);
156 134965
        return (u);
157
}
158
159
int
160 5733
ban_equal(const uint8_t *bs1, const uint8_t *bs2)
161
{
162
        unsigned u;
163
164
        /*
165
         * Compare two ban-strings.
166
         */
167 5733
        u = ban_len(bs1);
168 5733
        if (u != ban_len(bs2))
169 2163
                return (0);
170 3570
        if (bs1[BANS_FLAGS] & BANS_FLAG_NODEDUP)
171 63
                return (0);
172
173 3507
        return (!vmemcmp(bs1 + BANS_LENGTH, bs2 + BANS_LENGTH, u - BANS_LENGTH));
174 5733
}
175
176
void
177 22396
ban_mark_completed(struct ban *b)
178
{
179
        unsigned ln;
180
181 22396
        CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
182 22396
        Lck_AssertHeld(&ban_mtx);
183
184 22396
        AN(b->spec);
185 22396
        if (!(b->flags & BANS_FLAG_COMPLETED)) {
186 22396
                ln = ban_len(b->spec);
187 22396
                b->flags |= BANS_FLAG_COMPLETED;
188 22396
                b->spec[BANS_FLAGS] |= BANS_FLAG_COMPLETED;
189 22396
                VWMB();
190 22396
                vbe32enc(b->spec + BANS_LENGTH, BANS_HEAD_LEN);
191 22396
                VSC_C_main->bans_completed++;
192 22396
                bans_persisted_fragmentation += ln - ban_len(b->spec);
193 22396
                VSC_C_main->bans_persisted_fragmentation =
194 22396
                    bans_persisted_fragmentation;
195 22396
        }
196 22396
}
197
198
/*--------------------------------------------------------------------
199
 * Access a lump of bytes in a ban test spec
200
 */
201
202
static const void *
203 9408
ban_get_lump(const uint8_t **bs)
204
{
205
        const void *r;
206
        unsigned ln;
207
208 58800
        while (**bs == 0xff)
209 49392
                *bs += 1;
210 9408
        ln = vbe32dec(*bs);
211 9408
        *bs += PRNDUP(sizeof(uint32_t));
212 9408
        assert(PAOK(*bs));
213 9408
        r = (const void*)*bs;
214 9408
        *bs += ln;
215 9408
        return (r);
216
}
217
218
/*--------------------------------------------------------------------
219
 * Pick a test apart from a spec string
220
 */
221
222
static void
223 8358
ban_iter(const uint8_t **bs, struct ban_test *bt)
224
{
225
        const void *lump;
226
        uint64_t dtmp;
227
228 8358
        memset(bt, 0, sizeof *bt);
229 8358
        bt->arg2_double = nan("");
230 8358
        bt->arg1 = *(*bs)++;
231 8358
        if (BANS_HAS_ARG1_SPEC(bt->arg1)) {
232 2142
                bt->arg1_spec = (const char *)*bs;
233 2142
                (*bs) += (*bs)[0] + 2;
234 2142
        }
235 8358
        lump = ban_get_lump(bs);
236 8358
        bt->oper = *(*bs)++;
237 8358
        if (BANS_HAS_ARG2_DOUBLE(bt->arg1)) {
238 3297
                dtmp = vbe64dec(lump);
239 3297
                vmemcpy(&bt->arg2_double, &dtmp, sizeof dtmp);
240 3297
                return;
241 5061
        } else if (BANS_HAS_ARG2_BOOL(bt->arg1)) {
242 735
                bt->arg2_bool = *(uint8_t const *)lump;
243 735
                return;
244
        }
245 4326
        bt->arg2 = lump;
246 4326
        if (BANS_HAS_ARG2_SPEC(bt->oper))
247 1050
                bt->arg2_spec = ban_get_lump(bs);
248 8358
}
249
250
/*--------------------------------------------------------------------
251
 * A new object is created, grab a reference to the newest ban
252
 */
253
254
void
255 30364
BAN_NewObjCore(struct objcore *oc)
256
{
257
258 30364
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
259 30364
        AZ(oc->ban);
260 30364
        AN(oc->objhead);
261 30364
        Lck_Lock(&ban_mtx);
262 30364
        oc->ban = ban_start;
263 30364
        ban_start->refcount++;
264 30364
        VTAILQ_INSERT_TAIL(&ban_start->objcore, oc, ban_list);
265 30364
        Lck_Unlock(&ban_mtx);
266 30364
}
267
268
/*--------------------------------------------------------------------
269
 * An object is destroyed, release its ban reference
270
 */
271
272
void
273 47578
BAN_DestroyObj(struct objcore *oc)
274
{
275
276 47578
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
277 47578
        if (oc->ban == NULL)
278 38851
                return;
279 8727
        Lck_Lock(&ban_mtx);
280 8727
        CHECK_OBJ_ORNULL(oc->ban, BAN_MAGIC);
281 8727
        if (oc->ban != NULL) {
282 8727
                assert(oc->ban->refcount > 0);
283 8727
                oc->ban->refcount--;
284 8727
                VTAILQ_REMOVE(&oc->ban->objcore, oc, ban_list);
285 8727
                oc->ban = NULL;
286 8727
        }
287 8727
        Lck_Unlock(&ban_mtx);
288 47578
}
289
290
/*--------------------------------------------------------------------
291
 * Find a ban based on a timestamp.
292
 * Assume we have a BAN_Hold, so list traversal is safe.
293
 */
294
295
struct ban *
296 336
BAN_FindBan(vtim_real t0)
297
{
298
        struct ban *b;
299
        vtim_real t1;
300
301 336
        assert(ban_holds > 0);
302 336
        b = BANIDX_lookup(t0);
303 336
        VTAILQ_FOREACH_FROM(b, &ban_head, list) {
304 336
                t1 = ban_time(b->spec);
305 336
                if (t1 == t0)
306 336
                        return (b);
307 0
                if (t1 < t0)
308 0
                        break;
309 0
        }
310 0
        return (NULL);
311 336
}
312
313
/*--------------------------------------------------------------------
314
 * Grab a reference to a ban and associate the objcore with that ban.
315
 * Assume we have a BAN_Hold, so list traversal is safe.
316
 */
317
318
void
319 336
BAN_RefBan(struct objcore *oc, struct ban *b)
320
{
321
322 336
        Lck_Lock(&ban_mtx);
323 336
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
324 336
        AZ(oc->ban);
325 336
        CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
326 336
        assert(ban_holds > 0);
327 336
        b->refcount++;
328 336
        VTAILQ_INSERT_TAIL(&b->objcore, oc, ban_list);
329 336
        oc->ban = b;
330 336
        Lck_Unlock(&ban_mtx);
331 336
}
332
333
/*--------------------------------------------------------------------
334
 * Compile a full ban list and export this area to the stevedores for
335
 * persistence.
336
 */
337
338
static void
339 41463
ban_export(void)
340
{
341
        struct ban *b;
342
        struct vsb *vsb;
343
        unsigned ln;
344
345 41463
        Lck_AssertHeld(&ban_mtx);
346 41463
        ln = bans_persisted_bytes - bans_persisted_fragmentation;
347 41463
        vsb = VSB_new_auto();
348 41463
        AN(vsb);
349 84646
        VTAILQ_FOREACH_REVERSE(b, &ban_head, banhead_s, list)
350 43183
                AZ(VSB_bcat(vsb, b->spec, ban_len(b->spec)));
351 41463
        AZ(VSB_finish(vsb));
352 41463
        assert(VSB_len(vsb) == ln);
353 41463
        Lck_Unlock(&ban_mtx);
354 41463
        STV_BanExport((const uint8_t *)VSB_data(vsb), VSB_len(vsb));
355 41463
        VSB_destroy(&vsb);
356 41463
        Lck_Lock(&ban_mtx);
357 41463
        VSC_C_main->bans_persisted_bytes =
358 41463
            bans_persisted_bytes = ln;
359 41463
        VSC_C_main->bans_persisted_fragmentation =
360 41463
            bans_persisted_fragmentation = 0;
361 41463
}
362
363
/*
364
 * For both of these we do a full export on info failure to remove
365
 * holes in the exported list.
366
 * XXX: we should keep track of the size of holes in the last exported list
367
 * XXX: check if the ban_export should be batched in ban_cleantail
368
 */
369
void
370 23445
ban_info_new(const uint8_t *ban, unsigned len)
371
{
372 23445
        Lck_AssertHeld(&ban_mtx);
373 23445
        if (STV_BanInfoNew(ban, len))
374 0
                ban_export();
375 23445
}
376
377
void
378 1934
ban_info_drop(const uint8_t *ban, unsigned len)
379
{
380 1934
        Lck_AssertHeld(&ban_mtx);
381 1934
        if (STV_BanInfoDrop(ban, len))
382 0
                ban_export();
383 1934
}
384
385
/*--------------------------------------------------------------------
386
 * Put a skeleton ban in the list, unless there is an identical,
387
 * time & condition, ban already in place.
388
 *
389
 * If a newer ban has same condition, mark the inserted ban COMPLETED,
390
 * also mark any older bans, with the same condition COMPLETED.
391
 */
392
393
static void
394 693
ban_reload(const uint8_t *ban, unsigned len)
395
{
396
        struct ban *b, *b2;
397 693
        int duplicate = 0;
398 693
        vtim_real t0, t1, t2 = 9e99;
399 693
        ASSERT_CLI();
400 693
        Lck_AssertHeld(&ban_mtx);
401 693
        assert(ban_holds > 0);
402
403 693
        t0 = ban_time(ban);
404 693
        assert(len == ban_len(ban));
405
406 693
        b = BANIDX_lookup(t0);
407 1218
        VTAILQ_FOREACH_FROM(b, &ban_head, list) {
408 903
                t1 = ban_time(b->spec);
409 903
                assert(t1 < t2);
410 903
                t2 = t1;
411 903
                if (t1 == t0)
412 168
                        return;
413 735
                if (t1 < t0)
414 210
                        break;
415 525
                if (ban_equal(b->spec, ban))
416 357
                        duplicate = 1;
417 525
        }
418
419 525
        VSC_C_main->bans++;
420 525
        VSC_C_main->bans_added++;
421
422 525
        b2 = ban_alloc();
423 525
        AN(b2);
424 525
        b2->spec = malloc(len);
425 525
        AN(b2->spec);
426 525
        vmemcpy(b2->spec, ban, len);
427 525
        if (ban[BANS_FLAGS] & BANS_FLAG_REQ) {
428 105
                VSC_C_main->bans_req++;
429 105
                b2->flags |= BANS_FLAG_REQ;
430 105
        }
431 525
        if (duplicate)
432 357
                VSC_C_main->bans_dups++;
433 525
        if (duplicate || (ban[BANS_FLAGS] & BANS_FLAG_COMPLETED))
434 378
                ban_mark_completed(b2);
435 525
        if (b == NULL)
436 315
                VTAILQ_INSERT_TAIL(&ban_head, b2, list);
437
        else
438 210
                VTAILQ_INSERT_BEFORE(b, b2, list);
439 525
        bans_persisted_bytes += len;
440 525
        VSC_C_main->bans_persisted_bytes = bans_persisted_bytes;
441
442
        /* Hunt down older duplicates */
443 840
        for (b = VTAILQ_NEXT(b2, list); b != NULL; b = VTAILQ_NEXT(b, list)) {
444 315
                if (b->flags & BANS_FLAG_COMPLETED)
445 294
                        continue;
446 21
                if (ban_equal(b->spec, ban)) {
447 0
                        ban_mark_completed(b);
448 0
                        VSC_C_main->bans_dups++;
449 0
                }
450 21
        }
451 693
}
452
453
/*--------------------------------------------------------------------
454
 * Reload a series of persisted ban specs
455
 */
456
457
void
458 756
BAN_Reload(const uint8_t *ptr, unsigned len)
459
{
460
        const uint8_t *pe;
461
        unsigned l;
462
463 756
        AZ(ban_shutdown);
464 756
        pe = ptr + len;
465 756
        Lck_Lock(&ban_mtx);
466 1449
        while (ptr < pe) {
467
                /* XXX: This can be optimized by traversing the live
468
                 * ban list together with the reload list (combining
469
                 * the loops in BAN_Reload and ban_reload). */
470 693
                l = ban_len(ptr);
471 693
                assert(ptr + l <= pe);
472 693
                ban_reload(ptr, l);
473 693
                ptr += l;
474
        }
475 756
        Lck_Unlock(&ban_mtx);
476 756
}
477
478
/*--------------------------------------------------------------------
479
 * Get a bans timestamp
480
 */
481
482
vtim_real
483 1302
BAN_Time(const struct ban *b)
484
{
485
486 1302
        if (b == NULL)
487 399
                return (0.0);
488
489 903
        CHECK_OBJ(b, BAN_MAGIC);
490 903
        return (ban_time(b->spec));
491 1302
}
492
493
/*--------------------------------------------------------------------
494
 * Evaluate ban-spec
495
 */
496
497
int
498 2625
ban_evaluate(struct worker *wrk, const uint8_t *bsarg, struct objcore *oc,
499
    const struct http *reqhttp, unsigned *tests)
500
{
501
        struct ban_test bt;
502
        const uint8_t *bs, *be;
503
        const char *p;
504
        const char *arg1;
505
        double darg1, darg2;
506
        int barg1, barg2;
507
        hdr_t hdr;
508
        int rv;
509
510
        /*
511
         * for ttl, age and last_hit, fix the point in time such that banning
512
         * refers to the same point in time when the ban is evaluated
513
         *
514
         * for grace/keep, we assume that the absolute values are pola and that
515
         * users will most likely also specify a ttl criterion if they want to
516
         * fix a point in time (such as "obj.ttl > 5h && obj.keep > 3h")
517
         */
518
519 2625
        bs = bsarg;
520 2625
        be = bs + ban_len(bs);
521 2625
        bs += BANS_HEAD_LEN;
522 4326
        while (bs < be) {
523 2919
                (*tests)++;
524 2919
                ban_iter(&bs, &bt);
525 2919
                arg1 = NULL;
526 2919
                darg1 = darg2 = nan("");
527 2919
                barg1 = barg2 = -1;
528 2919
                switch (bt.arg1) {
529
                case BANS_ARG_URL:
530 567
                        AN(reqhttp);
531 567
                        arg1 = reqhttp->hd[HTTP_HDR_URL].b;
532 567
                        break;
533
                case BANS_ARG_REQHTTP:
534 189
                        AN(reqhttp);
535 189
                        CAST_HDR(hdr, bt.arg1_spec);
536 189
                        (void)http_GetHdr(reqhttp, hdr, &p);
537 189
                        arg1 = p;
538 189
                        break;
539
                case BANS_ARG_OBJHTTP:
540 672
                        CAST_HDR(hdr, bt.arg1_spec);
541 672
                        arg1 = HTTP_GetHdrPack(wrk, oc, hdr);
542 672
                        break;
543
                case BANS_ARG_OBJSTATUS:
544 294
                        arg1 = HTTP_GetHdrPack(wrk, oc, H__Status);
545 294
                        break;
546
                case BANS_ARG_OBJTTL:
547 105
                        darg1 = oc->ttl + oc->t_origin;
548 105
                        darg2 = bt.arg2_double + ban_time(bsarg);
549 105
                        break;
550
                case BANS_ARG_OBJAGE:
551 105
                        darg1 = 0.0 - oc->t_origin;
552 105
                        darg2 = 0.0 - (ban_time(bsarg) - bt.arg2_double);
553 105
                        break;
554
                case BANS_ARG_OBJGRACE:
555 63
                        darg1 = oc->grace;
556 63
                        darg2 = bt.arg2_double;
557 63
                        break;
558
                case BANS_ARG_OBJKEEP:
559 147
                        darg1 = oc->keep;
560 147
                        darg2 = bt.arg2_double;
561 147
                        break;
562
                case BANS_ARG_OBJLASTHIT:
563 42
                        if (isnan(oc->last_lru))
564 0
                                return (0);
565 42
                        darg1 = 0.0 - oc->last_lru;
566 42
                        darg2 = 0.0 - (ban_time(bsarg) - bt.arg2_double);
567 42
                        break;
568
                case BANS_ARG_OBJHFM:
569 210
                        barg1 = oc->flags & OC_F_HFM ? 1 : 0;
570 210
                        barg2 = bt.arg2_bool;
571 210
                        break;
572
                case BANS_ARG_OBJHFP:
573 210
                        barg1 = oc->flags & OC_F_HFP ? 1 : 0;
574 210
                        barg2 = bt.arg2_bool;
575 210
                        break;
576
                case BANS_ARG_OBJUNCACHEABLE:
577 315
                        barg1 = (oc->flags & (OC_F_HFM | OC_F_HFP)) ? 1 : 0;
578 315
                        barg2 = bt.arg2_bool;
579 315
                        break;
580
                default:
581 0
                        WRONG("Wrong BAN_ARG code");
582 0
                }
583
584 2919
                switch (bt.oper) {
585
                case BANS_OPER_EQ:
586 1470
                        if (barg1 != -1) {
587 294
                                if (barg1 != barg2)
588 147
                                        return (0);
589 1323
                        } else if (arg1 == NULL) {
590 231
                                if (isnan(darg1) || darg1 != darg2)
591 105
                                        return (0);
592 1071
                        } else if (vstrcmp(arg1, bt.arg2)) {
593 441
                                return (0);
594
                        }
595 777
                        break;
596
                case BANS_OPER_NEQ:
597 693
                        if (barg1 != -1) {
598 441
                                if (barg1 == barg2)
599 147
                                        return (0);
600 546
                        } else if (arg1 == NULL) {
601 147
                                if (! isnan(darg1) && darg1 == darg2)
602 84
                                        return (0);
603 168
                        } else if (!vstrcmp(arg1, bt.arg2)) {
604 42
                                return (0);
605
                        }
606 420
                        break;
607
                case BANS_OPER_MATCH:
608 504
                        if (arg1 == NULL)
609 21
                                return (0);
610 483
                        rv = VRE_match(bt.arg2_spec, arg1, 0, 0, NULL);
611 483
                        xxxassert(rv >= -1);
612 483
                        if (rv < 0)
613 42
                                return (0);
614 441
                        break;
615
                case BANS_OPER_NMATCH:
616 0
                        if (arg1 == NULL)
617 0
                                return (0);
618 0
                        rv = VRE_match(bt.arg2_spec, arg1, 0, 0, NULL);
619 0
                        xxxassert(rv >= -1);
620 0
                        if (rv >= 0)
621 0
                                return (0);
622 0
                        break;
623
                case BANS_OPER_GT:
624 168
                        AZ(arg1);
625 168
                        assert(! isnan(darg1));
626 168
                        if (!(darg1 > darg2))
627 168
                                return (0);
628 0
                        break;
629
                case BANS_OPER_GTE:
630 0
                        AZ(arg1);
631 0
                        assert(! isnan(darg1));
632 0
                        if (!(darg1 >= darg2))
633 0
                                return (0);
634 0
                        break;
635
                case BANS_OPER_LT:
636 63
                        AZ(arg1);
637 63
                        assert(! isnan(darg1));
638 63
                        if (!(darg1 < darg2))
639 21
                                return (0);
640 42
                        break;
641
                case BANS_OPER_LTE:
642 21
                        AZ(arg1);
643 21
                        assert(! isnan(darg1));
644 21
                        if (!(darg1 <= darg2))
645 0
                                return (0);
646 21
                        break;
647
                default:
648 0
                        WRONG("Wrong BAN_OPER code");
649 0
                }
650
        }
651 1407
        return (1);
652 2625
}
653
654
/*--------------------------------------------------------------------
655
 * Check an object against all applicable bans
656
 *
657
 * Return:
658
 *      -1 not all bans checked, but none of the checked matched
659
 *              Only if !has_req
660
 *      0 No bans matched, object moved to ban_start.
661
 *      1 Ban matched, object removed from ban list.
662
 */
663
664
int
665 26866
BAN_CheckObject(struct worker *wrk, struct objcore *oc, struct req *req)
666
{
667
        struct ban *b;
668
        struct vsl_log *vsl;
669
        struct ban *b0, *bn;
670
        unsigned tests;
671
672 26866
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
673 26866
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
674 26866
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
675 26866
        Lck_AssertHeld(&oc->objhead->mtx);
676 26866
        assert(oc->refcnt > 0);
677
678 26866
        vsl = req->vsl;
679
680 26866
        CHECK_OBJ_NOTNULL(oc->ban, BAN_MAGIC);
681
682
        /* First do an optimistic unlocked check */
683 26866
        b0 = ban_start;
684 26866
        CHECK_OBJ_NOTNULL(b0, BAN_MAGIC);
685
686 26866
        if (b0 == oc->ban)
687 25606
                return (0);
688
689
        /* If that fails, make a safe check */
690 1260
        Lck_Lock(&ban_mtx);
691 1260
        b0 = ban_start;
692 1260
        bn = oc->ban;
693 1260
        if (b0 != bn)
694 1260
                bn->refcount++;
695 1260
        Lck_Unlock(&ban_mtx);
696
697 1260
        AN(bn);
698
699 1260
        if (b0 == bn)
700 0
                return (0);
701
702 1260
        AN(b0);
703 1260
        AN(bn);
704
705
        /*
706
         * This loop is safe without locks, because we know we hold
707
         * a refcount on a ban somewhere in the list and we do not
708
         * inspect the list past that ban.
709
         */
710 1260
        tests = 0;
711 2142
        for (b = b0; b != bn; b = VTAILQ_NEXT(b, list)) {
712 1596
                CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
713 1596
                if (b->flags & BANS_FLAG_COMPLETED)
714 399
                        continue;
715 1197
                if (ban_evaluate(wrk, b->spec, oc, req->http, &tests))
716 714
                        break;
717 483
        }
718
719 1260
        Lck_Lock(&ban_mtx);
720 1260
        bn->refcount--;
721 1260
        VSC_C_main->bans_tested++;
722 1260
        VSC_C_main->bans_tests_tested += tests;
723
724 1260
        if (b == bn) {
725
                /* not banned */
726 546
                oc->ban->refcount--;
727 546
                VTAILQ_REMOVE(&oc->ban->objcore, oc, ban_list);
728 546
                VTAILQ_INSERT_TAIL(&b0->objcore, oc, ban_list);
729 546
                b0->refcount++;
730 546
                oc->ban = b0;
731 546
                b = NULL;
732 546
        }
733 1260
        if (b != NULL)
734 714
                VSC_C_main->bans_obj_killed++;
735
736 1260
        if (VTAILQ_LAST(&ban_head, banhead_s)->refcount == 0)
737 441
                ban_kick_lurker();
738
739 1260
        Lck_Unlock(&ban_mtx);
740
741 1260
        if (b == NULL) {
742
                /* not banned */
743 546
                ObjSendEvent(wrk, oc, OEV_BANCHG);
744 546
                return (0);
745
        } else {
746 1428
                VSLb(vsl, SLT_ExpBan,
747 714
                    "%ju banned lookup", VXID(ObjGetXID(wrk, oc)));
748 714
                return (1);
749
        }
750 26866
}
751
752
/*--------------------------------------------------------------------
753
 * CLI functions to add bans
754
 */
755
756
static void v_matchproto_(cli_func_t)
757 2688
ccf_ban(struct cli *cli, const char * const *av, void *priv)
758
{
759
        int narg, i;
760
        struct ban_proto *bp;
761 2688
        const char *err = NULL;
762
763 2688
        (void)priv;
764
765
        /* First do some cheap checks on the arguments */
766 11697
        for (narg = 0; av[narg + 2] != NULL; narg++)
767 9009
                continue;
768 2688
        if ((narg % 4) != 3) {
769 42
                VCLI_Out(cli, "Wrong number of arguments");
770 42
                VCLI_SetResult(cli, CLIS_PARAM);
771 42
                return;
772
        }
773 2835
        for (i = 3; i < narg; i += 4) {
774 210
                if (vstrcmp(av[i + 2], "&&")) {
775 21
                        VCLI_Out(cli, "Found \"%s\" expected &&", av[i + 2]);
776 21
                        VCLI_SetResult(cli, CLIS_PARAM);
777 21
                        return;
778
                }
779 189
        }
780
781 2625
        bp = BAN_Build();
782 2625
        if (bp == NULL) {
783 0
                VCLI_Out(cli, "Out of Memory");
784 0
                VCLI_SetResult(cli, CLIS_CANT);
785 0
                return;
786
        }
787 5040
        for (i = 0; i < narg; i += 4) {
788 2814
                err = BAN_AddTest(bp, av[i + 2], av[i + 3], av[i + 4]);
789 2814
                if (err)
790 399
                        break;
791 2415
        }
792
793 2625
        if (err == NULL) {
794
                // XXX racy - grab wstat lock?
795 2226
                err = BAN_Commit(bp);
796 2226
        }
797
798 2625
        if (err != NULL) {
799 399
                VCLI_Out(cli, "%s", err);
800 399
                BAN_Abandon(bp);
801 399
                VCLI_SetResult(cli, CLIS_PARAM);
802 399
        }
803 2688
}
804
805
#define Ms 60
806
#define Hs (Ms * 60)
807
#define Ds (Hs * 24)
808
#define Ws (Ds * 7)
809
#define Ys (Ds * 365)
810
811
#define Xfmt(buf, var, s, unit)                                         \
812
        ((var) >= s && (var) % s == 0)                                  \
813
                bprintf((buf), "%ju" unit, (var) / s)
814
815
// XXX move to VTIM?
816
#define vdur_render(buf, dur) do {                                      \
817
        uintmax_t dec = (uintmax_t)floor(dur);                          \
818
        uintmax_t frac = (uintmax_t)floor((dur) * 1e3) % UINTMAX_C(1000); \
819
        if (dec == 0 && frac == 0)                                      \
820
                (void) strncpy(buf, "0s", sizeof(buf));                 \
821
        else if (dec == 0)                                              \
822
                bprintf((buf), "%jums", frac);                          \
823
        else if (frac != 0)                                             \
824
                bprintf((buf), "%ju.%03jus", dec, frac);                \
825
        else if Xfmt(buf, dec, Ys, "y");                                \
826
        else if Xfmt(buf, dec, Ws, "w");                                \
827
        else if Xfmt(buf, dec, Ds, "d");                                \
828
        else if Xfmt(buf, dec, Hs, "h");                                \
829
        else if Xfmt(buf, dec, Ms, "m");                                \
830
        else                                                            \
831
                bprintf((buf), "%jus", dec);                            \
832
        } while (0)
833
834
static void
835 6804
ban_render(struct cli *cli, const uint8_t *bs, int quote)
836
{
837
        struct ban_test bt;
838
        const uint8_t *be;
839
        char buf[64];
840
841 6804
        be = bs + ban_len(bs);
842 6804
        bs += BANS_HEAD_LEN;
843 12243
        while (bs < be) {
844 5439
                ban_iter(&bs, &bt);
845 5439
                ASSERT_BAN_ARG(bt.arg1);
846 5439
                ASSERT_BAN_OPER(bt.oper);
847
848 5439
                if (BANS_HAS_ARG1_SPEC(bt.arg1))
849 2562
                        VCLI_Out(cli, "%s%.*s",
850 1281
                            arg_name[BAN_ARGIDX(bt.arg1)],
851 1281
                            bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
852
                else
853 4158
                        VCLI_Out(cli, "%s", arg_name[BAN_ARGIDX(bt.arg1)]);
854
855 5439
                VCLI_Out(cli, " %s ", ban_oper[BAN_OPERIDX(bt.oper)]);
856
857 5439
                if (BANS_HAS_ARG2_DOUBLE(bt.arg1)) {
858 5481
                        vdur_render(buf, bt.arg2_double);
859 2835
                        VCLI_Out(cli, "%s", buf);
860 5439
                } else if (BANS_HAS_ARG2_BOOL(bt.arg1)) {
861 0
                        VCLI_Out(cli, "%s", bt.arg2_bool ? "true" : "false");
862 2604
                } else if (quote) {
863 273
                        VCLI_Quote(cli, bt.arg2);
864 273
                } else {
865 2331
                        VCLI_Out(cli, "%s", bt.arg2);
866
                }
867
868 5439
                if (bs < be)
869 525
                        VCLI_Out(cli, " && ");
870
        }
871 6804
}
872
873
static void
874 1533
ban_list(struct cli *cli, struct ban *bl)
875
{
876
        struct ban *b;
877
        int64_t o;
878
879 1533
        VCLI_Out(cli, "Present bans:\n");
880 7812
        VTAILQ_FOREACH(b, &ban_head, list) {
881 6279
                o = bl == b ? 1 : 0;
882 12558
                VCLI_Out(cli, "%10.6f %5ju %s", ban_time(b->spec),
883 6279
                    (intmax_t)(b->refcount - o),
884 6279
                    b->flags & BANS_FLAG_COMPLETED ? "C" : "-");
885 6279
                if (DO_DEBUG(DBG_LURKER)) {
886 1974
                        VCLI_Out(cli, "%s%s %p ",
887 987
                            b->flags & BANS_FLAG_REQ ? "R" : "-",
888 987
                            b->flags & BANS_FLAG_OBJ ? "O" : "-",
889 987
                            b);
890 987
                }
891 6279
                VCLI_Out(cli, "  ");
892 6279
                ban_render(cli, b->spec, 0);
893 6279
                VCLI_Out(cli, "\n");
894 6279
                if (VCLI_Overflow(cli))
895 0
                        break;
896 6279
                if (DO_DEBUG(DBG_LURKER)) {
897 987
                        Lck_Lock(&ban_mtx);
898
                        struct objcore *oc;
899 1659
                        VTAILQ_FOREACH(oc, &b->objcore, ban_list)
900 672
                                VCLI_Out(cli, "  oc = %p\n", oc);
901 987
                        Lck_Unlock(&ban_mtx);
902 987
                }
903 6279
        }
904 1533
}
905
906
static void
907 126
ban_list_json(struct cli *cli, const char * const *av, struct ban *bl)
908
{
909
        struct ban *b;
910
        int64_t o;
911 126
        int n = 0;
912
        int ocs;
913
914 126
        VCLI_JSON_begin(cli, 2, av);
915 126
        VCLI_Out(cli, ",\n");
916 651
        VTAILQ_FOREACH(b, &ban_head, list) {
917 525
                o = bl == b ? 1 : 0;
918 525
                VCLI_Out(cli, "%s", n ? ",\n" : "");
919 525
                n++;
920 525
                VCLI_Out(cli, "{\n");
921 525
                VSB_indent(cli->sb, 2);
922 525
                VCLI_Out(cli, "\"time\": %.6f,\n", ban_time(b->spec));
923 525
                VCLI_Out(cli, "\"refs\": %ju,\n", (intmax_t)(b->refcount - o));
924 1050
                VCLI_Out(cli, "\"completed\": %s,\n",
925 525
                         b->flags & BANS_FLAG_COMPLETED ? "true" : "false");
926 525
                VCLI_Out(cli, "\"spec\": \"");
927 525
                ban_render(cli, b->spec, 1);
928 525
                VCLI_Out(cli, "\"");
929
930 525
                if (DO_DEBUG(DBG_LURKER)) {
931 126
                        VCLI_Out(cli, ",\n");
932 252
                        VCLI_Out(cli, "\"req_tests\": %s,\n",
933 126
                                 b->flags & BANS_FLAG_REQ ? "true" : "false");
934 252
                        VCLI_Out(cli, "\"obj_tests\": %s,\n",
935 126
                                 b->flags & BANS_FLAG_OBJ ? "true" : "false");
936 126
                        VCLI_Out(cli, "\"pointer\": \"%p\",\n", b);
937 126
                        if (VCLI_Overflow(cli))
938 0
                                break;
939
940 126
                        ocs = 0;
941 126
                        VCLI_Out(cli, "\"objcores\": [\n");
942 126
                        VSB_indent(cli->sb, 2);
943 126
                        Lck_Lock(&ban_mtx);
944
                        struct objcore *oc;
945 126
                        VTAILQ_FOREACH(oc, &b->objcore, ban_list) {
946 0
                                if (ocs)
947 0
                                        VCLI_Out(cli, ",\n");
948 0
                                VCLI_Out(cli, "%p", oc);
949 0
                                ocs++;
950 0
                        }
951 126
                        Lck_Unlock(&ban_mtx);
952 126
                        VSB_indent(cli->sb, -2);
953 126
                        VCLI_Out(cli, "\n]");
954 126
                }
955 525
                VSB_indent(cli->sb, -2);
956 525
                VCLI_Out(cli, "\n}");
957 525
        }
958 126
        VCLI_JSON_end(cli);
959 126
}
960
961
static void v_matchproto_(cli_func_t)
962 1659
ccf_ban_list(struct cli *cli, const char * const *av, void *priv)
963
{
964
        struct ban *bl;
965
966 1659
        (void)priv;
967
968
        /* Get a reference so we are safe to traverse the list */
969 1659
        Lck_Lock(&ban_mtx);
970 1659
        bl = VTAILQ_LAST(&ban_head, banhead_s);
971 1659
        bl->refcount++;
972 1659
        Lck_Unlock(&ban_mtx);
973
974 1659
        if (av[2] != NULL && vstrcmp(av[2], "-j") == 0)
975 126
                ban_list_json(cli, av, bl);
976
        else
977 1533
                ban_list(cli, bl);
978
979 1659
        Lck_Lock(&ban_mtx);
980 1659
        bl->refcount--;
981 1659
        ban_kick_lurker();      // XXX: Mostly for testcase b00009.vtc
982 1659
        Lck_Unlock(&ban_mtx);
983 1659
}
984
985
static struct cli_proto ban_cmds[] = {
986
        { CLICMD_BAN,                           "", ccf_ban },
987
        { CLICMD_BAN_LIST,                      "", ccf_ban_list,
988
          ccf_ban_list },
989
        { NULL }
990
};
991
992
/*--------------------------------------------------------------------
993
 */
994
995
void
996 20841
BAN_Compile(void)
997
{
998
        struct ban *b;
999
1000
        /*
1001
         * All bans have been read from all persistent stevedores. Export
1002
         * the compiled list
1003
         */
1004
1005 20841
        ASSERT_CLI();
1006 20841
        AZ(ban_shutdown);
1007
1008 20841
        Lck_Lock(&ban_mtx);
1009
1010
        /* Report the place-holder ban */
1011 20841
        b = VTAILQ_FIRST(&ban_head);
1012 20841
        ban_info_new(b->spec, ban_len(b->spec));
1013
1014 20841
        ban_export();
1015
1016 20841
        Lck_Unlock(&ban_mtx);
1017
1018 20841
        ban_start = VTAILQ_FIRST(&ban_head);
1019 20841
        BAN_Release();
1020 20841
}
1021
1022
void
1023 20884
BAN_Init(void)
1024
{
1025
        struct ban_proto *bp;
1026
1027 20884
        BAN_Build_Init();
1028 20884
        Lck_New(&ban_mtx, lck_ban);
1029 20884
        CLI_AddFuncs(ban_cmds);
1030
1031 20884
        ban_holds = 1;
1032
1033
        /* Add a placeholder ban */
1034 20884
        bp = BAN_Build();
1035 20884
        AN(bp);
1036 20884
        PTOK(pthread_cond_init(&ban_lurker_cond, NULL));
1037 20884
        AZ(BAN_Commit(bp));
1038 20884
        Lck_Lock(&ban_mtx);
1039 20884
        ban_mark_completed(VTAILQ_FIRST(&ban_head));
1040 20884
        Lck_Unlock(&ban_mtx);
1041 20884
}
1042
1043
/*--------------------------------------------------------------------
1044
 * Shutdown of the ban system.
1045
 *
1046
 * When this function returns, no new bans will be accepted, and no
1047
 * bans will be dropped (ban lurker thread stopped), so that no
1048
 * STV_BanInfo calls will be executed.
1049
 */
1050
1051
void
1052 20622
BAN_Shutdown(void)
1053
{
1054
        void *status;
1055
1056 20622
        Lck_Lock(&ban_mtx);
1057 20622
        ban_shutdown = 1;
1058 20622
        ban_kick_lurker();
1059 20622
        Lck_Unlock(&ban_mtx);
1060
1061 20622
        PTOK(pthread_join(ban_thread, &status));
1062 20622
        AZ(status);
1063
1064 20622
        Lck_Lock(&ban_mtx);
1065
        /* Export the ban list to compact it */
1066 20622
        ban_export();
1067 20622
        Lck_Unlock(&ban_mtx);
1068
1069 20622
        BAN_Build_Fini();
1070 20622
}