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