2 Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
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.
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.
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.
27 #include <sys/types.h>
28 #include <sys/socket.h>
32 #include <arpa/inet.h>
33 #include <netinet/in.h>
42 #include "socket_server.hxx"
43 #include "t2n_exception.hxx"
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).
55 socket_server::socket_server(int port, const std::string& ip)
56 : server(), socket_handler(0,tcp_s)
58 /* Create the socket. */
59 sock = socket (PF_INET, SOCK_STREAM, 0);
61 EXCEPTIONSTREAM(error,t2n_server_error,"error opening socket: " << strerror(errno));
63 set_socket_options(sock);
65 /* Give the socket a name. */
66 struct sockaddr_in sockaddr;
67 sockaddr.sin_family = AF_INET;
68 sockaddr.sin_port = htons(port);
70 if (inet_aton(ip.c_str(),&sockaddr.sin_addr) == 0)
71 EXCEPTIONSTREAM(error,t2n_server_error,"failed listening on invalid ip " << ip);
73 if (bind (sock, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) < 0)
75 // FIXME: Calls virtual function socket_server::get_logstream() in constructor
76 EXCEPTIONSTREAM(error,t2n_server_error,"error binding socket: " << strerror(errno));
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
88 socket_server::socket_server(const std::string& path, mode_t filemode, const std::string& user, const std::string& group)
89 : server(), socket_handler(0,unix_s)
93 // TODO: Every EXCEPTIONSTREAM in here calls virtual function get_logstream()
95 /* Create the socket. */
96 sock = socket (PF_UNIX, SOCK_STREAM, 0);
98 EXCEPTIONSTREAM(error,t2n_server_error,"error opening socket: " << strerror(errno));
100 set_socket_options(sock);
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));
107 /* just to make sure there is no other socket file */
108 unlink (unix_name.sun_path);
110 if (bind (sock, (struct sockaddr *) &unix_name, sizeof (unix_name)) < 0)
111 EXCEPTIONSTREAM(error,t2n_server_error,"error binding socket: " << strerror(errno));
113 /* change permissions */
114 if (chmod (unix_name.sun_path, filemode) != 0)
115 EXCEPTIONSTREAM(error,t2n_server_error,"error changing permission: " << strerror(errno));
117 if (!user.empty() && !group.empty())
119 // TODO maybe use current user/group if one of them is empty
121 struct passwd *socket_user = getpwnam (user.c_str());
122 if (socket_user == NULL)
123 EXCEPTIONSTREAM(error,t2n_server_error,"error getting socket user: " << strerror(errno));
125 struct group *socket_group = getgrnam (group.c_str());
126 if (socket_group == NULL)
127 EXCEPTIONSTREAM(error,t2n_server_error,"error getting socket group: " << strerror(errno));
129 if (chown (unix_name.sun_path, socket_user->pw_uid, socket_group->gr_gid) != 0)
130 EXCEPTIONSTREAM(error,t2n_server_error,"error changing socket ownership: " << strerror(errno));
139 socket_server::~socket_server()
141 // close all client connections
144 // server socket will be closed by destructor of socket_handler
146 if (get_type()==unix_s)
147 unlink(unix_path.c_str());
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)
153 socket_server_connection *conn = dynamic_cast<socket_server_connection*>(it->second);
155 conn->my_server = NULL;
159 /// start listening on a new server socket (called by the constructors)
160 void socket_server::start_listening()
162 if (listen (sock, 5) < 0)
163 EXCEPTIONSTREAM(error,t2n_server_error,"error listening to socket: " << strerror(errno));
165 /* clear & insert server sock into the fd_tab to prepare select */
166 FD_ZERO(&connection_set);
167 FD_SET (sock, &connection_set);
170 /// handle a new connection from a client
171 void socket_server::new_connection()
173 struct sockaddr_un clientname;
175 unsigned int size = sizeof (clientname);
176 int newsock = accept (sock,(struct sockaddr *) &clientname,&size);
179 // return on non-fatal errors (list taken from man-page)
180 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ECONNABORTED || errno == EINTR ||
181 errno == EMFILE || errno == ENFILE || errno == ENOBUFS || errno == ENOMEM ||
182 errno == EPROTO || errno == EPERM || errno == ETIMEDOUT)
184 LOGSTREAM(error,"non-fatal accept error: " << strerror(errno));
188 /* fatal error: will usually kill or restart the server */
189 EXCEPTIONSTREAM(error,t2n_server_error,"fatal error accepting connection: " << strerror(errno));
192 FD_SET (newsock, &connection_set);
194 socket_server_connection *nc=new socket_server_connection(newsock, get_type(), get_default_timeout());
195 nc->set_socket_options(newsock);
202 /** @brief look for new connections and new data in any of the existing connections
203 @param usec_timeout wait until new data is found, max timeout usecs.
206 @param usec_timeout_remaining if non-NULL the function will write the
207 not used time to the given target
208 @retval true if new data was found (does not mean that the received data
209 is a complete packet though)
211 bool socket_server::fill_buffer(long long usec_timeout,long long* usec_timeout_remaining)
213 fd_set used_fdset=connection_set;
217 struct timeval *timeout_ptr;
219 if (usec_timeout == -1)
225 // timeout von long long usec in int sec + int usec umrechnen
226 tval.tv_sec = usec_timeout / 1000000;
227 tval.tv_usec = usec_timeout % 1000000;
230 int ret=select (FD_SETSIZE, &used_fdset, NULL, NULL, timeout_ptr);
232 // return the timeout we did not use
233 if (usec_timeout > 0 && usec_timeout_remaining != NULL)
234 *usec_timeout_remaining=(tval.tv_sec*1000000)+tval.tv_usec;
240 // select interrupted by signal
244 EXCEPTIONSTREAM(error,t2n_server_error,"select error: " << strerror(errno));
249 // we have data pending
251 // check for new connection
252 if (FD_ISSET (sock, &used_fdset))
257 // check all connections for pending data
258 return fill_connection_buffers();
264 /// call fill_buffer() on all connections, called from fill_buffer()
265 bool socket_server::fill_connection_buffers()
267 bool data_found = false;
269 std::map<unsigned int, server_connection*>::iterator ie=connections.end();
270 for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
271 if (!i->second->server_connection::is_closed())
273 // shutdown all connections which throw exceptions to protect the server
276 if (i->second->fill_buffer(0))
279 catch (t2n_transfer_error &e)
280 { i->second->close(); }
286 /// remove the socket of a connection after the connection has been closed
287 void socket_server::remove_connection_socket(int sock)
289 FD_CLR(sock, &connection_set);
295 socket_server_connection::~socket_server_connection()
297 // Only notify parent server about going down.
298 // The real socket will be closed by the destructor of the base classes.
299 if (my_server && sock != -1)
301 socket_server *srv = dynamic_cast<socket_server*>(my_server);
303 srv->remove_connection_socket(sock);
307 /// close this connection. complete data waiting in the buffer can still be retrieved.
308 void socket_server_connection::close()
310 if (my_server && sock != -1)
312 socket_server *srv = dynamic_cast<socket_server*>(my_server);
314 srv->remove_connection_socket(sock);
317 if (!server_connection::is_closed())
319 socket_handler::close();
320 server_connection::close();