[master] 4abcaa4 Add support for testing HA-proxy

Poul-Henning Kamp phk at FreeBSD.org
Tue Mar 27 20:03:11 UTC 2018


commit 4abcaa49f109184d3de501c58f1abf3a7db1944e
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Tue Mar 27 19:56:11 2018 +0000

    Add support for testing HA-proxy
    
    Also a trivial coverage tes which runs if haproxy is in $PATH.
    
    Submitted by:   Frederic Lecaille <flecaille at haproxy.com>

diff --git a/bin/varnishtest/Makefile.am b/bin/varnishtest/Makefile.am
index efbac2a..c2c81d7 100644
--- a/bin/varnishtest/Makefile.am
+++ b/bin/varnishtest/Makefile.am
@@ -33,6 +33,7 @@ varnishtest_SOURCES = \
 		vtc.c \
 		vtc_barrier.c \
 		vtc_client.c \
+		vtc_haproxy.c \
 		vtc_h2_dectbl.h \
 		vtc_h2_enctbl.h \
 		vtc_h2_hpack.c \
diff --git a/bin/varnishtest/cmds.h b/bin/varnishtest/cmds.h
index 58c2e7b..290237d 100644
--- a/bin/varnishtest/cmds.h
+++ b/bin/varnishtest/cmds.h
@@ -34,7 +34,7 @@ CMD(client)
 CMD(delay)
 CMD(err_shell)
 CMD(feature)
-//CMD(haproxy)
+CMD(haproxy)
 CMD(logexpect)
 CMD(process)
 CMD(server)
diff --git a/bin/varnishtest/programs.h b/bin/varnishtest/programs.h
index 4849005..f8875ce 100644
--- a/bin/varnishtest/programs.h
+++ b/bin/varnishtest/programs.h
@@ -27,6 +27,7 @@
  *
  */
 
+VTC_PROG(haproxy)
 VTC_PROG(varnishadm)
 VTC_PROG(varnishd)
 VTC_PROG(varnishhist)
diff --git a/bin/varnishtest/tests/README b/bin/varnishtest/tests/README
index caadbd9..35d598d 100644
--- a/bin/varnishtest/tests/README
+++ b/bin/varnishtest/tests/README
@@ -21,6 +21,7 @@ Naming scheme
 	id ~ ^e		--> ESI tests
 	id ~ ^f		--> Security related tests
 	id ~ ^g		--> GZIP tests
+	id ~ ^h		--> HAproxy tests
 	id ~ ^j		--> JAIL tests
 	id ~ ^l		--> VSL tests
 	id ~ ^m		--> VMOD tests excluding director
