From ccec6fe36cf4c72b13799e516401cc5c0c51f5b9 Mon Sep 17 00:00:00 2001 From: Ondrej Martine Date: Fri, 30 Apr 2010 14:09:07 +0000 Subject: [PATCH] * command-line parsing - options now properly override configuration settings - fix arguments checking and cleanup error messages - "-D" accepts string level (eg, "info"), etc. [ ...prepare for switching to getopt soon... ] * signal handling and more gracious shutdown procedure - fix possible race condition in the signal handler (eg, static AmMutex and AmCondition) - dispose instances synchronously in main() instead of in the signal handler - replace multiple "exit()"s by a single exit point - remove PID file on failure exit * add possibility to disable daemon mode at compile-time (see DISABLE_DAEMON_MODE macro) git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@1872 8eb893ce-cfd4-0310-b710-fb5ebe64c474 --- core/AmConfig.cpp | 156 ++++-- core/AmConfig.h | 32 +- core/plug-in/stats/StatsUDPServer.cpp | 2 +- core/sems.cpp | 755 +++++++++++++------------- core/sems.h | 14 +- 5 files changed, 531 insertions(+), 428 deletions(-) diff --git a/core/AmConfig.cpp b/core/AmConfig.cpp index 0138e323..1e91a161 100644 --- a/core/AmConfig.cpp +++ b/core/AmConfig.cpp @@ -38,6 +38,8 @@ #include "AmUtils.h" #include +#include +#include string AmConfig::ConfigurationFile = CONFIG_FILE; string AmConfig::ModConfigPath = MOD_CFG_PATH; @@ -45,7 +47,16 @@ string AmConfig::PlugInPath = PLUG_IN_PATH; string AmConfig::LoadPlugins = ""; string AmConfig::ExcludePlugins = ""; string AmConfig::ExcludePayloads = ""; -int AmConfig::DaemonMode = DEFAULT_DAEMON_MODE; +int AmConfig::LogLevel = L_INFO; +bool AmConfig::LogStderr = false; + +#ifndef DISABLE_DAEMON_MODE +bool AmConfig::DaemonMode = DEFAULT_DAEMON_MODE; +string AmConfig::DaemonPidFile = DEFAULT_DAEMON_PID_FILE; +string AmConfig::DaemonUid = DEFAULT_DAEMON_UID; +string AmConfig::DaemonGid = DEFAULT_DAEMON_GID; +#endif + string AmConfig::LocalIP = ""; string AmConfig::PublicIP = ""; string AmConfig::PrefixSep = PREFIX_SEPARATOR; @@ -86,7 +97,7 @@ bool AmConfig::IgnoreSIGCHLD = true; int AmConfig::setSIPPort(const string& port) { - if(sscanf(port.c_str(),"%u",&AmConfig::LocalSIPPort) != 1) { + if(sscanf(port.c_str(),"%u",&LocalSIPPort) != 1) { return 0; } return 1; @@ -94,7 +105,7 @@ int AmConfig::setSIPPort(const string& port) int AmConfig::setRtpLowPort(const string& port) { - if(sscanf(port.c_str(),"%i",&AmConfig::RtpLowPort) != 1) { + if(sscanf(port.c_str(),"%i",&RtpLowPort) != 1) { return 0; } return 1; @@ -102,43 +113,74 @@ int AmConfig::setRtpLowPort(const string& port) int AmConfig::setRtpHighPort(const string& port) { - if(sscanf(port.c_str(),"%i",&AmConfig::RtpHighPort) != 1) { + if(sscanf(port.c_str(),"%i",&RtpHighPort) != 1) { return 0; } return 1; } -int AmConfig::setLoglevel(const string& ll) { - - if(sscanf(ll.c_str(),"%u",&log_level) != 1) { - return 0; +int AmConfig::setLogLevel(const string& level, bool apply) +{ + int n; + + if (sscanf(level.c_str(), "%i", &n) == 1) { + if (n < L_ERR || n > L_DBG) { + return 0; + } + } else { + string s(level); + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + + if (s == "error" || s == "err") { + n = L_ERR; + } else if (s == "warning" || s == "warn") { + n = L_WARN; + } else if (s == "info") { + n = L_INFO; + } else if (s=="debug" || s == "dbg") { + n = L_DBG; + } else { + return 0; + } + } + + LogLevel = n; + if (apply) { + log_level = LogLevel; } return 1; } -int AmConfig::setFork(const string& fork) { - if ( strcasecmp(fork.c_str(), "yes") == 0 ) { - DaemonMode = 1; - } else if ( strcasecmp(fork.c_str(), "no") == 0 ) { - DaemonMode = 0; +int AmConfig::setLogStderr(const string& s, bool apply) +{ + if ( strcasecmp(s.c_str(), "yes") == 0 ) { + LogStderr = true; + } else if ( strcasecmp(s.c_str(), "no") == 0 ) { + LogStderr = false; } else { return 0; - } + } + if (apply) { + log_stderr = LogStderr; + } return 1; -} +} -int AmConfig::setStderr(const string& s) { - if ( strcasecmp(s.c_str(), "yes") == 0 ) { - log_stderr = 1; - AmConfig::DaemonMode = 0; - } else if ( strcasecmp(s.c_str(), "no") == 0 ) { - log_stderr = 0; +#ifndef DISABLE_DAEMON_MODE + +int AmConfig::setDaemonMode(const string& fork) { + if ( strcasecmp(fork.c_str(), "yes") == 0 ) { + DaemonMode = true; + } else if ( strcasecmp(fork.c_str(), "no") == 0 ) { + DaemonMode = false; } else { return 0; - } + } return 1; } +#endif /* !DISABLE_DAEMON_MODE */ + int AmConfig::setSessionProcessorThreads(const string& th) { if(sscanf(th.c_str(),"%u",&SessionProcessorThreads) != 1) { return 0; @@ -163,7 +205,7 @@ int AmConfig::setSIPServerThreads(const string& th){ int AmConfig::setDeadRtpTime(const string& drt) { - if(sscanf(drt.c_str(),"%u",&AmConfig::DeadRtpTime) != 1) { + if(sscanf(drt.c_str(),"%u",&DeadRtpTime) != 1) { return 0; } return 1; @@ -171,11 +213,11 @@ int AmConfig::setDeadRtpTime(const string& drt) int AmConfig::readConfiguration() { - DBG("Reading configuration..."); + DBG("Reading configuration...\n"); AmConfigReader cfg; - if(cfg.loadFile(ConfigurationFile.c_str())){ + if(cfg.loadFile(AmConfig::ConfigurationFile.c_str())){ ERROR("while loading main configuration file\n"); return -1; } @@ -183,13 +225,16 @@ int AmConfig::readConfiguration() // take values from global configuration file // they will be overwritten by command line args - +#ifndef DISABLE_SYSLOG_LOG if (cfg.hasParameter("syslog_facility")) { - set_log_facility(cfg.getParameter("syslog_facility").c_str()); + set_syslog_facility(cfg.getParameter("syslog_facility").c_str()); } +#endif // plugin_config_path - ModConfigPath = cfg.getParameter("plugin_config_path",ModConfigPath); + if (cfg.hasParameter("plugin_config_path")) { + ModConfigPath = cfg.getParameter("plugin_config_path",ModConfigPath); + } if(!ModConfigPath.empty() && (ModConfigPath[ModConfigPath.length()-1] != '/')) ModConfigPath += '/'; @@ -219,7 +264,8 @@ int AmConfig::readConfiguration() } // outbound_proxy - OutboundProxy = cfg.getParameter("outbound_proxy"); + if (cfg.hasParameter("outbound_proxy")) + OutboundProxy = cfg.getParameter("outbound_proxy"); // force_outbound_proxy if(cfg.hasParameter("force_outbound_proxy")) { @@ -227,16 +273,20 @@ int AmConfig::readConfiguration() } // plugin_path - PlugInPath = cfg.getParameter("plugin_path"); + if (cfg.hasParameter("plugin_path")) + PlugInPath = cfg.getParameter("plugin_path"); // load_plugins - LoadPlugins = cfg.getParameter("load_plugins"); + if (cfg.hasParameter("load_plugins")) + LoadPlugins = cfg.getParameter("load_plugins"); // exclude_plugins - ExcludePlugins = cfg.getParameter("exclude_plugins"); + if (cfg.hasParameter("exclude_plugins")) + ExcludePlugins = cfg.getParameter("exclude_plugins"); // exclude_plugins - ExcludePayloads = cfg.getParameter("exclude_payloads"); + if (cfg.hasParameter("exclude_payload")) + ExcludePayloads = cfg.getParameter("exclude_payloads"); // user_agent if (cfg.getParameter("use_default_signature")=="yes") @@ -256,14 +306,17 @@ int AmConfig::readConfiguration() // log_level if(cfg.hasParameter("loglevel")){ - if(!setLoglevel(cfg.getParameter("loglevel"))){ + if(!setLogLevel(cfg.getParameter("loglevel"))){ ERROR("invalid log level specified\n"); return -1; } } - LogSessions = cfg.getParameter("log_sessions")=="yes"; - LogEvents = cfg.getParameter("log_events")=="yes"; + if(cfg.hasParameter("log_sessions")) + LogSessions = cfg.getParameter("log_sessions")=="yes"; + + if(cfg.hasParameter("log_events")) + LogEvents = cfg.getParameter("log_events")=="yes"; if (cfg.hasParameter("unhandled_reply_loglevel")) { string msglog = cfg.getParameter("unhandled_reply_loglevel"); @@ -320,18 +373,39 @@ int AmConfig::readConfiguration() AppSelect = App_SPECIFIED; } +#ifndef DISABLE_DAEMON_MODE + // fork if(cfg.hasParameter("fork")){ - if(!setFork(cfg.getParameter("fork"))){ + if(!setDaemonMode(cfg.getParameter("fork"))){ ERROR("invalid fork value specified," " valid are only yes or no\n"); return -1; } } + // daemon (alias for fork) + if(cfg.hasParameter("daemon")){ + if(!setDaemonMode(cfg.getParameter("daemon"))){ + ERROR("invalid daemon value specified," + " valid are only yes or no\n"); + return -1; + } + } + + if(cfg.hasParameter("daemon_uid")){ + DaemonUid = cfg.getParameter("daemon_uid"); + } + + if(cfg.hasParameter("daemon_gid")){ + DaemonGid = cfg.getParameter("daemon_gid"); + } + +#endif /* !DISABLE_DAEMON_MODE */ + // stderr if(cfg.hasParameter("stderr")){ - if(!setStderr(cfg.getParameter("stderr"))){ + if(!setLogStderr(cfg.getParameter("stderr"), false)){ ERROR("invalid stderr value specified," " valid are only yes or no\n"); return -1; @@ -447,9 +521,3 @@ int AmConfig::readConfiguration() return 0; } - -int AmConfig::init() -{ - return 0; -} - diff --git a/core/AmConfig.h b/core/AmConfig.h index e5138d49..225896b9 100644 --- a/core/AmConfig.h +++ b/core/AmConfig.h @@ -59,8 +59,21 @@ struct AmConfig /** semicolon separated list of payloads to exclude from loading */ static string ExcludePayloads; //static unsigned int MaxRecordTime; - /** run the programm in daemon mode? */ - static int DaemonMode; + /** log level */ + static int LogLevel; + /** log to stderr */ + static bool LogStderr; + +#ifndef DISABLE_DAEMON_MODE + /** run the program in daemon mode? */ + static bool DaemonMode; + /** PID file when in daemon mode */ + static string DaemonPidFile; + /** set UID when in daemon mode */ + static string DaemonUid; + /** set GID when in daemon mode */ + static string DaemonGid; +#endif /** local IP for SDP media advertising */ static string LocalIP; @@ -138,9 +151,6 @@ struct AmConfig static int UnhandledReplyLoglevel; - /** Init function. Resolves SMTP server address. */ - static int init(); - /** Read global configuration file and insert values. Maybe overwritten by * command line arguments */ static int readConfiguration(); @@ -156,11 +166,15 @@ struct AmConfig /** Setter for RtpHighPort, returns 0 on invalid value */ static int setRtpHighPort(const string& port); /** Setter for Loglevel, returns 0 on invalid value */ - static int setLoglevel(const string& level); - /** Setter for parameter fork, returns 0 on invalid value */ - static int setFork(const string& fork); + static int setLogLevel(const string& level, bool apply=true); /** Setter for parameter stderr, returns 0 on invalid value */ - static int setStderr(const string& s); + static int setLogStderr(const string& s, bool apply=true); + +#ifndef DISABLE_DAEMON_MODE + /** Setter for parameter DaemonMode, returns 0 on invalid value */ + static int setDaemonMode(const string& fork); +#endif + /** Setter for parameter SessionProcessorThreads, returns 0 on invalid value */ static int setSessionProcessorThreads(const string& th); /** Setter for parameter MediaProcessorThreads, returns 0 on invalid value */ diff --git a/core/plug-in/stats/StatsUDPServer.cpp b/core/plug-in/stats/StatsUDPServer.cpp index bec3020c..955af8ec 100644 --- a/core/plug-in/stats/StatsUDPServer.cpp +++ b/core/plug-in/stats/StatsUDPServer.cpp @@ -257,7 +257,7 @@ int StatsUDPServer::execute(char* msg_buf, string& reply, else if (cmd_str.length() > 4 && cmd_str.substr(0, 4) == "set_") { // setters if (cmd_str.substr(4, 8) == "loglevel") { - if (!AmConfig::setLoglevel(&cmd_str.c_str()[13])) + if (!AmConfig::setLogLevel(&cmd_str.c_str()[13])) reply= "invalid loglevel value.\n"; else reply= "loglevel set to "+int2str(log_level)+".\n"; diff --git a/core/sems.cpp b/core/sems.cpp index 29c61d7d..cc8cfdb3 100644 --- a/core/sems.cpp +++ b/core/sems.cpp @@ -30,12 +30,13 @@ #include "AmConfig.h" #include "AmPlugIn.h" #include "AmSessionContainer.h" -#include "AmSessionProcessor.h" #include "AmMediaProcessor.h" #include "AmRtpReceiver.h" #include "AmEventDispatcher.h" -#include "AmZRTP.h" +#ifdef WITH_ZRTP +# include "AmZRTP.h" +#endif #include "SipCtrlInterface.h" @@ -64,101 +65,185 @@ using std::string; using std::make_pair; -#ifndef sighandler_t -typedef void (*sighandler_t) (int); -#endif - -const char* progname; -string pid_file; -int main_pid=0; -int child_pid=0; -int is_main=1; -static int parse_args(int argc, char* argv[], const string& flags, - const string& options, std::map& args); +const char* progname = NULL; /**< Program name (actually argv[0])*/ +int main_pid = 0; /**< Main process PID */ -static void print_usage(char* progname); -static void print_version(); +/** SIP stack (controller interface) */ +static SipCtrlInterface sip_ctrl; -static string getLocalIP(const string& dev_name); -int sig_flag; -int deamon_mode=1; +static void print_usage(bool short_=false) +{ + if (short_) { + printf("Usage: %s [OPTIONS]\n" + "Try `%s -h' for more information.\n", + progname, progname); + } + else { + printf( + DEFAULT_SIGNATURE "\n" + "Usage: %s [OPTIONS]\n" + "Available options:\n" + " -f Set configuration file\n" + " -x Set path for plug-ins\n" + " -d Set network device (or IP address) for media advertising\n" +#ifndef DISABLE_DAEMON_MODE + " -E Enable debug mode (do not daemonize, log to stderr).\n" + " -P Set PID file\n" + " -u Set user ID\n" + " -g Set group ID\n" +#else + " -E Enable debug mode (log to stderr)\n" +#endif + " -D Set log level (0=error, 1=warning, 2=info, 3=debug; default=%d)\n" + " -v Print version\n" + " -h Print this help\n", + progname, AmConfig::LogLevel + ); + } +} -static void sig_usr_un(int signo) +/* Note: The function should not use log because it is called before logging is initialized. */ +static bool parse_args(int argc, char* argv[], + const string& flags, const string& options, + std::map& args) { - if (signo == SIGCHLD && AmConfig::IgnoreSIGCHLD) - return; + for(int i=1; i need_clean(true); + if( flags.find(*arg) != string::npos ) { + args[*arg] = "yes"; + } + else if(options.find(*arg) != string::npos) { + if(!argv[++i]){ + fprintf(stderr, "%s: missing argument for option '-%c'\n", progname, *arg); + return false; + } - clean_up_mut.lock(); + args[*arg] = argv[i]; + } + else { + fprintf(stderr, "%s: unknown option '-%c'\n", progname, *arg); + return false; + } + } - if(need_clean.get()) { + return true; +} - need_clean.set(false); - clean_up_mut.unlock(); +/* Note: The function should not use logging because it is called before + the logging is initialized. */ +static bool apply_args(std::map& args) +{ + for(std::map::iterator it = args.begin(); + it != args.end(); ++it){ - AmSessionContainer::dispose(); + switch( it->first ){ + case 'd': + AmConfig::LocalIP = it->second; + break; - AmRtpReceiver::dispose(); + case 'D': + if (!AmConfig::setLogLevel(it->second)) { + fprintf(stderr, "%s: invalid log level: %s\n", progname, it->second.c_str()); + return false; + } + break; - AmMediaProcessor::dispose(); + case 'E': +#ifndef DISABLE_DAEMON_MODE + AmConfig::DaemonMode = false; +#endif + if (!AmConfig::setLogStderr("yes")) { + return false; + } + break; - AmEventDispatcher::dispose(); - } - else { - clean_up_mut.unlock(); - } + case 'f': + AmConfig::ConfigurationFile = it->second; + break; - INFO("Finished.\n"); + case 'x': + AmConfig::PlugInPath = it->second; + break; - unlink(pid_file.c_str()); +#ifndef DISABLE_DAEMON_MODE + case 'P': + AmConfig::DaemonPidFile = it->second; + break; - exit(0); + case 'u': + AmConfig::DaemonUid = it->second; + break; + + case 'g': + AmConfig::DaemonGid = it->second; + break; +#endif + + case 'h': + case 'v': + default: + /* nothing to apply */ + break; + } } - return; + return true; } -int set_sighandler(sighandler_t sig_usr) +/** Flag to mark the shutdown is in progress (in the main process) */ +static AmCondition is_shutting_down(false); + +static void signal_handler(int sig) { - if (signal(SIGINT, sig_usr) == SIG_ERR ) { - ERROR("No SIGINT signal handler can be installed.\n"); - return -1; - } - - if (signal(SIGPIPE, sig_usr) == SIG_ERR ) { - ERROR("No SIGPIPE signal handler can be installed.\n"); - return -1; - } + WARN("Signal %s (%d) received.\n", strsignal(sig), sig); - if (signal(SIGCHLD , sig_usr) == SIG_ERR ) { - ERROR("No SIGCHLD signal handler can be installed.\n"); - return -1; + if (sig == SIGCHLD && AmConfig::IgnoreSIGCHLD) { + return; } - if (signal(SIGTERM , sig_usr) == SIG_ERR ) { - ERROR("No SIGTERM signal handler can be installed.\n"); - return -1; + if (main_pid == getpid()) { + if(!is_shutting_down.get()) { + is_shutting_down.set(true); + + INFO("Stopping SIP stack after signal\n"); + sip_ctrl.stop(); + } } + else { + /* exit other processes immediately */ + exit(0); + } +} - if (signal(SIGHUP , sig_usr) == SIG_ERR ) { - ERROR("No SIGHUP signal handler can be installed.\n"); - return -1; +int set_sighandler(void (*handler)(int)) +{ + static int sigs[] = { + SIGHUP, SIGPIPE, SIGINT, SIGTERM, SIGCHLD, 0 + }; + + for (int* sig = sigs; *sig; sig++) { + if (signal(*sig, handler) == SIG_ERR ) { + ERROR("Cannot install signal handler for %s.\n", strsignal(*sig)); + return -1; + } } return 0; } -int write_pid_file() +#ifndef DISABLE_DAEMON_MODE + +static int write_pid_file() { - FILE* fpid = fopen(pid_file.c_str(), "w"); + FILE* fpid = fopen(AmConfig::DaemonPidFile.c_str(), "w"); if (fpid) { string spid = int2str((int)getpid()); @@ -167,140 +252,234 @@ int write_pid_file() return 0; } else { - ERROR("Could not write pid file '%s': %s.\n", - pid_file.c_str(), strerror(errno)); + ERROR("Cannot write PID file '%s': %s.\n", + AmConfig::DaemonPidFile.c_str(), strerror(errno)); } return -1; } -// returns 0 if OK -static int use_args(char* progname, std::map& args) +#endif /* !DISABLE_DAEMON_MODE */ + + +/** Get the list of network interfaces with the associated PF_INET addresses */ +static bool getInterfaceList(int sd, std::vector >& if_list) { - for(std::map::iterator it = args.begin(); - it != args.end(); ++it){ - - if(it->second.empty()) - continue; - - switch( it->first ){ + struct ifconf ifc; + struct ifreq ifrs[MAX_NET_DEVICES]; - case 'h': - print_usage(progname); - exit(0); - break; + ifc.ifc_len = sizeof(struct ifreq) * MAX_NET_DEVICES; + ifc.ifc_req = ifrs; + memset(ifrs, 0, ifc.ifc_len); - case 'v': - print_version(); - exit(0); - break; + if(ioctl(sd, SIOCGIFCONF, &ifc)!=0){ + ERROR("getInterfaceList: ioctl: %s.\n", strerror(errno)); + return false; + } - case 'E': - AmConfig::setStderr("yes"); - continue; +#if !defined(BSD44SOCKETS) + int n_dev = ifc.ifc_len / sizeof(struct ifreq); + for(int i=0; isin_addr))); + } + } +#else // defined(BSD44SOCKETS) + struct ifreq* p_ifr = ifc.ifc_req; + while((char*)p_ifr - (char*)ifc.ifc_req < ifc.ifc_len){ - case 'd': - //if(AmConfig::LocalIP.empty()) - // AmConfig::LocalIP = getLocalIP(it->second); - AmConfig::LocalIP = it->second; - break; - - case 'x': - AmConfig::PlugInPath = it->second; - break; - - case 'D': - if(sscanf(it->second.c_str(), "%u", &log_level) != 1){ - fprintf(stderr, "%s: bad log level number: %s.\n", progname, it->second.c_str()); - return -1; - } - break; + if(p_ifr->ifr_addr.sa_family == PF_INET){ + struct sockaddr_in* sa = (struct sockaddr_in*)&p_ifr->ifr_addr; + if_list.push_back(make_pair((const char*)p_ifr->ifr_name, + inet_ntoa(sa->sin_addr))); + } - case 'f': - case 'P': - case 'u': - case 'g': - // already processed, ignore it here + p_ifr = (struct ifreq*)(((char*)p_ifr) + IFNAMSIZ + p_ifr->ifr_addr.sa_len); + } +#endif + + return true; +} + +/** Get the PF_INET address associated with the network interface */ +static string getLocalIP(const string& dev_name) +{ + string local_ip; + struct ifreq ifr; + std::vector > if_list; + +#ifdef SUPPORT_IPV6 + struct sockaddr_storage ss; + if(inet_aton_v6(dev_name.c_str(), &ss)) +#else + struct in_addr inp; + if(inet_aton(dev_name.c_str(), &inp)) +#endif + { + return dev_name; + } + + int sd = socket(PF_INET, SOCK_DGRAM, 0); + if(sd == -1){ + ERROR("socket: %s.\n", strerror(errno)); + goto error; + } + + if(dev_name.empty()) { + if (!getInterfaceList(sd, if_list)) { + goto error; + } + } + else { + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, dev_name.c_str(), IFNAMSIZ-1); + + if(ioctl(sd, SIOCGIFADDR, &ifr)!=0){ + ERROR("ioctl(SIOCGIFADDR): %s.\n", strerror(errno)); + goto error; + } + + if(ifr.ifr_addr.sa_family==PF_INET){ + struct sockaddr_in* sa = (struct sockaddr_in*)&ifr.ifr_addr; + struct sockaddr_in sa4; + memcpy(&sa4, sa, sizeof(struct sockaddr_in)); + + if_list.push_back(make_pair((char*)ifr.ifr_name, + inet_ntoa(sa4.sin_addr))); + } + } + + for( std::vector >::iterator it = if_list.begin(); + it != if_list.end(); ++it) { + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, it->first.c_str(), IFNAMSIZ-1); + + if(ioctl(sd, SIOCGIFFLAGS, &ifr)!=0){ + ERROR("ioctl(SIOCGIFFLAGS): %s.\n", strerror(errno)); + goto error; + } + + if( (ifr.ifr_flags & IFF_UP) && + (!dev_name.empty() || !(ifr.ifr_flags & IFF_LOOPBACK)) ) { + local_ip = it->second; break; - - default: - ERROR("%s: bad parameter '-%c'.\n", progname, it->first); - return -1; } } - return 0; + + if(ifr.ifr_flags & IFF_LOOPBACK){ + WARN("Media advertising using loopback address!\n" + "Try to use another network interface if your SEMS " + "should be accessible from the rest of the world.\n"); + } + + error: + close(sd); + return local_ip; } +/* + * Main + */ int main(int argc, char* argv[]) { + int success = false; std::map args; + std::map::iterator cfg_arg; - if(parse_args(argc, argv, "hvE", "ugPfiodxD", args)){ - print_usage(argv[0]); - return -1; + progname = strrchr(argv[0], '/'); + progname = (progname == NULL ? argv[0] : progname + 1); + +#ifndef DISABLE_DAEMON_MODE + if(!parse_args(argc, argv, "hvE", "fxdDugP", args)){ +#else + if(!parse_args(argc, argv, "hvE", "fxdD", args)){ +#endif + print_usage(true); + return 1; } if(args.find('h') != args.end()){ - print_usage(argv[0]); + print_usage(); return 0; } if(args.find('v') != args.end()){ - print_version(); + printf("%s\n", DEFAULT_SIGNATURE); return 0; } - init_log(); - - AmConfig::setStderr("yes"); - AmConfig::setLoglevel("1"); - - std::map::iterator cfg_arg; - if( (cfg_arg = args.find('f')) != args.end() ) - AmConfig::ConfigurationFile = cfg_arg->second; + /* apply command-line options */ + if(!apply_args(args)){ + print_usage(true); + goto error; + } - // if(!semsConfig.reloadFile(AmConfig::ConfigurationFile.c_str())) - // return -1; + init_logging(); + /* load and apply configuration file */ AmConfig::readConfiguration(); - - if(use_args(argv[0], args)){ - print_usage(argv[0]); - return -1; + log_level = AmConfig::LogLevel; + log_stderr = AmConfig::LogStderr; + + /* re-apply command-line options to override configuration file */ + if(!apply_args(args)){ + goto error; } AmConfig::LocalIP = getLocalIP(AmConfig::LocalIP); + if (AmConfig::LocalIP.empty()) { + ERROR("Cannot determine proper local address for media advertising!\n" + "Try using 'ifconfig -a' to find a proper interface and configure SEMS to use it.\n"); + goto error; + } + if (AmConfig::LocalSIPIP.empty()) { AmConfig::LocalSIPIP = AmConfig::LocalIP; } - print_version(); - printf( "\n\nConfiguration:\n" - " configuration file: %s\n" - " plug-in path: %s\n" - " daemon mode: %i\n" - " local SIP IP: %s\n" - " public media IP: %s\n" - " local SIP port: %i\n" - " local media IP: %s\n" - " outbound proxy: %s\n" - " application: %s\n" - "\n", - AmConfig::ConfigurationFile.c_str(), - AmConfig::PlugInPath.c_str(), - AmConfig::DaemonMode, - AmConfig::LocalSIPIP.c_str(), - AmConfig::PublicIP.c_str(), - AmConfig::LocalSIPPort, - AmConfig::LocalIP.c_str(), - AmConfig::OutboundProxy.c_str(), - AmConfig::Application.empty()? - "":AmConfig::Application.c_str() - ); + printf("Configuration:\n" +#ifdef _DEBUG + " log level: %s (%i)\n" + " log to stderr: %s\n" +#endif + " configuration file: %s\n" + " plug-in path: %s\n" +#ifndef DISABLE_DAEMON_MODE + " daemon mode: %s\n" + " daemon UID: %s\n" + " daemon GID: %s\n" +#endif + " local SIP IP: %s\n" + " public media IP: %s\n" + " local SIP port: %i\n" + " local media IP: %s\n" + " out-bound proxy: %s\n" + " application: %s\n" + "\n", +#ifdef _DEBUG + log_level2str[AmConfig::LogLevel], AmConfig::LogLevel, + AmConfig::LogStderr ? "yes" : "no", +#endif + AmConfig::ConfigurationFile.c_str(), + AmConfig::PlugInPath.c_str(), +#ifndef DISABLE_DAEMON_MODE + AmConfig::DaemonMode ? "yes" : "no", + AmConfig::DaemonUid.empty() ? "" : AmConfig::DaemonUid.c_str(), + AmConfig::DaemonGid.empty() ? "" : AmConfig::DaemonGid.c_str(), +#endif + AmConfig::LocalSIPIP.c_str(), + AmConfig::PublicIP.c_str(), + AmConfig::LocalSIPPort, + AmConfig::LocalIP.c_str(), + AmConfig::OutboundProxy.c_str(), + AmConfig::Application.empty() ? "" : AmConfig::Application.c_str()); - if(AmConfig::DaemonMode){ +#ifndef DISABLE_DAEMON_MODE - if( (cfg_arg = args.find('g')) != args.end() ){ + if(AmConfig::DaemonMode){ + if(!AmConfig::DaemonGid.empty()){ unsigned int gid; if(str2i(cfg_arg->second, gid)){ struct group* grnam = getgrnam(cfg_arg->second.c_str()); @@ -308,20 +487,20 @@ int main(int argc, char* argv[]) gid = grnam->gr_gid; } else{ - ERROR("Could not find group '%s' in the group database.\n", + ERROR("Cannot not find group '%s' in the group database.\n", cfg_arg->second.c_str()); - return -1; + goto error; } } if(setgid(gid)<0){ - ERROR("Cannot change gid to %i: %s.", + ERROR("Cannot change GID to %i: %s.", gid, strerror(errno)); - return -1; + goto error; } } - if( (cfg_arg = args.find('u')) != args.end() ){ + if(!AmConfig::DaemonUid.empty()){ unsigned int uid; if(str2i(cfg_arg->second, uid)){ struct passwd* pwnam = getpwnam(cfg_arg->second.c_str()); @@ -329,16 +508,16 @@ int main(int argc, char* argv[]) uid = pwnam->pw_uid; } else{ - ERROR("Could not find user '%s' in the user database.\n", + ERROR("Cannot not find user '%s' in the user database.\n", cfg_arg->second.c_str()); - return -1; + goto error; } } if(setuid(uid)<0){ - ERROR("Cannot change uid to %i: %s.", + ERROR("Cannot change UID to %i: %s.", uid, strerror(errno)); - return -1; + goto error; } } @@ -346,7 +525,7 @@ int main(int argc, char* argv[]) int pid; if ((pid=fork())<0){ ERROR("Cannot fork: %s.\n", strerror(errno)); - return -1; + goto error; }else if (pid!=0){ /* parent process => exit*/ return 0; @@ -358,262 +537,98 @@ int main(int argc, char* argv[]) /* fork again to drop group leadership */ if ((pid=fork())<0){ ERROR("Cannot fork: %s.\n", strerror(errno)); - return -1; + goto error; }else if (pid!=0){ /*parent process => exit */ return 0; } - if( (cfg_arg = args.find('P')) != args.end() ){ - pid_file = cfg_arg->second; - if(write_pid_file()<0) - return -1; + if(write_pid_file()<0) { + goto error; } /* try to replace stdin, stdout & stderr with /dev/null */ if (freopen("/dev/null", "r", stdin)==0){ - ERROR("Unable to replace stdin with /dev/null: %s.\n", + ERROR("Cannot replace stdin with /dev/null: %s.\n", strerror(errno)); /* continue, leave it open */ }; if (freopen("/dev/null", "w", stdout)==0){ - ERROR("Unable to replace stdout with /dev/null: %s.\n", + ERROR("Cannot replace stdout with /dev/null: %s.\n", strerror(errno)); /* continue, leave it open */ }; /* close stderr only if log_stderr=0 */ - if ((!log_stderr) &&(freopen("/dev/null", "w", stderr)==0)){ - ERROR("Unable to replace stderr with /dev/null: %s.\n", + if ((!log_stderr) && (freopen("/dev/null", "w", stderr)==0)){ + ERROR("Cannot replace stderr with /dev/null: %s.\n", strerror(errno)); /* continue, leave it open */ }; } - main_pid = getpid(); +#endif /* DISABLE_DAEMON_MODE */ - if(set_sighandler(sig_usr_un)) - return -1; + main_pid = getpid(); - if(AmConfig::init()) - return -1; + init_random(); - DBG("Loading plug-ins\n"); + if(set_sighandler(signal_handler)) + goto error; + + INFO("Loading plug-ins\n"); AmPlugIn::instance()->init(); if(AmPlugIn::instance()->load(AmConfig::PlugInPath, AmConfig::LoadPlugins)) - return -1; - - init_random(); + goto error; #ifdef WITH_ZRTP if (AmZRTP::init()) { - ERROR("Some error during zrtp initialization\n"); - return -1; + ERROR("Cannot initialize ZRTP\n"); + goto error; } #endif - DBG("Starting session container\n"); + INFO("Starting session container\n"); AmSessionContainer::instance()->start(); #ifdef SESSION_THREADPOOL - DBG("starting session processor threads\n"); + INFO("Starting session processor threads\n"); AmSessionProcessor::addThreads(AmConfig::SessionProcessorThreads); #endif - DBG("Starting media processor\n"); + INFO("Starting media processor\n"); AmMediaProcessor::instance()->init(); -// DBG("Starting mailer\n"); -// AmMailDeamon::instance()->start(); - - DBG("Starting RTP receiver\n"); + INFO("Starting RTP receiver\n"); AmRtpReceiver::instance()->start(); - DBG("Starting SIP stack\n"); - SipCtrlInterface sip_ctrl; + INFO("Starting SIP stack (control interface)\n"); sip_ctrl.load(); - sip_ctrl.run(AmConfig::LocalSIPIP,AmConfig::LocalSIPPort); - - return 0; -} - -static void print_usage(char* progname) -{ - printf( - "USAGE: %s [options]\n" - " Options:\n" - " -f config_filename: sets configuration file to use\n" - " -d device: sets network device for media advertising\n" - " -P pid_file: write a pid file.\n" - " -u uid: set user id.\n" - " -g gid: set group id.\n" - " -x plugin_path: path for plugins\n" - " -D log_level: sets log level (error=0, warning=1, info=2, debug=3).\n" - " -E : debug mode: do not fork and log to stderr.\n" - " -v : version.\n" - " -h : this help screen.\n" - "\n", - progname - ); -} + sip_ctrl.run(AmConfig::LocalSIPIP, AmConfig::LocalSIPPort); + + success = true; -static void print_version() -{ - printf("%s\n", DEFAULT_SIGNATURE); -} + INFO("Disposing RTP receiver\n"); + AmRtpReceiver::dispose(); -static void getInterfaceList(int sd, std::vector >& if_list) -{ - struct ifconf ifc; - struct ifreq ifrs[MAX_NET_DEVICES]; + INFO("Disposing media processor\n"); + AmMediaProcessor::dispose(); - ifc.ifc_len = sizeof(struct ifreq) * MAX_NET_DEVICES; - ifc.ifc_req = ifrs; - memset(ifrs, 0, ifc.ifc_len); - - if(ioctl(sd, SIOCGIFCONF, &ifc)!=0){ - ERROR("getInterfaceList: ioctl: %s.\n", strerror(errno)); - exit(-1); - } + INFO("Disposing session container\n"); + AmSessionContainer::dispose(); -#if !defined(BSD44SOCKETS) - int n_dev = ifc.ifc_len / sizeof(struct ifreq); - for(int i=0; iifr_addr.sa_family == PF_INET){ - struct sockaddr_in* sa = (struct sockaddr_in*)&p_ifr->ifr_addr; - if_list.push_back(make_pair((const char*)p_ifr->ifr_name, - inet_ntoa(sa->sin_addr))); - } + error: + INFO("Disposing plug-ins\n"); + AmPlugIn::dispose(); - p_ifr = (struct ifreq*)(((char*)p_ifr) + IFNAMSIZ + p_ifr->ifr_addr.sa_len); +#ifndef DISABLE_DAEMON_MODE + if (AmConfig::DaemonMode) { + unlink(AmConfig::DaemonPidFile.c_str()); } #endif -} - -static string getLocalIP(const string& dev_name) -{ - -#ifdef SUPPORT_IPV6 - struct sockaddr_storage ss; - if(inet_aton_v6(dev_name.c_str(), &ss)) -#else - struct in_addr inp; - if(inet_aton(dev_name.c_str(), &inp)) -#endif - { - return dev_name; - } - - int sd = socket(PF_INET, SOCK_DGRAM, 0); - if(sd == -1){ - ERROR("setLocalIP: socket: %s.\n", strerror(errno)); - exit(-1); - } - - struct ifreq ifr; - std::vector > if_list; - - if(dev_name.empty()) - getInterfaceList(sd, if_list); - else { - memset(&ifr, 0, sizeof(struct ifreq)); - strncpy(ifr.ifr_name, dev_name.c_str(), IFNAMSIZ-1); - - if(ioctl(sd, SIOCGIFADDR, &ifr)!=0){ - ERROR("setLocalIP: ioctl: %s.\n", strerror(errno)); - exit(-1); - } - - if(ifr.ifr_addr.sa_family==PF_INET){ - struct sockaddr_in* sa = (struct sockaddr_in*)&ifr.ifr_addr; - struct sockaddr_in sa4; - memcpy(&sa4, sa, sizeof(struct sockaddr_in)); - if_list.push_back(make_pair((char*)ifr.ifr_name, - inet_ntoa(sa4.sin_addr))); - } - } - - string local_ip; - for( std::vector >::iterator it = if_list.begin(); - it != if_list.end(); ++it) { - - memset(&ifr, 0, sizeof(struct ifreq)); - strncpy(ifr.ifr_name, it->first.c_str(), IFNAMSIZ-1); - - if(ioctl(sd, SIOCGIFFLAGS, &ifr)!=0){ - ERROR("setLocalIP: ioctl: %s.\n", strerror(errno)); - exit(-1); - } - - if( (ifr.ifr_flags & IFF_UP) && - (!dev_name.empty() || !(ifr.ifr_flags & IFF_LOOPBACK)) ) { - - local_ip = it->second; - break; - } - } - - close(sd); - - if(local_ip.empty()){ - ERROR("Could not determine proper local address for media advertising!\n"); - ERROR("Try using 'ifconfig -a' to find a proper interface and configure\n"); - ERROR("SEMS to use it.\n"); - exit(-1); - } - - if(ifr.ifr_flags & IFF_LOOPBACK){ - WARN("Media advertising using loopback address!\n"); - WARN("Try to use another network interface if your SEMS\n"); - WARN("should be joinable from the rest of the world.\n"); - } - - return local_ip; -} - -static int parse_args(int argc, char* argv[], - const string& flags, - const string& options, - std::map& args) -{ - for(int i=1; i