]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_select.cpp
cmd_quit Display quit messages of remote users
[user/henk/code/inspircd.git] / src / socketengines / socketengine_select.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) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
7  *
8  * This file is part of InspIRCd.  InspIRCd is free software: you can
9  * redistribute it and/or modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation, version 2.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21
22 #include "inspircd.h"
23 #include "socketengine.h"
24
25 #ifndef _WIN32
26 #include <sys/select.h>
27 #endif // _WIN32
28
29 /** A specialisation of the SocketEngine class, designed to use traditional select().
30  */
31 namespace
32 {
33         fd_set ReadSet, WriteSet, ErrSet;
34         int MaxFD = 0;
35 }
36
37 void SocketEngine::Init()
38 {
39         MAX_DESCRIPTORS = FD_SETSIZE;
40
41         FD_ZERO(&ReadSet);
42         FD_ZERO(&WriteSet);
43         FD_ZERO(&ErrSet);
44 }
45
46 void SocketEngine::Deinit()
47 {
48 }
49
50 void SocketEngine::RecoverFromFork()
51 {
52 }
53
54 bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
55 {
56         int fd = eh->GetFd();
57         if ((fd < 0) || (fd > GetMaxFds() - 1))
58                 return false;
59
60         if (!SocketEngine::AddFdRef(eh))
61                 return false;
62
63         eh->SetEventMask(event_mask);
64         OnSetEvent(eh, 0, event_mask);
65         FD_SET(fd, &ErrSet);
66         if (fd > MaxFD)
67                 MaxFD = fd;
68
69         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
70         return true;
71 }
72
73 void SocketEngine::DelFd(EventHandler* eh)
74 {
75         int fd = eh->GetFd();
76
77         if ((fd < 0) || (fd > GetMaxFds() - 1))
78                 return;
79
80         SocketEngine::DelFdRef(eh);
81
82         FD_CLR(fd, &ReadSet);
83         FD_CLR(fd, &WriteSet);
84         FD_CLR(fd, &ErrSet);
85         if (fd == MaxFD)
86                 --MaxFD;
87
88         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
89 }
90
91 void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
92 {
93         int fd = eh->GetFd();
94         int diff = old_mask ^ new_mask;
95
96         if (diff & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
97         {
98                 if (new_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
99                         FD_SET(fd, &ReadSet);
100                 else
101                         FD_CLR(fd, &ReadSet);
102         }
103         if (diff & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
104         {
105                 if (new_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
106                         FD_SET(fd, &WriteSet);
107                 else
108                         FD_CLR(fd, &WriteSet);
109         }
110 }
111
112 int SocketEngine::DispatchEvents()
113 {
114         static timeval tval = { 1, 0 };
115
116         fd_set rfdset = ReadSet, wfdset = WriteSet, errfdset = ErrSet;
117
118         int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval);
119         ServerInstance->UpdateTime();
120
121         for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++)
122         {
123                 int has_read = FD_ISSET(i, &rfdset), has_write = FD_ISSET(i, &wfdset), has_error = FD_ISSET(i, &errfdset);
124
125                 if (!(has_read || has_write || has_error))
126                         continue;
127
128                 --j;
129
130                 EventHandler* ev = GetRef(i);
131                 if (!ev)
132                         continue;
133
134                 if (has_error)
135                 {
136                         stats.ErrorEvents++;
137
138                         socklen_t codesize = sizeof(int);
139                         int errcode = 0;
140                         if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
141                                 errcode = errno;
142
143                         ev->HandleEvent(EVENT_ERROR, errcode);
144                         continue;
145                 }
146
147                 if (has_read)
148                 {
149                         stats.ReadEvents++;
150                         ev->SetEventMask(ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
151                         ev->HandleEvent(EVENT_READ);
152                         if (ev != GetRef(i))
153                                 continue;
154                 }
155
156                 if (has_write)
157                 {
158                         stats.WriteEvents++;
159                         int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
160                         SocketEngine::OnSetEvent(ev, ev->GetEventMask(), newmask);
161                         ev->SetEventMask(newmask);
162                         ev->HandleEvent(EVENT_WRITE);
163                 }
164         }
165
166         return sresult;
167 }