tipc_listener.cpp 6.54 KB
Newer Older
1
/*
2
    Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
3

4
    This file is part of libzmq, the ZeroMQ core engine in C++.
5

6 7 8
    libzmq is free software; you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License (LGPL) as published
    by the Free Software Foundation; either version 3 of the License, or
9 10
    (at your option) any later version.

11 12 13 14 15 16 17 18 19 20 21 22 23 24
    As a special exception, the Contributors give you permission to link
    this library with independent modules to produce an executable,
    regardless of the license terms of these independent modules, and to
    copy and distribute the resulting executable under terms of your choice,
    provided that you also meet, for each linked independent module, the
    terms and conditions of the license of that module. An independent
    module is a module which is not derived from or based on this library.
    If you modify this library, you must extend this exception to your
    version of the library.

    libzmq is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    License for more details.
25 26 27 28 29

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

30 31
#include "precompiled.hpp"

32 33
#include "tipc_listener.hpp"

34
#if defined ZMQ_HAVE_TIPC
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

#include <new>

#include <string.h>

#include "stream_engine.hpp"
#include "tipc_address.hpp"
#include "io_thread.hpp"
#include "session_base.hpp"
#include "config.hpp"
#include "err.hpp"
#include "ip.hpp"
#include "socket_base.hpp"

#include <unistd.h>
#include <sys/socket.h>
#include <fcntl.h>
52 53 54 55
#if defined ZMQ_HAVE_VXWORKS
#include <sockLib.h>
#include <tipc/tipc.h>
#else
56
#include <linux/tipc.h>
57
#endif
58 59

zmq::tipc_listener_t::tipc_listener_t (io_thread_t *io_thread_,
60 61
                                       socket_base_t *socket_,
                                       const options_t &options_) :
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
    own_t (io_thread_, options_),
    io_object_t (io_thread_),
    s (retired_fd),
    socket (socket_)
{
}

zmq::tipc_listener_t::~tipc_listener_t ()
{
    zmq_assert (s == retired_fd);
}

void zmq::tipc_listener_t::process_plug ()
{
    //  Start polling for incoming connections.
    handle = add_fd (s);
    set_pollin (handle);
}

void zmq::tipc_listener_t::process_term (int linger_)
{
    rm_fd (handle);
    close ();
    own_t::process_term (linger_);
}

void zmq::tipc_listener_t::in_event ()
{
    fd_t fd = accept ();

    //  If connection was reset by the peer in the meantime, just ignore it.
    //  TODO: Handle specific errors like ENFILE/EMFILE etc.
    if (fd == retired_fd) {
95
        socket->event_accept_failed (endpoint, zmq_errno ());
96 97 98 99
        return;
    }

    //  Create the engine object for this connection.
100 101
    stream_engine_t *engine =
      new (std::nothrow) stream_engine_t (fd, options, endpoint);
102 103 104 105 106 107 108 109
    alloc_assert (engine);

    //  Choose I/O thread to run connecter in. Given that we are already
    //  running in an I/O thread, there must be at least one available.
    io_thread_t *io_thread = choose_io_thread (options.affinity);
    zmq_assert (io_thread);

    //  Create and launch a session object.
110 111
    session_base_t *session =
      session_base_t::create (io_thread, false, socket, options, NULL);
112 113 114 115 116 117 118 119 120 121 122 123
    errno_assert (session);
    session->inc_seqnum ();
    launch_child (session);
    send_attach (session, engine, false);
    socket->event_accepted (endpoint, fd);
}

int zmq::tipc_listener_t::get_address (std::string &addr_)
{
    struct sockaddr_storage ss;
    socklen_t sl = sizeof (ss);

124 125 126
#ifdef ZMQ_HAVE_VXWORKS
    int rc = getsockname (s, (sockaddr *) &ss, (int *) &sl);
#else
127
    int rc = getsockname (s, (sockaddr *) &ss, &sl);
128
#endif
129 130 131 132 133 134 135 136 137 138 139
    if (rc != 0) {
        addr_.clear ();
        return rc;
    }

    tipc_address_t addr ((struct sockaddr *) &ss, sl);
    return addr.to_string (addr_);
}

int zmq::tipc_listener_t::set_address (const char *addr_)
{
140
    // Convert str to address struct
141
    int rc = address.resolve (addr_);
142 143
    if (rc != 0)
        return -1;
144 145 146 147 148 149 150 151

    // Cannot bind non-random Port Identity
    struct sockaddr_tipc *a = (sockaddr_tipc *) address.addr ();
    if (!address.is_random () && a->addrtype == TIPC_ADDR_ID) {
        errno = EINVAL;
        return -1;
    }

152 153 154 155 156
    //  Create a listening socket.
    s = open_socket (AF_TIPC, SOCK_STREAM, 0);
    if (s == -1)
        return -1;

157
    // If random Port Identity, update address object to reflect the assigned address
158 159
    if (address.is_random ()) {
        struct sockaddr_storage ss;
160 161 162
#ifdef ZMQ_HAVE_VXWORKS
        int sl = sizeof (ss);
#else
163
        socklen_t sl = sizeof (ss);
164
#endif
165
        int rc = getsockname (s, (sockaddr *) &ss, &sl);
166 167
        if (rc != 0)
            goto error;
168

169
        address = tipc_address_t ((struct sockaddr *) &ss, sl);
170 171 172
    }


173 174
    address.to_string (endpoint);

175 176
    //  Bind the socket to tipc name
    if (address.is_service ()) {
177 178 179
#ifdef ZMQ_HAVE_VXWORKS
        rc = bind (s, (sockaddr *) address.addr (), address.addrlen ());
#else
180
        rc = bind (s, address.addr (), address.addrlen ());
181
#endif
182 183 184
        if (rc != 0)
            goto error;
    }
185

186
    //  Listen for incoming connections.
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
    rc = listen (s, options.backlog);
    if (rc != 0)
        goto error;

    socket->event_listening (endpoint, s);
    return 0;

error:
    int err = errno;
    close ();
    errno = err;
    return -1;
}

void zmq::tipc_listener_t::close ()
{
    zmq_assert (s != retired_fd);
    int rc = ::close (s);
    errno_assert (rc == 0);
    s = retired_fd;
    socket->event_closed (endpoint, s);
}

zmq::fd_t zmq::tipc_listener_t::accept ()
{
    //  Accept one connection and deal with different failure modes.
    //  The situation where connection cannot be accepted due to insufficient
    //  resources is considered valid and treated by ignoring the connection.
    struct sockaddr_storage ss = {};
216
    socklen_t ss_len = sizeof (ss);
217 218

    zmq_assert (s != retired_fd);
219 220 221
#ifdef ZMQ_HAVE_VXWORKS
    fd_t sock = ::accept (s, (struct sockaddr *) &ss, (int *) &ss_len);
#else
222
    fd_t sock = ::accept (s, (struct sockaddr *) &ss, &ss_len);
223
#endif
224
    if (sock == -1) {
225 226 227 228
        errno_assert (errno == EAGAIN || errno == EWOULDBLOCK
                      || errno == ENOBUFS || errno == EINTR
                      || errno == ECONNABORTED || errno == EPROTO
                      || errno == EMFILE || errno == ENFILE);
229 230 231 232 233 234 235
        return retired_fd;
    }
    /*FIXME Accept filters?*/
    return sock;
}

#endif