varnish-cache/lib/libvarnishapi/vsc.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
 * Author: Martin Blix Grydeland <martin@varnish-software.com>
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
#include "config.h"
32
33
#include <sys/stat.h>
34
35
#include <fnmatch.h>
36
#include <stdint.h>
37
#include <stdio.h>
38
#include <stdlib.h>
39
#include <string.h>
40
#include <unistd.h>
41
42
#include "vdef.h"
43
#include "vas.h"
44
#include "miniobj.h"
45
#include "vqueue.h"
46
#include "vjsn.h"
47
#include "vsb.h"
48
#include "vsc_priv.h"
49
#include "vmb.h"
50
51
#include "vapi/vsc.h"
52
#include "vapi/vsm.h"
53
54
struct vsc_sf {
55
        unsigned                magic;
56
#define VSC_SF_MAGIC            0x558478dd
57
        VTAILQ_ENTRY(vsc_sf)    list;
58
        char                    *pattern;
59
};
60
VTAILQ_HEAD(vsc_sf_head, vsc_sf);
61
62
struct vsc_pt {
63
        struct VSC_point        point;
64
        char                    *name;
65
        int                     exposed;
66
};
67
68
struct vsc_seg {
69
        unsigned                magic;
70
#define VSC_SEG_MAGIC           0x801177d4
71
        VTAILQ_ENTRY(vsc_seg)   list;
72
        struct vsm_fantom       fantom[1];
73
        struct vsc_head         *head;
74
        char                    *body;
75
76
        struct vjsn             *vj;
77
78
        unsigned                npoints;
79
        struct vsc_pt           *points;
80
};
81
82
struct vsc {
83
        unsigned                magic;
84
#define VSC_MAGIC               0x3373554a
85
86
        struct vsc_sf_head      sf_list_include;
87
        struct vsc_sf_head      sf_list_exclude;
88
        VTAILQ_HEAD(,vsc_seg)   segs;
89
90
        VSC_new_f               *fnew;
91
        VSC_destroy_f           *fdestroy;
92
        void                    *priv;
93
};
94
95
/*--------------------------------------------------------------------
96
 * Build the static level, type and point descriptions
97
 */
98
99
enum vsc_levels {
100
#define VSC_LEVEL_F(v,l,e,d) v,
101
#include "tbl/vsc_levels.h"
102
};
103
104
static const struct VSC_level_desc levels[] = {
105
#define VSC_LEVEL_F(v,l,e,d) [v] = {#v, l, e, d},
106
#include "tbl/vsc_levels.h"
107
};
108
109
static const ssize_t nlevels = sizeof(levels)/sizeof(*levels);
110
111
/*--------------------------------------------------------------------*/
112
113
struct vsc *
114 1470
VSC_New(void)
115
{
116
        struct vsc *vsc;
117
118 1470
        ALLOC_OBJ(vsc, VSC_MAGIC);
119 1470
        if (vsc == NULL)
120 0
                return (vsc);
121 1470
        VTAILQ_INIT(&vsc->sf_list_include);
122 1470
        VTAILQ_INIT(&vsc->sf_list_exclude);
123 1470
        VTAILQ_INIT(&vsc->segs);
124 1470
        return (vsc);
125
}
126
127
/*--------------------------------------------------------------------*/
128
129
static int
130 28
vsc_f_arg(struct vsc *vsc, const char *opt)
131
{
132
        struct vsc_sf *sf;
133 28
        unsigned exclude = 0;
134
135 28
        AN(opt);
136
137 28
        ALLOC_OBJ(sf, VSC_SF_MAGIC);
138 28
        AN(sf);
139
140 28
        if (opt[0] == '^') {
141 8
                exclude = 1;
142 8
                opt++;
143
        }
144
145 28
        sf->pattern = strdup(opt);
146 28
        AN(sf->pattern);
147
148 28
        if (exclude)
149 8
                VTAILQ_INSERT_TAIL(&vsc->sf_list_exclude, sf, list);
150
        else
151 20
                VTAILQ_INSERT_TAIL(&vsc->sf_list_include, sf, list);
152
153 28
        return (1);
154
}
155
156
/*--------------------------------------------------------------------*/
157
158
int
159 28
VSC_Arg(struct vsc *vsc, char arg, const char *opt)
160
{
161
162 28
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
163
164 28
        switch (arg) {
165 28
        case 'f': return (vsc_f_arg(vsc, opt));
166 0
        default: return (0);
167
        }
168
}
169
170
171
/*--------------------------------------------------------------------
172
 */
