client_wrapper.hxx, socket_wrapper.hxx: reorder member initialization order
[libt2n] / src / socket_client.cpp
CommitLineData
19facd85
TJ
1/*
2Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
3
4The software in this package is distributed under the GNU General
5Public License version 2 (with a special exception described below).
6
7A copy of GNU General Public License (GPL) is included in this distribution,
8in the file COPYING.GPL.
9
10As a special exception, if other files instantiate templates or use macros
11or inline functions from this file, or you compile this file and link it
12with other works to produce a work based on this file, this file
13does not by itself cause the resulting work to be covered
14by the GNU General Public License.
15
16However the source code for this file must still be made available
17in accordance with section (3) of the GNU General Public License.
18
19This exception does not invalidate any other reasons why a work based
20on this file might be covered by the GNU General Public License.
21*/
7087e187
GE
22
23#include <stdio.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <sys/types.h>
28#include <sys/socket.h>
29#include <sys/un.h>
30#include <sys/time.h>
31#include <arpa/inet.h>
32#include <netinet/in.h>
33#include <netdb.h>
34#include <fcntl.h>
35#include <time.h>
36#include <pwd.h>
37#include <grp.h>
38
39#include <sstream>
40
41#include "socket_client.hxx"
42#include "t2n_exception.hxx"
e1614a6d 43#include "log.hxx"
7087e187
GE
44
45using namespace std;
46
47namespace libt2n
48{
49
fb3345ad 50/// returns a closed connection if connection could not be established, call get_last_error_msg() for details
b604df5f 51socket_client_connection::socket_client_connection(int _port, const std::string& _server,
e1614a6d
GE
52 long long _connect_timeout_usec, int _max_retries,
53 std::ostream *_logstream, log_level_values _log_level)
7087e187
GE
54 : client_connection(), socket_handler(0,tcp_s)
55{
56 max_retries=_max_retries;
b604df5f 57 connect_timeout_usec=_connect_timeout_usec;
7087e187
GE
58
59 server=_server;
60 port=_port;
61
e1614a6d
GE
62 set_logging(_logstream,_log_level);
63
fb3345ad
GE
64 try
65 {
66 tcp_connect(max_retries);
67 }
68 catch (t2n_communication_error &e)
69 {
70 lastErrorMsg=e.what();
71 LOGSTREAM(debug,"tcp connect error: " << lastErrorMsg);
56f3994d 72 // FIXME: Don't call virtual function in constructor. Currently not dangerous but bad design.
fb3345ad
GE
73 close();
74 }
e1614a6d 75
fb3345ad
GE
76 if (!connection::is_closed())
77 do_callbacks(new_connection);
7087e187
GE
78}
79
fb3345ad 80/// returns a closed connection if connection could not be established, call get_last_error_msg() for details
b604df5f 81socket_client_connection::socket_client_connection(const std::string& _path,
e1614a6d
GE
82 long long _connect_timeout_usec, int _max_retries,
83 std::ostream *_logstream, log_level_values _log_level)
7087e187
GE
84 : client_connection(), socket_handler(0,unix_s)
85{
86 max_retries=_max_retries;
b604df5f 87 connect_timeout_usec=_connect_timeout_usec;
7087e187
GE
88
89 path=_path;
90
e1614a6d
GE
91 set_logging(_logstream,_log_level);
92
fb3345ad
GE
93 try
94 {
95 unix_connect(max_retries);
96 }
97 catch (t2n_communication_error &e)
98 {
99 lastErrorMsg=e.what();
100 LOGSTREAM(debug,"unix connect error: " << lastErrorMsg);
56f3994d 101 // FIXME: Don't call virtual function in constructor. Currently not dangerous
fb3345ad
GE
102 close();
103 }
e1614a6d 104
fb3345ad
GE
105 if (!connection::is_closed())
106 do_callbacks(new_connection);
7087e187
GE
107}
108
ced847b2
TJ
109/**
110 * Destructor. Closes an open connection.
111 */
112socket_client_connection::~socket_client_connection()
113{
56f3994d 114 // Destructor of socket_handler will close the socket!
ced847b2
TJ
115}
116
117
487afb79 118/// establish a connection via tcp
b604df5f 119void socket_client_connection::tcp_connect(int max_retries)
7087e187 120{
b604df5f
GE
121 struct sockaddr_in sock_addr;
122
123 sock_addr.sin_family = AF_INET;
124 sock_addr.sin_port = htons(port);
125
126 // find the target ip
127 if (inet_aton(server.c_str(),&sock_addr.sin_addr)==0)
7087e187 128 {
b604df5f
GE
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);
7087e187 133
b604df5f
GE
134 memcpy(&sock_addr.sin_addr,server_hent->h_addr_list[0],sizeof(sock_addr.sin_addr));
135 }
7087e187 136
b604df5f
GE
137 sock = socket(PF_INET, SOCK_STREAM, 0);
138 if (!sock)
139 throw t2n_connect_error(string("socket() error: ")+strerror(errno));
7087e187 140
b604df5f
GE
141 try
142 {
143 connect_with_timeout((struct sockaddr *) &sock_addr,sizeof(sock_addr));
7087e187 144 }
b604df5f 145 catch (t2n_connect_error &e)
7087e187 146 {
b604df5f
GE
147 // recurse if retries left
148 if (max_retries > 0)
e1614a6d
GE
149 {
150 LOGSTREAM(debug,"retrying connect after connect error");
b604df5f 151 tcp_connect(max_retries-1);
e1614a6d
GE
152 }
153 else
fb3345ad 154 throw t2n_connect_error("no more retries left after connect error");
b604df5f 155 }
b604df5f 156}
7087e187 157
487afb79 158/// establish a connection via unix-socket
b604df5f
GE
159void socket_client_connection::unix_connect(int max_retries)
160{
161 struct sockaddr_un unix_addr;
7087e187 162
b604df5f
GE
163 unix_addr.sun_family = AF_UNIX;
164 strcpy (unix_addr.sun_path, path.c_str());
7087e187 165
b604df5f
GE
166 sock = socket(PF_UNIX, SOCK_STREAM, 0);
167 if (!sock)
168 throw t2n_connect_error(string("socket() error: ")+strerror(errno));
7087e187 169
b604df5f
GE
170 try
171 {
172 connect_with_timeout((struct sockaddr *) &unix_addr, sizeof(unix_addr));
173 }
174 catch (t2n_connect_error &e)
175 {
176 // recurse if retries left
177 if (max_retries > 0)
e1614a6d
GE
178 {
179 LOGSTREAM(debug,"retrying connect after connect error");
5b71156f 180 unix_connect(max_retries-1);
e1614a6d
GE
181 }
182 else
fb3345ad 183 throw t2n_connect_error("no more retries left after connect error");
7087e187 184 }
b604df5f
GE
185}
186
487afb79 187/// execute a connect on a prepared socket (tcp or unix) respecting timeouts
b604df5f
GE
188void socket_client_connection::connect_with_timeout(struct sockaddr *sock_addr,unsigned int sockaddr_size)
189{
7087e187 190 set_socket_options(sock);
91730468 191
039e52da
GE
192 /* non-blocking mode */
193 int flflags;
194 flflags=fcntl(sock,F_GETFL,0);
195 if (flflags < 0)
196 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
197
198 flflags &= (O_NONBLOCK ^ 0xFFFF);
199 if (fcntl(sock,F_SETFL,flflags) < 0)
200 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
201
202
e1614a6d 203 LOGSTREAM(debug,"connect_with_timeout()");
b604df5f
GE
204 int ret=::connect(sock,sock_addr, sockaddr_size);
205
206 if (ret < 0)
207 {
208 if (errno==EINPROGRESS)
209 {
e1614a6d
GE
210 LOGSTREAM(debug,"connect_with_timeout(): EINPROGRESS");
211
b604df5f
GE
212 /* set timeout */
213 struct timeval tval;
214 struct timeval *timeout_ptr;
215
216 if (connect_timeout_usec == -1)
217 timeout_ptr = NULL;
218 else
219 {
220 timeout_ptr = &tval;
221
222 // convert timeout from long long usec to int sec + int usec
223 tval.tv_sec = connect_timeout_usec / 1000000;
224 tval.tv_usec = connect_timeout_usec % 1000000;
225 }
226
227 fd_set connect_socket_set;
228 FD_ZERO(&connect_socket_set);
229 FD_SET(sock,&connect_socket_set);
230
231 int ret;
232 while ((ret=select(FD_SETSIZE, NULL, &connect_socket_set, NULL, timeout_ptr)) &&
233 ret < 0 && errno==EINTR);
234
235 if (ret < 0)
236 throw t2n_connect_error(string("connect() error (select): ")+strerror(errno));
237
238 socklen_t sopt=sizeof(int);
239 int valopt;
240 ret=getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &sopt);
241 if (ret < 0 || valopt)
242 throw t2n_connect_error(string("connect() error (getsockopt): ")+strerror(errno));
243 }
244 else
245 throw t2n_connect_error(string("connect() error: ")+strerror(errno));
246 }
e1614a6d
GE
247
248 LOGSTREAM(debug,"connect_with_timeout(): success");
7087e187
GE
249}
250
251void socket_client_connection::close()
252{
253 if (!client_connection::is_closed())
254 {
255 socket_handler::close();
256 client_connection::close();
257 }
258}
259
af84dfb5 260/** @brief try to reconnect the current connection with the same connection credentials (host and port or path)
fb3345ad
GE
261
262 @note will throw an exeption if reconnecting not possible
af84dfb5
GE
263*/
264void socket_client_connection::reconnect()
265{
e1614a6d
GE
266 LOGSTREAM(debug,"reconnect()");
267
af84dfb5
GE
268 // close the current connection if still open
269 close();
270
271 socket_type_value type=get_type();
272
273 if (type == tcp_s)
274 tcp_connect(max_retries);
275 else if (type == unix_s)
276 unix_connect(max_retries);
277
fb3345ad 278 // connection is open now, otherwise an execption would have been thrown
af84dfb5 279 reopen();
e1614a6d
GE
280
281 LOGSTREAM(debug,"reconnect() done, client_connection::is_closed() now " << client_connection::is_closed());
af84dfb5
GE
282}
283
7087e187 284}