varnish-cache/bin/varnishtest/vtc_haproxy.c
0
/*-
1
 * Copyright (c) 2008-2018 Varnish Software AS
2
 * All rights reserved.
3
 *
4
 * Author: Frédéric Lécaille <flecaille@haproxy.com>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
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
30
#include "config.h"
31
32
#include <inttypes.h>
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <sys/stat.h> /* for MUSL (mode_t) */
37
#include <sys/types.h>
38
#include <sys/socket.h>
39
#include <unistd.h>
40
41
#include "vtc.h"
42
43
#include "vfil.h"
44
#include "vpf.h"
45
#include "vre.h"
46
#include "vtcp.h"
47
#include "vsa.h"
48
#include "vtim.h"
49
50
#define HAPROXY_PROGRAM_ENV_VAR "HAPROXY_PROGRAM"
51
#define HAPROXY_ARGS_ENV_VAR    "HAPROXY_ARGS"
52
#define HAPROXY_OPT_WORKER      "-W"
53
#define HAPROXY_OPT_MCLI        "-S"
54
#define HAPROXY_OPT_DAEMON      "-D"
55
#define HAPROXY_SIGNAL          SIGINT
56
#define HAPROXY_EXPECT_EXIT     (128 + HAPROXY_SIGNAL)
57
58
struct envar {
59
        VTAILQ_ENTRY(envar) list;
60
        char *name;
61
        char *value;
62
};
63
64
struct haproxy {
65
        unsigned                magic;
66
#define HAPROXY_MAGIC           0x8a45cf75
67
        char                    *name;
68
        struct vtclog           *vl;
69
        VTAILQ_ENTRY(haproxy)   list;
70
71
        const char              *filename;
72
        struct vsb              *args;
73
        int                     opt_worker;
74
        int                     opt_mcli;
75
        int                     opt_daemon;
76
        int                     opt_check_mode;
77
        char                    *pid_fn;
78
        pid_t                   pid;
79
        pid_t                   ppid;
80
        int                     fds[4];
81
        char                    *cfg_fn;
82
        struct vsb              *cfg_vsb;
83
84
        pthread_t               tp;
85
        int                     expect_exit;
86
        int                     expect_signal;
87
        int                     its_dead_jim;
88
89
        /* UNIX socket CLI. */
90
        char                    *cli_fn;
91
        /* TCP socket CLI. */
92
        struct haproxy_cli *cli;
93
94
        /* master CLI */
95
        struct haproxy_cli *mcli;
96
97
        char                    *workdir;
98
        struct vsb              *msgs;
99
        char                    closed_sock[256]; /* Closed TCP socket */
100
        VTAILQ_HEAD(,envar) envars;
101
};
102
103
static VTAILQ_HEAD(, haproxy)   haproxies =
104
    VTAILQ_HEAD_INITIALIZER(haproxies);
105
106
struct haproxy_cli {
107
        unsigned                magic;
108
#define HAPROXY_CLI_MAGIC       0xb09a4ed8
109
        struct vtclog           *vl;
110
        char                    running;
111
112
        char                    *spec;
113
114
        int                     sock;
115
        char                    connect[256];
116
117
        pthread_t               tp;
118
        size_t                  txbuf_sz;
119
        char                    *txbuf;
120
        size_t                  rxbuf_sz;
121
        char                    *rxbuf;
122
123
        double                  timeout;
124
};
125
126
static void haproxy_write_conf(struct haproxy *h);
127
128
static void
129 425
haproxy_add_envar(struct haproxy *h,
130
                  const char *name, const char *value)
131
{
132
        struct envar *e;
133
134 425
        e = malloc(sizeof *e);
135 425
        AN(e);
136 425
        e->name = strdup(name);
137 425
        e->value = strdup(value);
138 425
        AN(e->name);
139 425
        AN(e->value);
140 425
        VTAILQ_INSERT_TAIL(&h->envars, e, list);
141 425
}
142
143
static void
144 250
haproxy_delete_envars(struct haproxy *h)
145
{
146
        struct envar *e, *e2;
147 675
        VTAILQ_FOREACH_SAFE(e, &h->envars, list, e2) {
148 425
                VTAILQ_REMOVE(&h->envars, e, list);
149 425
                free(e->name);
150 425
                free(e->value);
151 425
                free(e);
152 425
        }
153 250
}
154
155
static void
156 250
haproxy_build_env(const struct haproxy *h)
157
{
158
        struct envar *e;
159
160 675
        VTAILQ_FOREACH(e, &h->envars, list) {
161 425
                if (setenv(e->name, e->value, 0) == -1)
162 0
                        vtc_fatal(h->vl, "setenv() failed: %s (%d)",
163 0
                                  strerror(errno), errno);
164 425
        }
165 250
}
166
167
/**********************************************************************
168
 * Socket connect (same as client_tcp_connect()).
169
 */
170
171
static int
172 25
haproxy_cli_tcp_connect(struct vtclog *vl, const char *addr, double tmo,
173
    const char **errp)
174
{
175
        int fd;
176
        char mabuf[VTCP_ADDRBUFSIZE], mpbuf[VTCP_PORTBUFSIZE];
177
178 25
        AN(addr);
179 25
        AN(errp);
180 25
        fd = VTCP_open(addr, NULL, tmo, errp);
181 25
        if (fd < 0)
182 0
                return (fd);
183 25
        VTCP_myname(fd, mabuf, sizeof mabuf, mpbuf, sizeof mpbuf);
184 50
        vtc_log(vl, 3,
185 25
            "CLI connected fd %d from %s %s to %s", fd, mabuf, mpbuf, addr);
186 25
        return (fd);
187 25
}
188
189
/*
190
 * SECTION: haproxy.cli haproxy CLI Specification
191
 * SECTION: haproxy.cli.send
192
 * send STRING
193
 *         Push STRING on the CLI connection. STRING will be terminated by an
194
 *         end of line character (\n).
195
 */
