]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_ports.cpp
m_spanningtree Propagate topic changes via FTOPIC in order to prevent desync when...
[user/henk/code/inspircd.git] / src / socketengines / socketengine_ports.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
6  *
7  * This file is part of InspIRCd.  InspIRCd is free software: you can
8  * redistribute it and/or modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation, version 2.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 #include "inspircd.h"
22 #include "exitcodes.h"
23 #include <port.h>
24
25 #ifndef SOCKETENGINE_PORTS
26 #define SOCKETENGINE_PORTS
27
28 #ifndef __sun
29 # error You need Solaris 10 or later to make use of this code.
30 #endif
31
32 #include <vector>
33 #include <string>
34 #include <map>
35 #include "inspircd.h"
36 #include "socketengine.h"
37 #include <port.h>
38 #include <iostream>
39
40 /** A specialisation of the SocketEngine class, designed to use solaris 10 I/O completion ports
41  */
42 class PortsEngine : public SocketEngine
43 {
44 private:
45         /** These are used by epoll() to hold socket events
46          */
47         port_event_t* events;
48         int EngineHandle;
49 public:
50         /** Create a new PortsEngine
51          */
52         PortsEngine();
53         /** Delete a PortsEngine
54          */
55         virtual ~PortsEngine();
56         virtual bool AddFd(EventHandler* eh, int event_mask);
57         virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask);
58         virtual void DelFd(EventHandler* eh);
59         virtual int DispatchEvents();
60         virtual std::string GetName();
61 };
62
63 #endif
64
65
66 #include <ulimit.h>
67
68 PortsEngine::PortsEngine()
69 {
70         int max = ulimit(4, 0);
71         if (max > 0)
72         {
73                 MAX_DESCRIPTORS = max;
74         }
75         else
76         {
77                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
78                 std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
79                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
80         }
81         EngineHandle = port_create();
82
83         if (EngineHandle == -1)
84         {
85                 ServerInstance->Logs->Log("SOCKET", LOG_SPARSE, "ERROR: Could not initialize socket engine: %s", strerror(errno));
86                 ServerInstance->Logs->Log("SOCKET", LOG_SPARSE, "ERROR: This is a fatal error, exiting now.");
87                 std::cout << "ERROR: Could not initialize socket engine: " << strerror(errno) << std::endl;
88                 std::cout << "ERROR: This is a fatal error, exiting now." << std::endl;
89                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
90         }
91         CurrentSetSize = 0;
92
93         ref = new EventHandler* [GetMaxFds()];
94         events = new port_event_t[GetMaxFds()];
95         memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
96 }
97
98 PortsEngine::~PortsEngine()
99 {
100         this->Close(EngineHandle);
101         delete[] ref;
102         delete[] events;
103 }
104
105 static int mask_to_events(int event_mask)
106 {
107         int rv = 0;
108         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
109                 rv |= POLLRDNORM;
110         if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
111                 rv |= POLLWRNORM;
112         return rv;
113 }
114
115 bool PortsEngine::AddFd(EventHandler* eh, int event_mask)
116 {
117         int fd = eh->GetFd();
118         if ((fd < 0) || (fd > GetMaxFds() - 1))
119                 return false;
120
121         if (ref[fd])
122                 return false;
123
124         ref[fd] = eh;
125         SocketEngine::SetEventMask(eh, event_mask);
126         port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(event_mask), eh);
127
128         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
129         CurrentSetSize++;
130         return true;
131 }
132
133 void PortsEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
134 {
135         if (mask_to_events(new_mask) != mask_to_events(old_mask))
136                 port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), mask_to_events(new_mask), eh);
137 }
138
139 void PortsEngine::DelFd(EventHandler* eh)
140 {
141         int fd = eh->GetFd();
142         if ((fd < 0) || (fd > GetMaxFds() - 1))
143                 return;
144
145         port_dissociate(EngineHandle, PORT_SOURCE_FD, fd);
146
147         CurrentSetSize--;
148         ref[fd] = NULL;
149
150         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
151 }
152
153 int PortsEngine::DispatchEvents()
154 {
155         struct timespec poll_time;
156
157         poll_time.tv_sec = 1;
158         poll_time.tv_nsec = 0;
159
160         unsigned int nget = 1; // used to denote a retrieve request.
161         int ret = port_getn(EngineHandle, this->events, GetMaxFds() - 1, &nget, &poll_time);
162         ServerInstance->UpdateTime();
163
164         // first handle an error condition
165         if (ret == -1)
166                 return -1;
167
168         TotalEvents += nget;
169
170         unsigned int i;
171         for (i = 0; i < nget; i++)
172         {
173                 switch (this->events[i].portev_source)
174                 {
175                         case PORT_SOURCE_FD:
176                         {
177                                 int fd = this->events[i].portev_object;
178                                 EventHandler* eh = ref[fd];
179                                 if (eh)
180                                 {
181                                         int mask = eh->GetEventMask();
182                                         if (events[i].portev_events & POLLWRNORM)
183                                                 mask &= ~(FD_WRITE_WILL_BLOCK | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE);
184                                         if (events[i].portev_events & POLLRDNORM)
185                                                 mask &= ~FD_READ_WILL_BLOCK;
186                                         // reinsert port for next time around, pretending to be one-shot for writes
187                                         SetEventMask(eh, mask);
188                                         port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(mask), eh);
189                                         if (events[i].portev_events & POLLRDNORM)
190                                         {
191                                                 ReadEvents++;
192                                                 eh->HandleEvent(EVENT_READ);
193                                                 if (eh != ref[fd])
194                                                         continue;
195                                         }
196                                         if (events[i].portev_events & POLLWRNORM)
197                                         {
198                                                 WriteEvents++;
199                                                 eh->HandleEvent(EVENT_WRITE);
200                                         }
201                                 }
202                         }
203                         default:
204                         break;
205                 }
206         }
207
208         return (int)i;
209 }
210
211 std::string PortsEngine::GetName()
212 {
213         return "ports";
214 }
215
216 SocketEngine* CreateSocketEngine()
217 {
218         return new PortsEngine;
219 }