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(®istration_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); }