Change license from LGPL to GPL version 2 + linking exception. This fixes C++ templat...
[libt2n] / src / socket_server.cpp
1 /*
2 Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
3
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
6
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
9
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.
15
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.
18
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.
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_server.hxx"
42 #include "t2n_exception.hxx"
43 #include "log.hxx"
44
45 using namespace std;
46
47 namespace libt2n
48 {
49
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 */
54 socket_server::socket_server(int port, const std::string& ip)
55     : server(), socket_handler(0,tcp_s)
56 {
57     /* Create the socket. */
58     sock = socket (PF_INET, SOCK_STREAM, 0);
59     if (sock < 0)
60         EXCEPTIONSTREAM(error,t2n_server_error,"error opening socket: " << strerror(errno));
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)
70         EXCEPTIONSTREAM(error,t2n_server_error,"failed listening on invalid ip " << ip);
71
72     if (bind (sock, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) < 0)
73     {
74         // FIXME: Calls virtual function socket_server::get_logstream() in constructor
75         EXCEPTIONSTREAM(error,t2n_server_error,"error binding socket: " << strerror(errno));
76     }
77
78     start_listening();
79 }
80
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 */
87 socket_server::socket_server(const std::string& path, mode_t filemode, const std::string& user, const std::string& group)
88     : server(), socket_handler(0,unix_s)
89 {
90     unix_path=path;
91
92     // TODO: Every EXCEPTIONSTREAM in here calls virtual function get_logstream()
93
94     /* Create the socket. */
95     sock = socket (PF_UNIX, SOCK_STREAM, 0);
96     if (sock < 0)
97         EXCEPTIONSTREAM(error,t2n_server_error,"error opening socket: " << strerror(errno));
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)
110         EXCEPTIONSTREAM(error,t2n_server_error,"error binding socket: " << strerror(errno));
111
112     /* change permissions */
113     if (chmod (unix_name.sun_path, filemode) != 0) 
114         EXCEPTIONSTREAM(error,t2n_server_error,"error changing permission: " << strerror(errno));
115
116     if (!user.empty() && !group.empty())
117     {
118         // TODO maybe use current user/group if one of them is empty
119
120         struct passwd *socket_user = getpwnam (user.c_str());
121         if (socket_user == NULL) 
122             EXCEPTIONSTREAM(error,t2n_server_error,"error getting socket user: " << strerror(errno));
123
124         struct group *socket_group = getgrnam (group.c_str());
125         if (socket_group == NULL) 
126             EXCEPTIONSTREAM(error,t2n_server_error,"error getting socket group: " << strerror(errno));
127
128         if (chown (unix_name.sun_path, socket_user->pw_uid, socket_group->gr_gid) != 0) 
129             EXCEPTIONSTREAM(error,t2n_server_error,"error changing socket ownership: " << strerror(errno));
130     }
131
132     start_listening();
133 }
134
135 /**
136  * Destructor
137  */
138 socket_server::~socket_server()
139 {
140     // close all client connections
141     server::close();
142
143     // server socket will be closed by destructor of socket_handler
144
145     if (get_type()==unix_s)
146         unlink(unix_path.c_str());
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     }
156 }
157
158 /// start listening on a new server socket (called by the constructors)
159 void socket_server::start_listening()
160 {
161     if (listen (sock, 5) < 0)
162         EXCEPTIONSTREAM(error,t2n_server_error,"error listening to socket: " << strerror(errno));
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
169 /// handle a new connection from a client
170 void socket_server::new_connection()
171 {
172     struct sockaddr_un clientname;
173
174     unsigned int size = sizeof (clientname);
175     int newsock = accept (sock,(struct sockaddr *) &clientname,&size);
176     if (newsock < 0)
177     {
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)
182         {
183             LOGSTREAM(error,"non-fatal accept error: " << strerror(errno));
184             return;
185         }
186
187         /* fatal error: will usually kill or restart the server */
188         EXCEPTIONSTREAM(error,t2n_server_error,"fatal error accepting connection: " << strerror(errno));
189     }
190
191     FD_SET (newsock, &connection_set);
192
193     socket_server_connection *nc=new socket_server_connection(newsock, get_type(), get_default_timeout());
194     nc->set_socket_options(newsock);
195
196     add_connection(nc);
197
198     return;
199 }
200
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 */
210 bool socket_server::fill_buffer(long long usec_timeout,long long* usec_timeout_remaining)
211 {
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
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
235     if (ret < 0)
236     {
237         if (errno == EINTR)
238         {
239             // select interrupted by signal
240             ret=0;
241         }
242         else
243             EXCEPTIONSTREAM(error,t2n_server_error,"select error: " << strerror(errno));
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
257         return fill_connection_buffers();
258     }
259
260     return false;
261 }
262
263 /// call fill_buffer() on all connections, called from fill_buffer()
264 bool socket_server::fill_connection_buffers()
265 {
266     bool data_found = false;
267
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++)
270         if (!i->second->server_connection::is_closed())
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         }
283
284     return data_found;
285 }
286
287 /// remove the socket of a connection after the connection has been closed
288 void socket_server::remove_connection_socket(int sock)
289 {
290     FD_CLR(sock, &connection_set);
291 }
292
293 /**
294  * Destructor
295  */
296 socket_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
308 /// close this connection. complete data waiting in the buffer can still be retrieved.
309 void socket_server_connection::close()
310 {
311     if (my_server && sock != -1)
312     {
313         socket_server *srv = dynamic_cast<socket_server*>(my_server);
314         if (srv)
315             srv->remove_connection_socket(sock);
316     }
317
318     if (!server_connection::is_closed())
319     {
320         socket_handler::close();
321         server_connection::close();
322     }
323 }
324
325 }