2 Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
19 This exception does not invalidate any other reasons why a work based
20 on this file might be covered by the GNU General Public License.
27 #include <sys/types.h>
28 #include <sys/socket.h>
31 #include <arpa/inet.h>
32 #include <netinet/in.h>
41 #include "socket_client.hxx"
42 #include "t2n_exception.hxx"
50 /// returns a closed connection if connection could not be established, call get_last_error_msg() for details
51 socket_client_connection::socket_client_connection(int _port, const std::string& _server,
52 long long _connect_timeout_usec, int _max_retries,
53 std::ostream *_logstream, log_level_values _log_level)
54 : client_connection(), socket_handler(0,tcp_s)
56 max_retries=_max_retries;
57 connect_timeout_usec=_connect_timeout_usec;
62 set_logging(_logstream,_log_level);
66 tcp_connect(max_retries);
68 catch (t2n_communication_error &e)
70 lastErrorMsg=e.what();
71 LOGSTREAM(debug,"tcp connect error: " << lastErrorMsg);
72 // FIXME: Don't call virtual function in constructor. Currently not dangerous but bad design.
76 if (!connection::is_closed())
77 do_callbacks(new_connection);
80 /// returns a closed connection if connection could not be established, call get_last_error_msg() for details
81 socket_client_connection::socket_client_connection(const std::string& _path,
82 long long _connect_timeout_usec, int _max_retries,
83 std::ostream *_logstream, log_level_values _log_level)
84 : client_connection(), socket_handler(0,unix_s)
86 max_retries=_max_retries;
87 connect_timeout_usec=_connect_timeout_usec;
91 set_logging(_logstream,_log_level);
95 unix_connect(max_retries);
97 catch (t2n_communication_error &e)
99 lastErrorMsg=e.what();
100 LOGSTREAM(debug,"unix connect error: " << lastErrorMsg);
101 // FIXME: Don't call virtual function in constructor. Currently not dangerous
105 if (!connection::is_closed())
106 do_callbacks(new_connection);
110 * Destructor. Closes an open connection.
112 socket_client_connection::~socket_client_connection()
114 // Destructor of socket_handler will close the socket!
118 /// establish a connection via tcp
119 void socket_client_connection::tcp_connect(int max_retries)
121 struct sockaddr_in sock_addr;
123 sock_addr.sin_family = AF_INET;
124 sock_addr.sin_port = htons(port);
126 // find the target ip
127 if (inet_aton(server.c_str(),&sock_addr.sin_addr)==0)
129 struct hostent *server_hent;
130 server_hent=gethostbyname(server.c_str());
131 if (server_hent == NULL)
132 throw t2n_connect_error(string("can't find server ")+server);
134 memcpy(&sock_addr.sin_addr,server_hent->h_addr_list[0],sizeof(sock_addr.sin_addr));
137 sock = socket(PF_INET, SOCK_STREAM, 0);
139 throw t2n_connect_error(string("socket() error: ")+strerror(errno));
143 connect_with_timeout((struct sockaddr *) &sock_addr,sizeof(sock_addr));
145 catch (t2n_connect_error &e)
147 // recurse if retries left
150 LOGSTREAM(debug,"retrying connect after connect error");
151 tcp_connect(max_retries-1);
154 throw t2n_connect_error("no more retries left after connect error");
158 /// establish a connection via unix-socket
159 void socket_client_connection::unix_connect(int max_retries)
161 struct sockaddr_un unix_addr;
162 size_t path_size = path.size();
164 unix_addr.sun_family = AF_UNIX;
166 if (path_size >= sizeof(unix_addr.sun_path))
168 throw t2n_connect_error((std::string)"path '"
170 + "' exceeds permissible UNIX socket path length");
173 memcpy(unix_addr.sun_path, path.c_str(), path_size);
174 unix_addr.sun_path[path_size] = '\0';
176 sock = socket(PF_UNIX, SOCK_STREAM, 0);
178 throw t2n_connect_error(string("socket() error: ")+strerror(errno));
182 connect_with_timeout((struct sockaddr *) &unix_addr, sizeof(unix_addr));
184 catch (t2n_connect_error &e)
186 // recurse if retries left
189 LOGSTREAM(debug,"retrying connect after connect error");
190 unix_connect(max_retries-1);
193 throw t2n_connect_error("no more retries left after connect error");
197 /// execute a connect on a prepared socket (tcp or unix) respecting timeouts
198 void socket_client_connection::connect_with_timeout(struct sockaddr *sock_addr,unsigned int sockaddr_size)
200 set_socket_options(sock);
202 /* non-blocking mode */
204 flflags=fcntl(sock,F_GETFL,0);
206 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
208 flflags &= (O_NONBLOCK ^ 0xFFFF);
209 if (fcntl(sock,F_SETFL,flflags) < 0)
210 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
213 LOGSTREAM(debug,"connect_with_timeout()");
214 int ret=::connect(sock,sock_addr, sockaddr_size);
218 if (errno==EINPROGRESS)
220 LOGSTREAM(debug,"connect_with_timeout(): EINPROGRESS");
224 struct timeval *timeout_ptr;
226 if (connect_timeout_usec == -1)
232 // convert timeout from long long usec to int sec + int usec
233 tval.tv_sec = connect_timeout_usec / 1000000;
234 tval.tv_usec = connect_timeout_usec % 1000000;
237 fd_set connect_socket_set;
238 FD_ZERO(&connect_socket_set);
239 FD_SET(sock,&connect_socket_set);
242 while ((ret=select(FD_SETSIZE, NULL, &connect_socket_set, NULL, timeout_ptr)) &&
243 ret < 0 && errno==EINTR);
246 throw t2n_connect_error(string("connect() error (select): ")+strerror(errno));
248 socklen_t sopt=sizeof(int);
250 ret=getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &sopt);
251 if (ret < 0 || valopt)
252 throw t2n_connect_error(string("connect() error (getsockopt): ")+strerror(errno));
255 throw t2n_connect_error(string("connect() error: ")+strerror(errno));
258 LOGSTREAM(debug,"connect_with_timeout(): success");
261 void socket_client_connection::close()
263 if (!client_connection::is_closed())
265 socket_handler::close();
266 client_connection::close();
270 /** @brief try to reconnect the current connection with the same connection credentials (host and port or path)
272 @note will throw an exeption if reconnecting not possible
274 void socket_client_connection::reconnect()
276 LOGSTREAM(debug,"reconnect()");
278 // close the current connection if still open
281 socket_type_value type=get_type();
284 tcp_connect(max_retries);
285 else if (type == unix_s)
286 unix_connect(max_retries);
288 // connection is open now, otherwise an execption would have been thrown
291 LOGSTREAM(debug,"reconnect() done, client_connection::is_closed() now " << client_connection::is_closed());