[master] 94443fe Add VMOD unix.

Geoff Simmons geoff at uplex.de
Mon Mar 12 10:38:08 UTC 2018


commit 94443fed07dc76d92afa868925b68fa5c1eb3f73
Author: Geoff Simmons <geoff at uplex.de>
Date:   Mon Mar 12 11:32:35 2018 +0100

    Add VMOD unix.

diff --git a/bin/varnishtest/tests/m00047.vtc b/bin/varnishtest/tests/m00047.vtc
new file mode 100644
index 0000000..bfc8f7c
--- /dev/null
+++ b/bin/varnishtest/tests/m00047.vtc
@@ -0,0 +1,130 @@
+varnishtest "VMOD unix"
+
+# This test requires some manual verification, by checking the log,
+# because support for peer credentials via UDS varies by platform, see
+# below.
+
+server s1 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl+backend {
+	import unix;
+
+	sub vcl_backend_response {
+		set beresp.http.b-uid = unix.uid();
+		set beresp.http.b-gid = unix.gid();
+		set beresp.http.b-user = unix.user();
+		set beresp.http.b-group = unix.group();
+	}
+
+	sub vcl_deliver {
+		set resp.http.c-uid = unix.uid();
+		set resp.http.c-gid = unix.gid();
+		set resp.http.c-user = unix.user();
+		set resp.http.c-group = unix.group();
+	}
+} -start
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.http.b-uid == -1
+	expect resp.http.b-gid == -1
+	expect resp.http.b-user == ""
+	expect resp.http.b-group == ""
+	expect resp.http.c-uid == -1
+	expect resp.http.c-gid == -1
+	expect resp.http.c-user == ""
+	expect resp.http.c-group == ""
+} -run
+
+logexpect l1 -v v1 -d 1 -b {
+	expect * 1002 Begin     bereq
+	expect * =    VCL_Error	{^vmod unix error: not listening on a Unix domain socket$}
+	expect * =    VCL_Error	{^vmod unix error: not listening on a Unix domain socket$}
+	expect * =    VCL_Error	{^vmod unix error: not listening on a Unix domain socket$}
+	expect * =    VCL_Error	{^vmod unix error: not listening on a Unix domain socket$}
+} -run
+
+logexpect l1 -v v1 -d 1 -c {
+        expect * 1001 Begin     req
+        expect * =    VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+        expect * =    VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+        expect * =    VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+        expect * =    VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+} -run
+
+
+varnish v1 -errvcl {vmod unix failure: may not be called in vcl_init or vcl_fini} {
+	import unix;
+	import std;
+	backend b { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		std.log(unix.uid());
+	}
+}
+
+varnish v1 -errvcl {vmod unix failure: may not be called in vcl_init or vcl_fini} {
+	import unix;
+	import std;
+	backend b { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		std.log(unix.gid());
+	}
+}
+
+varnish v1 -errvcl {vmod unix failure: may not be called in vcl_init or vcl_fini} {
+	import unix;
+	import std;
+	backend b { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		std.log(unix.user());
+	}
+}
+
+varnish v1 -errvcl {vmod unix failure: may not be called in vcl_init or vcl_fini} {
+	import unix;
+	import std;
+	backend b { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		std.log(unix.group());
+	}
+}
+
+varnish v1 -stop
+server s1 -wait
+server s1 -start
+
+varnish v2 -arg "-a ${tmpdir}/v2.sock" -vcl+backend {
+	import unix;
+
+	sub vcl_backend_response {
+		set beresp.http.b-uid = unix.uid();
+		set beresp.http.b-gid = unix.gid();
+		set beresp.http.b-user = unix.user();
+		set beresp.http.b-group = unix.group();
+	}
+
+	sub vcl_deliver {
+		set resp.http.c-uid = unix.uid();
+		set resp.http.c-gid = unix.gid();
+		set resp.http.c-user = unix.user();
+		set resp.http.c-group = unix.group();
+	}
+} -start
+
+# Check the log output for the response header values to see how this
+# worked on your platform.
+
+client c2 -connect "${v2_addr}" {
+	txreq
+	rxresp
+	expect resp.status ~ "^(200|503)$"
+} -run
diff --git a/bin/varnishtest/vmods.h b/bin/varnishtest/vmods.h
index e732468..fb8f68f 100644
--- a/bin/varnishtest/vmods.h
+++ b/bin/varnishtest/vmods.h
@@ -33,3 +33,4 @@ VTC_VMOD(directors)
 VTC_VMOD(purge)
 VTC_VMOD(vtc)
 VTC_VMOD(blob)