173
174
static int
175 68950
vsc_filter(const struct vsc *vsc, const char *nm)
176
{
177
        struct vsc_sf *sf;
178
179 68950
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
180 71022
        VTAILQ_FOREACH(sf, &vsc->sf_list_exclude, list)
181 2352
                if (!fnmatch(sf->pattern, nm, 0))
182 280
                        return (1);
183 68670
        if (VTAILQ_EMPTY(&vsc->sf_list_include))
184 65758
                return (0);
185 6964
        VTAILQ_FOREACH(sf, &vsc->sf_list_include, list)
186 4076
                if (!fnmatch(sf->pattern, nm, 0))
187 24
                        return (0);
188 2888
        return (1);
189
}
190
191
/*--------------------------------------------------------------------
192
 */
193
194
static void
195 59698
vsc_clean_point(struct vsc_pt *point)
196
{
197 59698
        REPLACE(point->name, NULL);
198 59698
}
199
200
static void
201 68950
vsc_fill_point(const struct vsc *vsc, const struct vsc_seg *seg,
202
    const struct vjsn_val *vv, struct vsb *vsb, struct vsc_pt *point)
203
{
204
        struct vjsn_val *vt;
205
206 68950
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
207 68950
        memset(point, 0, sizeof *point);
208
209 68950
        vt = vjsn_child(vv, "name");
210 68950
        AN(vt);
211 68950
        assert(vt->type == VJSN_STRING);
212
213 68950
        VSB_clear(vsb);
214 68950
        VSB_printf(vsb, "%s.%s", seg->fantom->ident, vt->value);
215 68950
        AZ(VSB_finish(vsb));
216
217 68950
        if (vsc_filter(vsc, VSB_data(vsb)))
218 3168
                return;
219
220 65782
        point->name = strdup(VSB_data(vsb));
221 65782
        AN(point->name);
222 65782
        point->point.name = point->name;
223
224
#define DOF(n, k)                               \
225
        vt = vjsn_child(vv, k);                 \
226
        AN(vt);                                 \
227
        assert(vt->type == VJSN_STRING);        \
228
        point->point.n = vt->value;
229
230 65782
        DOF(ctype, "ctype");
231 65782
        DOF(sdesc, "oneliner");
232 65782
        DOF(ldesc, "docs");
233
#undef DOF
234 65782
        vt = vjsn_child(vv, "type");
235 65782
        AN(vt);
236 65782
        assert(vt->type == VJSN_STRING);
237
238 65782
        if (!strcmp(vt->value, "counter")) {
239 56316
                point->point.semantics = 'c';
240 9466
        } else if (!strcmp(vt->value, "gauge")) {
241 9220
                point->point.semantics = 'g';
242 246
        } else if (!strcmp(vt->value, "bitmap")) {
243 246
                point->point.semantics = 'b';
244
        } else {
245 0
                point->point.semantics = '?';
246
        }
247
248 65782
        vt = vjsn_child(vv, "format");
249 65782
        AN(vt);
250 65782
        assert(vt->type == VJSN_STRING);
251
252 65782
        if (!strcmp(vt->value, "integer")) {
253 57834
                point->point.format = 'i';
254 7948
        } else if (!strcmp(vt->value, "bytes")) {
255 7044
                point->point.format = 'B';
256 904
        } else if (!strcmp(vt->value, "bitmap")) {
257 246
                point->point.format = 'b';
258 658
        } else if (!strcmp(vt->value, "duration")) {
259 658
                point->point.format = 'd';
260
        } else {
261 0
                point->point.format = '?';
262
        }
263
264 65782
        vt = vjsn_child(vv, "level");
265 65782
        AN(vt);
266 65782
        assert(vt->type == VJSN_STRING);
267
268 65782
        if (!strcmp(vt->value, "info"))  {
269 32600
                point->point.level = &levels[info];
270 33182
        } else if (!strcmp(vt->value, "diag")) {
271 20594
                point->point.level = &levels[diag];
272 12588
        } else if (!strcmp(vt->value, "debug")) {
273 12588
                point->point.level = &levels[debug];
274
        } else {
275 0
                WRONG("Illegal level");
276
        }
277
278 65782
        vt = vjsn_child(vv, "index");
279 65782
        AN(vt);
280
281 65782
        point->point.ptr = (volatile void*)(seg->body + atoi(vt->value));
282
}
283
284
static void
285 3950
vsc_del_seg(const struct vsc *vsc, struct vsm *vsm, struct vsc_seg *sp)
286
{
287
        unsigned u;
288
        struct vsc_pt *pp;
289
290 3950
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
291 3950
        AN(vsm);
292 3950
        CHECK_OBJ_NOTNULL(sp, VSC_SEG_MAGIC);
293 3950
        AZ(VSM_Unmap(vsm, sp->fantom));
294 3950
        if (sp->vj != NULL) {
295 960
                vjsn_delete(&sp->vj);
296
        } else {
297 2990
                pp = sp->points;
298 62688
                for (u = 0; u < sp->npoints; u++, pp++)
299 59698
                        vsc_clean_point(pp);
300 2990
                free(sp->points);
301
        }
302 3950
        FREE_OBJ(sp);
303 3950
}
304
305
static struct vsc_seg *
306 4796
vsc_add_seg(const struct vsc *vsc, struct vsm *vsm, const struct vsm_fantom *fp)
307
{
308
        struct vsc_seg *sp, *spd;
309
        const char *e;
310
        struct vjsn_val *vv, *vve;
311
        struct vsb *vsb;
312
        struct vsc_pt *pp;
313
314 4796
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
315 4796
        AN(vsm);
316
317 4796
        ALLOC_OBJ(sp, VSC_SEG_MAGIC);
318 4796
        AN(sp);
319 4796
        *sp->fantom = *fp;
320 4796
        if (VSM_Map(vsm, sp->fantom)) {
321
                /*
322
                 * If the seg was removed between our call to VSM_Status()
323
                 * and now, we won't be able to map it.
324
                 */
325 0
                FREE_OBJ(sp);
326 0
                return (NULL);
327
        }
328 4796
        sp->head = sp->fantom->b;
329 4796
        if (sp->head->ready == 0) {
330 0
                VRMB();
331 0
                usleep(100000);
332
        }
333 4796
        assert(sp->head->ready > 0);
334 4796
        sp->body = (char*)sp->fantom->b + sp->head->body_offset;
335
336 4796
        if (!strcmp(fp->class, VSC_CLASS)) {
337 43642
                VTAILQ_FOREACH(spd, &vsc->segs, list)
338 43642
                        if (spd->head->doc_id == sp->head->doc_id)
339 3728
                                break;
340 3728
                AN(spd);
341
                // XXX: Refcount ?
342 3728
                vve = vjsn_child(spd->vj->value, "elements");
343 3728
                AN(vve);
344 3728
                sp->npoints = strtoul(vve->value, NULL, 0);
345 3728
                sp->points = calloc(sp->npoints, sizeof *sp->points);
346 3728
                AN(sp->points);
347 3728
                vsb = VSB_new_auto();
348 3728
                AN(vsb);
349 3728
                vve = vjsn_child(spd->vj->value, "elem");
350 3728
                AN(vve);
351 3728
                pp = sp->points;
352 72678
                VTAILQ_FOREACH(vv, &vve->children, list) {
353 68950
                        vsc_fill_point(vsc, sp, vv, vsb, pp);
354 68950
                        pp++;
355
                }
356 3728
                VSB_destroy(&vsb);
357 3728
                return (sp);
358
        }
359 1068
        assert(!strcmp(fp->class, VSC_DOC_CLASS));
360 1068
        sp->vj = vjsn_parse(sp->body, &e);
361 1068
        XXXAZ(e);
362 1068
        AN(sp->vj);
363 1068
        return (sp);
364
}
365
366
/*--------------------------------------------------------------------
367
 */
