]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_poll.cpp
Fix the poll socketengine to actually work.
[user/henk/code/inspircd.git] / src / socketengines / socketengine_poll.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *    the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "exitcodes.h"
16 #include "socketengines/socketengine_poll.h"
17 #include <ulimit.h>
18 #ifdef __FreeBSD__
19         #include <sys/sysctl.h>
20 #endif
21
22 PollEngine::PollEngine(InspIRCd* Instance) : SocketEngine(Instance)
23 {
24         // Poll requires no special setup (which is nice).
25         CurrentSetSize = 0;
26         MAX_DESCRIPTORS = 0;
27
28         ref = new EventHandler* [GetMaxFds()];
29         events = new struct pollfd[GetMaxFds()];
30
31         memset(events, 0, GetMaxFds() * sizeof(struct pollfd));
32         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
33 }
34
35 PollEngine::~PollEngine()
36 {
37         // No destruction required, either.
38         delete[] ref;
39         delete[] events;
40 }
41
42 bool PollEngine::AddFd(EventHandler* eh)
43 {
44         int fd = eh->GetFd();
45         if ((fd < 0) || (fd > GetMaxFds() - 1))
46         {
47                 ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
48                 return false;
49         }
50
51         if (GetRemainingFds() <= 1)
52         {
53                 ServerInstance->Logs->Log("SOCKET",DEBUG,"No remaining FDs cannot add fd: %d", fd);
54                 return false;
55         }
56
57         if (fd_mappings.find(fd) != fd_mappings.end())
58         {
59                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
60                 return false;
61         }
62
63         unsigned int index = CurrentSetSize;
64
65         fd_mappings[fd] = index;
66         ref[index] = eh;
67         events[index].fd = fd;
68         if (eh->Readable())
69         {
70                 events[index].events = POLLIN;
71         }
72         else
73         {
74                 events[index].events = POLLOUT;
75         }
76
77         ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
78         CurrentSetSize++;
79         return true;
80 }
81
82 EventHandler* PollEngine::GetRef(int fd)
83 {
84         std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
85         if (it == fd_mappings.end())
86                 return NULL;
87         return ref[it->second];
88 }
89
90 void PollEngine::WantWrite(EventHandler* eh)
91 {
92         std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
93         if (it == fd_mappings.end())
94         {
95                 ServerInstance->Logs->Log("SOCKET",DEBUG,"WantWrite() on unknown fd: %d", eh->GetFd());
96                 return;
97         }
98
99         events[it->second].events = POLLIN | POLLOUT;
100 }
101
102 bool PollEngine::DelFd(EventHandler* eh, bool force)
103 {
104         int fd = eh->GetFd();
105         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
106         {
107                 ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
108                 return false;
109         }
110
111         std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
112         if (it == fd_mappings.end())
113         {
114                 ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd() on unknown fd: %d", fd);
115                 return false;
116         }
117
118         unsigned int index = it->second;
119         unsigned int last_index = CurrentSetSize - 1;
120         int last_fd = events[last_index].fd;
121
122         if (index != last_index)
123         {
124                 // We need to move the last fd we got into this gap (gaps are evil!)
125
126                 // So update the mapping for the last fd to its new position
127                 fd_mappings[last_fd] = index;
128
129                 // move last_fd from last_index into index
130                 events[index].fd = last_fd;
131                 events[index].events = events[last_index].events;
132
133                 ref[index] = ref[last_index];
134         }
135
136         // Now remove all data for the last fd we got into out list.
137         // Above code made sure this always is right
138         fd_mappings.erase(it);
139         events[last_index].fd = 0;
140         events[last_index].events = 0;
141         ref[last_index] = NULL;
142
143         CurrentSetSize--;
144
145         ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d (index: %d) "
146                         "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
147         return true;
148 }
149
150 int PollEngine::GetMaxFds()
151 {
152 #ifndef __FreeBSD__
153         if (MAX_DESCRIPTORS)
154                 return MAX_DESCRIPTORS;
155
156         int max = ulimit(4, 0);
157         if (max > 0)
158         {
159                 MAX_DESCRIPTORS = max;
160                 return max;
161         }
162         else
163         {
164                 MAX_DESCRIPTORS = 0;
165                 ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
166                 printf("ERROR: Can't determine maximum number of open sockets: %s\n", strerror(errno));
167                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
168         }
169         return 0;
170 #else
171         if (!MAX_DESCRIPTORS)
172         {
173                 int mib[2], maxfiles;
174                 size_t len;
175
176                 mib[0] = CTL_KERN;
177                 mib[1] = KERN_MAXFILES;
178                 len = sizeof(maxfiles);
179                 sysctl(mib, 2, &maxfiles, &len, NULL, 0);
180                 MAX_DESCRIPTORS = maxfiles;
181                 return maxfiles;
182         }
183         return MAX_DESCRIPTORS;
184 #endif
185 }
186
187 int PollEngine::GetRemainingFds()
188 {
189         return MAX_DESCRIPTORS - CurrentSetSize;
190 }
191
192 int PollEngine::DispatchEvents()
193 {
194         int i = poll(events, CurrentSetSize, 1000);
195         int index;
196         socklen_t codesize = sizeof(int);
197         int errcode;
198         int processed = 0;
199
200         if (i > 0)
201         {
202                 for (index = 0; index < CurrentSetSize && processed != i; index++)
203                 {
204                         if (events[index].revents)
205                                 processed++;
206
207                         if (events[index].revents & POLLHUP)
208                         {
209                                 if (ref[index])
210                                         ref[index]->HandleEvent(EVENT_ERROR, 0);
211                                 continue;
212                         }
213
214                         if (events[index].revents & POLLERR)
215                         {
216                                 // Get fd
217                                 int fd = events[index].fd;
218
219                                 // Get error number
220                                 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
221                                         errcode = errno;
222                                 if (ref[index])
223                                         ref[index]->HandleEvent(EVENT_ERROR, errcode);
224                                 continue;
225                         }
226
227                         if (events[index].revents & POLLOUT)
228                         {
229                                 // Switch to wanting read again
230                                 // event handlers have to request to write again if they need it
231                                 events[index].events = POLLIN;
232
233                                 if (ref[index])
234                                         ref[index]->HandleEvent(EVENT_WRITE);
235                         }
236
237                         if (events[index].revents & POLLIN)
238                         {
239                                 if (ref[index])
240                                         ref[index]->HandleEvent(EVENT_READ);
241                         }
242                 }
243         }
244
245         return i;
246 }
247
248 std::string PollEngine::GetName()
249 {
250         return "poll";
251 }
252