* 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
sayer/1.4-spce2.6
Ondrej Martine 17 years ago
parent d56a9fa574
commit ccec6fe36c

@ -38,6 +38,8 @@
#include "AmUtils.h"
#include <fstream>
#include <cctype>
#include <algorithm>
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;
}

@ -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 */

@ -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";

@ -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<char,string>& 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 <file> Set configuration file\n"
" -x <dir> Set path for plug-ins\n"
" -d <device/ip> 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 <file> Set PID file\n"
" -u <uid> Set user ID\n"
" -g <gid> Set group ID\n"
#else
" -E Enable debug mode (log to stderr)\n"
#endif
" -D <level> 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<char,string>& args)
{
if (signo == SIGCHLD && AmConfig::IgnoreSIGCHLD)
return;
for(int i=1; i<argc; i++){
char* arg = argv[i];
WARN("Signal %d received.\n", signo);
if (!main_pid || (main_pid == getpid())) {
if( (*arg != '-') || !*(++arg) ) {
fprintf(stderr, "%s: invalid argument '%s'\n", progname, argv[i]);
return false;
}
static AmMutex clean_up_mut;
static AmCondition<bool> 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<char,string>& args)
{
for(std::map<char,string>::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<bool> 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<char,string>& args)
#endif /* !DISABLE_DAEMON_MODE */
/** Get the list of network interfaces with the associated PF_INET addresses */
static bool getInterfaceList(int sd, std::vector<std::pair<string,string> >& if_list)
{
for(std::map<char,string>::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; i<n_dev; i++){
if(ifrs[i].ifr_addr.sa_family==PF_INET){
struct sockaddr_in* sa = (struct sockaddr_in*)&ifrs[i].ifr_addr;
if_list.push_back(make_pair((char*)ifrs[i].ifr_name,
inet_ntoa(sa->sin_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<std::pair<string,string> > 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<std::pair<string,string> >::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<char,string> args;
std::map<char,string>::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<char,string>::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()?
"<not set>":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() ? "<not set>" : AmConfig::DaemonUid.c_str(),
AmConfig::DaemonGid.empty() ? "<not set>" : 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() ? "<not set>" : 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<std::pair<string,string> >& 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; i<n_dev; i++){
if(ifrs[i].ifr_addr.sa_family==PF_INET){
struct sockaddr_in* sa = (struct sockaddr_in*)&ifrs[i].ifr_addr;
struct sockaddr_in sa4;
memcpy(&sa4, sa, sizeof(struct sockaddr_in));
if_list.push_back(make_pair((char*)ifrs[i].ifr_name,
inet_ntoa(sa4.sin_addr)));
}
}
#else // defined(BSD44SOCKETS)
struct ifreq* p_ifr = ifc.ifc_req;
while((char*)p_ifr - (char*)ifc.ifc_req < ifc.ifc_len){
INFO("Disposing event dispatcher\n");
AmEventDispatcher::dispose();
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)));
}
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<std::pair<string,string> > 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<std::pair<string,string> >::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<char,string>& args)
{
for(int i=1; i<argc; i++){
char* arg = argv[i];
if( (*arg != '-') || !*(++arg) ) {
fprintf(stderr, "%s: invalid parameter: '%s'.\n", argv[0], argv[i]);
return -1;
}
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 parameter '-%c'.\n", argv[0], *arg);
return -1;
}
args[*arg] = argv[i];
}
else {
fprintf(stderr, "%s: unknown parameter '-%c'.\n", argv[0], arg[1]);
return -1;
}
}
return 0;
INFO("Exiting (%s)\n", success ? "success" : "failure");
return (success ? EXIT_SUCCESS : EXIT_FAILURE);
}

@ -20,8 +20,8 @@
* 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
* 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
*/
/** @file sems.h */
@ -35,17 +35,23 @@
#define PLUG_IN_PATH "/usr/local/lib/sems/plug-in"
#define DEFAULT_ANNOUNCE "default.wav"
#define DEFAULT_RECORD_TIME 30
#define DEFAULT_DAEMON_MODE 1
#define PREFIX_SEPARATOR ""
#define RTP_LOWPORT 1024
#define RTP_HIGHPORT 0xffff
#define MAX_FORWARDS 70
#ifndef DISABLE_DAEMON_MODE
# define DEFAULT_DAEMON_MODE true
# define DEFAULT_DAEMON_PID_FILE "/var/local/run/sems.pid"
# define DEFAULT_DAEMON_UID ""
# define DEFAULT_DAEMON_GID ""
#endif
#define DEFAULT_SIGNATURE "Sip Express Media Server " \
"(" SEMS_VERSION " (" ARCH "/" OS"))"
// session considered dead after 5 minutes no RTP
#define DEAD_RTP_TIME 5*60
#define DEAD_RTP_TIME 5*60
/* Session Timer defaul configuration: */
#define DEFAULT_ENABLE_SESSION_TIMER 1

Loading…
Cancel
Save