varnish-cache/lib/libvmod_debug/vmod_debug.c
1
/*-
2
 * Copyright (c) 2012-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@FreeBSD.org>
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 */
28
29
#include "config.h"
30
31
#include <errno.h>
32
#include <stdlib.h>
33
#include <stdio.h>
34
#include <string.h>
35
#include <sys/socket.h>
36
#include <unistd.h>
37
38
#include "cache/cache.h"
39
40
#include "vsa.h"
41
#include "vsb.h"
42
#include "vtcp.h"
43
#include "vtim.h"
44
#include "vcc_if.h"
45
#include "VSC_debug.h"
46
47
#include "common/common_param.h"
48
49
#define WARN_RETIRED()                                                  \
50
        do {                                                            \
51
                VSL(SLT_Error, 0,                                       \
52
                    "debug.%s is deprecated, use vmod-vtc instead.",    \
53
                    __func__);                                          \
54
        } while (0)
55
56
57
struct priv_vcl {
58
        unsigned                magic;
59
#define PRIV_VCL_MAGIC          0x8E62FA9D
60
        char                    *foo;
61
        uintptr_t               obj_cb;
62
        struct vcl              *vcl;
63
        struct vclref           *vclref;
64
};
65
66
static VCL_DURATION vcl_release_delay = 0.0;
67
68
static pthread_mutex_t vsc_mtx = PTHREAD_MUTEX_INITIALIZER;
69
static struct VSC_debug *vsc;
70
71
VCL_VOID v_matchproto_(td_debug_panic)
72 0
xyzzy_panic(VRT_CTX, const char *str, ...)
73
{
74
        va_list ap;
75
        const char *b;
76
77 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
78 0
        WARN_RETIRED();
79 0
        va_start(ap, str);
80 0
        b = VRT_String(ctx->ws, "PANIC: ", str, ap);
81 0
        va_end(ap);
82 0
        VAS_Fail("VCL", "", 0, b, VAS_VCL);
83
}
84
85
VCL_STRING v_matchproto_(td_debug_author)
86 5
xyzzy_author(VRT_CTX, VCL_ENUM person, VCL_ENUM someone)
87
{
88
        (void)someone;
89
90 5
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
91 5
        if (person == xyzzy_enum_phk)
92 2
                return ("Poul-Henning");
93 3
        assert(strcmp(person, "phk"));
94 3
        if (person == xyzzy_enum_des)
95 1
                return ("Dag-Erling");
96 2
        assert(strcmp(person, "des"));
97 2
        if (person == xyzzy_enum_kristian)
98 1
                return ("Kristian");
99 1
        assert(strcmp(person, "kristian"));
100 1
        if (person == xyzzy_enum_mithrandir)
101 1
                return ("Tollef");
102 0
        assert(strcmp(person, "mithrandir"));
103 0
        WRONG("Illegal VMOD enum");
104
}
105
106
VCL_VOID v_matchproto_(td_debug_test_priv_call)
107 2
xyzzy_test_priv_call(VRT_CTX, struct vmod_priv *priv)
108
{
109
110 2
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
111 2
        if (priv->priv == NULL) {
112 2
                priv->priv = strdup("BAR");
113 2
                priv->free = free;
114
        } else {
115 0
                assert(!strcmp(priv->priv, "BAR"));
116
        }
117 2
}
118
119
VCL_STRING v_matchproto_(td_debug_test_priv_task)
120 46
xyzzy_test_priv_task(VRT_CTX, struct vmod_priv *priv, VCL_STRING s)
121
{
122
123 46
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
124 46
        if (s == NULL || *s == '\0') {
125 28
                return priv->priv;
126 18
        } else if (priv->priv == NULL) {
127 11
                priv->priv = strdup(s);
128 11
                priv->free = free;
129
        } else {
130 7
                char *n = realloc(priv->priv,
131 7
                    strlen(priv->priv) + strlen(s) + 2);
132 7
                if (n == NULL)
133 0
                        return NULL;
134 7
                strcat(n, " ");
135 7
                strcat(n, s);
136 7
                priv->priv = n;
137
        }
138 18
        return (priv->priv);
139
}
140
141
VCL_STRING v_matchproto_(td_debug_test_priv_top)
142 16
xyzzy_test_priv_top(VRT_CTX, struct vmod_priv *priv, VCL_STRING s)
143
{
144
145 16
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
146 16
        if (priv->priv == NULL) {
147 2
                priv->priv = strdup(s);
148 2
                priv->free = free;
149
        }
150 16
        return (priv->priv);
151
}
152
153
VCL_VOID v_matchproto_(td_debug_test_priv_vcl)
154 2
xyzzy_test_priv_vcl(VRT_CTX, struct vmod_priv *priv)
155
{
156
        struct priv_vcl *priv_vcl;
157
158 2
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
159 2
        AN(priv);
160 2
        CAST_OBJ_NOTNULL(priv_vcl, priv->priv, PRIV_VCL_MAGIC);
161 2
        AN(priv_vcl->foo);
162 2
        assert(!strcmp(priv_vcl->foo, "FOO"));
163 2
}
164
165
VCL_BACKEND
166 0
xyzzy_no_backend(VRT_CTX)
167
{
168
169 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
170 0
        WARN_RETIRED();
171 0
        return (NULL);
172
}
173
174
VCL_STEVEDORE v_matchproto_(td_debug_no_stevedore)
175 0
xyzzy_no_stevedore(VRT_CTX)
176
{
177
178 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
179 0
        WARN_RETIRED();
180 0
        return (NULL);
181
}
182
183
VCL_VOID v_matchproto_(td_debug_rot52)
184 2
xyzzy_rot52(VRT_CTX, VCL_HTTP hp)
185
{
186
187 2
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
188 2
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
189
190 2
        http_PrintfHeader(hp, "Encrypted: ROT52");
191 2
}
192
193
VCL_STRING v_matchproto_(td_debug_argtest)
194 6
xyzzy_argtest(VRT_CTX, VCL_STRING one, VCL_REAL two, VCL_STRING three,
195
    VCL_STRING comma, VCL_INT four)
