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