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