368
369
static void
370 44426
vsc_expose(const struct vsc *vsc, const struct vsc_seg *sp, int del)
371
{
372
        struct vsc_pt *pp;
373
        unsigned u;
374
375 44426
        pp = sp->points;
376 535539
        for (u = 0; u < sp->npoints; u++, pp++) {
377 491113
                if (pp->name == NULL)
378 4896
                        continue;
379 486217
                if (vsc->fdestroy != NULL && pp->exposed &&
380 7056
                    (del || sp->head->ready == 2)) {
381 588
                        vsc->fdestroy(vsc->priv, &pp->point);
382 588
                        pp->exposed = 0;
383
                }
384 486217
                if (vsc->fnew != NULL && !pp->exposed &&
385 588
                    !del && sp->head->ready == 1) {
386 588
                        pp->point.priv = vsc->fnew(vsc->priv, &pp->point);
387 588
                        pp->exposed = 1;
388
                }
389
        }
390 44426
}
391
392
/*--------------------------------------------------------------------
393
 */
394
395
static int
396 39038
vsc_iter_seg(const struct vsc *vsc, const struct vsc_seg *sp,
397
    VSC_iter_f *fiter, void *priv)
398
{
399
        unsigned u;
400 39038
        int i = 0;
401
        struct vsc_pt *pp;
402
403 39038
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
404 39038
        CHECK_OBJ_NOTNULL(sp, VSC_SEG_MAGIC);
405 39038
        AN(fiter);
406 39038
        pp = sp->points;
407 398833
        for (u = 0; u < sp->npoints && i == 0; u++, pp++) {
408 359795
                if (pp->name != NULL)
409 358259
                        i = fiter(priv, &pp->point);
410
        }
411 39038
        return (i);
412
}
413
414
int
415 1905
VSC_Iter(struct vsc *vsc, struct vsm *vsm, VSC_iter_f *fiter, void *priv)
416
{
417
        struct vsm_fantom ifantom;
418
        struct vsc_seg *sp, *sp2;
419 1905
        int i = 0;
420
421 1905
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
422 1905
        AN(vsm);
423 1905
        sp = VTAILQ_FIRST(&vsc->segs);
424 53665
        VSM_FOREACH(&ifantom, vsm) {
425 69298
                if (strcmp(ifantom.class, VSC_CLASS) &&
426 18196
                    strcmp(ifantom.class, VSC_DOC_CLASS))
427 10626
                        continue;
428 40476
                sp2 = sp;
429 116692
                while (sp != NULL &&
430 71404
                    (strcmp(ifantom.ident, sp->fantom->ident) ||
431 35694
                    VSM_StillValid(vsm, sp->fantom) != VSM_valid)) {
432 30
                        sp2 = sp;
433 30
                        sp = VTAILQ_NEXT(sp, list);
434 30
                        VTAILQ_REMOVE(&vsc->segs, sp2, list);
435 30
                        vsc_expose(vsc, sp2, 1);
436 30
                        vsc_del_seg(vsc, vsm, sp2);
437
                }
438 40476
                if (sp == NULL) {
439 4796
                        sp = vsc_add_seg(vsc, vsm, &ifantom);
440 4796
                        if (sp != NULL) {
441 4796
                                VTAILQ_INSERT_TAIL(&vsc->segs, sp, list);
442 4796
                                sp2 = NULL;
443 4796
                                vsc_expose(vsc, sp, 0);
444
                        }
445
                } else {
446 35680
                        vsc_expose(vsc, sp, 0);
447 35680
                        sp2 = VTAILQ_NEXT(sp, list);
448
                }
449 40476
                if (sp != NULL && fiter != NULL && sp->head->ready < 2)
450 39038
                        i = vsc_iter_seg(vsc, sp, fiter, priv);
451 40476
                sp = sp2;
452 40476
                if (i)
453 1247
                        break;
454
        }
455 5047
        while (sp != NULL) {
456 1237
                sp2 = sp;
457 1237
                sp = VTAILQ_NEXT(sp, list);
458 1237
                VTAILQ_REMOVE(&vsc->segs, sp2, list);
459 1237
                vsc_expose(vsc, sp2, 1);
460 1237
                vsc_del_seg(vsc, vsm, sp2);
461
        }
462 1905
        return (i);
463
}
464
465
/*--------------------------------------------------------------------
466
 */
