/*
  This file is part of libmicrohttpd
  Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

/**
 * @file lib/daemon_start.c
 * @brief functions to start a daemon
 * @author Christian Grothoff
 */
#include "internal.h"
#include "connection_cleanup.h"
#include "daemon_close_all_connections.h"
#include "daemon_select.h"
#include "daemon_poll.h"
#include "daemon_epoll.h"
#include "request_resume.h"


/**
 * Set listen socket options to allow port rebinding (or not)
 * depending on how MHD was configured.
 *
 * @param[in,out] daemon the daemon with the listen socket to configure
 * @return #MHD_SC_OK on success (or non-fatal errors)
 */
static enum MHD_StatusCode
configure_listen_reuse (struct MHD_Daemon *daemon)
{
  const MHD_SCKT_OPT_BOOL_ on = 1;

  /* Apply the socket options according to
     listening_address_reuse. */
  if (daemon->allow_address_reuse)
  {
    /* User requested to allow reusing listening address:port. */
#ifndef MHD_WINSOCK_SOCKETS
    /* Use SO_REUSEADDR on non-W32 platforms, and do not fail if
     * it doesn't work. */
    if (0 > setsockopt (daemon->listen_socket,
                        SOL_SOCKET,
                        SO_REUSEADDR,
                        (void *) &on,
                        sizeof (on)))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
                _ ("setsockopt failed: %s\n"),
                MHD_socket_last_strerr_ ());
#endif
      return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED;
    }
    return MHD_SC_OK;
#endif /* ! MHD_WINSOCK_SOCKETS */
    /* Use SO_REUSEADDR on Windows and SO_REUSEPORT on most platforms.
     * Fail if SO_REUSEPORT is not defined or setsockopt fails.
     */
    /* SO_REUSEADDR on W32 has the same semantics
 as SO_REUSEPORT on BSD/Linux */
#if defined(MHD_WINSOCK_SOCKETS) || defined(SO_REUSEPORT)
    if (0 > setsockopt (daemon->listen_socket,
                        SOL_SOCKET,
#ifndef MHD_WINSOCK_SOCKETS
                        SO_REUSEPORT,
#else  /* MHD_WINSOCK_SOCKETS */
                        SO_REUSEADDR,
#endif /* MHD_WINSOCK_SOCKETS */
                        (void *) &on,
                        sizeof (on)))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
                _ ("setsockopt failed: %s\n"),
                MHD_socket_last_strerr_ ());
#endif
      return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED;
    }
    return MHD_SC_OK;
#else  /* !MHD_WINSOCK_SOCKETS && !SO_REUSEPORT */
    /* we're supposed to allow address:port re-use, but
 on this platform we cannot; fail hard */
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED,
              _ (
                "Cannot allow listening address reuse: SO_REUSEPORT not defined.\n"));
#endif
    return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED;
#endif /* !MHD_WINSOCK_SOCKETS && !SO_REUSEPORT */
  }

  /* if (! daemon->allow_address_reuse) */
  /* User requested to disallow reusing listening address:port.
   * Do nothing except for Windows where SO_EXCLUSIVEADDRUSE
   * is used and Solaris with SO_EXCLBIND.
   * Fail if MHD was compiled for W32 without SO_EXCLUSIVEADDRUSE
   * or setsockopt fails.
   */
#if (defined(MHD_WINSOCK_SOCKETS) && defined(SO_EXCLUSIVEADDRUSE)) || \
  (defined(__sun) && defined(SO_EXCLBIND))
  if (0 > setsockopt (daemon->listen_socket,
                      SOL_SOCKET,
#ifdef SO_EXCLUSIVEADDRUSE
                      SO_EXCLUSIVEADDRUSE,
#else  /* SO_EXCLBIND */
                      SO_EXCLBIND,
#endif /* SO_EXCLBIND */
                      (void *) &on,
                      sizeof (on)))
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED,
              _ ("setsockopt failed: %s\n"),
              MHD_socket_last_strerr_ ());
#endif
    return MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED;
  }
  return MHD_SC_OK;
#elif defined(MHD_WINSOCK_SOCKETS) /* SO_EXCLUSIVEADDRUSE not defined on W32? */
#ifdef HAVE_MESSAGES
  MHD_DLOG (daemon,
            MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED,
            _ (
              "Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined.\n"));
