Example #1
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);
}