get_sockets_set added for external select() calls. Boost test case added to check...
[libt2n] / src / socket_server.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*/
ac7fdc22 22
0cf4dc9b
GE
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>
673142e5 31#include <sys/stat.h>
0cf4dc9b
GE
32#include <arpa/inet.h>
33#include <netinet/in.h>
34#include <netdb.h>
35#include <fcntl.h>
36#include <time.h>
37#include <pwd.h>
38#include <grp.h>
39
aa499d20
GE
40#include <sstream>
41
ac7fdc22 42#include "socket_server.hxx"
0cf4dc9b 43#include "t2n_exception.hxx"
a7170401 44#include "log.hxx"
0cf4dc9b
GE
45
46using namespace std;
ac7fdc22
GE
47
48namespace libt2n
49{
50
94247295
GE
51/** @brief create a new tcp-based server
52 @param port tcp port you want to listen on
53 @param ip the local ip you want to listen on. "0.0.0.0" means all local ips (default).
54*/
644c4d26 55socket_server::socket_server(int port, const std::string& ip)
a11e19b7 56 : server(), socket_handler(0,tcp_s)
ac7fdc22 57{
cc68aabb
GE
58 /* Create the socket. */
59 sock = socket (PF_INET, SOCK_STREAM, 0);
60 if (sock < 0)
a7170401 61 EXCEPTIONSTREAM(error,t2n_server_error,"error opening socket: " << strerror(errno));
cc68aabb
GE
62
63 set_socket_options(sock);
64
65 /* Give the socket a name. */
66 struct sockaddr_in sockaddr;
67 sockaddr.sin_family = AF_INET;
68 sockaddr.sin_port = htons(port);
69
70 if (inet_aton(ip.c_str(),&sockaddr.sin_addr) == 0)
a7170401 71 EXCEPTIONSTREAM(error,t2n_server_error,"failed listening on invalid ip " << ip);
cc68aabb
GE
72
73 if (bind (sock, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) < 0)
238ad35f
TJ
74 {
75 // FIXME: Calls virtual function socket_server::get_logstream() in constructor
a7170401 76 EXCEPTIONSTREAM(error,t2n_server_error,"error binding socket: " << strerror(errno));
238ad35f 77 }
cc68aabb
GE
78
79 start_listening();
ac7fdc22
GE
80}
81
94247295
GE
82/** @brief create a new unix-socked-based server
83 @param path path of the socket
84 @param filemode permissions you want to open the socket with
85 @param user local username for the socket
86 @param group local groupname for the socket
87*/
644c4d26 88socket_server::socket_server(const std::string& path, mode_t filemode, const std::string& user, const std::string& group)
a11e19b7 89 : server(), socket_handler(0,unix_s)
ac7fdc22 90{
ac7fdc22
GE
91 unix_path=path;
92
238ad35f
TJ
93 // TODO: Every EXCEPTIONSTREAM in here calls virtual function get_logstream()
94
ac7fdc22 95 /* Create the socket. */
0cf4dc9b
GE
96 sock = socket (PF_UNIX, SOCK_STREAM, 0);
97 if (sock < 0)
a7170401 98 EXCEPTIONSTREAM(error,t2n_server_error,"error opening socket: " << strerror(errno));
0cf4dc9b
GE
99
100 set_socket_options(sock);
101
102 /* Give the socket a name. */
103 struct sockaddr_un unix_name;
104 unix_name.sun_family = AF_UNIX;
105 strncpy (unix_name.sun_path, unix_path.c_str(),sizeof(unix_name.sun_path));
106
107 /* just to make sure there is no other socket file */
108 unlink (unix_name.sun_path);
109
110 if (bind (sock, (struct sockaddr *) &unix_name, sizeof (unix_name)) < 0)
a7170401 111 EXCEPTIONSTREAM(error,t2n_server_error,"error binding socket: " << strerror(errno));
0cf4dc9b
GE
112
113 /* change permissions */
114 if (chmod (unix_name.sun_path, filemode) != 0)
a7170401 115 EXCEPTIONSTREAM(error,t2n_server_error,"error changing permission: " << strerror(errno));
0cf4dc9b 116
644c4d26 117 if (!user.empty() && !group.empty())
0cf4dc9b 118 {
644c4d26 119 // TODO maybe use current user/group if one of them is empty
0cf4dc9b 120
644c4d26
GE
121 struct passwd *socket_user = getpwnam (user.c_str());
122 if (socket_user == NULL)
a7170401
GE
123 EXCEPTIONSTREAM(error,t2n_server_error,"error getting socket user: " << strerror(errno));
124
644c4d26
GE
125 struct group *socket_group = getgrnam (group.c_str());
126 if (socket_group == NULL)
a7170401
GE
127 EXCEPTIONSTREAM(error,t2n_server_error,"error getting socket group: " << strerror(errno));
128
644c4d26 129 if (chown (unix_name.sun_path, socket_user->pw_uid, socket_group->gr_gid) != 0)
a7170401 130 EXCEPTIONSTREAM(error,t2n_server_error,"error changing socket ownership: " << strerror(errno));
0cf4dc9b
GE
131 }
132
cc68aabb
GE
133 start_listening();
134}
135
56f3994d
TJ
136/**
137 * Destructor
138 */
cc68aabb
GE
139socket_server::~socket_server()
140{
56f3994d
TJ
141 // close all client connections
142 server::close();
143
144 // server socket will be closed by destructor of socket_handler
cc68aabb
GE
145
146 if (get_type()==unix_s)
147 unlink(unix_path.c_str());
56f3994d
TJ
148
149 // disconnect connection<->server pointer
150 std::map<unsigned int, server_connection*>::iterator it, it_end = connections.end();
151 for (it = connections.begin(); it != it_end; ++it)
152 {
153 socket_server_connection *conn = dynamic_cast<socket_server_connection*>(it->second);
154 if (conn)
155 conn->my_server = NULL;
156 }
cc68aabb
GE
157}
158
159/// start listening on a new server socket (called by the constructors)
160void socket_server::start_listening()
161{
0cf4dc9b 162 if (listen (sock, 5) < 0)
a7170401 163 EXCEPTIONSTREAM(error,t2n_server_error,"error listening to socket: " << strerror(errno));
0cf4dc9b
GE
164
165 /* clear & insert server sock into the fd_tab to prepare select */
166 FD_ZERO(&connection_set);
167 FD_SET (sock, &connection_set);
1fdd03d2 168 sockets_set.insert(sock);
0cf4dc9b
GE
169}
170
cc68aabb 171/// handle a new connection from a client
0cf4dc9b
GE
172void socket_server::new_connection()
173{
04e6b271 174 struct sockaddr_un clientname;
ac7fdc22 175
04e6b271
GE
176 unsigned int size = sizeof (clientname);
177 int newsock = accept (sock,(struct sockaddr *) &clientname,&size);
178 if (newsock < 0)
179 {
c7857475
GE
180 // return on non-fatal errors (list taken from man-page)
181 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ECONNABORTED || errno == EINTR ||
182 errno == EMFILE || errno == ENFILE || errno == ENOBUFS || errno == ENOMEM ||
183 errno == EPROTO || errno == EPERM || errno == ETIMEDOUT)
04e6b271 184 {
c7857475 185 LOGSTREAM(error,"non-fatal accept error: " << strerror(errno));
04e6b271
GE
186 return;
187 }
188
c7857475
GE
189 /* fatal error: will usually kill or restart the server */
190 EXCEPTIONSTREAM(error,t2n_server_error,"fatal error accepting connection: " << strerror(errno));
04e6b271
GE
191 }
192
193 FD_SET (newsock, &connection_set);
1fdd03d2 194 sockets_set.insert(newsock);
04e6b271 195
a11e19b7
GE
196 socket_server_connection *nc=new socket_server_connection(newsock, get_type(), get_default_timeout());
197 nc->set_socket_options(newsock);
04e6b271 198
a11e19b7 199 add_connection(nc);
04e6b271
GE
200
201 return;
0cf4dc9b 202}
ac7fdc22 203
45a2ebc9
GE
204/** @brief look for new connections and new data in any of the existing connections
205 @param usec_timeout wait until new data is found, max timeout usecs.
206 -1: wait endless
207 0: return instantly
208 @param usec_timeout_remaining if non-NULL the function will write the
209 not used time to the given target
210 @retval true if new data was found (does not mean that the received data
211 is a complete packet though)
212*/
213bool socket_server::fill_buffer(long long usec_timeout,long long* usec_timeout_remaining)
ac7fdc22 214{
0cf4dc9b
GE
215 fd_set used_fdset=connection_set;
216
217 /* set timeout */
218 struct timeval tval;
219 struct timeval *timeout_ptr;
220
221 if (usec_timeout == -1)
222 timeout_ptr = NULL;
223 else
224 {
225 timeout_ptr = &tval;
226
227 // timeout von long long usec in int sec + int usec umrechnen
228 tval.tv_sec = usec_timeout / 1000000;
229 tval.tv_usec = usec_timeout % 1000000;
230 }
231
232 int ret=select (FD_SETSIZE, &used_fdset, NULL, NULL, timeout_ptr);
233
45a2ebc9
GE
234 // return the timeout we did not use
235 if (usec_timeout > 0 && usec_timeout_remaining != NULL)
236 *usec_timeout_remaining=(tval.tv_sec*1000000)+tval.tv_usec;
237
0cf4dc9b
GE
238 if (ret < 0)
239 {
240 if (errno == EINTR)
241 {
242 // select interrupted by signal
243 ret=0;
244 }
245 else
a7170401 246 EXCEPTIONSTREAM(error,t2n_server_error,"select error: " << strerror(errno));
0cf4dc9b
GE
247 }
248
249 if (ret > 0)
250 {
251 // we have data pending
252
253 // check for new connection
254 if (FD_ISSET (sock, &used_fdset))
255 {
256 new_connection();
257 }
258
259 // check all connections for pending data
94247295 260 return fill_connection_buffers();
0cf4dc9b 261 }
ac7fdc22 262
94247295 263 return false;
ac7fdc22
GE
264}
265
cc68aabb 266/// call fill_buffer() on all connections, called from fill_buffer()
94247295 267bool socket_server::fill_connection_buffers()
aa499d20 268{
20a05ab7 269 bool data_found = false;
94247295 270
a11e19b7
GE
271 std::map<unsigned int, server_connection*>::iterator ie=connections.end();
272 for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
644c4d26 273 if (!i->second->server_connection::is_closed())
c7857475
GE
274 {
275 // shutdown all connections which throw exceptions to protect the server
276 try
277 {
278 if (i->second->fill_buffer(0))
279 data_found=true;
280 }
281 catch (t2n_transfer_error &e)
282 { i->second->close(); }
c7857475 283 }
94247295
GE
284
285 return data_found;
aa499d20
GE
286}
287
cc68aabb 288/// remove the socket of a connection after the connection has been closed
a11e19b7 289void socket_server::remove_connection_socket(int sock)
ac7fdc22 290{
a11e19b7 291 FD_CLR(sock, &connection_set);
1fdd03d2 292 sockets_set.erase(sock);
ac7fdc22
GE
293}
294
56f3994d
TJ
295/**
296 * Destructor
297 */
298socket_server_connection::~socket_server_connection()
299{
300 // Only notify parent server about going down.
301 // The real socket will be closed by the destructor of the base classes.
302 if (my_server && sock != -1)
303 {
304 socket_server *srv = dynamic_cast<socket_server*>(my_server);
305 if (srv)
306 srv->remove_connection_socket(sock);
307 }
308}
309
cc68aabb 310/// close this connection. complete data waiting in the buffer can still be retrieved.
a11e19b7 311void socket_server_connection::close()
ac7fdc22 312{
56f3994d 313 if (my_server && sock != -1)
aa499d20 314 {
56f3994d
TJ
315 socket_server *srv = dynamic_cast<socket_server*>(my_server);
316 if (srv)
317 srv->remove_connection_socket(sock);
aa499d20
GE
318 }
319
56f3994d 320 if (!server_connection::is_closed())
aa499d20 321 {
56f3994d
TJ
322 socket_handler::close();
323 server_connection::close();
aa499d20 324 }
ac7fdc22
GE
325}
326
327}