Add missing include for chmod()
[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);
168}
169
cc68aabb 170/// handle a new connection from a client
0cf4dc9b
GE
171void socket_server::new_connection()
172{
04e6b271 173 struct sockaddr_un clientname;
ac7fdc22 174
04e6b271
GE
175 unsigned int size = sizeof (clientname);
176 int newsock = accept (sock,(struct sockaddr *) &clientname,&size);
177 if (newsock < 0)
178 {
c7857475
GE
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)
04e6b271 183 {
c7857475 184 LOGSTREAM(error,"non-fatal accept error: " << strerror(errno));
04e6b271
GE
185 return;
186 }
187
c7857475
GE
188 /* fatal error: will usually kill or restart the server */
189 EXCEPTIONSTREAM(error,t2n_server_error,"fatal error accepting connection: " << strerror(errno));
04e6b271
GE
190 }
191
192 FD_SET (newsock, &connection_set);
193
a11e19b7
GE
194 socket_server_connection *nc=new socket_server_connection(newsock, get_type(), get_default_timeout());
195 nc->set_socket_options(newsock);
04e6b271 196
a11e19b7 197 add_connection(nc);
04e6b271
GE
198
199 return;
0cf4dc9b 200}
ac7fdc22 201
45a2ebc9
GE
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.
204 -1: wait endless
205 0: return instantly
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)
210*/
211bool socket_server::fill_buffer(long long usec_timeout,long long* usec_timeout_remaining)
ac7fdc22 212{
0cf4dc9b
GE
213 fd_set used_fdset=connection_set;
214
215 /* set timeout */
216 struct timeval tval;
217 struct timeval *timeout_ptr;
218
219 if (usec_timeout == -1)
220 timeout_ptr = NULL;
221 else
222 {
223 timeout_ptr = &tval;
224
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;
228 }
229
230 int ret=select (FD_SETSIZE, &used_fdset, NULL, NULL, timeout_ptr);
231
45a2ebc9
GE
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;
235
0cf4dc9b
GE
236 if (ret < 0)
237 {
238 if (errno == EINTR)
239 {
240 // select interrupted by signal
241 ret=0;
242 }
243 else
a7170401 244 EXCEPTIONSTREAM(error,t2n_server_error,"select error: " << strerror(errno));
0cf4dc9b
GE
245 }
246
247 if (ret > 0)
248 {
249 // we have data pending
250
251 // check for new connection
252 if (FD_ISSET (sock, &used_fdset))
253 {
254 new_connection();
255 }
256
257 // check all connections for pending data
94247295 258 return fill_connection_buffers();
0cf4dc9b 259 }
ac7fdc22 260
94247295 261 return false;
ac7fdc22
GE
262}
263
cc68aabb 264/// call fill_buffer() on all connections, called from fill_buffer()
94247295 265bool socket_server::fill_connection_buffers()
aa499d20 266{
20a05ab7 267 bool data_found = false;
94247295 268
a11e19b7
GE
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++)
644c4d26 271 if (!i->second->server_connection::is_closed())
c7857475
GE
272 {
273 // shutdown all connections which throw exceptions to protect the server
274 try
275 {
276 if (i->second->fill_buffer(0))
277 data_found=true;
278 }
279 catch (t2n_transfer_error &e)
280 { i->second->close(); }
c7857475 281 }
94247295
GE
282
283 return data_found;
aa499d20
GE
284}
285
cc68aabb 286/// remove the socket of a connection after the connection has been closed
a11e19b7 287void socket_server::remove_connection_socket(int sock)
ac7fdc22 288{
a11e19b7 289 FD_CLR(sock, &connection_set);
ac7fdc22
GE
290}
291
56f3994d
TJ
292/**
293 * Destructor
294 */
295socket_server_connection::~socket_server_connection()
296{
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)
300 {
301 socket_server *srv = dynamic_cast<socket_server*>(my_server);
302 if (srv)
303 srv->remove_connection_socket(sock);
304 }
305}
306
cc68aabb 307/// close this connection. complete data waiting in the buffer can still be retrieved.
a11e19b7 308void socket_server_connection::close()
ac7fdc22 309{
56f3994d 310 if (my_server && sock != -1)
aa499d20 311 {
56f3994d
TJ
312 socket_server *srv = dynamic_cast<socket_server*>(my_server);
313 if (srv)
314 srv->remove_connection_socket(sock);
aa499d20
GE
315 }
316
56f3994d 317 if (!server_connection::is_closed())
aa499d20 318 {
56f3994d
TJ
319 socket_handler::close();
320 server_connection::close();
aa499d20 321 }
ac7fdc22
GE
322}
323
324}