varnish-cache/bin/varnishd/cache/cache_director.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
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 * Abstract director API
30
 *
31
 * The abstract director API does not know how we talk to the backend or
32
 * if there even is one in the usual meaning of the word.
33
 *
34
 */
35
36
#include "config.h"
37
38
#include "cache_varnishd.h"
39
#include "cache_director.h"
40
41
#include "vcli_serve.h"
42
#include "vtim.h"
43
44
/* -------------------------------------------------------------------*/
45
46
struct vdi_ahealth {
47
        const char              *name;
48
        int                     health;
49
};
50
51
#define VBE_AHEALTH(l,u,h)                                              \
52
        static const struct vdi_ahealth vdi_ah_##l[1] = {{#l,h}};       \
53
        const struct vdi_ahealth * const VDI_AH_##u = vdi_ah_##l;
54
VBE_AHEALTH_LIST
55
#undef VBE_AHEALTH
56
57
static const struct vdi_ahealth *
58 76
vdi_str2ahealth(const char *t)
59
{
60
#define VBE_AHEALTH(l,u,h) if (!strcasecmp(t, #l)) return (VDI_AH_##u);
61 76
VBE_AHEALTH_LIST
62
#undef VBE_AHEALTH
63 16
        if (!strcasecmp(t, "auto")) return (VDI_AH_PROBE);
64 8
        return (NULL);
65
}
66
67
static const char *
68 1890
VDI_Ahealth(const struct director *d)
69
{
70
71 1890
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
72 1890
        AN(d->vdir->admin_health);
73 1890
        return (d->vdir->admin_health->name);
74
}
75
76
/* Resolve director --------------------------------------------------*/
77
78
static VCL_BACKEND
79 3348
VDI_Resolve(VRT_CTX)
80
{
81
        const struct director *d;
82
        const struct director *d2;
83
        struct busyobj *bo;
84
85 3348
        bo = ctx->bo;
86 3348
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
87 3348
        CHECK_OBJ_ORNULL(bo->director_req, DIRECTOR_MAGIC);
88
89 10466
        for (d = bo->director_req; d != NULL &&
90 3770
            d->vdir->methods->resolve != NULL; d = d2) {
91 214
                CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
92 214
                AN(d->vdir);
93 214
                d2 = d->vdir->methods->resolve(ctx, d);
94 214
                if (d2 == NULL)
95 2
                        VSLb(bo->vsl, SLT_FetchError,
96
                            "Director %s returned no backend", d->vcl_name);
97
        }
98 3348
        CHECK_OBJ_ORNULL(d, DIRECTOR_MAGIC);
99 3348
        if (d == NULL)
100 6
                VSLb(bo->vsl, SLT_FetchError, "No backend");
101
        else
102 3342
                AN(d->vdir);
103 3348
        return (d);
104
}
105
106
/* Get a set of response headers -------------------------------------*/
107
108
int
109 3324
VDI_GetHdr(struct busyobj *bo)
110
{
111
        const struct director *d;
112
        struct vrt_ctx ctx[1];
113 3324
        int i = -1;
114
115 3324
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
116 3324
        INIT_OBJ(ctx, VRT_CTX_MAGIC);
117 3324
        VCL_Bo2Ctx(ctx, bo);
118
119 3324
        d = VDI_Resolve(ctx);
120 3324
        if (d != NULL) {
121 3318
                bo->director_resp = d;
122 3318
                AN(d->vdir->methods->gethdrs);
123 3318
                bo->director_state = DIR_S_HDRS;
124 3318
                i = d->vdir->methods->gethdrs(ctx, d);
125
        }
126 3324
        if (i)
127 236
                bo->director_state = DIR_S_NULL;
128 3324
        return (i);
129
}
130
131
/* Get IP number (if any ) -------------------------------------------*/
132
133
VCL_IP
134 2
VDI_GetIP(struct busyobj *bo)
135
{
136
        const struct director *d;
137
        struct vrt_ctx ctx[1];
138
139 2
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
140 2
        INIT_OBJ(ctx, VRT_CTX_MAGIC);
141 2
        VCL_Bo2Ctx(ctx, bo);
142
143 2
        d = bo->director_resp;
144 2
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
145 2
        assert(bo->director_state == DIR_S_HDRS ||
146
           bo->director_state == DIR_S_BODY);
147 2
        AZ(d->vdir->methods->resolve);
148 2
        if (d->vdir->methods->getip == NULL)
149 0
                return (NULL);
150 2
        return (d->vdir->methods->getip(ctx, d));
151
}
152
153
/* Finish fetch ------------------------------------------------------*/
154
155
void
156 3086
VDI_Finish(struct busyobj *bo)
157
{
158
        const struct director *d;
159
        struct vrt_ctx ctx[1];
160
161 3086
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
162 3086
        INIT_OBJ(ctx, VRT_CTX_MAGIC);
163 3086
        VCL_Bo2Ctx(ctx, bo);
164
165 3086
        d = bo->director_resp;
166 3086
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
167
168 3086
        AZ(d->vdir->methods->resolve);
169 3086
        AN(d->vdir->methods->finish);
170
171 3086
        assert(bo->director_state != DIR_S_NULL);
172 3086
        d->vdir->methods->finish(ctx, d);
173 3086
        bo->director_state = DIR_S_NULL;
174 3086
}
175
176
/* Get a connection --------------------------------------------------*/
177
178
enum sess_close
179 24
VDI_Http1Pipe(struct req *req, struct busyobj *bo)
180
{
181
        const struct director *d;
182
        struct vrt_ctx ctx[1];
183
184 24
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
185 24
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
186 24
        INIT_OBJ(ctx, VRT_CTX_MAGIC);
187 24
        VCL_Req2Ctx(ctx, req);
188 24
        VCL_Bo2Ctx(ctx, bo);
189
190 24
        d = VDI_Resolve(ctx);
191 24
        if (d == NULL || d->vdir->methods->http1pipe == NULL) {
192 0
                VSLb(bo->vsl, SLT_VCL_Error, "Backend does not support pipe");
193 0
                return (SC_TX_ERROR);
194
        }
195 24
        bo->director_resp = d;
196 24
        return (d->vdir->methods->http1pipe(ctx, d));
197
}
198
199
/* Check health --------------------------------------------------------
200
 *
201
 * If director has no healthy method, we just assume it is healthy.
202
 */
