/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2009 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/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 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 (ref[fd]) { ServerInstance->Logs->Log("SOCKET",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; } ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d)", fd, events[fd].events); CurrentSetSize++; return true; } void PollEngine::WantWrite(EventHandler* eh) { events[eh->GetFd()].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; } events[fd].fd = -1; events[fd].events = 0; CurrentSetSize--; ref[fd] = NULL; ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d", fd); return true; } int PollEngine::GetMaxFds() { #ifdef LINUX 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; #endif #ifdef FREEBSD 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, GetMaxFds() - 1, 1000); int fd = 0; socklen_t codesize = sizeof(int); int errcode; int processed = 0; if (i > 0) { for (fd = 0; fd < GetMaxFds() - 1 && processed != i; fd++) { if (events[fd].revents) processed++; 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; } 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 (ref[fd]) ref[fd]->HandleEvent(EVENT_WRITE); } if (events[fd].revents & POLLIN) { if (ref[fd]) ref[fd]->HandleEvent(EVENT_READ); } } } return i; } std::string PollEngine::GetName() { return "poll"; }