]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_select.cpp
Initialise Winsock from inside the socket engine.
[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
24 #ifndef _WIN32
25 #include <sys/select.h>
26 #endif // _WIN32
27
28 /** A specialisation of the SocketEngine class, designed to use traditional select().
29  */
30 namespace
31 {
32         fd_set ReadSet, WriteSet, ErrSet;
33         int MaxFD = 0;
34 }
35
36 void SocketEngine::Init()
37 {
38 #ifdef _WIN32
39         // Set up winsock.
40         WSADATA wsadata;
41         WSAStartup(MAKEWORD(2,2), &wsadata);
42 #endif
43
44         MaxSetSize = FD_SETSIZE;
45
46         FD_ZERO(&ReadSet);
47         FD_ZERO(&WriteSet);
48         FD_ZERO(&ErrSet);
49 }
50
51 void SocketEngine::Deinit()
52 {
53 }
54
55 void SocketEngine::RecoverFromFork()
56 {
57 }
58
59 bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
60 {
61         int fd = eh->GetFd();
62
63         if (fd < 0)
64                 return false;
65
66         if (static_cast<size_t>(fd) >= GetMaxFds())
67                 return false;
68
69         if (!SocketEngine::AddFdRef(eh))
70                 return false;
71
72         eh->SetEventMask(event_mask);
73         OnSetEvent(eh, 0, event_mask);
74         FD_SET(fd, &ErrSet);
75         if (fd > MaxFD)
76                 MaxFD = fd;
77
78         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
79         return true;
80 }
81
82 void SocketEngine::DelFd(EventHandler* eh)
83 {
84         int fd = eh->GetFd();
85
86         if (fd < 0)
87                 return;
88
89         if (static_cast<size_t>(fd) >= GetMaxFds())
90                 return;
91
92         SocketEngine::DelFdRef(eh);
93
94         FD_CLR(fd, &ReadSet);
95         FD_CLR(fd, &WriteSet);
96         FD_CLR(fd, &ErrSet);
97         if (fd == MaxFD)
98                 --MaxFD;
99
100         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
101 }
102
103 void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
104 {
105         int fd = eh->GetFd();
106         int diff = old_mask ^ new_mask;
107
108         if (diff & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
109         {
110                 if (new_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
111                         FD_SET(fd, &ReadSet);
112                 else
113                         FD_CLR(fd, &ReadSet);
114         }
115         if (diff & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
116         {
117                 if (new_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
118                         FD_SET(fd, &WriteSet);
119                 else
120                         FD_CLR(fd, &WriteSet);
121         }
122 }
123
124 int SocketEngine::DispatchEvents()
125 {
126         timeval tval;
127         tval.tv_sec = 1;
128         tval.tv_usec = 0;
129
130         fd_set rfdset = ReadSet, wfdset = WriteSet, errfdset = ErrSet;
131
132         int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval);
133         ServerInstance->UpdateTime();
134
135         for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++)
136         {
137                 int has_read = FD_ISSET(i, &rfdset), has_write = FD_ISSET(i, &wfdset), has_error = FD_ISSET(i, &errfdset);
138
139                 if (!(has_read || has_write || has_error))
140                         continue;
141
142                 --j;
143
144                 EventHandler* ev = GetRef(i);
145                 if (!ev)
146                         continue;
147
148                 if (has_error)
149                 {
150                         stats.ErrorEvents++;
151
152                         socklen_t codesize = sizeof(int);
153                         int errcode = 0;
154                         if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
155                                 errcode = errno;
156
157                         ev->OnEventHandlerError(errcode);
158                         continue;
159                 }
160
161                 if (has_read)
162                 {
163                         ev->SetEventMask(ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
164                         ev->OnEventHandlerRead();
165                         if (ev != GetRef(i))
166                                 continue;
167                 }
168
169                 if (has_write)
170                 {
171                         int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
172                         SocketEngine::OnSetEvent(ev, ev->GetEventMask(), newmask);
173                         ev->SetEventMask(newmask);
174                         ev->OnEventHandlerWrite();
175                 }
176         }
177
178         return sresult;
179 }