/src/libzmq/src/ipc_connecter.cpp
Line | Count | Source |
1 | | /* SPDX-License-Identifier: MPL-2.0 */ |
2 | | |
3 | | #include "precompiled.hpp" |
4 | | #include "ipc_connecter.hpp" |
5 | | |
6 | | #if defined ZMQ_HAVE_IPC |
7 | | |
8 | | #include <new> |
9 | | #include <string> |
10 | | |
11 | | #include "io_thread.hpp" |
12 | | #include "random.hpp" |
13 | | #include "err.hpp" |
14 | | #include "ip.hpp" |
15 | | #include "address.hpp" |
16 | | #include "ipc_address.hpp" |
17 | | #include "session_base.hpp" |
18 | | |
19 | | #if defined ZMQ_HAVE_WINDOWS |
20 | | #include <afunix.h> |
21 | | #else |
22 | | #include <unistd.h> |
23 | | #include <sys/types.h> |
24 | | #include <sys/socket.h> |
25 | | #include <sys/un.h> |
26 | | #endif |
27 | | |
28 | | zmq::ipc_connecter_t::ipc_connecter_t (class io_thread_t *io_thread_, |
29 | | class session_base_t *session_, |
30 | | const options_t &options_, |
31 | | address_t *addr_, |
32 | | bool delayed_start_) : |
33 | 0 | stream_connecter_base_t ( |
34 | 0 | io_thread_, session_, options_, addr_, delayed_start_) |
35 | 0 | { |
36 | 0 | zmq_assert (_addr->protocol == protocol_name::ipc); |
37 | 0 | } |
38 | | |
39 | | void zmq::ipc_connecter_t::out_event () |
40 | 0 | { |
41 | 0 | const fd_t fd = connect (); |
42 | 0 | rm_handle (); |
43 | | |
44 | | // Handle the error condition by attempt to reconnect. |
45 | 0 | if (fd == retired_fd) { |
46 | 0 | close (); |
47 | 0 | add_reconnect_timer (); |
48 | 0 | return; |
49 | 0 | } |
50 | | |
51 | 0 | create_engine (fd, get_socket_name<ipc_address_t> (fd, socket_end_local)); |
52 | 0 | } |
53 | | |
54 | | void zmq::ipc_connecter_t::start_connecting () |
55 | 0 | { |
56 | | // Open the connecting socket. |
57 | 0 | const int rc = open (); |
58 | | |
59 | | // Connect may succeed in synchronous manner. |
60 | 0 | if (rc == 0) { |
61 | 0 | _handle = add_fd (_s); |
62 | 0 | out_event (); |
63 | 0 | } |
64 | | |
65 | | // Connection establishment may be delayed. Poll for its completion. |
66 | 0 | else if (rc == -1 && errno == EINPROGRESS) { |
67 | 0 | _handle = add_fd (_s); |
68 | 0 | set_pollout (_handle); |
69 | 0 | _socket->event_connect_delayed ( |
70 | 0 | make_unconnected_connect_endpoint_pair (_endpoint), zmq_errno ()); |
71 | | |
72 | | // TODO, tcp_connecter_t adds a connect timer in this case; maybe this |
73 | | // should be done here as well (and then this could be pulled up to |
74 | | // stream_connecter_base_t). |
75 | 0 | } |
76 | | //stop connecting after called zmq_disconnect |
77 | 0 | else if (rc == -1 |
78 | 0 | && (options.reconnect_stop & ZMQ_RECONNECT_STOP_AFTER_DISCONNECT) |
79 | 0 | && errno == ECONNREFUSED && _socket->is_disconnected ()) { |
80 | 0 | if (_s != retired_fd) |
81 | 0 | close (); |
82 | 0 | } |
83 | | |
84 | | // Handle any other error condition by eventual reconnect. |
85 | 0 | else { |
86 | 0 | if (_s != retired_fd) |
87 | 0 | close (); |
88 | 0 | add_reconnect_timer (); |
89 | 0 | } |
90 | 0 | } |
91 | | |
92 | | int zmq::ipc_connecter_t::open () |
93 | 0 | { |
94 | 0 | zmq_assert (_s == retired_fd); |
95 | | |
96 | | // Create the socket. |
97 | 0 | _s = open_socket (AF_UNIX, SOCK_STREAM, 0); |
98 | 0 | if (_s == retired_fd) |
99 | 0 | return -1; |
100 | | |
101 | | // Set the non-blocking flag. |
102 | 0 | unblock_socket (_s); |
103 | | |
104 | | // Connect to the remote peer. |
105 | 0 | const int rc = ::connect (_s, _addr->resolved.ipc_addr->addr (), |
106 | 0 | _addr->resolved.ipc_addr->addrlen ()); |
107 | | |
108 | | // Connect was successful immediately. |
109 | 0 | if (rc == 0) |
110 | 0 | return 0; |
111 | | |
112 | | // Translate other error codes indicating asynchronous connect has been |
113 | | // launched to a uniform EINPROGRESS. |
114 | | #ifdef ZMQ_HAVE_WINDOWS |
115 | | const int last_error = WSAGetLastError (); |
116 | | if (last_error == WSAEINPROGRESS || last_error == WSAEWOULDBLOCK) |
117 | | errno = EINPROGRESS; |
118 | | else |
119 | | errno = wsa_error_to_errno (last_error); |
120 | | #else |
121 | 0 | if (rc == -1 && errno == EINTR) { |
122 | 0 | errno = EINPROGRESS; |
123 | 0 | } |
124 | 0 | #endif |
125 | | |
126 | | // Forward the error. |
127 | 0 | return -1; |
128 | 0 | } |
129 | | |
130 | | zmq::fd_t zmq::ipc_connecter_t::connect () |
131 | 0 | { |
132 | | // Following code should handle both Berkeley-derived socket |
133 | | // implementations and Solaris. |
134 | 0 | int err = 0; |
135 | 0 | zmq_socklen_t len = static_cast<zmq_socklen_t> (sizeof (err)); |
136 | 0 | const int rc = getsockopt (_s, SOL_SOCKET, SO_ERROR, |
137 | 0 | reinterpret_cast<char *> (&err), &len); |
138 | 0 | if (rc == -1) { |
139 | 0 | if (errno == ENOPROTOOPT) |
140 | 0 | errno = 0; |
141 | 0 | err = errno; |
142 | 0 | } |
143 | 0 | if (err != 0) { |
144 | | // Assert if the error was caused by 0MQ bug. |
145 | | // Networking problems are OK. No need to assert. |
146 | 0 | errno = err; |
147 | 0 | errno_assert (errno == ECONNREFUSED || errno == ECONNRESET |
148 | 0 | || errno == ETIMEDOUT || errno == EHOSTUNREACH |
149 | 0 | || errno == ENETUNREACH || errno == ENETDOWN); |
150 | |
|
151 | 0 | return retired_fd; |
152 | 0 | } |
153 | | |
154 | 0 | const fd_t result = _s; |
155 | 0 | _s = retired_fd; |
156 | 0 | return result; |
157 | 0 | } |
158 | | |
159 | | #endif |