]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_epoll.cpp
Add support for blocking tag messages with the deaf mode.
[user/henk/code/inspircd.git] / src / socketengines / socketengine_epoll.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2014-2015 Attila Molnar <attilamolnar@hush.com>
5  *   Copyright (C) 2014, 2016 Adam <Adam@anope.org>
6  *   Copyright (C) 2013, 2017, 2019 Sadie Powell <sadie@witchery.services>
7  *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
8  *   Copyright (C) 2012 Ariadne Conill <ariadne@dereferenced.org>
9  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
10  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
11  *   Copyright (C) 2007-2008 Dennis Friis <peavey@inspircd.org>
12  *   Copyright (C) 2006-2008 Craig Edwards <brain@inspircd.org>
13  *   Copyright (C) 2006, 2008 Robin Burchell <robin+git@viroteck.net>
14  *
15  * This file is part of InspIRCd.  InspIRCd is free software: you can
16  * redistribute it and/or modify it under the terms of the GNU General Public
17  * License as published by the Free Software Foundation, version 2.
18  *
19  * This program is distributed in the hope that it will be useful, but WITHOUT
20  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22  * details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26  */
27
28
29 #include "inspircd.h"
30
31 #include <sys/epoll.h>
32 #include <sys/resource.h>
33
34 /** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
35  */
36 namespace
37 {
38         int EngineHandle;
39
40         /** These are used by epoll() to hold socket events
41          */
42         std::vector<struct epoll_event> events(16);
43 }
44
45 void SocketEngine::Init()
46 {
47         LookupMaxFds();
48
49         // 128 is not a maximum, just a hint at the eventual number of sockets that may be polled,
50         // and it is completely ignored by 2.6.8 and later kernels, except it must be larger than zero.
51         EngineHandle = epoll_create(128);
52         if (EngineHandle == -1)
53                 InitError();
54 }
55
56 void SocketEngine::RecoverFromFork()
57 {
58 }
59
60 void SocketEngine::Deinit()
61 {
62         Close(EngineHandle);
63 }
64
65 static unsigned mask_to_epoll(int event_mask)
66 {
67         unsigned rv = 0;
68         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_POLL_WRITE | FD_WANT_SINGLE_WRITE))
69         {
70                 // we need to use standard polling on this FD
71                 if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
72                         rv |= EPOLLIN;
73                 if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
74                         rv |= EPOLLOUT;
75         }
76         else
77         {
78                 // we can use edge-triggered polling on this FD
79                 rv = EPOLLET;
80                 if (event_mask & (FD_WANT_FAST_READ | FD_WANT_EDGE_READ))
81                         rv |= EPOLLIN;
82                 if (event_mask & (FD_WANT_FAST_WRITE | FD_WANT_EDGE_WRITE))
83                         rv |= EPOLLOUT;
84         }
85         return rv;
86 }
87
88 bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
89 {
90         int fd = eh->GetFd();
91         if (fd < 0)
92         {
93                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd);
94                 return false;
95         }
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         struct epoll_event ev;
104         memset(&ev, 0, sizeof(ev));
105         ev.events = mask_to_epoll(event_mask);
106         ev.data.ptr = static_cast<void*>(eh);
107         int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
108         if (i < 0)
109         {
110                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error adding fd: %d to socketengine: %s", fd, strerror(errno));
111                 return false;
112         }
113
114         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
115
116         eh->SetEventMask(event_mask);
117         ResizeDouble(events);
118
119         return true;
120 }
121
122 void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
123 {
124         unsigned old_events = mask_to_epoll(old_mask);
125         unsigned new_events = mask_to_epoll(new_mask);
126         if (old_events != new_events)
127         {
128                 // ok, we actually have something to tell the kernel about
129                 struct epoll_event ev;
130                 memset(&ev, 0, sizeof(ev));
131                 ev.events = new_events;
132                 ev.data.ptr = static_cast<void*>(eh);
133                 epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
134         }
135 }
136
137 void SocketEngine::DelFd(EventHandler* eh)
138 {
139         int fd = eh->GetFd();
140         if (fd < 0)
141         {
142                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd);
143                 return;
144         }
145
146         // Do not initialize epoll_event because for EPOLL_CTL_DEL operations the event is ignored and can be NULL.
147         // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required a non-NULL pointer in event,
148         // even though this argument is ignored. Since Linux 2.6.9, event can be specified as NULL when using EPOLL_CTL_DEL.
149         struct epoll_event ev;
150         int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
151
152         if (i < 0)
153         {
154                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "epoll_ctl can't remove socket: %s", strerror(errno));
155         }
156
157         SocketEngine::DelFdRef(eh);
158
159         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
160 }
161
162 int SocketEngine::DispatchEvents()
163 {
164         int i = epoll_wait(EngineHandle, &events[0], events.size(), 1000);
165         ServerInstance->UpdateTime();
166
167         stats.TotalEvents += i;
168
169         for (int j = 0; j < i; j++)
170         {
171                 // Copy these in case the vector gets resized and ev invalidated
172                 const epoll_event ev = events[j];
173
174                 EventHandler* const eh = static_cast<EventHandler*>(ev.data.ptr);
175                 const int fd = eh->GetFd();
176                 if (fd < 0)
177                         continue;
178
179                 if (ev.events & EPOLLHUP)
180                 {
181                         stats.ErrorEvents++;
182                         eh->OnEventHandlerError(0);
183                         continue;
184                 }
185
186                 if (ev.events & EPOLLERR)
187                 {
188                         stats.ErrorEvents++;
189                         /* Get error number */
190                         socklen_t codesize = sizeof(int);
191                         int errcode;
192                         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
193                                 errcode = errno;
194                         eh->OnEventHandlerError(errcode);
195                         continue;
196                 }
197
198                 int mask = eh->GetEventMask();
199                 if (ev.events & EPOLLIN)
200                         mask &= ~FD_READ_WILL_BLOCK;
201                 if (ev.events & EPOLLOUT)
202                 {
203                         mask &= ~FD_WRITE_WILL_BLOCK;
204                         if (mask & FD_WANT_SINGLE_WRITE)
205                         {
206                                 int nm = mask & ~FD_WANT_SINGLE_WRITE;
207                                 OnSetEvent(eh, mask, nm);
208                                 mask = nm;
209                         }
210                 }
211                 eh->SetEventMask(mask);
212                 if (ev.events & EPOLLIN)
213                 {
214                         eh->OnEventHandlerRead();
215                         if (eh != GetRef(fd))
216                                 // whoa! we got deleted, better not give out the write event
217                                 continue;
218                 }
219                 if (ev.events & EPOLLOUT)
220                 {
221                         eh->OnEventHandlerWrite();
222                 }
223         }
224
225         return i;
226 }