196
static void v_matchproto_(cmd_f)
197 25
cmd_haproxy_cli_send(CMD_ARGS)
198
{
199
        struct vsb *vsb;
200
        struct haproxy_cli *hc;
201
        int j;
202
203 25
        (void)vl;
204 25
        CAST_OBJ_NOTNULL(hc, priv, HAPROXY_CLI_MAGIC);
205 25
        AZ(strcmp(av[0], "send"));
206 25
        AN(av[1]);
207 25
        AZ(av[2]);
208
209 25
        vsb = VSB_new_auto();
210 25
        AN(vsb);
211 25
        AZ(VSB_cat(vsb, av[1]));
212 25
        AZ(VSB_cat(vsb, "\n"));
213 25
        AZ(VSB_finish(vsb));
214 25
        if (hc->sock == -1) {
215
                int fd;
216
                const char *err;
217
                struct vsb *vsb_connect;
218
219 0
                vsb_connect = macro_expand(hc->vl, hc->connect);
220 0
                AN(vsb_connect);
221 0
                fd = haproxy_cli_tcp_connect(hc->vl,
222 0
                    VSB_data(vsb_connect), 10., &err);
223 0
                if (fd < 0)
224 0
                        vtc_fatal(hc->vl,
225 0
                            "CLI failed to open %s: %s", VSB_data(vsb), err);
226 0
                VSB_destroy(&vsb_connect);
227 0
                hc->sock = fd;
228 0
        }
229 25
        vtc_dump(hc->vl, 4, "CLI send", VSB_data(vsb), -1);
230
231 25
        if (VSB_tofile(vsb, hc->sock))
232 0
                vtc_fatal(hc->vl,
233 0
                    "CLI fd %d send error %s", hc->sock, strerror(errno));
234
235
        /* a CLI command must be followed by a SHUT_WR if we want HAProxy to
236
         * close after the response */
237 25
        j = shutdown(hc->sock, SHUT_WR);
238 25
        vtc_log(hc->vl, 3, "CLI shutting fd %d", hc->sock);
239 25
        if (!VTCP_Check(j))
240 0
                vtc_fatal(hc->vl, "Shutdown failed: %s", strerror(errno));
241
242 25
        VSB_destroy(&vsb);
243 25
}
244
245
#define HAPROXY_CLI_RECV_LEN (1 << 14)
246
static void
247 25
haproxy_cli_recv(struct haproxy_cli *hc)
248
{
249
        ssize_t ret;
250
        size_t rdz, left, off;
251
252 25
        rdz = ret = off = 0;
253
        /* We want to null terminate this buffer. */
254 25
        left = hc->rxbuf_sz - 1;
255 50
        while (!vtc_error && left > 0) {
256 50
                VTCP_set_read_timeout(hc->sock, hc->timeout);
257
258 50
                ret = recv(hc->sock, hc->rxbuf + off, HAPROXY_CLI_RECV_LEN, 0);
259 50
                if (ret < 0) {
260 0
                        if (errno == EINTR || errno == EAGAIN)
261 0
                                continue;
262
263 0
                        vtc_fatal(hc->vl,
264
                            "CLI fd %d recv() failed (%s)",
265 0
                            hc->sock, strerror(errno));
266
                }
267
                /* Connection closed. */
268 50
                if (ret == 0) {
269 25
                        if (hc->rxbuf[rdz - 1] != '\n')
270 0
                                vtc_fatal(hc->vl,
271
                                    "CLI rx timeout (fd: %d %.3fs ret: %zd)",
272 0
                                    hc->sock, hc->timeout, ret);
273
274 25
                        vtc_log(hc->vl, 4, "CLI connection normally closed");
275 25
                        vtc_log(hc->vl, 3, "CLI closing fd %d", hc->sock);
276 25
                        VTCP_close(&hc->sock);
277 25
                        break;
278
                }
279
280 25
                rdz += ret;
281 25
                left -= ret;
282 25
                off  += ret;
283
        }
284 25
        hc->rxbuf[rdz] = '\0';
285 25
        vtc_dump(hc->vl, 4, "CLI recv", hc->rxbuf, rdz);
286 25
}
287
288
/*
289
 * SECTION: haproxy.cli.expect
290
 * expect OP STRING
291
 *         Regex match the CLI reception buffer with STRING
292
 *         if OP is ~ or, on the contraty, if OP is !~ check that there is
293
 *         no regex match.
294
 */
