text: #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;
          } else
              ServerHandleSigchld(pid);
      }
  #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);
  
      ServerUseSigchld(&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
  