1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
17 // * Neither the name of the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
40 #include <sys/types.h>
47 #include <anna/core/functions.hpp>
48 #include <anna/core/tracing/Logger.hpp>
50 #include <anna/comm/internal/BinderSocket.hpp>
51 #include <anna/comm/ServerSocket.hpp>
56 //-------------------------------------------------------------------------------------------------
57 // No sabemos si tenemos que actuar como Cliente o Servidor; por lo que tenemos que establecer
58 // ambos extremos del Socket.
59 //-------------------------------------------------------------------------------------------------
60 comm::BinderSocket::BinderSocket(comm::ServerSocket* serverSocket) :
62 string("/tmp/").append(serverSocket->getLocalAccessPoint().serialize()),
65 a_serverSocket(*serverSocket) {
68 //-------------------------------------------------------------------------------------------------
69 // (1) Si consigue hacer el bind => es el primero. A partir de ahora dara acceso al resto.
71 // (0) a_bindDelay esta expresando en milisegundos => maxDelay estara en nanosegundos.
72 // (0.1) Guardara los fds asociados al socket (real) y al socket por el que enviamos la informacion
73 // para poder hacer multiples binds sobre una misma direccion.
74 // (0.2) Vamos a enviar 2 fds (el socket real y el socket UNIX).
75 // (1) El Communicator tendra que tener en cuenta que se trata de un socket bind-compartido
76 // para meter el fd del publicador en la lista de descriptores por los que es posible que nos
78 // (2) Si falla el bind => cierra los recursos. Ya que nos llegara el nuevo fd atraves del mensaje
80 // (3) Conectamos como cliente con el socket UNIX => cuando la parte servidora (a traves del
81 // Communicator) detecte nuestra presencia deberia enviarnos la informacion para que podamos
82 // crear el socket con el bind-compartido. Limpiamos la direccion local para evitar hacer otro
84 // (4) Si el 'connect' falla => se cierra el socket automaticamente => lo abre de nuevo.
85 // (5) "Cierra" el socket UNIX ya que no volvera a usarlo. A partir de ahora el valor bueno sera
86 // el indicado en la estructura que nos pasa alguno de los servidores que estan escuchando
89 // Visto en: http://mail-index.netbsd.org/current-users/1998/01/16/0010.html
90 //-------------------------------------------------------------------------------------------------
91 void comm::BinderSocket::requestBind(const struct sockaddr* s, const int len)
92 throw(RuntimeException) {
93 if(::bind(a_serverSocket.a_fd, s, len) != -1) { // (1)
94 unlink(getRemoteAccessPoint().getPath().c_str());
96 anna_socket_assert(::listen(a_fd, 10) == -1, "Error preparing listen access point");
100 const int xerrno = errno;
102 a_serverSocket.close(); // (2)
104 if(xerrno != EADDRINUSE)
105 throw RuntimeException(asString(), xerrno, ANNA_FILE_LOCATION);
107 this->a_localAccessPoint.clear(); // (3)
108 this->connect(); // (4)
109 waitBind(a_serverSocket.getBindDelay());
112 //-------------------------------------------------------------------------------------------------
113 // Este metodo se invoca desde el Communicator cuando se detecta activada en el socket UNIX
114 // empleado para comunicar todos los procesos que quieran compartir una determinada direccion.
116 // (1) Acepta la conexion por el socket UNIX. Ver (3) de sharedBind.
117 // (2) En el IOV debemos mandar al menos 1 byte.
118 // (3) Rellenamos la estructura que recojera algun otro proceso que esta esperando a leer del
120 // (4) Cierra la nueva conexion ya que no vamos a volver a usarla para nada.
121 //-------------------------------------------------------------------------------------------------
122 void comm::BinderSocket::responseBind()
123 throw(RuntimeException) {
129 socklen_t len(sizeof(sockaddr_un));
130 anna_memset(&s, 0, sizeof(sockaddr_un));
131 int newSocket = ::accept(a_fd, (sockaddr*) & s, &len); // (1)
133 if(newSocket == -1) {
134 const int xerrno = errno;
135 throw RuntimeException(asString(), xerrno, ANNA_FILE_LOCATION);
138 iov.iov_len = sizeof(garbage); // (2)
139 iov.iov_base = (char*) & garbage;
144 fds [0] = a_serverSocket.a_fd; // (3)
148 char buffer [sizeof(struct cmsghdr) + sizeof(fds)];
149 mh.msg_control = &buffer;
150 mh.msg_controllen = cmh.cmsg_len = sizeof(struct cmsghdr) + sizeof(fds);
151 cmh.cmsg_level = SOL_SOCKET;
152 cmh.cmsg_type = SCM_RIGHTS;
153 anna_memcpy(buffer, &cmh, sizeof(cmh));
154 anna_memcpy(buffer + sizeof(struct cmsghdr), fds, sizeof(fds));
157 mh.msg_accrights = (caddr_t) fds;
158 mh.msg_accrightslen = sizeof(fds);
161 if(sendmsg(newSocket, &mh, 0) < 0) {
164 throw RuntimeException(asString(), xerrno, ANNA_FILE_LOCATION);
167 ::close(newSocket); // (4)
169 string msg("anna::comm::BinderSocket::responseBind | ");
170 msg += functions::asText("ServerSocket's fd: ", fds [0]);
171 msg += functions::asText(" | BinderSocket's fd: ", fds [1]);
172 Logger::debug(msg, ANNA_FILE_LOCATION);
176 void comm::BinderSocket::waitBind(const Millisecond &maxDelay)
177 throw(RuntimeException) {
180 const int garbage(0);
182 iov.iov_len = sizeof(garbage);
183 iov.iov_base = (char*) & garbage;
189 char buffer [sizeof(struct cmsghdr) + sizeof(fds)];
190 mh.msg_control = &buffer;
191 mh.msg_controllen = sizeof(struct cmsghdr) + sizeof(fds);
194 mh.msg_accrights = (caddr_t) fds;
195 mh.msg_accrightslen = sizeof(fds);
200 poll.events = POLLIN | POLLRDNORM;
202 if(::poll(&poll, 1, maxDelay) != 1) {
203 string msg(a_serverSocket.asString());
204 msg += " | No data is received from shared bind";
205 throw RuntimeException(msg, ANNA_FILE_LOCATION);
208 r = recvmsg(a_fd, &mh, 0);
211 const int xerrno = errno;
212 std::string msg(a_serverSocket.asString());
217 msg += " | Error in shared bind reception";
218 throw RuntimeException(msg, xerrno, ANNA_FILE_LOCATION);
220 msg += " | No item received to obtain shared bind";
221 throw RuntimeException(msg, ANNA_FILE_LOCATION);
227 anna_memcpy(&fds, buffer + sizeof(cmsghdr), sizeof(fds));
229 a_serverSocket.a_fd = fds [0];
232 string msg("anna::comm::BinderSocket::waitBind | ");
233 msg += functions::asText("ServerSocket's fd: ", fds [0]);
234 msg += functions::asText(" | BinderSocket's fd: ", fds [1]);
235 Logger::debug(msg, ANNA_FILE_LOCATION);