203
204
/*--------------------------------------------------------------------
205
 * Test if backend is healthy and report when that last changed
206
 */
207
208
VCL_BOOL
209 640
VRT_Healthy(VRT_CTX, VCL_BACKEND d, VCL_TIME *changed)
210
{
211
212 640
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
213 640
        if (d == NULL)
214 0
                return (0);
215 640
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
216
217 640
        if (d->vdir->admin_health->health >= 0) {
218 84
                if (changed != NULL)
219 32
                        *changed = d->vdir->health_changed;
220 84
                return (d->vdir->admin_health->health);
221
        }
222
223 556
        if (d->vdir->methods->healthy == NULL) {
224 478
                if (changed != NULL)
225 210
                        *changed = d->vdir->health_changed;
226 478
                return (!d->sick);
227
        }
228
229 78
        return (d->vdir->methods->healthy(ctx, d, changed));
230
}
231
232
/* Send Event ----------------------------------------------------------
233
 */
234
235
void
236 2212
VDI_Event(const struct director *d, enum vcl_event_e ev)
237
{
238
239 2212
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
240 2212
        if (d->vdir->methods->event != NULL)
241 2128
                d->vdir->methods->event(d, ev);
242 2212
}
243
244
/* Dump panic info -----------------------------------------------------
245
 */
