Example #1
0
/// Set up a new view of the memcached cluster(s).  The view determines
/// how data is distributed around the cluster.
void BaseMemcachedStore::new_view(const MemcachedConfig& config)
{
  TRC_STATUS("Updating memcached store configuration");

  // Create a new view with the new server lists.
  MemcachedStoreView view(_vbuckets, _replicas);
  view.update(config);

  // Now copy the view so it can be accessed by the worker threads.
  pthread_rwlock_wrlock(&_view_lock);

  // Get the list of servers from the view.
  _servers = view.servers();

  // For each vbucket, get the list of read replicas and write replicas.
  for (int ii = 0; ii < _vbuckets; ++ii)
  {
    _read_replicas[ii] = view.read_replicas(ii);
    _write_replicas[ii] = view.write_replicas(ii);
  }

  // Update the view number as the last thing here, otherwise we could stall
  // other threads waiting for the lock.
  TRC_STATUS("Finished preparing new view, so flag that workers should switch to it");
  ++_view_number;

  pthread_rwlock_unlock(&_view_lock);
}
void CommunicationMonitor::track_communication_changes(unsigned long now_ms)
{
  if (_alarm == NULL)
  {
    return;
  }

  now_ms = now_ms ? now_ms : current_time_ms();

  if (now_ms > _next_check)
  {
    // Current time has passed our monitor interval time, so take the lock
    // and see if we are the lucky thread that gets to check for an alarm
    // condition.
    pthread_mutex_lock(&_lock);

    // If current time is still past the monitor interval time we are the
    // the lucky one, otherwise somebody beat us to the punch (so just drop
    // the lock and return).
    if (now_ms > _next_check)
    {
      // Grab the current counts and reset them to zero in a lockless manner.
      TRC_DEBUG("Check communication monitor state for alarm %d", _alarm->index());
      unsigned int succeeded = _succeeded.fetch_and(0);
      unsigned int failed = _failed.fetch_and(0);

      if (!_alarm->alarmed())
      {
        // A communication alarm is not currently set so see if one needs to
        // be. This will be the case if there were no successful comms over
        // the interval, and at least one failed comm.
        TRC_DEBUG("Alarm currently clear - successful attempts %d, failures %d",
                  succeeded, failed);
        if ((succeeded == 0) && (failed != 0))
        {
          TRC_STATUS("Setting alarm %d", _alarm->index());
          _alarm->set();
        }
      }
      else
      {
        // A communication alarm is currently set so see if it needs to be
        // cleared. This will be the case if at lease one successful comm
        // was reported over the interval.
        TRC_DEBUG("Alarm currently set - successful attempts %d",
                  succeeded);
        if (succeeded != 0)
        {
          TRC_STATUS("Clearing alarm %d", _alarm->index());
          _alarm->clear();
        }
      }

      _next_check = (_alarm->alarmed()) ? now_ms + _clear_confirm_ms :
                                             now_ms + _set_confirm_ms   ;
    }

    pthread_mutex_unlock(&_lock);
  }
}
Example #3
0
ConnectionPool::ConnectionPool(pjsip_host_port* target,
                               int num_connections,
                               int recycle_period,
                               pj_pool_t* pool,
                               pjsip_endpoint* endpt,
                               pjsip_tpfactory* tp_factory,
                               SNMP::IPCountTable* sprout_count_tbl) :
  _target(*target),
  _num_connections(num_connections),
  _recycle_period(recycle_period),
  _recycle_margin((recycle_period * RECYCLE_RANDOM_MARGIN)/100),
  _pool(pool),
  _endpt(endpt),
  _tpfactory(tp_factory),
  _recycler(NULL),
  _terminated(false),
  _active_connections(0),
  _sprout_count_tbl(sprout_count_tbl)
{
  TRC_STATUS("Creating connection pool to %.*s:%d", _target.host.slen, _target.host.ptr, _target.port);
  TRC_STATUS("  connections = %d, recycle time = %d +/- %d seconds", _num_connections, _recycle_period, _recycle_margin);

  pthread_mutex_init(&_tp_hash_lock, NULL);
  _tp_hash.resize(_num_connections);
}
Example #4
0
void Store::configure_connection(std::string cass_hostname,
                                 uint16_t cass_port,
                                 CommunicationMonitor* comm_monitor)
{
  TRC_STATUS("Configuring store connection");
  TRC_STATUS("  Hostname:  %s", cass_hostname.c_str());
  TRC_STATUS("  Port:      %u", cass_port);
  _cass_hostname = cass_hostname;
  _cass_port = cass_port;
  _comm_monitor = comm_monitor;
}
Example #5
0
void Store::configure_workers(ExceptionHandler* exception_handler,
                              unsigned int num_threads,
                              unsigned int max_queue)
{
  TRC_STATUS("Configuring store worker pool");
  TRC_STATUS("  Threads:   %u", num_threads);
  TRC_STATUS("  Max Queue: %u", max_queue);
  _exception_handler = exception_handler;
  _num_threads = num_threads;
  _max_queue = max_queue;
}
Example #6
0
LoadMonitor::LoadMonitor(int init_target_latency, int max_bucket_size,
                         float init_token_rate, float init_min_token_rate,
                         SNMP::ContinuousAccumulatorTable* token_rate_table,
                         SNMP::U32Scalar* smoothed_latency_scalar,
                         SNMP::U32Scalar* target_latency_scalar,
                         SNMP::U32Scalar* penalties_scalar,
                         SNMP::U32Scalar* token_rate_scalar)
                         : bucket(max_bucket_size, init_token_rate),
                           _token_rate_table(token_rate_table),
                           _smoothed_latency_scalar(smoothed_latency_scalar),
                           _target_latency_scalar(target_latency_scalar),
                           _penalties_scalar(penalties_scalar),
                           _token_rate_scalar(token_rate_scalar)
{
  pthread_mutexattr_t attrs;
  pthread_mutexattr_init(&attrs);
  pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
  pthread_mutex_init(&_lock, &attrs);
  pthread_mutexattr_destroy(&attrs);

  TRC_STATUS("Constructing LoadMonitor");
  TRC_STATUS("   Target latency (usecs)   : %d", init_target_latency);
  TRC_STATUS("   Max bucket size          : %d", max_bucket_size);
  TRC_STATUS("   Initial token fill rate/s: %f", init_token_rate);
  TRC_STATUS("   Min token fill rate/s    : %f", init_min_token_rate);

  REQUESTS_BEFORE_ADJUSTMENT = 20;
  SECONDS_BEFORE_ADJUSTMENT = 2;

  // Adjustment parameters for token bucket
  DECREASE_THRESHOLD = 0.0;
  DECREASE_FACTOR = 1.2;
  INCREASE_THRESHOLD = -0.005;
  INCREASE_FACTOR = 0.5;

  accepted = 0;
  rejected = 0;
  penalties = 0;
  pending_count = 0;
  max_pending_count = 0;
  target_latency = init_target_latency;
  smoothed_latency = init_target_latency;
  adjust_count = 0;

  timespec current_time;
  clock_gettime(CLOCK_MONOTONIC_COARSE, &current_time);
  last_adjustment_time_ms = (current_time.tv_sec * 1000) + (current_time.tv_nsec / 1000);
  min_token_rate = init_min_token_rate;

  // As this statistics reporting is continuous, we should
  // publish the statistics when initialised.
  update_statistics();
}
Example #7
0
ResultCode Store::connection_test()
{
  ResultCode rc = OK;

  // Check that we can connect to cassandra by getting a client. This logs in
  // and switches to the specified keyspace, so is a good test of whether
  // cassandra is working properly.
  TRC_STATUS("Starting store");
  try
  {
    get_client();
    release_client();
  }
  catch(TTransportException te)
  {
    TRC_ERROR("Store caught TTransportException: %s", te.what());
    rc = CONNECTION_ERROR;
  }
  catch(NotFoundException nfe)
  {
    TRC_ERROR("Store caught NotFoundException: %s", nfe.what());
    rc = NOT_FOUND;
  }
  catch(...)
  {
    TRC_ERROR("Store caught unknown exception!");
    rc = UNKNOWN_ERROR;
  }

  return rc;
}
Example #8
0
/// Loads the MMTEL AS plug-in, returning the supported Sproutlets.
bool MMTELASPlugin::load(struct options& opt, std::list<Sproutlet*>& sproutlets)
{
    bool plugin_loaded = true;

    SNMP::SuccessFailCountByRequestTypeTable* incoming_sip_transactions = SNMP::SuccessFailCountByRequestTypeTable::create("mmtel_as_incoming_sip_transactions",
            "1.2.826.0.1.1578918.9.3.24");
    SNMP::SuccessFailCountByRequestTypeTable* outgoing_sip_transactions = SNMP::SuccessFailCountByRequestTypeTable::create("mmtel_as_outgoing_sip_transactions",
            "1.2.826.0.1.1578918.9.3.25");
    if (opt.xdm_server != "")
    {
        // Create a connection to the XDMS.
        TRC_STATUS("Creating connection to XDMS %s", opt.xdm_server.c_str());
        _xdm_cxn_count_tbl = SNMP::IPCountTable::create("homer-ip-count",
                             ".1.2.826.0.1.1578918.9.3.2.1");
        _xdm_latency_tbl = SNMP::AccumulatorTable::create("homer-latency",
                           ".1.2.826.0.1.1578918.9.3.2.2");
        _xdm_connection = new XDMConnection(opt.xdm_server,
                                            http_resolver,
                                            load_monitor,
                                            _xdm_cxn_count_tbl,
                                            _xdm_latency_tbl);

        // Load the MMTEL AppServer
        _mmtel = new Mmtel("mmtel", _xdm_connection);
        _mmtel_sproutlet = new SproutletAppServerShim(_mmtel, incoming_sip_transactions, outgoing_sip_transactions, "mmtel." + opt.home_domain);
        sproutlets.push_back(_mmtel_sproutlet);
    }

    return plugin_loaded;
}
Example #9
0
ResultCode Store::start()
{
  ResultCode rc = OK;

  // Start the store.  We don't check for connectivity to Cassandra at this
  // point as some store users want the store to load even when Cassandra has
  // failed (it will recover later).  If a store user cares about the status
  // of Cassandra it should use the test() method.
  TRC_STATUS("Starting store");

  // Start the thread pool.
  if (_num_threads > 0)
  {
    _thread_pool = new Pool(this,
                            _num_threads,
                            _exception_handler,
                            _max_queue);

    if (!_thread_pool->start())
    {
      rc = RESOURCE_ERROR; // LCOV_EXCL_LINE
    }
  }

  return rc;
}
Example #10
0
void Store::stop()
{
  TRC_STATUS("Stopping store");
  if (_thread_pool != NULL)
  {
    _thread_pool->stop();
  }
}
Example #11
0
void Store::wait_stopped()
{
  TRC_STATUS("Waiting for store to stop");
  if (_thread_pool != NULL)
  {
    _thread_pool->join();

    delete _thread_pool; _thread_pool = NULL;
  }
}
Example #12
0
HttpResolver::HttpResolver(DnsCachedResolver* dns_client,
                           int address_family,
                           int blacklist_duration) :
  BaseResolver(dns_client),
  _address_family(address_family)
{
  TRC_DEBUG("Creating HTTP resolver");

  // Create the blacklist.
  create_blacklist(blacklist_duration);

  TRC_STATUS("Created HTTP resolver");
}
Example #13
0
/// Loads the I-CSCF plug-in, returning the supported Sproutlets.
bool ICSCFPlugin::load(struct options& opt, std::list<Sproutlet*>& sproutlets)
{
  bool plugin_loaded = true;

  // Create the SNMP tables here - they should exist based on whether the
  // plugin is loaded, not whether the Sproutlet is enabled, in order to
  // simplify SNMP polling of multiple differently-configured Sprout nodes.
  _incoming_sip_transactions_tbl = SNMP::SuccessFailCountByRequestTypeTable::create("icscf_incoming_sip_transactions",
                                                                                    "1.2.826.0.1.1578918.9.3.18");
  _outgoing_sip_transactions_tbl = SNMP::SuccessFailCountByRequestTypeTable::create("icscf_outgoing_sip_transactions",
                                                                                    "1.2.826.0.1.1578918.9.3.19");

  if (opt.enabled_icscf)
  {
    TRC_STATUS("I-CSCF plugin enabled");

    // Create the S-CSCF selector.
    _scscf_selector = new SCSCFSelector(opt.uri_scscf);

    // Create the I-CSCF ACR factory.
    _acr_factory = (ralf_processor != NULL) ?
                        (ACRFactory*)new RalfACRFactory(ralf_processor, ACR::ICSCF) :
                        new ACRFactory();

    // Create the I-CSCF sproutlet.
    _icscf_sproutlet = new ICSCFSproutlet(opt.prefix_icscf,
                                          opt.uri_bgcf,
                                          opt.port_icscf,
                                          opt.uri_icscf,
                                          hss_connection,
                                          _acr_factory,
                                          _scscf_selector,
                                          enum_service,
                                          _incoming_sip_transactions_tbl,
                                          _outgoing_sip_transactions_tbl,
                                          opt.override_npdi);
    _icscf_sproutlet->init();

    sproutlets.push_back(_icscf_sproutlet);
  }

  return plugin_loaded;
}
Example #14
0
SIPResolver::SIPResolver(DnsCachedResolver* dns_client,
                         int blacklist_duration) :
  BaseResolver(dns_client)
{
  TRC_DEBUG("Creating SIP resolver");

  // Create the NAPTR cache.
  std::map<std::string, int> naptr_services;
  naptr_services["SIP+D2U"] = IPPROTO_UDP;
  naptr_services["SIP+D2T"] = IPPROTO_TCP;
  create_naptr_cache(naptr_services);

  // Create the SRV cache.
  create_srv_cache();

  // Create the blacklist.
  create_blacklist(blacklist_duration);

  TRC_STATUS("Created SIP resolver");
}
Example #15
0
void ConnectionPool::recycle_connections()
{
  // The recycler periodically recycles the connections so that any new nodes
  // in the upstream proxy cluster get used reasonably soon after they are
  // active.  To avoid mucking around with variable length waits, the
  // algorithm waits for a fixed period (one second) then recycles connections
  // that are due to be recycled.

  while (!_terminated)
  {
#ifdef UNIT_TEST
    // A smaller pause here is much faster
    sleep(0.1);
#else
    sleep(1);
#endif

    int now = time(NULL);

    // Walk the vector of connections.  This is safe to do without the lock
    // because the vector is immutable.
    for (size_t ii = 0; ii < _tp_hash.size(); ++ii)
    {
      if (_tp_hash[ii].tp == NULL)
      {
        // This slot is empty, so try to populate it now.
        create_connection(ii);
      }
      else if ((_tp_hash[ii].connected) &&
               (_tp_hash[ii].recycle_time != 0) &&
               (now >= _tp_hash[ii].recycle_time))
      {
        // This slot is due to be recycled, so quiesce the existing
        // connection and create a new one.
        TRC_STATUS("Recycle TCP connection slot %d", ii);
        quiesce_connection(ii);
        create_connection(ii);
      }
    }
  }
}
Example #16
0
// Set up the SNMP agent. Returns 0 if it succeeds.
int snmp_setup(const char* name)
{
  // Make sure we start as a subagent, not a master agent.
  netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);

  // Use callback-based logging, and integrate it with the Clearwater logger
  snmp_enable_calllog();
  snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, logging_callback, NULL);

  netsnmp_container_init_list();
  int rc = init_agent(name);
  if (rc != 0)
  {
    TRC_WARNING("SNMP AgentX initialization failed");
  }
  else
  {
    TRC_STATUS("AgentX agent initialised");
  }
  return rc;
}
Example #17
0
void SCSCFSelector::update_scscf()
{
  // Check whether the file exists.
  struct stat s;
  if ((stat(_configuration.c_str(), &s) != 0) &&
      (errno == ENOENT))
  {
    TRC_STATUS("No S-CSCF configuration data (file %s does not exist)",
               _configuration.c_str());
    CL_SPROUT_SCSCF_FILE_MISSING.log();
    return;
  }

  TRC_STATUS("Loading S-CSCF configuration from %s", _configuration.c_str());

  // Read from the file
  std::ifstream fs(_configuration.c_str());
  std::string scscf_str((std::istreambuf_iterator<char>(fs)),
                         std::istreambuf_iterator<char>());

  if (scscf_str == "")
  {
    // LCOV_EXCL_START
    TRC_ERROR("Failed to read S-CSCF configuration data from %s",
              _configuration.c_str());
    CL_SPROUT_SCSCF_FILE_EMPTY.log();
    return;
    // LCOV_EXCL_STOP
  }

  // Now parse the document
  rapidjson::Document doc;
  doc.Parse<0>(scscf_str.c_str());

  if (doc.HasParseError())
  {
    TRC_ERROR("Failed to read S-CSCF configuration data: %s\nError: %s",
              scscf_str.c_str(),
              rapidjson::GetParseError_En(doc.GetParseError()));
    CL_SPROUT_SCSCF_FILE_INVALID.log();
    return;
  }

  try
  {
    std::vector<scscf_t> new_scscfs;

    JSON_ASSERT_CONTAINS(doc, "s-cscfs");
    JSON_ASSERT_ARRAY(doc["s-cscfs"]);
    const rapidjson::Value& scscfs_arr = doc["s-cscfs"];

    for (rapidjson::Value::ConstValueIterator scscfs_it = scscfs_arr.Begin();
         scscfs_it != scscfs_arr.End();
         ++scscfs_it)
    {
      try
      {
        scscf_t new_scscf;
        JSON_GET_STRING_MEMBER(*scscfs_it, "server", new_scscf.server);
        JSON_GET_INT_MEMBER(*scscfs_it, "priority", new_scscf.priority);
        JSON_GET_INT_MEMBER(*scscfs_it, "weight", new_scscf.weight);

        JSON_ASSERT_CONTAINS(*scscfs_it, "capabilities");
        JSON_ASSERT_ARRAY((*scscfs_it)["capabilities"]);
        const rapidjson::Value& cap_arr = (*scscfs_it)["capabilities"];
        std::vector<int> capabilities_vec;

        for (rapidjson::Value::ConstValueIterator cap_it = cap_arr.Begin();
             cap_it != cap_arr.End();
             ++cap_it)
        {
          capabilities_vec.push_back((*cap_it).GetInt());
        }

        // Sort the capabilities and remove duplicates
        std::sort(capabilities_vec.begin(), capabilities_vec.end());
        capabilities_vec.erase(unique(capabilities_vec.begin(),
                                      capabilities_vec.end()),
                               capabilities_vec.end() );
        new_scscf.capabilities = capabilities_vec;
        new_scscfs.push_back(new_scscf);
        capabilities_vec.clear();
      }
      catch (JsonFormatError err)
      {
        // Badly formed number block.
        TRC_WARNING("Badly formed S-CSCF entry (hit error at %s:%d)",
                    err._file, err._line);
        CL_SPROUT_SCSCF_FILE_INVALID.log();
      }
    }

    // Take a write lock on the mutex in RAII style
    boost::lock_guard<boost::shared_mutex> write_lock(_scscfs_rw_lock);
    _scscfs = new_scscfs;
  }
  catch (JsonFormatError err)
  {
    TRC_ERROR("Badly formed S-CSCF configuration file - missing s-cscfs object");
    CL_SPROUT_SCSCF_FILE_INVALID.log();
  }
}
Example #18
0
int main(int argc, char**argv)
{
  // Set up our exception signal handler for asserts and segfaults.
  signal(SIGABRT, signal_handler);
  signal(SIGSEGV, signal_handler);

  sem_init(&term_sem, 0, 0);
  signal(SIGTERM, terminate_handler);

  struct options options;
  options.local_host = "127.0.0.1";
  options.http_address = "0.0.0.0";
  options.http_port = 11888;
  options.http_threads = 1;
  options.http_worker_threads = 50;
  options.homestead_http_name = "homestead-http-name.unknown";
  options.digest_timeout = 300;
  options.home_domain = "home.domain";
  options.sas_server = "0.0.0.0";
  options.sas_system_name = "";
  options.access_log_enabled = false;
  options.log_to_file = false;
  options.log_level = 0;
  options.memcached_write_format = MemcachedWriteFormat::JSON;
  options.target_latency_us = 100000;
  options.max_tokens = 1000;
  options.init_token_rate = 100.0;
  options.min_token_rate = 10.0;
  options.exception_max_ttl = 600;
  options.http_blacklist_duration = HttpResolver::DEFAULT_BLACKLIST_DURATION;
  options.pidfile = "";
  options.daemon = false;

  if (init_logging_options(argc, argv, options) != 0)
  {
    return 1;
  }

  Log::setLoggingLevel(options.log_level);

  if ((options.log_to_file) && (options.log_directory != ""))
  {
    // Work out the program name from argv[0], stripping anything before the final slash.
    char* prog_name = argv[0];
    char* slash_ptr = rindex(argv[0], '/');

    if (slash_ptr != NULL)
    {
      prog_name = slash_ptr + 1;
    }

    Log::setLogger(new Logger(options.log_directory, prog_name));
  }

  TRC_STATUS("Log level set to %d", options.log_level);

  std::stringstream options_ss;

  for (int ii = 0; ii < argc; ii++)
  {
    options_ss << argv[ii];
    options_ss << " ";
  }

  std::string options_str = "Command-line options were: " + options_ss.str();

  TRC_INFO(options_str.c_str());

  if (init_options(argc, argv, options) != 0)
  {
    return 1;
  }

  if (options.daemon)
  {
    // Options parsed and validated, time to demonize before writing out our
    // pidfile or spwaning threads.
    int errnum = Utils::daemonize();
    if (errnum != 0)
    {
      TRC_ERROR("Failed to convert to daemon, %d (%s)", errnum, strerror(errnum));
      exit(0);
    }
  }

  if (options.pidfile != "")
  {
    int rc = Utils::lock_and_write_pidfile(options.pidfile);
    if (rc == -1)
    {
      // Failure to acquire pidfile lock
      TRC_ERROR("Could not write pidfile - exiting");
      return 2;
    }
  }

  start_signal_handlers();

  AccessLogger* access_logger = NULL;

  if (options.access_log_enabled)
  {
    TRC_STATUS("Access logging enabled to %s", options.access_log_directory.c_str());
    access_logger = new AccessLogger(options.access_log_directory);
  }

  HealthChecker* hc = new HealthChecker();
  hc->start_thread();

  // Create an exception handler. The exception handler doesn't need
  // to quiesce the process before killing it.
  exception_handler = new ExceptionHandler(options.exception_max_ttl,
                                           false,
                                           hc);

  SAS::init(options.sas_system_name,
            "memento",
            SASEvent::CURRENT_RESOURCE_BUNDLE,
            options.sas_server,
            sas_write,
            create_connection_in_management_namespace);

  // Ensure our random numbers are unpredictable.
  unsigned int seed;
  seed = time(NULL) ^ getpid();
  srand(seed);

  // Create alarm and communication monitor objects for the conditions
  // reported by memento.
  CommunicationMonitor* mc_comm_monitor = new CommunicationMonitor(new Alarm("memento", AlarmDef::MEMENTO_MEMCACHED_COMM_ERROR, AlarmDef::CRITICAL),
                                                                   "Memento",
                                                                   "Memcached");
  Alarm* mc_vbucket_alarm = new Alarm("memento", AlarmDef::MEMENTO_MEMCACHED_VBUCKET_ERROR, AlarmDef::MAJOR);
  CommunicationMonitor* hs_comm_monitor = new CommunicationMonitor(new Alarm("memento", AlarmDef::MEMENTO_HOMESTEAD_COMM_ERROR, AlarmDef::CRITICAL),
                                                                   "Memento",
                                                                   "Homestead");
  CommunicationMonitor* cass_comm_monitor = new CommunicationMonitor(new Alarm("memento", AlarmDef::MEMENTO_CASSANDRA_COMM_ERROR, AlarmDef::CRITICAL),
                                                                     "Memento",
                                                                     "Cassandra");

  TRC_DEBUG("Starting alarm request agent");
  AlarmReqAgent::get_instance().start();

  MemcachedStore* m_store = new MemcachedStore(true,
                                               "./cluster_settings",
                                               mc_comm_monitor,
                                               mc_vbucket_alarm);

  AuthStore::SerializerDeserializer* serializer;
  std::vector<AuthStore::SerializerDeserializer*> deserializers;

  if (options.memcached_write_format == MemcachedWriteFormat::JSON)
  {
    serializer = new AuthStore::JsonSerializerDeserializer();
  }
  else
  {
    serializer = new AuthStore::BinarySerializerDeserializer();
  }

  deserializers.push_back(new AuthStore::JsonSerializerDeserializer());
  deserializers.push_back(new AuthStore::BinarySerializerDeserializer());

  AuthStore* auth_store = new AuthStore(m_store,
                                        serializer,
                                        deserializers,
                                        options.digest_timeout);

  LoadMonitor* load_monitor = new LoadMonitor(options.target_latency_us,
                                              options.max_tokens,
                                              options.init_token_rate,
                                              options.min_token_rate);

  LastValueCache* stats_aggregator = new MementoLVC();

  // Create a DNS resolver and an HTTP specific resolver.
  int af = AF_INET;
  struct in6_addr dummy_addr;
  if (inet_pton(AF_INET6, options.local_host.c_str(), &dummy_addr) == 1)
  {
    TRC_DEBUG("Local host is an IPv6 address");
    af = AF_INET6;
  }

  DnsCachedResolver* dns_resolver = new DnsCachedResolver("127.0.0.1");
  HttpResolver* http_resolver = new HttpResolver(dns_resolver,
                                                 af,
                                                 options.http_blacklist_duration);
  HomesteadConnection* homestead_conn = new HomesteadConnection(options.homestead_http_name,
                                                                http_resolver,
                                                                load_monitor,
                                                                hs_comm_monitor);

  // Create and start the call list store.
  CallListStore::Store* call_list_store = new CallListStore::Store();
  call_list_store->configure_connection("localhost", 9160, cass_comm_monitor);

  // Test Cassandra connectivity.
  CassandraStore::ResultCode store_rc = call_list_store->connection_test();

  if (store_rc == CassandraStore::OK)
  {
    // Store can connect to Cassandra, so start it.
    store_rc = call_list_store->start();
  }

  if (store_rc != CassandraStore::OK)
  {
    TRC_ERROR("Unable to create call list store (RC = %d)", store_rc);
    exit(3);
  }

  HttpStack* http_stack = HttpStack::get_instance();
  HttpStackUtils::SimpleStatsManager stats_manager(stats_aggregator);

  CallListTask::Config call_list_config(auth_store, homestead_conn, call_list_store, options.home_domain, stats_aggregator, hc, options.api_key);

  MementoSasLogger sas_logger;
  HttpStackUtils::PingHandler ping_handler;
  HttpStackUtils::SpawningHandler<CallListTask, CallListTask::Config> call_list_handler(&call_list_config, &sas_logger);
  HttpStackUtils::HandlerThreadPool pool(options.http_worker_threads, exception_handler);

  try
  {
    http_stack->initialize();
    http_stack->configure(options.http_address,
                          options.http_port,
                          options.http_threads,
                          exception_handler,
                          access_logger,
                          load_monitor,
                          &stats_manager);
    http_stack->register_handler("^/ping$", &ping_handler);
    http_stack->register_handler("^/org.projectclearwater.call-list/users/[^/]*/call-list.xml$",
                                    pool.wrap(&call_list_handler));
    http_stack->start();
  }
  catch (HttpStack::Exception& e)
  {
    TRC_ERROR("Failed to initialize HttpStack stack - function %s, rc %d", e._func, e._rc);
    exit(2);
  }

  TRC_STATUS("Start-up complete - wait for termination signal");
  sem_wait(&term_sem);
  TRC_STATUS("Termination signal received - terminating");

  try
  {
    http_stack->stop();
    http_stack->wait_stopped();
  }
  catch (HttpStack::Exception& e)
  {
    TRC_ERROR("Failed to stop HttpStack stack - function %s, rc %d", e._func, e._rc);
  }

  call_list_store->stop();
  call_list_store->wait_stopped();

  hc->stop_thread();

  delete homestead_conn; homestead_conn = NULL;
  delete call_list_store; call_list_store = NULL;
  delete http_resolver; http_resolver = NULL;
  delete dns_resolver; dns_resolver = NULL;
  delete load_monitor; load_monitor = NULL;
  delete auth_store; auth_store = NULL;
  delete call_list_store; call_list_store = NULL;
  delete m_store; m_store = NULL;
  delete exception_handler; exception_handler = NULL;
  delete hc; hc = NULL;


  // Stop the alarm request agent
  AlarmReqAgent::get_instance().stop();

  delete mc_comm_monitor; mc_comm_monitor = NULL;
  delete mc_vbucket_alarm; mc_vbucket_alarm = NULL;
  delete hs_comm_monitor; hs_comm_monitor = NULL;
  delete cass_comm_monitor; cass_comm_monitor = NULL;

  SAS::term();

  signal(SIGTERM, SIG_DFL);
  sem_destroy(&term_sem);
}
Example #19
0
void SIFCService::update_sets()
{
  // Check whether the file exists.
  struct stat s;
  TRC_DEBUG("stat(%s) returns %d", _configuration.c_str(), stat(_configuration.c_str(), &s));
  if ((stat(_configuration.c_str(), &s) != 0) &&
      (errno == ENOENT))
  {
    TRC_STATUS("No shared iFCs configuration (file %s does not exist)",
               _configuration.c_str());
    CL_SPROUT_SIFC_FILE_MISSING.log();
    set_alarm();
    return;
  }

  TRC_STATUS("Loading shared iFCs configuration from %s", _configuration.c_str());

  // Read from the file
  std::ifstream fs(_configuration.c_str());
  std::string sifc_str((std::istreambuf_iterator<char>(fs)),
                        std::istreambuf_iterator<char>());

  if (sifc_str == "")
  {
    TRC_ERROR("Failed to read shared iFCs configuration data from %s",
              _configuration.c_str());
    CL_SPROUT_SIFC_FILE_EMPTY.log();
    set_alarm();
    return;
  }

  // Now parse the document
  rapidxml::xml_document<>* root = new rapidxml::xml_document<>;

  try
  {
    root->parse<0>(root->allocate_string(sifc_str.c_str()));
  }
  catch (rapidxml::parse_error& err)
  {
    TRC_ERROR("Failed to parse the shared iFCs configuration data:\n %s\n %s",
              sifc_str.c_str(),
              err.what());
    CL_SPROUT_SIFC_FILE_INVALID_XML.log();
    set_alarm();
    delete root; root = NULL;
    return;
  }

  if (!root->first_node(SIFCService::SHARED_IFCS_SETS))
  {
    TRC_ERROR("Invalid shared iFCs configuration file - missing SharedIFCsSets block");
    CL_SPROUT_SIFC_FILE_MISSING_SHARED_IFCS_SETS.log();
    set_alarm();
    delete root; root = NULL;
    return;
  }

  // At this point, we're definitely going to override the iFCs we've got.
  // Update our map, taking a lock while we do so.
  boost::lock_guard<boost::shared_mutex> write_lock(_sets_rw_lock);
  _shared_ifc_sets.clear();
  bool any_errors = false;

  rapidxml::xml_node<>* sets = root->first_node(SIFCService::SHARED_IFCS_SETS);
  rapidxml::xml_node<>* set = NULL;

  for (set = sets->first_node(SIFCService::SHARED_IFCS_SET);
       set != NULL;
       set = set->next_sibling(SIFCService::SHARED_IFCS_SET))
  {
    rapidxml::xml_node<>* set_id_node = set->first_node(SIFCService::SET_ID);

    if (!set_id_node)
    {
      TRC_ERROR("Invalid shared iFC block - missing SetID. Skipping this entry");
      CL_SPROUT_SIFC_FILE_MISSING_SET_ID.log();
      any_errors = true;
      continue;
    }

    std::string set_id_str = std::string(set_id_node->value());
    Utils::trim(set_id_str);
    int32_t set_id = std::atoi(set_id_str.c_str());

    if (set_id_str != std::to_string(set_id))
    {
      TRC_ERROR("Invalid shared iFC block - SetID (%s) isn't an int. Skipping this entry",
                set_id_str.c_str());
      CL_SPROUT_SIFC_FILE_INVALID_SET_ID.log(set_id_str.c_str());
      any_errors = true;
      continue;
    }

    if (_shared_ifc_sets.count(set_id) != 0)
    {
      TRC_ERROR("Invalid shared iFC block - SetID (%d) is repeated. Skipping this entry",
                set_id);
      CL_SPROUT_SIFC_FILE_REPEATED_SET_ID.log(set_id_str.c_str());
      any_errors = true;
      continue;
    }

    std::vector<std::pair<int32_t, std::string>> ifc_set;

    for (rapidxml::xml_node<>* ifc = set->first_node(RegDataXMLUtils::IFC);
         ifc != NULL;
         ifc = ifc->next_sibling(RegDataXMLUtils::IFC))
    {
      int32_t priority = 0;
      rapidxml::xml_node<>* priority_node = ifc->first_node(RegDataXMLUtils::PRIORITY);

      if (priority_node)
      {
        std::string priority_str = std::string(priority_node->value());
        Utils::trim(priority_str);
        priority = std::atoi(priority_str.c_str());

        if (priority_str != std::to_string(priority))
        {
          TRC_ERROR("Invalid shared iFC block - Priority (%s) isn't an int. Skipping this entry",
                    priority_str.c_str());
          CL_SPROUT_SIFC_FILE_INVALID_PRIORITY.log(priority_str.c_str());
          any_errors = true;
          continue;
        }
      }

      // Creating the iFC always passes; we don't validate the iFC any further
      // at this stage. We've validated this against a schema before allowing
      // any upload though.
      std::string ifc_str;
      rapidxml::print(std::back_inserter(ifc_str), *ifc, 0);
      ifc_set.push_back(std::make_pair(priority, ifc_str));
    }

    TRC_STATUS("Adding %lu iFCs for ID %d", ifc_set.size(), set_id);
    _shared_ifc_sets.insert(std::make_pair(set_id, ifc_set));
  }

  if (any_errors)
  {
    set_alarm();
  }
  else
  {
    clear_alarm();
  }

  delete root; root = NULL;
}
Example #20
0
void LoadMonitor::request_complete(int latency)
{
  pthread_mutex_lock(&_lock);
  pending_count -= 1;
  smoothed_latency = (7 * smoothed_latency + latency) / 8;
  adjust_count += 1;

  if (adjust_count >= REQUESTS_BEFORE_ADJUSTMENT)
  {
    // We've seen the right number of requests, but ensure
    // that an appropriate amount of time has passed, so the rate doesn't
    // fluctuate wildly if latency spikes for a few milliseconds
    timespec current_time;
    clock_gettime(CLOCK_MONOTONIC_COARSE, &current_time);
    unsigned long current_time_ms = (current_time.tv_sec * 1000) + (current_time.tv_nsec / 1000);
    if (current_time_ms >= (last_adjustment_time_ms + (SECONDS_BEFORE_ADJUSTMENT * 1000)))
    {
      // This algorithm is based on the Welsh and Culler "Adaptive Overload
      // Control for Busy Internet Servers" paper, although based on a smoothed
      // mean latency, rather than the 90th percentile as per the paper.
      // Also, the additive increase is scaled as a proportion of the maximum
      // bucket size, rather than an absolute number as per the paper.
      float err = ((float) (smoothed_latency - target_latency)) / target_latency;

      // Work out the percentage of accepted requests (for logs)
      float accepted_percent = (accepted + rejected == 0) ? 100.0 : 100 * (((float) accepted) / (accepted + rejected));

      TRC_INFO("Accepted %f%% of requests, latency error = %f, overload responses = %d",
          accepted_percent, err, penalties);

      // latency is above where we want it to be, or we are getting overload responses from
      // Homer/Homestead, so adjust the rate downwards by a multiplicative factor

      if (err > DECREASE_THRESHOLD || penalties > 0)
      {
        float new_rate = bucket.rate / DECREASE_FACTOR;
        if (new_rate < min_token_rate)
        {
          new_rate = min_token_rate;
        }
        bucket.update_rate(new_rate);
        TRC_STATUS("Maximum incoming request rate/second decreased to %f "
                   "(based on a smoothed mean latency of %d and %d upstream overload responses)",
                   bucket.rate,
                   smoothed_latency,
                   penalties);
      }
      else if (err < INCREASE_THRESHOLD)
      {
        // Our latency is below the threshold, so increasing our permitted request rate would be
        // sensible. Before doing that, we check that we're using a significant proportion of our
        // current rate - if we're allowing 100 requests/sec, and we get 1 request/sec because it's
        // a quiet period, then it's going to be handled quickly, but that's not sufficient evidence
        // to increase our rate.
        float maximum_permitted_requests = bucket.rate * (current_time_ms - last_adjustment_time_ms) / 1000;

        // Arbitrary threshold - require 50% of our current permitted rate to be used
        float minimum_threshold = maximum_permitted_requests * 0.5;

        if (accepted > minimum_threshold)
        {
          float new_rate = bucket.rate + (-1 * err * bucket.max_size * INCREASE_FACTOR);
          bucket.update_rate(new_rate);
          TRC_STATUS("Maximum incoming request rate/second increased to %f "
                     "(based on a smoothed mean latency of %d and %d upstream overload responses)",
                     bucket.rate,
                     smoothed_latency,
                     penalties);
        }
        else
        {
          TRC_STATUS("Maximum incoming request rate/second unchanged - only handled %d requests"
                     " recently, minimum threshold for a change is %f",
                     accepted,
                     minimum_threshold);
        }
      }
      else
      {
        TRC_DEBUG("Maximum incoming request rate/second is unchanged at %f",
                  bucket.rate);
      }

      update_statistics();

      // Reset counts
      last_adjustment_time_ms = current_time_ms;
      adjust_count = 0;
      accepted = 0;
      rejected = 0;
      penalties = 0;
    }
  }

  pthread_mutex_unlock(&_lock);
}
Example #21
0
/// Load the plug-ins, returning the resulting list of Sproutlets.
bool PluginLoader::load(std::list<Sproutlet*>& sproutlets)
{
  TRC_STATUS("Loading plug-ins from %s", _path.c_str());
  bool plugins_loaded = true;

  DIR* d = opendir(_path.c_str());

  if (d != NULL)
  {
    struct dirent *de;
    while ((de = readdir(d)) != NULL)
    {
      // The file name isn't a reliable indication that the file is a shared
      // object, but checking for a ".so" extension filters out files like "."
      // and ".." and prevents spurious error logs. If a file isn't a valid
      // shared object, dlopen will return NULL and we'll log an error.
      
      // We don't check the file type as given by de->d_type - this would also
      // filter out directories like "." and "..", but some filesystems like
      // XFS don't support this.
      Plugin p;
      p.name = _path + "/";
      p.name.append(de->d_name);
      p.handle = NULL;
      p.plugin = NULL;

      if (p.name.compare(p.name.length() - 3, 3, ".so") != 0)
      {
        TRC_DEBUG("Skipping %s - doesn't have .so extension", p.name.c_str());
        continue;
      }

      TRC_STATUS("Attempt to load plug-in %s", p.name.c_str());

      dlerror();
      p.handle = dlopen(p.name.c_str(), RTLD_NOW);
      if (p.handle != NULL)
      {
        p.plugin = static_cast<SproutletPlugin*>(dlsym(p.handle, "sproutlet_plugin"));

        if (p.plugin != NULL)
        {
          std::list<Sproutlet*> plugin_sproutlets;
          plugins_loaded = p.plugin->load(_opt, plugin_sproutlets);

          if (!plugins_loaded)
          {
            // There was an error loading one of the plugins. Return an error
            // now so that Sprout is killed, rather than running with 
            // unexpected plugins.
            TRC_ERROR("Failed to successfully load plug-in %s", p.name.c_str());
            break;
          }

          for (std::list<Sproutlet*>::const_iterator i = plugin_sproutlets.begin();
               i != plugin_sproutlets.end();
               ++i)
          {
            Sproutlet* s = *i;
            TRC_DEBUG("Sproutlet %s using API version %d",
                      s->service_name().c_str(), s->api_version());
            if (api_supported(s->api_version()))
            {
              // The API version required by the sproutlet is supported.
              sproutlets.push_back(s);
              TRC_STATUS("Loaded sproutlet %s using API version %d",
                         s->service_name().c_str(), s->api_version());
            }
            else
            {
              // The API version required by the sproutlet is not supported.
              TRC_ERROR("Sproutlet %s requires unsupported API version %d",
                        s->service_name().c_str(), s->api_version());
            }
          }
        }
      }

      if (p.plugin != NULL)
      {
        // Add shared object to the list of loaded plugins.
        _loaded.push_back(p);
      }
      else
      {
        TRC_ERROR("Error loading Sproutlet plug-in %s - %s",
                  p.name.c_str(), dlerror());
        if (p.handle != NULL)
        {
          dlclose(p.handle);
        }
      }
    }
    closedir(d);
  }

  TRC_STATUS("Finished loading plug-ins");
  return plugins_loaded;
}
Example #22
0
void BgcfService::update_routes()
{
  // Check whether the file exists.
  struct stat s;
  TRC_DEBUG("stat(%s) returns %d", _configuration.c_str(), stat(_configuration.c_str(), &s));
  if ((stat(_configuration.c_str(), &s) != 0) &&
      (errno == ENOENT))
  {
    TRC_STATUS("No BGCF configuration (file %s does not exist)",
               _configuration.c_str());
    CL_SPROUT_BGCF_FILE_MISSING.log();
    return;
  }

  TRC_STATUS("Loading BGCF configuration from %s", _configuration.c_str());

  // Read from the file
  std::ifstream fs(_configuration.c_str());
  std::string bgcf_str((std::istreambuf_iterator<char>(fs)),
                        std::istreambuf_iterator<char>());

  if (bgcf_str == "")
  {
    // LCOV_EXCL_START
    TRC_ERROR("Failed to read BGCF configuration data from %s",
              _configuration.c_str());
    CL_SPROUT_BGCF_FILE_EMPTY.log();
    return;
    // LCOV_EXCL_STOP
  }

  // Now parse the document
  rapidjson::Document doc;
  doc.Parse<0>(bgcf_str.c_str());

  if (doc.HasParseError())
  {
    TRC_ERROR("Failed to read BGCF configuration data: %s\nError: %s",
              bgcf_str.c_str(),
              rapidjson::GetParseError_En(doc.GetParseError()));
    CL_SPROUT_BGCF_FILE_INVALID.log();
    return;
  }

  try
  {
    std::map<std::string, std::vector<std::string>> new_domain_routes;
    std::map<std::string, std::vector<std::string>> new_number_routes;

    JSON_ASSERT_CONTAINS(doc, "routes");
    JSON_ASSERT_ARRAY(doc["routes"]);
    const rapidjson::Value& routes_arr = doc["routes"];

    for (rapidjson::Value::ConstValueIterator routes_it = routes_arr.Begin();
         routes_it != routes_arr.End();
         ++routes_it)
    {
      // An entry is valid if it has either a domain (string) OR a
      // number (string) AND an array of routes
      if ((((((*routes_it).HasMember("domain")) &&
             ((*routes_it)["domain"].IsString()))   &&
            (!(*routes_it).HasMember("number"))) ||
           ((!(*routes_it).HasMember("domain"))  &&
            (((*routes_it).HasMember("number")) &&
             ((*routes_it)["number"].IsString())))) &&
          ((*routes_it).HasMember("route") &&
           (*routes_it)["route"].IsArray()))
      {
        std::vector<std::string> route_vec;
        const rapidjson::Value& route_arr = (*routes_it)["route"];

        for (rapidjson::Value::ConstValueIterator route_it = route_arr.Begin();
             route_it != route_arr.End();
            ++route_it)
        {
          std::string route_uri = (*route_it).GetString();
          TRC_DEBUG("  %s", route_uri.c_str());
          route_vec.push_back(route_uri);
        }

        std::string routing_value;

        if ((*routes_it).HasMember("domain"))
        {
          routing_value = (*routes_it)["domain"].GetString();
          new_domain_routes.insert(std::make_pair(routing_value, route_vec));
        }
        else
        {
          routing_value = (*routes_it)["number"].GetString();
          new_number_routes.insert(
                    std::make_pair(PJUtils::remove_visual_separators(routing_value),
                                   route_vec));
        }

        route_vec.clear();

        TRC_DEBUG("Add route for %s", routing_value.c_str());
      }
      else
      {
        TRC_WARNING("Badly formed BGCF route entry");
        CL_SPROUT_BGCF_FILE_INVALID.log();
      }
    }

    // Take a write lock on the mutex in RAII style
    boost::lock_guard<boost::shared_mutex> write_lock(_routes_rw_lock);
    _domain_routes = new_domain_routes;
    _number_routes = new_number_routes;
  }
  catch (JsonFormatError err)
  {
    TRC_ERROR("Badly formed BGCF configuration file - missing routes object");
    CL_SPROUT_BGCF_FILE_INVALID.log();
  }
}
Example #23
0
void JSONEnumService::update_enum()
{
  // Check whether the file exists.
  struct stat s;
  if ((stat(_configuration.c_str(), &s) != 0) &&
      (errno == ENOENT))
  {
    TRC_STATUS("No ENUM configuration (file %s does not exist)",
               _configuration.c_str());
    CL_SPROUT_ENUM_FILE_MISSING.log(_configuration.c_str());
    return;
  }

  TRC_STATUS("Loading ENUM configuration from %s", _configuration.c_str());

  // Read from the file
  std::ifstream fs(_configuration.c_str());
  std::string enum_str((std::istreambuf_iterator<char>(fs)),
                        std::istreambuf_iterator<char>());

  if (enum_str == "")
  {
    // LCOV_EXCL_START
    TRC_ERROR("Failed to read ENUM configuration data from %s",
              _configuration.c_str());
    CL_SPROUT_ENUM_FILE_EMPTY.log(_configuration.c_str());
    return;
    // LCOV_EXCL_STOP
  }

  // Now parse the document
  rapidjson::Document doc;
  doc.Parse<0>(enum_str.c_str());

  if (doc.HasParseError())
  {
    TRC_ERROR("Failed to read ENUM configuration data: %s\nError: %s",
              enum_str.c_str(),
              rapidjson::GetParseError_En(doc.GetParseError()));
    CL_SPROUT_ENUM_FILE_INVALID.log(_configuration.c_str());
    return;
  }

  try
  {
    std::vector<NumberPrefix> new_number_prefixes;
    std::map<std::string, NumberPrefix> new_prefix_regex_map;

    JSON_ASSERT_CONTAINS(doc, "number_blocks");
    JSON_ASSERT_ARRAY(doc["number_blocks"]);
    const rapidjson::Value& nb_arr = doc["number_blocks"];

    for (rapidjson::Value::ConstValueIterator nb_it = nb_arr.Begin();
         nb_it != nb_arr.End();
         ++nb_it)
    {
      try
      {
        std::string prefix;
        JSON_GET_STRING_MEMBER(*nb_it, "prefix", prefix);
        std::string regex;
        JSON_GET_STRING_MEMBER(*nb_it, "regex", regex);

        // Entry is well-formed, so strip off visual separators and add it.
        TRC_DEBUG("Found valid number prefix block %s", prefix.c_str());
        NumberPrefix pfix;
        prefix = PJUtils::remove_visual_separators(prefix);
        pfix.prefix = prefix;

        if (parse_regex_replace(regex, pfix.match, pfix.replace))
        {
          // Create an array in order of entries in json file, and a map
          // (automatically sorted in order of key length) so we can later
          // match numbers to the most specific prefixes
          new_number_prefixes.push_back(pfix);
          new_prefix_regex_map.insert(std::make_pair(prefix, pfix));
          TRC_STATUS("  Adding number prefix %s, regex=%s",
                     pfix.prefix.c_str(), regex.c_str());
        }
        else
        {
          TRC_WARNING("Badly formed regular expression in ENUM number block %s",
                      regex.c_str());
        }
      }
      catch (JsonFormatError err)
      {
        // Badly formed number block.
        TRC_WARNING("Badly formed ENUM number block (hit error at %s:%d)",
                    err._file, err._line);
        CL_SPROUT_ENUM_FILE_INVALID.log(_configuration.c_str());
      }
    }

    // Take a write lock on the mutex in RAII style
    boost::lock_guard<boost::shared_mutex> write_lock(_number_prefixes_rw_lock);
    _number_prefixes = new_number_prefixes;
    _prefix_regex_map = new_prefix_regex_map;
  }
  catch (JsonFormatError err)
  {
    TRC_ERROR("Badly formed ENUM configuration data - missing number_blocks object");
    CL_SPROUT_ENUM_FILE_INVALID.log(_configuration.c_str());
  }
}
Example #24
0
void SasService::extract_config()
{
  // Check whether the file exists.
  struct stat s;
  TRC_DEBUG("stat(%s) returns %d", _configuration.c_str(), stat(_configuration.c_str(), &s));
  if ((stat(_configuration.c_str(), &s) != 0) &&
      (errno == ENOENT))
  {
    TRC_STATUS("No SAS configuration (file %s does not exist)",
               _configuration.c_str());
    CL_SAS_FILE_MISSING.log();
    return;
  }

  TRC_STATUS("Loading SAS configuration from %s", _configuration.c_str());

  // Read from the file
  std::ifstream fs(_configuration.c_str());
  std::string sas_str((std::istreambuf_iterator<char>(fs)),
                       std::istreambuf_iterator<char>());

  if (sas_str == "")
  {
    TRC_ERROR("Failed to read SAS configuration data from %s",
              _configuration.c_str());
    CL_SAS_FILE_EMPTY.log();
    return;
  }

  // Now parse the document
  rapidjson::Document doc;
  doc.Parse<0>(sas_str.c_str());

  if (doc.HasParseError())
  {
    TRC_ERROR("Failed to read SAS configuration data: %s\nError: %s",
              sas_str.c_str(),
              rapidjson::GetParseError_En(doc.GetParseError()));
    CL_SAS_FILE_INVALID.log();
    return;
  }

  try
  {
    JSON_ASSERT_CONTAINS(doc, "sas_servers");
    JSON_ASSERT_ARRAY(doc["sas_servers"]);
    rapidjson::Value& sas_servers = doc["sas_servers"];

    for (rapidjson::Value::ValueIterator sas_it = sas_servers.Begin();
         sas_it != sas_servers.End();
         ++sas_it)
    {
      JSON_ASSERT_OBJECT(*sas_it);
      JSON_ASSERT_CONTAINS(*sas_it, "ip");
      JSON_ASSERT_STRING((*sas_it)["ip"]);

      boost::lock_guard<boost::shared_mutex> write_lock(_sas_server_lock);
      _single_sas_server = (*sas_it)["ip"].GetString();
    }

    // We have a valid rapidjson object.  Write this to the _sas_servers member
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    sas_servers.Accept(writer);

    TRC_DEBUG("New _sas_servers config:  %s", buffer.GetString());

    boost::lock_guard<boost::shared_mutex> write_lock(_sas_server_lock);
    _sas_servers = buffer.GetString();
  }
  catch (JsonFormatError err)
  {
    TRC_ERROR("Badly formed SAS configuration file");
    CL_SAS_FILE_INVALID.log();
  }
}
Example #25
0
int main(int argc, char**argv)
{
  // Set up our exception signal handler for asserts and segfaults.
  signal(SIGABRT, signal_handler);
  signal(SIGSEGV, signal_handler);

  sem_init(&term_sem, 0, 0);
  signal(SIGTERM, terminate_handler);

  AstaireResolver* astaire_resolver = NULL;

  struct options options;
  options.local_host = "127.0.0.1";
  options.http_address = "0.0.0.0";
  options.http_port = 11888;
  options.http_threads = 1;
  options.http_worker_threads = 50;
  options.homestead_http_name = "homestead-http-name.unknown";
  options.digest_timeout = 300;
  options.home_domain = "home.domain";
  options.sas_system_name = "";
  options.access_log_enabled = false;
  options.log_to_file = false;
  options.log_level = 0;
  options.astaire = "";
  options.cassandra = "";
  options.memcached_write_format = MemcachedWriteFormat::JSON;
  options.target_latency_us = 100000;
  options.max_tokens = 1000;
  options.init_token_rate = 100.0;
  options.min_token_rate = 10.0;
  options.min_token_rate = 0.0;
  options.exception_max_ttl = 600;
  options.astaire_blacklist_duration = AstaireResolver::DEFAULT_BLACKLIST_DURATION;
  options.http_blacklist_duration = HttpResolver::DEFAULT_BLACKLIST_DURATION;
  options.pidfile = "";
  options.daemon = false;

  if (init_logging_options(argc, argv, options) != 0)
  {
    return 1;
  }

  Utils::daemon_log_setup(argc,
                          argv,
                          options.daemon,
                          options.log_directory,
                          options.log_level,
                          options.log_to_file);

  std::stringstream options_ss;

  for (int ii = 0; ii < argc; ii++)
  {
    options_ss << argv[ii];
    options_ss << " ";
  }

  std::string options_str = "Command-line options were: " + options_ss.str();

  TRC_INFO(options_str.c_str());

  if (init_options(argc, argv, options) != 0)
  {
    return 1;
  }

  if (options.pidfile != "")
  {
    int rc = Utils::lock_and_write_pidfile(options.pidfile);
    if (rc == -1)
    {
      // Failure to acquire pidfile lock
      TRC_ERROR("Could not write pidfile - exiting");
      return 2;
    }
  }

  start_signal_handlers();

  AccessLogger* access_logger = NULL;

  if (options.access_log_enabled)
  {
    TRC_STATUS("Access logging enabled to %s", options.access_log_directory.c_str());
    access_logger = new AccessLogger(options.access_log_directory);
  }

  HealthChecker* hc = new HealthChecker();
  hc->start_thread();

  // Create an exception handler. The exception handler doesn't need
  // to quiesce the process before killing it.
  exception_handler = new ExceptionHandler(options.exception_max_ttl,
                                           false,
                                           hc);

  // Initialise the SasService, to read the SAS config to pass into SAS::Init
  SasService* sas_service = new SasService(options.sas_system_name, "memento", false);

  // Ensure our random numbers are unpredictable.
  unsigned int seed;
  seed = time(NULL) ^ getpid();
  srand(seed);

  // Create a DNS resolver.
  int af = AF_INET;
  struct in6_addr dummy_addr;
  if (inet_pton(AF_INET6, options.local_host.c_str(), &dummy_addr) == 1)
  {
    TRC_DEBUG("Local host is an IPv6 address");
    af = AF_INET6;
  }

  DnsCachedResolver* dns_resolver = new DnsCachedResolver("127.0.0.1");

  // Create alarm and communication monitor objects for the conditions
  // reported by memento.
  AlarmManager* alarm_manager = new AlarmManager();
  CommunicationMonitor* astaire_comm_monitor = new CommunicationMonitor(new Alarm(alarm_manager,
                                                                                  "memento",
                                                                                  AlarmDef::MEMENTO_ASTAIRE_COMM_ERROR,
                                                                                  AlarmDef::CRITICAL),
                                                                        "Memento",
                                                                        "Astaire");
  CommunicationMonitor* hs_comm_monitor = new CommunicationMonitor(new Alarm(alarm_manager,
                                                                             "memento",
                                                                             AlarmDef::MEMENTO_HOMESTEAD_COMM_ERROR,
                                                                             AlarmDef::CRITICAL),
                                                                   "Memento",
                                                                   "Homestead");
  CommunicationMonitor* cass_comm_monitor = new CommunicationMonitor(new Alarm(alarm_manager,
                                                                               "memento",
                                                                               AlarmDef::MEMENTO_CASSANDRA_COMM_ERROR,
                                                                               AlarmDef::CRITICAL),
                                                                     "Memento",
                                                                     "Cassandra");

  astaire_resolver = new AstaireResolver(dns_resolver,
                                         af,
                                         options.astaire_blacklist_duration);

  // Default the astaire hostname to the loopback IP
  if (options.astaire == "")
  {
    if (af == AF_INET6)
    {
      options.astaire = "[::1]";
    }
    else
    {
      options.astaire = "127.0.0.1";
    }
  }

  memcached_store = (Store*)new TopologyNeutralMemcachedStore(options.astaire,
                                                              astaire_resolver,
                                                              false,
                                                              astaire_comm_monitor);

  AuthStore::SerializerDeserializer* serializer;
  std::vector<AuthStore::SerializerDeserializer*> deserializers;

  if (options.memcached_write_format == MemcachedWriteFormat::JSON)
  {
    serializer = new AuthStore::JsonSerializerDeserializer();
  }
  else
  {
    serializer = new AuthStore::BinarySerializerDeserializer();
  }

  deserializers.push_back(new AuthStore::JsonSerializerDeserializer());
  deserializers.push_back(new AuthStore::BinarySerializerDeserializer());

  AuthStore* auth_store = new AuthStore(memcached_store,
                                        serializer,
                                        deserializers,
                                        options.digest_timeout);

  LoadMonitor* load_monitor = new LoadMonitor(options.target_latency_us,
                                              options.max_tokens,
                                              options.init_token_rate,
                                              options.min_token_rate,
                                              options.max_token_rate);

  LastValueCache* stats_aggregator = new MementoLVC();

  // Create a HTTP specific resolver.
  HttpResolver* http_resolver = new HttpResolver(dns_resolver,
                                                 af,
                                                 options.http_blacklist_duration);

  HttpClient* http_client = new HttpClient(false,
                                           http_resolver,
                                           nullptr,
                                           load_monitor,
                                           SASEvent::HttpLogLevel::PROTOCOL,
                                           hs_comm_monitor);

  HttpConnection* http_connection = new HttpConnection(options.homestead_http_name,
                                                       http_client);

  HomesteadConnection* homestead_conn = new HomesteadConnection(http_connection);

  // Default to a 30s blacklist/graylist duration and port 9160
  CassandraResolver* cass_resolver = new CassandraResolver(dns_resolver,
                                                           af,
                                                           30,
                                                           30,
                                                           9160);
  // Default the cassandra hostname to the loopback IP
  if (options.cassandra == "")
  {
    if (af == AF_INET6)
    {
      options.cassandra = "[::1]";
    }
    else
    {
      options.cassandra = "127.0.0.1";
    }
  }

  // Create and start the call list store.
  CallListStore::Store* call_list_store = new CallListStore::Store();
  call_list_store->configure_connection(options.cassandra, 9160, cass_comm_monitor, cass_resolver);

  // Test Cassandra connectivity.
  CassandraStore::ResultCode store_rc = call_list_store->connection_test();

  if (store_rc == CassandraStore::OK)
  {
    // Store can connect to Cassandra, so start it.
    store_rc = call_list_store->start();
  }

  if (store_rc != CassandraStore::OK)
  {
    TRC_ERROR("Unable to create call list store (RC = %d)", store_rc);
    exit(3);
  }

  HttpStackUtils::SimpleStatsManager stats_manager(stats_aggregator);
  HttpStack* http_stack = new HttpStack(options.http_threads,
                                        exception_handler,
                                        access_logger,
                                        load_monitor,
                                        &stats_manager);

  CallListTask::Config call_list_config(auth_store, homestead_conn, call_list_store, options.home_domain, stats_aggregator, hc, options.api_key);

  MementoSasLogger sas_logger;
  HttpStackUtils::PingHandler ping_handler;
  HttpStackUtils::SpawningHandler<CallListTask, CallListTask::Config> call_list_handler(&call_list_config, &sas_logger);
  HttpStackUtils::HandlerThreadPool pool(options.http_worker_threads, exception_handler);

  try
  {
    http_stack->initialize();
    http_stack->bind_tcp_socket(options.http_address,
                                options.http_port);
    http_stack->register_handler("^/ping$", &ping_handler);
    http_stack->register_handler("^/org.projectclearwater.call-list/users/[^/]*/call-list.xml$",
                                    pool.wrap(&call_list_handler));
    http_stack->start();
  }
  catch (HttpStack::Exception& e)
  {
    TRC_ERROR("Failed to initialize HttpStack stack - function %s, rc %d", e._func, e._rc);
    exit(2);
  }

  TRC_STATUS("Start-up complete - wait for termination signal");
  sem_wait(&term_sem);
  TRC_STATUS("Termination signal received - terminating");

  try
  {
    http_stack->stop();
    http_stack->wait_stopped();
  }
  catch (HttpStack::Exception& e)
  {
    TRC_ERROR("Failed to stop HttpStack stack - function %s, rc %d", e._func, e._rc);
  }

  call_list_store->stop();
  call_list_store->wait_stopped();

  hc->stop_thread();

  delete homestead_conn; homestead_conn = NULL;
  delete http_connection; http_connection = NULL;
  delete http_client; http_client = NULL;
  delete call_list_store; call_list_store = NULL;
  delete http_resolver; http_resolver = NULL;
  delete cass_resolver; cass_resolver = NULL;
  delete dns_resolver; dns_resolver = NULL;
  delete load_monitor; load_monitor = NULL;
  delete auth_store; auth_store = NULL;
  delete call_list_store; call_list_store = NULL;
  delete astaire_resolver; astaire_resolver = NULL;
  delete memcached_store; memcached_store = NULL;
  delete exception_handler; exception_handler = NULL;
  delete hc; hc = NULL;
  delete http_stack; http_stack = NULL;

  delete astaire_comm_monitor; astaire_comm_monitor = NULL;
  delete hs_comm_monitor; hs_comm_monitor = NULL;
  delete cass_comm_monitor; cass_comm_monitor = NULL;
  delete alarm_manager; alarm_manager = NULL;

  delete sas_service; sas_service = NULL;

  signal(SIGTERM, SIG_DFL);
  sem_destroy(&term_sem);
}
Example #26
0
int main(int argc, char**argv)
{
  CommunicationMonitor* hss_comm_monitor = NULL;
  CommunicationMonitor* cassandra_comm_monitor = NULL;

  // Set up our exception signal handler for asserts and segfaults.
  signal(SIGABRT, signal_handler);
  signal(SIGSEGV, signal_handler);

  sem_init(&term_sem, 0, 0);
  signal(SIGTERM, terminate_handler);

  struct options options;
  options.local_host = "127.0.0.1";
  options.home_domain = "dest-realm.unknown";
  options.diameter_conf = "homestead.conf";
  options.dns_servers.push_back("127.0.0.1");
  options.http_address = "0.0.0.0";
  options.http_port = 8888;
  options.http_threads = 1;
  options.cache_threads = 10;
  options.cassandra = "localhost";
  options.dest_realm = "";
  options.dest_host = "dest-host.unknown";
  options.max_peers = 2;
  options.server_name = "sip:server-name.unknown";
  options.scheme_unknown = "Unknown";
  options.scheme_digest = "SIP Digest";
  options.scheme_aka = "Digest-AKAv1-MD5";
  options.access_log_enabled = false;
  options.impu_cache_ttl = 0;
  options.hss_reregistration_time = 1800;
  options.sprout_http_name = "sprout-http-name.unknown";
  options.log_to_file = false;
  options.log_level = 0;
  options.sas_server = "0.0.0.0";
  options.sas_system_name = "";
  options.diameter_timeout_ms = 200;
  options.alarms_enabled = false;
  options.target_latency_us = 100000;
  options.max_tokens = 20;
  options.init_token_rate = 100.0;
  options.min_token_rate = 10.0;
  options.exception_max_ttl = 600;
  options.http_blacklist_duration = HttpResolver::DEFAULT_BLACKLIST_DURATION;
  options.diameter_blacklist_duration = DiameterResolver::DEFAULT_BLACKLIST_DURATION;

  boost::filesystem::path p = argv[0];
  // Copy the filename to a string so that we can be sure of its lifespan -
  // the value passed to openlog must be valid for the duration of the program.
  std::string filename = p.filename().c_str();
  openlog(filename.c_str(), PDLOG_PID, PDLOG_LOCAL6);
  CL_HOMESTEAD_STARTED.log();

  if (init_logging_options(argc, argv, options) != 0)
  {
    closelog();
    return 1;
  }

  Log::setLoggingLevel(options.log_level);

  if ((options.log_to_file) && (options.log_directory != ""))
  {
    // Work out the program name from argv[0], stripping anything before the final slash.
    char* prog_name = argv[0];
    char* slash_ptr = rindex(argv[0], '/');
    if (slash_ptr != NULL)
    {
      prog_name = slash_ptr + 1;
    }
    Log::setLogger(new Logger(options.log_directory, prog_name));
  }

  TRC_STATUS("Log level set to %d", options.log_level);

  std::stringstream options_ss;
  for (int ii = 0; ii < argc; ii++)
  {
    options_ss << argv[ii];
    options_ss << " ";
  }
  std::string options_str = "Command-line options were: " + options_ss.str();

  TRC_INFO(options_str.c_str());

  if (init_options(argc, argv, options) != 0)
  {
    closelog();
    return 1;
  }

  AccessLogger* access_logger = NULL;
  if (options.access_log_enabled)
  {
    TRC_STATUS("Access logging enabled to %s", options.access_log_directory.c_str());
    access_logger = new AccessLogger(options.access_log_directory);
  }

  // Create a DNS resolver and a SIP specific resolver.
  int af = AF_INET;
  struct in6_addr dummy_addr;
  if (inet_pton(AF_INET6, options.local_host.c_str(), &dummy_addr) == 1)
  {
    TRC_DEBUG("Local host is an IPv6 address");
    af = AF_INET6;
  }

  SAS::init(options.sas_system_name,
            "homestead",
            SASEvent::CURRENT_RESOURCE_BUNDLE,
            options.sas_server,
            sas_write);

  // Set up the statistics (Homestead specific and Diameter)
  const static std::string known_stats[] = {
    "H_latency_us",
    "H_hss_latency_us",
    "H_hss_digest_latency_us",
    "H_hss_subscription_latency_us",
    "H_cache_latency_us",
    "H_incoming_requests",
    "H_rejected_overload",
    "H_diameter_invalid_dest_host",
    "H_diameter_invalid_dest_realm",
  };

  const static int num_known_stats = sizeof(known_stats) / sizeof(std::string);
  LastValueCache* lvc = new LastValueCache(num_known_stats,
                                           known_stats,
                                           "homestead",
                                           1000);
  StatisticsManager* stats_manager = new StatisticsManager(lvc);
  StatisticCounter* realm_counter =  new StatisticCounter("H_diameter_invalid_dest_realm",
                                                          lvc);
  StatisticCounter* host_counter = new StatisticCounter("H_diameter_invalid_dest_host",
                                                        lvc);

  if (options.alarms_enabled)
  {
    // Create Homesteads's alarm objects. Note that the alarm identifier strings must match those
    // in the alarm definition JSON file exactly.

    hss_comm_monitor = new CommunicationMonitor(new Alarm("homestead", AlarmDef::HOMESTEAD_HSS_COMM_ERROR,
                                                                       AlarmDef::CRITICAL));

    cassandra_comm_monitor = new CommunicationMonitor(new Alarm("homestead", AlarmDef::HOMESTEAD_CASSANDRA_COMM_ERROR,
                                                                             AlarmDef::CRITICAL));

    // Start the alarm request agent
    AlarmReqAgent::get_instance().start();
    AlarmState::clear_all("homestead");
  }

  // Create an exception handler. The exception handler doesn't need
  // to quiesce the process before killing it.
  HealthChecker* hc = new HealthChecker();
  pthread_t health_check_thread;
  pthread_create(&health_check_thread,
                 NULL,
                 &HealthChecker::static_main_thread_function,
                 (void*)hc);
  exception_handler = new ExceptionHandler(options.exception_max_ttl,
                                           false,
                                           hc);

  LoadMonitor* load_monitor = new LoadMonitor(options.target_latency_us,
                                              options.max_tokens,
                                              options.init_token_rate,
                                              options.min_token_rate);
  DnsCachedResolver* dns_resolver = new DnsCachedResolver(options.dns_servers);
  HttpResolver* http_resolver = new HttpResolver(dns_resolver,
                                                 af,
                                                 options.http_blacklist_duration);

  Cache* cache = Cache::get_instance();
  cache->configure_connection(options.cassandra,
                              9160,
                              cassandra_comm_monitor);
  cache->configure_workers(exception_handler,
                           options.cache_threads,
                           0);

  // Test the connection to Cassandra before starting the store.
  CassandraStore::ResultCode rc = cache->connection_test();

  if (rc == CassandraStore::OK)
  {
    // Cassandra connection is good, so start the store.
    rc = cache->start();
  }

  if (rc != CassandraStore::OK)
  {
    CL_HOMESTEAD_CASSANDRA_CACHE_INIT_FAIL.log(rc);
    closelog();
    TRC_ERROR("Failed to initialize the Cassandra cache with error code %d.", rc);
    TRC_STATUS("Homestead is shutting down");
    exit(2);
  }

  HttpConnection* http = new HttpConnection(options.sprout_http_name,
                                            false,
                                            http_resolver,
                                            SASEvent::HttpLogLevel::PROTOCOL,
                                            NULL);
  SproutConnection* sprout_conn = new SproutConnection(http);

  RegistrationTerminationTask::Config* rtr_config = NULL;
  PushProfileTask::Config* ppr_config = NULL;
  Diameter::SpawningHandler<RegistrationTerminationTask, RegistrationTerminationTask::Config>* rtr_task = NULL;
  Diameter::SpawningHandler<PushProfileTask, PushProfileTask::Config>* ppr_task = NULL;
  Cx::Dictionary* dict = NULL;

  Diameter::Stack* diameter_stack = Diameter::Stack::get_instance();

  try
  {
    diameter_stack->initialize();
    diameter_stack->configure(options.diameter_conf, 
                              exception_handler, 
                              hss_comm_monitor,
                              realm_counter,
                              host_counter);
    dict = new Cx::Dictionary();

    rtr_config = new RegistrationTerminationTask::Config(cache, dict, sprout_conn, options.hss_reregistration_time);
    ppr_config = new PushProfileTask::Config(cache, dict, options.impu_cache_ttl, options.hss_reregistration_time);
    rtr_task = new Diameter::SpawningHandler<RegistrationTerminationTask, RegistrationTerminationTask::Config>(dict, rtr_config);
    ppr_task = new Diameter::SpawningHandler<PushProfileTask, PushProfileTask::Config>(dict, ppr_config);

    diameter_stack->advertize_application(Diameter::Dictionary::Application::AUTH,
                                          dict->TGPP, dict->CX);
    diameter_stack->register_handler(dict->CX, dict->REGISTRATION_TERMINATION_REQUEST, rtr_task);
    diameter_stack->register_handler(dict->CX, dict->PUSH_PROFILE_REQUEST, ppr_task);
    diameter_stack->register_fallback_handler(dict->CX);
    diameter_stack->start();
  }
  catch (Diameter::Stack::Exception& e)
  {
    CL_HOMESTEAD_DIAMETER_INIT_FAIL.log(e._func, e._rc);
    closelog();
    TRC_ERROR("Failed to initialize Diameter stack - function %s, rc %d", e._func, e._rc);
    TRC_STATUS("Homestead is shutting down");
    exit(2);
  }

  HttpStack* http_stack = HttpStack::get_instance();
  HssCacheTask::configure_diameter(diameter_stack,
                                   options.dest_realm.empty() ? options.home_domain : options.dest_realm,
                                   options.dest_host == "0.0.0.0" ? "" : options.dest_host,
                                   options.server_name,
                                   dict);
  HssCacheTask::configure_cache(cache);
  HssCacheTask::configure_health_checker(hc);
  HssCacheTask::configure_stats(stats_manager);

  // We should only query the cache for AV information if there is no HSS.  If there is an HSS, we
  // should always hit it.  If there is not, the AV information must have been provisioned in the
  // "cache" (which becomes persistent).
  bool hss_configured = !(options.dest_realm.empty() && (options.dest_host.empty() || options.dest_host == "0.0.0.0"));

  ImpiTask::Config impi_handler_config(hss_configured,
                                       options.impu_cache_ttl,
                                       options.scheme_unknown,
                                       options.scheme_digest,
                                       options.scheme_aka,
                                       options.diameter_timeout_ms);
  ImpiRegistrationStatusTask::Config registration_status_handler_config(hss_configured, options.diameter_timeout_ms);
  ImpuLocationInfoTask::Config location_info_handler_config(hss_configured, options.diameter_timeout_ms);
  ImpuRegDataTask::Config impu_handler_config(hss_configured, options.hss_reregistration_time, options.diameter_timeout_ms);
  ImpuIMSSubscriptionTask::Config impu_handler_config_old(hss_configured, options.hss_reregistration_time, options.diameter_timeout_ms);

  HttpStackUtils::PingHandler ping_handler;
  HttpStackUtils::SpawningHandler<ImpiDigestTask, ImpiTask::Config> impi_digest_handler(&impi_handler_config);
  HttpStackUtils::SpawningHandler<ImpiAvTask, ImpiTask::Config> impi_av_handler(&impi_handler_config);
  HttpStackUtils::SpawningHandler<ImpiRegistrationStatusTask, ImpiRegistrationStatusTask::Config> impi_reg_status_handler(&registration_status_handler_config);
  HttpStackUtils::SpawningHandler<ImpuLocationInfoTask, ImpuLocationInfoTask::Config> impu_loc_info_handler(&location_info_handler_config);
  HttpStackUtils::SpawningHandler<ImpuRegDataTask, ImpuRegDataTask::Config> impu_reg_data_handler(&impu_handler_config);
  HttpStackUtils::SpawningHandler<ImpuIMSSubscriptionTask, ImpuIMSSubscriptionTask::Config> impu_ims_sub_handler(&impu_handler_config_old);

  try
  {
    http_stack->initialize();
    http_stack->configure(options.http_address,
                          options.http_port,
                          options.http_threads,
                          exception_handler,
                          access_logger,
                          load_monitor,
                          stats_manager);
    http_stack->register_handler("^/ping$",
                                    &ping_handler);
    http_stack->register_handler("^/impi/[^/]*/digest$",
                                    &impi_digest_handler);
    http_stack->register_handler("^/impi/[^/]*/av",
                                    &impi_av_handler);
    http_stack->register_handler("^/impi/[^/]*/registration-status$",
                                    &impi_reg_status_handler);
    http_stack->register_handler("^/impu/[^/]*/location$",
                                    &impu_loc_info_handler);
    http_stack->register_handler("^/impu/[^/]*/reg-data$",
                                    &impu_reg_data_handler);
    http_stack->register_handler("^/impu/",
                                    &impu_ims_sub_handler);
    http_stack->start();
  }
  catch (HttpStack::Exception& e)
  {
    CL_HOMESTEAD_HTTP_INIT_FAIL.log(e._func, e._rc);
    closelog();
    TRC_ERROR("Failed to initialize HttpStack stack - function %s, rc %d", e._func, e._rc);
    TRC_STATUS("Homestead is shutting down");
    exit(2);
  }

  DiameterResolver* diameter_resolver = NULL;
  RealmManager* realm_manager = NULL;
  
  if (hss_configured)
  {
    diameter_resolver = new DiameterResolver(dns_resolver,
                                             af,
                                             options.diameter_blacklist_duration);
    realm_manager = new RealmManager(diameter_stack,
                                     options.dest_realm,
                                     options.dest_host,
                                     options.max_peers,
                                     diameter_resolver);
    realm_manager->start();
  }

  TRC_STATUS("Start-up complete - wait for termination signal");
  sem_wait(&term_sem);
  TRC_STATUS("Termination signal received - terminating");
  CL_HOMESTEAD_ENDED.log();

  try
  {
    http_stack->stop();
    http_stack->wait_stopped();
  }
  catch (HttpStack::Exception& e)
  {
    CL_HOMESTEAD_HTTP_STOP_FAIL.log(e._func, e._rc);
    TRC_ERROR("Failed to stop HttpStack stack - function %s, rc %d", e._func, e._rc);
  }

  cache->stop();
  cache->wait_stopped();

  try
  {
    diameter_stack->stop();
    diameter_stack->wait_stopped();
  }
  catch (Diameter::Stack::Exception& e)
  {
    CL_HOMESTEAD_DIAMETER_STOP_FAIL.log(e._func, e._rc);
    TRC_ERROR("Failed to stop Diameter stack - function %s, rc %d", e._func, e._rc);
  }
  delete dict; dict = NULL;
  delete ppr_config; ppr_config = NULL;
  delete rtr_config; rtr_config = NULL;
  delete ppr_task; ppr_task = NULL;
  delete rtr_task; rtr_task = NULL;

  delete sprout_conn; sprout_conn = NULL;

  if (hss_configured)
  {
    realm_manager->stop();
    delete realm_manager; realm_manager = NULL;
    delete diameter_resolver; diameter_resolver = NULL;
    delete dns_resolver; dns_resolver = NULL;
  }

  delete realm_counter; realm_counter = NULL;
  delete host_counter; host_counter = NULL;
  delete stats_manager; stats_manager = NULL;
  delete lvc; lvc = NULL;

  hc->terminate();
  pthread_join(health_check_thread, NULL);
  delete hc; hc = NULL;
  delete exception_handler; exception_handler = NULL;

  delete load_monitor; load_monitor = NULL;

  SAS::term();
  closelog();

  if (options.alarms_enabled)
  {
    // Stop the alarm request agent
    AlarmReqAgent::get_instance().stop();

    // Delete Homestead's alarm objects
    delete hss_comm_monitor;
    delete cassandra_comm_monitor;
  }

  signal(SIGTERM, SIG_DFL);
  sem_destroy(&term_sem);
}
Example #27
0
JSONEnumService::JSONEnumService(std::string configuration)
{
  // Check whether the file exists.
  struct stat s;
  if ((stat(configuration.c_str(), &s) != 0) &&
      (errno == ENOENT))
  {
    TRC_STATUS("No ENUM configuration (file %s does not exist)",
               configuration.c_str());
    CL_SPROUT_ENUM_FILE_MISSING.log(configuration.c_str());
    return;
  }

  TRC_STATUS("Loading ENUM configuration from %s", configuration.c_str());

  // Read from the file
  std::ifstream fs(configuration.c_str());
  std::string enum_str((std::istreambuf_iterator<char>(fs)),
                        std::istreambuf_iterator<char>());

  if (enum_str == "")
  {
    // LCOV_EXCL_START
    TRC_ERROR("Failed to read ENUM configuration data from %s",
              configuration.c_str());
    CL_SPROUT_ENUM_FILE_EMPTY.log(configuration.c_str());
    return;
    // LCOV_EXCL_STOP
  }

  // Now parse the document
  rapidjson::Document doc;
  doc.Parse<0>(enum_str.c_str());

  if (doc.HasParseError())
  {
    TRC_ERROR("Failed to read ENUM configuration data: %s\nError: %s",
              enum_str.c_str(),
              rapidjson::GetParseError_En(doc.GetParseError()));
    CL_SPROUT_ENUM_FILE_INVALID.log(configuration.c_str());
    return;
  }

  try
  {
    JSON_ASSERT_CONTAINS(doc, "number_blocks");
    JSON_ASSERT_ARRAY(doc["number_blocks"]);
    const rapidjson::Value& nb_arr = doc["number_blocks"];

    for (rapidjson::Value::ConstValueIterator nb_it = nb_arr.Begin();
         nb_it != nb_arr.End();
         ++nb_it)
    {
      try
      {
        std::string prefix; 
        JSON_GET_STRING_MEMBER(*nb_it, "prefix", prefix);
        std::string regex;
        JSON_GET_STRING_MEMBER(*nb_it, "regex", regex);

        // Entry is well-formed, so add it.
        TRC_DEBUG("Found valid number prefix block %s", prefix.c_str());
        NumberPrefix *pfix = new NumberPrefix;
        pfix->prefix = prefix;

        if (parse_regex_replace(regex, pfix->match, pfix->replace))
        {
          _number_prefixes.push_back(pfix);
          TRC_STATUS("  Adding number prefix %s, regex=%s",
                     pfix->prefix.c_str(), regex.c_str());
        }
        else
        {
          TRC_WARNING("Badly formed regular expression in ENUM number block %s",
                      regex.c_str());
          delete pfix;
        }
      }
      catch (JsonFormatError err)
      {
        // Badly formed number block.
        TRC_WARNING("Badly formed ENUM number block (hit error at %s:%d)",
                    err._file, err._line);
        CL_SPROUT_ENUM_FILE_INVALID.log(configuration.c_str());
      }
    }
  }
  catch (JsonFormatError err)
  {
    TRC_ERROR("Badly formed ENUM configuration data - missing number_blocks object");
    CL_SPROUT_ENUM_FILE_INVALID.log(configuration.c_str());
  }
}
Example #28
0
bool MemcachedConfigFileReader::read_config(MemcachedConfig& config)
{
  bool seen_servers = false;
  config.servers.clear();
  config.new_servers.clear();
  config.tombstone_lifetime = DEFAULT_TOMBSTONE_LIFETIME;
  config.filename = _filename;

  std::ifstream f(_filename);

  if (f.is_open() && f.good())
  {
    TRC_STATUS("Reloading memcached configuration from '%s'", _filename.c_str());

    while (f.good())
    {
      std::string line;
      getline(f, line);
      line = Utils::strip_whitespace(line);

      TRC_DEBUG("Got line: %s", line.c_str());

      if ((line.length() > 0) && (line[0] != '#'))
      {
        // Read a non-blank line.
        std::vector<std::string> tokens;
        Utils::split_string(line, 
                            '=', 
                            tokens, 
                            0, 
                            true);

        std::string key;
        std::string value;
        if (tokens.size() == 1)
        {
          key = tokens[0];
          value = "";
        }
        else if (tokens.size() == 2)
        {
          key = tokens[0];
          value = tokens[1];
        }
        else
        {
          TRC_ERROR("Malformed config file (got bad line: '%s')",
                    line.c_str());
          return false;
        }

        TRC_STATUS(" %s=%s", key.c_str(), value.c_str());

        if (key == "servers")
        {
          // Found line defining servers.
          Utils::split_string(value, ',', config.servers, 0, true);
          seen_servers = true;
        }
        else if (key == "new_servers")
        {
          // Found line defining new servers.
          Utils::split_string(value, ',', config.new_servers, 0, true);
        }
        else
        {
          TRC_ERROR("Malformed config file (got bad line: '%s')",
                    line.c_str());
          return false;
        }
      }
    }
  }
  else
  {
    TRC_ERROR("Failed to open '%s'", _filename.c_str());
    return false;
  }

  return seen_servers;
}