]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_epoll.cpp
5552cccf6b2a9167f4f72f03b7445cbfa60ad682
[user/henk/code/inspircd.git] / src / socketengines / socketengine_epoll.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 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 <sys/epoll.h>
17 #include "socketengines/socketengine_epoll.h"
18 #include <ulimit.h>
19
20 EPollEngine::EPollEngine(InspIRCd* Instance) : SocketEngine(Instance)
21 {
22         MAX_DESCRIPTORS = 0;
23         // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
24         EngineHandle = epoll_create(GetMaxFds() / 4);
25
26         if (EngineHandle == -1)
27         {
28                 ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
29                 ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
30                 printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno));
31                 printf("ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.\n");
32                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
33         }
34         CurrentSetSize = 0;
35
36         ref = new EventHandler* [GetMaxFds()];
37         events = new struct epoll_event[GetMaxFds()];
38
39         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
40 }
41
42 EPollEngine::~EPollEngine()
43 {
44         this->Close(EngineHandle);
45         delete[] ref;
46         delete[] events;
47 }
48
49 bool EPollEngine::AddFd(EventHandler* eh)
50 {
51         int fd = eh->GetFd();
52         if ((fd < 0) || (fd > GetMaxFds() - 1))
53         {
54                 ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
55                 return false;
56         }
57
58         if (GetRemainingFds() <= 1)
59         {
60                 ServerInstance->Logs->Log("SOCKET",DEBUG,"No remaining FDs cannot add fd: %d", fd);
61                 return false;
62         }
63
64         if (ref[fd])
65         {
66                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
67                 return false;
68         }
69
70         struct epoll_event ev;
71         memset(&ev,0,sizeof(struct epoll_event));
72         eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT;
73         ev.data.fd = fd;
74         int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
75         if (i < 0)
76         {
77                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno));
78                 return false;
79         }
80
81         ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
82
83         ref[fd] = eh;
84         CurrentSetSize++;
85         return true;
86 }
87
88 void EPollEngine::WantWrite(EventHandler* eh)
89 {
90         /** Use oneshot so that the system removes the writeable
91          * status for us and saves us a call.
92          */
93         struct epoll_event ev;
94         memset(&ev,0,sizeof(struct epoll_event));
95         ev.events = EPOLLOUT;
96         ev.data.fd = eh->GetFd();
97         epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
98 }
99
100 bool EPollEngine::DelFd(EventHandler* eh, bool force)
101 {
102         int fd = eh->GetFd();
103         if ((fd < 0) || (fd > GetMaxFds() - 1))
104         {
105                 ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
106                 return false;
107         }
108
109         struct epoll_event ev;
110         memset(&ev,0,sizeof(struct epoll_event));
111         eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT;
112         ev.data.fd = fd;
113         int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
114
115         if (i < 0 && !force)
116         {
117                 ServerInstance->Logs->Log("SOCKET",DEBUG,"Cant remove socket: %s", strerror(errno));
118                 return false;
119         }
120
121         ref[fd] = NULL;
122         CurrentSetSize--;
123
124         ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
125         return true;
126 }
127
128 int EPollEngine::GetMaxFds()
129 {
130         if (MAX_DESCRIPTORS)
131                 return MAX_DESCRIPTORS;
132
133         int max = ulimit(4, 0);
134         if (max > 0)
135         {
136                 MAX_DESCRIPTORS = max;
137                 return max;
138         }
139         else
140         {
141                 ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
142                 printf("ERROR: Can't determine maximum number of open sockets!\n");
143                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
144         }
145         return 0;
146 }
147
148 int EPollEngine::GetRemainingFds()
149 {
150         return GetMaxFds() - CurrentSetSize;
151 }
152
153 int EPollEngine::DispatchEvents()
154 {
155         socklen_t codesize;
156         int errcode;
157         int i = epoll_wait(EngineHandle, events, GetMaxFds() - 1, 1000);
158
159         TotalEvents += i;
160
161         for (int j = 0; j < i; j++)
162         {
163                 if (events[j].events & EPOLLHUP)
164                 {
165                         ErrorEvents++;
166                         if (ref[events[j].data.fd])
167                                 ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, 0);
168                         continue;
169                 }
170                 if (events[j].events & EPOLLERR)
171                 {
172                         ErrorEvents++;
173                         /* Get error number */
174                         if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
175                                 errcode = errno;
176                         if (ref[events[j].data.fd])
177                                 ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, errcode);
178                         continue;
179                 }
180                 if (events[j].events & EPOLLOUT)
181                 {
182                         WriteEvents++;
183                         struct epoll_event ev;
184                         memset(&ev,0,sizeof(struct epoll_event));
185                         ev.events = EPOLLIN;
186                         ev.data.fd = events[j].data.fd;
187                         epoll_ctl(EngineHandle, EPOLL_CTL_MOD, events[j].data.fd, &ev);
188                         if (ref[events[j].data.fd])
189                                 ref[events[j].data.fd]->HandleEvent(EVENT_WRITE);
190                 }
191                 else
192                 {
193                         ReadEvents++;
194                         if (ref[events[j].data.fd])
195                                 ref[events[j].data.fd]->HandleEvent(EVENT_READ);
196                 }
197         }
198
199         return i;
200 }
201
202 std::string EPollEngine::GetName()
203 {
204         return "epoll";
205 }
206