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
40
#include "cache_director.h"
41
#include "cache_backend.h"
42
43
#include "vcli_serve.h"
44
#include "vtim.h"
45
46
/* -------------------------------------------------------------------*/
47
48
struct vdi_ahealth {
49
        const char              *name;
50
};
51
52
#define VBE_AHEALTH(l,u)                                                \
53
        static const struct vdi_ahealth vdi_ah_##l[1] = {{#l}};         \
54
        const struct vdi_ahealth * const VDI_AH_##u = vdi_ah_##l;
55
VBE_AHEALTH_LIST
56
#undef VBE_AHEALTH
57
58
static const struct vdi_ahealth *
59 32
vdi_str2ahealth(const char *t)
60
{
61
#define VBE_AHEALTH(l,u) if (!strcasecmp(t, #l)) return (VDI_AH_##u);
62 32
VBE_AHEALTH_LIST
63
#undef VBE_AHEALTH
64 4
        if (!strcasecmp(t, "auto")) return (VDI_AH_PROBE);
65 2
        return (NULL);
66
}
67
68
const char *
69 713
VDI_Ahealth(const struct director *d)
70
{
71
72 713
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
73 713
        AN(d->admin_health);
74 713
        return (d->admin_health->name);
75
}
76
77
/* Resolve director --------------------------------------------------*/
78
79
static const struct director *
80 1358
vdi_resolve(struct worker *wrk, struct busyobj *bo)
81
{
82
        const struct director *d;
83
        const struct director *d2;
84
85 1358
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
86 1358
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
87
88 1446
        for (d = bo->director_req; d != NULL && d->resolve != NULL; d = d2) {
89 88
                CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
90 88
                d2 = d->resolve(d, wrk, bo);
91 88
                if (d2 == NULL)
92 1
                        VSLb(bo->vsl, SLT_FetchError,
93
                            "Director %s returned no backend", d->vcl_name);
94
        }
95 1358
        CHECK_OBJ_ORNULL(d, DIRECTOR_MAGIC);
96 1358
        if (d == NULL)
97 3
                VSLb(bo->vsl, SLT_FetchError, "No backend");
98 1358
        bo->director_resp = d;
99 1358
        return (d);
100
}
101
102
/* Get a set of response headers -------------------------------------*/
103
104
int
105 1348
VDI_GetHdr(struct worker *wrk, struct busyobj *bo)
106
{
107
        const struct director *d;
108 1348
        int i = -1;
109
110 1348
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
111 1348
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
112
113 1348
        d = vdi_resolve(wrk, bo);
114 1348
        if (d != NULL) {
115 1345
                AN(d->gethdrs);
116 1345
                bo->director_state = DIR_S_HDRS;
117 1345
                i = d->gethdrs(d, wrk, bo);
118
        }
119 1348
        if (i)
120 77
                bo->director_state = DIR_S_NULL;
121 1348
        return (i);
122
}
123
124
/* Setup body fetch --------------------------------------------------*/
125
126
int
127 747
VDI_GetBody(struct worker *wrk, struct busyobj *bo)
128
{
129
        const struct director *d;
130
131 747
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
132 747
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
133
134 747
        d = bo->director_resp;
135 747
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
136 747
        AZ(d->resolve);
137
138 747
        assert(bo->director_state == DIR_S_HDRS);
139 747
        bo->director_state = DIR_S_BODY;
140 747
        if (d->getbody == NULL)
141 747
                return (0);
142 0
        return (d->getbody(d, wrk, bo));
143
}
144
145
/* Get IP number (if any ) -------------------------------------------*/
146
147
const struct suckaddr *
148 1
VDI_GetIP(struct worker *wrk, struct busyobj *bo)
149
{
150
        const struct director *d;
151
152 1
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
153 1
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
154
155 1
        d = bo->director_resp;
156 1
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
157 1
        assert(bo->director_state == DIR_S_HDRS ||
158
           bo->director_state == DIR_S_BODY);
159 1
        AZ(d->resolve);
160 1
        if (d->getip == NULL)
161 0
                return (NULL);
162 1
        return (d->getip(d, wrk, bo));
163
}
164
165
/* Finish fetch ------------------------------------------------------*/
166
167
void
168 1269
VDI_Finish(struct worker *wrk, struct busyobj *bo)
169
{
170
        const struct director *d;
171
172 1269
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
173 1269
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
174
175 1269
        d = bo->director_resp;
176 1269
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
177
178 1269
        AZ(d->resolve);
179 1269
        AN(d->finish);
180
181 1269
        assert(bo->director_state != DIR_S_NULL);
182 1269
        d->finish(d, wrk, bo);
183 1270
        bo->director_state = DIR_S_NULL;
184 1270
}
185
186
/* Get a connection --------------------------------------------------*/
187
188
enum sess_close
189 10
VDI_Http1Pipe(struct req *req, struct busyobj *bo)
190
{
191
        const struct director *d;
192
193 10
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
194 10
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
195
196 10
        d = vdi_resolve(req->wrk, bo);
197 10
        if (d == NULL || d->http1pipe == NULL) {
198 0
                VSLb(bo->vsl, SLT_VCL_Error, "Backend does not support pipe");
199 0
                return (SC_TX_ERROR);
200
        }
201 10
        return (d->http1pipe(d, req, bo));
202
}
203
204
/* Check health --------------------------------------------------------
205
 *
206
 * If director has no healthy method, we just assume it is healthy.
207
 */
