]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_epoll.cpp
Merge pull request #1266 from Adam-/insp20+cloakip
[user/henk/code/inspircd.git] / src / socketengines / socketengine_epoll.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
6  *
7  * This file is part of InspIRCd.  InspIRCd is free software: you can
8  * redistribute it and/or modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation, version 2.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 #include <vector>
22 #include <string>
23 #include <map>
24 #include "inspircd.h"
25 #include "exitcodes.h"
26 #include "socketengine.h"
27 #include <sys/epoll.h>
28 #include <sys/resource.h>
29 #include <iostream>
30 #define EP_DELAY 5
31
32 /** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
33  */
34 class EPollEngine : public SocketEngine
35 {
36 private:
37         /** These are used by epoll() to hold socket events
38          */
39         struct epoll_event* events;
40         int EngineHandle;
41 public:
42         /** Create a new EPollEngine
43          */
44         EPollEngine();
45         /** Delete an EPollEngine
46          */
47         virtual ~EPollEngine();
48         virtual bool AddFd(EventHandler* eh, int event_mask);
49         virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
50         virtual void DelFd(EventHandler* eh);
51         virtual int DispatchEvents();
52         virtual std::string GetName();
53 };
54
55 EPollEngine::EPollEngine()
56 {
57         CurrentSetSize = 0;
58
59         struct rlimit limit;
60         if (!getrlimit(RLIMIT_NOFILE, &limit))
61         {
62                 MAX_DESCRIPTORS = limit.rlim_cur;
63         }
64         else
65         {
66                 ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
67                 std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
68                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
69         }
70
71         // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
72         EngineHandle = epoll_create(GetMaxFds() / 4);
73
74         if (EngineHandle == -1)
75         {
76                 ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
77                 ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
78                 std::cout << "ERROR: Could not initialize epoll socket engine: " << strerror(errno) << std::endl;
79                 std::cout << "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now." << std::endl;
80                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
81         }
82
83         ref = new EventHandler* [GetMaxFds()];
84         events = new struct epoll_event[GetMaxFds()];
85
86         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
87 }
88
89 EPollEngine::~EPollEngine()
90 {
91         this->Close(EngineHandle);
92         delete[] ref;
93         delete[] events;
94 }
95
96 static unsigned mask_to_epoll(int event_mask)
97 {
98         unsigned rv = 0;
99         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_POLL_WRITE | FD_WANT_SINGLE_WRITE))
100         {
101                 // we need to use standard polling on this FD
102                 if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
103                         rv |= EPOLLIN;
104                 if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
105                         rv |= EPOLLOUT;
106         }
107         else
108         {
109                 // we can use edge-triggered polling on this FD
110                 rv = EPOLLET;
111                 if (event_mask & (FD_WANT_FAST_READ | FD_WANT_EDGE_READ))
112                         rv |= EPOLLIN;
113                 if (event_mask & (FD_WANT_FAST_WRITE | FD_WANT_EDGE_WRITE))
114                         rv |= EPOLLOUT;
115         }
116         return rv;
117 }
118
119 bool EPollEngine::AddFd(EventHandler* eh, int event_mask)
120 {
121         int fd = eh->GetFd();
122         if ((fd < 0) || (fd > GetMaxFds() - 1))
123         {
124                 ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
125                 return false;
126         }
127
128         if (ref[fd])
129         {
130                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
131                 return false;
132         }
133
134         struct epoll_event ev;
135         memset(&ev,0,sizeof(ev));
136         ev.events = mask_to_epoll(event_mask);
137         ev.data.fd = fd;
138         int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
139         if (i < 0)
140         {
141                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno));
142                 return false;
143         }
144
145         ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
146
147         ref[fd] = eh;
148         SocketEngine::SetEventMask(eh, event_mask);
149         CurrentSetSize++;
150         return true;
151 }
152
153 void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
154 {
155         unsigned old_events = mask_to_epoll(old_mask);
156         unsigned new_events = mask_to_epoll(new_mask);
157         if (old_events != new_events)
158         {
159                 // ok, we actually have something to tell the kernel about
160                 struct epoll_event ev;
161                 memset(&ev,0,sizeof(ev));
162                 ev.events = new_events;
163                 ev.data.fd = eh->GetFd();
164                 epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
165         }
166 }
167
168 void EPollEngine::DelFd(EventHandler* eh)
169 {
170         int fd = eh->GetFd();
171         if ((fd < 0) || (fd > GetMaxFds() - 1))
172         {
173                 ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
174                 return;
175         }
176
177         struct epoll_event ev;
178         memset(&ev,0,sizeof(ev));
179         ev.data.fd = fd;
180         int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
181
182         if (i < 0)
183         {
184                 ServerInstance->Logs->Log("SOCKET",DEBUG,"epoll_ctl can't remove socket: %s", strerror(errno));
185         }
186
187         ref[fd] = NULL;
188
189         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
190         CurrentSetSize--;
191 }
192
193 int EPollEngine::DispatchEvents()
194 {
195         socklen_t codesize = sizeof(int);
196         int errcode;
197         int i = epoll_wait(EngineHandle, events, GetMaxFds() - 1, 1000);
198         ServerInstance->UpdateTime();
199
200         TotalEvents += i;
201
202         for (int j = 0; j < i; j++)
203         {
204                 EventHandler* eh = ref[events[j].data.fd];
205                 if (!eh)
206                 {
207                         ServerInstance->Logs->Log("SOCKET",DEBUG,"Got event on unknown fd: %d", events[j].data.fd);
208                         epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]);
209                         continue;
210                 }
211                 if (events[j].events & EPOLLHUP)
212                 {
213                         ErrorEvents++;
214                         eh->HandleEvent(EVENT_ERROR, 0);
215                         continue;
216                 }
217                 if (events[j].events & EPOLLERR)
218                 {
219                         ErrorEvents++;
220                         /* Get error number */
221                         if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
222                                 errcode = errno;
223                         eh->HandleEvent(EVENT_ERROR, errcode);
224                         continue;
225                 }
226                 int mask = eh->GetEventMask();
227                 if (events[j].events & EPOLLIN)
228                         mask &= ~FD_READ_WILL_BLOCK;
229                 if (events[j].events & EPOLLOUT)
230                 {
231                         mask &= ~FD_WRITE_WILL_BLOCK;
232                         if (mask & FD_WANT_SINGLE_WRITE)
233                         {
234                                 int nm = mask & ~FD_WANT_SINGLE_WRITE;
235                                 OnSetEvent(eh, mask, nm);
236                                 mask = nm;
237                         }
238                 }
239                 SetEventMask(eh, mask);
240                 if (events[j].events & EPOLLIN)
241                 {
242                         ReadEvents++;
243                         eh->HandleEvent(EVENT_READ);
244                         if (eh != ref[events[j].data.fd])
245                                 // whoa! we got deleted, better not give out the write event
246                                 continue;
247                 }
248                 if (events[j].events & EPOLLOUT)
249                 {
250                         WriteEvents++;
251                         eh->HandleEvent(EVENT_WRITE);
252                 }
253         }
254
255         return i;
256 }
257
258 std::string EPollEngine::GetName()
259 {
260         return "epoll";
261 }
262
263 SocketEngine* CreateSocketEngine()
264 {
265         return new EPollEngine;
266 }