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