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 17
haproxy_add_envar(struct haproxy *h,
130
                  const char *name, const char *value)
131
{
132
        struct envar *e;
133
134 17
        e = malloc(sizeof *e);
135 17
        AN(e);
136 17
        e->name = strdup(name);
137 17
        e->value = strdup(value);
138 17
        AN(e->name);
139 17
        AN(e->value);
140 17
        VTAILQ_INSERT_TAIL(&h->envars, e, list);
141 17
}
142
143
static void
144 10
haproxy_delete_envars(struct haproxy *h)
145
{
146
        struct envar *e, *e2;
147 27
        VTAILQ_FOREACH_SAFE(e, &h->envars, list, e2) {
148 17
                VTAILQ_REMOVE(&h->envars, e, list);
149 17
                free(e->name);
150 17
                free(e->value);
151 17
                free(e);
152 17
        }
153 10
}
154
155
static void
156 10
haproxy_build_env(const struct haproxy *h)
157
{
158
        struct envar *e;
159
160 27
        VTAILQ_FOREACH(e, &h->envars, list) {
161 17
                if (setenv(e->name, e->value, 0) == -1)
162 0
                        vtc_fatal(h->vl, "setenv() failed: %s (%d)",
163 0
                                  strerror(errno), errno);
164 17
        }
165 10
}
166
167
/**********************************************************************
168
 * Socket connect (same as client_tcp_connect()).
169
 */
170
171
static int
172 1
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 1
        AN(addr);
179 1
        AN(errp);
180 1
        fd = VTCP_open(addr, NULL, tmo, errp);
181 1
        if (fd < 0)
182 0
                return (fd);
183 1
        VTCP_myname(fd, mabuf, sizeof mabuf, mpbuf, sizeof mpbuf);
184 2
        vtc_log(vl, 3,
185 1
            "CLI connected fd %d from %s %s to %s", fd, mabuf, mpbuf, addr);
186 1
        return (fd);