196
{
197
        char buf[100];
198
199 6
        bprintf(buf, "%s %g %s %s %ld", one, two, three, comma, four);
200 6
        return (WS_Copy(ctx->ws, buf, -1));
201
}
202
203
VCL_INT v_matchproto_(td_debug_vre_limit)
204 2
xyzzy_vre_limit(VRT_CTX)
205
{
206
        (void)ctx;
207 2
        return (cache_param->vre_limits.match);
208
}
209
210
static void v_matchproto_(obj_event_f)
211 2
obj_cb(struct worker *wrk, void *priv, struct objcore *oc, unsigned event)
212
{
213
        const struct priv_vcl *priv_vcl;
214
        const char *what;
215
216 2
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
217 2
        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
218 2
        CAST_OBJ_NOTNULL(priv_vcl, priv, PRIV_VCL_MAGIC);
219 2
        switch (event) {
220 1
        case OEV_INSERT: what = "insert"; break;
221 1
        case OEV_EXPIRE: what = "expire"; break;
222 0
        default: WRONG("Wrong object event");
223
        }
224
225
        /* We cannot trust %p to be 0x... format as expected by m00021.vtc */
226 2
        VSL(SLT_Debug, 0, "Object Event: %s 0x%jx", what,
227
            (intmax_t)(uintptr_t)oc);
228 2
}
229
230
VCL_VOID v_matchproto_(td_debug_register_obj_events)
231 1
xyzzy_register_obj_events(VRT_CTX, struct vmod_priv *priv)
232
{
233
        struct priv_vcl *priv_vcl;
234
235 1
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
236 1
        CAST_OBJ_NOTNULL(priv_vcl, priv->priv, PRIV_VCL_MAGIC);
237 1
        AZ(priv_vcl->obj_cb);
238 1
        priv_vcl->obj_cb = ObjSubscribeEvents(obj_cb, priv_vcl,
239
                OEV_INSERT|OEV_EXPIRE);
240 1
        VSL(SLT_Debug, 0, "Subscribed to Object Events");
241 1
}
242
243
VCL_VOID v_matchproto_(td_debug_fail)
244 14
xyzzy_fail(VRT_CTX)
245
{
246
247 14
        VRT_fail(ctx, "Forced failure");
248 14
}
249
250
static void v_matchproto_(vmod_priv_free_f)
251 6
priv_vcl_free(void *priv)
252
{
253
        struct priv_vcl *priv_vcl;
254
255 6
        CAST_OBJ_NOTNULL(priv_vcl, priv, PRIV_VCL_MAGIC);
256 6
        AN(priv_vcl->foo);
257 6
        free(priv_vcl->foo);
258 6
        if (priv_vcl->obj_cb != 0) {
259 1
                ObjUnsubscribeEvents(&priv_vcl->obj_cb);
260 1
                VSL(SLT_Debug, 0, "Unsubscribed from Object Events");
261
        }
262 6
        AZ(priv_vcl->vcl);
263 6
        AZ(priv_vcl->vclref);
264 6
        FREE_OBJ(priv_vcl);
265 6
        AZ(priv_vcl);
266 6
}
267
268
static int
269 41
event_load(VRT_CTX, struct vmod_priv *priv)
270
{
271
        struct priv_vcl *priv_vcl;
272
273 41
        AN(ctx->msg);
274 41
        if (cache_param->nuke_limit == 42) {
275 1
                VSB_printf(ctx->msg, "nuke_limit is not the answer.");
276 1
                return (-1);
277
        }
278
279 40
        ALLOC_OBJ(priv_vcl, PRIV_VCL_MAGIC);
280 40
        AN(priv_vcl);
281 40
        priv_vcl->foo = strdup("FOO");
282 40
        AN(priv_vcl->foo);
283 40
        priv->priv = priv_vcl;
284 40
        priv->free = priv_vcl_free;
285 40
        return (0);
286
}
287
288
static int
289 43
event_warm(VRT_CTX, const struct vmod_priv *priv)
290
{
291
        struct priv_vcl *priv_vcl;
292
        char buf[32];
293
294 43
        VSL(SLT_Debug, 0, "%s: VCL_EVENT_WARM", VCL_Name(ctx->vcl));
295
296 43
        AN(ctx->msg);
297 43
        if (cache_param->max_esi_depth == 42) {
298 3
                VSB_printf(ctx->msg, "max_esi_depth is not the answer.");
299 3
                return (-1);
300
        }
301
302 40
        CAST_OBJ_NOTNULL(priv_vcl, priv->priv, PRIV_VCL_MAGIC);
303 40
        AZ(priv_vcl->vcl);
304 40
        AZ(priv_vcl->vclref);
305
306 40
        bprintf(buf, "vmod-debug ref on %s", VCL_Name(ctx->vcl));
307 40
        priv_vcl->vcl = ctx->vcl;
308 40
        priv_vcl->vclref = VRT_ref_vcl(ctx, buf);
309 40
        return (0);
310
}
311
312
static void*
313 1
cooldown_thread(void *priv)
314
{
315
        struct vrt_ctx ctx;
316
        struct priv_vcl *priv_vcl;
317
318 1
        CAST_OBJ_NOTNULL(priv_vcl, priv, PRIV_VCL_MAGIC);
319 1
        AN(priv_vcl->vcl);
320 1
        AN(priv_vcl->vclref);
321
322 1
        INIT_OBJ(&ctx, VRT_CTX_MAGIC);
323 1
        ctx.vcl = priv_vcl->vcl;
324
325 1
        VTIM_sleep(vcl_release_delay);
326 1
        VRT_rel_vcl(&ctx, &priv_vcl->vclref);
327 1
        priv_vcl->vcl = NULL;
328 1
        return (NULL);
329
}
330
331
static int
332 8
event_cold(VRT_CTX, const struct vmod_priv *priv)
333
{
334
        pthread_t thread;
335
        struct priv_vcl *priv_vcl;
336
337 8
        CAST_OBJ_NOTNULL(priv_vcl, priv->priv, PRIV_VCL_MAGIC);
338 8
        AN(priv_vcl->vcl);
339 8
        AN(priv_vcl->vclref);
340
341 8
        VSL(SLT_Debug, 0, "%s: VCL_EVENT_COLD", VCL_Name(ctx->vcl));
342
343 8
        if (vcl_release_delay == 0.0) {
344 7
                VRT_rel_vcl(ctx, &priv_vcl->vclref);
345 7
                priv_vcl->vcl = NULL;
346 7
                return (0);
347
        }
348
349 1
        AZ(pthread_create(&thread, NULL, cooldown_thread, priv_vcl));
350 1
        AZ(pthread_detach(thread));
351 1
        return (0);
352
}
353
354
int v_matchproto_(vmod_event_f)
355 98
event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
356
{
357
358 98
        switch (e) {
359 41
        case VCL_EVENT_LOAD: return (event_load(ctx, priv));
360 43
        case VCL_EVENT_WARM: return (event_warm(ctx, priv));
361 8
        case VCL_EVENT_COLD: return (event_cold(ctx, priv));
362
        case VCL_EVENT_DISCARD:
363 6
                if (vsc)
364 0
                        VSC_debug_Destroy(&vsc);
365 6
                return (0);
366 0
        default: return (0);
367
        }
368
}
369
370
VCL_VOID v_matchproto_(td_debug_sleep)
371 0
xyzzy_sleep(VRT_CTX, VCL_DURATION t)
372
{
373
374 0
        CHECK_OBJ_ORNULL(ctx, VRT_CTX_MAGIC);
375 0
        WARN_RETIRED();
376 0
        VTIM_sleep(t);
377 0
}
378
379
static struct ws *
380 0
wsfind(VRT_CTX, VCL_ENUM which)
381
{
382 0
        if (!strcmp(which, "client"))
383 0
                return (ctx->ws);
384 0
        else if (!strcmp(which, "backend"))
385 0
                return (ctx->bo->ws);
386 0
        else if (!strcmp(which, "session"))
387 0
                return (ctx->req->sp->ws);
388 0
        else if (!strcmp(which, "thread"))
389 0
                return (ctx->req->wrk->aws);
390
        else
391 0
                WRONG("No such workspace.");
392
}
393
394
void
395 0
xyzzy_workspace_allocate(VRT_CTX, VCL_ENUM which, VCL_INT size)
396
{
397
        struct ws *ws;
398
        char *s;
399 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
400 0
        WARN_RETIRED();
401
402 0
        ws = wsfind(ctx, which);
403
404 0
        WS_Assert(ws);
405 0
        AZ(ws->r);
406
407 0
        if (size < 0) {
408 0
                size += WS_Reserve(ws, 0);
409 0
                WS_Release(ws, 0);
410
        }
411 0
        if (size <= 0) {
412 0
                VRT_fail(ctx, "Attempted negative WS allocation");
413 0
                return;
414
        }
415 0
        s = WS_Alloc(ws, size);
416 0
        if (!s)
417 0
                return;
418 0
        memset(s, '\0', size);
419
}
420
421
VCL_INT
422 0
xyzzy_workspace_free(VRT_CTX, VCL_ENUM which)
423
{
424
        struct ws *ws;
425
        unsigned u;
426
427 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
428 0
        WARN_RETIRED();
429
430 0
        ws = wsfind(ctx, which);
431
432 0
        WS_Assert(ws);
433 0
        u = WS_Reserve(ws, 0);
434 0
        WS_Release(ws, 0);
435
436 0
        return (u);
437
}
438
439
VCL_BOOL
440 0
xyzzy_workspace_overflowed(VRT_CTX, VCL_ENUM which)
441
{
442
        struct ws *ws;
443 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
444 0
        WARN_RETIRED();
445
446 0
        ws = wsfind(ctx, which);
447 0
        WS_Assert(ws);
448
449 0
        return (WS_Overflowed(ws));
450
}
451
452
static uintptr_t debug_ws_snap;
453
454
void
455 0
xyzzy_workspace_snap(VRT_CTX, VCL_ENUM which)
456
{
457
        struct ws *ws;
458 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
459 0
        WARN_RETIRED();
460
461 0
        ws = wsfind(ctx, which);
462 0
        WS_Assert(ws);
463
464 0
        debug_ws_snap = WS_Snapshot(ws);
465 0
}
466
467
void
468 0
xyzzy_workspace_reset(VRT_CTX, VCL_ENUM which)
469
{
470
        struct ws *ws;
471 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
472 0
        WARN_RETIRED();
473
474 0
        ws = wsfind(ctx, which);
475 0
        WS_Assert(ws);
476
477 0
        WS_Reset(ws, debug_ws_snap);
478 0
}
479
480
void
481 0
xyzzy_workspace_overflow(VRT_CTX, VCL_ENUM which)
482
{
483
        struct ws *ws;
484 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
485 0
        WARN_RETIRED();
486
487 0
        ws = wsfind(ctx, which);
488 0
        WS_Assert(ws);
489
490 0
        WS_MarkOverflow(ws);
491 0
}
492
493
VCL_VOID v_matchproto_(td_debug_vcl_release_delay)
494 1
xyzzy_vcl_release_delay(VRT_CTX, VCL_DURATION delay)
495
{
496
497 1
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
498 1
        assert(delay > 0.0);
499 1
        vcl_release_delay = delay;
500 1
}
501
502
VCL_BOOL v_matchproto_(td_debug_match_acl)
503 2
xyzzy_match_acl(VRT_CTX, VCL_ACL acl, VCL_IP ip)
504
{
505
506 2
        CHECK_OBJ_ORNULL(ctx, VRT_CTX_MAGIC);
507 2
        CHECK_OBJ_ORNULL(acl, VRT_ACL_MAGIC);
508 2
        assert(VSA_Sane(ip));
509
510 2
        return (VRT_acl_match(ctx, acl, ip));
511
}
512
513
VCL_BOOL
514 0
xyzzy_barrier_sync(VRT_CTX, VCL_STRING addr)
515
{
516
        const char *err;
517
        char buf[32];
518
        int sock, i;
519
        ssize_t sz;
520
521 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
522 0
        WARN_RETIRED();
523 0
        AN(addr);
524 0
        AN(*addr);
525
526 0
        VSLb(ctx->vsl, SLT_Debug, "barrier_sync(\"%s\")", addr);
527 0
        sock = VTCP_open(addr, NULL, 0., &err);
528 0
        if (sock < 0) {
529 0
                VSLb(ctx->vsl, SLT_Error, "Barrier connection failed: %s", err);
530 0
                return (0);
531
        }
532
533 0
        sz = read(sock, buf, sizeof buf);
534 0
        i = errno;
535 0
        closefd(&sock);
536 0
        if (sz == 0)
537 0
                return (1);
538 0
        if (sz < 0)
539 0
                VSLb(ctx->vsl, SLT_Error,
540
                    "Barrier read failed: %s (errno=%d)", strerror(i), i);
541 0
        if (sz > 0)
542 0
                VSLb(ctx->vsl, SLT_Error, "Barrier unexpected data (%zdB)", sz);
543 0
        return (0);
544
}
545
546
VCL_VOID v_matchproto_(td_debug_test_probe)
547 0
xyzzy_test_probe(VRT_CTX, VCL_PROBE probe, VCL_PROBE same)
548
{
549
550 0
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
551 0
        CHECK_OBJ_NOTNULL(probe, VRT_BACKEND_PROBE_MAGIC);
552 0
        CHECK_OBJ_ORNULL(same, VRT_BACKEND_PROBE_MAGIC);
553 0
        AZ(same == NULL || probe == same);
554 0
}
555
556
VCL_INT
557 0
xyzzy_typesize(VRT_CTX, VCL_STRING s)
558
{
559 0
        size_t i = 0;
560
        const char *p;
561
562 0
        WARN_RETIRED();
563
        (void)ctx;
564 0
        for (p = s; *p; p++) {
565 0
                switch (*p) {
566 0
                case 'p':       i += sizeof(void *); break;
567 0
                case 'i':       i += sizeof(int); break;
568 0
                case 'd':       i += sizeof(double); break;
569 0
                case 'f':       i += sizeof(float); break;
570 0
                case 'l':       i += sizeof(long); break;
571 0
                case 's':       i += sizeof(short); break;
572 0
                case 'z':       i += sizeof(size_t); break;
573 0
                case 'o':       i += sizeof(off_t); break;
574 0
                case 'j':       i += sizeof(intmax_t); break;
575 0
                default:        return(-1);
576
                }
577
        }
578 0
        return ((VCL_INT)i);
579
}
580
581
VCL_VOID
582 0
xyzzy_vsc_new(VRT_CTX)
583
{
584
        (void)ctx;
585 0
        AZ(pthread_mutex_lock(&vsc_mtx));
586 0
        if (vsc == NULL)
587 0
                vsc = VSC_debug_New("");
588 0
        AN(vsc);
589 0
        AZ(pthread_mutex_unlock(&vsc_mtx));
590 0
}
591
592
VCL_VOID
593 0
xyzzy_vsc_destroy(VRT_CTX)
594
{
595
        (void)ctx;
596 0
        AZ(pthread_mutex_lock(&vsc_mtx));
597 0
        if (vsc)
598 0
                VSC_debug_Destroy(&vsc);
599 0
        AZ(vsc);
600 0
        AZ(pthread_mutex_unlock(&vsc_mtx));
601 0
}