295
static void v_matchproto_(cmd_f)
296 25
cmd_haproxy_cli_expect(CMD_ARGS)
297
{
298
        struct haproxy_cli *hc;
299
        struct vsb vsb[1];
300
        vre_t *vre;
301
        int error, erroroffset, i, ret;
302
        char *cmp, *spec, errbuf[VRE_ERROR_LEN];
303
304 25
        (void)vl;
305 25
        CAST_OBJ_NOTNULL(hc, priv, HAPROXY_CLI_MAGIC);
306 25
        AZ(strcmp(av[0], "expect"));
307 25
        av++;
308
309 25
        cmp = av[0];
310 25
        spec = av[1];
311 25
        AN(cmp);
312 25
        AN(spec);
313 25
        AZ(av[2]);
314
315 25
        assert(!strcmp(cmp, "~") || !strcmp(cmp, "!~"));
316
317 25
        haproxy_cli_recv(hc);
318
319 25
        vre = VRE_compile(spec, 0, &error, &erroroffset, 1);
320 25
        if (vre == NULL) {
321 0
                AN(VSB_init(vsb, errbuf, sizeof errbuf));
322 0
                AZ(VRE_error(vsb, error));
323 0
                AZ(VSB_finish(vsb));
324 0
                VSB_fini(vsb);
325 0
                vtc_fatal(hc->vl, "CLI regexp error: '%s' (@%d) (%s)",
326 0
                    errbuf, erroroffset, spec);
327
        }
328
329 25
        i = VRE_match(vre, hc->rxbuf, 0, 0, NULL);
330
331 25
        VRE_free(&vre);
332
333 25
        ret = (i >= 0 && *cmp == '~') || (i < 0 && *cmp == '!');
334 25
        if (!ret)
335 0
                vtc_fatal(hc->vl, "CLI expect failed %s \"%s\"", cmp, spec);
336
        else
337 25
                vtc_log(hc->vl, 4, "CLI expect match %s \"%s\"", cmp, spec);
338 25
}
339
340
static const struct cmds haproxy_cli_cmds[] = {
341
#define CMD_HAPROXY_CLI(n) { #n, cmd_haproxy_cli_##n },
342
        CMD_HAPROXY_CLI(send)
343
        CMD_HAPROXY_CLI(expect)
344
#undef CMD_HAPROXY_CLI
345
        { NULL, NULL }
346
};
347
348
/**********************************************************************
349
 * HAProxy CLI client thread
350
 */
351
352
static void *
353 25
haproxy_cli_thread(void *priv)
354
{
355
        struct haproxy_cli *hc;
356
        struct vsb *vsb;
357
        int fd;
358
        const char *err;
359
360 25
        CAST_OBJ_NOTNULL(hc, priv, HAPROXY_CLI_MAGIC);
361 25
        AN(*hc->connect);
362
363 25
        vsb = macro_expand(hc->vl, hc->connect);
364 25
        AN(vsb);
365
366 25
        fd = haproxy_cli_tcp_connect(hc->vl, VSB_data(vsb), 10., &err);
367 25
        if (fd < 0)
368 0
                vtc_fatal(hc->vl,
369 0
                    "CLI failed to open %s: %s", VSB_data(vsb), err);
370 25
        VTCP_blocking(fd);
371 25
        hc->sock = fd;
372 25
        parse_string(hc->vl, hc, hc->spec);
373 25
        vtc_log(hc->vl, 2, "CLI ending");
374 25
        VSB_destroy(&vsb);
375 25
        return (NULL);
376
}
377
378
/**********************************************************************
379
 * Wait for the CLI client thread to stop
380
 */
381
382
static void
383 25
haproxy_cli_wait(struct haproxy_cli *hc)
384
{
385
        void *res;
386
387 25
        CHECK_OBJ_NOTNULL(hc, HAPROXY_CLI_MAGIC);
388 25
        vtc_log(hc->vl, 2, "CLI waiting");
389 25
        PTOK(pthread_join(hc->tp, &res));
390 25
        if (res != NULL)
391 0
                vtc_fatal(hc->vl, "CLI returned \"%s\"", (char *)res);
392 25
        REPLACE(hc->spec, NULL);
393 25
        hc->tp = 0;
394 25
        hc->running = 0;
395 25
}
396
397
/**********************************************************************
398
 * Start the CLI client thread
399
 */
400
401
static void
402 25
haproxy_cli_start(struct haproxy_cli *hc)
403
{
404 25
        CHECK_OBJ_NOTNULL(hc, HAPROXY_CLI_MAGIC);
405 25
        vtc_log(hc->vl, 2, "CLI starting");
406 25
        PTOK(pthread_create(&hc->tp, NULL, haproxy_cli_thread, hc));
407 25
        hc->running = 1;
408
409 25
}
410
411
/**********************************************************************
412
 * Run the CLI client thread
413
 */
414
415
static void
416 25
haproxy_cli_run(struct haproxy_cli *hc)
417
{
418 25
        haproxy_cli_start(hc);
419 25
        haproxy_cli_wait(hc);
420 25
}
421
422
/**********************************************************************
423
 *
424
 */
425
426
static void
427 75
haproxy_wait_pidfile(struct haproxy *h)
428
{
429 75
        char buf_err[1024] = {0};
430 75
        int usleep_time = 1000;
431
        double t0;
432
        pid_t pid;
433
434 75
        vtc_log(h->vl, 3, "wait-pid-file");
435 3317
        for (t0 = VTIM_mono(); VTIM_mono() - t0 < 3;) {
436 3317
                if (vtc_error)
437 0
                        return;
438
439 3317
                if (VPF_Read(h->pid_fn, &pid) != 0) {
440 3242
                        bprintf(buf_err,
441
                            "Could not read PID file '%s'", h->pid_fn);
442 3242
                        usleep(usleep_time);
443 3242
                        continue;
444
                }
445
446 75
                if (!h->opt_daemon && pid != h->pid) {
447 0
                        bprintf(buf_err,
448
                            "PID file has different PID (%ld != %lld)",
449
                            (long)pid, (long long)h->pid);
450 0
                        usleep(usleep_time);
451 0
                        continue;
452
                }
453
454 75
                if (kill(pid, 0) < 0) {
455 0
                        bprintf(buf_err,
456
                            "Could not find PID %ld process", (long)pid);
457 0
                        usleep(usleep_time);
458 0
                        continue;
459
                }
460
461 75
                h->pid = pid;
462
463 150
                vtc_log(h->vl, 2, "haproxy PID %ld successfully started",
464 75
                    (long)pid);
465 75
                return;
466
        }
467 0
        vtc_fatal(h->vl, "haproxy %s PID file check failed:\n\t%s\n",
468 0
                  h->name, buf_err);
469 75
}
470
471
/**********************************************************************
472
 * Allocate and initialize a CLI client
473
 */
