libt2n: (gerd) refactored connection classes
[libt2n] / src / socket_server.cpp
CommitLineData
ac7fdc22
GE
1/***************************************************************************
2 * Copyright (C) 2006 by Gerd v. Egidy *
3 * gve@intra2net.com *
4 * *
5 * This library is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU Lesser General Public License version *
7 * 2.1 as published by the Free Software Foundation. *
8 * *
9 * This library is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18 ***************************************************************************/
19
0cf4dc9b
GE
20#include <stdio.h>
21#include <errno.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <sys/types.h>
25#include <sys/socket.h>
26#include <sys/un.h>
27#include <sys/time.h>
28#include <arpa/inet.h>
29#include <netinet/in.h>
30#include <netdb.h>
31#include <fcntl.h>
32#include <time.h>
33#include <pwd.h>
34#include <grp.h>
35
aa499d20
GE
36#include <sstream>
37
ac7fdc22 38#include "socket_server.hxx"
0cf4dc9b
GE
39#include "t2n_exception.hxx"
40
41using namespace std;
ac7fdc22
GE
42
43namespace libt2n
44{
45
46socket_server::socket_server(int port, const char* ip)
a11e19b7 47 : server(), socket_handler(0,tcp_s)
ac7fdc22 48{
a11e19b7 49 // TODO
ac7fdc22
GE
50}
51
0cf4dc9b 52socket_server::socket_server(const char* path, mode_t filemode, const char* user, const char* group)
a11e19b7 53 : server(), socket_handler(0,unix_s)
ac7fdc22 54{
ac7fdc22
GE
55 unix_path=path;
56
ac7fdc22 57 /* Create the socket. */
0cf4dc9b
GE
58 sock = socket (PF_UNIX, SOCK_STREAM, 0);
59 if (sock < 0)
ac7fdc22
GE
60 {
61 string err="error opening socket: ";
0cf4dc9b
GE
62 err+=strerror(errno);
63 log(error, err);
64 throw t2n_server_error(err);
65 }
66
67 set_socket_options(sock);
68
69 /* Give the socket a name. */
70 struct sockaddr_un unix_name;
71 unix_name.sun_family = AF_UNIX;
72 strncpy (unix_name.sun_path, unix_path.c_str(),sizeof(unix_name.sun_path));
73
74 /* just to make sure there is no other socket file */
75 unlink (unix_name.sun_path);
76
77 if (bind (sock, (struct sockaddr *) &unix_name, sizeof (unix_name)) < 0)
78 {
79 string err="error binding socket: ";
80 err+=strerror(errno);
81 log(error, err);
82 throw t2n_server_error(err);
83 }
84
85 /* change permissions */
86 if (chmod (unix_name.sun_path, filemode) != 0)
87 {
88 string err="error changing permission: ";
89 err+=strerror(errno);
90 log(error, err);
91 throw t2n_server_error(err);
92 }
93
94 struct passwd *socket_user = getpwnam (user);
95 if (socket_user == NULL)
96 {
97 string err="error getting socket user: ";
98 err+=strerror(errno);
99 log(error, err);
100 throw t2n_server_error(err);
101 }
102
103 struct group *socket_group = getgrnam (group);
104 if (socket_group == NULL)
105 {
106 string err="error getting socket group: ";
107 err+=strerror(errno);
108 log(error, err);
109 throw t2n_server_error(err);
110 }
111
112 if (chown (unix_name.sun_path, socket_user->pw_uid, socket_group->gr_gid) != 0)
113 {
114 string err="error changing socket ownership: ";
115 err+=strerror(errno);
116 log(error, err);
117 throw t2n_server_error(err);
118 }
119
120 if (listen (sock, 5) < 0)
121 {
122 string err="error listening to socket: ";
123 err+=strerror(errno);
124 log(error, err);
125 throw t2n_server_error(err);
126 }
127
128 /* clear & insert server sock into the fd_tab to prepare select */
129 FD_ZERO(&connection_set);
130 FD_SET (sock, &connection_set);
131}
132
ac7fdc22
GE
133socket_server::~socket_server()
134{
a11e19b7 135 if (get_type()==unix_s)
ac7fdc22
GE
136 unlink(unix_path.c_str());
137}
138
0cf4dc9b
GE
139void socket_server::new_connection()
140{
04e6b271 141 struct sockaddr_un clientname;
ac7fdc22 142
04e6b271
GE
143 unsigned int size = sizeof (clientname);
144 int newsock = accept (sock,(struct sockaddr *) &clientname,&size);
145 if (newsock < 0)
146 {
147 if (errno == EAGAIN)
148 {
149 log(error, "accept error (EAGAIN): no connection waiting");
150 return;
151 }
152
153 /* default: break */
154 string err="error accepting connection: ";
155 err+=strerror(errno);
156 log(error, err);
157 throw t2n_server_error(err);
158 }
159
160 FD_SET (newsock, &connection_set);
161
a11e19b7
GE
162 socket_server_connection *nc=new socket_server_connection(newsock, get_type(), get_default_timeout());
163 nc->set_socket_options(newsock);
04e6b271 164
a11e19b7 165 add_connection(nc);
04e6b271
GE
166
167 return;
0cf4dc9b 168}
ac7fdc22
GE
169
170void socket_server::fill_buffer(long long usec_timeout)
171{
0cf4dc9b
GE
172 fd_set used_fdset=connection_set;
173
174 /* set timeout */
175 struct timeval tval;
176 struct timeval *timeout_ptr;
177
178 if (usec_timeout == -1)
179 timeout_ptr = NULL;
180 else
181 {
182 timeout_ptr = &tval;
183
184 // timeout von long long usec in int sec + int usec umrechnen
185 tval.tv_sec = usec_timeout / 1000000;
186 tval.tv_usec = usec_timeout % 1000000;
187 }
188
189 int ret=select (FD_SETSIZE, &used_fdset, NULL, NULL, timeout_ptr);
190
191 if (ret < 0)
192 {
193 if (errno == EINTR)
194 {
195 // select interrupted by signal
196 ret=0;
197 }
198 else
199 {
200 string err="select error: ";
201 err+=strerror(errno);
202 log(error, err);
203 throw t2n_server_error(err);
204 }
205 }
206
207 if (ret > 0)
208 {
209 // we have data pending
210
211 // check for new connection
212 if (FD_ISSET (sock, &used_fdset))
213 {
214 new_connection();
215 }
216
217 // check all connections for pending data
218 fill_connection_buffers();
219 }
ac7fdc22 220
0cf4dc9b 221 return;
ac7fdc22
GE
222}
223
aa499d20
GE
224void socket_server::fill_connection_buffers()
225{
a11e19b7
GE
226 std::map<unsigned int, server_connection*>::iterator ie=connections.end();
227 for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
aa499d20 228 if (!i->second->is_closed())
a11e19b7 229 i->second->fill_buffer();
aa499d20
GE
230}
231
a11e19b7 232void socket_server::remove_connection_socket(int sock)
ac7fdc22 233{
a11e19b7 234 FD_CLR(sock, &connection_set);
ac7fdc22
GE
235}
236
a11e19b7 237void socket_server_connection::close()
ac7fdc22 238{
a11e19b7 239 if (!is_closed())
aa499d20 240 {
a11e19b7
GE
241 socket_handler::close();
242 server_connection::close();
aa499d20
GE
243 }
244
a11e19b7 245 if (my_server)
aa499d20 246 {
a11e19b7 247 dynamic_cast<socket_server*>(my_server)->remove_connection_socket(sock);
aa499d20 248 }
ac7fdc22
GE
249}
250
251}