mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-23 20:51:58 +00:00
merged new xmlrpc-c revision 1472 from https://xmlrpc-c.svn.sourceforge.net/svnroot/xmlrpc-c/trunk
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@8545 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
@@ -1,11 +1,18 @@
|
||||
#include <cassert>
|
||||
#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"
|
||||
@@ -17,47 +24,21 @@ using namespace xmlrpc_c;
|
||||
|
||||
namespace xmlrpc_c {
|
||||
|
||||
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) {
|
||||
|
||||
this->registryP = ®istry;
|
||||
this->logFileName = logFileName;
|
||||
this->keepaliveTimeout = keepaliveTimeout;
|
||||
this->keepaliveMaxConn = keepaliveMaxConn;
|
||||
this->timeout = timeout;
|
||||
this->dontAdvertise = dontAdvertise;
|
||||
|
||||
if (portNumber > 0xffff)
|
||||
throw(error("Port number exceeds the maximum possible port number "
|
||||
"(65535)"));
|
||||
else {
|
||||
this->portNumber = portNumber;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
serverAbyss::~serverAbyss() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
static void
|
||||
sigterm(int const sig) {
|
||||
TraceExit("Signal %d received. Exiting...\n",sig);
|
||||
sigterm(int const signalClass) {
|
||||
|
||||
cerr << "Signal of Class " << signalClass << " received. Exiting" << endl;
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
sigchld(int const sig) {
|
||||
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).
|
||||
@@ -65,7 +46,8 @@ sigchld(int const sig) {
|
||||
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 others, it doesn't.
|
||||
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. */
|
||||
@@ -73,7 +55,7 @@ sigchld(int const sig) {
|
||||
bool zombiesExist;
|
||||
bool error;
|
||||
|
||||
assert(sig == SIGCHLD);
|
||||
assert(signalClass == SIGCHLD);
|
||||
|
||||
zombiesExist = true; // initial assumption
|
||||
error = false; // no error yet
|
||||
@@ -94,8 +76,25 @@ sigchld(int const sig) {
|
||||
#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(void) {
|
||||
setupSignalHandlers(struct signalHandlers * const oldHandlersP) {
|
||||
#ifndef _WIN32
|
||||
struct sigaction mysigaction;
|
||||
|
||||
@@ -104,93 +103,366 @@ setupSignalHandlers(void) {
|
||||
|
||||
/* These signals abort the program, with tracing */
|
||||
mysigaction.sa_handler = sigterm;
|
||||
sigaction(SIGTERM, &mysigaction, NULL);
|
||||
sigaction(SIGINT, &mysigaction, NULL);
|
||||
sigaction(SIGHUP, &mysigaction, NULL);
|
||||
sigaction(SIGUSR1, &mysigaction, NULL);
|
||||
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, NULL);
|
||||
sigaction(SIGPIPE, &mysigaction, &oldHandlersP->pipe);
|
||||
|
||||
/* This signal indicates a child process (request handler) has died */
|
||||
mysigaction.sa_handler = sigchld;
|
||||
sigaction(SIGCHLD, &mysigaction, NULL);
|
||||
sigaction(SIGCHLD, &mysigaction, &oldHandlersP->chld);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
setAdditionalServerParms(
|
||||
unsigned int const keepaliveTimeout,
|
||||
unsigned int const keepaliveMaxConn,
|
||||
unsigned int const timeout,
|
||||
bool const dontAdvertise,
|
||||
TServer * const srvP) {
|
||||
|
||||
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 (keepaliveTimeout > 0)
|
||||
srvP->keepalivetimeout = keepaliveTimeout;
|
||||
if (keepaliveMaxConn > 0)
|
||||
srvP->keepalivemaxconn = keepaliveMaxConn;
|
||||
if (timeout > 0)
|
||||
srvP->timeout = timeout;
|
||||
srvP->advertise = !dontAdvertise;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
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(®istry);
|
||||
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() {
|
||||
|
||||
xmlrpc_env env;
|
||||
xmlrpc_env_init(&env);
|
||||
|
||||
DateInit();
|
||||
MIMETypeInit();
|
||||
|
||||
TServer srv;
|
||||
/* We do some pretty ugly stuff for an object method: we set signal
|
||||
handlers, which are process-global.
|
||||
|
||||
const char * const logfileArg(this->logFileName.length() == 0 ?
|
||||
NULL : this->logFileName.c_str());
|
||||
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.
|
||||
|
||||
ServerCreate(&srv, "XmlRpcServer", this->portNumber,
|
||||
DEFAULT_DOCS, logfileArg);
|
||||
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;
|
||||
|
||||
setAdditionalServerParms(
|
||||
this->keepaliveTimeout, this->keepaliveMaxConn, this->timeout,
|
||||
this->dontAdvertise, &srv);
|
||||
|
||||
xmlrpc_c::server_abyss_set_handlers(&srv, *this->registryP);
|
||||
|
||||
ServerInit(&srv);
|
||||
|
||||
setupSignalHandlers();
|
||||
|
||||
ServerRun(&srv);
|
||||
setupSignalHandlers(&oldHandlers);
|
||||
|
||||
/* We can't exist here because ServerRun doesn't return */
|
||||
assert(false);
|
||||
ServerRun(&this->cServer);
|
||||
|
||||
restoreSignalHandlers(oldHandlers);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
server_abyss_set_handlers(TServer * const srvP,
|
||||
xmlrpc_c::registry const& registry) {
|
||||
|
||||
serverAbyss::runOnce() {
|
||||
|
||||
xmlrpc_server_abyss_set_handlers(srvP, registry.c_registry());
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user