#endif
  return MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED;
#endif /* MHD_WINSOCK_SOCKETS */
  /* Not on WINSOCK, simply doing nothing will do */
  return MHD_SC_OK;
}


/**
 * Open, configure and bind the listen socket (if required).
 *
 * @param[in,out] daemon daemon to open the socket for
 * @return #MHD_SC_OK on success
 */
static enum MHD_StatusCode
open_listen_socket (struct MHD_Daemon *daemon)
{
  enum MHD_StatusCode sc;
  socklen_t addrlen;
  struct sockaddr_storage ss;
  const struct sockaddr *sa;
  int pf;
  bool use_v6;

  if (MHD_INVALID_SOCKET != daemon->listen_socket)
    return MHD_SC_OK; /* application opened it for us! */
  pf = -1;
  /* Determine address family */
  switch (daemon->listen_af)
  {
  case MHD_AF_NONE:
    if (0 == daemon->listen_sa_len)
    {
      /* no listening desired, that's OK */
      return MHD_SC_OK;
    }
    /* we have a listen address, get AF from there! */
    switch (daemon->listen_sa.ss_family)
    {
    case AF_INET:
      pf = PF_INET;
      use_v6 = false;
      break;
#ifdef AF_INET6
    case AF_INET6:
      pf = PF_INET6;
      use_v6 = true;
      break;
#endif
#ifdef AF_UNIX
    case AF_UNIX:
      pf = PF_UNIX;
      use_v6 = false;
      break;
#endif
    default:
      return MHD_SC_AF_NOT_SUPPORTED_BY_BUILD;
    } /* switch on ss_family */
    break;   /* MHD_AF_NONE */
  case MHD_AF_AUTO:
#if HAVE_INET6
    pf = PF_INET6;
    use_v6 = true;
#else
    pf = PF_INET;
    use_v6 = false;
#endif
    break;
  case MHD_AF_INET4:
    use_v6 = false;
    pf = PF_INET;
    break;
  case MHD_AF_INET6:
  case MHD_AF_DUAL:
#if HAVE_INET6
    pf = PF_INET6;
    use_v6 = true;
    break;
#else
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD,
              _ ("IPv6 not supported by this build.\n"));
#endif
    return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD;
#endif
  }
  mhd_assert (-1 != pf);
  /* try to open listen socket */
try_open_listen_socket:
  daemon->listen_socket = MHD_socket_create_listen_ (pf);
  if ( (MHD_INVALID_SOCKET == daemon->listen_socket) &&
       (MHD_AF_AUTO == daemon->listen_af) &&
       (use_v6) )
  {
    use_v6 = false;
    pf = PF_INET;
    goto try_open_listen_socket;
  }
  if (MHD_INVALID_SOCKET == daemon->listen_socket)
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET,
              _ ("Failed to create socket for listening: %s\n"),
              MHD_socket_last_strerr_ ());
#endif
    return MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET;
  }

  if (MHD_SC_OK !=
      (sc = configure_listen_reuse (daemon)))
    return sc;

  /* configure for dual stack (or not) */
  if (use_v6)
  {
#if defined IPPROTO_IPV6 && defined IPV6_V6ONLY
    /* Note: "IPV6_V6ONLY" is declared by Windows Vista ff., see "IPPROTO_IPV6 Socket Options"
 (http://msdn.microsoft.com/en-us/library/ms738574%28v=VS.85%29.aspx);
 and may also be missing on older POSIX systems; good luck if you have any of those,
 your IPv6 socket may then also bind against IPv4 anyway... */
    const MHD_SCKT_OPT_BOOL_ v6_only =
      (MHD_AF_INET6 == daemon->listen_af);
    if (0 > setsockopt (daemon->listen_socket,
                        IPPROTO_IPV6,
                        IPV6_V6ONLY,
                        (const void *) &v6_only,
                        sizeof (v6_only)))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_FAILED,
                _ ("setsockopt failed: %s\n"),
                MHD_socket_last_strerr_ ());
#endif
    }
#else
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED,
              _ (
                "Cannot explicitly setup dual stack behavior on this platform.\n"));
