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 732
VSC_New(void)
115
{
116
        struct vsc *vsc;
117
118 732
        ALLOC_OBJ(vsc, VSC_MAGIC);
119 732
        if (vsc == NULL)
120 0
                return (vsc);
121 732
        VTAILQ_INIT(&vsc->sf_list_include);
122 732
        VTAILQ_INIT(&vsc->sf_list_exclude);
123 732
        VTAILQ_INIT(&vsc->segs);
124 732
        return (vsc);
125
}
126
127
/*--------------------------------------------------------------------*/
128
129
static int
130 14
vsc_f_arg(struct vsc *vsc, const char *opt)
131
{
132
        struct vsc_sf *sf;
133 14
        unsigned exclude = 0;
134
135 14
        AN(opt);
136
137 14
        ALLOC_OBJ(sf, VSC_SF_MAGIC);
138 14
        AN(sf);
139
140 14
        if (opt[0] == '^') {
141 4
                exclude = 1;
142 4
                opt++;
143
        }
144
145 14
        sf->pattern = strdup(opt);
146 14
        AN(sf->pattern);
147
148 14
        if (exclude)
149 4
                VTAILQ_INSERT_TAIL(&vsc->sf_list_exclude, sf, list);
150
        else
151 10
                VTAILQ_INSERT_TAIL(&vsc->sf_list_include, sf, list);
152
153 14
        return (1);
154
}
155
156
/*--------------------------------------------------------------------*/
157
158
int
159 14
VSC_Arg(struct vsc *vsc, char arg, const char *opt)
160
{
161
162 14
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
163
164 14
        switch (arg) {
165 14
        case 'f': return (vsc_f_arg(vsc, opt));
166 0
        default: return (0);
167
        }
168
}
169
170
171
/*--------------------------------------------------------------------
172
 */
173
174
static int
175 32878
vsc_filter(const struct vsc *vsc, const char *nm)
176
{
177
        struct vsc_sf *sf;
178
179 32878
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
180 33899
        VTAILQ_FOREACH(sf, &vsc->sf_list_exclude, list)
181 1156
                if (!fnmatch(sf->pattern, nm, 0))
182 135
                        return (1);
183 32743
        if (VTAILQ_EMPTY(&vsc->sf_list_include))
184 31327
                return (0);
185 3392
        VTAILQ_FOREACH(sf, &vsc->sf_list_include, list)
186 1988
                if (!fnmatch(sf->pattern, nm, 0))
187 12
                        return (0);
188 1404
        return (1);
189
}
190
191
/*--------------------------------------------------------------------
192
 */
193
194
static void
195 28297
vsc_clean_point(struct vsc_pt *point)
196
{
197 28297
        REPLACE(point->name, NULL);
198 28297
}
199
200
static void
201 32878
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 32878
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
207 32878
        memset(point, 0, sizeof *point);
208
209 32878
        vt = vjsn_child(vv, "name");
210 32878
        AN(vt);
211 32878
        assert(vt->type == VJSN_STRING);
212
213 32878
        VSB_clear(vsb);
214 32878
        VSB_printf(vsb, "%s.%s", seg->fantom->ident, vt->value);
215 32878
        AZ(VSB_finish(vsb));
216
217 32878
        if (vsc_filter(vsc, VSB_data(vsb)))
218 1539
                return;
219
220 31339
        point->name = strdup(VSB_data(vsb));
221 31339
        AN(point->name);
222 31339
        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 31339
        DOF(ctype, "ctype");
231 31339
        DOF(sdesc, "oneliner");
232 31339
        DOF(ldesc, "docs");
233
#undef DOF
234 31339
        vt = vjsn_child(vv, "type");
235 31339
        AN(vt);
236 31339
        assert(vt->type == VJSN_STRING);
237
238 31339
        if (!strcmp(vt->value, "counter")) {
239 25890
                point->point.semantics = 'c';
240 5449
        } else if (!strcmp(vt->value, "gauge")) {
241 5325
                point->point.semantics = 'g';
242 124
        } else if (!strcmp(vt->value, "bitmap")) {
243 124
                point->point.semantics = 'b';
244
        } else {
245 0
                point->point.semantics = '?';
246
        }
247
248 31339
        vt = vjsn_child(vv, "format");
249 31339
        AN(vt);
250 31339
        assert(vt->type == VJSN_STRING);
251
252 31339
        if (!strcmp(vt->value, "integer")) {
253 27367
                point->point.format = 'i';
254 3972
        } else if (!strcmp(vt->value, "bytes")) {
255 3531
                point->point.format = 'B';
256 441
        } else if (!strcmp(vt->value, "bitmap")) {
257 124
                point->point.format = 'b';
258 317
        } else if (!strcmp(vt->value, "duration")) {
259 317
                point->point.format = 'd';
260
        } else {
261 0
                point->point.format = '?';
262
        }
263
264 31339
        vt = vjsn_child(vv, "level");
265 31339
        AN(vt);
266 31339
        assert(vt->type == VJSN_STRING);
