]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_poll.cpp
8fa0a13c5ad256958f9bde0f843d2b5aac5b6084
[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 #include <sys/poll.h>
33 #include <sys/resource.h>
34
35 /** A specialisation of the SocketEngine class, designed to use poll().
36  */
37 namespace
38 {
39         /** These are used by poll() to hold socket events
40          */
41         std::vector<struct pollfd> events(16);
42         /** This vector maps fds to an index in the events array.
43          */
44         std::vector<int> fd_mappings(16);
45 }
46
47 void SocketEngine::Init()
48 {
49         struct rlimit limits;
50         if (!getrlimit(RLIMIT_NOFILE, &limits))
51         {
52                 MAX_DESCRIPTORS = limits.rlim_cur;
53         }
54         else
55         {
56                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
57                 std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
58                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
59         }
60 }
61
62 void SocketEngine::Deinit()
63 {
64 }
65
66 void SocketEngine::RecoverFromFork()
67 {
68 }
69
70 static int mask_to_poll(int event_mask)
71 {
72         int rv = 0;
73         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
74                 rv |= POLLIN;
75         if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
76                 rv |= POLLOUT;
77         return rv;
78 }
79
80 bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
81 {
82         int fd = eh->GetFd();
83         if ((fd < 0) || (fd > GetMaxFds() - 1))
84         {
85                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
86                 return false;
87         }
88
89         if (static_cast<unsigned int>(fd) < fd_mappings.size() && fd_mappings[fd] != -1)
90         {
91                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
92                 return false;
93         }
94
95         unsigned int index = CurrentSetSize;
96
97         if (!SocketEngine::AddFdRef(eh))
98         {
99                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
100                 return false;
101         }
102
103         while (static_cast<unsigned int>(fd) >= fd_mappings.size())
104                 fd_mappings.resize(fd_mappings.size() * 2, -1);
105         fd_mappings[fd] = index;
106
107         ResizeDouble(events);
108         events[index].fd = fd;
109         events[index].events = mask_to_poll(event_mask);
110
111         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
112         eh->SetEventMask(event_mask);
113         return true;
114 }
115
116 void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
117 {
118         int fd = eh->GetFd();
119         if (fd < 0 || static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
120         {
121                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
122                 return;
123         }
124
125         events[fd_mappings[fd]].events = mask_to_poll(new_mask);
126 }
127
128 void SocketEngine::DelFd(EventHandler* eh)
129 {
130         int fd = eh->GetFd();
131         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
132         {
133                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
134                 return;
135         }
136
137         if (static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
138         {
139                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
140                 return;
141         }
142
143         unsigned int index = fd_mappings[fd];
144         unsigned int last_index = CurrentSetSize - 1;
145         int last_fd = events[last_index].fd;
146
147         if (index != last_index)
148         {
149                 // We need to move the last fd we got into this gap (gaps are evil!)
150
151                 // So update the mapping for the last fd to its new position
152                 fd_mappings[last_fd] = index;
153
154                 // move last_fd from last_index into index
155                 events[index].fd = last_fd;
156                 events[index].events = events[last_index].events;
157         }
158
159         // Now remove all data for the last fd we got into out list.
160         // Above code made sure this always is right
161         fd_mappings[fd] = -1;
162         events[last_index].fd = 0;
163         events[last_index].events = 0;
164
165         SocketEngine::DelFdRef(eh);
166
167         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
168                         "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
169 }
170
171 int SocketEngine::DispatchEvents()
172 {
173         int i = poll(&events[0], CurrentSetSize, 1000);
174         int processed = 0;
175         ServerInstance->UpdateTime();
176
177         for (int index = 0; index < CurrentSetSize && processed < i; index++)
178         {
179                 struct pollfd& pfd = events[index];
180
181                 // Copy these in case the vector gets resized and pfd invalidated
182                 const int fd = pfd.fd;
183                 const short revents = pfd.revents;
184
185                 if (revents)
186                         processed++;
187
188                 EventHandler* eh = GetRef(fd);
189                 if (!eh)
190                         continue;
191
192                 if (revents & POLLHUP)
193                 {
194                         eh->HandleEvent(EVENT_ERROR, 0);
195                         continue;
196                 }
197
198                 if (revents & POLLERR)
199                 {
200                         // Get error number
201                         socklen_t codesize = sizeof(int);
202                         int errcode;
203                         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
204                                 errcode = errno;
205                         eh->HandleEvent(EVENT_ERROR, errcode);
206                         continue;
207                 }
208
209                 if (revents & POLLIN)
210                 {
211                         eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
212                         eh->HandleEvent(EVENT_READ);
213                         if (eh != GetRef(fd))
214                                 // whoops, deleted out from under us
215                                 continue;
216                 }
217
218                 if (revents & POLLOUT)
219                 {
220                         int mask = eh->GetEventMask();
221                         mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
222                         eh->SetEventMask(mask);
223
224                         // The vector could've been resized, reference can be invalid by now; don't use it
225                         events[index].events = mask_to_poll(mask);
226                         eh->HandleEvent(EVENT_WRITE);
227                 }
228         }
229
230         return i;
231 }