474
475
static struct haproxy_cli *
476 250
haproxy_cli_new(struct haproxy *h)
477
{
478
        struct haproxy_cli *hc;
479
480 250
        ALLOC_OBJ(hc, HAPROXY_CLI_MAGIC);
481 250
        AN(hc);
482
483 250
        hc->vl = h->vl;
484 250
        vtc_log_set_cmd(hc->vl, haproxy_cli_cmds);
485 250
        hc->sock = -1;
486 250
        bprintf(hc->connect, "${%s_cli_sock}", h->name);
487
488 250
        hc->txbuf_sz = hc->rxbuf_sz = 2048 * 1024;
489 250
        hc->txbuf = malloc(hc->txbuf_sz);
490 250
        AN(hc->txbuf);
491 250
        hc->rxbuf = malloc(hc->rxbuf_sz);
492 250
        AN(hc->rxbuf);
493
494 250
        return (hc);
495
}
496
497
/* creates a master CLI client (-mcli) */
498
static struct haproxy_cli *
499 250
haproxy_mcli_new(struct haproxy *h)
500
{
501
        struct haproxy_cli *hc;
502
503 250
        ALLOC_OBJ(hc, HAPROXY_CLI_MAGIC);
504 250
        AN(hc);
505
506 250
        hc->vl = h->vl;
507 250
        vtc_log_set_cmd(hc->vl, haproxy_cli_cmds);
508 250
        hc->sock = -1;
509 250
        bprintf(hc->connect, "${%s_mcli_sock}", h->name);
510
511 250
        hc->txbuf_sz = hc->rxbuf_sz = 2048 * 1024;
512 250
        hc->txbuf = malloc(hc->txbuf_sz);
513 250
        AN(hc->txbuf);
514 250
        hc->rxbuf = malloc(hc->rxbuf_sz);
515 250
        AN(hc->rxbuf);
516
517 250
        return (hc);
518
}
519
520
/* Bind an address/port for the master CLI (-mcli) */
521
static int
522 0
haproxy_create_mcli(struct haproxy *h)
523
{
524
        int sock;
525
        const char *err;
526
        char buf[128], addr[128], port[128];
527 0
        char vsabuf[vsa_suckaddr_len];
528
        const struct suckaddr *sua;
529
530 0
        sock = VTCP_listen_on(default_listen_addr, NULL, 100, &err);
531 0
        if (err != NULL)
532 0
                vtc_fatal(h->vl,
533 0
                          "Create listen socket failed: %s", err);
534 0
        assert(sock > 0);
535 0
        sua = VSA_getsockname(sock, vsabuf, sizeof vsabuf);
536 0
        AN(sua);
537
538 0
        VTCP_name(sua, addr, sizeof addr, port, sizeof port);
539 0
        bprintf(buf, "%s_mcli", h->name);
540 0
        if (VSA_Get_Proto(sua) == AF_INET)
541 0
                macro_def(h->vl, buf, "sock", "%s:%s", addr, port);
542
        else
543 0
                macro_def(h->vl, buf, "sock", "[%s]:%s", addr, port);
544 0
        macro_def(h->vl, buf, "addr", "%s", addr);
545 0
        macro_def(h->vl, buf, "port", "%s", port);
546
547 0
        return (sock);
548 0
}
549
550
static void
551 500
haproxy_cli_delete(struct haproxy_cli *hc)
552
{
553 500
        CHECK_OBJ_NOTNULL(hc, HAPROXY_CLI_MAGIC);
554 500
        REPLACE(hc->spec, NULL);
555 500
        REPLACE(hc->txbuf, NULL);
556 500
        REPLACE(hc->rxbuf, NULL);
557 500
        FREE_OBJ(hc);
558 500
}
559
560
/**********************************************************************
561
 * Allocate and initialize a haproxy
562
 */