208
209
int
210 8
VRT_Healthy(VRT_CTX, VCL_BACKEND d)
211
{
212
213 8
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
214 8
        if (d == NULL)
215 0
                return (0);
216 8
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
217 8
        if (!VDI_Healthy(d, NULL))
218 4
                return (0);
219 4
        if (d->healthy == NULL)
220 0
                return (1);
221 4
        return (d->healthy(d, ctx->bo, NULL));
222
}
223
224
/* Send Event ----------------------------------------------------------
225
 */
226
227
void
228 844
VDI_Event(const struct director *d, enum vcl_event_e ev)
229
{
230
231 844
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
232 844
        if (d->event != NULL)
233 844
                d->event(d, ev);
234 844
}
235
236
/* Dump panic info -----------------------------------------------------
237
 */
238
239
void
240 1
VDI_Panic(const struct director *d, struct vsb *vsb, const char *nm)
241
{
242 1
        if (d == NULL)
243 1
                return;
244 1
        VSB_printf(vsb, "%s = %p {\n", nm, d);
245 1
        VSB_indent(vsb, 2);
246 1
        VSB_printf(vsb, "vcl_name = %s,\n", d->vcl_name);
247 1
        VSB_printf(vsb, "type = %s {\n", d->name);
248 1
        VSB_indent(vsb, 2);
249 1
        if (d->panic != NULL)
250 1
                d->panic(d, vsb);
251 1
        VSB_indent(vsb, -2);
252 1
        VSB_printf(vsb, "},\n");
253 1
        VSB_indent(vsb, -2);
254 1
        VSB_printf(vsb, "},\n");
255
}
256
257
/*--------------------------------------------------------------------
258
 * Test if backend is healthy and report when it last changed
259
 */
