-/* +------------------------------------+
- * | Inspire Internet Relay Chat Daemon |
- * +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
*
- * InspIRCd: (C) 2002-2008 InspIRCd Development Team
- * See: http://www.inspircd.org/wiki/index.php/Credits
+ * 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>
+ * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
*
- * This program is free but copyrighted software; see
- * the file COPYING for details.
+ * 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 "socketengines/socketengine_poll.h"
-#include <ulimit.h>
+#include "inspircd.h"
-PollEngine::PollEngine(InspIRCd* Instance) : SocketEngine(Instance)
+#include <sys/poll.h>
+#include <sys/resource.h>
+
+/** A specialisation of the SocketEngine class, designed to use poll().
+ */
+namespace
{
- // Poll requires no special setup (which is nice).
- CurrentSetSize = 0;
- MAX_DESCRIPTORS = 0;
+ /** These are used by poll() to hold socket events
+ */
+ std::vector<struct pollfd> events(16);
+ /** This vector maps fds to an index in the events array.
+ */
+ std::vector<int> fd_mappings(16);
+}
- ref = new EventHandler* [GetMaxFds()];
- events = new struct pollfd[GetMaxFds()];
+void SocketEngine::Init()
+{
+ struct rlimit limits;
+ if (!getrlimit(RLIMIT_NOFILE, &limits))
+ {
+ MAX_DESCRIPTORS = limits.rlim_cur;
+ }
+ else
+ {
+ // MAX_DESCRIPTORS is mainly used for display purposes, it's not a problem that getrlimit() failed
+ MAX_DESCRIPTORS = -1;
+ }
+}
- 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;
}
-bool PollEngine::AddFd(EventHandler* eh)
+static int mask_to_poll(int event_mask)
+{
+ int rv = 0;
+ if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
+ rv |= POLLIN;
+ if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
+ rv |= POLLOUT;
+ return rv;
+}
+
+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 (GetRemainingFds() <= 1)
+ if (static_cast<unsigned int>(fd) < fd_mappings.size() && fd_mappings[fd] != -1)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"No remaining FDs cannot add fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
return false;
}
- if (ref[fd])
+ unsigned int index = CurrentSetSize;
+
+ 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;
}
- ref[fd] = eh;
- events[fd].fd = fd;
- if (eh->Readable())
- {
- events[fd].events = POLLIN;
- }
- else
- {
- events[fd].events = POLLOUT;
- }
+ while (static_cast<unsigned int>(fd) >= fd_mappings.size())
+ fd_mappings.resize(fd_mappings.size() * 2, -1);
+ fd_mappings[fd] = index;
- ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d)", fd, events[fd].events);
- CurrentSetSize++;
- return true;
-}
+ ResizeDouble(events);
+ events[index].fd = fd;
+ events[index].events = mask_to_poll(event_mask);
-void PollEngine::WantWrite(EventHandler* eh)
-{
- events[eh->GetFd()].events = POLLIN | POLLOUT;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
+ eh->SetEventMask(event_mask);
+ return true;
}
-bool PollEngine::DelFd(EventHandler* eh, bool force)
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
int fd = eh->GetFd();
- if ((fd < 0) || (fd > MAX_DESCRIPTORS))
+ if (fd < 0 || static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
- return false;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
+ return;
}
- events[fd].fd = -1;
- events[fd].events = 0;
-
- CurrentSetSize--;
- ref[fd] = NULL;
-
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d", fd);
- return true;
+ events[fd_mappings[fd]].events = mask_to_poll(new_mask);
}
-int PollEngine::GetMaxFds()
+void SocketEngine::DelFd(EventHandler* eh)
{
- if (MAX_DESCRIPTORS)
- return MAX_DESCRIPTORS;
+ int fd = eh->GetFd();
+ if (fd < 0)
+ {
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd);
+ return;
+ }
- int max = ulimit(4, 0);
- if (max > 0)
+ if (static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
{
- MAX_DESCRIPTORS = max;
- return max;
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
+ return;
}
- else
+
+ unsigned int index = fd_mappings[fd];
+ unsigned int last_index = CurrentSetSize - 1;
+ int last_fd = events[last_index].fd;
+
+ if (index != last_index)
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
- printf("ERROR: Can't determine maximum number of open sockets!\n");
- ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+ // We need to move the last fd we got into this gap (gaps are evil!)
+
+ // So update the mapping for the last fd to its new position
+ fd_mappings[last_fd] = index;
+
+ // move last_fd from last_index into index
+ events[index].fd = last_fd;
+ events[index].events = events[last_index].events;
}
- return 0;
-}
-int PollEngine::GetRemainingFds()
-{
- return MAX_DESCRIPTORS - CurrentSetSize;
+ // Now remove all data for the last fd we got into out list.
+ // Above code made sure this always is right
+ fd_mappings[fd] = -1;
+ events[last_index].fd = 0;
+ events[last_index].events = 0;
+
+ SocketEngine::DelFdRef(eh);
+
+ 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, GetMaxFds() - 1, 1000);
- int fd = 0;
- socklen_t codesize = sizeof(int);
- int errcode;
+ int i = poll(&events[0], CurrentSetSize, 1000);
int processed = 0;
+ ServerInstance->UpdateTime();
- if (i > 0)
+ for (int index = 0; index < CurrentSetSize && processed < i; index++)
{
- for (fd = 0; fd < GetMaxFds() - 1 && processed != i; fd++)
- {
- if (events[fd].revents)
- processed++;
+ struct pollfd& pfd = events[index];
- if (events[fd].revents & POLLHUP)
- {
- if (ref[fd])
- ref[fd]->HandleEvent(EVENT_ERROR, 0);
- continue;
- }
-
- if (events[fd].revents & POLLERR)
- {
- // Get error number
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
- errcode = errno;
- if (ref[fd])
- ref[fd]->HandleEvent(EVENT_ERROR, errcode);
- continue;
- }
+ // Copy these in case the vector gets resized and pfd invalidated
+ const int fd = pfd.fd;
+ const short revents = pfd.revents;
- if (events[fd].revents & POLLOUT)
- {
- // Switch to wanting read again
- // event handlers have to request to write again if they need it
- events[fd].events = POLLIN;
+ if (revents)
+ processed++;
+ EventHandler* eh = GetRef(fd);
+ if (!eh)
+ continue;
- if (ref[fd])
- ref[fd]->HandleEvent(EVENT_WRITE);
- }
+ if (revents & POLLHUP)
+ {
+ eh->HandleEvent(EVENT_ERROR, 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->HandleEvent(EVENT_ERROR, errcode);
+ continue;
+ }
- if (events[fd].revents & POLLIN)
- {
- if (ref[fd])
- ref[fd]->HandleEvent(EVENT_READ);
- }
+ if (revents & POLLIN)
+ {
+ eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
+ eh->HandleEvent(EVENT_READ);
+ if (eh != GetRef(fd))
+ // whoops, deleted out from under us
+ continue;
+ }
+
+ if (revents & POLLOUT)
+ {
+ int mask = eh->GetEventMask();
+ mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
+ eh->SetEventMask(mask);
+
+ // The vector could've been resized, reference can be invalid by now; don't use it
+ events[index].events = mask_to_poll(mask);
+ eh->HandleEvent(EVENT_WRITE);
}
}
return i;
}
-
-std::string PollEngine::GetName()
-{
- return "poll";
-}
-