/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2013-2015 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2013, 2017, 2019 Sadie Powell <sadie@witchery.services>
+ * Copyright (C) 2012 Robby <robby@chatbelgie.be>
+ * Copyright (C) 2012 ChrisTX <xpipe@hotmail.de>
+ * Copyright (C) 2011, 2014 Adam <Adam@anope.org>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2006-2008 Craig Edwards <brain@inspircd.org>
*
* 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
*/
-#include "inspircd_config.h"
-
#include "inspircd.h"
-#include "socketengine.h"
-#ifndef WINDOWS
+#ifndef _WIN32
#include <sys/select.h>
-#endif // WINDOWS
+#endif // _WIN32
/** A specialisation of the SocketEngine class, designed to use traditional select().
*/
-class SelectEngine : public SocketEngine
+namespace
{
-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()
+ fd_set ReadSet, WriteSet, ErrSet;
+ int MaxFD = 0;
+}
+
+void SocketEngine::Init()
{
- MAX_DESCRIPTORS = FD_SETSIZE;
- CurrentSetSize = 0;
+#ifdef _WIN32
+ // Set up winsock.
+ WSADATA wsadata;
+ WSAStartup(MAKEWORD(2,2), &wsadata);
+#endif
+
+ MaxSetSize = FD_SETSIZE;
+
+ FD_ZERO(&ReadSet);
+ FD_ZERO(&WriteSet);
+ FD_ZERO(&ErrSet);
+}
- ref = new EventHandler* [GetMaxFds()];
- memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
+void SocketEngine::Deinit()
+{
}
-SelectEngine::~SelectEngine()
+void SocketEngine::RecoverFromFork()
{
- delete[] ref;
}
-bool SelectEngine::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)
+ return false;
+
+ if (static_cast<size_t>(fd) >= GetMaxFds())
return false;
- if (ref[fd])
+ if (!SocketEngine::AddFdRef(eh))
return false;
- ref[fd] = eh;
- SocketEngine::SetEventMask(eh, event_mask);
- CurrentSetSize++;
+ eh->SetEventMask(event_mask);
+ OnSetEvent(eh, 0, event_mask);
+ FD_SET(fd, &ErrSet);
+ if (fd > MaxFD)
+ MaxFD = fd;
- 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;
- CurrentSetSize--;
- ref[fd] = NULL;
+ if (static_cast<size_t>(fd) >= GetMaxFds())
+ return;
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
-}
+ SocketEngine::DelFdRef(eh);
-void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
-{
- // deal with it later
+ FD_CLR(fd, &ReadSet);
+ FD_CLR(fd, &WriteSet);
+ FD_CLR(fd, &ErrSet);
+ if (fd == MaxFD)
+ --MaxFD;
+
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
-int SelectEngine::DispatchEvents()
+void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
{
- timeval tval;
- int sresult = 0;
- socklen_t codesize = sizeof(int);
- int errcode = 0;
-
- fd_set wfdset, rfdset, errfdset;
- FD_ZERO(&wfdset);
- FD_ZERO(&rfdset);
- FD_ZERO(&errfdset);
+ int fd = eh->GetFd();
+ int diff = old_mask ^ new_mask;
- /* Populate the select FD sets (this is why select sucks compared to epoll, kqueue) */
- for (unsigned int i = 0; i < FD_SETSIZE; i++)
+ if (diff & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
{
- EventHandler* eh = ref[i];
- if (!eh)
- continue;
- int state = eh->GetEventMask();
- if (state & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
- FD_SET (i, &rfdset);
- if (state & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
- FD_SET (i, &wfdset);
- FD_SET (i, &errfdset);
+ if (new_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
+ FD_SET(fd, &ReadSet);
+ else
+ FD_CLR(fd, &ReadSet);
}
+ if (diff & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
+ {
+ if (new_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
+ FD_SET(fd, &WriteSet);
+ else
+ FD_CLR(fd, &WriteSet);
+ }
+}
- /* One second wait */
+int SocketEngine::DispatchEvents()
+{
+ timeval tval;
tval.tv_sec = 1;
tval.tv_usec = 0;
- sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval);
- ServerInstance->UpdateTime();
+ fd_set rfdset = ReadSet, wfdset = WriteSet, errfdset = ErrSet;
- /* Nothing to process this time around */
- if (sresult < 1)
- return 0;
+ int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval);
+ ServerInstance->UpdateTime();
- for (int i = 0; i < FD_SETSIZE; i++)
+ for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++)
{
- EventHandler* ev = ref[i];
- if (ev)
+ 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))
+ continue;
+
+ --j;
+
+ EventHandler* ev = GetRef(i);
+ if (!ev)
+ continue;
+
+ if (has_error)
{
- if (FD_ISSET (i, &errfdset))
- {
- ErrorEvents++;
- if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
- errcode = errno;
+ 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;
- }
- else
- {
- /* NOTE: This is a pair of seperate if statements as the socket
- * may be in both read and writeable state at the same time.
- * If an error event occurs above it is not worth processing the
- * read and write states even if set.
- */
- if (FD_ISSET (i, &rfdset))
- {
- ReadEvents++;
- SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
- ev->HandleEvent(EVENT_READ);
- if (ev != ref[i])
- continue;
- }
- if (FD_ISSET (i, &wfdset))
- {
- WriteEvents++;
- SetEventMask(ev, ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
- 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;
-}