/* * $Id: sems.cpp,v 1.6.2.4 2005/04/13 10:57:09 rco Exp $ * * Copyright (C) 2002-2003 Fhg Fokus * * This file is part of sems, a free SIP media server. * * sems 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 * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * sems 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 "sems.h" #include "AmUtils.h" #include "AmConfig.h" //#include "SemsConfiguration.h" #include "AmPlugIn.h" #include "AmSessionContainer.h" #include "AmMail.h" #include "AmServer.h" #include "AmCtrlInterface.h" #include "AmInterfaceHandler.h" #include "AmMediaProcessor.h" #include "AmIcmpWatcher.h" #include "AmRtpReceiver.h" //#include "AmSessionTimer.h" #include "log.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using std::string; using std::pair; 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, map& args); static void print_usage(char* progname); static string getLocalIP(const string& dev_name); int sig_flag; int deamon_mode=1; static void sig_usr_un(int signo) { WARN("signal %d received\n", signo); if(!main_pid || (main_pid == getpid())){ unlink(pid_file.c_str()); INFO("finished\n"); exit(0); } return; } int set_sighandler(sighandler_t sig_usr) { 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; } if (signal(SIGCHLD , sig_usr) == SIG_ERR ) { ERROR("no SIGCHLD signal handler can be installed\n"); return -1; } if (signal(SIGTERM , sig_usr) == SIG_ERR ) { ERROR("no SIGTERM signal handler can be installed\n"); return -1; } if (signal(SIGHUP , sig_usr) == SIG_ERR ) { ERROR("no SIGHUP signal handler can be installed\n"); return -1; } return 0; } int write_pid_file() { FILE* fpid = fopen(pid_file.c_str(),"w"); if(fpid){ string spid = int2str((int)getpid()); fwrite(spid.c_str(),spid.length(),1,fpid); fclose(fpid); return 0; } else { ERROR("could not write pid file '%s': %s\n", pid_file.c_str(),strerror(errno)); } return -1; } // returns 0 if OK static int use_args(char* progname, map& args) { for(map::iterator it = args.begin(); it != args.end(); ++it){ if(it->second.empty()) continue; switch( it->first ){ case 'h': print_usage(progname); exit(0); break; case 'E': AmConfig::setStderr("yes"); continue; 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; case 'f': case 'P': case 'u': case 'g': // already processed, ignore it here break; default: ERROR("%s: bad parameter '-%c'\n",progname,it->first); return -1; } } return 0; } AmFifoCtrlInterface* init_fifo(const string& path) { AmFifoCtrlInterface* ctrl = new AmFifoCtrlInterface(); if(ctrl->createFifo(path)) goto fifo_error; // avoid the FIFO reaches EOF... if((child_pid=fork())>0){ int fifo_write=0; if((fifo_write=open(path.c_str(), O_WRONLY)) == -1){ ERROR("while opening fifo `%s': %s\n", path.c_str(),strerror(errno)); kill(child_pid,SIGTERM); } waitpid(child_pid,0,0); if(fifo_write != -1) ::close(fifo_write); exit(0); } // the main process is waiting // for that child to terminate is_main = 0; if(ctrl->init(path)) goto fifo_error; goto fifo_end; fifo_error: delete ctrl; ctrl = 0; fifo_end: return ctrl; } int main(int argc, char* argv[]) { map args; if(parse_args(argc, argv, "hE","ugPfiodxD", args)){ print_usage(argv[0]); return -1; } if(args.find('h') != args.end()){ print_usage(argv[0]); return 0; } init_log(); AmConfig::setStderr("yes"); AmConfig::setLoglevel("1"); map::iterator cfg_arg; if( (cfg_arg = args.find('f')) != args.end() ) AmConfig::ConfigurationFile = cfg_arg->second; // if(!semsConfig.reloadFile(AmConfig::ConfigurationFile.c_str())) // return -1; AmConfig::readConfiguration(); // semsConfig.warnUnknownParams(); if(use_args(argv[0],args)){ print_usage(argv[0]); return -1; } //if(AmConfig::LocalIP.empty()) AmConfig::LocalIP = getLocalIP(AmConfig::LocalIP); printf( "\n\nConfiguration:\n" " configuration file: %s\n" " Ser's unix socket: %s\n" " our unix socket: %s\n" " reply unix socket: %s\n" " plug-in path: %s\n" " daemon mode: %i\n" " local IP: %s\n" "\n", AmConfig::ConfigurationFile.c_str(), AmConfig::SerSocketName.c_str(), AmConfig::SocketName.c_str(), AmConfig::ReplySocketName.c_str(), AmConfig::PlugInPath.c_str(), AmConfig::DaemonMode, AmConfig::LocalIP.c_str() ); if(AmConfig::DaemonMode){ if( (cfg_arg = args.find('g')) != args.end() ){ unsigned int gid; if(str2i(cfg_arg->second,gid)){ struct group* grnam = getgrnam(cfg_arg->second.c_str()); if(grnam != NULL){ gid = grnam->gr_gid; } else{ ERROR("could not find group '%s' in the group database\n", cfg_arg->second.c_str()); return -1; } } if(setgid(gid)<0){ ERROR("cannot change gid to %i: %s", gid, strerror(errno)); return -1; } } if( (cfg_arg = args.find('u')) != args.end() ){ unsigned int uid; if(str2i(cfg_arg->second,uid)){ struct passwd* pwnam = getpwnam(cfg_arg->second.c_str()); if(pwnam != NULL){ uid = pwnam->pw_uid; } else{ ERROR("could not find user '%s' in the user database.\n", cfg_arg->second.c_str()); return -1; } } if(setuid(uid)<0){ ERROR("cannot change uid to %i: %s", uid, strerror(errno)); return -1; } } /* fork to become!= group leader*/ int pid; if ((pid=fork())<0){ ERROR("Cannot fork:%s\n", strerror(errno)); return -1; }else if (pid!=0){ /* parent process => exit*/ return 0; } /* become session leader to drop the ctrl. terminal */ if (setsid()<0){ ERROR("setsid failed: %s\n",strerror(errno)); } /* fork again to drop group leadership */ if ((pid=fork())<0){ ERROR("Cannot fork:%s\n", strerror(errno)); return -1; }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; } /* 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", strerror(errno)); /* continue, leave it open */ }; if (freopen("/dev/null", "w", stdout)==0){ ERROR("unable to 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", strerror(errno)); /* continue, leave it open */ }; } main_pid = getpid(); if(set_sighandler(sig_usr_un)) return -1; if(AmConfig::init()) return -1; DBG("Loading plug-ins\n"); if(AmPlugIn::instance()->load(AmConfig::PlugInPath)) return -1; DBG("Starting session container\n"); AmSessionContainer::instance()->start(); DBG("Starting media processor\n"); AmMediaProcessor::instance()->init(); DBG("Starting mailer\n"); AmMailDeamon::instance()->start(); DBG("Starting RTP receiver\n"); AmRtpReceiver::instance()->start(); //DBG("Starting Session Timer\n"); //AmSessionTimer::instance()->start(); //DBG("Starting ICMP watcher\n"); //AmIcmpWatcher::instance()->start(); AmUnixCtrlInterface* un_ctrl=0; if(!AmConfig::SocketName.empty()){ un_ctrl = new AmUnixCtrlInterface(); if(un_ctrl->init(AmConfig::SocketName.c_str())){ delete un_ctrl; un_ctrl = 0; } else { AmServer::instance()->regIface(IfaceDesc(un_ctrl, AmRequestHandler::get() )); AmReplyHandler* rh = AmReplyHandler::get(); if(!rh) return -1; AmServer::instance()->regIface(IfaceDesc(rh->getCtrl(),rh)); } } if(// fifo_ctrl || un_ctrl){ AmServer::instance()->run(); } else { ERROR("Sems cannot start without a working link to Ser.\n" "Please set in the config file 'socket_name' \n" " parameter and check the log file" " for errors or warnings.\nExiting.\n"); return -1; } return 0; } static void print_usage(char* progname) { printf( "USAGE: %s [options]\n" " Options:\n" " -f config_filename: sets configuration file to use\n" " -i fifo_name: path and file name of our fifo file.\n" " -o ser_fifo_name: path and file name of Ser's fifo file.\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: root 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" " -h : this help screen.\n" "\n" " Notes:\n" " * plug-ins are searched in plugin_path/{apps,audio}/*.so.\n", progname ); } static void getInterfaceList(int sd, vector >& if_list) { struct ifconf ifc; struct ifreq ifrs[MAX_NET_DEVICES]; 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",strerror(errno)); exit(-1); } int n_dev = ifc.ifc_len / sizeof(struct ifreq); for(int i=0; isin_addr))); } } } static string getLocalIP(const string& dev_name) { //DBG("getLocalIP(%s)\n",dev_name.c_str()); #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; 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; if_list.push_back(make_pair((char*)ifr.ifr_name, inet_ntoa(sa->sin_addr))); } } string local_ip; for( 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, map& args) { for(int i=1; i