]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_poll.cpp
Change all socketengine methods to be static
[user/henk/code/inspircd.git] / src / socketengines / socketengine_poll.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2014 Adam <Adam@anope.org>
5  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
6  *   Copyright (C) 2009 Uli Schlachter <psychon@znc.in>
7  *   Copyright (C) 2009 Craig Edwards <craigedwards@brainbox.cc>
8  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
9  *
10  * This file is part of InspIRCd.  InspIRCd is free software: you can
11  * redistribute it and/or modify it under the terms of the GNU General Public
12  * License as published by the Free Software Foundation, version 2.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23
24 #include <iostream>
25 #include <vector>
26 #include <string>
27 #include <map>
28 #include "exitcodes.h"
29 #include "inspircd.h"
30 #include "socketengine.h"
31
32 #ifndef _WIN32
33 # ifndef __USE_XOPEN
34 #  define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
35 # endif
36 # include <poll.h>
37 # include <sys/poll.h>
38 # include <sys/resource.h>
39 #else
40 # define struct pollfd WSAPOLLFD
41 # define poll WSAPoll
42 #endif
43
44 /** A specialisation of the SocketEngine class, designed to use poll().
45  */
46 namespace
47 {
48         /** These are used by poll() to hold socket events
49          */
50         std::vector<struct pollfd> events(16);
51         /** This vector maps fds to an index in the events array.
52          */
53         std::vector<int> fd_mappings(16);
54 }
55
56 void SocketEngine::Init()
57 {
58         struct rlimit limits;
59         if (!getrlimit(RLIMIT_NOFILE, &limits))
60         {
61                 MAX_DESCRIPTORS = limits.rlim_cur;
62         }
63         else
64         {
65                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
66                 std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
67                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
68         }
69 }
70
71 void SocketEngine::Deinit()
72 {
73 }
74
75 void SocketEngine::RecoverFromFork()
76 {
77 }
78
79 static int mask_to_poll(int event_mask)
80 {
81         int rv = 0;
82         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
83                 rv |= POLLIN;
84         if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
85                 rv |= POLLOUT;
86         return rv;
87 }
88
89 bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
90 {
91         int fd = eh->GetFd();
92         if ((fd < 0) || (fd > GetMaxFds() - 1))
93         {
94                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
95                 return false;
96         }
97
98         if (static_cast<unsigned int>(fd) < fd_mappings.size() && fd_mappings[fd] != -1)
99         {
100                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
101                 return false;
102         }
103
104         unsigned int index = CurrentSetSize;
105
106         if (!SocketEngine::AddFdRef(eh))
107         {
108                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
109                 return false;
110         }
111
112         while (static_cast<unsigned int>(fd) >= fd_mappings.size())
113                 fd_mappings.resize(fd_mappings.size() * 2, -1);
114         fd_mappings[fd] = index;
115
116         ResizeDouble(events);
117         events[index].fd = fd;
118         events[index].events = mask_to_poll(event_mask);
119
120         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
121         eh->SetEventMask(event_mask);
122         return true;
123 }
124
125 void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
126 {
127         int fd = eh->GetFd();
128         if (fd < 0 || static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
129         {
130                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
131                 return;
132         }
133
134         events[fd_mappings[fd]].events = mask_to_poll(new_mask);
135 }
136
137 void SocketEngine::DelFd(EventHandler* eh)
138 {
139         int fd = eh->GetFd();
140         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
141         {
142                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
143                 return;
144         }
145
146         if (static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
147         {
148                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
149                 return;
150         }
151
152         unsigned int index = fd_mappings[fd];
153         unsigned int last_index = CurrentSetSize - 1;
154         int last_fd = events[last_index].fd;
155
156         if (index != last_index)
157         {
158                 // We need to move the last fd we got into this gap (gaps are evil!)
159
160                 // So update the mapping for the last fd to its new position
161                 fd_mappings[last_fd] = index;
162
163                 // move last_fd from last_index into index
164                 events[index].fd = last_fd;
165                 events[index].events = events[last_index].events;
166         }
167
168         // Now remove all data for the last fd we got into out list.
169         // Above code made sure this always is right
170         fd_mappings[fd] = -1;
171         events[last_index].fd = 0;
172         events[last_index].events = 0;
173
174         SocketEngine::DelFdRef(eh);
175
176         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
177                         "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
178 }
179
180 int SocketEngine::DispatchEvents()
181 {
182         int i = poll(&events[0], CurrentSetSize, 1000);
183         int processed = 0;
184         ServerInstance->UpdateTime();
185
186         for (int index = 0; index < CurrentSetSize && processed < i; index++)
187         {
188                 struct pollfd& pfd = events[index];
189
190                 // Copy these in case the vector gets resized and pfd invalidated
191                 const int fd = pfd.fd;
192                 const short revents = pfd.revents;
193
194                 if (revents)
195                         processed++;
196
197                 EventHandler* eh = GetRef(fd);
198                 if (!eh)
199                         continue;
200
201                 if (revents & POLLHUP)
202                 {
203                         eh->HandleEvent(EVENT_ERROR, 0);
204                         continue;
205                 }
206
207                 if (revents & POLLERR)
208                 {
209                         // Get error number
210                         socklen_t codesize = sizeof(int);
211                         int errcode;
212                         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
213                                 errcode = errno;
214                         eh->HandleEvent(EVENT_ERROR, errcode);
215                         continue;
216                 }
217
218                 if (revents & POLLIN)
219                 {
220                         eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
221                         eh->HandleEvent(EVENT_READ);
222                         if (eh != GetRef(fd))
223                                 // whoops, deleted out from under us
224                                 continue;
225                 }
226
227                 if (revents & POLLOUT)
228                 {
229                         int mask = eh->GetEventMask();
230                         mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
231                         eh->SetEventMask(mask);
232
233                         // The vector could've been resized, reference can be invalid by now; don't use it
234                         events[index].events = mask_to_poll(mask);
235                         eh->HandleEvent(EVENT_WRITE);
236                 }
237         }
238
239         return i;
240 }