mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-04-09 06:01:47 +00:00
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@8545 d0543943-73ff-0310-b7d9-9358b9ac24b2
469 lines
12 KiB
C++
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(®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() {
|
|
|
|
/* 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
|