/*
Copyright (C) 2005-2007 Michel de Boer <michel@twinklephone.com>
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "twinkle_config.h"
#ifdef HAVE_KDE
#include <kapplication.h>
#include <kcmdlineargs.h>
#endif
#include <qapplication.h>
#include <qtranslator.h>
#include <qmime.h>
#include <qprogressdialog.h>
#include <qtextcodec.h>
#include "mphoneform.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <unistd.h>
#include "address_book.h"
#include "address_finder.h"
#include "call_history.h"
#include "cmd_socket.h"
#include "events.h"
#include "listener.h"
#include "log.h"
#include "protocol.h"
#include "sender.h"
#include "transaction_mgr.h"
#include "user.h"
#include "util.h"
#include "phone.h"
#include "gui.h"
#include "qt_translator.h"
#include "command_args.h"
#include "sockets/interfaces.h"
#include "sockets/socket.h"
#include "threads/thread.h"
#include "audits/memman.h"
using namespace std;
// 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)); }
// Initialize random generator
t_init_rand init_rand;
// Language translator for the core of Twinkle
t_translator *translator = NULL;
// Indicates if application is ending (because user pressed Quit)
bool end_app;
// Memory manager for memory leak tracing
t_memman *memman;
// IP address on which the phone is running
string user_host;
// SIP socket for sending and receiving signaling
t_socket_udp *sip_socket;
// 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 UDP sender thread
// The following threads write to this queue:
// - phone UAS
// - phone UAC
// - transaction manager
t_event_queue *evq_sender_udp;
// 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;
/** Command arguments. */
t_command_args g_cmd_args;
// Thread id of main thread
pthread_t thread_id_main;
// Indicates if LinuxThreads or NPTL is active.
bool threading_is_LinuxThreads;
void parse_main_args(int argc, char **argv, bool &cli_mode, bool &override_lock_file,
list<string> &config_files)
{
cli_mode = false;
override_lock_file = false;
config_files.clear();
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
// Help
cout << "Usage: twinkle [options]\n\n";
cout << "Options:\n";
cout << " -c";
cout << "\t\tRun in command line interface (CLI) mode\n";
cout << endl;
cout << " --share <dir>";
cout << "\tSet the share directory.\n";
cout << endl;
cout << " -f <profile>";
cout << "\tStartup with a specific profile. You will not be requested\n";
cout << "\t\tto choose a profile at startup. The profiles that you created\n";
cout << "\t\tare the .cfg files in your .twinkle directory.\n";
cout << "\t\tYou may specify multiple profiles separated by spaces.\n";
cout << endl;
cout << " --force";
cout << "\tIf a lock file is detected at startup, then override it\n";
cout << "\t\tand startup.\n";
cout << endl;
cout << " -i <IP addr>";
cout << "\tIf you have multiple IP addresses on your computer,\n";
cout << "\t\tthen you can supply the IP address to use here.\n";
cout << endl;
cout << " --sip-port <port>\n";
cout << "\t\tPort for SIP UDP signalling.\n";
cout << "\t\tThis port overrides the port from the system settings.\n";
cout << endl;
cout << " --rtp-port <port>\n";
cout << "\t\tPort for RTP.\n";
cout << "\t\tThis port overrides the port from the system settings.\n";
cout << endl;
cout << " --nic <NIC>";
cout << "\tIf you have multiple NICs on your computer,\n";
cout << "\t\tthen you can supply the NIC name to use here (e.g. eth0).\n";
cout << endl;
cout << " --call <address>\n";
cout << "\t\tInstruct Twinkle to call the address.\n";
cout << "\t\tWhen Twinkle is already running, this will instruct the running\n";
cout << "\t\tprocess to call the address.\n";
cout << "\t\tThe address may be a full or partial SIP URI. A partial SIP URI\n";
cout << "\t\twill be completed with the information from the user profile.\n";
cout << endl;
cout << "\t\tA subject may be passed by appending '?subject=<subject>'\n";
cout << "\t\tto the address.\n";
cout << endl;
cout << "\t\tExamples:\n";
cout << "\t\ttwinkle --call 123456\n";
cout << "\t\ttwinkle --call sip:example@example.com?subject=hello\n";
cout << endl;
cout << " --cmd <cli command>\n";
cout << "\t\tInstruct Twinkle to execute the CLI command. You can run\n";
cout << "\t\tall commands from the command line interface mode.\n";
cout << "\t\tWhen Twinkle is already running, this will instruct the running\n";
cout << "\t\tprocess to execute the CLI command.\n";
cout << endl;
cout << "\t\tExamples:\n";
cout << "\t\ttwinkle --cmd answer\n";
cout << "\t\ttwinkle --cmd mute\n";
cout << "\t\ttwinkle --cmd 'transfer 12345'\n";
cout << endl;
cout << " --immediate";
cout << "\tThis option can be used in conjunction with --call or --cmd\n";
cout << "\t\tIt indicates the the command or call is to be performed\n";
cout << "\t\timmediately without asking the user for any confirmation.\n";
cout << endl;
cout << " --set-profile <profile>\n";
cout << "\t\tMake <profile> the active profile.\n";
cout << "\t\tWhen using this option in conjuction with --call and --cmd,\n";
cout << "\t\tthen the profile is activated before executing --call or \n";
cout << "\t\t--cmd.\n";
cout << endl;
cout << " --show";
cout << "\t\tInstruct a running instance of Twinkle to show the main window\n";
cout << "\t\tand take focus.\n";
cout << endl;
cout << " --hide";
cout << "\t\tInstruct a running instance of Twinkle to hide in the sytem tray.\n";
cout << "\t\tIf no system tray is used, then Twinkle will minimize.\n";
cout << endl;
cout << " --help-cli [cli command]\n";
cout << "\t\tWithout a cli command this option lists all available CLI\n";
cout << "\t\tcommands. With a CLI command this option prints help on\n";
cout << "\t\tthe CLI command.\n";
cout << endl;
cout << " --version";
cout << "\tGet version information.\n";
exit(0);
} else if (strcmp(argv[i], "--version") == 0) {
// Get version
QString s = sys_config->about(false).c_str();
cout << s;
exit(0);
} else if (strcmp(argv[i], "-c") == 0) {
// CLI mode
cli_mode = true;
} else if (strcmp(argv[i], "--share") == 0) {
if (i < argc - 1 && argv[i+1][0] != '-') {
i++;
sys_config->set_dir_share(argv[i]);
} else {
cout << argv[0] << ": ";
cout << "Directory missing for option '-share'.\n";
exit(0);
}
} else if (strcmp(argv[i], "-f") == 0) {
if (i < argc - 1 && argv[i+1][0] != '-') {
while (i < argc -1 && argv[i+1][0] != '-') {
i++;
// Config file name
QString config_file = argv[i];
if (!config_file.endsWith(USER_FILE_EXT))
{
config_file += USER_FILE_EXT;
}
config_files.push_back(config_file.ascii());
}
} else {
cout << argv[0] << ": ";
cout << "Config file name missing for option '-f'.\n";
exit(0);
}
} else if (strcmp(argv[i], "--force") == 0) {
override_lock_file = true;
} else if (strcmp(argv[i], "-i") == 0) {
if (i < argc - 1) {
i++;
// IP address
user_host = argv[i];
if (!exists_interface(user_host)) {
cout << argv[0] << ": ";
cout << "There is no interface with IP address ";
cout << user_host << endl;
exit(0);
}
} else {
cout << argv[0] << ": ";
cout << "IP address missing for option '-i'.\n";
exit(0);
}
} else if (strcmp(argv[i], "--sip-port") == 0) {
if (i < argc - 1) {
i++;
g_cmd_args.override_sip_udp_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++;
g_cmd_args.override_rtp_port = atoi(argv[i]);
} else {
cout << argv[0] << ": ";
cout << "Port missing for option '--rtp-port'\n";
}
} else if (strcmp(argv[i], "--nic") == 0) {
if (i < argc - 1) {
i++;
// NIC name, e.g. eth0
string user_dev = argv[i];
string ip;
if (exists_interface_dev(user_dev, ip)) {
user_host = ip;
} else {
cout << argv[0] << ": ";
cout << "There is no network interface ";
cout << user_dev << endl;
exit(0);
}
} else {
cout << argv[0] << ": ";
cout << "NIC name missing for option '-d'.\n";
exit(0);
}
} else if (strcmp(argv[i], "--call") == 0) {
if (i < argc - 1) {
i++;
// SIP URI
g_cmd_args.callto_destination = argv[i];
if (g_cmd_args.callto_destination.isEmpty()) {
cout << argv[0] << ": ";
cout << "--call argument may not be empty.\n";
exit(0);
}
} else {
cout << argv[0] << ": ";
cout << "SIP URI missing for option '--call'.\n";
exit(0);
}
} else if (strcmp(argv[i], "--cmd") == 0) {
if (i < argc - 1) {
i++;
// CLI command
g_cmd_args.cli_command = argv[i];
if (g_cmd_args.cli_command.isEmpty()) {
cout << argv[0] << ": ";
cout << "--cmd argument may not be empty.\n";
exit(0);
}
} else {
cout << argv[0] << ": ";
cout << "CLI command missing for option '--cmd'.\n";
exit(0);
}
} else if (strcmp(argv[i], "--immediate") == 0) {
// Immediate mode
g_cmd_args.cmd_immediate_mode = true;
} else if (strcmp(argv[i], "--set-profile") == 0) {
if (i < argc - 1) {
i++;
// Set profile
g_cmd_args.cmd_set_profile = argv[i];
} else {
cout << argv[0] << ": ";
cout << "Profile missing for option '--set-profile'.\n";
exit(0);
}
} else if (strcmp(argv[i], "--show") == 0) {
// Show main window
g_cmd_args.cmd_show = true;
} else if (strcmp(argv[i], "--hide") == 0) {
// Hide main window
g_cmd_args.cmd_hide = true;
} else if (strcmp(argv[i], "--help-cli") == 0) {
string cmd_help("help ");
if (i < argc -1) {
i++;
// Help CLI
cmd_help += argv[i];
}
t_phone p;
t_userintf u(&p);
u.exec_command(cmd_help);
exit(0);
} else {
cout << argv[0] << ": ";
cout << "Unknown option '" << argv[i] << "'." << endl;
cout << argv[0] << ": ";
cout << "Use --help to get a list of available command line options.\n";
exit(0);
}
}
if (!g_cmd_args.callto_destination.isEmpty() && !g_cmd_args.cli_command.isEmpty()) {
cout << argv[0] << ": ";
cout << "--call and --cmd cannot be used at the same time.\n";
exit(0);
}
return;
}
bool open_sip_socket(bool cli_mode) {
// Open socket for SIP signaling
try {
sip_socket = new t_socket_udp(sys_config->get_sip_udp_port(true));
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;
if (cli_mode) {
msg = QString("Failed to create a UDP socket (SIP) on port %1")
.arg(sys_config->get_sip_udp_port()).ascii();
} else {
msg = qApp->translate("GUI", "Failed to create a UDP socket (SIP) on port %1")
.arg(sys_config->get_sip_udp_port()).ascii();
}
msg += "\n";
msg += get_error_str(err);
log_file->write_report(msg, "::main", LOG_NORMAL, LOG_CRITICAL);
ui->cb_show_msg(msg, MSG_CRITICAL);
return false;
}
return true;
}
QApplication *create_user_interface(bool cli_mode, char **argv, QTranslator *qtranslator) {
QApplication *qa = NULL;
if (cli_mode) {
// CLI mode
ui = new t_userintf(phone);
MEMMAN_NEW(ui);
} else {
// GUI mode
#ifdef HAVE_KDE
// Store the defualt mime source factory for the embedded icons.
// This is created by Qt. The KApplication constructor seems to destroy
// this default.
QMimeSourceFactory *factory_qt = QMimeSourceFactory::takeDefaultFactory();
// Initialize the KApplication
KCmdLineArgs::init(1, argv, "twinkle", PRODUCT_NAME, "Soft phone",
PRODUCT_VERSION);
qa = new KApplication();
MEMMAN_NEW(qa);
// Store the KDE mime source factory
QMimeSourceFactory *factory_kde = QMimeSourceFactory::takeDefaultFactory();
// Make the Qt factory the default to make the embedded icons work.
QMimeSourceFactory::setDefaultFactory(factory_qt);
// Add the KDE factory
QMimeSourceFactory::addFactory(factory_kde);
#else
int tmp = 1;
qa = new QApplication(tmp, argv);
MEMMAN_NEW(qa);
#endif
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
// Install Qt translator
// Do not report to memman as the translator will be deleted
// automatically when the QApplication is deleted.
qtranslator = new QTranslator(0);
qtranslator->load(QString("twinkle_") + QTextCodec::locale(),
QString(sys_config->get_dir_lang().c_str()));
qa->installTranslator(qtranslator);
// Create translator for translation of strings from the core
translator = new t_qt_translator(qa);
MEMMAN_NEW(translator);
ui = new t_gui(phone);
MEMMAN_NEW(ui);
}
return qa;
}
int main( int argc, char ** argv )
{
string error_msg;
bool cli_mode;
bool override_lock_file;
list<string> config_files;
// Initialize globals
end_app = false;
// Determine threading implementation
threading_is_LinuxThreads = t_thread::is_LinuxThreads();
QApplication *qa = NULL;
QTranslator *qtranslator = NULL;
// Store id of main thread
thread_id_main = t_thread::self();
memman = new t_memman();
MEMMAN_NEW(memman);
evq_trans_mgr = new t_event_queue();
MEMMAN_NEW(evq_trans_mgr);
evq_sender_udp = new t_event_queue();
MEMMAN_NEW(evq_sender_udp);
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);
// Create system configuration object
sys_config = new t_sys_settings();
MEMMAN_NEW(sys_config);
// Parse command line arguments
parse_main_args(argc, argv, cli_mode, override_lock_file, config_files);
sys_config->set_override_sip_udp_port(g_cmd_args.override_sip_udp_port);
sys_config->set_override_rtp_port(g_cmd_args.override_rtp_port);
// Checking the environment and creating the lock is done at
// this early stage to improve performance of the --call parameter.
// Creation of the QApplication object for the GUI is slow.
// However for errors, the user interface must be created to give
// either a message box or text formatted error.
// Check requirements on environment
// If check fails, then display error after user interface has been
// created.
string env_error_msg;
bool env_check_ok = sys_config->check_environment(env_error_msg);
// Create a lock file to guarantee that the application runs only once.
bool already_running;
bool lock_created;
string lock_error_msg;
if (env_check_ok &&
!(lock_created = sys_config->create_lock_file(lock_error_msg, already_running)))
{
bool must_exit = false;
// Show the main window of the running Twinkle process.
if (already_running && g_cmd_args.cmd_show) {
cmdsocket::cmd_show();
must_exit = true;
}
// Hide the main window of the running Twinkle process.
if (already_running && g_cmd_args.cmd_hide) {
cmdsocket::cmd_hide();
must_exit = true;
}
// Activate a profile in the running Twinkle process.
if (already_running && !g_cmd_args.cmd_set_profile.isEmpty()) {
cmdsocket::cmd_cli(string("user ") + g_cmd_args.cmd_set_profile.ascii(), true);
// Do not exit now as this option may be used in conjuction
// with --call or --cmd
must_exit = true;
}
// If Twinkle is running already and the --call parameter
// is present, then send the call destination to the running
// Twinkle process.
if (already_running && !g_cmd_args.callto_destination.isEmpty()) {
cmdsocket::cmd_call(g_cmd_args.callto_destination.ascii(),
g_cmd_args.cmd_immediate_mode);
exit(0);
}
// If the --cmd parameter is present, send the cli command
// to the running Twinkle process
if (already_running && !g_cmd_args.cli_command.isEmpty()) {
cmdsocket::cmd_cli(g_cmd_args.cli_command.ascii(),
g_cmd_args.cmd_immediate_mode);
exit(0);
}
// Exit if an instruction for a running instance was given.
if (must_exit) {
exit(0);
}
}
// Read system configuration
bool sys_config_read = sys_config->read_config(error_msg);
qa = create_user_interface(cli_mode, argv, qtranslator);
if (!sys_config_read) {
ui->cb_show_msg(error_msg, MSG_CRITICAL);
exit(1);
}
// Get default values from system configuration
if (config_files.empty()) {
list<string> start_user_profiles = sys_config->get_start_user_profiles();
for (list<string>::iterator i = start_user_profiles.begin();
i != start_user_profiles.end(); i++)
{
QString config_file = (*i).c_str();
config_file += USER_FILE_EXT;
config_files.push_back(config_file.ascii());
}
}
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;
}
}
if (!env_check_ok) {
// Environment is not good
// Call the check_environment once more to get proper translation
// of the error message. The previous check was done before
// the QApplication was created.
(void)sys_config->check_environment(env_error_msg);
ui->cb_show_msg(env_error_msg, MSG_CRITICAL);
exit(1);
}
// Show error if lock file could not be created
if (!lock_created) {
string msg;
// Call create lock file once more to get proper translation of
// error message.
if (!sys_config->create_lock_file(msg, already_running)) {
if (already_running) {
if (!cli_mode) {
msg += "\n\n";
msg += qApp->translate("GUI",
"Override lock file and start anyway?").ascii();
}
if (override_lock_file || ui->cb_ask_msg(msg, MSG_WARNING)) {
sys_config->delete_lock_file();
if (!sys_config->create_lock_file(msg,
already_running))
{
ui->cb_show_msg(msg, MSG_CRITICAL);
exit(1);
}
} else {
exit(1);
}
} else {
ui->cb_show_msg(msg, MSG_CRITICAL);
exit(1);
}
}
// If for some obscure reason the lock file could be
// created this time, then continue.
}
// Create log file
log_file = new t_log();
MEMMAN_NEW(log_file);
// Write threading implementation to log file. May be useful for debugging.
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);
}
bool profile_selected = false;
while(!profile_selected) {
// Select user profile
if (config_files.empty()) {
if (!ui->select_user_config(config_files)) {
sys_config->delete_lock_file();
exit(1);
}
}
for (list<string>::iterator i = config_files.begin();
i != config_files.end(); i++)
{
t_user user_config;
// Read user configuration
if (user_config.read_config(*i, error_msg)) {
t_user *dup_user;
if (phone->add_phone_user(
user_config, &dup_user))
{
profile_selected = true;
} else {
if (cli_mode) {
error_msg = QString("The following profiles are both for user %1").arg(user_config.get_name().c_str()).ascii();
} else {
error_msg = qApp->translate("GUI", "The following profiles are both for user %1").arg(user_config.get_name().c_str()).ascii();
}
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";
if (cli_mode) {
error_msg += QString("You can only run multiple profiles for different users.").ascii();
error_msg += "\n";
error_msg += QString("If these are users for different domains, then enable the following option in your user profile (SIP protocol):");
error_msg += "\n";
error_msg += QString("Use domain name to create a unique contact header");
} else {
error_msg += qApp->translate("GUI", "You can only run multiple profiles for different users.").ascii();
error_msg += "\n";
error_msg += qApp->translate("GUI", "If these are users for different domains, then enable the following option in your user profile (SIP protocol)");
error_msg += ":\n";
error_msg += qApp->translate("GUI", "Use domain name to create a unique contact header");
}
ui->cb_show_msg(error_msg, MSG_CRITICAL);
profile_selected = false;
break;
}
} else {
ui->cb_show_msg(error_msg, MSG_CRITICAL);
profile_selected = false;
break;
}
}
if (profile_selected && !open_sip_socket(cli_mode)) {
// Opening SIP socket failed. Let user pick a user profile
// again, so he can make changes in settings to fix the error.
profile_selected = false;
}
// In CLI mode the user cannot select another profile.
if (!profile_selected) {
if (cli_mode) {
sys_config->delete_lock_file();
exit(1);
}
}
config_files.clear();
}
// Initialize RTP port settings.
phone->init_rtp_ports();
// Create call history
call_history = new t_call_history();
MEMMAN_NEW(call_history);
// 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);
}
// Preload the address finder (KABC is only available in GUI mode)
if (!cli_mode) {
t_address_finder::preload();
}
// Pick network interface
if (user_host.empty()) {
user_host = ui->select_network_intf();
if (user_host.empty()) {
sys_config->delete_lock_file();
exit(1);
}
}
// Discover NAT type if STUN is enabled
list<t_user *> user_list = phone->ref_users();
ui->cb_nat_discovery_progress_start(user_list.size());
list<string> msg_list;
int progressStep = 0;
for (list<t_user *>::iterator i = user_list.begin(); i != user_list.end(); i++) {
ui->cb_nat_discovery_progress_step(progressStep);
if (ui->cb_nat_discovery_cancelled()) {
log_file->write_report("User aborted NAT discovery.", "::main");
sys_config->delete_lock_file();
exit(1);
}
if (!phone->stun_discover_nat(*i, error_msg)) {
msg_list.push_back(error_msg);
}
progressStep++;
}
ui->cb_nat_discovery_finished();
for (list<string>::iterator i = msg_list.begin();
i != msg_list.end(); i++)
{
ui->cb_show_msg(*i, MSG_WARNING);
}
// Open socket for external commands from the command line
string cmd_sock_name = sys_config->get_dir_user();
cmd_sock_name += '/';
cmd_sock_name += CMD_SOCKNAME;
t_socket_local *sock_cmd = NULL;
try {
// The local socket may still exist if Twinkle got killed
// previously, so remove it if it is still there.
unlink(cmd_sock_name.c_str());
sock_cmd = new t_socket_local();
MEMMAN_NEW(sock_cmd);
sock_cmd->bind(cmd_sock_name);
sock_cmd->listen(5);
string log_msg = "Created local socket: ";
log_msg += cmd_sock_name;
log_file->write_report(log_msg, "::main");
}
catch (int e) {
if (sock_cmd) {
MEMMAN_DELETE(sock_cmd);
delete sock_cmd;
sock_cmd = NULL;
}
string log_msg = "Failed to create local socket: ";
log_msg += cmd_sock_name;
log_msg += "\n";
log_msg += get_error_str(e);
log_msg += "\n";
log_file->write_report(log_msg, "::main", LOG_NORMAL, LOG_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);
}
}
// Create threads
t_thread *thr_sender_udp;
t_thread *thr_listen_udp;
t_thread *thr_timekeeper;
t_thread *thr_alarm_catcher;
t_thread *thr_sig_catcher;
t_thread *thr_trans_mgr;
t_thread *thr_phone_uas;
t_thread *thr_listen_cmd = NULL;
try {
// UDP sender thread
thr_sender_udp = new t_thread(sender_udp, NULL);
MEMMAN_NEW(thr_sender_udp);
// UDP listener thread
thr_listen_udp = new t_thread(listen_udp, NULL);
MEMMAN_NEW(thr_listen_udp);
// 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);
// External command listener thread
if (sock_cmd) {
thr_listen_cmd = new t_thread(cmdsocket::listen_cmd, sock_cmd);
MEMMAN_NEW(thr_listen_cmd);
}
} 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);
}
// Start UI event loop (CLI/QApplication/KApplication)
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;
// Terminate threads
// Kill the threads getting receiving input from the outside world first,
// so no new inputs come in during termination.
if (thr_listen_cmd) {
thr_listen_cmd->cancel();
thr_listen_cmd->join();
log_file->write_report("thr_listen_cmd stopped.", "::main", LOG_NORMAL, LOG_DEBUG);
}
thr_listen_udp->cancel();
thr_listen_udp->join();
log_file->write_report("thr_listen_udp stopped.", "::main", LOG_NORMAL, LOG_DEBUG);
evq_trans_layer->push_quit();
thr_phone_uas->join();
log_file->write_report("thr_phone_uas stopped.", "::main", LOG_NORMAL, LOG_DEBUG);
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();
log_file->write_report("thr_sig_catcher stopped.", "::main",
LOG_NORMAL, LOG_DEBUG);
thr_alarm_catcher->cancel();
thr_alarm_catcher->join();
log_file->write_report("thr_alarm_catcher stopped.", "::main",
LOG_NORMAL, LOG_DEBUG);
}
evq_timekeeper->push_quit();
thr_timekeeper->join();
log_file->write_report("thr_timekeeper stopped.", "::main", LOG_NORMAL, LOG_DEBUG);
evq_sender_udp->push_quit();
thr_sender_udp->join();
log_file->write_report("thr_sender_udp stopped.", "::main", LOG_NORMAL, LOG_DEBUG);
if (thr_listen_cmd) {
MEMMAN_DELETE(thr_listen_cmd);
delete thr_listen_cmd;
}
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;
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_udp);
delete thr_sender_udp;
MEMMAN_DELETE(ab_local);
delete ab_local;
MEMMAN_DELETE(call_history);
delete call_history;
call_history = NULL;
MEMMAN_DELETE(ui);
delete ui;
ui = NULL;
MEMMAN_DELETE(sip_socket);
delete sip_socket;
if (sock_cmd) {
MEMMAN_DELETE(sock_cmd);
delete sock_cmd;
unlink(cmd_sock_name.c_str());
}
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_udp);
delete evq_sender_udp;
MEMMAN_DELETE(evq_trans_layer);
delete evq_trans_layer;
MEMMAN_DELETE(evq_timekeeper);
delete evq_timekeeper;
if (translator) {
MEMMAN_DELETE(translator);
delete translator;
translator = NULL;
}
if (qtranslator) {
MEMMAN_DELETE(qtranslator);
delete(qtranslator);
}
if (qa) {
MEMMAN_DELETE(qa);
delete qa;
}
// 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;
}
syntax highlighted by Code2HTML, v. 0.9.1