initial load of upstream version 1.06.32
[xmlrpc-c] / src / cpp / server_abyss.cpp
1 #include <cassert>
2 #include <cstdlib>
3 #include <string>
4 #include <memory>
5 #include <signal.h>
6 #include <errno.h>
7 #include <iostream>
8 #include <sys/wait.h>
9
10 #include "xmlrpc-c/girerr.hpp"
11 using girerr::error;
12 using girerr::throwf;
13 #include "xmlrpc-c/base.h"
14 #include "xmlrpc-c/base.hpp"
15 #include "xmlrpc-c/server_abyss.h"
16 #include "xmlrpc-c/registry.hpp"
17 #include "xmlrpc-c/server_abyss.hpp"
18
19 using namespace std;
20 using namespace xmlrpc_c;
21
22 namespace xmlrpc_c {
23
24 namespace {
25
26
27 static void 
28 sigterm(int const signalClass) {
29
30     cerr << "Signal of Class " << signalClass << " received.  Exiting" << endl;
31
32     exit(1);
33 }
34
35
36
37 static void 
38 sigchld(int const signalClass) {
39 /*----------------------------------------------------------------------------
40    This is a signal handler for a SIGCHLD signal (which informs us that
41    one of our child processes has terminated).
42
43    We respond by reaping the zombie process.
44
45    Implementation note: In some systems, just setting the signal handler
46    to SIG_IGN (ignore signal) does this.  In others, it doesn't.
47 -----------------------------------------------------------------------------*/
48 #ifndef _WIN32
49     /* Reap zombie children until there aren't any more. */
50
51     bool zombiesExist;
52     bool error;
53
54     assert(signalClass == SIGCHLD);
55     
56     zombiesExist = true;  // initial assumption
57     error = false;  // no error yet
58     while (zombiesExist && !error) {
59         int status;
60         pid_t const pid = waitpid((pid_t) -1, &status, WNOHANG);
61     
62         if (pid == 0)
63             zombiesExist = false;
64         else if (pid < 0) {
65             /* because of ptrace */
66             if (errno == EINTR) {
67                 // This is OK - it's a ptrace notification
68             } else
69                 error = true;
70         }
71     }
72 #endif /* _WIN32 */
73 }
74
75
76
77 void
78 setupSignalHandlers(void) {
79 #ifndef _WIN32
80     struct sigaction mysigaction;
81    
82     sigemptyset(&mysigaction.sa_mask);
83     mysigaction.sa_flags = 0;
84
85     /* These signals abort the program, with tracing */
86     mysigaction.sa_handler = sigterm;
87     sigaction(SIGTERM, &mysigaction, NULL);
88     sigaction(SIGINT,  &mysigaction, NULL);
89     sigaction(SIGHUP,  &mysigaction, NULL);
90     sigaction(SIGUSR1, &mysigaction, NULL);
91
92     /* This signal indicates connection closed in the middle */
93     mysigaction.sa_handler = SIG_IGN;
94     sigaction(SIGPIPE, &mysigaction, NULL);
95    
96     /* This signal indicates a child process (request handler) has died */
97     mysigaction.sa_handler = sigchld;
98     sigaction(SIGCHLD, &mysigaction, NULL);
99 #endif
100 }    
101
102
103 } // namespace
104
105
106
107 serverAbyss::constrOpt::constrOpt() {
108     present.registryPtr      = false;
109     present.registryP        = false;
110     present.socketFd         = false;
111     present.portNumber       = false;
112     present.logFileName      = false;
113     present.keepaliveTimeout = false;
114     present.keepaliveMaxConn = false;
115     present.timeout          = false;
116     present.dontAdvertise    = false;
117     present.uriPath          = false;
118     present.chunkResponse    = false;
119     
120     // Set default values
121     value.dontAdvertise = false;
122     value.uriPath       = string("/RPC2");
123     value.chunkResponse = false;
124 }
125
126
127
128 #define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
129 serverAbyss::constrOpt & \
130 serverAbyss::constrOpt::OPTION_NAME(TYPE const& arg) { \
131     this->value.OPTION_NAME = arg; \
132     this->present.OPTION_NAME = true; \
133     return *this; \
134 }
135
136 DEFINE_OPTION_SETTER(registryPtr,      xmlrpc_c::registryPtr);
137 DEFINE_OPTION_SETTER(registryP,        const registry *);
138 DEFINE_OPTION_SETTER(socketFd,         xmlrpc_socket);
139 DEFINE_OPTION_SETTER(portNumber,       uint);
140 DEFINE_OPTION_SETTER(logFileName,      string);
141 DEFINE_OPTION_SETTER(keepaliveTimeout, uint);
142 DEFINE_OPTION_SETTER(keepaliveMaxConn, uint);
143 DEFINE_OPTION_SETTER(timeout,          uint);
144 DEFINE_OPTION_SETTER(dontAdvertise,    bool);
145 DEFINE_OPTION_SETTER(uriPath,          string);
146 DEFINE_OPTION_SETTER(chunkResponse,    bool);
147
148
149
150 void
151 serverAbyss::setAdditionalServerParms(constrOpt const& opt) {
152
153     /* The following ought to be parameters on ServerCreate(), but it
154        looks like plugging them straight into the TServer structure is
155        the only way to set them.  
156     */
157
158     if (opt.present.keepaliveTimeout)
159         ServerSetKeepaliveTimeout(&this->cServer, opt.value.keepaliveTimeout);
160     if (opt.present.keepaliveMaxConn)
161         ServerSetKeepaliveMaxConn(&this->cServer, opt.value.keepaliveMaxConn);
162     if (opt.present.timeout)
163         ServerSetTimeout(&this->cServer, opt.value.timeout);
164     ServerSetAdvertise(&this->cServer, !opt.value.dontAdvertise);
165 }
166
167
168
169 static void
170 createServer(bool         const  logFileNameGiven,
171              string       const& logFileName,
172              bool         const  socketFdGiven,
173              int          const  socketFd,
174              bool         const  portNumberGiven,
175              unsigned int const  portNumber,
176              TServer *    const  srvPP) {
177              
178     const char * const logfileArg(logFileNameGiven ? 
179                                   logFileName.c_str() : NULL);
180
181     const char * const serverName("XmlRpcServer");
182
183     bool created;
184         
185     if (socketFdGiven)
186         created =
187             ServerCreateSocket(srvPP, serverName, socketFd,
188                                DEFAULT_DOCS, logfileArg);
189     else if (portNumberGiven) {
190         if (portNumber > 0xffff)
191             throwf("Port number %u exceeds the maximum possible port number "
192                    "(65535)", portNumber);
193
194         created =
195             ServerCreate(srvPP, serverName, portNumber,
196                          DEFAULT_DOCS, logfileArg);
197     } else
198         created = 
199             ServerCreateNoAccept(srvPP, serverName,
200                                  DEFAULT_DOCS, logfileArg);
201
202     if (!created)
203         throw(error("Failed to create Abyss server.  See Abyss error log for "
204                     "reason."));
205 }
206
207
208
209 void
210 serverAbyss::initialize(constrOpt const& opt) {
211
212     const registry * registryP;
213
214     if (!opt.present.registryP && !opt.present.registryPtr)
215         throwf("You must specify the 'registryP' or 'registryPtr' option");
216     else if (opt.present.registryP && opt.present.registryPtr)
217         throwf("You may not specify both the 'registryP' and "
218                "the 'registryPtr' options");
219     else {
220         if (opt.present.registryP)
221             registryP = opt.value.registryP;
222         else {
223             this->registryPtr = opt.value.registryPtr;
224             registryP = this->registryPtr.get();
225         }
226     }
227     if (opt.present.portNumber && opt.present.socketFd)
228         throwf("You can't specify both portNumber and socketFd options");
229
230     DateInit();
231     
232     createServer(opt.present.logFileName, opt.value.logFileName,
233                  opt.present.socketFd,    opt.value.socketFd,
234                  opt.present.portNumber,  opt.value.portNumber,
235                  &this->cServer);
236
237     try {
238         setAdditionalServerParms(opt);
239         
240         // chunked response implementation is incomplete.  We must
241         // eventually get away from libxmlrpc_server_abyss and
242         // register our own handler with the Abyss server.  At that
243         // time, we'll have some place to pass
244         // opt.value.chunkResponse.
245         
246         xmlrpc_c::server_abyss_set_handlers(&this->cServer,
247                                             registryP,
248                                             opt.value.uriPath);
249         
250         if (opt.present.portNumber || opt.present.socketFd)
251             ServerInit(&this->cServer);
252         
253         setupSignalHandlers();
254     } catch (...) {
255         ServerFree(&this->cServer);
256         throw;
257     }
258 }
259
260
261
262 serverAbyss::serverAbyss(constrOpt const& opt) {
263
264     initialize(opt);
265 }
266
267
268
269 serverAbyss::serverAbyss(
270     xmlrpc_c::registry const& registry,
271     unsigned int       const  portNumber,
272     string             const& logFileName,
273     unsigned int       const  keepaliveTimeout,
274     unsigned int       const  keepaliveMaxConn,
275     unsigned int       const  timeout,
276     bool               const  dontAdvertise,
277     bool               const  socketBound,
278     xmlrpc_socket      const  socketFd) {
279 /*----------------------------------------------------------------------------
280   This is a backward compatibility interface.  This used to be the only
281   constructor.
282 -----------------------------------------------------------------------------*/
283     serverAbyss::constrOpt opt;
284
285     opt.registryP(&registry);
286     if (logFileName.length() > 0)
287         opt.logFileName(logFileName);
288     if (keepaliveTimeout > 0)
289         opt.keepaliveTimeout(keepaliveTimeout);
290     if (keepaliveMaxConn > 0)
291         opt.keepaliveMaxConn(keepaliveMaxConn);
292     if (timeout > 0)
293         opt.timeout(timeout);
294     opt.dontAdvertise(dontAdvertise);
295     if (socketBound)
296         opt.socketFd(socketFd);
297     else
298         opt.portNumber(portNumber);
299
300     initialize(opt);
301 }
302
303
304
305 serverAbyss::~serverAbyss() {
306
307     ServerFree(&this->cServer);
308 }
309
310
311
312 void
313 serverAbyss::run() {
314
315     ServerRun(&this->cServer);
316 }
317  
318
319
320 void
321 serverAbyss::runOnce() {
322
323     ServerRunOnce(&this->cServer);
324 }
325
326
327
328 void
329 serverAbyss::runConn(int const socketFd) {
330
331     ServerRunConn(&this->cServer, socketFd);
332 }
333
334
335
336 void
337 server_abyss_set_handlers(TServer * const  srvP,
338                           registry  const& registry,
339                           string    const& uriPath) {
340
341     xmlrpc_server_abyss_set_handlers2(srvP,
342                                       uriPath.c_str(),
343                                       registry.c_registry());
344 }
345
346
347
348 void
349 server_abyss_set_handlers(TServer *        const  srvP,
350                           const registry * const  registryP,
351                           string           const& uriPath) {
352
353     xmlrpc_server_abyss_set_handlers2(srvP,
354                                       uriPath.c_str(),
355                                       registryP->c_registry());
356 }
357
358
359
360 void
361 server_abyss_set_handlers(TServer *   const  srvP,
362                           registryPtr const  registryPtr,
363                           string      const& uriPath) {
364
365     xmlrpc_server_abyss_set_handlers2(srvP,
366                                       uriPath.c_str(),
367                                       registryPtr->c_registry());
368 }
369
370
371
372 } // namespace