260
261
unsigned
262 1670
VDI_Healthy(const struct director *d, double *changed)
263
{
264 1670
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
265
266 1670
        if (changed != NULL)
267 103
                *changed = d->health_changed;
268
269 1670
        if (d->admin_health == VDI_AH_PROBE)
270 1587
                return (d->health);
271
272 83
        if (d->admin_health == VDI_AH_SICK)
273 61
                return (0);
274
275 22
        if (d->admin_health == VDI_AH_DELETED)
276 2
                return (0);
277
278 20
        if (d->admin_health == VDI_AH_HEALTHY)
279 20
                return (1);
280
281 0
        WRONG("Wrong admin health");
282
}
283
284
/*---------------------------------------------------------------------*/
285
286
static int v_matchproto_(vcl_be_func)
287 694
do_list(struct cli *cli, struct director *d, void *priv)
288
{
289
        int *probes;
290
        char time_str[VTIM_FORMAT_SIZE];
291
        struct backend *be;
292
293 694
        AN(priv);
294 694
        probes = priv;
295 694
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
296 694
        CAST_OBJ_NOTNULL(be, d->priv, BACKEND_MAGIC);
297 694
        if (d->admin_health == VDI_AH_DELETED)
298 10
                return (0);
299
300 684
        VCLI_Out(cli, "\n%-30s", d->display_name);
301
302 684
        VCLI_Out(cli, " %-10s", VDI_Ahealth(d));
303
304 684
        if (be->probe == NULL)
305 661
                VCLI_Out(cli, " %-20s", "Healthy (no probe)");
306
        else {
307 23
                if (d->health)
308 17
                        VCLI_Out(cli, " %-20s", "Healthy ");
309
                else
310 6
                        VCLI_Out(cli, " %-20s", "Sick ");
311 23
                VBP_Status(cli, be, *probes);
312
        }
313
314 684
        VTIM_format(d->health_changed, time_str);
315 684
        VCLI_Out(cli, " %s", time_str);
316
317 684
        return (0);
318
}
319
320
static void v_matchproto_(cli_func_t)
321 594
cli_backend_list(struct cli *cli, const char * const *av, void *priv)
322
{
323 594
        int probes = 0;
324
325
        (void)priv;
326 594
        ASSERT_CLI();
327 594
        if (av[2] != NULL && !strcmp(av[2], "-p")) {
328 6
                av++;
329 6
                probes = 1;
330 588
        } else if (av[2] != NULL && av[2][0] == '-') {
331 0
                VCLI_Out(cli, "Invalid flags %s", av[2]);
332 0
                VCLI_SetResult(cli, CLIS_PARAM);
333 0
                return;
334 588
        } else if (av[3] != NULL) {
335 0
                VCLI_Out(cli, "Too many arguments");
336 0
                VCLI_SetResult(cli, CLIS_PARAM);
337 0
                return;
338
        }
339 594
        VCLI_Out(cli, "%-30s %-10s %-20s %s", "Backend name", "Admin",
340
            "Probe", "Last updated");
341 594
        (void)VCL_IterDirector(cli, av[2], do_list, &probes);
342
}
343
344
/*---------------------------------------------------------------------*/
345
346
static int v_matchproto_(vcl_be_func)
347 28
do_set_health(struct cli *cli, struct director *d, void *priv)
348
{
349
        unsigned prev;
350
351
        (void)cli;
352 28
        AN(priv);
353 28
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
354 28
        if (d->admin_health == VDI_AH_DELETED)
355 0
                return (0);
356 28
        prev = VDI_Healthy(d, NULL);
357 28
        d->admin_health = *(const struct vdi_ahealth **)priv;
358 28
        (void)VDI_Ahealth(d);                   // Acts like type-check
359 28
        if (prev != VDI_Healthy(d, NULL))
360 27
                d->health_changed = VTIM_real();
361
362 28
        return (0);
363
}
364
365
static void v_matchproto_()
366 32
cli_backend_set_health(struct cli *cli, const char * const *av, void *priv)
367
{
368
        const struct vdi_ahealth *ah;
369
        int n;
370
371
        (void)av;
372
        (void)priv;
373 32
        ASSERT_CLI();
374 32
        AN(av[2]);
375 32
        AN(av[3]);
376 32
        ah = vdi_str2ahealth(av[3]);
377 32
        if (ah == NULL || ah == VDI_AH_DELETED) {
378 2
                VCLI_Out(cli, "Invalid state %s", av[3]);
379 2
                VCLI_SetResult(cli, CLIS_PARAM);
380 34
                return;
381
        }
382 30
        n = VCL_IterDirector(cli, av[2], do_set_health, &ah);
383 30
        if (n == 0) {
384 2
                VCLI_Out(cli, "No Backends matches");
385 2
                VCLI_SetResult(cli, CLIS_PARAM);
386
        }
387
}
388
389
/*---------------------------------------------------------------------*/
390
391
static struct cli_proto backend_cmds[] = {
392
        { CLICMD_BACKEND_LIST,          "", cli_backend_list },
393
        { CLICMD_BACKEND_SET_HEALTH,    "", cli_backend_set_health },
394
        { NULL }
395
};
396
397
/*---------------------------------------------------------------------*/
398
399
void
400 614
VDI_Init(void)
401
{
402
403 614
        CLI_AddFuncs(backend_cmds);
404 614
}