#endif
#endif
  }

  /* Determine address to bind to */
  if (0 != daemon->listen_sa_len)
  {
    /* Bind address explicitly given */
    sa = (const struct sockaddr *) &daemon->listen_sa;
    addrlen = daemon->listen_sa_len;
  }
  else
  {
    /* Compute bind address based on port and AF */
#if HAVE_INET6
    if (use_v6)
    {
#ifdef IN6ADDR_ANY_INIT
      static const struct in6_addr static_in6any = IN6ADDR_ANY_INIT;
#endif
      struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;

      addrlen = sizeof (struct sockaddr_in6);
      memset (sin6,
              0,
              sizeof (struct sockaddr_in6));
      sin6->sin6_family = AF_INET6;
      sin6->sin6_port = htons (daemon->listen_port);
#ifdef IN6ADDR_ANY_INIT
      sin6->sin6_addr = static_in6any;
#endif
#if HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
      sin6->sin6_len = sizeof (struct sockaddr_in6);
#endif
    }
    else
#endif
    {
      struct sockaddr_in *sin4 = (struct sockaddr_in *) &ss;

      addrlen = sizeof (struct sockaddr_in);
      memset (sin4,
              0,
              sizeof (struct sockaddr_in));
      sin4->sin_family = AF_INET;
      sin4->sin_port = htons (daemon->listen_port);
      if (0 != INADDR_ANY)
        sin4->sin_addr.s_addr = htonl (INADDR_ANY);
#if HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
      sin4->sin_len = sizeof (struct sockaddr_in);
#endif
    }
    sa = (const struct sockaddr *) &ss;
  }

  /* actually do the bind() */
  if (-1 == bind (daemon->listen_socket,
                  sa,
                  addrlen))
  {
#ifdef HAVE_MESSAGES
    unsigned int port = 0;

    switch (sa->sa_family)
    {
    case AF_INET:
      if (addrlen == sizeof (struct sockaddr_in))
        port = ntohs (((const struct sockaddr_in *) sa)->sin_port);
      else
        port = UINT16_MAX + 1; /* indicate size error */
      break;
    case AF_INET6:
      if (addrlen == sizeof (struct sockaddr_in6))
        port = ntohs (((const struct sockaddr_in6 *) sa)->sin6_port);
      else
        port = UINT16_MAX + 1; /* indicate size error */
      break;
    default:
      port = UINT_MAX; /* AF_UNIX? */
      break;
    }
    MHD_DLOG (daemon,
              MHD_SC_LISTEN_SOCKET_BIND_FAILED,
              _ ("Failed to bind to port %u: %s\n"),
              port,
              MHD_socket_last_strerr_ ());
#endif
    return MHD_SC_LISTEN_SOCKET_BIND_FAILED;
  }

  /* setup TCP_FASTOPEN */
#ifdef TCP_FASTOPEN
  if (MHD_FOM_DISABLE != daemon->fast_open_method)
  {
    if (0 != setsockopt (daemon->listen_socket,
                         IPPROTO_TCP,
                         TCP_FASTOPEN,
                         &daemon->fo_queue_length,
                         sizeof (daemon->fo_queue_length)))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                MHD_SC_FAST_OPEN_FAILURE,
                _ ("setsockopt failed: %s\n"),
                MHD_socket_last_strerr_ ());
#endif
      if (MHD_FOM_REQUIRE == daemon->fast_open_method)
        return MHD_SC_FAST_OPEN_FAILURE;
    }
  }
#endif

  /* setup listening */
  if (0 > listen (daemon->listen_socket,
                  daemon->listen_backlog))
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_LISTEN_FAILURE,
              _ ("Failed to listen for connections: %s\n"),
              MHD_socket_last_strerr_ ());
#endif
    return MHD_SC_LISTEN_FAILURE;
  }
  return MHD_SC_OK;
}


/**
 * Obtain the listen port number from the socket (if it
 * was not explicitly set by us, i.e. if we were given
 * a listen socket or if the port was 0 and the OS picked
 * a free one).
 *
 * @param[in,out] daemon daemon to obtain the port number for
 */
static void
get_listen_port_number (struct MHD_Daemon *daemon)
{
  struct sockaddr_storage servaddr;
  socklen_t addrlen;

  if ( (0 != daemon->listen_port) ||
       (MHD_INVALID_SOCKET == daemon->listen_socket) )
    return; /* nothing to be done */

  memset (&servaddr,
          0,
          sizeof (struct sockaddr_storage));
  addrlen = sizeof (servaddr);
  if (0 != getsockname (daemon->listen_socket,
                        (struct sockaddr *) &servaddr,
                        &addrlen))
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE,
              _ ("Failed to get listen port number: %s\n"),
              MHD_socket_last_strerr_ ());
