/* Copyright (C) 2005-2009 Michel de Boer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include "address_book.h" #include "call_history.h" #include "events.h" #include "line.h" #include "listener.h" #include "log.h" #include "phone.h" #include "protocol.h" #include "sender.h" #include "sys_settings.h" #include "transaction_mgr.h" #include "translator.h" #include "user.h" #include "userintf.h" #include "util.h" #include "sockets/connection_table.h" #include "sockets/interfaces.h" #include "sockets/socket.h" #include "threads/thread.h" #include "utils/mime_database.h" #include "audits/memman.h" using namespace std; using namespace utils; // Class to initialize the random generator before objects of // other classes are created. Initializing just from the main function // is too late. class t_init_rand { public: t_init_rand(); }; t_init_rand::t_init_rand() { srand(time(NULL)); } // Memory manager for memory leak tracing t_memman *memman; // Initialize random generator t_init_rand init_rand; // Indicates if application is ending (because user pressed Quit) bool end_app; // Language translator t_translator *translator = NULL; // IP address on which the phone is running string user_host; // Local host name string local_hostname; // SIP UDP socket for sending and receiving signaling t_socket_udp *sip_socket; // SIP TCP socket for sending and receiving signaling t_socket_tcp *sip_socket_tcp; // SIP connection table for connection oriented transport t_connection_table *connection_table; // Event queue that is handled by the transaction manager thread // The following threads write to this queue // - UDP listener // - transaction layer // - timekeeper t_event_queue *evq_trans_mgr; // Event queue that is handled by the sender thread // The following threads write to this queue: // - phone UAS // - phone UAC // - transaction manager t_event_queue *evq_sender; // Event queue that is handled by the transaction layer thread // The following threads write to this queue // - transaction manager // - timekeeper t_event_queue *evq_trans_layer; // Event queue that is handled by the phone timekeeper thread // The following threads write into this queue // - phone UAS // - phone UAC // - transaction manager t_event_queue *evq_timekeeper; // The timekeeper t_timekeeper *timekeeper; // The transaction manager t_transaction_mgr *transaction_mgr; // The phone t_phone *phone; // User interface t_userintf *ui; // Log file t_log *log_file; // System config t_sys_settings *sys_config; // Call history t_call_history *call_history; // Local address book t_address_book *ab_local; // Mime database t_mime_database *mime_database; // If a port number is passed by the user on the command line, then // that port number overrides the port from the system settings. unsigned short g_override_sip_port = 0; unsigned short g_override_rtp_port = 0; // Indicates if LinuxThreads or NPTL is active. bool threading_is_LinuxThreads; int main(int argc, char *argv[]) { string error_msg; end_app = false; memman = new t_memman(); MEMMAN_NEW(memman); translator = new t_translator(); MEMMAN_NEW(translator); connection_table = new t_connection_table(); MEMMAN_NEW(connection_table); evq_trans_mgr = new t_event_queue(); MEMMAN_NEW(evq_trans_mgr); evq_sender = new t_event_queue(); MEMMAN_NEW(evq_sender); evq_trans_layer = new t_event_queue(); MEMMAN_NEW(evq_trans_layer); evq_timekeeper = new t_event_queue(); MEMMAN_NEW(evq_timekeeper); timekeeper = new t_timekeeper(); MEMMAN_NEW(timekeeper); transaction_mgr = new t_transaction_mgr(); MEMMAN_NEW(transaction_mgr); phone = new t_phone(); MEMMAN_NEW(phone); sys_config = new t_sys_settings(); MEMMAN_NEW(sys_config); ui = new t_userintf(phone); MEMMAN_NEW(ui); // Check requirements on environment if (!sys_config->check_environment(error_msg)) { // Environment is not good ui->cb_show_msg(error_msg, MSG_CRITICAL); exit(1); } // Read system configuration if (!sys_config->read_config(error_msg)) { ui->cb_show_msg(error_msg, MSG_CRITICAL); exit(1); } // Get default values from system configuration list config_files; list start_user_profiles = sys_config->get_start_user_profiles(); for (list::iterator i = start_user_profiles.begin(); i != start_user_profiles.end(); i++) { string config_file = *i; config_file += USER_FILE_EXT; config_files.push_back(config_file); } #if 0 // DEPRECATED if (user_host.empty()) { string ip; if (exists_interface(sys_config->get_start_user_host())) { user_host = sys_config->get_start_user_host(); } else if (exists_interface_dev(sys_config->get_start_user_nic(), ip)) { user_host = ip; } } #endif user_host = AUTO_IP4_ADDRESS; local_hostname = get_local_hostname(); // Create a lock file to guarantee that the application // runs only once. bool already_running; if (!sys_config->create_lock_file(false, error_msg, already_running)) { ui->cb_show_msg(error_msg, MSG_CRITICAL); exit(1); } log_file = new t_log(); MEMMAN_NEW(log_file); call_history = new t_call_history(); MEMMAN_NEW(call_history); // Determine threading implementation threading_is_LinuxThreads = t_thread::is_LinuxThreads(); if (threading_is_LinuxThreads) { log_file->write_report("Threading implementation is LinuxThreads.", "::main", LOG_NORMAL, LOG_INFO); } else { log_file->write_report("Threading implementation is NPTL.", "::main", LOG_NORMAL, LOG_INFO); } // Take default user profile if there are is no default is sys settings if (config_files.empty()) config_files.push_back(USER_CONFIG_FILE); if (argc >= 2) { config_files.clear(); for (int i = 1; i < argc && i < 2; i++) { config_files.push_back(argv[i]); } } for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--sip-port") == 0) { if (i < argc - 1) { i++; sys_config->set_override_sip_port(atoi(argv[i])); } else { cout << argv[0] << ": "; cout << "Port missing for option '--sip-port'\n"; } } else if (strcmp(argv[i], "--rtp-port") == 0) { if (i < argc - 1) { i++; sys_config->set_override_rtp_port(atoi(argv[i])); } else { cout << argv[0] << ": "; cout << "Port missing for option '--rtp-port'\n"; } } } // Activate users for (list::iterator i = config_files.begin(); i != config_files.end(); i++) { t_user *user_config = new t_user(); MEMMAN_NEW(user_config); if (!user_config->read_config(*i, error_msg)) { ui->cb_show_msg(error_msg, MSG_CRITICAL); sys_config->delete_lock_file(); exit(1); } t_user *dup_user; if(!phone->add_phone_user(*user_config, &dup_user)) { error_msg = "The following profiles are both for user "; error_msg += user_config->get_name(); error_msg += '@'; error_msg += user_config->get_domain(); error_msg += ":\n\n"; error_msg += user_config->get_profile_name(); error_msg += "\n"; error_msg += dup_user->get_profile_name(); error_msg += "\n\n"; error_msg += "You can only run multiple profiles "; error_msg += "for different users."; ui->cb_show_msg(error_msg, MSG_CRITICAL); sys_config->delete_lock_file(); exit(1); } MEMMAN_DELETE(user_config); delete user_config; } // Read call history if (!call_history->load(error_msg)) { log_file->write_report(error_msg, "::main", LOG_NORMAL, LOG_WARNING); } // Create local address book ab_local = new t_address_book(); MEMMAN_NEW(ab_local); // Read local address book if (!ab_local->load(error_msg)) { log_file->write_report(error_msg, "::main", LOG_NORMAL, LOG_WARNING); ui->cb_show_msg(error_msg, MSG_WARNING); } // Create mime database mime_database = new t_mime_database(); MEMMAN_NEW(mime_database); if (!mime_database->load(error_msg)) { log_file->write_report(error_msg, "::main", LOG_NORMAL, LOG_WARNING); } // Initialize RTP port settings. phone->init_rtp_ports(); // Open UDP socket for SIP signaling try { sip_socket = new t_socket_udp(sys_config->get_sip_port()); MEMMAN_NEW(sip_socket); if (sip_socket->enable_icmp()) { log_file->write_report("ICMP processing enabled.", "::main"); } else { log_file->write_report("ICMP processing disabled.", "::main"); } } catch (int err) { string msg("Failed to create a UDP socket (SIP) on port "); msg += int2str(sys_config->get_sip_port()); msg += "\n"; msg += get_error_str(err); log_file->write_report(msg, "::main", LOG_NORMAL, LOG_CRITICAL); ui->cb_show_msg(msg, MSG_CRITICAL); sys_config->delete_lock_file(); exit(1); } // Open TCP socket for SIP signaling try { sip_socket_tcp = new t_socket_tcp(sys_config->get_sip_port()); MEMMAN_NEW(sip_socket_tcp); } catch (int err) { string msg("Failed to create a TCP socket (SIP) on port "); msg += int2str(sys_config->get_sip_port()); msg += "\n"; msg += get_error_str(err); log_file->write_report(msg, "::main", LOG_NORMAL, LOG_CRITICAL); ui->cb_show_msg(msg, MSG_CRITICAL); sys_config->delete_lock_file(); exit(1); } #if 0 // DEPRECATED // Pick network interface if (user_host.empty()) { user_host = ui->select_network_intf(); if (user_host.empty()) { sys_config->delete_lock_file(); exit(1); } } #endif // Discover NAT type if STUN is enabled list msg_list; if (!phone->stun_discover_nat(msg_list)) { for (list::iterator i = msg_list.begin(); i != msg_list.end(); i++) { ui->cb_show_msg(*i, MSG_WARNING); } } // Dedicated thread will catch SIGALRM, SIGINT, SIGTERM, SIGCHLD signals, // therefore all threads must block these signals. Block now, then all // created threads will inherit the signal mask. // In LinuxThreads the sigwait does not work very well, so // in LinuxThreads a signal handler is used instead. if (!threading_is_LinuxThreads) { sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGALRM); sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGTERM); sigaddset(&sigset, SIGCHLD); sigprocmask(SIG_BLOCK, &sigset, NULL); } else { if (!phone->set_sighandler()) { string msg = "Failed to register signal handler."; log_file->write_report(msg, "::main", LOG_NORMAL, LOG_CRITICAL); ui->cb_show_msg(msg, MSG_CRITICAL); sys_config->delete_lock_file(); exit(1); } } // Ignore SIGPIPE so read from broken sockets will not cause // the process to terminate. (void)signal(SIGPIPE, SIG_IGN); // Create threads t_thread *thr_sender; t_thread *thr_tcp_sender; t_thread *thr_listen_udp; t_thread *thr_listen_data_tcp; t_thread *thr_listen_conn_tcp; t_thread *thr_conn_timeout_handler; t_thread *thr_timekeeper; t_thread *thr_alarm_catcher = NULL; t_thread *thr_sig_catcher = NULL; t_thread *thr_trans_mgr; t_thread *thr_phone_uas; try { // SIP sender thread thr_sender = new t_thread(sender_loop, NULL); MEMMAN_NEW(thr_sender); // SIP TCP sender thread thr_tcp_sender = new t_thread(tcp_sender_loop, NULL); MEMMAN_NEW(thr_tcp_sender); // UDP listener thread thr_listen_udp = new t_thread(listen_udp, NULL); MEMMAN_NEW(thr_listen_udp); // TCP data listener thread thr_listen_data_tcp = new t_thread(listen_for_data_tcp, NULL); MEMMAN_NEW(thr_listen_data_tcp); // TCP connection listener thread thr_listen_conn_tcp = new t_thread(listen_for_conn_requests_tcp, NULL); MEMMAN_NEW(thr_listen_conn_tcp); // Connection timeout handler thread thr_conn_timeout_handler = new t_thread(connection_timeout_main, NULL); MEMMAN_NEW(thr_conn_timeout_handler); // Timekeeper thread thr_timekeeper = new t_thread(timekeeper_main, NULL); MEMMAN_NEW(thr_timekeeper); if (!threading_is_LinuxThreads) { // Alarm catcher thread thr_alarm_catcher = new t_thread(timekeeper_sigwait, NULL); MEMMAN_NEW(thr_alarm_catcher); // Signal catcher thread thr_sig_catcher = new t_thread(phone_sigwait, NULL); MEMMAN_NEW(thr_sig_catcher); } // Transaction manager thread thr_trans_mgr = new t_thread(transaction_mgr_main, NULL); MEMMAN_NEW(thr_trans_mgr); // Phone thread (UAS) thr_phone_uas = new t_thread(phone_uas_main, NULL); MEMMAN_NEW(thr_phone_uas); } catch (int) { string msg = "Failed to create threads."; log_file->write_report(msg, "::main", LOG_NORMAL, LOG_CRITICAL); ui->cb_show_msg(msg, MSG_CRITICAL); sys_config->delete_lock_file(); exit(1); } // Validate sound devices if (!sys_config->exec_audio_validation(true, true, true, error_msg)) { ui->cb_show_msg(error_msg, MSG_WARNING); } try { ui->run(); } catch (string e) { string msg = "Exception: "; msg += e; log_file->write_report(msg, "::main", LOG_NORMAL, LOG_CRITICAL); ui->cb_show_msg(msg, MSG_CRITICAL); sys_config->delete_lock_file(); exit(1); } catch (...) { string msg = "Unknown exception"; log_file->write_report(msg, "::main", LOG_NORMAL, LOG_CRITICAL); ui->cb_show_msg(msg, MSG_CRITICAL); sys_config->delete_lock_file(); exit(1); } // Application is ending end_app = true; // Kill the threads getting receiving input from the outside world first, // so no new inputs come in during termination. thr_listen_udp->cancel(); thr_listen_udp->join(); thr_listen_conn_tcp->cancel(); thr_listen_conn_tcp->join(); connection_table->cancel_select(); thr_listen_data_tcp->join(); thr_conn_timeout_handler->join(); thr_tcp_sender->join(); evq_trans_layer->push_quit(); thr_phone_uas->join(); evq_trans_mgr->push_quit(); thr_trans_mgr->join(); if (!threading_is_LinuxThreads) { try { thr_sig_catcher->cancel(); } catch (int) { // Thread terminated already by itself } thr_sig_catcher->join(); thr_alarm_catcher->cancel(); thr_alarm_catcher->join(); } evq_timekeeper->push_quit(); thr_timekeeper->join(); evq_sender->push_quit(); thr_sender->join(); sys_config->remove_all_tmp_files(); MEMMAN_DELETE(thr_phone_uas); delete thr_phone_uas; MEMMAN_DELETE(thr_trans_mgr); delete thr_trans_mgr; MEMMAN_DELETE(thr_timekeeper); delete thr_timekeeper; MEMMAN_DELETE(thr_conn_timeout_handler); delete thr_conn_timeout_handler; if (!threading_is_LinuxThreads) { MEMMAN_DELETE(thr_sig_catcher); delete thr_sig_catcher; MEMMAN_DELETE(thr_alarm_catcher); delete thr_alarm_catcher; } MEMMAN_DELETE(thr_listen_udp); delete thr_listen_udp; MEMMAN_DELETE(thr_sender); delete thr_sender; MEMMAN_DELETE(thr_tcp_sender); delete thr_tcp_sender; MEMMAN_DELETE(thr_listen_data_tcp); delete thr_listen_data_tcp; MEMMAN_DELETE(thr_listen_conn_tcp); delete thr_listen_conn_tcp; MEMMAN_DELETE(mime_database); delete mime_database; MEMMAN_DELETE(ab_local); delete ab_local; MEMMAN_DELETE(call_history); delete call_history; MEMMAN_DELETE(ui); delete ui; ui = NULL; MEMMAN_DELETE(connection_table); delete connection_table; MEMMAN_DELETE(sip_socket_tcp); delete sip_socket_tcp; MEMMAN_DELETE(sip_socket); delete sip_socket; MEMMAN_DELETE(phone); delete phone; MEMMAN_DELETE(transaction_mgr); delete transaction_mgr; MEMMAN_DELETE(timekeeper); delete timekeeper; MEMMAN_DELETE(evq_trans_mgr); delete evq_trans_mgr; MEMMAN_DELETE(evq_sender); delete evq_sender; MEMMAN_DELETE(evq_trans_layer); delete evq_trans_layer; MEMMAN_DELETE(evq_timekeeper); delete evq_timekeeper; MEMMAN_DELETE(translator); delete translator; translator = NULL; // Report memory leaks // Report deletion of log_file and sys_config already to get // a correct report. MEMMAN_DELETE(sys_config); MEMMAN_DELETE(log_file); MEMMAN_DELETE(memman); MEMMAN_REPORT; delete log_file; delete memman; sys_config->delete_lock_file(); delete sys_config; }