246
247
void
248 2
VDI_Panic(const struct director *d, struct vsb *vsb, const char *nm)
249
{
250 2
        if (d == NULL)
251 0
                return;
252 2
        VSB_printf(vsb, "%s = %p {\n", nm, d);
253 2
        VSB_indent(vsb, 2);
254 2
        VSB_printf(vsb, "cli_name = %s,\n", d->vdir->cli_name);
255 2
        VSB_printf(vsb, "health = %s,\n", d->sick ?  "sick" : "healthy");
256 2
        VSB_printf(vsb, "admin_health = %s, changed = %f,\n",
257 2
            VDI_Ahealth(d), d->vdir->health_changed);
258 2
        VSB_printf(vsb, "type = %s {\n", d->vdir->methods->type);
259 2
        VSB_indent(vsb, 2);
260 2
        if (d->vdir->methods->panic != NULL)
261 2
                d->vdir->methods->panic(d, vsb);
262 2
        VSB_indent(vsb, -2);
263 2
        VSB_printf(vsb, "},\n");
264 2
        VSB_indent(vsb, -2);
265 2
        VSB_printf(vsb, "},\n");
266
}
267
268
269
/*---------------------------------------------------------------------*/
270
271
struct list_args {
272
        unsigned        magic;
273
#define LIST_ARGS_MAGIC 0x7e7cefeb
274
        int             p;
275
        int             v;
276
        int             j;
277
        const char      *jsep;
278
};
279
280
static int v_matchproto_(vcl_be_func)
281 1904
do_list(struct cli *cli, struct director *d, void *priv)
282
{
283
        char time_str[VTIM_FORMAT_SIZE];
284
        struct list_args *la;
285
286 1904
        CAST_OBJ_NOTNULL(la, priv, LIST_ARGS_MAGIC);
287 1904
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
288
289 1904
        if (d->vdir->admin_health == VDI_AH_DELETED)
290 34
                return (0);
291
292
        // XXX admin health "probe" for the no-probe case is confusing
293 1870
        VCLI_Out(cli, "\n%-30s %-7s ", d->vdir->cli_name, VDI_Ahealth(d));
294
295 1870
        if (d->vdir->methods->list != NULL)
296 1780
                d->vdir->methods->list(d, cli->sb, 0, 0, 0);
297
        else
298 90
                VCLI_Out(cli, "%-10s", d->sick ? "sick" : "healthy");
299
300 1870
        VTIM_format(d->vdir->health_changed, time_str);
301 1870
        VCLI_Out(cli, " %s", time_str);
302 1870
        if ((la->p || la->v) && d->vdir->methods->list != NULL)
303 24
                d->vdir->methods->list(d, cli->sb, la->p, la->v, 0);
304 1870
        return (0);
305
}
306
307
static int v_matchproto_(vcl_be_func)
308 18
do_list_json(struct cli *cli, struct director *d, void *priv)
309
{
310
        struct list_args *la;
311
312 18
        CAST_OBJ_NOTNULL(la, priv, LIST_ARGS_MAGIC);
313 18
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
314
315 18
        if (d->vdir->admin_health == VDI_AH_DELETED)
316 0
                return (0);
317
318 18
        VCLI_Out(cli, "%s", la->jsep);
319 18
        la->jsep = ",\n";
320
        // XXX admin health "probe" for the no-probe case is confusing
321 18
        VCLI_JSON_str(cli, d->vdir->cli_name);
322 18
        VCLI_Out(cli, ": {\n");
323 18
        VSB_indent(cli->sb, 2);
324 18
        VCLI_Out(cli, "\"type\": \"%s\",\n", d->vdir->methods->type);
325 18
        VCLI_Out(cli, "\"admin_health\": \"%s\",\n", VDI_Ahealth(d));
326 18
        VCLI_Out(cli, "\"probe_message\": ");
327 18
        if (d->vdir->methods->list != NULL)
328 12
                d->vdir->methods->list(d, cli->sb, 0, 0, 1);
329
        else
330 6
                VCLI_Out(cli, "\"%s\"", d->sick ? "sick" : "healthy");
331 18
        VCLI_Out(cli, ",\n");
332
333 18
        if ((la->p || la->v) && d->vdir->methods->list != NULL) {
334 2
                VCLI_Out(cli, "\"probe_details\": ");
335 2
                d->vdir->methods->list(d, cli->sb, la->p, la->v, 1);
336
        }
337 18
        VCLI_Out(cli, "\"last_change\": %.3f\n", d->vdir->health_changed);
338 18
        VSB_indent(cli->sb, -2);
339 18
        VCLI_Out(cli, "}");
340 18
        return (0);
341
}
342
343
static void v_matchproto_(cli_func_t)
344 1376
cli_backend_list(struct cli *cli, const char * const *av, void *priv)
345
{
346
        const char *p;
347
        struct list_args la[1];
348
        int i;
349
350
        (void)priv;
351 1376
        ASSERT_CLI();
352 1376
        INIT_OBJ(la, LIST_ARGS_MAGIC);
353 1376
        la->jsep = "";
354 1408
        for (i = 2; av[i] != NULL && av[i][0] == '-'; i++) {
355 64
                for(p = av[i] + 1; *p; p++) {
356 32
                        switch(*p) {
357 6
                        case 'j': la->j = 1; break;
358 26
                        case 'p': la->p = !la->p; break;
359 0
                        case 'v': la->p = !la->p; break;
360
                        default:
361 0
                                VCLI_Out(cli, "Invalid flag %c", *p);
362 0
                                VCLI_SetResult(cli, CLIS_PARAM);
363 0
                                return;
364
                        }
365
                }
366
        }
367 1376
        if (av[i] != NULL && av[i+1] != NULL) {
368 0
                VCLI_Out(cli, "Too many arguments");
369 0
                VCLI_SetResult(cli, CLIS_PARAM);
370 0
                return;
371
        }
372 1376
        if (la->j) {
373 6
                VCLI_JSON_begin(cli, 2, av);
374 6
                VCLI_Out(cli, ",\n");
375 6
                VCLI_Out(cli, "{\n");
376 6
                VSB_indent(cli->sb, 2);
377 6
                (void)VCL_IterDirector(cli, av[i], do_list_json, la);
378 6
                VSB_indent(cli->sb, -2);
379 6
                VCLI_Out(cli, "\n");
380 6
                VCLI_Out(cli, "}");
381 6
                VCLI_JSON_end(cli);
382
        } else {
383 1370
                VCLI_Out(cli, "%-30s %-7s %-10s %s",
384
                    "Backend name", "Admin", "Probe", "Last change");
385 1370
                (void)VCL_IterDirector(cli, av[i], do_list, la);
386
        }
387
}
388
389
/*---------------------------------------------------------------------*/
390
391
struct set_health {
392
        unsigned                        magic;
393
#define SET_HEALTH_MAGIC                0x0c46b9fb
394
        const struct vdi_ahealth        *ah;
395
};
396
397
static int v_matchproto_(vcl_be_func)
398 62
do_set_health(struct cli *cli, struct director *d, void *priv)
399
{
400
        struct set_health *sh;
401
402
        (void)cli;
403 62
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
404 62
        CAST_OBJ_NOTNULL(sh, priv, SET_HEALTH_MAGIC);
405 62
        if (d->vdir->admin_health == VDI_AH_DELETED)
406 0
                return (0);
407 62
        if (d->vdir->admin_health != sh->ah) {
408 58
                d->vdir->health_changed = VTIM_real();
409 58
                d->vdir->admin_health = sh->ah;
410 58
                d->sick &= ~0x02;
411 58
                d->sick |= sh->ah->health ? 0 : 0x02;
412
        }
413 62
        return (0);
414
}
415
416
static void v_matchproto_()
417 76
cli_backend_set_health(struct cli *cli, const char * const *av, void *priv)
418
{
419
        int n;
420
        struct set_health sh[1];
421
422
        (void)av;
423
        (void)priv;
424 76
        ASSERT_CLI();
425 76
        AN(av[2]);
426 76
        AN(av[3]);
427 76
        INIT_OBJ(sh, SET_HEALTH_MAGIC);
428 76
        sh->ah = vdi_str2ahealth(av[3]);
429 76
        if (sh->ah == NULL || sh->ah == VDI_AH_DELETED) {
430 8
                VCLI_Out(cli, "Invalid state %s", av[3]);
431 8
                VCLI_SetResult(cli, CLIS_PARAM);
432 8
                return;
433
        }
434 68
        n = VCL_IterDirector(cli, av[2], do_set_health, sh);
435 68
        if (n == 0) {
436 6
                VCLI_Out(cli, "No Backends matches");
437 6
                VCLI_SetResult(cli, CLIS_PARAM);
438
        }
439
}
440
441
/*---------------------------------------------------------------------*/
442
443
static struct cli_proto backend_cmds[] = {
444
        { CLICMD_BACKEND_LIST,          "",
445
             cli_backend_list, cli_backend_list },
446
        { CLICMD_BACKEND_SET_HEALTH,    "", cli_backend_set_health },
447
        { NULL }
448
};
449
450
/*---------------------------------------------------------------------*/
451
452
void
453 1376
VDI_Init(void)
454
{
455
456 1376
        CLI_AddFuncs(backend_cmds);
457 1376
}