267
268 31339
        if (!strcmp(vt->value, "info"))  {
269 15920
                point->point.level = &levels[info];
270 15419
        } else if (!strcmp(vt->value, "diag")) {
271 9171
                point->point.level = &levels[diag];
272 6248
        } else if (!strcmp(vt->value, "debug")) {
273 6248
                point->point.level = &levels[debug];
274
        } else {
275 0
                WRONG("Illegal level");
276
        }
277
278 31339
        vt = vjsn_child(vv, "index");
279 31339
        AN(vt);
280
281 31339
        point->point.ptr = (volatile void*)(seg->body + atoi(vt->value));
282
}
283
284
static void
285 1966
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 1966
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
291 1966
        AN(vsm);
292 1966
        CHECK_OBJ_NOTNULL(sp, VSC_SEG_MAGIC);
293 1966
        AZ(VSM_Unmap(vsm, sp->fantom));
294 1966
        if (sp->vj != NULL) {
295 469
                vjsn_delete(&sp->vj);
296
        } else {
297 1497
                pp = sp->points;
298 29794
                for (u = 0; u < sp->npoints; u++, pp++)
299 28297
                        vsc_clean_point(pp);
300 1497
                free(sp->points);
301
        }
302 1966
        FREE_OBJ(sp);
303 1966
}
304
305
static struct vsc_seg *
306 2389
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 2389
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
315 2389
        AN(vsm);
316
317 2389
        ALLOC_OBJ(sp, VSC_SEG_MAGIC);
318 2389
        AN(sp);
319 2389
        *sp->fantom = *fp;
320 2389
        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 2389
        sp->head = sp->fantom->b;
329 2389
        if (sp->head->ready == 0) {
330 0
                VRMB();
331 0
                usleep(100000);
332
        }
333 2389
        assert(sp->head->ready > 0);
334 2389
        sp->body = (char*)sp->fantom->b + sp->head->body_offset;
335
336 2389
        if (!strcmp(fp->class, VSC_CLASS)) {
337 22230
                VTAILQ_FOREACH(spd, &vsc->segs, list)
338 22230
                        if (spd->head->doc_id == sp->head->doc_id)
339 1866
                                break;
340 1866
                AN(spd);
341
                // XXX: Refcount ?
342 1866
                vve = vjsn_child(spd->vj->value, "elements");
343 1866
                AN(vve);
344 1866
                sp->npoints = strtoul(vve->value, NULL, 0);
345 1866
                sp->points = calloc(sp->npoints, sizeof *sp->points);
346 1866
                AN(sp->points);
347 1866
                vsb = VSB_new_auto();
348 1866
                AN(vsb);
349 1866
                vve = vjsn_child(spd->vj->value, "elem");
350 1866
                AN(vve);
351 1866
                pp = sp->points;
352 34744
                VTAILQ_FOREACH(vv, &vve->children, list) {
353 32878
                        vsc_fill_point(vsc, sp, vv, vsb, pp);
354 32878
                        pp++;
355
                }
356 1866
                VSB_destroy(&vsb);
357 1866
                return (sp);
358
        }
359 523
        assert(!strcmp(fp->class, VSC_DOC_CLASS));
360 523
        sp->vj = vjsn_parse(sp->body, &e);
361 523
        XXXAZ(e);
362 523
        AN(sp->vj);
363 523
        return (sp);
364
}
365
366
/*--------------------------------------------------------------------
367
 */
368
369
static void
370 22214
vsc_expose(const struct vsc *vsc, const struct vsc_seg *sp, int del)
371
{
372
        struct vsc_pt *pp;
373
        unsigned u;
374
375 22214
        pp = sp->points;
376 260900
        for (u = 0; u < sp->npoints; u++, pp++) {
377 238686
                if (pp->name == NULL)
378 2373
                        continue;
379 236313
                if (vsc->fdestroy != NULL && pp->exposed &&
380 3468
                    (del || sp->head->ready == 2)) {
381 289
                        vsc->fdestroy(vsc->priv, &pp->point);
382 289
                        pp->exposed = 0;
383
                }
384 236313
                if (vsc->fnew != NULL && !pp->exposed &&
385 289
                    !del && sp->head->ready == 1) {
386 289
                        pp->point.priv = vsc->fnew(vsc->priv, &pp->point);
387 289
                        pp->exposed = 1;
388
                }
389
        }
390 22214
}
391
392
/*--------------------------------------------------------------------
393
 */
394
395
static int
396 19529
vsc_iter_seg(const struct vsc *vsc, const struct vsc_seg *sp,
397
    VSC_iter_f *fiter, void *priv)
398
{
399
        unsigned u;
400 19529
        int i = 0;
401
        struct vsc_pt *pp;
402
403 19529
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
404 19529
        CHECK_OBJ_NOTNULL(sp, VSC_SEG_MAGIC);
405 19529
        AN(fiter);
406 19529
        pp = sp->points;
407 195363
        for (u = 0; u < sp->npoints && i == 0; u++, pp++) {
408 175834
                if (pp->name != NULL)
409 175081
                        i = fiter(priv, &pp->point);
410
        }
411 19529
        return (i);
412
}
413
414
int
415 947
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 947
        int i = 0;