#endif /* HAVE_MESSAGES */
    return;
  }
#ifdef MHD_POSIX_SOCKETS
  if (sizeof (servaddr) < addrlen)
  {
    /* should be impossible with `struct sockaddr_storage` */
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE,
              _ (
                "Failed to get listen port number (`struct sockaddr_storage` too small!?).\n"));
#endif /* HAVE_MESSAGES */
    return;
  }
#endif /* MHD_POSIX_SOCKETS */
  switch (servaddr.ss_family)
  {
  case AF_INET:
    {
      struct sockaddr_in *s4 = (struct sockaddr_in *) &servaddr;

      daemon->listen_port = ntohs (s4->sin_port);
      break;
    }
#ifdef HAVE_INET6
  case AF_INET6:
    {
      struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &servaddr;

      daemon->listen_port = ntohs (s6->sin6_port);
      break;
    }
#endif /* HAVE_INET6 */
#ifdef AF_UNIX
  case AF_UNIX:
    daemon->listen_port = 0;   /* special value for UNIX domain sockets */
    break;
#endif
  default:
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_LISTEN_PORT_INTROSPECTION_UNKNOWN_AF,
              _ ("Unknown address family!\n"));
#endif
    daemon->listen_port = 0;   /* ugh */
    break;
  }
}


#ifdef EPOLL_SUPPORT
/**
 * Setup file descriptor to be used for epoll() control.
 *
 * @param daemon the daemon to setup epoll FD for
 * @return the epoll() fd to use
 */
static int
setup_epoll_fd (struct MHD_Daemon *daemon)
{
  int fd;

#ifndef HAVE_MESSAGES
  (void) daemon; /* Mute compiler warning. */
#endif /* ! HAVE_MESSAGES */

#ifdef USE_EPOLL_CREATE1
  fd = epoll_create1 (EPOLL_CLOEXEC);
#else  /* ! USE_EPOLL_CREATE1 */
  fd = epoll_create (MAX_EVENTS);
#endif /* ! USE_EPOLL_CREATE1 */
  if (MHD_INVALID_SOCKET == fd)
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_EPOLL_CTL_CREATE_FAILED,
              _ ("Call to epoll_create1 failed: %s\n"),
              MHD_socket_last_strerr_ ());
#endif
    return MHD_INVALID_SOCKET;
  }
#if ! defined(USE_EPOLL_CREATE1)
  if (! MHD_socket_noninheritable_ (fd))
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_EPOLL_CTL_CONFIGURE_NOINHERIT_FAILED,
              _ ("Failed to set noninheritable mode on epoll FD.\n"));
#endif
  }
#endif /* ! USE_EPOLL_CREATE1 */
  return fd;
}


/**
 * Setup epoll() FD for the daemon and initialize it to listen
 * on the listen FD.
 * @remark To be called only from thread that process
 * daemon's select()/poll()/etc.
 *
 * @param daemon daemon to initialize for epoll()
 * @return #MHD_SC_OK on success
 */
static enum MHD_StatusCode
setup_epoll_to_listen (struct MHD_Daemon *daemon)
{
  struct epoll_event event;
  MHD_socket ls;

  /* FIXME: update function! */
  daemon->epoll_fd = setup_epoll_fd (daemon);
  if (-1 == daemon->epoll_fd)
    return MHD_SC_EPOLL_CTL_CREATE_FAILED;
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
  if (! daemon->disallow_upgrade)
  {
    daemon->epoll_upgrade_fd = setup_epoll_fd (daemon);
    if (MHD_INVALID_SOCKET == daemon->epoll_upgrade_fd)
      return MHD_SC_EPOLL_CTL_CREATE_FAILED;
  }
#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
  if ( (MHD_INVALID_SOCKET == (ls = daemon->listen_socket)) ||
       (daemon->was_quiesced) )
    return MHD_SC_OK; /* non-listening daemon */
  event.events = EPOLLIN;
  event.data.ptr = daemon;
  if (0 != epoll_ctl (daemon->epoll_fd,
                      EPOLL_CTL_ADD,
                      ls,
                      &event))
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_EPOLL_CTL_ADD_FAILED,
              _ ("Call to epoll_ctl failed: %s\n"),
              MHD_socket_last_strerr_ ());
