[master] d719686dc varnishd: copy acceptor out
Nils Goroll
nils.goroll at uplex.de
Mon Sep 30 15:53:06 UTC 2024
commit d719686dc48fad2b677a1b2eaeca86c78cf1018a
Author: Asad Sajjad Ahmed <asadsa at varnish-software.com>
Date: Mon Sep 4 13:07:02 2023 +0200
varnishd: copy acceptor out
Copy acceptor code out from the child and the manager to a new directory
called 'acceptor'.
Updated to varnish-cache as of 48a2a41cbc97498e044af7b7b3545427e3289872 (just
after 7.6)
Signed-off-by: Asad Sajjad Ahmed <asadsa at varnish-software.com>
Signed-off-by: Nils Goroll <nils.goroll at uplex.de>
diff --git a/bin/varnishd/acceptor/cache_acceptor.c b/bin/varnishd/acceptor/cache_acceptor.c
new file mode 100644
index 000000000..8989bc53b
--- /dev/null
+++ b/bin/varnishd/acceptor/cache_acceptor.c
@@ -0,0 +1,834 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2015 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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.
+ *
+ * This source file has the various trickery surrounding the accept/listen
+ * sockets.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include "cache/cache_varnishd.h"
+#include "acceptor/cache_acceptor.h"
+
+#include "cache/cache_transport.h"
+#include "cache/cache_pool.h"
+#include "common/heritage.h"
+
+#include "vcli_serve.h"
+#include "vsa.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+static pthread_t VCA_thread;
+static vtim_dur vca_pace = 0.0;
+static struct lock pace_mtx;
+static unsigned pool_accepting;
+static pthread_mutex_t shut_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+struct wrk_accept {
+ unsigned magic;
+#define WRK_ACCEPT_MAGIC 0x8c4b4d59
+
+ /* Accept stuff */
+ struct sockaddr_storage acceptaddr;
+ socklen_t acceptaddrlen;
+ int acceptsock;
+ struct listen_sock *acceptlsock;
+};
+
+struct poolsock {
+ unsigned magic;
+#define POOLSOCK_MAGIC 0x1b0a2d38
+ VTAILQ_ENTRY(poolsock) list;
+ struct listen_sock *lsock;
+ struct pool_task task[1];
+ struct pool *pool;
+};
+
+/*--------------------------------------------------------------------
+ * TCP options we want to control
+ */
+
+union sock_arg {
+ struct linger lg;
+ struct timeval tv;
+ int i;
+};
+
+static struct sock_opt {
+ int level;
+ int optname;
+ const char *strname;
+ unsigned mod;
+ socklen_t sz;
+ union sock_arg arg[1];
+} sock_opts[] = {
+ /* Note: Setting the mod counter to something not-zero is needed
+ * to force the setsockopt() calls on startup */
+#define SOCK_OPT(lvl, nam, typ) { lvl, nam, #nam, 1, sizeof(typ) },
+
+ SOCK_OPT(SOL_SOCKET, SO_LINGER, struct linger)
+ SOCK_OPT(SOL_SOCKET, SO_KEEPALIVE, int)
+ SOCK_OPT(SOL_SOCKET, SO_SNDTIMEO, struct timeval)
+ SOCK_OPT(SOL_SOCKET, SO_RCVTIMEO, struct timeval)
+
+ SOCK_OPT(IPPROTO_TCP, TCP_NODELAY, int)
+
+#if defined(HAVE_TCP_KEEP)
+ SOCK_OPT(IPPROTO_TCP, TCP_KEEPIDLE, int)
+ SOCK_OPT(IPPROTO_TCP, TCP_KEEPCNT, int)
+ SOCK_OPT(IPPROTO_TCP, TCP_KEEPINTVL, int)
+#elif defined(HAVE_TCP_KEEPALIVE)
+ SOCK_OPT(IPPROTO_TCP, TCP_KEEPALIVE, int)
+#endif
+
+#undef SOCK_OPT
+};
+
+static const int n_sock_opts = sizeof sock_opts / sizeof sock_opts[0];
+
+struct conn_heritage {
+ unsigned sess_set;
+ unsigned listen_mod;
+};
+
+/*--------------------------------------------------------------------
+ * We want to get out of any kind of trouble-hit TCP connections as fast
+ * as absolutely possible, so we set them LINGER disabled, so that even if
+ * there are outstanding write data on the socket, a close(2) will return
+ * immediately.
+ */
+static const struct linger disable_so_linger = {
+ .l_onoff = 0,
+};
+
+/*
+ * We turn on keepalives by default to assist in detecting clients that have
+ * hung up on connections returning from waitinglists
+ */
+
+static const unsigned enable_so_keepalive = 1;
+
+/* We disable Nagle's algorithm in favor of low latency setups.
+ */
+
+static const unsigned enable_tcp_nodelay = 1;
+
+/*--------------------------------------------------------------------
+ * lacking a better place, we put some generic periodic updates
+ * into the vca_acct() loop which we are running anyway
+ */
+static void
+vca_periodic(vtim_real t0)
+{
+ vtim_real now;
+
+ now = VTIM_real();
+ VSC_C_main->uptime = (uint64_t)(now - t0);
+
+ VTIM_postel = FEATURE(FEATURE_HTTP_DATE_POSTEL);
+}
+
+/*--------------------------------------------------------------------
+ * Some kernels have bugs/limitations with respect to which options are
+ * inherited from the accept/listen socket, so we have to keep track of
+ * which, if any, sockopts we have to set on the accepted socket.
+ */
+
+static int
+vca_sock_opt_init(void)
+{
+ struct sock_opt *so;
+ union sock_arg tmp;
+ int n, chg = 0;
+ size_t sz;
+
+ memset(&tmp, 0, sizeof tmp);
+
+ for (n = 0; n < n_sock_opts; n++) {
+ so = &sock_opts[n];
+
+#define SET_VAL(nm, so, fld, val) \
+ do { \
+ if (!strcmp(#nm, so->strname)) { \
+ assert(so->sz == sizeof so->arg->fld); \
+ so->arg->fld = (val); \
+ } \
+ } while (0)
+
+#define NEW_VAL(nm, so, fld, val) \
+ do { \
+ if (!strcmp(#nm, so->strname)) { \
+ sz = sizeof tmp.fld; \
+ assert(so->sz == sz); \
+ tmp.fld = (val); \
+ if (memcmp(&so->arg->fld, &(tmp.fld), sz)) { \
+ memcpy(&so->arg->fld, &(tmp.fld), sz); \
+ so->mod++; \
+ chg = 1; \
+ } \
+ } \
+ } while (0)
+
+ SET_VAL(SO_LINGER, so, lg, disable_so_linger);
+ SET_VAL(SO_KEEPALIVE, so, i, enable_so_keepalive);
+ NEW_VAL(SO_SNDTIMEO, so, tv,
+ VTIM_timeval_sock(cache_param->idle_send_timeout));
+ NEW_VAL(SO_RCVTIMEO, so, tv,
+ VTIM_timeval_sock(cache_param->timeout_idle));
+ SET_VAL(TCP_NODELAY, so, i, enable_tcp_nodelay);
+#if defined(HAVE_TCP_KEEP)
+ NEW_VAL(TCP_KEEPIDLE, so, i,
+ (int)cache_param->tcp_keepalive_time);
+ NEW_VAL(TCP_KEEPCNT, so, i,
+ (int)cache_param->tcp_keepalive_probes);
+ NEW_VAL(TCP_KEEPINTVL, so, i,
+ (int)cache_param->tcp_keepalive_intvl);
+#elif defined(HAVE_TCP_KEEPALIVE)
+ NEW_VAL(TCP_KEEPALIVE, so, i,
+ (int)cache_param->tcp_keepalive_time);
+#endif
+ }
+ return (chg);
+}
+
+static void
+vca_sock_opt_test(const struct listen_sock *ls, const struct sess *sp)
+{
+ struct conn_heritage *ch;
+ struct sock_opt *so;
+ union sock_arg tmp;
+ socklen_t l;
+ int i, n;
+
+ CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+ for (n = 0; n < n_sock_opts; n++) {
+ so = &sock_opts[n];
+ ch = &ls->conn_heritage[n];
+ if (ch->sess_set) {
+ VSL(SLT_Debug, sp->vxid,
+ "sockopt: Not testing nonhereditary %s for %s=%s",
+ so->strname, ls->name, ls->endpoint);
+ continue;
+ }
+ if (so->level == IPPROTO_TCP && ls->uds) {
+ VSL(SLT_Debug, sp->vxid,
+ "sockopt: Not testing incompatible %s for %s=%s",
+ so->strname, ls->name, ls->endpoint);
+ continue;
+ }
+ memset(&tmp, 0, sizeof tmp);
+ l = so->sz;
+ i = getsockopt(sp->fd, so->level, so->optname, &tmp, &l);
+ if (i == 0 && memcmp(&tmp, so->arg, so->sz)) {
+ VSL(SLT_Debug, sp->vxid,
+ "sockopt: Test confirmed %s non heredity for %s=%s",
+ so->strname, ls->name, ls->endpoint);
+ ch->sess_set = 1;
+ }
+ if (i && errno != ENOPROTOOPT)
+ VTCP_Assert(i);
+ }
+}
+
+static void
+vca_sock_opt_set(const struct listen_sock *ls, const struct sess *sp)
+{
+ struct conn_heritage *ch;
+ struct sock_opt *so;
+ vxid_t vxid;
+ int n, sock;
+
+ CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
+
+ if (sp != NULL) {
+ CHECK_OBJ(sp, SESS_MAGIC);
+ sock = sp->fd;
+ vxid = sp->vxid;
+ } else {
+ sock = ls->sock;
+ vxid = NO_VXID;
+ }
+
+ for (n = 0; n < n_sock_opts; n++) {
+ so = &sock_opts[n];
+ ch = &ls->conn_heritage[n];
+ if (so->level == IPPROTO_TCP && ls->uds) {
+ VSL(SLT_Debug, vxid,
+ "sockopt: Not setting incompatible %s for %s=%s",
+ so->strname, ls->name, ls->endpoint);
+ continue;
+ }
+ if (sp == NULL && ch->listen_mod == so->mod) {
+ VSL(SLT_Debug, vxid,
+ "sockopt: Not setting unmodified %s for %s=%s",
+ so->strname, ls->name, ls->endpoint);
+ continue;
+ }
+ if (sp != NULL && !ch->sess_set) {
+ VSL(SLT_Debug, sp->vxid,
+ "sockopt: %s may be inherited for %s=%s",
+ so->strname, ls->name, ls->endpoint);
+ continue;
+ }
+ VSL(SLT_Debug, vxid,
+ "sockopt: Setting %s for %s=%s",
+ so->strname, ls->name, ls->endpoint);
+ VTCP_Assert(setsockopt(sock,
+ so->level, so->optname, so->arg, so->sz));
+ if (sp == NULL)
+ ch->listen_mod = so->mod;
+ }
+}
+
+/*--------------------------------------------------------------------
+ * If accept(2)'ing fails, we pace ourselves to relive any resource
+ * shortage if possible.
+ */
+
+static void
+vca_pace_check(void)
+{
+ vtim_dur p;
+
+ if (vca_pace == 0.0)
+ return;
+ Lck_Lock(&pace_mtx);
+ p = vca_pace;
+ Lck_Unlock(&pace_mtx);
+ if (p > 0.0)
+ VTIM_sleep(p);
+}
+
+static void
+vca_pace_bad(void)
+{
+
+ Lck_Lock(&pace_mtx);
+ vca_pace += cache_param->acceptor_sleep_incr;
+ if (vca_pace > cache_param->acceptor_sleep_max)
+ vca_pace = cache_param->acceptor_sleep_max;
+ Lck_Unlock(&pace_mtx);
+}
+
+static void
+vca_pace_good(void)
+{
+
+ if (vca_pace == 0.0)
+ return;
+ Lck_Lock(&pace_mtx);
+ vca_pace *= cache_param->acceptor_sleep_decay;
+ if (vca_pace < cache_param->acceptor_sleep_incr)
+ vca_pace = 0.0;
+ Lck_Unlock(&pace_mtx);
+}
+
+/*--------------------------------------------------------------------
+ * The pool-task for a newly accepted session
+ *
+ * Called from assigned worker thread
+ */
+
+static void
+vca_mk_tcp(const struct wrk_accept *wa,
+ struct sess *sp, char *laddr, char *lport, char *raddr, char *rport)
+{
+ struct suckaddr *sa = NULL;
+ ssize_t sz;
+
+ AN(SES_Reserve_remote_addr(sp, &sa, &sz));
+ AN(sa);
+ assert(sz == vsa_suckaddr_len);
+ AN(VSA_Build(sa, &wa->acceptaddr, wa->acceptaddrlen));
+ sp->sattr[SA_CLIENT_ADDR] = sp->sattr[SA_REMOTE_ADDR];
+
+ VTCP_name(sa, raddr, VTCP_ADDRBUFSIZE, rport, VTCP_PORTBUFSIZE);
+ AN(SES_Set_String_Attr(sp, SA_CLIENT_IP, raddr));
+ AN(SES_Set_String_Attr(sp, SA_CLIENT_PORT, rport));
+
+
+ AN(SES_Reserve_local_addr(sp, &sa, &sz));
+ AN(VSA_getsockname(sp->fd, sa, sz));
+ sp->sattr[SA_SERVER_ADDR] = sp->sattr[SA_LOCAL_ADDR];
+ VTCP_name(sa, laddr, VTCP_ADDRBUFSIZE, lport, VTCP_PORTBUFSIZE);
+}
+
+static void
+vca_mk_uds(struct wrk_accept *wa, struct sess *sp, char *laddr, char *lport,
+ char *raddr, char *rport)
+{
+ struct suckaddr *sa = NULL;
+ ssize_t sz;
+
+ (void) wa;
+ AN(SES_Reserve_remote_addr(sp, &sa, &sz));
+ AN(sa);
+ assert(sz == vsa_suckaddr_len);
+ AZ(SES_Set_remote_addr(sp, bogo_ip));
+ sp->sattr[SA_CLIENT_ADDR] = sp->sattr[SA_REMOTE_ADDR];
+ sp->sattr[SA_LOCAL_ADDR] = sp->sattr[SA_REMOTE_ADDR];
+ sp->sattr[SA_SERVER_ADDR] = sp->sattr[SA_REMOTE_ADDR];
+ AN(SES_Set_String_Attr(sp, SA_CLIENT_IP, "0.0.0.0"));
+ AN(SES_Set_String_Attr(sp, SA_CLIENT_PORT, "0"));
+
+ strcpy(laddr, "0.0.0.0");
+ strcpy(raddr, "0.0.0.0");
+ strcpy(lport, "0");
+ strcpy(rport, "0");
+}
+
+static void v_matchproto_(task_func_t)
+vca_make_session(struct worker *wrk, void *arg)
+{
+ struct sess *sp;
+ struct req *req;
+ struct wrk_accept *wa;
+ char laddr[VTCP_ADDRBUFSIZE];
+ char lport[VTCP_PORTBUFSIZE];
+ char raddr[VTCP_ADDRBUFSIZE];
+ char rport[VTCP_PORTBUFSIZE];
+
+ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+ CAST_OBJ_NOTNULL(wa, arg, WRK_ACCEPT_MAGIC);
+
+ VTCP_blocking(wa->acceptsock);
+
+ /* Turn accepted socket into a session */
+ AN(WS_Reservation(wrk->aws));
+ sp = SES_New(wrk->pool);
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ wrk->stats->s_sess++;
+
+ sp->t_open = VTIM_real();
+ sp->t_idle = sp->t_open;
+ sp->vxid = VXID_Get(wrk, VSL_CLIENTMARKER);
+
+ sp->fd = wa->acceptsock;
+ wa->acceptsock = -1;
+ sp->listen_sock = wa->acceptlsock;
+
+ assert((size_t)wa->acceptaddrlen <= vsa_suckaddr_len);
+
+ if (wa->acceptlsock->uds)
+ vca_mk_uds(wa, sp, laddr, lport, raddr, rport);
+ else
+ vca_mk_tcp(wa, sp, laddr, lport, raddr, rport);
+
+ AN(wa->acceptlsock->name);
+ VSL(SLT_Begin, sp->vxid, "sess 0 %s",
+ wa->acceptlsock->transport->name);
+ VSL(SLT_SessOpen, sp->vxid, "%s %s %s %s %s %.6f %d",
+ raddr, rport, wa->acceptlsock->name, laddr, lport,
+ sp->t_open, sp->fd);
+
+ vca_pace_good();
+ wrk->stats->sess_conn++;
+
+ if (wa->acceptlsock->test_heritage) {
+ vca_sock_opt_test(wa->acceptlsock, sp);
+ wa->acceptlsock->test_heritage = 0;
+ }
+ vca_sock_opt_set(wa->acceptlsock, sp);
+
+ req = Req_New(sp);
+ CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+ req->htc->rfd = &sp->fd;
+
+ SES_SetTransport(wrk, sp, req, wa->acceptlsock->transport);
+ WS_Release(wrk->aws, 0);
+}
+
+/*--------------------------------------------------------------------
+ * This function accepts on a single socket for a single thread pool.
+ *
+ * As long as we can stick the accepted connection to another thread
+ * we do so, otherwise we put the socket back on the "BACK" pool
+ * and handle the new connection ourselves.
+ */
+
+static void v_matchproto_(task_func_t)
+vca_accept_task(struct worker *wrk, void *arg)
+{
+ struct wrk_accept wa;
+ struct poolsock *ps;
+ struct listen_sock *ls;
+ int i;
+ char laddr[VTCP_ADDRBUFSIZE];
+ char lport[VTCP_PORTBUFSIZE];
+
+ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+ CAST_OBJ_NOTNULL(ps, arg, POOLSOCK_MAGIC);
+ ls = ps->lsock;
+ CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
+
+ while (!pool_accepting)
+ VTIM_sleep(.1);
+
+ /* Dont hold on to (possibly) discarded VCLs */
+ if (wrk->wpriv->vcl != NULL)
+ VCL_Rel(&wrk->wpriv->vcl);
+
+ while (!ps->pool->die) {
+ INIT_OBJ(&wa, WRK_ACCEPT_MAGIC);
+ wa.acceptlsock = ls;
+
+ vca_pace_check();
+
+ wa.acceptaddrlen = sizeof wa.acceptaddr;
+ do {
+ i = accept(ls->sock, (void*)&wa.acceptaddr,
+ &wa.acceptaddrlen);
+ } while (i < 0 && errno == EAGAIN && !ps->pool->die);
+
+ if (i < 0 && ps->pool->die)
+ break;
+
+ if (i < 0 && ls->sock == -2) {
+ /* Shut down in progress */
+ sleep(2);
+ continue;
+ }
+
+ if (i < 0) {
+ switch (errno) {
+ case ECONNABORTED:
+ wrk->stats->sess_fail_econnaborted++;
+ break;
+ case EINTR:
+ wrk->stats->sess_fail_eintr++;
+ break;
+ case EMFILE:
+ wrk->stats->sess_fail_emfile++;
+ vca_pace_bad();
+ break;
+ case EBADF:
+ wrk->stats->sess_fail_ebadf++;
+ vca_pace_bad();
+ break;
+ case ENOBUFS:
+ case ENOMEM:
+ wrk->stats->sess_fail_enomem++;
+ vca_pace_bad();
+ break;
+ default:
+ wrk->stats->sess_fail_other++;
+ vca_pace_bad();
+ break;
+ }
+
+ i = errno;
+ wrk->stats->sess_fail++;
+
+ if (wa.acceptlsock->uds) {
+ bstrcpy(laddr, "0.0.0.0");
+ bstrcpy(lport, "0");
+ } else {
+ VTCP_myname(ls->sock, laddr, VTCP_ADDRBUFSIZE,
+ lport, VTCP_PORTBUFSIZE);
+ }
+
+ VSL(SLT_SessError, NO_VXID, "%s %s %s %d %d \"%s\"",
+ wa.acceptlsock->name, laddr, lport,
+ ls->sock, i, VAS_errtxt(i));
+ (void)Pool_TrySumstat(wrk);
+ continue;
+ }
+
+ wa.acceptsock = i;
+
+ if (!Pool_Task_Arg(wrk, TASK_QUEUE_REQ,
+ vca_make_session, &wa, sizeof wa)) {
+ /*
+ * We couldn't get another thread, so we will handle
+ * the request in this worker thread, but first we
+ * must reschedule the listening task so it will be
+ * taken up by another thread again.
+ */
+ if (!ps->pool->die) {
+ AZ(Pool_Task(wrk->pool, ps->task,
+ TASK_QUEUE_VCA));
+ return;
+ }
+ }
+ if (!ps->pool->die && DO_DEBUG(DBG_SLOW_ACCEPTOR))
+ VTIM_sleep(2.0);
+
+ }
+
+ VSL(SLT_Debug, NO_VXID, "XXX Accept thread dies %p", ps);
+ FREE_OBJ(ps);
+}
+
+/*--------------------------------------------------------------------
+ * Called when a worker and attached thread pool is created, to
+ * allocate the tasks which will listen to sockets for that pool.
+ */
+
+void
+VCA_NewPool(struct pool *pp)
+{
+ struct listen_sock *ls;
+ struct poolsock *ps;
+
+ VTAILQ_FOREACH(ls, &heritage.socks, list) {
+ ALLOC_OBJ(ps, POOLSOCK_MAGIC);
+ AN(ps);
+ ps->lsock = ls;
+ ps->task->func = vca_accept_task;
+ ps->task->priv = ps;
+ ps->pool = pp;
+ VTAILQ_INSERT_TAIL(&pp->poolsocks, ps, list);
+ AZ(Pool_Task(pp, ps->task, TASK_QUEUE_VCA));
+ }
+}
+
+void
+VCA_DestroyPool(struct pool *pp)
+{
+ struct poolsock *ps;
+
+ while (!VTAILQ_EMPTY(&pp->poolsocks)) {
+ ps = VTAILQ_FIRST(&pp->poolsocks);
+ VTAILQ_REMOVE(&pp->poolsocks, ps, list);
+ }
+}
+
+/*--------------------------------------------------------------------*/
+
+static void * v_matchproto_()
+vca_acct(void *arg)
+{
+ struct listen_sock *ls;
+ vtim_real t0;
+
+ // XXX Actually a misnomer now because the accept happens in a pool
+ // thread. Rename to accept-nanny or so?
+ THR_SetName("cache-acceptor");
+ THR_Init();
+ (void)arg;
+
+ t0 = VTIM_real();
+ vca_periodic(t0);
+
+ pool_accepting = 1;
+
+ while (1) {
+ (void)sleep(1);
+ if (vca_sock_opt_init()) {
+ PTOK(pthread_mutex_lock(&shut_mtx));
+ VTAILQ_FOREACH(ls, &heritage.socks, list) {
+ if (ls->sock == -2)
+ continue; // VCA_Shutdown
+ assert (ls->sock > 0);
+ vca_sock_opt_set(ls, NULL);
+ /* If one of the options on a socket has
+ * changed, also force a retest of whether
+ * the values are inherited to the
+ * accepted sockets. This should then
+ * catch any false positives from previous
+ * tests that could happen if the set
+ * value of an option happened to just be
+ * the OS default for that value, and
+ * wasn't actually inherited from the
+ * listening socket. */
+ ls->test_heritage = 1;
+ }
+ PTOK(pthread_mutex_unlock(&shut_mtx));
+ }
+ vca_periodic(t0);
+ }
+ NEEDLESS(return (NULL));
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VCA_Start(struct cli *cli)
+{
+ struct listen_sock *ls;
+
+ (void)vca_sock_opt_init();
+
+ VTAILQ_FOREACH(ls, &heritage.socks, list) {
+ CHECK_OBJ_NOTNULL(ls->transport, TRANSPORT_MAGIC);
+ assert (ls->sock > 0); // We know where stdin is
+ if (cache_param->tcp_fastopen &&
+ VTCP_fastopen(ls->sock, cache_param->listen_depth))
+ VSL(SLT_Error, NO_VXID,
+ "Kernel TCP Fast Open: sock=%d, errno=%d %s",
+ ls->sock, errno, VAS_errtxt(errno));
+ if (listen(ls->sock, cache_param->listen_depth)) {
+ VCLI_SetResult(cli, CLIS_CANT);
+ VCLI_Out(cli, "Listen failed on socket '%s': %s",
+ ls->endpoint, VAS_errtxt(errno));
+ return;
+ }
+ AZ(ls->conn_heritage);
+ ls->conn_heritage = calloc(n_sock_opts,
+ sizeof *ls->conn_heritage);
+ AN(ls->conn_heritage);
+ ls->test_heritage = 1;
+ vca_sock_opt_set(ls, NULL);
+ if (cache_param->accept_filter && VTCP_filter_http(ls->sock))
+ VSL(SLT_Error, NO_VXID,
+ "Kernel filtering: sock=%d, errno=%d %s",
+ ls->sock, errno, VAS_errtxt(errno));
+ }
+
+ PTOK(pthread_create(&VCA_thread, NULL, vca_acct, NULL));
+}
+
+/*--------------------------------------------------------------------*/
+
+static void v_matchproto_(cli_func_t)
+ccf_listen_address(struct cli *cli, const char * const *av, void *priv)
+{
+ struct listen_sock *ls;
+ char h[VTCP_ADDRBUFSIZE], p[VTCP_PORTBUFSIZE];
+
+ (void)cli;
+ (void)av;
+ (void)priv;
+
+ /*
+ * This CLI command is primarily used by varnishtest. Don't
+ * respond until listen(2) has been called, in order to avoid
+ * a race where varnishtest::client would attempt to connect(2)
+ * before listen(2) has been called.
+ */
+ while (!pool_accepting)
+ VTIM_sleep(.1);
+
+ PTOK(pthread_mutex_lock(&shut_mtx));
+ VTAILQ_FOREACH(ls, &heritage.socks, list) {
+ if (!ls->uds) {
+ VTCP_myname(ls->sock, h, sizeof h, p, sizeof p);
+ VCLI_Out(cli, "%s %s %s\n", ls->name, h, p);
+ }
+ else
+ VCLI_Out(cli, "%s %s -\n", ls->name, ls->endpoint);
+ }
+ PTOK(pthread_mutex_unlock(&shut_mtx));
+}
+
+/*--------------------------------------------------------------------*/
+
+static struct cli_proto vca_cmds[] = {
+ { CLICMD_DEBUG_LISTEN_ADDRESS, "d", ccf_listen_address },
+ { NULL }
+};
+
+void
+VCA_Init(void)
+{
+
+ CLI_AddFuncs(vca_cmds);
+ Lck_New(&pace_mtx, lck_vcapace);
+}
+
+void
+VCA_Shutdown(void)
+{
+ struct listen_sock *ls;
+ int i;
+
+ PTOK(pthread_mutex_lock(&shut_mtx));
+ VTAILQ_FOREACH(ls, &heritage.socks, list) {
+ i = ls->sock;
+ ls->sock = -2;
+ (void)close(i);
+ }
+ PTOK(pthread_mutex_unlock(&shut_mtx));
+}
+
+/*--------------------------------------------------------------------
+ * Transport protocol registration
+ *
+ */
+
+static VTAILQ_HEAD(,transport) transports =
+ VTAILQ_HEAD_INITIALIZER(transports);
+
+static uint16_t next_xport;
+
+static void
+XPORT_Register(struct transport *xp)
+{
+
+ CHECK_OBJ_NOTNULL(xp, TRANSPORT_MAGIC);
+ AZ(xp->number);
+
+ xp->number = ++next_xport;
+ VTAILQ_INSERT_TAIL(&transports, xp, list);
+}
+
+void
+XPORT_Init(void)
+{
+
+ ASSERT_MGT();
+
+#define TRANSPORT_MACRO(name) XPORT_Register(&name##_transport);
+ TRANSPORTS
+#undef TRANSPORT_MACRO
+}
+
+const struct transport *
+XPORT_Find(const char *name)
+{
+ const struct transport *xp;
+
+ ASSERT_MGT();
+
+ VTAILQ_FOREACH(xp, &transports, list)
+ if (xp->proto_ident != NULL &&
+ !strcasecmp(xp->proto_ident, name))
+ return (xp);
+ return (NULL);
+}
+
+const struct transport *
+XPORT_ByNumber(uint16_t no)
+{
+ const struct transport *xp;
+
+ VTAILQ_FOREACH(xp, &transports, list)
+ if (xp->number == no)
+ return (xp);
+ return (NULL);
+}
diff --git a/bin/varnishd/acceptor/cache_acceptor.h b/bin/varnishd/acceptor/cache_acceptor.h
new file mode 100644
index 000000000..5e9fbe282
--- /dev/null
+++ b/bin/varnishd/acceptor/cache_acceptor.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2015 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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.
+ *
+ */
+
+/* cache_acceptor.c */
+
+void VCA_Init(void);
+void VCA_Start(struct cli *cli);
+void VCA_Shutdown(void);
diff --git a/bin/varnishd/acceptor/mgt_acceptor.c b/bin/varnishd/acceptor/mgt_acceptor.c
new file mode 100644
index 000000000..aac7295c2
--- /dev/null
+++ b/bin/varnishd/acceptor/mgt_acceptor.c
@@ -0,0 +1,383 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2015 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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.
+ *
+ * Acceptor socket management
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "mgt/mgt.h"
+#include "acceptor/cache_acceptor.h" // XXX mgt_acceptor should not use
+#include "acceptor/mgt_acceptor.h"
+#include "common/heritage.h"
+
+#include "vav.h"
+#include "vcli_serve.h"
+#include "vsa.h"
+#include "vss.h"
+#include "vtcp.h"
+#include "vus.h"
+
+struct listen_arg {
+ unsigned magic;
+#define LISTEN_ARG_MAGIC 0xbb2fc333
+ VTAILQ_ENTRY(listen_arg) list;
+ const char *endpoint;
+ const char *name;
+ VTAILQ_HEAD(,listen_sock) socks;
+ const struct transport *transport;
+ const struct uds_perms *perms;
+};
+
+struct uds_perms {
+ unsigned magic;
+#define UDS_PERMS_MAGIC 0x84fb5635
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+};
+
+static VTAILQ_HEAD(,listen_arg) listen_args =
+ VTAILQ_HEAD_INITIALIZER(listen_args);
+
+static int
+mac_vus_bind(void *priv, const struct sockaddr_un *uds)
+{
+ return (VUS_bind(uds, priv));
+}
+
+static int
+mac_opensocket(struct listen_sock *ls)
+{
+ int fail;
+ const char *err;
+
+ CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
+ if (ls->sock > 0) {
+ MCH_Fd_Inherit(ls->sock, NULL);
+ closefd(&ls->sock);
+ }
+ if (!ls->uds)
+ ls->sock = VTCP_bind(ls->addr, NULL);
+ else
+ ls->sock = VUS_resolver(ls->endpoint, mac_vus_bind, NULL, &err);
+ fail = errno;
+ if (ls->sock < 0) {
+ AN(fail);
+ return (fail);
+ }
+ if (ls->perms != NULL) {
+ CHECK_OBJ(ls->perms, UDS_PERMS_MAGIC);
+ assert(ls->uds);
+ errno = 0;
+ if (ls->perms->mode != 0 &&
+ chmod(ls->endpoint, ls->perms->mode) != 0)
+ return (errno);
+ if (chown(ls->endpoint, ls->perms->uid, ls->perms->gid) != 0)
+ return (errno);
+ }
+ MCH_Fd_Inherit(ls->sock, "sock");
+ return (0);
+}
+
+/*=====================================================================
+ * Reopen the accept sockets to get rid of listen status.
+ * returns the highest errno encountered, 0 for success
+ */
+
+int
+MAC_reopen_sockets(void)
+{
+ struct listen_sock *ls;
+ int err, fail = 0;
+
+ VTAILQ_FOREACH(ls, &heritage.socks, list) {
+ VJ_master(JAIL_MASTER_PRIVPORT);
+ err = mac_opensocket(ls);
+ VJ_master(JAIL_MASTER_LOW);
+ if (err == 0)
+ continue;
+ fail = vmax(fail, err);
+ MGT_Complain(C_ERR,
+ "Could not reopen listen socket %s: %s",
+ ls->endpoint, VAS_errtxt(err));
+ }
+ return (fail);
+}
+
+/*--------------------------------------------------------------------*/
+
+static struct listen_sock *
+mk_listen_sock(const struct listen_arg *la, const struct suckaddr *sa)
+{
+ struct listen_sock *ls;
+ int fail;
+
+ ALLOC_OBJ(ls, LISTEN_SOCK_MAGIC);
+ AN(ls);
+ ls->sock = -1;
+ ls->addr = VSA_Clone(sa);
+ AN(ls->addr);
+ REPLACE(ls->endpoint, la->endpoint);
+ ls->name = la->name;
+ ls->transport = la->transport;
+ ls->perms = la->perms;
+ ls->uds = VUS_is(la->endpoint);
+ VJ_master(JAIL_MASTER_PRIVPORT);
+ fail = mac_opensocket(ls);
+ VJ_master(JAIL_MASTER_LOW);
+ if (fail) {
+ VSA_free(&ls->addr);
+ free(ls->endpoint);
+ FREE_OBJ(ls);
+ if (fail != EAFNOSUPPORT)
+ ARGV_ERR("Could not get socket %s: %s\n",
+ la->endpoint, VAS_errtxt(fail));
+ return (NULL);
+ }
+ return (ls);
+}
+
+static int v_matchproto_(vss_resolved_f)
+mac_tcp(void *priv, const struct suckaddr *sa)
+{
+ struct listen_arg *la;
+ struct listen_sock *ls;
+ char abuf[VTCP_ADDRBUFSIZE], pbuf[VTCP_PORTBUFSIZE];
+ char nbuf[VTCP_ADDRBUFSIZE+VTCP_PORTBUFSIZE+2];
+
+ CAST_OBJ_NOTNULL(la, priv, LISTEN_ARG_MAGIC);
+
+ VTAILQ_FOREACH(ls, &heritage.socks, list) {
+ if (!ls->uds && !VSA_Compare(sa, ls->addr))
+ ARGV_ERR("-a arguments %s and %s have same address\n",
+ ls->endpoint, la->endpoint);
+ }
+ ls = mk_listen_sock(la, sa);
+ if (ls == NULL)
+ return (0);
+ AZ(ls->uds);
+ if (VSA_Port(ls->addr) == 0) {
+ /*
+ * If the argv port number is zero, we adopt whatever
+ * port number this VTCP_bind() found us, as if
+ * it was specified by the argv.
+ */
+ VSA_free(&ls->addr);
+ ls->addr = VTCP_my_suckaddr(ls->sock);
+ VTCP_myname(ls->sock, abuf, sizeof abuf,
+ pbuf, sizeof pbuf);
+ if (VSA_Get_Proto(sa) == AF_INET6)
+ bprintf(nbuf, "[%s]:%s", abuf, pbuf);
+ else
+ bprintf(nbuf, "%s:%s", abuf, pbuf);
+ REPLACE(ls->endpoint, nbuf);
+ }
+ VTAILQ_INSERT_TAIL(&la->socks, ls, arglist);
+ VTAILQ_INSERT_TAIL(&heritage.socks, ls, list);
+ return (0);
+}
+
+static int v_matchproto_(vus_resolved_f)
+mac_uds(void *priv, const struct sockaddr_un *uds)
+{
+ struct listen_arg *la;
+ struct listen_sock *ls;
+
+ CAST_OBJ_NOTNULL(la, priv, LISTEN_ARG_MAGIC);
+ (void) uds;
+
+ VTAILQ_FOREACH(ls, &heritage.socks, list) {
+ if (ls->uds && strcmp(ls->endpoint, la->endpoint) == 0)
+ ARGV_ERR("-a arguments %s and %s have same address\n",
+ ls->endpoint, la->endpoint);
+ }
+ ls = mk_listen_sock(la, bogo_ip);
+ if (ls == NULL)
+ return (0);
+ AN(ls->uds);
+ VTAILQ_INSERT_TAIL(&la->socks, ls, arglist);
+ VTAILQ_INSERT_TAIL(&heritage.socks, ls, list);
+ return (0);
+}
+
+void
+MAC_Arg(const char *spec)
+{
+ char **av;
+ struct listen_arg *la;
+ const char *err;
+ int error;
+ const struct transport *xp = NULL;
+ const char *name;
+ char name_buf[8];
+ static unsigned seq = 0;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ mode_t mode = 0;
+ struct uds_perms *perms;
+
+ av = MGT_NamedArg(spec, &name, "-a");
+ AN(av);
+
+ ALLOC_OBJ(la, LISTEN_ARG_MAGIC);
+ AN(la);
+ VTAILQ_INIT(&la->socks);
+ VTAILQ_INSERT_TAIL(&listen_args, la, list);
+ la->endpoint = av[1];
+
+ if (name == NULL) {
+ bprintf(name_buf, "a%u", seq++);
+ name = strdup(name_buf);
+ AN(name);
+ }
+ la->name = name;
+
+ if (*la->endpoint != '/' && strchr(la->endpoint, '/') != NULL)
+ ARGV_ERR("Unix domain socket addresses must be"
+ " absolute paths in -a (%s)\n", la->endpoint);
+
+ if (VUS_is(la->endpoint) && heritage.min_vcl_version < 41)
+ heritage.min_vcl_version = 41;
+
+ for (int i = 2; av[i] != NULL; i++) {
+ char *eq, *val;
+ int len;
+
+ if ((eq = strchr(av[i], '=')) == NULL) {
+ if (xp != NULL)
+ ARGV_ERR("Too many protocol sub-args"
+ " in -a (%s)\n", av[i]);
+ xp = XPORT_Find(av[i]);
+ if (xp == NULL)
+ ARGV_ERR("Unknown protocol '%s'\n", av[i]);
+ continue;
+ }
+ if (la->endpoint[0] != '/')
+ ARGV_ERR("Invalid sub-arg %s"
+ " in -a\n", av[i]);
+
+ val = eq + 1;
+ len = eq - av[i];
+ assert(len >= 0);
+ if (len == 0)
+ ARGV_ERR("Invalid sub-arg %s in -a\n", av[i]);
+
+ if (strncmp(av[i], "user", len) == 0) {
+ if (pwd != NULL)
+ ARGV_ERR("Too many user sub-args in -a (%s)\n",
+ av[i]);
+ pwd = getpwnam(val);
+ if (pwd == NULL)
+ ARGV_ERR("Unknown user %s in -a\n", val);
+ continue;
+ }
+
+ if (strncmp(av[i], "group", len) == 0) {
+ if (grp != NULL)
+ ARGV_ERR("Too many group sub-args in -a (%s)\n",
+ av[i]);
+ grp = getgrnam(val);
+ if (grp == NULL)
+ ARGV_ERR("Unknown group %s in -a\n", val);
+ continue;
+ }
+
+ if (strncmp(av[i], "mode", len) == 0) {
+ long m;
+ char *p;
+
+ if (mode != 0)
+ ARGV_ERR("Too many mode sub-args in -a (%s)\n",
+ av[i]);
+ if (*val == '\0')
+ ARGV_ERR("Empty mode sub-arg in -a\n");
+ errno = 0;
+ m = strtol(val, &p, 8);
+ if (*p != '\0')
+ ARGV_ERR("Invalid mode sub-arg %s in -a\n",
+ val);
+ if (errno)
+ ARGV_ERR("Cannot parse mode sub-arg %s in -a: "
+ "%s\n", val, VAS_errtxt(errno));
+ if (m <= 0 || m > 0777)
+ ARGV_ERR("Mode sub-arg %s out of range in -a\n",
+ val);
+ mode = (mode_t) m;
+ continue;
+ }
+
+ ARGV_ERR("Invalid sub-arg %s in -a\n", av[i]);
+ }
+
+ if (xp == NULL)
+ xp = XPORT_Find("http");
+ AN(xp);
+ la->transport = xp;
+
+ if (pwd != NULL || grp != NULL || mode != 0) {
+ ALLOC_OBJ(perms, UDS_PERMS_MAGIC);
+ AN(perms);
+ if (pwd != NULL)
+ perms->uid = pwd->pw_uid;
+ else
+ perms->uid = (uid_t) -1;
+ if (grp != NULL)
+ perms->gid = grp->gr_gid;
+ else
+ perms->gid = (gid_t) -1;
+ perms->mode = mode;
+ la->perms = perms;
+ }
+ else
+ AZ(la->perms);
+
+ if (VUS_is(la->endpoint))
+ error = VUS_resolver(av[1], mac_uds, la, &err);
+ else
+ error = VSS_resolver_range(av[1], "80", mac_tcp, la, &err);
+
+ if (error)
+ ARGV_ERR("Got no socket(s) for %s (%s)\n", av[1], err);
+ else if (VTAILQ_EMPTY(&la->socks))
+ ARGV_ERR("Got no socket(s) for %s\n", av[1]);
+ VAV_Free(av);
+}
diff --git a/bin/varnishd/acceptor/mgt_acceptor.h b/bin/varnishd/acceptor/mgt_acceptor.h
new file mode 100644
index 000000000..61eb43627
--- /dev/null
+++ b/bin/varnishd/acceptor/mgt_acceptor.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2011 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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.
+ *
+ */
+
+/* mgt_acceptor.c */
+
+void MAC_Arg(const char *);
+int MAC_reopen_sockets(void);
More information about the varnish-commit
mailing list