mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			657 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			657 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* Copyright (c) 2018 MariaDB Corporation.
 | 
						|
 | 
						|
   This program is free software; you can redistribute it and/or modify
 | 
						|
   it under the terms of the GNU General Public License as published by
 | 
						|
   the Free Software Foundation; version 2 of the License.
 | 
						|
 | 
						|
   This program 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 General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU General Public License
 | 
						|
   along with this program; if not, write to the Free Software
 | 
						|
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA */
 | 
						|
 | 
						|
/* Accepting connections on Windows */
 | 
						|
 | 
						|
#include <my_global.h>
 | 
						|
#include <sql_class.h>
 | 
						|
#include <sql_connect.h>
 | 
						|
#include <mysqld.h>
 | 
						|
#include <mswsock.h>
 | 
						|
#include <mysql/psi/mysql_socket.h>
 | 
						|
#include <sddl.h>
 | 
						|
#include <vector>
 | 
						|
#include <handle_connections_win.h>
 | 
						|
 | 
						|
/* From mysqld.cc */
 | 
						|
extern HANDLE hEventShutdown;
 | 
						|
extern Dynamic_array<MYSQL_SOCKET> listen_sockets;
 | 
						|
#ifdef HAVE_POOL_OF_THREADS
 | 
						|
extern PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ();
 | 
						|
extern void tp_win_callback_prolog();
 | 
						|
#else
 | 
						|
#define get_threadpool_win_callback_environ() 0
 | 
						|
#define tp_win_callback_prolog() do{}while(0)
 | 
						|
#endif
 | 
						|
static SECURITY_ATTRIBUTES pipe_security;
 | 
						|
 | 
						|
/**
 | 
						|
  Abstract base class for accepting new connection,
 | 
						|
  asynchronously (i.e the accept() operation can be posted,
 | 
						|
  and result is retrieved later) , and creating a new connection.
 | 
						|
*/
 | 
						|
 | 
						|
