diff options
Diffstat (limited to 'src/socketengines')
-rw-r--r-- | src/socketengines/socketengine_epoll.cpp | 188 | ||||
-rw-r--r-- | src/socketengines/socketengine_kqueue.cpp | 224 | ||||
-rw-r--r-- | src/socketengines/socketengine_poll.cpp | 250 | ||||
-rw-r--r-- | src/socketengines/socketengine_ports.cpp | 220 | ||||
-rw-r--r-- | src/socketengines/socketengine_select.cpp | 150 |
5 files changed, 299 insertions, 733 deletions
diff --git a/src/socketengines/socketengine_epoll.cpp b/src/socketengines/socketengine_epoll.cpp index d5f017347..60b365ee1 100644 --- a/src/socketengines/socketengine_epoll.cpp +++ b/src/socketengines/socketengine_epoll.cpp @@ -18,79 +18,40 @@ */ -#include <vector> -#include <string> -#include <map> #include "inspircd.h" -#include "exitcodes.h" -#include "socketengine.h" + #include <sys/epoll.h> #include <sys/resource.h> -#include <iostream> -#define EP_DELAY 5 /** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll(). */ -class EPollEngine : public SocketEngine +namespace { -private: - /** These are used by epoll() to hold socket events - */ - struct epoll_event* events; int EngineHandle; -public: - /** Create a new EPollEngine - */ - EPollEngine(); - /** Delete an EPollEngine + + /** These are used by epoll() to hold socket events */ - virtual ~EPollEngine(); - virtual bool AddFd(EventHandler* eh, int event_mask); - virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask); - virtual void DelFd(EventHandler* eh); - virtual int DispatchEvents(); - virtual std::string GetName(); -}; + std::vector<struct epoll_event> events(1); +} -EPollEngine::EPollEngine() +void SocketEngine::Init() { - CurrentSetSize = 0; - - struct rlimit limit; - if (!getrlimit(RLIMIT_NOFILE, &limit)) - { - MAX_DESCRIPTORS = limit.rlim_cur; - } - else - { - ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!"); - std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl; - ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE); - } - - // This is not a maximum, just a hint at the eventual number of sockets that may be polled. - EngineHandle = epoll_create(GetMaxFds() / 4); + LookupMaxFds(); + // 128 is not a maximum, just a hint at the eventual number of sockets that may be polled, + // and it is completely ignored by 2.6.8 and later kernels, except it must be larger than zero. + EngineHandle = epoll_create(128); if (EngineHandle == -1) - { - ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno)); - ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now."); - std::cout << "ERROR: Could not initialize epoll socket engine: " << strerror(errno) << std::endl; - std::cout << "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now." << std::endl; - ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE); - } - - ref = new EventHandler* [GetMaxFds()]; - events = new struct epoll_event[GetMaxFds()]; + InitError(); +} - memset(ref, 0, GetMaxFds() * sizeof(EventHandler*)); +void SocketEngine::RecoverFromFork() +{ } -EPollEngine::~EPollEngine() +void SocketEngine::Deinit() { - this->Close(EngineHandle); - delete[] ref; - delete[] events; + Close(EngineHandle); } static unsigned mask_to_epoll(int event_mask) @@ -116,41 +77,41 @@ static unsigned mask_to_epoll(int event_mask) return rv; } -bool EPollEngine::AddFd(EventHandler* eh, int event_mask) +bool SocketEngine::AddFd(EventHandler* eh, int event_mask) { int fd = eh->GetFd(); - if ((fd < 0) || (fd > GetMaxFds() - 1)) + if (fd < 0) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds()); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd); return false; } - if (ref[fd]) + if (!SocketEngine::AddFdRef(eh)) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd); return false; } struct epoll_event ev; - memset(&ev,0,sizeof(ev)); + memset(&ev, 0, sizeof(ev)); ev.events = mask_to_epoll(event_mask); - ev.data.fd = fd; + ev.data.ptr = static_cast<void*>(eh); int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev); if (i < 0) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno)); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error adding fd: %d to socketengine: %s", fd, strerror(errno)); return false; } - ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd); + + eh->SetEventMask(event_mask); + ResizeDouble(events); - ref[fd] = eh; - SocketEngine::SetEventMask(eh, event_mask); - CurrentSetSize++; return true; } -void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) +void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) { unsigned old_events = mask_to_epoll(old_mask); unsigned new_events = mask_to_epoll(new_mask); @@ -158,75 +119,78 @@ void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) { // ok, we actually have something to tell the kernel about struct epoll_event ev; - memset(&ev,0,sizeof(ev)); + memset(&ev, 0, sizeof(ev)); ev.events = new_events; - ev.data.fd = eh->GetFd(); + ev.data.ptr = static_cast<void*>(eh); epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev); } } -void EPollEngine::DelFd(EventHandler* eh) +void SocketEngine::DelFd(EventHandler* eh) { int fd = eh->GetFd(); - if ((fd < 0) || (fd > GetMaxFds() - 1)) + if (fd < 0) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds()); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd); return; } + // Do not initialize epoll_event because for EPOLL_CTL_DEL operations the event is ignored and can be NULL. + // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required a non-NULL pointer in event, + // even though this argument is ignored. Since Linux 2.6.9, event can be specified as NULL when using EPOLL_CTL_DEL. struct epoll_event ev; - memset(&ev,0,sizeof(ev)); - ev.data.fd = fd; int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev); if (i < 0) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"epoll_ctl can't remove socket: %s", strerror(errno)); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "epoll_ctl can't remove socket: %s", strerror(errno)); } - ref[fd] = NULL; + SocketEngine::DelFdRef(eh); - ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd); - CurrentSetSize--; + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd); } -int EPollEngine::DispatchEvents() +int SocketEngine::DispatchEvents() { - socklen_t codesize = sizeof(int); - int errcode; - int i = epoll_wait(EngineHandle, events, GetMaxFds() - 1, 1000); + int i = epoll_wait(EngineHandle, &events[0], events.size(), 1000); ServerInstance->UpdateTime(); - TotalEvents += i; + stats.TotalEvents += i; for (int j = 0; j < i; j++) { - EventHandler* eh = ref[events[j].data.fd]; - if (!eh) - { - ServerInstance->Logs->Log("SOCKET",DEBUG,"Got event on unknown fd: %d", events[j].data.fd); - epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]); + // Copy these in case the vector gets resized and ev invalidated + const epoll_event ev = events[j]; + + EventHandler* const eh = static_cast<EventHandler*>(ev.data.ptr); + const int fd = eh->GetFd(); + if (fd < 0) continue; - } - if (events[j].events & EPOLLHUP) + + if (ev.events & EPOLLHUP) { - ErrorEvents++; - eh->HandleEvent(EVENT_ERROR, 0); + stats.ErrorEvents++; + eh->OnEventHandlerError(0); continue; } - if (events[j].events & EPOLLERR) + + if (ev.events & EPOLLERR) { - ErrorEvents++; + stats.ErrorEvents++; /* Get error number */ - if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) + socklen_t codesize = sizeof(int); + int errcode; + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) errcode = errno; - eh->HandleEvent(EVENT_ERROR, errcode); + eh->OnEventHandlerError(errcode); continue; } + int mask = eh->GetEventMask(); - if (events[j].events & EPOLLIN) + if (ev.events & EPOLLIN) mask &= ~FD_READ_WILL_BLOCK; - if (events[j].events & EPOLLOUT) + if (ev.events & EPOLLOUT) { mask &= ~FD_WRITE_WILL_BLOCK; if (mask & FD_WANT_SINGLE_WRITE) @@ -236,31 +200,19 @@ int EPollEngine::DispatchEvents() mask = nm; } } - SetEventMask(eh, mask); - if (events[j].events & EPOLLIN) + eh->SetEventMask(mask); + if (ev.events & EPOLLIN) { - ReadEvents++; - eh->HandleEvent(EVENT_READ); - if (eh != ref[events[j].data.fd]) + eh->OnEventHandlerRead(); + if (eh != GetRef(fd)) // whoa! we got deleted, better not give out the write event continue; } - if (events[j].events & EPOLLOUT) + if (ev.events & EPOLLOUT) { - WriteEvents++; - eh->HandleEvent(EVENT_WRITE); + eh->OnEventHandlerWrite(); } } return i; } - -std::string EPollEngine::GetName() -{ - return "epoll"; -} - -SocketEngine* CreateSocketEngine() -{ - return new EPollEngine; -} diff --git a/src/socketengines/socketengine_kqueue.cpp b/src/socketengines/socketengine_kqueue.cpp index 8694a0bdd..b23cfbd9d 100644 --- a/src/socketengines/socketengine_kqueue.cpp +++ b/src/socketengines/socketengine_kqueue.cpp @@ -20,70 +20,36 @@ #include "inspircd.h" -#include "exitcodes.h" + #include <sys/types.h> #include <sys/event.h> #include <sys/time.h> -#include "socketengine.h" -#include <iostream> +#include <sys/sysctl.h> /** A specialisation of the SocketEngine class, designed to use BSD kqueue(). */ -class KQueueEngine : public SocketEngine +namespace { -private: int EngineHandle; + unsigned int ChangePos = 0; /** These are used by kqueue() to hold socket events */ - struct kevent* ke_list; - /** This is a specialised time value used by kqueue() - */ - struct timespec ts; -public: - /** Create a new KQueueEngine - */ - KQueueEngine(); - /** Delete a KQueueEngine - */ - virtual ~KQueueEngine(); - bool AddFd(EventHandler* eh, int event_mask); - void OnSetEvent(EventHandler* eh, int old_mask, int new_mask); - virtual void DelFd(EventHandler* eh); - virtual int DispatchEvents(); - virtual std::string GetName(); - virtual void RecoverFromFork(); -}; + std::vector<struct kevent> ke_list(16); -#include <sys/sysctl.h> + /** Pending changes + */ + std::vector<struct kevent> changelist(8); +} -KQueueEngine::KQueueEngine() +/** Initialize the kqueue engine + */ +void SocketEngine::Init() { - MAX_DESCRIPTORS = 0; - int mib[2]; - size_t len; - - mib[0] = CTL_KERN; -#ifdef KERN_MAXFILESPERPROC - mib[1] = KERN_MAXFILESPERPROC; -#else - mib[1] = KERN_MAXFILES; -#endif - len = sizeof(MAX_DESCRIPTORS); - sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0); - if (MAX_DESCRIPTORS <= 0) - { - ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!"); - std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl; - ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE); - } - - this->RecoverFromFork(); - ke_list = new struct kevent[GetMaxFds()]; - ref = new EventHandler* [GetMaxFds()]; - memset(ref, 0, GetMaxFds() * sizeof(EventHandler*)); + LookupMaxFds(); + RecoverFromFork(); } -void KQueueEngine::RecoverFromFork() +void SocketEngine::RecoverFromFork() { /* * The only bad thing about kqueue is that its fd cant survive a fork and is not inherited. @@ -92,177 +58,141 @@ void KQueueEngine::RecoverFromFork() */ EngineHandle = kqueue(); if (EngineHandle == -1) - { - ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); - ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: this is a fatal error, exiting now."); - std::cout << "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features." << std::endl; - std::cout << "ERROR: this is a fatal error, exiting now." << std::endl; - ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE); - } - CurrentSetSize = 0; + InitError(); } -KQueueEngine::~KQueueEngine() +/** Shutdown the kqueue engine + */ +void SocketEngine::Deinit() { - this->Close(EngineHandle); - delete[] ref; - delete[] ke_list; + Close(EngineHandle); } -bool KQueueEngine::AddFd(EventHandler* eh, int event_mask) +static struct kevent* GetChangeKE() +{ + if (ChangePos >= changelist.size()) + changelist.resize(changelist.size() * 2); + return &changelist[ChangePos++]; +} + +bool SocketEngine::AddFd(EventHandler* eh, int event_mask) { int fd = eh->GetFd(); - if ((fd < 0) || (fd > GetMaxFds() - 1)) + if (fd < 0) return false; - if (ref[fd]) + if (!SocketEngine::AddFdRef(eh)) return false; // We always want to read from the socket... - struct kevent ke; - EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); + struct kevent* ke = GetChangeKE(); + EV_SET(ke, fd, EVFILT_READ, EV_ADD, 0, 0, static_cast<void*>(eh)); - int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); - if (i == -1) - { - ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to add fd: %d %s", - fd, strerror(errno)); - return false; - } + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd); - ref[fd] = eh; - SocketEngine::SetEventMask(eh, event_mask); + eh->SetEventMask(event_mask); OnSetEvent(eh, 0, event_mask); - CurrentSetSize++; + ResizeDouble(ke_list); - ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd); return true; } -void KQueueEngine::DelFd(EventHandler* eh) +void SocketEngine::DelFd(EventHandler* eh) { int fd = eh->GetFd(); - if ((fd < 0) || (fd > GetMaxFds() - 1)) + if (fd < 0) { - ServerInstance->Logs->Log("SOCKET",DEFAULT,"DelFd() on invalid fd: %d", fd); + ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "DelFd() on invalid fd: %d", fd); return; } - struct kevent ke; - // First remove the write filter ignoring errors, since we can't be // sure if there are actually any write filters registered. - EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); - kevent(EngineHandle, &ke, 1, 0, 0, NULL); + struct kevent* ke = GetChangeKE(); + EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); // Then remove the read filter. - EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL); - int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL); - - if (j < 0) - { - ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to remove fd: %d %s", - fd, strerror(errno)); - } + ke = GetChangeKE(); + EV_SET(ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL); - CurrentSetSize--; - ref[fd] = NULL; + SocketEngine::DelFdRef(eh); - ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd); } -void KQueueEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) +void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) { if ((new_mask & FD_WANT_POLL_WRITE) && !(old_mask & FD_WANT_POLL_WRITE)) { // new poll-style write - struct kevent ke; - EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, NULL); - int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); - if (i < 0) { - ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s", - eh->GetFd(), strerror(errno)); - } + struct kevent* ke = GetChangeKE(); + EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, static_cast<void*>(eh)); } else if ((old_mask & FD_WANT_POLL_WRITE) && !(new_mask & FD_WANT_POLL_WRITE)) { // removing poll-style write - struct kevent ke; - EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); - int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); - if (i < 0) { - ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s", - eh->GetFd(), strerror(errno)); - } + struct kevent* ke = GetChangeKE(); + EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); } if ((new_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)) && !(old_mask & (FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))) { - // new one-shot write - struct kevent ke; - EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL); - int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); - if (i < 0) { - ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s", - eh->GetFd(), strerror(errno)); - } + struct kevent* ke = GetChangeKE(); + EV_SET(ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, static_cast<void*>(eh)); } } -int KQueueEngine::DispatchEvents() +int SocketEngine::DispatchEvents() { + struct timespec ts; ts.tv_nsec = 0; ts.tv_sec = 1; - int i = kevent(EngineHandle, NULL, 0, &ke_list[0], GetMaxFds(), &ts); + int i = kevent(EngineHandle, &changelist.front(), ChangePos, &ke_list.front(), ke_list.size(), &ts); + ChangePos = 0; ServerInstance->UpdateTime(); - TotalEvents += i; + if (i < 0) + return i; + + stats.TotalEvents += i; for (int j = 0; j < i; j++) { - EventHandler* eh = ref[ke_list[j].ident]; + struct kevent& kev = ke_list[j]; + EventHandler* eh = static_cast<EventHandler*>(kev.udata); if (!eh) continue; - if (ke_list[j].flags & EV_EOF) + + // Copy these in case the vector gets resized and kev invalidated + const int fd = eh->GetFd(); + const short filter = kev.filter; + if (fd < 0) + continue; + + if (kev.flags & EV_EOF) { - ErrorEvents++; - eh->HandleEvent(EVENT_ERROR, ke_list[j].fflags); + stats.ErrorEvents++; + eh->OnEventHandlerError(kev.fflags); continue; } - if (ke_list[j].filter == EVFILT_WRITE) + if (filter == EVFILT_WRITE) { - WriteEvents++; /* When mask is FD_WANT_FAST_WRITE or FD_WANT_SINGLE_WRITE, * we set a one-shot write, so we need to clear that bit * to detect when it set again. */ const int bits_to_clr = FD_WANT_SINGLE_WRITE | FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK; - SetEventMask(eh, eh->GetEventMask() & ~bits_to_clr); - eh->HandleEvent(EVENT_WRITE); - - if (eh != ref[ke_list[j].ident]) - // whoops, deleted out from under us - continue; + eh->SetEventMask(eh->GetEventMask() & ~bits_to_clr); + eh->OnEventHandlerWrite(); } - if (ke_list[j].filter == EVFILT_READ) + else if (filter == EVFILT_READ) { - ReadEvents++; - SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK); - eh->HandleEvent(EVENT_READ); + eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK); + eh->OnEventHandlerRead(); } } return i; } - -std::string KQueueEngine::GetName() -{ - return "kqueue"; -} - -SocketEngine* CreateSocketEngine() -{ - return new KQueueEngine; -} diff --git a/src/socketengines/socketengine_poll.cpp b/src/socketengines/socketengine_poll.cpp index e38e0fac1..339045a8c 100644 --- a/src/socketengines/socketengine_poll.cpp +++ b/src/socketengines/socketengine_poll.cpp @@ -1,6 +1,7 @@ /* * InspIRCd -- Internet Relay Chat Daemon * + * Copyright (C) 2014 Adam <Adam@anope.org> * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> * Copyright (C) 2009 Uli Schlachter <psychon@znc.in> * Copyright (C) 2009 Craig Edwards <craigedwards@brainbox.cc> @@ -21,88 +22,33 @@ #include "inspircd.h" -#include "exitcodes.h" -#ifndef SOCKETENGINE_POLL -#define SOCKETENGINE_POLL - -#include <iostream> -#include <vector> -#include <string> -#include <map> -#include "inspircd_config.h" -#include "inspircd.h" -#include "socketengine.h" - -#ifndef _WIN32 -# ifndef __USE_XOPEN -# define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/ -# endif -# include <poll.h> -# include <sys/poll.h> -# include <sys/resource.h> -#else -# define struct pollfd WSAPOLLFD -# define poll WSAPoll -#endif - -class InspIRCd; +#include <sys/poll.h> +#include <sys/resource.h> /** A specialisation of the SocketEngine class, designed to use poll(). */ -class PollEngine : public SocketEngine +namespace { -private: /** These are used by poll() to hold socket events */ - struct pollfd *events; - /** This map maps fds to an index in the events array. - */ - std::map<int, unsigned int> fd_mappings; -public: - /** Create a new PollEngine + std::vector<struct pollfd> events(16); + /** This vector maps fds to an index in the events array. */ - PollEngine(); - /** Delete a PollEngine - */ - virtual ~PollEngine(); - virtual bool AddFd(EventHandler* eh, int event_mask); - virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask); - virtual EventHandler* GetRef(int fd); - virtual void DelFd(EventHandler* eh); - virtual int DispatchEvents(); - virtual std::string GetName(); -}; - -#endif - -PollEngine::PollEngine() -{ - CurrentSetSize = 0; - struct rlimit limits; - if (!getrlimit(RLIMIT_NOFILE, &limits)) - { - MAX_DESCRIPTORS = limits.rlim_cur; - } - else - { - ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno)); - std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl; - ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE); - } + std::vector<int> fd_mappings(16, -1); +} - ref = new EventHandler* [GetMaxFds()]; - events = new struct pollfd[GetMaxFds()]; +void SocketEngine::Init() +{ + LookupMaxFds(); +} - memset(events, 0, GetMaxFds() * sizeof(struct pollfd)); - memset(ref, 0, GetMaxFds() * sizeof(EventHandler*)); +void SocketEngine::Deinit() +{ } -PollEngine::~PollEngine() +void SocketEngine::RecoverFromFork() { - // No destruction required, either. - delete[] ref; - delete[] events; } static int mask_to_poll(int event_mask) @@ -115,71 +61,70 @@ static int mask_to_poll(int event_mask) return rv; } -bool PollEngine::AddFd(EventHandler* eh, int event_mask) +bool SocketEngine::AddFd(EventHandler* eh, int event_mask) { int fd = eh->GetFd(); - if ((fd < 0) || (fd > GetMaxFds() - 1)) + if (fd < 0) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds()); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd); return false; } - if (fd_mappings.find(fd) != fd_mappings.end()) + if (static_cast<unsigned int>(fd) < fd_mappings.size() && fd_mappings[fd] != -1) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd); return false; } unsigned int index = CurrentSetSize; + if (!SocketEngine::AddFdRef(eh)) + { + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd); + return false; + } + + while (static_cast<unsigned int>(fd) >= fd_mappings.size()) + fd_mappings.resize(fd_mappings.size() * 2, -1); fd_mappings[fd] = index; - ref[index] = eh; + + ResizeDouble(events); events[index].fd = fd; events[index].events = mask_to_poll(event_mask); - ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d; index %d)", fd, events[index].events, index); - SocketEngine::SetEventMask(eh, event_mask); - CurrentSetSize++; + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index); + eh->SetEventMask(event_mask); return true; } -EventHandler* PollEngine::GetRef(int fd) -{ - std::map<int, unsigned int>::iterator it = fd_mappings.find(fd); - if (it == fd_mappings.end()) - return NULL; - return ref[it->second]; -} - -void PollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) +void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) { - std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd()); - if (it == fd_mappings.end()) + int fd = eh->GetFd(); + if (fd < 0 || static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"SetEvents() on unknown fd: %d", eh->GetFd()); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd()); return; } - events[it->second].events = mask_to_poll(new_mask); + events[fd_mappings[fd]].events = mask_to_poll(new_mask); } -void PollEngine::DelFd(EventHandler* eh) +void SocketEngine::DelFd(EventHandler* eh) { int fd = eh->GetFd(); - if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + if (fd < 0) { - ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds()); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd); return; } - std::map<int, unsigned int>::iterator it = fd_mappings.find(fd); - if (it == fd_mappings.end()) + if (static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1) { - ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd() on unknown fd: %d", fd); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd); return; } - unsigned int index = it->second; + unsigned int index = fd_mappings[fd]; unsigned int last_index = CurrentSetSize - 1; int last_fd = events[last_index].fd; @@ -193,89 +138,78 @@ void PollEngine::DelFd(EventHandler* eh) // move last_fd from last_index into index events[index].fd = last_fd; events[index].events = events[last_index].events; - - ref[index] = ref[last_index]; } // Now remove all data for the last fd we got into out list. // Above code made sure this always is right - fd_mappings.erase(it); + fd_mappings[fd] = -1; events[last_index].fd = 0; events[last_index].events = 0; - ref[last_index] = NULL; - CurrentSetSize--; + SocketEngine::DelFdRef(eh); - ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d (index: %d) " + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) " "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index); } -int PollEngine::DispatchEvents() +int SocketEngine::DispatchEvents() { - int i = poll(events, CurrentSetSize, 1000); - int index; - socklen_t codesize = sizeof(int); - int errcode; + int i = poll(&events[0], CurrentSetSize, 1000); int processed = 0; ServerInstance->UpdateTime(); - if (i > 0) + for (size_t index = 0; index < CurrentSetSize && processed < i; index++) { - for (index = 0; index < CurrentSetSize && processed != i; index++) + struct pollfd& pfd = events[index]; + + // Copy these in case the vector gets resized and pfd invalidated + const int fd = pfd.fd; + const short revents = pfd.revents; + + if (revents) + processed++; + + EventHandler* eh = GetRef(fd); + if (!eh) + continue; + + if (revents & POLLHUP) { - if (events[index].revents) - processed++; - EventHandler* eh = ref[index]; - if (!eh) - continue; + eh->OnEventHandlerError(0); + continue; + } + + if (revents & POLLERR) + { + // Get error number + socklen_t codesize = sizeof(int); + int errcode; + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) + errcode = errno; + eh->OnEventHandlerError(errcode); + continue; + } - if (events[index].revents & POLLHUP) - { - eh->HandleEvent(EVENT_ERROR, 0); + if (revents & POLLIN) + { + eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK); + eh->OnEventHandlerRead(); + if (eh != GetRef(fd)) + // whoops, deleted out from under us continue; - } + } - if (events[index].revents & POLLERR) - { - // Get fd - int fd = events[index].fd; + if (revents & POLLOUT) + { + int mask = eh->GetEventMask(); + mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE); + eh->SetEventMask(mask); - // Get error number - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) - errcode = errno; - eh->HandleEvent(EVENT_ERROR, errcode); - continue; - } - - if (events[index].revents & POLLIN) - { - SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK); - eh->HandleEvent(EVENT_READ); - if (eh != ref[index]) - // whoops, deleted out from under us - continue; - } - - if (events[index].revents & POLLOUT) - { - int mask = eh->GetEventMask(); - mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE); - SetEventMask(eh, mask); - events[index].events = mask_to_poll(mask); - eh->HandleEvent(EVENT_WRITE); - } + // The vector could've been resized, reference can be invalid by now; don't use it + events[index].events = mask_to_poll(mask); + eh->OnEventHandlerWrite(); } } return i; } - -std::string PollEngine::GetName() -{ - return "poll"; -} - -SocketEngine* CreateSocketEngine() -{ - return new PollEngine; -} diff --git a/src/socketengines/socketengine_ports.cpp b/src/socketengines/socketengine_ports.cpp deleted file mode 100644 index f7c547d45..000000000 --- a/src/socketengines/socketengine_ports.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc> - * - * This file is part of InspIRCd. InspIRCd 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. - * - * 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, see <http://www.gnu.org/licenses/>. - */ - - -#include "inspircd.h" -#include "exitcodes.h" -#include <port.h> - -#ifndef SOCKETENGINE_PORTS -#define SOCKETENGINE_PORTS - -#ifndef __sun -# error You need Solaris 10 or later to make use of this code. -#endif - -#include <vector> -#include <string> -#include <map> -#include "inspircd_config.h" -#include "inspircd.h" -#include "socketengine.h" -#include <port.h> -#include <iostream> - -/** 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; - int EngineHandle; -public: - /** Create a new PortsEngine - */ - PortsEngine(); - /** Delete a PortsEngine - */ - virtual ~PortsEngine(); - virtual bool AddFd(EventHandler* eh, int event_mask); - virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask); - virtual void DelFd(EventHandler* eh); - virtual int DispatchEvents(); - virtual std::string GetName(); -}; - -#endif - - -#include <ulimit.h> - -PortsEngine::PortsEngine() -{ - int max = ulimit(4, 0); - if (max > 0) - { - MAX_DESCRIPTORS = max; - } - else - { - ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!"); - std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl; - ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE); - } - EngineHandle = port_create(); - - if (EngineHandle == -1) - { - ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno)); - ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: This is a fatal error, exiting now."); - std::cout << "ERROR: Could not initialize socket engine: " << strerror(errno) << std::endl; - std::cout << "ERROR: This is a fatal error, exiting now." << std::endl; - ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE); - } - CurrentSetSize = 0; - - ref = new EventHandler* [GetMaxFds()]; - events = new port_event_t[GetMaxFds()]; - memset(ref, 0, GetMaxFds() * sizeof(EventHandler*)); -} - -PortsEngine::~PortsEngine() -{ - this->Close(EngineHandle); - delete[] ref; - delete[] events; -} - -static int mask_to_events(int event_mask) -{ - int rv = 0; - if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ)) - rv |= POLLRDNORM; - if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE)) - rv |= POLLWRNORM; - return rv; -} - -bool PortsEngine::AddFd(EventHandler* eh, int event_mask) -{ - int fd = eh->GetFd(); - if ((fd < 0) || (fd > GetMaxFds() - 1)) - return false; - - if (ref[fd]) - return false; - - ref[fd] = eh; - SocketEngine::SetEventMask(eh, event_mask); - port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(event_mask), eh); - - ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd); - CurrentSetSize++; - return true; -} - -void PortsEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) -{ - if (mask_to_events(new_mask) != mask_to_events(old_mask)) - port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), mask_to_events(new_mask), eh); -} - -void PortsEngine::DelFd(EventHandler* eh) -{ - int fd = eh->GetFd(); - if ((fd < 0) || (fd > GetMaxFds() - 1)) - return; - - port_dissociate(EngineHandle, PORT_SOURCE_FD, fd); - - CurrentSetSize--; - ref[fd] = NULL; - - ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd); -} - -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 ret = port_getn(EngineHandle, this->events, GetMaxFds() - 1, &nget, &poll_time); - ServerInstance->UpdateTime(); - - // first handle an error condition - if (ret == -1) - return -1; - - TotalEvents += nget; - - unsigned int i; - for (i = 0; i < nget; i++) - { - switch (this->events[i].portev_source) - { - case PORT_SOURCE_FD: - { - int fd = this->events[i].portev_object; - EventHandler* eh = ref[fd]; - if (eh) - { - int mask = eh->GetEventMask(); - if (events[i].portev_events & POLLWRNORM) - mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE); - if (events[i].portev_events & POLLRDNORM) - mask &= ~FD_READ_WILL_BLOCK; - // reinsert port for next time around, pretending to be one-shot for writes - SetEventMask(eh, mask); - port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(mask), eh); - if (events[i].portev_events & POLLRDNORM) - { - ReadEvents++; - eh->HandleEvent(EVENT_READ); - if (eh != ref[fd]) - continue; - } - if (events[i].portev_events & POLLWRNORM) - { - WriteEvents++; - eh->HandleEvent(EVENT_WRITE); - } - } - } - default: - break; - } - } - - return (int)i; -} - -std::string PortsEngine::GetName() -{ - return "ports"; -} - -SocketEngine* CreateSocketEngine() -{ - return new PortsEngine; -} diff --git a/src/socketengines/socketengine_select.cpp b/src/socketengines/socketengine_select.cpp index 0b5abaf30..03f0aca62 100644 --- a/src/socketengines/socketengine_select.cpp +++ b/src/socketengines/socketengine_select.cpp @@ -1,6 +1,7 @@ /* * InspIRCd -- Internet Relay Chat Daemon * + * Copyright (C) 2014 Adam <Adam@anope.org> * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc> * @@ -18,10 +19,7 @@ */ -#include "inspircd_config.h" - #include "inspircd.h" -#include "socketengine.h" #ifndef _WIN32 #include <sys/select.h> @@ -29,76 +27,63 @@ /** A specialisation of the SocketEngine class, designed to use traditional select(). */ -class SelectEngine : public SocketEngine +namespace { fd_set ReadSet, WriteSet, ErrSet; - int MaxFD; - -public: - /** Create a new SelectEngine - */ - SelectEngine(); - /** Delete a SelectEngine - */ - virtual ~SelectEngine(); - virtual bool AddFd(EventHandler* eh, int event_mask); - virtual void DelFd(EventHandler* eh); - void OnSetEvent(EventHandler* eh, int, int); - virtual int DispatchEvents(); - virtual std::string GetName(); -}; - -SelectEngine::SelectEngine() -{ - MAX_DESCRIPTORS = FD_SETSIZE; - CurrentSetSize = 0; + int MaxFD = 0; +} - ref = new EventHandler* [GetMaxFds()]; - memset(ref, 0, GetMaxFds() * sizeof(EventHandler*)); +void SocketEngine::Init() +{ + MaxSetSize = FD_SETSIZE; FD_ZERO(&ReadSet); FD_ZERO(&WriteSet); FD_ZERO(&ErrSet); - MaxFD = 0; } -SelectEngine::~SelectEngine() +void SocketEngine::Deinit() { - delete[] ref; } -bool SelectEngine::AddFd(EventHandler* eh, int event_mask) +void SocketEngine::RecoverFromFork() +{ +} + +bool SocketEngine::AddFd(EventHandler* eh, int event_mask) { int fd = eh->GetFd(); - if ((fd < 0) || (fd > GetMaxFds() - 1)) + + if (fd < 0) return false; - if (ref[fd]) + if (static_cast<size_t>(fd) >= GetMaxFds()) return false; - ref[fd] = eh; + if (!SocketEngine::AddFdRef(eh)) + return false; - SocketEngine::SetEventMask(eh, event_mask); + eh->SetEventMask(event_mask); OnSetEvent(eh, 0, event_mask); FD_SET(fd, &ErrSet); if (fd > MaxFD) MaxFD = fd; - CurrentSetSize++; - - ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd); return true; } -void SelectEngine::DelFd(EventHandler* eh) +void SocketEngine::DelFd(EventHandler* eh) { int fd = eh->GetFd(); - if ((fd < 0) || (fd > GetMaxFds() - 1)) + if (fd < 0) + return; + + if (static_cast<size_t>(fd) >= GetMaxFds()) return; - CurrentSetSize--; - ref[fd] = NULL; + SocketEngine::DelFdRef(eh); FD_CLR(fd, &ReadSet); FD_CLR(fd, &WriteSet); @@ -106,10 +91,10 @@ void SelectEngine::DelFd(EventHandler* eh) if (fd == MaxFD) --MaxFD; - ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd); } -void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) +void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) { int fd = eh->GetFd(); int diff = old_mask ^ new_mask; @@ -130,7 +115,7 @@ void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) } } -int SelectEngine::DispatchEvents() +int SocketEngine::DispatchEvents() { timeval tval; tval.tv_sec = 1; @@ -141,63 +126,48 @@ int SelectEngine::DispatchEvents() int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval); ServerInstance->UpdateTime(); - /* Nothing to process this time around */ - if (sresult < 1) - return 0; - for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++) { int has_read = FD_ISSET(i, &rfdset), has_write = FD_ISSET(i, &wfdset), has_error = FD_ISSET(i, &errfdset); - if (has_read || has_write || has_error) - { - --j; + if (!(has_read || has_write || has_error)) + continue; - EventHandler* ev = ref[i]; - if (!ev) - continue; + --j; - if (has_error) - { - ErrorEvents++; + EventHandler* ev = GetRef(i); + if (!ev) + continue; - socklen_t codesize = sizeof(int); - int errcode = 0; - if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0) - errcode = errno; + if (has_error) + { + stats.ErrorEvents++; + + socklen_t codesize = sizeof(int); + int errcode = 0; + if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0) + errcode = errno; + + ev->OnEventHandlerError(errcode); + continue; + } - ev->HandleEvent(EVENT_ERROR, errcode); + if (has_read) + { + ev->SetEventMask(ev->GetEventMask() & ~FD_READ_WILL_BLOCK); + ev->OnEventHandlerRead(); + if (ev != GetRef(i)) continue; - } - - if (has_read) - { - ReadEvents++; - SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK); - ev->HandleEvent(EVENT_READ); - if (ev != ref[i]) - continue; - } - if (has_write) - { - WriteEvents++; - int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE)); - this->OnSetEvent(ev, ev->GetEventMask(), newmask); - SetEventMask(ev, newmask); - ev->HandleEvent(EVENT_WRITE); - } + } + + if (has_write) + { + int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE)); + SocketEngine::OnSetEvent(ev, ev->GetEventMask(), newmask); + ev->SetEventMask(newmask); + ev->OnEventHandlerWrite(); } } return sresult; } - -std::string SelectEngine::GetName() -{ - return "select"; -} - -SocketEngine* CreateSocketEngine() -{ - return new SelectEngine; -} |