467
468
void
469 2
VSC_State(struct vsc *vsc, VSC_new_f *fn, VSC_destroy_f *fd, void *priv)
470
{
471
        struct vsc_seg *sp;
472
473 2
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
474 2
        assert((fn == NULL && fd == NULL) || (fn != NULL && fd != NULL));
475 2
        if (fd == NULL) {
476 0
                VTAILQ_FOREACH(sp, &vsc->segs, list)
477 0
                        vsc_expose(vsc, sp, 1);
478
        }
479 2
        vsc->fnew = fn;
480 2
        vsc->fdestroy = fd;
481 2
        vsc->priv = priv;
482 2
}
483
484
/*--------------------------------------------------------------------
485
 */
486
487
const struct VSC_level_desc *
488 4
VSC_ChangeLevel(const struct VSC_level_desc *old, int chg)
489
{
490
        int i;
491
492 4
        if (old == NULL)
493 2
                old = &levels[0];
494 4
        for (i = 0; i < nlevels; i++)
495 4
                if (old == &levels[i])
496 4
                        break;
497 4
        if (i == nlevels)
498 0
                i = 0;
499
500 4
        i += chg;
501 4
        if (i >= nlevels)
502 0
                i = nlevels - 1;
503 4
        if (i < 0)
504 0
                i = 0;
505 4
        return (&levels[i]);
506
}
507
508
/*--------------------------------------------------------------------*/
509
510
static void
511 2876
vsc_delete_sf_list(struct vsc_sf_head *head)
512
{
513
        struct vsc_sf *sf;
514
515 5764
        while (!VTAILQ_EMPTY(head)) {
516 12
                sf = VTAILQ_FIRST(head);
517 12
                CHECK_OBJ_NOTNULL(sf, VSC_SF_MAGIC);
518 12
                VTAILQ_REMOVE(head, sf, list);
519 12
                free(sf->pattern);
520 12
                FREE_OBJ(sf);
521
        }
522 2876
}
523
524
void
525 1438
VSC_Destroy(struct vsc **vscp, struct vsm *vsm)
526
{
527
        struct vsc *vsc;
528
        struct vsc_seg *sp, *sp2;
529
530 1438
        TAKE_OBJ_NOTNULL(vsc, vscp, VSC_MAGIC);
531 1438
        vsc_delete_sf_list(&vsc->sf_list_include);
532 1438
        vsc_delete_sf_list(&vsc->sf_list_exclude);
533 4121
        VTAILQ_FOREACH_SAFE(sp, &vsc->segs, list, sp2) {
534 2683
                VTAILQ_REMOVE(&vsc->segs, sp, list);
535 2683
                vsc_expose(vsc, sp, 1);
536 2683
                vsc_del_seg(vsc, vsm, sp);
537
        }
538 1438
        FREE_OBJ(vsc);
539 1438
}
540