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