diff --git a/bin/varnishtest/tests/h00001.vtc b/bin/varnishtest/tests/h00001.vtc
new file mode 100644
index 0000000..3c599a5
--- /dev/null
+++ b/bin/varnishtest/tests/h00001.vtc
@@ -0,0 +1,32 @@
+varnishtest "Basic HAproxy test"
+
+feature ignore_unknown_macro
+
+feature cmd {haproxy --version 2>&1 | grep -q 'HA-Proxy version'}
+
+server s1 {
+    rxreq
+    txresp -body "s1 >>> Hello world!"
+} -start
+
+haproxy h1 -arg "-d" -conf {
+    defaults
+	mode   http
+	timeout connect         5s
+	timeout server          30s
+	timeout client          30s
+
+    backend be1
+	server srv1 ${s1_addr}:${s1_port}
+
+    frontend http1
+	use_backend be1
+	bind "fd@${fe1}"
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+    txreq -url "/"
+    rxresp
+    expect resp.status == 200
+    expect resp.body == "s1 >>> Hello world!"
+} -run
diff --git a/bin/varnishtest/vtc_haproxy.c b/bin/varnishtest/vtc_haproxy.c
new file mode 100644
index 0000000..17c62fb
--- /dev/null
+++ b/bin/varnishtest/vtc_haproxy.c
@@ -0,0 +1,616 @@
+/*-
+ * Copyright (c) 2008-2018 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Frédéric Lécaille <flecaille at haproxy.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vtc.h"
+
+#include "vav.h"
+#include "vfil.h"
+#include "vpf.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+#define HAPROXY_PROGRAM_ENV_VAR	"HAPROXY_PROGRAM"
+#define HAPROXY_OPT_WORKER	"-W"
+#define HAPROXY_OPT_DAEMON	"-D"
+#define HAPROXY_OPT_CHECK_MODE	"-c"
+#define HAPROXY_SIGNAL		SIGINT
+#define HAPROXY_EXPECT_EXIT	(128 + HAPROXY_SIGNAL)
+
+struct haproxy {
+	unsigned		magic;
+#define HAPROXY_MAGIC		0x8a45cf75
+	char			*name;
+	struct vtclog		*vl;
+	VTAILQ_ENTRY(haproxy)	list;
+
+	char			*filename;
+	struct vsb		*args;
+	int			opt_worker;
+	int			opt_daemon;
+	int			opt_check_mode;
+	char			*pid_fn;
+	pid_t			pid;
+	pid_t			ppid;
+	int			fds[4];
+	char			*cfg_fn;
+
+	pthread_t		tp;
+	int			expect_exit;
+	int			expect_signal;
+	int			kill_status;
+	int			kill_errno;
+
+	const char		*cli_fn;
+
+	char			*workdir;
+};
+
+static void haproxy_cleanup(struct haproxy *h, int is_haproxy_listener);
+
+static VTAILQ_HEAD(, haproxy)	haproxies =
+    VTAILQ_HEAD_INITIALIZER(haproxies);
+
+/**********************************************************************
+ *
+ */
+
+static void
+wait_stopped(struct haproxy *h)
+{
+	vtc_log(h->vl, 3, "wait-stopped");
+	while (1) {
+		if (h->kill_status >= 0 || h->kill_errno == ESRCH)
+			break;
+
+		usleep(1000);
+		h->kill_status = kill(h->pid, 0);
+		h->kill_errno = errno;
+	}
+}
+
+static void
+wait_running(struct haproxy *h)
+{
+	char buf_err[1024] = {0};
+	int usleep_time = 1000;
+	double t0;
+	pid_t pid;
+
+	if (h->opt_check_mode)
+		return;
+
+	if (h->pid_fn == NULL) {
+		/*
+		 * If we use no pid-file, check that the process
+		 * we started is still running.
+		 */
+		if (kill(h->pid, 0) < 0)
+			vtc_fatal(h->vl, "Process %ld not there: %s",
+			    (long)h->pid, strerror(errno));
+		return;
+	}
+
+	vtc_log(h->vl, 3, "wait-running");
+	for (t0 = VTIM_mono(); VTIM_mono() - t0 < 3;) {
+		if (vtc_error)
+			return;
+
+		if (VPF_read(h->pid_fn, &pid) != 0) {
+			bprintf(buf_err,
+			    "Could not read PID file '%s'", h->pid_fn);
+			usleep(usleep_time);
+			continue;
+		}
+
+		if (!h->opt_daemon && pid != h->pid) {
+			bprintf(buf_err,
+			    "PID file has different PID (%ld != %lld)",
+			    (long)pid, (long long)h->pid);
+			usleep(usleep_time);
+			continue;
+		}
+
+		if (kill(pid, 0) < 0) {
+			bprintf(buf_err,
+			    "Could not find PID %ld process", (long)pid);
+			usleep(usleep_time);
+			continue;
+		}
+
+		h->pid = pid;
+
+		vtc_log(h->vl, 2, "haproxy PID %ld successfully started",
+		    (long)pid);
+		return;
+	}
+	vtc_fatal(h->vl, "haproxy %s PID file check failed:\n\t%s\n",
+		  h->name, buf_err);
+}
+
+/**********************************************************************
+ * Allocate and initialize a haproxy
+ */
+
+static struct haproxy *
+haproxy_new(const char *name)
+{
+	struct haproxy *h;
+	struct vsb *vsb;
+	char buf[PATH_MAX];
+
+	ALLOC_OBJ(h, HAPROXY_MAGIC);
+	AN(h);
+	REPLACE(h->name, name);
+
+	h->args = VSB_new_auto();
+
+	h->vl = vtc_logopen(name);
+	AN(h->vl);
+
+	h->filename = getenv(HAPROXY_PROGRAM_ENV_VAR);
+	if (h->filename == NULL)
+		REPLACE(h->filename, "haproxy");
+
+	bprintf(buf, "${tmpdir}/%s", name);
+	vsb = macro_expand(h->vl, buf);
+	AN(vsb);
+	h->workdir = strdup(VSB_data(vsb));
+	AN(h->workdir);
+	VSB_destroy(&vsb);
+
+	bprintf(buf, "%s/stats.sock", h->workdir);
+	h->cli_fn = strdup(buf);
+	AN(h->cli_fn);
+
+	bprintf(buf, "%s/cfg", h->workdir);
+	h->cfg_fn = strdup(buf);
+	AN(h->cfg_fn);
+
+	VSB_printf(h->args, "-f %s ", h->cfg_fn);
+
+	bprintf(buf, "rm -rf %s ; mkdir -p %s", h->workdir, h->workdir);
+	AZ(system(buf));
+
+	VTAILQ_INSERT_TAIL(&haproxies, h, list);
+
+	return (h);
+}
+
+/**********************************************************************
+ * Delete a haproxy instance
+ */
+
+static void
+haproxy_delete(struct haproxy *h)
+{
+	char buf[PATH_MAX];
+
+	CHECK_OBJ_NOTNULL(h, HAPROXY_MAGIC);
+	vtc_logclose(h->vl);
+
+	if (!leave_temp) {
+		bprintf(buf, "rm -rf %s", h->workdir);
+		AZ(system(buf));
+	}
+
+	free(h->name);
+	free(h->workdir);
+	VSB_destroy(&h->args);
+
+	/* XXX: MEMLEAK (?) */
+	FREE_OBJ(h);
+}
+
+/**********************************************************************
+ * HAProxy listener
+ */
+
+static void *
+haproxy_thread(void *priv)
+{
+	struct haproxy *h;
+
+	CAST_OBJ_NOTNULL(h, priv, HAPROXY_MAGIC);
+	return (vtc_record(h->vl, h->fds[0]));
+}
+
+/**********************************************************************
+ * Start a HAProxy instance.
+ */
+
+static void
+haproxy_start(struct haproxy *h)
+{
+	char **argv;
+	int i;
+	char buf[PATH_MAX];
+	struct vsb *vsb;
+
+	vtc_log(h->vl, 2, "%s", __func__);
+
+	AZ(VSB_finish(h->args));
+	argv = VAV_Parse(VSB_data(h->args), NULL, 0);
+	AN(argv);
+	if (argv[0] != NULL)
+		vtc_fatal(h->vl, "Could not parse argv-string: %s", argv[0]);
+	for (i = 1; argv[i] != NULL; i++) {
+		/* Check if HAPROXY_OPT_WORKER option was provided. */
+		if (!strcmp(argv[i], HAPROXY_OPT_WORKER)) {
+			h->opt_worker = 1;
+		} else if (!strcmp(argv[i], HAPROXY_OPT_DAEMON)) {
+			h->opt_daemon = 1;
+		} else if (!strcmp(argv[i], HAPROXY_OPT_CHECK_MODE)) {
+			h->opt_check_mode = 1;
+		}
+	}
+	VAV_Free(argv);
+	vtc_log(h->vl, 4, "opt_worker %d opt_daemon %d opt_check_mode %d",
+	    h->opt_worker, h->opt_daemon, h->opt_check_mode);
+
+	vsb = VSB_new_auto();
+	AN(vsb);
+	VSB_printf(vsb, "%s %s", h->filename, VSB_data(h->args));
+
+	if (h->opt_worker || h->opt_daemon) {
+		bprintf(buf, "%s/pid", h->workdir);
+		h->pid_fn = strdup(buf);
+		AN(h->pid_fn);
+		VSB_printf(vsb, " -p %s", h->pid_fn);
+	}
+
+	AZ(VSB_finish(vsb));
+	vtc_dump(h->vl, 4, "argv", VSB_data(vsb), -1);
+
+	if (h->opt_worker && !h->opt_daemon) {
+		/*
+		 * HAProxy master process must exit with status 128 + <signum>
+		 * if signaled by <signum> signal.
+		 */
+		h->expect_exit = HAPROXY_EXPECT_EXIT;
+	} else {
+		h->expect_exit = 0;
+	}
+
+	AZ(pipe(&h->fds[0]));
+	AZ(pipe(&h->fds[2]));
+	h->pid = h->ppid = fork();
+	assert(h->pid >= 0);
+	if (h->pid == 0) {
+		AZ(chdir(h->name));
+		AZ(dup2(h->fds[0], 0));
+		assert(dup2(h->fds[3], 1) == 1);
+		assert(dup2(1, 2) == 2);
+		closefd(&h->fds[0]);
+		closefd(&h->fds[1]);
+		closefd(&h->fds[2]);
+		closefd(&h->fds[3]);
+		AZ(execl("/bin/sh", "/bin/sh", "-c", VSB_data(vsb), NULL));
+		exit(1);
+	}
+	VSB_destroy(&vsb);
+
+	vtc_log(h->vl, 3, "PID: %ld", (long)h->pid);
+	macro_def(h->vl, h->name, "pid", "%ld", (long)h->pid);
+	macro_def(h->vl, h->name, "name", "%s", h->workdir);
+
+	closefd(&h->fds[0]);
+	closefd(&h->fds[3]);
+	h->fds[0] = h->fds[2];
+	h->fds[2] = h->fds[3] = -1;
+
+	AZ(pthread_create(&h->tp, NULL, haproxy_thread, h));
+
+	wait_running(h);
+}
+
+/**********************************************************************
+ * Stop a HAProxy
+ */
+
+static void
+haproxy_stop(struct haproxy *h)
+{
+	if (h->opt_check_mode)
+		return;
+
+	if (h->pid < 0)
+		haproxy_start(h);
+
+	vtc_log(h->vl, 2, "Stop");
+	h->kill_status = kill(h->pid, HAPROXY_SIGNAL);
+	h->kill_errno = errno;
+	h->expect_signal = -HAPROXY_SIGNAL;
+
+	wait_stopped(h);
+}
+
+/**********************************************************************
+ * Cleanup
+ */
+
+static void
+haproxy_cleanup(struct haproxy *h, int is_haproxy_listener)
+{
+	void *p;
+
+	vtc_log(h->vl, 2, "%s (%d)", __func__, is_haproxy_listener);
+	if (!is_haproxy_listener) {
+		/* Close the STDIN connection. */
+		closefd(&h->fds[1]);
+
+		/* Wait until STDOUT+STDERR closes */
+		AZ(pthread_join(h->tp, &p));
+		closefd(&h->fds[0]);
+		if (h->opt_daemon)
+			return;
+	}
+
+	if (h->ppid == -1)
+		return;
+
+	vtc_wait4(h->vl, h->ppid, h->expect_exit, h->expect_signal, 0);
+	h->ppid = -1;
+}
+
+/**********************************************************************
+ * Wait for a HAProxy instance.
+ */
+
+static void
+haproxy_wait(struct haproxy *h)
+{
+	if (h->pid < 0)
+		return;
+
+	vtc_log(h->vl, 2, "Wait");
+
+	haproxy_stop(h);
+
+	haproxy_cleanup(h, 0);
+}
+
+#define HAPROXY_BE_FD_STR     "fd@${"
+#define HAPROXY_BE_FD_STRLEN  strlen(HAPROXY_BE_FD_STR)
+
+static int
+haproxy_build_backends(const struct haproxy *h, const char *vsb_data)
+{
+	char *s, *p, *q;
+
+	s = strdup(vsb_data);
+	if (!s)
+		return -1;
+
+	p = s;
+	while (1) {
+		int sock;
+		char buf[128], addr[128], port[128];
+		const char *err;
+
+		p = strstr(p, HAPROXY_BE_FD_STR);
+		if (!p)
+			break;
+
+		q = p += HAPROXY_BE_FD_STRLEN;
+		while (*q && *q != '}')
+			q++;
+		if (*q != '}')
+			break;
+
+		*q++ = '\0';
+		sock = VTCP_listen_on(":0", NULL, 1, &err);
+		if (err != NULL)
+			vtc_fatal(h->vl,
+			    "Create listen socket failed: %s", err);
+
+		VTCP_myname(sock, addr, sizeof addr, port, sizeof port);
+		bprintf(buf, "%s_%s", h->name, p);
+		macro_def(h->vl, buf, "sock", "%s %s", addr, port);
+		macro_def(h->vl, buf, "addr", "%s", addr);
+		macro_def(h->vl, buf, "port", "%s", port);
+
+		bprintf(buf, "%d", sock);
+		vtc_log(h->vl, 4, "setenv(%s, %s)", p, buf);
+		if (setenv(p, buf, 0) == -1)
+			vtc_fatal(h->vl, "setenv() failed: %s (%d)",
+				  strerror(errno), errno);
+		p = q;
+	}
+	free(s);
+	return (0);
+}
+
+/**********************************************************************
+ * Write a configuration for <h> HAProxy instance.
+ */
+
+static void
+haproxy_write_conf(const struct haproxy *h, const char *cfg)
+{
+	struct vsb *vsb, *vsb2;
+
+	vsb = VSB_new_auto();
+	AN(vsb);
+
+	vsb2 = VSB_new_auto();
+	AN(vsb2);
+
+	VSB_printf(vsb, "    global\n\tstats socket %s "
+		   "level admin mode 600\n", h->cli_fn);
+	AZ(VSB_cat(vsb, cfg));
+	AZ(VSB_finish(vsb));
+
+	AZ(haproxy_build_backends(h, VSB_data(vsb)));
+
+	if (VFIL_writefile(h->workdir, h->cfg_fn,
+	    VSB_data(vsb), VSB_len(vsb)) != 0)
+		vtc_fatal(h->vl,
+		    "failed to write haproxy configuration file: %s (%d)",
+		    strerror(errno), errno);
+
+	vtc_dump(h->vl, 4, "conf", VSB_data(vsb), VSB_len(vsb));
+
+	VSB_destroy(&vsb2);
+	VSB_destroy(&vsb);
+}
+
+/* SECTION: haproxy haproxy
+ *
+ * Define and interact with haproxy instances.
+ *
+ * To define a haproxy server, you'll use this syntax::
+ *
+ *	haproxy hNAME [-arg STRING] [-conf STRING]
+ *
+ * The first ``haproxy hNAME`` invocation will start the haproxy master
+ * process in the background, waiting for the ``-start`` switch to actually
+ * start the child.
+ *
+ * Arguments:
+ *
+ * hNAME
+ *	   Identify the HAProxy server with a string, it must starts with 'h'.
+ *
+ * \-arg STRING
+ *         Pass an argument to haproxy, for example "-h simple_list".
+ *
+ * \-conf STRING
+ *         Specify the configuration to be loaded by this HAProxy instance.
+ *
+ * You can decide to start the HAProxy instance and/or wait for several events::
+ *
+ *         haproxy hNAME [-start] [-wait-running] [-wait-stopped]
+ *
+ * \-start
+ *         Start this HAProxy instance.
+ *
+ * \-stop
+ *         Stop this HAProxy instance.
+ *
+ * \-wait-running
+ *         Wait for that instance to terminate.
+ *
+ * \-wait-stopped
+ *         Wait for that instance to terminate.
+ *
+ * \-cleanup
+ *         Once HAProxy is stopped, clean everything after it. This is only used
+ *         in very few tests and you should never need it.
+ *
+ * \-expectexit NUMBER
+ *	   Expect haproxy to exit(3) with this value
+ *
+ */
+
+void
+cmd_haproxy(CMD_ARGS)
+{
+	struct haproxy *h, *h2;
+
+	(void)priv;
+	(void)cmd;
+
+	if (av == NULL) {
+		/* Reset and free */
+		VTAILQ_FOREACH_SAFE(h, &haproxies, list, h2) {
+			vtc_log(h->vl, 2,
+			    "Reset and free %s haproxy %d", h->name, h->pid);
+			if (h->pid >= 0)
+				haproxy_wait(h);
+			VTAILQ_REMOVE(&haproxies, h, list);
+			haproxy_delete(h);
+		}
+		return;
+	}
+
+	AZ(strcmp(av[0], "haproxy"));
+	av++;
+
+	VTC_CHECK_NAME(vl, av[0], "haproxy", 'h');
+	VTAILQ_FOREACH(h, &haproxies, list)
+		if (!strcmp(h->name, av[0]))
+			break;
+	if (h == NULL)
+		h = haproxy_new(av[0]);
+	av++;
+
+	for (; *av != NULL; av++) {
+		if (vtc_error)
+			break;
+		if (!strcmp(*av, "-arg")) {
+			AN(av[1]);
+			AZ(h->pid);
+			VSB_cat(h->args, " ");
+			VSB_cat(h->args, av[1]);
+			av++;
+			continue;
+		}
+		if (!strcmp(*av, "-cleanup")) {
+			AZ(av[1]);
+			haproxy_cleanup(h, 0);
+			continue;
+		}
+		if (!strcmp(*av, "-conf")) {
+			AN(av[1]);
+			haproxy_write_conf(h, av[1]);
+			av++;
+			continue;
+		}
+		if (!strcmp(*av, "-expectexit")) {
+			h->expect_exit = strtoul(av[1], NULL, 0);
+			av++;
+			continue;
+		}
+		if (!strcmp(*av, "-start")) {
+			haproxy_start(h);
+			continue;
+		}
+		if (!strcmp(*av, "-stop")) {
+			haproxy_stop(h);
+			continue;
+		}
+		if (!strcmp(*av, "-wait-stopped")) {
+			wait_stopped(h);
+			continue;
+		}
+		if (!strcmp(*av, "-wait-running")) {
+			wait_running(h);
+			continue;
+		}
+		vtc_fatal(h->vl, "Unknown haproxy argument: %s", *av);
+	}
+}
diff --git a/bin/varnishtest/vtc_subr.c b/bin/varnishtest/vtc_subr.c
index 39cafbe..e232f95 100644
--- a/bin/varnishtest/vtc_subr.c
+++ b/bin/varnishtest/vtc_subr.c
@@ -160,14 +160,15 @@ vtc_wait4(struct vtclog *vl, long pid,
 	if (r < 0)
 		vtc_fatal(vl, "wait4 failed on pid %ld: %s",
 		    pid, strerror(errno));
-	vtc_log(vl, 2, "WAIT4 pid=%ld r=%d status=0x%04x (user %.6f sys %.6f)",
-	    pid, r, status,
+	assert(r == pid);
+	vtc_log(vl, 2, "WAIT4 pid=%ld status=0x%04x (user %.6f sys %.6f)",
+	    pid, status,
 	    ru.ru_utime.tv_sec + 1e-6 * ru.ru_utime.tv_usec,
 	    ru.ru_stime.tv_sec + 1e-6 * ru.ru_stime.tv_usec
 	);
 
 	if (WIFEXITED(status) && expect_signal <= 0 &&
-	    (WEXITSTATUS(status) == expect_status))
+	    WEXITSTATUS(status) == expect_status)
 		return;
 
 	if (expect_signal < 0)


More information about the varnish-commit mailing list