187 1
}
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 1
cmd_haproxy_cli_send(CMD_ARGS)
198
{
199
        struct vsb *vsb;
200
        struct haproxy_cli *hc;
201
        int j;
202
203 1
        (void)vl;
204 1
        CAST_OBJ_NOTNULL(hc, priv, HAPROXY_CLI_MAGIC);
205 1
        AZ(strcmp(av[0], "send"));
206 1
        AN(av[1]);
207 1
        AZ(av[2]);
208
209 1
        vsb = VSB_new_auto();
210 1
        AN(vsb);
211 1
        AZ(VSB_cat(vsb, av[1]));
212 1
        AZ(VSB_cat(vsb, "\n"));
213 1
        AZ(VSB_finish(vsb));
214 1
        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 1
        vtc_dump(hc->vl, 4, "CLI send", VSB_data(vsb), -1);
230
231 1
        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 1
        j = shutdown(hc->sock, SHUT_WR);
238 1
        vtc_log(hc->vl, 3, "CLI shutting fd %d", hc->sock);
239 1
        if (!VTCP_Check(j))
240 0
                vtc_fatal(hc->vl, "Shutdown failed: %s", strerror(errno));
241
242 1
        VSB_destroy(&vsb);
243 1
}
244
245
#define HAPROXY_CLI_RECV_LEN (1 << 14)
246
static void
247 1
haproxy_cli_recv(struct haproxy_cli *hc)
248
{
249
        ssize_t ret;
250
        size_t rdz, left, off;
251
252 1
        rdz = ret = off = 0;
253
        /* We want to null terminate this buffer. */
254 1
        left = hc->rxbuf_sz - 1;
255 2
        while (!vtc_error && left > 0) {
256 2
                VTCP_set_read_timeout(hc->sock, hc->timeout);
257
258 2
                ret = recv(hc->sock, hc->rxbuf + off, HAPROXY_CLI_RECV_LEN, 0);
259 2
                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 2
                if (ret == 0) {
269 1
                        if (rdz > 0 && 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 1
                        vtc_log(hc->vl, 4, "CLI connection normally closed");
275 1
                        vtc_log(hc->vl, 3, "CLI closing fd %d", hc->sock);
276 1
                        VTCP_close(&hc->sock);
277 1
                        break;
278
                }
279
280 1
                rdz += ret;
281 1
                left -= ret;
282 1
                off  += ret;
283
        }
284 1
        hc->rxbuf[rdz] = '\0';
285 1
        vtc_dump(hc->vl, 4, "CLI recv", hc->rxbuf, rdz);
286 1
}
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 contrary, if OP is !~ check that there is
293
 *         no regex match.
294
 */
295
static void v_matchproto_(cmd_f)
296 1
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 1
        (void)vl;
305 1
        CAST_OBJ_NOTNULL(hc, priv, HAPROXY_CLI_MAGIC);
306 1
        AZ(strcmp(av[0], "expect"));
307 1
        av++;
308
309 1
        cmp = av[0];
310 1
        spec = av[1];
311 1
        AN(cmp);
312 1
        AN(spec);
313 1
        AZ(av[2]);
314
315 1
        assert(!strcmp(cmp, "~") || !strcmp(cmp, "!~"));
316
317 1
        haproxy_cli_recv(hc);
318
319 1
        vre = VRE_compile(spec, 0, &error, &erroroffset, 1);
320 1
        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 1
        i = VRE_match(vre, hc->rxbuf, 0, 0, NULL);
330
331 1
        VRE_free(&vre);
332
333 1
        ret = (i >= 0 && *cmp == '~') || (i < 0 && *cmp == '!');
334 1
        if (!ret)
335 0
                vtc_fatal(hc->vl, "CLI expect failed %s \"%s\"", cmp, spec);
336
        else
337 1
                vtc_log(hc->vl, 4, "CLI expect match %s \"%s\"", cmp, spec);
338 1
}
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 1
haproxy_cli_thread(void *priv)
354
{
355
        struct haproxy_cli *hc;
356
        struct vsb *vsb;
357
        int fd;
358
        const char *err;
359
360 1
        CAST_OBJ_NOTNULL(hc, priv, HAPROXY_CLI_MAGIC);
361 1
        AN(*hc->connect);
362
363 1
        vsb = macro_expand(hc->vl, hc->connect);
364 1
        AN(vsb);
365
366 1
        fd = haproxy_cli_tcp_connect(hc->vl, VSB_data(vsb), 10., &err);
367 1
        if (fd < 0)
368 0
                vtc_fatal(hc->vl,
369 0
                    "CLI failed to open %s: %s", VSB_data(vsb), err);
370 1
        VTCP_blocking(fd);
371 1
        hc->sock = fd;
372 1
        parse_string(hc->vl, hc, hc->spec);
373 1
        vtc_log(hc->vl, 2, "CLI ending");
374 1
        VSB_destroy(&vsb);
375 1
        return (NULL);
376
}
377
378
/**********************************************************************
379
 * Wait for the CLI client thread to stop
380
 */
381
382
static void
383 1
haproxy_cli_wait(struct haproxy_cli *hc)
384
{
385
        void *res;
386
387 1
        CHECK_OBJ_NOTNULL(hc, HAPROXY_CLI_MAGIC);
388 1
        vtc_log(hc->vl, 2, "CLI waiting");
389 1
        PTOK(pthread_join(hc->tp, &res));
390 1
        if (res != NULL)
391 0
                vtc_fatal(hc->vl, "CLI returned \"%s\"", (char *)res);
392 1
        REPLACE(hc->spec, NULL);
393 1
        hc->tp = 0;
394 1
        hc->running = 0;
395 1
}
396
397
/**********************************************************************
398
 * Start the CLI client thread
399
 */
400
401
static void
402 1
haproxy_cli_start(struct haproxy_cli *hc)
403
{
404 1
        CHECK_OBJ_NOTNULL(hc, HAPROXY_CLI_MAGIC);
405 1
        vtc_log(hc->vl, 2, "CLI starting");
406 1
        PTOK(pthread_create(&hc->tp, NULL, haproxy_cli_thread, hc));
407 1
        hc->running = 1;
408
409 1
}
410
411
/**********************************************************************
412
 * Run the CLI client thread
413
 */
414
415
static void
416 1
haproxy_cli_run(struct haproxy_cli *hc)
417
{
418 1
        haproxy_cli_start(hc);
419 1
        haproxy_cli_wait(hc);
420 1
}
421
422
/**********************************************************************
423
 *
424
 */
