Some build fixed for libxml2.
[xapian.git] / xapian-core / net / tcpserver.cc
blob126db0af0f30840fbaf295d7deefdbab92b76402
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
19 * USA
20 * -----END-LICENCE-----
23 #include "config.h"
24 #include "tcpserver.h"
25 #include "stats.h"
26 #include "netutils.h"
27 #include "socketcommon.h"
28 #include "utils.h"
30 #include <errno.h>
31 #include <string.h>
32 #include <unistd.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>
38 #include <netdb.h>
39 #include <signal.h>
40 #include <sys/wait.h>
41 #ifdef TIMING_PATCH
42 #include <sys/time.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_,
51 #ifndef TIMING_PATCH
52 bool verbose_)
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_),
59 verbose(verbose_)
60 #ifdef TIMING_PATCH
61 , timing(timing_)
62 #endif /* TIMING_PATCH */
67 int
68 TcpServer::get_listening_socket(int port)
70 int socketfd = socket(PF_INET, SOCK_STREAM, 0);
72 if (socketfd < 0) {
73 throw OmNetworkError("socket", errno);
76 int retval;
79 int optval = 1;
80 retval = setsockopt(socketfd,
81 SOL_SOCKET,
82 SO_REUSEADDR,
83 (void *)&optval,
84 sizeof(optval));
87 if (retval < 0) {
88 int saved_errno = errno; // note down in case close hits an error
89 close(socketfd);
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),
100 sizeof(addr));
102 if (retval < 0) {
103 int saved_errno = errno; // note down in case close hits an error
104 close(socketfd);
105 throw OmNetworkError("bind failed", saved_errno);
108 // FIXME: backlog argument should perhaps be larger.
109 retval = listen(socketfd, 1);
111 if (retval < 0) {
112 int saved_errno = errno; // note down in case close hits an error
113 close(socketfd);
114 throw OmNetworkError("listen failed", saved_errno);
116 return socketfd;
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),
139 sizeof(address),
140 AF_INET);
142 if (hent == 0) {
143 close(con_socket);
144 std::string errmsg = "gethostbyaddr: ";
145 switch(h_errno) {
146 case HOST_NOT_FOUND:
147 errmsg += "Unknown host";
148 break;
149 case NO_DATA:
150 #if NO_DATA != NO_ADDRESS
151 /* Is this ever different? */
152 case NO_ADDRESS:
153 #endif
154 errmsg += "No address for hostname";
155 break;
156 case NO_RECOVERY:
157 errmsg += "Unrecoverable name server error";
158 break;
159 case TRY_AGAIN:
160 errmsg += "Temporary nameserver error";
161 break;
162 default:
163 errmsg += "Unknown error.";
165 throw OmNetworkError(errmsg);
168 if (verbose) {
169 std::cout << "Connection from " << hent->h_name << ", port " <<
170 #ifndef TIMING_PATCH
171 remote_address.sin_port << std::endl;
172 #else /* TIMING_PATCH */
173 remote_address.sin_port << ". (tcpserver.cc)" << std::endl;
174 #endif /* TIMING_PATCH */
177 return con_socket;
180 TcpServer::~TcpServer()
182 close(listen_socket);
185 void
186 TcpServer::run_once()
188 int connected_socket = get_connected_socket();
189 #ifdef TIMING_PATCH
190 struct timeval stp, etp;
191 // record start time
192 int returnval = gettimeofday(&stp,NULL);
193 if (returnval != 0) {
194 std::cerr << "Could not get time of day...\n";
196 #endif /* TIMING_PATCH */
197 int pid = fork();
198 if (pid == 0) {
199 // child code
200 close(listen_socket);
201 try {
202 #ifndef TIMING_PATCH
203 SocketServer sserv(db, connected_socket, -1,
204 msecs_active_timeout,
205 msecs_idle_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 */
211 sserv.run();
212 } catch (const OmError &err) {
213 std::cerr << "Got exception " << err.get_type()
214 << ": " << err.get_msg() << std::endl;
215 } catch (...) {
216 // ignore other exceptions
218 close(connected_socket);
220 #ifndef TIMING_PATCH
221 if (verbose) std::cout << "Closing connection.\n";
222 #else /* TIMING_PATCH */
223 // record end time
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 */
231 exit(0);
232 } else if (pid > 0) {
233 // parent code
234 close(connected_socket);
235 } else {
236 // fork() failed
237 int saved_errno = errno; // note down in case close hits an error
238 close(connected_socket);
239 throw OmNetworkError("fork failed", saved_errno);
243 void
244 TcpServer::run()
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.
251 #ifndef HAVE_WAITPID
252 signal(SIGCHLD, SIG_IGN);
253 #else
254 signal(SIGCHLD, on_SIGCHLD);
255 #endif
256 signal(SIGTERM, on_SIGTERM);
257 while (1) {
258 try {
259 run_once();
260 } catch (const OmError &err) {
261 // FIXME: better error handling.
262 std::cerr << "Caught " << err.get_type()
263 << ": " << err.get_msg() << std::endl;
264 } catch (...) {
265 // FIXME: better error handling.
266 std::cerr << "Caught exception." << std::endl;
271 //////////////////////////////////////////////////////////////
272 void
273 TcpServer::on_SIGTERM (int sig)
275 signal (SIGTERM, SIG_DFL);
276 /* terminate all processes in my process group */
277 #ifdef HAVE_KILLPG
278 killpg(0, SIGTERM);
279 #else
280 kill(0, SIGTERM);
281 #endif
282 exit (0);
285 //////////////////////////////////////////////////////////////
286 void
287 TcpServer::on_SIGCHLD (int sig)
289 int status;
290 while (waitpid(-1, &status, WNOHANG) > 0);