]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_poll.cpp
Add support for blocking tag messages with the deaf mode.
[user/henk/code/inspircd.git] / src / socketengines / socketengine_poll.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2014-2015 Attila Molnar <attilamolnar@hush.com>
5  *   Copyright (C) 2014, 2017 Adam <Adam@anope.org>
6  *   Copyright (C) 2013, 2016-2017 Sadie Powell <sadie@witchery.services>
7  *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
8  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
9  *   Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
10  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
11  *
12  * This file is part of InspIRCd.  InspIRCd is free software: you can
13  * redistribute it and/or modify it under the terms of the GNU General Public
14  * License as published by the Free Software Foundation, version 2.
15  *
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24
25
26 #include "inspircd.h"
27
28 #include <sys/poll.h>
29 #include <sys/resource.h>
30
31 /** A specialisation of the SocketEngine class, designed to use poll().
32  */
33 namespace
34 {
35         /** These are used by poll() to hold socket events
36          */
37         std::vector<struct pollfd> events(16);
38         /** This vector maps fds to an index in the events array.
39          */
40         std::vector<int> fd_mappings(16, -1);
41 }
42
43 void SocketEngine::Init()
44 {
45         LookupMaxFds();
46 }
47
48 void SocketEngine::Deinit()
49 {
50 }
51
52 void SocketEngine::RecoverFromFork()
53 {
54 }
55
56 static int mask_to_poll(int event_mask)
57 {
58         int rv = 0;
59         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
60                 rv |= POLLIN;
61         if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
62                 rv |= POLLOUT;
63         return rv;
64 }
65
66 bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
67 {
68         int fd = eh->GetFd();
69         if (fd < 0)
70         {
71                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd);
72                 return false;
73         }
74
75         if (static_cast<unsigned int>(fd) < fd_mappings.size() && fd_mappings[fd] != -1)
76         {
77                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
78                 return false;
79         }
80
81         unsigned int index = CurrentSetSize;
82
83         if (!SocketEngine::AddFdRef(eh))
84         {
85                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
86                 return false;
87         }
88
89         while (static_cast<unsigned int>(fd) >= fd_mappings.size())
90                 fd_mappings.resize(fd_mappings.size() * 2, -1);
91         fd_mappings[fd] = index;
92
93         ResizeDouble(events);
94         events[index].fd = fd;
95         events[index].events = mask_to_poll(event_mask);
96
97         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[index].events, index);
98         eh->SetEventMask(event_mask);
99         return true;
100 }
101
102 void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
103 {
104         int fd = eh->GetFd();
105         if (fd < 0 || static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
106         {
107                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
108                 return;
109         }
110
111         events[fd_mappings[fd]].events = mask_to_poll(new_mask);
112 }
113
114 void SocketEngine::DelFd(EventHandler* eh)
115 {
116         int fd = eh->GetFd();
117         if (fd < 0)
118         {
119                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd);
120                 return;
121         }
122
123         if (static_cast<unsigned int>(fd) >= fd_mappings.size() || fd_mappings[fd] == -1)
124         {
125                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
126                 return;
127         }
128
129         unsigned int index = fd_mappings[fd];
130         unsigned int last_index = CurrentSetSize - 1;
131         int last_fd = events[last_index].fd;
132
133         if (index != last_index)
134         {
135                 // We need to move the last fd we got into this gap (gaps are evil!)
136
137                 // So update the mapping for the last fd to its new position
138                 fd_mappings[last_fd] = index;
139
140                 // move last_fd from last_index into index
141                 events[index].fd = last_fd;
142                 events[index].events = events[last_index].events;
143         }
144
145         // Now remove all data for the last fd we got into out list.
146         // Above code made sure this always is right
147         fd_mappings[fd] = -1;
148         events[last_index].fd = 0;
149         events[last_index].events = 0;
150
151         SocketEngine::DelFdRef(eh);
152
153         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
154                         "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
155 }
156
157 int SocketEngine::DispatchEvents()
158 {
159         int i = poll(&events[0], CurrentSetSize, 1000);
160         int processed = 0;
161         ServerInstance->UpdateTime();
162
163         for (size_t index = 0; index < CurrentSetSize && processed < i; index++)
164         {
165                 struct pollfd& pfd = events[index];
166
167                 // Copy these in case the vector gets resized and pfd invalidated
168                 const int fd = pfd.fd;
169                 const short revents = pfd.revents;
170
171                 if (revents)
172                         processed++;
173
174                 EventHandler* eh = GetRef(fd);
175                 if (!eh)
176                         continue;
177
178                 if (revents & POLLHUP)
179                 {
180                         eh->OnEventHandlerError(0);
181                         continue;
182                 }
183
184                 if (revents & POLLERR)
185                 {
186                         // Get error number
187                         socklen_t codesize = sizeof(int);
188                         int errcode;
189                         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
190                                 errcode = errno;
191                         eh->OnEventHandlerError(errcode);
192                         continue;
193                 }
194
195                 if (revents & POLLIN)
196                 {
197                         eh->SetEventMask(eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
198                         eh->OnEventHandlerRead();
199                         if (eh != GetRef(fd))
200                                 // whoops, deleted out from under us
201                                 continue;
202                 }
203
204                 if (revents & POLLOUT)
205                 {
206                         int mask = eh->GetEventMask();
207                         mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE);
208                         eh->SetEventMask(mask);
209
210                         // The vector could've been resized, reference can be invalid by now; don't use it
211                         events[index].events = mask_to_poll(mask);
212                         eh->OnEventHandlerWrite();
213                 }
214         }
215
216         return i;
217 }