+VTC_VMOD(unix)
diff --git a/configure.ac b/configure.ac
index cf14412..c0dc0f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -216,6 +216,8 @@ AC_CHECK_FUNCS([setppriv])
 AC_CHECK_FUNCS([fallocate])
 AC_CHECK_FUNCS([closefrom])
 AC_CHECK_FUNCS([sigaltstack])
+AC_CHECK_FUNCS([getpeereid])
+AC_CHECK_FUNCS([getpeerucred])
 
 save_LIBS="${LIBS}"
 LIBS="${PTHREAD_LIBS}"
@@ -758,6 +760,7 @@ AC_CONFIG_FILES([
     lib/libvmod_purge/Makefile
     lib/libvmod_vtc/Makefile
     lib/libvmod_blob/Makefile
+    lib/libvmod_unix/Makefile
     man/Makefile
     varnishapi.pc
     varnishapi-uninstalled.pc
diff --git a/doc/sphinx/Makefile.am b/doc/sphinx/Makefile.am
index 490ee50..810e903 100644
--- a/doc/sphinx/Makefile.am
+++ b/doc/sphinx/Makefile.am
@@ -218,6 +218,10 @@ reference/vmod_blob.generated.rst: reference $(top_builddir)/lib/libvmod_blob/vm
 	cp $(top_builddir)/lib/libvmod_blob/vmod_blob.rst $@ || true
 BUILT_SOURCES += reference/vmod_blob.generated.rst
 
+reference/vmod_unix.generated.rst: reference $(top_builddir)/lib/libvmod_unix/vmod_unix.rst
+	cp $(top_builddir)/lib/libvmod_unix/vmod_unix.rst $@ || true
+BUILT_SOURCES += reference/vmod_unix.generated.rst
+
 EXTRA_DIST += $(BUILT_SOURCES)
 MAINTAINERCLEANFILES = $(EXTRA_DIST)
 
diff --git a/doc/sphinx/reference/index.rst b/doc/sphinx/reference/index.rst
index 39ccb81..9df60eb 100644
--- a/doc/sphinx/reference/index.rst
+++ b/doc/sphinx/reference/index.rst
@@ -25,6 +25,7 @@ The Varnish Reference Manual
 	vmod_vtc.generated.rst
 	vmod_purge.generated.rst
 	vmod_blob.generated.rst
+	vmod_unix.generated.rst
 	directors.rst
 	varnish-counters.rst
 	vsl.rst
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 4afd3d0..411bd80 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -10,4 +10,5 @@ SUBDIRS = \
 	libvmod_directors \
 	libvmod_purge \
 	libvmod_vtc \
-	libvmod_blob
+	libvmod_blob \
+	libvmod_unix
diff --git a/lib/libvmod_unix/Makefile.am b/lib/libvmod_unix/Makefile.am
new file mode 100644
index 0000000..8dafeba
--- /dev/null
+++ b/lib/libvmod_unix/Makefile.am
@@ -0,0 +1,8 @@
+#
+
+libvmod_unix_la_SOURCES = \
+	vmod_unix.c \
+	cred_compat.h
+
+# Use vmodtool.py generated automake boilerplate
+include $(srcdir)/automake_boilerplate.am
diff --git a/lib/libvmod_unix/automake_boilerplate.am b/lib/libvmod_unix/automake_boilerplate.am
new file mode 100644
index 0000000..b71674a
--- /dev/null
+++ b/lib/libvmod_unix/automake_boilerplate.am
@@ -0,0 +1,39 @@
+
+# Boilerplate generated by vmodtool.py - changes will be overwritten
+
+AM_LDFLAGS  = $(AM_LT_LDFLAGS)
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/bin/varnishd \
+	-I$(top_builddir)/include
+
+vmoddir = $(pkglibdir)/vmods
+vmodtool = $(top_srcdir)/lib/libvcc/vmodtool.py
+vmodtoolargs = --strict --boilerplate
+
+vmod_LTLIBRARIES = libvmod_unix.la
+
+libvmod_unix_la_CFLAGS = \
+	@SAN_CFLAGS@
+
+libvmod_unix_la_LDFLAGS = \
+	$(AM_LDFLAGS) \
+	$(VMOD_LDFLAGS) \
+	@SAN_LDFLAGS@
+
+nodist_libvmod_unix_la_SOURCES = vcc_if.c vcc_if.h
+
+$(libvmod_unix_la_OBJECTS): vcc_if.h
+
+vcc_if.h vmod_unix.rst vmod_unix.man.rst: vcc_if.c
+
+vcc_if.c: $(vmodtool) $(srcdir)/vmod.vcc
+	@PYTHON@ $(vmodtool) $(vmodtoolargs) $(srcdir)/vmod.vcc
+
+EXTRA_DIST = vmod.vcc automake_boilerplate.am
+
+CLEANFILES = $(builddir)/vcc_if.c $(builddir)/vcc_if.h \
+	$(builddir)/vmod_unix.rst \
+	$(builddir)/vmod_unix.man.rst
+
diff --git a/lib/libvmod_unix/cred_compat.h b/lib/libvmod_unix/cred_compat.h
new file mode 100644
index 0000000..5c2340b
--- /dev/null
+++ b/lib/libvmod_unix/cred_compat.h
@@ -0,0 +1,87 @@
+/*-
+ * Copyright 2018 UPLEX - Nils Goroll Systemoptimierung
+ * All rights reserved.
+ *
+ * Authors: Geoffrey Simmons <geoffrey.simmons at uplex.de>
+ *
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#if defined(HAVE_GETPEEREID)
+#include <unistd.h>
+#endif
+
+#if defined(HAVE_GETPEERUCRED)
+#include <ucred.h>
+#endif
+
+#define CREDS_FAIL -1
+#define NOT_SUPPORTED -2
+
+static int
+get_ids(int fd, uid_t *uid, gid_t *gid)
+{
+
+#if defined(SO_PEERCRED)
+
+	struct ucred ucred;
+	socklen_t l = sizeof(ucred);
+
+	errno = 0;
+	if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *) &ucred, &l) != 0)
+		return (CREDS_FAIL);
+	*uid = ucred.uid;
+	*gid = ucred.gid;
+	return (0);
+
+#elif defined(HAVE_GETPEEREID)
+
+	errno = 0;
+	if (getpeereid(fd, uid, gid) != 0)
+		return (CREDS_FAIL);
+	return (0);
+
+#elif defined(HAVE_GETPEERUCRED)
+
+	ucred_t *ucred;
+
+	errno = 0;
+	if (getpeerucred(fd, &ucred) != 0)
+		return (CREDS_FAIL);
+	*uid = ucred_geteuid(ucred);
+	*gid = ucred_getegid(ucred);
+	ucred_free(ucred);
+	return (0);
+
+#else
+	(void) fd;
+	(void) uid;
+	(void) gid;
+	return (NOT_SUPPORTED);
+#endif
+
+}
diff --git a/lib/libvmod_unix/vmod.vcc b/lib/libvmod_unix/vmod.vcc
new file mode 100644
index 0000000..1035df3
--- /dev/null
+++ b/lib/libvmod_unix/vmod.vcc
@@ -0,0 +1,110 @@
+#-
+# This document is licensed under the same conditions as Varnish itself.
+# See LICENSE for details.
+#
+# Authors: Geoffrey Simmons <geoffrey.simmons at uplex.de>
+#
+
+$Module unix 3 utilities for Unix domain sockets
+
+$ABI strict
+
+DESCRIPTION
+===========
+
+This VMOD provides information about the credentials of the peer
+process (user and group of the process owner) that is connected to
+Varnish via a Unix domain socket, if the platform supports it.
+
+Examples::
+
+  sub vcl_recv {
+	# Return "403 Forbidden" if the connected peer is
+	# not running as the user "trusteduser".
+	if (unix.user() != "trusteduser") {
+		return( synth(403) );
+	}
+
+	# Require the connected peer to run in the group
+        # "trustedgroup".
+	if (unix.group() != "trustedgroup") {
+		return( synth(403) );
+	}
+
+	# Require the connected peer to run under a specific numeric
+        # user id.
+	if (unix.uid() != 4711) {
+		return( synth(403) );
+	}
+
+	# Require the connected peer to run under a numeric group id.
+	if (unix.gid() != 815) {
+		return( synth(403) );
+	}
+  }
+
+Obtaining the peer credentials is possible on a platform that supports
+one of the following:
+
+* ``getpeereid(3)`` (such as FreeBSD and other BSD-derived systems)
+
+* ``getpeerucred(3)`` (SunOS and descendants)
+
+  * Reading peer credentials on such a system may require that the
+    Varnish child process runs with certain privileges (such as
+    ``PRIV_PROC_INFO``)
+
+* the socket option ``SO_PEERCRED`` for ``getsockopt(2)`` (Linux)
+
+On most platforms, the value returned is the effective user or group
+that was valid when the peer process initiated the connection.
+
+$Function STRING user()
+
+Return the user name of the peer process owner.
+
+$Function STRING group()
+
+Return the group name of the peer process owner.
+
+$Function INT uid()
+
+Return the numeric user id of the peer process owner.
+
+$Function INT gid()
+
+Return the numeric group id of the peer process owner.
+
+ERRORS
+======
+
+All functions in this VMOD are subject to the following constraints:
+
+* None of them may be called in ``vcl_init`` or ``vcl_fini``. If one
+  of them is called in ``vcl_init``, then the VCL program will fail to
+  load, with an error message from the VMOD.
+
+* If called on a platform that is not supported, then VCL failure is
+  invoked. An error message is written to the log (with the
+  ``VCL_Error`` tag), and for all VCL subroutines except for
+  ``vcl_synth``, control is directed immediately to ``vcl_synth``,
+  with the response status set to 503 and the reason string set to
+  "VCL failed".
+
+  If the failure occurs during ``vcl_synth``, then ``vcl_synth`` is
+  aborted, and the the response line "503 VCL failed" is sent.
+  
+* If the current listener is not a Unix domain socket, or if the
+  attempt to read credentials fails, then a ``VCL_Error`` message is
+  written to the log. The STRING functions (``vmod_user`` and
+  ``vmod_group``) return NULL, while the INT functions (``vmod_user``
+  and ``vmod_gid``) return -1.
+
+SEE ALSO
+========
+
+* :ref:`varnishd(1)`
+* :ref:`vcl(7)`
+* ``getpeereid(3)``
+* ``getpeerucred(3)``
+* ``getsockopt(2)``
diff --git a/lib/libvmod_unix/vmod_unix.c b/lib/libvmod_unix/vmod_unix.c
new file mode 100644
index 0000000..ee1f3e0
--- /dev/null
+++ b/lib/libvmod_unix/vmod_unix.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright 2018 UPLEX - Nils Goroll Systemoptimierung
+ * All rights reserved.
+ *
+ * Authors: Geoffrey Simmons <geoffrey.simmons at uplex.de>
+ *
+ * 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 "cred_compat.h"
+
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+
+#include "cache/cache.h"
+#include "vcl.h"
+#include "common/heritage.h"
+
+#include "vcc_if.h"
+
+#define FAIL(ctx, msg) \
+	VRT_fail((ctx), "vmod unix failure: " msg)
+
+#define ERR(ctx, msg) \
+	VSLb((ctx)->vsl, SLT_VCL_Error, "vmod unix error: " msg)
+
+#define VERR(ctx, fmt, ...) \
+	VSLb((ctx)->vsl, SLT_VCL_Error, "vmod unix error: " fmt, __VA_ARGS__)
+
+#define FAILNOINIT(ctx) \
+	FAIL((ctx), "may not be called in vcl_init or vcl_fini")
+
+#define ERRNOTUDS(ctx) \
+	ERR((ctx), "not listening on a Unix domain socket")
+
+#define FAIL_SUPPORT(ctx) \
+	FAIL((ctx), "not supported on this platform")
+
+#define ERRNOCREDS(ctx) \
+	VERR((ctx), "could not read peer credentials: %s", strerror(errno))
+
+#define ERRNOMEM(ctx) \
+	ERR((ctx), "out of space")
+
+static struct sess *
+get_sp(VRT_CTX)
+{
+	struct sess *sp;
+
+	if (VALID_OBJ(ctx->req, REQ_MAGIC))
+		sp = ctx->req->sp;
+	else {
+		CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC);
+		sp = ctx->bo->sp;
+	}
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->listen_sock, LISTEN_SOCK_MAGIC);
+	return (sp);
+}
+
+#define NUM_FUNC(func)					\
+VCL_INT							\
+vmod_##func(VRT_CTX)					\
+{							\
+	struct sess *sp;				\
+	uid_t uid;					\
+	gid_t gid;					\
+	int ret;					\
+							\
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);		\
+	if ((ctx->method & VCL_MET_TASK_H) != 0) {	\
+		FAILNOINIT(ctx);			\
+		return (-1);				\
+	}						\
+							\
+	sp = get_sp(ctx);				\
+	if (! sp->listen_sock->uds) {			\
+		ERRNOTUDS(ctx);				\
+		return (-1);				\
+	}						\
+							\
+	ret = get_ids(sp->fd, &uid, &gid);		\
+	if (ret == 0)					\
+		return (func);				\
+							\
+	if (ret == NOT_SUPPORTED)			\
+		FAIL_SUPPORT(ctx);			\
+	else if (ret == CREDS_FAIL)			\
+		ERRNOCREDS(ctx);			\
+	return (-1);					\
+}
+
+NUM_FUNC(uid)
+NUM_FUNC(gid)
+
+#define NAME_FUNC(func, type, get, id, fld)			\
+VCL_STRING							\
+vmod_##func(VRT_CTX)						\
+{								\
+	struct type *s;						\
+	id##_t i;						\
+	VCL_STRING name;					\
+								\
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);			\
+	i = (id##_t) vmod_##id(ctx);				\
+	if (i == -1)						\
+		return (NULL);					\
+								\
+	errno = 0;						\
+	s = get(i);						\
+	if (s == NULL) {					\
+		ERRNOCREDS(ctx);				\
+		return (NULL);					\
+	}							\
+	if ((name = WS_Copy(ctx->ws, s->fld, -1)) == NULL) {	\
+		ERRNOMEM(ctx);					\
+		return (NULL);					\
+	}							\
+	return (name);						\
+}
+
+NAME_FUNC(user, passwd, getpwuid, uid, pw_name)
+NAME_FUNC(group, group, getgrgid, gid, gr_name)
diff --git a/man/Makefile.am b/man/Makefile.am
index 2187ff9..e7c00df 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -19,7 +19,8 @@ dist_man_MANS = \
 	vmod_purge.3 \
 	vmod_std.3 \
 	vmod_vtc.3 \
-	vmod_blob.3
+	vmod_blob.3 \
+	vmod_unix.3
 
 CLEANFILES = $(dist_man_MANS)
 
@@ -101,4 +102,7 @@ vmod_vtc.3: $(top_builddir)/lib/libvmod_vtc/vmod_vtc.man.rst
 vmod_blob.3: $(top_builddir)/lib/libvmod_blob/vmod_blob.man.rst
 	${RST2MAN} $(RST2ANY_FLAGS) $? $@
 
+vmod_unix.3: $(top_builddir)/lib/libvmod_unix/vmod_unix.man.rst
+	${RST2MAN} $(RST2ANY_FLAGS) $? $@
+
 .NOPATH: $(dist_man_MANS)


More information about the varnish-commit mailing list