563
564
static struct haproxy *
565 250
haproxy_new(const char *name)
566
{
567
        struct haproxy *h;
568
        struct vsb *vsb;
569
        char buf[PATH_MAX];
570
        int closed_sock;
571
        char addr[128], port[128];
572
        const char *err;
573
        const char *env_args;
574 250
        char vsabuf[vsa_suckaddr_len];
575
        const struct suckaddr *sua;
576
577 250
        ALLOC_OBJ(h, HAPROXY_MAGIC);
578 250
        AN(h);
579 250
        REPLACE(h->name, name);
580
581 250
        h->args = VSB_new_auto();
582 250
        env_args = getenv(HAPROXY_ARGS_ENV_VAR);
583 250
        if (env_args) {
584 0
                VSB_cat(h->args, env_args);
585 0
                VSB_cat(h->args, " ");
586 0
        }
587
588 250
        h->vl = vtc_logopen("%s", name);
589 250
        vtc_log_set_cmd(h->vl, haproxy_cli_cmds);
590 250
        AN(h->vl);
591
592 250
        h->filename = getenv(HAPROXY_PROGRAM_ENV_VAR);
593 250
        if (h->filename == NULL)
594 250
                h->filename = "haproxy";
595
596 250
        bprintf(buf, "${tmpdir}/%s", name);
597 250
        vsb = macro_expand(h->vl, buf);
598 250
        AN(vsb);
599 250
        h->workdir = strdup(VSB_data(vsb));
600 250
        AN(h->workdir);
601 250
        VSB_destroy(&vsb);
602
603 250
        bprintf(buf, "%s/stats.sock", h->workdir);
604 250
        h->cli_fn = strdup(buf);
605 250
        AN(h->cli_fn);
606
607 250
        bprintf(buf, "%s/cfg", h->workdir);
608 250
        h->cfg_fn = strdup(buf);
609 250
        AN(h->cfg_fn);
610
611
        /* Create a new TCP socket to reserve an IP:port and close it asap.
612
         * May be useful to simulate an unreachable server.
613
         */
614 250
        bprintf(h->closed_sock, "%s_closed", h->name);
615 250
        closed_sock = VTCP_listen_on("127.0.0.1:0", NULL, 100, &err);
616 250
        if (err != NULL)
617 0
                vtc_fatal(h->vl,
618 0
                        "Create listen socket failed: %s", err);
619 250
        assert(closed_sock > 0);
620 250
        sua = VSA_getsockname(closed_sock, vsabuf, sizeof vsabuf);
621 250
        AN(sua);
622 250
        VTCP_name(sua, addr, sizeof addr, port, sizeof port);
623 250
        if (VSA_Get_Proto(sua) == AF_INET)
624 250
                macro_def(h->vl, h->closed_sock, "sock", "%s:%s", addr, port);
625
        else
626 0
                macro_def(h->vl, h->closed_sock, "sock", "[%s]:%s", addr, port);
627 250
        macro_def(h->vl, h->closed_sock, "addr", "%s", addr);
628 250
        macro_def(h->vl, h->closed_sock, "port", "%s", port);
629 250
        VTCP_close(&closed_sock);
630
631 250
        h->cli = haproxy_cli_new(h);
632 250
        AN(h->cli);
633
634 250
        h->mcli = haproxy_mcli_new(h);
635 250
        AN(h->mcli);
636
637 250
        bprintf(buf, "rm -rf \"%s\" ; mkdir -p \"%s\"", h->workdir, h->workdir);
638 250
        AZ(system(buf));
639
640 250
        VTAILQ_INIT(&h->envars);
641 250
        VTAILQ_INSERT_TAIL(&haproxies, h, list);
642
643 250
        return (h);
644 250
}
645
646
/**********************************************************************
647
 * Delete a haproxy instance
648
 */
649
650
static void
651 250
haproxy_delete(struct haproxy *h)
652
{
653
        char buf[PATH_MAX];
654
655 250
        CHECK_OBJ_NOTNULL(h, HAPROXY_MAGIC);
656 250
        vtc_logclose(h->vl);
657
658 250
        if (!leave_temp) {
659 250
                bprintf(buf, "rm -rf \"%s\"", h->workdir);
660 250
                AZ(system(buf));
661 250
        }
662
663 250
        free(h->name);
664 250
        free(h->workdir);
665 250
        free(h->cli_fn);
666 250
        free(h->cfg_fn);
667 250
        free(h->pid_fn);
668 250
        VSB_destroy(&h->args);
669 250
        haproxy_cli_delete(h->cli);
670 250
        haproxy_cli_delete(h->mcli);
671
672
        /* XXX: MEMLEAK (?) */
673 250
        FREE_OBJ(h);
674 250
}
675
676
/**********************************************************************
677
 * HAProxy listener
678
 */
679
680
static void *
681 250
haproxy_thread(void *priv)
682
{
683
        struct haproxy *h;
684
685 250
        CAST_OBJ_NOTNULL(h, priv, HAPROXY_MAGIC);
686 250
        (void)vtc_record(h->vl, h->fds[0], h->msgs);
687 250
        h->its_dead_jim = 1;
688 250
        return (NULL);
689
}
690
691
692
/**********************************************************************
693
 * Start a HAProxy instance.
694
 */