struct Listener
 | 
						|
{
 | 
						|
  /** Windows handle of the Listener.
 | 
						|
   Subclasses would use SOCKET or named pipe handle
 | 
						|
  */
 | 
						|
  HANDLE m_handle;
 | 
						|
  /** Required for all async IO*/
 | 
						|
  OVERLAPPED m_overlapped;
 | 
						|
 | 
						|
  /** Create new listener
 | 
						|
  @param handle - @see m_handle
 | 
						|
  @param wait_handle - usually, event handle or INVALID_HANDLE_VALUE 
 | 
						|
                       @see wait_handle
 | 
						|
  */
 | 
						|
  Listener(HANDLE handle, HANDLE wait_handle):
 | 
						|
    m_handle(handle), m_overlapped()
 | 
						|
  {
 | 
						|
      m_overlapped.hEvent= wait_handle;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
    if not NULL, this handle can be be used in WaitForSingle/MultipleObject(s).
 | 
						|
    This handle will be closed when object is destroyed.
 | 
						|
 | 
						|
    If NULL, the completion notification happens in threadpool.
 | 
						|
  */
 | 
						|
  HANDLE wait_handle()
 | 
						|
  {
 | 
						|
    return m_overlapped.hEvent;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Start waiting for new client connection. */
 | 
						|
  virtual void begin_accept()= 0;
 | 
						|
 | 
						|
  /**
 | 
						|
   Completion callback,called whenever IO posted by begin_accept is finished
 | 
						|
   Listener needs to create a new THD then (or, call scheduler so it creates one)
 | 
						|
 | 
						|
   @param success - whether IO completed successfull
 | 
						|
  */
 | 
						|
  virtual void completion_callback(bool success)= 0;
 | 
						|
 | 
						|
  /**
 | 
						|
   Completion callback for Listener, that uses events for waiting
 | 
						|
   to IO. Not suitable for threadpool etc. Retrieves the status of
 | 
						|
   completed IO from the OVERLAPPED structure
 | 
						|
  */
 | 
						|
  void completion_callback()
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(wait_handle() && (wait_handle() != INVALID_HANDLE_VALUE));
 | 
						|
    DWORD bytes;
 | 
						|
    return completion_callback(
 | 
						|
      GetOverlappedResult(wait_handle(), &m_overlapped, &bytes, FALSE));
 | 
						|
  }
 | 
						|
 | 
						|
  /** Cancel an in-progress IO. Useful for threadpool-bound IO */
 | 
						|
  void cancel()
 | 
						|
  {
 | 
						|
    CancelIoEx(m_handle, &m_overlapped);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Destructor. Closes wait handle, if it was passed in constructor */
 | 
						|
  virtual ~Listener()
 | 
						|
  {
 | 
						|
    if (m_overlapped.hEvent)
 | 
						|
      CloseHandle(m_overlapped.hEvent);
 | 
						|
  };
 | 
						|
};
 | 
						|
 | 
						|
/* Winsock extension functions. */
 | 
						|
static LPFN_ACCEPTEX my_AcceptEx;
 | 
						|
static LPFN_GETACCEPTEXSOCKADDRS my_GetAcceptExSockaddrs;
 | 
						|
 | 
						|
/**
 | 
						|
  Listener that handles socket connections.
 | 
						|
  Can be threadpool-bound (i.e the completion is executed in threadpool thread),
 | 
						|
  or use events for waits.
 | 
						|
 | 
						|
  Threadpool-bound listener should be used with threadpool scheduler, for better
 | 
						|
  performance.
 | 
						|
*/
 | 
						|
struct Socket_Listener: public Listener
 | 
						|
{
 | 
						|
  /** Client socket passed to AcceptEx() call.*/
 | 
						|
  SOCKET m_client_socket;
 | 
						|
 | 
						|
  /** Listening socket. */
 | 
						|
  MYSQL_SOCKET m_listen_socket;
 | 
						|
 | 
						|
  /** Buffer for sockaddrs passed to AcceptEx()/GetAcceptExSockaddrs() */
 | 
						|
  char m_buffer[2 * sizeof(sockaddr_storage) + 32];
 | 
						|
 | 
						|
  /* Threadpool IO struct.*/
 | 
						|
  PTP_IO m_tp_io;
 | 
						|
 | 
						|
  /**
 | 
						|
    Callback for  Windows threadpool's StartThreadpoolIo() function.
 | 
						|
  */
 | 
						|
  static void CALLBACK tp_accept_completion_callback(
 | 
						|
    PTP_CALLBACK_INSTANCE, PVOID context, PVOID , ULONG io_result,
 | 
						|
    ULONG_PTR, PTP_IO io)
 | 
						|
  {
 | 
						|
    tp_win_callback_prolog();
 | 
						|
    Listener *listener= (Listener *)context;
 | 
						|
 | 
						|
    if (io_result == ERROR_OPERATION_ABORTED)
 | 
						|
    {
 | 
						|
      /* ERROR_OPERATION_ABORTED  caused by CancelIoEx()*/
 | 
						|
      CloseThreadpoolIo(io);
 | 
						|
      delete listener;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    listener->completion_callback(io_result == 0);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
  Constructor
 | 
						|
  @param listen_socket - listening socket
 | 
						|
  @PTP_CALLBACK_ENVIRON callback_environ  - threadpool environment, or NULL
 | 
						|
    if threadpool is not used for completion callbacks.
 | 
						|
  */
 | 
						|
  Socket_Listener(MYSQL_SOCKET listen_socket, PTP_CALLBACK_ENVIRON callback_environ) :
 | 
						|
    Listener((HANDLE)listen_socket.fd,0),
 | 
						|
    m_client_socket(INVALID_SOCKET),
 | 
						|
    m_listen_socket(listen_socket)
 | 
						|
  {
 | 
						|
    if (callback_environ)
 | 
						|
    {
 | 
						|
      /* Accept executed in threadpool. */
 | 
						|
      m_tp_io= CreateThreadpoolIo(m_handle,
 | 
						|
        tp_accept_completion_callback, this, callback_environ);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /* Completion signaled via event. */
 | 
						|
      m_tp_io= 0;
 | 
						|
      m_overlapped.hEvent= CreateEvent(0, FALSE , FALSE, 0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
  Use AcceptEx to asynchronously wait for new connection;
 | 
						|
  */
 | 
						|
  void begin_accept()
 | 
						|
  {
 | 
						|
retry :
 | 
						|
    m_client_socket= socket(m_listen_socket.address_family, SOCK_STREAM,
 | 
						|
                            IPPROTO_TCP);
 | 
						|
    if (m_client_socket == INVALID_SOCKET)
 | 
						|
    {
 | 
						|
      sql_perror("socket() call failed.");
 | 
						|
      unireg_abort(1);
 | 
						|
    }
 | 
						|
 | 
						|
    DWORD bytes_received;
 | 
						|
    if (m_tp_io)
 | 
						|
      StartThreadpoolIo(m_tp_io);
 | 
						|
 | 
						|
    BOOL ret= my_AcceptEx(
 | 
						|
      (SOCKET)m_handle,
 | 
						|
      m_client_socket,
 | 
						|
      m_buffer,
 | 
						|
      0,
 | 
						|
      sizeof(sockaddr_storage) + 16,
 | 
						|
      sizeof(sockaddr_storage) + 16,
 | 
						|
      &bytes_received,
 | 
						|
      &m_overlapped);
 | 
						|
 | 
						|
    DWORD last_error=  ret? 0: WSAGetLastError();
 | 
						|
    if (last_error == WSAECONNRESET || last_error == ERROR_NETNAME_DELETED)
 | 
						|
    {
 | 
						|
      if (m_tp_io)
 | 
						|
        CancelThreadpoolIo(m_tp_io);
 | 
						|
      closesocket(m_client_socket);
 | 
						|
      goto retry;
 | 
						|
    }
 | 
						|
 | 
						|
    if (ret || last_error == ERROR_IO_PENDING || abort_loop)
 | 
						|
      return;
 | 
						|
 | 
						|
    sql_print_error("my_AcceptEx failed, last error %u", last_error);
 | 
						|
    abort();
 | 
						|
  }
 | 
						|
 | 
						|
  /* Create new socket connection.*/
 | 
						|
  void completion_callback(bool success)
 | 
						|
  {
 | 
						|
    if (!success)
 | 
						|
    {
 | 
						|
      /* my_AcceptEx() returned error */
 | 
						|
      closesocket(m_client_socket);
 | 
						|
      begin_accept();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    MYSQL_SOCKET s_client{m_client_socket};
 | 
						|
 | 
						|
#ifdef HAVE_PSI_SOCKET_INTERFACE
 | 
						|
    /* Parse socket addresses buffer filled by AcceptEx(),
 | 
						|
       only needed for PSI instrumentation. */
 | 
						|
    sockaddr *local_addr, *remote_addr;
 | 
						|
    int local_addr_len, remote_addr_len;
 | 
						|
 | 
						|
    my_GetAcceptExSockaddrs(m_buffer,
 | 
						|
      0, sizeof(sockaddr_storage) + 16, sizeof(sockaddr_storage) + 16,
 | 
						|
      &local_addr, &local_addr_len, &remote_addr, &remote_addr_len);
 | 
						|
 | 
						|
    s_client.m_psi= PSI_SOCKET_CALL(init_socket)
 | 
						|
      (key_socket_client_connection, (const my_socket*)&m_listen_socket.fd,
 | 
						|
       remote_addr, remote_addr_len);
 | 
						|
#endif
 | 
						|
 | 
						|
    /* Start accepting new connection. After this point, do not use
 | 
						|
    any member data, they could be used by a different (threadpool) thread. */
 | 
						|
    begin_accept();
 | 
						|
 | 
						|
    /* Some chores post-AcceptEx() that we need to create a normal socket.*/
 | 
						|
    if (setsockopt(s_client.fd, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
 | 
						|
      (char *)&m_listen_socket.fd, sizeof(m_listen_socket.fd)))
 | 
						|
    {
 | 
						|
      if (!abort_loop)
 | 
						|
      {
 | 
						|
        sql_perror("setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
 | 
						|
        abort();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /* Create a new connection.*/
 | 
						|
    handle_accepted_socket(s_client, m_listen_socket);
 | 
						|
  }
 | 
						|
 | 
						|
  ~Socket_Listener()
 | 
						|
  {
 | 
						|
    if (m_client_socket != INVALID_SOCKET)
 | 
						|
      closesocket(m_client_socket);
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    Retrieve the pointer to the Winsock extension functions
 | 
						|
    AcceptEx and GetAcceptExSockaddrs.
 | 
						|
  */
 | 
						|
  static void init_winsock_extensions()
 | 
						|
  {
 | 
						|
    if (listen_sockets.size() == 0) {
 | 
						|
      /* --skip-networking was used*/
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    SOCKET s= mysql_socket_getfd(listen_sockets.at(0));
 | 
						|
    GUID guid_AcceptEx= WSAID_ACCEPTEX;
 | 
						|
    GUID guid_GetAcceptExSockaddrs= WSAID_GETACCEPTEXSOCKADDRS;
 | 
						|
 | 
						|
    GUID *guids[]= { &guid_AcceptEx, &guid_GetAcceptExSockaddrs };
 | 
						|
    void *funcs[]= { &my_AcceptEx, &my_GetAcceptExSockaddrs };
 | 
						|
    DWORD bytes;
 | 
						|
    for (int i= 0; i < array_elements(guids); i++)
 | 
						|
    {
 | 
						|
      if (WSAIoctl(s,
 | 
						|
        SIO_GET_EXTENSION_FUNCTION_POINTER,
 | 
						|
        guids[i], sizeof(GUID),
 | 
						|
        funcs[i], sizeof(void *),
 | 
						|
        &bytes, 0, 0) == -1)
 | 
						|
      {
 | 
						|
        sql_print_error("WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER) failed");
 | 
						|
        unireg_abort(1);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
  Create a security descriptor for pipe.
 | 
						|
  - Use low integrity level, so that it is possible to connect
 | 
						|
  from any process.
 | 
						|
  - Give current user read/write access to pipe.
 | 
						|
  - Give Everyone read/write access to pipe minus FILE_CREATE_PIPE_INSTANCE
 | 
						|
*/
 | 
						|
static void init_pipe_security_descriptor()
 | 
						|
{
 | 
						|
#define SDDL_FMT "S:(ML;; NW;;; LW) D:(A;; 0x%08x;;; WD)(A;; FRFW;;; %s)"
 | 
						|
#define EVERYONE_PIPE_ACCESS_MASK                                             \
 | 
						|
  (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES | READ_CONTROL |      \
 | 
						|
   SYNCHRONIZE | FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
 | 
						|
 | 
						|
#ifndef SECURITY_MAX_SID_STRING_CHARACTERS
 | 
						|
/* Old SDK does not have this constant */
 | 
						|
#define SECURITY_MAX_SID_STRING_CHARACTERS 187
 | 
						|
#endif
 | 
						|
 | 
						|
  /*
 | 
						|
    Figure out SID of the user that runs the server, then create SDDL string
 | 
						|
    for pipe permissions, and convert it to the security descriptor.
 | 
						|
  */
 | 
						|
  char sddl_string[sizeof(SDDL_FMT) + 8 + SECURITY_MAX_SID_STRING_CHARACTERS];
 | 
						|
  struct
 | 
						|
  {
 | 
						|
    TOKEN_USER token_user;
 | 
						|
    BYTE buffer[SECURITY_MAX_SID_SIZE];
 | 
						|
  } token_buffer;
 | 
						|
  HANDLE token;
 | 
						|
  DWORD tmp;
 | 
						|
 | 
						|
  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
 | 
						|
    goto fail;
 | 
						|
 | 
						|
  if (!GetTokenInformation(token, TokenUser, &token_buffer,
 | 
						|
                           (DWORD) sizeof(token_buffer), &tmp))
 | 
						|
    goto fail;
 | 
						|
 | 
						|
  CloseHandle(token);
 | 
						|
 | 
						|
  char *current_user_string_sid;
 | 
						|
  if (!ConvertSidToStringSid(token_buffer.token_user.User.Sid,
 | 
						|
                             ¤t_user_string_sid))
 | 
						|
    goto fail;
 | 
						|
 | 
						|
  snprintf(sddl_string, sizeof(sddl_string), SDDL_FMT,
 | 
						|
          (unsigned int)EVERYONE_PIPE_ACCESS_MASK,
 | 
						|
           current_user_string_sid);
 | 
						|
  LocalFree(current_user_string_sid);
 | 
						|
 | 
						|
  if (ConvertStringSecurityDescriptorToSecurityDescriptor(sddl_string,
 | 
						|
      SDDL_REVISION_1, &pipe_security.lpSecurityDescriptor, 0))
 | 
						|
    return;
 | 
						|
 | 
						|
fail:
 | 
						|
  sql_perror("Can't start server : Initialize security descriptor");
 | 
						|
  unireg_abort(1);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Pipe Listener.
 | 
						|
  Only event notification mode is implemented, no threadpool
 | 
						|
*/
 | 
						|
struct Pipe_Listener : public Listener
 | 
						|
{
 | 
						|
  PTP_CALLBACK_ENVIRON m_tp_env;
 | 
						|
  Pipe_Listener():
 | 
						|
    Listener(create_named_pipe(), CreateEvent(0, FALSE, FALSE, 0)),
 | 
						|
    m_tp_env(get_threadpool_win_callback_environ())
 | 
						|
  {
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
  Creates local named pipe instance \\.\pipe\$socket for named pipe connection.
 | 
						|
  */
 | 
						|
  static HANDLE create_named_pipe()
 | 
						|
  {
 | 
						|
    static bool first_instance= true;
 | 
						|
    static char pipe_name[512];
 | 
						|
    DWORD open_mode= PIPE_ACCESS_DUPLEX |
 | 
						|
      FILE_FLAG_OVERLAPPED;
 | 
						|
 | 
						|
    if (first_instance)
 | 
						|
    {
 | 
						|
      snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\pipe\\%s", mysqld_unix_port);
 | 
						|
      open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
 | 
						|
      init_pipe_security_descriptor();
 | 
						|
      pipe_security.nLength= sizeof(SECURITY_ATTRIBUTES);
 | 
						|
      pipe_security.bInheritHandle= FALSE;
 | 
						|
    }
 | 
						|
    HANDLE pipe_handle= CreateNamedPipe(pipe_name,
 | 
						|
      open_mode,
 | 
						|
      PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
 | 
						|
      PIPE_UNLIMITED_INSTANCES,
 | 
						|
      (int)global_system_variables.net_buffer_length,
 | 
						|
      (int)global_system_variables.net_buffer_length,
 | 
						|
      NMPWAIT_USE_DEFAULT_WAIT,
 | 
						|
      &pipe_security);
 | 
						|
    if (pipe_handle == INVALID_HANDLE_VALUE)
 | 
						|
    {
 | 
						|
      sql_perror("Create named pipe failed");
 | 
						|
      sql_print_error("Aborting");
 | 
						|
      unireg_abort(1);
 | 
						|
    }
 | 
						|
    first_instance= false;
 | 
						|
    return pipe_handle;
 | 
						|
  }
 | 
						|
 | 
						|
  static void create_pipe_connection(HANDLE pipe)
 | 
						|
  {
 | 
						|
    if (auto connect= new CONNECT(pipe))
 | 
						|
      create_new_thread(connect);
 | 
						|
    else
 | 
						|
    {
 | 
						|
      CloseHandle(pipe);
 | 
						|
      statistic_increment(aborted_connects, &LOCK_status);
 | 
						|
      statistic_increment(connection_errors_internal, &LOCK_status);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* Threadpool callback.*/
 | 
						|
  static void CALLBACK tp_create_pipe_connection(
 | 
						|
  PTP_CALLBACK_INSTANCE,void *Context)
 | 
						|
  {
 | 
						|
    tp_win_callback_prolog();
 | 
						|
    create_pipe_connection(Context);
 | 
						|
  }
 | 
						|
 | 
						|
  void begin_accept()
 | 
						|
  {
 | 
						|
    BOOL connected= ConnectNamedPipe(m_handle, &m_overlapped);
 | 
						|
    if (connected)
 | 
						|
    {
 | 
						|
      /*  Overlapped ConnectNamedPipe should return zero. */
 | 
						|
      sql_perror("Overlapped ConnectNamedPipe() already connected.");
 | 
						|
      abort();
 | 
						|
    }
 | 
						|
    DWORD last_error= GetLastError();
 | 
						|
    switch (last_error)
 | 
						|
    {
 | 
						|
      case ERROR_PIPE_CONNECTED:
 | 
						|
        /* Client is already connected, so signal an event.*/
 | 
						|
        {
 | 
						|
          /*
 | 
						|
            Cleanup overlapped (so that subsequent  GetOverlappedResult()
 | 
						|
            does not show results of previous IO
 | 
						|
          */
 | 
						|
          HANDLE e= m_overlapped.hEvent;
 | 
						|
          memset(&m_overlapped, 0, sizeof(m_overlapped));
 | 
						|
          m_overlapped.hEvent = e;
 | 
						|
        }
 | 
						|
        if (!SetEvent(m_overlapped.hEvent))
 | 
						|
        {
 | 
						|
          sql_perror("SetEvent() failed for connected pipe.");
 | 
						|
          abort();
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case ERROR_IO_PENDING:
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        sql_perror("ConnectNamedPipe() failed.");
 | 
						|
        abort();
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void completion_callback(bool success)
 | 
						|
  {
 | 
						|
    if (!success)
 | 
						|
    {
 | 
						|
#ifdef DBUG_OFF
 | 
						|
      sql_print_warning("ConnectNamedPipe completed with %u", GetLastError());
 | 
						|
#endif
 | 
						|
      CloseHandle(m_handle);
 | 
						|
      m_handle= create_named_pipe();
 | 
						|
      begin_accept();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    HANDLE pipe= m_handle;
 | 
						|
    m_handle= create_named_pipe();
 | 
						|
    begin_accept();
 | 
						|
    // If threadpool is on, create connection in threadpool thread
 | 
						|
    if (!m_tp_env || !TrySubmitThreadpoolCallback(tp_create_pipe_connection, pipe, m_tp_env))
 | 
						|
      create_pipe_connection(pipe);
 | 
						|
  }
 | 
						|
 | 
						|
  ~Pipe_Listener()
 | 
						|
  {
 | 
						|
    if (m_handle != INVALID_HANDLE_VALUE)
 | 
						|
    {
 | 
						|
      CloseHandle(m_handle);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  static void cleanup()
 | 
						|
  {
 | 
						|
    LocalFree(pipe_security.lpSecurityDescriptor);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 /* The shutdown event, which is set whenever*/
 | 
						|
static void create_shutdown_event()
 | 
						|
{
 | 
						|
  char shutdown_event_name[40];
 | 
						|
  sprintf_s(shutdown_event_name, "MySQLShutdown%u", GetCurrentProcessId());
 | 
						|
  if (!(hEventShutdown= CreateEvent(0, FALSE, FALSE, shutdown_event_name)))
 | 
						|
  {
 | 
						|
    sql_print_error("Can't create shutdown event, Windows error %u", GetLastError());
 | 
						|
    unireg_abort(1);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Accept new client connections on Windows.
 | 
						|
 | 
						|
  Since we deal with pipe and sockets, they cannot be put into a select/loop.
 | 
						|
  But we can use asynchronous IO, and WaitForMultipleObject() loop.
 | 
						|
 | 
						|
  In addition, for slightly better performance, if we're using threadpool,
 | 
						|
  socket connections are accepted directly in the threadpool.
 | 
						|
 | 
						|
  The mode of operation is therefore
 | 
						|
 | 
						|
  1. There is WaitForMultipleObject() loop that waits for shutdown notification
 | 
						|
  (hEventShutdown),and possibly pipes and sockets(e.g if threadpool is not used)
 | 
						|
  This loop ends when shutdown notification is detected.
 | 
						|
 | 
						|
  2. If threadpool is used, new socket connections are accepted there.
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
#define NUM_PIPE_LISTENERS 24
 | 
						|
#define SHUTDOWN_IDX 0
 | 
						|
#define LISTENER_START_IDX 1
 | 
						|
 | 
						|
static std::vector<Listener *> all_listeners;
 | 
						|
static std::vector<HANDLE> wait_events;
 | 
						|
 | 
						|
void network_init_win()
 | 
						|
{
 | 
						|
  Socket_Listener::init_winsock_extensions();
 | 
						|
 | 
						|
  /* Listen for TCP connections on "extra-port" (no threadpool).*/
 | 
						|
  for (uint i= 0 ; i < listen_sockets.elements() ; i++)
 | 
						|
  {
 | 
						|
    MYSQL_SOCKET *sock= listen_sockets.get_pos(i);
 | 
						|
    if (sock->is_extra_port)
 | 
						|
      all_listeners.push_back(new Socket_Listener(*sock, 0));
 | 
						|
  }
 | 
						|
 | 
						|
  /* Listen for named pipe connections */
 | 
						|
  if (mysqld_unix_port[0] && !opt_bootstrap && opt_enable_named_pipe)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Use several listeners for pipe, to reduce ERROR_PIPE_BUSY on client side.
 | 
						|
    */
 | 
						|
    for (int i= 0; i < NUM_PIPE_LISTENERS; i++)
 | 
						|
      all_listeners.push_back(new Pipe_Listener());
 | 
						|
  }
 | 
						|
 | 
						|
  for (uint i= 0 ; i < listen_sockets.elements() ; i++)
 | 
						|
  {
 | 
						|
    MYSQL_SOCKET *sock= listen_sockets.get_pos(i);
 | 
						|
    if (sock->is_extra_port)
 | 
						|
      continue;
 | 
						|
    /* Wait for TCP connections.*/
 | 
						|
    SetFileCompletionNotificationModes((HANDLE) sock->fd,
 | 
						|
                                       FILE_SKIP_SET_EVENT_ON_HANDLE);
 | 
						|
    all_listeners.push_back(
 | 
						|
      new Socket_Listener(*sock, get_threadpool_win_callback_environ()));
 | 
						|
  }
 | 
						|
 | 
						|
  if (all_listeners.size() == 0 && !opt_bootstrap)
 | 
						|
  {
 | 
						|
    sql_print_error("Either TCP connections or named pipe connections must be enabled.");
 | 
						|
    unireg_abort(1);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void handle_connections_win()
 | 
						|
{
 | 
						|
  int n_waits;
 | 
						|
 | 
						|
  create_shutdown_event();
 | 
						|
  wait_events.push_back(hEventShutdown);
 | 
						|
  n_waits= 1;
 | 
						|
 | 
						|
  for (size_t i= 0; i < all_listeners.size(); i++)
 | 
						|
  {
 | 
						|
    HANDLE wait_handle= all_listeners[i]->wait_handle();
 | 
						|
    if (wait_handle)
 | 
						|
    {
 | 
						|
      DBUG_ASSERT((i == 0) || (all_listeners[i - 1]->wait_handle() != 0));
 | 
						|
      wait_events.push_back(wait_handle);
 | 
						|
    }
 | 
						|
    all_listeners[i]->begin_accept();
 | 
						|
  }
 | 
						|
 | 
						|
  mysqld_win_set_startup_complete();
 | 
						|
 | 
						|
  // WaitForMultipleObjects can't wait on more than MAXIMUM_WAIT_OBJECTS
 | 
						|
  // handles simultaneously. Since MAXIMUM_WAIT_OBJECTS is only 64, there is
 | 
						|
  // a theoretical possiblity of exceeding that limit on installations where
 | 
						|
  // host name resolves to a lot of addresses.
 | 
						|
  if (wait_events.size() > MAXIMUM_WAIT_OBJECTS)
 | 
						|
  {
 | 
						|
    sql_print_warning(
 | 
						|
      "Too many wait events (%lu). Some connection listeners won't be handled. "
 | 
						|
      "Try to switch \"thread-handling\" to \"pool-of-threads\" and/or disable "
 | 
						|
      "\"extra-port\".", static_cast<ulong>(wait_events.size()));
 | 
						|
    wait_events.resize(MAXIMUM_WAIT_OBJECTS);
 | 
						|
  }
 | 
						|
 | 
						|
  for (;;)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(wait_events.size() <= MAXIMUM_WAIT_OBJECTS);
 | 
						|
    DWORD idx = WaitForMultipleObjects((DWORD)wait_events.size(),
 | 
						|
                                       wait_events.data(), FALSE, INFINITE);
 | 
						|
    DBUG_ASSERT((int)idx >= 0 && (int)idx < (int)wait_events.size());
 | 
						|
 | 
						|
    if (idx == SHUTDOWN_IDX)
 | 
						|
      break;
 | 
						|
 | 
						|
    all_listeners[idx - LISTENER_START_IDX]->completion_callback();
 | 
						|
  }
 | 
						|
 | 
						|
  mysqld_win_initiate_shutdown();
 | 
						|
 | 
						|
  /* Cleanup */
 | 
						|
  for (size_t i= 0; i < all_listeners.size(); i++)
 | 
						|
  {
 | 
						|
    Listener *listener= all_listeners[i];
 | 
						|
    if (listener->wait_handle())
 | 
						|
      delete listener;
 | 
						|
    else
 | 
						|
      // Threadpool-bound listener will be deleted in threadpool
 | 
						|
      // Do not call destructor, because callback maybe running.
 | 
						|
      listener->cancel();
 | 
						|
  }
 | 
						|
  Pipe_Listener::cleanup();
 | 
						|
}
 |