-/* +------------------------------------+
- * | Inspire Internet Relay Chat Daemon |
- * +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
*
- * InspIRCd: (C) 2002-2009 InspIRCd Development Team
- * See: http://wiki.inspircd.org/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>
-#ifdef __FreeBSD__
- #include <sys/sysctl.h>
-#endif
+#include "inspircd.h"
+
+#include <sys/poll.h>
+#include <sys/resource.h>
-PollEngine::PollEngine()
+/** 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, bool writeFirst)
+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 (fd_mappings.find(fd) != fd_mappings.end())
+ 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;
}
- unsigned int index = CurrentSetSize;
-
+ 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;
- if (writeFirst)
- {
- events[index].events = POLLOUT;
- }
- else
- {
- events[index].events = POLLIN;
- }
+ events[index].events = mask_to_poll(event_mask);
- ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
- 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::WantWrite(EventHandler* eh)
+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,"WantWrite() on unknown fd: %d", eh->GetFd());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
return;
}
- events[it->second].events = POLLIN | POLLOUT;
+ events[fd_mappings[fd]].events = mask_to_poll(new_mask);
}
-bool PollEngine::DelFd(EventHandler* eh, bool force)
+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());
- return false;
+ 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);
- return false;
+ 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;
// 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);
- return true;
}
-int PollEngine::GetMaxFds()
+int SocketEngine::DispatchEvents()
{
-#ifndef __FreeBSD__
- if (MAX_DESCRIPTORS)
- return MAX_DESCRIPTORS;
+ int i = poll(&events[0], CurrentSetSize, 1000);
+ int processed = 0;
+ ServerInstance->UpdateTime();
- int max = ulimit(4, 0);
- if (max > 0)
- {
- MAX_DESCRIPTORS = max;
- return max;
- }
- else
+ for (int index = 0; index < CurrentSetSize && processed < i; index++)
{
- MAX_DESCRIPTORS = 0;
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
- printf("ERROR: Can't determine maximum number of open sockets: %s\n", strerror(errno));
- ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
- }
- return 0;
-#else
- if (!MAX_DESCRIPTORS)
- {
- int mib[2], maxfiles;
- size_t len;
-
- mib[0] = CTL_KERN;
- mib[1] = KERN_MAXFILES;
- len = sizeof(maxfiles);
- sysctl(mib, 2, &maxfiles, &len, NULL, 0);
- MAX_DESCRIPTORS = maxfiles;
- return maxfiles;
- }
- return MAX_DESCRIPTORS;
-#endif
-}
+ struct pollfd& pfd = events[index];
-int PollEngine::GetRemainingFds()
-{
- return MAX_DESCRIPTORS - CurrentSetSize;
-}
+ // Copy these in case the vector gets resized and pfd invalidated
+ const int fd = pfd.fd;
+ const short revents = pfd.revents;
-int PollEngine::DispatchEvents()
-{
- int i = poll(events, CurrentSetSize, 1000);
- int index;
- socklen_t codesize = sizeof(int);
- int errcode;
- int processed = 0;
+ if (revents)
+ processed++;
- if (i > 0)
- {
- for (index = 0; index < CurrentSetSize && processed != i; index++)
+ EventHandler* eh = GetRef(fd);
+ if (!eh)
+ continue;
+
+ if (revents & POLLHUP)
{
- if (events[index].revents)
- processed++;
+ eh->HandleEvent(EVENT_ERROR, 0);
+ continue;
+ }
- if (events[index].revents & POLLHUP)
- {
- if (ref[index])
- ref[index]->HandleEvent(EVENT_ERROR, 0);
- continue;
- }
-
- if (events[index].revents & POLLERR)
- {
- // Get fd
- int fd = events[index].fd;
-
- // Get error number
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
- errcode = errno;
- if (ref[index])
- ref[index]->HandleEvent(EVENT_ERROR, errcode);
+ 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 (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 (events[index].revents & POLLOUT)
- {
- // Switch to wanting read again
- // event handlers have to request to write again if they need it
- events[index].events = POLLIN;
-
- if (ref[index])
- ref[index]->HandleEvent(EVENT_WRITE);
- }
-
- if (events[index].revents & POLLIN)
- {
- if (ref[index])
- ref[index]->HandleEvent(EVENT_READ);
- }
+ }
+
+ 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";
-}
-