1 /*
2  * Copyright (C) 2002-2003 Fhg Fokus
3  *
4  * This file is part of SEMS, a free SIP media server.
5  *
6  * SEMS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version. This program is released under
10  * the GPL with the additional exemption that compiling, linking,
11  * and/or using OpenSSL is allowed.
12  *
13  * For a license to use the SEMS software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * SEMS is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 
28 #include "sems.h"
29 #include "AmUtils.h"
30 #include "AmConfig.h"
31 #include "AmPlugIn.h"
32 #include "AmSessionContainer.h"
33 #include "AmMediaProcessor.h"
34 #include "AmRtpReceiver.h"
35 #include "AmEventDispatcher.h"
36 #include "AmSessionProcessor.h"
37 #include "AmAppTimer.h"
38 
39 #ifdef WITH_ZRTP
40 # include "AmZRTP.h"
41 #endif
42 
43 #include "SipCtrlInterface.h"
44 #include "sip/trans_table.h"
45 #include "sip/async_file_writer.h"
46 
47 #include "log.h"
48 
49 #include <unistd.h>
50 #include <stdio.h>
51 #include <errno.h>
52 #include <signal.h>
53 
54 #include <grp.h>
55 #include <pwd.h>
56 
57 #include <sys/resource.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <fcntl.h>
61 
62 #include <event2/thread.h>
63 
64 #ifdef PROPAGATE_COREDUMP_SETTINGS
65 #include <sys/prctl.h>
66 #endif
67 
68 #include <string>
69 using std::string;
70 
71 #if defined(__linux__)
72 #include <sys/prctl.h>
73 #endif
74 
75 const char* progname = NULL;    /**< Program name (actually argv[0])*/
76 int main_pid = 0;               /**< Main process PID */
77 
78 /** SIP stack (controller interface) */
79 #define sip_ctrl (*SipCtrlInterface::instance())
80 
81 
print_usage(bool short_=false)82 static void print_usage(bool short_=false)
83 {
84   if (short_) {
85     printf("Usage: %s [OPTIONS]\n"
86            "Try `%s -h' for more information.\n",
87            progname, progname);
88   }
89   else {
90     printf(
91         DEFAULT_SIGNATURE "\n"
92         "Usage: %s [OPTIONS]\n"
93         "Available options:\n"
94         "    -f <file>       Set configuration file\n"
95         "    -x <dir>        Set path for plug-ins\n"
96         "    -d <device/ip>  Set network device (or IP address) for media advertising\n"
97 #ifndef DISABLE_DAEMON_MODE
98         "    -E              Enable debug mode (do not daemonize, log to stderr).\n"
99         "    -P <file>       Set PID file\n"
100         "    -u <uid>        Set user ID\n"
101         "    -g <gid>        Set group ID\n"
102 #else
103         "    -E              Enable debug mode (log to stderr)\n"
104 #endif
105         "    -D <level>      Set log level (0=error, 1=warning, 2=info, 3=debug; default=%d)\n"
106         "    -v              Print version\n"
107         "    -h              Print this help\n",
108         progname, AmConfig::LogLevel
109     );
110   }
111 }
112 
113 /* Note: The function should not use log because it is called before logging is initialized. */
parse_args(int argc,char * argv[],std::map<char,string> & args)114 static bool parse_args(int argc, char* argv[], std::map<char,string>& args)
115 {
116 #ifndef DISABLE_DAEMON_MODE
117     static const char* opts = ":hvEf:x:d:D:u:g:P:";
118 #else
119     static const char* opts = ":hvEf:x:d:D:";
120 #endif
121 
122     opterr = 0;
123 
124     while (true) {
125 	int c = getopt(argc, argv, opts);
126 	switch (c) {
127 	case -1:
128 	    return true;
129 
130 	case ':':
131 	    fprintf(stderr, "%s: missing argument for option '-%c'\n", progname, optopt);
132 	    return false;
133 
134 	case '?':
135 	    fprintf(stderr, "%s: unknown option '-%c'\n", progname, optopt);
136 	    return false;
137 
138 	default:
139 	    args[c] = (optarg ? optarg : "yes");
140 	}
141     }
142 }
143 
set_default_interface(const string & iface_name)144 static void set_default_interface(const string& iface_name)
145 {
146   unsigned int idx=0;
147   map<string,unsigned short>::iterator if_it = AmConfig::SIP_If_names.find("default");
148   if(if_it == AmConfig::SIP_If_names.end()) {
149     AmConfig::SIP_interface intf;
150     intf.name = "default";
151     AmConfig::SIP_Ifs.push_back(intf);
152     AmConfig::SIP_If_names["default"] = AmConfig::SIP_Ifs.size()-1;
153     idx = AmConfig::SIP_Ifs.size()-1;
154   }
155   else {
156     idx = if_it->second;
157   }
158   AmConfig::SIP_Ifs[idx].LocalIP = iface_name;
159 
160   if_it = AmConfig::RTP_If_names.find("default");
161   if(if_it == AmConfig::RTP_If_names.end()) {
162     AmConfig::RTP_interface intf;
163     intf.name = "default";
164     AmConfig::RTP_Ifs.push_back(intf);
165     AmConfig::RTP_If_names["default"] = AmConfig::RTP_Ifs.size()-1;
166     idx = AmConfig::RTP_Ifs.size()-1;
167   }
168   else {
169     idx = if_it->second;
170   }
171   AmConfig::RTP_Ifs[idx].LocalIP = iface_name;
172 }
173 
174 /* Note: The function should not use logging because it is called before
175    the logging is initialized. */
apply_args(std::map<char,string> & args)176 static bool apply_args(std::map<char,string>& args)
177 {
178   for(std::map<char,string>::iterator it = args.begin();
179       it != args.end(); ++it){
180 
181     switch( it->first ){
182     case 'd':
183       set_default_interface(it->second);
184       break;
185 
186     case 'D':
187       if (!AmConfig::setLogLevel(it->second)) {
188         fprintf(stderr, "%s: invalid log level: %s\n", progname, it->second.c_str());
189         return false;
190       }
191       break;
192 
193     case 'E':
194 #ifndef DISABLE_DAEMON_MODE
195      AmConfig::DaemonMode = false;
196 #endif
197      if (!AmConfig::setLogStderr("yes")) {
198        return false;
199      }
200      break;
201 
202     case 'f':
203       AmConfig::ConfigurationFile = it->second;
204       break;
205 
206     case 'x':
207       AmConfig::PlugInPath = it->second;
208       break;
209 
210 #ifndef DISABLE_DAEMON_MODE
211     case 'P':
212       AmConfig::DaemonPidFile = it->second;
213       break;
214 
215     case 'u':
216       AmConfig::DaemonUid = it->second;
217       break;
218 
219     case 'g':
220       AmConfig::DaemonGid = it->second;
221       break;
222 #endif
223 
224     case 'h':
225     case 'v':
226     default:
227       /* nothing to apply */
228       break;
229     }
230   }
231 
232   return true;
233 }
234 
235 /** Flag to mark the shutdown is in progress (in the main process) */
236 static AmCondition<bool> is_shutting_down(false);
237 
signal_handler(int sig)238 static void signal_handler(int sig)
239 {
240   if (sig == SIGUSR1 || sig == SIGUSR2) {
241     DBG("brodcasting User event to %u sessions...\n",
242 	AmSession::getSessionNum());
243     AmEventDispatcher::instance()->
244       broadcast(new AmSystemEvent(sig == SIGUSR1?
245 				  AmSystemEvent::User1 : AmSystemEvent::User2));
246     return;
247   }
248 
249   if (sig == SIGCHLD && AmConfig::IgnoreSIGCHLD) {
250     return;
251   }
252 
253   if (sig == SIGPIPE && AmConfig::IgnoreSIGPIPE) {
254     return;
255   }
256 
257   WARN("Signal %s (%d) received.\n", strsignal(sig), sig);
258 
259   if (sig == SIGHUP) {
260     AmSessionContainer::instance()->broadcastShutdown();
261     return;
262   }
263 
264   if (sig == SIGTERM) {
265     AmSessionContainer::instance()->enableUncleanShutdown();
266   }
267 
268   if (main_pid == getpid()) {
269     if(!is_shutting_down.get()) {
270       is_shutting_down.set(true);
271 
272       INFO("Stopping SIP stack after signal\n");
273       sip_ctrl.stop();
274     }
275   }
276   else {
277     /* exit other processes immediately */
278     exit(0);
279   }
280 }
281 
set_sighandler(void (* handler)(int))282 int set_sighandler(void (*handler)(int))
283 {
284   static int sigs[] = {
285     SIGHUP, SIGPIPE, SIGINT, SIGTERM, SIGCHLD, SIGUSR1, SIGUSR2, 0
286   };
287 
288   for (int* sig = sigs; *sig; sig++) {
289     if (signal(*sig, handler) == SIG_ERR ) {
290       ERROR("Cannot install signal handler for %s.\n", strsignal(*sig));
291       return -1;
292     }
293   }
294 
295   return 0;
296 }
297 
298 #ifndef DISABLE_DAEMON_MODE
299 
write_pid_file()300 static int write_pid_file()
301 {
302   FILE* fpid = fopen(AmConfig::DaemonPidFile.c_str(), "w");
303 
304   if (fpid) {
305     string spid = int2str((int)getpid());
306     fwrite(spid.c_str(), spid.length(), 1, fpid);
307     fclose(fpid);
308     return 0;
309   }
310   else {
311     ERROR("Cannot write PID file '%s': %s.\n",
312         AmConfig::DaemonPidFile.c_str(), strerror(errno));
313   }
314 
315   return -1;
316 }
317 
318 #endif /* !DISABLE_DAEMON_MODE */
319 
set_fd_limit()320 int set_fd_limit()
321 {
322   struct rlimit rlim;
323   if(getrlimit(RLIMIT_NOFILE,&rlim) < 0) {
324     ERROR("getrlimit: %s\n",strerror(errno));
325     return -1;
326   }
327 
328   rlim.rlim_cur = rlim.rlim_max;
329 
330   if(setrlimit(RLIMIT_NOFILE,&rlim) < 0) {
331     ERROR("setrlimit: %s\n",strerror(errno));
332     return -1;
333   }
334 
335   INFO("Open FDs limit has been raised to %u",
336        (unsigned int)rlim.rlim_cur);
337 
338   return 0;
339 }
340 
341 /*
342  * Main
343  */
main(int argc,char * argv[])344 int main(int argc, char* argv[])
345 {
346   int success = false;
347   std::map<char,string> args;
348   #ifndef DISABLE_DAEMON_MODE
349   int fd[2] = {0,0};
350   #endif
351 
352   progname = strrchr(argv[0], '/');
353   progname = (progname == NULL ? argv[0] : progname + 1);
354 
355   if(!parse_args(argc, argv, args)){
356     print_usage(true);
357     return 1;
358   }
359 
360   if(args.find('h') != args.end()){
361     print_usage();
362     return 0;
363   }
364 
365   if(args.find('v') != args.end()){
366     printf("%s\n", DEFAULT_SIGNATURE);
367     return 0;
368   }
369 
370   // Init default interface
371   //AmConfig::Ifs.push_back(AmConfig::IP_interface());
372 
373   /* apply command-line options */
374   if(!apply_args(args)){
375     print_usage(true);
376     goto error;
377   }
378 
379   init_logging();
380 
381   /* load and apply configuration file */
382   if(AmConfig::readConfiguration()){
383     ERROR("Errors occured while reading configuration file: exiting.");
384     return -1;
385   }
386 
387   log_level = AmConfig::LogLevel;
388   log_stderr = AmConfig::LogStderr;
389 
390   /* re-apply command-line options to override configuration file */
391   if(!apply_args(args)){
392     goto error;
393   }
394 
395   if(AmConfig::finalizeIPConfig() < 0)
396     goto error;
397 
398   printf("Configuration:\n"
399 #ifdef _DEBUG
400          "       log level:           %s (%i)\n"
401          "       log to stderr:       %s\n"
402 #endif
403 	 "       configuration file:  %s\n"
404 	 "       plug-in path:        %s\n"
405 #ifndef DISABLE_DAEMON_MODE
406          "       daemon mode:         %s\n"
407          "       daemon UID:          %s\n"
408          "       daemon GID:          %s\n"
409 #endif
410 	 "       application:         %s\n"
411 	 "\n",
412 #ifdef _DEBUG
413 	 log_level2str[AmConfig::LogLevel], AmConfig::LogLevel,
414          AmConfig::LogStderr ? "yes" : "no",
415 #endif
416 	 AmConfig::ConfigurationFile.c_str(),
417 	 AmConfig::PlugInPath.c_str(),
418 #ifndef DISABLE_DAEMON_MODE
419 	 AmConfig::DaemonMode ? "yes" : "no",
420 	 AmConfig::DaemonUid.empty() ? "<not set>" : AmConfig::DaemonUid.c_str(),
421 	 AmConfig::DaemonGid.empty() ? "<not set>" : AmConfig::DaemonGid.c_str(),
422 #endif
423 	 AmConfig::Application.empty() ? "<not set>" : AmConfig::Application.c_str());
424 
425   AmConfig::dump_Ifs();
426 
427   if(set_fd_limit() < 0) {
428     WARN("could not raise FD limit");
429   }
430 
431 #ifndef DISABLE_DAEMON_MODE
432 
433   if(AmConfig::DaemonMode){
434 #ifdef PROPAGATE_COREDUMP_SETTINGS
435     struct rlimit lim;
436     bool have_limit = false;
437     if (getrlimit(RLIMIT_CORE, &lim) == 0) have_limit = true;
438     int dumpable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
439 #endif
440 
441     if(!AmConfig::DaemonGid.empty()){
442       unsigned int gid;
443       if(str2i(AmConfig::DaemonGid, gid)){
444 	struct group* grnam = getgrnam(AmConfig::DaemonGid.c_str());
445 	if(grnam != NULL){
446 	  gid = grnam->gr_gid;
447 	}
448 	else{
449 	  ERROR("Cannot find group '%s' in the group database.\n",
450 		AmConfig::DaemonGid.c_str());
451 	  goto error;
452 	}
453       }
454       if(setgid(gid)<0){
455 	ERROR("Cannot change GID to %i: %s.",
456 	      gid,
457 	      strerror(errno));
458 	goto error;
459       }
460     }
461 
462     if(!AmConfig::DaemonUid.empty()){
463       unsigned int uid;
464       if(str2i(AmConfig::DaemonUid, uid)){
465 	struct passwd* pwnam = getpwnam(AmConfig::DaemonUid.c_str());
466 	if(pwnam != NULL){
467 	  uid = pwnam->pw_uid;
468 	}
469 	else{
470 	  ERROR("Cannot find user '%s' in the user database.\n",
471 		AmConfig::DaemonUid.c_str());
472 	  goto error;
473 	}
474       }
475       if(setuid(uid)<0){
476 	ERROR("Cannot change UID to %i: %s.",
477 	      uid,
478 	      strerror(errno));
479 	goto error;
480       }
481     }
482 
483 #if defined(__linux__)
484     if(!AmConfig::DaemonUid.empty() || !AmConfig::DaemonGid.empty()){
485       if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
486 	WARN("unable to set daemon to dump core after setuid/setgid\n");
487       }
488     }
489 #endif
490 
491     /* fork to become!= group leader*/
492     if (pipe(fd) == -1) { /* Create a pipe */
493         ERROR("Cannot create pipe.\n");
494         goto error;
495     }
496     int pid;
497     if ((pid=fork())<0){
498       ERROR("Cannot fork: %s.\n", strerror(errno));
499       goto error;
500     }else if (pid!=0){
501       close(fd[1]);
502       /* parent process => wait for result from child*/
503       for(int i=0;i<2;i++){
504         DBG("waiting for child[%d] response\n", i);
505         if (read(fd[0], &pid, sizeof(int))<0) {
506 	  DBG("Error reading pid from child.\n");
507 	}
508         if(pid<0){
509           ERROR("Child [%d] return an error: %d\n", i, pid);
510           close(fd[0]);
511           goto error;
512         }
513         DBG("child [%d] pid:%d\n", i, pid);
514       }
515       DBG("all children return OK. bye world!\n");
516       close(fd[0]);
517       return 0;
518     }else {
519       /* child */
520       close(fd[0]);
521       main_pid = getpid();
522       if (write(fd[1], &main_pid, sizeof(int))<0) {
523 	DBG("Error writing pid.\n");
524       }
525     }
526     /* become session leader to drop the ctrl. terminal */
527     if (setsid()<0){
528       ERROR("setsid failed: %s.\n", strerror(errno));
529     }
530     /* fork again to drop group  leadership */
531     if ((pid=fork())<0){
532       ERROR("Cannot fork: %s.\n", strerror(errno));
533       goto error;
534     }else if (pid!=0){
535       /*parent process => exit */
536       close(fd[1]);
537       main_pid = getpid();
538       DBG("I'm out. pid: %d", main_pid);
539       return 0;
540     }
541 
542     if(write_pid_file()<0) {
543       goto error;
544     }
545 
546 #ifdef PROPAGATE_COREDUMP_SETTINGS
547     if (have_limit) {
548       if (setrlimit(RLIMIT_CORE, &lim) < 0) ERROR("failed to set RLIMIT_CORE\n");
549       if (prctl(PR_SET_DUMPABLE, dumpable, 0, 0, 0)<0) ERROR("cannot re-set core dumping to %d!\n", dumpable);
550     }
551 #endif
552 
553     /* try to replace stdin, stdout & stderr with /dev/null */
554     if (freopen("/dev/null", "r", stdin)==0){
555       ERROR("Cannot replace stdin with /dev/null: %s.\n",
556 	    strerror(errno));
557       /* continue, leave it open */
558     };
559     if (freopen("/dev/null", "w", stdout)==0){
560       ERROR("Cannot replace stdout with /dev/null: %s.\n",
561 	    strerror(errno));
562       /* continue, leave it open */
563     };
564     /* close stderr only if log_stderr=0 */
565     if ((!log_stderr) && (freopen("/dev/null", "w", stderr)==0)){
566       ERROR("Cannot replace stderr with /dev/null: %s.\n",
567 	    strerror(errno));
568       /* continue, leave it open */
569     };
570   }
571 
572 #endif /* DISABLE_DAEMON_MODE */
573 
574   main_pid = getpid();
575 
576   init_random();
577 
578   if(set_sighandler(signal_handler))
579     goto error;
580 
581 #ifdef WITH_ZRTP
582   if (AmZRTP::init()) {
583     ERROR("Cannot initialize ZRTP\n");
584     goto error;
585   }
586 #endif
587 
588   INFO("Starting application timer scheduler\n");
589   AmAppTimer::instance()->start();
590 
591   INFO("Starting session container\n");
592   AmSessionContainer::instance()->start();
593 
594 #ifdef SESSION_THREADPOOL
595   INFO("Starting session processor threads\n");
596   AmSessionProcessor::addThreads(AmConfig::SessionProcessorThreads);
597 #endif
598 
599   INFO("Starting media processor\n");
600   AmMediaProcessor::instance()->init();
601 
602   // init thread usage with libevent
603   // before it's too late
604   if(evthread_use_pthreads() != 0) {
605     ERROR("cannot init thread usage with libevent");
606     goto error;
607   }
608 
609   // start the asynchronous file writer (sorry, no better place...)
610   async_file_writer::instance()->start();
611 
612   INFO("Starting RTP receiver\n");
613   AmRtpReceiver::instance()->start();
614 
615   INFO("Starting SIP stack (control interface)\n");
616   if(sip_ctrl.load()) {
617     goto error;
618   }
619 
620   INFO("Loading plug-ins\n");
621   AmPlugIn::instance()->init();
622   if(AmPlugIn::instance()->load(AmConfig::PlugInPath, AmConfig::LoadPlugins))
623     goto error;
624 
625   AmPlugIn::instance()->registerLoggingPlugins();
626 
627   AmSessionContainer::instance()->initMonitoring();
628 
629   INFO("Starting RTP MUX receiver\n");
630   AmRtpReceiver::instance()->startRtpMuxReceiver();
631 
632   #ifndef DISABLE_DAEMON_MODE
633   if(fd[1]) {
634     if (write(fd[1], &main_pid, sizeof(int))<0) {
635        DBG("error writing main_pid to parent\n");
636     }
637     close(fd[1]); fd[1] = 0;
638   }
639   #endif
640 
641   INFO("SEMS " SEMS_VERSION " (" ARCH "/" OS") started");
642 
643   // running the server
644   if(sip_ctrl.run() != -1)
645     success = true;
646 
647   // session container stops active sessions
648   INFO("Disposing session container\n");
649   AmSessionContainer::dispose();
650 
651   DBG("** Transaction table dump: **\n");
652   dumps_transactions();
653   DBG("*****************************\n");
654 
655   INFO("Disposing RTP receiver\n");
656   AmRtpReceiver::dispose();
657 
658   INFO("Disposing media processor\n");
659   AmMediaProcessor::dispose();
660 
661   INFO("Disposing event dispatcher\n");
662   AmEventDispatcher::dispose();
663 
664  error:
665   INFO("Disposing plug-ins\n");
666   AmPlugIn::dispose();
667 
668   async_file_writer::instance()->stop();
669   async_file_writer::instance()->join();
670 
671 #ifndef DISABLE_DAEMON_MODE
672   if (AmConfig::DaemonMode) {
673     unlink(AmConfig::DaemonPidFile.c_str());
674   }
675   if(fd[1]){
676      main_pid = -1;
677      if (write(fd[1], &main_pid, sizeof(int))<0) {
678        DBG("error writing -1 to parent\n");
679      }
680      close(fd[1]);
681   }
682 #endif
683 
684   sip_ctrl.cleanup();
685 
686   INFO("Exiting (%s)\n", success ? "success" : "failure");
687   return (success ? EXIT_SUCCESS : EXIT_FAILURE);
688 }
689