From cbb8fda4a7e763738e9675ec9f96286c87879176 Mon Sep 17 00:00:00 2001 From: brain Date: Tue, 23 Oct 2007 22:30:25 +0000 Subject: Move socketengines into their own dir. This was all w00t's idea, but i told him no because i didnt think it would work. Now ive done it myself :P ner ner ne ner ner :) git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@8332 e03df62e-2008-0410-955e-edbf42e46eb7 --- configure | 2 +- include/socketengine_epoll.h | 64 ---- include/socketengine_iocp.h | 242 ------------- include/socketengine_kqueue.h | 69 ---- include/socketengine_ports.h | 68 ---- include/socketengine_select.h | 69 ---- include/socketengines/socketengine_epoll.h | 64 ++++ include/socketengines/socketengine_iocp.h | 242 +++++++++++++ include/socketengines/socketengine_kqueue.h | 69 ++++ include/socketengines/socketengine_ports.h | 68 ++++ include/socketengines/socketengine_select.h | 69 ++++ src/socketengine.cpp | 8 +- src/socketengine_epoll.cpp | 164 --------- src/socketengine_iocp.cpp | 515 ---------------------------- src/socketengine_kqueue.cpp | 169 --------- src/socketengine_ports.cpp | 129 ------- src/socketengine_select.cpp | 165 --------- src/socketengines/socketengine_epoll.cpp | 164 +++++++++ src/socketengines/socketengine_iocp.cpp | 515 ++++++++++++++++++++++++++++ src/socketengines/socketengine_kqueue.cpp | 169 +++++++++ src/socketengines/socketengine_ports.cpp | 129 +++++++ src/socketengines/socketengine_select.cpp | 165 +++++++++ 22 files changed, 1659 insertions(+), 1659 deletions(-) delete mode 100644 include/socketengine_epoll.h delete mode 100644 include/socketengine_iocp.h delete mode 100644 include/socketengine_kqueue.h delete mode 100644 include/socketengine_ports.h delete mode 100644 include/socketengine_select.h create mode 100644 include/socketengines/socketengine_epoll.h create mode 100644 include/socketengines/socketengine_iocp.h create mode 100644 include/socketengines/socketengine_kqueue.h create mode 100644 include/socketengines/socketengine_ports.h create mode 100644 include/socketengines/socketengine_select.h delete mode 100644 src/socketengine_epoll.cpp delete mode 100644 src/socketengine_iocp.cpp delete mode 100644 src/socketengine_kqueue.cpp delete mode 100644 src/socketengine_ports.cpp delete mode 100644 src/socketengine_select.cpp create mode 100644 src/socketengines/socketengine_epoll.cpp create mode 100644 src/socketengines/socketengine_iocp.cpp create mode 100644 src/socketengines/socketengine_kqueue.cpp create mode 100644 src/socketengines/socketengine_ports.cpp create mode 100644 src/socketengines/socketengine_select.cpp diff --git a/configure b/configure index 3908411d8..ba44b745c 100755 --- a/configure +++ b/configure @@ -1166,7 +1166,7 @@ print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n"; #ifndef __CONFIGURATION_SOCKETENGINE__ #define __CONFIGURATION_SOCKETENGINE__ -#include "$se.h" +#include "socketengines/$se.h" #endif EOF diff --git a/include/socketengine_epoll.h b/include/socketengine_epoll.h deleted file mode 100644 index 736a109eb..000000000 --- a/include/socketengine_epoll.h +++ /dev/null @@ -1,64 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#ifndef __SOCKETENGINE_EPOLL__ -#define __SOCKETENGINE_EPOLL__ - -#include -#include -#include -#include "inspircd_config.h" -#include "globals.h" -#include "inspircd.h" -#include "socketengine.h" -#include -#define EP_DELAY 5 - -class InspIRCd; - -/** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll(). - */ -class EPollEngine : public SocketEngine -{ -private: - /** These are used by epoll() to hold socket events - */ - struct epoll_event events[MAX_DESCRIPTORS]; -public: - /** Create a new EPollEngine - * @param Instance The creator of this object - */ - EPollEngine(InspIRCd* Instance); - /** Delete an EPollEngine - */ - virtual ~EPollEngine(); - virtual bool AddFd(EventHandler* eh); - virtual int GetMaxFds(); - virtual int GetRemainingFds(); - virtual bool DelFd(EventHandler* eh, bool force = false); - virtual int DispatchEvents(); - virtual std::string GetName(); - virtual void WantWrite(EventHandler* eh); -}; - -/** Creates a SocketEngine - */ -class SocketEngineFactory -{ -public: - /** Create a new instance of SocketEngine based on EpollEngine - */ - SocketEngine* Create(InspIRCd* Instance) { return new EPollEngine(Instance); } -}; - -#endif diff --git a/include/socketengine_iocp.h b/include/socketengine_iocp.h deleted file mode 100644 index b024828f1..000000000 --- a/include/socketengine_iocp.h +++ /dev/null @@ -1,242 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#ifndef __SOCKETENGINE_IOCP__ -#define __SOCKETENGINE_IOCP__ - -#define READ_BUFFER_SIZE 600 -#define USING_IOCP 1 - -#include "inspircd_config.h" -#include "inspircd_win32wrapper.h" -#include "globals.h" -#include "inspircd.h" -#include "socketengine.h" - -/** Socket overlapped event types - */ -enum SocketIOEvent -{ - /** Read ready */ - SOCKET_IO_EVENT_READ_READY = 0, - /** Write ready */ - SOCKET_IO_EVENT_WRITE_READY = 1, - /** Accept ready */ - SOCKET_IO_EVENT_ACCEPT = 2, - /** Error occured */ - SOCKET_IO_EVENT_ERROR = 3, - /** Number of events */ - NUM_SOCKET_IO_EVENTS = 4, -}; - -/** Represents a windows overlapped IO event - */ -class Overlapped -{ - public: - /** Overlap event */ - OVERLAPPED m_overlap; - /** Type of event */ - SocketIOEvent m_event; -#ifdef WIN64 - /** Parameters */ - unsigned __int64 m_params; -#else - /** Parameters */ - unsigned long m_params; -#endif - /** Create an overlapped event - */ - Overlapped(SocketIOEvent ev, int params) : m_event(ev), m_params(params) - { - memset(&m_overlap, 0, sizeof(OVERLAPPED)); - } -}; - -/** Specific to UDP sockets with overlapped IO - */ -struct udp_overlap -{ - unsigned char udp_buffer[600]; - unsigned long udp_len; - sockaddr udp_sockaddr[2]; - unsigned long udp_sockaddr_len; -}; - -/** Specific to accepting sockets with overlapped IO - */ -struct accept_overlap -{ - int socket; - char buf[1024]; -}; - -/** Implementation of SocketEngine that implements windows IO Completion Ports - */ -class IOCPEngine : public SocketEngine -{ - /** Creates a "fake" file descriptor for use with an IOCP socket. - * This is a little slow, but it isnt called too much. We'll fix it - * in a future release. - * @return -1 if there are no free slots, and an integer if it finds one. - */ - __inline int GenerateFd(int RealFd) - { - int index_hash = RealFd % MAX_DESCRIPTORS; - if(ref[index_hash] == 0) - return index_hash; - else - { - register int i = 0; - for(; i < MAX_DESCRIPTORS; ++i) - if(ref[i] == 0) - return i; - } - return -1; - } - - /** Global I/O completion port that sockets attach to. - */ - HANDLE m_completionPort; - - /** This is kinda shitty... :/ for getting an address from a real fd. - */ - map m_binding; - -public: - /** Holds the preallocated buffer passed to WSARecvFrom - * function. Yes, I know, it's a dirty hack. - */ - udp_overlap * udp_ov; - - /** Creates an IOCP Socket Engine - * @param Instance The creator of this object - */ - IOCPEngine(InspIRCd* Instance); - - /** Deletes an IOCP socket engine and all the attached sockets - */ - ~IOCPEngine(); - - /** Adds an event handler to the completion port, and sets up initial events. - * @param eh EventHandler to add - * @return True if success, false if no room - */ - bool AddFd(EventHandler* eh); - - /** Gets the maximum number of file descriptors that this engine can handle. - * @return The number of file descriptors - */ - __inline int GetMaxFds() { return MAX_DESCRIPTORS; } - - /** Gets the number of free/remaining file descriptors under this engine. - * @return Remaining count - */ - __inline int GetRemainingFds() - { - register int count = 0; - register int i = 0; - for(; i < MAX_DESCRIPTORS; ++i) - if(ref[i] == 0) - ++count; - return count; - } - - /** Removes a file descriptor from the set, preventing it from receiving any more events - * @return True if remove was successful, false otherwise - */ - bool DelFd(EventHandler* eh, bool force = false); - - /** Called every loop to handle input/output events for all sockets under this engine - * @return The number of "changed" sockets. - */ - int DispatchEvents(); - - /** Gets the name of this socket engine as a string. - * @return string of socket engine name - */ - std::string GetName(); - - /** Queues a Write event on the specified event handler. - * @param eh EventHandler that needs data sent on - */ - void WantWrite(EventHandler* eh); - - /** Posts a completion event on the specified socket. - * @param eh EventHandler for message - * @param type Event Type - * @param param Event Parameter - * @return True if added, false if not - */ - bool PostCompletionEvent(EventHandler* eh, SocketIOEvent type, int param); - - /** Posts a read event on the specified socket - * @param eh EventHandler (socket) - */ - void PostReadEvent(EventHandler* eh); - - /** Posts an accept event on the specified socket - * @param eh EventHandler (socket) - */ - void PostAcceptEvent(EventHandler* eh); - - /** Returns the EventHandler attached to a specific fd. - * If the fd isnt in the socketengine, returns NULL. - * @param fd The event handler to look for - * @return A pointer to the event handler, or NULL - */ - EventHandler* GetRef(int fd); - - /** Returns true if a file descriptor exists in - * the socket engine's list. - * @param fd The event handler to look for - * @return True if this fd has an event handler - */ - bool HasFd(int fd); - - /** Returns the EventHandler attached to a specific fd. - * If the fd isnt in the socketengine, returns NULL. - * @param fd The event handler to look for - * @return A pointer to the event handler, or NULL - */ - EventHandler* GetIntRef(int fd); - - bool BoundsCheckFd(EventHandler* eh); - - virtual int Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen); - - virtual int RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); - - virtual int Blocking(int fd); - - virtual int NonBlocking(int fd); - - virtual int GetSockName(EventHandler* fd, sockaddr *name, socklen_t* namelen); - - virtual int Close(int fd); - - virtual int Close(EventHandler* fd); -}; - -/** Creates a SocketEngine - */ -class SocketEngineFactory -{ -public: - /** Create a new instance of SocketEngine based on IOCPEngine - */ - SocketEngine* Create(InspIRCd* Instance) { return new IOCPEngine(Instance); } -}; - -#endif - diff --git a/include/socketengine_kqueue.h b/include/socketengine_kqueue.h deleted file mode 100644 index c34075cd2..000000000 --- a/include/socketengine_kqueue.h +++ /dev/null @@ -1,69 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#ifndef __SOCKETENGINE_KQUEUE__ -#define __SOCKETENGINE_KQUEUE__ - -#include -#include -#include -#include "inspircd_config.h" -#include "globals.h" -#include "inspircd.h" -#include -#include -#include -#include "socketengine.h" - -class InspIRCd; - -/** A specialisation of the SocketEngine class, designed to use FreeBSD kqueue(). - */ -class KQueueEngine : public SocketEngine -{ -private: - /** These are used by kqueue() to hold socket events - */ - struct kevent ke_list[MAX_DESCRIPTORS]; - /** This is a specialised time value used by kqueue() - */ - struct timespec ts; -public: - /** Create a new KQueueEngine - * @param Instance The creator of this object - */ - KQueueEngine(InspIRCd* Instance); - /** Delete a KQueueEngine - */ - virtual ~KQueueEngine(); - virtual bool AddFd(EventHandler* eh); - virtual int GetMaxFds(); - virtual int GetRemainingFds(); - virtual bool DelFd(EventHandler* eh, bool force = false); - virtual int DispatchEvents(); - virtual std::string GetName(); - virtual void WantWrite(EventHandler* eh); - virtual void RecoverFromFork(); -}; - -/** Creates a SocketEngine - */ -class SocketEngineFactory -{ - public: - /** Create a new instance of SocketEngine based on KQueueEngine - */ - SocketEngine* Create(InspIRCd* Instance) { return new KQueueEngine(Instance); } -}; - -#endif diff --git a/include/socketengine_ports.h b/include/socketengine_ports.h deleted file mode 100644 index a5951a138..000000000 --- a/include/socketengine_ports.h +++ /dev/null @@ -1,68 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#ifndef __SOCKETENGINE_PORTS__ -#define __SOCKETENGINE_PORTS__ - -#ifndef __sun -# error You need Solaris 10 or later to make use of this code. -#endif - -#include -#include -#include -#include "inspircd_config.h" -#include "globals.h" -#include "inspircd.h" -#include "socketengine.h" -#include - -class InspIRCd; - -/** A specialisation of the SocketEngine class, designed to use solaris 10 I/O completion ports - */ -class PortsEngine : public SocketEngine -{ -private: - /** These are used by epoll() to hold socket events - */ - port_event_t events[MAX_DESCRIPTORS]; -public: - /** Create a new PortsEngine - * @param Instance The creator of this object - */ - PortsEngine(InspIRCd* Instance); - /** Delete a PortsEngine - */ - virtual ~PortsEngine(); - virtual bool AddFd(EventHandler* eh); - virtual int GetMaxFds(); - virtual int GetRemainingFds(); - virtual bool DelFd(EventHandler* eh, bool force = false); - virtual int DispatchEvents(); - virtual std::string GetName(); - virtual void WantWrite(EventHandler* eh); -}; - -/** Creates a SocketEngine - */ -class SocketEngineFactory -{ -public: - /** Create a new instance of SocketEngine based on PortsEngine - */ - SocketEngine* Create(InspIRCd* Instance) { return new PortsEngine(Instance); } -}; - -#endif - diff --git a/include/socketengine_select.h b/include/socketengine_select.h deleted file mode 100644 index 2126e8ec5..000000000 --- a/include/socketengine_select.h +++ /dev/null @@ -1,69 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#ifndef __SOCKETENGINE_SELECT__ -#define __SOCKETENGINE_SELECT__ - -#include -#include -#include -#include -#include "inspircd_config.h" -#include "globals.h" -#include "inspircd.h" -#include "socketengine.h" - -class InspIRCd; - -/** A specialisation of the SocketEngine class, designed to use traditional select(). - */ -class SelectEngine : public SocketEngine -{ -private: - /** Because select() does not track an fd list for us between calls, we have one of our own - */ - std::map fds; - /** List of writeable ones (WantWrite()) - */ - bool writeable[MAX_DESCRIPTORS]; - /** The read set and write set, populated before each call to select(). - */ - fd_set wfdset, rfdset, errfdset; -public: - /** Create a new SelectEngine - * @param Instance The creator of this object - */ - SelectEngine(InspIRCd* Instance); - /** Delete a SelectEngine - */ - virtual ~SelectEngine(); - virtual bool AddFd(EventHandler* eh); - virtual int GetMaxFds(); - virtual int GetRemainingFds(); - virtual bool DelFd(EventHandler* eh, bool force = false); - virtual int DispatchEvents(); - virtual std::string GetName(); - virtual void WantWrite(EventHandler* eh); -}; - -/** Creates a SocketEngine - */ -class SocketEngineFactory -{ -public: - /** Create a new instance of SocketEngine based on SelectEngine - */ - SocketEngine* Create(InspIRCd* Instance) { return new SelectEngine(Instance); } -}; - -#endif diff --git a/include/socketengines/socketengine_epoll.h b/include/socketengines/socketengine_epoll.h new file mode 100644 index 000000000..736a109eb --- /dev/null +++ b/include/socketengines/socketengine_epoll.h @@ -0,0 +1,64 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#ifndef __SOCKETENGINE_EPOLL__ +#define __SOCKETENGINE_EPOLL__ + +#include +#include +#include +#include "inspircd_config.h" +#include "globals.h" +#include "inspircd.h" +#include "socketengine.h" +#include +#define EP_DELAY 5 + +class InspIRCd; + +/** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll(). + */ +class EPollEngine : public SocketEngine +{ +private: + /** These are used by epoll() to hold socket events + */ + struct epoll_event events[MAX_DESCRIPTORS]; +public: + /** Create a new EPollEngine + * @param Instance The creator of this object + */ + EPollEngine(InspIRCd* Instance); + /** Delete an EPollEngine + */ + virtual ~EPollEngine(); + virtual bool AddFd(EventHandler* eh); + virtual int GetMaxFds(); + virtual int GetRemainingFds(); + virtual bool DelFd(EventHandler* eh, bool force = false); + virtual int DispatchEvents(); + virtual std::string GetName(); + virtual void WantWrite(EventHandler* eh); +}; + +/** Creates a SocketEngine + */ +class SocketEngineFactory +{ +public: + /** Create a new instance of SocketEngine based on EpollEngine + */ + SocketEngine* Create(InspIRCd* Instance) { return new EPollEngine(Instance); } +}; + +#endif diff --git a/include/socketengines/socketengine_iocp.h b/include/socketengines/socketengine_iocp.h new file mode 100644 index 000000000..b024828f1 --- /dev/null +++ b/include/socketengines/socketengine_iocp.h @@ -0,0 +1,242 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#ifndef __SOCKETENGINE_IOCP__ +#define __SOCKETENGINE_IOCP__ + +#define READ_BUFFER_SIZE 600 +#define USING_IOCP 1 + +#include "inspircd_config.h" +#include "inspircd_win32wrapper.h" +#include "globals.h" +#include "inspircd.h" +#include "socketengine.h" + +/** Socket overlapped event types + */ +enum SocketIOEvent +{ + /** Read ready */ + SOCKET_IO_EVENT_READ_READY = 0, + /** Write ready */ + SOCKET_IO_EVENT_WRITE_READY = 1, + /** Accept ready */ + SOCKET_IO_EVENT_ACCEPT = 2, + /** Error occured */ + SOCKET_IO_EVENT_ERROR = 3, + /** Number of events */ + NUM_SOCKET_IO_EVENTS = 4, +}; + +/** Represents a windows overlapped IO event + */ +class Overlapped +{ + public: + /** Overlap event */ + OVERLAPPED m_overlap; + /** Type of event */ + SocketIOEvent m_event; +#ifdef WIN64 + /** Parameters */ + unsigned __int64 m_params; +#else + /** Parameters */ + unsigned long m_params; +#endif + /** Create an overlapped event + */ + Overlapped(SocketIOEvent ev, int params) : m_event(ev), m_params(params) + { + memset(&m_overlap, 0, sizeof(OVERLAPPED)); + } +}; + +/** Specific to UDP sockets with overlapped IO + */ +struct udp_overlap +{ + unsigned char udp_buffer[600]; + unsigned long udp_len; + sockaddr udp_sockaddr[2]; + unsigned long udp_sockaddr_len; +}; + +/** Specific to accepting sockets with overlapped IO + */ +struct accept_overlap +{ + int socket; + char buf[1024]; +}; + +/** Implementation of SocketEngine that implements windows IO Completion Ports + */ +class IOCPEngine : public SocketEngine +{ + /** Creates a "fake" file descriptor for use with an IOCP socket. + * This is a little slow, but it isnt called too much. We'll fix it + * in a future release. + * @return -1 if there are no free slots, and an integer if it finds one. + */ + __inline int GenerateFd(int RealFd) + { + int index_hash = RealFd % MAX_DESCRIPTORS; + if(ref[index_hash] == 0) + return index_hash; + else + { + register int i = 0; + for(; i < MAX_DESCRIPTORS; ++i) + if(ref[i] == 0) + return i; + } + return -1; + } + + /** Global I/O completion port that sockets attach to. + */ + HANDLE m_completionPort; + + /** This is kinda shitty... :/ for getting an address from a real fd. + */ + map m_binding; + +public: + /** Holds the preallocated buffer passed to WSARecvFrom + * function. Yes, I know, it's a dirty hack. + */ + udp_overlap * udp_ov; + + /** Creates an IOCP Socket Engine + * @param Instance The creator of this object + */ + IOCPEngine(InspIRCd* Instance); + + /** Deletes an IOCP socket engine and all the attached sockets + */ + ~IOCPEngine(); + + /** Adds an event handler to the completion port, and sets up initial events. + * @param eh EventHandler to add + * @return True if success, false if no room + */ + bool AddFd(EventHandler* eh); + + /** Gets the maximum number of file descriptors that this engine can handle. + * @return The number of file descriptors + */ + __inline int GetMaxFds() { return MAX_DESCRIPTORS; } + + /** Gets the number of free/remaining file descriptors under this engine. + * @return Remaining count + */ + __inline int GetRemainingFds() + { + register int count = 0; + register int i = 0; + for(; i < MAX_DESCRIPTORS; ++i) + if(ref[i] == 0) + ++count; + return count; + } + + /** Removes a file descriptor from the set, preventing it from receiving any more events + * @return True if remove was successful, false otherwise + */ + bool DelFd(EventHandler* eh, bool force = false); + + /** Called every loop to handle input/output events for all sockets under this engine + * @return The number of "changed" sockets. + */ + int DispatchEvents(); + + /** Gets the name of this socket engine as a string. + * @return string of socket engine name + */ + std::string GetName(); + + /** Queues a Write event on the specified event handler. + * @param eh EventHandler that needs data sent on + */ + void WantWrite(EventHandler* eh); + + /** Posts a completion event on the specified socket. + * @param eh EventHandler for message + * @param type Event Type + * @param param Event Parameter + * @return True if added, false if not + */ + bool PostCompletionEvent(EventHandler* eh, SocketIOEvent type, int param); + + /** Posts a read event on the specified socket + * @param eh EventHandler (socket) + */ + void PostReadEvent(EventHandler* eh); + + /** Posts an accept event on the specified socket + * @param eh EventHandler (socket) + */ + void PostAcceptEvent(EventHandler* eh); + + /** Returns the EventHandler attached to a specific fd. + * If the fd isnt in the socketengine, returns NULL. + * @param fd The event handler to look for + * @return A pointer to the event handler, or NULL + */ + EventHandler* GetRef(int fd); + + /** Returns true if a file descriptor exists in + * the socket engine's list. + * @param fd The event handler to look for + * @return True if this fd has an event handler + */ + bool HasFd(int fd); + + /** Returns the EventHandler attached to a specific fd. + * If the fd isnt in the socketengine, returns NULL. + * @param fd The event handler to look for + * @return A pointer to the event handler, or NULL + */ + EventHandler* GetIntRef(int fd); + + bool BoundsCheckFd(EventHandler* eh); + + virtual int Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen); + + virtual int RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); + + virtual int Blocking(int fd); + + virtual int NonBlocking(int fd); + + virtual int GetSockName(EventHandler* fd, sockaddr *name, socklen_t* namelen); + + virtual int Close(int fd); + + virtual int Close(EventHandler* fd); +}; + +/** Creates a SocketEngine + */ +class SocketEngineFactory +{ +public: + /** Create a new instance of SocketEngine based on IOCPEngine + */ + SocketEngine* Create(InspIRCd* Instance) { return new IOCPEngine(Instance); } +}; + +#endif + diff --git a/include/socketengines/socketengine_kqueue.h b/include/socketengines/socketengine_kqueue.h new file mode 100644 index 000000000..c34075cd2 --- /dev/null +++ b/include/socketengines/socketengine_kqueue.h @@ -0,0 +1,69 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#ifndef __SOCKETENGINE_KQUEUE__ +#define __SOCKETENGINE_KQUEUE__ + +#include +#include +#include +#include "inspircd_config.h" +#include "globals.h" +#include "inspircd.h" +#include +#include +#include +#include "socketengine.h" + +class InspIRCd; + +/** A specialisation of the SocketEngine class, designed to use FreeBSD kqueue(). + */ +class KQueueEngine : public SocketEngine +{ +private: + /** These are used by kqueue() to hold socket events + */ + struct kevent ke_list[MAX_DESCRIPTORS]; + /** This is a specialised time value used by kqueue() + */ + struct timespec ts; +public: + /** Create a new KQueueEngine + * @param Instance The creator of this object + */ + KQueueEngine(InspIRCd* Instance); + /** Delete a KQueueEngine + */ + virtual ~KQueueEngine(); + virtual bool AddFd(EventHandler* eh); + virtual int GetMaxFds(); + virtual int GetRemainingFds(); + virtual bool DelFd(EventHandler* eh, bool force = false); + virtual int DispatchEvents(); + virtual std::string GetName(); + virtual void WantWrite(EventHandler* eh); + virtual void RecoverFromFork(); +}; + +/** Creates a SocketEngine + */ +class SocketEngineFactory +{ + public: + /** Create a new instance of SocketEngine based on KQueueEngine + */ + SocketEngine* Create(InspIRCd* Instance) { return new KQueueEngine(Instance); } +}; + +#endif diff --git a/include/socketengines/socketengine_ports.h b/include/socketengines/socketengine_ports.h new file mode 100644 index 000000000..a5951a138 --- /dev/null +++ b/include/socketengines/socketengine_ports.h @@ -0,0 +1,68 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#ifndef __SOCKETENGINE_PORTS__ +#define __SOCKETENGINE_PORTS__ + +#ifndef __sun +# error You need Solaris 10 or later to make use of this code. +#endif + +#include +#include +#include +#include "inspircd_config.h" +#include "globals.h" +#include "inspircd.h" +#include "socketengine.h" +#include + +class InspIRCd; + +/** A specialisation of the SocketEngine class, designed to use solaris 10 I/O completion ports + */ +class PortsEngine : public SocketEngine +{ +private: + /** These are used by epoll() to hold socket events + */ + port_event_t events[MAX_DESCRIPTORS]; +public: + /** Create a new PortsEngine + * @param Instance The creator of this object + */ + PortsEngine(InspIRCd* Instance); + /** Delete a PortsEngine + */ + virtual ~PortsEngine(); + virtual bool AddFd(EventHandler* eh); + virtual int GetMaxFds(); + virtual int GetRemainingFds(); + virtual bool DelFd(EventHandler* eh, bool force = false); + virtual int DispatchEvents(); + virtual std::string GetName(); + virtual void WantWrite(EventHandler* eh); +}; + +/** Creates a SocketEngine + */ +class SocketEngineFactory +{ +public: + /** Create a new instance of SocketEngine based on PortsEngine + */ + SocketEngine* Create(InspIRCd* Instance) { return new PortsEngine(Instance); } +}; + +#endif + diff --git a/include/socketengines/socketengine_select.h b/include/socketengines/socketengine_select.h new file mode 100644 index 000000000..2126e8ec5 --- /dev/null +++ b/include/socketengines/socketengine_select.h @@ -0,0 +1,69 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#ifndef __SOCKETENGINE_SELECT__ +#define __SOCKETENGINE_SELECT__ + +#include +#include +#include +#include +#include "inspircd_config.h" +#include "globals.h" +#include "inspircd.h" +#include "socketengine.h" + +class InspIRCd; + +/** A specialisation of the SocketEngine class, designed to use traditional select(). + */ +class SelectEngine : public SocketEngine +{ +private: + /** Because select() does not track an fd list for us between calls, we have one of our own + */ + std::map fds; + /** List of writeable ones (WantWrite()) + */ + bool writeable[MAX_DESCRIPTORS]; + /** The read set and write set, populated before each call to select(). + */ + fd_set wfdset, rfdset, errfdset; +public: + /** Create a new SelectEngine + * @param Instance The creator of this object + */ + SelectEngine(InspIRCd* Instance); + /** Delete a SelectEngine + */ + virtual ~SelectEngine(); + virtual bool AddFd(EventHandler* eh); + virtual int GetMaxFds(); + virtual int GetRemainingFds(); + virtual bool DelFd(EventHandler* eh, bool force = false); + virtual int DispatchEvents(); + virtual std::string GetName(); + virtual void WantWrite(EventHandler* eh); +}; + +/** Creates a SocketEngine + */ +class SocketEngineFactory +{ +public: + /** Create a new instance of SocketEngine based on SelectEngine + */ + SocketEngine* Create(InspIRCd* Instance) { return new SelectEngine(Instance); } +}; + +#endif diff --git a/src/socketengine.cpp b/src/socketengine.cpp index f17d2f1c5..ce7b1c9de 100644 --- a/src/socketengine.cpp +++ b/src/socketengine.cpp @@ -14,21 +14,21 @@ /* $Core: libIRCDsocketengine */ /********* DEFAULTS **********/ -/* $ExtraSources: socketengine_select.cpp */ +/* $ExtraSources: socketengines/socketengine_select.cpp */ /* $ExtraObjects: socketengine_select.o */ /* $If: USE_KQUEUE */ -/* $ExtraSources: socketengine_kqueue.cpp */ +/* $ExtraSources: socketengines/socketengine_kqueue.cpp */ /* $ExtraObjects: socketengine_kqueue.o */ /* $EndIf */ /* $If: USE_EPOLL */ -/* $ExtraSources: socketengine_epoll.cpp */ +/* $ExtraSources: socketengines/socketengine_epoll.cpp */ /* $ExtraObjects: socketengine_epoll.o */ /* $EndIf */ /* $If: USE_PORTS */ -/* $ExtraSources: socketengine_ports.cpp */ +/* $ExtraSources: socketengines/socketengine_ports.cpp */ /* $ExtraObjects: socketengine_ports.o */ /* $EndIf */ diff --git a/src/socketengine_epoll.cpp b/src/socketengine_epoll.cpp deleted file mode 100644 index ec5362f8c..000000000 --- a/src/socketengine_epoll.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include "exitcodes.h" -#include -#include "socketengine_epoll.h" - -EPollEngine::EPollEngine(InspIRCd* Instance) : SocketEngine(Instance) -{ - EngineHandle = epoll_create(MAX_DESCRIPTORS); - - if (EngineHandle == -1) - { - ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno)); - ServerInstance->Log(SPARSE,"ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now."); - printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno)); - printf("ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.\n"); - ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE); - } - CurrentSetSize = 0; -} - -EPollEngine::~EPollEngine() -{ - this->Close(EngineHandle); -} - -bool EPollEngine::AddFd(EventHandler* eh) -{ - int fd = eh->GetFd(); - if ((fd < 0) || (fd > MAX_DESCRIPTORS)) - { - ServerInstance->Log(DEBUG,"Out of range FD"); - return false; - } - - if (GetRemainingFds() <= 1) - return false; - - if (ref[fd]) - return false; - - struct epoll_event ev; - memset(&ev,0,sizeof(struct epoll_event)); - eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT; - ev.data.fd = fd; - int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev); - if (i < 0) - { - return false; - } - - ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); - - ref[fd] = eh; - CurrentSetSize++; - return true; -} - -void EPollEngine::WantWrite(EventHandler* eh) -{ - /** Use oneshot so that the system removes the writeable - * status for us and saves us a call. - */ - struct epoll_event ev; - memset(&ev,0,sizeof(struct epoll_event)); - ev.events = EPOLLOUT; - ev.data.fd = eh->GetFd(); - epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev); -} - -bool EPollEngine::DelFd(EventHandler* eh, bool force) -{ - int fd = eh->GetFd(); - if ((fd < 0) || (fd > MAX_DESCRIPTORS)) - return false; - - struct epoll_event ev; - memset(&ev,0,sizeof(struct epoll_event)); - eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT; - ev.data.fd = fd; - int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev); - - if (i < 0 && !force) - { - ServerInstance->Log(DEBUG,"Cant remove socket: %s", strerror(errno)); - return false; - } - - ref[fd] = NULL; - CurrentSetSize--; - - ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); - return true; -} - -int EPollEngine::GetMaxFds() -{ - return MAX_DESCRIPTORS; -} - -int EPollEngine::GetRemainingFds() -{ - return MAX_DESCRIPTORS - CurrentSetSize; -} - -int EPollEngine::DispatchEvents() -{ - socklen_t codesize; - int errcode; - int i = epoll_wait(EngineHandle, events, MAX_DESCRIPTORS, 1000); - for (int j = 0; j < i; j++) - { - if (events[j].events & EPOLLHUP) - { - if (ref[events[j].data.fd]) - ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, 0); - continue; - } - if (events[j].events & EPOLLERR) - { - /* Get error number */ - if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) - errcode = errno; - if (ref[events[j].data.fd]) - ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, errcode); - continue; - } - if (events[j].events & EPOLLOUT) - { - struct epoll_event ev; - memset(&ev,0,sizeof(struct epoll_event)); - ev.events = EPOLLIN; - ev.data.fd = events[j].data.fd; - epoll_ctl(EngineHandle, EPOLL_CTL_MOD, events[j].data.fd, &ev); - if (ref[events[j].data.fd]) - ref[events[j].data.fd]->HandleEvent(EVENT_WRITE); - } - else - { - if (ref[events[j].data.fd]) - ref[events[j].data.fd]->HandleEvent(EVENT_READ); - } - } - - return i; -} - -std::string EPollEngine::GetName() -{ - return "epoll"; -} - diff --git a/src/socketengine_iocp.cpp b/src/socketengine_iocp.cpp deleted file mode 100644 index 66ae7f8ba..000000000 --- a/src/socketengine_iocp.cpp +++ /dev/null @@ -1,515 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "socketengine_iocp.h" -#include "exitcodes.h" -#include - -IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance) -{ - /* Create completion port */ - m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0); - - if (!m_completionPort) - { - ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); - ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now."); - printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); - printf("ERROR: this is a fatal error, exiting now."); - ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE); - } - - /* Null variables out. */ - CurrentSetSize = 0; - EngineHandle = 0; - memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS); -} - -IOCPEngine::~IOCPEngine() -{ - /* Clean up winsock and close completion port */ - CloseHandle(m_completionPort); - WSACleanup(); -} - -bool IOCPEngine::AddFd(EventHandler* eh) -{ - /* Does it at least look valid? */ - if (!eh) - return false; - - int* fake_fd = new int(GenerateFd(eh->GetFd())); - int is_accept = 0; - int opt_len = sizeof(int); - - /* In range? */ - if ((*fake_fd < 0) || (*fake_fd > MAX_DESCRIPTORS)) - { - delete fake_fd; - return false; - } - - /* Already an entry here */ - if (ref[*fake_fd]) - { - delete fake_fd; - return false; - } - - /* are we a listen socket? */ - getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len); - - /* set up the read event so the socket can actually receive data :P */ - eh->Extend("internal_fd", fake_fd); - - unsigned long completion_key = (ULONG_PTR)*fake_fd; - /* assign the socket to the completion port */ - if (!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0)) - return false; - - /* setup initial events */ - if(is_accept) - PostAcceptEvent(eh); - else - PostReadEvent(eh); - - /* log message */ - ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", *fake_fd, eh->GetFd(), eh); - - /* post a write event if there is data to be written */ - if(eh->Writeable()) - WantWrite(eh); - - /* we're all good =) */ - try - { - m_binding.insert( map::value_type( eh->GetFd(), eh ) ); - } - catch (...) - { - /* Ohshi-, map::insert failed :/ */ - return false; - } - - ++CurrentSetSize; - ref[*fake_fd] = eh; - - return true; -} - -bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */) -{ - if (!eh) - return false; - - int* fake_fd = NULL; - - if (!eh->GetExt("internal_fd", fake_fd)) - return false; - - int fd = eh->GetFd(); - - void* m_readEvent = NULL; - void* m_writeEvent = NULL; - void* m_acceptEvent = NULL; - - ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", *fake_fd, eh->GetFd(), eh); - - /* Cancel pending i/o operations. */ - if (CancelIo((HANDLE)fd) == FALSE) - return false; - - /* Free the buffer, and delete the event. */ - if (eh->GetExt("windows_readevent", m_readEvent)) - { - if(((Overlapped*)m_readEvent)->m_params != 0) - delete ((udp_overlap*)((Overlapped*)m_readEvent)->m_params); - - delete ((Overlapped*)m_readEvent); - eh->Shrink("windows_readevent"); - } - - if(eh->GetExt("windows_writeevent", m_writeEvent)) - { - delete ((Overlapped*)m_writeEvent); - eh->Shrink("windows_writeevent"); - } - - if(eh->GetExt("windows_acceptevent", m_acceptEvent)) - { - delete ((accept_overlap*)((Overlapped*)m_acceptEvent)->m_params); - delete ((Overlapped*)m_acceptEvent); - eh->Shrink("windows_accepevent"); - } - - /* Clear binding */ - ref[*fake_fd] = 0; - m_binding.erase(eh->GetFd()); - - delete fake_fd; - eh->Shrink("internal_fd"); - - /* decrement set size */ - --CurrentSetSize; - - /* success */ - return true; -} - -void IOCPEngine::WantWrite(EventHandler* eh) -{ - if (!eh) - return; - - void* m_writeEvent = NULL; - - int* fake_fd = NULL; - if (!eh->GetExt("internal_fd", fake_fd)) - return; - - /* Post event - write begin */ - if(!eh->GetExt("windows_writeevent", m_writeEvent)) - { - ULONG_PTR completion_key = (ULONG_PTR)*fake_fd; - Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0); - eh->Shrink("windows_writeevent"); - eh->Extend("windows_writeevent",ov); - PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap); - } -} - -bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param) -{ - if (!eh) - return false; - - int* fake_fd = NULL; - if (!eh->GetExt("internal_fd", fake_fd)) - return false; - - Overlapped * ov = new Overlapped(type, param); - ULONG_PTR completion_key = (ULONG_PTR)*fake_fd; - return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap); -} - -void IOCPEngine::PostReadEvent(EventHandler * eh) -{ - if (!eh) - return; - - Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0); - DWORD flags = 0; - DWORD r_length = 0; - WSABUF buf; - - /* by passing a null buffer pointer, we can have this working in the same way as epoll.. - * its slower, but it saves modifying all network code. - */ - buf.buf = 0; - buf.len = 0; - - /* determine socket type. */ - DWORD sock_type; - int sock_len = sizeof(DWORD); - if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1) - { - /* wtfhax? */ - PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); - delete ov; - return; - } - switch(sock_type) - { - case SOCK_DGRAM: /* UDP Socket */ - { - udp_overlap * uv = new udp_overlap; - uv->udp_sockaddr_len = sizeof(sockaddr); - buf.buf = (char*)uv->udp_buffer; - buf.len = sizeof(uv->udp_buffer); - ov->m_params = (unsigned long)uv; - if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0)) - { - int err = WSAGetLastError(); - if(err != WSA_IO_PENDING) - { - delete ov; - PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); - return; - } - } - } - break; - - case SOCK_STREAM: /* TCP Socket */ - { - if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR) - { - if(WSAGetLastError() != WSA_IO_PENDING) - { - delete ov; - PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); - return; - } - } - } - break; - - default: - { - printf("unknwon socket type: %u\n", sock_type); - return; - } - break; - } - eh->Extend("windows_readevent", ov); -} - -int IOCPEngine::DispatchEvents() -{ - DWORD len; - LPOVERLAPPED overlap; - Overlapped * ov; - EventHandler * eh; - ULONG_PTR intfd; - int ret; - unsigned long bytes_recv; - - while (GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000)) - { - if (intfd < 0 || intfd > MAX_DESCRIPTORS) - continue; - - // woot, we got an event on a socket :P - eh = ref[intfd]; - ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap); - - if (eh == 0) - continue; - - void* m_readEvent = NULL; - void* m_writeEvent = NULL; - - eh->GetExt("windows_readevent", m_readEvent); - eh->GetExt("windows_writeevent", m_writeEvent); - - switch(ov->m_event) - { - case SOCKET_IO_EVENT_WRITE_READY: - { - eh->Shrink("windows_writeevent"); - eh->HandleEvent(EVENT_WRITE, 0); - } - break; - - case SOCKET_IO_EVENT_READ_READY: - { - if(ov->m_params) - { - // if we had params, it means we are a udp socket with a udp_overlap pointer in this long. - udp_overlap * uv = (udp_overlap*)ov->m_params; - uv->udp_len = len; - this->udp_ov = uv; - eh->Shrink("windows_readevent"); - eh->HandleEvent(EVENT_READ, 0); - this->udp_ov = 0; - delete uv; - PostReadEvent(eh); - } - else - { - ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv); - eh->Shrink("windows_readevent"); - if(ret != 0 || bytes_recv == 0) - { - /* end of file */ - PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */ - } - else - { - eh->HandleEvent(EVENT_READ, 0); - PostReadEvent(eh); - } - } - } - break; - - case SOCKET_IO_EVENT_ACCEPT: - { - /* this is kinda messy.. :/ */ - eh->HandleEvent(EVENT_READ, ov->m_params); - delete ((accept_overlap*)ov->m_params); - eh->Shrink("windows_acceptevent"); - PostAcceptEvent(eh); - } - break; - - case SOCKET_IO_EVENT_ERROR: - { - eh->HandleEvent(EVENT_ERROR, ov->m_params); - } - break; - } - - delete ov; - } - - return 0; -} - -void IOCPEngine::PostAcceptEvent(EventHandler * eh) -{ - if (!eh) - return; - - int on = 1; - u_long arg = 1; - struct linger linger = { 0 }; - - int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED); - - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); - /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */ - linger.l_onoff = 1; - linger.l_linger = 1; - setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger)); - ioctlsocket(fd, FIONBIO, &arg); - - int len = sizeof(sockaddr_in) + 16; - DWORD dwBytes; - accept_overlap* ao = new accept_overlap; - memset(ao->buf, 0, 1024); - ao->socket = fd; - - Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao); - eh->Extend("windows_acceptevent", ov); - - if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE) - { - int err = WSAGetLastError(); - if(err != WSA_IO_PENDING) - { - printf("PostAcceptEvent err: %d\n", err); - } - } -} - - -std::string IOCPEngine::GetName() -{ - return "iocp"; -} - -EventHandler * IOCPEngine::GetRef(int fd) -{ - map::iterator itr = m_binding.find(fd); - return (itr == m_binding.end()) ? 0 : itr->second; -} - -bool IOCPEngine::HasFd(int fd) -{ - return (GetRef(fd) != 0); -} - -bool IOCPEngine::BoundsCheckFd(EventHandler* eh) -{ - int * internal_fd; - if (!eh || eh->GetFd() < 0) - return false; - - if(!eh->GetExt("internal_fd", internal_fd)) - return false; - - if(*internal_fd > MAX_DESCRIPTORS) - return false; - - return true; -} - -EventHandler * IOCPEngine::GetIntRef(int fd) -{ - if(fd < 0 || fd > MAX_DESCRIPTORS) - return 0; - return ref[fd]; -} - -int IOCPEngine::Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen) -{ - SOCKET s = fd->GetFd(); - - Overlapped* acceptevent = NULL; - if (!fd->GetExt("windows_acceptevent", acceptevent)) - /* Shit, no accept event on this socket! :( */ - return -1; - - Overlapped* ovl = acceptevent; - accept_overlap* ov = (accept_overlap*)ovl->m_params; - - sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10]; - sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38]; - - memcpy(addr, client_address, sizeof(sockaddr_in)); - *addrlen = sizeof(sockaddr_in); - - return ov->socket; -} - -int IOCPEngine::GetSockName(EventHandler* fd, sockaddr *name, socklen_t* namelen) -{ - Overlapped* ovl = NULL; - - if (!fd->GetExt("windows_acceptevent", ovl)) - return -1; - - accept_overlap* ov = (accept_overlap*)ovl->m_params; - - sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10]; - sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38]; - - memcpy(name, server_address, sizeof(sockaddr_in)); - *namelen = sizeof(sockaddr_in); - - return 0; -} - -int IOCPEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) -{ - udp_overlap * ov = NULL; - if (!fd->GetExt("windows_readevent", ov)) - return -1; - memcpy(buf, ov->udp_buffer, ov->udp_len); - memcpy(from, ov->udp_sockaddr, *fromlen); - return ov->udp_len; -} - -int IOCPEngine::Blocking(int fd) -{ - unsigned long opt = 0; - return ioctlsocket(fd, FIONBIO, &opt); -} - -int IOCPEngine::NonBlocking(int fd) -{ - unsigned long opt = 1; - return ioctlsocket(fd, FIONBIO, &opt); -} - -int IOCPEngine::Close(int fd) -{ - return closesocket(fd); -} - -int IOCPEngine::Close(EventHandler* fd) -{ - return this->Close(fd->GetFd()); -} - diff --git a/src/socketengine_kqueue.cpp b/src/socketengine_kqueue.cpp deleted file mode 100644 index 50f6242e2..000000000 --- a/src/socketengine_kqueue.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include "exitcodes.h" -#include -#include -#include -#include "socketengine_kqueue.h" - - -KQueueEngine::KQueueEngine(InspIRCd* Instance) : SocketEngine(Instance) -{ - this->RecoverFromFork(); -} - -void KQueueEngine::RecoverFromFork() -{ - /* - * The only bad thing about kqueue is that its fd cant survive a fork and is not inherited. - * BUM HATS. - * - */ - EngineHandle = kqueue(); - if (EngineHandle == -1) - { - ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); - ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now."); - printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); - printf("ERROR: this is a fatal error, exiting now."); - ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE); - } - CurrentSetSize = 0; -} - -KQueueEngine::~KQueueEngine() -{ - this->Close(EngineHandle); -} - -bool KQueueEngine::AddFd(EventHandler* eh) -{ - int fd = eh->GetFd(); - - if ((fd < 0) || (fd > MAX_DESCRIPTORS)) - return false; - - if (GetRemainingFds() <= 1) - return false; - - if (ref[fd]) - return false; - - struct kevent ke; - EV_SET(&ke, fd, eh->Readable() ? EVFILT_READ : EVFILT_WRITE, EV_ADD, 0, 0, NULL); - - int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); - if (i == -1) - { - return false; - } - - ref[fd] = eh; - CurrentSetSize++; - - ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); - return true; -} - -bool KQueueEngine::DelFd(EventHandler* eh, bool force) -{ - int fd = eh->GetFd(); - - if ((fd < 0) || (fd > MAX_DESCRIPTORS)) - return false; - - struct kevent ke; - EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL); - - int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); - - EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); - - int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL); - - if ((j < 0) && (i < 0) && !force) - return false; - - CurrentSetSize--; - ref[fd] = NULL; - - ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); - return true; -} - -void KQueueEngine::WantWrite(EventHandler* eh) -{ - /** When changing an item in a kqueue, there is no 'modify' call - * as in epoll. Instead, we add the item again, and this overwrites - * the original setting rather than adding it twice. See man kqueue. - */ - struct kevent ke; - EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL); - kevent(EngineHandle, &ke, 1, 0, 0, NULL); -} - -int KQueueEngine::GetMaxFds() -{ - return MAX_DESCRIPTORS; -} - -int KQueueEngine::GetRemainingFds() -{ - return MAX_DESCRIPTORS - CurrentSetSize; -} - -int KQueueEngine::DispatchEvents() -{ - ts.tv_nsec = 0; - ts.tv_sec = 1; - int i = kevent(EngineHandle, NULL, 0, &ke_list[0], MAX_DESCRIPTORS, &ts); - for (int j = 0; j < i; j++) - { - if (ke_list[j].flags & EV_EOF) - { - /* We love you kqueue, oh yes we do *sings*! - * kqueue gives us the error number directly in the EOF state! - * Unlike smelly epoll and select, where we have to getsockopt - * to get the error, this saves us time and cpu cycles. Go BSD! - */ - if (ref[ke_list[j].ident]) - ref[ke_list[j].ident]->HandleEvent(EVENT_ERROR, ke_list[j].fflags); - continue; - } - if (ke_list[j].flags & EVFILT_WRITE) - { - /* This looks wrong but its right. As above, theres no modify - * call in kqueue. See the manpage. - */ - struct kevent ke; - EV_SET(&ke, ke_list[j].ident, EVFILT_READ, EV_ADD, 0, 0, NULL); - kevent(EngineHandle, &ke, 1, 0, 0, NULL); - if (ref[ke_list[j].ident]) - ref[ke_list[j].ident]->HandleEvent(EVENT_WRITE); - } - else - { - if (ref[ke_list[j].ident]) - ref[ke_list[j].ident]->HandleEvent(EVENT_READ); - } - } - - return i; -} - -std::string KQueueEngine::GetName() -{ - return "kqueue"; -} diff --git a/src/socketengine_ports.cpp b/src/socketengine_ports.cpp deleted file mode 100644 index 352b73062..000000000 --- a/src/socketengine_ports.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include "exitcodes.h" -#include -#include "socketengine_ports.h" - -PortsEngine::PortsEngine(InspIRCd* Instance) : SocketEngine(Instance) -{ - EngineHandle = port_create(); - - if (EngineHandle == -1) - { - ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno)); - ServerInstance->Log(SPARSE,"ERROR: This is a fatal error, exiting now."); - printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno)); - printf("ERROR: This is a fatal error, exiting now.\n"); - ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE); - } - CurrentSetSize = 0; -} - -PortsEngine::~PortsEngine() -{ - this->Close(EngineHandle); -} - -bool PortsEngine::AddFd(EventHandler* eh) -{ - int fd = eh->GetFd(); - if ((fd < 0) || (fd > MAX_DESCRIPTORS)) - return false; - - if (GetRemainingFds() <= 1) - return false; - - if (ref[fd]) - return false; - - ref[fd] = eh; - port_associate(EngineHandle, PORT_SOURCE_FD, fd, eh->Readable() ? POLLRDNORM : POLLWRNORM, eh); - - ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); - CurrentSetSize++; - return true; -} - -void PortsEngine::WantWrite(EventHandler* eh) -{ - port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), POLLWRNORM, eh); -} - -bool PortsEngine::DelFd(EventHandler* eh, bool force) -{ - int fd = eh->GetFd(); - if ((fd < 0) || (fd > MAX_DESCRIPTORS)) - return false; - - port_dissociate(EngineHandle, PORT_SOURCE_FD, fd); - - CurrentSetSize--; - ref[fd] = NULL; - - ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); - return true; -} - -int PortsEngine::GetMaxFds() -{ - return MAX_DESCRIPTORS; -} - -int PortsEngine::GetRemainingFds() -{ - return MAX_DESCRIPTORS - CurrentSetSize; -} - -int PortsEngine::DispatchEvents() -{ - struct timespec poll_time; - - poll_time.tv_sec = 1; - poll_time.tv_nsec = 0; - - unsigned int nget = 1; // used to denote a retrieve request. - int i = port_getn(EngineHandle, this->events, MAX_DESCRIPTORS, &nget, &poll_time); - - // first handle an error condition - if (i == -1) - return i; - - for (i = 0; i < nget; i++) - { - switch (this->events[i].portev_source) - { - case PORT_SOURCE_FD: - { - int fd = this->events[i].portev_object; - if (ref[fd]) - { - // reinsert port for next time around - port_associate(EngineHandle, PORT_SOURCE_FD, fd, POLLRDNORM, ref[fd]); - ref[fd]->HandleEvent((this->events[i].portev_events & POLLRDNORM) ? EVENT_READ : EVENT_WRITE); - } - } - default: - break; - } - } - - return i; -} - -std::string PortsEngine::GetName() -{ - return "ports"; -} - diff --git a/src/socketengine_select.cpp b/src/socketengine_select.cpp deleted file mode 100644 index 7ba386812..000000000 --- a/src/socketengine_select.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#include "socketengine_select.h" - - -SelectEngine::SelectEngine(InspIRCd* Instance) : SocketEngine(Instance) -{ - EngineHandle = 0; - CurrentSetSize = 0; - memset(writeable, 0, sizeof(writeable)); -} - -SelectEngine::~SelectEngine() -{ -} - -bool SelectEngine::AddFd(EventHandler* eh) -{ - int fd = eh->GetFd(); - if ((fd < 0) || (fd > MAX_DESCRIPTORS)) - return false; - - if (GetRemainingFds() <= 1) - return false; - - if (ref[fd]) - return false; - - fds[fd] = fd; - ref[fd] = eh; - CurrentSetSize++; - - ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); - return true; -} - -void SelectEngine::WantWrite(EventHandler* eh) -{ - writeable[eh->GetFd()] = true; -} - -bool SelectEngine::DelFd(EventHandler* eh, bool force) -{ - int fd = eh->GetFd(); - - if ((fd < 0) || (fd > MAX_DESCRIPTORS)) - return false; - - std::map::iterator t = fds.find(fd); - if (t != fds.end()) - fds.erase(t); - - CurrentSetSize--; - ref[fd] = NULL; - fds[fd] = 0; - - ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); - return true; -} - -int SelectEngine::GetMaxFds() -{ - return FD_SETSIZE; -} - -int SelectEngine::GetRemainingFds() -{ - return FD_SETSIZE - CurrentSetSize; -} - -int SelectEngine::DispatchEvents() -{ - int result = 0; - timeval tval; - int sresult = 0; - EventHandler* ev[MAX_DESCRIPTORS]; - socklen_t codesize; - int errcode; - - FD_ZERO(&wfdset); - FD_ZERO(&rfdset); - FD_ZERO(&errfdset); - - for (std::map::iterator a = fds.begin(); a != fds.end(); a++) - { - if (ref[a->second]->Readable()) - FD_SET (a->second, &rfdset); - else - FD_SET (a->second, &wfdset); - if (writeable[a->second]) - FD_SET (a->second, &wfdset); - - FD_SET (a->second, &errfdset); - } - tval.tv_sec = 1; - tval.tv_usec = 0; - sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval); - if (sresult > 0) - { - for (std::map::iterator a = fds.begin(); a != fds.end(); a++) - { - if ((FD_ISSET (a->second, &rfdset)) || (FD_ISSET (a->second, &wfdset)) || FD_ISSET (a->second, &errfdset)) - { - ev[result++] = ref[a->second]; - } - } - } - - /** An event handler may remove its own descriptor from the list, therefore it is not - * safe to directly iterate over the list and dispatch events there with STL iterators. - * Thats a shame because it makes this code slower and more resource intensive, but maybe - * the user should stop using select(), as select() smells anyway. - */ - for (int i = 0; i < result; i++) - { - if (ev[i]) - { - if (FD_ISSET (ev[i]->GetFd(), &errfdset)) - { - if (ev[i]) - { - if (getsockopt(ev[i]->GetFd(), SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) - errcode = errno; - - ev[i]->HandleEvent(EVENT_ERROR, errcode); - } - continue; - } - if (ev[i]) - { - if (writeable[ev[i]->GetFd()]) - { - writeable[ev[i]->GetFd()] = false; - if (ev[i]) - ev[i]->HandleEvent(EVENT_WRITE); - } - else - { - if (ev[i]) - ev[i]->HandleEvent(ev[i]->Readable() ? EVENT_READ : EVENT_WRITE); - } - } - } - } - - return result; -} - -std::string SelectEngine::GetName() -{ - return "select"; -} diff --git a/src/socketengines/socketengine_epoll.cpp b/src/socketengines/socketengine_epoll.cpp new file mode 100644 index 000000000..9da569b8f --- /dev/null +++ b/src/socketengines/socketengine_epoll.cpp @@ -0,0 +1,164 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#include "inspircd.h" +#include "exitcodes.h" +#include +#include "socketengines/socketengine_epoll.h" + +EPollEngine::EPollEngine(InspIRCd* Instance) : SocketEngine(Instance) +{ + EngineHandle = epoll_create(MAX_DESCRIPTORS); + + if (EngineHandle == -1) + { + ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno)); + ServerInstance->Log(SPARSE,"ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now."); + printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno)); + printf("ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.\n"); + ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE); + } + CurrentSetSize = 0; +} + +EPollEngine::~EPollEngine() +{ + this->Close(EngineHandle); +} + +bool EPollEngine::AddFd(EventHandler* eh) +{ + int fd = eh->GetFd(); + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + { + ServerInstance->Log(DEBUG,"Out of range FD"); + return false; + } + + if (GetRemainingFds() <= 1) + return false; + + if (ref[fd]) + return false; + + struct epoll_event ev; + memset(&ev,0,sizeof(struct epoll_event)); + eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT; + ev.data.fd = fd; + int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev); + if (i < 0) + { + return false; + } + + ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); + + ref[fd] = eh; + CurrentSetSize++; + return true; +} + +void EPollEngine::WantWrite(EventHandler* eh) +{ + /** Use oneshot so that the system removes the writeable + * status for us and saves us a call. + */ + struct epoll_event ev; + memset(&ev,0,sizeof(struct epoll_event)); + ev.events = EPOLLOUT; + ev.data.fd = eh->GetFd(); + epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev); +} + +bool EPollEngine::DelFd(EventHandler* eh, bool force) +{ + int fd = eh->GetFd(); + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + struct epoll_event ev; + memset(&ev,0,sizeof(struct epoll_event)); + eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT; + ev.data.fd = fd; + int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev); + + if (i < 0 && !force) + { + ServerInstance->Log(DEBUG,"Cant remove socket: %s", strerror(errno)); + return false; + } + + ref[fd] = NULL; + CurrentSetSize--; + + ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); + return true; +} + +int EPollEngine::GetMaxFds() +{ + return MAX_DESCRIPTORS; +} + +int EPollEngine::GetRemainingFds() +{ + return MAX_DESCRIPTORS - CurrentSetSize; +} + +int EPollEngine::DispatchEvents() +{ + socklen_t codesize; + int errcode; + int i = epoll_wait(EngineHandle, events, MAX_DESCRIPTORS, 1000); + for (int j = 0; j < i; j++) + { + if (events[j].events & EPOLLHUP) + { + if (ref[events[j].data.fd]) + ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, 0); + continue; + } + if (events[j].events & EPOLLERR) + { + /* Get error number */ + if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) + errcode = errno; + if (ref[events[j].data.fd]) + ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, errcode); + continue; + } + if (events[j].events & EPOLLOUT) + { + struct epoll_event ev; + memset(&ev,0,sizeof(struct epoll_event)); + ev.events = EPOLLIN; + ev.data.fd = events[j].data.fd; + epoll_ctl(EngineHandle, EPOLL_CTL_MOD, events[j].data.fd, &ev); + if (ref[events[j].data.fd]) + ref[events[j].data.fd]->HandleEvent(EVENT_WRITE); + } + else + { + if (ref[events[j].data.fd]) + ref[events[j].data.fd]->HandleEvent(EVENT_READ); + } + } + + return i; +} + +std::string EPollEngine::GetName() +{ + return "epoll"; +} + diff --git a/src/socketengines/socketengine_iocp.cpp b/src/socketengines/socketengine_iocp.cpp new file mode 100644 index 000000000..84371ca3a --- /dev/null +++ b/src/socketengines/socketengine_iocp.cpp @@ -0,0 +1,515 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#include "socketengines/socketengine_iocp.h" +#include "exitcodes.h" +#include + +IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance) +{ + /* Create completion port */ + m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0); + + if (!m_completionPort) + { + ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); + ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now."); + printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); + printf("ERROR: this is a fatal error, exiting now."); + ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE); + } + + /* Null variables out. */ + CurrentSetSize = 0; + EngineHandle = 0; + memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS); +} + +IOCPEngine::~IOCPEngine() +{ + /* Clean up winsock and close completion port */ + CloseHandle(m_completionPort); + WSACleanup(); +} + +bool IOCPEngine::AddFd(EventHandler* eh) +{ + /* Does it at least look valid? */ + if (!eh) + return false; + + int* fake_fd = new int(GenerateFd(eh->GetFd())); + int is_accept = 0; + int opt_len = sizeof(int); + + /* In range? */ + if ((*fake_fd < 0) || (*fake_fd > MAX_DESCRIPTORS)) + { + delete fake_fd; + return false; + } + + /* Already an entry here */ + if (ref[*fake_fd]) + { + delete fake_fd; + return false; + } + + /* are we a listen socket? */ + getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len); + + /* set up the read event so the socket can actually receive data :P */ + eh->Extend("internal_fd", fake_fd); + + unsigned long completion_key = (ULONG_PTR)*fake_fd; + /* assign the socket to the completion port */ + if (!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0)) + return false; + + /* setup initial events */ + if(is_accept) + PostAcceptEvent(eh); + else + PostReadEvent(eh); + + /* log message */ + ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", *fake_fd, eh->GetFd(), eh); + + /* post a write event if there is data to be written */ + if(eh->Writeable()) + WantWrite(eh); + + /* we're all good =) */ + try + { + m_binding.insert( map::value_type( eh->GetFd(), eh ) ); + } + catch (...) + { + /* Ohshi-, map::insert failed :/ */ + return false; + } + + ++CurrentSetSize; + ref[*fake_fd] = eh; + + return true; +} + +bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */) +{ + if (!eh) + return false; + + int* fake_fd = NULL; + + if (!eh->GetExt("internal_fd", fake_fd)) + return false; + + int fd = eh->GetFd(); + + void* m_readEvent = NULL; + void* m_writeEvent = NULL; + void* m_acceptEvent = NULL; + + ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", *fake_fd, eh->GetFd(), eh); + + /* Cancel pending i/o operations. */ + if (CancelIo((HANDLE)fd) == FALSE) + return false; + + /* Free the buffer, and delete the event. */ + if (eh->GetExt("windows_readevent", m_readEvent)) + { + if(((Overlapped*)m_readEvent)->m_params != 0) + delete ((udp_overlap*)((Overlapped*)m_readEvent)->m_params); + + delete ((Overlapped*)m_readEvent); + eh->Shrink("windows_readevent"); + } + + if(eh->GetExt("windows_writeevent", m_writeEvent)) + { + delete ((Overlapped*)m_writeEvent); + eh->Shrink("windows_writeevent"); + } + + if(eh->GetExt("windows_acceptevent", m_acceptEvent)) + { + delete ((accept_overlap*)((Overlapped*)m_acceptEvent)->m_params); + delete ((Overlapped*)m_acceptEvent); + eh->Shrink("windows_accepevent"); + } + + /* Clear binding */ + ref[*fake_fd] = 0; + m_binding.erase(eh->GetFd()); + + delete fake_fd; + eh->Shrink("internal_fd"); + + /* decrement set size */ + --CurrentSetSize; + + /* success */ + return true; +} + +void IOCPEngine::WantWrite(EventHandler* eh) +{ + if (!eh) + return; + + void* m_writeEvent = NULL; + + int* fake_fd = NULL; + if (!eh->GetExt("internal_fd", fake_fd)) + return; + + /* Post event - write begin */ + if(!eh->GetExt("windows_writeevent", m_writeEvent)) + { + ULONG_PTR completion_key = (ULONG_PTR)*fake_fd; + Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0); + eh->Shrink("windows_writeevent"); + eh->Extend("windows_writeevent",ov); + PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap); + } +} + +bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param) +{ + if (!eh) + return false; + + int* fake_fd = NULL; + if (!eh->GetExt("internal_fd", fake_fd)) + return false; + + Overlapped * ov = new Overlapped(type, param); + ULONG_PTR completion_key = (ULONG_PTR)*fake_fd; + return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap); +} + +void IOCPEngine::PostReadEvent(EventHandler * eh) +{ + if (!eh) + return; + + Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0); + DWORD flags = 0; + DWORD r_length = 0; + WSABUF buf; + + /* by passing a null buffer pointer, we can have this working in the same way as epoll.. + * its slower, but it saves modifying all network code. + */ + buf.buf = 0; + buf.len = 0; + + /* determine socket type. */ + DWORD sock_type; + int sock_len = sizeof(DWORD); + if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1) + { + /* wtfhax? */ + PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); + delete ov; + return; + } + switch(sock_type) + { + case SOCK_DGRAM: /* UDP Socket */ + { + udp_overlap * uv = new udp_overlap; + uv->udp_sockaddr_len = sizeof(sockaddr); + buf.buf = (char*)uv->udp_buffer; + buf.len = sizeof(uv->udp_buffer); + ov->m_params = (unsigned long)uv; + if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0)) + { + int err = WSAGetLastError(); + if(err != WSA_IO_PENDING) + { + delete ov; + PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); + return; + } + } + } + break; + + case SOCK_STREAM: /* TCP Socket */ + { + if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR) + { + if(WSAGetLastError() != WSA_IO_PENDING) + { + delete ov; + PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); + return; + } + } + } + break; + + default: + { + printf("unknwon socket type: %u\n", sock_type); + return; + } + break; + } + eh->Extend("windows_readevent", ov); +} + +int IOCPEngine::DispatchEvents() +{ + DWORD len; + LPOVERLAPPED overlap; + Overlapped * ov; + EventHandler * eh; + ULONG_PTR intfd; + int ret; + unsigned long bytes_recv; + + while (GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000)) + { + if (intfd < 0 || intfd > MAX_DESCRIPTORS) + continue; + + // woot, we got an event on a socket :P + eh = ref[intfd]; + ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap); + + if (eh == 0) + continue; + + void* m_readEvent = NULL; + void* m_writeEvent = NULL; + + eh->GetExt("windows_readevent", m_readEvent); + eh->GetExt("windows_writeevent", m_writeEvent); + + switch(ov->m_event) + { + case SOCKET_IO_EVENT_WRITE_READY: + { + eh->Shrink("windows_writeevent"); + eh->HandleEvent(EVENT_WRITE, 0); + } + break; + + case SOCKET_IO_EVENT_READ_READY: + { + if(ov->m_params) + { + // if we had params, it means we are a udp socket with a udp_overlap pointer in this long. + udp_overlap * uv = (udp_overlap*)ov->m_params; + uv->udp_len = len; + this->udp_ov = uv; + eh->Shrink("windows_readevent"); + eh->HandleEvent(EVENT_READ, 0); + this->udp_ov = 0; + delete uv; + PostReadEvent(eh); + } + else + { + ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv); + eh->Shrink("windows_readevent"); + if(ret != 0 || bytes_recv == 0) + { + /* end of file */ + PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */ + } + else + { + eh->HandleEvent(EVENT_READ, 0); + PostReadEvent(eh); + } + } + } + break; + + case SOCKET_IO_EVENT_ACCEPT: + { + /* this is kinda messy.. :/ */ + eh->HandleEvent(EVENT_READ, ov->m_params); + delete ((accept_overlap*)ov->m_params); + eh->Shrink("windows_acceptevent"); + PostAcceptEvent(eh); + } + break; + + case SOCKET_IO_EVENT_ERROR: + { + eh->HandleEvent(EVENT_ERROR, ov->m_params); + } + break; + } + + delete ov; + } + + return 0; +} + +void IOCPEngine::PostAcceptEvent(EventHandler * eh) +{ + if (!eh) + return; + + int on = 1; + u_long arg = 1; + struct linger linger = { 0 }; + + int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED); + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); + /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */ + linger.l_onoff = 1; + linger.l_linger = 1; + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger)); + ioctlsocket(fd, FIONBIO, &arg); + + int len = sizeof(sockaddr_in) + 16; + DWORD dwBytes; + accept_overlap* ao = new accept_overlap; + memset(ao->buf, 0, 1024); + ao->socket = fd; + + Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao); + eh->Extend("windows_acceptevent", ov); + + if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE) + { + int err = WSAGetLastError(); + if(err != WSA_IO_PENDING) + { + printf("PostAcceptEvent err: %d\n", err); + } + } +} + + +std::string IOCPEngine::GetName() +{ + return "iocp"; +} + +EventHandler * IOCPEngine::GetRef(int fd) +{ + map::iterator itr = m_binding.find(fd); + return (itr == m_binding.end()) ? 0 : itr->second; +} + +bool IOCPEngine::HasFd(int fd) +{ + return (GetRef(fd) != 0); +} + +bool IOCPEngine::BoundsCheckFd(EventHandler* eh) +{ + int * internal_fd; + if (!eh || eh->GetFd() < 0) + return false; + + if(!eh->GetExt("internal_fd", internal_fd)) + return false; + + if(*internal_fd > MAX_DESCRIPTORS) + return false; + + return true; +} + +EventHandler * IOCPEngine::GetIntRef(int fd) +{ + if(fd < 0 || fd > MAX_DESCRIPTORS) + return 0; + return ref[fd]; +} + +int IOCPEngine::Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen) +{ + SOCKET s = fd->GetFd(); + + Overlapped* acceptevent = NULL; + if (!fd->GetExt("windows_acceptevent", acceptevent)) + /* Shit, no accept event on this socket! :( */ + return -1; + + Overlapped* ovl = acceptevent; + accept_overlap* ov = (accept_overlap*)ovl->m_params; + + sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10]; + sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38]; + + memcpy(addr, client_address, sizeof(sockaddr_in)); + *addrlen = sizeof(sockaddr_in); + + return ov->socket; +} + +int IOCPEngine::GetSockName(EventHandler* fd, sockaddr *name, socklen_t* namelen) +{ + Overlapped* ovl = NULL; + + if (!fd->GetExt("windows_acceptevent", ovl)) + return -1; + + accept_overlap* ov = (accept_overlap*)ovl->m_params; + + sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10]; + sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38]; + + memcpy(name, server_address, sizeof(sockaddr_in)); + *namelen = sizeof(sockaddr_in); + + return 0; +} + +int IOCPEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) +{ + udp_overlap * ov = NULL; + if (!fd->GetExt("windows_readevent", ov)) + return -1; + memcpy(buf, ov->udp_buffer, ov->udp_len); + memcpy(from, ov->udp_sockaddr, *fromlen); + return ov->udp_len; +} + +int IOCPEngine::Blocking(int fd) +{ + unsigned long opt = 0; + return ioctlsocket(fd, FIONBIO, &opt); +} + +int IOCPEngine::NonBlocking(int fd) +{ + unsigned long opt = 1; + return ioctlsocket(fd, FIONBIO, &opt); +} + +int IOCPEngine::Close(int fd) +{ + return closesocket(fd); +} + +int IOCPEngine::Close(EventHandler* fd) +{ + return this->Close(fd->GetFd()); +} + diff --git a/src/socketengines/socketengine_kqueue.cpp b/src/socketengines/socketengine_kqueue.cpp new file mode 100644 index 000000000..18a2261ef --- /dev/null +++ b/src/socketengines/socketengine_kqueue.cpp @@ -0,0 +1,169 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#include "inspircd.h" +#include "exitcodes.h" +#include +#include +#include +#include "socketengines/socketengine_kqueue.h" + + +KQueueEngine::KQueueEngine(InspIRCd* Instance) : SocketEngine(Instance) +{ + this->RecoverFromFork(); +} + +void KQueueEngine::RecoverFromFork() +{ + /* + * The only bad thing about kqueue is that its fd cant survive a fork and is not inherited. + * BUM HATS. + * + */ + EngineHandle = kqueue(); + if (EngineHandle == -1) + { + ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); + ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now."); + printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); + printf("ERROR: this is a fatal error, exiting now."); + ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE); + } + CurrentSetSize = 0; +} + +KQueueEngine::~KQueueEngine() +{ + this->Close(EngineHandle); +} + +bool KQueueEngine::AddFd(EventHandler* eh) +{ + int fd = eh->GetFd(); + + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + if (GetRemainingFds() <= 1) + return false; + + if (ref[fd]) + return false; + + struct kevent ke; + EV_SET(&ke, fd, eh->Readable() ? EVFILT_READ : EVFILT_WRITE, EV_ADD, 0, 0, NULL); + + int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); + if (i == -1) + { + return false; + } + + ref[fd] = eh; + CurrentSetSize++; + + ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); + return true; +} + +bool KQueueEngine::DelFd(EventHandler* eh, bool force) +{ + int fd = eh->GetFd(); + + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + struct kevent ke; + EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL); + + int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); + + EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + + int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL); + + if ((j < 0) && (i < 0) && !force) + return false; + + CurrentSetSize--; + ref[fd] = NULL; + + ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); + return true; +} + +void KQueueEngine::WantWrite(EventHandler* eh) +{ + /** When changing an item in a kqueue, there is no 'modify' call + * as in epoll. Instead, we add the item again, and this overwrites + * the original setting rather than adding it twice. See man kqueue. + */ + struct kevent ke; + EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL); + kevent(EngineHandle, &ke, 1, 0, 0, NULL); +} + +int KQueueEngine::GetMaxFds() +{ + return MAX_DESCRIPTORS; +} + +int KQueueEngine::GetRemainingFds() +{ + return MAX_DESCRIPTORS - CurrentSetSize; +} + +int KQueueEngine::DispatchEvents() +{ + ts.tv_nsec = 0; + ts.tv_sec = 1; + int i = kevent(EngineHandle, NULL, 0, &ke_list[0], MAX_DESCRIPTORS, &ts); + for (int j = 0; j < i; j++) + { + if (ke_list[j].flags & EV_EOF) + { + /* We love you kqueue, oh yes we do *sings*! + * kqueue gives us the error number directly in the EOF state! + * Unlike smelly epoll and select, where we have to getsockopt + * to get the error, this saves us time and cpu cycles. Go BSD! + */ + if (ref[ke_list[j].ident]) + ref[ke_list[j].ident]->HandleEvent(EVENT_ERROR, ke_list[j].fflags); + continue; + } + if (ke_list[j].flags & EVFILT_WRITE) + { + /* This looks wrong but its right. As above, theres no modify + * call in kqueue. See the manpage. + */ + struct kevent ke; + EV_SET(&ke, ke_list[j].ident, EVFILT_READ, EV_ADD, 0, 0, NULL); + kevent(EngineHandle, &ke, 1, 0, 0, NULL); + if (ref[ke_list[j].ident]) + ref[ke_list[j].ident]->HandleEvent(EVENT_WRITE); + } + else + { + if (ref[ke_list[j].ident]) + ref[ke_list[j].ident]->HandleEvent(EVENT_READ); + } + } + + return i; +} + +std::string KQueueEngine::GetName() +{ + return "kqueue"; +} diff --git a/src/socketengines/socketengine_ports.cpp b/src/socketengines/socketengine_ports.cpp new file mode 100644 index 000000000..ecaec5070 --- /dev/null +++ b/src/socketengines/socketengine_ports.cpp @@ -0,0 +1,129 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#include "inspircd.h" +#include "exitcodes.h" +#include +#include "socketengines/socketengine_ports.h" + +PortsEngine::PortsEngine(InspIRCd* Instance) : SocketEngine(Instance) +{ + EngineHandle = port_create(); + + if (EngineHandle == -1) + { + ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno)); + ServerInstance->Log(SPARSE,"ERROR: This is a fatal error, exiting now."); + printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno)); + printf("ERROR: This is a fatal error, exiting now.\n"); + ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE); + } + CurrentSetSize = 0; +} + +PortsEngine::~PortsEngine() +{ + this->Close(EngineHandle); +} + +bool PortsEngine::AddFd(EventHandler* eh) +{ + int fd = eh->GetFd(); + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + if (GetRemainingFds() <= 1) + return false; + + if (ref[fd]) + return false; + + ref[fd] = eh; + port_associate(EngineHandle, PORT_SOURCE_FD, fd, eh->Readable() ? POLLRDNORM : POLLWRNORM, eh); + + ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); + CurrentSetSize++; + return true; +} + +void PortsEngine::WantWrite(EventHandler* eh) +{ + port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), POLLWRNORM, eh); +} + +bool PortsEngine::DelFd(EventHandler* eh, bool force) +{ + int fd = eh->GetFd(); + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + port_dissociate(EngineHandle, PORT_SOURCE_FD, fd); + + CurrentSetSize--; + ref[fd] = NULL; + + ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); + return true; +} + +int PortsEngine::GetMaxFds() +{ + return MAX_DESCRIPTORS; +} + +int PortsEngine::GetRemainingFds() +{ + return MAX_DESCRIPTORS - CurrentSetSize; +} + +int PortsEngine::DispatchEvents() +{ + struct timespec poll_time; + + poll_time.tv_sec = 1; + poll_time.tv_nsec = 0; + + unsigned int nget = 1; // used to denote a retrieve request. + int i = port_getn(EngineHandle, this->events, MAX_DESCRIPTORS, &nget, &poll_time); + + // first handle an error condition + if (i == -1) + return i; + + for (i = 0; i < nget; i++) + { + switch (this->events[i].portev_source) + { + case PORT_SOURCE_FD: + { + int fd = this->events[i].portev_object; + if (ref[fd]) + { + // reinsert port for next time around + port_associate(EngineHandle, PORT_SOURCE_FD, fd, POLLRDNORM, ref[fd]); + ref[fd]->HandleEvent((this->events[i].portev_events & POLLRDNORM) ? EVENT_READ : EVENT_WRITE); + } + } + default: + break; + } + } + + return i; +} + +std::string PortsEngine::GetName() +{ + return "ports"; +} + diff --git a/src/socketengines/socketengine_select.cpp b/src/socketengines/socketengine_select.cpp new file mode 100644 index 000000000..1fe138f14 --- /dev/null +++ b/src/socketengines/socketengine_select.cpp @@ -0,0 +1,165 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#include "inspircd.h" +#include +#include "socketengines/socketengine_select.h" + + +SelectEngine::SelectEngine(InspIRCd* Instance) : SocketEngine(Instance) +{ + EngineHandle = 0; + CurrentSetSize = 0; + memset(writeable, 0, sizeof(writeable)); +} + +SelectEngine::~SelectEngine() +{ +} + +bool SelectEngine::AddFd(EventHandler* eh) +{ + int fd = eh->GetFd(); + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + if (GetRemainingFds() <= 1) + return false; + + if (ref[fd]) + return false; + + fds[fd] = fd; + ref[fd] = eh; + CurrentSetSize++; + + ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); + return true; +} + +void SelectEngine::WantWrite(EventHandler* eh) +{ + writeable[eh->GetFd()] = true; +} + +bool SelectEngine::DelFd(EventHandler* eh, bool force) +{ + int fd = eh->GetFd(); + + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + std::map::iterator t = fds.find(fd); + if (t != fds.end()) + fds.erase(t); + + CurrentSetSize--; + ref[fd] = NULL; + fds[fd] = 0; + + ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); + return true; +} + +int SelectEngine::GetMaxFds() +{ + return FD_SETSIZE; +} + +int SelectEngine::GetRemainingFds() +{ + return FD_SETSIZE - CurrentSetSize; +} + +int SelectEngine::DispatchEvents() +{ + int result = 0; + timeval tval; + int sresult = 0; + EventHandler* ev[MAX_DESCRIPTORS]; + socklen_t codesize; + int errcode; + + FD_ZERO(&wfdset); + FD_ZERO(&rfdset); + FD_ZERO(&errfdset); + + for (std::map::iterator a = fds.begin(); a != fds.end(); a++) + { + if (ref[a->second]->Readable()) + FD_SET (a->second, &rfdset); + else + FD_SET (a->second, &wfdset); + if (writeable[a->second]) + FD_SET (a->second, &wfdset); + + FD_SET (a->second, &errfdset); + } + tval.tv_sec = 1; + tval.tv_usec = 0; + sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval); + if (sresult > 0) + { + for (std::map::iterator a = fds.begin(); a != fds.end(); a++) + { + if ((FD_ISSET (a->second, &rfdset)) || (FD_ISSET (a->second, &wfdset)) || FD_ISSET (a->second, &errfdset)) + { + ev[result++] = ref[a->second]; + } + } + } + + /** An event handler may remove its own descriptor from the list, therefore it is not + * safe to directly iterate over the list and dispatch events there with STL iterators. + * Thats a shame because it makes this code slower and more resource intensive, but maybe + * the user should stop using select(), as select() smells anyway. + */ + for (int i = 0; i < result; i++) + { + if (ev[i]) + { + if (FD_ISSET (ev[i]->GetFd(), &errfdset)) + { + if (ev[i]) + { + if (getsockopt(ev[i]->GetFd(), SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) + errcode = errno; + + ev[i]->HandleEvent(EVENT_ERROR, errcode); + } + continue; + } + if (ev[i]) + { + if (writeable[ev[i]->GetFd()]) + { + writeable[ev[i]->GetFd()] = false; + if (ev[i]) + ev[i]->HandleEvent(EVENT_WRITE); + } + else + { + if (ev[i]) + ev[i]->HandleEvent(ev[i]->Readable() ? EVENT_READ : EVENT_WRITE); + } + } + } + } + + return result; +} + +std::string SelectEngine::GetName() +{ + return "select"; +} -- cgit v1.2.3