#endif
    return MHD_SC_EPOLL_CTL_ADD_FAILED;
  }
  daemon->listen_socket_in_epoll = true;
  if (MHD_ITC_IS_VALID_ (daemon->itc))
  {
    event.events = EPOLLIN;
    event.data.ptr = (void *) daemon->epoll_itc_marker;
    if (0 != epoll_ctl (daemon->epoll_fd,
                        EPOLL_CTL_ADD,
                        MHD_itc_r_fd_ (daemon->itc),
                        &event))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                MHD_SC_EPOLL_CTL_ADD_FAILED,
                _ ("Call to epoll_ctl failed: %s\n"),
                MHD_socket_last_strerr_ ());
#endif
      return MHD_SC_EPOLL_CTL_ADD_FAILED;
    }
  }
  return MHD_SC_OK;
}


#endif


/**
 * Thread that runs the polling loop until the daemon
 * is explicitly shut down.
 *
 * @param cls `struct MHD_Deamon` to run select loop in a thread for
 * @return always 0 (on shutdown)
 */
static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
MHD_polling_thread (void *cls)
{
  struct MHD_Daemon *daemon = cls;

  MHD_thread_init_ (&daemon->pid);
  while (! daemon->shutdown)
  {
    switch (daemon->event_loop_syscall)
    {
    case MHD_ELS_AUTO:
      MHD_PANIC ("MHD_ELS_AUTO should have been mapped to preferred style.\n");
      break;
    case MHD_ELS_SELECT:
      MHD_daemon_select_ (daemon,
                          MHD_YES);
      break;
    case MHD_ELS_POLL:
#if HAVE_POLL
      MHD_daemon_poll_ (daemon,
                        MHD_YES);
#else
      MHD_PANIC ("MHD_ELS_POLL not supported, should have failed earlier.\n");
#endif
      break;
    case MHD_ELS_EPOLL:
#ifdef EPOLL_SUPPORT
      MHD_daemon_epoll_ (daemon,
                         MHD_YES);
#else
      MHD_PANIC ("MHD_ELS_EPOLL not supported, should have failed earlier.\n");
#endif
      break;
    }
    MHD_connection_cleanup_ (daemon);
  }
  /* Resume any pending for resume connections, join
   * all connection's threads (if any) and finally cleanup
   * everything. */
  if (! daemon->disallow_suspend_resume)
    MHD_resume_suspended_connections_ (daemon);
  MHD_daemon_close_all_connections_ (daemon);

  return (MHD_THRD_RTRN_TYPE_) 0;
}


/**
 * Setup the thread pool (if needed).
 *
 * @param[in,out] daemon daemon to setup thread pool for
 * @return #MHD_SC_OK on success
 */