695
696
static void
697 250
haproxy_start(struct haproxy *h)
698
{
699
        char buf[PATH_MAX];
700
        struct vsb *vsb;
701
702 250
        vtc_log(h->vl, 2, "%s", __func__);
703
704 250
        AZ(VSB_finish(h->args));
705 500
        vtc_log(h->vl, 4, "opt_worker %d opt_daemon %d opt_check_mode %d opt_mcli %d",
706 250
            h->opt_worker, h->opt_daemon, h->opt_check_mode, h->opt_mcli);
707
708 250
        vsb = VSB_new_auto();
709 250
        AN(vsb);
710
711 250
        VSB_printf(vsb, "exec \"%s\"", h->filename);
712 250
        if (h->opt_check_mode)
713 50
                VSB_cat(vsb, " -c");
714 200
        else if (h->opt_daemon)
715 75
                VSB_cat(vsb, " -D");
716
        else
717 125
                VSB_cat(vsb, " -d");
718
719 250
        if (h->opt_worker) {
720 0
                VSB_cat(vsb, " -W");
721 0
                if (h->opt_mcli) {
722
                        int sock;
723 0
                        sock = haproxy_create_mcli(h);
724 0
                        VSB_printf(vsb, " -S \"fd@%d\"", sock);
725 0
                }
726 0
        }
727
728 250
        VSB_printf(vsb, " %s", VSB_data(h->args));
729
730 250
        VSB_printf(vsb, " -f \"%s\" ", h->cfg_fn);
731
732 250
        if (h->opt_worker || h->opt_daemon) {
733 75
                bprintf(buf, "%s/pid", h->workdir);
734 75
                h->pid_fn = strdup(buf);
735 75
                AN(h->pid_fn);
736 75
                VSB_printf(vsb, " -p \"%s\"", h->pid_fn);
737 75
        }
738
739 250
        AZ(VSB_finish(vsb));
740 250
        vtc_dump(h->vl, 4, "argv", VSB_data(vsb), -1);
741
742 250
        if (h->opt_worker && !h->opt_daemon) {
743
                /*
744
                 * HAProxy master process must exit with status 128 + <signum>
745
                 * if signaled by <signum> signal.
746
                 */
747 0
                h->expect_exit = HAPROXY_EXPECT_EXIT;
748 0
        }
749
750 250
        haproxy_write_conf(h);
751
752 250
        AZ(pipe(&h->fds[0]));
753 250
        vtc_log(h->vl, 4, "XXX %d @%d", h->fds[1], __LINE__);
754 250
        AZ(pipe(&h->fds[2]));
755 250
        h->pid = h->ppid = fork();
756 250
        assert(h->pid >= 0);
757 500
        if (h->pid == 0) {
758 250
                haproxy_build_env(h);
759 250
                haproxy_delete_envars(h);
760 250
                AZ(chdir(h->name));
761 250
                AZ(dup2(h->fds[0], 0));
762 250
                assert(dup2(h->fds[3], 1) == 1);
763 250
                assert(dup2(1, 2) == 2);
764 250
                closefd(&h->fds[0]);
765 250
                closefd(&h->fds[1]);
766 250
                closefd(&h->fds[2]);
767 250
                closefd(&h->fds[3]);
768 250
                AZ(execl("/bin/sh", "/bin/sh", "-c", VSB_data(vsb), (char*)0));
769 0
                exit(1);
770
        }
771 250
        VSB_destroy(&vsb);
772
773 250
        vtc_log(h->vl, 3, "PID: %ld", (long)h->pid);
774 250
        macro_def(h->vl, h->name, "pid", "%ld", (long)h->pid);
775 250
        macro_def(h->vl, h->name, "name", "%s", h->workdir);
776
777 250
        closefd(&h->fds[0]);
778 250
        closefd(&h->fds[3]);
779 250
        h->fds[0] = h->fds[2];
780 250
        h->fds[2] = h->fds[3] = -1;
781
782 250
        PTOK(pthread_create(&h->tp, NULL, haproxy_thread, h));
783
784 250
        if (h->pid_fn != NULL)
785 75
                haproxy_wait_pidfile(h);
786 250
}
787
788
789
/**********************************************************************
790
 * Wait for a HAProxy instance.
791
 */
792
793
static void
794 250
haproxy_wait(struct haproxy *h)
795
{
796
        void *p;
797
        int i, n, sig;
798
799 250
        vtc_log(h->vl, 2, "Wait");
800
801 250
        if (h->pid < 0)
802 0
                haproxy_start(h);
803
804 0
        if (h->cli->spec)
805 0
                haproxy_cli_run(h->cli);
806
807 0
        if (h->mcli->spec)
808 0
                haproxy_cli_run(h->mcli);
809
810 250
        closefd(&h->fds[1]);
811
812 250
        sig = SIGINT;
813 250
        n = 0;
814 250
        vtc_log(h->vl, 2, "Stop HAproxy pid=%ld", (long)h->pid);
815 5875
        while (h->opt_daemon || (!h->opt_check_mode && !h->its_dead_jim)) {
816 5700
                assert(h->pid > 0);
817 5700
                if (n == 0) {
818 475
                        i = kill(h->pid, sig);
819 475
                        if (i == 0)
820 400
                                h->expect_signal = -sig;
821 725
                        if (i && errno == ESRCH)
822 75
                                break;
823 800
                        vtc_log(h->vl, 4,
824 400
                            "Kill(%d)=%d: %s", sig, i, strerror(errno));
825 400
                }
826 525
                usleep(100000);
827 525
                if (++n == 20) {
828 275
                        switch (sig) {
829 200
                        case SIGINT:    sig = SIGTERM ; break;
830 75
                        case SIGTERM:   sig = SIGKILL ; break;
831 0
                        default:        break;
832
                        }
833 275
                        n = 0;
834 275
                }
835
        }
836
837 250
        PTOK(pthread_join(h->tp, &p));
838 250
        AZ(p);
839 250
        closefd(&h->fds[0]);
840 250
        if (!h->opt_daemon) {
841 175
                vtc_wait4(h->vl, h->ppid, h->expect_exit, h->expect_signal, 0);
842 175
                h->ppid = -1;
843 175
        }
844 250
        h->pid = -1;
845 250
}
846
847
#define HAPROXY_BE_FD_STR     "fd@${"
848
#define HAPROXY_BE_FD_STRLEN  strlen(HAPROXY_BE_FD_STR)
849
850
static int
851 250
haproxy_build_backends(struct haproxy *h, const char *vsb_data)
852
{
853
        char *s, *p, *q;
854
855 250
        s = strdup(vsb_data);
856 250
        if (!s)
857 0
                return (-1);
858
859 250
        p = s;
860 675
        while (1) {
861
                int sock;
862
                char buf[128], addr[128], port[128];
863
                const char *err;
864 675
                char vsabuf[vsa_suckaddr_len];
865
                const struct suckaddr *sua;
866
867 675
                p = strstr(p, HAPROXY_BE_FD_STR);
868 675
                if (!p)
869 250
                        break;
870
871 425
                q = p += HAPROXY_BE_FD_STRLEN;
872 1750
                while (*q && *q != '}')
873 1325
                        q++;
874 425
                if (*q != '}')
875 0
                        break;
876
877 425
                *q++ = '\0';
878 425
                sock = VTCP_listen_on("127.0.0.1:0", NULL, 100, &err);
879 425
                if (err != NULL)
880 0
                        vtc_fatal(h->vl,
881 0
                            "Create listen socket failed: %s", err);
882 425
                assert(sock > 0);
883 425
                sua = VSA_getsockname(sock, vsabuf, sizeof vsabuf);
884 425
                AN(sua);
885
886 425
                VTCP_name(sua, addr, sizeof addr, port, sizeof port);
887 425
                bprintf(buf, "%s_%s", h->name, p);
888 425
                if (VSA_Get_Proto(sua) == AF_INET)
889 425
                        macro_def(h->vl, buf, "sock", "%s:%s", addr, port);
890
                else
891 0
                        macro_def(h->vl, buf, "sock", "[%s]:%s", addr, port);
892 425
                macro_def(h->vl, buf, "addr", "%s", addr);
893 425
                macro_def(h->vl, buf, "port", "%s", port);
894
895 425
                bprintf(buf, "%d", sock);
896 425
                vtc_log(h->vl, 4, "setenv(%s, %s)", p, buf);
897 425
                haproxy_add_envar(h, p, buf);
898 425
                p = q;
899 675
        }
900 250
        free(s);
901 250
        return (0);
902 250
}
903
904
static void
905 50
haproxy_check_conf(struct haproxy *h, const char *expect)
906
{
907
908 50
        h->msgs = VSB_new_auto();
909 50
        AN(h->msgs);
910 50
        h->opt_check_mode = 1;
911 50
        haproxy_start(h);
912 50
        haproxy_wait(h);
913 50
        AZ(VSB_finish(h->msgs));
914 50
        if (strstr(VSB_data(h->msgs), expect) == NULL)
915 0
                vtc_fatal(h->vl, "Did not find expected string '%s'", expect);
916 50
        vtc_log(h->vl, 2, "Found expected '%s'", expect);
917 50
        VSB_destroy(&h->msgs);
918 50
}
919
920
/**********************************************************************
921
 * Write a configuration for <h> HAProxy instance.
922
 */
