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