static enum MHD_StatusCode
setup_thread_pool (struct MHD_Daemon *daemon)
{
  /* Coarse-grained count of connections per thread (note error
   * due to integer division). Also keep track of how many
   * connections are leftover after an equal split. */
  unsigned int conns_per_thread = daemon->global_connection_limit
                                  / daemon->threading_mode;
  unsigned int leftover_conns = daemon->global_connection_limit
                                % daemon->threading_mode;
  int i;
  enum MHD_StatusCode sc;

  /* Allocate memory for pooled objects */
  daemon->worker_pool = MHD_calloc_ (daemon->threading_mode,
                                     sizeof (struct MHD_Daemon));
  if (NULL == daemon->worker_pool)
    return MHD_SC_THREAD_POOL_MALLOC_FAILURE;

  /* Start the workers in the pool */
  for (i = 0; i < daemon->threading_mode; i++)
  {
    /* Create copy of the Daemon object for each worker */
    struct MHD_Daemon *d = &daemon->worker_pool[i];

    memcpy (d,
            daemon,
            sizeof (struct MHD_Daemon));
    /* Adjust pooling params for worker daemons; note that memcpy()
 has already copied MHD_USE_INTERNAL_POLLING_THREAD thread mode into
 the worker threads. */
    d->master = daemon;
    d->worker_pool_size = 0;
    d->worker_pool = NULL;
    /* Divide available connections evenly amongst the threads.
     * Thread indexes in [0, leftover_conns) each get one of the
     * leftover connections. */
    d->global_connection_limit = conns_per_thread;
    if (((unsigned int) i) < leftover_conns)
      ++d->global_connection_limit;

    if (! daemon->disable_itc)
    {
      if (! MHD_itc_init_ (d->itc))
      {
#ifdef HAVE_MESSAGES
        MHD_DLOG (daemon,
                  MHD_SC_ITC_INITIALIZATION_FAILED,
                  _ (
                    "Failed to create worker inter-thread communication channel: %s\n"),
                  MHD_itc_last_strerror_ () );
#endif
        sc = MHD_SC_ITC_INITIALIZATION_FAILED;
        goto thread_failed;
      }
      if ( (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
           (! MHD_SCKT_FD_FITS_FDSET_ (MHD_itc_r_fd_ (d->itc),
                                       NULL)) )
      {
#ifdef HAVE_MESSAGES
        MHD_DLOG (daemon,
                  MHD_SC_ITC_DESCRIPTOR_TOO_LARGE,
                  _ (
                    "File descriptor for inter-thread communication channel exceeds maximum value.\n"));
#endif
        MHD_itc_destroy_chk_ (d->itc);
        sc = MHD_SC_ITC_DESCRIPTOR_TOO_LARGE;
        goto thread_failed;
      }
    }
    else
    {
      MHD_itc_set_invalid_ (d->itc);
    }

#ifdef EPOLL_SUPPORT
    if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
         (MHD_SC_OK != (sc = setup_epoll_to_listen (d))) )
      goto thread_failed;
#endif

    /* Must init cleanup connection mutex for each worker */
    if (! MHD_mutex_init_ (&d->cleanup_connection_mutex))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE,
                _ ("MHD failed to initialize cleanup connection mutex.\n"));
#endif
      if (! daemon->disable_itc)
        MHD_itc_destroy_chk_ (d->itc);
      sc = MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE;
      goto thread_failed;
    }

    /* Spawn the worker thread */
    if (! MHD_create_named_thread_ (&d->pid,
                                    "MHD-worker",
                                    daemon->thread_stack_limit_b,
                                    &MHD_polling_thread,
                                    d))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                MHD_SC_THREAD_POOL_LAUNCH_FAILURE,
                _ ("Failed to create pool thread: %s\n"),
                MHD_strerror_ (errno));
#endif
      /* Free memory for this worker; cleanup below handles
       * all previously-created workers. */
      if (! daemon->disable_itc)
        MHD_itc_destroy_chk_ (d->itc);
      MHD_mutex_destroy_chk_ (&d->cleanup_connection_mutex);
      sc = MHD_SC_THREAD_POOL_LAUNCH_FAILURE;
      goto thread_failed;
    }
  }   /* end for() */
  return MHD_SC_OK;

thread_failed:
  /* If no worker threads created, then shut down normally. Calling
     MHD_stop_daemon (as we do below) doesn't work here since it
     assumes a 0-sized thread pool means we had been in the default
     MHD_USE_INTERNAL_POLLING_THREAD mode. */
  if (0 == i)
  {
    if (NULL != daemon->worker_pool)
    {
      free (daemon->worker_pool);
      daemon->worker_pool = NULL;
    }
    return MHD_SC_THREAD_LAUNCH_FAILURE;
  }
  /* Shutdown worker threads we've already created. Pretend
     as though we had fully initialized our daemon, but
     with a smaller number of threads than had been
     requested. */
  daemon->worker_pool_size = i;
  daemon->listen_socket = MHD_daemon_quiesce (daemon);
  return sc;
}


/**
 * Start a webserver.
 *
 * @param daemon daemon to start; you can no longer set
 *        options on this daemon after this call!
 * @return #MHD_SC_OK on success
 * @ingroup event
 */
enum MHD_StatusCode
MHD_daemon_start (struct MHD_Daemon *daemon)
{
  enum MHD_StatusCode sc;

  if (MHD_ELS_AUTO == daemon->event_loop_syscall)
  {
#if EPOLL_SUPPORT
    /* We do not support thread-per-connection in combination
 with epoll, so use poll in this case, otherwise prefer
 epoll. */
    if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode)
      daemon->event_loop_syscall = MHD_ELS_POLL;
    else
      daemon->event_loop_syscall = MHD_ELS_EPOLL;
#elif HAVE_POLL
    daemon->event_loop_syscall = MHD_ELS_POLL;
#else
    daemon->event_loop_syscall = MHD_ELS_SELECT;
#endif
  }

