| | varnish-cache/lib/libvarnishapi/vsc.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 |
|
* Author: Martin Blix Grydeland <martin@varnish-software.com> |
| 7 |
|
* |
| 8 |
|
* SPDX-License-Identifier: BSD-2-Clause |
| 9 |
|
* |
| 10 |
|
* Redistribution and use in source and binary forms, with or without |
| 11 |
|
* modification, are permitted provided that the following conditions |
| 12 |
|
* are met: |
| 13 |
|
* 1. Redistributions of source code must retain the above copyright |
| 14 |
|
* notice, this list of conditions and the following disclaimer. |
| 15 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
| 16 |
|
* notice, this list of conditions and the following disclaimer in the |
| 17 |
|
* documentation and/or other materials provided with the distribution. |
| 18 |
|
* |
| 19 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 20 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 21 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 22 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
| 23 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 24 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 25 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 26 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 27 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 28 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 29 |
|
* SUCH DAMAGE. |
| 30 |
|
*/ |
| 31 |
|
|
| 32 |
|
#include "config.h" |
| 33 |
|
|
| 34 |
|
#include <sys/stat.h> |
| 35 |
|
|
| 36 |
|
#include <fnmatch.h> |
| 37 |
|
#include <stdint.h> |
| 38 |
|
#include <stdio.h> |
| 39 |
|
#include <stdlib.h> |
| 40 |
|
#include <string.h> |
| 41 |
|
#include <unistd.h> |
| 42 |
|
|
| 43 |
|
#include "vdef.h" |
| 44 |
|
#include "vas.h" |
| 45 |
|
#include "miniobj.h" |
| 46 |
|
#include "vqueue.h" |
| 47 |
|
#include "vjsn.h" |
| 48 |
|
#include "vsb.h" |
| 49 |
|
#include "vsc_priv.h" |
| 50 |
|
#include "vtim.h" |
| 51 |
|
|
| 52 |
|
#include "vapi/vsc.h" |
| 53 |
|
#include "vapi/vsm.h" |
| 54 |
|
|
| 55 |
|
struct vsc_sf_mode { |
| 56 |
|
const char *name; |
| 57 |
|
unsigned include; |
| 58 |
|
unsigned fail; |
| 59 |
|
unsigned append; |
| 60 |
|
}; |
| 61 |
|
|
| 62 |
|
static const struct vsc_sf_mode VSC_SF_INCLUDE[1] = {{"include", 1, 1, 1}}; |
| 63 |
|
static const struct vsc_sf_mode VSC_SF_EXCLUDE[1] = {{"exclude", 0, 0, 1}}; |
| 64 |
|
static const struct vsc_sf_mode VSC_SF_REQUIRE[1] = {{"require", 1, 0, 0}}; |
| 65 |
|
|
| 66 |
|
struct vsc_sf { |
| 67 |
|
unsigned magic; |
| 68 |
|
#define VSC_SF_MAGIC 0x558478dd |
| 69 |
|
VTAILQ_ENTRY(vsc_sf) list; |
| 70 |
|
char *pattern; |
| 71 |
|
const struct vsc_sf_mode *mode; |
| 72 |
|
}; |
| 73 |
|
|
| 74 |
|
VTAILQ_HEAD(vsc_sf_head, vsc_sf); |
| 75 |
|
|
| 76 |
|
struct vsc_pt { |
| 77 |
|
struct VSC_point point; |
| 78 |
|
char *name; |
| 79 |
|
}; |
| 80 |
|
|
| 81 |
|
enum vsc_seg_type { |
| 82 |
|
VSC_SEG_COUNTERS = 1, |
| 83 |
|
VSC_SEG_DOCS, |
| 84 |
|
}; |
| 85 |
|
|
| 86 |
|
struct vsc_seg { |
| 87 |
|
unsigned magic; |
| 88 |
|
#define VSC_SEG_MAGIC 0x801177d4 |
| 89 |
|
enum vsc_seg_type type; |
| 90 |
|
VTAILQ_ENTRY(vsc_seg) list; |
| 91 |
|
VTAILQ_ENTRY(vsc_seg) doc_list; |
| 92 |
|
struct vsm_fantom fantom[1]; |
| 93 |
|
const struct vsc_head *head; |
| 94 |
|
const char *body; |
| 95 |
|
|
| 96 |
|
struct vjsn *vj; |
| 97 |
|
|
| 98 |
|
unsigned npoints; |
| 99 |
|
struct vsc_pt *points; |
| 100 |
|
|
| 101 |
|
int mapped; |
| 102 |
|
int exposed; |
| 103 |
|
}; |
| 104 |
|
VTAILQ_HEAD(vsc_seg_head, vsc_seg); |
| 105 |
|
|
| 106 |
|
struct vsc { |
| 107 |
|
unsigned magic; |
| 108 |
|
#define VSC_MAGIC 0x3373554a |
| 109 |
|
|
| 110 |
|
unsigned raw; |
| 111 |
|
struct vsc_sf_head sf_list; |
| 112 |
|
struct vsc_seg_head segs; |
| 113 |
|
struct vsc_seg_head docs; |
| 114 |
|
|
| 115 |
|
VSC_new_f *fnew; |
| 116 |
|
VSC_destroy_f *fdestroy; |
| 117 |
|
void *priv; |
| 118 |
|
}; |
| 119 |
|
|
| 120 |
|
/*-------------------------------------------------------------------- |
| 121 |
|
* Build the static level, type and point descriptions |
| 122 |
|
*/ |
| 123 |
|
|
| 124 |
|
enum vsc_levels { |
| 125 |
|
#define VSC_LEVEL_F(v,l,e,d) v, |
| 126 |
|
#include "tbl/vsc_levels.h" |
| 127 |
|
}; |
| 128 |
|
|
| 129 |
|
static const struct VSC_level_desc levels[] = { |
| 130 |
|
#define VSC_LEVEL_F(v,l,e,d) [v] = {#v, l, e, d}, |
| 131 |
|
#include "tbl/vsc_levels.h" |
| 132 |
|
}; |
| 133 |
|
|
| 134 |
|
static const ssize_t nlevels = vcountof(levels); |
| 135 |
|
|
| 136 |
|
/*--------------------------------------------------------------------*/ |
| 137 |
|
|
| 138 |
|
struct vsc * |
| 139 |
3006 |
VSC_New(void) |
| 140 |
|
{ |
| 141 |
|
struct vsc *vsc; |
| 142 |
|
|
| 143 |
3006 |
ALLOC_OBJ(vsc, VSC_MAGIC); |
| 144 |
3006 |
if (vsc == NULL) |
| 145 |
0 |
return (vsc); |
| 146 |
3006 |
VTAILQ_INIT(&vsc->sf_list); |
| 147 |
3006 |
VTAILQ_INIT(&vsc->segs); |
| 148 |
3006 |
VTAILQ_INIT(&vsc->docs); |
| 149 |
3006 |
return (vsc); |
| 150 |
3006 |
} |
| 151 |
|
|
| 152 |
|
/*--------------------------------------------------------------------*/ |
| 153 |
|
|
| 154 |
|
static int |
| 155 |
111 |
vsc_sf_arg(struct vsc *vsc, const char *glob, const struct vsc_sf_mode *mode) |
| 156 |
|
{ |
| 157 |
|
struct vsc_sf *sf; |
| 158 |
|
|
| 159 |
111 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 160 |
111 |
AN(glob); |
| 161 |
111 |
AN(mode); |
| 162 |
|
|
| 163 |
111 |
ALLOC_OBJ(sf, VSC_SF_MAGIC); |
| 164 |
111 |
AN(sf); |
| 165 |
111 |
REPLACE(sf->pattern, glob); |
| 166 |
111 |
sf->mode = mode; |
| 167 |
111 |
AN(mode->name); |
| 168 |
111 |
if (mode->append) |
| 169 |
87 |
VTAILQ_INSERT_TAIL(&vsc->sf_list, sf, list); |
| 170 |
|
else |
| 171 |
24 |
VTAILQ_INSERT_HEAD(&vsc->sf_list, sf, list); |
| 172 |
111 |
return (1); |
| 173 |
|
} |
| 174 |
|
|
| 175 |
|
static int |
| 176 |
63 |
vsc_f_arg(struct vsc *vsc, const char *opt) |
| 177 |
|
{ |
| 178 |
|
|
| 179 |
63 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 180 |
63 |
AN(opt); |
| 181 |
|
|
| 182 |
63 |
if (opt[0] == '^') |
| 183 |
15 |
return (vsc_sf_arg(vsc, opt + 1, VSC_SF_EXCLUDE)); |
| 184 |
48 |
return (vsc_sf_arg(vsc, opt, VSC_SF_INCLUDE)); |
| 185 |
63 |
} |
| 186 |
|
|
| 187 |
|
/*--------------------------------------------------------------------*/ |
| 188 |
|
|
| 189 |
|
int |
| 190 |
111 |
VSC_Arg(struct vsc *vsc, char arg, const char *opt) |
| 191 |
|
{ |
| 192 |
|
|
| 193 |
111 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 194 |
|
|
| 195 |
111 |
switch (arg) { |
| 196 |
21 |
case 'I': return (vsc_sf_arg(vsc, opt, VSC_SF_INCLUDE)); |
| 197 |
3 |
case 'X': return (vsc_sf_arg(vsc, opt, VSC_SF_EXCLUDE)); |
| 198 |
24 |
case 'R': return (vsc_sf_arg(vsc, opt, VSC_SF_REQUIRE)); |
| 199 |
63 |
case 'f': return (vsc_f_arg(vsc, opt)); |
| 200 |
0 |
case 'r': vsc->raw = !vsc->raw; return (1); |
| 201 |
0 |
default: return (0); |
| 202 |
|
} |
| 203 |
111 |
} |
| 204 |
|
|
| 205 |
|
unsigned |
| 206 |
21 |
VSC_IsRaw(const struct vsc *vsc) |
| 207 |
|
{ |
| 208 |
|
|
| 209 |
21 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 210 |
21 |
return (vsc->raw); |
| 211 |
|
} |
| 212 |
|
|
| 213 |
|
/*-------------------------------------------------------------------- |
| 214 |
|
*/ |
| 215 |
|
|
| 216 |
|
static int |
| 217 |
170220 |
vsc_filter(const struct vsc *vsc, const char *nm) |
| 218 |
|
{ |
| 219 |
|
struct vsc_sf *sf; |
| 220 |
170220 |
unsigned res = 0; |
| 221 |
|
|
| 222 |
170220 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 223 |
204759 |
VTAILQ_FOREACH(sf, &vsc->sf_list, list) { |
| 224 |
35154 |
if (!fnmatch(sf->pattern, nm, 0)) |
| 225 |
615 |
return (!sf->mode->include); |
| 226 |
34539 |
res |= sf->mode->fail; |
| 227 |
34539 |
} |
| 228 |
169605 |
return (res); |
| 229 |
170220 |
} |
| 230 |
|
|
| 231 |
|
/*-------------------------------------------------------------------- |
| 232 |
|
*/ |
| 233 |
|
|
| 234 |
|
static void |
| 235 |
146040 |
vsc_clean_point(struct vsc_pt *point) |
| 236 |
|
{ |
| 237 |
146040 |
REPLACE(point->name, NULL); |
| 238 |
146040 |
} |
| 239 |
|
|
| 240 |
|
static void |
| 241 |
170220 |
vsc_fill_point(const struct vsc *vsc, const struct vsc_seg *seg, |
| 242 |
|
const struct vjsn_val *vv, struct vsb *vsb, struct vsc_pt *point) |
| 243 |
|
{ |
| 244 |
|
struct vjsn_val *vt; |
| 245 |
|
|
| 246 |
170220 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 247 |
170220 |
memset(point, 0, sizeof *point); |
| 248 |
|
|
| 249 |
170220 |
vt = vjsn_child(vv, "name"); |
| 250 |
170220 |
AN(vt); |
| 251 |
170220 |
assert(vjsn_is_string(vt)); |
| 252 |
|
|
| 253 |
170220 |
VSB_clear(vsb); |
| 254 |
170220 |
VSB_printf(vsb, "%s.%s", seg->fantom->ident, vt->value); |
| 255 |
170220 |
AZ(VSB_finish(vsb)); |
| 256 |
|
|
| 257 |
170220 |
if (vsc_filter(vsc, VSB_data(vsb))) |
| 258 |
11940 |
return; |
| 259 |
|
|
| 260 |
158280 |
point->name = strdup(VSB_data(vsb)); |
| 261 |
158280 |
AN(point->name); |
| 262 |
158280 |
point->point.name = point->name; |
| 263 |
|
|
| 264 |
|
#define DOF(n, k) \ |
| 265 |
|
vt = vjsn_child(vv, k); \ |
| 266 |
|
AN(vt); \ |
| 267 |
|
assert(vjsn_is_string(vt)); \ |
| 268 |
|
point->point.n = vt->value; |
| 269 |
|
|
| 270 |
158280 |
DOF(ctype, "ctype"); |
| 271 |
158280 |
DOF(sdesc, "oneliner"); |
| 272 |
158280 |
DOF(ldesc, "docs"); |
| 273 |
|
#undef DOF |
| 274 |
158280 |
vt = vjsn_child(vv, "type"); |
| 275 |
158280 |
AN(vt); |
| 276 |
158280 |
assert(vjsn_is_string(vt)); |
| 277 |
|
|
| 278 |
158280 |
if (!strcmp(vt->value, "counter")) { |
| 279 |
139547 |
point->point.semantics = 'c'; |
| 280 |
158280 |
} else if (!strcmp(vt->value, "gauge")) { |
| 281 |
18324 |
point->point.semantics = 'g'; |
| 282 |
18733 |
} else if (!strcmp(vt->value, "bitmap")) { |
| 283 |
409 |
point->point.semantics = 'b'; |
| 284 |
409 |
} else { |
| 285 |
0 |
point->point.semantics = '?'; |
| 286 |
|
} |
| 287 |
|
|
| 288 |
158280 |
vt = vjsn_child(vv, "format"); |
| 289 |
158280 |
AN(vt); |
| 290 |
158280 |
assert(vjsn_is_string(vt)); |
| 291 |
|
|
| 292 |
158280 |
if (!strcmp(vt->value, "integer")) { |
| 293 |
142151 |
point->point.format = 'i'; |
| 294 |
158280 |
} else if (!strcmp(vt->value, "bytes")) { |
| 295 |
14295 |
point->point.format = 'B'; |
| 296 |
16129 |
} else if (!strcmp(vt->value, "bitmap")) { |
| 297 |
409 |
point->point.format = 'b'; |
| 298 |
1834 |
} else if (!strcmp(vt->value, "duration")) { |
| 299 |
1425 |
point->point.format = 'd'; |
| 300 |
1425 |
} else { |
| 301 |
0 |
point->point.format = '?'; |
| 302 |
|
} |
| 303 |
|
|
| 304 |
158280 |
vt = vjsn_child(vv, "level"); |
| 305 |
158280 |
AN(vt); |
| 306 |
158280 |
assert(vjsn_is_string(vt)); |
| 307 |
|
|
| 308 |
158280 |
if (!strcmp(vt->value, "info")) { |
| 309 |
77377 |
point->point.level = &levels[info]; |
| 310 |
158280 |
} else if (!strcmp(vt->value, "diag")) { |
| 311 |
44617 |
point->point.level = &levels[diag]; |
| 312 |
80903 |
} else if (!strcmp(vt->value, "debug")) { |
| 313 |
36286 |
point->point.level = &levels[debug]; |
| 314 |
36286 |
} else { |
| 315 |
0 |
WRONG("Illegal level"); |
| 316 |
|
} |
| 317 |
|
|
| 318 |
158280 |
vt = vjsn_child(vv, "index"); |
| 319 |
158280 |
AN(vt); |
| 320 |
|
|
| 321 |
158280 |
point->point.ptr = (volatile const void*)(seg->body + atoi(vt->value)); |
| 322 |
158280 |
point->point.raw = vsc->raw; |
| 323 |
170220 |
} |
| 324 |
|
|
| 325 |
|
static struct vsc_seg * |
| 326 |
35369 |
vsc_new_seg(const struct vsm_fantom *fp, enum vsc_seg_type type) |
| 327 |
|
{ |
| 328 |
|
struct vsc_seg *sp; |
| 329 |
|
|
| 330 |
35369 |
ALLOC_OBJ(sp, VSC_SEG_MAGIC); |
| 331 |
35369 |
AN(sp); |
| 332 |
35369 |
*sp->fantom = *fp; |
| 333 |
35369 |
sp->type = type; |
| 334 |
|
|
| 335 |
35369 |
return (sp); |
| 336 |
|
} |
| 337 |
|
|
| 338 |
|
static void |
| 339 |
32912 |
vsc_unmap_seg(const struct vsc *vsc, struct vsm *vsm, struct vsc_seg *sp) |
| 340 |
|
{ |
| 341 |
|
unsigned u; |
| 342 |
|
struct vsc_pt *pp; |
| 343 |
|
|
| 344 |
32912 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 345 |
32912 |
AN(vsm); |
| 346 |
32912 |
CHECK_OBJ_NOTNULL(sp, VSC_SEG_MAGIC); |
| 347 |
|
|
| 348 |
32912 |
AZ(sp->exposed); |
| 349 |
32912 |
if (!sp->mapped) |
| 350 |
23958 |
return; |
| 351 |
|
|
| 352 |
8954 |
if (sp->type == VSC_SEG_COUNTERS) { |
| 353 |
6695 |
pp = sp->points; |
| 354 |
152735 |
for (u = 0; u < sp->npoints; u++, pp++) |
| 355 |
146040 |
vsc_clean_point(pp); |
| 356 |
6695 |
free(sp->points); |
| 357 |
6695 |
sp->points = NULL; |
| 358 |
6695 |
sp->npoints = 0; |
| 359 |
6695 |
AZ(sp->vj); |
| 360 |
8954 |
} else if (sp->type == VSC_SEG_DOCS) { |
| 361 |
2259 |
if (sp->vj != NULL) |
| 362 |
2259 |
vjsn_delete(&sp->vj); |
| 363 |
2259 |
AZ(sp->vj); |
| 364 |
2259 |
AZ(sp->points); |
| 365 |
2259 |
} else { |
| 366 |
0 |
WRONG("Invalid segment type"); |
| 367 |
|
} |
| 368 |
|
|
| 369 |
8954 |
AZ(VSM_Unmap(vsm, sp->fantom)); |
| 370 |
8954 |
sp->head = NULL; |
| 371 |
8954 |
sp->body = NULL; |
| 372 |
8954 |
sp->mapped = 0; |
| 373 |
32912 |
} |
| 374 |
|
|
| 375 |
|
static int |
| 376 |
76821 |
vsc_map_seg(const struct vsc *vsc, struct vsm *vsm, struct vsc_seg *sp) |
| 377 |
|
{ |
| 378 |
|
const struct vsc_head *head; |
| 379 |
|
struct vsc_seg *spd; |
| 380 |
|
const char *e; |
| 381 |
|
struct vjsn_val *vv, *vve; |
| 382 |
|
struct vsb *vsb; |
| 383 |
|
struct vsc_pt *pp; |
| 384 |
|
int retry; |
| 385 |
|
|
| 386 |
76821 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 387 |
76821 |
AN(vsm); |
| 388 |
76821 |
CHECK_OBJ_NOTNULL(sp, VSC_SEG_MAGIC); |
| 389 |
|
|
| 390 |
76821 |
if (sp->mapped) |
| 391 |
65410 |
return (0); |
| 392 |
|
|
| 393 |
11411 |
AZ(sp->exposed); |
| 394 |
|
|
| 395 |
11411 |
if (VSM_Map(vsm, sp->fantom)) |
| 396 |
0 |
return (-1); |
| 397 |
11411 |
head = sp->fantom->b; |
| 398 |
|
|
| 399 |
|
/* It isn't ready yet. Sleep and try again. If it still |
| 400 |
|
* isn't ready, fail the mapping. The transitions inside |
| 401 |
|
* varnishd that we are waiting for are just some memcpy() |
| 402 |
|
* operations, so there is no reason to allow a long retry |
| 403 |
|
* time. */ |
| 404 |
11411 |
for (retry = 10; retry > 0 && head->ready == 0; retry--) |
| 405 |
0 |
VTIM_sleep(0.01); |
| 406 |
|
|
| 407 |
11411 |
if (head->ready == 0) { |
| 408 |
0 |
AZ(VSM_Unmap(vsm, sp->fantom)); |
| 409 |
0 |
return (-1); |
| 410 |
|
} |
| 411 |
|
|
| 412 |
11411 |
sp->head = head; |
| 413 |
11411 |
sp->body = (char*)sp->fantom->b + sp->head->body_offset; |
| 414 |
11411 |
sp->mapped = 1; |
| 415 |
|
|
| 416 |
11411 |
if (sp->type == VSC_SEG_DOCS) { |
| 417 |
|
/* Parse the DOCS json */ |
| 418 |
2643 |
sp->vj = vjsn_parse(sp->body, &e); |
| 419 |
2643 |
XXXAZ(e); |
| 420 |
2643 |
AN(sp->vj); |
| 421 |
2643 |
return (0); |
| 422 |
|
} |
| 423 |
|
|
| 424 |
8768 |
assert(sp->type == VSC_SEG_COUNTERS); |
| 425 |
|
|
| 426 |
|
/* Find the corresponding DOCS seg. We are not able to |
| 427 |
|
* read and match on the doc_id until the DOCS section is |
| 428 |
|
* mapped. Iterate over all the DOCS sections, attempt to |
| 429 |
|
* map if needed, and then check the doc_id. */ |
| 430 |
29907 |
VTAILQ_FOREACH(spd, &vsc->docs, doc_list) { |
| 431 |
29907 |
CHECK_OBJ_NOTNULL(spd, VSC_SEG_MAGIC); |
| 432 |
29907 |
assert(spd->type == VSC_SEG_DOCS); |
| 433 |
29907 |
if (!spd->mapped && vsc_map_seg(vsc, vsm, spd)) |
| 434 |
0 |
continue; /* Failed to map it */ |
| 435 |
29907 |
AN(spd->mapped); |
| 436 |
29907 |
if (spd->head->doc_id == sp->head->doc_id) |
| 437 |
8768 |
break; /* We have a match */ |
| 438 |
21139 |
} |
| 439 |
8768 |
if (spd == NULL) { |
| 440 |
|
/* Could not find the right DOCS seg. Leave this |
| 441 |
|
* seg as unmapped. */ |
| 442 |
0 |
vsc_unmap_seg(vsc, vsm, sp); |
| 443 |
0 |
return (-1); |
| 444 |
|
} |
| 445 |
|
|
| 446 |
|
/* Create the VSC points list */ |
| 447 |
8768 |
vve = vjsn_child(spd->vj->value, "elements"); |
| 448 |
8768 |
AN(vve); |
| 449 |
8768 |
sp->npoints = strtoul(vve->value, NULL, 0); |
| 450 |
8768 |
sp->points = calloc(sp->npoints, sizeof *sp->points); |
| 451 |
8768 |
AN(sp->points); |
| 452 |
8768 |
vsb = VSB_new_auto(); |
| 453 |
8768 |
AN(vsb); |
| 454 |
8768 |
vve = vjsn_child(spd->vj->value, "elem"); |
| 455 |
8768 |
AN(vve); |
| 456 |
8768 |
pp = sp->points; |
| 457 |
178988 |
VTAILQ_FOREACH(vv, &vve->children, list) { |
| 458 |
170220 |
vsc_fill_point(vsc, sp, vv, vsb, pp); |
| 459 |
170220 |
pp++; |
| 460 |
170220 |
} |
| 461 |
8768 |
VSB_destroy(&vsb); |
| 462 |
8768 |
return (0); |
| 463 |
76821 |
} |
| 464 |
|
|
| 465 |
|
/*-------------------------------------------------------------------- |
| 466 |
|
*/ |
| 467 |
|
|
| 468 |
|
static void |
| 469 |
107090 |
vsc_expose(const struct vsc *vsc, struct vsc_seg *sp, int del) |
| 470 |
|
{ |
| 471 |
|
struct vsc_pt *pp; |
| 472 |
|
unsigned u; |
| 473 |
|
int expose; |
| 474 |
|
|
| 475 |
107090 |
if (!sp->mapped) { |
| 476 |
23958 |
AZ(sp->exposed); |
| 477 |
23958 |
return; |
| 478 |
|
} |
| 479 |
|
|
| 480 |
83903 |
if (vsc->fnew != NULL && !sp->exposed && |
| 481 |
933 |
!del && sp->head->ready == 1) |
| 482 |
771 |
expose = 1; |
| 483 |
87748 |
else if (vsc->fdestroy != NULL && sp->exposed && |
| 484 |
6158 |
(del || sp->head->ready == 2)) |
| 485 |
771 |
expose = 0; |
| 486 |
|
else |
| 487 |
81590 |
return; |
| 488 |
|
|
| 489 |
1542 |
pp = sp->points; |
| 490 |
17262 |
for (u = 0; u < sp->npoints; u++, pp++) { |
| 491 |
15720 |
if (pp->name == NULL) |
| 492 |
2130 |
continue; |
| 493 |
13590 |
if (expose) |
| 494 |
6795 |
pp->point.priv = vsc->fnew(vsc->priv, &pp->point); |
| 495 |
|
else |
| 496 |
6795 |
vsc->fdestroy(vsc->priv, &pp->point); |
| 497 |
13590 |
} |
| 498 |
1542 |
sp->exposed = expose; |
| 499 |
107090 |
} |
| 500 |
|
|
| 501 |
|
/*-------------------------------------------------------------------- |
| 502 |
|
*/ |
| 503 |
|
|
| 504 |
|
static void |
| 505 |
6944 |
vsc_del_segs(struct vsc *vsc, struct vsm *vsm, struct vsc_seg_head *head) |
| 506 |
|
{ |
| 507 |
|
struct vsc_seg *sp, *sp2; |
| 508 |
|
|
| 509 |
39856 |
VTAILQ_FOREACH_SAFE(sp, head, list, sp2) { |
| 510 |
32912 |
CHECK_OBJ(sp, VSC_SEG_MAGIC); |
| 511 |
32912 |
VTAILQ_REMOVE(head, sp, list); |
| 512 |
32912 |
if (sp->type == VSC_SEG_DOCS) |
| 513 |
5733 |
VTAILQ_REMOVE(&vsc->docs, sp, doc_list); |
| 514 |
32912 |
vsc_expose(vsc, sp, 1); |
| 515 |
32912 |
vsc_unmap_seg(vsc, vsm, sp); |
| 516 |
32912 |
FREE_OBJ(sp); |
| 517 |
32912 |
} |
| 518 |
6944 |
} |
| 519 |
|
|
| 520 |
|
/*-------------------------------------------------------------------- |
| 521 |
|
*/ |
| 522 |
|
|
| 523 |
|
static int |
| 524 |
67117 |
vsc_iter_seg(const struct vsc *vsc, const struct vsc_seg *sp, |
| 525 |
|
VSC_iter_f *fiter, void *priv) |
| 526 |
|
{ |
| 527 |
|
unsigned u; |
| 528 |
67117 |
int i = 0; |
| 529 |
|
struct vsc_pt *pp; |
| 530 |
|
|
| 531 |
67117 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 532 |
67117 |
CHECK_OBJ_NOTNULL(sp, VSC_SEG_MAGIC); |
| 533 |
67117 |
AN(fiter); |
| 534 |
67117 |
pp = sp->points; |
| 535 |
897501 |
for (u = 0; u < sp->npoints && i == 0; u++, pp++) { |
| 536 |
830384 |
if (pp->name != NULL) |
| 537 |
824069 |
i = fiter(priv, &pp->point); |
| 538 |
830384 |
} |
| 539 |
67117 |
return (i); |
| 540 |
|
} |
| 541 |
|
|
| 542 |
|
int |
| 543 |
4010 |
VSC_Iter(struct vsc *vsc, struct vsm *vsm, VSC_iter_f *fiter, void *priv) |
| 544 |
|
{ |
| 545 |
|
enum vsc_seg_type type; |
| 546 |
|
struct vsm_fantom ifantom; |
| 547 |
|
struct vsc_seg *sp, *sp2; |
| 548 |
|
struct vsc_seg_head removed; |
| 549 |
4010 |
int i = 0; |
| 550 |
|
|
| 551 |
4010 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 552 |
4010 |
AN(vsm); |
| 553 |
|
|
| 554 |
|
/* First walk the VSM segment list and consolidate with the shadow |
| 555 |
|
* VSC seg list. We avoid calling any of the callback functions |
| 556 |
|
* while iterating the VSMs. This removes any headaches wrt to |
| 557 |
|
* callbacks calling VSM_Status(). */ |
| 558 |
4010 |
VTAILQ_INIT(&removed); |
| 559 |
4010 |
sp = VTAILQ_FIRST(&vsc->segs); |
| 560 |
209641 |
VSM_FOREACH(&ifantom, vsm) { |
| 561 |
205631 |
AN(ifantom.category); |
| 562 |
205631 |
if (!strcmp(ifantom.category, VSC_CLASS)) |
| 563 |
149714 |
type = VSC_SEG_COUNTERS; |
| 564 |
55917 |
else if (!strcmp(ifantom.category, VSC_DOC_CLASS)) |
| 565 |
31953 |
type = VSC_SEG_DOCS; |
| 566 |
|
else { |
| 567 |
|
/* Not one of the categories we care about */ |
| 568 |
23964 |
continue; |
| 569 |
|
} |
| 570 |
|
|
| 571 |
183785 |
while (sp != NULL) { |
| 572 |
148416 |
CHECK_OBJ_NOTNULL(sp, VSC_SEG_MAGIC); |
| 573 |
148416 |
if (VSM_StillValid(vsm, sp->fantom) == VSM_valid && |
| 574 |
146298 |
!strcmp(ifantom.ident, sp->fantom->ident)) { |
| 575 |
|
/* sp matches the expected value */ |
| 576 |
146298 |
break; |
| 577 |
|
} |
| 578 |
|
|
| 579 |
|
/* sp is no longer in the VSM list. Remove it from |
| 580 |
|
* our list. */ |
| 581 |
2118 |
sp2 = sp; |
| 582 |
2118 |
sp = VTAILQ_NEXT(sp, list); |
| 583 |
2118 |
VTAILQ_REMOVE(&vsc->segs, sp2, list); |
| 584 |
2118 |
VTAILQ_INSERT_TAIL(&removed, sp2, list); |
| 585 |
|
} |
| 586 |
|
|
| 587 |
181667 |
if (sp == NULL) { |
| 588 |
|
/* New entries are always appended last in the VSM |
| 589 |
|
* list. Since we have iterated past all the |
| 590 |
|
* entries in our shadow list, the VSM entry is a |
| 591 |
|
* new entry we have not seen before. */ |
| 592 |
35369 |
sp = vsc_new_seg(&ifantom, type); |
| 593 |
35369 |
AN(sp); |
| 594 |
35369 |
VTAILQ_INSERT_TAIL(&vsc->segs, sp, list); |
| 595 |
35369 |
if (type == VSC_SEG_DOCS) |
| 596 |
6117 |
VTAILQ_INSERT_TAIL(&vsc->docs, sp, doc_list); |
| 597 |
35369 |
} |
| 598 |
|
|
| 599 |
181667 |
assert(sp->type == type); |
| 600 |
181667 |
sp = VTAILQ_NEXT(sp, list); |
| 601 |
|
} |
| 602 |
4010 |
while (sp != NULL) { |
| 603 |
|
/* Clean up the tail end of the shadow list. */ |
| 604 |
0 |
CHECK_OBJ_NOTNULL(sp, VSC_SEG_MAGIC); |
| 605 |
0 |
sp2 = sp; |
| 606 |
0 |
sp = VTAILQ_NEXT(sp, list); |
| 607 |
|
|
| 608 |
0 |
VTAILQ_REMOVE(&vsc->segs, sp2, list); |
| 609 |
0 |
VTAILQ_INSERT_TAIL(&removed, sp2, list); |
| 610 |
|
} |
| 611 |
|
|
| 612 |
4010 |
vsc_del_segs(vsc, vsm, &removed); |
| 613 |
|
|
| 614 |
|
/* Iterate our shadow list, reporting on each pointer value */ |
| 615 |
93601 |
VTAILQ_FOREACH(sp, &vsc->segs, list) { |
| 616 |
92606 |
CHECK_OBJ_NOTNULL(sp, VSC_SEG_MAGIC); |
| 617 |
|
|
| 618 |
92606 |
if (sp->type != VSC_SEG_COUNTERS) |
| 619 |
18428 |
continue; |
| 620 |
|
|
| 621 |
|
/* Attempt to map the VSM. This is a noop if it was |
| 622 |
|
* already mapped. If we fail we skip this seg on this |
| 623 |
|
* call to VSC_Iter(), but will attempt again the next |
| 624 |
|
* time VSC_Iter() is called. */ |
| 625 |
74178 |
if (vsc_map_seg(vsc, vsm, sp)) |
| 626 |
0 |
continue; |
| 627 |
|
|
| 628 |
|
/* Expose the counters if necessary */ |
| 629 |
74178 |
vsc_expose(vsc, sp, 0); |
| 630 |
|
|
| 631 |
74178 |
if (fiter != NULL && sp->head->ready == 1) |
| 632 |
67117 |
i = vsc_iter_seg(vsc, sp, fiter, priv); |
| 633 |
74178 |
if (i) |
| 634 |
3015 |
break; |
| 635 |
71163 |
} |
| 636 |
|
|
| 637 |
4010 |
return (i); |
| 638 |
|
} |
| 639 |
|
|
| 640 |
|
/*-------------------------------------------------------------------- |
| 641 |
|
*/ |
| 642 |
|
|
| 643 |
|
void |
| 644 |
21 |
VSC_State(struct vsc *vsc, VSC_new_f *fn, VSC_destroy_f *fd, void *priv) |
| 645 |
|
{ |
| 646 |
|
struct vsc_seg *sp; |
| 647 |
|
|
| 648 |
21 |
CHECK_OBJ_NOTNULL(vsc, VSC_MAGIC); |
| 649 |
21 |
assert((fn == NULL && fd == NULL) || (fn != NULL && fd != NULL)); |
| 650 |
21 |
if (fd == NULL) { |
| 651 |
0 |
VTAILQ_FOREACH(sp, &vsc->segs, list) |
| 652 |
0 |
vsc_expose(vsc, sp, 1); |
| 653 |
0 |
} |
| 654 |
21 |
vsc->fnew = fn; |
| 655 |
21 |
vsc->fdestroy = fd; |
| 656 |
21 |
vsc->priv = priv; |
| 657 |
21 |
} |
| 658 |
|
|
| 659 |
|
/*-------------------------------------------------------------------- |
| 660 |
|
*/ |
| 661 |
|
|
| 662 |
|
const struct VSC_level_desc * |
| 663 |
33 |
VSC_ChangeLevel(const struct VSC_level_desc *old, int chg) |
| 664 |
|
{ |
| 665 |
|
int i; |
| 666 |
|
|
| 667 |
33 |
if (old == NULL) |
| 668 |
21 |
old = &levels[0]; |
| 669 |
36 |
for (i = 0; i < nlevels; i++) |
| 670 |
36 |
if (old == &levels[i]) |
| 671 |
33 |
break; |
| 672 |
33 |
if (i == nlevels) |
| 673 |
0 |
i = 0; |
| 674 |
|
|
| 675 |
33 |
i += chg; |
| 676 |
33 |
if (i >= nlevels) |
| 677 |
0 |
i = nlevels - 1; |
| 678 |
33 |
if (i < 0) |
| 679 |
0 |
i = 0; |
| 680 |
33 |
return (&levels[i]); |
| 681 |
|
} |
| 682 |
|
|
| 683 |
|
/*--------------------------------------------------------------------*/ |
| 684 |
|
|
| 685 |
|
void |
| 686 |
2934 |
VSC_Destroy(struct vsc **vscp, struct vsm *vsm) |
| 687 |
|
{ |
| 688 |
|
struct vsc *vsc; |
| 689 |
|
struct vsc_sf *sf, *sf2; |
| 690 |
|
|
| 691 |
2934 |
TAKE_OBJ_NOTNULL(vsc, vscp, VSC_MAGIC); |
| 692 |
|
|
| 693 |
2994 |
VTAILQ_FOREACH_SAFE(sf, &vsc->sf_list, list, sf2) { |
| 694 |
60 |
CHECK_OBJ_NOTNULL(sf, VSC_SF_MAGIC); |
| 695 |
60 |
VTAILQ_REMOVE(&vsc->sf_list, sf, list); |
| 696 |
60 |
free(sf->pattern); |
| 697 |
60 |
FREE_OBJ(sf); |
| 698 |
60 |
} |
| 699 |
|
|
| 700 |
2934 |
vsc_del_segs(vsc, vsm, &vsc->segs); |
| 701 |
2934 |
assert(VTAILQ_EMPTY(&vsc->docs)); |
| 702 |
2934 |
FREE_OBJ(vsc); |
| 703 |
2934 |
} |