1 /* tcpserver.cc: class for TCP/IP-based server.
3 * ----START-LICENCE----
4 * Copyright 1999,2000,2001 BrightStation PLC
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * -----END-LICENCE-----
24 #include "tcpserver.h"
27 #include "socketcommon.h"
33 #include <netinet/in_systm.h>
34 #include <netinet/in.h>
35 #include <netinet/ip.h>
36 #include <sys/socket.h>
37 #include <sys/types.h>
44 #define uint64_t unsigned long long
45 #endif /* TIMING_PATCH */
47 /// The TcpServer constructor, taking a database and a listening port.
48 TcpServer::TcpServer(OmDatabase db_
, int port_
,
49 int msecs_active_timeout_
,
50 int msecs_idle_timeout_
,
53 #else /* TIMING_PATCH */
54 bool verbose_
, bool timing_
)
55 #endif /* TIMING_PATCH */
56 : port(port_
), db(db_
), listen_socket(get_listening_socket(port_
)),
57 msecs_active_timeout(msecs_active_timeout_
),
58 msecs_idle_timeout(msecs_idle_timeout_
),
62 #endif /* TIMING_PATCH */
68 TcpServer::get_listening_socket(int port
)
70 int socketfd
= socket(PF_INET
, SOCK_STREAM
, 0);
73 throw OmNetworkError("socket", errno
);
80 retval
= setsockopt(socketfd
,
88 int saved_errno
= errno
; // note down in case close hits an error
90 throw OmNetworkError("setsockopt failed", saved_errno
);
93 struct sockaddr_in addr
;
94 addr
.sin_family
= AF_INET
;
95 addr
.sin_port
= htons(port
);
96 addr
.sin_addr
.s_addr
= INADDR_ANY
;
98 retval
= bind(socketfd
,
99 reinterpret_cast<sockaddr
*>(&addr
),
103 int saved_errno
= errno
; // note down in case close hits an error
105 throw OmNetworkError("bind failed", saved_errno
);
108 // FIXME: backlog argument should perhaps be larger.
109 retval
= listen(socketfd
, 1);
112 int saved_errno
= errno
; // note down in case close hits an error
114 throw OmNetworkError("listen failed", saved_errno
);
120 TcpServer::get_connected_socket()
122 struct sockaddr_in remote_address
;
123 socklen_t remote_address_size
= sizeof(remote_address
);
124 // accept connections
125 int con_socket
= accept(listen_socket
,
126 reinterpret_cast<sockaddr
*>(&remote_address
),
127 &remote_address_size
);
129 if (con_socket
< 0) {
130 throw OmNetworkError("accept failed", errno
);
133 if (remote_address_size
!= sizeof(remote_address
)) {
134 throw OmNetworkError("accept: unexpected remote address size");
137 struct in_addr address
= remote_address
.sin_addr
;
138 struct hostent
*hent
= gethostbyaddr(reinterpret_cast<char *>(&address
),
144 std::string errmsg
= "gethostbyaddr: ";
147 errmsg
+= "Unknown host";
150 #if NO_DATA != NO_ADDRESS
151 /* Is this ever different? */
154 errmsg
+= "No address for hostname";
157 errmsg
+= "Unrecoverable name server error";
160 errmsg
+= "Temporary nameserver error";
163 errmsg
+= "Unknown error.";
165 throw OmNetworkError(errmsg
);
169 std::cout
<< "Connection from " << hent
->h_name
<< ", port " <<
171 remote_address
.sin_port
<< std::endl
;
172 #else /* TIMING_PATCH */
173 remote_address
.sin_port
<< ". (tcpserver.cc)" << std::endl
;
174 #endif /* TIMING_PATCH */
180 TcpServer::~TcpServer()
182 close(listen_socket
);
186 TcpServer::run_once()
188 int connected_socket
= get_connected_socket();
190 struct timeval stp
, etp
;
192 int returnval
= gettimeofday(&stp
,NULL
);
193 if (returnval
!= 0) {
194 std::cerr
<< "Could not get time of day...\n";
196 #endif /* TIMING_PATCH */
200 close(listen_socket
);
203 SocketServer
sserv(db
, connected_socket
, -1,
204 msecs_active_timeout
,
206 #else /* TIMING_PATCH */
207 SocketServer
sserv(db
, connected_socket
, -1,
208 msecs_active_timeout
,
209 msecs_idle_timeout
, timing
);
210 #endif /* TIMING_PATCH */
212 } catch (const OmError
&err
) {
213 std::cerr
<< "Got exception " << err
.get_type()
214 << ": " << err
.get_msg() << std::endl
;
216 // ignore other exceptions
218 close(connected_socket
);
221 if (verbose
) std::cout
<< "Closing connection.\n";
222 #else /* TIMING_PATCH */
224 returnval
= gettimeofday(&etp
, NULL
);
225 if (returnval
!= 0) {
226 std::cerr
<< "Could not get time of day...\n";
228 uint64_t total
= ((1000000 * etp
.tv_sec
) + etp
.tv_usec
) - ((1000000 * stp
.tv_sec
) + stp
.tv_usec
);
229 if (verbose
) std::cout
<< "Connection held open for " << total
<< " usecs. (tcpserver.cc)\n\n";
230 #endif /* TIMING_PATCH */
232 } else if (pid
> 0) {
234 close(connected_socket
);
237 int saved_errno
= errno
; // note down in case close hits an error
238 close(connected_socket
);
239 throw OmNetworkError("fork failed", saved_errno
);
246 // set up signal handlers
247 /* NOTE: Changed from SIGCLD to SIGCHLD, as I believe it to be
248 * more portable. If any systems only understand SIGCLD, then
249 * we'll have to add a define, but it may not be necessary.
252 signal(SIGCHLD
, SIG_IGN
);
254 signal(SIGCHLD
, on_SIGCHLD
);
256 signal(SIGTERM
, on_SIGTERM
);
260 } catch (const OmError
&err
) {
261 // FIXME: better error handling.
262 std::cerr
<< "Caught " << err
.get_type()
263 << ": " << err
.get_msg() << std::endl
;
265 // FIXME: better error handling.
266 std::cerr
<< "Caught exception." << std::endl
;
271 //////////////////////////////////////////////////////////////
273 TcpServer::on_SIGTERM (int sig
)
275 signal (SIGTERM
, SIG_DFL
);
276 /* terminate all processes in my process group */
285 //////////////////////////////////////////////////////////////
287 TcpServer::on_SIGCHLD (int sig
)
290 while (waitpid(-1, &status
, WNOHANG
) > 0);