#ifdef EPOLL_SUPPORT
  if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
       (0 == daemon->worker_pool_size) &&
       (MHD_INVALID_SOCKET != daemon->listen_socket) &&
       (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) )
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID,
              _ (
                "Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL is not supported.\n"));
#endif
    return MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID;
  }
#endif

  /* Setup ITC */
  if ( (! daemon->disable_itc) &&
       (0 == daemon->worker_pool_size) )
  {
    if (! MHD_itc_init_ (daemon->itc))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                MHD_SC_ITC_INITIALIZATION_FAILED,
                _ ("Failed to create inter-thread communication channel: %s\n"),
                MHD_itc_last_strerror_ ());
#endif
      return MHD_SC_ITC_INITIALIZATION_FAILED;
    }
    if ( (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
         (! MHD_SCKT_FD_FITS_FDSET_ (MHD_itc_r_fd_ (daemon->itc),
                                     NULL)) )
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                MHD_SC_ITC_DESCRIPTOR_TOO_LARGE,
                _ (
                  "File descriptor for inter-thread communication channel exceeds maximum value.\n"));
#endif
      return MHD_SC_ITC_DESCRIPTOR_TOO_LARGE;
    }
  }

  if (MHD_SC_OK != (sc = open_listen_socket (daemon)))
    return sc;

  /* Check listen socket is in range (if we are limited) */
  if ( (MHD_INVALID_SOCKET != daemon->listen_socket) &&
       (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
       (! MHD_SCKT_FD_FITS_FDSET_ (daemon->listen_socket,
                                   NULL)) )
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_LISTEN_SOCKET_TOO_LARGE,
              _ ("Socket descriptor larger than FD_SETSIZE: %d > %d\n"),
              daemon->listen_socket,
              FD_SETSIZE);
#endif
    return MHD_SC_LISTEN_SOCKET_TOO_LARGE;
  }

  /* set listen socket to non-blocking */
  if ( (MHD_INVALID_SOCKET != daemon->listen_socket) &&
       (! MHD_socket_nonblocking_ (daemon->listen_socket)) )
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE,
              _ ("Failed to set nonblocking mode on listening socket: %s\n"),
              MHD_socket_last_strerr_ ());
#endif
    if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) ||
         (daemon->worker_pool_size > 0) )
    {
      /* Accept must be non-blocking. Multiple children may wake
       * up to handle a new connection, but only one will win the
       * race.  The others must immediately return. As this is
       * not possible, we must fail hard here. */
      return MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE;
    }
  }

#ifdef EPOLL_SUPPORT
  /* Setup epoll */
  if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
       (0 == daemon->worker_pool_size) &&
       (MHD_INVALID_SOCKET != daemon->listen_socket) &&
       (MHD_SC_OK != (sc = setup_epoll_to_listen (daemon))) )
    return sc;
#endif

  /* Setup main listen thread (only if we have no thread pool or
     external event loop and do have a listen socket) */
  /* FIXME: why no worker thread if we have no listen socket? */
  if ( ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) ||
         (1 == daemon->threading_mode) ) &&
       (MHD_INVALID_SOCKET != daemon->listen_socket) &&
       (! MHD_create_named_thread_ (&daemon->pid,
                                    (MHD_TM_THREAD_PER_CONNECTION ==
                                     daemon->threading_mode)
                                    ? "MHD-listen"
                                    : "MHD-single",
                                    daemon->thread_stack_limit_b,
                                    &MHD_polling_thread,
                                    daemon) ) )
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              MHD_SC_THREAD_MAIN_LAUNCH_FAILURE,
              _ ("Failed to create listen thread: %s\n"),
              MHD_strerror_ (errno));
#endif
    return MHD_SC_THREAD_MAIN_LAUNCH_FAILURE;
  }

  /* Setup worker threads */
  /* FIXME: why no thread pool if we have no listen socket? */
  if ( (1 < daemon->threading_mode) &&
       (MHD_INVALID_SOCKET != daemon->listen_socket) &&
       (MHD_SC_OK != (sc = setup_thread_pool (daemon))) )
    return sc;

  /* make sure we know our listen port (if any) */
  get_listen_port_number (daemon);

  return MHD_SC_OK;
}


/* end of daemon_start.c */
