client_wrapper.hxx, socket_wrapper.hxx: reorder member initialization order
[libt2n] / src / socket_client.cpp
... / ...
CommitLineData
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*/
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"
43#include "log.hxx"
44
45using namespace std;
46
47namespace libt2n
48{
49
50/// returns a closed connection if connection could not be established, call get_last_error_msg() for details
51socket_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)
55{
56 max_retries=_max_retries;
57 connect_timeout_usec=_connect_timeout_usec;
58
59 server=_server;
60 port=_port;
61
62 set_logging(_logstream,_log_level);
63
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);
72 // FIXME: Don't call virtual function in constructor. Currently not dangerous but bad design.
73 close();
74 }
75
76 if (!connection::is_closed())
77 do_callbacks(new_connection);
78}
79
80/// returns a closed connection if connection could not be established, call get_last_error_msg() for details
81socket_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)
85{
86 max_retries=_max_retries;
87 connect_timeout_usec=_connect_timeout_usec;
88
89 path=_path;
90
91 set_logging(_logstream,_log_level);
92
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);
101 // FIXME: Don't call virtual function in constructor. Currently not dangerous
102 close();
103 }
104
105 if (!connection::is_closed())
106 do_callbacks(new_connection);
107}
108
109/**
110 * Destructor. Closes an open connection.
111 */
112socket_client_connection::~socket_client_connection()
113{
114 // Destructor of socket_handler will close the socket!
115}
116
117
118/// establish a connection via tcp
119void socket_client_connection::tcp_connect(int max_retries)
120{
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)
128 {
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);
133
134 memcpy(&sock_addr.sin_addr,server_hent->h_addr_list[0],sizeof(sock_addr.sin_addr));
135 }
136
137 sock = socket(PF_INET, SOCK_STREAM, 0);
138 if (!sock)
139 throw t2n_connect_error(string("socket() error: ")+strerror(errno));
140
141 try
142 {
143 connect_with_timeout((struct sockaddr *) &sock_addr,sizeof(sock_addr));
144 }
145 catch (t2n_connect_error &e)
146 {
147 // recurse if retries left
148 if (max_retries > 0)
149 {
150 LOGSTREAM(debug,"retrying connect after connect error");
151 tcp_connect(max_retries-1);
152 }
153 else
154 throw t2n_connect_error("no more retries left after connect error");
155 }
156}
157
158/// establish a connection via unix-socket
159void socket_client_connection::unix_connect(int max_retries)
160{
161 struct sockaddr_un unix_addr;
162
163 unix_addr.sun_family = AF_UNIX;
164 strcpy (unix_addr.sun_path, path.c_str());
165
166 sock = socket(PF_UNIX, SOCK_STREAM, 0);
167 if (!sock)
168 throw t2n_connect_error(string("socket() error: ")+strerror(errno));
169
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)
178 {
179 LOGSTREAM(debug,"retrying connect after connect error");
180 unix_connect(max_retries-1);
181 }
182 else
183 throw t2n_connect_error("no more retries left after connect error");
184 }
185}
186
187/// execute a connect on a prepared socket (tcp or unix) respecting timeouts
188void socket_client_connection::connect_with_timeout(struct sockaddr *sock_addr,unsigned int sockaddr_size)
189{
190 set_socket_options(sock);
191
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
203 LOGSTREAM(debug,"connect_with_timeout()");
204 int ret=::connect(sock,sock_addr, sockaddr_size);
205
206 if (ret < 0)
207 {
208 if (errno==EINPROGRESS)
209 {
210 LOGSTREAM(debug,"connect_with_timeout(): EINPROGRESS");
211
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 }
247
248 LOGSTREAM(debug,"connect_with_timeout(): success");
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
260/** @brief try to reconnect the current connection with the same connection credentials (host and port or path)
261
262 @note will throw an exeption if reconnecting not possible
263*/
264void socket_client_connection::reconnect()
265{
266 LOGSTREAM(debug,"reconnect()");
267
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
278 // connection is open now, otherwise an execption would have been thrown
279 reopen();
280
281 LOGSTREAM(debug,"reconnect() done, client_connection::is_closed() now " << client_connection::is_closed());
282}
283
284}