425
426
static void
427 3
haproxy_wait_pidfile(struct haproxy *h)
428
{
429 3
        char buf_err[1024] = {0};
430 3
        int usleep_time = 1000;
431
        double t0;
432
        pid_t pid;
433
434 3
        vtc_log(h->vl, 3, "wait-pid-file");
435 658
        for (t0 = VTIM_mono(); VTIM_mono() - t0 < 3;) {
436 658
                if (vtc_error)
437 0
                        return;
438
439 658
                if (VPF_Read(h->pid_fn, &pid) != 0) {
440 655
                        bprintf(buf_err,
441
                            "Could not read PID file '%s'", h->pid_fn);
442 655
                        usleep(usleep_time);
443 655
                        continue;
444
                }
445
446 3
                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 3
                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 3
                h->pid = pid;
462
463 6
                vtc_log(h->vl, 2, "haproxy PID %ld successfully started",
464 3
                    (long)pid);
465 3
                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 3
}
470
471
/**********************************************************************
472
 * Allocate and initialize a CLI client
473
 */
474
475
static struct haproxy_cli *
476 10
haproxy_cli_new(struct haproxy *h)
477
{
478
        struct haproxy_cli *hc;
479
480 10
        ALLOC_OBJ(hc, HAPROXY_CLI_MAGIC);
481 10
        AN(hc);
482
483 10
        hc->vl = h->vl;
484 10
        vtc_log_set_cmd(hc->vl, haproxy_cli_cmds);
485 10
        hc->sock = -1;
486 10
        bprintf(hc->connect, "${%s_cli_sock}", h->name);
487
488 10
        hc->txbuf_sz = hc->rxbuf_sz = 2048 * 1024;
489 10
        hc->txbuf = malloc(hc->txbuf_sz);
490 10
        AN(hc->txbuf);
491 10
        hc->rxbuf = malloc(hc->rxbuf_sz);
492 10
        AN(hc->rxbuf);
493
494 10
        return (hc);
495
}
496
497
/* creates a master CLI client (-mcli) */
498
static struct haproxy_cli *
499 10
haproxy_mcli_new(struct haproxy *h)
500
{
501
        struct haproxy_cli *hc;
502
503 10
        ALLOC_OBJ(hc, HAPROXY_CLI_MAGIC);
504 10
        AN(hc);
505
506 10
        hc->vl = h->vl;
507 10
        vtc_log_set_cmd(hc->vl, haproxy_cli_cmds);
508 10
        hc->sock = -1;
509 10
        bprintf(hc->connect, "${%s_mcli_sock}", h->name);
510
511 10
        hc->txbuf_sz = hc->rxbuf_sz = 2048 * 1024;
512 10
        hc->txbuf = malloc(hc->txbuf_sz);
513 10
        AN(hc->txbuf);
514 10
        hc->rxbuf = malloc(hc->rxbuf_sz);
515 10
        AN(hc->rxbuf);
516
517 10
        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 20
haproxy_cli_delete(struct haproxy_cli *hc)
552
{
553 20
        CHECK_OBJ_NOTNULL(hc, HAPROXY_CLI_MAGIC);
554 20
        REPLACE(hc->spec, NULL);
555 20
        REPLACE(hc->txbuf, NULL);
556 20
        REPLACE(hc->rxbuf, NULL);
557 20
        FREE_OBJ(hc);
558 20
}
559
560
/**********************************************************************
561
 * Allocate and initialize a haproxy
562
 */
563
564
static struct haproxy *
565 10
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 10
        char vsabuf[vsa_suckaddr_len];
575
        const struct suckaddr *sua;
576
577 10
        ALLOC_OBJ(h, HAPROXY_MAGIC);
578 10
        AN(h);
579 10
        REPLACE(h->name, name);
580
581 10
        h->args = VSB_new_auto();
582 10
        env_args = getenv(HAPROXY_ARGS_ENV_VAR);
583 10
        if (env_args) {
584 0
                VSB_cat(h->args, env_args);
585 0
                VSB_cat(h->args, " ");
586 0
        }
587
588 10
        h->vl = vtc_logopen("%s", name);
589 10
        vtc_log_set_cmd(h->vl, haproxy_cli_cmds);
590 10
        AN(h->vl);
591
592 10
        h->filename = getenv(HAPROXY_PROGRAM_ENV_VAR);
593 10
        if (h->filename == NULL)
594 10
                h->filename = "haproxy";
595
596 10
        bprintf(buf, "${tmpdir}/%s", name);
597 10
        vsb = macro_expand(h->vl, buf);
598 10
        AN(vsb);
599 10
        h->workdir = strdup(VSB_data(vsb));
600 10
        AN(h->workdir);
601 10
        VSB_destroy(&vsb);
602
603 10
        bprintf(buf, "%s/stats.sock", h->workdir);
604 10
        h->cli_fn = strdup(buf);
605 10
        AN(h->cli_fn);
606
607 10
        bprintf(buf, "%s/cfg", h->workdir);
608 10
        h->cfg_fn = strdup(buf);
609 10
        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 10
        bprintf(h->closed_sock, "%s_closed", h->name);
615 10
        closed_sock = VTCP_listen_on("127.0.0.1:0", NULL, 100, &err);
616 10
        if (err != NULL)
617 0
                vtc_fatal(h->vl,
618 0
                        "Create listen socket failed: %s", err);
619 10
        assert(closed_sock > 0);
620 10
        sua = VSA_getsockname(closed_sock, vsabuf, sizeof vsabuf);
621 10
        AN(sua);
622 10
        VTCP_name(sua, addr, sizeof addr, port, sizeof port);
623 10
        if (VSA_Get_Proto(sua) == AF_INET)
624 10
                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 10
        macro_def(h->vl, h->closed_sock, "addr", "%s", addr);
628 10
        macro_def(h->vl, h->closed_sock, "port", "%s", port);
629 10
        VTCP_close(&closed_sock);
630
631 10
        h->cli = haproxy_cli_new(h);
632 10
        AN(h->cli);
633
634 10
        h->mcli = haproxy_mcli_new(h);
635 10
        AN(h->mcli);
636
637 10
        bprintf(buf, "rm -rf \"%s\" ; mkdir -p \"%s\"", h->workdir, h->workdir);
638 10
        AZ(system(buf));
639
640 10
        VTAILQ_INIT(&h->envars);
641 10
        VTAILQ_INSERT_TAIL(&haproxies, h, list);
642
643 10
        return (h);
644 10
}
645
646
/**********************************************************************
647
 * Delete a haproxy instance
648
 */
649
650
static void
651 10
haproxy_delete(struct haproxy *h)
652
{
653
        char buf[PATH_MAX];
654
655 10
        CHECK_OBJ_NOTNULL(h, HAPROXY_MAGIC);
656 10
        vtc_logclose(h->vl);
657
658 10
        if (!leave_temp) {
659 10
                bprintf(buf, "rm -rf \"%s\"", h->workdir);
660 10
                AZ(system(buf));
661 10
        }
662
663 10
        free(h->name);
664 10
        free(h->workdir);
665 10
        free(h->cli_fn);
666 10
        free(h->cfg_fn);
667 10
        free(h->pid_fn);
668 10
        VSB_destroy(&h->args);
669 10
        haproxy_cli_delete(h->cli);
670 10
        haproxy_cli_delete(h->mcli);
671
672
        /* XXX: MEMLEAK (?) */
673 10
        FREE_OBJ(h);
674 10
}
675
676
/**********************************************************************
677
 * HAProxy listener
678
 */
679
680
static void *
681 10
haproxy_thread(void *priv)
682
{
683
        struct haproxy *h;
684
685 10
        CAST_OBJ_NOTNULL(h, priv, HAPROXY_MAGIC);
686 10
        (void)vtc_record(h->vl, h->fds[0], h->msgs);
687 10
        h->its_dead_jim = 1;
688 10
        return (NULL);
689
}
690
691
692
/**********************************************************************
693
 * Start a HAProxy instance.
694
 */
695
696
static void
697 10
haproxy_start(struct haproxy *h)
698
{
699
        char buf[PATH_MAX];
700
        struct vsb *vsb;
701
702 10
        vtc_log(h->vl, 2, "%s", __func__);
703
704 10
        AZ(VSB_finish(h->args));
705 20
        vtc_log(h->vl, 4, "opt_worker %d opt_daemon %d opt_check_mode %d opt_mcli %d",
706 10
            h->opt_worker, h->opt_daemon, h->opt_check_mode, h->opt_mcli);
707
708 10
        vsb = VSB_new_auto();
709 10
        AN(vsb);
710
711 10
        VSB_printf(vsb, "exec \"%s\"", h->filename);
712 10
        if (h->opt_check_mode)
713 2
                VSB_cat(vsb, " -c");
714 8
        else if (h->opt_daemon)
715 3
                VSB_cat(vsb, " -D");
716
        else
717 5
                VSB_cat(vsb, " -d");
718
719 10
        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 10
        VSB_printf(vsb, " %s", VSB_data(h->args));
729
730 10
        VSB_printf(vsb, " -f \"%s\" ", h->cfg_fn);
731
732 10
        if (h->opt_worker || h->opt_daemon) {
733 3
                bprintf(buf, "%s/pid", h->workdir);
734 3
                h->pid_fn = strdup(buf);
735 3
                AN(h->pid_fn);
736 3
                VSB_printf(vsb, " -p \"%s\"", h->pid_fn);
737 3
        }
738
739 10
        AZ(VSB_finish(vsb));
740 10
        vtc_dump(h->vl, 4, "argv", VSB_data(vsb), -1);
741
742 10
        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 10
        haproxy_write_conf(h);
751
752 10
        AZ(pipe(&h->fds[0]));
753 10
        vtc_log(h->vl, 4, "XXX %d @%d", h->fds[1], __LINE__);
754 10
        AZ(pipe(&h->fds[2]));
755 10
        h->pid = h->ppid = fork();
756 10
        assert(h->pid >= 0);
757 20
        if (h->pid == 0) {
758 10
                haproxy_build_env(h);
759 10
                haproxy_delete_envars(h);
760 10
                AZ(chdir(h->name));
761 10
                AZ(dup2(h->fds[0], 0));
762 10
                assert(dup2(h->fds[3], 1) == 1);
763 10
                assert(dup2(1, 2) == 2);
764 10
                closefd(&h->fds[0]);
765 10
                closefd(&h->fds[1]);
766 10
                closefd(&h->fds[2]);
767 10
                closefd(&h->fds[3]);
768 10
                AZ(execl("/bin/sh", "/bin/sh", "-c", VSB_data(vsb), (char*)0));
769 0
                exit(1);
770
        }
771 10
        VSB_destroy(&vsb);
772
773 10
        vtc_log(h->vl, 3, "PID: %ld", (long)h->pid);
774 10
        macro_def(h->vl, h->name, "pid", "%ld", (long)h->pid);
775 10
        macro_def(h->vl, h->name, "name", "%s", h->workdir);
776
777 10
        closefd(&h->fds[0]);
778 10
        closefd(&h->fds[3]);
779 10
        h->fds[0] = h->fds[2];
780 10
        h->fds[2] = h->fds[3] = -1;
781
782 10
        PTOK(pthread_create(&h->tp, NULL, haproxy_thread, h));
783
784 10
        if (h->pid_fn != NULL)
785 3
                haproxy_wait_pidfile(h);
786 10
}
787
788
789
/**********************************************************************
790
 * Wait for a HAProxy instance.
791
 */
792
793
static void
794 10
haproxy_wait(struct haproxy *h)
795
{
796
        void *p;
797
        int i, n, sig;
798
799 10
        vtc_log(h->vl, 2, "Wait");
800
801 10
        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 10
        closefd(&h->fds[1]);
811
812 10
        sig = SIGINT;
813 10
        n = 0;
814 10
        vtc_log(h->vl, 2, "Stop HAproxy pid=%ld", (long)h->pid);
815 235
        while (h->opt_daemon || (!h->opt_check_mode && !h->its_dead_jim)) {
816 228
                assert(h->pid > 0);
817 228
                if (n == 0) {
818 19
                        i = kill(h->pid, sig);
819 19
                        if (i == 0)
820 16
                                h->expect_signal = -sig;
821 29
                        if (i && errno == ESRCH)
822 3
                                break;
823 32
                        vtc_log(h->vl, 4,
824 16
                            "Kill(%d)=%d: %s", sig, i, strerror(errno));
825 16
                }
826 21
                usleep(100000);
827 21
                if (++n == 20) {
828 11
                        switch (sig) {
829 8
                        case SIGINT:    sig = SIGTERM ; break;
830 3
                        case SIGTERM:   sig = SIGKILL ; break;
831 0
                        default:        break;
832
                        }
833 11
                        n = 0;
834 11
                }
835
        }
836
837 10
        PTOK(pthread_join(h->tp, &p));
838 10
        AZ(p);
839 10
        closefd(&h->fds[0]);
840 10
        if (!h->opt_daemon) {
841 7
                vtc_wait4(h->vl, h->ppid, h->expect_exit, h->expect_signal, 0);
842 7
                h->ppid = -1;
843 7
        }
844 10
        h->pid = -1;
845 10
}
846
847
#define HAPROXY_BE_FD_STR     "fd@${"
848
#define HAPROXY_BE_FD_STRLEN  strlen(HAPROXY_BE_FD_STR)
849
850
static int
851 10
haproxy_build_backends(struct haproxy *h, const char *vsb_data)
852
{
853
        char *s, *p, *q;
854
855 10
        s = strdup(vsb_data);
856 10
        if (!s)
857 0
                return (-1);
858
859 10
        p = s;
860 27
        while (1) {
861
                int sock;
862
                char buf[128], addr[128], port[128];
863
                const char *err;
864 27
                char vsabuf[vsa_suckaddr_len];
865
                const struct suckaddr *sua;
866
867 27
                p = strstr(p, HAPROXY_BE_FD_STR);
868 27
                if (!p)
869 10
                        break;
870
871 17
                q = p += HAPROXY_BE_FD_STRLEN;
872 70
                while (*q && *q != '}')
873 53
                        q++;
874 17
                if (*q != '}')
875 0
                        break;
876
877 17
                *q++ = '\0';
878 17
                sock = VTCP_listen_on("127.0.0.1:0", NULL, 100, &err);
879 17
                if (err != NULL)
880 0
                        vtc_fatal(h->vl,
881 0
                            "Create listen socket failed: %s", err);
882 17
                assert(sock > 0);
883 17
                sua = VSA_getsockname(sock, vsabuf, sizeof vsabuf);
884 17
                AN(sua);
885
886 17
                VTCP_name(sua, addr, sizeof addr, port, sizeof port);
887 17
                bprintf(buf, "%s_%s", h->name, p);
888 17
                if (VSA_Get_Proto(sua) == AF_INET)
889 17
                        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 17
                macro_def(h->vl, buf, "addr", "%s", addr);
893 17
                macro_def(h->vl, buf, "port", "%s", port);
894
895 17
                bprintf(buf, "%d", sock);
896 17
                vtc_log(h->vl, 4, "setenv(%s, %s)", p, buf);
897 17
                haproxy_add_envar(h, p, buf);
898 17
                p = q;
899 27
        }
900 10
        free(s);
901 10
        return (0);
902 10
}
903
904
static void
905 2
haproxy_check_conf(struct haproxy *h, const char *expect)
906
{
907
908 2
        h->msgs = VSB_new_auto();
909 2
        AN(h->msgs);
910 2
        h->opt_check_mode = 1;
911 2
        haproxy_start(h);
912 2
        haproxy_wait(h);
913 2
        AZ(VSB_finish(h->msgs));
914 2
        if (strstr(VSB_data(h->msgs), expect) == NULL)
915 0
                vtc_fatal(h->vl, "Did not find expected string '%s'", expect);
916 2
        vtc_log(h->vl, 2, "Found expected '%s'", expect);
917 2
        VSB_destroy(&h->msgs);
918 2
}
919
920
/**********************************************************************
921
 * Write a configuration for <h> HAProxy instance.
922
 */
923
924
static void
925 10
haproxy_store_conf(struct haproxy *h, const char *cfg, int auto_be)
926
{
927
        struct vsb *vsb, *vsb2;
928
929 10
        vsb = VSB_new_auto();
930 10
        AN(vsb);
931
932 10
        vsb2 = VSB_new_auto();
933 10
        AN(vsb2);
934
935 20
        VSB_printf(vsb, "    global\n\tstats socket \"%s\" "
936 10
                   "level admin mode 600\n", h->cli_fn);
937 10
        VSB_cat(vsb, "    stats socket \"fd@${cli}\" level admin\n");
938 10
        AZ(VSB_cat(vsb, cfg));
939
940 10
        if (auto_be)
941 1
                cmd_server_gen_haproxy_conf(vsb);
942
943 10
        AZ(VSB_finish(vsb));
944
945 10
        AZ(haproxy_build_backends(h, VSB_data(vsb)));
946
947 10
        h->cfg_vsb = macro_expand(h->vl, VSB_data(vsb));
948 10
        AN(h->cfg_vsb);
949
950 10
        VSB_destroy(&vsb2);
951 10
        VSB_destroy(&vsb);
952 10
}
953
954
static void
955 10
haproxy_write_conf(struct haproxy *h)
956
{
957
        struct vsb *vsb;
958
959 10
        vsb = macro_expand(h->vl, VSB_data(h->cfg_vsb));
960 10
        AN(vsb);
961 10
        assert(VSB_len(vsb) >= 0);
962
963 10
        vtc_dump(h->vl, 4, "conf", VSB_data(vsb), VSB_len(vsb));
964 30
        if (VFIL_writefile(h->workdir, h->cfg_fn,
965 20
            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 10
        VSB_destroy(&vsb);
971 10
}
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 1013
cmd_haproxy(CMD_ARGS)
1041
{
1042
        struct haproxy *h, *h2;
1043
1044 1013
        (void)priv;
1045
1046 1013
        if (av == NULL) {
1047
                /* Reset and free */
1048 1012
                VTAILQ_FOREACH_SAFE(h, &haproxies, list, h2) {
1049 20
                        vtc_log(h->vl, 2,
1050
                            "Reset and free %s haproxy %ld",
1051 10
                            h->name, (long)h->pid);
1052 10
                        if (h->pid >= 0)
1053 7
                                haproxy_wait(h);
1054 10
                        VTAILQ_REMOVE(&haproxies, h, list);
1055 10
                        haproxy_delete(h);
1056 10
                }
1057 1002
                return;
1058
        }
1059
1060 11
        AZ(strcmp(av[0], "haproxy"));
1061 11
        av++;
1062
1063 11
        VTC_CHECK_NAME(vl, av[0], "haproxy", 'h');
1064 13
        VTAILQ_FOREACH(h, &haproxies, list)
1065 3
                if (!strcmp(h->name, av[0]))
1066 1
                        break;
1067 10
        if (h == NULL)
1068 10
                h = haproxy_new(av[0]);
1069 11
        av++;
1070
1071 34
        for (; *av != NULL; av++) {
1072 23
                if (vtc_error)
1073 0
                        break;
1074
1075 23
                if (!strcmp(*av, "-conf-OK")) {
1076 1
                        AN(av[1]);
1077 1
                        haproxy_store_conf(h, av[1], 0);
1078 1
                        h->expect_exit = 0;
1079 1
                        haproxy_check_conf(h, "");
1080 1
                        av++;
1081 1
                        continue;
1082
                }
1083 22
                if (!strcmp(*av, "-conf-BAD")) {
1084 1
                        AN(av[1]);
1085 1
                        AN(av[2]);
1086 1
                        haproxy_store_conf(h, av[2], 0);
1087 1
                        h->expect_exit = 1;
1088 1
                        haproxy_check_conf(h, av[1]);
1089 1
                        av += 2;
1090 1
                        continue;
1091
                }
1092
1093 21
                if (!strcmp(*av, HAPROXY_OPT_DAEMON)) {
1094 3
                        h->opt_daemon = 1;
1095 3
                        continue;
1096
                }
1097 18
                if (!strcmp(*av, HAPROXY_OPT_WORKER)) {
1098 0
                        h->opt_worker = 1;
1099 0
                        continue;
1100
                }
1101 18
                if (!strcmp(*av, HAPROXY_OPT_MCLI)) {
1102 0
                        h->opt_mcli = 1;
1103 0
                        continue;
1104
                }
1105 18
                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 18
                if (!strcmp(*av, "-cli")) {
1115 1
                        REPLACE(h->cli->spec, av[1]);
1116 1
                        if (h->tp)
1117 1
                                haproxy_cli_run(h->cli);
1118 1
                        av++;
1119 1
                        continue;
1120
                }
1121
1122 17
                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 17
                if (!strcmp(*av, "-conf")) {
1131 7
                        AN(av[1]);
1132 7
                        haproxy_store_conf(h, av[1], 0);
1133 7
                        av++;
1134 7
                        continue;
1135
                }
1136 10
                if (!strcmp(*av, "-conf+backend")) {
1137 1
                        AN(av[1]);
1138 1
                        haproxy_store_conf(h, av[1], 1);
1139 1
                        av++;
1140 1
                        continue;
1141
                }
1142
1143 9
                if (!strcmp(*av, "-expectexit")) {
1144 0
                        h->expect_exit = strtoul(av[1], NULL, 0);
1145 0
                        av++;
1146 0
                        continue;
1147
                }
1148 9
                if (!strcmp(*av, "-start")) {
1149 8
                        haproxy_start(h);
1150 8
                        continue;
1151
                }
1152 1
                if (!strcmp(*av, "-wait")) {
1153 1
                        haproxy_wait(h);
1154 1
                        continue;
1155
                }
1156 0
                vtc_fatal(h->vl, "Unknown haproxy argument: %s", *av);
1157
        }
1158 1013
}