freeswitch/libs/xmlrpc-c/src/cpp/server_abyss.cpp

469 lines
12 KiB
C++

#include <cstdlib>
#include <string>
#include <memory>
#include <signal.h>
#include <errno.h>
#include <iostream>
#ifndef _WIN32
#include <sys/wait.h>
#endif
#include "assertx.hpp"
#include "xmlrpc-c/string_int.h"
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
using girerr::throwf;
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/server_abyss.h"
#include "xmlrpc-c/registry.hpp"
#include "xmlrpc-c/server_abyss.hpp"
using namespace std;
using namespace xmlrpc_c;
namespace xmlrpc_c {
namespace {
static void
sigterm(int const signalClass) {
cerr << "Signal of Class " << signalClass << " received. Exiting" << endl;
exit(1);
}
static void
sigchld(int const ASSERT_ONLY_ARG(signalClass)) {
/*----------------------------------------------------------------------------
This is a signal handler for a SIGCHLD signal (which informs us that
one of our child processes has terminated).
We respond by reaping the zombie process.
Implementation note: In some systems, just setting the signal handler
to SIG_IGN (ignore signal) does this. In some, the system does this
automatically if the signal is blocked.
-----------------------------------------------------------------------------*/
#ifndef _WIN32
/* Reap zombie children until there aren't any more. */
bool zombiesExist;
bool error;
assert(signalClass == SIGCHLD);
zombiesExist = true; // initial assumption
error = false; // no error yet
while (zombiesExist && !error) {
int status;
pid_t const pid = waitpid((pid_t) -1, &status, WNOHANG);
if (pid == 0)
zombiesExist = false;
else if (pid < 0) {
/* because of ptrace */
if (errno == EINTR) {
// This is OK - it's a ptrace notification
} else
error = true;
}
}
#endif /* _WIN32 */
}
struct signalHandlers {
#ifndef WIN32
struct sigaction term;
struct sigaction int_;
struct sigaction hup;
struct sigaction usr1;
struct sigaction pipe;
struct sigaction chld;
#else
int dummy;
#endif
};
void
setupSignalHandlers(struct signalHandlers * const oldHandlersP) {
#ifndef _WIN32
struct sigaction mysigaction;
sigemptyset(&mysigaction.sa_mask);
mysigaction.sa_flags = 0;
/* These signals abort the program, with tracing */
mysigaction.sa_handler = sigterm;
sigaction(SIGTERM, &mysigaction, &oldHandlersP->term);
sigaction(SIGINT, &mysigaction, &oldHandlersP->int_);
sigaction(SIGHUP, &mysigaction, &oldHandlersP->hup);
sigaction(SIGUSR1, &mysigaction, &oldHandlersP->usr1);
/* This signal indicates connection closed in the middle */
mysigaction.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &mysigaction, &oldHandlersP->pipe);
/* This signal indicates a child process (request handler) has died */
mysigaction.sa_handler = sigchld;
sigaction(SIGCHLD, &mysigaction, &oldHandlersP->chld);
#endif
}
void
restoreSignalHandlers(struct signalHandlers const& oldHandlers) {
#ifndef _WIN32
sigaction(SIGCHLD, &oldHandlers.chld, NULL);
sigaction(SIGPIPE, &oldHandlers.pipe, NULL);
sigaction(SIGUSR1, &oldHandlers.usr1, NULL);
sigaction(SIGHUP, &oldHandlers.hup, NULL);
sigaction(SIGINT, &oldHandlers.int_, NULL);
sigaction(SIGTERM, &oldHandlers.term, NULL);
#endif
}
// We need 'global' because methods of class serverAbyss call
// functions in the Abyss C library. By virtue of global's static
// storage class, the program loader will call its constructor and
// destructor and thus initialize and terminate the Abyss C library.
class abyssGlobalState {
public:
abyssGlobalState() {
const char * error;
AbyssInit(&error);
if (error) {
string const e(error);
xmlrpc_strfree(error);
throwf("AbyssInit() failed. %s", e.c_str());
}
}
~abyssGlobalState() {
AbyssTerm();
}
} const global;
} // namespace
serverAbyss::shutdown::shutdown(serverAbyss * const serverAbyssP) :
serverAbyssP(serverAbyssP) {}
serverAbyss::shutdown::~shutdown() {}
void
serverAbyss::shutdown::doit(string const&,
void * const) const {
this->serverAbyssP->terminate();
}
serverAbyss::constrOpt::constrOpt() {
present.registryPtr = false;
present.registryP = false;
present.socketFd = false;
present.portNumber = false;
present.logFileName = false;
present.keepaliveTimeout = false;
present.keepaliveMaxConn = false;
present.timeout = false;
present.dontAdvertise = false;
present.uriPath = false;
present.chunkResponse = false;
// Set default values
value.dontAdvertise = false;
value.uriPath = string("/RPC2");
value.chunkResponse = false;
}
#define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
serverAbyss::constrOpt & \
serverAbyss::constrOpt::OPTION_NAME(TYPE const& arg) { \
this->value.OPTION_NAME = arg; \
this->present.OPTION_NAME = true; \
return *this; \
}
DEFINE_OPTION_SETTER(registryPtr, xmlrpc_c::registryPtr);
DEFINE_OPTION_SETTER(registryP, const registry *);
DEFINE_OPTION_SETTER(socketFd, XMLRPC_SOCKET);
DEFINE_OPTION_SETTER(portNumber, unsigned int);
DEFINE_OPTION_SETTER(logFileName, string);
DEFINE_OPTION_SETTER(keepaliveTimeout, unsigned int);
DEFINE_OPTION_SETTER(keepaliveMaxConn, unsigned int);
DEFINE_OPTION_SETTER(timeout, unsigned int);
DEFINE_OPTION_SETTER(dontAdvertise, bool);
DEFINE_OPTION_SETTER(uriPath, string);
DEFINE_OPTION_SETTER(chunkResponse, bool);
void
serverAbyss::setAdditionalServerParms(constrOpt const& opt) {
/* The following ought to be parameters on ServerCreate(), but it
looks like plugging them straight into the TServer structure is
the only way to set them.
*/
if (opt.present.keepaliveTimeout)
ServerSetKeepaliveTimeout(&this->cServer, opt.value.keepaliveTimeout);
if (opt.present.keepaliveMaxConn)
ServerSetKeepaliveMaxConn(&this->cServer, opt.value.keepaliveMaxConn);
if (opt.present.timeout)
ServerSetTimeout(&this->cServer, opt.value.timeout);
ServerSetAdvertise(&this->cServer, !opt.value.dontAdvertise);
}
static void
createServer(bool const logFileNameGiven,
string const& logFileName,
bool const socketFdGiven,
int const socketFd,
bool const portNumberGiven,
unsigned int const portNumber,
TServer * const srvPP) {
const char * const logfileArg(logFileNameGiven ?
logFileName.c_str() : NULL);
const char * const serverName("XmlRpcServer");
abyss_bool created;
if (socketFdGiven)
created =
ServerCreateSocket(srvPP, serverName, socketFd,
DEFAULT_DOCS, logfileArg);
else if (portNumberGiven) {
if (portNumber > 0xffff)
throwf("Port number %u exceeds the maximum possible port number "
"(65535)", portNumber);
created =
ServerCreate(srvPP, serverName, portNumber,
DEFAULT_DOCS, logfileArg);
} else
created =
ServerCreateNoAccept(srvPP, serverName,
DEFAULT_DOCS, logfileArg);
if (!created)
throw(error("Failed to create Abyss server. See Abyss error log for "
"reason."));
}
void
serverAbyss::initialize(constrOpt const& opt) {
const registry * registryP;
if (!opt.present.registryP && !opt.present.registryPtr)
throwf("You must specify the 'registryP' or 'registryPtr' option");
else if (opt.present.registryP && opt.present.registryPtr)
throwf("You may not specify both the 'registryP' and "
"the 'registryPtr' options");
else {
if (opt.present.registryP)
registryP = opt.value.registryP;
else {
this->registryPtr = opt.value.registryPtr;
registryP = this->registryPtr.get();
}
}
if (opt.present.portNumber && opt.present.socketFd)
throwf("You can't specify both portNumber and socketFd options");
DateInit();
createServer(opt.present.logFileName, opt.value.logFileName,
opt.present.socketFd, opt.value.socketFd,
opt.present.portNumber, opt.value.portNumber,
&this->cServer);
try {
setAdditionalServerParms(opt);
// chunked response implementation is incomplete. We must
// eventually get away from libxmlrpc_server_abyss and
// register our own handler with the Abyss server. At that
// time, we'll have some place to pass
// opt.value.chunkResponse.
xmlrpc_c::server_abyss_set_handlers(&this->cServer,
registryP,
opt.value.uriPath);
if (opt.present.portNumber || opt.present.socketFd)
ServerInit(&this->cServer);
} catch (...) {
ServerFree(&this->cServer);
throw;
}
}
serverAbyss::serverAbyss(constrOpt const& opt) {
initialize(opt);
}
serverAbyss::serverAbyss(
xmlrpc_c::registry const& registry,
unsigned int const portNumber,
string const& logFileName,
unsigned int const keepaliveTimeout,
unsigned int const keepaliveMaxConn,
unsigned int const timeout,
bool const dontAdvertise,
bool const socketBound,
XMLRPC_SOCKET const socketFd) {
/*----------------------------------------------------------------------------
This is a backward compatibility interface. This used to be the only
constructor.
-----------------------------------------------------------------------------*/
serverAbyss::constrOpt opt;
opt.registryP(&registry);
if (logFileName.length() > 0)
opt.logFileName(logFileName);
if (keepaliveTimeout > 0)
opt.keepaliveTimeout(keepaliveTimeout);
if (keepaliveMaxConn > 0)
opt.keepaliveMaxConn(keepaliveMaxConn);
if (timeout > 0)
opt.timeout(timeout);
opt.dontAdvertise(dontAdvertise);
if (socketBound)
opt.socketFd(socketFd);
else
opt.portNumber(portNumber);
initialize(opt);
}
serverAbyss::~serverAbyss() {
ServerFree(&this->cServer);
}
void
serverAbyss::run() {
/* We do some pretty ugly stuff for an object method: we set signal
handlers, which are process-global.
One example of where this can be hairy is: Caller has a child
process unrelated to the Abyss server. That child dies. We
get his death of a child signal and Caller never knows.
We really expect to be the only thing in the process, at least
for the time we're running. If you want the Abyss Server
to behave more like an object and own the signals yourself,
use runOnce() in a loop instead of run().
*/
signalHandlers oldHandlers;
setupSignalHandlers(&oldHandlers);
ServerRun(&this->cServer);
restoreSignalHandlers(oldHandlers);
}
void
serverAbyss::runOnce() {
ServerRunOnce(&this->cServer);
}
void
serverAbyss::runConn(int const socketFd) {
ServerRunConn(&this->cServer, socketFd);
}
void
serverAbyss::terminate() {
ServerTerminate(&this->cServer);
}
void
server_abyss_set_handlers(TServer * const srvP,
registry const& registry,
string const& uriPath) {
xmlrpc_server_abyss_set_handlers2(srvP,
uriPath.c_str(),
registry.c_registry());
}
void
server_abyss_set_handlers(TServer * const srvP,
const registry * const registryP,
string const& uriPath) {
xmlrpc_server_abyss_set_handlers2(srvP,
uriPath.c_str(),
registryP->c_registry());
}
void
server_abyss_set_handlers(TServer * const srvP,
registryPtr const registryPtr,
string const& uriPath) {
xmlrpc_server_abyss_set_handlers2(srvP,
uriPath.c_str(),
registryPtr->c_registry());
}
} // namespace