Remove warnings (work package 1)
[anna.git] / source / comm / Socket.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <sys/time.h>
10 #include <fcntl.h>
11 #include <strings.h>
12 #include <sys/types.h>
13 #include <poll.h>
14
15 #include <algorithm>
16
17 #include <anna/core/tracing/Logger.hpp>
18 #include <anna/core/tracing/TraceMethod.hpp>
19 #include <anna/core/DataBlock.hpp>
20
21 #include <anna/xml/Node.hpp>
22
23 #include <anna/comm/Socket.hpp>
24 #include <anna/comm/INetAddress.hpp>
25 #include <anna/comm/functions.hpp>
26 #include <anna/comm/Application.hpp>
27 #include <anna/comm/TransportFactory.hpp>
28 #include <anna/comm/ReceiverFactory.hpp>
29
30 using namespace std;
31 using namespace anna;
32 using namespace anna::comm;
33
34 Socket::Socket(const Domain::_v domain, const Type::_v type, TransportFactory* transportFactory) :
35   a_domain(domain),
36   a_type(type),
37   a_fd(-1),
38   a_isBound(false),
39   a_reuseMode(false),
40   a_category(0),
41   a_receiverFactory(NULL) {
42   if((a_transportFactory = transportFactory) == NULL) {
43     try {
44       comm::Application& app = comm::functions::getApp();
45       a_transportFactory = &app.comm::Application::getDefaultTransportFactory();
46     } catch(Exception& ex) {
47       ex.trace();
48     }
49   }
50 }
51
52 Socket::Socket(const INetAddress& localAddress, const Type::_v type, TransportFactory* transportFactory) :
53   a_domain(Domain::Inet),
54   a_type(type),
55   a_fd(-1),
56   a_localAccessPoint(localAddress),
57   a_isBound(false),
58   a_reuseMode(false),
59   a_category(0),
60   a_receiverFactory(NULL) {
61   if((a_transportFactory = transportFactory) == NULL) {
62     try {
63       comm::Application& app = comm::functions::getApp();
64       a_transportFactory = &app.comm::Application::getDefaultTransportFactory();
65     } catch(Exception& ex) {
66       ex.trace();
67     }
68   }
69 }
70
71 Socket::Socket(const std::string& path, const Type::_v type, TransportFactory* transportFactory) :
72   a_transportFactory(NULL),
73   a_domain(Domain::Unix),
74   a_type(type),
75   a_fd(-1),
76   a_localAccessPoint(path),
77   a_isBound(false),
78   a_reuseMode(false),
79   a_category(0),
80   a_receiverFactory(NULL) {
81   if((a_transportFactory = transportFactory) == NULL) {
82     try {
83       comm::Application& app = comm::functions::getApp();
84       a_transportFactory = &app.comm::Application::getDefaultTransportFactory();
85     } catch(Exception& ex) {
86       ex.trace();
87     }
88   }
89 }
90
91 Socket::~Socket() {
92   try {
93     close();
94   } catch(RuntimeException& ex) {
95     ex.trace();
96   }
97 }
98
99 bool Socket::support(const char* transportClassName) const
100 throw() {
101   if(a_transportFactory == NULL)
102     return false;
103
104   return anna_strcmp(a_transportFactory->getName().c_str(), transportClassName) == 0;
105 }
106
107 //---------------------------------------------------------------------------
108 // No hace falta que sea MT-safe porque siempre se invoca desde m�odo que
109 // ya tienen activa su propia zona de exclusin.
110 //---------------------------------------------------------------------------
111 void Socket::open()
112 throw(RuntimeException) {
113   if(a_fd != -1)
114     return;
115
116   if(a_transportFactory == NULL) {
117     std::string msg(asString());
118     msg += " | Transport factory was not especified";
119     throw RuntimeException(msg, ANNA_FILE_LOCATION);
120   }
121
122   anna_comm_socket_check(a_fd = socket((int) a_domain, (int) a_type, 0), "Cannot create the socket");
123   int value;
124   socklen_t len = sizeof(int);
125   anna_comm_socket_check(
126     getsockopt(a_fd, SOL_SOCKET, SO_REUSEADDR, &value, &len),
127     "Cannot obtain reuse mode"
128   );
129   a_reuseMode = (value == 1);
130
131   if(a_type == Type::Datagram) {
132     value = 1;
133     anna_comm_socket_check(
134       setsockopt(a_fd, SOL_SOCKET, SO_BROADCAST, &value, sizeof(int)),
135       "Cannot activate broadcast system"
136     );
137   }
138
139   LOGDEBUG(
140     string msg("comm::Socket::open | ");
141     msg += asString();
142     Logger::debug(msg, ANNA_FILE_LOCATION);
143   );
144 }
145
146 void Socket::close()
147 throw() {
148   if(a_fd == -1)
149     return;
150
151   Guard guard(*this, "comm::Socket::close");
152
153   if(a_fd != -1) {
154     a_isBound = false;
155     do_close();
156     a_fd = -1;
157   }
158
159   LOGDEBUG(
160     string msg("comm::Socket::close | ");
161     msg += asString();
162     Logger::debug(msg, ANNA_FILE_LOCATION);
163   );
164 }
165
166 bool Socket::setBlockingMode(const bool blockingMode)
167 throw(RuntimeException) {
168   int opts;
169   int result;
170   anna_socket_assert(a_fd == -1, "Socket is not opened");
171   anna_comm_socket_check(opts = fcntl(a_fd, F_GETFL), "Cannot obtain state bits (F_GETFL)");
172   result = !(opts & O_NONBLOCK);
173
174   if(blockingMode == false)
175     opts |= O_NONBLOCK;
176   else
177     opts &= ~O_NONBLOCK;
178
179   anna_comm_socket_check(fcntl(a_fd, F_SETFL, opts), "Cannot set state bits (F_SETFL)");
180   LOGDEBUG(
181     string msg("comm::Socket::setBlockingMode | Current: ");
182     msg += functions::asString(blockingMode);
183     msg += functions::asText(" | Previous: ", result != 0);
184     Logger::debug(msg, ANNA_FILE_LOCATION);
185   )
186   return (result != 0);
187 }
188
189 bool Socket::setReuseMode(const bool reuseMode)
190 throw(RuntimeException) {
191   anna_socket_assert(a_fd == -1, "Socket is not opened");
192
193   if(a_reuseMode == reuseMode)
194     return reuseMode;
195
196   bool result;
197   result = a_reuseMode;
198   int value = htons((a_reuseMode = reuseMode) == true);
199   anna_comm_socket_check(
200     setsockopt(a_fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)),
201     "Cannot set the reuse mode"
202   );
203   return result;
204 }
205
206 void Socket::bind()
207 throw(RuntimeException) {
208   Guard guard(*this, "comm::Socket::bind");
209
210   if(a_fd == -1)
211     open();
212
213   anna_socket_assert(a_isBound == true, "Already attached");
214   setReuseMode(true);
215   sockaddr* s(NULL);
216   int len(0);
217   a_localAccessPoint.translate(*this, s, len);
218
219   if(do_bind(s, len) < 0) {
220     RuntimeException ex(asString(), errno, ANNA_FILE_LOCATION);
221     close();
222     throw ex;
223   }
224
225   a_isBound = true;
226   LOGDEBUG(
227     string msg("comm::Socket::bind | ");
228     msg += asString();
229     Logger::debug(msg, ANNA_FILE_LOCATION);
230   );
231 }
232
233 int Socket::do_bind(const struct sockaddr *addr, const int len)
234 throw(RuntimeException) {
235   return ::bind(a_fd, addr, len);
236 }
237
238 std::string Socket::asString() const
239 throw() {
240   std::string msg("comm::Socket { Domain: ");
241
242   switch(a_domain) {
243   case Domain::Unix: msg += "Unix"; break;
244   case Domain::Inet: msg += "Inet"; break;
245   default: msg += '<'; msg += functions::asString((int) a_domain); msg += '>'; break;
246   }
247
248   msg += " | Type: ";
249   msg += (a_type == Type::Stream) ? "Stream" : "Datagram";
250   msg += " | ";
251
252   if(a_transportFactory != NULL) {
253     msg += a_transportFactory->asString();
254     msg += " | ";
255   }
256
257   msg += "fd: ";
258   msg += functions::asString(a_fd);
259   msg += " | LocalPoint: ";
260   a_localAccessPoint.asString(msg);
261   msg += " | Bound: ";
262   msg += functions::asString(a_isBound);
263   msg += " | Opened: ";
264   msg += functions::asString(isOpened());
265
266   if(a_category != 0)
267     msg += functions::asText(" | Category: ", a_category);
268
269   if(a_receiverFactory != NULL) {
270     msg += " | ";
271     msg += a_receiverFactory->asString();
272   }
273
274   return msg += " }";
275 }
276
277 xml::Node* Socket::asXML(xml::Node* parent) const
278 throw(RuntimeException) {
279   const char* aux;
280   xml::Node* result = parent->createChild("comm.Socket");
281
282   switch(a_domain) {
283   case Domain::Unix: aux = "Unix"; break;
284   case Domain::Inet: aux = "Inet"; break;
285   default: aux = functions::asString((int) a_domain).c_str(); break;
286   }
287
288   result->createAttribute("Domain", aux);
289   result->createAttribute("Type", (a_type == Type::Stream) ? "Stream" : "Datagram");
290
291   if(a_transportFactory != NULL)
292     a_transportFactory->asXML(result);
293
294   result->createAttribute("fd", a_fd);
295   result->createAttribute("Bound", functions::asString(a_isBound));
296   result->createAttribute("Opened", functions::asString(isOpened()));
297   a_localAccessPoint.asXML("comm.LocalPoint", result);
298
299   if(a_fd != -1) {
300     int opts;
301
302     if((opts = fcntl(a_fd, F_GETFL)) != -1)
303       result->createAttribute("Mode", (opts & O_NONBLOCK) ? "Non-Blocking" : "Blocking");
304   }
305
306   if(a_category != 0)
307     result->createAttribute("Category", a_category);
308
309   if(a_receiverFactory != NULL)
310     a_receiverFactory->asXML(result);
311
312   return result;
313 }
314
315 const char* Socket::asText(const Socket::Notify::_v v)
316 throw() {
317   static const char* text [] = { "None", "ReceiveData", "Close", "Corrupt" };
318   return text [v];
319 }
320