923
924
static void
925 250
haproxy_store_conf(struct haproxy *h, const char *cfg, int auto_be)
926
{
927
        struct vsb *vsb, *vsb2;
928
929 250
        vsb = VSB_new_auto();
930 250
        AN(vsb);
931
932 250
        vsb2 = VSB_new_auto();
933 250
        AN(vsb2);
934
935 500
        VSB_printf(vsb, "    global\n\tstats socket \"%s\" "
936 250
                   "level admin mode 600\n", h->cli_fn);
937 250
        VSB_cat(vsb, "    stats socket \"fd@${cli}\" level admin\n");
938 250
        AZ(VSB_cat(vsb, cfg));
939
940 250
        if (auto_be)
941 25
                cmd_server_gen_haproxy_conf(vsb);
942
943 250
        AZ(VSB_finish(vsb));
944
945 250
        AZ(haproxy_build_backends(h, VSB_data(vsb)));
946
947 250
        h->cfg_vsb = macro_expand(h->vl, VSB_data(vsb));
948 250
        AN(h->cfg_vsb);
949
950 250
        VSB_destroy(&vsb2);
951 250
        VSB_destroy(&vsb);
952 250
}
953
954
static void
955 250
haproxy_write_conf(struct haproxy *h)
956
{
957
        struct vsb *vsb;
958
959 250
        vsb = macro_expand(h->vl, VSB_data(h->cfg_vsb));
960 250
        AN(vsb);
961 250
        assert(VSB_len(vsb) >= 0);
962
963 250
        vtc_dump(h->vl, 4, "conf", VSB_data(vsb), VSB_len(vsb));
964 750
        if (VFIL_writefile(h->workdir, h->cfg_fn,
965 500
            VSB_data(vsb), VSB_len(vsb)) != 0)
966 0
                vtc_fatal(h->vl,
967
                    "failed to write haproxy configuration file: %s (%d)",
968 0
                    strerror(errno), errno);
969
970 250
        VSB_destroy(&vsb);
971 250
}
972
973
/* SECTION: haproxy haproxy
974
 *
975
 * Define and interact with haproxy instances.
976
 *
977
 * To define a haproxy server, you'll use this syntax::
978
 *
979
 *      haproxy hNAME -conf-OK CONFIG
980
 *      haproxy hNAME -conf-BAD ERROR CONFIG
981
 *      haproxy hNAME [-D] [-W] [-arg STRING] [-conf[+vcl] STRING]
982
 *
983
 * The first ``haproxy hNAME`` invocation will start the haproxy master
984
 * process in the background, waiting for the ``-start`` switch to actually
985
 * start the child.
986
 *
987
 * Arguments:
988
 *
989
 * hNAME
990
 *         Identify the HAProxy server with a string, it must starts with 'h'.
991
 *
992
 * \-conf-OK CONFIG
993
 *         Run haproxy in '-c' mode to check config is OK
994
 *         stdout/stderr should contain 'Configuration file is valid'
995
 *         The exit code should be 0.
996
 *
997
 * \-conf-BAD ERROR CONFIG
998
 *         Run haproxy in '-c' mode to check config is BAD.
999
 *         "ERROR" should be part of the diagnostics on stdout/stderr.
1000
 *         The exit code should be 1.
1001
 *
1002
 * \-D
1003
 *         Run HAproxy in daemon mode.  If not given '-d' mode used.
1004
 *
1005
 * \-W
1006
 *         Enable HAproxy in Worker mode.
1007
 *
1008
 * \-S
1009
 *         Enable HAproxy Master CLI in Worker mode
1010
 *
1011
 * \-arg STRING
1012
 *         Pass an argument to haproxy, for example "-h simple_list".
1013
 *
1014
 * \-cli STRING
1015
 *         Specify the spec to be run by the command line interface (CLI).
1016
 *
1017
 * \-mcli STRING
1018
 *         Specify the spec to be run by the command line interface (CLI)
1019
 *         of the Master process.
1020
 *
1021
 * \-conf STRING
1022
 *         Specify the configuration to be loaded by this HAProxy instance.
1023
 *
1024
 * \-conf+backend STRING
1025
 *         Specify the configuration to be loaded by this HAProxy instance,
1026
 *         all server instances will be automatically appended
1027
 *
1028
 * \-start
1029
 *         Start this HAProxy instance.
1030
 *
1031
 * \-wait
1032
 *         Stop this HAProxy instance.
1033
 *
1034
 * \-expectexit NUMBER
1035
 *         Expect haproxy to exit(3) with this value
1036
 *
1037
 */
