/* +------------------------------------+
* | Inspire Internet Relay Chat Daemon |
* +------------------------------------+
*
* InspIRCd: (C) 2002-2009 InspIRCd Development Team
* See: http://wiki.inspircd.org/Credits
*
* This program is free but copyrighted software; see
* the file COPYING for details.
*
* ---------------------------------------------------
*/
#include "inspircd.h"
#include "exitcodes.h"
#include "socketengines/socketengine_poll.h"
#include <ulimit.h>
#ifdef __FreeBSD__
#include <sys/sysctl.h>
#endif
PollEngine::PollEngine(InspIRCd* Instance) : SocketEngine(Instance)
{
// Poll requires no special setup (which is nice).
CurrentSetSize = 0;
MAX_DESCRIPTORS = 0;
ref = new EventHandler* [GetMaxFds()];
events = new struct pollfd[GetMaxFds()];
memset(events, 0, GetMaxFds() * sizeof(struct pollfd));
memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
}
PollEngine::~PollEngine()
{
// No destruction required, either.
delete[] ref;
delete[] events;
}
bool PollEngine::AddFd(EventHandler* eh)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > GetMaxFds() - 1))
{
ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
return false;
}
if (GetRemainingFds() <= 1)
{
ServerInstance->Logs->Log("SOCKET",DEBUG,"No remaining FDs cannot add fd: %d", fd);
return false;
}
if (fd_mappings.find(fd) != fd_mappings.end())
{
ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
return false;
}
unsigned int index = CurrentSetSize;
fd_mappings[fd] = index;
ref[index] = eh;
events[index].fd = fd;
if (eh->Readable())
{
events[index].events = POLLIN;
}
else
{
events[index].events = POLLOUT;
}
ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
CurrentSetSize++;
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)
{
std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
if (it == fd_mappings.end())
{
ServerInstance->Logs->Log("SOCKET",DEBUG,"WantWrite() on unknown fd: %d", eh->GetFd());
return;
}
events[it->second].events = POLLIN | POLLOUT;
}
bool PollEngine::DelFd(EventHandler* eh, bool force)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
{
ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
return false;
}
std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
if (it == fd_mappings.end())
{
ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd() on unknown fd: %d", fd);
return false;
}
unsigned int index = it->second;
unsigned int last_index = CurrentSetSize - 1;
int last_fd = events[last_index].fd;
if (index != last_index)
{
// 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;
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);
events[last_index].fd = 0;
events[last_index].events = 0;
ref[last_index] = NULL;
CurrentSetSize--;
ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d (index: %d) "
"(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
return true;
}
int PollEngine::GetMaxFds()
{
#ifndef __FreeBSD__
if (MAX_DESCRIPTORS)
return MAX_DESCRIPTORS;
int max = ulimit(4, 0);
if (max > 0)
{
MAX_DESCRIPTORS = max;
return max;
}
else
{
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
}
int PollEngine::GetRemainingFds()
{
return MAX_DESCRIPTORS - CurrentSetSize;
}
int PollEngine::DispatchEvents()
{
int i = poll(events, CurrentSetSize, 1000);
int index;
socklen_t codesize = sizeof(int);
int errcode;
int processed = 0;
if (i > 0)
{
for (index = 0; index < CurrentSetSize && processed != i; index++)
{
if (events[index].revents)
processed++;
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);
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);
}
}
}
return i;
}
std::string PollEngine::GetName()
{
return "poll";
}