420
421 947
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
422 947
        AN(vsm);
423 947
        sp = VTAILQ_FIRST(&vsc->segs);
424 26809
        VSM_FOREACH(&ifantom, vsm) {
425 34595
                if (strcmp(ifantom.class, VSC_CLASS) &&
426 9062
                    strcmp(ifantom.class, VSC_DOC_CLASS))
427 5285
                        continue;
428 20248
                sp2 = sp;
429 58385
                while (sp != NULL &&
430 35740
                    (strcmp(ifantom.ident, sp->fantom->ident) ||
431 17866
                    VSM_StillValid(vsm, sp->fantom) != VSM_valid)) {
432 15
                        sp2 = sp;
433 15
                        sp = VTAILQ_NEXT(sp, list);
434 15
                        VTAILQ_REMOVE(&vsc->segs, sp2, list);
435 15
                        vsc_expose(vsc, sp2, 1);
436 15
                        vsc_del_seg(vsc, vsm, sp2);
437
                }
438 20248
                if (sp == NULL) {
439 2389
                        sp = vsc_add_seg(vsc, vsm, &ifantom);
440 2389
                        if (sp != NULL) {
441 2389
                                VTAILQ_INSERT_TAIL(&vsc->segs, sp, list);
442 2389
                                sp2 = NULL;
443 2389
                                vsc_expose(vsc, sp, 0);
444
                        }
445
                } else {
446 17859
                        vsc_expose(vsc, sp, 0);
447 17859
                        sp2 = VTAILQ_NEXT(sp, list);
448
                }
449 20248
                if (sp != NULL && fiter != NULL && sp->head->ready < 2)
450 19529
                        i = vsc_iter_seg(vsc, sp, fiter, priv);
451 20248
                sp = sp2;
452 20248
                if (i)
453 618
                        break;
454
        }
455 2529
        while (sp != NULL) {
456 635
                sp2 = sp;
457 635
                sp = VTAILQ_NEXT(sp, list);
458 635
                VTAILQ_REMOVE(&vsc->segs, sp2, list);
459 635
                vsc_expose(vsc, sp2, 1);
460 635
                vsc_del_seg(vsc, vsm, sp2);
461
        }
462 947
        return (i);
463
}
464
465
/*--------------------------------------------------------------------
466
 */
467
468
void
469 1
VSC_State(struct vsc *vsc, VSC_new_f *fn, VSC_destroy_f *fd, void *priv)
470
{
471
        struct vsc_seg *sp;
472
473 1
        CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC);
474 1
        assert((fn == NULL && fd == NULL) || (fn != NULL && fd != NULL));
475 1
        if (fd == NULL) {
476 0
                VTAILQ_FOREACH(sp, &vsc->segs, list)
477 0
                        vsc_expose(vsc, sp, 1);
478
        }
479 1
        vsc->fnew = fn;
480 1
        vsc->fdestroy = fd;
481 1
        vsc->priv = priv;
482 1
}
483
484
/*--------------------------------------------------------------------
485
 */
486
487
const struct VSC_level_desc *
488 2
VSC_ChangeLevel(const struct VSC_level_desc *old, int chg)
489
{
490
        int i;
491
492 2
        if (old == NULL)
493 1
                old = &levels[0];
494 2
        for (i = 0; i < nlevels; i++)
495 2
                if (old == &levels[i])
496 2
                        break;
497 2
        if (i == nlevels)
498 0
                i = 0;
499
500 2
        i += chg;
501 2
        if (i >= nlevels)
502 0
                i = nlevels - 1;
503 2
        if (i < 0)
504 0
                i = 0;
505 2
        return (&levels[i]);
506
}
507
508
/*--------------------------------------------------------------------*/
509
510
static void
511 1432
vsc_delete_sf_list(struct vsc_sf_head *head)
512
{
513
        struct vsc_sf *sf;
514
515 2870
        while (!VTAILQ_EMPTY(head)) {
516 6
                sf = VTAILQ_FIRST(head);
517 6
                CHECK_OBJ_NOTNULL(sf, VSC_SF_MAGIC);
518 6
                VTAILQ_REMOVE(head, sf, list);
519 6
                free(sf->pattern);
520 6
                FREE_OBJ(sf);
521
        }
522 1432
}
523
524
void
525 716
VSC_Destroy(struct vsc **vscp, struct vsm *vsm)
526
{
527
        struct vsc *vsc;
528
        struct vsc_seg *sp, *sp2;
529
530 716
        TAKE_OBJ_NOTNULL(vsc, vscp, VSC_MAGIC);
531 716
        vsc_delete_sf_list(&vsc->sf_list_include);
532 716
        vsc_delete_sf_list(&vsc->sf_list_exclude);
533 2032
        VTAILQ_FOREACH_SAFE(sp, &vsc->segs, list, sp2) {
534 1316
                VTAILQ_REMOVE(&vsc->segs, sp, list);
535 1316
                vsc_expose(vsc, sp, 1);
536 1316
                vsc_del_seg(vsc, vsm, sp);
537
        }
538 716
        FREE_OBJ(vsc);
539 716
}
540