1038
1039
void
1040 24700
cmd_haproxy(CMD_ARGS)
1041
{
1042
        struct haproxy *h, *h2;
1043
1044 24700
        (void)priv;
1045
1046 24700
        if (av == NULL) {
1047
                /* Reset and free */
1048 24675
                VTAILQ_FOREACH_SAFE(h, &haproxies, list, h2) {
1049 500
                        vtc_log(h->vl, 2,
1050
                            "Reset and free %s haproxy %ld",
1051 250
                            h->name, (long)h->pid);
1052 250
                        if (h->pid >= 0)
1053 175
                                haproxy_wait(h);
1054 250
                        VTAILQ_REMOVE(&haproxies, h, list);
1055 250
                        haproxy_delete(h);
1056 250
                }
1057 24425
                return;
1058
        }
1059
1060 275
        AZ(strcmp(av[0], "haproxy"));
1061 275
        av++;
1062
1063 275
        VTC_CHECK_NAME(vl, av[0], "haproxy", 'h');
1064 325
        VTAILQ_FOREACH(h, &haproxies, list)
1065 75
                if (!strcmp(h->name, av[0]))
1066 25
                        break;
1067 250
        if (h == NULL)
1068 250
                h = haproxy_new(av[0]);
1069 275
        av++;
1070
1071 850
        for (; *av != NULL; av++) {
1072 575
                if (vtc_error)
1073 0
                        break;
1074
1075 575
                if (!strcmp(*av, "-conf-OK")) {
1076 25
                        AN(av[1]);
1077 25
                        haproxy_store_conf(h, av[1], 0);
1078 25
                        h->expect_exit = 0;
1079 25
                        haproxy_check_conf(h, "");
1080 25
                        av++;
1081 25
                        continue;
1082
                }
1083 550
                if (!strcmp(*av, "-conf-BAD")) {
1084 25
                        AN(av[1]);
1085 25
                        AN(av[2]);
1086 25
                        haproxy_store_conf(h, av[2], 0);
1087 25
                        h->expect_exit = 1;
1088 25
                        haproxy_check_conf(h, av[1]);
1089 25
                        av += 2;
1090 25
                        continue;
1091
                }
1092
1093 525
                if (!strcmp(*av, HAPROXY_OPT_DAEMON)) {
1094 75
                        h->opt_daemon = 1;
1095 75
                        continue;
1096
                }
1097 450
                if (!strcmp(*av, HAPROXY_OPT_WORKER)) {
1098 0
                        h->opt_worker = 1;
1099 0
                        continue;
1100
                }
1101 450
                if (!strcmp(*av, HAPROXY_OPT_MCLI)) {
1102 0
                        h->opt_mcli = 1;
1103 0
                        continue;
1104
                }
1105 450
                if (!strcmp(*av, "-arg")) {
1106 0
                        AN(av[1]);
1107 0
                        AZ(h->pid);
1108 0
                        VSB_cat(h->args, " ");
1109 0
                        VSB_cat(h->args, av[1]);
1110 0
                        av++;
1111 0
                        continue;
1112
                }
1113
1114 450
                if (!strcmp(*av, "-cli")) {
1115 25
                        REPLACE(h->cli->spec, av[1]);
1116 25
                        if (h->tp)
1117 25
                                haproxy_cli_run(h->cli);
1118 25
                        av++;
1119 25
                        continue;
1120
                }
1121
1122 425
                if (!strcmp(*av, "-mcli")) {
1123 0
                        REPLACE(h->mcli->spec, av[1]);
1124 0
                        if (h->tp)
1125 0
                                haproxy_cli_run(h->mcli);
1126 0
                        av++;
1127 0
                        continue;
1128
                }
1129
1130 425
                if (!strcmp(*av, "-conf")) {
1131 175
                        AN(av[1]);
1132 175
                        haproxy_store_conf(h, av[1], 0);
1133 175
                        av++;
1134 175
                        continue;
1135
                }
1136 250
                if (!strcmp(*av, "-conf+backend")) {
1137 25
                        AN(av[1]);
1138 25
                        haproxy_store_conf(h, av[1], 1);
1139 25
                        av++;
1140 25
                        continue;
1141
                }
1142
1143 225
                if (!strcmp(*av, "-expectexit")) {
1144 0
                        h->expect_exit = strtoul(av[1], NULL, 0);
1145 0
                        av++;
1146 0
                        continue;
1147
                }
1148 225
                if (!strcmp(*av, "-start")) {
1149 200
                        haproxy_start(h);
1150 200
                        continue;
1151
                }
1152 25
                if (!strcmp(*av, "-wait")) {
1153 25
                        haproxy_wait(h);
1154 25
                        continue;
1155
                }
1156 0
                vtc_fatal(h->vl, "Unknown haproxy argument: %s", *av);
1157
        }
1158 24700
}